This document provides comprehensive guidance for deploying the Stellar Micro Donation API to production environments.
The production deployment uses Docker Compose with secure configurations that:
- Handle secrets securely using Docker Secrets
- Apply resource limits to prevent container exhaustion
- Implement restart policies for high availability
- Configure health checks and monitoring
- Use proper logging configurations
Create the secrets directory and populate it with your production credentials:
mkdir -p secretsYou need the following secrets:
# Encryption key (64-character hex)
openssl rand -hex 32 > secrets/encryption_key.txt
# Generate API keys for your production environment
echo "prod_key_1,prod_key_2,prod_key_3" > secrets/api_keys.txt
# Database encryption key (64-character hex)
openssl rand -hex 32 > secrets/db_encryption_key.txt
# Stellar keypair (from your Stellar account)
echo "SXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" > secrets/stellar_secret_key.txt
echo "GXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" > secrets/stellar_public_key.txt
# JWT secret for token signing (64-character hex)
openssl rand -hex 32 > secrets/jwt_secret.txtIMPORTANT:
- Never commit secret files to version control
- Rotate secrets regularly using the API key rotation procedure
- Use a secure secret management system (e.g., HashiCorp Vault, AWS Secrets Manager)
Note: The commands below use plain
docker-compose up. In this mode thedeploy.resourcesblock indocker-compose.prod.ymlis silently ignored by Docker Compose — resource limits and reservations are not enforced. For guaranteed resource enforcement, either use Docker Swarm (docker stack deploy) or addmem_limit/cpuskeys directly to the service. See the Resource Configuration section for details.
# Start the production deployment
docker-compose -f docker-compose.prod.yml up -d
# Check status
docker-compose -f docker-compose.prod.yml ps
# View logs
docker-compose -f docker-compose.prod.yml logs -f api# Check health endpoint
curl http://localhost:3000/health
# Run smoke tests against production
npm run test:smokeThe production deployment uses file-based secret injection. Configure these environment variables:
| Variable | Purpose | Example |
|---|---|---|
ENCRYPTION_KEY_FILE |
Path to encryption key secret | /run/secrets/encryption_key |
API_KEYS_FILE |
Path to API keys secret | /run/secrets/api_keys |
DB_ENCRYPTION_KEY_FILE |
Path to database encryption key | /run/secrets/db_encryption_key |
STELLAR_SECRET_KEY_FILE |
Path to Stellar secret key | /run/secrets/stellar_secret_key |
STELLAR_PUBLIC_KEY_FILE |
Path to Stellar public key | /run/secrets/stellar_public_key |
JWT_SECRET_FILE |
Path to JWT signing secret | /run/secrets/jwt_secret |
NODE_ENV |
Environment mode | production |
DB_PATH |
SQLite database location | /app/data/donations.db |
LOG_LEVEL |
Logging level | info, debug, error |
SECURE_SECRETS |
Enable secure secret file handling | true |
SHUTDOWN_TIMEOUT_MS |
Graceful-shutdown budget in milliseconds | 30000 |
The production docker-compose applies the following resource limits:
⚠️ Swarm-only caveat — read before deployingThe
deploy.resourcesblock indocker-compose.prod.ymlis a Docker Swarm / Docker Stack construct. When you deploy with plaindocker-compose up, Docker Compose silently ignores the entiredeploy.resourcesblock — neither the limits nor the reservations below are applied to the container. Resource guarantees in that scenario are entirely up to the host OS and Docker daemon defaults.
Deployment command deploy.resourceshonoured?docker-compose -f docker-compose.prod.yml up -d❌ ignored docker stack deploy -c docker-compose.prod.yml stellar✅ enforced Plain
docker-compose upusers: add top-levelmem_limit/cpuskeys directly under the service to get container-level resource enforcement:services: api: mem_limit: 1g cpus: '2.0' # ... rest of the service definition
- Limit: 2 CPUs max
- Reservation/request: 1 CPU (Swarm scheduler hint only)
- Limit: 1 GB max
- Reservation/request: 512 MB (Swarm scheduler hint only)
Adjust these values based on your expected traffic and infrastructure.
An initial local process-level resource observation using the existing load suite with 10 concurrent users and 50 iterations per scenario reached approximately one CPU core and 151 MiB maximum resident memory. The recommended 1 CPU / 512 MiB request and 2 CPU / 1 GiB limit provide scheduling capacity and burst headroom above that observation. Because the donation-creation scenario had a separate functional baseline failure during this run, operators should repeat the profile with healthy production-like traffic before reducing these values.
# Swarm-only — ignored by plain docker-compose up (see caveat above)
deploy:
resources:
limits:
cpus: '2.0' # Adjust based on traffic
memory: 1G # Adjust based on data size
reservations:
cpus: '1.0' # Swarm scheduler hint
memory: 512M # Swarm scheduler hintThe application uses SHUTDOWN_TIMEOUT_MS=30000, giving it 30 seconds to drain in-flight requests after SIGTERM. The production Compose service sets stop_grace_period: 35s, providing a five-second buffer before Docker sends SIGKILL.
The container grace period must be greater than or equal to the application shutdown budget.
The production deployment uses restart: always, which means:
- Container automatically restarts if it crashes
- Container restarts after daemon restart
- Respects a restart delay for failing services
For Swarm/Kubernetes deployments, adjust the restart policy in your orchestration configuration.
Health checks run every 30 seconds:
- Endpoint:
GET /health - Timeout: 5 seconds
- Retries: 3 consecutive failures mark unhealthy
- Startup Grace: 15 seconds before first check
The health check endpoint verifies:
- Database connectivity
- API responsiveness
- Essential service availability
Logs are configured with JSON driver:
- Max size: 100 MB per log file
- Max files: 10 rotated logs
- Labels: Automatically tagged with service and environment
Logs are mounted to the logs volume for external log aggregation. Configure your logging infrastructure to consume logs from:
/app/logs/
- Persists SQLite database across restarts
- Location:
/app/data/donations.db - Survives container termination
- Collects application logs
- Location:
/app/logs/ - Should be regularly backed up or streamed to log aggregation service
Best Practices:
- Use Docker Swarm secrets or Kubernetes secrets in orchestrated environments
- Consider external secret management (Vault, AWS Secrets Manager, etc.)
- Implement secret rotation regularly
- Never expose secrets in logs
File-based Secrets:
- Secrets are mounted read-only
- Only accessible to the container process
- Permissions:
0440(user-readable, group-readable)
- Bind API to
localhostinternally, expose via reverse proxy/load balancer - Use TLS/SSL for all external connections
- Implement rate limiting on public endpoints
- Use API key authentication for all requests
- Keep database file on encrypted volumes
- Regular automated backups with verification
- Use database-level encryption where possible
- Monitor for unauthorized access
- Use minimal base image (Node.js Alpine)
- Run as non-root user where possible
- Regularly update base image and dependencies
- Scan images for vulnerabilities
Regular automated backups should:
- Back up the SQLite database file (
/app/data/donations.db) - Back up configuration and secrets (in secure location)
- Retain backups for minimum 30 days
- Test restore procedures regularly
# Manual backup
docker-compose -f docker-compose.prod.yml exec api tar czf - /app/data > backup-$(date +%Y%m%d).tar.gz
# Restore from backup
docker-compose -f docker-compose.prod.yml down
tar xzf backup-20240724.tar.gz
docker-compose -f docker-compose.prod.yml up -dUse cron or Docker container for regular backups:
# Add to crontab for daily backups at 2 AM
0 2 * * * cd /path/to/api && docker-compose -f docker-compose.prod.yml exec -T api tar czf - /app/data | gzip > /backups/stellar-api-$(date +\%Y\%m\%d).tar.gzMonitor these key metrics:
- Container CPU: Should stay below 60% of limit
- Container Memory: Should stay below 80% of limit
- Database Size: Track growth over time
- API Response Time: P99 latency
- Error Rate: 4xx/5xx response rate
Set up alerts for:
- Container restart rate > 5 per hour
- Memory usage > 90%
- Health check failures
- API response time > 1s
- Disk usage > 80%
Forward logs to your centralized logging system:
# Example: Using Filebeat to stream logs
# Configure Filebeat to watch /app/logs/ volume- Monitor health check status
- Review error logs
- Check disk usage
- Review performance metrics
- Test backup restoration
- Audit access logs
- Update dependencies
- Rotate API keys
- Review security logs
- Verify disaster recovery readiness
# Check logs
docker-compose -f docker-compose.prod.yml logs api
# Verify secrets exist
ls -la secrets/
# Check environment variables
docker-compose -f docker-compose.prod.yml config# Manual health check
docker-compose -f docker-compose.prod.yml exec api wget -qO- http://localhost:3000/health
# Check database
docker-compose -f docker-compose.prod.yml exec api sqlite3 /app/data/donations.db "SELECT COUNT(*) FROM sqlite_master WHERE type='table';"# Check memory usage
docker-compose -f docker-compose.prod.yml stats api
# If needed, increase memory limit in docker-compose.prod.yml# Check volume sizes
docker volume ls
# Inspect logs volume
du -sh /var/lib/docker/volumes/*/\_data
# Clean old logs
docker-compose -f docker-compose.prod.yml exec api find /app/logs -mtime +30 -deleteFor production Swarm deployments:
# Create secrets in Swarm
docker secret create encryption_key - < secrets/encryption_key.txt
docker secret create api_keys - < secrets/api_keys.txt
# Deploy stack
docker stack deploy -c docker-compose.prod.yml stellarFor Kubernetes deployments:
# Create secrets
kubectl create secret generic stellar-secrets \
--from-file=encryption_key=secrets/encryption_key.txt \
--from-file=api_keys=secrets/api_keys.txt
# Mount in pod spec
volumeMounts:
- name: secrets
mountPath: /run/secrets
readOnly: trueFor simpler setups, use .env.prod:
# Create .env.prod (never commit this file!)
ENCRYPTION_KEY=$(cat secrets/encryption_key.txt)
API_KEYS=$(cat secrets/api_keys.txt)
# ... other variables
# Deploy with env file
docker-compose -f docker-compose.prod.yml --env-file .env.prod up -d// In production startup:
db.run("PRAGMA journal_mode = WAL"); // Write-Ahead Logging
db.run("PRAGMA synchronous = NORMAL"); // Reduce sync overhead
db.run("PRAGMA cache_size = 10000"); // Increase cache
db.run("PRAGMA temp_store = MEMORY"); // Use memory for temp tablesImplement connection pooling for better resource utilization:
const pool = createPool({
max: 5,
min: 2,
acquire: 30000,
idle: 10000
});- API Documentation: See
docs/API_EXAMPLES.md - Architecture: See
docs/ARCHITECTURE.md - Configuration: See
docs/CONFIGURATION.md - Security: See
SECURITY.md - Backup Guide: See
docs/BACKUP_RESTORE.md
Before going live:
- Secrets directory created with all required files
- Resource limits configured for your infrastructure
- Health check endpoint verified
- Logging configuration matches your log aggregation
- Backup/restore procedure tested
- SSL/TLS certificates provisioned
- Database migrations run successfully
- Smoke tests pass against production environment
- Monitoring and alerting configured
- Incident response plan documented
- Team trained on deployment procedures
- Disaster recovery procedures tested