Installation & Setup Informational Other

SecBoard installation — Linux server from build archive (greenfield)

Back to manual
Deploy *_build.tar.gz on a clean Linux host: Python 3.12 (SOABI/glibc gates), venv, migrate, collectstatic, Gunicorn (SecBoard.wsgi:application) behind nginx, TLS, optional Celery/Redis. Full runbook: DEPLOYMENT_INSTRUCTIONS.md; post-deploy app setup: PROJECT_SETUP_GUIDE.md.

Goal & audience

Mid-level Linux / DevOps engineers: extract the build once, configure .env, run check --deploy, migrate, and collectstatic, run Gunicorn on SecBoard.wsgi:application on loopback behind nginx with TLS, and — where your edition includes them — Celery worker and Celery beat with Redis, all from the same venv, paths, and .env as Gunicorn.

Hardware (indicative)

ResourceMinimum (light load)Production (typical)
CPU2 vCPU4+ vCPU, tune to load
RAM4 GB8+ GB; separate DB host preferred
Disk20 GB free40+ GB; plan for logs and media/
NetworkOutbound HTTPS for pipDNS to this host for TLS (e.g. Let's Encrypt)

Software & network

  • OS: e.g. Ubuntu Server LTS; align glibc with the build host (see gate P8).
  • Python 3.12 + python3.12-venv; nginx; DB client libs per requirements.txt.
  • Database created and reachable; Redis if Celery is used.
  • Artifact: *_build.tar.gz with SHA256 on the change ticket.
sudo apt-get update
sudo apt-get install -y python3.12 python3.12-venv python3.12-dev nginx curl ca-certificates build-essential
# Add libpq-dev OR default-libmysqlclient-dev per requirements.txt

Pre-flight (gates)

#Check
P1python3.12 --version matches the build / release notes.
P1bpython3.12 -c "import sysconfig; print(sysconfig.get_config_var('SOABI'))" (e.g. cpython-312-x86_64-linux-gnu) — record on change ticket.
P1cpython3.12 -c "import sys; print(sys.version)" — full banner for support.
P2Know Cython vs Nuitka for your artifact.
P3Archive path and SHA256 on the change ticket.
P4Database reachable; credentials ready for .env.
P5Sudo or deploy role: install path, /etc/systemd/system/, nginx (and Supervisor only if that is your standard).
P6Outbound HTTPS for pip install -r requirements.txt (or an internal mirror).
P7OS packages as above; DB client libraries per requirements.txt.
P8ldd --version | head -n1 — compare glibc with the build host; older distros may need a matching artifact.
P9Install root exists and is empty before extract, e.g. sudo mkdir -p /srv/SecBoard_yoursite.
P10(If Celery) Redis installed/reachable; REDIS_* in .env; redis.service active before worker and beat.

Architecture (brief)

Client → nginx :443 → Gunicorn (127.0.0.1:PORT) → Django → DB / Redis
                                              ↑
                            Celery worker / beat (if present)

Gunicorn listens on loopback only; expose 80/443 via nginx.

Placeholders

PlaceholderMeaning
/srv/SecBoard_yoursiteInstall root (empty on greenfield).
yoursitePublic DNS hostname.
secboard_yoursite.serviceYour systemd unit name for Gunicorn.
youruser:yourgroupOS user/group for Gunicorn/Celery (venv and manage.py must use the same user).

End-to-end sequence (condensed)

0 prerequisites → 1 stop units (no-op until first install) → 2–3 inspect tar, extract, chown4 venv + pip5 .env + check --deploy6 dirs, migrate, collectstatic, chmod7 optional .so smoke test → 8–11 Gunicorn, systemd ReadWritePaths, nginx, start → 12 TLS after DNS → 13 verification → 14 optional firewall (allow SSH first). Use exactly one process manager for the app: systemd or Supervisor, not both for the same process.

Inspect archive, then extract

Run tar -tzf your_build.tar.gz | head once. The inner top-level folder name often differs from the .tar.gz basename. If there is exactly one top-level directory, use --strip-components=1; if files sit at archive root, omit it.

sudo systemctl stop secboard_yoursite.service 2>/dev/null || true
# If Celery uses separate units, stop them here too.

sudo mkdir -p /srv/SecBoard_yoursite
tar -tzf /path/to/yoursite_build.tar.gz | head
sudo tar -xzf /path/to/yoursite_build.tar.gz -C /srv/SecBoard_yoursite --strip-components=1
sudo chown -R youruser:yourgroup /srv/SecBoard_yoursite

Python ABI gate, venv, Django

cd /srv/SecBoard_yoursite
python3.12 -c "import sys; print(sys.version)"
python3.12 -c "import sysconfig; print(sysconfig.get_config_var('SOABI'))"
ldd --version | head -n1

python3.12 -m venv venv
source venv/bin/activate
pip install --upgrade pip setuptools wheel
pip install -r requirements.txt

cp -n .env.example .env
# Edit .env: DB_*, SECRET_KEY, ALLOWED_HOSTS, CSRF_TRUSTED_ORIGINS
# (include http://127.0.0.1:<gunicorn_port> if admins SSH port-forward to the real --bind port),
# CORS_*, REDIS_*, PUBLIC_BASE_URL, SITE_*, DEBUG=0. Do NOT keep a bundled .env from the build machine.

python3.12 manage.py check --deploy
mkdir -p logs media staticfiles
python3.12 manage.py showmigrations
python3.12 manage.py migrate --noinput
python3.12 manage.py collectstatic --noinput
chmod -R 755 staticfiles media
chmod -R 775 logs

After any .env change: restart Gunicorn and (if used) Celery worker and beatsudo systemctl restart …. Workers read the environment at process start; stale values can cause DisallowedHost and a misleading HTTP 400 with CSRF/session text (see troubleshooting in DEPLOYMENT_INSTRUCTIONS.md).

Optional: compiled build smoke test

cd /srv/SecBoard_yoursite && source venv/bin/activate
ls -la app_conf/*.so 2>/dev/null || ls -la app_conf/
python3.12 -c "from app_conf import models; print('OK:', models.__file__)"

Celery worker, Redis, and Celery beat (editions that include them)

When your edition ships background tasks: Redis running before worker/beat; REDIS_* in .env must match. Prefer separate systemd units (or separate Supervisor programs) for Gunicorn, worker, and beat. Same identity as Gunicorn: User= / Group= / WorkingDirectory=, venv PATH, DJANGO_SETTINGS_MODULE=SecBoard.settings (or as shipped in deploy/). With ProtectSystem=strict, include the install directory in ReadWritePaths= along with logs, media, and staticfiles. Example dependencies: After=network.target redis.service, Wants=redis.service. Many builds use django-celery-beat with --scheduler=django_celery_beat.schedulers:DatabaseScheduler — exact ExecStart is in DEPLOYMENT_INSTRUCTIONS.md and any deploy/*.service samples. After every new tar deploy, restart Celery so code matches Gunicorn.

Gunicorn (WSGI)

Use only SecBoard.wsgi:application (matches SecBoard/wsgi.py; a Nuitka bootstrap .py is normal). Put --bind 127.0.0.1:PORT in systemd or Supervisor; nginx proxy_pass must target that loopback port.

gunicorn SecBoard.wsgi:application \
  --bind 127.0.0.1:8000 \
  --workers 3 \
  --timeout 60 \
  --max-requests 1000 \
  --max-requests-jitter 100

nginx & TLS

Terminate TLS at nginx; forward Host, X-Real-IP, X-Forwarded-For, and X-Forwarded-Proto to Gunicorn. After edits: sudo nginx -t then reload. Obtain certificates only when DNS points to this host (e.g. Certbot).

Post-deploy checks (important)

When Django uses SECURE_SSL_REDIRECT, a naive curl http://127.0.0.1:PORT/… may return 301 to https://127.0.0.1:PORT, which then fails because Gunicorn on that port speaks plain HTTP, not TLS. Prefer probing the app through nginx on 443 from the same host:

curl -fsS --resolve "yoursite:443:127.0.0.1" "https://yoursite/" || exit 1
systemctl is-active secboard_yoursite.service 2>/dev/null || true
tail -n 80 /srv/SecBoard_yoursite/logs/gunicorn_error.log

Replace yoursite and paths with your deployment. Use curl -f / --fail in automation so 4xx/5xx are errors.

Optional firewall

If using ufw, allow SSH (22/tcp) before ufw enable so you are not locked out. Example:

sudo ufw allow 22/tcp
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable

License validation: The SecBoard platform (the host where the product is deployed) must have outbound HTTPS access to https://license.secboard.online/ for license verification, activation, and heartbeats when your edition uses the central license service. If you restrict egress (host firewall, cloud security group, or corporate proxy), allow that endpoint (and any other license URLs in your product docs); otherwise those checks may fail.

Rollback (high level)

Prefer sudo systemctl stop …, then sudo mv /srv/SecBoard_yoursite /srv/SecBoard_yoursite_backup_$(date +%s), sudo mkdir -p /srv/SecBoard_yoursite, and redeploy a known-good archive — avoid destructive rm -rf when data matters. Stop worker/beat with Gunicorn when rolling back so no process writes to a half-replaced tree.


Attachments

No attachments for this article.