Skip to content

Uplink Deployment Guide

Quick Reference

# 90% of deployments
./deploy.sh deploy-main

# Only when needed
./deploy.sh deploy-full     # Infrastructure changes
./deploy.sh deploy-rebuild  # Dependency changes
./deploy.sh fix-websocket   # Printer 502 errors

Deployment Decision Matrix

📁 By File Type Changed

File Changed Command Reason
*/views.py deploy-main Python code in memory needs refresh
*/models.py deploy-main Model classes need reimport
*/api.py deploy-main API endpoints need reload
*/serializers.py deploy-main Serializer logic needs refresh
*/forms.py deploy-main Form validation needs reload
*/utils.py deploy-main Utility functions need reimport
*/admin.py deploy-main Admin config needs reload
*/migrations/*.py deploy-main Runs migrations + restarts
*/templates/*.html deploy-main or static Templates cached in memory
*/static/*.css/js deploy-main or static Needs collectstatic
uplink/settings.py deploy-main Settings imported at startup
uplink/urls.py deploy-main URL patterns imported at startup
.env deploy-full Environment needs container recreate
docker-compose.yml deploy-full Compose config needs down/up
docker-compose.prod.yml deploy-full Compose override needs reload
nginx.conf deploy-full Nginx config needs reload
Dockerfile deploy-rebuild Image needs rebuild
pyproject.toml deploy-rebuild Dependencies need reinstall
requirements.txt deploy-rebuild Dependencies need reinstall
Pipfile deploy-rebuild Dependencies need reinstall

🎯 By Scenario

Scenario 1: Fixed a Bug in a View

# You edited: orders/views.py
./deploy.sh deploy-main
Why: Python already imported the old view function. Need fresh Python process.

What happens: 1. ✓ Stops containers (old code stops running) 2. ✓ Starts containers (imports fresh code) 3. ✓ Restarts nginx (clears cached connections)


Scenario 2: Added New Database Field

# You ran: python manage.py makemigrations
# You committed: orders/migrations/0042_order_new_field.py
./deploy.sh deploy-main
Why: Need to run migration AND restart with new model definition.

What happens: 1. ✓ Runs python manage.py migrate (updates DB) 2. ✓ Stops/starts containers (loads new model) 3. ✓ Restarts nginx


Scenario 3: Updated CSS Styling

# You edited: static/uplink/css/main.css

# Option A: Just update static files (if no Django changes)
./deploy.sh static

# Option B: Full deployment
./deploy.sh deploy-main
Why: collectstatic copies files to STATICFILES_ROOT. Templates might also reference new styles.

What happens (Option A): 1. ✓ Runs collectstatic only (fast, no restart)

What happens (Option B): 1. ✓ Runs collectstatic 2. ✓ Restarts containers (reloads templates)


Scenario 4: Changed Environment Variable

# You edited: .env
# Changed: DEBUG=True → DEBUG=False
./deploy.sh deploy-full
Why: Containers read .env at startup. Running containers don't see new values.

What happens: 1. ✓ Stops ALL containers 2. ✓ Starts containers (reads new .env) 3. ✓ Runs migrations and collectstatic


Scenario 5: Added New Python Package

# You edited: pyproject.toml
# Added: django-cors-headers = "^4.0.0"
./deploy.sh deploy-rebuild
Why: Package isn't installed in Docker image. Need to rebuild with pip install.

What happens: 1. ✓ Stops containers 2. ✓ Rebuilds Docker image (runs pip install) 3. ✓ Starts containers with new packages 4. ✓ Runs migrations and collectstatic


Scenario 6: Printer Getting 502 Errors

# Symptom: Printer service can't connect
# Error: WebSocket handshake 502 Bad Gateway
./deploy.sh fix-websocket
Why: Nginx has cached connection to old daphne container. Need to clear cache.

What happens: 1. ✓ Stops daphne 2. ✓ Starts daphne 3. ✓ Restarts nginx (clears upstream cache) 4. ⏱️ Only ~5s downtime


Scenario 7: Changed Nginx Configuration

# You edited: nginx.conf
# Changed: client_max_body_size 100M → 200M
./deploy.sh deploy-full
Why: Nginx container needs to reload config file.

What happens: 1. ✓ Stops ALL containers 2. ✓ Starts containers (nginx reads new config) 3. ✓ Runs migrations and collectstatic


Scenario 8: Changed Docker Compose Service Config

# You edited: docker-compose.prod.yml
# Changed: gunicorn workers 4 → 8
./deploy.sh deploy-full
Why: Service config (command, environment, volumes) needs docker compose down + up.

What happens: 1. ✓ Stops ALL containers 2. ✓ Starts containers with new config 3. ✓ Runs migrations and collectstatic


Scenario 9: Service Is Hung/Unresponsive

# Symptom: Web interface is slow, requests timing out
# Want: Quick restart without waiting for migrations
./deploy.sh docker-restart
Why: Emergency restart. Skip migrations/static collection for speed.

What happens: 1. ✓ Restarts web, daphne, huey 2. ✓ Waits for health checks 3. ✓ Restarts nginx 4. ⏱️ Only ~10s downtime


🚨 Troubleshooting

Problem: "Invalid line:" warnings during startup

Symptoms:

uplink_web  | Invalid line:   
uplink_web  | Invalid line:   
Diagnosis: Empty lines in .env file (harmless but annoying) Impact: None - app works fine, just cosmetic warnings Solution (optional):
# Remove blank lines from .env (be careful!)
sed -i '/^$/d' .env

# Or manually edit .env to remove empty lines
vim .env
Note: Empty lines in .env are actually fine for organization. You can ignore these warnings.

Problem: Deployment says "failed to start" but containers are actually running

Symptoms:

✗ web failed to start
But docker compose ps shows containers are "Up" and healthy Diagnosis: Health check timing issue or slow startup Solution: The deployment script now auto-detects this and continues. If it doesn't:
# Check if containers are actually running
docker compose ps

# If they're up and healthy, deployment actually succeeded
# Just restart nginx manually:
docker compose restart nginx

Problem: Code changes not showing up after deployment

Diagnosis: Python has old code cached in memory

# Solution: Force fresh containers
./deploy.sh deploy-main

Problem: Printer service getting 502 errors

Diagnosis: Nginx has stale upstream connections to daphne

# Solution: Restart WebSocket infrastructure
./deploy.sh fix-websocket

Problem: New package not found after deploy-main

Diagnosis: Package not installed in Docker image

# Solution: Rebuild image
./deploy.sh deploy-rebuild

Problem: Environment variable changes not applied

Diagnosis: Containers still using old .env

# Solution: Full restart with new env
./deploy.sh deploy-full

Problem: Static files 404 or old version showing

Diagnosis: collectstatic not run or whitenoise not reloaded

# Solution 1: Quick static update
./deploy.sh static

# Solution 2: Full deployment
./deploy.sh deploy-main


📊 Downtime Comparison

Command Downtime Frequency Risk Level
deploy-main ~30s Daily/Weekly Low
deploy-full ~60s Monthly Low
deploy-rebuild ~3min Rare Medium
fix-websocket ~5s As needed Very Low
docker-restart ~10s Emergency Low

🎓 Best Practices

  1. Default to deploy-main: 90% of code changes use this
  2. Test locally first: Run docker-compose up locally to verify
  3. Check logs after deploy: ./deploy.sh logs to verify startup
  4. Monitor health endpoint: Check /health/ returns 200
  5. Keep deploys small: Smaller changes = easier rollback
  6. Document .env changes: Always note when .env changes require deploy-full
  7. Bundle migrations: Multiple migrations in one deploy-main is fine

🔄 Deployment Workflow

Standard Deployment (Code Changes)

# 1. Make changes locally
vim orders/views.py

# 2. Test locally
./deploy.sh docker-up
# Test in browser

# 3. Commit and push
git add orders/views.py
git commit -m "Fix order display bug"
git push

# 4. Deploy to production
ssh uplink@Uplink2
cd ~/app
./deploy.sh deploy-main

Migration Deployment

# 1. Create migration locally
python manage.py makemigrations

# 2. Test migration locally
python manage.py migrate

# 3. Commit and push
git add orders/migrations/0043_*.py
git commit -m "Add new field to Order model"
git push

# 4. Deploy (migrations run automatically)
ssh uplink@Uplink2
cd ~/app
./deploy.sh deploy-main  # Runs migrations automatically

💡 Tips

  • Use ./deploy.sh without arguments to see the decision guide
  • Check docker compose ps after deployment to verify all services running
  • Use ./deploy.sh docker-logs web to tail logs during deployment
  • Keep printer service URL updated if hostname changes

📝 Deployment Checklist

Before running any deployment:

  • [ ] Code committed and pushed to Git
  • [ ] Tested changes locally
  • [ ] Chose correct deployment command
  • [ ] Notified users if downtime > 1 minute
  • [ ] Ready to monitor logs after deployment

After deployment:

  • [ ] Check /health/ endpoint returns 200
  • [ ] Verify docker compose ps shows all services running
  • [ ] Check logs for errors: ./deploy.sh logs
  • [ ] Test affected functionality
  • [ ] Monitor for 5-10 minutes

🆘 Need Help?

Run the deployment script without arguments to see the interactive guide:

./deploy.sh

Or check specific command help:

./deploy.sh deploy-main --help  # (not implemented yet)