Distributed Deployment & Load Balancing Guide
Overviewβ
Yes, Gojang supports distributed deployment and load balancing! π
Gojang applications can be deployed across multiple servers with load balancing to achieve:
- High availability - No single point of failure
- Horizontal scaling - Handle more traffic by adding servers
- Better performance - Distribute load across multiple instances
- Zero-downtime deployments - Update servers one at a time
Key Requirements:
- PostgreSQL database (not SQLite) - Shared database for all instances
- Persistent session storage (Redis or database-backed) - Share sessions across instances
- Load balancer (Nginx, HAProxy, or cloud load balancer) - Distribute traffic
Why It Worksβ
Gojang is stateless by design (except for sessions), which makes it perfect for distributed deployments:
β Shared Database - All instances connect to the same PostgreSQL database β Session Storage - Sessions can be stored in Redis or PostgreSQL (not in-memory) β No Local File Dependencies - Static files can be served via CDN or shared storage β Stateless Handlers - Request handlers don't maintain local state
Architecture Patternsβ
Pattern 1: Simple Load Balancing (Single Server, Multiple Instances)β
Run multiple instances on the same server on different ports:
βββββββββββββββββββββββββββββββββββββββ
β Load Balancer (Nginx) β
β Port 80/443 β
βββββββββββββββββββββββββββββββββββββββ
β
βββββββββββΌββββββββββ
β β β
βΌ βΌ βΌ
ββββββββββ ββββββββββ ββββββββββ
β App:1 β β App:2 β β App:3 β
β :8080 β β :8081 β β :8082 β
ββββββββββ ββββββββββ ββββββββββ
β
βΌ
ββββββββββββββββββββ
β PostgreSQL β
β Redis (opt) β
ββββββββββββββββββββ
Benefits:
- Easy to set up
- No network latency between instances and database
- Good for vertical scaling
Limitations:
- Single server is still a point of failure
- Limited by single server's resources
Pattern 2: Multi-Server Deployment (Distributed)β
Deploy instances across multiple servers:
ββββββββββββββββββββββββββββββββββββββ
β Cloud Load Balancer (AWS) β
β or HAProxy/Nginx β
ββββββββββββββββββββββββββββββββββββββ
β
βββββββββββΌβββββββββββ
β β β
βΌ βΌ βΌ
ββββββββββ ββββββββββ ββββββββββ
βServer 1β βServer 2β βServer 3β
β App:80 β β App:80 β β App:80 β
ββββββββββ ββββββββββ ββββββββββ
β
βββββββββββ΄βββββββββββ
β β
βΌ βΌ
ββββββββββββββββ ββββββββββββββββ
β PostgreSQL β β Redis β
β (Primary) β β (Sessions) β
ββββββββββββββββ ββββββββββββββββ
Benefits:
- True high availability
- Geographic distribution possible
- Independent server failures
Limitations:
- More complex setup
- Network latency considerations
- Higher costs
Pattern 3: Container Orchestration (Docker/Kubernetes)β
Deploy as containers with auto-scaling:
ββββββββββββββββββββββββββββββββββββββ
β Cloud Load Balancer β
ββββββββββββββββββββββββββββββββββββββ
β
βββββββββββββββββββββββββββββββββββββββ
β Kubernetes / Docker Swarm β
β ββββββββ ββββββββ ββββββββ β
β β Pod1 β β Pod2 β β Pod3 β ... β
β β App β β App β β App β β
β ββββββββ ββββββββ ββββββββ β
βββββββββββββββββββββββββββββββββββββββ
β
βββββββββββ΄βββββββββββ
β β
βΌ βΌ
ββββββββββββββββ ββββββββββββββββ
β PostgreSQL β β Redis β
β (RDS) β β (ElastiCache)β
ββββββββββββββββ ββββββββββββββββ
Benefits:
- Auto-scaling based on load
- Easy deployment and rollback
- Self-healing (restart failed containers)
Step-by-Step Setupβ
Step 1: Configure PostgreSQL (Shared Database)β
All instances must connect to the same PostgreSQL database.
Environment variable:
DATABASE_URL=postgres://user:password@db-server:5432/gojang?sslmode=require
In your application code:
// Database URL is read from environment variable
// All instances will connect to the same PostgreSQL server
dbURL := os.Getenv("DATABASE_URL")
Using Docker Compose:
services:
db:
image: postgres:15-alpine
environment:
- POSTGRES_DB=gojang
- POSTGRES_USER=gojang
- POSTGRES_PASSWORD=securepassword
volumes:
- postgres-data:/var/lib/postgresql/data
Step 2: Configure Persistent Session Storageβ
Default behavior - Sessions are stored in memory (NOT suitable for distributed deployment):
// β In-memory sessions - lost on restart, not shared between instances
sessionManager := scs.New()
// Default store is in-memory
For distributed deployment - Use Redis or database-backed sessions:
Option A: Redis Sessions (Recommended)β
1. Add Redis dependency:
go get github.com/alexedwards/scs/redisstore
go get github.com/gomodule/redigo/redis
2. Update session manager code:
Edit app/gojang/http/middleware/session.go:
package middleware
import (
"time"
"os"
"github.com/gojangframework/gojang/app/gojang/config"
"github.com/alexedwards/scs/v2"
"github.com/alexedwards/scs/redisstore"
"github.com/gomodule/redigo/redis"
)
func NewSessionManager(cfg *config.Config) *scs.SessionManager {
sessionManager := scs.New()
sessionManager.Lifetime = cfg.SessionLifetime
sessionManager.Cookie.Name = "session_id"
sessionManager.Cookie.HttpOnly = true
sessionManager.Cookie.Secure = !cfg.Debug
sessionManager.Cookie.SameSite = 2
sessionManager.Cookie.Path = "/"
sessionManager.IdleTimeout = 30 * time.Minute
// Use Redis for distributed sessions
redisURL := os.Getenv("REDIS_URL")
if redisURL != "" {
pool := &redis.Pool{
MaxIdle: 10,
IdleTimeout: 240 * time.Second,
Dial: func() (redis.Conn, error) {
return redis.DialURL(redisURL)
},
}
sessionManager.Store = redisstore.New(pool)
}
return sessionManager
}
3. Set Redis URL environment variable:
REDIS_URL=redis://localhost:6379
4. Docker Compose with Redis:
services:
app:
environment:
- REDIS_URL=redis://redis:6379
depends_on:
- redis
redis:
image: redis:7-alpine
restart: unless-stopped
volumes:
- redis-data:/data
Option B: PostgreSQL Sessionsβ
1. Add PostgreSQL session store:
go get github.com/alexedwards/scs/postgresstore
2. Update session manager:
import (
"github.com/alexedwards/scs/postgresstore"
)
func NewSessionManager(cfg *config.Config, db *sql.DB) *scs.SessionManager {
sessionManager := scs.New()
// ... other configuration ...
// Use PostgreSQL for sessions
sessionManager.Store = postgresstore.New(db)
return sessionManager
}
3. Create sessions table:
CREATE TABLE sessions (
token TEXT PRIMARY KEY,
data BYTEA NOT NULL,
expiry TIMESTAMPTZ NOT NULL
);
CREATE INDEX sessions_expiry_idx ON sessions (expiry);
Step 3: Configure Load Balancerβ
Option A: Nginx Load Balancerβ
1. Create /etc/nginx/conf.d/gojang-lb.conf:
upstream gojang_backend {
# Load balancing method
least_conn; # Route to server with fewest connections
# Your application instances
server 127.0.0.1:8080 max_fails=3 fail_timeout=30s;
server 127.0.0.1:8081 max_fails=3 fail_timeout=30s;
server 127.0.0.1:8082 max_fails=3 fail_timeout=30s;
# Health checks (requires nginx-plus or use proxy_next_upstream)
# For graceful handling of down servers
}
server {
listen 80;
server_name yourdomain.com;
# Redirect to HTTPS
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name yourdomain.com;
ssl_certificate /etc/nginx/ssl/cert.pem;
ssl_certificate_key /etc/nginx/ssl/key.pem;
# Security headers
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
location / {
proxy_pass http://gojang_backend;
# Pass original headers
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# WebSocket support (if needed)
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
# Timeouts
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
# Retry failed requests on next server
proxy_next_upstream error timeout invalid_header http_500 http_502 http_503;
}
}
2. Test and reload Nginx:
sudo nginx -t
sudo systemctl reload nginx
Option B: HAProxy Load Balancerβ
Create /etc/haproxy/haproxy.cfg:
global
log /dev/log local0
log /dev/log local1 notice
maxconn 4096
defaults
log global
mode http
option httplog
option dontlognull
timeout connect 5000
timeout client 50000
timeout server 50000
frontend http_front
bind *:80
redirect scheme https code 301 if !{ ssl_fc }
frontend https_front
bind *:443 ssl crt /etc/haproxy/certs/cert.pem
default_backend gojang_backend
# Sticky sessions (if not using Redis)
# cookie SERVERID insert indirect nocache
backend gojang_backend
balance leastconn # Load balancing algorithm
option httpchk GET /health # Health check endpoint
server app1 127.0.0.1:8080 check
server app2 127.0.0.1:8081 check
server app3 127.0.0.1:8082 check
Option C: Cloud Load Balancersβ
AWS Application Load Balancer (ALB):
- Automatically distributes traffic across multiple targets
- Health checks included
- Auto-scaling group integration
- SSL/TLS termination
Google Cloud Load Balancer:
- Global or regional load balancing
- Automatic failover
- CDN integration
Cloudflare Load Balancing:
- Geographic routing
- DDoS protection
- Health monitoring
Step 4: Run Multiple Instancesβ
Using systemd (single server)β
1. Create service files for each instance:
/etc/systemd/system/gojang@.service:
[Unit]
Description=Gojang Application Instance %i
After=network.target postgresql.service
[Service]
Type=simple
User=gojang
WorkingDirectory=/opt/gojang
Environment="PORT=808%i"
Environment="DEBUG=false"
Environment="DATABASE_URL=postgres://user:pass@localhost:5432/gojang"
Environment="REDIS_URL=redis://localhost:6379"
ExecStart=/opt/gojang/gojang
Restart=on-failure
RestartSec=5s
[Install]
WantedBy=multi-user.target
2. Enable and start instances:
sudo systemctl enable gojang@0
sudo systemctl enable gojang@1
sudo systemctl enable gojang@2
sudo systemctl start gojang@0 # Runs on port 8080
sudo systemctl start gojang@1 # Runs on port 8081
sudo systemctl start gojang@2 # Runs on port 8082
Using Docker Composeβ
docker-compose.yml:
version: '3.8'
services:
app1:
build: .
restart: unless-stopped
environment:
- PORT=8080
- DEBUG=false
- DATABASE_URL=postgres://gojang:password@db:5432/gojang?sslmode=disable
- REDIS_URL=redis://redis:6379
- SESSION_KEY=${SESSION_KEY}
depends_on:
- db
- redis
networks:
- gojang-network
app2:
build: .
restart: unless-stopped
environment:
- PORT=8080
- DEBUG=false
- DATABASE_URL=postgres://gojang:password@db:5432/gojang?sslmode=disable
- REDIS_URL=redis://redis:6379
- SESSION_KEY=${SESSION_KEY}
depends_on:
- db
- redis
networks:
- gojang-network
app3:
build: .
restart: unless-stopped
environment:
- PORT=8080
- DEBUG=false
- DATABASE_URL=postgres://gojang:password@db:5432/gojang?sslmode=disable
- REDIS_URL=redis://redis:6379
- SESSION_KEY=${SESSION_KEY}
depends_on:
- db
- redis
networks:
- gojang-network
nginx:
image: nginx:alpine
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
depends_on:
- app1
- app2
- app3
networks:
- gojang-network
db:
image: postgres:15-alpine
restart: unless-stopped
environment:
- POSTGRES_DB=gojang
- POSTGRES_USER=gojang
- POSTGRES_PASSWORD=password
volumes:
- postgres-data:/var/lib/postgresql/data
networks:
- gojang-network
redis:
image: redis:7-alpine
restart: unless-stopped
volumes:
- redis-data:/data
networks:
- gojang-network
networks:
gojang-network:
driver: bridge
volumes:
postgres-data:
redis-data:
Start all services:
docker-compose up -d --scale app=3
Health Checksβ
Add a health check endpoint to your application for load balancer monitoring.
Create app/health/health.handler.go:
package health
import (
"net/http"
)
// HealthCheck returns 200 OK if the application is healthy
func HealthCheck(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
w.Write([]byte("OK"))
}
Add to router:
// Add this import:
// "github.com/gojangframework/gojang/app/health"
r.Get("/health", health.HealthCheck)
Test:
curl http://localhost:8080/health
# Should return: OK
Testing Your Distributed Setupβ
1. Verify All Instances Are Runningβ
# Check all instances
curl http://localhost:8080/health
curl http://localhost:8081/health
curl http://localhost:8082/health
2. Test Session Persistenceβ
Login on one instance:
curl -c cookies.txt -X POST http://localhost:8080/login \
-d "email=user@example.com" \
-d "password=password123"
Verify session works on another instance:
curl -b cookies.txt http://localhost:8081/dashboard
# Should still be logged in
3. Test Load Distributionβ
# Send multiple requests through load balancer
for i in {1..10}; do
curl http://localhost/
sleep 1
done
# Check nginx access logs to see distribution
sudo tail -f /var/log/nginx/access.log
4. Test Failoverβ
Stop one instance:
sudo systemctl stop gojang@1
Verify requests still work:
curl http://localhost/
# Should work - load balancer routes to other instances
Deployment Strategiesβ
Zero-Downtime Deploymentβ
Deploy updates without taking the entire site offline:
1. Update one instance at a time:
# Stop first instance
sudo systemctl stop gojang@0
# Update application binary
sudo cp new-gojang /opt/gojang/gojang
# Start first instance
sudo systemctl start gojang@0
# Verify it works
curl http://localhost:8080/health
# Repeat for other instances
2. Using Docker:
# Build new image
docker-compose build
# Update one service at a time
docker-compose up -d --no-deps --build app1
sleep 10
docker-compose up -d --no-deps --build app2
sleep 10
docker-compose up -d --no-deps --build app3
Blue-Green Deploymentβ
Run two complete environments and switch traffic:
Production (Blue) β Users
Staging (Green) β Testing
After validation:
Production (Green) β Users
Staging (Blue) β Idle
Monitoring & Troubleshootingβ
Monitor Instance Healthβ
Check instance status:
# systemd
sudo systemctl status gojang@0
sudo systemctl status gojang@1
sudo systemctl status gojang@2
# Docker
docker-compose ps
Monitor logs:
# All instances
sudo journalctl -u 'gojang@*' -f
# Specific instance
sudo journalctl -u gojang@0 -f
Monitor Load Balancerβ
Nginx statistics:
# Check which backends are up
sudo tail -f /var/log/nginx/access.log
# Backend status (requires nginx-plus or stub_status module)
curl http://localhost/nginx_status
Monitor Redisβ
# Connect to Redis
redis-cli
# Check session count
DBSIZE
# Monitor commands
MONITOR
Common Issuesβ
Issue: Sessions lost after deployment
- Ensure Redis is running and accessible
- Verify
REDIS_URLis set correctly on all instances - Check Redis connection in logs
Issue: Uneven load distribution
- Check load balancing algorithm (try
least_conninstead ofround_robin) - Verify all instances are responding to health checks
- Check server weights in load balancer config
Issue: Database connection errors
- Increase PostgreSQL
max_connections - Configure connection pooling properly
- Check network connectivity from all instances
Best Practicesβ
β DOβ
- Use PostgreSQL instead of SQLite for distributed deployments
- Use Redis for session storage (or database-backed sessions)
- Implement health checks for load balancer monitoring
- Use connection pooling for database connections
- Set proper timeouts on load balancer and application
- Monitor all instances individually and collectively
- Use secrets management for sensitive configuration
- Test failover scenarios before going to production
- Implement graceful shutdown to finish ongoing requests
- Use same SECRET_KEY across all instances
β DON'Tβ
- Don't use in-memory sessions for distributed setups
- Don't use SQLite - it doesn't support concurrent access from multiple servers
- Don't store files locally - use S3 or shared storage
- Don't skip health checks - load balancer needs them
- Don't use different SECRET_KEY values - sessions won't work
- Don't deploy to all instances simultaneously - use rolling deployment
- Don't ignore database connection limits - scale accordingly
Scaling Checklistβ
Before going distributed:
- Switch to PostgreSQL from SQLite
- Implement Redis session storage (or database-backed)
- Add health check endpoint (
/health) - Configure load balancer (Nginx, HAProxy, or cloud LB)
- Use environment variables for all configuration
- Set same SESSION_KEY on all instances
- Test session persistence across instances
- Test database connectivity from all instances
- Configure monitoring and alerting
- Plan deployment strategy (rolling, blue-green)
- Document runbooks for common operations
- Test failover scenarios
Example: Complete AWS Deploymentβ
Architecture:
- Application Load Balancer (ALB)
- EC2 Auto Scaling Group (3 instances)
- RDS PostgreSQL (primary + read replica)
- ElastiCache Redis (for sessions)
Benefits:
- Auto-scaling based on CPU/memory
- Multi-AZ deployment for high availability
- Managed database and cache
- SSL termination at load balancer
- CloudWatch monitoring included
Estimated cost: ~$150-300/month for small setup
Conclusionβ
Gojang is fully capable of distributed deployment with the following considerations:
β
Use PostgreSQL - Required for shared state
β
Use Redis - Required for shared sessions
β
Use Load Balancer - Required for traffic distribution
β
Stateless design - Makes scaling easy
With proper configuration, Gojang applications can scale horizontally to handle millions of requests with high availability and zero-downtime deployments.
Next Steps:
- Review the Deployment Guide for basic deployment
- Set up PostgreSQL and Redis
- Configure persistent session storage
- Set up load balancer
- Test distributed deployment in staging
- Monitor and optimize
Happy scaling! π