Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

You can host a WireGuard VPN server in Docker by running the LinuxServer.io WireGuard image, persisting its /config directory, creating one peer configuration per client, and making the server’s UDP port reachable from the internet. A running container alone does not guarantee remote access: you may also need to configure the Docker host firewall and forward the UDP port through your router. Before importing a client profile, decide whether it should route all traffic through the VPN or only selected networks.

What you need before starting

  • A Linux host running Docker and Docker Compose, with a kernel that supports WireGuard. The LinuxServer.io image guide also discusses loading required modules on the host if they are not already available.
  • A host directory for persistent configuration files. The example below uses /opt/wireguard; choose a path appropriate to your system and make sure the mapped user can write to it.
  • A reachable public endpoint: an external IP address or domain name, plus a UDP port you can publish and, if the Docker host is behind a router, forward to that host.
  • Client devices on which you can import a WireGuard configuration or scan its QR code.

The steps use the LinuxServer.io image, documented at LinuxServer.io’s WireGuard image page. Its project README describes WireGuard as “an extremely simple yet fast and modern VPN that utilizes state-of-the-art cryptography”; that is the project’s characterization, not an independent performance comparison. LinuxServer.io project README.

Choose what client traffic will use the VPN

Make this decision before distributing client profiles. The image documentation’s default ALLOWEDIPS value, 0.0.0.0/0, ::0/0, routes all IPv4 and IPv6 traffic through the VPN. This is a full tunnel. For split tunneling, narrow ALLOWEDIPS to the networks clients should reach through the server, and include the server’s WireGuard address where appropriate; the documentation gives 10.13.13.1 as an example. Use the actual network ranges in your setup rather than copying that example blindly. LinuxServer.io configuration guidance.

Choice Client routing behavior Useful when
Full tunnel All IPv4 and IPv6 traffic is routed through the VPN when connected. You want the client’s general internet traffic to use the VPN server’s connection.
Split tunnel Only the specified networks use the VPN; other traffic follows the client’s usual route. You need access to selected home or private networks without sending all client traffic through the VPN.

Check the generated client profile’s AllowedIPs before importing it. A profile with the full-tunnel default has a different effect from one limited to your LAN or other selected networks.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Create a Docker Compose deployment

LinuxServer.io recommends Compose for this image. Create a directory for the deployment and save a Compose file there. Replace the example endpoint, user and group IDs, timezone, peer names, and any network values with choices for your host and network.

services:
  wireguard:
    image: lscr.io/linuxserver/wireguard:latest
    container_name: wireguard
    cap_add:
      - NET_ADMIN
      # Optional: use when required modules are not loaded on the host
      - SYS_MODULE
    environment:
      - PUID=1000
      - PGID=1000
      - TZ=Etc/UTC
      - SERVERURL=your-public-ip-or-domain
      - SERVERPORT=51820
      - PEERS=phone,laptop
      - PEERDNS=auto
      - INTERNAL_SUBNET=10.13.13.0
      - ALLOWEDIPS=0.0.0.0/0,::0/0
      # Optional; example peer names and interval are documented by LinuxServer.io
      # - PERSISTENTKEEPALIVE_PEERS=phone,laptop
    volumes:
      - /opt/wireguard:/config
      # Optional: mount when the container needs access to host modules
      - /lib/modules:/lib/modules
    ports:
      - 51820:51820/udp
    sysctls:
      - net.ipv4.conf.all.src_valid_mark=1
    restart: unless-stopped

The values shown are an example based on LinuxServer.io’s documented Compose configuration, not universal requirements. In particular, the sample uses UDP port 51820 and tunnel network 10.13.13.0; adapt them if your network or deployment requires different values. NET_ADMIN is needed by the image to create the WireGuard interface. SYS_MODULE and the /lib/modules mount are optional when the needed modules are already loaded; alternatively, load the modules on the host. LinuxServer.io marks the net.ipv4.conf.all.src_valid_mark=1 sysctl as required for client mode, so do not treat it as a universal server-mode requirement. Some Portainer versions may not correctly apply the capabilities or sysctl this image needs. LinuxServer.io image documentation.

PUID and PGID map the container’s file access to a host user and group to help avoid volume-permission problems. Set them to IDs that make sense for your host. The /config bind mount is important: it keeps generated server and peer files outside the container so they persist across restarts and container replacement.

Set the endpoint and peer values

  • SERVERURL is the external IP address or domain clients use to reach the server. If your public IP changes, a domain backed by a suitable dynamic-DNS arrangement can provide a stable name; the image documentation supports using an IP or domain.
  • SERVERPORT is the external port advertised to clients. Keep it consistent with the published container port and the router’s forwarding rule, unless you intentionally use a different external port.
  • PEERS creates client profiles. A number creates that many peers; comma-separated names make them easier to identify, such as phone,laptop.
  • PEERDNS selects DNS behavior for clients. Choose a resolver appropriate to the networks you want clients to use.
  • INTERNAL_SUBNET sets the WireGuard tunnel’s internal network. Avoid choosing a range that conflicts with networks clients already use.
  • ALLOWEDIPS sets the traffic routes in the generated client profiles. Use the full-tunnel or split-tunnel choice described above.
  • PERSISTENTKEEPALIVE_PEERS is an optional setting for peers that need keepalives; the documentation’s example interval is 25 seconds when enabled for listed peers. This is a configuration option, not a universal requirement.

Start the container and create client profiles

  1. From the directory containing your Compose file, run docker compose up -d.
  2. Check startup output with docker compose logs -f wireguard. Resolve any image, permission, capability, or module errors shown there before proceeding.
  3. Inspect the host directory you mounted at /config. LinuxServer.io’s image generates server and peer configuration files there, including client configuration files and QR code images.
  4. Import the configuration for each device using its WireGuard app, or scan that peer’s QR code. Use a distinct peer for each client device so you can identify and manage configurations individually.
  5. Protect the configuration files and any QR codes. They contain connection credentials for the VPN. If you enable LOG_CONFS=true, configuration QR codes can also appear in Docker logs; treat access to those logs as sensitive.

Do not publish peer files or QR codes. If you need to change server-mode environment variables later, review LinuxServer.io’s regeneration behavior first: several variables trigger configuration regeneration. The documentation says existing peer keys are retained during normal regeneration, while deleting peer folders changes that behavior. Keep a recoverable copy of /config before making such changes. LinuxServer.io regeneration notes.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Allow connections from outside your network

The Compose example publishes UDP port 51820 from the container to the Docker host. Publishing the port does not by itself make the service reachable from the internet. If the host is behind a home router, create an inbound UDP forwarding rule from the router’s external port to the Docker host’s LAN address and the chosen UDP port. Also check that the host firewall allows that traffic. The exact settings depend on your router, host firewall, addressing, and network provider. A router that cannot forward the needed UDP port may need to be replaced or supplemented; new hardware is unnecessary if your current router can do the job. LinuxServer.io port and networking guidance.

For clients connecting from outside, make sure the profile’s endpoint uses the public IP or domain and the correct external UDP port. A private LAN address works only for clients that can reach that LAN directly.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Test remote access and handle connections from home

Test from a network outside the server’s LAN, such as a phone using mobile data. Activate the peer and check whether it can reach the networks or internet traffic you intended to route. A successful container start is not proof of public reachability: the path also depends on port publishing, host firewall rules, router forwarding, public addressing, and any restrictions imposed by the network provider.

When a client is back on the same home LAN, using the public endpoint may fail even though remote access works. Some routers do not send a connection from the LAN to the public WAN address back to the LAN server, a behavior commonly called hairpin NAT or NAT reflection. Depending on your network, use router NAT reflection or split-horizon DNS so the same domain resolves to an internal address at home and a public address away from home. LinuxServer.io local-access notes.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Troubleshoot common setup failures

  • The container exits or the interface is not created: inspect docker compose logs wireguard. Check that Docker has applied NET_ADMIN, and confirm the host kernel has WireGuard and required iptables support. If needed, load the relevant modules on the host or use the optional module access documented for the image.
  • The client cannot connect from outside: confirm the Compose port mapping is UDP, the router forwards the selected external UDP port to the Docker host, and the host firewall allows it. Verify the client endpoint is the public address or domain and uses the correct port.
  • The tunnel connects but the client cannot reach the intended destination: inspect the profile’s AllowedIPs and confirm it includes the network you want to route. For split tunneling, include only the selected destinations; for full tunneling, the profile must include the all-traffic routes.
  • The server is reachable remotely but not from the home LAN using its public name: check whether the router supports NAT reflection. If not, split-horizon DNS or a LAN-specific endpoint may fit your network better.
  • Changes do not appear in peer files: check whether the changed environment variable triggers configuration regeneration and review the image’s rules before removing or altering peer folders. Back up /config first.

LinuxServer.io documents the image’s configuration and networking behavior, but your actual router, firewall, ISP, and client setup determine whether a particular deployment is reachable.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.