Deploying the Zitadel template and opening it on the configured domain ended in {"code":5,"message":"Not Found"} (#516). Zitadel resolves its virtual instance and builds every URL (OIDC issuer, console environment, login redirects) from ZITADEL_EXTERNALDOMAIN + ZITADEL_EXTERNALPORT + ZITADEL_EXTERNALSECURE. The template hardcoded ZITADEL_EXTERNALPORT: 8080 (the internal container port), so all generated URLs pointed at http://<domain>:8080 instead of the domain/port actually served by Traefik.
Fix
Expose ZITADEL_EXTERNALDOMAIN, ZITADEL_EXTERNALPORT and ZITADEL_EXTERNALSECURE as template env vars, defaulting to the generated domain over HTTP (80 / false), matching the official compose reference. The domain in the Domains tab and ZITADEL_EXTERNALDOMAIN are both derived from ${main_domain}, so they always match on first deploy. Users on HTTPS or a custom domain can flip the clearly named vars in the Env tab (comments in the compose/toml explain this, and that it must happen before the first deploy since Zitadel binds the instance to the domain on first start).
Pin the image to v4.16.0 (was latest) and keep the built-in login v1 (ZITADEL_DEFAULTINSTANCE_FEATURES_LOGINV2_REQUIRED: false) so no separate login-v2 container is required.
Add the official readiness healthcheck (/app/zitadel ready), remove the unused /app/data volume, the bogus empty SMTP env keys, the published random host port and the obsolete version: key.
🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.
## 📋 Pull Request Information
**Original PR:** https://github.com/Dokploy/templates/pull/1001
**Author:** [@Siumauricio](https://github.com/Siumauricio)
**Created:** 7/14/2026
**Status:** ✅ Merged
**Merged:** 7/14/2026
**Merged by:** [@Siumauricio](https://github.com/Siumauricio)
**Base:** `canary` ← **Head:** `fix/zitadel-domain`
---
### 📝 Commits (1)
- [`6786237`](https://github.com/Dokploy/templates/commit/6786237720611c0342a8e5dd2773c2d762f4f5e2) fix(zitadel): wire EXTERNALDOMAIN/PORT/SECURE to the generated domain
### 📊 Changes
**3 files changed** (+31 additions, -22 deletions)
<details>
<summary>View changed files</summary>
📝 `blueprints/zitadel/docker-compose.yml` (+19 -18)
📝 `blueprints/zitadel/meta.json` (+1 -1)
📝 `blueprints/zitadel/template.toml` (+11 -3)
</details>
### 📄 Description
## Problem
Deploying the Zitadel template and opening it on the configured domain ended in `{"code":5,"message":"Not Found"}` (#516). Zitadel resolves its virtual instance and builds every URL (OIDC issuer, console environment, login redirects) from `ZITADEL_EXTERNALDOMAIN` + `ZITADEL_EXTERNALPORT` + `ZITADEL_EXTERNALSECURE`. The template hardcoded `ZITADEL_EXTERNALPORT: 8080` (the internal container port), so all generated URLs pointed at `http://<domain>:8080` instead of the domain/port actually served by Traefik.
## Fix
- Expose `ZITADEL_EXTERNALDOMAIN`, `ZITADEL_EXTERNALPORT` and `ZITADEL_EXTERNALSECURE` as template env vars, defaulting to the generated domain over HTTP (`80` / `false`), matching the [official compose reference](https://zitadel.com/docs/self-hosting/deploy/compose). The domain in the Domains tab and `ZITADEL_EXTERNALDOMAIN` are both derived from `${main_domain}`, so they always match on first deploy. Users on HTTPS or a custom domain can flip the clearly named vars in the Env tab (comments in the compose/toml explain this, and that it must happen before the first deploy since Zitadel binds the instance to the domain on first start).
- Pin the image to `v4.16.0` (was `latest`) and keep the built-in login v1 (`ZITADEL_DEFAULTINSTANCE_FEATURES_LOGINV2_REQUIRED: false`) so no separate login-v2 container is required.
- Add the official readiness healthcheck (`/app/zitadel ready`), remove the unused `/app/data` volume, the bogus empty SMTP env keys, the published random host port and the obsolete `version:` key.
## Evidence (deployed on a Dokploy instance)
- Deploy: `done`, container healthy.
- `http://<generated-domain>/` → `302 /ui/login` → **HTTP 200 «ZITADEL • Management Console»**
- `http://<generated-domain>/debug/healthz` → `200`
- OIDC discovery issuer now exactly matches the routed domain (previously carried `:8080`):
```json
{"issuer":"http://compose-...-31-220-108-27.sslip.io","authorization_endpoint":"http://compose-...-31-220-108-27.sslip.io/oauth/v2/authorize", ...}
```
Closes #516
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---
<sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
📋 Pull Request Information
Original PR: https://github.com/Dokploy/templates/pull/1001
Author: @Siumauricio
Created: 7/14/2026
Status: ✅ Merged
Merged: 7/14/2026
Merged by: @Siumauricio
Base:
canary← Head:fix/zitadel-domain📝 Commits (1)
6786237fix(zitadel): wire EXTERNALDOMAIN/PORT/SECURE to the generated domain📊 Changes
3 files changed (+31 additions, -22 deletions)
View changed files
📝
blueprints/zitadel/docker-compose.yml(+19 -18)📝
blueprints/zitadel/meta.json(+1 -1)📝
blueprints/zitadel/template.toml(+11 -3)📄 Description
Problem
Deploying the Zitadel template and opening it on the configured domain ended in
{"code":5,"message":"Not Found"}(#516). Zitadel resolves its virtual instance and builds every URL (OIDC issuer, console environment, login redirects) fromZITADEL_EXTERNALDOMAIN+ZITADEL_EXTERNALPORT+ZITADEL_EXTERNALSECURE. The template hardcodedZITADEL_EXTERNALPORT: 8080(the internal container port), so all generated URLs pointed athttp://<domain>:8080instead of the domain/port actually served by Traefik.Fix
ZITADEL_EXTERNALDOMAIN,ZITADEL_EXTERNALPORTandZITADEL_EXTERNALSECUREas template env vars, defaulting to the generated domain over HTTP (80/false), matching the official compose reference. The domain in the Domains tab andZITADEL_EXTERNALDOMAINare both derived from${main_domain}, so they always match on first deploy. Users on HTTPS or a custom domain can flip the clearly named vars in the Env tab (comments in the compose/toml explain this, and that it must happen before the first deploy since Zitadel binds the instance to the domain on first start).v4.16.0(waslatest) and keep the built-in login v1 (ZITADEL_DEFAULTINSTANCE_FEATURES_LOGINV2_REQUIRED: false) so no separate login-v2 container is required./app/zitadel ready), remove the unused/app/datavolume, the bogus empty SMTP env keys, the published random host port and the obsoleteversion:key.Evidence (deployed on a Dokploy instance)
done, container healthy.http://<generated-domain>/→302 /ui/login→ HTTP 200 «ZITADEL • Management Console»http://<generated-domain>/debug/healthz→200:8080):Closes #516
🤖 Generated with Claude Code
🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.