Production Deployment Runbook¶
Step-by-step guide for deploying to AWS Lightsail with real-world troubleshooting solutions.
Prerequisites¶
AWS Infrastructure¶
| Service | Purpose |
|---|---|
| Lightsail/EC2 | Application server (Ubuntu 22.04+) |
| RDS PostgreSQL | Database server |
| Route 53/DNS | Domain name configuration |
| S3 (optional) | Backups and file storage |
Server Requirements¶
- Ubuntu 22.04 or later
- Docker and Docker Compose installed
- Git configured with SSH key
- Ports 80, 443 open in firewall/security group
DNS Configuration¶
Point these domains to your server IP:
| Domain | Purpose |
|---|---|
yourdomain.com |
Landing page |
erp.yourdomain.com |
Odoo ERP |
service.yourdomain.com (or pwa.) |
Field Service PWA |
docs.yourdomain.com |
Documentation |
Phase 1: Server Setup¶
1.1 Install Docker¶
# Update system
sudo apt update && sudo apt upgrade -y
# Install Docker
curl -fsSL https://get.docker.com | sh
sudo usermod -aG docker $USER
# Log out and back in for group changes
exit
# SSH back in
# Verify Docker
docker --version
docker compose version
1.2 Setup GitHub SSH Key¶
# Generate SSH key
ssh-keygen -t ed25519 -C "your-email@example.com"
# Start SSH agent
eval "$(ssh-agent -s)"
ssh-add ~/.ssh/id_ed25519
# Copy public key
cat ~/.ssh/id_ed25519.pub
# Add to GitHub → Settings → SSH and GPG keys
# Test connection
ssh -T git@github.com
1.3 Clone Repository¶
Phase 2: Environment Configuration¶
2.1 Create Production .env¶
2.2 Critical .env Settings¶
# Environment
ENVIRONMENT=production
GIT_BRANCH=main
COMPOSE_FILE=docker-compose.yml:docker-compose.prod.yml
# Database (AWS RDS)
DB_HOST=your-db.xxxxx.us-east-1.rds.amazonaws.com
DB_PORT=5432
DB_NAME=production
DB_USER=your_user
DB_PASSWORD=your_secure_password
# Domains
DOMAIN_LANDING=yourdomain.com
DOMAIN_ERP=erp.yourdomain.com
DOMAIN_PWA=service.yourdomain.com
DOMAIN_DOCS=docs.yourdomain.com
# Flask (MUST change for production)
FLASK_ENV=production
PWA_SECRET_KEY=<generate-with-python>
LANDING_SECRET_KEY=<generate-with-python>
# SSL
LETSENCRYPT_EMAIL=admin@yourdomain.com
2.3 Generate Secret Keys¶
2.4 IMPORTANT: Quote Special Characters¶
Values with special characters (&, (), :, ,, etc.) MUST be quoted:
# WRONG - will cause errors
COMPANY_NAME=JDX Blinds & Shutters
COMPANY_PHONE=(512) 910-4020
# CORRECT - quoted values
COMPANY_NAME="JDX Blinds & Shutters"
COMPANY_PHONE="(512) 910-4020"
COMPANY_ADDRESS="123 Main St, Suite 100, Austin, TX 78701"
COMPANY_HOURS="Mon-Fri: 9AM-6PM, Sat: 10AM-4PM"
META_TITLE="Company Name | Custom Window Treatments"
META_DESCRIPTION="Description with special chars & symbols."
2.5 Verify Configuration¶
Phase 3: Database Connection¶
3.1 Test RDS Connection¶
# Test from server
psql -h your-db.xxxxx.rds.amazonaws.com -U your_user -d production -c "SELECT 1;"
3.2 Restore Production Database (if migrating)¶
# Upload backup to server
scp backup.sql.gz ubuntu@server-ip:~/
# Restore
gunzip -c backup.sql.gz | psql -h your-db.rds.amazonaws.com -U your_user -d production
Phase 4: Odoo Data Directory¶
4.1 Create Data Directory¶
4.2 Restore Filestore (if migrating)¶
4.3 Fix Permissions¶
CRITICAL: Odoo runs as uid 101 inside the container.
Phase 5: Odoo Configuration¶
5.1 Production odoo.conf Settings¶
The configs/odoo.conf file contains critical production settings:
[options]
addons_path = /mnt/extra-addons/helpdesk,/mnt/extra-addons/odoo
data_dir = /var/lib/odoo
; Database settings - CRITICAL for production
db_name = production
dbfilter =
; Performance settings
workers = 0
max_cron_threads = 1
limit_memory_hard = 2684354560
limit_memory_soft = 2147483648
limit_time_cpu = 600
limit_time_real = 1200
; Logging
log_level = info
logrotate = True
; Disable demo data
without_demo = all
; Security settings for production
list_db = True
proxy_mode = True
admin_passwd = your_secure_admin_password
5.2 Critical Settings Explained¶
| Setting | Value | Purpose |
|---|---|---|
db_name |
production |
Forces Odoo to use this database |
dbfilter |
(empty) | MUST be empty - setting a value causes redirect issues |
list_db |
True |
Combined with db_name, auto-selects the database |
proxy_mode |
True |
Required when behind nginx - trusts X-Forwarded headers |
5.3 docker-compose.prod.yml Command Override¶
Production uses command-line overrides for SSL:
command: ["--", "--addons-path=/mnt/extra-addons/helpdesk,/mnt/extra-addons/odoo", "--db_sslmode=require"]
Important: Only --db_sslmode=require is set via command line. All other settings come from odoo.conf.
5.4 Common Mistakes¶
| Mistake | Symptom | Fix |
|---|---|---|
Setting dbfilter = ^production$ |
Redirects to database selector | Set dbfilter = (empty) |
Using --list_db on command line |
error: no such option |
Use list_db in config file only |
Using --proxy_mode on command line |
error: no such option |
Use proxy_mode in config file only |
Missing proxy_mode = True |
HTTPS/redirect issues behind nginx | Add to odoo.conf |
5.5 Verify Configuration¶
# Check config inside container
docker compose exec odoo cat /etc/odoo/odoo.conf | grep -E "db_name|dbfilter|list_db|proxy_mode"
# Expected output:
# db_name = production
# dbfilter =
# list_db = True
# proxy_mode = True
# Test login page works
curl -sI https://erp.yourdomain.com/web | grep -i location
# Should NOT show /web/database/selector
Phase 6: SSL Certificate Setup¶
6.1 Create Certificate Directory¶
6.2 Stop Nginx (if running)¶
6.3 Request Let's Encrypt Certificates¶
docker run --rm -p 80:80 \
-v $(pwd)/nginx/ssl:/etc/letsencrypt \
certbot/certbot certonly \
--standalone \
--non-interactive \
--agree-tos \
--email admin@yourdomain.com \
-d yourdomain.com \
-d www.yourdomain.com \
-d erp.yourdomain.com \
-d service.yourdomain.com \
-d docs.yourdomain.com
6.4 Fix Certificate Permissions¶
Let's Encrypt creates restrictive permissions. Fix them:
# Fix archive directory permissions
sudo chmod 755 nginx/ssl/archive
sudo chmod 755 nginx/ssl/archive/yourdomain.com*
sudo chmod 644 nginx/ssl/archive/yourdomain.com*/*.pem
# Fix accounts directory
sudo chmod 755 nginx/ssl/accounts
6.5 Handle Symlinks Issue¶
Let's Encrypt uses symlinks from live/ to archive/. If nginx can't read them, copy actual files:
# Remove symlinks
sudo rm nginx/ssl/live/yourdomain.com/*.pem
# Copy actual certificate files
sudo cp nginx/ssl/archive/yourdomain.com*/fullchain1.pem nginx/ssl/live/yourdomain.com/fullchain.pem
sudo cp nginx/ssl/archive/yourdomain.com*/privkey1.pem nginx/ssl/live/yourdomain.com/privkey.pem
# Fix permissions
sudo chmod 644 nginx/ssl/live/yourdomain.com/*.pem
6.6 Configure Nginx¶
Phase 7: Deploy Application¶
7.1 Build and Start¶
# Full deployment
./production_deploy.sh deploy
# Or step by step:
docker compose build
docker compose up -d
7.2 If Nginx Fails to Start¶
# Check nginx logs
docker compose logs nginx --tail=30
# Common fixes:
# 1. Certificate not found - recreate container
docker compose up -d --force-recreate nginx
# 2. Permission denied - fix ssl permissions (see 5.4)
# 3. Volume mount issue - verify mount works
docker run --rm -v $(pwd)/nginx/ssl:/etc/letsencrypt:ro alpine ls -la /etc/letsencrypt/live/
7.3 Verify All Services¶
# Check container status
docker compose ps
# Test each service
curl -I https://yourdomain.com
curl -I https://erp.yourdomain.com
curl -I https://service.yourdomain.com
curl -I https://docs.yourdomain.com
# Check Odoo health from inside network
docker compose exec nginx curl -s http://odoo:8069/web/health
Phase 8: Post-Deployment¶
8.1 SSL Auto-Renewal Cron¶
Add this line:
0 3 * * 0 cd /home/ubuntu/odoo15-jdx-production && docker run --rm -v $(pwd)/nginx/ssl:/etc/letsencrypt certbot/certbot renew --quiet
8.2 Verify Auto-Start on Reboot¶
All services have restart: always in docker-compose.prod.yml, so they auto-start after reboot.
Troubleshooting¶
Odoo Permission Denied¶
Fix:
SSL Certificate Not Found¶
Fixes:
-
Check mount works:
-
Fix permissions:
-
Copy actual files instead of symlinks (see 5.5)
-
Force recreate container:
Nginx Crash Loop¶
# Check logs
docker compose logs nginx --tail=30
# If SSL issue, temporarily use HTTP-only config
# Edit nginx/conf.d/default.conf to comment out SSL blocks
# Or recreate with new volumes
docker compose stop nginx
docker compose rm -f nginx
docker compose up -d nginx
.env Parse Errors¶
Fix: Quote values with special characters:
Container Won't Pull Image¶
Fix: Build the image first:
Health Check Fails for Odoo¶
In production mode, Odoo port 8069 is not exposed. Test through nginx:
Odoo Redirects to Database Selector¶
Cause: Incorrect dbfilter or list_db settings in odoo.conf.
Fix: Ensure these exact settings in configs/odoo.conf:
Key insight: dbfilter must be empty (not ^production$ or any regex). Setting any value causes Odoo to redirect to the database selector.
Verify:
# Check config
docker compose exec odoo cat /etc/odoo/odoo.conf | grep -E "db_name|dbfilter|list_db"
# Test redirect - should NOT go to /web/database/selector
curl -sI https://erp.yourdomain.com/web | grep -i location
Odoo Command Line Option Errors¶
Cause: These options are config-file only, not command line.
Fix: Set in configs/odoo.conf, not in docker-compose command:
| Option | Where to Set |
|---|---|
list_db |
Config file only |
proxy_mode |
Config file only |
db_sslmode |
Command line OK |
database |
Command line OK |
db-filter |
Command line OK |
Quick Reference¶
Deployment Commands¶
./production_deploy.sh deploy # Full deployment
./production_deploy.sh update # Quick update
./production_deploy.sh start # Start services
./production_deploy.sh stop # Stop services
./production_deploy.sh restart # Restart all
./production_deploy.sh status # Check status
./production_deploy.sh logs odoo # View logs
./production_deploy.sh health # Health check
./production_deploy.sh config # Show config
SSL Commands¶
./production_deploy.sh init-ssl # Full SSL setup
./production_deploy.sh configure-nginx # Generate nginx config
./production_deploy.sh renew-ssl # Renew certificates
Direct Docker Commands¶
docker compose ps # Container status
docker compose logs -f service # Follow logs
docker compose restart service # Restart one service
docker compose up -d --force-recreate service # Recreate container
docker compose exec service command # Run command in container
Production URLs¶
| Service | URL |
|---|---|
| Landing Page | https://yourdomain.com |
| Odoo ERP | https://erp.yourdomain.com |
| Field Service PWA | https://service.yourdomain.com |
| Documentation | https://docs.yourdomain.com |
File Locations¶
| Path | Purpose |
|---|---|
~/odoo15-jdx-production/ |
Application root |
odoo-data/ |
Odoo filestore and sessions |
nginx/ssl/ |
SSL certificates |
nginx/conf.d/default.conf |
Active nginx config |
.env |
Environment configuration |
backups/ |
Local backup storage |