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.
1. [15/01/2026] Removing all vue.js, sprinkles and yarn from Uplink¶
Things removed¶
/app/directory- Removing the
vue.jsreferences fromproduct_stock_adjustment.html 'BASE_DIR / "frontend/dist"'fromsettings.pyvue.jsand Yarn from.gitignore- Sprinkles and Yarn from
Procfile.toml <script src="{% static 'js/chunk-vendors.js' %}"></script>fromjavascript.html<script src="{% static 'js/app.js' %}"></script>fromjavascript.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.
3. [02/02/2026] Editing the upgrade plan to combine uv and dockerUpdated the Uplink 2.0 plan to combine Phase 1 (uv) and Phase 3 (Docker) into a single phase. This decision was made after encountering system-wide package pollution that broke virtual environment isolation. Docker provides a clean environment that eliminates these conflicts.¶
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 orchestrationdbservice: MySQL 8.0 with health checksredisservice: Redis 7 Alpine for Huey and Channelswebservice: Django development server on port 8000daphneservice: ASGI server for WebSockets on port 9000hueyservice: 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