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. A name identifies a role, not a single socket: list
several ListenStream= lines under one name — or point several socket units at
the same name — and Everlock serves every one of them the same way. That is how
one frontend reaches both IPv4 and IPv6, or several ports; see
IPv4 and IPv6. 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. This is the build for 64-bit x86; browse
everlock.sh/dl/ for arm64 and CPU feature levels
(amd64v2 / amd64v3 / amd64v4). Every build embeds the same model — see
the embedded model.
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):
To keep everlock on the shell PATH, link it — the symlink always resolves to
the current binary, including after a self-update:
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 socket is bound by systemd with nothing
accepting on it — connections complete the TCP handshake and then hang, rather
than being refused. Everlock reports unrecognised names at startup:
ERROR socket activation: inherited socket(s) named ["http6"] match no frontend
and will never be served — connections to them hang. Known names: [...]
Leaving FileDescriptorName= out does not mean "no name": it defaults to the
unit name including the .socket suffix, which matches nothing. 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.
The units above bind every address on the host. To name addresses instead —
required on a machine that also runs an OS sshd or a systemd-resolved stub,
since a wildcard would collide with them — list one ListenStream= per address
and add two settings:
[Socket]
ListenStream=198.51.100.7:80
ListenStream=[2001:db8::1]:80
BindIPv6Only=ipv6-only
FreeBind=yes
FileDescriptorName=http
Service=everlock.service
BindIPv6Only= otherwise follows the host's net.ipv6.bindv6only sysctl, a
value you inherit rather than choose. FreeBind=yes lets the socket bind an
address the interface has not configured yet — systemd recommends it for any
specific-address bind, and here it is load-bearing, because these units are
Requires= of the service and a socket that cannot bind at boot takes Everlock
down with it. IPv4 and IPv6 covers the full
picture, including doing it without socket activation.
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 not used for a socket-activated frontend — they only decide whether it is enabled (HTTPS starts because--frontend-http-listen-httpsis present). Every address comes from the.socketunit. They are worth keeping readable, but do not edit them expecting a listener to move; edit the socket unit. Note this also means the unprivileged service never binds these ports itself: withUser=everlockand noAmbientCapabilities=CAP_NET_BIND_SERVICE, a frontend whose socket Everlock cannot find has no working fallback for any port below 1024. 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
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:
# … maintenance …
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:
|
# → "socket-activated by systemd; adopting inherited listeners"
# One line per listener actually being served — count them. With several
# sockets under one name this is the authoritative answer, not the unit files.
|
| |
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:
|
# 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:
# /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
- IPv4 and IPv6 — serving either family or both, with and without socket activation
- 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