The First Hour on a New VPS: IPv6, Reverse DNS and Port 25
Fresh Ubuntu 24.04 VPS: IPv6 that cloud-init didn't set, swap, SSH keys only, a provider mail block on port 25, PTR records, then KeyHelp on top.
My own stuff is spread over too many boxes. My blog, my brand site, a community forum, a few small things for a client. One of those boxes isn't even mine, it belongs to my boss, and I want to give it back. So I ordered one server that holds everything of mine.
This is the first hour on that box. Not the generic checklist, I already wrote that one. This is the stuff that was specific to a fresh VPS and that I'd have missed if I hadn't checked: IPv6 that wasn't configured, a provider firewall that blocked outgoing mail, and reverse DNS.
TL;DR: Reinstall the image, don't trust what's on it. Check ip -6 addr right away, if the IPv6 isn't there, stop cloud-init from managing the network and write your own netplan file. Keys only for SSH, swap file, full upgrade. Before you install any mail stack, test outgoing port 25 with nc -vz. Mine was blocked by a policy in the provider's panel. Set A, AAAA and PTR for v4 and v6 to the same hostname. Then install the panel.
Why this box
The spec I took from a German VPS provider:
- 16 shared vCores
- 32 GB RAM
- 640 GB SSD
- about 26 € per month including the IPv4, billed 3 months in advance, 3 months minimum
I started on a smaller plan (4 vCores, 8 GB, 160 GB) and upgraded. My old boxes cost about 23 € per month each and have much less RAM. The reason for 32 GB is a Hytale test server. Web and mail are cheap on memory, a game server is not. How much a Hytale server really needs is in Hytale server RAM: how much.
Step 0: reinstall
Before I got serious with it, the box had a Windows evaluation image on it from a test. With RDP open to the internet. That's a machine I don't want to build on, so the first thing was a clean reinstall to Ubuntu 24.04 from the provider's panel.
If you tested anything on a VPS before you decided to keep it, reinstall. It's two minutes.
The basics, short
The full version is in my Ubuntu server initial setup checklist. What I did here:
apt update && apt full-upgrade -y
hostnamectl set-hostname server.example.com
timedatectl set-timezone Europe/Berlin
SSH with keys only, as a drop-in:
cat > /etc/ssh/sshd_config.d/00-keys-only.conf <<'EOF'
PasswordAuthentication no
KbdInteractiveAuthentication no
EOF
sshd -t && systemctl reload ssh
Test a new login in a second terminal before you close the first one. On Ubuntu cloud images there's a cloud-init drop-in that can turn password login back on, the details are in SSH hardening on Ubuntu.
8 GB swap:
fallocate -l 8G /swap.img
chmod 600 /swap.img
mkswap /swap.img
swapon /swap.img
echo '/swap.img none swap sw 0 0' >> /etc/fstab
free -h
With 32 GB RAM the swap is a buffer, not extra memory. I just don't want the OOM killer to be the first thing that happens when something leaks.
IPv6: the provider gives you a /64, cloud-init doesn't use it
IPv4 worked out of the box. The address sat in a /22 with the gateway in the same range, nothing to do there.
IPv6 was a different story. The panel showed a /64 for the server. On the box:
ip -6 addr show
Only the link-local fe80:: address. No global one. ping -6 to anything outside went nowhere. The network config that cloud-init generated simply didn't have the IPv6 in it.
You could edit the cloud-init netplan file, but cloud-init may write it again later. So I did two things.
First, tell cloud-init to stop managing the network:
cat > /etc/cloud/cloud.cfg.d/99-disable-network-config.cfg <<'EOF'
network: {config: disabled}
EOF
This doesn't delete the existing config. The file cloud-init already wrote stays in /etc/netplan/ and keeps doing IPv4. It just won't be touched again.
Second, my own netplan file for IPv6:
# /etc/netplan/60-ipv6.yaml
network:
version: 2
ethernets:
ens3:
addresses:
- "2001:db8:36:fe6::1/64"
routes:
- to: "::/0"
via: "fe80::1"
on-link: true
Check your interface name with ip link first. Mine is ens3, yours may differ. Netplan merges all files in /etc/netplan/ in name order, so the 60- file adds to the cloud-init one instead of replacing it. The gateway fe80::1 is a link-local address, which is why on-link: true is there. Use whatever gateway your provider documents.
Then:
chmod 600 /etc/netplan/60-ipv6.yaml
netplan try
netplan try applies the config and rolls it back after 120 seconds unless you confirm. On a box you only reach over SSH, that's the command you want, not netplan apply. If your session survives, press Enter.
Check:
ip -6 addr show
ip -6 route
ping -6 -c 3 2606:4700:4700::1111
A global address, a default route via fe80::1, and replies. Done.
Gotcha: port 25 was blocked outside the box
The plan was to run mail on this server, so before installing anything I tested if the box can even talk to other mail servers:
dig +short MX gmail.com
nc -vz gmail-smtp-in.l.google.com 25
No connection. And nothing on the fresh server itself was blocking it.
The block was on the provider's side. In their control panel there's a firewall policy attached to the server, and one of them was a "Mail block" for outgoing port 25. Many providers do this on new servers against spam. I removed the policy in the panel and ran nc -vz again: connected.
The lesson: test outgoing 25 before you install Postfix or a panel. Otherwise you spend an evening reading mail logs for a problem that has nothing to do with your config. Mail just sits in the queue with connection timeouts and it looks like a DNS problem or a broken relay.
Some providers don't have a switch for it and want a support ticket. Either way, it's a provider thing, not a Linux thing.
DNS and reverse DNS
Before the panel goes on, the hostname has to be real. In the DNS zone:
server.example.com. 300 IN A 203.0.113.10
server.example.com. 300 IN AAAA 2001:db8:36:fe6::1
TTL 300, because this box is new and I may still change things. You can raise it later.
The reverse DNS (PTR) is not in your zone. It's set in the provider's panel, per IP. I set both: the IPv4 and the IPv6 point back to server.example.com. That's the name the mail server will use in HELO, and receivers check that A, PTR and HELO match. More on that in SPF, DKIM and DMARC from zero.
Check from outside:
dig +short A server.example.com
dig +short AAAA server.example.com
dig +short -x 203.0.113.10
dig +short -x 2001:db8:36:fe6::1
Both -x lookups should return server.example.com.. Don't forget the IPv6 one. If your server has a working IPv6 and the receiver has one too, mail goes out over IPv6. A missing PTR there gets you rejected even when IPv4 is perfect.
Gotcha: rDNS before the panel
Set hostname, A, AAAA and PTR before you install the panel or Postfix. The installer picks up the hostname and writes it into the mail config and the panel URL. Changing it afterwards is possible but annoying.
KeyHelp on top
Then the panel. I use KeyHelp, free, and I wrote about it in KeyHelp as a free Plesk alternative. Install was the same as there:
wget https://install.keyhelp.de/install_keyhelp.sh
bash install_keyhelp.sh --non-interactive
Because DNS already pointed at the box, the panel got a Let's Encrypt certificate for its hostname by itself. Multiple PHP versions came with it, which helps when the sites you move over aren't all on the same PHP.
I also turned on the panel API, restricted to localhost. I'll use it for migration scripts on the box itself. When the migration is done, the key goes. An API key with admin rights that nobody uses is just a risk.
What's next
Not done when I wrote this:
- Move the community forum from an old box.
- Move a small client's WordPress, Nextcloud and mail.
- Move my blog.
- The Hytale test server.
For the game server I already know one thing I'll do: cap its memory in systemd, so a leak in the game can't eat the RAM that the web and mail side need.
# /etc/systemd/system/hytale.service.d/memory.conf
[Service]
MemoryMax=16G
systemctl daemon-reload
systemctl restart hytale
systemctl show hytale -p MemoryMax
MemoryMax is a hard limit. If the process goes over it, the kernel kills it inside that unit, not some random other service. The JVM heap (-Xmx) should sit clearly below that limit, because Java uses memory outside the heap too. The number above is a plan, not a measured value. How a plain systemd unit for it looks is in Hytale server without a panel.
Checklist
- Reinstall the image.
- Full upgrade, hostname, timezone, SSH keys only, swap.
ip -6 addr: global IPv6 there? If not, disable cloud-init network, own netplan file,netplan try.nc -vz <mx> 25: outgoing mail allowed? If not, check the provider panel.- A + AAAA with low TTL, PTR for v4 and v6, all the same name.
dig -xfor both.- Then the panel.
One hour, and the box is ready for real work. Steps 3 and 4 are the easy ones to skip, and they're the ones that hurt when you find out later.