6.5 KiB
Production Deployment Guide
This document outlines the changes made to prepare JMP Server for production deployment on a server.
Key Changes Summary
1. Removed Direct Port Exposures
- Removed direct port mappings for:
homepage:3001(was exposing internal port)bookstack:6875(was exposing internal port)actual_server:5006(was exposing internal port)
- All services now route exclusively through Traefik reverse proxy
- Only SSH port
2222(Gitea) exposed directly for Git operations
2. Resource Limits & Reservations
All containers now have CPU and memory limits:
- Traefik: 2 CPU / 512M limit, 0.5 CPU / 256M reserved
- Gitea: 2 CPU / 1G limit, 1 CPU / 512M reserved
- Gitea Runner: 4 CPU / 2G limit (for CI/CD workloads)
- Bookstack: 2 CPU / 512M limit, 1 CPU / 256M reserved
- Databases: 2 CPU / 1G limit, 1 CPU / 512M reserved
- OpenCloud: 4 CPU / 4G limit, 2 CPU / 2G reserved
Adjust these based on your server's available resources.
3. Health Checks
All services now include health checks:
- Databases:
pg_isreadyormysqladmin pingchecks - Web services: HTTP endpoint checks
- Service dependencies now use
condition: service_healthyinstead of justdepends_on
This ensures proper startup ordering and service reliability.
4. Log Rotation
Configured JSON file logging with rotation:
- Max file size: 50-100MB per log file
- Max files retained: 3-5 files
- Prevents logs from filling disk
5. Traefik Authentication
- Before: Had invalid placeholder
$$apr1$$... - After: Uses environment variable
${TRAEFIK_BASICAUTH_USERS}
Generate credentials:
openssl passwd -apr1 admin
# Output: $apr1$r61.qW2G$Lc3uT7c5p...
Set in .env:
TRAEFIK_BASICAUTH_USERS=admin:$apr1$r61.qW2G$Lc3uT7c5p...
6. Database Restart Policies
- Databases: Changed to
restart: always(critical services) - Applications: Keep
restart: unless-stopped(graceful restart handling)
7. OpenCloud Network Configuration
Fixed network issues for OpenCloud:
- Explicitly declared
proxy_netas external in opencloud-compose - Added network driver specification
- Prevents network conflicts when deploying separately
8. Container Dependencies
Updated all service dependencies to use health conditions:
depends_on:
gitea-db:
condition: service_healthy
This ensures services wait for databases to be healthy before starting.
9. Actual Server Configuration
- Removed direct port exposure
5006:5006 - Fixed Traefik loadbalancer port (was
5232, now5006) - Added to
proxy_netnetwork - Added health check
Environment Variables
Required Changes for Production
Copy .env.production.example to .env and update:
- DOMAIN: Your actual domain name
- TRAEFIK_BASICAUTH_USERS: Generated htpasswd hash
- All database passwords: Strong, random passwords
- API keys & tokens: Generate new secure values
- OpenCloud settings: Domain and admin password
Sensitive Variables
These should never be committed to git:
- All
*PASSWORD*variables *TOKEN*variables*SECRET*variablesAPP_KEYvalues
Keep .env in .gitignore and use .env.production.example as template.
Pre-Deployment Checklist
- Copy
.env.production.exampleto.env - Generate Traefik basicauth credentials with
openssl passwd -apr1 - Set all database passwords (random, 16+ characters)
- Generate API tokens (Gitea runner, Vaultwarden admin, etc.)
- Configure domain in
.env(DOMAIN variable) - Review resource limits for your server capacity
- Ensure backup storage path is accessible (
./backups/) - Verify SSL/TLS certificates will work (Let's Encrypt requirements)
Deployment Steps
-
Prepare server:
mkdir -p ~/jmp-server cd ~/jmp-server # Copy all files here -
Configure environment:
cp .env.production.example .env # Edit .env with your values -
Start services:
./start.sh # or: docker compose up -d -
Verify health:
docker compose ps # Check all services are "Up" and health checks pass docker compose logs -f traefik # Watch for ACME certificate provisioning -
Test routing:
curl https://traefik.${DOMAIN}/ curl https://gitea.${DOMAIN}/ curl https://bookstack.${DOMAIN}/
Monitoring & Maintenance
View Service Status
docker compose ps
docker compose stats
Check Health
docker compose exec gitea-db pg_isready -h localhost -U ${POSTGRES_USER_GITEA}
docker compose exec bookstack-db mysqladmin ping -h localhost -u root -p${MYSQL_PASSWORD_BOOKSTACK}
View Logs
# Traefik routing
docker compose logs -f traefik
# Gitea
docker compose logs -f gitea
# Backup status
docker compose logs -f backup
Backup Management
Backups are automated (see backup.env). Stored in ./backups/.
Security Considerations
- Firewall: Only expose ports 80, 443, and 2222 (SSH) to internet
- HTTPS: Let's Encrypt certificates auto-renewed by Traefik
- Database access: Internal networks prevent direct DB access
- Secrets: Never store passwords in compose files; use
.envonly - Log retention: Configure log rotation in Docker daemon or use central logging
- Backups: Store backups on separate storage/cloud
Troubleshooting
Service won't start
docker compose logs -f [service_name]
# Check: Missing dependencies, failed health checks, port conflicts
Services can't communicate
docker network ls
docker network inspect proxy_net
# Verify all services are on correct networks
Traefik routing issues
docker compose exec traefik traefik api dashboard
# Check router/service configurations
Memory/CPU pressure
docker compose stats
# Review resource limits in docker-compose.yaml
# Adjust based on actual usage
Resource Recommendations
For a typical small server (4GB RAM, 2 CPU):
- Gitea + Runner: 3G RAM, 2.5 CPU
- OpenCloud: 2G RAM, 2 CPU
- Other services: 1G RAM, 1.5 CPU
- Total: ~6G RAM recommended, 6 CPU recommended
Adjust limits down if server is smaller, test under load for your workload.
Next Steps
- Configure OpenCloud separately if needed:
docker compose -f opencloud-compose/docker-compose.yml up -d - Set up external backup storage for production data protection
- Configure monitoring/alerting (optional external monitoring)
- Document your domain names and admin credentials securely