From b9ce149a7e11ce54808b2bd4d2a7ea3414987c31 Mon Sep 17 00:00:00 2001 From: Md Bayazid Bostame Date: Sat, 28 Mar 2026 23:12:55 +0100 Subject: [PATCH] docs: document deployment and ci cd process --- DEPLOYMENT.md | 296 ++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 253 insertions(+), 43 deletions(-) diff --git a/DEPLOYMENT.md b/DEPLOYMENT.md index 807ec8c..024fdc7 100644 --- a/DEPLOYMENT.md +++ b/DEPLOYMENT.md @@ -1,68 +1,278 @@ -# Deployment +# Deployment and CI/CD + +## Current deployment model +- one private GitHub repository +- `develop` deploys to the test server +- `main` is reserved for production deployment +- GitHub Actions uploads the repository contents to the server over SSH +- the server does not need GitHub access to deploy + +This is intentional. For a private repository, server-side `git clone` adds unnecessary credential management. ## Branch strategy -- `develop`: test/staging deployments -- `main`: production deployments -- keep one private GitHub repository +- `develop`: test deployment branch +- `main`: production branch +- feature branches: normal product work -## Local development -- current `docker-compose.yml` remains the development stack -- use `.env` or `.env.dev.example` as the template +## Environments +### Development / test +- target server: `192.168.2.55` +- deployment path: `/opt/workdock` +- stack file: `docker-compose.prod.yml` +- env file on server: `.env.test` +- current access URL: `http://192.168.2.55:8088` -## Test deployment server -- copy `.env.test.example` to `.env.test` on the server -- recommended path: `/opt/workdock/.env.test` -- current test target: `192.168.2.55` +### Production +- same deployment mechanism +- different server +- env file on server: `.env.prod` +- should run behind real HTTPS +- should keep `DEBUG=0` -## Production deployment server -- copy `.env.prod.example` to `.env.prod` on the server -- use HTTPS and secure cookies in production +## Important design choice +The current test server is a LAN-only HTTP deployment. + +Because the Django settings enforce secure-cookie checks when `DEBUG=0`, the test deployment uses: +- `DJANGO_DEBUG=1` +- `RUN_DJANGO_CHECK=0` + +That is acceptable for this internal test box only. + +Production must use: +- `DJANGO_DEBUG=0` +- `DJANGO_SECURE_COOKIES=1` +- HTTPS +- `RUN_DJANGO_CHECK=1` + +## Files used for deployment +- [docker-compose.prod.yml](/Users/bostame/Documents/workdock-platform/docker-compose.prod.yml) +- [scripts/deploy_stack.sh](/Users/bostame/Documents/workdock-platform/scripts/deploy_stack.sh) +- [backend/entrypoint-web-prod.sh](/Users/bostame/Documents/workdock-platform/backend/entrypoint-web-prod.sh) +- [backend/entrypoint-worker-prod.sh](/Users/bostame/Documents/workdock-platform/backend/entrypoint-worker-prod.sh) +- [deploy/Caddyfile](/Users/bostame/Documents/workdock-platform/deploy/Caddyfile) +- [.env.test.example](/Users/bostame/Documents/workdock-platform/.env.test.example) +- [.env.prod.example](/Users/bostame/Documents/workdock-platform/.env.prod.example) +- [.github/workflows/deploy-test.yml](/Users/bostame/Documents/workdock-platform/.github/workflows/deploy-test.yml) +- [.github/workflows/deploy-prod.yml](/Users/bostame/Documents/workdock-platform/.github/workflows/deploy-prod.yml) + +## What `deploy_stack.sh` does +The deployment script: +1. validates the env file exists +2. builds `web`, `worker`, and `caddy` +3. starts `db` and `redis` +4. initializes writable volume ownership for: + - `/app/media` + - `/app/staticfiles` + - `/app/backups` +5. runs: + - `migrate` + - `bootstrap_initial_users` + - `collectstatic` +6. optionally runs `manage.py check` +7. starts: + - `web` + - `worker` + - `caddy` +8. waits for `/healthz/` + +## Proxmox / LXC requirement +This project is running in an Ubuntu CT on Proxmox, with Docker inside the CT. + +For this to work, the CT needed Proxmox-side configuration in: +- `/etc/pve/lxc/.conf` + +Required settings: +```conf +features: nesting=1,keyctl=1 +lxc.apparmor.profile: unconfined +lxc.mount.entry: /dev/null sys/module/apparmor/parameters/enabled none bind 0 0 +``` + +Then restart the CT: +```bash +pct restart +``` + +Without this, Docker containers in the CT fail with: +```text +open sysctl net.ipv4.ip_unprivileged_port_start ... permission denied +``` + +This is a Proxmox/LXC nested-Docker issue, not an application bug. ## Server bootstrap -Install: -- `git` -- `docker` -- `docker compose plugin` -- `curl` - -Recommended app directory: -- `/opt/workdock` - -## First clone on server +Run on the server once: +```bash +apt-get update +apt-get install -y ca-certificates curl gnupg git +install -m 0755 -d /etc/apt/keyrings +curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg +chmod a+r /etc/apt/keyrings/docker.gpg +echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo $VERSION_CODENAME) stable" > /etc/apt/sources.list.d/docker.list +apt-get update +apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin +systemctl enable --now docker +``` + +## Server directory layout +Current test server path: +```bash +/opt/workdock +``` + +Important server-local files: +- `/opt/workdock/.env.test` +- later `/opt/workdock/.env.prod` + +These env files are intentionally not uploaded from GitHub Actions. + +## Test env file +Create on the server: ```bash -git clone git@github.com:OWNER/REPO.git /opt/workdock -cd /opt/workdock cp .env.test.example .env.test ``` -## Test deploy manually -```bash -cd /opt/workdock -./scripts/deploy_stack.sh .env.test docker-compose.prod.yml +Current important values for the LAN test box: +```env +DJANGO_DEBUG=1 +DJANGO_ALLOWED_HOSTS=192.168.2.55,localhost,127.0.0.1 +DJANGO_CSRF_TRUSTED_ORIGINS=http://192.168.2.55:8088 +DJANGO_SECURE_COOKIES=0 +DJANGO_SECURE_SSL_REDIRECT=0 +APP_PORT=8088 +SITE_ADDRESS=:80 ``` -## Production deploy manually -```bash -cd /opt/workdock -./scripts/deploy_stack.sh .env.prod docker-compose.prod.yml +Generate strong values for: +- `DJANGO_SECRET_KEY` +- `POSTGRES_PASSWORD` + +## Production env file +Production should use: +```env +DJANGO_DEBUG=0 +DJANGO_SECURE_COOKIES=1 +DJANGO_SECURE_SSL_REDIRECT=1 ``` -## GitHub Actions secrets -### Development environment +And a real HTTPS hostname in: +- `DJANGO_ALLOWED_HOSTS` +- `DJANGO_CSRF_TRUSTED_ORIGINS` +- `SITE_ADDRESS` + +## Manual test deployment +If you need to deploy manually on the test server: +```bash +cd /opt/workdock +RUN_DJANGO_CHECK=0 DEPLOY_HEALTH_URL="http://127.0.0.1:8088/healthz/" ./scripts/deploy_stack.sh .env.test docker-compose.prod.yml +``` + +Manual production deployment: +```bash +cd /opt/workdock +RUN_DJANGO_CHECK=1 ./scripts/deploy_stack.sh .env.prod docker-compose.prod.yml +``` + +## GitHub Actions workflows +### Test deployment workflow +File: +- [deploy-test.yml](/Users/bostame/Documents/workdock-platform/.github/workflows/deploy-test.yml) + +Behavior: +- triggers on push to `develop` +- can also be run manually with `workflow_dispatch` +- checks out the repo in GitHub Actions +- uploads the working tree to the server over SSH +- runs the server deployment script + +### Production deployment workflow +File: +- [deploy-prod.yml](/Users/bostame/Documents/workdock-platform/.github/workflows/deploy-prod.yml) + +Behavior: +- manual only +- uploads the working tree to the production server +- runs the production deployment script + +## GitHub environment setup +In GitHub: +1. open repository settings +2. open `Environments` +3. create: + - `development` + - `production` + +### Development environment secrets +Add: - `TEST_DEPLOY_HOST` - `TEST_DEPLOY_USER` -- `TEST_DEPLOY_SSH_KEY` - `TEST_DEPLOY_PORT` - `TEST_DEPLOY_PATH` +- `TEST_DEPLOY_SSH_KEY` -### Production environment +Current test values: +- `TEST_DEPLOY_HOST=192.168.2.55` +- `TEST_DEPLOY_USER=root` +- `TEST_DEPLOY_PORT=22` +- `TEST_DEPLOY_PATH=/opt/workdock` +- `TEST_DEPLOY_SSH_KEY=` + +### Production environment secrets +Add: - `PROD_DEPLOY_HOST` - `PROD_DEPLOY_USER` -- `PROD_DEPLOY_SSH_KEY` - `PROD_DEPLOY_PORT` - `PROD_DEPLOY_PATH` +- `PROD_DEPLOY_SSH_KEY` -## Important note for the test server -`.env.test.example` is intentionally configured for an HTTP LAN test deployment. -That means `RUN_DJANGO_CHECK=0` is used in the test deploy workflow, because the application security checks require secure cookies when `DEBUG=0`. -For real production, use `.env.prod` behind HTTPS and keep `RUN_DJANGO_CHECK=1`. +## How the CI/CD test deploy works +### Normal flow +1. push code to `develop` +2. GitHub Actions runs `Deploy Test` +3. workflow uploads repository contents to `/opt/workdock` +4. server keeps its local `.env.test` +5. `deploy_stack.sh` rebuilds and restarts the stack +6. workflow succeeds only after `/healthz/` is healthy + +### Manual trigger +From GitHub Actions: +1. open `Deploy Test` +2. click `Run workflow` + +## How to validate a deployment +### From your machine +```bash +curl -I http://192.168.2.55:8088/healthz/ +``` + +### On the server +```bash +cd /opt/workdock +docker compose --env-file .env.test -f docker-compose.prod.yml ps +docker compose --env-file .env.test -f docker-compose.prod.yml logs --tail=100 web +docker compose --env-file .env.test -f docker-compose.prod.yml logs --tail=100 worker +docker compose --env-file .env.test -f docker-compose.prod.yml logs --tail=100 caddy +``` + +## Rollback +This deployment path is source-upload based, not image-tag based. + +Rollback options: +1. revert the bad commit on `develop` and let GitHub Actions deploy again +2. manually re-upload a previous working checkout and rerun `deploy_stack.sh` + +For production, you may later want image-tag based rollback. That is not necessary yet for the test box. + +## Operational notes +- server-local env files must survive deployments +- do not store `.env.test` or `.env.prod` in Git +- test deployment is intentionally weaker than production on transport security +- production should not reuse the test env model + +## Current known-good state +Validated manually: +- repository pushed to private GitHub +- server bootstrap completed +- test stack deployed successfully +- health check reachable at: + - `http://192.168.2.55:8088/healthz/`