Fluxheim can run as a native systemd service when you manually compile the binary or install an RPM package.
The packaged unit is intentionally conservative:
- runs as the
fluxheimuser and group; - validates the config before starting;
- uses
/run/fluxheim,/var/lib/fluxheim,/var/cache/fluxheim, and/var/log/fluxheimas writable service paths; - keeps
/etc/fluxheimand/srv/fluxheimreadable but not writable by the service; - runs with
NoNewPrivilegesand grants onlyCAP_NET_BIND_SERVICE, allowing the unprivilegedfluxheimuser to bind production ports80and443without running the service as root; - uses strict system path protection, private temporary and device namespaces, kernel/control-group write protection, namespace restrictions, native syscall architecture filtering, and a conservative system-service/network syscall allow-list;
- limits address families to IPv4, IPv6, and Unix domain sockets;
- stops with
SIGTERMand lets Fluxheim shut down gracefully.
These systemd controls are the supported 1.0 host sandbox. They are
deployment-level controls and do not require a special Fluxheim binary.
The packaged native config listens on 0.0.0.0:80 by default. HTTPS uses
0.0.0.0:443 once server.tls_listen and certificate paths are enabled. This
matches normal bare-metal web server expectations while keeping the process
unprivileged.
Build Fluxheim:
cargo build --release --lockedInstall the binary where the provided unit expects it:
sudo install -Dm0755 target/release/fluxheim /usr/bin/fluxheimInstall the service user, runtime directories, default config, and default static page:
sudo install -Dm0644 packaging/systemd/fluxheim.sysusers /usr/lib/sysusers.d/fluxheim.conf
sudo systemd-sysusers fluxheim.conf
sudo install -Dm0644 packaging/rpm/fluxheim.tmpfiles /usr/lib/tmpfiles.d/fluxheim.conf
sudo systemd-tmpfiles --create fluxheim.conf
sudo scripts/prepare-server.py --owner fluxheim:fluxheimThe prepare script is intentionally path-restricted. Any path override must be
absolute, must not pass through a symlinked existing directory, and must stay
below one of Fluxheim's standard native roots: /etc/fluxheim, /run/fluxheim,
/var/lib/fluxheim, /var/cache/fluxheim, /var/log/fluxheim, or
/srv/fluxheim.
Install the systemd service, optional socket unit, and environment file:
sudo install -Dm0644 packaging/systemd/fluxheim.service /etc/systemd/system/fluxheim.service
sudo install -Dm0644 packaging/systemd/fluxheim.socket /etc/systemd/system/fluxheim.socket
sudo install -Dm0644 packaging/systemd/fluxheim.env /etc/sysconfig/fluxheim
sudo systemctl daemon-reloadValidate before starting:
sudo -u fluxheim /usr/bin/fluxheim --config /etc/fluxheim/fluxheim.toml --validate-configStart and enable:
sudo systemctl enable --now fluxheim.service
sudo systemctl status fluxheim.serviceView logs:
journalctl -u fluxheim.service -fThe unit defaults to:
FLUXHEIM_CONFIG=/etc/fluxheim/fluxheim.toml
To use another config, edit /etc/sysconfig/fluxheim:
FLUXHEIM_CONFIG=/etc/fluxheim/fluxheim.toml
Then reload systemd and restart:
sudo systemctl daemon-reload
sudo systemctl restart fluxheim.serviceFor 1.0, treat native service changes as validate-then-restart unless a
specific runtime reload path is documented for the setting you changed:
sudo -u fluxheim /usr/bin/fluxheim --config /etc/fluxheim/fluxheim.toml --validate-config
sudo systemctl restart fluxheim.serviceFluxheim exits on SIGTERM; the unit uses TimeoutStopSec=30s so the process
has time to drain and shut down cleanly before systemd escalates.
Starting with 1.7.11, native listeners stop accepting after the configured
grace interval and wait for established connection tasks within the configured
graceful-shutdown timeout. Public HTTP/HTTPS listeners can also be inherited
through systemd socket activation. This makes restarts bounded and
connection-aware and lets systemd retain the listening socket between process
generations.
RPM packages ship a disabled fluxheim.socket matching the packaged
server.listen = ["0.0.0.0:80"] default. The addresses in the socket unit must
exactly match server.listen and server.tls_listen. For example, for a config
listening on 0.0.0.0:80 and 0.0.0.0:443, replace the socket unit with:
[Unit]
Description=Fluxheim public listeners
[Socket]
ListenStream=0.0.0.0:80
ListenStream=0.0.0.0:443
NoDelay=true
Service=fluxheim.service
[Install]
WantedBy=sockets.targetThe first conversion from a directly bound service requires one stop/start and therefore is not itself zero-downtime. Disable the direct service owner, then activate the persistent socket owner:
sudo systemctl daemon-reload
sudo systemctl disable --now fluxheim.service
sudo systemctl enable --now fluxheim.socket
sudo systemctl start fluxheim.serviceKeep fluxheim.socket enabled and do not re-enable fluxheim.service; incoming
traffic can activate the service, and explicit service restarts retain the
socket in systemd. After this one-time conversion, validate an update and use
systemctl restart fluxheim.service while the socket unit remains active.
Do not add addresses that are absent from the Fluxheim config and do not omit
configured public addresses. Fluxheim rejects partial activation, wrong-process
descriptors, non-TCP descriptors, duplicate addresses, and address mismatches;
it never silently binds a fallback public socket once activation was requested.
The packaged service uses Type=notify and considers startup successful only
after Fluxheim reports that native startup completed. The complete rollout
model and current limitation are documented in
Zero-Downtime Upgrades.
RPM packages install a one-shot renewal unit and timer:
fluxheim-acme.servicefluxheim-acme.timer
The service runs fluxheim-acme --config ${FLUXHEIM_CONFIG} renew as the same
fluxheim user, with the same runtime/state/cache/log directories as the web
service. It does not bind ports and does not receive CAP_NET_BIND_SERVICE.
After successful renewal it requests live certificate activation through
/run/fluxheim/fluxheim-cert-reload.sock.
Set tls.acme.automation = "external" when using the timer so the main
webserver does not also run the background renewal loop.
For issuers with External Account Binding, install the ACME credential drop-in for the ACME unit and use credential names in the TOML:
sudo install -d /etc/systemd/system/fluxheim-acme.service.d
sudo cp /usr/share/doc/fluxheim/systemd/actalis-eab-acme.conf \
/etc/systemd/system/fluxheim-acme.service.d/actalis-eab.conf
sudo systemctl daemon-reload[tls.acme.issuers.eab]
key_id_credential = "actalis-eab-kid"
hmac_key_credential = "actalis-eab-hmac-key"Enable scheduled renewal:
sudo systemctl enable --now fluxheim-acme.timer
sudo systemctl start fluxheim-acme.serviceThe default unit is strict enough for normal static/proxy deployments. If a deployment needs extra host access, prefer a local drop-in instead of editing the packaged unit:
sudo systemctl edit fluxheim.serviceFor example, add another read-only content root:
[Service]
ReadOnlyPaths=/etc/fluxheim /srv/fluxheim /srv/sitesThen validate and restart:
sudo systemctl daemon-reload
sudo systemctl restart fluxheim.serviceThe default native paths are:
| Path | Purpose |
|---|---|
/etc/fluxheim/fluxheim.toml |
Main config. |
/etc/fluxheim/conf.d |
Optional split config directory. |
/etc/fluxheim/tls |
Static certificate chains and private keys. |
/srv/fluxheim |
Default static site root. |
/var/lib/fluxheim |
State and future ACME/snapshot storage. |
/var/cache/fluxheim |
Cache storage. |
/var/log/fluxheim |
Optional file logs. |
/run/fluxheim |
PID file, upgrade socket, and certificate reload socket. |
Keep private keys mode 0600 or stricter and owned by the runtime user when
Fluxheim reads them directly:
sudo chown fluxheim:fluxheim /etc/fluxheim/tls/key.pem
sudo chmod 0600 /etc/fluxheim/tls/key.pem