Skip to content

Environments

Four environments exist in practice.

1. Local development

Infrastructure in Docker, applications on the host.

Piece How it runs
PostgreSQL backend/docker-compose-dev.yml, published on 5433
RabbitMQ same file, 5672 and 15672
Django python manage.py runserver on 8000
Console pnpm dev on 5001
Landing pnpm dev

The repository-root .env holds only MANAGE_PY_PATH, a developer-specific absolute path used by the VS Code launch configuration. It is not application configuration.

2. Developer / staging

One host serving staging.towitlinks.com, fronted by the host-level nginx configuration in staging.nginx.

Route Upstream
/ 127.0.0.1:3001 — the console container
/api/, /admin/, /twilio/, /google/ 127.0.0.1:8123 — the staging Django container
/rabbitmq/ 127.0.0.1:15672
/static/ /home/vladimir/VoIP/backend/staticfiles/ on the host
/android/ /home/vladimir/build_apk/VoIP/frontend/android/app/build/outputs/ — the APKs

/api/ carries the WebSocket upgrade headers and a proxy_read_timeout of 86400 (staging.nginx:18-26), a leftover from the abandoned Channels work.

The backend runs from backend/docker-compose-staging.yml: postgres:16, the web container on 8123:8000, and Redis as the Channels backend. The console runs from frontend/docker-compose-staging.yml on 3001:80, from Dockerfile.staging, which bakes nginx and frontend_staging.conf into the image.

Staging is refreshed either by rebuild_and_run_staging.sh over SSH, or automatically for the backend when something lands on develop — see Deployment.

3. Production

Host Serves
towitlinks.com, www.towitlinks.com The landing container on port 3000 (landing.nginx:3)
app.towitlinks.com The console SPA and the Django API (frontend/frontend.conf:3)

The production backend compose file (backend/docker-compose.yml) runs web on 8000:8000, celery_worker, celery_beat and rabbitmq. There is no database service — PostgreSQL is external. Container logs ship to a Graylog instance at 154.53.40.104:1514 through the syslog driver, tagged links-backend (:15-18).

Configuration comes from Docker Swarm secrets named TWTLNKPROD_*, loaded by backend/entrypoint.sh:30-98, not from an .env file.

The production console image is unusual: frontend/Dockerfile serves nothing. It copies the built bundle into /built-files, and entrypoint.sh rsyncs it into a /voip-frontend volume and then sleeps. A separate nginx service in the Swarm serves that volume. This means a deployment is a volume refresh, not a container swap, and nginx keeps serving the old files until the rsync finishes.

The control plane is a Docker Swarm stack defined in infrastructure/docker-compose.yml: portainer/agent:2.21.5 as a global service, portainer/portainer-ce:2.21.5 on the manager, and a custom portainer_nginx image built from infrastructure/nginx/Dockerfile, publishing port 80. Portainer itself is reachable at portainer.towithouston.com (infrastructure/nginx/my_custom_conf/portainer.conf:3).

TLS is not in this repository

Every nginx server block in the repository listens on port 80 only. Certificates are handled outside version control — most likely certbot rewriting the live host copies, or an upstream proxy. Confirm with the team before changing any nginx file.

4. Android build host

A dedicated machine at 154.53.40.104, with the repository checked out at ~/build_apk/VoIP. .github/workflows/android.yml SSHes in and runs rebuild_android.sh. Artifacts are served from the staging host under /android/. See Mobile builds.

Other hostnames in the codebase

Host Used for
chat.towitlinks.com Linked from the landing layout (landing/src/routes/+layout.svelte:8)
links.towithouston.com Twilio webhook / WSS domain (backend/.env.example:99-100)
minios3.towithouston.com MinIO / S3 object storage
whisper.towithouston.com Audio transcription service
portainer.towithouston.com Portainer UI

Branching and promotion

main is the default branch and develop is the integration branch. Feature branches use feat/, fix/, ref/, test/ and similar prefixes. Work merges into develop by pull request, and develop into main by pull request.

Promotion to production is manual: the production workflows only push images, and a human triggers the deployment in Portainer. See Deployment.