Docker Production Deployment Guide
This guide covers deploying VUP projects to production using Docker and Docker Compose. This deployment solution is suitable for full-stack applications (API + Frontend + Database) and is optimized for applications with DAU ≤ 5000.
What is Docker Deployment
Docker deployment provides:
- Containerization - Isolated environment for each service
- Easy Updates - Update by replacing files and restarting, no need to rebuild images
- Resource Management - CPU and memory limits to prevent resource exhaustion
- Service Orchestration - Manage multiple services (database, API, frontend, nginx) with a single command
- Volume Mapping - Direct file mapping for easy updates without rebuilding
Prerequisites
Before starting, ensure you have:
- Docker >= 20.10
- Docker Compose >= 2.0
- Server with at least 2GB RAM and 2 CPU cores
- Domain name (optional, for HTTPS)
Recommended Deployment Directory
For production deployments, it's recommended to use a standard Linux directory structure:
Recommended Path: /opt/vup or /opt/vup-cli
This follows Linux FHS (Filesystem Hierarchy Standard) conventions where /opt is used for optional application software packages.
Setup Deployment Directory
# Create deployment directory
sudo mkdir -p /opt/vup
sudo chown $USER:$USER /opt/vup
# Copy deploy directory to recommended location
cp -r /path/to/project-vue/deploy/* /opt/vup/
# Or clone/copy the entire deploy directory
cd /opt/vupDirectory Structure
After setup, your deployment directory should look like:
/opt/vup/
├── docker-compose.yml
├── .env
├── builds/
├── nginx/
├── scripts/
├── data/
├── logs/
└── backups/Note: All paths in this guide assume deployment in
/opt/vup. If you use a different directory, adjust the paths accordingly.
Quick Start
1. Setup Deployment Directory
# Create and navigate to deployment directory
sudo mkdir -p /opt/vup
sudo chown $USER:$USER /opt/vup
cd /opt/vup
# Copy deploy directory from your project
cp -r /path/to/project-vue/deploy/* /opt/vup/2. Prepare Environment Variables
Create a .env file in the deployment directory:
cd /opt/vup
cp .env.example .envEdit .env with your configuration:
# Database Configuration
MYSQL_DATABASE=vup_db
MYSQL_USER=vup_user
MYSQL_PASSWORD=your_secure_password
MYSQL_ROOT_PASSWORD=your_root_password
MYSQL_PORT=3306
# API Configuration
CORS_ORIGIN=https://yourdomain.com
JWT_SECRET=your_jwt_secret_key
JWT_EXPIRES_IN=7d
JWT_REFRESH_EXPIRES_IN=30d
UPLOAD_MAX_FILE_SIZE=1073741824
UPLOAD_BASE_URL=https://yourdomain.com/uploads
# Frontend Configuration
API_BASE_URL=https://yourdomain.com/api
APP_BASE_URL=https://yourdomain.com
# Port Configuration
HTTP_PORT=80
HTTPS_PORT=4433. Build Applications
Build your API and Admin applications:
# Build API
cd apps/nest-template
pnpm build
# Build Admin (Nuxt)
cd apps/nuxt-template
pnpm build4. Copy Build Outputs
Copy build outputs to the deployment directory:
# Copy API build output
cp -r /path/to/project-vue/apps/nest-template/.output /opt/vup/builds/api
# Copy Admin build output
cp -r /path/to/project-vue/apps/nuxt-template/.output /opt/vup/builds/admin5. Start Services
cd /opt/vup
# Start all services
docker compose up -d
# View logs
docker compose logs -f
# Check service status
docker compose ps6. Run Database Migrations
# Run migrations
docker compose run --rm migrationService Overview
MariaDB Database
- Container:
vup-mariadb - Image:
mariadb:11.3 - Port:
3306(configurable viaMYSQL_PORT) - Data Volume:
./data/mariadb - Backup Volume:
./backups/mariadb - Resource Limits: 0.8 CPU, 1GB RAM
API Service
- Container:
vup-api - Image:
node:20-alpine - Port:
9310(internal) - Build Directory:
./builds/api - Resource Limits: 0.6 CPU, 512MB RAM
Admin Frontend Service
- Container:
vup-admin - Image:
node:20-alpine - Port:
3000(internal, Nuxt SSR) - Build Directory:
./builds/admin - Resource Limits: 0.2 CPU, 256MB RAM
Nginx Reverse Proxy
- Container:
vup-nginx - Image:
nginx:alpine - Ports:
80(HTTP),443(HTTPS) - Resource Limits: 0.2 CPU, 128MB RAM
Migration Service
- Container:
migration(one-time use) - Image:
node:20-alpine - Profile:
migration(doesn't start automatically)
Configuration Details
Docker Compose Configuration
The docker-compose.yml file uses volume mapping for easy updates:
- Advantage: Update by replacing files and restarting, no need to rebuild images
- Suitable for: DAU ≤ 5000
- Update Process: Replace build files → Restart service
Nginx Configuration
Nginx acts as a reverse proxy with the following routes:
/api/→http://api:9310/api/- API service/admin/→http://admin:3000/- Admin frontend service/uploads/→ Static file service (from volume mount)/→ Redirects to/admin//health→ Health check endpoint
Features
- Reverse Proxy - API and Admin services accessed through reverse proxy
- Static File Service - Uploaded files served directly by Nginx
- File Upload Support - Maximum upload size 1GB
- Gzip Compression - Enabled for better performance
- Cache Control - Upload files cached for 30 days
- CORS Support - Cross-origin access for uploaded files
Testing Nginx Configuration
# Test configuration syntax
docker compose exec nginx nginx -t
# Reload configuration (without downtime)
docker compose exec nginx nginx -s reload
# View logs
docker compose exec nginx tail -f /var/log/nginx/access.log
docker compose exec nginx tail -f /var/log/nginx/error.logHTTPS Configuration (Optional)
To enable HTTPS:
Place SSL certificate files in
nginx/ssl/:cert.pem- SSL certificatekey.pem- SSL private key
Uncomment the HTTPS server block in
nginx/conf.d/default.confUpdate
server_nameto your actual domainRestart nginx:
docker compose restart nginxNote: For production, consider using Let's Encrypt for free SSL certificates.
Deployment Scripts
The deploy/scripts/ directory contains utility scripts for maintenance:
backup.sh - Database Backup
Backs up MariaDB database and uploaded files, automatically cleans old backups (retains 7 days).
./scripts/backup.shCron Configuration (Daily at 2:00 AM):
# Assuming deployment in /opt/vup
0 2 * * * /opt/vup/scripts/backup.sh >> /opt/vup/logs/backup.log 2>&1cleanup.sh - Cleanup Script
Cleans old logs (retains 30 days), old backups (retains 7 days), and unused Docker resources. Checks disk usage.
./scripts/cleanup.shCron Configuration (Daily at 3:00 AM):
# Assuming deployment in /opt/vup
0 3 * * * /opt/vup/scripts/cleanup.sh >> /opt/vup/logs/cleanup.log 2>&1monitor.sh - Monitoring Script
Displays service status, resource usage, disk and memory usage, health checks, and alerts.
./scripts/monitor.shCron Configuration (Hourly):
# Assuming deployment in /opt/vup
0 * * * * /opt/vup/scripts/monitor.sh >> /opt/vup/logs/monitor.log 2>&1restore.sh - Database Restore
Restores database from backup file. Automatically creates a temporary backup before restoration.
# List backups
ls -lh backups/mariadb/
# Restore specific backup
./scripts/restore.sh backups/mariadb/backup_20240101_020000.sql.gzWarning: Restore operation will overwrite the current database. A temporary backup is created automatically before restoration.
Setting Up Cron Jobs
Create a setup script (/opt/vup/scripts/setup-cron.sh):
#!/bin/bash
# Assuming deployment in /opt/vup
DEPLOY_DIR="/opt/vup"
# Backup task (Daily at 2:00 AM)
echo "0 2 * * * ${DEPLOY_DIR}/scripts/backup.sh >> ${DEPLOY_DIR}/logs/backup.log 2>&1" | crontab -
# Cleanup task (Daily at 3:00 AM)
echo "0 3 * * * ${DEPLOY_DIR}/scripts/cleanup.sh >> ${DEPLOY_DIR}/logs/cleanup.log 2>&1" | crontab -
# Monitor task (Hourly)
echo "0 * * * * ${DEPLOY_DIR}/scripts/monitor.sh >> ${DEPLOY_DIR}/logs/monitor.log 2>&1" | crontab -
echo "Cron jobs configured"Make it executable:
chmod +x /opt/vup/scripts/setup-cron.sh
/opt/vup/scripts/setup-cron.shEnsure scripts have execute permissions:
chmod +x scripts/*.shDaily Operations
View Logs
# View all service logs
docker compose logs -f
# View specific service logs
docker compose logs -f api
docker compose logs -f admin
docker compose logs -f nginx
docker compose logs -f mariadb
# View local log files
tail -f logs/api/app.log
tail -f logs/admin/app.log
tail -f nginx/logs/access.log
tail -f nginx/logs/error.logRestart Services
# Restart all services
docker compose restart
# Restart specific service
docker compose restart api
docker compose restart admin
docker compose restart nginx
# Stop all services
docker compose stop
# Start all services
docker compose startHealth Checks
# Check service status
docker compose ps
# Check API health
curl http://localhost/api/health
# Check Admin health
curl http://localhost/admin/
# Check database connection
docker compose exec mariadb mysqladmin ping -h localhostUpdate Application
- Build new version:
# Build API
cd apps/nest-template
pnpm build
# Build Admin
cd apps/nuxt-template
pnpm build- Copy build outputs:
# Backup current version (optional)
cp -r /opt/vup/builds/api /opt/vup/builds/api.backup
cp -r /opt/vup/builds/admin /opt/vup/builds/admin.backup
# Copy new builds
cp -r /path/to/project-vue/apps/nest-template/.output /opt/vup/builds/api
cp -r /path/to/project-vue/apps/nuxt-template/.output /opt/vup/builds/admin- Restart services:
cd /opt/vup
docker compose restart api adminTroubleshooting
502 Bad Gateway
- Check if API or Admin services are running:bash
docker compose ps - Check service logs:bash
docker compose logs api docker compose logs admin - Verify service names in nginx config (
api,admin) - Verify ports (API: 9310, Admin: 3000)
403 Forbidden
- Check file permissions:bash
ls -la /opt/vup/data/uploads - Verify
uploadsdirectory exists - Check nginx configuration
413 Request Entity Too Large
- Check
client_max_body_sizein nginx config - Verify API service
UPLOAD_MAX_FILE_SIZEenvironment variable
Database Connection Issues
- Check database container status:bash
docker compose ps mariadb - Check database logs:bash
docker compose logs mariadb - Verify environment variables:bash
docker compose exec api env | grep MYSQL
Service Won't Start
- Check resource limits (CPU/memory)
- Check disk space:bash
df -h - Check Docker logs:bash
docker compose logs
Script Execution Issues
- Check script permissions:bash
ls -l scripts/ chmod +x scripts/*.sh - Test script manually:bash
./scripts/backup.sh
Best Practices
Resource Management
- Monitor resource usage regularly:bash
docker stats - Adjust resource limits in
docker-compose.ymlbased on actual usage - Set appropriate CPU and memory limits to prevent resource exhaustion
Backup Strategy
- Daily Backups: Configure cron job for daily database backups
- Backup Retention: Keep backups for at least 7 days
- Test Restores: Periodically test backup restoration
- Offsite Backups: Store backups in a separate location
Monitoring
- Set up hourly monitoring with
monitor.sh - Monitor disk usage to prevent full disk
- Set up alerts for service failures
- Monitor resource usage trends
Security
- Use strong passwords for database
- Keep Docker and images updated
- Use HTTPS in production
- Restrict database port access (use firewall)
- Regularly review and update environment variables
Performance Optimization
- Enable Gzip compression in Nginx
- Configure appropriate cache headers
- Monitor and optimize database queries
- Use CDN for static assets (if applicable)
Directory Structure
The recommended deployment directory structure (/opt/vup):
/opt/vup/
├── docker-compose.yml # Docker Compose configuration
├── .env # Environment variables (not in git)
├── builds/ # Build outputs
│ ├── api/ # API build output
│ └── admin/ # Admin build output
├── nginx/ # Nginx configuration
│ ├── nginx.conf # Main nginx config
│ ├── conf.d/ # Site configurations
│ │ └── default.conf # Default site config
│ ├── ssl/ # SSL certificates (optional)
│ └── logs/ # Nginx logs
├── scripts/ # Deployment scripts
│ ├── backup.sh # Database backup
│ ├── cleanup.sh # Cleanup script
│ ├── monitor.sh # Monitoring script
│ ├── restore.sh # Database restore
│ └── setup-cron.sh # Cron setup script
├── data/ # Data volumes
│ ├── mariadb/ # Database data
│ └── uploads/ # Uploaded files
├── logs/ # Application logs
│ ├── api/ # API logs
│ ├── admin/ # Admin logs
│ ├── backup.log # Backup script logs
│ ├── cleanup.log # Cleanup script logs
│ └── monitor.log # Monitor script logs
└── backups/ # Backup files
├── mariadb/ # Database backups
└── api/ # API backupsNote: If you deploy to a different directory, adjust all paths in scripts and cron jobs accordingly.
Related Resources
- Docker Official Documentation
- Docker Compose Documentation
- Nginx Official Documentation
- MariaDB Official Documentation
- Vercel Deployment Guide - Alternative deployment for frontend applications