The repository includes a hardened single-host backend unit. This deployment does not install the React dashboard; serve a separately built dashboard through a same-origin reverse proxy if it is required.
Build and install the backend:
scripts/build-release.sh
sudo install -m 0755 target/release/model-port /usr/local/bin/model-port
sudo install -d -m 0750 /etc/modelport
sudo install -m 0640 deploy/systemd/modelport.env.example /etc/modelport/modelport.env
sudo install -m 0644 deploy/systemd/modelport.service /etc/systemd/system/modelport.serviceEdit /etc/modelport/modelport.env and replace every required placeholder. The
file contains router, admin, database, and provider credentials; restrict access
to administrators.
Then enable the unit:
sudo systemctl daemon-reload
sudo systemctl enable --now modelport
sudo systemctl status modelportThe unit uses:
StateDirectory=modelport
WorkingDirectory=/var/lib/modelport
MODELPORT_STATE_DIR=/var/lib/modelport
systemd creates /var/lib/modelport for the dynamic service user with mode
0700. Runtime state is stored in mandatory PostgreSQL; the directory remains
useful for explicit backup files and a consistent working directory.
PostgreSQL access uses SQLx with rustls and embedded migrations. For a remote
production database, set
MODELPORT_ENTERPRISE_MODE=1, MODELPORT_DATABASE_TLS_MODE=verify-full, and a
trusted sslrootcert in the database URL. Test connectivity and migration
permissions before starting the service.
Validate using the same environment file without shell-expanding or printing its values:
sudo systemctl stop modelport
sudo systemd-run --wait --pipe --collect \
--property=EnvironmentFile=/etc/modelport/modelport.env \
/usr/local/bin/model-port config validate
sudo systemctl start modelportThis transient command runs as root but only reads configuration. For policies
that prohibit transient units, add the validation command as an ExecStartPre
drop-in so it uses the service's normal identity and environment. Never paste
the environment file into an issue.
Logs and health:
sudo journalctl -u modelport -f
curl -fsS http://127.0.0.1:38082/livez
curl -fsS -H "x-api-key: $MODELPORT_AUTH_TOKEN" \
http://127.0.0.1:38082/readyzreadyz checks auth/control storage plus the normalized request/attempt ledger
and returns authenticated diagnostics; it does not gate on every Provider. See
Operations.
Keep MODELPORT_BIND=127.0.0.1:38082 when Nginx/Caddy runs on the same host.
Expose one HTTPS origin that serves the dashboard and proxies /admin, /v1,
/livez, /readyz, /health, and /metrics to the backend.
Set:
MODELPORT_ADMIN_COOKIE_SECURE=1
MODELPORT_ALLOWED_ORIGINS=https://modelport.example.com
MODELPORT_TRUSTED_PROXIES=127.0.0.1,::1MODELPORT_ALLOWED_ORIGINS validates dashboard writes; it does not enable
browser CORS. A same-origin proxy is the supported layout.
Preserve the original Host authority including a non-default port. For Nginx,
use proxy_set_header Host $http_host; $host may drop the port and cause the
Origin/Host write check to fail. A single-hop proxy should overwrite
X-Forwarded-For with $remote_addr. ModelPort accepts forwarded headers only
from MODELPORT_TRUSTED_PROXIES and removes trusted hops from the right-hand end
of the chain, so configure every trusted hop explicitly.
sudo systemctl stop modelport
sudo systemd-run --wait --pipe --collect \
--property=EnvironmentFile=/etc/modelport/modelport.env \
/usr/local/bin/model-port backup export /var/lib/modelport/backup.json
sudo systemd-run --wait --pipe --collect \
--property=EnvironmentFile=/etc/modelport/modelport.env \
/usr/local/bin/model-port backup validate /var/lib/modelport/backup.json
sudo install -m 0755 target/release/model-port /usr/local/bin/model-port
sudo systemctl start modelportThe transient CLI receives the service EnvironmentFile. It runs as root for this
offline maintenance operation; keep the backup mode restrictive and remove or
relocate it after validation. For PostgreSQL, use pg_dump in addition to the
application backup.
After upgrading, run smoke and the acceptance checks appropriate to the change.
The shipped unit uses DynamicUser, a private temporary directory, no ambient
capabilities, a strict filesystem, and a restrictive umask. Review rather than
blindly weaken these controls when adding a TLS key, custom CA, Unix socket, or
external secret agent. Keep provider keys out of the unit file itself; put them
in the protected EnvironmentFile or a systemd credential mechanism.