Qlockify Deployment

Main deployment and operations repository for Qlockify.

This repo is the entrypoint for running the full product stack in production.

  • Deployment repository declared by origin: https://git.amiirkhl.ir/Qlockify/qlockify-core-deployment.git
  • Backend repository declared by its origin: https://git.amiirkhl.ir/Qlockify/qlockify-backend-deployment.git
  • Frontend repository declared by its origin: https://git.amiirkhl.ir/Qlockify/qlockify-frontend-deployment.git

Use this repo for:

  • Docker Compose orchestration
  • Nginx
  • SSL certificate mounting
  • domain routing
  • environment layout
  • production service startup

Use the backend and frontend repos for application-level implementation details.

What This Repo Contains

  • docker-compose.yml
  • Nginx config
  • Postgres support files
  • Dockerfiles for production images
  • deployment environment samples
  • container networking and volume wiring

Architecture

Main deployed services:

  • nginx
  • frontend
  • backend
  • celery
  • celery-beat
  • redis
  • db

Traffic pattern:

  • qlockify.ir serves the frontend
  • backend traffic is served from qlockify.ir under /api and /admin
  • Nginx terminates TLS and proxies requests to the frontend and backend containers

Expected Repository Layout

Docker builds read from nested application directories inside this repository:

  • ./backend/qlockify-backend-deployment
  • ./frontend/qlockify-frontend-deployment

Expected layout:

qlockify-deployment/
  backend/
    Dockerfile
    .env.sample
    qlockify-backend-deployment/
  frontend/
    Dockerfile
    .env.sample
    qlockify-frontend-deployment/
  nginx/
  postgres/
  docker-compose.yml

Deployment Flow

1. Place application source

Put the app repos into:

  • ./backend/qlockify-backend-deployment
  • ./frontend/qlockify-frontend-deployment

2. Configure env files

Create and fill:

  • ./.env
  • ./backend/qlockify-backend-deployment/.env
  • ./frontend/qlockify-frontend-deployment/.env

3. Build and run

docker compose up -d --build

The backend container runs:

  • database migrations
  • collectstatic
  • Gunicorn startup

Domain and Routing

Configured domains:

  • qlockify.ir
  • www.qlockify.ir

Behavior:

  • www.qlockify.ir redirects to qlockify.ir
  • http redirects to https
  • frontend is served from qlockify.ir
  • backend traffic is proxied from qlockify.ir/api and qlockify.ir/admin

Before production startup:

  1. Point DNS records for qlockify.ir and www.qlockify.ir to the server.
  2. Make sure 80 and 443 are open on the server firewall.
  3. Make sure the TLS certificate covers all required names.

SSL Certificates

Place certificate files here:

./nginx/certs/fullchain.pem
./nginx/certs/privkey.pem

The repository intentionally keeps only:

  • ./nginx/certs/.gitkeep

Real certificate files are ignored by git.

Required Backend Environment

Set these in:

./backend/qlockify-backend-deployment/.env

Core production values:

  • DJANGO_ALLOWED_HOSTS=qlockify.ir,www.qlockify.ir
  • CORS_ALLOWED_ORIGINS=https://qlockify.ir,https://www.qlockify.ir
  • CSRF_TRUSTED_ORIGINS=https://qlockify.ir,https://www.qlockify.ir
  • BASE_URL=https://qlockify.ir
  • POSTGRES_HOST=db
  • REDIS_HOST=redis
  • REDIS_URL=redis://redis:6379/0
  • CELERY_BROKER_URL=redis://redis:6379/0
  • CELERY_RESULT_BACKEND=redis://redis:6379/1

Google OAuth values:

  • GOOGLE_OAUTH_CLIENT_ID=...
  • GOOGLE_OAUTH_CLIENT_SECRET=...
  • GOOGLE_OAUTH_REDIRECT_URI=https://qlockify.ir/api/users/oauth/google/callback/
  • GOOGLE_OAUTH_FRONTEND_CALLBACK_URL=https://qlockify.ir/auth/google/callback

Required Frontend Environment

Set this in:

./frontend/qlockify-frontend-deployment/.env
VITE_API_BASE_URL=/api

Background Workers

This stack includes:

  • celery for async jobs
  • celery-beat for scheduled jobs

If background scheduling stops working, inspect:

docker compose logs -f celery
docker compose logs -f celery-beat

Notifications and SSE

Notifications use Server-Sent Events at /api/notifications/stream/.

Current behavior:

  • Nginx disables buffering for the SSE endpoint
  • Gunicorn is tuned to tolerate connected streams for current traffic
  • if concurrency grows materially, move SSE to async workers or a dedicated ASGI service

Useful Operations

Build/rebuild:

docker compose up -d --build

Restart a subset:

docker compose up -d --build nginx backend frontend

Inspect running services:

docker compose ps

Follow logs:

docker compose logs -f nginx
docker compose logs -f backend
docker compose logs -f celery
docker compose logs -f celery-beat

Stop everything:

docker compose down

Backup the deployed data:

chmod +x ./scripts/backup.sh
./scripts/backup.sh

By default, backup archives are written to ./backups/. Each archive contains:

  • PostgreSQL data from the db service
  • media files from the Docker media volume
  • .env files from the deployment, backend, and frontend projects

Restore a backup archive on another machine:

chmod +x ./scripts/restore.sh
./scripts/restore.sh ./backups/qlockify-backup-YYYYMMDD-HHMMSS.tar.gz
docker compose up -d --build

Restore is destructive for the database by default. To restore only part of an archive, use:

RESTORE_SKIP_DB=1 ./scripts/restore.sh ./backups/qlockify-backup-YYYYMMDD-HHMMSS.tar.gz
RESTORE_SKIP_MEDIA=1 ./scripts/restore.sh ./backups/qlockify-backup-YYYYMMDD-HHMMSS.tar.gz
RESTORE_SKIP_ENV=1 ./scripts/restore.sh ./backups/qlockify-backup-YYYYMMDD-HHMMSS.tar.gz

CI/CD with Gitea Actions

This repository now ships with a Gitea Actions deployment workflow in:

  • .gitea/workflows/deploy.yml

The backend and frontend repositories each ship with their own workflow files:

  • backend: .gitea/workflows/backend.yml
  • frontend: .gitea/workflows/frontend.yml

Deployment behavior:

  • backend repo push to main: runs backend CI, then updates the backend checkout on the server and rebuilds backend, celery, and celery-beat
  • frontend repo push to main: runs frontend lint/build, then updates the frontend checkout on the server and rebuilds frontend
  • deployment repo push to main: validates deployment files, then updates the deployment checkout on the server and rebuilds nginx plus the app services

The remote deploy entrypoint is:

  • ./scripts/deploy.sh

One-Time Server Bootstrap

Before Actions can deploy automatically, make sure the server is prepared once.

  1. Clone all three repositories on the server into the expected layout:
~/qlockify-deployment
~/qlockify-deployment/backend/qlockify-backend-deployment
~/qlockify-deployment/frontend/qlockify-frontend-deployment
  1. Make sure the server can git fetch all three repositories non-interactively.

Recommended approach:

  • add a deploy SSH key on the server
  • add the public key to Gitea as a deploy key or a machine-user SSH key
  • switch the server-side git remotes to SSH URLs
  1. Pull the latest deployment repo once so the server has scripts/deploy.sh.

  2. Make the deploy script executable:

chmod +x ~/qlockify-deployment/scripts/deploy.sh
  1. Make sure the deploy user can run Docker Compose on the server.

Gitea Runner Setup

Gitea Actions requires a trusted runner. Gitea's official docs describe the runner and label model here:

  • Actions overview: https://docs.gitea.com/usage/actions/overview
  • Act Runner: https://docs.gitea.com/usage/actions/act-runner

Recommended label setup for this project:

qlockify-python:docker://python:3.14-bookworm
qlockify-node:docker://node:22-bookworm
qlockify-deploy:docker://ubuntu:24.04

Example non-interactive runner registration:

./act_runner register \
  --no-interactive \
  --instance https://git.amiirkhl.ir \
  --token <runner_registration_token> \
  --name qlockify-runner \
  --labels "qlockify-python:docker://python:3.14-bookworm,qlockify-node:docker://node:22-bookworm,qlockify-deploy:docker://ubuntu:24.04"

Then start the runner daemon:

./act_runner daemon

Gitea Secrets

Create these Actions secrets either at the Qlockify organization level or per repository.

  • SSH_PRIVATE_KEY
    • private key used by the workflow to SSH into the deployment server
  • SSH_KNOWN_HOSTS
    • output of:
ssh-keyscan -H <your-server-hostname-or-ip>

Do not create GITEA_TOKEN manually. Gitea provides a built-in job token and exposes it as ${{ secrets.GITEA_TOKEN }}. See:

  • https://docs.gitea.com/usage/actions/token-permissions

Gitea Variables

Create these Actions variables in the Gitea UI:

  • DEPLOY_HOST
  • DEPLOY_PORT
  • DEPLOY_USER
  • DEPLOY_PATH
  • DEPLOY_BRANCH
  • BACKEND_BRANCH
  • FRONTEND_BRANCH

Suggested values for your current server layout:

DEPLOY_HOST=h9arjloaye
DEPLOY_PORT=22
DEPLOY_USER=ubuntu
DEPLOY_PATH=/home/ubuntu/qlockify-deployment
DEPLOY_BRANCH=main
BACKEND_BRANCH=main
FRONTEND_BRANCH=main

Gitea variables are available in workflows through ${{ vars.NAME }}. Gitea documents that here:

  • https://docs.gitea.com/usage/actions/actions-variables

If all three repositories live under the same Qlockify organization, the cleanest setup is:

  1. Add one organization-level runner to Qlockify
  2. Add organization-level variables for the shared deploy target
  3. Add organization-level secrets for the SSH key and known hosts

This keeps the three repositories consistent and avoids copying the same values three times.

First Deploy Check

After creating the runner, variables, and secrets:

  1. push the deployment repository first
  2. confirm the deployment workflow succeeds
  3. then push backend or frontend changes and confirm their repo-specific workflows deploy only the affected services

If a workflow fails on the server step, first check:

  • runner logs
  • repository Actions logs
  • docker compose logs -f backend
  • docker compose logs -f frontend
  • docker compose logs -f celery
  • docker compose logs -f celery-beat

Scope Boundary

This repo should document:

  • infrastructure
  • runtime topology
  • domains
  • Nginx
  • Docker Compose
  • SSL
  • operational startup and troubleshooting

It should not duplicate the application-specific implementation details already documented in the backend and frontend repositories.

Description
No description provided
Readme 98 KiB
Languages
Shell 90.2%
Dockerfile 9.8%