Skip to content

Uplink 2.0 Upgrade Logs

This document is for logging the steps that were taken to update Uplink to Uplink 2.0 as detailed in 'Uplink2.0_PLAN.md'. In short, it's what was actually done as opposed to what was planned.

Things removed

  • /app/ directory
  • Removing the vue.js references from product_stock_adjustment.html
  • 'BASE_DIR / "frontend/dist"' from settings.py
  • vue.js and Yarn from .gitignore
  • Sprinkles and Yarn from Procfile.toml
  • <script src="{% static 'js/chunk-vendors.js' %}"></script> from javascript.html
  • <script src="{% static 'js/app.js' %}"></script> from javascript.html

Things to add back in

  • focus on specific fields on the stock adjustment page. This was previously handled through vue.js, so just to be handled through vanilla JS. Added as issue #1042 on Uplink Github.

2. [02/02/2026] Attempted to switch to uv from pipenv but ran into venv conflicts

Attempted to switch Uplink from using pipenv to using uv but there were many issues and envrionment conflicts regarding packages that had been installed on my local machine and another venv.

Attempted to resolve the issues on my local environment but wasn't able to after quite a while of trying, so I think it would be best to combine steps 1 and 3 of the Uplink 2.0 plan, uv and Docker. That way I will know that the environment is clean and won't be subject to the same kind of errors I've been encountering.

Changes to plan: - Added "Plan Revision: Why Dockerize First" section documenting the rationale - Combined Phase 1 and Phase 3 into new "Phase 1: Containerization with uv (COMBINED)" - Renumbered subsequent phases - Added comprehensive "Appendix D: Deployment Procedures" with rigorous dev and production deployment workflows

4. [02/02/2026] Created Docker configuration files

Created all necessary Docker files for containerized development and production deployment:

Files created: - Dockerfile - Production-ready container using Python 3.9 + uv - Uses python:3.9-slim base image - Installs system dependencies (gcc, mysqlclient-dev, netcat) - Copies uv from official image for fast dependency installation - Includes health check for container monitoring - Defaults to gunicorn for production

  • docker-compose.yml - Development environment orchestration
  • db service: MySQL 8.0 with health checks
  • redis service: Redis 7 Alpine for Huey and Channels
  • web service: Django development server on port 8000
  • daphne service: ASGI server for WebSockets on port 9000
  • huey service: Background task worker
  • Named volumes for data persistence
  • Custom network for service communication

  • docker-compose.prod.yml - Production overrides

  • Switches to gunicorn with 4 workers
  • Removes source code volume mounts
  • Sets DEBUG=False
  • Configures restart policies

  • .dockerignore - Optimized build context

  • Excludes git, cache files, logs
  • Excludes docs, legacy Pipenv files
  • Reduces image size and build time

  • docker-entrypoint.sh - Container startup script

  • Waits for database availability
  • Runs migrations automatically
  • Collects static files
  • Made executable with chmod +x

  • .env.example - Environment variable template

  • Django core settings (DEBUG, SECRET_KEY)
  • Database credentials
  • Redis URL
  • External API keys (FedEx, PrestaShop, Xero)
  • Mailgun email configuration

Rollback plan: Created comprehensive ROLLBACK.md in project root documenting: - Quick rollback procedure (< 5 minutes) - What's preserved (Pipfile, Pipfile.lock, database, source code) - Troubleshooting common Docker issues before rollback - How to completely remove Docker if needed - Production deployment safety (unaffected by local Docker experiments)

Safety measures: - Pipfile and Pipfile.lock kept in repository (commented out in .dockerignore) - Local database remains untouched by Docker - Pipenv environment still fully functional - Can switch between Docker and pipenv at any time - Production deployment still uses pipenv (zero risk)

Next steps: 1. Create .env file from .env.example with actual credentials 2. Build Docker images: docker-compose build 3. Start services: docker-compose up -d 4. Run initial migrations: docker-compose exec web python manage.py migrate 5. Create superuser: docker-compose exec web python manage.py createsuperuser 6. Test application at http://localhost:8000 7. If any issues: Follow ROLLBACK.md to return to pipenv

5. [24/02/2026] Production deployment complete with SSL and gunicorn

Successfully deployed Uplink 2.0 to production server (uplink.sensational.systems) with full Docker containerization:

6. [24/02/2026] Restructured Docker Compose with base+override pattern and environment detection

Restructured the Docker Compose configuration to support three deployment modes (pipenv, docker-dev, docker-prod) without requiring .env changes:

Architecture Changes: - Split single docker-compose.yml into base + environment-specific overrides: - docker-compose.yml (base configuration, shared across environments) - docker-compose.dev.yml (development overrides: volume mounts, local MySQL, DEBUG=True) - docker-compose.prod.yml (production overrides: no volumes, DigitalOcean MySQL, DEBUG=False) - Backed up previous monolithic compose file to docker-compose-old.yml

Environment Detection: - Updated deploy.sh with automatic environment detection: - Checks for /srv/uplink/app/.production marker file OR hostname="uplink" - Returns "production" or "development" automatically - Added get_compose_cmd() function to return appropriate compose command: - Development: docker compose -f docker-compose.yml -f docker-compose.dev.yml - Production: docker compose -f docker-compose.yml -f docker-compose.prod.yml - All deploy.sh functions now use $COMPOSE_CMD variable (environment-aware)

Local Development Setup: - Enabled local MySQL 8.0 container (port 3309) for development isolation - Base compose file has db service with restart: "no" (disabled by default) - Dev override enables with restart: unless-stopped - Added DATABASE_USER=uplink, DATABASE_PASS=admin to dev environment variables - Hot-reload enabled via volume mounts (- .:/app) - nginx/certbot disabled in dev via profiles: [production-only]

Production Configuration: - No volume mounts (code baked into images) - DATABASE_HOST=${DATABASE_HOST} from .env (DigitalOcean managed MySQL) - All services have restart: always policy - nginx and certbot enabled for SSL termination

Three-Mode Operation: 1. Pipenv (legacy): pipenv run honcho start - Uses host MySQL on port 3307 2. Docker Dev: ./deploy.sh docker-up - Uses Docker MySQL container locally 3. Docker Prod: On server, ./deploy.sh deploy-full - Uses DigitalOcean MySQL cluster

Documentation Updates: - Created docs/DOCKER_COMPOSE_STRUCTURE.md - Comprehensive guide to new architecture - Updated docs/development/LOCAL_DEVELOPMENT.md - Dual-mode setup instructions - Updated docs/deployment/DOCKER_DEPLOYMENT.md - Added environment detection to disaster recovery - Updated README.md - Docker setup instructions with explicit compose file references - Updated .env - Added comments explaining three-mode operation

Benefits: - No .env changes needed to switch between deployment modes - Local development isolated from host MySQL (prevents conflicts) - Production uses external DigitalOcean MySQL (no containerized DB in prod) - Clear separation of concerns (base config vs environment-specific overrides) - Easy to test production-like setup locally - Automatic environment detection reduces human error

Verification: - ✅ Local development working with Docker MySQL container - ✅ All 5 containers running healthy (db, redis, web, daphne, huey) - ✅ Database migrations applied successfully - ✅ Login page accessible at http://localhost:8001 - ✅ Environment detection showing "[development]" on local machine - ✅ Production deployment commands unchanged (./deploy.sh deploy-full)

Next Steps: - Create .production marker file on production server: touch /srv/uplink/app/.production - Test production deployment with new compose file structure - Commit all changes to git (docker-compose.yml, docker-compose.dev.yml, deploy.sh, docs/)

Infrastructure: - ✅ 6 containers running: web (gunicorn), daphne, huey, redis, nginx, certbot - ✅ SSL/HTTPS working with Let's Encrypt certificates - ✅ nginx reverse proxy on ports 80/443 - ✅ Whitenoise serving static files - ✅ Connected to DigitalOcean MySQL cluster (no Docker MySQL)

Production configuration: - Switched from runserver to gunicorn with 4 workers - Set DEBUG=False for production - gunicorn timeout: 120s for long-running requests - nginx serving as reverse proxy and SSL termination - Automatic SSL certificate renewal via certbot container

Branch status: - Production running on 1130-dockerise-uplink-and-switch-to-using-uv branch - Branch ready to merge to main (28 commits ahead, 0 behind) - Next step: Merge Docker branch to main so production can run from main

What's left: - Merge 1130-dockerise-uplink-and-switch-to-using-uv to main - Switch production server to main branch - Update DNS to point to new server (when ready for cutover) - Decommission old server