docs: document deployment and ci cd process

This commit is contained in:
Md Bayazid Bostame
2026-03-28 23:12:55 +01:00
parent 06377eb335
commit b9ce149a7e

View File

@@ -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/<CTID>.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 <CTID>
```
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=<private key that can ssh to root@192.168.2.55>`
### 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/`