POS Kasir uses environment variables for configuration. This guide covers all available settings and how to configure them.
Environment Files
POS Kasir uses two environment files:
.env - Backend configuration (Go API)
web/.env - Frontend configuration (TanStack Start)
Create Environment Files
Copy the example files: cp .env.example .env
cp web/.env.example web/.env
Configure Backend
Edit .env with your settings (see sections below)
Configure Frontend
Edit web/.env with your API endpoint: VITE_API_BASE = http://localhost:8080/api/v1
Backend Configuration
Server & Application
Basic application settings:
# Environment: production or development
APP_ENV = development
# Application name
APP_NAME = POS-Kasir
# Server port
APP_PORT = 8080
# Cookie domain (leave empty for localhost)
COOKIE_DOMAIN =
# Allow cross-origin requests from frontend
WEB_FRONTEND_CROSS_ORIGIN = false
APP_ENV
string
default: "development"
Set to production in production environment. Affects logging and error handling.
Port where the backend API will listen. Must match Docker port mapping if using Docker.
WEB_FRONTEND_CROSS_ORIGIN
Enable if frontend is on different domain. Set to true in production with separate domains.
Database Configuration
PostgreSQL database settings:
# Database host (use 'localhost' for local, service name for Docker)
DB_HOST = localhost
# Database port
DB_PORT = 5432
# Database credentials
DB_USER = postgres
DB_PASSWORD = your_secure_password
DB_NAME = pos_kasir
# SSL mode: disable, require, verify-ca, verify-full
DB_SSLMODE = disable
# Connection pool settings
DB_MAX_OPEN_CONNECTIONS = 10
DB_MAX_IDLE_CONNECTIONS = 2
DB_MAX_LIFETIME_MINUTES = 10
Always use strong passwords for DB_PASSWORD in production environments.
Database host. Use localhost for local development, or container name when using Docker.
SSL mode for database connection. Use require or higher in production.
Maximum number of open connections to database. Tune based on load.
Migration Settings
Database migration configuration:
# Auto-run migrations on startup (not recommended for production)
AUTO_MIGRATE = false
# Path to migration files
MIGRATIONS_PATH = ./sqlc/migrations
Keep AUTO_MIGRATE=false in production. Run migrations manually using make migrate-up.
Logging Configuration
Configure application logging:
# Log level: debug, info, warn, error
LOG_LEVEL = info
# Output logs in JSON format
LOG_JSON_FORMAT = false
Logging level. Use debug for development, info or warn for production.
Enable JSON logging for production log aggregation systems.
JWT Authentication
JSON Web Token settings for user authentication:
# Secret key for signing JWT tokens (use strong random string)
JWT_SECRET = your-super-secret-jwt-key-change-this-in-production
# Token expiration in hours
JWT_DURATION_HOURS = 24
# JWT issuer identifier
JWT_ISSUER = poskasir
Critical : Generate a strong random JWT_SECRET for production:Never commit this secret to version control!
Secret key for signing JWT tokens. Must be strong and unique in production.
Token validity period in hours. Balance security with user experience.
Midtrans Payment Gateway
Configuration for Midtrans digital payment integration:
# Midtrans server key (get from Midtrans dashboard)
MIDTRANS_SERVER_KEY = your-midtrans-server-key
# Use production environment (false = sandbox mode)
MIDTRANS_IS_PROD = false
Get your Midtrans credentials from Midtrans Dashboard :
Sandbox : For testing
Production : For real transactions
Server key from Midtrans dashboard. Use sandbox key for testing.
Set to true only when using production credentials and real payments.
How to Get Midtrans Credentials
Sign up at Midtrans
Go to Settings → Access Keys
Copy your Server Key
Start with Sandbox mode for testing
Switch to Production when ready for real payments
Cloudflare R2 Storage
Configuration for cloud object storage (for product images):
# Cloudflare account ID
R2_ACCOUNT_ID = your-cloudflare-account-id
# R2 access credentials
R2_ACCESS_KEY = your-r2-access-key
R2_SECRET_KEY = your-r2-secret-key
# R2 bucket name
R2_BUCKET = pos-kasir
# Public domain for accessing images
R2_PUBLIC_DOMAIN = https://your-bucket.r2.dev
# Presigned URL expiry in seconds
R2_EXPIRY_SECONDS = 3600
Your Cloudflare account ID from the dashboard.
R2_BUCKET
string
default: "pos-kasir"
Name of your R2 bucket. Create it in Cloudflare dashboard first.
Public URL for accessing uploaded images. Configure in R2 bucket settings.
Log in to Cloudflare Dashboard
Go to R2 → Create Bucket
Name your bucket (e.g., pos-kasir)
Go to Settings → R2 API Tokens
Create an API token with edit permissions
Copy your Access Key and Secret Key
Configure public access or custom domain for R2_PUBLIC_DOMAIN
Frontend Configuration
The frontend requires minimal configuration:
# Backend API base URL
VITE_API_BASE = http://localhost:8080/api/v1
Full URL to the backend API. Include the /api/v1 path.
Environment-Specific URLs
Local Development
Production
Docker Network
VITE_API_BASE = http://localhost:8080/api/v1
Complete Configuration Example
Full .env Example for Development
# ==============================================
# Server & Application Config
# ==============================================
APP_ENV = development
APP_NAME = POS-Kasir
APP_PORT = 8080
COOKIE_DOMAIN =
WEB_FRONTEND_CROSS_ORIGIN = false
# ==============================================
# Database (PostgreSQL)
# ==============================================
DB_HOST = localhost
DB_PORT = 5432
DB_USER = postgres
DB_PASSWORD = postgres
DB_NAME = pos_kasir
DB_SSLMODE = disable
DB_MAX_OPEN_CONNECTIONS = 10
DB_MAX_IDLE_CONNECTIONS = 2
DB_MAX_LIFETIME_MINUTES = 10
# ==============================================
# Migration
# ==============================================
AUTO_MIGRATE = false
MIGRATIONS_PATH = ./sqlc/migrations
# ==============================================
# Logger
# ==============================================
LOG_LEVEL = info
LOG_JSON_FORMAT = false
# ==============================================
# JWT (Authentication)
# ==============================================
JWT_SECRET = dev-secret-change-in-production
JWT_DURATION_HOURS = 24
JWT_ISSUER = poskasir
# ==============================================
# Midtrans (Payment Gateway)
# ==============================================
MIDTRANS_SERVER_KEY = SB-Mid-server-your-sandbox-key
MIDTRANS_IS_PROD = false
# ==============================================
# Cloudflare R2 (Object Storage)
# ==============================================
R2_ACCOUNT_ID = your-account-id
R2_ACCESS_KEY = your-access-key
R2_SECRET_KEY = your-secret-key
R2_BUCKET = pos-kasir
R2_PUBLIC_DOMAIN = https://pos-kasir.r2.dev
R2_EXPIRY_SECONDS = 3600
Full .env Example for Production
# ==============================================
# Server & Application Config
# ==============================================
APP_ENV = production
APP_NAME = POS-Kasir
APP_PORT = 8080
COOKIE_DOMAIN = .yourdomain.com
WEB_FRONTEND_CROSS_ORIGIN = true
# ==============================================
# Database (PostgreSQL)
# ==============================================
DB_HOST = your-database-host.com
DB_PORT = 5432
DB_USER = pos_user
DB_PASSWORD = strong-random-password-here
DB_NAME = pos_kasir_prod
DB_SSLMODE = require
DB_MAX_OPEN_CONNECTIONS = 25
DB_MAX_IDLE_CONNECTIONS = 5
DB_MAX_LIFETIME_MINUTES = 15
# ==============================================
# Migration
# ==============================================
AUTO_MIGRATE = false
MIGRATIONS_PATH = ./sqlc/migrations
# ==============================================
# Logger
# ==============================================
LOG_LEVEL = warn
LOG_JSON_FORMAT = true
# ==============================================
# JWT (Authentication)
# ==============================================
JWT_SECRET = use-openssl-rand-base64-64-to-generate-this
JWT_DURATION_HOURS = 24
JWT_ISSUER = poskasir
# ==============================================
# Midtrans (Payment Gateway)
# ==============================================
MIDTRANS_SERVER_KEY = Mid-server-your-production-key
MIDTRANS_IS_PROD = true
# ==============================================
# Cloudflare R2 (Object Storage)
# ==============================================
R2_ACCOUNT_ID = your-production-account-id
R2_ACCESS_KEY = your-production-access-key
R2_SECRET_KEY = your-production-secret-key
R2_BUCKET = pos-kasir-prod
R2_PUBLIC_DOMAIN = https://cdn.yourdomain.com
R2_EXPIRY_SECONDS = 3600
Security Best Practices
Follow these security practices for production deployments:
Never commit .env files to version control
Add .env to .gitignore
Use environment-specific secrets
Rotate secrets regularly
Use strong random strings for JWT_SECRET
Use strong passwords for DB_PASSWORD
Enable SSL with DB_SSLMODE=require in production
Limit database user permissions
Use separate databases for dev/staging/production
Regularly backup your database
Set APP_ENV=production in production
Enable WEB_FRONTEND_CROSS_ORIGIN only if needed
Set appropriate COOKIE_DOMAIN
Keep JWT_DURATION_HOURS reasonable (24-48 hours)
Use LOG_LEVEL=warn or error in production
Keep Midtrans in sandbox mode until ready
Restrict R2 bucket access appropriately
Use separate credentials for production
Monitor API usage and costs
Verification
After configuration, verify your settings:
Test Database Connection
Should display current migration version without errors.
Check Application Startup
docker-compose up -d
docker-compose logs backend
Look for successful startup messages and no connection errors.
Test API Health
curl http://localhost:8080/api/v1/health
Verify Frontend Connection
Open browser to http://localhost:3000 and check browser console for API connection.
Next Steps
First Steps Learn how to use POS Kasir after configuration
API Reference Explore the API documentation