VPNSmith
self-host-vpnINFO

WireGuard QR Code: Generate One in a Second, and Handle It Like a Private Key

One qrencode command turns a client config into a QR code the mobile app reads instantly. It is also the fastest way to leak a private key, because a QR code left in a terminal buffer or a screenshot is the config, in full, readable by anything with a camera.

By Eric Gerard · Founder · VPNSmith - Self-host VPN & GDPR VPS specialist3 min readPhoto via Pexels

Affiliate disclosure - This post contains Contabo affiliate links. If you grab a VPS through them, we earn a commission at no extra cost to you.

Typing a base64 private key into a phone keyboard is unpleasant enough that people copy configs through chat apps instead, which is worse. The QR code exists to avoid both.

It is one command, and the part worth reading is what happens after you scan it.

Generating one

Install the encoder:

apt install qrencode      # Debian, Ubuntu
dnf install qrencode      # Fedora, RHEL

Then, on the machine holding the client config:

qrencode -t ansiutf8 < /etc/wireguard/client1.conf

The code appears in the terminal, drawn in block characters. Open WireGuard on the phone, add a tunnel, scan from QR code, point the camera at the screen. The tunnel is added with its name, addresses, keys and peer, complete.

There is no special format. The QR code contains the config file as plain text. Anything that can read a QR code can read your config.

For a file instead of a terminal drawing:

qrencode -o client1.png < /etc/wireguard/client1.conf

If the code will not scan, the usual causes are a terminal font that distorts the blocks, or a very long config. -t utf8 instead of ansiutf8 renders smaller, and reducing the terminal font size helps more than anything else.

Seen from behind, a woman holds up a smartphone to photograph a large QR code printed on a green panel, her screen showing the code framed inside a payment app in Spanish
Seen from behind, a woman holds up a smartphone to photograph a large QR code printed on a green panel, her screen showing the code framed inside a payment app in Spanish

The part that matters

A WireGuard client config contains PrivateKey. The QR code contains the config. Therefore the QR code is the private key, in a format designed to be read at a distance by a camera.

That changes how it should be handled:

  • Do not photograph it. A photo goes into your camera roll and, on most phones, straight into a cloud backup.
  • Do not put it in a chat message or an email. Both are stored copies you do not control.
  • Clear the terminal after scanning. clear alone does not empty the scrollback. Use reset, or close the terminal window entirely.
  • Delete PNG files immediately. A client1.png sitting in a home directory outlives the reason it was created.
  • Do not display it where a camera can see it, which in a shared office includes the window behind you.

One private key per client is the rule the QR code makes easy to break. Generating one config and scanning it onto three phones gives all three the same identity, and the server then cannot tell them apart, cannot revoke one, and will misroute traffic when two are connected at once. Adding a distinct peer per device is covered in adding a WireGuard peer.

Generating the config first, if you do not have one

The QR code is the last step. Before it, the client needs a key pair and a peer entry on the server:

wg genkey | tee client1.key | wg pubkey > client1.pub

Then a config referencing the server's public key, endpoint and AllowedIPs, and a matching [Peer] block on the server carrying client1.pub. Skipping the server side is the most common reason a scanned tunnel connects to nothing: the phone is configured, and the server has never heard of it.

Full walkthroughs, including the server side, are in PiVPN WireGuard setup for a Raspberry Pi and self-hosting on a VPS for a rented server. Both generate client configs you can pipe straight into qrencode.

If the scan works but the tunnel does not

The QR code has done its job the moment the tunnel appears in the app. Everything after that is ordinary WireGuard.

Check the phone shows a recent handshake. If it does not, work through handshake troubleshooting: endpoint address, port reachable from outside, server firewall, and the peer entry. If the handshake succeeds but pages hang, that is MTU, and it is especially common on mobile networks.

Platform specific setup notes are in WireGuard Android setup and WireGuard iOS setup.

★ Nuremberg GDPR datacenter · ✓ Dedicated IPv4 included · 200+ Mbps guaranteed

Self-host your VPN on your own VPS → ContaboFull root access · public IPv4 · pick your region

Frequently asked questions

How do I create a WireGuard QR code?
Install qrencode and pipe a client config file into it with the terminal output option: qrencode -t ansiutf8 < client.conf. The WireGuard mobile app scans it directly from the screen. There is no separate format, the QR code simply contains the config file as text.
Is a WireGuard QR code safe to share?
No. The QR code contains the entire config file including the client's private key. Anyone who photographs it, or finds it in a screenshot or a scrollback buffer, has full access to your tunnel as that peer.
Can I generate the QR code as an image file?
Yes, qrencode -o client.png < client.conf writes a PNG. Delete it once scanned, because an image file persists in ways a terminal display does not, including in cloud photo backups if it lands in a synced folder.
Why does my phone scan the code but the tunnel does not connect?
The QR code only carries the config. If the handshake never completes, the problem is the config itself or the network path: wrong endpoint, wrong port, firewall on the server, or a peer entry missing on the server side for this client's public key.