Create Qcalc Prod Server on Docker
qCalc — Docker, Ubuntu Linux, VPS: Installation Guide
This guide covers a Docker-based deployment of qCalc on an Ubuntu VPS.
All services (qCalc/Gunicorn, Nginx, Certbot, PostgreSQL, Memcached) run as Docker containers defined in setup/docker/template_docker.yml.
Prerequisites
- Ubuntu 22.04 LTS or 24.04 LTS VPS with root / sudo access
- A registered domain name pointed at the VPS IP address
- SSH access to the server
1. Initial Linux Server Setup
Follow Initial Linux Server Setup if you do not have a user account in linux
2. Install Docker Engine and Docker Compose
Following instruction set is for Ubuntu 26.04 LTS.
sudo apt update
sudo apt install -y ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg \
-o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] \
https://download.docker.com/linux/ubuntu \
$(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}") stable" \
| sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt update
sudo apt install -y \
docker-ce \
docker-ce-cli \
containerd.io \
docker-buildx-plugin \
docker-compose-plugin
sudo systemctl enable --now docker
sudo usermod -aG docker ${USER}
newgrp docker
docker info
docker compose version
3. Clone the qCalc Repository from Git
cd ~
git clone https://github.com/qcalc/qcalc.git qcalc_dock
4. Create the Project Directory Structure
mkdir -p ~/qcalc_dock/.local/nginx/templates
mkdir -p ~/qcalc_dock/.local/nginx/conf
mkdir -p ~/qcalc_dock/.local/certbot/conf
mkdir -p ~/qcalc_dock/.local/certbot/www
mkdir -p ~/qcalc_dock/.local/log/nginx
mkdir -p ~/qcalc_dock/.local/log/gunicorn
mkdir -p ~/qcalc_dock/.local/log/certbot
mkdir -p ~/qcalc_dock/.temp
mkdir -p ~/qcalc_dock/qcalc/.setup
5. Set Up the Docker Files
The compose template is at qcalc/setup/docker/template_docker.yml. Copy it one level up as docker-compose.yml:
cp ~/qcalc_dock/qcalc/setup/docker/template_docker_compose.yml ~/qcalc_dock/docker-compose.yml
cp ~/qcalc_dock/qcalc/setup/docker/template_dockerfile ~/qcalc_dock/Dockerfile
cp ~/qcalc_dock/qcalc/setup/docker/.dockerignore ~/qcalc_dock/.dockerignore
The compose file uses
~/qcalc_dock/.local/paths for all volumes. No edits are required unless you change the installation directory.
6. Set Up Nginx Configuration Files
6a. Main nginx.conf
cp ~/qcalc_dock/qcalc/setup/nginx/template_nginx.conf-v1.4j.conf \
~/qcalc_dock/.local/nginx/nginx.conf
6b. Initial HTTP-only config (used before the SSL certificate exists)
cp ~/qcalc_dock/qcalc/setup/nginx/template_default.conf.init \
~/qcalc_dock/.local/nginx/conf/default.conf.init
Edit the file and replace <replace_with_your_domain> with your actual domain:
nano ~/qcalc_dock/.local/nginx/conf/default.conf.init
6c. Full HTTPS template (used after the SSL certificate is obtained)
cp ~/qcalc_dock/qcalc/setup/nginx/template_default.conf.template-v1.8j.conf \
~/qcalc_dock/.local/nginx/templates/default.conf.template.off
The
.offsuffix prevents Nginx from processing this template before the SSL certificate exists.${NGINX_HOST}placeholders in this file are automatically replaced by the Nginx Docker container using theNGINX_HOSTenvironment variable set indocker-compose.yml.
7. Configure Environment Files
7a. setup.env
cp ~/qcalc_dock/qcalc/setup/env/template_setup.env ~/qcalc_dock/qcalc/setup.env
nano ~/qcalc_dock/qcalc/setup.env
QCALC_SCHEME='https'
QCALC_DOMAIN="<yourdomain.com>"
QCALC_ENV_FILE=".setup/prod.env"
DJANGO_SETTINGS_MODULE="config.settings.prd"
7b. Production .env file
cp ~/qcalc_dock/qcalc/setup/env/template_prod.env ~/qcalc_dock/qcalc/.setup/prod.env
nano ~/qcalc_dock/qcalc/.setup/prod.env
Key settings to fill in (see setup/env/template_all_env_settings.env for the full reference):
Set the DJANGO_SECRET_KEY value after generating the key using the command mentioned below.
ROBOTS_TXT="robots.prod.txt"
DJANGO_DEBUG="False"
DJANGO_SECRET_KEY="<generate: python -c 'import secrets; print(secrets.token_urlsafe(50))'>"
DB_ENGINE="django.db.backends.postgresql_psycopg2"
DB_NAME="qcalc"
DB_USER="postgres"
DB_PASSWORD="postgres" # matches POSTGRES_PASSWORD in docker-compose.yml
DB_HOST="postgres" # Docker service/container name, not localhost
DB_PORT="5432"
DEFAULT_CACHE_ALIAS="memcached"
MEMCACHE_HOST="memcached" # Docker service/container name
MEMCACHE_PORT="11211"
# Paths inside the qcalc container (must match docker-compose.yml volumes)
FILE_UPLOAD_TEMP_DIR="/usr/src/qcalc_dock/.temp/"
JSON_FILES_DIR="/usr/src/qcalc_dock/qcalc_res/json/"
HELP_FILES_DIR="/usr/src/qcalc_dock/qcalc_res/help/"
DOCS_FILES_DIR="/usr/src/qcalc_dock/qcalc_res/docs/"
AI_MODELS_DIR="/usr/src/qcalc_dock/qcalc_res/model/"
FIXER_API_KEY="<your_fixer_api_key>" # obtain an API key for currency rates update
OPENW_API_KEY="<your_openweather_api_key>" # this is optional
DJANGO_EMAIL_BACKEND="django.core.mail.backends.smtp.EmailBackend"
DJANGO_EMAIL_HOST="<smtp.yourmailprovider.com>"
DJANGO_EMAIL_PORT="587"
DJANGO_EMAIL_USE_TLS="True"
DJANGO_EMAIL_HOST_USER="<you@yourdomain.com>"
DJANGO_EMAIL_HOST_PASSWORD="<your_email_password>"
Important:
DB_HOSTandMEMCACHE_HOSTmust be the Docker container/service names (postgres,memcached), notlocalhost.
7c. gpref.json
cp ~/qcalc_dock/qcalc/setup/env/template_gpref.json ~/qcalc_dock/qcalc/gpref.json
8. Create Docker Named Volumes
docker volume create static
or if you have opted for postgres
docker volume create pgdata
or if you have opted for mysql
docker volume create mysqldata
9. Phase 1 — Start Containers with HTTP-Only Nginx
Activate the HTTP-only Nginx config so Certbot can complete the ACME challenge:
# Uses cp_conf_init.sh logic — copy init config into active default.conf
cp ~/qcalc_dock/.local/nginx/conf/default.conf.init \
~/qcalc_dock/.local/nginx/conf/default.conf
cd ~/qcalc_dock
docker compose up
10. Obtain the SSL Certificate
Run Certbot inside the certbot container to issue the certificate via the webroot method:
docker exec -it certbot certbot certonly \
--webroot --webroot-path=/var/www/certbot \
--email <your_email_id> \
--agree-tos --no-eff-email \
--cert-name <your_domain> \
-d <your_domainm> -d www.<your_domain>
Expected output confirms certificate paths:
Certificate is saved at: /etc/letsencrypt/live/yourdomain.com/fullchain.pem
Key is saved at: /etc/letsencrypt/live/yourdomain.com/privkey.pem
On the host these map to ~/qcalc_dock/.local/certbot/conf/live/yourdomain.com/.
11. Phase 2 — Switch Nginx to HTTPS
Remove the HTTP-only config and activate the full HTTPS template:
mv ~/qcalc_dock/qcalc_res/nginx/conf/default.conf \
~/qcalc_dock/qcalc_res/nginx/conf/default.conf.init
mv ~/qcalc_dock/qcalc_res/nginx/templates/default.conf.template.off \
~/qcalc_dock/qcalc_res/nginx/templates/default.conf.template
cd ~/qcalc_dock
The Nginx container automatically processes default.conf.template, substitutes ${NGINX_HOST} with yourdomain.com, and writes the result to conf.d/default.conf.
Restart the containers to pick up the new config:
cd ~/qcalc_dock
docker compose restart
Open https://<your_domain> in a browser to verify the site is live and the certificate is valid.
12. Post-Installation
- Log in to the Django admin at
https://<your_domain>/admin/with the superuser created duringcollectstatic/migrate(the qCalc container runs these automatically on startup perdocker-compose.yml). The default credentials aresuper/super— change the password immediately. - Replace placeholder API keys in
.setup/prod.env(Fixer.io, OpenWeather, OpenAI, etc.). - Review
robots.prod.txt— copy and rename a template fromqcalc/qsite/static/txt/to matchROBOTS_TXTin your~qcalc_dock/qcalc/.setup/.env file.
cp ~/qcalc_dock/qcalc/qsite/static/txt/template_robots.prod.txt \
~/qcalc_dock/qcalc/qsite/static/txt/robots.prod.txt
You verify it using the url: https://<your_domain>/robots.txt
Day-to-Day Operations
Pull latest code and redeploy
cd ~/qcalc_dock/
git pull
docker compose restart qcalc
Verify static files inside the container
docker exec -it qcalc python manage.py findstatic js/qcalc.js
Run a management command inside the container
docker exec -it qcalc python manage.py <command>
View logs
# Gunicorn
tail -f ~/qcalc_dock/.local/log/gunicorn/error_1.log
# Nginx
tail -f ~/qcalc_dock/.local/log/nginx/access.log
# Container stdout
docker compose logs -f qcalc
docker compose logs -f nginx
Quick Reference — Useful Commands
| Task | Command |
|---|---|
| Start all containers | docker compose up -d |
| Stop all containers | docker compose down |
| Restart a single service | docker compose restart qcalc |
| Rebuild qcalc image | docker compose build qcalc |
| Rebuild and restart | docker compose up -d --build qcalc |
| Reset to HTTP-only nginx | cp_conf_init.sh (or its inline equivalent in step 9) |
| Activate HTTPS nginx | cp_conf_template.sh (or its inline equivalent in step 11) |
| Renew SSL certificate | docker exec -it certbot certbot renew |
| Open a shell in container | docker exec -it qcalc bash |
Directory Layout Summary
~/qcalc_dock/ # qCalc PROJECT dir (Git-managed)
├── docker-compose.yml # Copied from qcalc/setup/docker/template_docker.yml
├── qcalc/ # Django project, qCalc ROOT dir
│ ├── setup.env # Startup configuration (not in Git)
│ ├── .setup/ # not in Git
│ │ └── prod.env # Production secrets (not in Git)
│ └── qsite # qCalc APP dir
├── qcalc_res/ # Resource files (json, help, model)
├── .local/ # not in Git
│ ├── nginx/
│ │ ├── nginx.conf # Nginx main config (host-mounted)
│ │ ├── conf/
│ │ │ ├── default.conf # Active site config (switched per phase)
│ │ │ └── default.conf.init # HTTP-only config (kept as backup)
│ │ └── templates/
│ │ ├── default.conf.template # Active HTTPS template (Phase 2)
│ │ └── default.conf.template.off # Inactive HTTPS template (Phase 1)
│ ├── certbot/
│ │ ├── conf/ # Let's Encrypt certificates
│ │ └── www/ # ACME challenge webroot
│ └── log/
│ ├── gunicorn/
│ ├── nginx/
│ └── certbot/
└── .temp/ # Temporary file uploads, not in Git