GuidesNetwork and VPNWi-Fi logins with FreeRADIUS

Run a RADIUS server with FreeRADIUS and rootless Podman

FreeRADIUS on Debian 13 in rootless Podman under a user of its own, so that everyone logs in to Wi-Fi with WPA2 or WPA3 Enterprise, VPNs and network devices with their own user name and password, checked against certificates you make yourself.

Tested on FreeRADIUS 3.2.10 on Debian 13 (trixie) on a Melonslab server Updated September 26, 2026

Recommended server for this guide

VC-S Micro · 2 vCPU · 8 GB Memory · 250 GB Storage

Month to month, no lock-in 7-day money-back guarantee

€7.99/mo

Deploy now
On this page

What you will set up

A RADIUS server checks logins for other devices. Wi-Fi access points use it for WPA2 and WPA3 Enterprise, where everyone has their own user name and password instead of one shared Wi-Fi password. Many VPN servers, firewalls and switches can use it too. When someone leaves, you remove their line, and their login stops working everywhere.

FreeRADIUS is the most widely used open-source RADIUS server. Here it runs as a container, under a user of its own called radius, with certificates you make yourself, so that devices can check they are talking to your server before they send a password.

Every step below was run on a Melonslab server with Debian 13:

  • A Linux client at another address logged in with PEAP and MSCHAPv2, the method Wi-Fi uses, with eapol_test, the test tool from wpa_supplicant, the Wi-Fi client in Linux and Android. It checked the server's certificate against our own CA, and refused to log in when given a different CA.
  • Logins with a password were accepted, a wrong password was rejected, and a wrong shared secret, or an address not on the list, got no answer.
  • The records the client sent when it connected were saved, in a file for each device.
  • A renewed server certificate worked without any change on the client, and everything came back by itself after a reboot.

FreeRADIUS used about 45 MB of memory. The steps for phones, laptops and access points follow the vendors' own documentation.

Before you start

You need:

  • a server set up as in the Podman guide, with ufw from the security guide. FreeRADIUS does not need Caddy;
  • the public IP address of each device that will send logins to the server, such as your office's address for its access points. Devices behind the same router all send from the router's address.

The examples use 203.0.113.10 for your server, 198.51.100.20 for your office, and radius.example.com as the server's name in its certificate. The name does not need a DNS record. Replace them throughout.

RADIUS was designed for local networks. With the Wi-Fi method in this guide, the password travels inside an encrypted connection from the device all the way to the server, but other details, such as the user name, are protected only by the shared secret. That is why this guide lets only your office's address reach the server, and uses a long random secret. For more protection, or if your office's address changes, connect the office to the server with WireGuard, and send RADIUS through the tunnel.

1. Allow your devices

As root, let only your office's address reach the two RADIUS ports, 1812 for logins and 1813 for accounting:

ufw allow from 198.51.100.20 to any port 1812:1813 proto udp

Give the same rule once for each address the logins come from.

2. Create the user

useradd -m -s /bin/bash radius
loginctl enable-linger radius
machinectl shell radius@

Everything from now on runs as radius.

3. Copy the settings out of the image

FreeRADIUS keeps its settings in its image. Copy out the two parts you change, the certificate templates and the settings for EAP, the methods Wi-Fi uses:

mkdir -p ~/radius ~/.config/containers/systemd
cd ~/radius
podman create --name radius-tmp docker.io/freeradius/freeradius-server:latest-3.2
podman cp radius-tmp:/etc/freeradius/certs ./certs
podman cp radius-tmp:/etc/freeradius/mods-available/eap ./eap
podman rm radius-tmp

4. Make your own certificates

The image comes with test certificates, which are the same for everyone who downloads it, and valid for 60 days. Make your own: a CA, which devices trust, and a server certificate that the CA signs. Set a random password for their keys, a validity of ten years for the CA and 825 days for the server certificate, Apple's limit for server certificates, and your names:

KEYPW=$(openssl rand -hex 16)
sed -i "s/= whatever/= $KEYPW/" certs/ca.cnf certs/server.cnf eap
sed -i 's/^default_days.*/default_days = 3650/; s/"Example Certificate Authority"/"example.com RADIUS CA"/' certs/ca.cnf
sed -i 's/^default_days.*/default_days = 825/; s/"Example Server Certificate"/"radius.example.com"/' certs/server.cnf

Then remove the test certificates, make yours with the tools in the image, and let FreeRADIUS read them:

podman run --rm -v ./certs:/etc/freeradius/certs --entrypoint sh docker.io/freeradius/freeradius-server:latest-3.2 -c 'cd /etc/freeradius/certs && make destroycerts && ./bootstrap'
podman unshare chgrp -R 101 certs
chmod -R g+rX certs
openssl x509 -in certs/server.crt -noout -subject -enddate

The last command shows CN=radius.example.com and the date the certificate expires. The other fields, such as O=Example Inc., are shown only in the certificate's details. FreeRADIUS runs as user 101 inside its container, and the chgrp gives that user's group access to the keys.

5. Add your devices and users

Create a shared secret for your office, and another for testing from the server itself, by running this twice:

openssl rand -hex 16

Create ~/radius/clients.conf, with the first secret in place of SHARED_SECRET and the second in place of LOCAL_SECRET:

# The devices that may ask this server to check logins: one block each.
client office {
    ipaddr = 198.51.100.20
    secret = SHARED_SECRET
}

# For testing from the server itself
client localhost {
    ipaddr = 127.0.0.1
    secret = LOCAL_SECRET
}

Create ~/radius/users, with one line for each person:

# One line per user.
anna    Cleartext-Password := "PASSWORD"

The home folder of radius can be read only by that user and root, so the passwords stay private.

6. Describe the container

Create ~/.config/containers/systemd/radius.container:

[Unit]
Description=FreeRADIUS

[Container]
ContainerName=radius
Image=docker.io/freeradius/freeradius-server:latest-3.2
Volume=%h/radius/clients.conf:/etc/freeradius/clients.conf:ro
Volume=%h/radius/users:/etc/freeradius/mods-config/files/authorize:ro
Volume=%h/radius/eap:/etc/freeradius/mods-available/eap:ro
Volume=%h/radius/certs:/etc/freeradius/certs:ro
Volume=radius-log:/var/log/freeradius
PublishPort=1812:1812/udp
PublishPort=1813:1813/udp
Exec=-l stdout
AutoUpdate=registry

[Service]
Restart=always

[Install]
WantedBy=default.target

Your files replace the ones in the image, and the radius-log volume keeps the accounting records. -l stdout sends FreeRADIUS' log to podman logs. Start it:

systemctl --user daemon-reload
systemctl --user start radius
podman logs radius

The log ends with Ready to process requests.

7. Test it

Log in as anna from inside the container, first with the password as it is, then with MSCHAPv2, which Wi-Fi uses inside its encrypted connection:

podman exec radius radtest anna PASSWORD 127.0.0.1 0 LOCAL_SECRET
podman exec radius radtest -t mschap anna PASSWORD 127.0.0.1 0 LOCAL_SECRET

Both answer Received Access-Accept. With a wrong password, the answer is Access-Reject.

To add a user, or change a password, edit ~/radius/users and run systemctl --user restart radius. The same goes for clients.conf.

8. Connect Wi-Fi

In your access point or router, choose WPA2-Enterprise or WPA3-Enterprise as the security for the network. Some call it 802.1X. Then enter:

  • RADIUS server: 203.0.113.10, port 1812;
  • Shared secret: the office's secret from step 5;
  • Accounting server, if there is one: the same address, port 1813 and the same secret.

With accounting, the access point reports when each person connects and disconnects. FreeRADIUS saves the reports in a folder for each device, which podman exec radius ls /var/log/freeradius/radacct shows.

Some devices need your CA to check that they are talking to your server. Print it:

cat ~/radius/certs/ca.pem

Copy everything, from -----BEGIN CERTIFICATE----- to -----END CERTIFICATE-----, to a file called radius-ca.crt, and bring it to the devices that ask for it. On each device, choose the network, and log in with the user name and password from step 5:

  • Android: install radius-ca.crt as a Wi-Fi certificate, under Install a certificate in the security settings. Where it is varies between phones. When you connect, choose PEAP as the EAP method, MSCHAPV2 as Phase 2 authentication, the certificate as CA certificate, and radius.example.com as Domain.
  • iPhone, iPad and Mac: after the user name and password, the device shows the server's certificate. Check that it says radius.example.com, and choose Trust.
  • Windows: Windows asks whether to continue connecting. Choose Show certificate details, check that it says radius.example.com, and choose Connect.
  • Linux: in the network settings, choose Protected EAP (PEAP), the file as CA certificate, and MSCHAPv2 as Inner authentication.

A device that is not given the CA either asks you to trust the certificate, as Apple's and Windows do, or refuses to connect, as Android does. Never tell a device not to check the certificate: anyone could then pretend to be your network and collect passwords.

9. Keep it up to date

Turn on Podman's daily updates:

systemctl --user enable --now podman-auto-update.timer

The tag latest-3.2 gets every release of FreeRADIUS 3.2. podman auto-update --dry-run shows whether an update is waiting.

10. Renew the server certificate

The server certificate from step 4 expires after 825 days, and openssl x509 -in ~/radius/certs/server.crt -noout -enddate shows when. Before then, make a new one with the same CA, so that nothing changes on your devices:

cd ~/radius
podman run --rm -v ./certs:/etc/freeradius/certs --entrypoint sh docker.io/freeradius/freeradius-server:latest-3.2 -c 'cd /etc/freeradius/certs && echo "unique_subject = no" > index.txt.attr && rm -f server.crt server.csr server.key server.p12 server.pem && make server'
podman unshare chgrp -R 101 certs
chmod -R g+rX certs
systemctl --user restart radius

The first line of the command lets the CA sign a second certificate with the same name. The CA itself lasts ten years.

11. Back up

As radius:

mkdir -p ~/backup
tar -czf ~/backup/radius.tar.gz radius

That saves the users, the devices and their secrets, and the certificates, with the CA's key. Copy ~/backup to another machine, and keep it private: with the CA's key, anyone could make a certificate your devices trust.

Troubleshooting

A device gets no answer, and the log shows from unknown client with an address. The device sends from that address, not the one in clients.conf. Put that address in its client block and in the ufw rule from step 1.

The log says Shared secret is incorrect. The secret in the device differs from the one in clients.conf.

A device gets no answer, and the log shows nothing. Check the ufw rule from step 1. If it is right, the device may be too old to send Message-Authenticator, which this version of FreeRADIUS requires, as protection against the Blast-RADIUS attack published in 2024. Update its firmware. If there is no update, and the device does not use Wi-Fi logins, add require_message_authenticator = no to its client block, knowing that it weakens that device's protection.

The container stops with Permission denied for a certificate. Run the chgrp and chmod lines from step 4 again.

A phone refuses to connect. Check that it has radius-ca.crt as its CA certificate, and radius.example.com, the name in the server certificate, as its domain.

Run it on your own server

VC-S Micro

€7.99/mo

vCPU
2
Memory
8 GB
Storage
250 GB
Transfer
10 TB
Standard
HDD · RAID 10
  • Full root access
  • Native /64 IPv6
  • RAID-protected storage
  • Malmö, Sweden
  • Month to month, no lock-in
  • 7-day money-back guarantee
All guides