NoplagDocs

Production deployment

Putting the engine behind auth, running it under systemd, sizing the box, and updating safely.

The self-hosting guide covers installing and operating the engine. This page is the productionizing recipes: the engine ships with no authentication, so the step between "works on my machine" and "deployed" is mostly about what you put in front of it.

Auth in front: three recipes

Basic auth (nginx) — the fifteen-minute option, right for a small team on a private tool:

server {
    listen 443 ssl;
    server_name engine.internal.example.com;
 
    auth_basic           "Noplag Engine";
    auth_basic_user_file /etc/nginx/htpasswd;
 
    location / {
        proxy_pass http://127.0.0.1:8000;
        proxy_set_header Host $host;
        # SSE progress streams need buffering off
        proxy_buffering off;
        proxy_read_timeout 300s;
    }
}

proxy_buffering off matters: the progress endpoint is a server-sent-events stream, and a buffering proxy makes it appear silent.

oauth2-proxy — SSO in front of the engine (Google Workspace, GitHub, any OIDC provider). Run oauth2-proxy with --upstream http://127.0.0.1:8000 and your provider config; the engine stays unaware of auth entirely.

Caddy — TLS and basic auth in four lines:

engine.internal.example.com {
    basic_auth {
        team {$HASHED_PASSWORD}
    }
    reverse_proxy 127.0.0.1:8000 { flush_interval -1 }
}

Server-to-server — if only your own backend calls the engine, skip the proxy: bind the engine to a private interface (or a Docker network your app shares) and never expose the port.

systemd (bare installs)

[Unit]
Description=Noplag Engine
After=network-online.target postgresql.service
 
[Service]
User=noplag
WorkingDirectory=/opt/noplag-engine
Environment=DATABASE_URL=postgresql+psycopg://noplag:...@localhost:5432/noplag
ExecStart=/opt/noplag-engine/.venv/bin/uvicorn noplag_engine.api.app:app --host 127.0.0.1 --port 8000
Restart=on-failure
 
[Install]
WantedBy=multi-user.target

Bind to 127.0.0.1 and let the reverse proxy own the public interface.

Sizing

  • CPU first. Alignment dominates a check's wall clock and spreads over a process pool sized to the machine — cores buy throughput more than RAM does.
  • RAM goes to Postgres. Follow normal Postgres sizing (shared_buffers ~25% of RAM) rather than giving it to the app process.
  • Disk: expect roughly 1.5–2× the plain-text size of your corpus in Postgres, and put Postgres on real SSD/NVMe — the GIN index workload is unforgiving on slow volumes.
  • A 4-core box with 8–16 GB RAM handles a small-team workload with a multi-million-chunk corpus comfortably. Scale cores with concurrent checks.

Health checks and monitoring

  • GET /health does no DB call — use it for liveness probes and uptime monitors.
  • For a deeper signal, run a tiny canary check on a schedule and alert on failures or duration regressions; that exercises DB, retrieval, and alignment in one probe.

Updating

git pull
pip install -e .            # or rebuild the image
alembic upgrade head
systemctl restart noplag-engine

Migrations are additive and safe to run before restarting. Read the release notes before jumping versions: fingerprint-parameter changes invalidate the corpus and are always called out explicitly — see Upgrading the engine.

Backups

Everything lives in Postgres; standard Postgres backup practice applies. After a restore, run ANALYZE before serving checks — see the self-hosting guide.

On this page