Fedora

Setup WireGuard VPN on Fedora 44 / 43 / 42

WireGuard ships in the upstream Linux kernel and has been the default modern VPN choice on Fedora for years. The tooling is small (one userspace package, one CLI), the config files are short, and a working tunnel between a Fedora server and a phone or laptop takes about fifteen minutes from a clean install. There is no daemon to keep running, no certificate chain to renew, and the protocol’s resistance to traffic analysis makes it the right pick for both remote-worker access and personal “always-on” mobile VPNs.

Original content from computingforgeeks.com - post 167804

This guide installs WireGuard tooling on Fedora, generates server and client keypairs, writes the two configuration files, opens the right firewalld port, brings the wg0 interface up, and shows how to render the client config as a QR code that a phone WireGuard app can import in a single scan. Every command was executed on a real Fedora install and the captured output is what your terminal will show.

dnf install wireguard-tools qrencode firewalld kernel module on Fedora

What you will have at the end

  • A Fedora server running WireGuard on UDP/51820 as the VPN endpoint.
  • A 10.99.99.0/24 private network with the server at 10.99.99.1 and one phone client at 10.99.99.2.
  • firewalld zone configured to accept WireGuard traffic and masquerade outbound from the VPN subnet.
  • A client config you can render as a QR code, then scan from the WireGuard mobile app to import in one tap.
  • A systemd unit so the tunnel comes back automatically after reboot.

The same shape works for laptop clients; only the import step differs (paste the config file into /etc/wireguard/wg0.conf on the laptop and run wg-quick up wg0 there).

Install WireGuard tooling and supporting packages

WireGuard itself is built into the Linux kernel that ships with Fedora, so there is no kernel module to install. Only the userspace tools (wg and wg-quick), the QR code renderer, and firewalld are needed:

sudo dnf install -y wireguard-tools qrencode firewalld

Verify the userspace tooling and the in-kernel module:

wg --version
rpm -q wireguard-tools qrencode firewalld
modinfo wireguard | head -2

The modinfo line confirms the kernel module is present even before you bring an interface up. WireGuard has been in-tree since Linux 5.6, so every supported Fedora release has it.

Generate keypairs for the server and the phone client

WireGuard uses Curve25519 keypairs for both authentication and encryption. Generate one pair per peer:

cd /etc/wireguard
umask 077
sudo bash -c 'wg genkey | tee server.key | wg pubkey > server.pub'
sudo bash -c 'wg genkey | tee phone.key | wg pubkey > phone.pub'
sudo ls -l

The command output is shown above.

wg genkey wg pubkey generate WireGuard server and phone client keys on Fedora

Private keys live in the .key files (mode 600) and public keys in the .pub files. Treat the private keys like SSH private keys: they grant full VPN membership, never paste them anywhere outside the configuration files.

Write the server configuration

Open /etc/wireguard/wg0.conf in your editor and write the server-side config. Substitute the real key values from the files you just generated:

sudo $EDITOR /etc/wireguard/wg0.conf

Server config body:

[Interface]
Address = 10.99.99.1/24
ListenPort = 51820
PrivateKey = <contents of /etc/wireguard/server.key>

# Forward and masquerade outbound traffic from VPN clients
PostUp   = firewall-cmd --zone=public --add-masquerade
PostUp   = sysctl -w net.ipv4.ip_forward=1
PostDown = firewall-cmd --zone=public --remove-masquerade

[Peer]
# Phone client
PublicKey = <contents of /etc/wireguard/phone.pub>
AllowedIPs = 10.99.99.2/32

The AllowedIPs in the server’s [Peer] stanza is the source filter, not a route: it tells the server which inbound IPs from this peer are legitimate. One address per phone.

Open the firewall and bring the interface up

Open UDP/51820 in firewalld and turn on masquerading for the outbound interface. The PostUp lines in the config will reapply masquerade on every interface bring-up, but you need the permanent firewalld rules so the port survives a reboot:

sudo systemctl enable --now firewalld
sudo firewall-cmd --permanent --add-port=51820/udp
sudo firewall-cmd --permanent --add-masquerade
sudo firewall-cmd --reload

Now bring the tunnel up. wg-quick reads the configuration file, sets the interface address, applies the peer list, and runs the PostUp hooks:

sudo wg-quick up wg0
sudo wg show wg0
ip -4 addr show wg0

The command output is shown above.

wg-quick up wg0 firewalld masquerade wg show on Fedora

The interface is now listening for inbound peer handshakes. To make it come back automatically after a reboot, enable the unit:

sudo systemctl enable wg-quick@wg0

Write the phone client configuration

The phone config is the mirror of the server config. Save it as /tmp/wg-phone.conf on the server (you will not keep it there long, just long enough to QR-encode it):

sudo $EDITOR /tmp/wg-phone.conf

Phone config body. Replace vpn.example.com with your server’s public hostname or IP:

[Interface]
Address = 10.99.99.2/32
PrivateKey = <contents of /etc/wireguard/phone.key>
DNS = 1.1.1.1, 9.9.9.9

[Peer]
PublicKey = <contents of /etc/wireguard/server.pub>
Endpoint = vpn.example.com:51820
AllowedIPs = 0.0.0.0/0, ::/0
PersistentKeepalive = 25

Three values matter most:

  • AllowedIPs = 0.0.0.0/0, ::/0: route ALL phone traffic through the tunnel. Drop to 10.99.99.0/24 for split tunneling (only LAN reachable through VPN, regular internet stays direct).
  • Endpoint: the public address the phone connects to. Use a hostname behind dynamic DNS if your server’s IP changes.
  • PersistentKeepalive = 25: sends a keepalive packet every 25 seconds. Required when the client sits behind NAT so the NAT translation stays open.

Render the phone config as a QR code

The WireGuard mobile app imports configurations from QR codes. qrencode can render straight to the terminal in ANSI block characters; the phone scans the screen and the entire config flows across in one go:

sudo qrencode -t ansiutf8 < /tmp/wg-phone.conf

The command output is shown above.

qrencode ansiutf8 WireGuard phone client config on Fedora

Open the WireGuard app on the phone, tap Add Tunnel, choose Create from QR code, scan the terminal. The tunnel imports with the right name, keys, and routes. Toggle it on and you are routing through the Fedora server. Toggle off when not needed.

If terminal QR is awkward (font scaling, SSH session colour issues), write to a PNG instead and scan that:

sudo qrencode -o /tmp/wg-phone-qr.png < /tmp/wg-phone.conf
# Then copy the PNG off the server (sftp, paste in chat, etc.) and scan

Once the phone has the config, delete the working copy and the QR PNG from the server. The private key has already moved to the phone; leaving copies on the server adds risk without benefit:

sudo shred -u /tmp/wg-phone.conf /tmp/wg-phone-qr.png 2>/dev/null
sudo shred -u /etc/wireguard/phone.key

Keep /etc/wireguard/phone.pub; the server still references it via the [Peer] stanza.

Verify end-to-end connectivity

From the phone, after the toggle is on, check that traffic exits via the server’s public IP:

  • Open a browser to https://api.ipify.org. The reported IP should match your Fedora server’s WAN IP, not the phone’s mobile-carrier IP.
  • From the server, sudo wg show wg0 should now show a non-zero latest handshake timestamp and traffic counters that go up when you browse on the phone.
  • From the phone, ping 10.99.99.1. The reply should arrive in a few milliseconds; that confirms the tunnel is up and routed.

Add a laptop client

Repeat the keypair + config pattern on a laptop, with the laptop generating its own keypair locally so the private key never leaves that device:

# On the laptop
sudo dnf install -y wireguard-tools           # Fedora laptop
# or: sudo apt install wireguard               # Ubuntu/Debian
# or: brew install wireguard-tools             # macOS
cd /etc/wireguard
umask 077
sudo bash -c 'wg genkey | tee laptop.key | wg pubkey'

Send only the laptop’s public key back to the Fedora server, then on the server append a new [Peer] stanza pointing at 10.99.99.3/32, reload the config, and the laptop joins:

# On the server
sudo wg set wg0 peer <LAPTOP-PUBLIC-KEY> allowed-ips 10.99.99.3/32
sudo wg-quick save wg0   # persists the runtime change to /etc/wireguard/wg0.conf

The laptop config mirrors the phone config except Address = 10.99.99.3/32 and a different private key.

Troubleshooting

Handshake never completes

If wg show reports a peer with latest handshake: (none) after a minute of trying:

  • From the phone, try a different network. Many corporate Wi-Fi networks block UDP/51820.
  • Confirm the Endpoint in the phone config is reachable: nc -uvz vpn.example.com 51820 from another machine.
  • Check the server’s firewall: sudo firewall-cmd --list-all must show 51820/udp in the ports list.
  • Verify the PublicKey in the server’s [Peer] stanza matches the phone’s actual public key. A copy-paste typo here is the most common cause.

Tunnel comes up but the phone has no internet

Ping 10.99.99.1 from the phone: if that works but external IPs do not, the server is not forwarding or masquerading. Verify:

sudo sysctl net.ipv4.ip_forward         # must be 1
sudo firewall-cmd --query-masquerade    # must be yes

Persist ip_forward across reboots:

echo 'net.ipv4.ip_forward = 1' | sudo tee /etc/sysctl.d/99-wireguard.conf
sudo sysctl -p /etc/sysctl.d/99-wireguard.conf

Where this fits in the Fedora 44 Workstation series

For non-Fedora hosts running the same protocol, the patterns translate directly to Rocky Linux 10 and AlmaLinux 10 and to Ubuntu 26.04 LTS. To restrict which subnets clients can reach (split tunneling at the server side) combine WireGuard with the firewalld rich rules covered in the Configure firewalld on Fedora guide in this series.

Related Articles

Fedora Install Visual Studio Code on Fedora 44 / 43 / 42 Desktop How To Install Budgie Desktop on Fedora 43/42/41/40 Fedora How To Install Xfce Desktop on Fedora 41 CentOS Install Apache Solr on CentOS / Fedora / Rocky Linux

Leave a Comment

Press ESC to close