Preparing an AlmaLinux 10 server
For whoever is running the desk. Ten minutes, then go on to Install.
Written against AlmaLinux 10.2. Rocky Linux and RHEL 10 are the same throughout; Debian and Ubuntu differ only in the package manager and the Docker repository path.
The machine
Two cores and 2 GB of memory will run a small desk; four cores and 8 GB is comfortable. Allow 20 GB of disk to start, the images are about 700 MB, and after that it is your attachments and your database that grow.
Two things must be true before you begin, and neither is something the installer can do for you:
- A name points at this machine. An
Arecord, and anAAAAas well only if IPv6 genuinely works here, certificate authorities prefer IPv6 when a record exists, so anAAAApointing somewhere that does not answer fails the certificate while IPv4 looks perfect. - Ports 80 and 443 are open to the internet. Both. The certificate authority reaches you on 80, from outside; a firewall that only allows your own address will not do.
Check the name first, from somewhere that is not the server:
dig +short A your-desk.example.comdig +short AAAA your-desk.example.com1. Bring it up to date
dnf -y updatednf -y install curl chronysystemctl enable --now chronydReboot if that updated the kernel, and it usually will on a machine that has been sitting:
uname -r; rpm -q kernel | tail -1 # different? rebootrebootThis is not tidiness. Docker’s packages bring in kernel-modules-extra built for the newest installed
kernel, and the modules it needs (overlay, br_netfilter) will not load into a kernel that is still
the old one. The symptom is docker.service failing to start straight after a clean install, with
nothing obviously wrong, which is a miserable thing to debug on a machine you have owned for ten minutes.
The clock matters more than it looks, too: certificate validation, signed links and two-factor codes all depend on it.
2. Docker
dnf -y install dnf-plugins-corednf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repodnf -y install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-pluginsystemctl enable --now dockerThe docker-compose-plugin is the part the desk needs: every command in the guide is
docker compose something.
If docker.service fails to start anyway, the journal says why in one line:
journalctl -xeu docker.service --no-pager | tail -30The one to expect looks like a firewall problem and is not:
failed to register "bridge" driver: failed to add jump rules to ipv4 NAT tableiptables v1.8.11 (nf_tables): RULE_APPEND failed (No such file or directory): rule in chain PREROUTINGThat is the kernel mismatch above, seen from Docker’s side: it is asking netfilter for the NAT table, and the module that provides it will not load into the kernel that is running. Reboot. If it survives a reboot, find out which of the two it really is:
uname -r; rpm -q kernelmodprobe iptable_nat; echo "exit: $?"lsmod | grep -E 'nf_nat|iptable_nat|br_netfilter|overlay'A modprobe that fails with “module not found” is still a mismatch. A modprobe that succeeds, after
which Docker starts, means the modules were merely not loaded, put nf_nat and iptable_nat in
/etc/modules-load.d/docker.conf so the next reboot does not repeat it.
3. The firewall
dnf -y install firewalldsystemctl enable --now firewalldfirewall-cmd --permanent --add-service=sshfirewall-cmd --permanent --add-service=httpfirewall-cmd --permanent --add-service=httpsfirewall-cmd --reloadfirewall-cmd --list-servicesNothing else needs opening. The database, the cache and the desk itself are reached only from inside the container network or over the machine’s own loopback.
4. SELinux
Leave it enforcing. The desk’s compose file already carries what it needs: the one file mounted from
the host (the Caddyfile) is mounted :ro,z, which is the difference between Caddy starting and Caddy
restarting for ever on “permission denied”.
getenforce # EnforcingIf you are adding bind mounts of your own, z on each of them is what you will need.
5. Check it
docker run --rm alpine echo "containers work"curl -s -o /dev/null -w '%{http_code}\n' http://your-desk.example.com/ # anything but a timeout is fineThe second one is checking that port 80 reaches this machine from outside, a timeout here means the certificate will fail later, and it is far easier to find now.
Then
Go on to Install. You will need a Sixnix registry login before the first step.