Documentation
Running Everlock under systemd with socket activation
This is a full setup for a native Linux host. systemd binds each listening socket and passes it to Everlock, which gives you two things: an unprivileged process (systemd binds the low ports, so Everlock doesn't need to), and graceful restarts (systemd keeps the socket bound across a restart — covered at the end).
Everlock adopts each inherited socket by a name that the socket unit declares as
FileDescriptorName. The names and their default ports:
| Frontend | FileDescriptorName | Port |
|---|---|---|
| HTTP | http | 80 |
| HTTPS | https | 443 |
| Git/admin SSH | ssh | 2222 |
| SMTP (inbound) | smtp-inbound | 25 |
| SMTP (submission) | smtp-submission | 587 |
| IMAPS | imap-tls | 993 |
| IMAP STARTTLS | imap-starttls | 143 |
| DNS (UDP) | dns-udp | 53 |
| DNS (TCP) | dns-tcp | 53 |
You only create the units for the services you actually run — skip the rest.
1. Download the binary
Fetch the build for your CPU and model variant. This
is the SmolVLM build for 64-bit x86; browse everlock.sh/dl/
for arm64 and CPU feature levels (amd64v2 / amd64v3 / amd64v4). The smolvlm / qwen3
segment selects the embedded AI model, which decides the AI runtime's capabilities —
see AI model variants.
curl -fL https://everlock.sh/dl/latest/smolvlm/everlock-linux-amd64 -o everlock chmod +x everlock ./everlock version # confirms the build, variant, and platform
The binary is installed in the next step, into a directory the service user owns:
the self-update replaces the binary from inside the service, and the unit's
ProtectSystem=strict hardening leaves only the state directory writable.
2. Create a service user, data directory, and install the binary
Everlock runs unprivileged. The binary lives at /var/lib/everlock/bin/everlock,
owned by the service user, so the daily self-update can swap it in place under
the unit's filesystem sandbox (StateDirectory keeps /var/lib/everlock
writable while ProtectSystem=strict makes everything else read-only):
sudo useradd --system --home /var/lib/everlock --shell /usr/sbin/nologin everlock sudo install -d -o everlock -g everlock /var/lib/everlock /var/lib/everlock/bin sudo install -m 0755 -o everlock -g everlock everlock /var/lib/everlock/bin/everlock
To keep everlock on the shell PATH, link it — the symlink always resolves to
the current binary, including after a self-update:
sudo ln -s /var/lib/everlock/bin/everlock /usr/local/bin/everlock
3. Create the socket units
One .socket file per port. They are nearly identical — only ListenStream,
FileDescriptorName, and the description change. Create only the ones you need;
each unit's FileDescriptorName must exactly match the name in the table above.
HTTP — /etc/systemd/system/everlock-http.socket:
[Unit] Description=Everlock HTTP socket Wants=network-online.target After=network-online.target [Socket] ListenStream=80 FileDescriptorName=http Service=everlock.service [Install] WantedBy=sockets.target
HTTPS — /etc/systemd/system/everlock-https.socket:
[Unit] Description=Everlock HTTPS socket Wants=network-online.target After=network-online.target [Socket] ListenStream=443 FileDescriptorName=https Service=everlock.service [Install] WantedBy=sockets.target
Git/admin SSH — /etc/systemd/system/everlock-ssh.socket:
[Unit] Description=Everlock SSH socket Wants=network-online.target After=network-online.target [Socket] ListenStream=2222 FileDescriptorName=ssh Service=everlock.service [Install] WantedBy=sockets.target
SMTP inbound — /etc/systemd/system/everlock-smtp-inbound.socket:
[Unit] Description=Everlock SMTP inbound socket Wants=network-online.target After=network-online.target [Socket] ListenStream=25 FileDescriptorName=smtp-inbound Service=everlock.service [Install] WantedBy=sockets.target
SMTP submission — /etc/systemd/system/everlock-smtp-submission.socket:
[Unit] Description=Everlock SMTP submission socket Wants=network-online.target After=network-online.target [Socket] ListenStream=587 FileDescriptorName=smtp-submission Service=everlock.service [Install] WantedBy=sockets.target
IMAPS — /etc/systemd/system/everlock-imap-tls.socket:
[Unit] Description=Everlock IMAPS socket Wants=network-online.target After=network-online.target [Socket] ListenStream=993 FileDescriptorName=imap-tls Service=everlock.service [Install] WantedBy=sockets.target
DNS answers on both UDP and TCP port 53, so it takes two units — note the UDP one
uses ListenDatagram. /etc/systemd/system/everlock-dns-udp.socket:
[Unit] Description=Everlock DNS UDP socket Wants=network-online.target After=network-online.target [Socket] ListenDatagram=53 FileDescriptorName=dns-udp Service=everlock.service [Install] WantedBy=sockets.target
/etc/systemd/system/everlock-dns-tcp.socket:
[Unit] Description=Everlock DNS TCP socket Wants=network-online.target After=network-online.target [Socket] ListenStream=53 FileDescriptorName=dns-tcp Service=everlock.service [Install] WantedBy=sockets.target
The two lines that matter in each: Service=everlock.service routes the socket's
descriptor to the shared service, and FileDescriptorName is the name Everlock
looks it up by — get it wrong and that frontend silently falls back to binding the
port itself. The Wants=/After=network-online.target pair holds each bind until
the network is actually up — socket units start early in boot, before the network
is configured.
4. Create the service unit
/etc/systemd/system/everlock.service. In Requires= and After=, list only the
sockets you created, and in ExecStart enable the matching frontends:
[Unit] Description=Everlock Wants=network-online.target After=network-online.target # Bind the sockets before we start, and keep them bound across our restarts. Requires=everlock-http.socket everlock-https.socket everlock-ssh.socket \ everlock-smtp-inbound.socket everlock-smtp-submission.socket \ everlock-imap-tls.socket everlock-dns-udp.socket everlock-dns-tcp.socket After=everlock-http.socket everlock-https.socket everlock-ssh.socket \ everlock-smtp-inbound.socket everlock-smtp-submission.socket \ everlock-imap-tls.socket everlock-dns-udp.socket everlock-dns-tcp.socket [Service] Type=simple User=everlock Group=everlock StateDirectory=everlock ExecStart=/var/lib/everlock/bin/everlock serve \ --data-dir /var/lib/everlock \ --frontend-http \ --frontend-http-listen-http 0.0.0.0:80 \ --frontend-http-listen-https 0.0.0.0:443 \ --frontend-ssh --frontend-ssh-listen 0.0.0.0:2222 \ --frontend-smtp --frontend-smtp-listen 0.0.0.0:25 \ --frontend-smtp-submission --frontend-smtp-submission-listen 0.0.0.0:587 \ --frontend-imap --frontend-imap-tls-listen 0.0.0.0:993 \ --frontend-dns \ --frontend-dns-listen-udp 0.0.0.0:53 \ --frontend-dns-listen-tcp 0.0.0.0:53 # Supervision + the drain window for a graceful restart. Restart=always RestartSec=1 TimeoutStopSec=90 # Optional hardening (relax if a backend needs more filesystem access). NoNewPrivileges=true ProtectSystem=strict ProtectHome=true PrivateTmp=true [Install] WantedBy=multi-user.target
A few things worth knowing:
- The listen addresses in
ExecStartare overridden by the adopted socket — they only decide whether a frontend is enabled (HTTPS starts because--frontend-http-listen-httpsis present). The real port comes from the.socketunit, so keep them consistent to avoid confusion. Restart=alwaysis required for the self-update: a socket-activated update drains and exits, and this is what brings the new binary back up.TimeoutStopSecis your drain cap — in-flight requests get up to this long to finish before systemd kills the old process.
5. mDNS (optional, not socket-activated)
Unlike DNS, mDNS relies on multicast, which can't be socket-activated — so it gets
no .socket unit and is enabled purely through a flag on ExecStart:
--frontend-mdns
mDNS advertises on the local network (UDP 5353, a high port needing no special privilege) and needs the host's real network — it won't work behind network namespacing that hides the LAN. Because it isn't socket-activated, a restart briefly rebinds its socket; for discovery traffic that's invisible.
Mixing the two needs nothing special in the unit: the same always-on process adopts
the systemd-passed sockets and binds mDNS's own socket itself. The only consequence
is that the service keeps host-network access (no PrivateNetwork= isolation) — which
Everlock needs anyway for outbound updates and ACME, so it isn't a trade-off mDNS
introduces on its own.
Everything else, DNS included, is socket-activated above — so the service needs no capabilities: systemd binds every privileged port (including 53) and hands it over, and the process runs fully unprivileged.
6. Enable and start
sudo systemctl daemon-reload sudo systemctl enable --now everlock.service
Requires= pulls in and binds the sockets first, then starts the service, which
adopts them.
Stopping for maintenance
systemctl stop everlock stops only the service — the socket units stay bound,
and the next inbound connection starts the service right back up. On an
internet-facing host that happens within seconds (DNS queries, mail delivery
attempts, HTTP scanners). For work that needs Everlock to stay down — store
surgery, moving the data directory — take the sockets down with it:
systemctl stop 'everlock*' # the service and every everlock-* socket systemctl is-active everlock # confirm: inactive # … maintenance … systemctl start everlock-http.socket everlock-https.socket everlock-ssh.socket \ everlock-smtp-inbound.socket everlock-smtp-submission.socket \ everlock-imap-tls.socket everlock-dns-udp.socket everlock-dns-tcp.socket systemctl start everlock.service
Start the sockets explicitly (a systemctl start 'everlock*' glob usually has no
effect — globs only expand to loaded units), then the service, which adopts them.
7. Verify
# The startup log should mention adopting inherited listeners: journalctl -u everlock -b | grep -i socket-activated # → "socket-activated by systemd; adopting inherited listeners" systemctl list-sockets | grep everlock # the bound ports + their service ss -tulnp | grep -E ':(80|443|2222|25|587|993|53)\b' # -u also lists the DNS UDP socket
8. Get the bootstrap admin password and connect
On its first start with no configuration, Everlock creates an admin user with an
auto-generated password and logs it once — it is never shown again. Grab it from
the journal:
journalctl -u everlock -b | grep -A2 'bootstrap admin credentials' # username: admin # password: <auto-generated — copy it now>
Then connect to the admin console and enable the backends you want — the sockets are just transports; services like sites, Git, mail, and the vault are turned on here:
ssh -p 2222 admin@your-host # use the SSH port from your everlock-ssh.socket # /backends enable site-http git-ssh …
How updates and graceful restart work
This is the payoff of the socket-activation setup. Everlock never rebinds the ports on a restart — systemd keeps them, so the connection backlog survives while the new process takes over:
sequenceDiagram participant sd as systemd participant old as Everlock (old) participant new as Everlock (new) sd->>old: SIGTERM (self-update / restart) old->>old: stop accepting, drain in-flight old-->>sd: exit(0) — socket stays bound by systemd sd->>new: start, pass sockets via LISTEN_FDS new->>new: adopt the same fds (no rebind) Note over sd,new: queued connections preserved
- Automatic updates (on by default). Once a day Everlock checks
everlock.sh, and on a new digest it downloads, verifies, and applies the binary — then takes the graceful restart above. Because systemd holds the sockets, the swap is seamless. To turn it off, add--no-updatetoExecStart(hard-off, for pinning a recovered binary) or flip it at runtime with/server settings set update.enabled false. The self-update page covers the full mechanism, manual updates, and rollback. - Manual updates. Replace
/var/lib/everlock/bin/everlock(keep it owned by theeverlockuser) andsudo systemctl restart everlock— the restart drains and re-adopts the same sockets. The apply path also keeps the previous binary aseverlock.oldfor a quick rollback.
Read next
- Self-update — the update mechanism, manual updates, and rollback
- Deployment options — standalone and container alternatives
- Frontends — every transport and its configuration
- Publishing over public HTTPS — ACME and certificates