Fresh Ubuntu 24.04 Production Setup Guide¶
This guide provides step-by-step instructions for setting up Uplink on a new Ubuntu 24.04 LTS VM with Docker, running it in parallel with your existing Ubuntu 18.04 production server for safe migration.
Migration Strategy: Parallel Operation - Set up new Ubuntu 24.04 VM with Docker - Run both old and new servers simultaneously - Test thoroughly on new server - Gradually shift traffic to new server - Decommission old server after validation
Table of Contents¶
- Server Specifications
- Pre-Setup Checklist
- Phase 1: New Server Setup
- Phase 2: Running in Parallel
- Phase 3: Traffic Cutover
- Phase 4: Decommissioning Old Server
Server Specifications¶
Minimum Recommended Specs¶
For Current Application Load: - CPU: 2-4 vCPUs (2 cores minimum, 4 recommended) - RAM: 4GB minimum, 8GB recommended - MySQL container: ~512MB-1GB - Redis container: ~256MB-512MB - Web container: ~512MB-1GB - Daphne container: ~256MB-512MB - Huey container: ~256MB-512MB - OS + overhead: ~1GB - Disk: 40GB minimum, 80GB+ recommended - OS: ~10GB - Docker images: ~5GB - Database: ~5-10GB (depends on your data) - Media files: ~5-10GB (depends on your uploads) - Logs: ~2GB - Free space for growth: ~20GB+ - Network: 100 Mbps minimum, 1 Gbps recommended - OS: Ubuntu 24.04 LTS (Server)
Calculating Your Actual Requirements¶
# On your current Ubuntu 18.04 server, check current usage:
# CPU usage
top -bn1 | grep "Cpu(s)"
# Memory usage
free -h
# Disk usage
df -h
du -sh /srv/uplink/app
du -sh /srv/uplink/app/media
du -sh /var/lib/mysql
# Database size
mysql -u root -p -e "SELECT table_schema AS 'Database', ROUND(SUM(data_length + index_length) / 1024 / 1024, 2) AS 'Size (MB)' FROM information_schema.tables GROUP BY table_schema;"
Sizing Formula: - CPU: Current usage + 50% headroom - RAM: Current usage × 1.5 (Docker overhead) - Disk: Current usage × 2 (for Docker images, volumes, backups)
Example Configurations¶
Small Setup (Development/Staging): - 2 vCPUs - 4GB RAM - 40GB SSD
Medium Setup (Production - Low/Medium Traffic): - 4 vCPUs - 8GB RAM - 80GB SSD
Large Setup (Production - High Traffic): - 8 vCPUs - 16GB RAM - 160GB SSD
Pre-Setup Checklist¶
1. Backup Current Production Data¶
On your Ubuntu 18.04 server:
# Create backups directory
mkdir -p /srv/uplink/backups
# Backup database
mysqldump -u uplink_user -p uplink | gzip > /srv/uplink/backups/uplink_db_full_$(date +%Y%m%d_%H%M%S).sql.gz
# Backup media files
tar -czf /srv/uplink/backups/media_full_$(date +%Y%m%d_%H%M%S).tar.gz /srv/uplink/app/media/
# Backup .env file
cp /srv/uplink/app/.env /srv/uplink/backups/.env.backup.$(date +%Y%m%d)
# Verify backups
ls -lh /srv/uplink/backups/
2. Transfer Backups to New Server¶
You'll need these on the new server. Options:
Option A: Direct transfer via scp
# From old server (Ubuntu 18.04)
scp /srv/uplink/backups/uplink_db_full_*.sql.gz user@new-server:/tmp/
scp /srv/uplink/backups/media_full_*.tar.gz user@new-server:/tmp/
scp /srv/uplink/backups/.env.backup.* user@new-server:/tmp/
Option B: Via intermediate machine (your laptop)
# Download from old server to laptop
scp user@old-server:/srv/uplink/backups/uplink_db_full_*.sql.gz ~/Downloads/
scp user@old-server:/srv/uplink/backups/media_full_*.tar.gz ~/Downloads/
scp user@old-server:/srv/uplink/backups/.env.backup.* ~/Downloads/
# Upload from laptop to new server
scp ~/Downloads/uplink_db_full_*.sql.gz user@new-server:/tmp/
scp ~/Downloads/media_full_*.tar.gz user@new-server:/tmp/
scp ~/Downloads/.env.backup.* user@new-server:/tmp/
3. Document Current Production Setup¶
On Ubuntu 18.04 server, document:
# Current services and ports
sudo netstat -tlnp | grep -E "python|mysql|redis|nginx"
# Current environment variables (sanitize secrets!)
cat /srv/uplink/app/.env | grep -v "PASSWORD\|SECRET\|KEY"
# Nginx configuration
cat /etc/nginx/sites-available/uplink
# DNS/domain setup
host yourdomain.com
# SSL certificates location
ls -la /etc/letsencrypt/live/
Phase 1: New Server Setup¶
Step 1.1: Provision Ubuntu 24.04 VM¶
- Create new VM with Ubuntu 24.04 LTS Server
- Assign sufficient resources (see specs above)
- Note the IP address
- Configure SSH access
- Set hostname:
sudo hostnamectl set-hostname uplink-new
Step 1.2: Initial System Setup¶
SSH into the new Ubuntu 24.04 server:
# Update system
sudo apt-get update
sudo apt-get upgrade -y
# Install basic tools
sudo apt-get install -y \
git \
curl \
wget \
vim \
htop \
net-tools \
unzip \
build-essential
# Set timezone (match old server)
sudo timedatectl set-timezone Europe/London # Adjust as needed
# Configure firewall
sudo ufw allow 22/tcp # SSH
sudo ufw allow 80/tcp # HTTP
sudo ufw allow 443/tcp # HTTPS
sudo ufw enable
Step 1.3: Install Docker¶
# Remove old Docker versions if any
sudo apt-get remove docker docker-engine docker.io containerd runc
# Update package index
sudo apt-get update
# Install prerequisites
sudo apt-get install -y \
ca-certificates \
curl \
gnupg \
lsb-release
# Add Docker's official GPG key
sudo mkdir -p /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
# Set up Docker repository
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
$(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
# Install Docker Engine
sudo apt-get update
sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
# Verify installation
sudo docker --version
sudo docker compose version
# Add user to docker group
sudo usermod -aG docker $USER
# Apply group changes
newgrp docker
# Test Docker
docker run hello-world
# Enable Docker to start on boot
sudo systemctl enable docker
Step 1.4: Clone Repository¶
# Create application directory
sudo mkdir -p /srv/uplink
sudo chown $USER:$USER /srv/uplink
# Clone repository
cd /srv/uplink
git clone https://github.com/your-org/uplink.git app
cd app
# Checkout Docker branch
git checkout 1130-dockerise-uplink-and-switch-to-using-uv
# Verify Docker files exist
ls -l Dockerfile docker-compose.yml docker-compose.prod.yml
Step 1.5: Configure Environment¶
cd /srv/uplink/app
# Create .env from backup or template
# If you transferred backup:
cp /tmp/.env.backup.* .env
# Or copy from example:
cp .env.production.example .env
# Edit .env for Docker
nano .env
# Required changes for Docker:
# DATABASE_HOST=db
# DATABASE_PORT=3306
# REDIS_HOST=redis
# REDIS_PORT=6379
# HUEY_IMMEDIATE=False
#
# Important: Update these from old server:
# - SECRET_KEY (escape $ as $$)
# - DATABASE_NAME, DATABASE_USER, DATABASE_PASSWORD
# - ALLOWED_HOSTS (add new server IP)
# - PRESTASHOP URLs
# - Any API keys/credentials
Step 1.6: Import Database¶
cd /srv/uplink/app
# Create directory for backups
mkdir -p /srv/uplink/backups
# If backup is in /tmp, move it
mv /tmp/uplink_db_full_*.sql.gz /srv/uplink/backups/
# Build and start only MySQL container first
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d db
# Wait for MySQL to be ready (30 seconds)
sleep 30
# Check MySQL is running
docker compose ps db
# Import database
gunzip < /srv/uplink/backups/uplink_db_full_*.sql.gz | docker compose exec -T db mysql -u root -p$MYSQL_ROOT_PASSWORD uplink
# Verify import
docker compose exec db mysql -u uplink_user -p uplink -e "SHOW TABLES;"
docker compose exec db mysql -u uplink_user -p uplink -e "SELECT COUNT(*) FROM catalogue_product;"
Step 1.7: Import Media Files¶
cd /srv/uplink/app
# Extract media backup
tar -xzf /srv/uplink/backups/media_full_*.tar.gz -C /tmp/
# Copy media files to project (adjust path if needed)
cp -r /tmp/srv/uplink/app/media/* ./media/
# Fix permissions
sudo chown -R www-data:www-data media/
# Verify
ls -la media/
Step 1.8: Build and Start All Services¶
cd /srv/uplink/app
# Build all Docker images
docker compose -f docker-compose.yml -f docker-compose.prod.yml build
# Start all services
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d
# Check all containers running
docker compose ps
# Should see: db, redis, web, daphne, huey all "running"
Step 1.9: Run Migrations and Setup¶
cd /srv/uplink/app
# Run migrations (should be no-op if database already has schema)
docker compose exec web python manage.py migrate
# Collect static files
docker compose exec web python manage.py collectstatic --noinput
# Create superuser (if needed for testing)
docker compose exec web python manage.py createsuperuser
# Run system check
docker compose exec web python manage.py check
Step 1.10: Configure Systemd for Auto-Start¶
# Create systemd service
sudo nano /etc/systemd/system/uplink-docker.service
# Add this content:
[Unit]
Description=Uplink Docker Compose Service
Requires=docker.service
After=docker.service network-online.target
Wants=network-online.target
[Service]
Type=oneshot
RemainAfterExit=yes
WorkingDirectory=/srv/uplink/app
ExecStart=/usr/bin/docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d
ExecStop=/usr/bin/docker compose down
User=$USER
Group=docker
[Install]
WantedBy=multi-user.target
# Enable and start
sudo systemctl daemon-reload
sudo systemctl enable uplink-docker.service
sudo systemctl start uplink-docker.service
# Verify
sudo systemctl status uplink-docker.service
Step 1.11: Setup Cron Jobs¶
# Install crontab for Docker
cd /srv/uplink/app
crontab crontab.production
# Verify crontab
crontab -l
# Note: Cron jobs will use docker compose exec -T
Step 1.12: Install and Configure Nginx (Optional)¶
If you want SSL/domain on the new server:
# Install Nginx
sudo apt-get install -y nginx
# Create Nginx config
sudo nano /etc/nginx/sites-available/uplink
# Basic config (adjust domain):
server {
listen 80;
server_name new-uplink.yourdomain.com; # Temporary subdomain
location / {
proxy_pass http://127.0.0.1:8000;
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;
}
location /ws/ {
proxy_pass http://127.0.0.1:9000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
}
location /static/ {
alias /srv/uplink/app/static/;
}
location /media/ {
alias /srv/uplink/app/media/;
}
}
# Enable site
sudo ln -s /etc/nginx/sites-available/uplink /etc/nginx/sites-enabled/
sudo rm /etc/nginx/sites-enabled/default
# Test and reload
sudo nginx -t
sudo systemctl reload nginx
# Install Certbot for SSL (optional, for testing subdomain)
sudo apt-get install -y certbot python3-certbot-nginx
sudo certbot --nginx -d new-uplink.yourdomain.com
Phase 2: Running in Parallel¶
Step 2.1: DNS Configuration for Testing¶
Option A: Subdomain for New Server (Recommended)
Create a subdomain pointing to new server:
- uplink.yourdomain.com → Old server (Ubuntu 18.04)
- new-uplink.yourdomain.com → New server (Ubuntu 24.04)
This allows testing without affecting production.
Option B: Hosts File Testing
On your local machine:
# Edit /etc/hosts (Mac/Linux) or C:\Windows\System32\drivers\etc\hosts (Windows)
# Add:
NEW_SERVER_IP test-uplink.local
Step 2.2: Testing Checklist¶
Test these on the new server via subdomain or test URL:
Basic Functionality:
- [ ] Access admin panel: https://new-uplink.yourdomain.com/admin/
- [ ] Login with superuser credentials
- [ ] View product list
- [ ] View order list
- [ ] Create test product
- [ ] Create test order
- [ ] Upload media file
- [ ] View uploaded media
PrestaShop Integration: - [ ] Test PrestaShop product import (check extra_hosts working) - [ ] Test PrestaShop order import - [ ] Test product sync to PrestaShop
WebSocket/Real-time: - [ ] Test printer command (WebSocket via Daphne) - [ ] Test device communication
Background Tasks:
- [ ] Check Huey processing tasks
- [ ] Monitor Huey logs: docker compose logs -f huey
Cron Jobs:
- [ ] Wait for cron execution (2 min intervals)
- [ ] Check cron ran: docker compose logs web | grep prestashop
Performance:
- [ ] Page load times acceptable
- [ ] Database queries performing well
- [ ] Check resource usage: docker stats
Step 2.3: Monitoring Both Servers¶
Old Server (Ubuntu 18.04):
# Check services
sudo systemctl status uplink uplink-daphne uplink-huey
# Monitor logs
sudo journalctl -u uplink -f
# Check resource usage
htop
New Server (Ubuntu 24.04):
# Check containers
docker compose ps
# Monitor logs
docker compose logs -f
# Check resource usage
docker stats
htop
Step 2.4: Data Sync Strategy (If Needed)¶
If production data changes significantly during testing:
Option A: Periodic Database Snapshots
On old server:
# Daily snapshot script
#!/bin/bash
mysqldump -u uplink_user -p uplink | gzip > /srv/uplink/backups/daily_sync_$(date +%Y%m%d).sql.gz
# Transfer to new server
scp /srv/uplink/backups/daily_sync_*.sql.gz user@new-server:/tmp/
On new server:
# Import latest snapshot (during low-traffic period)
docker compose stop web daphne huey
gunzip < /tmp/daily_sync_*.sql.gz | docker compose exec -T db mysql -u root -p uplink
docker compose start web daphne huey
Option B: Read-only Mode on New Server
Keep new server read-only for testing, sync data periodically.
Step 2.5: Validation Period¶
Run in parallel for 1-2 weeks minimum:
Week 1: Internal Testing - Team tests new server extensively - Identify any issues - Fix bugs/configuration - Performance tuning
Week 2: Limited Production Traffic - Route small percentage of traffic to new server (if using load balancer) - Or use for specific non-critical operations - Monitor closely for issues
Phase 3: Traffic Cutover¶
Step 3.1: Pre-Cutover Checklist¶
- [ ] All tests passing on new server
- [ ] Team comfortable with new server
- [ ] Backups of both servers completed
- [ ] Rollback plan documented and tested
- [ ] Maintenance window scheduled
- [ ] Stakeholders notified
- [ ] DNS TTL reduced to 300s (5 min) 24 hours before cutover
Step 3.2: Final Data Sync¶
During maintenance window:
# On OLD server - stop application
sudo systemctl stop uplink uplink-daphne uplink-huey
# Create final backup
mysqldump -u uplink_user -p uplink | gzip > /srv/uplink/backups/final_cutover_$(date +%Y%m%d_%H%M%S).sql.gz
# Transfer to new server
scp /srv/uplink/backups/final_cutover_*.sql.gz user@new-server:/tmp/
# On NEW server - import final data
cd /srv/uplink/app
docker compose stop web daphne huey
gunzip < /tmp/final_cutover_*.sql.gz | docker compose exec -T db mysql -u root -p uplink
docker compose start web daphne huey
# Verify
docker compose exec web python manage.py check
docker compose ps
Step 3.3: DNS Cutover¶
Update DNS records to point to new server:
- If using Cloudflare/DNS provider, update A record
- Wait for DNS propagation (5-30 minutes with low TTL)
- Monitor traffic shifting to new server
Step 3.4: SSL Certificate Transfer (If Needed)¶
If using Let's Encrypt on old server:
Option A: Generate new certificate on new server
Option B: Transfer existing certificate
# On old server
sudo tar -czf /tmp/letsencrypt-backup.tar.gz /etc/letsencrypt/
# Transfer to new server
scp /tmp/letsencrypt-backup.tar.gz user@new-server:/tmp/
# On new server
sudo tar -xzf /tmp/letsencrypt-backup.tar.gz -C /
sudo systemctl reload nginx
Step 3.5: Post-Cutover Verification¶
Immediately after cutover:
- [ ] Website accessible at production URL
- [ ] SSL certificate valid
- [ ] Login works
- [ ] Can create/edit data
- [ ] PrestaShop integration working
- [ ] Cron jobs executing
- [ ] No errors in logs
- [ ] Monitor for 1-2 hours continuously
Monitor closely:
# New server
docker compose logs -f
docker stats
htop
# Check error logs specifically
docker compose logs web | grep -i error
docker compose logs daphne | grep -i error
docker compose logs huey | grep -i error
Step 3.6: Rollback Procedure (If Needed)¶
If critical issues arise:
# 1. Update DNS back to old server IP
# (5-30 min for propagation)
# 2. On old server - restart services
sudo systemctl start uplink uplink-daphne uplink-huey
sudo systemctl status uplink
# 3. Verify old server working
curl -I http://localhost:8000/admin/
# 4. Communicate status to users
Phase 4: Decommissioning Old Server¶
Wait 2-4 weeks after successful cutover before decommissioning.
Step 4.1: Keep Old Server as Backup¶
For first 2-4 weeks: - Keep old server running but not serving traffic - Take regular backups from new server - Monitor new server stability - Document any issues and fixes
Step 4.2: Final Cleanup (After 2-4 Weeks)¶
When confident new server is stable:
# On old server
# 1. Stop services
sudo systemctl stop uplink uplink-daphne uplink-huey mysql redis
# 2. Disable auto-start
sudo systemctl disable uplink uplink-daphne uplink-huey mysql redis
# 3. Archive final backup
tar -czf /srv/uplink/final-archive-$(date +%Y%m%d).tar.gz /srv/uplink/
# 4. Transfer archive to safe storage
# 5. Consider destroying VM or repurposing
Step 4.3: Documentation Updates¶
Update all documentation with: - New server IP - New Docker-based deployment procedures - Updated troubleshooting for Docker - New backup/restore procedures
Troubleshooting Parallel Operation¶
Issue: Both Servers Accessing Same PrestaShop¶
Problem: Old and new servers both trying to sync with PrestaShop
Solution:
# On new server during testing, disable PrestaShop cron jobs
crontab -e
# Comment out prestashop_* commands temporarily
# Or set PrestaShop to read-only mode on new server
Issue: Database Conflicts¶
Problem: Changes made on old server not reflected on new
Solution: - Keep new server read-only during testing - Sync database nightly from old to new - Or route all writes to old server until cutover
Issue: SSL Certificate Conflicts¶
Problem: Can't get SSL for both servers with same domain
Solution:
- Use subdomain for new server (new-uplink.yourdomain.com)
- Get SSL for subdomain
- After cutover, get SSL for main domain on new server
Issue: Session Conflicts¶
Problem: Users get logged out switching between servers
Solution: - Use different session cookie names - Or use only one server at a time - Or use shared session storage (Redis)
Cost and Resource Planning¶
Expected Costs (Example - AWS)¶
Small Setup: - t3.medium (2 vCPU, 4GB RAM): ~$30/month - 40GB SSD: ~$4/month - Data transfer: ~$5/month - Total: ~$39/month
Medium Setup: - t3.large (2 vCPU, 8GB RAM): ~$60/month - 80GB SSD: ~$8/month - Data transfer: ~$10/month - Total: ~$78/month
During Parallel Operation: - Old server + New server = ~2x normal costs - Run parallel for 2-4 weeks - Additional cost: ~$80-160 for testing period
Resource Monitoring¶
# On new server, monitor and adjust
docker stats
# If containers are resource-constrained, scale up VM
# If over-provisioned, can scale down after testing
Success Criteria¶
Migration complete when:
- ✅ New server running Ubuntu 24.04 LTS
- ✅ All services in Docker containers
- ✅ Database migrated successfully
- ✅ Media files migrated
- ✅ DNS pointing to new server
- ✅ SSL certificates valid
- ✅ All functionality tested and working
- ✅ Cron jobs executing correctly
- ✅ Performance acceptable
- ✅ No errors in logs for 24+ hours
- ✅ Team trained on Docker deployment
- ✅ Old server decommissioned (after validation period)
Congratulations! You're now running on Ubuntu 24.04 with Docker! 🎉🐳