docker.recipes

Firezone WireGuard VPN

intermediate

Self-hosted WireGuard VPN server with web UI for managing users and devices.

Overview

Firezone is a modern, open-source WireGuard VPN server that provides enterprise-grade remote access capabilities through an intuitive web interface. Built on the WireGuard protocol, Firezone offers superior performance and security compared to traditional VPN solutions like OpenVPN, while maintaining the simplicity and reliability that makes WireGuard the gold standard for modern VPN implementations. The platform focuses on zero-trust network access principles, allowing organizations to provide secure remote connectivity without the complexity of traditional VPN infrastructure. This deployment creates a complete VPN infrastructure using three specialized services: the Firezone application server handles user management and WireGuard configuration generation, a dedicated PostgreSQL database stores user accounts and device configurations, and Caddy provides automatic HTTPS termination with Let's Encrypt certificates. The Firezone container runs with elevated network privileges (NET_ADMIN and SYS_MODULE capabilities) to manage WireGuard tunnels and IP forwarding, while the entire stack operates on an isolated Docker network for enhanced security. This configuration is ideal for organizations seeking a self-hosted VPN solution that combines the performance benefits of WireGuard with enterprise management features. Unlike simple WireGuard deployments that require manual key management, this stack provides automated user provisioning, device management, and centralized policy control through a modern web interface, making it suitable for teams that need scalable remote access without vendor lock-in.

Key Features

  • WireGuard protocol implementation with automatic key generation and distribution for optimal VPN performance
  • Web-based administration interface for managing users, devices, and network policies without command-line complexity
  • OIDC/SAML single sign-on integration for enterprise authentication systems and identity providers
  • Automatic HTTPS certificate provisioning through Caddy with Let's Encrypt integration
  • PostgreSQL-backed user and device management with full ACID compliance for data integrity
  • Network policy enforcement with subnet routing and DNS configuration per user or group
  • Real-time connection monitoring and logging with detailed client status and bandwidth usage
  • Multi-platform client support with automatic configuration file generation for Windows, macOS, iOS, and Android

Common Use Cases

  • 1Remote workforce enablement for companies transitioning to hybrid work models requiring secure corporate network access
  • 2Homelab enthusiasts seeking professional-grade VPN capabilities with web management for family device access
  • 3Development teams needing secure access to staging environments and internal services across distributed locations
  • 4Small to medium businesses replacing expensive commercial VPN solutions with self-hosted infrastructure
  • 5Educational institutions providing secure network access for students and faculty across campus and remote locations
  • 6Consultants and contractors requiring isolated client network access with professional management capabilities
  • 7Organizations with compliance requirements needing full control over VPN infrastructure and audit logs

Prerequisites

  • Docker host with minimum 1GB RAM for PostgreSQL and Firezone application components
  • Public IP address or dynamic DNS configuration for external client connectivity
  • Domain name with DNS control for automatic HTTPS certificate generation via Let's Encrypt
  • UDP port 51820 accessible from the internet for WireGuard protocol traffic
  • Basic understanding of network routing and firewall configuration for subnet management
  • Administrative access to configure ip_forward and network sysctls on the Docker host

For development & testing. Review security settings, change default credentials, and test thoroughly before production use. See Terms

docker-compose.yml

docker-compose.yml
1services:
2 firezone:
3 image: firezone/firezone:latest
4 container_name: firezone
5 cap_add:
6 - NET_ADMIN
7 - SYS_MODULE
8 environment:
9 - DEFAULT_ADMIN_EMAIL=${ADMIN_EMAIL}
10 - DEFAULT_ADMIN_PASSWORD=${ADMIN_PASSWORD}
11 - EXTERNAL_URL=${EXTERNAL_URL}
12 - DATABASE_HOST=db
13 - DATABASE_NAME=firezone
14 - DATABASE_USER=firezone
15 - DATABASE_PASSWORD=${DB_PASSWORD}
16 - SECRET_KEY_BASE=${SECRET_KEY}
17 - LIVE_VIEW_SIGNING_SALT=${SIGNING_SALT}
18 - COOKIE_SIGNING_SALT=${COOKIE_SALT}
19 - COOKIE_ENCRYPTION_SALT=${ENCRYPTION_SALT}
20 - WIREGUARD_IPV4_ENABLED=true
21 - WIREGUARD_IPV4_NETWORK=10.3.2.0/24
22 - WIREGUARD_IPV4_ADDRESS=10.3.2.1
23 volumes:
24 - firezone-data:/var/firezone
25 ports:
26 - "51820:51820/udp"
27 sysctls:
28 - net.ipv4.ip_forward=1
29 - net.ipv4.conf.all.src_valid_mark=1
30 depends_on:
31 - db
32 networks:
33 - firezone-network
34 restart: unless-stopped
35
36 db:
37 image: postgres:15-alpine
38 container_name: firezone-db
39 environment:
40 - POSTGRES_USER=firezone
41 - POSTGRES_PASSWORD=${DB_PASSWORD}
42 - POSTGRES_DB=firezone
43 volumes:
44 - postgres-data:/var/lib/postgresql/data
45 networks:
46 - firezone-network
47 restart: unless-stopped
48
49 caddy:
50 image: caddy:alpine
51 container_name: firezone-caddy
52 ports:
53 - "80:80"
54 - "443:443"
55 volumes:
56 - ./Caddyfile:/etc/caddy/Caddyfile:ro
57 - caddy-data:/data
58 - caddy-config:/config
59 depends_on:
60 - firezone
61 networks:
62 - firezone-network
63 restart: unless-stopped
64
65volumes:
66 firezone-data:
67 postgres-data:
68 caddy-data:
69 caddy-config:
70
71networks:
72 firezone-network:
73 driver: bridge

.env Template

.env
1# Firezone VPN
2ADMIN_EMAIL=admin@example.com
3ADMIN_PASSWORD=secure_admin_password
4EXTERNAL_URL=https://vpn.example.com
5DB_PASSWORD=secure_db_password
6
7# Generate these with: openssl rand -base64 32
8SECRET_KEY=your_64_char_secret_key
9SIGNING_SALT=your_signing_salt
10COOKIE_SALT=your_cookie_salt
11ENCRYPTION_SALT=your_encryption_salt

Usage Notes

  1. 1Web UI at https://localhost
  2. 2WireGuard on UDP port 51820
  3. 3Add users and devices via web UI
  4. 4Download WireGuard configs for clients
  5. 5Supports SSO with OIDC providers

Individual Services(3 services)

Copy individual services to mix and match with your existing compose files.

firezone
firezone:
  image: firezone/firezone:latest
  container_name: firezone
  cap_add:
    - NET_ADMIN
    - SYS_MODULE
  environment:
    - DEFAULT_ADMIN_EMAIL=${ADMIN_EMAIL}
    - DEFAULT_ADMIN_PASSWORD=${ADMIN_PASSWORD}
    - EXTERNAL_URL=${EXTERNAL_URL}
    - DATABASE_HOST=db
    - DATABASE_NAME=firezone
    - DATABASE_USER=firezone
    - DATABASE_PASSWORD=${DB_PASSWORD}
    - SECRET_KEY_BASE=${SECRET_KEY}
    - LIVE_VIEW_SIGNING_SALT=${SIGNING_SALT}
    - COOKIE_SIGNING_SALT=${COOKIE_SALT}
    - COOKIE_ENCRYPTION_SALT=${ENCRYPTION_SALT}
    - WIREGUARD_IPV4_ENABLED=true
    - WIREGUARD_IPV4_NETWORK=10.3.2.0/24
    - WIREGUARD_IPV4_ADDRESS=10.3.2.1
  volumes:
    - firezone-data:/var/firezone
  ports:
    - 51820:51820/udp
  sysctls:
    - net.ipv4.ip_forward=1
    - net.ipv4.conf.all.src_valid_mark=1
  depends_on:
    - db
  networks:
    - firezone-network
  restart: unless-stopped
db
db:
  image: postgres:15-alpine
  container_name: firezone-db
  environment:
    - POSTGRES_USER=firezone
    - POSTGRES_PASSWORD=${DB_PASSWORD}
    - POSTGRES_DB=firezone
  volumes:
    - postgres-data:/var/lib/postgresql/data
  networks:
    - firezone-network
  restart: unless-stopped
caddy
caddy:
  image: caddy:alpine
  container_name: firezone-caddy
  ports:
    - "80:80"
    - "443:443"
  volumes:
    - ./Caddyfile:/etc/caddy/Caddyfile:ro
    - caddy-data:/data
    - caddy-config:/config
  depends_on:
    - firezone
  networks:
    - firezone-network
  restart: unless-stopped

Quick Start

terminal
1# 1. Create the compose file
2cat > docker-compose.yml << 'EOF'
3services:
4 firezone:
5 image: firezone/firezone:latest
6 container_name: firezone
7 cap_add:
8 - NET_ADMIN
9 - SYS_MODULE
10 environment:
11 - DEFAULT_ADMIN_EMAIL=${ADMIN_EMAIL}
12 - DEFAULT_ADMIN_PASSWORD=${ADMIN_PASSWORD}
13 - EXTERNAL_URL=${EXTERNAL_URL}
14 - DATABASE_HOST=db
15 - DATABASE_NAME=firezone
16 - DATABASE_USER=firezone
17 - DATABASE_PASSWORD=${DB_PASSWORD}
18 - SECRET_KEY_BASE=${SECRET_KEY}
19 - LIVE_VIEW_SIGNING_SALT=${SIGNING_SALT}
20 - COOKIE_SIGNING_SALT=${COOKIE_SALT}
21 - COOKIE_ENCRYPTION_SALT=${ENCRYPTION_SALT}
22 - WIREGUARD_IPV4_ENABLED=true
23 - WIREGUARD_IPV4_NETWORK=10.3.2.0/24
24 - WIREGUARD_IPV4_ADDRESS=10.3.2.1
25 volumes:
26 - firezone-data:/var/firezone
27 ports:
28 - "51820:51820/udp"
29 sysctls:
30 - net.ipv4.ip_forward=1
31 - net.ipv4.conf.all.src_valid_mark=1
32 depends_on:
33 - db
34 networks:
35 - firezone-network
36 restart: unless-stopped
37
38 db:
39 image: postgres:15-alpine
40 container_name: firezone-db
41 environment:
42 - POSTGRES_USER=firezone
43 - POSTGRES_PASSWORD=${DB_PASSWORD}
44 - POSTGRES_DB=firezone
45 volumes:
46 - postgres-data:/var/lib/postgresql/data
47 networks:
48 - firezone-network
49 restart: unless-stopped
50
51 caddy:
52 image: caddy:alpine
53 container_name: firezone-caddy
54 ports:
55 - "80:80"
56 - "443:443"
57 volumes:
58 - ./Caddyfile:/etc/caddy/Caddyfile:ro
59 - caddy-data:/data
60 - caddy-config:/config
61 depends_on:
62 - firezone
63 networks:
64 - firezone-network
65 restart: unless-stopped
66
67volumes:
68 firezone-data:
69 postgres-data:
70 caddy-data:
71 caddy-config:
72
73networks:
74 firezone-network:
75 driver: bridge
76EOF
77
78# 2. Create the .env file
79cat > .env << 'EOF'
80# Firezone VPN
81ADMIN_EMAIL=admin@example.com
82ADMIN_PASSWORD=secure_admin_password
83EXTERNAL_URL=https://vpn.example.com
84DB_PASSWORD=secure_db_password
85
86# Generate these with: openssl rand -base64 32
87SECRET_KEY=your_64_char_secret_key
88SIGNING_SALT=your_signing_salt
89COOKIE_SALT=your_cookie_salt
90ENCRYPTION_SALT=your_encryption_salt
91EOF
92
93# 3. Start the services
94docker compose up -d
95
96# 4. View logs
97docker compose logs -f

One-Liner

Run this command to download and set up the recipe in one step:

terminal
1curl -fsSL https://docker.recipes/api/recipes/firezone-vpn/run | bash

Troubleshooting

  • Clients cannot connect to VPN: Verify UDP port 51820 is open in firewall and router port forwarding is configured correctly
  • Web UI shows database connection errors: Check that db container is healthy and DB_PASSWORD environment variable matches between firezone and db services
  • HTTPS certificate generation fails: Ensure domain DNS points to server IP and ports 80/443 are accessible for Let's Encrypt validation
  • WireGuard tunnel establishes but no internet access: Verify net.ipv4.ip_forward=1 sysctl is applied and Docker host IP forwarding is enabled
  • firezone container fails to start with capability errors: Ensure Docker daemon has privileges to grant NET_ADMIN and SYS_MODULE capabilities to containers
  • Configuration changes not persisting: Check that firezone-data volume is properly mounted and has correct permissions for the container user

Community Notes

Loading...
Loading notes...

Download Recipe Kit

Get all files in a ready-to-deploy package

Includes docker-compose.yml, .env template, README, and license

Ad Space