[GH-ISSUE #5791] After migration from legacy zitadel IdP to Dex, login via Zitadel reports "Errors.App.NotFound" #11999

Open
opened 2026-08-05 01:32:03 -04:00 by saavagebueno · 0 comments
Owner

Originally created by @undes1red on GitHub (Apr 2, 2026).
Original GitHub issue: https://github.com/netbirdio/netbird/issues/5791

Describe the problem

I followed the migration tutorial here. The migration succeeded as https://<your-domain>/oauth2/.well-known/openid-configuration returns a valid json and my private networks are functioning. However, I can not log into the dashboard. The Continue with Email does not accept my credentials, and Continue with Zitadel shows Errors.App.NotFound. I affirm the AUTH_AUTHORITY is set to https://<your-domain>/oauth2 and I restarted the dashboard container, so I don't know why dashboard still redirects to oauth/v2.

To Reproduce

Steps to reproduce the behavior:

  1. Migrate a legacy Zitadel deployment to Dex following this tutorial.
  2. Open the dashboard.
  3. Click Continue with Zitadel
  4. See the error.

Expected behavior

I should see the zitadel login page and I can log in using my old credentials.

Are you using NetBird Cloud?

Self-hosted

NetBird version

0.67.2

Is any other VPN software installed?

No.

Debug output

docker-compose.yaml

services:
  # Caddy reverse proxy
  caddy:
    image: caddy
    restart: unless-stopped
    networks: [ netbird ]
    ports:
      - '<my_ip>:443'
      - '<my_ip>:443/udp'
      - '<my_ip>:80'
      - '<my_ip>:8080'
    volumes:
      - netbird_caddy_data:/data
      - ./Caddyfile:/etc/caddy/Caddyfile
    logging:
      driver: "json-file"
      options:
        max-size: "500m"
        max-file: "2"

  # UI dashboard
  dashboard:
    image: netbirdio/dashboard:latest
    restart: unless-stopped
    networks: [netbird]
    env_file:
      - ./dashboard.env
    logging:
      driver: "json-file"
      options:
        max-size: "500m"
        max-file: "2"

  # Signal
  signal:
    image: netbirdio/signal:latest
    restart: unless-stopped
    networks: [netbird]
    logging:
      driver: "json-file"
      options:
        max-size: "500m"
        max-file: "2"

  # Relay
  relay:
    image: netbirdio/relay:latest
    restart: unless-stopped
    networks: [netbird]
    ports:
      - '3478:3478/udp'  # STUN UDP port (add more lines if using multiple ports)
    env_file:
      - ./relay.env
    logging:
      driver: "json-file"
      options:
        max-size: "500m"
        max-file: "2"

  # Management
  management:
    image: netbirdio/management:latest
    restart: unless-stopped
    networks: [netbird]
    volumes:
      - netbird_management:/var/lib/netbird
      - ./management.json:/etc/netbird/management.json
    command: [
      "--port", "80",
      "--log-file", "console",
      "--log-level", "info",
      "--disable-anonymous-metrics=false",
      "--single-account-mode-domain=netbird.selfhosted",
      "--dns-domain=netbird.selfhosted",
      "--idp-sign-key-refresh-enabled",
    ]
    logging:
      driver: "json-file"
      options:
        max-size: "500m"
        max-file: "2"

  # Zitadel - identity provider
  zitadel:
    restart: 'always'
    networks: [netbird]
    image: 'ghcr.io/zitadel/zitadel:v2.64.1'
    command: 'start-from-init --masterkeyFromEnv --tlsMode external'
    env_file:
      - ./zitadel.env
    depends_on:
      zdb:
        condition: 'service_healthy'
    volumes:
      - ./machinekey:/machinekey
      - netbird_zitadel_certs:/zdb-certs:ro
    logging:
      driver: "json-file"
      options:
        max-size: "500m"
        max-file: "2"

  # Postgres for Zitadel
  zdb:
    restart: 'always'
    networks: [netbird]
    image: 'postgres:16-alpine'
    env_file:
      - ./zdb.env
    volumes:
      - netbird_zdb_data:/var/lib/postgresql/data:rw
    healthcheck:
      test: ["CMD-SHELL", "pg_isready", "-d", "db_prod"]
      interval: 5s
      timeout: 60s
      retries: 10
      start_period: 5s
    logging:
      driver: "json-file"
      options:
        max-size: "500m"
        max-file: "2"

volumes:
  netbird_zdb_data:
    external: true
  netbird_management:
    external: true
  netbird_caddy_data:
    external: true
  netbird_zitadel_certs:
    external: true

networks:
  netbird:

dashboard.env:

# Endpoints
NETBIRD_MGMT_API_ENDPOINT=https://<my-domain>
NETBIRD_MGMT_GRPC_API_ENDPOINT=https://<my-domain>
# OIDC
USE_AUTH0=false
AUTH_AUDIENCE=netbird-dashboard
AUTH_CLIENT_ID=netbird-dashboard
AUTH_AUTHORITY=https://<my-domain>/oauth2
AUTH_SUPPORTED_SCOPES=openid profile email groups
AUTH_REDIRECT_URI=/nb-auth
AUTH_SILENT_REDIRECT_URI=/nb-silent-auth
# SSL
NGINX_SSL_PORT=443
# Letsencrypt
LETSENCRYPT_DOMAIN=none

Caddyfile:

{
  debug
        servers :80,:443 {
    protocols h1 h2c h2 h3
  }
}

(security_headers) {
    header * {
        # enable HSTS
        # https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Headers_Cheat_Sheet.html#strict-transport-security-hsts
        # NOTE: Read carefully how this header works before using it.
        # If the HSTS header is misconfigured or if there is a problem with
        # the SSL/TLS certificate being used, legitimate users might be unable
        # to access the website. For example, if the HSTS header is set to a
        # very long duration and the SSL/TLS certificate expires or is revoked,
        # legitimate users might be unable to access the website until
        # the HSTS header duration has expired.
        # The recommended value for the max-age is 2 year (63072000 seconds).
        # But we are using 1 hour (3600 seconds) for testing purposes
        # and ensure that the website is working properly before setting
        # to two years.

        Strict-Transport-Security "max-age=3600; includeSubDomains; preload"

        # disable clients from sniffing the media type
        # https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Headers_Cheat_Sheet.html#x-content-type-options
        X-Content-Type-Options "nosniff"

        # clickjacking protection
        # https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Headers_Cheat_Sheet.html#x-frame-options
        X-Frame-Options "SAMEORIGIN"

        # xss protection
        # https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Headers_Cheat_Sheet.html#x-xss-protection
        X-XSS-Protection "1; mode=block"

        # Remove -Server header, which is an information leak
        # Remove Caddy from Headers
        -Server

        # keep referrer data off of HTTP connections
        # https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Headers_Cheat_Sheet.html#referrer-policy
        Referrer-Policy strict-origin-when-cross-origin
    }
}

:80, tunnel.qkv.link:443 {
    # handle /.well-known/microsoft-identity-association.json {
    #     root * /hostfile
    #     file_server
    # }
    import security_headers
    # relay
    reverse_proxy /relay* relay:80
    # Signal
    reverse_proxy /signalexchange.SignalExchange/* h2c://signal:10000

    # Management
    reverse_proxy /api/* management:80
    reverse_proxy /management.ManagementService/* h2c://management:80
    reverse_proxy /oauth2/* management:80
    # Zitadel
    reverse_proxy /zitadel.admin.v1.AdminService/* h2c://zitadel:8080
    reverse_proxy /admin/v1/* h2c://zitadel:8080
    reverse_proxy /zitadel.auth.v1.AuthService/* h2c://zitadel:8080
    reverse_proxy /auth/v1/* h2c://zitadel:8080
    reverse_proxy /zitadel.management.v1.ManagementService/* h2c://zitadel:8080
    reverse_proxy /management/v1/* h2c://zitadel:8080
    reverse_proxy /zitadel.system.v1.SystemService/* h2c://zitadel:8080
    reverse_proxy /system/v1/* h2c://zitadel:8080
    reverse_proxy /assets/v1/* h2c://zitadel:8080
    reverse_proxy /ui/* h2c://zitadel:8080
    reverse_proxy /oidc/v1/* h2c://zitadel:8080
    reverse_proxy /saml/v2/* h2c://zitadel:8080
    reverse_proxy /oauth/v2/* h2c://zitadel:8080
    reverse_proxy /.well-known/openid-configuration h2c://zitadel:8080
    reverse_proxy /openapi/* h2c://zitadel:8080
    reverse_proxy /debug/* h2c://zitadel:8080
    reverse_proxy /device/* h2c://zitadel:8080
    reverse_proxy /device h2c://zitadel:8080
    # Dashboard
    reverse_proxy /* dashboard:80
}

Screenshots

Image

Have you tried these troubleshooting steps?

  • Reviewed client troubleshooting (if applicable)
  • Checked for newer NetBird versions
  • Searched for similar issues on GitHub (including closed ones)
  • Restarted the NetBird client
  • Disabled other VPN software
  • Checked firewall settings
Originally created by @undes1red on GitHub (Apr 2, 2026). Original GitHub issue: https://github.com/netbirdio/netbird/issues/5791 **Describe the problem** I followed the migration tutorial [here](https://docs.netbird.io/selfhosted/migration/external-to-embedded-idp). The migration succeeded as `https://<your-domain>/oauth2/.well-known/openid-configuration` returns a valid json and my private networks are functioning. However, I can not log into the dashboard. The `Continue with Email` does not accept my credentials, and `Continue with Zitadel` shows `Errors.App.NotFound`. I affirm the `AUTH_AUTHORITY` is set to `https://<your-domain>/oauth2` and I restarted the dashboard container, so I don't know why dashboard still redirects to `oauth/v2`. **To Reproduce** Steps to reproduce the behavior: 1. Migrate a legacy Zitadel deployment to Dex following this [tutorial]((https://docs.netbird.io/selfhosted/migration/external-to-embedded-idp)). 2. Open the dashboard. 3. Click `Continue with Zitadel` 4. See the error. **Expected behavior** I should see the zitadel login page and I can log in using my old credentials. **Are you using NetBird Cloud?** Self-hosted **NetBird version** 0.67.2 **Is any other VPN software installed?** No. **Debug output** docker-compose.yaml ``` services: # Caddy reverse proxy caddy: image: caddy restart: unless-stopped networks: [ netbird ] ports: - '<my_ip>:443' - '<my_ip>:443/udp' - '<my_ip>:80' - '<my_ip>:8080' volumes: - netbird_caddy_data:/data - ./Caddyfile:/etc/caddy/Caddyfile logging: driver: "json-file" options: max-size: "500m" max-file: "2" # UI dashboard dashboard: image: netbirdio/dashboard:latest restart: unless-stopped networks: [netbird] env_file: - ./dashboard.env logging: driver: "json-file" options: max-size: "500m" max-file: "2" # Signal signal: image: netbirdio/signal:latest restart: unless-stopped networks: [netbird] logging: driver: "json-file" options: max-size: "500m" max-file: "2" # Relay relay: image: netbirdio/relay:latest restart: unless-stopped networks: [netbird] ports: - '3478:3478/udp' # STUN UDP port (add more lines if using multiple ports) env_file: - ./relay.env logging: driver: "json-file" options: max-size: "500m" max-file: "2" # Management management: image: netbirdio/management:latest restart: unless-stopped networks: [netbird] volumes: - netbird_management:/var/lib/netbird - ./management.json:/etc/netbird/management.json command: [ "--port", "80", "--log-file", "console", "--log-level", "info", "--disable-anonymous-metrics=false", "--single-account-mode-domain=netbird.selfhosted", "--dns-domain=netbird.selfhosted", "--idp-sign-key-refresh-enabled", ] logging: driver: "json-file" options: max-size: "500m" max-file: "2" # Zitadel - identity provider zitadel: restart: 'always' networks: [netbird] image: 'ghcr.io/zitadel/zitadel:v2.64.1' command: 'start-from-init --masterkeyFromEnv --tlsMode external' env_file: - ./zitadel.env depends_on: zdb: condition: 'service_healthy' volumes: - ./machinekey:/machinekey - netbird_zitadel_certs:/zdb-certs:ro logging: driver: "json-file" options: max-size: "500m" max-file: "2" # Postgres for Zitadel zdb: restart: 'always' networks: [netbird] image: 'postgres:16-alpine' env_file: - ./zdb.env volumes: - netbird_zdb_data:/var/lib/postgresql/data:rw healthcheck: test: ["CMD-SHELL", "pg_isready", "-d", "db_prod"] interval: 5s timeout: 60s retries: 10 start_period: 5s logging: driver: "json-file" options: max-size: "500m" max-file: "2" volumes: netbird_zdb_data: external: true netbird_management: external: true netbird_caddy_data: external: true netbird_zitadel_certs: external: true networks: netbird: ``` dashboard.env: ``` # Endpoints NETBIRD_MGMT_API_ENDPOINT=https://<my-domain> NETBIRD_MGMT_GRPC_API_ENDPOINT=https://<my-domain> # OIDC USE_AUTH0=false AUTH_AUDIENCE=netbird-dashboard AUTH_CLIENT_ID=netbird-dashboard AUTH_AUTHORITY=https://<my-domain>/oauth2 AUTH_SUPPORTED_SCOPES=openid profile email groups AUTH_REDIRECT_URI=/nb-auth AUTH_SILENT_REDIRECT_URI=/nb-silent-auth # SSL NGINX_SSL_PORT=443 # Letsencrypt LETSENCRYPT_DOMAIN=none ``` Caddyfile: ``` { debug servers :80,:443 { protocols h1 h2c h2 h3 } } (security_headers) { header * { # enable HSTS # https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Headers_Cheat_Sheet.html#strict-transport-security-hsts # NOTE: Read carefully how this header works before using it. # If the HSTS header is misconfigured or if there is a problem with # the SSL/TLS certificate being used, legitimate users might be unable # to access the website. For example, if the HSTS header is set to a # very long duration and the SSL/TLS certificate expires or is revoked, # legitimate users might be unable to access the website until # the HSTS header duration has expired. # The recommended value for the max-age is 2 year (63072000 seconds). # But we are using 1 hour (3600 seconds) for testing purposes # and ensure that the website is working properly before setting # to two years. Strict-Transport-Security "max-age=3600; includeSubDomains; preload" # disable clients from sniffing the media type # https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Headers_Cheat_Sheet.html#x-content-type-options X-Content-Type-Options "nosniff" # clickjacking protection # https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Headers_Cheat_Sheet.html#x-frame-options X-Frame-Options "SAMEORIGIN" # xss protection # https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Headers_Cheat_Sheet.html#x-xss-protection X-XSS-Protection "1; mode=block" # Remove -Server header, which is an information leak # Remove Caddy from Headers -Server # keep referrer data off of HTTP connections # https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Headers_Cheat_Sheet.html#referrer-policy Referrer-Policy strict-origin-when-cross-origin } } :80, tunnel.qkv.link:443 { # handle /.well-known/microsoft-identity-association.json { # root * /hostfile # file_server # } import security_headers # relay reverse_proxy /relay* relay:80 # Signal reverse_proxy /signalexchange.SignalExchange/* h2c://signal:10000 # Management reverse_proxy /api/* management:80 reverse_proxy /management.ManagementService/* h2c://management:80 reverse_proxy /oauth2/* management:80 # Zitadel reverse_proxy /zitadel.admin.v1.AdminService/* h2c://zitadel:8080 reverse_proxy /admin/v1/* h2c://zitadel:8080 reverse_proxy /zitadel.auth.v1.AuthService/* h2c://zitadel:8080 reverse_proxy /auth/v1/* h2c://zitadel:8080 reverse_proxy /zitadel.management.v1.ManagementService/* h2c://zitadel:8080 reverse_proxy /management/v1/* h2c://zitadel:8080 reverse_proxy /zitadel.system.v1.SystemService/* h2c://zitadel:8080 reverse_proxy /system/v1/* h2c://zitadel:8080 reverse_proxy /assets/v1/* h2c://zitadel:8080 reverse_proxy /ui/* h2c://zitadel:8080 reverse_proxy /oidc/v1/* h2c://zitadel:8080 reverse_proxy /saml/v2/* h2c://zitadel:8080 reverse_proxy /oauth/v2/* h2c://zitadel:8080 reverse_proxy /.well-known/openid-configuration h2c://zitadel:8080 reverse_proxy /openapi/* h2c://zitadel:8080 reverse_proxy /debug/* h2c://zitadel:8080 reverse_proxy /device/* h2c://zitadel:8080 reverse_proxy /device h2c://zitadel:8080 # Dashboard reverse_proxy /* dashboard:80 } ``` **Screenshots** <img width="3368" height="563" alt="Image" src="https://github.com/user-attachments/assets/d307b256-56b2-4515-994e-dbf5886cada0" /> **Have you tried these troubleshooting steps?** - [x] Reviewed [client troubleshooting](https://docs.netbird.io/how-to/troubleshooting-client) (if applicable) - [ ] Checked for newer NetBird versions - [ ] Searched for similar issues on GitHub (including closed ones) - [x] Restarted the NetBird client - [ ] Disabled other VPN software - [ ] Checked firewall settings
saavagebueno added the triage-needed label 2026-08-05 01:32:03 -04:00
Sign in to join this conversation.
No Label triage-needed
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: DYNR/netbird#11999