VPS and cloud
Run a Linux service with a persistent data directory and HTTPS proxy.
This procedure runs a source build on Linux with systemd and a reverse proxy on the same host. You need administrator access, Node.js 20.20.2, npm 10.8.2, a TLS-capable proxy, and a persistent disk. For containers, use Docker.
Build the app
Clone the repository into /opt/durindoor, then build:
cd /opt/durindoor
npm install --no-audit --no-fund
npm run build
npm run verify:staticThe build creates .next/standalone/custom-server.js. The static check must exit zero before you use this build.
Create the service account and data directory
If the account does not exist, create a system user with no interactive login:
sudo useradd --system --home-dir /var/lib/durindoor --shell /usr/sbin/nologin durindoor
sudo install -d -o durindoor -g durindoor -m 0700 /var/lib/durindoorGive that account read and execute access to /opt/durindoor and your Node executable. Keep /var/lib/durindoor writable by the service account. A permission failure can make the app fall back to its native data-directory default; check logs and the actual data path.
Save the environment
Create /etc/durindoor.env, owned by root with mode 0600. Generate separate JWT_SECRET and API_KEY_SECRET values with openssl rand -hex 32. Insert those values and a password of your own before starting:
NODE_ENV=production
PORT=20128
HOSTNAME=127.0.0.1
DATA_DIR=/var/lib/durindoor
JWT_SECRET=<unique-generated-value>
API_KEY_SECRET=<different-unique-generated-value>
INITIAL_PASSWORD=<your-private-password>
BASE_URL=https://durindoor.example.com
NEXT_PUBLIC_BASE_URL=https://durindoor.example.com
AUTH_COOKIE_SECURE=trueReplace the example origin with your real HTTPS origin. Keep both base URLs consistent. The app removes forwarded host and scheme headers, so those headers do not replace this configuration.
Install and start the unit
Save /etc/systemd/system/durindoor.service:
[Unit]
Description=DurinDoor
After=network.target
[Service]
Type=simple
User=durindoor
Group=durindoor
WorkingDirectory=/opt/durindoor
EnvironmentFile=/etc/durindoor.env
ExecStart=/usr/bin/node /opt/durindoor/.next/standalone/custom-server.js
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.targetUse the absolute path from command -v node if it differs from /usr/bin/node.
sudo systemctl daemon-reload
sudo systemctl enable --now durindoor
curl -fsS http://127.0.0.1:20128/api/healthExpect {"ok":true}. Read failures with journalctl -u durindoor -n 100.
Add TLS and verify access
Configure nginx or Caddy to proxy HTTPS to http://127.0.0.1:20128. Use the reverse-proxy examples, which cover streaming and WebSocket upgrades. Restrict dashboard access with a VPN, firewall, or proxy access control.
Open the public dashboard URL, sign in, enable Require API Key, and create a separate key for each client. Run the inference smoke test through the public URL. Health alone does not verify the database or provider.
Keep HOSTNAME=127.0.0.1 for a same-host proxy. Binding 0.0.0.0 exposes the app directly unless a firewall prevents it. A proxy in another container or on another host does not meet the app's loopback forwarding-header trust rule.
Restart, upgrade, or recover
sudo systemctl restart durindoor
journalctl -u durindoor -fRestart after environment changes. For an upgrade, stop the service, back up the full data directory and secrets, build the selected version, and start the service. Follow Upgrading for database verification and rollback.
If startup repeatedly fails, stop the unit before restoring data. Preserve the logs, current data directory, and pre-upgrade backup. Verify the Node path, environment file, port availability, and service-account permissions before retrying.