From c17db222ccfa4d4ae249c4f0fd4aa535da0fe6b0 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 12 Jun 2026 11:54:22 +0000 Subject: [PATCH] Deployed ac547bb to edge with MkDocs 1.6.1 and mike 2.2.0 --- edge/404.html | 28 + edge/advanced/api/index.html | 28 + edge/advanced/config/amnezia/index.html | 28 + .../config/experimental-config/index.html | 30 +- .../config/external-authentication/index.html | 2514 +++++++++++++++++ .../config/optional-config/index.html | 30 +- .../config/unattended-setup/index.html | 28 + edge/advanced/metrics/prometheus/index.html | 28 + .../advanced/migrate/from-14-to-15/index.html | 28 + edge/advanced/migrate/index.html | 28 + edge/contributing/general/index.html | 28 + .../issues-and-pull-requests/index.html | 28 + edge/contributing/translation/index.html | 28 + edge/examples/tutorials/adguard/index.html | 28 + .../tutorials/auto-updates/index.html | 28 + .../tutorials/basic-installation/index.html | 30 +- edge/examples/tutorials/caddy/index.html | 28 + edge/examples/tutorials/docker-run/index.html | 28 + edge/examples/tutorials/dockerless/index.html | 28 + edge/examples/tutorials/podman-nft/index.html | 28 + .../tutorials/reverse-proxyless/index.html | 28 + edge/examples/tutorials/routed/index.html | 28 + edge/examples/tutorials/traefik/index.html | 28 + edge/faq/index.html | 28 + edge/getting-started/index.html | 28 + edge/guides/2fa/index.html | 28 + edge/guides/admin/index.html | 28 + edge/guides/cli/index.html | 28 + edge/guides/clients/index.html | 28 + edge/guides/setup/index.html | 28 + edge/index.html | 28 + edge/search/search_index.json | 2 +- edge/sitemap.xml | 62 +- edge/sitemap.xml.gz | Bin 467 -> 479 bytes 34 files changed, 3391 insertions(+), 33 deletions(-) create mode 100644 edge/advanced/config/external-authentication/index.html diff --git a/edge/404.html b/edge/404.html index d3052352..499a0963 100644 --- a/edge/404.html +++ b/edge/404.html @@ -677,6 +677,34 @@ +
  • + + + + + + + + External Authentication + + + + + + + + +
  • + + + + + + + + + +
  • diff --git a/edge/advanced/api/index.html b/edge/advanced/api/index.html index 70743ad0..0742fc0c 100644 --- a/edge/advanced/api/index.html +++ b/edge/advanced/api/index.html @@ -792,6 +792,34 @@ +
  • + + + + + + + + External Authentication + + + + + + + + +
  • + + + + + + + + + +
  • diff --git a/edge/advanced/config/amnezia/index.html b/edge/advanced/config/amnezia/index.html index 0f62534b..7a169aef 100644 --- a/edge/advanced/config/amnezia/index.html +++ b/edge/advanced/config/amnezia/index.html @@ -797,6 +797,34 @@ +
  • + + + + + + + + External Authentication + + + + + + + + +
  • + + + + + + + + + +
  • diff --git a/edge/advanced/config/experimental-config/index.html b/edge/advanced/config/experimental-config/index.html index edc0ebdf..2cd7f85a 100644 --- a/edge/advanced/config/experimental-config/index.html +++ b/edge/advanced/config/experimental-config/index.html @@ -18,7 +18,7 @@ - + @@ -696,6 +696,34 @@ +
  • + + + + + + + + External Authentication + + + + + + + + +
  • + + + + + + + + + +
  • diff --git a/edge/advanced/config/external-authentication/index.html b/edge/advanced/config/external-authentication/index.html new file mode 100644 index 00000000..efb1bc16 --- /dev/null +++ b/edge/advanced/config/external-authentication/index.html @@ -0,0 +1,2514 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + External Authentication - wg-easy + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + Skip to content + + +
    +
    + +
    + + + + + + +
    + + +
    + +
    + + + + + + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + + + + + +
    + +
    + + + + + + + + + + + + + + + + + + + + + +

    External Authentication

    + +

    OAuth

    +

    Setup

    +

    To enable OAuth set the env var OAUTH_PROVIDERS to any of the following providers:

    + + + + + + + + + + + + + + + + + + + + + +
    ProviderValue
    Googlegoogle
    GitHubgithub
    Generic OIDCoidc
    +

    You can enable multiple providers by separating them with a comma:

    +

    e.g. google,github

    +

    Auto Register

    +

    To automatically register users that log in with an OAuth provider, set the following environment variable to true:

    + + + + + + + + + + + + + + + + + +
    EnvRequiredDefaultDescription
    OAUTH_AUTO_REGISTER✖️falseEnable auto-registration
    +

    When enabled:

    +
      +
    • +

      If a user logs in with an email address that is not yet registered, a new account will be created for them.

      +
    • +
    • +

      If a user logs in with an email address that is already registered, their account will be linked to the OAuth provider (if not already linked), regardless of the value of OAUTH_AUTO_REGISTER.

      +
    • +
    +
    +

     Security

    +

    Users will be created with Admin Permissions, as the permissions system is not yet implemented. Only enable this if you trust all users that can log in with the OAuth provider.

    +

    Use Allowed Domains to restrict which users can log in.

    +
    +

    Allowed Domains

    +

    To only allow users with an email address from a specific domain to log in, set the following environment variable to the allowed domain.

    + + + + + + + + + + + + + + + + + +
    EnvRequiredDefaultDescription
    OAUTH_ALLOWED_DOMAINS✖️-Allowed email domains
    +

    You can allow multiple domains by separating them with a comma:

    +

    e.g. example.com,example.org

    +

    Auto Launch

    +

    To automatically launch the OAuth login flow when visiting the login page, set the following environment variable to the provider you want to launch:

    + + + + + + + + + + + + + + + + + +
    EnvRequiredDefaultDescription
    OAUTH_AUTO_LAUNCH✖️-Auto launch an OAuth provider
    +

    When enabled:

    +
      +
    • Visiting the login page will automatically redirect to the selected provider's login page
    • +
    • The user can still access the normal login page by visiting /login?auto_launch=false
    • +
    • You can auto launch any provider by visiting /login?auto_launch=<provider>
    • +
    +

    Redirect URIs

    +

    You have to configure the following redirect URIs in your OAuth provider:

    +
      +
    • https://<your-domain>/api/auth/<provider>/callback + Used to log in to with the provider
    • +
    • https://<your-domain>/api/auth/<provider>/link + Used to link an existing account to the provider
    • +
    +

    If your provider does not support multiple redirect URIs (e.g. GitHub) but allows multiple URIs under the same base, then configure:

    +
      +
    • https://<your-domain>/api/auth/<provider>/
    • +
    +

    Provider Configuration

    +

    Google

    + + + + + + + + + + + + + + + + + + + + +
    EnvRequiredDescription
    OAUTH_GOOGLE_CLIENT_ID✔️Google Client ID
    OAUTH_GOOGLE_CLIENT_SECRET✔️Google Client Secret
    +
    Setup
    + +
      +
    1. Go to Google Cloud Console
    2. +
    3. Create an OAuth 2.0 Client ID (Web application)
    4. +
    5. Add Authorized redirect URI: See Redirect URIs
    6. +
    7. Copy the Client ID and Client Secret to the environment variables
    8. +
    +

    GitHub

    + + + + + + + + + + + + + + + + + + + + +
    EnvRequiredDescription
    OAUTH_GITHUB_CLIENT_ID✔️GitHub Client ID
    OAUTH_GITHUB_CLIENT_SECRET✔️GitHub Client Secret
    +
    Setup
    + +
      +
    1. Go to GitHub Developer Settings
    2. +
    3. Create a new OAuth App
    4. +
    5. Add Authorization callback URL: See Redirect URIs
    6. +
    7. Create a new client secret
    8. +
    9. Copy the Client ID and Client Secret to the environment variables
    10. +
    +

    Generic OIDC

    +

    This supports generic OIDC providers like Authelia, Authentik, etc.

    +

    The provider needs to support:

    +
      +
    • PKCE
    • +
    • default scopes: openid email profile
    • +
    • Client Secret Authentication client_secret_post
    • +
    +

    The provider needs to be available with HTTPS and have a valid certificate.

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    EnvRequiredDefaultExampleDescription
    OAUTH_OIDC_SERVER✔️-https://auth.example.comOIDC Server
    OAUTH_OIDC_CLIENT_ID✔️--OIDC Client ID
    OAUTH_OIDC_CLIENT_SECRET✔️--OIDC Client Secret
    OAUTH_OIDC_NAME✖️OIDCAutheliaProvider Name
    +
    Authelia Setup
    +

    Generate Client ID and Secret:

    +
    # Client ID
    +docker run --rm authelia/authelia:latest authelia crypto rand --length 72 --charset rfc3986
    +# Client Secret
    +docker run --rm authelia/authelia:latest authelia crypto hash generate pbkdf2 --variant sha512 --random --random.length 72 --random.charset rfc3986
    +
    +
    - client_id: '...'
    +  client_name: wg-easy
    +  client_secret: '$pbkdf2-...'
    +  redirect_uris:
    +      - https://<your-domain>/api/auth/oidc/callback
    +      - https://<your-domain>/api/auth/oidc/link
    +  scopes:
    +      - openid
    +      - profile
    +      - email
    +  authorization_policy: one_factor
    +  pre_configured_consent_duration: 1 week
    +  require_pkce: true
    +  token_endpoint_auth_method: client_secret_post
    +
    +

    Generic OAuth

    +

    Not currently supported

    +

    Disable Password Authentication

    +

    To disable password-based authentication and only allow login via OAuth providers, set the following environment variable to true:

    + + + + + + + + + + + + + + + + + +
    EnvRequiredDefaultDescription
    DISABLE_PASSWORD_AUTH✖️falseDisable password authentication
    +

    When enabled:

    +
      +
    • Users will not be able to log in with a password
    • +
    +
    +

    Access Recovery

    +

    Before disabling password authentication, ensure that at least one OAuth provider is configured and that you have successfully linked an administrator account.

    +

    If no login method is available, you will not be able to log in to the application and will need to reset the configuration to regain access.

    +
    + + + + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + +
    + + +
    + +
    +
    +
    +
    + + + + + + + + + + + + + \ No newline at end of file diff --git a/edge/advanced/config/optional-config/index.html b/edge/advanced/config/optional-config/index.html index c3597090..a2075e7e 100644 --- a/edge/advanced/config/optional-config/index.html +++ b/edge/advanced/config/optional-config/index.html @@ -15,7 +15,7 @@ - + @@ -686,6 +686,34 @@ + + +
  • + + + + + + + + External Authentication + + + + + + + + +
  • + + + + + + + + diff --git a/edge/advanced/config/unattended-setup/index.html b/edge/advanced/config/unattended-setup/index.html index 24a4b63d..651b595d 100644 --- a/edge/advanced/config/unattended-setup/index.html +++ b/edge/advanced/config/unattended-setup/index.html @@ -688,6 +688,34 @@ +
  • + + + + + + + + External Authentication + + + + + + + + +
  • + + + + + + + + + +
  • diff --git a/edge/advanced/metrics/prometheus/index.html b/edge/advanced/metrics/prometheus/index.html index 58a12893..4c6448c5 100644 --- a/edge/advanced/metrics/prometheus/index.html +++ b/edge/advanced/metrics/prometheus/index.html @@ -693,6 +693,34 @@ +
  • + + + + + + + + External Authentication + + + + + + + + +
  • + + + + + + + + + +
  • diff --git a/edge/advanced/migrate/from-14-to-15/index.html b/edge/advanced/migrate/from-14-to-15/index.html index 73cf8d8c..777f358f 100644 --- a/edge/advanced/migrate/from-14-to-15/index.html +++ b/edge/advanced/migrate/from-14-to-15/index.html @@ -693,6 +693,34 @@ +
  • + + + + + + + + External Authentication + + + + + + + + +
  • + + + + + + + + + +
  • diff --git a/edge/advanced/migrate/index.html b/edge/advanced/migrate/index.html index 7e7566bb..f4f0d310 100644 --- a/edge/advanced/migrate/index.html +++ b/edge/advanced/migrate/index.html @@ -688,6 +688,34 @@ +
  • + + + + + + + + External Authentication + + + + + + + + +
  • + + + + + + + + + +
  • diff --git a/edge/contributing/general/index.html b/edge/contributing/general/index.html index 59118b20..f4fbbcee 100644 --- a/edge/contributing/general/index.html +++ b/edge/contributing/general/index.html @@ -690,6 +690,34 @@ +
  • + + + + + + + + External Authentication + + + + + + + + +
  • + + + + + + + + + +
  • diff --git a/edge/contributing/issues-and-pull-requests/index.html b/edge/contributing/issues-and-pull-requests/index.html index 01ddefed..7d667405 100644 --- a/edge/contributing/issues-and-pull-requests/index.html +++ b/edge/contributing/issues-and-pull-requests/index.html @@ -690,6 +690,34 @@ +
  • + + + + + + + + External Authentication + + + + + + + + +
  • + + + + + + + + + +
  • diff --git a/edge/contributing/translation/index.html b/edge/contributing/translation/index.html index f0d6e64f..261cd5b2 100644 --- a/edge/contributing/translation/index.html +++ b/edge/contributing/translation/index.html @@ -690,6 +690,34 @@ +
  • + + + + + + + + External Authentication + + + + + + + + +
  • + + + + + + + + + +
  • diff --git a/edge/examples/tutorials/adguard/index.html b/edge/examples/tutorials/adguard/index.html index 1c4319b9..f75c4943 100644 --- a/edge/examples/tutorials/adguard/index.html +++ b/edge/examples/tutorials/adguard/index.html @@ -692,6 +692,34 @@ +
  • + + + + + + + + External Authentication + + + + + + + + +
  • + + + + + + + + + +
  • diff --git a/edge/examples/tutorials/auto-updates/index.html b/edge/examples/tutorials/auto-updates/index.html index 563ad52c..6fa692ab 100644 --- a/edge/examples/tutorials/auto-updates/index.html +++ b/edge/examples/tutorials/auto-updates/index.html @@ -692,6 +692,34 @@ +
  • + + + + + + + + External Authentication + + + + + + + + +
  • + + + + + + + + + +
  • diff --git a/edge/examples/tutorials/basic-installation/index.html b/edge/examples/tutorials/basic-installation/index.html index fbc65565..608e20e7 100644 --- a/edge/examples/tutorials/basic-installation/index.html +++ b/edge/examples/tutorials/basic-installation/index.html @@ -692,6 +692,34 @@ +
  • + + + + + + + + External Authentication + + + + + + + + +
  • + + + + + + + + + +
  • @@ -1961,7 +1989,7 @@

    Basic Installation

    - +

    Requirements

      diff --git a/edge/examples/tutorials/caddy/index.html b/edge/examples/tutorials/caddy/index.html index 4a39327d..11b15d30 100644 --- a/edge/examples/tutorials/caddy/index.html +++ b/edge/examples/tutorials/caddy/index.html @@ -692,6 +692,34 @@ +
    1. + + + + + + + + External Authentication + + + + + + + + +
    2. + + + + + + + + + +
    3. diff --git a/edge/examples/tutorials/docker-run/index.html b/edge/examples/tutorials/docker-run/index.html index a5f37142..8369334a 100644 --- a/edge/examples/tutorials/docker-run/index.html +++ b/edge/examples/tutorials/docker-run/index.html @@ -687,6 +687,34 @@ +
    4. + + + + + + + + External Authentication + + + + + + + + +
    5. + + + + + + + + + +
    6. diff --git a/edge/examples/tutorials/dockerless/index.html b/edge/examples/tutorials/dockerless/index.html index 0a7c608d..1016af55 100644 --- a/edge/examples/tutorials/dockerless/index.html +++ b/edge/examples/tutorials/dockerless/index.html @@ -687,6 +687,34 @@ +
    7. + + + + + + + + External Authentication + + + + + + + + +
    8. + + + + + + + + + +
    9. diff --git a/edge/examples/tutorials/podman-nft/index.html b/edge/examples/tutorials/podman-nft/index.html index 8bc90b7a..d5ad6761 100644 --- a/edge/examples/tutorials/podman-nft/index.html +++ b/edge/examples/tutorials/podman-nft/index.html @@ -692,6 +692,34 @@ +
    10. + + + + + + + + External Authentication + + + + + + + + +
    11. + + + + + + + + + +
    12. diff --git a/edge/examples/tutorials/reverse-proxyless/index.html b/edge/examples/tutorials/reverse-proxyless/index.html index fa29a77c..f12af63a 100644 --- a/edge/examples/tutorials/reverse-proxyless/index.html +++ b/edge/examples/tutorials/reverse-proxyless/index.html @@ -692,6 +692,34 @@ +
    13. + + + + + + + + External Authentication + + + + + + + + +
    14. + + + + + + + + + +
    15. diff --git a/edge/examples/tutorials/routed/index.html b/edge/examples/tutorials/routed/index.html index 2eb8b0b7..fa08e901 100644 --- a/edge/examples/tutorials/routed/index.html +++ b/edge/examples/tutorials/routed/index.html @@ -692,6 +692,34 @@ +
    16. + + + + + + + + External Authentication + + + + + + + + +
    17. + + + + + + + + + +
    18. diff --git a/edge/examples/tutorials/traefik/index.html b/edge/examples/tutorials/traefik/index.html index fd060d2a..92795a01 100644 --- a/edge/examples/tutorials/traefik/index.html +++ b/edge/examples/tutorials/traefik/index.html @@ -692,6 +692,34 @@ +
    19. + + + + + + + + External Authentication + + + + + + + + +
    20. + + + + + + + + + +
    21. diff --git a/edge/faq/index.html b/edge/faq/index.html index 2b0c8b3c..3f6b8b60 100644 --- a/edge/faq/index.html +++ b/edge/faq/index.html @@ -812,6 +812,34 @@ +
    22. + + + + + + + + External Authentication + + + + + + + + +
    23. + + + + + + + + + +
    24. diff --git a/edge/getting-started/index.html b/edge/getting-started/index.html index efba14f5..4abfe090 100644 --- a/edge/getting-started/index.html +++ b/edge/getting-started/index.html @@ -802,6 +802,34 @@ +
    25. + + + + + + + + External Authentication + + + + + + + + +
    26. + + + + + + + + + +
    27. diff --git a/edge/guides/2fa/index.html b/edge/guides/2fa/index.html index 75b052d2..fbf031cf 100644 --- a/edge/guides/2fa/index.html +++ b/edge/guides/2fa/index.html @@ -690,6 +690,34 @@ +
    28. + + + + + + + + External Authentication + + + + + + + + +
    29. + + + + + + + + + +
    30. diff --git a/edge/guides/admin/index.html b/edge/guides/admin/index.html index d0f48301..c34ff5b9 100644 --- a/edge/guides/admin/index.html +++ b/edge/guides/admin/index.html @@ -690,6 +690,34 @@ +
    31. + + + + + + + + External Authentication + + + + + + + + +
    32. + + + + + + + + + +
    33. diff --git a/edge/guides/cli/index.html b/edge/guides/cli/index.html index 642476fa..96105c38 100644 --- a/edge/guides/cli/index.html +++ b/edge/guides/cli/index.html @@ -690,6 +690,34 @@ +
    34. + + + + + + + + External Authentication + + + + + + + + +
    35. + + + + + + + + + +
    36. diff --git a/edge/guides/clients/index.html b/edge/guides/clients/index.html index d905fdf0..57329a3d 100644 --- a/edge/guides/clients/index.html +++ b/edge/guides/clients/index.html @@ -690,6 +690,34 @@ +
    37. + + + + + + + + External Authentication + + + + + + + + +
    38. + + + + + + + + + +
    39. diff --git a/edge/guides/setup/index.html b/edge/guides/setup/index.html index f0cae63a..aa6049c7 100644 --- a/edge/guides/setup/index.html +++ b/edge/guides/setup/index.html @@ -688,6 +688,34 @@ +
    40. + + + + + + + + External Authentication + + + + + + + + +
    41. + + + + + + + + + +
    42. diff --git a/edge/index.html b/edge/index.html index 34313afe..ad275ff1 100644 --- a/edge/index.html +++ b/edge/index.html @@ -798,6 +798,34 @@ +
    43. + + + + + + + + External Authentication + + + + + + + + +
    44. + + + + + + + + + +
    45. diff --git a/edge/search/search_index.json b/edge/search/search_index.json index 765cfa5a..4dec2020 100644 --- a/edge/search/search_index.json +++ b/edge/search/search_index.json @@ -1 +1 @@ -{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"],"fields":{"title":{"boost":1000.0},"text":{"boost":1.0},"tags":{"boost":1000000.0}}},"docs":[{"location":"","title":"Welcome to the Documentation for wg-easy","text":"

      \u00a0This Documentation is Versioned

      Make sure to select the correct version of this documentation! It should match the version of the image you are using. The default version corresponds to the most recent stable release.

      This documentation provides you not only with the basic setup and configuration of wg-easy but also with advanced configuration, elaborate usage scenarios, detailed examples, hints and more.

      "},{"location":"#about","title":"About","text":"

      wg-easy is the easiest way to run WireGuard VPN + Web-based Admin UI.

      "},{"location":"#contents","title":"Contents","text":""},{"location":"#getting-started","title":"Getting Started","text":"

      If you're new to wg-easy, make sure to read the Getting Started chapter first. If you want to look at examples for Docker Run and Compose, we have an Examples page.

      "},{"location":"#contributing","title":"Contributing","text":"

      We are always happy to welcome new contributors. For guidelines and entrypoints please have a look at the Contributing section.

      "},{"location":"#migration","title":"Migration","text":"

      If you are migrating from an older version of wg-easy, please read the Migration chapter.

      "},{"location":"faq/","title":"FAQ","text":"

      Here are some frequently asked questions or errors about wg-easy. If you have a question that is not answered here, please feel free to open a discussion on GitHub.

      "},{"location":"faq/#how-do-i-restrict-client-access-to-specific-networks-or-servers","title":"How do I restrict client access to specific networks or servers?","text":"

      Use the Per-Client Firewall feature to enforce server-side restrictions on what each client can access.

      Requirements: This feature requires iptables (and ip6tables for IPv6) to be installed on the host system.

      1. Enable \"Per-Client Firewall\" in Admin Panel \u2192 Interface
      2. Edit a client and configure \"Firewall Allowed IPs\"
      3. Specify which destinations the client should be allowed to access

      Unlike \"Allowed IPs\" which only controls client-side routing, firewall rules are enforced by the server and cannot be bypassed.

      See the Admin Panel Guide and Client Guide for detailed configuration.

      "},{"location":"faq/#error-wireguard-exited-with-the-error-cannot-find-device-wg0","title":"Error: WireGuard exited with the error: Cannot find device \"wg0\"","text":"

      This error indicates that the WireGuard interface wg0 does not exist. This can happen if the WireGuard kernel module is not loaded or if the interface was not created properly.

      To resolve this issue, you can try the following steps:

      1. Load the WireGuard kernel module: If the WireGuard kernel module is not loaded, you can load it manually by running:

        sudo modprobe wireguard\n
      2. Load the WireGuard kernel module on boot: If you want to ensure that the WireGuard kernel module is loaded automatically on boot, you can add it to the /etc/modules file:

        echo \"wireguard\" | sudo tee -a /etc/modules\n
      "},{"location":"faq/#cant-initialize-iptables-table-nat-table-does-not-exist-do-you-need-to-insmod","title":"can't initialize iptables table `nat': Table does not exist (do you need to insmod?)","text":"

      This error indicates that the nat table in iptables does not exist. This can happen if the iptables kernel module is not loaded or if the nat table is not supported by your kernel.

      To resolve this issue, you can try the following steps:

      1. Load the nat kernel module: If the nat kernel module is not loaded, you can load it manually by running:

        sudo modprobe iptable_nat\n
      2. Load the nat kernel module on boot: If you want to ensure that the nat kernel module is loaded automatically on boot, you can add it to the /etc/modules file:

         echo \"iptable_nat\" | sudo tee -a /etc/modules\n
      "},{"location":"faq/#cant-initialize-ip6tables-table-nat-table-does-not-exist-do-you-need-to-insmod","title":"can't initialize ip6tables table `nat': Table does not exist (do you need to insmod?)","text":"

      This error indicates that the nat table in ip6tables does not exist. This can happen if the ip6tables kernel module is not loaded or if the nat table is not supported by your kernel.

      To resolve this issue, you can try the following steps:

      1. Load the nat kernel module: If the nat kernel module is not loaded, you can load it manually by running:

        sudo modprobe ip6table_nat\n
      2. Load the nat kernel module on boot: If you want to ensure that the nat kernel module is loaded automatically on boot, you can add it to the /etc/modules file:

         echo \"ip6table_nat\" | sudo tee -a /etc/modules\n
      "},{"location":"faq/#cant-initialize-iptables-table-filter-permission-denied","title":"can't initialize iptables table `filter': Permission denied","text":"

      This error indicates that the filter table in iptables cannot be initialized due to permission issues. This can happen if you are not running the command with sufficient privileges.

      To resolve this issue, you can try the following steps:

      1. Load the filter kernel module: If the filter kernel module is not loaded, you can load it manually by running:

        sudo modprobe iptable_filter\n
      2. Load the filter kernel module on boot: If you want to ensure that the filter kernel module is loaded automatically on boot, you can add it to the /etc/modules file:

        echo \"iptable_filter\" | sudo tee -a /etc/modules\n
      "},{"location":"faq/#cant-initialize-ip6tables-table-filter-permission-denied","title":"can't initialize ip6tables table `filter': Permission denied","text":"

      This error indicates that the filter table in ip6tables cannot be initialized due to permission issues. This can happen if you are not running the command with sufficient privileges.

      To resolve this issue, you can try the following steps:

      1. Load the filter kernel module: If the filter kernel module is not loaded, you can load it manually by running:

        sudo modprobe ip6table_filter\n
      2. Load the filter kernel module on boot: If you want to ensure that the filter kernel module is loaded automatically on boot, you can add it to the /etc/modules file:

         echo \"ip6table_filter\" | sudo tee -a /etc/modules\n
      "},{"location":"faq/#clients-lose-connectivity-after-restarting-the-container-when-using-multiple-networks","title":"Clients lose connectivity after restarting the container when using multiple networks?","text":"

      When you attach multiple Docker networks (e.g., wg and a reverse proxy network like traefik or nginx) to the wg-easy container, Docker might assign the network interfaces randomly (e.g., swapping eth0 and eth1). Since wg-easy expects the wireguard interface to act as eth0 and configures POSTROUTING rules for it, connectivity will break if the interfaces are swapped upon container restart.

      To solve this, specify the interface_name and gw_priority explicitly in your docker-compose.yml file to guarantee that the wg network always binds to eth0 and acts as the default gateway.

      Example docker-compose.yml:

      services:\n    wg-easy:\n        # ... other configuration ...\n        networks:\n            wg:\n                interface_name: eth0\n                gw_priority: 1\n                ipv4_address: 10.42.42.42\n            nginx:\n                interface_name: eth1\n                gw_priority: 0\n\nnetworks:\n    wg:\n        # ... wg network config ...\n    nginx:\n        external: true\n
      "},{"location":"getting-started/","title":"Getting Started","text":"

      This page explains how to get started with wg-easy. The guide uses Docker Compose as a reference. In our examples, we mount the named volume etc_wireguard to /etc/wireguard inside the container.

      "},{"location":"getting-started/#preliminary-steps","title":"Preliminary Steps","text":"

      Before you can get started with deploying your own VPN, there are some requirements to be met:

      1. You need to have a host that you can manage
      2. You need to have a domain name or a public IP address
      3. You need a supported architecture (x86_64, arm64)
      "},{"location":"getting-started/#host-setup","title":"Host Setup","text":"

      There are a few requirements for a suitable host system:

      1. You need to have a container runtime installed

      About the Container Runtime

      On the host, you need to have a suitable container runtime (like Docker or Podman) installed. We assume Docker Compose is installed. We have aligned file names and configuration conventions with the latest Docker Compose specification. If you're using podman, make sure to read the related documentation.

      "},{"location":"getting-started/#deploying-the-actual-image","title":"Deploying the Actual Image","text":""},{"location":"getting-started/#tagging-convention","title":"Tagging Convention","text":"

      To understand which tags you should use, read this section carefully. Our CI will automatically build, test and push new images to the following container registry:

      1. GitHub Container Registry (ghcr.io/wg-easy/wg-easy)
      2. Codeberg Container Registry (codeberg.org/wg-easy/wg-easy) (IPv6 support)

      All workflows are using the tagging convention listed below. It is subsequently applied to all images.

      tag Type Example Description 15 latest minor for that major tag ghcr.io/wg-easy/wg-easy:15 latest features for specific major versions, no breaking changes, recommended 15.0 latest patch for that minor tag ghcr.io/wg-easy/wg-easy:15.0 latest patches for specific minor version 15.0.0 specific tag ghcr.io/wg-easy/wg-easy:15.0.0 specific release, no updates edge push to master ghcr.io/wg-easy/wg-easy:edge mostly unstable, gets frequent package and code updates development pull requests ghcr.io/wg-easy/wg-easy:development used for development, testing code from PRs latest latest tag ghcr.io/wg-easy/wg-easy:latest or ghcr.io/wg-easy/wg-easy points to the v14 release, should be avoided

      When publishing a tag we follow the Semantic Versioning specification. Pin to the latest major version to avoid breaking changes (e.g. 15), avoid using the latest tag.

      "},{"location":"getting-started/#follow-tutorials","title":"Follow tutorials","text":"
      • Basic Installation with Docker Compose (Recommended)
      • Simple Installation with Docker Run
      • Advanced Installation with Podman

      Use the Correct Commands For Stopping and Starting wg-easy

      Use sudo docker compose up / down, not sudo docker compose start / stop. Otherwise, the container is not properly destroyed and you may experience problems during startup because of inconsistent state.

      That's it! It really is that easy.

      "},{"location":"advanced/api/","title":"API","text":"

      Breaking Changes

      This API is not yet stable and may change in the future. The API is currently in development and is subject to change without notice. The API is not yet documented, but we will add documentation as the API stabilizes.

      You can use the API to interact with the application programmatically. The API is available at /api and supports both GET and POST requests. The API is designed to be simple and easy to use, with a focus on providing a consistent interface for all endpoints.

      There is no documentation for the API yet, but this will be added as the underlying library supports it.

      "},{"location":"advanced/api/#authentication","title":"Authentication","text":"

      To use the API, you need to authenticate using Basic Authentication. The username and password are the same as the ones you use to log in to the web application. If you use 2FA, the API will not work. You need to disable 2FA in the web application to use the API.

      "},{"location":"advanced/api/#authentication-example","title":"Authentication Example","text":"
      import requests\nfrom requests.auth import HTTPBasicAuth\n\nurl = \"https://example.com:51821/api/client\"\nresponse = requests.get(url, auth=HTTPBasicAuth('username', 'password'))\nif response.status_code == 200:\n    data = response.json()\n    print(data)\nelse:\n    print(f\"Error: {response.status_code}\")\n
      "},{"location":"advanced/api/#endpoints","title":"Endpoints","text":"

      The Endpoints are not yet documented. But as file-based routing is used, you can find the endpoints in the src/server/api folder. The method is defined in the file name.

      "},{"location":"advanced/api/#endpoints-example","title":"Endpoints Example","text":"File Name Endpoint Method src/server/api/client.get.ts /api/client GET src/server/api/setup/2.post.ts /api/setup/2 POST"},{"location":"advanced/config/amnezia/","title":"AmneziaWG","text":""},{"location":"advanced/config/amnezia/#introduction","title":"Introduction","text":"

      AmneziaWG is a modified version of the WireGuard protocol with enhanced traffic obfuscation capabilities. AmneziaWG's primary goal is to counter deep packet inspection (DPI) systems and bypass VPN blocking.

      AmneziaWG adds multi-level transport-layer obfuscation by:

      • Modifying packet headers
      • Randomizing handshake message sizes
      • Disguising traffic to resemble popular UDP protocols

      These measures make it harder for third parties to analyze or identify your traffic, enhancing both privacy and security.

      "},{"location":"advanced/config/amnezia/#activating-amneziawg","title":"Activating AmneziaWG","text":"

      You must install the AmneziaWG kernel module on the host system.

      Experimental support for AmneziaWG can be enabled by setting the EXPERIMENTAL_AWG environment variable to true. Starting from wg-easy version 16, this setting will be enabled by default. This feature is still under development and may change in future releases.

      When enabled, wg-easy will automatically detect whether the AmneziaWG kernel module is available. If it is not, the system will fall back to the standard WireGuard module.

      To override this automatic detection, set the OVERRIDE_AUTO_AWG environment variable. By default, this variable is unset.

      Possible values:

      • awg \u2014 Force use of AmneziaWG
      • wg \u2014 Force use of standard WireGuard
      "},{"location":"advanced/config/amnezia/#amneziawg-parameters","title":"AmneziaWG Parameters","text":"

      Parameter descriptions can be found in the AmneziaWG documentation and on the kernel module page.

      All parameters except I1-I5 will be set at first startup. For information on how to set I1-I5 parameters, refer to the AmneziaWG documentation.

      If a parameter is not set, it will not be added to the configuration. If all AmneziaWG-specific parameters are absent, AmneziaWG will be fully compatible with standard WireGuard.

      "},{"location":"advanced/config/amnezia/#parameter-compatibility-table","title":"Parameter Compatibility Table","text":"Parameter Can differ between server and client Configurable on server Configurable on client Jc \u2705 Yes \u2705 \u2705 Jmin \u2705 Yes \u2705 \u2705 Jmax \u2705 Yes \u2705 \u2705 S1-S4 \u274c No, must match \u2705 \u274c (copied from server) H1-H4 \u274c No, must match \u2705 \u274c (copied from server) I1-I5 \u2705 Yes \u2705 \u2705"},{"location":"advanced/config/amnezia/#client-applications","title":"Client Applications","text":"

      To be able to connect to wg-easy if AmneziaWG is enabled, you must have an AmneziaWG-compatible client. Where an AmneziaWG app is available for your platform, it is recommended to use it rather than Amnezia VPN.

      Android:

      • AmneziaWG - AmneziaWG Official Client
      • WG Tunnel - Third Party Client
      • Amnezia VPN - Amnezia VPN Official Client

      iOS and macOS:

      • AmneziaWG - AmneziaWG Official Client
      • Amnezia VPN - Amnezia VPN Official Client

      Windows:

      • AmneziaWG - AmneziaWG Official Client (Requires building from source code)
      • Amnezia VPN - Amnezia VPN Official Client

      Linux:

      • Amnezia VPN - Amnezia VPN Official Client
      • amneziawg-tools - AmneziaWG Tools

      OpenWRT:

      • AmneziaWG OpenWRT - AmneziaWG OpenWRT Packages
      • AmneziaWG OpenWRT - AmneziaWG OpenWRT Packages
      "},{"location":"advanced/config/experimental-config/","title":"Experimental Configuration","text":"

      There are several experimental features that can be enabled by setting the appropriate environment variables. These features are not guaranteed to be stable and may change in future releases.

      Env Default Example Description Notes More Info EXPERIMENTAL_AWG false true Enables experimental AmneziaWG support Planned to be enabled by default in v16 See here"},{"location":"advanced/config/optional-config/","title":"Optional Configuration","text":"

      You can set these environment variables to configure the container. They are not required, but can be useful in some cases.

      Env Default Example Description PORT 51821 6789 TCP port for Web UI. HOST 0.0.0.0 localhost IP address web UI binds to. INSECURE false true If access over http is allowed DISABLE_IPV6 false true If IPv6 support should be disabled DISABLE_VERSION_CHECK false true If wg-easy should check for new updates

      IPv6 Caveats

      Disabling IPv6 will disable the creation of the default IPv6 firewall rules and won't add a IPv6 address to the interface and clients.

      You will however still see a IPv6 address in the Web UI, but it won't be used.

      This option can be removed in the future, as more devices support IPv6.

      "},{"location":"advanced/config/unattended-setup/","title":"Unattended Setup","text":"

      If you want to run the setup without any user interaction, e.g. with a tool like Ansible, you can use these environment variables to configure the setup.

      These will only be used during the first start of the container. After that, the setup will be disabled.

      Env Example Description Group INIT_ENABLED true Enables the below env vars 0 INIT_USERNAME admin Sets admin username 1 INIT_PASSWORD Se!ureP%ssw Sets admin password 1 INIT_HOST vpn.example.com Host clients will connect to 1 INIT_PORT 51820 Port clients will connect to and wireguard will listen on 1 INIT_DNS 1.1.1.1,8.8.8.8 Sets global dns setting 2 INIT_IPV4_CIDR 10.8.0.0/24 Sets IPv4 cidr 3 INIT_IPV6_CIDR 2001:0DB8::/32 Sets IPv6 cidr 3 INIT_ALLOWED_IPS 10.8.0.0/24,2001:0DB8::/32 Sets global Allowed IPs 4

      Variables have to be used together

      If variables are in the same group, you have to set all of them. For example, if you set INIT_IPV4_CIDR, you also have to set INIT_IPV6_CIDR.

      If you want to skip the setup process, you have to configure group 1

      Security

      The initial username and password is not checked for complexity. Make sure to set a long enough username and password. Otherwise, the user won't be able to log in.

      It's recommended to remove the variables after the setup is done to prevent the password from being exposed.

      "},{"location":"advanced/metrics/prometheus/","title":"Prometheus","text":"

      To monitor the WireGuard server, you can use Prometheus and Grafana. The container exposes a /metrics/prometheus endpoint that can be scraped by Prometheus.

      "},{"location":"advanced/metrics/prometheus/#enable-prometheus","title":"Enable Prometheus","text":"

      To enable Prometheus metrics, go to Admin Panel > General and enable Prometheus.

      You can optionally set a Bearer Password for the metrics endpoints. This is useful if you want to expose the metrics endpoint to the internet.

      "},{"location":"advanced/metrics/prometheus/#configure-prometheus","title":"Configure Prometheus","text":"

      You need to add a scrape config to your Prometheus configuration file. Here is an example:

      scrape_configs:\n    - job_name: 'wg-easy'\n      scrape_interval: 30s\n      metrics_path: /metrics/prometheus\n      static_configs:\n          - targets:\n                - 'localhost:51821'\n      authorization:\n          type: Bearer\n          credentials: 'SuperSecurePassword'\n
      "},{"location":"advanced/metrics/prometheus/#grafana-dashboard","title":"Grafana Dashboard","text":"

      You can use the following Grafana dashboard to visualize the metrics:

      21733

      Unofficial

      The Grafana dashboard is not official and is not maintained by the wg-easy team. If you have any issues with the dashboard, please contact the author of the dashboard. See #1299 for more information.

      "},{"location":"advanced/migrate/","title":"Migrate","text":"

      If you want to migrate from an older version of wg-easy to the new version, you can find the migration guides listed below.

      • Migrate from v14 to v15 : This guide should also work for any version before v14.
      "},{"location":"advanced/migrate/from-14-to-15/","title":"Migrate from v14 to v15","text":"

      This guide will help you migrate from v14 to version v15 of wg-easy.

      "},{"location":"advanced/migrate/from-14-to-15/#changes","title":"Changes","text":"
      • This is a complete rewrite of the wg-easy project, therefore the configuration files and the way you interact with the project have changed.
      • If you use armv6 or armv7, you unfortunately won't be able to migrate to v15.
      • If you are connecting to the Web UI via HTTP, you need to set the INSECURE environment variable to true in the new container.
      "},{"location":"advanced/migrate/from-14-to-15/#migration","title":"Migration","text":""},{"location":"advanced/migrate/from-14-to-15/#backup","title":"Backup","text":"

      Before you start the migration, make sure to back up your existing configuration files.

      Go into the Web UI and click the Backup button, this should download a wg0.json file.

      Or download the wg0.json file from your container volume to your pc.

      You will need this file for the migration

      You will also need to back up the old environment variables you set for the container, as they will not be automatically migrated.

      "},{"location":"advanced/migrate/from-14-to-15/#remove-old-container","title":"Remove old container","text":"
      1. Stop the running container

      If you are using docker run

      docker stop wg-easy\n

      If you are using docker compose

      docker compose down\n
      "},{"location":"advanced/migrate/from-14-to-15/#start-new-container","title":"Start new container","text":"

      Follow the instructions in the Getting Started or Basic Installation guide to start the new container.

      In the setup wizard, select that you already have a configuration file and upload the wg0.json file you downloaded in the backup step.

      "},{"location":"advanced/migrate/from-14-to-15/#environment-variables","title":"Environment Variables","text":"

      v15 does not use the same environment variables as v14, most of them have been moved to the Admin Panel in the Web UI.

      "},{"location":"advanced/migrate/from-14-to-15/#done","title":"Done","text":"

      You have now successfully migrated to v15 of wg-easy.

      "},{"location":"contributing/general/","title":"General Information","text":""},{"location":"contributing/general/#coding-style","title":"Coding Style","text":"

      When refactoring, writing or altering files, adhere to these rules:

      1. Adjust your style of coding to the style that is already present! Even if you do not like it, this is due to consistency. There was a lot of work involved in making all files consistent.
      2. Use pnpm lint to check your scripts! Your contributions are checked by GitHub Actions too, so you will need to do this.
      3. Use the provided .vscode/settings.json file.
      "},{"location":"contributing/general/#documentation","title":"Documentation","text":"

      Make sure to select edge in the dropdown menu at the top. Navigate to the page you would like to edit and click the edit button in the top right. This allows you to make changes and create a pull-request.

      Alternatively you can make the changes locally. For that you'll need to have Docker installed. Run

      pnpm docs:serve\n

      This serves the documentation on your local machine on port 8080. Each change will be hot-reloaded onto the page you view, just edit, save and look at the result.

      "},{"location":"contributing/issues-and-pull-requests/","title":"Issues and Pull Requests","text":"

      This project is Open Source. That means that you can contribute on enhancements, bug fixing or improving the documentation.

      "},{"location":"contributing/issues-and-pull-requests/#opening-an-issue","title":"Opening an Issue","text":"

      Attention

      Before opening an issue, read the README carefully, study the docs for your version (maybe latest) and your search engine you trust. The issue tracker is not meant to be used for unrelated questions!

      When opening an issue, please provide details use case to let the community reproduce your problem.

      Attention

      Use the issue templates to provide the necessary information. Issues which do not use these templates are not worked on and closed.

      By raising issues, I agree to these terms and I understand, that the rules set for the issue tracker will help both maintainers as well as everyone to find a solution.

      Maintainers take the time to improve on this project and help by solving issues together. It is therefore expected from others to make an effort and comply with the rules.

      "},{"location":"contributing/issues-and-pull-requests/#filing-a-bug-report","title":"Filing a Bug Report","text":"

      Thank you for participating in this project and reporting a bug. wg-easy is a community-driven project, and each contribution counts!

      Maintainers and moderators are volunteers. We greatly appreciate reports that take the time to provide detailed information via the template, enabling us to help you in the best and quickest way. Ignoring the template provided may seem easier, but discourages receiving any support.

      Markdown formatting can be used in almost all text fields (unless stated otherwise in the description).

      Be as precise as possible, and if in doubt, it's best to add more information that too few.

      When an option is marked with \"not officially supported\" / \"unsupported\", then support is dependent on availability from specific maintainers.

      "},{"location":"contributing/issues-and-pull-requests/#pull-requests","title":"Pull Requests","text":"

      Motivation

      You want to add a feature? Feel free to start creating an issue explaining what you want to do and how you're thinking doing it. Other users may have the same need and collaboration may lead to better results.

      "},{"location":"contributing/issues-and-pull-requests/#submit-a-pull-request","title":"Submit a Pull-Request","text":"

      The development workflow is the following:

      1. Fork the project
      2. Write the code that is needed :D
      3. Document your improvements if necessary
      4. Commit (and sign your commit), push and create a pull-request to merge into master. Please use the pull-request template to provide a minimum of contextual information and make sure to meet the requirements of the checklist.

      Pull requests are automatically tested against the CI and will be reviewed when tests pass. When your changes are validated, your branch is merged. CI builds the new :edge image on every push to the master branch and your changes will be included in the next version release.

      "},{"location":"contributing/translation/","title":"Translation","text":"

      This project supports multiple languages. If you would like to contribute a translation, please follow these steps:

      "},{"location":"contributing/translation/#add-new-translation","title":"Add new Translation","text":"

      Create a new file in src/i18n/locales. Name it <locale_code>.json (e.g. fr.json for French).

      Import and add the newly created file in src/i18n/i18n.config.ts.

      Add your language in the src/nuxt.config.ts file. You have to specify code, language and name.

      code is the name of the translation file without the extension (e.g. fr for fr.json).

      language is the BCP 47 language tag with region (e.g. fr-FR for French). See www.lingoes.net for a list of language codes.

      name is the display name of the language (e.g. Fran\u00e7ais for French).

      "},{"location":"contributing/translation/#update-existing-translation","title":"Update existing Translation","text":"

      If you need to update an existing translation, simply edit the corresponding <locale_code>.json file in src/i18n/locales.

      "},{"location":"contributing/translation/#contribute-changes","title":"Contribute changes","text":"

      See Pull Requests on how to contribute your translation.

      "},{"location":"examples/tutorials/adguard/","title":"AdGuard Home","text":"

      This tutorial is a follow-up to the official Traefik tutorial. It will guide you through integrating AdGuard Home with your existing wg-easy and Traefik setup to provide network-wide DNS ad-blocking.

      "},{"location":"examples/tutorials/adguard/#prerequisites","title":"Prerequisites","text":"
      • A working wg-easy and Traefik setup from the previous guides.

      Important: Following this guide will reset your WireGuard configuration.

      The process involves re-creating the wg-easy container and its data, which means all existing WireGuard clients and settings will be deleted.

      You will need to create your clients again after completing this guide.

      "},{"location":"examples/tutorials/adguard/#add-adguard-configuration","title":"Add adguard configuration","text":"
      1. Create a directory for the configuration files:

        sudo mkdir -p /etc/docker/containers/adguard\n
      2. Create volumes for persistent data:

        sudo mkdir -p /etc/docker/volumes/adguard/adguard_work\nsudo mkdir -p /etc/docker/volumes/adguard/adguard_conf\nsudo chmod -R 700 /etc/docker/volumes/adguard\n
      3. Create the docker-compose.yml file.

      File: /etc/docker/containers/adguard/docker-compose.yml

      services:\n    adguard:\n        image: adguard/adguardhome:v0.107.64\n        container_name: adguard\n        restart: unless-stopped\n        volumes:\n            - /etc/docker/volumes/adguard/adguard_work:/opt/adguardhome/work\n            - /etc/docker/volumes/adguard/adguard_conf:/opt/adguardhome/conf\n        networks:\n            wg:\n                interface_name: eth0\n                ipv4_address: 10.42.42.43\n                ipv6_address: fdcc:ad94:bacf:61a3::2b\n            traefik:\n                interface_name: eth1\n        labels:\n            - 'traefik.enable=true'\n            - 'traefik.http.routers.adguard.rule=Host(`adguard.$example.com$`)'\n            - 'traefik.http.routers.adguard.entrypoints=websecure'\n            - 'traefik.http.routers.adguard.service=adguard'\n            - 'traefik.http.services.adguard.loadbalancer.server.port=3000'\n            - 'traefik.docker.network=traefik'\n\nnetworks:\n    wg:\n        external: true\n    traefik:\n        external: true\n
      "},{"location":"examples/tutorials/adguard/#update-wg-easy-configuration","title":"Update wg-easy configuration","text":"

      Modify the corresponding sections of your existing wg-easy compose file to match the updated version below.

      File: /etc/docker/containers/wg-easy/docker-compose.yml

      services:\n  wg-easy:\n    ports:\n      - \"51820:51820/udp\"\n    ...\n    networks:\n      wg:\n        interface_name: eth0\n        ...\n      traefik:\n        interface_name: eth1\n      ...\n    ...\n    environment:\n      # Unattended Setup\n      - INIT_ENABLED=true\n      # Replace $username$ with your username\n      - INIT_USERNAME=$username$\n      # Replace $password$ with your unhashed password\n      - INIT_PASSWORD=$password$\n      # Replace $example.com$ with your domain\n      - INIT_HOST=wg-easy.$example.com$\n      - INIT_PORT=51820\n      - INIT_DNS=10.42.42.43,fdcc:ad94:bacf:61a3::2b\n      - INIT_IPV4_CIDR=10.8.0.0/24\n      - INIT_IPV6_CIDR=fd42:42:42::/64\n    ...\n\nnetworks:\n  wg:\n    # Prevents Docker Compose from prefixing the network name.\n    name: wg\n    ...\n  ...\n
      "},{"location":"examples/tutorials/adguard/#setup-wireguard","title":"Setup Wireguard","text":"
      1. Restart wg-easy:

        cd /etc/docker/containers/wg-easy\nsudo docker compose down -v\nsudo docker compose up -d\n
      2. Edit Wireguard's Hooks.

        In the Admin Panel of your WireGuard server, go to the Hooks tab and replace it with:

        PostUp

        iptables -A INPUT -p udp -m udp --dport {{port}} -j ACCEPT; ip6tables -A INPUT -p udp -m udp --dport {{port}} -j ACCEPT; iptables -t nat -A PREROUTING -i wg0 -p udp --dport 53 -j DNAT --to-destination 10.42.42.43; iptables -t nat -A PREROUTING -i wg0 -p tcp --dport 53 -j DNAT --to-destination 10.42.42.43; ip6tables -t nat -A PREROUTING -i wg0 -p udp --dport 53 -j DNAT --to-destination fdcc:ad94:bacf:61a3::2b; ip6tables -t nat -A PREROUTING -i wg0 -p tcp --dport 53 -j DNAT --to-destination fdcc:ad94:bacf:61a3::2b; iptables -A FORWARD -i wg0 -j ACCEPT; iptables -A FORWARD -o wg0 -j ACCEPT; ip6tables -A FORWARD -i wg0 -j ACCEPT; ip6tables -A FORWARD -o wg0 -j ACCEPT; iptables -t nat -A POSTROUTING -s {{ipv4Cidr}} -o {{device}} -j MASQUERADE; ip6tables -t nat -A POSTROUTING -s {{ipv6Cidr}} -o {{device}} -j MASQUERADE;\n

        PostDown

        iptables -D INPUT -p udp -m udp --dport {{port}} -j ACCEPT || true; ip6tables -D INPUT -p udp -m udp --dport {{port}} -j ACCEPT || true; iptables -t nat -D PREROUTING -i wg0 -p udp --dport 53 -j DNAT --to-destination 10.42.42.43 || true; iptables -t nat -D PREROUTING -i wg0 -p tcp --dport 53 -j DNAT --to-destination 10.42.42.43 || true; ip6tables -t nat -D PREROUTING -i wg0 -p udp --dport 53 -j DNAT --to-destination fdcc:ad94:bacf:61a3::2b || true; ip6tables -t nat -D PREROUTING -i wg0 -p tcp --dport 53 -j DNAT --to-destination fdcc:ad94:bacf:61a3::2b || true; iptables -D FORWARD -i wg0 -j ACCEPT || true; iptables -D FORWARD -o wg0 -j ACCEPT || true; ip6tables -D FORWARD -i wg0 -j ACCEPT || true; ip6tables -D FORWARD -o wg0 -j ACCEPT || true; iptables -t nat -D POSTROUTING -s {{ipv4Cidr}} -o {{device}} -j MASQUERADE || true; ip6tables -t nat -D POSTROUTING -s {{ipv6Cidr}} -o {{device}} -j MASQUERADE || true;\n
      3. Restart wg-easy to apply changes:

        sudo docker restart wg-easy\n
      "},{"location":"examples/tutorials/adguard/#setup-adguard-home","title":"Setup Adguard Home","text":"
      1. Start adguard service:

        cd /etc/docker/containers/adguard\nsudo docker compose up -d\n
      2. Navigate to https://adguard.$example.com$ to begin the AdGuard Home setup.

      Important: Configure AdGuard Home Admin Web Interface Port

      During the initial AdGuard Home setup on the Step 2/5 page, you must set the Admin Web Interface Port to 3000. Do not use the default port 80, as it will not work with the Traefik configuration.

      After completing the setup, the AdGuard UI might appear unresponsive. This is expected. Simply reload the page, and the panel will display correctly.

      If you accidentally left it default (80), you will need to manually edit the docker-compose.yml file for AdGuard Home (/etc/docker/containers/adguard/docker-compose.yml) and change the line traefik.http.services.adguard.loadbalancer.server.port=3000 to traefik.http.services.adguard.loadbalancer.server.port=80. After making this change, restart AdGuard Home by navigating to /etc/docker/containers/adguard and running sudo docker compose up -d.

      "},{"location":"examples/tutorials/adguard/#final-system-checks","title":"Final System Checks","text":""},{"location":"examples/tutorials/adguard/#firewall","title":"Firewall","text":"

      Ensure the ports 80/tcp, 443/tcp, 443/udp, and 51820/udp are open.

      "},{"location":"examples/tutorials/adguard/#optional-optimizing-udp-buffer-sizes","title":"Optional: Optimizing UDP Buffer Sizes","text":"

      AdGuard Home, as a DNS server, handles a large volume of UDP packets. To ensure optimal performance, it is recommended to increase the system's UDP buffer sizes. You can apply these settings using your system's sysctl configuration (e.g., by creating a file in /etc/sysctl.d/).

      net.core.rmem_max = 7500000\nnet.core.wmem_max = 7500000\n

      After adding these settings, remember to apply them (e.g., by running sudo sysctl --system or rebooting)

      "},{"location":"examples/tutorials/auto-updates/","title":"Auto Updates","text":""},{"location":"examples/tutorials/auto-updates/#docker-compose","title":"Docker Compose","text":"

      With Docker Compose wg-easy can be updated with a single command:

      cd /etc/docker/containers/wg-easy\nsudo docker compose up -d --pull always\n
      "},{"location":"examples/tutorials/auto-updates/#watchtower","title":"Watchtower","text":"

      If you want the updates to be fully automatic you can install Watchtower. This will check for updates every day at 4:00 AM and update the container if a new version is available.

      File: /etc/docker/containers/watchtower/docker-compose.yml

      services:\n    watchtower:\n        image: nickfedor/watchtower:latest\n        volumes:\n            - /var/run/docker.sock:/var/run/docker.sock\n        env_file:\n            - watchtower.env\n        restart: unless-stopped\n

      File: /etc/docker/containers/watchtower/watchtower.env

      WATCHTOWER_CLEANUP=true\nWATCHTOWER_SCHEDULE=0 0 4 * * *\nTZ=Europe/Berlin\n\n# Email\n# WATCHTOWER_NOTIFICATIONS_LEVEL=info\n# WATCHTOWER_NOTIFICATIONS=email\n# WATCHTOWER_NOTIFICATION_EMAIL_FROM=mail@example.com\n# WATCHTOWER_NOTIFICATION_EMAIL_TO=mail@example.com\n# WATCHTOWER_NOTIFICATION_EMAIL_SERVER=smtp.example.com\n# WATCHTOWER_NOTIFICATION_EMAIL_SERVER_USER=mail@example.com\n# WATCHTOWER_NOTIFICATION_EMAIL_SERVER_PASSWORD=\"SuperSecurePassword\"\n# WATCHTOWER_NOTIFICATION_EMAIL_SERVER_PORT=587\n
      cd /etc/docker/containers/watchtower\nsudo docker compose up -d\n
      "},{"location":"examples/tutorials/auto-updates/#docker-run","title":"Docker Run","text":"
      sudo docker stop wg-easy\nsudo docker rm wg-easy\nsudo docker pull ghcr.io/wg-easy/wg-easy\n

      And then run the docker run -d \\ ... command from Docker Run again.

      "},{"location":"examples/tutorials/auto-updates/#podman","title":"Podman","text":"

      To update wg-easy (and every container that has auto updates enabled), you can run the following command:

      sudo podman auto-update\n
      "},{"location":"examples/tutorials/basic-installation/","title":"Basic Installation","text":""},{"location":"examples/tutorials/basic-installation/#requirements","title":"Requirements","text":"
      1. You need to have a host that you can manage
      2. You need to have a domain name or a public IP address
      3. You need a supported architecture (x86_64, arm64)
      4. You need curl installed on your host
      "},{"location":"examples/tutorials/basic-installation/#install-docker","title":"Install Docker","text":"

      Follow the Docs here: https://docs.docker.com/engine/install/ and install Docker on your host.

      "},{"location":"examples/tutorials/basic-installation/#install-wg-easy","title":"Install wg-easy","text":"
      1. Create a directory for the configuration files (you can choose any directory you like):

        sudo mkdir -p /etc/docker/containers/wg-easy\n
      2. Download docker compose file

        sudo curl -o /etc/docker/containers/wg-easy/docker-compose.yml https://raw.githubusercontent.com/wg-easy/wg-easy/master/docker-compose.yml\n
      3. Start wg-easy

         cd /etc/docker/containers/wg-easy\n sudo docker compose up -d\n
      "},{"location":"examples/tutorials/basic-installation/#setup-firewall","title":"Setup Firewall","text":"

      If you are using a firewall, you need to open the following ports:

      • UDP 51820 (WireGuard)

      These ports can be changed, so if you change them you have to update your firewall rules accordingly.

      "},{"location":"examples/tutorials/basic-installation/#setup-reverse-proxy","title":"Setup Reverse Proxy","text":"
      • To setup traefik follow the instructions here: Traefik
      • To setup caddy follow the instructions here: Caddy
      • If you do not want to use a reverse proxy follow the instructions here: No Reverse Proxy
      "},{"location":"examples/tutorials/basic-installation/#update-wg-easy","title":"Update wg-easy","text":"

      To update wg-easy to the latest version, run:

      cd /etc/docker/containers/wg-easy\nsudo docker compose pull\nsudo docker compose up -d\n
      "},{"location":"examples/tutorials/basic-installation/#auto-update","title":"Auto Update","text":"

      If you want to enable auto-updates, follow the instructions here: Auto Updates

      "},{"location":"examples/tutorials/caddy/","title":"Caddy","text":"

      Opinionated

      This guide is opinionated. If you use other conventions or folder layouts, feel free to change the commands and paths.

      We're using Caddy here as reverse proxy to serve wg-easy on https://wg-easy.example.com via TLS.

      "},{"location":"examples/tutorials/caddy/#create-a-docker-composition-for-caddy","title":"Create a docker composition for caddy","text":"
      .\n\u251c\u2500\u2500 compose.yml\n\u2514\u2500\u2500 Caddyfile\n\n1 directory, 2 files\n
      # compose.yml\n\nservices:\n    caddy:\n        container_name: caddy\n        image: caddy:2.10.0-alpine\n        # publish everything you deem necessary\n        ports:\n            - '80:80/tcp'\n            - '443:443/tcp'\n            - '443:443/udp'\n        networks:\n            - caddy\n        restart: unless-stopped\n        volumes:\n            - './Caddyfile:/etc/caddy/Caddyfile:ro'\n            - config:/config\n            - data:/data\n\nnetworks:\n    caddy:\n        name: caddy\n\nvolumes:\n    config:\n    data:\n
      # Caddyfile\n\n{\n        # setup your email address\n        email mail@example.com\n}\n\nwg-easy.example.com {\n        # since the container will share the network with wg-easy\n        # we can use the proper container name\n        reverse_proxy wg-easy:80\n        tls internal\n}\n

      ...and start it with:

      sudo docker compose up -d\n
      "},{"location":"examples/tutorials/caddy/#adapt-the-docker-composition-of-wg-easy","title":"Adapt the docker composition of wg-easy","text":"
      services:\n  wg-easy:\n    # sync container name and port according to Caddyfile\n    container_name: wg-easy\n    environment:\n      - PORT=80\n    # no need to publish the HTTP server anymore\n    ports:\n      - \"51820:51820/udp\"\n    # add to caddy network\n    networks:\n      caddy:\n    ...\n\nnetworks:\n  caddy:\n    external: true\n  ...\n

      ...and restart it with:

      sudo docker compose up -d\n

      You can now access wg-easy at https://wg-easy.example.com and start the setup.

      "},{"location":"examples/tutorials/docker-run/","title":"Docker Run","text":"

      To setup the IPv6 Network, simply run once:

      docker network create \\\n  -d bridge --ipv6 \\\n  --subnet 10.42.42.0/24 \\\n  --subnet fdcc:ad94:bacf:61a3::/64 \\\n   wg\n

      To automatically install & run wg-easy, simply run:

      docker run -d \\\n  --net wg \\\n  -e INSECURE=true \\\n  --name wg-easy \\\n  --ip6 fdcc:ad94:bacf:61a3::2a \\\n  --ip 10.42.42.42 \\\n  -v ~/.wg-easy:/etc/wireguard \\\n  -v /lib/modules:/lib/modules:ro \\\n  -p 51820:51820/udp \\\n  -p 51821:51821/tcp \\\n  --cap-add NET_ADMIN \\\n  --cap-add SYS_MODULE \\\n  --sysctl net.ipv4.ip_forward=1 \\\n  --sysctl net.ipv4.conf.all.src_valid_mark=1 \\\n  --sysctl net.ipv6.conf.all.disable_ipv6=0 \\\n  --sysctl net.ipv6.conf.all.forwarding=1 \\\n  --sysctl net.ipv6.conf.default.forwarding=1 \\\n  --restart unless-stopped \\\n  ghcr.io/wg-easy/wg-easy:15\n

      The Web UI will now be available at http://0.0.0.0:51821.

      "},{"location":"examples/tutorials/dockerless/","title":"Without Docker","text":"

      This is currently not yet supported.

      "},{"location":"examples/tutorials/podman-nft/","title":"Podman + nftables","text":"

      This guide will show you how to run wg-easy with rootful Podman and nftables.

      "},{"location":"examples/tutorials/podman-nft/#requirements","title":"Requirements","text":"
      1. Podman installed with version 4.4 or higher
      "},{"location":"examples/tutorials/podman-nft/#configuration","title":"Configuration","text":"

      Create a Folder for the configuration files:

      sudo mkdir -p /etc/containers/systemd/wg-easy\nsudo mkdir -p /etc/containers/volumes/wg-easy\n

      Create a file /etc/containers/systemd/wg-easy/wg-easy.container with the following content:

      [Container]\nContainerName=wg-easy\nImage=ghcr.io/wg-easy/wg-easy:15\nAutoUpdate=registry\n\nVolume=/etc/containers/volumes/wg-easy:/etc/wireguard:Z\nNetwork=wg-easy.network\nPublishPort=51820:51820/udp\nPublishPort=51821:51821/tcp\n\n# this is used to allow access over HTTP\n# remove this when using a reverse proxy\nEnvironment=INSECURE=true\n\nAddCapability=NET_ADMIN\nAddCapability=SYS_MODULE\nAddCapability=NET_RAW\nSysctl=net.ipv4.ip_forward=1\nSysctl=net.ipv4.conf.all.src_valid_mark=1\nSysctl=net.ipv6.conf.all.disable_ipv6=0\nSysctl=net.ipv6.conf.all.forwarding=1\nSysctl=net.ipv6.conf.default.forwarding=1\n\n[Install]\n# this is used to start the container on boot\nWantedBy=default.target\n

      Create a file /etc/containers/systemd/wg-easy/wg-easy.network with the following content:

      [Network]\nNetworkName=wg-easy\nIPv6=true\n
      "},{"location":"examples/tutorials/podman-nft/#load-kernel-modules","title":"Load Kernel Modules","text":"

      You will need to load the following kernel modules

      wireguard\nnft_masq\n

      Create a file /etc/modules-load.d/wg-easy.conf with the following content:

      wireguard\nnft_masq\n
      "},{"location":"examples/tutorials/podman-nft/#start-the-container","title":"Start the Container","text":"
      sudo systemctl daemon-reload\nsudo systemctl start wg-easy\n
      "},{"location":"examples/tutorials/podman-nft/#edit-hooks","title":"Edit Hooks","text":"

      In the Admin Panel of your WireGuard server, go to the Hooks tab and add the following hook:

      1. PostUp

        nft add table inet wg_table; nft add chain inet wg_table prerouting { type nat hook prerouting priority 100 \\; }; nft add chain inet wg_table postrouting { type nat hook postrouting priority 100 \\; }; nft add rule inet wg_table postrouting ip saddr {{ipv4Cidr}} oifname {{device}} masquerade; nft add rule inet wg_table postrouting ip6 saddr {{ipv6Cidr}} oifname {{device}} masquerade; nft add chain inet wg_table input { type filter hook input priority 0 \\; policy accept \\; }; nft add rule inet wg_table input udp dport {{port}} accept; nft add rule inet wg_table input tcp dport {{uiPort}} accept; nft add chain inet wg_table forward { type filter hook forward priority 0 \\; policy accept \\; }; nft add rule inet wg_table forward iifname \"wg0\" accept; nft add rule inet wg_table forward oifname \"wg0\" accept;\n
      2. PostDown

        nft delete table inet wg_table\n

      If you don't have iptables loaded on your server, you could see many errors in the logs or in the UI. You can ignore them.

      "},{"location":"examples/tutorials/podman-nft/#restart-the-container","title":"Restart the Container","text":"

      Restart the container to apply the new hooks:

      sudo systemctl restart wg-easy\n
      "},{"location":"examples/tutorials/reverse-proxyless/","title":"No Reverse Proxy","text":"

      Insecure

      This is insecure. You should use a reverse proxy to secure the connection.

      Only use this method if you know what you are doing.

      If you only allow access to the web UI from your local network, you can skip the reverse proxy setup. This is not recommended, but it is possible.

      "},{"location":"examples/tutorials/reverse-proxyless/#setup","title":"Setup","text":"
      • Edit the docker-compose.yml file and uncomment environment and INSECURE

      • Set INSECURE to true to allow access to the web UI over a non-secure connection.

      • The docker-compose.yml file should look something like this:

        environment:\n    - INSECURE=true\n
      • Save the file and restart wg-easy.

      • Make sure that the Web UI is not accessible from outside your local network.

      "},{"location":"examples/tutorials/routed/","title":"Routed setup (No NAT)","text":"

      This guide shows how to run wg-easy with a routed setup, so packets are forwarded instead of NATed.

      In a routed design, each WireGuard client keeps its own IPv4/IPv6 address. That means you can identify clients by their real addresses instead of seeing everything as the WireGuard server\u2019s IP.

      "},{"location":"examples/tutorials/routed/#requirements","title":"Requirements","text":"
      1. You know how to add static routes on your router to the WireGuard server.
      "},{"location":"examples/tutorials/routed/#docker-setup","title":"Docker setup","text":"

      To make use of our own IPv4/IPv6 addresses, run the container with the network_mode: host option.

      services:\n    wg-easy:\n        image: ghcr.io/wg-easy/wg-easy:15\n        container_name: wg-easy\n        network_mode: 'host'\n        volumes:\n            - ./config:/etc/wireguard\n            - /lib/modules:/lib/modules:ro\n        cap_add:\n            - NET_ADMIN\n            - SYS_MODULE\n        devices:\n            - /dev/net/tun:/dev/net/tun\n        restart: unless-stopped\n

      Because we\u2019re on the host network, remove any ports: and container sysctls: you might have had before.

      "},{"location":"examples/tutorials/routed/#kernel-parameters-on-the-host","title":"Kernel parameters (on the host)","text":"

      With host networking, system sysctls must be set on the host. On your host, create /etc/sysctl.d/90-wireguard.conf:

      net.ipv4.ip_forward=1\nnet.ipv4.conf.all.src_valid_mark=1\nnet.ipv6.conf.all.disable_ipv6=0\nnet.ipv6.conf.all.forwarding=1\nnet.ipv6.conf.default.forwarding=1\n

      Apply and verify:

      sysctl -p /etc/sysctl.d/90-wireguard.conf\nsysctl -n net.ipv4.ip_forward   # should print 1\n
      "},{"location":"examples/tutorials/routed/#add-static-routes-on-your-router","title":"Add static routes on your router","text":"

      Pick an IPv4 and IPv6 subnet for your clients and add static routes on your router, pointing to the WireGuard server's LAN addresses.

      "},{"location":"examples/tutorials/routed/#example","title":"Example","text":"

      2001:db8::/32

      The documentation prefix 2001:db8::/32 (RFC 3849) used in this example is not meant for production use, replace it with your own ISP-assigned IPv6 prefix (GUA) or local prefix (ULA)

      I want my WireGuard clients in 192.168.0.0/24 and 2001:db8:abc:0::/64.

      • Routed IPv4 subnet: 192.168.0.0/24
      • Routed IPv6 prefix: 2001:db8:abc:0::/64
      • WireGuard server IPs: 192.168.10.118 and 2001:db8:abc:10:216:3eff:fedb:949e

      On your router:

      • Route 192.168.0.0/24 \u2192 next hop 192.168.10.118
      • Route 2001:db8:abc:0::/64 \u2192 next hop 2001:db8:abc:10:216:3eff:fedb:949e

      Don't forget to create the necessary firewall rules to allow these subnets to travel across your LAN. Some routers or servers may require specific Outbound NAT rules for the chosen IPv4 and IPv6 subnets to allow traffic to traverse your LAN.

      "},{"location":"examples/tutorials/routed/#wg-easy-configuration","title":"wg-easy configuration","text":"

      In the Web UI \u2192 Admin \u2192 Interface, click Change CIDR and set the IPv4/IPv6 routed subnets you chose above. Save.

      Then go to Admin \u2192 Hooks and add:

      PostUp

      iptables -A INPUT -p udp -m udp --dport {{port}} -j ACCEPT; iptables -A FORWARD -i wg0 -j ACCEPT; iptables -A FORWARD -o wg0 -j ACCEPT; ip6tables -A INPUT -p udp -m udp --dport {{port}} -j ACCEPT; ip6tables -A FORWARD -i wg0 -j ACCEPT; ip6tables -A FORWARD -o wg0 -j ACCEPT\n

      PostDown

      iptables -D INPUT -p udp -m udp --dport {{port}} -j ACCEPT; iptables -D FORWARD -i wg0 -j ACCEPT; iptables -D FORWARD -o wg0 -j ACCEPT; ip6tables -D INPUT -p udp -m udp --dport {{port}} -j ACCEPT; ip6tables -D FORWARD -i wg0 -j ACCEPT; ip6tables -D FORWARD -o wg0 -j ACCEPT\n

      Important: When using nftables use the following hooks instead.

      PostUp

      nft add chain ip filter WG_EASY; nft add rule ip filter DOCKER-USER jump WG_EASY; nft add rule ip filter WG_EASY iifname {{device}} accept; nft add rule ip filter WG_EASY oifname {{device}} accept; nft add chain ip6 filter WG_EASY; nft add rule ip6 filter DOCKER-USER jump WG_EASY; nft add rule ip6 filter WG_EASY iifname {{device}} accept; nft add rule ip6 filter WG_EASY oifname {{device}} accept;\n

      PostDown

      nft delete rule ip filter DOCKER-USER handle $(nft -a list chain ip filter DOCKER-USER | awk '/jump WG_EASY/ {print $NF}'); nft flush chain ip filter WG_EASY; nft delete chain ip filter WG_EASY; nft delete rule ip6 filter DOCKER-USER handle $(nft -a list chain ip6 filter DOCKER-USER | awk '/jump WG_EASY/ {print $NF}'); nft flush chain ip6 filter WG_EASY; nft delete chain ip6 filter WG_EASY\n
      "},{"location":"examples/tutorials/traefik/","title":"Traefik","text":"

      Opinionated

      This guide is opinionated. If you use other conventions or folder layouts, feel free to change the commands and paths.

      "},{"location":"examples/tutorials/traefik/#create-docker-compose-project","title":"Create docker compose project","text":"
      sudo mkdir -p /etc/docker/containers/traefik\ncd /etc/docker/containers/traefik\n
      "},{"location":"examples/tutorials/traefik/#create-docker-compose-file","title":"Create docker compose file","text":"

      File: /etc/docker/containers/traefik/docker-compose.yml

      services:\n    traefik:\n        image: traefik:3.3\n        container_name: traefik\n        restart: unless-stopped\n        ports:\n            - '80:80'\n            - '443:443/tcp'\n            - '443:443/udp'\n        volumes:\n            - /var/run/docker.sock:/var/run/docker.sock\n            - /etc/docker/volumes/traefik/traefik.yml:/traefik.yml:ro\n            - /etc/docker/volumes/traefik/traefik_dynamic.yml:/traefik_dynamic.yml:ro\n            - /etc/docker/volumes/traefik/acme.json:/acme.json\n        networks:\n            - traefik\n\nnetworks:\n    traefik:\n        external: true\n
      "},{"location":"examples/tutorials/traefik/#create-traefikyml","title":"Create traefik.yml","text":"

      File: /etc/docker/volumes/traefik/traefik.yml

      log:\n    level: INFO\n\nentryPoints:\n    web:\n        address: ':80/tcp'\n        http:\n            redirections:\n                entryPoint:\n                    to: websecure\n                    scheme: https\n    websecure:\n        address: ':443/tcp'\n        http:\n            middlewares:\n                - compress@file\n                - hsts@file\n            tls:\n                certResolver: letsencrypt\n        http3: {}\n\napi:\n    dashboard: true\n\ncertificatesResolvers:\n    letsencrypt:\n        acme:\n            email: $mail@example.com$\n            storage: acme.json\n            httpChallenge:\n                entryPoint: web\n\nproviders:\n    docker:\n        watch: true\n        network: traefik\n        exposedByDefault: false\n    file:\n        filename: traefik_dynamic.yml\n\nserversTransport:\n    insecureSkipVerify: true\n
      "},{"location":"examples/tutorials/traefik/#create-traefik_dynamicyml","title":"Create traefik_dynamic.yml","text":"

      File: /etc/docker/volumes/traefik/traefik_dynamic.yml

      http:\n    middlewares:\n        services:\n            basicAuth:\n                users:\n                    - '$username$:$password$'\n        compress:\n            compress: {}\n        hsts:\n            headers:\n                stsSeconds: 2592000\n    routers:\n        api:\n            rule: Host(`traefik.$example.com$`)\n            entrypoints:\n                - websecure\n            middlewares:\n                - services\n            service: api@internal\n\ntls:\n    options:\n        default:\n            cipherSuites:\n                - TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256\n                - TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384\n                - TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256\n            sniStrict: true\n
      "},{"location":"examples/tutorials/traefik/#create-acmejson","title":"Create acme.json","text":"
      sudo touch /etc/docker/volumes/traefik/acme.json\nsudo chmod 600 /etc/docker/volumes/traefik/acme.json\n
      "},{"location":"examples/tutorials/traefik/#create-network","title":"Create network","text":"
      sudo docker network create traefik\n
      "},{"location":"examples/tutorials/traefik/#start-traefik","title":"Start traefik","text":"
      sudo docker compose up -d\n

      You can now access the Traefik dashboard at https://traefik.$example.com$ with the credentials you set in traefik_dynamic.yml.

      "},{"location":"examples/tutorials/traefik/#add-labels-to-wg-easy","title":"Add Labels to wg-easy","text":"

      To add labels to your wg-easy service, you can add the following to your docker-compose.yml file:

      File: /etc/docker/containers/wg-easy/docker-compose.yml

      services:\n  wg-easy:\n    ...\n    container_name: wg-easy\n    networks:\n      ...\n      traefik: {}\n    labels:\n      - \"traefik.enable=true\"\n      - \"traefik.http.routers.wg-easy.rule=Host(`wg-easy.$example.com$`)\"\n      - \"traefik.http.routers.wg-easy.entrypoints=websecure\"\n      - \"traefik.http.routers.wg-easy.service=wg-easy\"\n      - \"traefik.http.services.wg-easy.loadbalancer.server.port=51821\"\n      - \"traefik.docker.network=traefik\"\n    ...\n\nnetworks:\n  ...\n  traefik:\n    external: true\n
      "},{"location":"examples/tutorials/traefik/#restart-wg-easy","title":"Restart wg-easy","text":"
      cd /etc/docker/containers/wg-easy\nsudo docker compose up -d\n

      You can now access wg-easy at https://wg-easy.$example.com$ and start the setup.

      "},{"location":"guides/2fa/","title":"2FA","text":"

      The user can enable 2FA from the Account page. The Account page is accessible from the dropdown menu in the top right corner of the application.

      "},{"location":"guides/2fa/#enable-totp","title":"Enable TOTP","text":"
      • Enable Two Factor Authentication: Enable TOTP for the user.
      "},{"location":"guides/2fa/#configure-totp","title":"Configure TOTP","text":"

      A QR code will be displayed. Scan the QR code with your TOTP application (e.g., Google Authenticator, Authy, etc.) to add the account.

      To verify that the TOTP key is working, the user must enter the TOTP code generated by the TOTP application.

      • TOTP Key: The TOTP key for the user. This key is used to generate the TOTP code.
      • TOTP Code: The current TOTP code for the user. This code is used to verify the TOTP key.
      • Enable Two Factor Authentication: Enable TOTP for the user.
      "},{"location":"guides/2fa/#disable-totp","title":"Disable TOTP","text":"

      To disable TOTP, the user must enter the current password.

      • Current Password: The current password of the user.
      • Disable Two Factor Authentication: Disable TOTP for the user.
      "},{"location":"guides/admin/","title":"Admin Panel","text":""},{"location":"guides/admin/#interface-settings","title":"Interface Settings","text":""},{"location":"guides/admin/#per-client-firewall","title":"Per-Client Firewall","text":"

      Enable server-side firewall filtering to enforce network access restrictions per client.

      When enabled, each client can have custom \"Firewall Allowed IPs\" configured that restrict which destinations they can access through the VPN. These restrictions are enforced by the server using iptables/ip6tables and cannot be bypassed by the client.

      Experimental Feature

      This feature is currently experimental. While functional, it should be thoroughly tested in your environment before relying on it for production security requirements. Always verify that firewall rules are working as expected using test traffic or by manually inspecting the rules.

      Requirements:

      • iptables must be installed on the host system
      • ip6tables must be installed if IPv6 is enabled (default)
      • The feature cannot be enabled if these tools are not available

      Note

      Most Linux distributions include iptables by default. If you're running in a minimal container environment, you may need to install the iptables package on the host system.

      Enable this feature if you want to:

      • Restrict certain clients to only access specific servers or networks
      • Prevent clients from accessing the internet while allowing LAN access
      • Enforce port-based restrictions (e.g., only allow HTTP/HTTPS)
      • Separate routing configuration from security enforcement

      How it works:

      1. Enable \"Per-Client Firewall\" in Admin Panel \u2192 Interface
      2. Edit any client to see the new \"Firewall Allowed IPs\" field
      3. Specify allowed destinations (IPs, subnets, ports) for that client
      4. Server enforces these rules automatically

      See Edit Client \u2192 Firewall Allowed IPs for detailed configuration syntax and examples.

      "},{"location":"guides/cli/","title":"CLI","text":"

      If you want to use the CLI, you can run it with

      "},{"location":"guides/cli/#docker-compose","title":"Docker Compose","text":"
      cd /etc/docker/containers/wg-easy\ndocker compose exec -it wg-easy cli\n
      "},{"location":"guides/cli/#docker-run","title":"Docker Run","text":"
      docker run --rm -it \\\n    -v ~/.wg-easy:/etc/wireguard \\\n    ghcr.io/wg-easy/wg-easy:15 \\\n    cli\n
      "},{"location":"guides/cli/#reset-password","title":"Reset Password","text":"

      If you want to reset the password for the admin user, you can run the following command:

      "},{"location":"guides/cli/#by-prompt","title":"By Prompt","text":"
      cd /etc/docker/containers/wg-easy\ndocker compose exec -it wg-easy cli db:admin:reset\n

      You are asked to provide the new password

      "},{"location":"guides/cli/#by-argument","title":"By Argument","text":"
      cd /etc/docker/containers/wg-easy\ndocker compose exec -it wg-easy cli db:admin:reset --password <new_password>\n

      This will reset the password for the admin user to the new password you provided. If you include special characters in the password, make sure to escape them properly.

      "},{"location":"guides/cli/#show-clients","title":"Show Clients","text":"

      List all clients that are currently configured with details such as client ID, Name, Public Key, and enabled status.

      cli clients:list\n
      "},{"location":"guides/cli/#show-client-qr-code","title":"Show Client QR Code","text":"

      Display the QR code for a specific client, which can be scanned by a compatible app to import the client's configuration.

      cli clients:qr <client_id>\n

      Replace <client_id> with the actual client ID you want to show the QR code for.

      IPv6 Support

      IPv6 support is enabled by default, even if you disabled it using environment variables. To disable it pass the --no-ipv6 flag when running the CLI.

      cli clients:qr <client_id> --no-ipv6\n
      "},{"location":"guides/clients/","title":"Edit Client","text":""},{"location":"guides/clients/#general","title":"General","text":"
      • Name: The name of the client.
      • Enabled: Whether the client can connect to the VPN.
      • Expire Date: The date the client will be disabled.
      "},{"location":"guides/clients/#address","title":"Address","text":"
      • IPv4: The IPv4 address of the client.
      • IPv6: The IPv6 address of the client.
      "},{"location":"guides/clients/#allowed-ips","title":"Allowed IPs","text":"

      Which IPs will be routed through the VPN.

      This will not prevent the user from modifying it locally and accessing IP ranges that they should not be able to access.

      Use the Firewall Allowed IPs feature to prevent access to IP ranges that the user should not be able to access.

      "},{"location":"guides/clients/#firewall-allowed-ips","title":"Firewall Allowed IPs","text":"

      Attention

      This field only appears when Per-Client Firewall is enabled in the Admin Panel \u2192 Interface settings.

      Server-side firewall rules that restrict which destinations the client can access, regardless of their local configuration.

      Unlike \"Allowed IPs\" which only controls routing on the client side, these rules are enforced by the server using iptables/ip6tables and cannot be bypassed by the client.

      Supported Formats:

      • 10.10.0.3, 2001:db8::1 - Allow access to a single IP address
      • 10.10.0.0/24, 2001:db8::/32 - Allow access to an entire subnet
      • 192.168.1.5:443 - Allow access to specific port (TCP+UDP)
      • 192.168.1.5:443/tcp - Allow access to specific port (TCP only)
      • 192.168.1.5:443/udp - Allow access to specific port (UDP only)
      • 10.10.0.0/24:443 - Allow access to an entire subnet on a specific port (TCP+UDP)
      • 10.10.0.0/24:443/tcp - Allow access to an entire subnet on a specific port (TCP only)
      • 10.10.0.0/24:443/udp - Allow access to an entire subnet on a specific port (UDP only)
      • [2001:db8::1]:443 - IPv6 address with port (brackets required)
      • [2001:db8::/32]:443/tcp - IPv6 CIDR with port and protocol

      Invalid Formats

      Protocol specifiers (/tcp or /udp) require a port number. The following formats are not supported and will result in an error:

      • 10.10.0.3/tcp (use 10.10.0.3:443/tcp instead)
      • 10.10.0.0/24/udp (use 10.10.0.0/24:53/udp instead)

      Behavior:

      • Empty: Falls back to the client's \"Allowed IPs\" setting
      • Specified: Only listed destinations are accessible (allow-only, everything else is blocked)
      • Disable for specific client: To disable firewall filtering for a single client while keeping it enabled for others, add 0.0.0.0/0, ::/0 to allow all traffic

      Note

      To allow clients to reach the VPN server itself (e.g. for DNS), include the server's VPN address in the firewall allowed IPs.

      Use Case Examples:

      • Allow only specific servers: 10.10.0.5
      • Allow only internal network: 10.10.0.0/24, 192.168.1.0/24
      • Allow only web browsing: 0.0.0.0/0:80, 0.0.0.0/0:443, [::/0]:80, [::/0]:443
      • Block internet, allow LAN: Leave \"Allowed IPs\" as 0.0.0.0/0, ::/0 but set Firewall IPs to 10.0.0.0/8, 192.168.0.0/16
      "},{"location":"guides/clients/#server-allowed-ips","title":"Server Allowed IPs","text":"

      Which IPs will be routed to the client.

      "},{"location":"guides/clients/#dns","title":"DNS","text":"

      The DNS server that the client will use.

      "},{"location":"guides/clients/#advanced","title":"Advanced","text":"
      • MTU: The maximum transmission unit for the client.
      • Persistent Keepalive: The interval for sending keepalive packets to the server.
      "},{"location":"guides/clients/#hooks","title":"Hooks","text":"

      This can only be used for clients that use wg-quick. Setting this will throw a error when importing the config on other clients.

      • PreUp: Commands to run before the interface is brought up.
      • PostUp: Commands to run after the interface is brought up.
      • PreDown: Commands to run before the interface is brought down.
      • PostDown: Commands to run after the interface is brought down.
      "},{"location":"guides/clients/#actions","title":"Actions","text":"
      • Save: Save the changes made in the form.
      • Revert: Revert the changes made in the form.
      • Delete: Delete the client.
      "},{"location":"guides/setup/","title":"Setup","text":""},{"location":"guides/setup/#user-setup","title":"User Setup","text":"
      • Username: The username of the user.
      • Password: The password of the user.
      • Confirm Password: The password of the user.
      "},{"location":"guides/setup/#existing-setup","title":"Existing Setup","text":"

      If you have the config from the previous version, you can import it by clicking \"Yes\". This currently expects a config from v14.

      If this is the first time you are using this, you can click \"No\" to create a new config.

      "},{"location":"guides/setup/#no-host-setup","title":"No - Host Setup","text":"
      • Host: The host of the server. The clients will connect to this address. This can be a domain name or an IP address. Make sure to wrap it in brackets if it is an IPv6 address. For example: [::1] or [2001:db8::1].
      • Port: The port of the server. The clients will connect to this port. The server will listen on this port.
      "},{"location":"guides/setup/#yes-migration","title":"Yes - Migration","text":"

      Select the wg0.json file from the previous version. Read Migrate from v14 to v15 for more information.

      "}]} \ No newline at end of file +{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"],"fields":{"title":{"boost":1000.0},"text":{"boost":1.0},"tags":{"boost":1000000.0}}},"docs":[{"location":"","title":"Welcome to the Documentation for wg-easy","text":"

      \u00a0This Documentation is Versioned

      Make sure to select the correct version of this documentation! It should match the version of the image you are using. The default version corresponds to the most recent stable release.

      This documentation provides you not only with the basic setup and configuration of wg-easy but also with advanced configuration, elaborate usage scenarios, detailed examples, hints and more.

      "},{"location":"#about","title":"About","text":"

      wg-easy is the easiest way to run WireGuard VPN + Web-based Admin UI.

      "},{"location":"#contents","title":"Contents","text":""},{"location":"#getting-started","title":"Getting Started","text":"

      If you're new to wg-easy, make sure to read the Getting Started chapter first. If you want to look at examples for Docker Run and Compose, we have an Examples page.

      "},{"location":"#contributing","title":"Contributing","text":"

      We are always happy to welcome new contributors. For guidelines and entrypoints please have a look at the Contributing section.

      "},{"location":"#migration","title":"Migration","text":"

      If you are migrating from an older version of wg-easy, please read the Migration chapter.

      "},{"location":"faq/","title":"FAQ","text":"

      Here are some frequently asked questions or errors about wg-easy. If you have a question that is not answered here, please feel free to open a discussion on GitHub.

      "},{"location":"faq/#how-do-i-restrict-client-access-to-specific-networks-or-servers","title":"How do I restrict client access to specific networks or servers?","text":"

      Use the Per-Client Firewall feature to enforce server-side restrictions on what each client can access.

      Requirements: This feature requires iptables (and ip6tables for IPv6) to be installed on the host system.

      1. Enable \"Per-Client Firewall\" in Admin Panel \u2192 Interface
      2. Edit a client and configure \"Firewall Allowed IPs\"
      3. Specify which destinations the client should be allowed to access

      Unlike \"Allowed IPs\" which only controls client-side routing, firewall rules are enforced by the server and cannot be bypassed.

      See the Admin Panel Guide and Client Guide for detailed configuration.

      "},{"location":"faq/#error-wireguard-exited-with-the-error-cannot-find-device-wg0","title":"Error: WireGuard exited with the error: Cannot find device \"wg0\"","text":"

      This error indicates that the WireGuard interface wg0 does not exist. This can happen if the WireGuard kernel module is not loaded or if the interface was not created properly.

      To resolve this issue, you can try the following steps:

      1. Load the WireGuard kernel module: If the WireGuard kernel module is not loaded, you can load it manually by running:

        sudo modprobe wireguard\n
      2. Load the WireGuard kernel module on boot: If you want to ensure that the WireGuard kernel module is loaded automatically on boot, you can add it to the /etc/modules file:

        echo \"wireguard\" | sudo tee -a /etc/modules\n
      "},{"location":"faq/#cant-initialize-iptables-table-nat-table-does-not-exist-do-you-need-to-insmod","title":"can't initialize iptables table `nat': Table does not exist (do you need to insmod?)","text":"

      This error indicates that the nat table in iptables does not exist. This can happen if the iptables kernel module is not loaded or if the nat table is not supported by your kernel.

      To resolve this issue, you can try the following steps:

      1. Load the nat kernel module: If the nat kernel module is not loaded, you can load it manually by running:

        sudo modprobe iptable_nat\n
      2. Load the nat kernel module on boot: If you want to ensure that the nat kernel module is loaded automatically on boot, you can add it to the /etc/modules file:

         echo \"iptable_nat\" | sudo tee -a /etc/modules\n
      "},{"location":"faq/#cant-initialize-ip6tables-table-nat-table-does-not-exist-do-you-need-to-insmod","title":"can't initialize ip6tables table `nat': Table does not exist (do you need to insmod?)","text":"

      This error indicates that the nat table in ip6tables does not exist. This can happen if the ip6tables kernel module is not loaded or if the nat table is not supported by your kernel.

      To resolve this issue, you can try the following steps:

      1. Load the nat kernel module: If the nat kernel module is not loaded, you can load it manually by running:

        sudo modprobe ip6table_nat\n
      2. Load the nat kernel module on boot: If you want to ensure that the nat kernel module is loaded automatically on boot, you can add it to the /etc/modules file:

         echo \"ip6table_nat\" | sudo tee -a /etc/modules\n
      "},{"location":"faq/#cant-initialize-iptables-table-filter-permission-denied","title":"can't initialize iptables table `filter': Permission denied","text":"

      This error indicates that the filter table in iptables cannot be initialized due to permission issues. This can happen if you are not running the command with sufficient privileges.

      To resolve this issue, you can try the following steps:

      1. Load the filter kernel module: If the filter kernel module is not loaded, you can load it manually by running:

        sudo modprobe iptable_filter\n
      2. Load the filter kernel module on boot: If you want to ensure that the filter kernel module is loaded automatically on boot, you can add it to the /etc/modules file:

        echo \"iptable_filter\" | sudo tee -a /etc/modules\n
      "},{"location":"faq/#cant-initialize-ip6tables-table-filter-permission-denied","title":"can't initialize ip6tables table `filter': Permission denied","text":"

      This error indicates that the filter table in ip6tables cannot be initialized due to permission issues. This can happen if you are not running the command with sufficient privileges.

      To resolve this issue, you can try the following steps:

      1. Load the filter kernel module: If the filter kernel module is not loaded, you can load it manually by running:

        sudo modprobe ip6table_filter\n
      2. Load the filter kernel module on boot: If you want to ensure that the filter kernel module is loaded automatically on boot, you can add it to the /etc/modules file:

         echo \"ip6table_filter\" | sudo tee -a /etc/modules\n
      "},{"location":"faq/#clients-lose-connectivity-after-restarting-the-container-when-using-multiple-networks","title":"Clients lose connectivity after restarting the container when using multiple networks?","text":"

      When you attach multiple Docker networks (e.g., wg and a reverse proxy network like traefik or nginx) to the wg-easy container, Docker might assign the network interfaces randomly (e.g., swapping eth0 and eth1). Since wg-easy expects the wireguard interface to act as eth0 and configures POSTROUTING rules for it, connectivity will break if the interfaces are swapped upon container restart.

      To solve this, specify the interface_name and gw_priority explicitly in your docker-compose.yml file to guarantee that the wg network always binds to eth0 and acts as the default gateway.

      Example docker-compose.yml:

      services:\n    wg-easy:\n        # ... other configuration ...\n        networks:\n            wg:\n                interface_name: eth0\n                gw_priority: 1\n                ipv4_address: 10.42.42.42\n            nginx:\n                interface_name: eth1\n                gw_priority: 0\n\nnetworks:\n    wg:\n        # ... wg network config ...\n    nginx:\n        external: true\n
      "},{"location":"getting-started/","title":"Getting Started","text":"

      This page explains how to get started with wg-easy. The guide uses Docker Compose as a reference. In our examples, we mount the named volume etc_wireguard to /etc/wireguard inside the container.

      "},{"location":"getting-started/#preliminary-steps","title":"Preliminary Steps","text":"

      Before you can get started with deploying your own VPN, there are some requirements to be met:

      1. You need to have a host that you can manage
      2. You need to have a domain name or a public IP address
      3. You need a supported architecture (x86_64, arm64)
      "},{"location":"getting-started/#host-setup","title":"Host Setup","text":"

      There are a few requirements for a suitable host system:

      1. You need to have a container runtime installed

      About the Container Runtime

      On the host, you need to have a suitable container runtime (like Docker or Podman) installed. We assume Docker Compose is installed. We have aligned file names and configuration conventions with the latest Docker Compose specification. If you're using podman, make sure to read the related documentation.

      "},{"location":"getting-started/#deploying-the-actual-image","title":"Deploying the Actual Image","text":""},{"location":"getting-started/#tagging-convention","title":"Tagging Convention","text":"

      To understand which tags you should use, read this section carefully. Our CI will automatically build, test and push new images to the following container registry:

      1. GitHub Container Registry (ghcr.io/wg-easy/wg-easy)
      2. Codeberg Container Registry (codeberg.org/wg-easy/wg-easy) (IPv6 support)

      All workflows are using the tagging convention listed below. It is subsequently applied to all images.

      tag Type Example Description 15 latest minor for that major tag ghcr.io/wg-easy/wg-easy:15 latest features for specific major versions, no breaking changes, recommended 15.0 latest patch for that minor tag ghcr.io/wg-easy/wg-easy:15.0 latest patches for specific minor version 15.0.0 specific tag ghcr.io/wg-easy/wg-easy:15.0.0 specific release, no updates edge push to master ghcr.io/wg-easy/wg-easy:edge mostly unstable, gets frequent package and code updates development pull requests ghcr.io/wg-easy/wg-easy:development used for development, testing code from PRs latest latest tag ghcr.io/wg-easy/wg-easy:latest or ghcr.io/wg-easy/wg-easy points to the v14 release, should be avoided

      When publishing a tag we follow the Semantic Versioning specification. Pin to the latest major version to avoid breaking changes (e.g. 15), avoid using the latest tag.

      "},{"location":"getting-started/#follow-tutorials","title":"Follow tutorials","text":"
      • Basic Installation with Docker Compose (Recommended)
      • Simple Installation with Docker Run
      • Advanced Installation with Podman

      Use the Correct Commands For Stopping and Starting wg-easy

      Use sudo docker compose up / down, not sudo docker compose start / stop. Otherwise, the container is not properly destroyed and you may experience problems during startup because of inconsistent state.

      That's it! It really is that easy.

      "},{"location":"advanced/api/","title":"API","text":"

      Breaking Changes

      This API is not yet stable and may change in the future. The API is currently in development and is subject to change without notice. The API is not yet documented, but we will add documentation as the API stabilizes.

      You can use the API to interact with the application programmatically. The API is available at /api and supports both GET and POST requests. The API is designed to be simple and easy to use, with a focus on providing a consistent interface for all endpoints.

      There is no documentation for the API yet, but this will be added as the underlying library supports it.

      "},{"location":"advanced/api/#authentication","title":"Authentication","text":"

      To use the API, you need to authenticate using Basic Authentication. The username and password are the same as the ones you use to log in to the web application. If you use 2FA, the API will not work. You need to disable 2FA in the web application to use the API.

      "},{"location":"advanced/api/#authentication-example","title":"Authentication Example","text":"
      import requests\nfrom requests.auth import HTTPBasicAuth\n\nurl = \"https://example.com:51821/api/client\"\nresponse = requests.get(url, auth=HTTPBasicAuth('username', 'password'))\nif response.status_code == 200:\n    data = response.json()\n    print(data)\nelse:\n    print(f\"Error: {response.status_code}\")\n
      "},{"location":"advanced/api/#endpoints","title":"Endpoints","text":"

      The Endpoints are not yet documented. But as file-based routing is used, you can find the endpoints in the src/server/api folder. The method is defined in the file name.

      "},{"location":"advanced/api/#endpoints-example","title":"Endpoints Example","text":"File Name Endpoint Method src/server/api/client.get.ts /api/client GET src/server/api/setup/2.post.ts /api/setup/2 POST"},{"location":"advanced/config/amnezia/","title":"AmneziaWG","text":""},{"location":"advanced/config/amnezia/#introduction","title":"Introduction","text":"

      AmneziaWG is a modified version of the WireGuard protocol with enhanced traffic obfuscation capabilities. AmneziaWG's primary goal is to counter deep packet inspection (DPI) systems and bypass VPN blocking.

      AmneziaWG adds multi-level transport-layer obfuscation by:

      • Modifying packet headers
      • Randomizing handshake message sizes
      • Disguising traffic to resemble popular UDP protocols

      These measures make it harder for third parties to analyze or identify your traffic, enhancing both privacy and security.

      "},{"location":"advanced/config/amnezia/#activating-amneziawg","title":"Activating AmneziaWG","text":"

      You must install the AmneziaWG kernel module on the host system.

      Experimental support for AmneziaWG can be enabled by setting the EXPERIMENTAL_AWG environment variable to true. Starting from wg-easy version 16, this setting will be enabled by default. This feature is still under development and may change in future releases.

      When enabled, wg-easy will automatically detect whether the AmneziaWG kernel module is available. If it is not, the system will fall back to the standard WireGuard module.

      To override this automatic detection, set the OVERRIDE_AUTO_AWG environment variable. By default, this variable is unset.

      Possible values:

      • awg \u2014 Force use of AmneziaWG
      • wg \u2014 Force use of standard WireGuard
      "},{"location":"advanced/config/amnezia/#amneziawg-parameters","title":"AmneziaWG Parameters","text":"

      Parameter descriptions can be found in the AmneziaWG documentation and on the kernel module page.

      All parameters except I1-I5 will be set at first startup. For information on how to set I1-I5 parameters, refer to the AmneziaWG documentation.

      If a parameter is not set, it will not be added to the configuration. If all AmneziaWG-specific parameters are absent, AmneziaWG will be fully compatible with standard WireGuard.

      "},{"location":"advanced/config/amnezia/#parameter-compatibility-table","title":"Parameter Compatibility Table","text":"Parameter Can differ between server and client Configurable on server Configurable on client Jc \u2705 Yes \u2705 \u2705 Jmin \u2705 Yes \u2705 \u2705 Jmax \u2705 Yes \u2705 \u2705 S1-S4 \u274c No, must match \u2705 \u274c (copied from server) H1-H4 \u274c No, must match \u2705 \u274c (copied from server) I1-I5 \u2705 Yes \u2705 \u2705"},{"location":"advanced/config/amnezia/#client-applications","title":"Client Applications","text":"

      To be able to connect to wg-easy if AmneziaWG is enabled, you must have an AmneziaWG-compatible client. Where an AmneziaWG app is available for your platform, it is recommended to use it rather than Amnezia VPN.

      Android:

      • AmneziaWG - AmneziaWG Official Client
      • WG Tunnel - Third Party Client
      • Amnezia VPN - Amnezia VPN Official Client

      iOS and macOS:

      • AmneziaWG - AmneziaWG Official Client
      • Amnezia VPN - Amnezia VPN Official Client

      Windows:

      • AmneziaWG - AmneziaWG Official Client (Requires building from source code)
      • Amnezia VPN - Amnezia VPN Official Client

      Linux:

      • Amnezia VPN - Amnezia VPN Official Client
      • amneziawg-tools - AmneziaWG Tools

      OpenWRT:

      • AmneziaWG OpenWRT - AmneziaWG OpenWRT Packages
      • AmneziaWG OpenWRT - AmneziaWG OpenWRT Packages
      "},{"location":"advanced/config/experimental-config/","title":"Experimental Configuration","text":"

      There are several experimental features that can be enabled by setting the appropriate environment variables. These features are not guaranteed to be stable and may change in future releases.

      Env Default Example Description Notes More Info EXPERIMENTAL_AWG false true Enables experimental AmneziaWG support Planned to be enabled by default in v16 See here"},{"location":"advanced/config/external-authentication/","title":"External Authentication","text":""},{"location":"advanced/config/external-authentication/#oauth","title":"OAuth","text":""},{"location":"advanced/config/external-authentication/#setup","title":"Setup","text":"

      To enable OAuth set the env var OAUTH_PROVIDERS to any of the following providers:

      Provider Value Google google GitHub github Generic OIDC oidc

      You can enable multiple providers by separating them with a comma:

      e.g. google,github

      "},{"location":"advanced/config/external-authentication/#auto-register","title":"Auto Register","text":"

      To automatically register users that log in with an OAuth provider, set the following environment variable to true:

      Env Required Default Description OAUTH_AUTO_REGISTER \u2716\ufe0f false Enable auto-registration

      When enabled:

      • If a user logs in with an email address that is not yet registered, a new account will be created for them.

      • If a user logs in with an email address that is already registered, their account will be linked to the OAuth provider (if not already linked), regardless of the value of OAUTH_AUTO_REGISTER.

      \u00a0Security

      Users will be created with Admin Permissions, as the permissions system is not yet implemented. Only enable this if you trust all users that can log in with the OAuth provider.

      Use Allowed Domains to restrict which users can log in.

      "},{"location":"advanced/config/external-authentication/#allowed-domains","title":"Allowed Domains","text":"

      To only allow users with an email address from a specific domain to log in, set the following environment variable to the allowed domain.

      Env Required Default Description OAUTH_ALLOWED_DOMAINS \u2716\ufe0f - Allowed email domains

      You can allow multiple domains by separating them with a comma:

      e.g. example.com,example.org

      "},{"location":"advanced/config/external-authentication/#auto-launch","title":"Auto Launch","text":"

      To automatically launch the OAuth login flow when visiting the login page, set the following environment variable to the provider you want to launch:

      Env Required Default Description OAUTH_AUTO_LAUNCH \u2716\ufe0f - Auto launch an OAuth provider

      When enabled:

      • Visiting the login page will automatically redirect to the selected provider's login page
      • The user can still access the normal login page by visiting /login?auto_launch=false
      • You can auto launch any provider by visiting /login?auto_launch=<provider>
      "},{"location":"advanced/config/external-authentication/#redirect-uris","title":"Redirect URIs","text":"

      You have to configure the following redirect URIs in your OAuth provider:

      • https://<your-domain>/api/auth/<provider>/callback Used to log in to with the provider
      • https://<your-domain>/api/auth/<provider>/link Used to link an existing account to the provider

      If your provider does not support multiple redirect URIs (e.g. GitHub) but allows multiple URIs under the same base, then configure:

      • https://<your-domain>/api/auth/<provider>/
      "},{"location":"advanced/config/external-authentication/#provider-configuration","title":"Provider Configuration","text":""},{"location":"advanced/config/external-authentication/#google","title":"Google","text":"Env Required Description OAUTH_GOOGLE_CLIENT_ID \u2714\ufe0f Google Client ID OAUTH_GOOGLE_CLIENT_SECRET \u2714\ufe0f Google Client Secret Setup
      1. Go to Google Cloud Console
      2. Create an OAuth 2.0 Client ID (Web application)
      3. Add Authorized redirect URI: See Redirect URIs
      4. Copy the Client ID and Client Secret to the environment variables
      "},{"location":"advanced/config/external-authentication/#github","title":"GitHub","text":"Env Required Description OAUTH_GITHUB_CLIENT_ID \u2714\ufe0f GitHub Client ID OAUTH_GITHUB_CLIENT_SECRET \u2714\ufe0f GitHub Client Secret Setup
      1. Go to GitHub Developer Settings
      2. Create a new OAuth App
      3. Add Authorization callback URL: See Redirect URIs
      4. Create a new client secret
      5. Copy the Client ID and Client Secret to the environment variables
      "},{"location":"advanced/config/external-authentication/#generic-oidc","title":"Generic OIDC","text":"

      This supports generic OIDC providers like Authelia, Authentik, etc.

      The provider needs to support:

      • PKCE
      • default scopes: openid email profile
      • Client Secret Authentication client_secret_post

      The provider needs to be available with HTTPS and have a valid certificate.

      Env Required Default Example Description OAUTH_OIDC_SERVER \u2714\ufe0f - https://auth.example.com OIDC Server OAUTH_OIDC_CLIENT_ID \u2714\ufe0f - - OIDC Client ID OAUTH_OIDC_CLIENT_SECRET \u2714\ufe0f - - OIDC Client Secret OAUTH_OIDC_NAME \u2716\ufe0f OIDC Authelia Provider Name"},{"location":"advanced/config/external-authentication/#authelia-setup","title":"Authelia Setup","text":"

      Generate Client ID and Secret:

      # Client ID\ndocker run --rm authelia/authelia:latest authelia crypto rand --length 72 --charset rfc3986\n# Client Secret\ndocker run --rm authelia/authelia:latest authelia crypto hash generate pbkdf2 --variant sha512 --random --random.length 72 --random.charset rfc3986\n
      - client_id: '...'\n  client_name: wg-easy\n  client_secret: '$pbkdf2-...'\n  redirect_uris:\n      - https://<your-domain>/api/auth/oidc/callback\n      - https://<your-domain>/api/auth/oidc/link\n  scopes:\n      - openid\n      - profile\n      - email\n  authorization_policy: one_factor\n  pre_configured_consent_duration: 1 week\n  require_pkce: true\n  token_endpoint_auth_method: client_secret_post\n
      "},{"location":"advanced/config/external-authentication/#generic-oauth","title":"Generic OAuth","text":"

      Not currently supported

      "},{"location":"advanced/config/external-authentication/#disable-password-authentication","title":"Disable Password Authentication","text":"

      To disable password-based authentication and only allow login via OAuth providers, set the following environment variable to true:

      Env Required Default Description DISABLE_PASSWORD_AUTH \u2716\ufe0f false Disable password authentication

      When enabled:

      • Users will not be able to log in with a password

      Access Recovery

      Before disabling password authentication, ensure that at least one OAuth provider is configured and that you have successfully linked an administrator account.

      If no login method is available, you will not be able to log in to the application and will need to reset the configuration to regain access.

      "},{"location":"advanced/config/optional-config/","title":"Optional Configuration","text":"

      You can set these environment variables to configure the container. They are not required, but can be useful in some cases.

      Env Default Example Description PORT 51821 6789 TCP port for Web UI. HOST 0.0.0.0 localhost IP address web UI binds to. INSECURE false true If access over http is allowed DISABLE_IPV6 false true If IPv6 support should be disabled DISABLE_VERSION_CHECK false true If wg-easy should check for new updates

      IPv6 Caveats

      Disabling IPv6 will disable the creation of the default IPv6 firewall rules and won't add a IPv6 address to the interface and clients.

      You will however still see a IPv6 address in the Web UI, but it won't be used.

      This option can be removed in the future, as more devices support IPv6.

      "},{"location":"advanced/config/unattended-setup/","title":"Unattended Setup","text":"

      If you want to run the setup without any user interaction, e.g. with a tool like Ansible, you can use these environment variables to configure the setup.

      These will only be used during the first start of the container. After that, the setup will be disabled.

      Env Example Description Group INIT_ENABLED true Enables the below env vars 0 INIT_USERNAME admin Sets admin username 1 INIT_PASSWORD Se!ureP%ssw Sets admin password 1 INIT_HOST vpn.example.com Host clients will connect to 1 INIT_PORT 51820 Port clients will connect to and wireguard will listen on 1 INIT_DNS 1.1.1.1,8.8.8.8 Sets global dns setting 2 INIT_IPV4_CIDR 10.8.0.0/24 Sets IPv4 cidr 3 INIT_IPV6_CIDR 2001:0DB8::/32 Sets IPv6 cidr 3 INIT_ALLOWED_IPS 10.8.0.0/24,2001:0DB8::/32 Sets global Allowed IPs 4

      Variables have to be used together

      If variables are in the same group, you have to set all of them. For example, if you set INIT_IPV4_CIDR, you also have to set INIT_IPV6_CIDR.

      If you want to skip the setup process, you have to configure group 1

      Security

      The initial username and password is not checked for complexity. Make sure to set a long enough username and password. Otherwise, the user won't be able to log in.

      It's recommended to remove the variables after the setup is done to prevent the password from being exposed.

      "},{"location":"advanced/metrics/prometheus/","title":"Prometheus","text":"

      To monitor the WireGuard server, you can use Prometheus and Grafana. The container exposes a /metrics/prometheus endpoint that can be scraped by Prometheus.

      "},{"location":"advanced/metrics/prometheus/#enable-prometheus","title":"Enable Prometheus","text":"

      To enable Prometheus metrics, go to Admin Panel > General and enable Prometheus.

      You can optionally set a Bearer Password for the metrics endpoints. This is useful if you want to expose the metrics endpoint to the internet.

      "},{"location":"advanced/metrics/prometheus/#configure-prometheus","title":"Configure Prometheus","text":"

      You need to add a scrape config to your Prometheus configuration file. Here is an example:

      scrape_configs:\n    - job_name: 'wg-easy'\n      scrape_interval: 30s\n      metrics_path: /metrics/prometheus\n      static_configs:\n          - targets:\n                - 'localhost:51821'\n      authorization:\n          type: Bearer\n          credentials: 'SuperSecurePassword'\n
      "},{"location":"advanced/metrics/prometheus/#grafana-dashboard","title":"Grafana Dashboard","text":"

      You can use the following Grafana dashboard to visualize the metrics:

      21733

      Unofficial

      The Grafana dashboard is not official and is not maintained by the wg-easy team. If you have any issues with the dashboard, please contact the author of the dashboard. See #1299 for more information.

      "},{"location":"advanced/migrate/","title":"Migrate","text":"

      If you want to migrate from an older version of wg-easy to the new version, you can find the migration guides listed below.

      • Migrate from v14 to v15 : This guide should also work for any version before v14.
      "},{"location":"advanced/migrate/from-14-to-15/","title":"Migrate from v14 to v15","text":"

      This guide will help you migrate from v14 to version v15 of wg-easy.

      "},{"location":"advanced/migrate/from-14-to-15/#changes","title":"Changes","text":"
      • This is a complete rewrite of the wg-easy project, therefore the configuration files and the way you interact with the project have changed.
      • If you use armv6 or armv7, you unfortunately won't be able to migrate to v15.
      • If you are connecting to the Web UI via HTTP, you need to set the INSECURE environment variable to true in the new container.
      "},{"location":"advanced/migrate/from-14-to-15/#migration","title":"Migration","text":""},{"location":"advanced/migrate/from-14-to-15/#backup","title":"Backup","text":"

      Before you start the migration, make sure to back up your existing configuration files.

      Go into the Web UI and click the Backup button, this should download a wg0.json file.

      Or download the wg0.json file from your container volume to your pc.

      You will need this file for the migration

      You will also need to back up the old environment variables you set for the container, as they will not be automatically migrated.

      "},{"location":"advanced/migrate/from-14-to-15/#remove-old-container","title":"Remove old container","text":"
      1. Stop the running container

      If you are using docker run

      docker stop wg-easy\n

      If you are using docker compose

      docker compose down\n
      "},{"location":"advanced/migrate/from-14-to-15/#start-new-container","title":"Start new container","text":"

      Follow the instructions in the Getting Started or Basic Installation guide to start the new container.

      In the setup wizard, select that you already have a configuration file and upload the wg0.json file you downloaded in the backup step.

      "},{"location":"advanced/migrate/from-14-to-15/#environment-variables","title":"Environment Variables","text":"

      v15 does not use the same environment variables as v14, most of them have been moved to the Admin Panel in the Web UI.

      "},{"location":"advanced/migrate/from-14-to-15/#done","title":"Done","text":"

      You have now successfully migrated to v15 of wg-easy.

      "},{"location":"contributing/general/","title":"General Information","text":""},{"location":"contributing/general/#coding-style","title":"Coding Style","text":"

      When refactoring, writing or altering files, adhere to these rules:

      1. Adjust your style of coding to the style that is already present! Even if you do not like it, this is due to consistency. There was a lot of work involved in making all files consistent.
      2. Use pnpm lint to check your scripts! Your contributions are checked by GitHub Actions too, so you will need to do this.
      3. Use the provided .vscode/settings.json file.
      "},{"location":"contributing/general/#documentation","title":"Documentation","text":"

      Make sure to select edge in the dropdown menu at the top. Navigate to the page you would like to edit and click the edit button in the top right. This allows you to make changes and create a pull-request.

      Alternatively you can make the changes locally. For that you'll need to have Docker installed. Run

      pnpm docs:serve\n

      This serves the documentation on your local machine on port 8080. Each change will be hot-reloaded onto the page you view, just edit, save and look at the result.

      "},{"location":"contributing/issues-and-pull-requests/","title":"Issues and Pull Requests","text":"

      This project is Open Source. That means that you can contribute on enhancements, bug fixing or improving the documentation.

      "},{"location":"contributing/issues-and-pull-requests/#opening-an-issue","title":"Opening an Issue","text":"

      Attention

      Before opening an issue, read the README carefully, study the docs for your version (maybe latest) and your search engine you trust. The issue tracker is not meant to be used for unrelated questions!

      When opening an issue, please provide details use case to let the community reproduce your problem.

      Attention

      Use the issue templates to provide the necessary information. Issues which do not use these templates are not worked on and closed.

      By raising issues, I agree to these terms and I understand, that the rules set for the issue tracker will help both maintainers as well as everyone to find a solution.

      Maintainers take the time to improve on this project and help by solving issues together. It is therefore expected from others to make an effort and comply with the rules.

      "},{"location":"contributing/issues-and-pull-requests/#filing-a-bug-report","title":"Filing a Bug Report","text":"

      Thank you for participating in this project and reporting a bug. wg-easy is a community-driven project, and each contribution counts!

      Maintainers and moderators are volunteers. We greatly appreciate reports that take the time to provide detailed information via the template, enabling us to help you in the best and quickest way. Ignoring the template provided may seem easier, but discourages receiving any support.

      Markdown formatting can be used in almost all text fields (unless stated otherwise in the description).

      Be as precise as possible, and if in doubt, it's best to add more information that too few.

      When an option is marked with \"not officially supported\" / \"unsupported\", then support is dependent on availability from specific maintainers.

      "},{"location":"contributing/issues-and-pull-requests/#pull-requests","title":"Pull Requests","text":"

      Motivation

      You want to add a feature? Feel free to start creating an issue explaining what you want to do and how you're thinking doing it. Other users may have the same need and collaboration may lead to better results.

      "},{"location":"contributing/issues-and-pull-requests/#submit-a-pull-request","title":"Submit a Pull-Request","text":"

      The development workflow is the following:

      1. Fork the project
      2. Write the code that is needed :D
      3. Document your improvements if necessary
      4. Commit (and sign your commit), push and create a pull-request to merge into master. Please use the pull-request template to provide a minimum of contextual information and make sure to meet the requirements of the checklist.

      Pull requests are automatically tested against the CI and will be reviewed when tests pass. When your changes are validated, your branch is merged. CI builds the new :edge image on every push to the master branch and your changes will be included in the next version release.

      "},{"location":"contributing/translation/","title":"Translation","text":"

      This project supports multiple languages. If you would like to contribute a translation, please follow these steps:

      "},{"location":"contributing/translation/#add-new-translation","title":"Add new Translation","text":"

      Create a new file in src/i18n/locales. Name it <locale_code>.json (e.g. fr.json for French).

      Import and add the newly created file in src/i18n/i18n.config.ts.

      Add your language in the src/nuxt.config.ts file. You have to specify code, language and name.

      code is the name of the translation file without the extension (e.g. fr for fr.json).

      language is the BCP 47 language tag with region (e.g. fr-FR for French). See www.lingoes.net for a list of language codes.

      name is the display name of the language (e.g. Fran\u00e7ais for French).

      "},{"location":"contributing/translation/#update-existing-translation","title":"Update existing Translation","text":"

      If you need to update an existing translation, simply edit the corresponding <locale_code>.json file in src/i18n/locales.

      "},{"location":"contributing/translation/#contribute-changes","title":"Contribute changes","text":"

      See Pull Requests on how to contribute your translation.

      "},{"location":"examples/tutorials/adguard/","title":"AdGuard Home","text":"

      This tutorial is a follow-up to the official Traefik tutorial. It will guide you through integrating AdGuard Home with your existing wg-easy and Traefik setup to provide network-wide DNS ad-blocking.

      "},{"location":"examples/tutorials/adguard/#prerequisites","title":"Prerequisites","text":"
      • A working wg-easy and Traefik setup from the previous guides.

      Important: Following this guide will reset your WireGuard configuration.

      The process involves re-creating the wg-easy container and its data, which means all existing WireGuard clients and settings will be deleted.

      You will need to create your clients again after completing this guide.

      "},{"location":"examples/tutorials/adguard/#add-adguard-configuration","title":"Add adguard configuration","text":"
      1. Create a directory for the configuration files:

        sudo mkdir -p /etc/docker/containers/adguard\n
      2. Create volumes for persistent data:

        sudo mkdir -p /etc/docker/volumes/adguard/adguard_work\nsudo mkdir -p /etc/docker/volumes/adguard/adguard_conf\nsudo chmod -R 700 /etc/docker/volumes/adguard\n
      3. Create the docker-compose.yml file.

      File: /etc/docker/containers/adguard/docker-compose.yml

      services:\n    adguard:\n        image: adguard/adguardhome:v0.107.64\n        container_name: adguard\n        restart: unless-stopped\n        volumes:\n            - /etc/docker/volumes/adguard/adguard_work:/opt/adguardhome/work\n            - /etc/docker/volumes/adguard/adguard_conf:/opt/adguardhome/conf\n        networks:\n            wg:\n                interface_name: eth0\n                ipv4_address: 10.42.42.43\n                ipv6_address: fdcc:ad94:bacf:61a3::2b\n            traefik:\n                interface_name: eth1\n        labels:\n            - 'traefik.enable=true'\n            - 'traefik.http.routers.adguard.rule=Host(`adguard.$example.com$`)'\n            - 'traefik.http.routers.adguard.entrypoints=websecure'\n            - 'traefik.http.routers.adguard.service=adguard'\n            - 'traefik.http.services.adguard.loadbalancer.server.port=3000'\n            - 'traefik.docker.network=traefik'\n\nnetworks:\n    wg:\n        external: true\n    traefik:\n        external: true\n
      "},{"location":"examples/tutorials/adguard/#update-wg-easy-configuration","title":"Update wg-easy configuration","text":"

      Modify the corresponding sections of your existing wg-easy compose file to match the updated version below.

      File: /etc/docker/containers/wg-easy/docker-compose.yml

      services:\n  wg-easy:\n    ports:\n      - \"51820:51820/udp\"\n    ...\n    networks:\n      wg:\n        interface_name: eth0\n        ...\n      traefik:\n        interface_name: eth1\n      ...\n    ...\n    environment:\n      # Unattended Setup\n      - INIT_ENABLED=true\n      # Replace $username$ with your username\n      - INIT_USERNAME=$username$\n      # Replace $password$ with your unhashed password\n      - INIT_PASSWORD=$password$\n      # Replace $example.com$ with your domain\n      - INIT_HOST=wg-easy.$example.com$\n      - INIT_PORT=51820\n      - INIT_DNS=10.42.42.43,fdcc:ad94:bacf:61a3::2b\n      - INIT_IPV4_CIDR=10.8.0.0/24\n      - INIT_IPV6_CIDR=fd42:42:42::/64\n    ...\n\nnetworks:\n  wg:\n    # Prevents Docker Compose from prefixing the network name.\n    name: wg\n    ...\n  ...\n
      "},{"location":"examples/tutorials/adguard/#setup-wireguard","title":"Setup Wireguard","text":"
      1. Restart wg-easy:

        cd /etc/docker/containers/wg-easy\nsudo docker compose down -v\nsudo docker compose up -d\n
      2. Edit Wireguard's Hooks.

        In the Admin Panel of your WireGuard server, go to the Hooks tab and replace it with:

        PostUp

        iptables -A INPUT -p udp -m udp --dport {{port}} -j ACCEPT; ip6tables -A INPUT -p udp -m udp --dport {{port}} -j ACCEPT; iptables -t nat -A PREROUTING -i wg0 -p udp --dport 53 -j DNAT --to-destination 10.42.42.43; iptables -t nat -A PREROUTING -i wg0 -p tcp --dport 53 -j DNAT --to-destination 10.42.42.43; ip6tables -t nat -A PREROUTING -i wg0 -p udp --dport 53 -j DNAT --to-destination fdcc:ad94:bacf:61a3::2b; ip6tables -t nat -A PREROUTING -i wg0 -p tcp --dport 53 -j DNAT --to-destination fdcc:ad94:bacf:61a3::2b; iptables -A FORWARD -i wg0 -j ACCEPT; iptables -A FORWARD -o wg0 -j ACCEPT; ip6tables -A FORWARD -i wg0 -j ACCEPT; ip6tables -A FORWARD -o wg0 -j ACCEPT; iptables -t nat -A POSTROUTING -s {{ipv4Cidr}} -o {{device}} -j MASQUERADE; ip6tables -t nat -A POSTROUTING -s {{ipv6Cidr}} -o {{device}} -j MASQUERADE;\n

        PostDown

        iptables -D INPUT -p udp -m udp --dport {{port}} -j ACCEPT || true; ip6tables -D INPUT -p udp -m udp --dport {{port}} -j ACCEPT || true; iptables -t nat -D PREROUTING -i wg0 -p udp --dport 53 -j DNAT --to-destination 10.42.42.43 || true; iptables -t nat -D PREROUTING -i wg0 -p tcp --dport 53 -j DNAT --to-destination 10.42.42.43 || true; ip6tables -t nat -D PREROUTING -i wg0 -p udp --dport 53 -j DNAT --to-destination fdcc:ad94:bacf:61a3::2b || true; ip6tables -t nat -D PREROUTING -i wg0 -p tcp --dport 53 -j DNAT --to-destination fdcc:ad94:bacf:61a3::2b || true; iptables -D FORWARD -i wg0 -j ACCEPT || true; iptables -D FORWARD -o wg0 -j ACCEPT || true; ip6tables -D FORWARD -i wg0 -j ACCEPT || true; ip6tables -D FORWARD -o wg0 -j ACCEPT || true; iptables -t nat -D POSTROUTING -s {{ipv4Cidr}} -o {{device}} -j MASQUERADE || true; ip6tables -t nat -D POSTROUTING -s {{ipv6Cidr}} -o {{device}} -j MASQUERADE || true;\n
      3. Restart wg-easy to apply changes:

        sudo docker restart wg-easy\n
      "},{"location":"examples/tutorials/adguard/#setup-adguard-home","title":"Setup Adguard Home","text":"
      1. Start adguard service:

        cd /etc/docker/containers/adguard\nsudo docker compose up -d\n
      2. Navigate to https://adguard.$example.com$ to begin the AdGuard Home setup.

      Important: Configure AdGuard Home Admin Web Interface Port

      During the initial AdGuard Home setup on the Step 2/5 page, you must set the Admin Web Interface Port to 3000. Do not use the default port 80, as it will not work with the Traefik configuration.

      After completing the setup, the AdGuard UI might appear unresponsive. This is expected. Simply reload the page, and the panel will display correctly.

      If you accidentally left it default (80), you will need to manually edit the docker-compose.yml file for AdGuard Home (/etc/docker/containers/adguard/docker-compose.yml) and change the line traefik.http.services.adguard.loadbalancer.server.port=3000 to traefik.http.services.adguard.loadbalancer.server.port=80. After making this change, restart AdGuard Home by navigating to /etc/docker/containers/adguard and running sudo docker compose up -d.

      "},{"location":"examples/tutorials/adguard/#final-system-checks","title":"Final System Checks","text":""},{"location":"examples/tutorials/adguard/#firewall","title":"Firewall","text":"

      Ensure the ports 80/tcp, 443/tcp, 443/udp, and 51820/udp are open.

      "},{"location":"examples/tutorials/adguard/#optional-optimizing-udp-buffer-sizes","title":"Optional: Optimizing UDP Buffer Sizes","text":"

      AdGuard Home, as a DNS server, handles a large volume of UDP packets. To ensure optimal performance, it is recommended to increase the system's UDP buffer sizes. You can apply these settings using your system's sysctl configuration (e.g., by creating a file in /etc/sysctl.d/).

      net.core.rmem_max = 7500000\nnet.core.wmem_max = 7500000\n

      After adding these settings, remember to apply them (e.g., by running sudo sysctl --system or rebooting)

      "},{"location":"examples/tutorials/auto-updates/","title":"Auto Updates","text":""},{"location":"examples/tutorials/auto-updates/#docker-compose","title":"Docker Compose","text":"

      With Docker Compose wg-easy can be updated with a single command:

      cd /etc/docker/containers/wg-easy\nsudo docker compose up -d --pull always\n
      "},{"location":"examples/tutorials/auto-updates/#watchtower","title":"Watchtower","text":"

      If you want the updates to be fully automatic you can install Watchtower. This will check for updates every day at 4:00 AM and update the container if a new version is available.

      File: /etc/docker/containers/watchtower/docker-compose.yml

      services:\n    watchtower:\n        image: nickfedor/watchtower:latest\n        volumes:\n            - /var/run/docker.sock:/var/run/docker.sock\n        env_file:\n            - watchtower.env\n        restart: unless-stopped\n

      File: /etc/docker/containers/watchtower/watchtower.env

      WATCHTOWER_CLEANUP=true\nWATCHTOWER_SCHEDULE=0 0 4 * * *\nTZ=Europe/Berlin\n\n# Email\n# WATCHTOWER_NOTIFICATIONS_LEVEL=info\n# WATCHTOWER_NOTIFICATIONS=email\n# WATCHTOWER_NOTIFICATION_EMAIL_FROM=mail@example.com\n# WATCHTOWER_NOTIFICATION_EMAIL_TO=mail@example.com\n# WATCHTOWER_NOTIFICATION_EMAIL_SERVER=smtp.example.com\n# WATCHTOWER_NOTIFICATION_EMAIL_SERVER_USER=mail@example.com\n# WATCHTOWER_NOTIFICATION_EMAIL_SERVER_PASSWORD=\"SuperSecurePassword\"\n# WATCHTOWER_NOTIFICATION_EMAIL_SERVER_PORT=587\n
      cd /etc/docker/containers/watchtower\nsudo docker compose up -d\n
      "},{"location":"examples/tutorials/auto-updates/#docker-run","title":"Docker Run","text":"
      sudo docker stop wg-easy\nsudo docker rm wg-easy\nsudo docker pull ghcr.io/wg-easy/wg-easy\n

      And then run the docker run -d \\ ... command from Docker Run again.

      "},{"location":"examples/tutorials/auto-updates/#podman","title":"Podman","text":"

      To update wg-easy (and every container that has auto updates enabled), you can run the following command:

      sudo podman auto-update\n
      "},{"location":"examples/tutorials/basic-installation/","title":"Basic Installation","text":""},{"location":"examples/tutorials/basic-installation/#requirements","title":"Requirements","text":"
      1. You need to have a host that you can manage
      2. You need to have a domain name or a public IP address
      3. You need a supported architecture (x86_64, arm64)
      4. You need curl installed on your host
      "},{"location":"examples/tutorials/basic-installation/#install-docker","title":"Install Docker","text":"

      Follow the Docs here: https://docs.docker.com/engine/install/ and install Docker on your host.

      "},{"location":"examples/tutorials/basic-installation/#install-wg-easy","title":"Install wg-easy","text":"
      1. Create a directory for the configuration files (you can choose any directory you like):

        sudo mkdir -p /etc/docker/containers/wg-easy\n
      2. Download docker compose file

        sudo curl -o /etc/docker/containers/wg-easy/docker-compose.yml https://raw.githubusercontent.com/wg-easy/wg-easy/master/docker-compose.yml\n
      3. Start wg-easy

         cd /etc/docker/containers/wg-easy\n sudo docker compose up -d\n
      "},{"location":"examples/tutorials/basic-installation/#setup-firewall","title":"Setup Firewall","text":"

      If you are using a firewall, you need to open the following ports:

      • UDP 51820 (WireGuard)

      These ports can be changed, so if you change them you have to update your firewall rules accordingly.

      "},{"location":"examples/tutorials/basic-installation/#setup-reverse-proxy","title":"Setup Reverse Proxy","text":"
      • To setup traefik follow the instructions here: Traefik
      • To setup caddy follow the instructions here: Caddy
      • If you do not want to use a reverse proxy follow the instructions here: No Reverse Proxy
      "},{"location":"examples/tutorials/basic-installation/#update-wg-easy","title":"Update wg-easy","text":"

      To update wg-easy to the latest version, run:

      cd /etc/docker/containers/wg-easy\nsudo docker compose pull\nsudo docker compose up -d\n
      "},{"location":"examples/tutorials/basic-installation/#auto-update","title":"Auto Update","text":"

      If you want to enable auto-updates, follow the instructions here: Auto Updates

      "},{"location":"examples/tutorials/caddy/","title":"Caddy","text":"

      Opinionated

      This guide is opinionated. If you use other conventions or folder layouts, feel free to change the commands and paths.

      We're using Caddy here as reverse proxy to serve wg-easy on https://wg-easy.example.com via TLS.

      "},{"location":"examples/tutorials/caddy/#create-a-docker-composition-for-caddy","title":"Create a docker composition for caddy","text":"
      .\n\u251c\u2500\u2500 compose.yml\n\u2514\u2500\u2500 Caddyfile\n\n1 directory, 2 files\n
      # compose.yml\n\nservices:\n    caddy:\n        container_name: caddy\n        image: caddy:2.10.0-alpine\n        # publish everything you deem necessary\n        ports:\n            - '80:80/tcp'\n            - '443:443/tcp'\n            - '443:443/udp'\n        networks:\n            - caddy\n        restart: unless-stopped\n        volumes:\n            - './Caddyfile:/etc/caddy/Caddyfile:ro'\n            - config:/config\n            - data:/data\n\nnetworks:\n    caddy:\n        name: caddy\n\nvolumes:\n    config:\n    data:\n
      # Caddyfile\n\n{\n        # setup your email address\n        email mail@example.com\n}\n\nwg-easy.example.com {\n        # since the container will share the network with wg-easy\n        # we can use the proper container name\n        reverse_proxy wg-easy:80\n        tls internal\n}\n

      ...and start it with:

      sudo docker compose up -d\n
      "},{"location":"examples/tutorials/caddy/#adapt-the-docker-composition-of-wg-easy","title":"Adapt the docker composition of wg-easy","text":"
      services:\n  wg-easy:\n    # sync container name and port according to Caddyfile\n    container_name: wg-easy\n    environment:\n      - PORT=80\n    # no need to publish the HTTP server anymore\n    ports:\n      - \"51820:51820/udp\"\n    # add to caddy network\n    networks:\n      caddy:\n    ...\n\nnetworks:\n  caddy:\n    external: true\n  ...\n

      ...and restart it with:

      sudo docker compose up -d\n

      You can now access wg-easy at https://wg-easy.example.com and start the setup.

      "},{"location":"examples/tutorials/docker-run/","title":"Docker Run","text":"

      To setup the IPv6 Network, simply run once:

      docker network create \\\n  -d bridge --ipv6 \\\n  --subnet 10.42.42.0/24 \\\n  --subnet fdcc:ad94:bacf:61a3::/64 \\\n   wg\n

      To automatically install & run wg-easy, simply run:

      docker run -d \\\n  --net wg \\\n  -e INSECURE=true \\\n  --name wg-easy \\\n  --ip6 fdcc:ad94:bacf:61a3::2a \\\n  --ip 10.42.42.42 \\\n  -v ~/.wg-easy:/etc/wireguard \\\n  -v /lib/modules:/lib/modules:ro \\\n  -p 51820:51820/udp \\\n  -p 51821:51821/tcp \\\n  --cap-add NET_ADMIN \\\n  --cap-add SYS_MODULE \\\n  --sysctl net.ipv4.ip_forward=1 \\\n  --sysctl net.ipv4.conf.all.src_valid_mark=1 \\\n  --sysctl net.ipv6.conf.all.disable_ipv6=0 \\\n  --sysctl net.ipv6.conf.all.forwarding=1 \\\n  --sysctl net.ipv6.conf.default.forwarding=1 \\\n  --restart unless-stopped \\\n  ghcr.io/wg-easy/wg-easy:15\n

      The Web UI will now be available at http://0.0.0.0:51821.

      "},{"location":"examples/tutorials/dockerless/","title":"Without Docker","text":"

      This is currently not yet supported.

      "},{"location":"examples/tutorials/podman-nft/","title":"Podman + nftables","text":"

      This guide will show you how to run wg-easy with rootful Podman and nftables.

      "},{"location":"examples/tutorials/podman-nft/#requirements","title":"Requirements","text":"
      1. Podman installed with version 4.4 or higher
      "},{"location":"examples/tutorials/podman-nft/#configuration","title":"Configuration","text":"

      Create a Folder for the configuration files:

      sudo mkdir -p /etc/containers/systemd/wg-easy\nsudo mkdir -p /etc/containers/volumes/wg-easy\n

      Create a file /etc/containers/systemd/wg-easy/wg-easy.container with the following content:

      [Container]\nContainerName=wg-easy\nImage=ghcr.io/wg-easy/wg-easy:15\nAutoUpdate=registry\n\nVolume=/etc/containers/volumes/wg-easy:/etc/wireguard:Z\nNetwork=wg-easy.network\nPublishPort=51820:51820/udp\nPublishPort=51821:51821/tcp\n\n# this is used to allow access over HTTP\n# remove this when using a reverse proxy\nEnvironment=INSECURE=true\n\nAddCapability=NET_ADMIN\nAddCapability=SYS_MODULE\nAddCapability=NET_RAW\nSysctl=net.ipv4.ip_forward=1\nSysctl=net.ipv4.conf.all.src_valid_mark=1\nSysctl=net.ipv6.conf.all.disable_ipv6=0\nSysctl=net.ipv6.conf.all.forwarding=1\nSysctl=net.ipv6.conf.default.forwarding=1\n\n[Install]\n# this is used to start the container on boot\nWantedBy=default.target\n

      Create a file /etc/containers/systemd/wg-easy/wg-easy.network with the following content:

      [Network]\nNetworkName=wg-easy\nIPv6=true\n
      "},{"location":"examples/tutorials/podman-nft/#load-kernel-modules","title":"Load Kernel Modules","text":"

      You will need to load the following kernel modules

      wireguard\nnft_masq\n

      Create a file /etc/modules-load.d/wg-easy.conf with the following content:

      wireguard\nnft_masq\n
      "},{"location":"examples/tutorials/podman-nft/#start-the-container","title":"Start the Container","text":"
      sudo systemctl daemon-reload\nsudo systemctl start wg-easy\n
      "},{"location":"examples/tutorials/podman-nft/#edit-hooks","title":"Edit Hooks","text":"

      In the Admin Panel of your WireGuard server, go to the Hooks tab and add the following hook:

      1. PostUp

        nft add table inet wg_table; nft add chain inet wg_table prerouting { type nat hook prerouting priority 100 \\; }; nft add chain inet wg_table postrouting { type nat hook postrouting priority 100 \\; }; nft add rule inet wg_table postrouting ip saddr {{ipv4Cidr}} oifname {{device}} masquerade; nft add rule inet wg_table postrouting ip6 saddr {{ipv6Cidr}} oifname {{device}} masquerade; nft add chain inet wg_table input { type filter hook input priority 0 \\; policy accept \\; }; nft add rule inet wg_table input udp dport {{port}} accept; nft add rule inet wg_table input tcp dport {{uiPort}} accept; nft add chain inet wg_table forward { type filter hook forward priority 0 \\; policy accept \\; }; nft add rule inet wg_table forward iifname \"wg0\" accept; nft add rule inet wg_table forward oifname \"wg0\" accept;\n
      2. PostDown

        nft delete table inet wg_table\n

      If you don't have iptables loaded on your server, you could see many errors in the logs or in the UI. You can ignore them.

      "},{"location":"examples/tutorials/podman-nft/#restart-the-container","title":"Restart the Container","text":"

      Restart the container to apply the new hooks:

      sudo systemctl restart wg-easy\n
      "},{"location":"examples/tutorials/reverse-proxyless/","title":"No Reverse Proxy","text":"

      Insecure

      This is insecure. You should use a reverse proxy to secure the connection.

      Only use this method if you know what you are doing.

      If you only allow access to the web UI from your local network, you can skip the reverse proxy setup. This is not recommended, but it is possible.

      "},{"location":"examples/tutorials/reverse-proxyless/#setup","title":"Setup","text":"
      • Edit the docker-compose.yml file and uncomment environment and INSECURE

      • Set INSECURE to true to allow access to the web UI over a non-secure connection.

      • The docker-compose.yml file should look something like this:

        environment:\n    - INSECURE=true\n
      • Save the file and restart wg-easy.

      • Make sure that the Web UI is not accessible from outside your local network.

      "},{"location":"examples/tutorials/routed/","title":"Routed setup (No NAT)","text":"

      This guide shows how to run wg-easy with a routed setup, so packets are forwarded instead of NATed.

      In a routed design, each WireGuard client keeps its own IPv4/IPv6 address. That means you can identify clients by their real addresses instead of seeing everything as the WireGuard server\u2019s IP.

      "},{"location":"examples/tutorials/routed/#requirements","title":"Requirements","text":"
      1. You know how to add static routes on your router to the WireGuard server.
      "},{"location":"examples/tutorials/routed/#docker-setup","title":"Docker setup","text":"

      To make use of our own IPv4/IPv6 addresses, run the container with the network_mode: host option.

      services:\n    wg-easy:\n        image: ghcr.io/wg-easy/wg-easy:15\n        container_name: wg-easy\n        network_mode: 'host'\n        volumes:\n            - ./config:/etc/wireguard\n            - /lib/modules:/lib/modules:ro\n        cap_add:\n            - NET_ADMIN\n            - SYS_MODULE\n        devices:\n            - /dev/net/tun:/dev/net/tun\n        restart: unless-stopped\n

      Because we\u2019re on the host network, remove any ports: and container sysctls: you might have had before.

      "},{"location":"examples/tutorials/routed/#kernel-parameters-on-the-host","title":"Kernel parameters (on the host)","text":"

      With host networking, system sysctls must be set on the host. On your host, create /etc/sysctl.d/90-wireguard.conf:

      net.ipv4.ip_forward=1\nnet.ipv4.conf.all.src_valid_mark=1\nnet.ipv6.conf.all.disable_ipv6=0\nnet.ipv6.conf.all.forwarding=1\nnet.ipv6.conf.default.forwarding=1\n

      Apply and verify:

      sysctl -p /etc/sysctl.d/90-wireguard.conf\nsysctl -n net.ipv4.ip_forward   # should print 1\n
      "},{"location":"examples/tutorials/routed/#add-static-routes-on-your-router","title":"Add static routes on your router","text":"

      Pick an IPv4 and IPv6 subnet for your clients and add static routes on your router, pointing to the WireGuard server's LAN addresses.

      "},{"location":"examples/tutorials/routed/#example","title":"Example","text":"

      2001:db8::/32

      The documentation prefix 2001:db8::/32 (RFC 3849) used in this example is not meant for production use, replace it with your own ISP-assigned IPv6 prefix (GUA) or local prefix (ULA)

      I want my WireGuard clients in 192.168.0.0/24 and 2001:db8:abc:0::/64.

      • Routed IPv4 subnet: 192.168.0.0/24
      • Routed IPv6 prefix: 2001:db8:abc:0::/64
      • WireGuard server IPs: 192.168.10.118 and 2001:db8:abc:10:216:3eff:fedb:949e

      On your router:

      • Route 192.168.0.0/24 \u2192 next hop 192.168.10.118
      • Route 2001:db8:abc:0::/64 \u2192 next hop 2001:db8:abc:10:216:3eff:fedb:949e

      Don't forget to create the necessary firewall rules to allow these subnets to travel across your LAN. Some routers or servers may require specific Outbound NAT rules for the chosen IPv4 and IPv6 subnets to allow traffic to traverse your LAN.

      "},{"location":"examples/tutorials/routed/#wg-easy-configuration","title":"wg-easy configuration","text":"

      In the Web UI \u2192 Admin \u2192 Interface, click Change CIDR and set the IPv4/IPv6 routed subnets you chose above. Save.

      Then go to Admin \u2192 Hooks and add:

      PostUp

      iptables -A INPUT -p udp -m udp --dport {{port}} -j ACCEPT; iptables -A FORWARD -i wg0 -j ACCEPT; iptables -A FORWARD -o wg0 -j ACCEPT; ip6tables -A INPUT -p udp -m udp --dport {{port}} -j ACCEPT; ip6tables -A FORWARD -i wg0 -j ACCEPT; ip6tables -A FORWARD -o wg0 -j ACCEPT\n

      PostDown

      iptables -D INPUT -p udp -m udp --dport {{port}} -j ACCEPT; iptables -D FORWARD -i wg0 -j ACCEPT; iptables -D FORWARD -o wg0 -j ACCEPT; ip6tables -D INPUT -p udp -m udp --dport {{port}} -j ACCEPT; ip6tables -D FORWARD -i wg0 -j ACCEPT; ip6tables -D FORWARD -o wg0 -j ACCEPT\n

      Important: When using nftables use the following hooks instead.

      PostUp

      nft add chain ip filter WG_EASY; nft add rule ip filter DOCKER-USER jump WG_EASY; nft add rule ip filter WG_EASY iifname {{device}} accept; nft add rule ip filter WG_EASY oifname {{device}} accept; nft add chain ip6 filter WG_EASY; nft add rule ip6 filter DOCKER-USER jump WG_EASY; nft add rule ip6 filter WG_EASY iifname {{device}} accept; nft add rule ip6 filter WG_EASY oifname {{device}} accept;\n

      PostDown

      nft delete rule ip filter DOCKER-USER handle $(nft -a list chain ip filter DOCKER-USER | awk '/jump WG_EASY/ {print $NF}'); nft flush chain ip filter WG_EASY; nft delete chain ip filter WG_EASY; nft delete rule ip6 filter DOCKER-USER handle $(nft -a list chain ip6 filter DOCKER-USER | awk '/jump WG_EASY/ {print $NF}'); nft flush chain ip6 filter WG_EASY; nft delete chain ip6 filter WG_EASY\n
      "},{"location":"examples/tutorials/traefik/","title":"Traefik","text":"

      Opinionated

      This guide is opinionated. If you use other conventions or folder layouts, feel free to change the commands and paths.

      "},{"location":"examples/tutorials/traefik/#create-docker-compose-project","title":"Create docker compose project","text":"
      sudo mkdir -p /etc/docker/containers/traefik\ncd /etc/docker/containers/traefik\n
      "},{"location":"examples/tutorials/traefik/#create-docker-compose-file","title":"Create docker compose file","text":"

      File: /etc/docker/containers/traefik/docker-compose.yml

      services:\n    traefik:\n        image: traefik:3.3\n        container_name: traefik\n        restart: unless-stopped\n        ports:\n            - '80:80'\n            - '443:443/tcp'\n            - '443:443/udp'\n        volumes:\n            - /var/run/docker.sock:/var/run/docker.sock\n            - /etc/docker/volumes/traefik/traefik.yml:/traefik.yml:ro\n            - /etc/docker/volumes/traefik/traefik_dynamic.yml:/traefik_dynamic.yml:ro\n            - /etc/docker/volumes/traefik/acme.json:/acme.json\n        networks:\n            - traefik\n\nnetworks:\n    traefik:\n        external: true\n
      "},{"location":"examples/tutorials/traefik/#create-traefikyml","title":"Create traefik.yml","text":"

      File: /etc/docker/volumes/traefik/traefik.yml

      log:\n    level: INFO\n\nentryPoints:\n    web:\n        address: ':80/tcp'\n        http:\n            redirections:\n                entryPoint:\n                    to: websecure\n                    scheme: https\n    websecure:\n        address: ':443/tcp'\n        http:\n            middlewares:\n                - compress@file\n                - hsts@file\n            tls:\n                certResolver: letsencrypt\n        http3: {}\n\napi:\n    dashboard: true\n\ncertificatesResolvers:\n    letsencrypt:\n        acme:\n            email: $mail@example.com$\n            storage: acme.json\n            httpChallenge:\n                entryPoint: web\n\nproviders:\n    docker:\n        watch: true\n        network: traefik\n        exposedByDefault: false\n    file:\n        filename: traefik_dynamic.yml\n\nserversTransport:\n    insecureSkipVerify: true\n
      "},{"location":"examples/tutorials/traefik/#create-traefik_dynamicyml","title":"Create traefik_dynamic.yml","text":"

      File: /etc/docker/volumes/traefik/traefik_dynamic.yml

      http:\n    middlewares:\n        services:\n            basicAuth:\n                users:\n                    - '$username$:$password$'\n        compress:\n            compress: {}\n        hsts:\n            headers:\n                stsSeconds: 2592000\n    routers:\n        api:\n            rule: Host(`traefik.$example.com$`)\n            entrypoints:\n                - websecure\n            middlewares:\n                - services\n            service: api@internal\n\ntls:\n    options:\n        default:\n            cipherSuites:\n                - TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256\n                - TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384\n                - TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256\n            sniStrict: true\n
      "},{"location":"examples/tutorials/traefik/#create-acmejson","title":"Create acme.json","text":"
      sudo touch /etc/docker/volumes/traefik/acme.json\nsudo chmod 600 /etc/docker/volumes/traefik/acme.json\n
      "},{"location":"examples/tutorials/traefik/#create-network","title":"Create network","text":"
      sudo docker network create traefik\n
      "},{"location":"examples/tutorials/traefik/#start-traefik","title":"Start traefik","text":"
      sudo docker compose up -d\n

      You can now access the Traefik dashboard at https://traefik.$example.com$ with the credentials you set in traefik_dynamic.yml.

      "},{"location":"examples/tutorials/traefik/#add-labels-to-wg-easy","title":"Add Labels to wg-easy","text":"

      To add labels to your wg-easy service, you can add the following to your docker-compose.yml file:

      File: /etc/docker/containers/wg-easy/docker-compose.yml

      services:\n  wg-easy:\n    ...\n    container_name: wg-easy\n    networks:\n      ...\n      traefik: {}\n    labels:\n      - \"traefik.enable=true\"\n      - \"traefik.http.routers.wg-easy.rule=Host(`wg-easy.$example.com$`)\"\n      - \"traefik.http.routers.wg-easy.entrypoints=websecure\"\n      - \"traefik.http.routers.wg-easy.service=wg-easy\"\n      - \"traefik.http.services.wg-easy.loadbalancer.server.port=51821\"\n      - \"traefik.docker.network=traefik\"\n    ...\n\nnetworks:\n  ...\n  traefik:\n    external: true\n
      "},{"location":"examples/tutorials/traefik/#restart-wg-easy","title":"Restart wg-easy","text":"
      cd /etc/docker/containers/wg-easy\nsudo docker compose up -d\n

      You can now access wg-easy at https://wg-easy.$example.com$ and start the setup.

      "},{"location":"guides/2fa/","title":"2FA","text":"

      The user can enable 2FA from the Account page. The Account page is accessible from the dropdown menu in the top right corner of the application.

      "},{"location":"guides/2fa/#enable-totp","title":"Enable TOTP","text":"
      • Enable Two Factor Authentication: Enable TOTP for the user.
      "},{"location":"guides/2fa/#configure-totp","title":"Configure TOTP","text":"

      A QR code will be displayed. Scan the QR code with your TOTP application (e.g., Google Authenticator, Authy, etc.) to add the account.

      To verify that the TOTP key is working, the user must enter the TOTP code generated by the TOTP application.

      • TOTP Key: The TOTP key for the user. This key is used to generate the TOTP code.
      • TOTP Code: The current TOTP code for the user. This code is used to verify the TOTP key.
      • Enable Two Factor Authentication: Enable TOTP for the user.
      "},{"location":"guides/2fa/#disable-totp","title":"Disable TOTP","text":"

      To disable TOTP, the user must enter the current password.

      • Current Password: The current password of the user.
      • Disable Two Factor Authentication: Disable TOTP for the user.
      "},{"location":"guides/admin/","title":"Admin Panel","text":""},{"location":"guides/admin/#interface-settings","title":"Interface Settings","text":""},{"location":"guides/admin/#per-client-firewall","title":"Per-Client Firewall","text":"

      Enable server-side firewall filtering to enforce network access restrictions per client.

      When enabled, each client can have custom \"Firewall Allowed IPs\" configured that restrict which destinations they can access through the VPN. These restrictions are enforced by the server using iptables/ip6tables and cannot be bypassed by the client.

      Experimental Feature

      This feature is currently experimental. While functional, it should be thoroughly tested in your environment before relying on it for production security requirements. Always verify that firewall rules are working as expected using test traffic or by manually inspecting the rules.

      Requirements:

      • iptables must be installed on the host system
      • ip6tables must be installed if IPv6 is enabled (default)
      • The feature cannot be enabled if these tools are not available

      Note

      Most Linux distributions include iptables by default. If you're running in a minimal container environment, you may need to install the iptables package on the host system.

      Enable this feature if you want to:

      • Restrict certain clients to only access specific servers or networks
      • Prevent clients from accessing the internet while allowing LAN access
      • Enforce port-based restrictions (e.g., only allow HTTP/HTTPS)
      • Separate routing configuration from security enforcement

      How it works:

      1. Enable \"Per-Client Firewall\" in Admin Panel \u2192 Interface
      2. Edit any client to see the new \"Firewall Allowed IPs\" field
      3. Specify allowed destinations (IPs, subnets, ports) for that client
      4. Server enforces these rules automatically

      See Edit Client \u2192 Firewall Allowed IPs for detailed configuration syntax and examples.

      "},{"location":"guides/cli/","title":"CLI","text":"

      If you want to use the CLI, you can run it with

      "},{"location":"guides/cli/#docker-compose","title":"Docker Compose","text":"
      cd /etc/docker/containers/wg-easy\ndocker compose exec -it wg-easy cli\n
      "},{"location":"guides/cli/#docker-run","title":"Docker Run","text":"
      docker run --rm -it \\\n    -v ~/.wg-easy:/etc/wireguard \\\n    ghcr.io/wg-easy/wg-easy:15 \\\n    cli\n
      "},{"location":"guides/cli/#reset-password","title":"Reset Password","text":"

      If you want to reset the password for the admin user, you can run the following command:

      "},{"location":"guides/cli/#by-prompt","title":"By Prompt","text":"
      cd /etc/docker/containers/wg-easy\ndocker compose exec -it wg-easy cli db:admin:reset\n

      You are asked to provide the new password

      "},{"location":"guides/cli/#by-argument","title":"By Argument","text":"
      cd /etc/docker/containers/wg-easy\ndocker compose exec -it wg-easy cli db:admin:reset --password <new_password>\n

      This will reset the password for the admin user to the new password you provided. If you include special characters in the password, make sure to escape them properly.

      "},{"location":"guides/cli/#show-clients","title":"Show Clients","text":"

      List all clients that are currently configured with details such as client ID, Name, Public Key, and enabled status.

      cli clients:list\n
      "},{"location":"guides/cli/#show-client-qr-code","title":"Show Client QR Code","text":"

      Display the QR code for a specific client, which can be scanned by a compatible app to import the client's configuration.

      cli clients:qr <client_id>\n

      Replace <client_id> with the actual client ID you want to show the QR code for.

      IPv6 Support

      IPv6 support is enabled by default, even if you disabled it using environment variables. To disable it pass the --no-ipv6 flag when running the CLI.

      cli clients:qr <client_id> --no-ipv6\n
      "},{"location":"guides/clients/","title":"Edit Client","text":""},{"location":"guides/clients/#general","title":"General","text":"
      • Name: The name of the client.
      • Enabled: Whether the client can connect to the VPN.
      • Expire Date: The date the client will be disabled.
      "},{"location":"guides/clients/#address","title":"Address","text":"
      • IPv4: The IPv4 address of the client.
      • IPv6: The IPv6 address of the client.
      "},{"location":"guides/clients/#allowed-ips","title":"Allowed IPs","text":"

      Which IPs will be routed through the VPN.

      This will not prevent the user from modifying it locally and accessing IP ranges that they should not be able to access.

      Use the Firewall Allowed IPs feature to prevent access to IP ranges that the user should not be able to access.

      "},{"location":"guides/clients/#firewall-allowed-ips","title":"Firewall Allowed IPs","text":"

      Attention

      This field only appears when Per-Client Firewall is enabled in the Admin Panel \u2192 Interface settings.

      Server-side firewall rules that restrict which destinations the client can access, regardless of their local configuration.

      Unlike \"Allowed IPs\" which only controls routing on the client side, these rules are enforced by the server using iptables/ip6tables and cannot be bypassed by the client.

      Supported Formats:

      • 10.10.0.3, 2001:db8::1 - Allow access to a single IP address
      • 10.10.0.0/24, 2001:db8::/32 - Allow access to an entire subnet
      • 192.168.1.5:443 - Allow access to specific port (TCP+UDP)
      • 192.168.1.5:443/tcp - Allow access to specific port (TCP only)
      • 192.168.1.5:443/udp - Allow access to specific port (UDP only)
      • 10.10.0.0/24:443 - Allow access to an entire subnet on a specific port (TCP+UDP)
      • 10.10.0.0/24:443/tcp - Allow access to an entire subnet on a specific port (TCP only)
      • 10.10.0.0/24:443/udp - Allow access to an entire subnet on a specific port (UDP only)
      • [2001:db8::1]:443 - IPv6 address with port (brackets required)
      • [2001:db8::/32]:443/tcp - IPv6 CIDR with port and protocol

      Invalid Formats

      Protocol specifiers (/tcp or /udp) require a port number. The following formats are not supported and will result in an error:

      • 10.10.0.3/tcp (use 10.10.0.3:443/tcp instead)
      • 10.10.0.0/24/udp (use 10.10.0.0/24:53/udp instead)

      Behavior:

      • Empty: Falls back to the client's \"Allowed IPs\" setting
      • Specified: Only listed destinations are accessible (allow-only, everything else is blocked)
      • Disable for specific client: To disable firewall filtering for a single client while keeping it enabled for others, add 0.0.0.0/0, ::/0 to allow all traffic

      Note

      To allow clients to reach the VPN server itself (e.g. for DNS), include the server's VPN address in the firewall allowed IPs.

      Use Case Examples:

      • Allow only specific servers: 10.10.0.5
      • Allow only internal network: 10.10.0.0/24, 192.168.1.0/24
      • Allow only web browsing: 0.0.0.0/0:80, 0.0.0.0/0:443, [::/0]:80, [::/0]:443
      • Block internet, allow LAN: Leave \"Allowed IPs\" as 0.0.0.0/0, ::/0 but set Firewall IPs to 10.0.0.0/8, 192.168.0.0/16
      "},{"location":"guides/clients/#server-allowed-ips","title":"Server Allowed IPs","text":"

      Which IPs will be routed to the client.

      "},{"location":"guides/clients/#dns","title":"DNS","text":"

      The DNS server that the client will use.

      "},{"location":"guides/clients/#advanced","title":"Advanced","text":"
      • MTU: The maximum transmission unit for the client.
      • Persistent Keepalive: The interval for sending keepalive packets to the server.
      "},{"location":"guides/clients/#hooks","title":"Hooks","text":"

      This can only be used for clients that use wg-quick. Setting this will throw a error when importing the config on other clients.

      • PreUp: Commands to run before the interface is brought up.
      • PostUp: Commands to run after the interface is brought up.
      • PreDown: Commands to run before the interface is brought down.
      • PostDown: Commands to run after the interface is brought down.
      "},{"location":"guides/clients/#actions","title":"Actions","text":"
      • Save: Save the changes made in the form.
      • Revert: Revert the changes made in the form.
      • Delete: Delete the client.
      "},{"location":"guides/setup/","title":"Setup","text":""},{"location":"guides/setup/#user-setup","title":"User Setup","text":"
      • Username: The username of the user.
      • Password: The password of the user.
      • Confirm Password: The password of the user.
      "},{"location":"guides/setup/#existing-setup","title":"Existing Setup","text":"

      If you have the config from the previous version, you can import it by clicking \"Yes\". This currently expects a config from v14.

      If this is the first time you are using this, you can click \"No\" to create a new config.

      "},{"location":"guides/setup/#no-host-setup","title":"No - Host Setup","text":"
      • Host: The host of the server. The clients will connect to this address. This can be a domain name or an IP address. Make sure to wrap it in brackets if it is an IPv6 address. For example: [::1] or [2001:db8::1].
      • Port: The port of the server. The clients will connect to this port. The server will listen on this port.
      "},{"location":"guides/setup/#yes-migration","title":"Yes - Migration","text":"

      Select the wg0.json file from the previous version. Read Migrate from v14 to v15 for more information.

      "}]} \ No newline at end of file diff --git a/edge/sitemap.xml b/edge/sitemap.xml index dd524c26..d2b4081c 100644 --- a/edge/sitemap.xml +++ b/edge/sitemap.xml @@ -2,118 +2,122 @@ https://wg-easy.github.io/wg-easy/edge/ - 2026-06-03 + 2026-06-12 https://wg-easy.github.io/wg-easy/edge/faq/ - 2026-06-03 + 2026-06-12 https://wg-easy.github.io/wg-easy/edge/getting-started/ - 2026-06-03 + 2026-06-12 https://wg-easy.github.io/wg-easy/edge/advanced/api/ - 2026-06-03 + 2026-06-12 https://wg-easy.github.io/wg-easy/edge/advanced/config/amnezia/ - 2026-06-03 + 2026-06-12 https://wg-easy.github.io/wg-easy/edge/advanced/config/experimental-config/ - 2026-06-03 + 2026-06-12 + + + https://wg-easy.github.io/wg-easy/edge/advanced/config/external-authentication/ + 2026-06-12 https://wg-easy.github.io/wg-easy/edge/advanced/config/optional-config/ - 2026-06-03 + 2026-06-12 https://wg-easy.github.io/wg-easy/edge/advanced/config/unattended-setup/ - 2026-06-03 + 2026-06-12 https://wg-easy.github.io/wg-easy/edge/advanced/metrics/prometheus/ - 2026-06-03 + 2026-06-12 https://wg-easy.github.io/wg-easy/edge/advanced/migrate/ - 2026-06-03 + 2026-06-12 https://wg-easy.github.io/wg-easy/edge/advanced/migrate/from-14-to-15/ - 2026-06-03 + 2026-06-12 https://wg-easy.github.io/wg-easy/edge/contributing/general/ - 2026-06-03 + 2026-06-12 https://wg-easy.github.io/wg-easy/edge/contributing/issues-and-pull-requests/ - 2026-06-03 + 2026-06-12 https://wg-easy.github.io/wg-easy/edge/contributing/translation/ - 2026-06-03 + 2026-06-12 https://wg-easy.github.io/wg-easy/edge/examples/tutorials/adguard/ - 2026-06-03 + 2026-06-12 https://wg-easy.github.io/wg-easy/edge/examples/tutorials/auto-updates/ - 2026-06-03 + 2026-06-12 https://wg-easy.github.io/wg-easy/edge/examples/tutorials/basic-installation/ - 2026-06-03 + 2026-06-12 https://wg-easy.github.io/wg-easy/edge/examples/tutorials/caddy/ - 2026-06-03 + 2026-06-12 https://wg-easy.github.io/wg-easy/edge/examples/tutorials/docker-run/ - 2026-06-03 + 2026-06-12 https://wg-easy.github.io/wg-easy/edge/examples/tutorials/dockerless/ - 2026-06-03 + 2026-06-12 https://wg-easy.github.io/wg-easy/edge/examples/tutorials/podman-nft/ - 2026-06-03 + 2026-06-12 https://wg-easy.github.io/wg-easy/edge/examples/tutorials/reverse-proxyless/ - 2026-06-03 + 2026-06-12 https://wg-easy.github.io/wg-easy/edge/examples/tutorials/routed/ - 2026-06-03 + 2026-06-12 https://wg-easy.github.io/wg-easy/edge/examples/tutorials/traefik/ - 2026-06-03 + 2026-06-12 https://wg-easy.github.io/wg-easy/edge/guides/2fa/ - 2026-06-03 + 2026-06-12 https://wg-easy.github.io/wg-easy/edge/guides/admin/ - 2026-06-03 + 2026-06-12 https://wg-easy.github.io/wg-easy/edge/guides/cli/ - 2026-06-03 + 2026-06-12 https://wg-easy.github.io/wg-easy/edge/guides/clients/ - 2026-06-03 + 2026-06-12 https://wg-easy.github.io/wg-easy/edge/guides/setup/ - 2026-06-03 + 2026-06-12 \ No newline at end of file diff --git a/edge/sitemap.xml.gz b/edge/sitemap.xml.gz index 47a2b865840ef3ee859ab723f6a9859530e974ae..02bcd9c7551f9590f4f738aefa5976524531f211 100644 GIT binary patch literal 479 zcmV<50U-V#iwFn+Oe<;v|8r?{Wo=<_E_iKh0L_=dZrd;nK=1vEz;{_muoZ2QxVL@5 z_5qAU+d^eY6{#lK*N<+qVu#&&kaV#`(BuP2)8gUzviQv#W^yqcyIsHSHW*B_E=dIWC#}!tb5D+Ofkb`GlO{9Ef%HwNxBX)`+-+p720nK0 z>)@jqRx7lcnG^zD`^oWKPQ8nNB^vF7`k+_kf8Pb-CE8)X-QTP2z1r@X9Z|KLTv=Fiwm_OjCTzn$XG(P VF}`@oe}9OL;ScPf+pAC!0069}iwFpSZXap_|8r?{Wo=<_E_iKh0L_=pZrd;nfbV^Zz;{_m_JeIm+}oaD zdjLkF9ijTGNHxixesr4+JM7kjBp9$n$m9o+rpUwNHTca77IG;bhuydxHW*E^E-uI6 z=a28|%kVfo9BT1|yphe3j>DNbf7AMMxs2o(17sRgSu~lmEJ)vldOLm{riYD;Rl(cr zbsT&$)2fA5HH$)^+qgJB*VE|IABjf0pg!nz^S|E$aY%OBZ}*?oR-O;CPJVN03i z$L^1-MqtKh(JIL+HMa)~$i*4bxg?pMu~G+2ZYjXn56CQsqjnz^C$;<33`=52e08d8 zij-6tv4HOb#t~H{1<|Tpy;lXFWzEzF9T$k?Va*85fba?-dnC=3Q*q!)qHL*9Y*$D! zQ#D(OdFqMl1muizQR)KUpCb)ed+Q0vCi8?vl^Xk_5?NQFm6Ht+Rh+pes)B28f=X`X z>#esVE2*|D&i_Ls86Ic%)UI!-&I-_ehSmsRgNv;pjCUO%NMk~+G2U6^zrIAr^c(DN JoFuRi000PQ-|hea