Production Deployment
This page covers the additional steps required to harden a Strawly deployment for production use.
Checklist
Before going live, work through each item:
- [ ] Change the default admin password (instructions)
- [ ] Generate strong secrets with
./scripts/generate-secrets.sh - [ ] Set
CORS_ORIGINto your production frontend URL - [ ] Enable HTTPS via a reverse proxy
- [ ] Use a managed PostgreSQL database (not a Docker volume)
- [ ] Set up automated database backups
- [ ] Set up Redis for token revocation (required for multi-instance deployments)
- [ ] Configure log aggregation
- [ ] Set up monitoring and alerting
- [ ] Configure firewall rules
- [ ] Set Docker resource limits
- [ ] Set up automated image updates
HTTPS with a reverse proxy
Strawly does not handle TLS termination itself. Put a reverse proxy in front of it.
Example nginx configuration (adapt as needed):
server {
listen 443 ssl;
server_name strawly.example.com;
ssl_certificate /etc/letsencrypt/live/strawly.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/strawly.example.com/privkey.pem;
location / {
proxy_pass http://localhost:3000;
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;
}
}
server {
listen 443 ssl;
server_name api.strawly.example.com;
# ... same SSL config ...
location / {
proxy_pass http://localhost:3001;
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;
}
}
Use Certbot or Caddy to manage certificates automatically.
After adding a reverse proxy, update CORS_ORIGIN in .env to your production frontend URL and regenerate:
npm run generate-compose
docker compose -f docker-compose.generated.yml up -d
Managed PostgreSQL
For production, use a managed database service (AWS RDS, Azure Database for PostgreSQL, etc.) rather than the Docker volume, which is not backed up by default.
-
Provision a managed PostgreSQL 14+ database.
-
Update
.env:bash DATABASE_URL=postgresql://strawly:password@your-db-host:5432/strawly -
Remove the
postgresservice from the generated compose, or setPOSTGRES_ENABLED=falseif supported in your deployment version. -
Run migrations against the managed database on first deploy:
bash docker compose -f docker-compose.generated.yml run --rm migrations
Database backups
If using the Docker volume (not recommended for production), schedule regular dumps:
# Example: daily pg_dump to a local file
docker exec strawly-postgres pg_dump -U strawly strawly > backup-$(date +%Y%m%d).sql
For managed databases, use the provider's built-in backup and point-in-time recovery features.
Redis for token revocation
The backend uses an in-memory store for JWT token revocation by default. This works for single-instance deployments, but does not survive restarts or share state across multiple backend instances.
For production or multi-instance deployments, configure Redis:
-
Add a Redis service to your deployment or use a managed Redis instance.
-
Set
REDIS_URLin.env:bash REDIS_URL=redis://localhost:6379 -
Regenerate and redeploy.
Docker resource limits
Add resource limits to prevent any one service from consuming all available memory or CPU:
# In docker-compose.generated.yml (or a local override)
services:
backend:
deploy:
resources:
limits:
cpus: '2.0'
memory: 4G
reservations:
cpus: '1.0'
memory: 2G
Horizontal scaling
To run multiple backend instances behind a load balancer:
- Configure Redis for token revocation (required — see above).
- Put a load balancer in front (nginx, HAProxy, or a cloud load balancer).
-
Scale the backend:
bash docker compose -f docker-compose.generated.yml up -d --scale backend=3