Documentation

Last updated: 2026-08-02

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:

FrontendFileDescriptorNamePort
HTTPhttp80
HTTPShttps443
Git/admin SSHssh2222
SMTP (inbound)smtp-inbound25
SMTP (submission)smtp-submission587
IMAPSimap-tls993
IMAP STARTTLSimap-starttls143
DNS (UDP)dns-udp53
DNS (TCP)dns-tcp53

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 ExecStart are overridden by the adopted socket — they only decide whether a frontend is enabled (HTTPS starts because --frontend-http-listen-https is present). The real port comes from the .socket unit, so keep them consistent to avoid confusion.
  • Restart=always is required for the self-update: a socket-activated update drains and exits, and this is what brings the new binary back up.
  • TimeoutStopSec is 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
A restart under socket activation: systemd keeps the socket bound, so the backlog survives while the new process takes over.
  • 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-update to ExecStart (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 the everlock user) and sudo systemctl restart everlock — the restart drains and re-adopts the same sockets. The apply path also keeps the previous binary as everlock.old for a quick rollback.
deployment systemd socket-activation operations