--- since: 1.2.1 --- # **DjangoPlay — Deployment & CI/CD Architecture** --- ## **1. Overview** DjangoPlay uses **GitLab CI/CD as the primary source-control and deployment automation platform**. 1. **DjangoPlay web application** — GitLab CI → manual production deployment over SSH → production GCP VM. 2. **Python/PyPI libraries** — GitLab CI → test → build → version check → publish to PyPI. 3. **GitHub is a mirror**, not the primary CI/CD source. GitLab triggers GitHub Actions after changes to `main`. Production deployment is intentionally simple: - GitLab hosts the primary repository. - GitLab CI/CD controls the deployment pipeline. - Production deployment is initiated manually from the `main` branch. - GitLab connects to the production GCP VM over SSH. - The production server pulls the latest `main` branch. - Python dependencies are installed. - Django migrations are applied. - Static files are collected. - DjangoPlay and Celery services are restarted. GitHub is maintained as a **repository mirror** and is not the primary deployment platform. --- ## **2. Deployment Architecture** The current production delivery architecture is: ```mermaid flowchart LR DEV["Developer"] GITLAB["GitLab
Primary Repository"] CI["GitLab CI/CD
Pipeline Orchestration"] SSH["SSH Deployment
CI → Production VM"] VM["GCP Production VM"] NGINX["Nginx"] GUNICORN["Gunicorn"] DJANGO["DjangoPlay"] CELERY["Celery"] REDIS["Redis"] POSTGRES["PostgreSQL"] AUTHX["AuthX"] DEV --> GITLAB GITLAB --> CI CI --> SSH SSH --> VM VM --> NGINX NGINX --> GUNICORN GUNICORN --> DJANGO DJANGO --> POSTGRES DJANGO --> REDIS REDIS --> CELERY DJANGO --> AUTHX classDef source fill:#fceee8,stroke:#d65a2a,stroke-width:2px,color:#7d321c classDef ci fill:#e6f1fb,stroke:#2474b5,stroke-width:2px,color:#174d7a classDef infra fill:#e5f6f1,stroke:#159a7a,stroke-width:2px,color:#075d4b classDef app fill:#f5f3ed,stroke:#777,stroke-width:2px,color:#444 classDef auth fill:#f3eafa,stroke:#7b4bb7,stroke-width:2px,color:#542d82 class DEV,GITLAB source class CI,SSH ci class VM,NGINX,GUNICORN,DJANGO,CELERY,REDIS,POSTGRES infra class AUTHX auth ```` The deployment path is deliberately direct. There is currently no Kubernetes cluster, container orchestration platform, or cloud-native deployment controller in the production path. --- # **3. Source Control and Repository Strategy** GitLab is the **authoritative repository** for DjangoPlay. The normal development flow is: ```text Developer │ ▼ GitLab │ ▼ main │ ├──────────────► Production Deployment │ └──────────────► GitHub Mirror ``` The production deployment pipeline operates from the GitLab repository. GitHub receives a mirror of the repository for redundancy, visibility, and repository availability. GitHub is therefore **not the source of truth for production deployment**. --- ## **3.1 GitHub Mirror Architecture** After changes reach `main`, GitLab triggers a GitHub repository-dispatch event. ```mermaid flowchart LR GITLAB["GitLab
Primary Repository"] CI["GitLab CI
github-mirror.yml"] DISPATCH["GitHub
repository_dispatch"] ACTION["GitHub Actions
Mirror Workflow"] GITHUB["GitHub Repository
Mirror"] GITLAB --> CI CI --> DISPATCH DISPATCH --> ACTION ACTION --> GITHUB classDef primary fill:#fceee8,stroke:#d65a2a,stroke-width:2px,color:#7d321c classDef ci fill:#e6f1fb,stroke:#2474b5,stroke-width:2px,color:#174d7a classDef mirror fill:#f5f3ed,stroke:#777,stroke-width:2px,color:#444 class GITLAB primary class CI,DISPATCH,ACTION ci class GITHUB mirror ``` The GitHub workflow performs a mirror operation using: * GitLab access token * GitHub access token * GitLab repository path The mirror pushes branches and tags and prunes removed branches. Internal GitLab references are intentionally not mirrored. --- # **4. DjangoPlay Web Application Deployment** The DjangoPlay web application uses a **manual production deployment** pipeline. The deployment job runs only for the `main` branch and is configured as a manual GitLab CI job. This provides an explicit deployment gate between a successful merge to `main` and production rollout. --- ## **4.1 Production Deployment Flow** ```mermaid flowchart TD MAIN["GitLab main
Latest application code"] PIPELINE["GitLab CI/CD"] MANUAL["Manual Deploy
deploy_production"] SSH["SSH
Production server"] UPDATE["Update application
checkout + pull main"] DEPS["Install dependencies
pip install -e .[dev]
djangoplay-cli
"] MIGRATE["Django migrations
manage.py migrate --noinput"] STATIC["Collect static files
collectstatic --noinput"] RESTART["Restart services
djangoplay + djangoplay-celery"] LIVE["Production
app.djangoplay.org"] MAIN --> PIPELINE PIPELINE --> MANUAL MANUAL --> SSH SSH --> UPDATE UPDATE --> DEPS DEPS --> MIGRATE MIGRATE --> STATIC STATIC --> RESTART RESTART --> LIVE classDef source fill:#fceee8,stroke:#d65a2a,stroke-width:2px,color:#7d321c classDef ci fill:#e6f1fb,stroke:#2474b5,stroke-width:2px,color:#174d7a classDef deploy fill:#e5f6f1,stroke:#159a7a,stroke-width:2px,color:#075d4b classDef production fill:#f3eafa,stroke:#7b4bb7,stroke-width:2px,color:#542d82 class MAIN source class PIPELINE,MANUAL,SSH ci class UPDATE,DEPS,MIGRATE,STATIC,RESTART deploy class LIVE production ``` --- # **5. Production Deployment Steps** The deployment job performs the following operations. ## **5.1 Connect to Production** GitLab CI starts an SSH agent and loads the deployment private key. The production host is obtained from the GitLab CI/CD variable: ```text SERVER_HOST ``` The server's SSH host key is added to the CI runner's `known_hosts`. The deployment connection uses the production deployment account and then uses `sudo` for operations performed as the `djangoplay` system user. --- ## **5.2 Determine the Application Directory** The production configuration is stored under: ```text /opt/djangoplay/.dplay/config.yaml ``` The deployment process reads the configured: ```text webapp_dir ``` value from that configuration. This is important because the production `.dplay` configuration is owned by the `djangoplay` system user and is **not assumed to exist under the SSH user's home directory**. --- ## **5.3 Update the Application** The deployment process switches to the configured application directory and updates the production checkout: ```bash git checkout main git stash git pull origin main ``` The production application therefore follows the GitLab `main` branch. --- ## **5.4 Install Dependencies** The deployment environment activates the application's virtual environment: ```text .venv ``` The deployment then installs: ```bash pip install -e '.[dev]' pip install djangoplay-cli ``` The editable installation ensures that the deployed Django application uses the checked-out source tree. --- ## **5.5 Run Database Migrations** Django migrations are executed automatically during deployment: ```bash python manage.py migrate --noinput ``` Database schema changes are therefore applied before the application services are restarted. --- ## **5.6 Collect Static Files** Production static files are collected using: ```bash python manage.py collectstatic --noinput ``` The deployment does not require a separate manual static-file collection step. --- ## **5.7 Restart Application Services** The deployment completes by restarting: ```text djangoplay djangoplay-celery ``` The first service runs the Django web application. The second service runs the Celery worker. The deployment therefore updates both synchronous web processing and background task processing together. --- # **6. Production Runtime Architecture** The deployment pipeline ultimately updates the following production runtime: ```mermaid flowchart TD CF["Cloudflare
DNS / Edge"] NGINX["Nginx
Reverse Proxy / TLS"] GUNICORN["Gunicorn
WSGI Server"] DJANGO["DjangoPlay
Web Application"] CELERY["Celery Worker
Background Processing"] REDIS["Redis
Cache / Broker"] POSTGRES["PostgreSQL
Application Database"] AUTHX["AuthX
Identity Service"] EXTERNAL["External Services
Email • AI • APIs • R2"] CF --> NGINX NGINX --> GUNICORN GUNICORN --> DJANGO DJANGO --> POSTGRES DJANGO --> REDIS REDIS --> CELERY DJANGO --> AUTHX DJANGO --> EXTERNAL CELERY --> EXTERNAL classDef edge fill:#fceee8,stroke:#d65a2a,stroke-width:2px,color:#7d321c classDef app fill:#e6f1fb,stroke:#2474b5,stroke-width:2px,color:#174d7a classDef infra fill:#e5f6f1,stroke:#159a7a,stroke-width:2px,color:#075d4b classDef auth fill:#f3eafa,stroke:#7b4bb7,stroke-width:2px,color:#542d82 classDef external fill:#eeeeff,stroke:#5a55c9,stroke-width:2px,color:#403b91 class CF edge class NGINX,GUNICORN,DJANGO,CELERY app class REDIS,POSTGRES infra class AUTHX auth class EXTERNAL external ``` --- # **7. Production Server Services** The current production VM runs the core DjangoPlay services directly. | Service | Role | | ------------------- | ----------------------------------- | | `nginx` | Reverse proxy and TLS termination | | `djangoplay` | Django/Gunicorn application service | | `djangoplay-celery` | Celery background worker | | Redis | Cache and Celery broker | | PostgreSQL | Primary application database | | AuthX | Identity and authentication service | The application and worker are restarted together during deployment. --- # **8. Production Configuration** Production configuration is maintained outside the Git repository. The deployment server uses the DjangoPlay configuration structure under: ```text /opt/djangoplay/.dplay/ ``` The primary configuration includes: ```text config.yaml .secrets .authx .appdata ``` The exact configuration keys and their requirement classification are documented separately in the environment configuration reference. The deployment documentation should therefore describe **how configuration participates in deployment**, rather than duplicating the complete configuration reference. --- # **9. Deployment Secrets** Deployment credentials are stored as GitLab CI/CD variables rather than being committed to the repository. The deployment pipeline uses credentials such as: | Variable | Purpose | | ----------------- | -------------------------------- | | `SSH_PRIVATE_KEY` | SSH authentication to production | | `SERVER_HOST` | Production server hostname | | `GH_TOKEN` | GitHub mirror trigger | The exact GitLab project/group variable configuration is an operational concern and should be managed through GitLab CI/CD settings. Private keys, API tokens, passwords, and service credentials must never be committed to source control. --- # **10. Deployment Environment** The current production environment is intentionally lightweight. ```text Cloudflare │ ▼ GCP e2-micro VM │ ├── Nginx ├── Gunicorn / DjangoPlay ├── Celery ├── Redis ├── PostgreSQL └── AuthX ``` The small VM is an intentional operational constraint. The current deployment architecture prioritizes: * Low operational overhead * Simple administration * Predictable deployment * Minimal infrastructure * Direct service management * Easy recovery and troubleshooting --- # **11. Application Deployment vs Library Release** DjangoPlay web application deployment and Python package releases use different CI/CD flows. ## **11.1 DjangoPlay Web Application** ```text GitLab main │ ▼ Manual production deployment │ ▼ SSH → GCP VM │ ▼ Django + Celery restart ``` ## **11.2 Python Libraries** Python libraries use a package-release pipeline: ```text GitLab main │ ▼ Test │ ▼ Build │ ▼ Version Check │ ▼ Publish │ ▼ PyPI ``` This separation is intentional. A library release publishes a Python package, while the DjangoPlay web application deployment updates a running production system. --- # **12. PyPI Release Architecture** The standard Python library pipeline consists of four primary stages: 1. Test 2. Build 3. Version Check 4. Publish The pipeline is reused across Python libraries. ```mermaid flowchart LR SOURCE["GitLab
main branch"] TEST["Test
Ruff + Pytest"] BUILD["Build
Python package artifacts"] VERSION["Version Check
Semver + PyPI comparison"] PUBLISH["Publish
Twine"] PYPI["PyPI
Published package"] SOURCE --> TEST TEST --> BUILD BUILD --> VERSION VERSION --> PUBLISH PUBLISH --> PYPI classDef source fill:#fceee8,stroke:#d65a2a,stroke-width:2px,color:#7d321c classDef pipeline fill:#e6f1fb,stroke:#2474b5,stroke-width:2px,color:#174d7a classDef publish fill:#e5f6f1,stroke:#159a7a,stroke-width:2px,color:#075d4b class SOURCE source class TEST,BUILD,VERSION,PUBLISH pipeline class PYPI publish ``` --- # **13. PyPI Test Stage** The test stage uses a Python 3.11 CI image. It performs: ```bash pip install -e ".[dev]" pip install pytest ruff ``` and then executes: ```bash ruff check . pytest --tb=short ``` The current shared library pipeline treats these checks as non-blocking by using: ```bash ruff check . || true pytest --tb=short || true ``` The stage therefore provides automated validation feedback without currently blocking the subsequent build stage on lint or test failures. This behavior is intentional in the current pipeline and should be revisited if the project adopts strict CI quality gates. --- # **14. PyPI Build Stage** The build stage creates standard Python distribution artifacts: ```bash python -m build ``` The generated artifacts are stored under: ```text dist/ ``` and passed to subsequent jobs as CI artifacts. Typical outputs include: ```text *.whl *.tar.gz ``` --- # **15. PyPI Version Check** Before publishing, the pipeline: 1. Reads the version from `pyproject.toml`. 2. Validates the version format. 3. Queries PyPI for the currently published version. 4. Prevents publishing when the same version already exists. The purpose is to prevent accidental republishing of an already-published package version. If PyPI cannot be reached, the current implementation proceeds rather than blocking the release. --- # **16. PyPI Publishing** The publish job uses Twine: ```bash twine upload dist/* --non-interactive ``` Authentication is supplied through GitLab CI/CD variables: ```text PYPI_USERNAME PYPI_PASSWORD ``` The conventional PyPI token username is: ```text __token__ ``` The PyPI API token is stored as the password value. --- # **17. PyPI Pipeline Trigger Rules** The standard library pipeline supports: * Push to `main` * Manual web-triggered pipelines * API-triggered pipelines * Merge request pipelines Publishing itself is prevented for merge request pipelines. The publish stage runs automatically after the required preceding stages for `main`. This provides a distinction between: ```text Pipeline execution │ ├── Merge Request → validation │ └── main → release eligible ``` --- # **18. GitLab CI Configuration Structure** The CI configuration is intentionally split into reusable files. For the DjangoPlay web application: ```text .gitlab/ └── ci/ ├── github-mirror.yml └── deploy.yml .gitlab-ci.yml ``` The root pipeline includes the individual CI definitions: ```yaml include: - local: '.gitlab/ci/github-mirror.yml' - local: '.gitlab/ci/deploy.yml' ``` For Python libraries, the common structure is: ```text .gitlab/ └── ci/ ├── pypi-release.yml └── github-mirror.yml .gitlab-ci.yml ``` The root CI file defines the stages and includes the reusable pipeline components. --- # **19. Deployment Safety Characteristics** The current deployment process includes several operational safeguards. ### **Manual production gate** Production deployment is explicitly manual: ```yaml rules: - if: '$CI_COMMIT_BRANCH == "main"' when: manual ``` A merge to `main` therefore does not automatically restart production. ### **SSH key-based access** Production deployment uses an SSH private key stored as a protected CI/CD variable. ### **Known-host verification** The deployment runner records the production host key before connecting. ### **Non-interactive migrations** Database migrations run with: ```bash --noinput ``` ### **Service restart** The Django and Celery services are restarted together so that application code and asynchronous workers are updated consistently. --- # **20. Current Deployment Limitations** The current architecture intentionally remains simple, but it has known limitations. ### **Single production host** The production runtime currently uses a single GCP VM. Failure of the VM therefore affects the application. ### **Direct SSH deployment** Deployment is performed through SSH rather than a container orchestration platform or managed deployment service. ### **Single primary database** PostgreSQL is currently the primary application database rather than a multi-node database cluster. ### **Manual production gate** Production deployment requires manual approval. ### **Limited horizontal scaling** The current deployment model is optimized for simplicity rather than multi-instance horizontal scaling. These limitations are architectural constraints of the current deployment, not accidental requirements. --- # **21. Future Deployment Evolution** As DjangoPlay usage grows, the deployment architecture can evolve without changing the application's modular architecture. Possible future improvements include: * Multiple application instances * Managed PostgreSQL * Redis high availability * Dedicated Celery workers * Load balancing * Automated rollback * Containerized deployments * Infrastructure-as-code * Automated health checks * Zero-downtime deployment * Separate staging deployment * Deployment observability The current architecture deliberately does not introduce these components until operational requirements justify their complexity. --- # **22. Deployment and Infrastructure Relationship** Deployment updates the application running on the infrastructure documented in the infrastructure architecture. ```mermaid flowchart TD DEPLOY["GitLab Deployment"] VM["GCP Production VM"] APP["DjangoPlay"] CELERY["Celery"] REDIS["Redis"] DB["PostgreSQL"] AUTHX["AuthX"] DEPLOY --> VM VM --> APP VM --> CELERY VM --> REDIS VM --> DB VM --> AUTHX APP --> DB APP --> REDIS APP --> AUTHX REDIS --> CELERY classDef deploy fill:#e6f1fb,stroke:#2474b5,stroke-width:2px,color:#174d7a classDef vm fill:#e5f6f1,stroke:#159a7a,stroke-width:2px,color:#075d4b classDef auth fill:#f3eafa,stroke:#7b4bb7,stroke-width:2px,color:#542d82 class DEPLOY deploy class VM,APP,CELERY,REDIS,DB vm class AUTHX auth ``` Deployment is therefore an **application delivery mechanism**, while the infrastructure architecture describes the runtime environment in which the application operates. --- # **23. Operational Responsibilities** | Area | Current Responsibility | | ----------------------- | ------------------------ | | Source repository | GitLab | | CI/CD | GitLab CI/CD | | Production approval | Manual GitLab deployment | | Deployment transport | SSH | | Production compute | GCP VM | | Reverse proxy | Nginx | | Application server | Gunicorn | | Web application | DjangoPlay | | Background processing | Celery | | Cache / broker | Redis | | Application database | PostgreSQL | | Identity | AuthX | | Static / asset delivery | Cloudflare / R2 | | Repository mirror | GitHub | | Python package registry | PyPI | --- # **24. Related Documentation** The deployment architecture should be read together with the following documents: | Document | Purpose | | -------------------------------- | ---------------------------------------------- | | `djangoplay-infrastructure.md` | Production infrastructure and runtime topology | | `architecture-style.md` | Overall architectural style | | `app-architecture.md` | Standard Django application architecture | | `authentication-architecture.md` | AuthX and authentication architecture | | `security-architecture.md` | Security boundaries and controls | | `integration-architecture.md` | Internal and external integrations | | `scaling.md` | Scaling strategy and future growth | | `env-configs.md` | Complete environment/configuration reference | The environment configuration reference remains the authoritative source for individual configuration keys and their setup/runtime requirements. --- # **25. Summary** DjangoPlay uses **GitLab CI/CD as its primary delivery platform**. For the Django web application, the current production flow is: ```text GitLab │ ▼ main │ ▼ Manual GitLab Deployment │ ▼ SSH │ ▼ GCP Production VM │ ├── Pull latest main ├── Install dependencies ├── Run migrations ├── Collect static files └── Restart Django + Celery ``` Python libraries use a separate package-release flow: ```text GitLab │ ▼ Test │ ▼ Build │ ▼ Version Check │ ▼ PyPI ``` GitHub is maintained as a mirror through a GitLab-triggered GitHub Actions workflow and is not part of the primary production deployment path. The current deployment architecture favors **simplicity, explicit production control, and low operational overhead**, while leaving room for future horizontal scaling and more automated deployment mechanisms.