FreeBSD 15 VPS Setup Notes

Table of Contents



The following are some rough notes describing how I did the initial FreeBSD setup on a VPS, including - what I think are essential - pf (“firewall”) and ssh settings. The setup I did for the services running on this VPS is logged here.

Netcup Server Control Panel (scp)

flash FreeBSD image

select VPS -> Medien -> Images -> FreeBSD 15.1 UEFI amd64

Settings:

Installationsmethode
Minimal - minimal system with ssh preinstalled

Partitionierung
Eine große Partition für das Betriebssystem, die den kompletten freien Speicherplatz beinhaltet

Hostname
ax-vps0

Sprache
en_US.UTF-8

Zeitzone
Europe/Berlin

Confirm with Installieren and it should be done in a few minutes.

first ssh into server as root

update base and pkgs

Update base system:

freebsd-update fetch
freebsd-update install

Update packages:

pkg update
pkg upgrade

install the following pkgs

This list gets updated automatically on every org-publish!

pkg prime-list
bash
bastille
bind-tools
btop
doas
eza
fastfetch
fd-find
fish
git
git-delta
goaccess
ncdu2
nginx
nnn
pftop
pkg
py312-certbot
py312-certbot-nginx
qemu-guest-agent
ripgrep
rsync
starship
stow
tmux
vim
wget

create user account

Run the interactive adduser cmd - dont forget to add wheel to other groups when asked.

doas.conf

Create /usr/local/etc/doas.conf with the following content, which allows all members of the wheel group to execute commands as the root user without password.
Remove the nopass if this is not desired, or use permit persist :wheel, which caches the password for a few minutes (this does not work with FreeBSD doas, ported from OpenBSD, but opendoas is in the ports - “OpenDoas unlike OpenBSD’s doas supports persist on FreeBSD”).

permit nopass :wheel

at this point, we should log out as root and ssh back in as normal user

chsh -s /usr/local/bin/fish

run as normal user ax!

git clone syscfg and stow my dotfiles

cd syscfg/dotfiles/
rm ~/.config/fish/config.fish
stow -vR --target=$HOME *

ssh setup and config

copy public key

run on local machine:

ssh-copy-id ax@<server-ip4-addr>

Then verify the key-based login works before disabling password-based login!

hardening

Make sure those lines are set in /etc/ssh/sshd_config.

KbdInteractiveAuthentication no
PasswordAuthentication no
PermitRootLogin no
UsePAM no

Don’t forget to doas service sshd restart.

pf

current /etc/pf.conf

(inspired by https://docs.vultr.com/how-to-install-nginx-web-server-on-freebsd-14-0)

ext_if = "vtnet0"

allowed_ports = "{ 22, 80, 443 }"

# Allow internal traffic
set skip on lo

# --- Bastille ---
# <jails> = table of jail IPs. Bastille adds/removes entries here as jails
# start and stop, so this stays empty until a jail is running.
# nat = rewrite the source address of packets leaving the box that came from
# a jail (10.0.0.x, private, unroutable) to the VPS public IP, so replies
# find their way back. This is what makes `pkg install` work inside a jail.
table <jails> persist
nat on $ext_if from <jails> to any -> ($ext_if:0)

# Block non-permitted traffic
block all

# LOL - but now pkg update and upgrade seem to work reliably
### Block all IPv6
block in quick inet6 all
block out quick inet6 all

# Allow incoming traffic
pass in on $ext_if proto tcp to port $allowed_ports

# Allow outgoing traffic
pass out on $ext_if from any to any

Load new config with pfctl -f /etc/pf.conf.

TODO disable ipv6 properly

setup swap

straight from the FreeBSD handbook:

doas dd if=/dev/zero of=/usr/swap0 bs=1m count=1024
doas chmod 0600 /usr/swap0

add this line to /etc/fstab

md none swap sw,file=/usr/swap0,late 0 0

then

doas swapon -aL

Author: ax

Created: 2026-09-09 Mi 22:56