Browse documentation

Documentation overview

Tutorial

Reference

Project

Keep it running

The app works in your terminal. Now comes the traditional next step: making it work when your terminal is closed.

Run the notebook under systemd

This Linux example assumes the repository is deployed at /srv/wred, dependencies are installed in /srv/wred/.venv, and a dedicated wred user and group can read the application. Create those resources before enabling the service. Run behind either proxy from the previous chapters.

Put this unit at /etc/systemd/system/wred.service. Its ExecStart uses Weft’s explicit loopback forwarding trust.

wred.serviceDownload
[Unit]
Description=Wred notebook
After=network-online.target
Wants=network-online.target

[Service]
Type=exec
User=wred
Group=wred
WorkingDirectory=/srv/wred
EnvironmentFile=/etc/wred/notebook.env
Environment=PYTHONUNBUFFERED=1
ExecStart=/srv/wred/.venv/bin/weft example_app.asgi:app --host 127.0.0.1 --port 8000 --workers 2 --lifespan on --forwarded-allow-ips 127.0.0.1
Restart=on-failure
RestartSec=3
TimeoutStopSec=45
NoNewPrivileges=true
PrivateTmp=true

[Install]
WantedBy=multi-user.target

Restart=on-failure restarts a failed process. Systemd supervises it; it doesn’t check whether your database credentials are good or your application is emotionally ready. See systemd.service.

Keep environment settings outside the checkout

Create /etc/wred/notebook.env with your database settings. The values below target the local development database; replace them for your deployment. Restrict access if the URI contains credentials.

/etc/wred/notebook.env
MONGODB_URI=mongodb://127.0.0.1:27017
MONGODB_DATABASE=wred_notebook
Terminal
sudo systemctl daemon-reload
sudo systemctl enable --now wred
sudo systemctl status wred
sudo journalctl -u wred -f

Keep production credentials out of Git and shell history. The service manager reads the environment file; this example does not require a dotenv package.

Choose workers with the database in mind

The unit starts two workers as an example. Each worker gets its own MongoDB client and connection pool. More workers mean more pools and separate in-memory rate-limit buckets. Benchmark your workload, including database calls and middleware, before multiplying workers because the CPU graph had room for more colors.

Keep MongoDB reachable and authenticated appropriately, back up the data, and verify that you can restore it. A volume is persistence; it is not a backup with a more reassuring name.

The notebook’s health endpoint returns a simple application response. It does not ping MongoDB on every request. Use startup checks, database monitoring, and a deliberately designed readiness check for your own deployment.

When it doesn’t work

SymptomCheck first
502 from the proxyIs the backend running on the same host and port? Check the service log and request the backend’s health endpoint locally.
Startup failureCan the service reach MongoDB? Did its virtualenv install PyMongo and ServeStatic? Is the working directory the repository root?
403 at login or refreshThe custom header, same-origin browser request, public Host, and forwarded HTTPS scheme.
401 on note requestsThe access token, its expiry, and the login’s existence. The refresh cookie alone does not authorize CRUD routes.
Static page instead of JSONA static catch-all or SPA fallback caught an API path. Put API routing first and inspect prefix stripping.
Secure cookie missing over HTTPSThe ASGI scheme is probably still HTTP. Fix trusted proxy handling.
429 changes between requestsThe notebook limiter is per worker. Use a shared limiter for a shared policy.
SSE arrives all at onceClient and proxy buffering. Use curl -N and inspect the streaming location.
Different frontend port failsIt is a different origin. Use a development proxy or design CORS and auth together.

Make it your application

Add routes in a module that exposes register_routes(app, ...). Pass database and service objects explicitly, as the notebook does. Put shared HTTP behavior in middleware and resource lifecycle in the outer app.

For blocking libraries, use an appropriate worker thread or replace them with an async client. Don’t start untracked tasks inside request handlers and call it a job queue. Durable work needs a durable queue and workers that own it.

You now have routes, data, auth, a frontend, middleware, HTTPS, and a supervised process. Wred is still small. Your app was allowed to grow without filing a feature request.