docs: introduce versioned docs and migrate to fuma (#11166)

## Todos for release

- [x] Configure Env in docs project on vercel
- [x] Configure Root Path in the docs project on vercel
- [ ] Remove old CSP https://github.com/zitadel/website/pull/1592

## What we did

This pull request migrates the project documentation from the old
`docs/` directory to the new `apps/docs/` directory, introduces a new
documentation system built with Next.js and Fumadocs, and updates all
relevant references, configuration files, and documentation to reflect
this change. It also adds new configuration and ignore files for the new
documentation app, updates CI and linting to exclude the new docs from
certain checks, and revises the contributing guidelines accordingly.

**Documentation System Migration and New Docs App**

* Migrated all documentation from `docs/` to `apps/docs/`, and updated
all references in `README.md`, `CONTRIBUTING.md`, and other files to
point to the new location.
[[1]](diffhunk://#diff-eca12c0a30e25b4b46522ebf89465a03ba72a03f540796c979137931d8f92055L585-L640)
[[2]](diffhunk://#diff-eca12c0a30e25b4b46522ebf89465a03ba72a03f540796c979137931d8f92055L660-R607)
[[3]](diffhunk://#diff-eca12c0a30e25b4b46522ebf89465a03ba72a03f540796c979137931d8f92055L740-R687)
[[4]](diffhunk://#diff-b335630551682c19a781afebcf4d07bf978fb1f8ac04c6bf87428ed5106870f5L2-R3)
[[5]](diffhunk://#diff-b335630551682c19a781afebcf4d07bf978fb1f8ac04c6bf87428ed5106870f5L30-R30)
* Added a new Next.js/Fumadocs-based documentation app under
`apps/docs/`, including core app files, layouts, routing, search API,
and a comprehensive `README.md` with development and contribution
instructions.
[[1]](diffhunk://#diff-5a1b07344a2c1b4d3f37b23ff1388b62cd9f57dea3c1cd23d8a0412b7602b132R1-R74)
[[2]](diffhunk://#diff-462b9ad1eabbb7d1c29bb9c36e4931eb180fd7190900e6d2babf8f4d66ad1c28R1-R39)
[[3]](diffhunk://#diff-e16ae25660ded787b10ac35dea96d5ecaacf895dae0afc8a9bd4382dc79a8c87R1-R7)
[apps/docs/app/[[...slug]]/layout.tsxR1-R81](diffhunk://#diff-59e08acde4e805b7aeccef1dcf98f1d71dfc550777e6b9402085cee0e9fa4e0aR1-R81),
[apps/docs/app/[[...slug]]/page.tsxR1-R79](diffhunk://#diff-e5df3f80d0fa01e12d63d81f29c57a9d14e846c78fd3beabb2ef768e38fd9580R1-R79),
[[4]](diffhunk://#diff-389b34918e040cacaa87cd7201ffa462cc2b0b716736f537e3d3c660ac69353bR1-R7)
[[5]](diffhunk://#diff-d3b03416d1c457b19f1c27b26f2db741412df841b875c26ccadc36e4522247f4R1-R29)
[[6]](diffhunk://#diff-c8fb8339570a5305809be7c618e14705fd86390dc278e6ce8ba224a7bc8b0c3cR1-R25)

**Configuration and Tooling Updates**

* Updated `.github/workflows/codeql.yml`, `.golangci.yaml`, and
`.github/dependabot.yml` to properly handle the new docs app: excluded
`apps/docs` from certain checks, added npm dependency updates for the
docs app, and excluded generated content.
[[1]](diffhunk://#diff-12783128521e452af0cfac94b99b8d250413c516ec71fe6d97dbea666ff7ba27L8-R14)
[[2]](diffhunk://#diff-9917ddc9f1c3304218f7269265b746d997c5c0615478177b5fceecd33ef47cb5R5-R6)
[[3]](diffhunk://#diff-9917ddc9f1c3304218f7269265b746d997c5c0615478177b5fceecd33ef47cb5R126-R129)
[[4]](diffhunk://#diff-dd4fbda47e51f1e35defb9275a9cd9c212ecde0b870cba89ddaaae65c5f3cd28R89-R106)
* Updated `.devcontainer/devcontainer.json` to use the latest Go 1.25.3
version for consistency.

**Licensing and Miscellaneous**

* Added `apps/docs/` to the list of licensed directories in
`LICENSING.md`.

These changes ensure the documentation is now maintained in a modern,
scalable system and all project tooling is updated to support the new
structure.

---------

Co-authored-by: Federico Coppede <[email protected]>
This commit is contained in:
Florian Forster
2026-01-29 08:38:36 -08:00
committed by GitHub
co-authored by Federico Coppede
parent 7b6395c348
commit d8ea041c56
2826 changed files with 78739 additions and 23571 deletions
@@ -0,0 +1,2 @@
- [Read more about Zitadel's TLS Modes](/self-hosting/manage/tls_modes)
- [Read more about how Zitadel uses HTTP/2](/self-hosting/manage/http2)
@@ -0,0 +1,16 @@
import CodeBlock from '@/components/docusaurus/code-block';
import ComposeYaml from "./docker-compose.yaml";
<>With these examples, you create and run a minimal {props.link} configuration for Zitadel with <a href="https://docs.docker.com/compose">Docker Compose</a>.
Whereas the guide focuses on the configuration for {props.name}, you can inspect the configurations for the Zitadel API, the Zitadel login and the database in the base Docker Compose file.</>
<details>
<summary>base docker-compose.yaml</summary>
<CodeBlock language="yaml">{ComposeYaml}</CodeBlock>
</details>
<>For running {props.name}, you will extend the base Docker Compose file with the {props.name} specific Docker Compose file.</>
<details>
<summary>specific docker-compose.yaml</summary>
<CodeBlock language="yaml">{props.compose}</CodeBlock>
</details>
@@ -0,0 +1,90 @@
import CodeBlock from '@/components/docusaurus/code-block';
export const Description = ({mode, name}) => {
let desc
switch (mode) {
case "disabled":
desc = <>Neither {name} nor Zitadel terminates TLS.
Nevertheless, {name} forwards unencrypted HTTP/2 traffic, aka h2c, to Zitadel.</>;
break;
case "external":
desc = <>{name} terminates TLS and forwards the requests to Zitadel via unencrypted h2c.
This example uses an unsafe self-signed certificate for {name}.</>;
break;
case "enabled":
desc = <>{name} terminates TLS and forwards the requests to Zitadel via encrypted HTTP/2.
This example uses an unsafe self-signed certificate for {name} and the same for Zitadel.</>;
break;
}
return (
<>
{desc}
<> By executing the commands below, you will download the files necessary to run Zitadel behind {name} with the following config:</>
</>)
}
export const Commands = ({mode, name, lower, configfilename}) => {
let genCert = '# Generate a self signed certificate and key.\nopenssl req -x509 -batch -subj "/CN=127.0.0.1.sslip.io/O=Zitadel Demo" -nodes -newkey rsa:2048 -keyout ./selfsigned.key -out ./selfsigned.crt 2>/dev/null\n\n';
let connPort = "443"
let connInsecureFlag = "--insecure "
let connScheme = "https"
let grpcPlainTextFlag = ""
if (mode === "disabled") {
genCert = ''
connPort = "80"
grpcPlainTextFlag = "--plaintext "
connScheme = "http"
// We only need that flag for TLS connections with the self-signed cert
connInsecureFlag = ""
}
return (
<div>
<CodeBlock language="bash">
{'# Download the configuration files.'}{'\n'}
{'export ZITADEL_CONFIG_FILES=https://raw.githubusercontent.com/zitadel/zitadel/main/docs/self-hosting/manage/reverseproxy\n'}
{'wget ${ZITADEL_CONFIG_FILES}/docker-compose.yaml -O docker-compose-base.yaml --quiet \n'}
{'wget ${ZITADEL_CONFIG_FILES}/'}{lower}{'/docker-compose.yaml -O docker-compose-'}{lower}{'.yaml --quiet \n'}
{'wget ${ZITADEL_CONFIG_FILES}/'}{lower}{'/'}{configfilename}{' -O '}{configfilename}{' --quiet \n'}
{'\n'}
{genCert}
{'# Run the database, and '}{name}{'.'}{'\n'}
{'docker compose --file docker-compose-base.yaml --file docker-compose-'}{lower}{'.yaml up --detach --wait db zitadel-init zitadel-'}{mode}{'-tls login-'}{mode}{'-tls proxy-'}{mode}{'-tls'}{'\n'}
{'\n'}
{'# Test that gRPC and HTTP APIs work. Empty brackets like {} means success.\n'}
{'# Make sure you have the grpcurl cli installed on your machine https://github.com/fullstorydev/grpcurl?tab=readme-ov-file#installation\n'}
{'grpcurl '}{connInsecureFlag}{grpcPlainTextFlag}{'127.0.0.1.sslip.io:'}{connPort}{' zitadel.admin.v1.AdminService/Healthz\n'}
{'curl '}{connInsecureFlag}{connScheme}{'://127.0.0.1.sslip.io:'}{connPort}{'/admin/v1/healthz\n'}
</CodeBlock>
</div>
)
}
export const LoginURL = ({mode}) => {
let scheme = "https";
if (mode === "disabled") {
scheme = "http"
}
const url = scheme + "://127.0.0.1.sslip.io/ui/console/[email protected]";
return <a href={url}>{url}</a>
}
<Description mode={props.mode} name={props.providername} link={props.link}/>
<details open>
<summary>{props.configfilename}</summary>
<CodeBlock >{props.configfilecontent}</CodeBlock>
</details>
<Commands mode={props.mode} name={props.providername} lower={props.lower} configfilename={props.configfilename}/>
<>When the docker compose command exits successfully, go to <LoginURL mode={props.mode}/> and log in:</>
- **username**: *zitadel-admin@<span></span>zitadel.127.0.0.1.sslip.io*
- **password**: *Password1!*
If the management console loads normally, you know that the HTTP and gRPC-Web and gRPC APIs are working correctly.
<CodeBlock language="bash">
{'# You can now stop the database, the Zitadel API, the Zitadel login and '}{props.providername}{'.'}{'\n'}
{'docker compose --file docker-compose-base.yaml --file docker-compose-'}{props.lower}{'.yaml down'}{'\n'}
</CodeBlock>
@@ -0,0 +1,4 @@
http://127.0.0.1.sslip.io {
reverse_proxy /ui/v2/login/* http://login-disabled-tls:3000
reverse_proxy h2c://zitadel-disabled-tls:8080
}
@@ -0,0 +1,41 @@
services:
proxy-disabled-tls:
image: "caddy:latest"
volumes:
- "./disabled-tls.Caddyfile:/etc/caddy/Caddyfile:ro"
ports:
- "80:80"
networks:
- app
depends_on:
zitadel-disabled-tls:
condition: 'service_healthy'
proxy-external-tls:
image: "caddy:latest"
volumes:
- "./external-tls.Caddyfile:/etc/caddy/Caddyfile:ro"
- "./selfsigned.crt:/etc/certs/selfsigned.crt:ro"
- "./selfsigned.key:/etc/certs/selfsigned.key:ro"
ports:
- "443:443"
networks:
- app
depends_on:
zitadel-external-tls:
condition: 'service_healthy'
proxy-enabled-tls:
image: "caddy:latest"
volumes:
- "./enabled-tls.Caddyfile:/etc/caddy/Caddyfile:ro"
- "./selfsigned.crt:/etc/certs/selfsigned.crt:ro"
- "./selfsigned.key:/etc/certs/selfsigned.key:ro"
ports:
- "443:443"
networks:
- app
depends_on:
zitadel-enabled-tls:
condition: 'service_healthy'
@@ -0,0 +1,9 @@
https://127.0.0.1.sslip.io {
tls /etc/certs/selfsigned.crt /etc/certs/selfsigned.key
reverse_proxy /ui/v2/login/* http://login-enabled-tls:3000
reverse_proxy https://zitadel-enabled-tls:8080 {
transport http {
tls_insecure_skip_verify
}
}
}
@@ -0,0 +1,5 @@
https://127.0.0.1.sslip.io {
tls /etc/certs/selfsigned.crt /etc/certs/selfsigned.key
reverse_proxy /ui/v2/login/* http://login-external-tls:3000
reverse_proxy h2c://zitadel-external-tls:8080
}
@@ -0,0 +1,32 @@
---
title: Configure Zitadel with Caddy
sidebar_label: Caddy
---
import ProxyGuideOverview from '../_proxy_guide_overview.mdx';
import ProxyGuideTLSMode from '../_proxy_guide_tls_mode.mdx';
import ProxyGuideMore from '../_proxy_guide_more.mdx';
import Compose from "./docker-compose.yaml";
import ConfigDisabled from "./disabled-tls.Caddyfile";
import ConfigExternal from "./external-tls.Caddyfile";
import ConfigEnabled from "./enabled-tls.Caddyfile";
export const providername = 'Caddy';
export const lower = "caddy";
export const link = <a href="https://caddyserver.com/">{providername}</a>
<ProxyGuideOverview components={props.components} name={providername} link={link} compose={Compose}></ProxyGuideOverview>
You can either setup your environment for <a href={'#tls-mode-external'}>TLS mode external</a> or <a href={'#tls-mode-enabled'}>TLS mode enabled</a>.
## TLS mode external
<ProxyGuideTLSMode components={props.components} mode="external" configfilename="external-tls.Caddyfile" configfilecontent={ConfigExternal} providername={providername} link={link} lower={lower}></ProxyGuideTLSMode>
## TLS mode enabled
<ProxyGuideTLSMode components={props.components} mode="enabled" configfilename="enabled-tls.Caddyfile" configfilecontent={ConfigEnabled} providername={providername} link={link} lower={lower}></ProxyGuideTLSMode>
## More Information
<ProxyGuideMore components={props.components} ></ProxyGuideMore>
@@ -0,0 +1,23 @@
---
title: Configure Zitadel with Cloudflare
sidebar_label: Cloudflare
---
## Settings
- [Make sure HTTP/2 is enabled](https://support.cloudflare.com/hc/en-us/articles/200168076-Understanding-Cloudflare-HTTP-2-and-HTTP-3-Support)
- [Verify that gRPC is enabled](https://support.cloudflare.com/hc/en-us/articles/360050483011-Understanding-Cloudflare-gRPC-support)
- [Verify that traffic is proxied through cloudflare](https://developers.cloudflare.com/dns/manage-dns-records/reference/proxied-dns-records/)
- [Configure Zitadel to use the TLS Mode enabled](/self-hosting/manage/tls_modes#enabled)
<Callout>
[Cloudflare does only support gRPC with TLS!](https://support.cloudflare.com/hc/en-us/articles/360050483011-Understanding-Cloudflare-gRPC-support)
</Callout>
## Troubleshooting
If something is not working please check the cloudflare WAF rules for potential violations.
These two rules are known to be triggered:
- 100001 Anomaly:Header:User-Agent - Missing Cloudflare Specials
- 100004 Anomaly:Header:User-Agent, Anomaly:Header:Referer - Missing or empty
@@ -0,0 +1,9 @@
---
title: Configure Zitadel with Cloudflare Tunnel
sidebar_label: Cloudflare Tunnel
---
<Callout type="warn">
[The Cloudflare tunnel client currently has an issue which disallows it to force HTTP/2 usage towards the origin.](https://github.com/cloudflare/cloudflared/issues/682)
</Callout>
@@ -0,0 +1,166 @@
services:
zitadel-disabled-tls:
extends:
service: zitadel-init
command: 'start-from-setup --masterkey "MasterkeyNeedsToHave32Characters"'
environment:
ZITADEL_EXTERNALPORT: 80
ZITADEL_EXTERNALSECURE: false
ZITADEL_TLS_ENABLED: false
networks:
- app
- db
depends_on:
zitadel-init:
condition: 'service_completed_successfully'
db:
condition: 'service_healthy'
zitadel-external-tls:
extends:
service: zitadel-init
command: 'start-from-setup --masterkey "MasterkeyNeedsToHave32Characters"'
environment:
ZITADEL_EXTERNALPORT: 443
ZITADEL_EXTERNALSECURE: true
ZITADEL_TLS_ENABLED: false
networks:
- app
- db
depends_on:
db:
condition: 'service_healthy'
zitadel-init:
condition: 'service_completed_successfully'
zitadel-enabled-tls:
extends:
service: zitadel-init
command: 'start-from-setup --masterkey "MasterkeyNeedsToHave32Characters"'
environment:
ZITADEL_EXTERNALPORT: 443
ZITADEL_EXTERNALSECURE: true
ZITADEL_TLS_ENABLED: true
ZITADEL_TLS_CERTPATH: /etc/certs/selfsigned.crt
ZITADEL_TLS_KEYPATH: /etc/certs/selfsigned.key
volumes:
- ./selfsigned.crt:/etc/certs/selfsigned.crt
- ./selfsigned.key:/etc/certs/selfsigned.key
networks:
- app
- db
depends_on:
zitadel-init:
condition: 'service_completed_successfully'
db:
condition: 'service_healthy'
zitadel-init:
image: '${ZITADEL_IMAGE:-ghcr.io/zitadel/zitadel:latest}'
command: 'init'
depends_on:
db:
condition: 'service_healthy'
environment:
# Using an external domain other than localhost proofs, that the proxy configuration works.
# If Zitadel can't resolve a requests original host to this domain,
# it will return a 404 Instance not found error.
ZITADEL_EXTERNALDOMAIN: 127.0.0.1.sslip.io
# In case something doesn't work as expected,
# it can be handy to be able to read the access logs.
ZITADEL_LOGSTORE_ACCESS_STDOUT_ENABLED: true
# For convenience, ZITADEL should not ask to change the initial admin users password.
ZITADEL_FIRSTINSTANCE_ORG_HUMAN_PASSWORDCHANGEREQUIRED: false
# database configuration
ZITADEL_DATABASE_POSTGRES_HOST: db
ZITADEL_DATABASE_POSTGRES_USER_PASSWORD: zitadel_pw
# Set up a service account with IAM_LOGIN_CLIENT role and write the PAT to the file ./login-client.pat
ZITADEL_FIRSTINSTANCE_LOGINCLIENTPATPATH: /current-dir/login-client.pat
ZITADEL_FIRSTINSTANCE_ORG_LOGINCLIENT_MACHINE_USERNAME: login-client
ZITADEL_FIRSTINSTANCE_ORG_LOGINCLIENT_MACHINE_NAME: Automatically Initialized IAM Login Client
ZITADEL_FIRSTINSTANCE_ORG_LOGINCLIENT_PAT_EXPIRATIONDATE: '2029-01-01T00:00:00Z'
# The master key is used to
networks:
- db
healthcheck:
test: [ "CMD", "/app/zitadel", "ready" ]
interval: '10s'
timeout: '5s'
retries: 5
start_period: '10s'
volumes:
- '.:/current-dir:rw'
db:
restart: 'always'
image: postgres:17-alpine
environment:
POSTGRES_PASSWORD: postgres
healthcheck:
test: [ "CMD-SHELL", "pg_isready" ]
interval: 5s
timeout: 60s
retries: 10
start_period: 5s
networks:
- db
volumes:
- 'data:/var/lib/postgresql/data:rw'
login-disabled-tls:
restart: 'unless-stopped'
image: 'ghcr.io/zitadel/zitadel-login:latest'
environment:
- ZITADEL_API_URL=http://zitadel-disabled-tls:8080
- NEXT_PUBLIC_BASE_PATH=/ui/v2/login
- ZITADEL_SERVICE_USER_TOKEN_FILE=/current-dir/login-client.pat
- CUSTOM_REQUEST_HEADERS=Host:127.0.0.1.sslip.io
- NODE_ENV=test
volumes:
- '.:/current-dir:ro'
networks:
- app
depends_on:
zitadel-disabled-tls:
condition: 'service_healthy'
login-external-tls:
restart: 'unless-stopped'
image: 'ghcr.io/zitadel/zitadel-login:latest'
environment:
- ZITADEL_API_URL=http://zitadel-external-tls:8080
- NEXT_PUBLIC_BASE_PATH=/ui/v2/login
- ZITADEL_SERVICE_USER_TOKEN_FILE=/current-dir/login-client.pat
- CUSTOM_REQUEST_HEADERS=Host:127.0.0.1.sslip.io
volumes:
- '.:/current-dir:ro'
networks:
- app
depends_on:
zitadel-external-tls:
condition: 'service_healthy'
login-enabled-tls:
restart: 'unless-stopped'
image: 'ghcr.io/zitadel/zitadel-login:latest'
environment:
- ZITADEL_API_URL=https://zitadel-enabled-tls:8080
- NEXT_PUBLIC_BASE_PATH=/ui/v2/login
- ZITADEL_SERVICE_USER_TOKEN_FILE=/current-dir/login-client.pat
- CUSTOM_REQUEST_HEADERS=Host:127.0.0.1.sslip.io
- NODE_TLS_REJECT_UNAUTHORIZED=0
volumes:
- '.:/current-dir:ro'
networks:
- app
depends_on:
zitadel-enabled-tls:
condition: 'service_healthy'
networks:
app:
db:
volumes:
data:
@@ -0,0 +1,41 @@
services:
proxy-disabled-tls:
image: "httpd:latest"
volumes:
- "./httpd-disabled-tls.conf:/usr/local/apache2/conf/httpd.conf"
ports:
- "80:80"
networks:
- app
depends_on:
zitadel-disabled-tls:
condition: 'service_healthy'
proxy-external-tls:
image: "httpd:latest"
volumes:
- "./httpd-external-tls.conf:/usr/local/apache2/conf/httpd.conf"
- "./selfsigned.crt:/etc/certs/selfsigned.crt:ro"
- "./selfsigned.key:/etc/certs/selfsigned.key:ro"
ports:
- "443:443"
networks:
- app
depends_on:
zitadel-external-tls:
condition: 'service_healthy'
proxy-enabled-tls:
image: "httpd:latest"
volumes:
- "./httpd-enabled-tls.conf:/usr/local/apache2/conf/httpd.conf"
- "./selfsigned.crt:/etc/certs/selfsigned.crt:ro"
- "./selfsigned.key:/etc/certs/selfsigned.key:ro"
ports:
- "443:443"
networks:
- app
depends_on:
zitadel-enabled-tls:
condition: 'service_healthy'
@@ -0,0 +1,52 @@
LoadModule mpm_event_module modules/mod_mpm_event.so
LoadModule authn_file_module modules/mod_authn_file.so
LoadModule authn_core_module modules/mod_authn_core.so
LoadModule authz_host_module modules/mod_authz_host.so
LoadModule authz_groupfile_module modules/mod_authz_groupfile.so
LoadModule authz_user_module modules/mod_authz_user.so
LoadModule authz_core_module modules/mod_authz_core.so
LoadModule access_compat_module modules/mod_access_compat.so
LoadModule auth_basic_module modules/mod_auth_basic.so
LoadModule reqtimeout_module modules/mod_reqtimeout.so
LoadModule filter_module modules/mod_filter.so
LoadModule mime_module modules/mod_mime.so
LoadModule log_config_module modules/mod_log_config.so
LoadModule env_module modules/mod_env.so
LoadModule headers_module modules/mod_headers.so
LoadModule setenvif_module modules/mod_setenvif.so
LoadModule version_module modules/mod_version.so
LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_http_module modules/mod_proxy_http.so
LoadModule ssl_module modules/mod_ssl.so
LoadModule proxy_http2_module modules/mod_proxy_http2.so
LoadModule unixd_module modules/mod_unixd.so
LoadModule status_module modules/mod_status.so
LoadModule autoindex_module modules/mod_autoindex.so
LoadModule dir_module modules/mod_dir.so
LoadModule alias_module modules/mod_alias.so
LoadModule rewrite_module modules/mod_rewrite.so
ServerRoot "/usr/local/apache2"
LogLevel debug
ErrorLog /proc/self/fd/2
CustomLog /proc/self/fd/1 "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\""
Listen 80
<VirtualHost *:80>
# Enable HTTP/2 for gRPC support (cleartext)
Protocols h2c http/1.1
# Proxy settings
ProxyPreserveHost on
ProxyAddHeaders on
RequestHeader set X-Forwarded-Proto "http"
# Route login UI first (more specific path)
ProxyPass /ui/v2/login http://login-disabled-tls:3000/ui/v2/login
ProxyPassReverse /ui/v2/login http://login-disabled-tls:3000/ui/v2/login
# Route everything else to Zitadel
ProxyPass / h2c://zitadel-disabled-tls:8080/
ProxyPassReverse / h2c://zitadel-disabled-tls:8080/
</VirtualHost>
@@ -0,0 +1,77 @@
LoadModule mpm_event_module modules/mod_mpm_event.so
LoadModule authn_file_module modules/mod_authn_file.so
LoadModule authn_core_module modules/mod_authn_core.so
LoadModule authz_host_module modules/mod_authz_host.so
LoadModule authz_groupfile_module modules/mod_authz_groupfile.so
LoadModule authz_user_module modules/mod_authz_user.so
LoadModule authz_core_module modules/mod_authz_core.so
LoadModule access_compat_module modules/mod_access_compat.so
LoadModule auth_basic_module modules/mod_auth_basic.so
LoadModule reqtimeout_module modules/mod_reqtimeout.so
LoadModule filter_module modules/mod_filter.so
LoadModule mime_module modules/mod_mime.so
LoadModule log_config_module modules/mod_log_config.so
LoadModule env_module modules/mod_env.so
LoadModule headers_module modules/mod_headers.so
LoadModule setenvif_module modules/mod_setenvif.so
LoadModule version_module modules/mod_version.so
LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_http_module modules/mod_proxy_http.so
LoadModule ssl_module modules/mod_ssl.so
LoadModule proxy_http2_module modules/mod_proxy_http2.so
LoadModule unixd_module modules/mod_unixd.so
LoadModule status_module modules/mod_status.so
LoadModule autoindex_module modules/mod_autoindex.so
LoadModule dir_module modules/mod_dir.so
LoadModule alias_module modules/mod_alias.so
LoadModule rewrite_module modules/mod_rewrite.so
LoadModule http2_module modules/mod_http2.so
ServerRoot "/usr/local/apache2"
LogLevel debug
ErrorLog /proc/self/fd/2
CustomLog /proc/self/fd/1 "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\""
Listen 443
SSLRandomSeed startup builtin
SSLRandomSeed connect builtin
<VirtualHost *:443>
# Enable HTTP/2 and configure ALPN for gRPC
Protocols h2 http/1.1
H2Direct on
# SSL Configuration
SSLEngine on
SSLProxyEngine on
SSLCertificateFile /etc/certs/selfsigned.crt
SSLCertificateKeyFile /etc/certs/selfsigned.key
# Modern SSL settings for gRPC compatibility
SSLProtocol all -SSLv3 -TLSv1 -TLSv1.1
SSLCipherSuite ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305
SSLHonorCipherOrder off
# Relax SSL Proxy Configuration to debug
SSLProxyVerify none
SSLProxyCheckPeerCN off
SSLProxyCheckPeerName off
SSLProxyCheckPeerExpire off
# Add timeouts to prevent hanging
ProxyTimeout 60
# Proxy settings
ProxyPreserveHost on
ProxyAddHeaders on
RequestHeader set X-Forwarded-Proto "https"
# Route login UI
ProxyPass /ui/v2/login http://login-enabled-tls:3000/ui/v2/login
ProxyPassReverse /ui/v2/login http://login-enabled-tls:3000/ui/v2/login
# Route to Zitadel with TLS
ProxyPass / https://zitadel-enabled-tls:8080/
ProxyPassReverse / https://zitadel-enabled-tls:8080/
</VirtualHost>
@@ -0,0 +1,63 @@
LoadModule mpm_event_module modules/mod_mpm_event.so
LoadModule authn_file_module modules/mod_authn_file.so
LoadModule authn_core_module modules/mod_authn_core.so
LoadModule authz_host_module modules/mod_authz_host.so
LoadModule authz_groupfile_module modules/mod_authz_groupfile.so
LoadModule authz_user_module modules/mod_authz_user.so
LoadModule authz_core_module modules/mod_authz_core.so
LoadModule access_compat_module modules/mod_access_compat.so
LoadModule auth_basic_module modules/mod_auth_basic.so
LoadModule reqtimeout_module modules/mod_reqtimeout.so
LoadModule filter_module modules/mod_filter.so
LoadModule mime_module modules/mod_mime.so
LoadModule log_config_module modules/mod_log_config.so
LoadModule env_module modules/mod_env.so
LoadModule headers_module modules/mod_headers.so
LoadModule setenvif_module modules/mod_setenvif.so
LoadModule version_module modules/mod_version.so
LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_http_module modules/mod_proxy_http.so
LoadModule ssl_module modules/mod_ssl.so
LoadModule proxy_http2_module modules/mod_proxy_http2.so
LoadModule unixd_module modules/mod_unixd.so
LoadModule status_module modules/mod_status.so
LoadModule autoindex_module modules/mod_autoindex.so
LoadModule dir_module modules/mod_dir.so
LoadModule alias_module modules/mod_alias.so
LoadModule rewrite_module modules/mod_rewrite.so
LoadModule http2_module modules/mod_http2.so
LogLevel debug
ErrorLog /proc/self/fd/2
CustomLog /proc/self/fd/1 "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\""
Listen 443
<VirtualHost *:443>
# Enable HTTP/2 and configure ALPN
Protocols h2 http/1.1
H2Direct on
# Configure SSL for gRPC
SSLEngine on
SSLCertificateFile /etc/certs/selfsigned.crt
SSLCertificateKeyFile /etc/certs/selfsigned.key
# Enable ALPN protocols for gRPC
SSLProtocol all -SSLv3 -TLSv1 -TLSv1.1
SSLCipherSuite ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305
SSLHonorCipherOrder off
SSLSessionTickets off
ProxyPreserveHost on
ProxyAddHeaders on
RequestHeader set X-Forwarded-Proto "https"
# Route login requests
ProxyPass /ui/v2/login http://login-external-tls:3000/ui/v2/login
ProxyPassReverse /ui/v2/login http://login-external-tls:3000/ui/v2/login
# Route gRPC/API requests with HTTP/2 support
ProxyPass / h2c://zitadel-external-tls:8080/
ProxyPassReverse / h2c://zitadel-external-tls:8080/
</VirtualHost>
@@ -0,0 +1,28 @@
---
title: Configure Zitadel with Apache httpd
sidebar_label: Apache httpd
---
import ProxyGuideOverview from '../_proxy_guide_overview.mdx';
import ProxyGuideTLSMode from '../_proxy_guide_tls_mode.mdx';
import ProxyGuideMore from '../_proxy_guide_more.mdx';
import Compose from "./docker-compose.yaml";
import ConfigDisabled from "./httpd-disabled-tls.conf";
import ConfigExternal from "./httpd-external-tls.conf";
import ConfigEnabled from "./httpd-enabled-tls.conf";
export const providername = "Apache httpd";
export const lower = "httpd";
export const link = <a href="https://httpd.apache.org//">{providername}</a>
<ProxyGuideOverview components={props.components} name={providername} link={link} compose={Compose}></ProxyGuideOverview>
You can either setup your environment for <a href={'#tls-mode-disabled'}>TLS mode disabled</a>, <a href={'#tls-mode-external'}>TLS mode external</a> or <a href={'#tls-mode-enabled'}>TLS mode enabled</a>.
## TLS mode external
<ProxyGuideTLSMode components={props.components} mode="external" configfilename="httpd-external-tls.conf" configfilecontent={ConfigExternal} providername={providername} link={link} lower={lower}></ProxyGuideTLSMode>
## More Information
<ProxyGuideMore components={props.components} ></ProxyGuideMore>
@@ -0,0 +1,41 @@
services:
proxy-disabled-tls:
image: "nginx:mainline-alpine"
volumes:
- "./nginx-disabled-tls.conf:/etc/nginx/nginx.conf:ro"
ports:
- "80:80"
networks:
- app
depends_on:
zitadel-disabled-tls:
condition: 'service_healthy'
proxy-external-tls:
image: "nginx:mainline-alpine"
volumes:
- "./nginx-external-tls.conf:/etc/nginx/nginx.conf:ro"
- "./selfsigned.crt:/etc/certs/selfsigned.crt:ro"
- "./selfsigned.key:/etc/certs/selfsigned.key:ro"
ports:
- "443:443"
networks:
- app
depends_on:
zitadel-external-tls:
condition: 'service_healthy'
proxy-enabled-tls:
image: "nginx:mainline-alpine"
volumes:
- "./nginx-enabled-tls.conf:/etc/nginx/nginx.conf:ro"
- "./selfsigned.crt:/etc/certs/selfsigned.crt:ro"
- "./selfsigned.key:/etc/certs/selfsigned.key:ro"
ports:
- "443:443"
networks:
- app
depends_on:
zitadel-enabled-tls:
condition: 'service_healthy'
@@ -0,0 +1,32 @@
---
title: Configure Zitadel with NGINX
sidebar_label: NGINX
---
import ProxyGuideOverview from '../_proxy_guide_overview.mdx';
import ProxyGuideTLSMode from '../_proxy_guide_tls_mode.mdx';
import ProxyGuideMore from '../_proxy_guide_more.mdx';
import Compose from "./docker-compose.yaml";
import ConfigDisabled from "./nginx-disabled-tls.conf";
import ConfigExternal from "./nginx-external-tls.conf";
import ConfigEnabled from "./nginx-enabled-tls.conf";
export const providername = 'NGINX';
export const lower = "nginx";
export const link = <a href="https://nginx.com/">{providername}</a>;
<ProxyGuideOverview components={props.components} name={providername} link={link} compose={Compose}></ProxyGuideOverview>
You can either setup your environment for <a href={'#tls-mode-disabled'}>TLS mode disabled</a>, <a href={'#tls-mode-external'}>TLS mode external</a> or <a href={'#tls-mode-enabled'}>TLS mode enabled</a>.
## TLS mode external
<ProxyGuideTLSMode components={props.components} mode="external" configfilename="nginx-external-tls.conf" configfilecontent={ConfigExternal} providername={providername} link={link} lower={lower}></ProxyGuideTLSMode>
## TLS mode enabled
<ProxyGuideTLSMode components={props.components} mode="enabled" configfilename="nginx-enabled-tls.conf" configfilecontent={ConfigEnabled} providername={providername} link={link} lower={lower}></ProxyGuideTLSMode>
## More Information
<ProxyGuideMore components={props.components} ></ProxyGuideMore>
@@ -0,0 +1,17 @@
events {
worker_connections 1024;
}
http {
server {
listen 80;
http2 on;
location /ui/v2/login {
proxy_pass http://login-disabled-tls:3000;
proxy_set_header Host $host;
}
location / {
grpc_pass grpc://zitadel-disabled-tls:8080;
grpc_set_header Host $host;
}
}
}
@@ -0,0 +1,20 @@
events {
worker_connections 1024;
}
http {
server {
listen 443 ssl;
http2 on;
ssl_certificate /etc/certs/selfsigned.crt;
ssl_certificate_key /etc/certs/selfsigned.key;
location /ui/v2/login {
proxy_pass http://login-enabled-tls:3000;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto https;
}
location / {
grpc_pass grpcs://zitadel-enabled-tls:8080;
grpc_set_header Host $host;
}
}
}
@@ -0,0 +1,21 @@
events {
worker_connections 1024;
}
http {
server {
listen 443 ssl;
http2 on;
ssl_certificate /etc/certs/selfsigned.crt;
ssl_certificate_key /etc/certs/selfsigned.key;
location /ui/v2/login {
proxy_pass http://login-external-tls:3000;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto https;
}
location / {
grpc_pass grpc://zitadel-external-tls:8080;
grpc_set_header Host $host;
grpc_set_header X-Forwarded-Proto https;
}
}
}
@@ -0,0 +1,13 @@
---
title: Reverse Proxy Configuration
---
Check out one of the following guides to configure your favorite reverse proxy:
- [Traefik](/self-hosting/manage/reverseproxy/traefik)
- [NGINX](/self-hosting/manage/reverseproxy/nginx)
- [Caddy](/self-hosting/manage/reverseproxy/caddy)
- [Apache httpd](/self-hosting/manage/reverseproxy/httpd)
- [Cloudflare](/self-hosting/manage/reverseproxy/cloudflare)
- [Cloudflare Tunnel](/self-hosting/manage/reverseproxy/cloudflare_tunnel)
- [Fronting Zitadel Cloud](/self-hosting/manage/reverseproxy/zitadel_cloud)
@@ -0,0 +1,41 @@
services:
proxy-disabled-tls:
image: "traefik:latest"
volumes:
- "./traefik-disabled-tls.yaml:/etc/traefik/traefik.yaml:ro"
ports:
- "80:80"
networks:
- app
depends_on:
zitadel-disabled-tls:
condition: 'service_healthy'
proxy-external-tls:
image: "traefik:latest"
volumes:
- "./traefik-external-tls.yaml:/etc/traefik/traefik.yaml:ro"
- "./selfsigned.crt:/etc/certs/selfsigned.crt:ro"
- "./selfsigned.key:/etc/certs/selfsigned.key:ro"
ports:
- "443:443"
networks:
- app
depends_on:
zitadel-external-tls:
condition: 'service_healthy'
proxy-enabled-tls:
image: "traefik:latest"
volumes:
- "./traefik-enabled-tls.yaml:/etc/traefik/traefik.yaml:ro"
- "./selfsigned.crt:/etc/certs/selfsigned.crt:ro"
- "./selfsigned.key:/etc/certs/selfsigned.key:ro"
ports:
- "443:443"
networks:
- app
depends_on:
zitadel-enabled-tls:
condition: 'service_healthy'
@@ -0,0 +1,32 @@
---
title: Configure Zitadel with Traefik
sidebar_label: Traefik
---
import ProxyGuideOverview from '../_proxy_guide_overview.mdx';
import ProxyGuideTLSMode from '../_proxy_guide_tls_mode.mdx';
import ProxyGuideMore from '../_proxy_guide_more.mdx';
import Compose from "./docker-compose.yaml";
import ConfigDisabled from "./traefik-disabled-tls.yaml";
import ConfigExternal from "./traefik-external-tls.yaml";
import ConfigEnabled from "./traefik-enabled-tls.yaml";
export const providername = 'Traefik';
export const lower = "traefik";
export const link = <a href="https://doc.traefik.io/traefik/">{providername}</a>;
<ProxyGuideOverview components={props.components} name={providername} link={link} compose={Compose}></ProxyGuideOverview>
You can either setup your environment for <a href={'#tls-mode-disabled'}>TLS mode disabled</a>, <a href={'#tls-mode-external'}>TLS mode external</a> or <a href={'#tls-mode-enabled'}>TLS mode enabled</a>.
## TLS mode external
<ProxyGuideTLSMode components={props.components} mode="external" configfilename="traefik-external-tls.yaml" configfilecontent={ConfigExternal} providername={providername} link={link} lower={lower}></ProxyGuideTLSMode>
## TLS mode enabled
<ProxyGuideTLSMode components={props.components} mode="enabled" configfilename="traefik-enabled-tls.yaml" configfilecontent={ConfigEnabled} providername={providername} link={link} lower={lower}></ProxyGuideTLSMode>
## More Information
<ProxyGuideMore components={props.components} ></ProxyGuideMore>
@@ -0,0 +1,31 @@
log:
level: "DEBUG"
providers:
file:
filename: "/etc/traefik/traefik.yaml"
entrypoints:
web:
address: ":80"
http:
routers:
zitadel:
entryPoints:
- "web"
service: "zitadel"
rule: '!PathPrefix(`/ui/v2/login`)'
login:
entryPoints:
- "web"
service: "login"
rule: 'PathPrefix(`/ui/v2/login`)'
services:
zitadel:
loadBalancer:
passHostHeader: true
servers:
- url: "h2c://zitadel-disabled-tls:8080"
login:
loadBalancer:
passHostHeader: true
servers:
- url: "http://login-disabled-tls:3000"
@@ -0,0 +1,46 @@
log:
level: "DEBUG"
providers:
file:
filename: "/etc/traefik/traefik.yaml"
entrypoints:
web:
address: ":443"
http:
routers:
zitadel:
entryPoints:
- "web"
service: "zitadel"
rule: '!PathPrefix(`/ui/v2/login`)'
tls: {}
login:
entryPoints:
- "web"
service: "login"
rule: 'PathPrefix(`/ui/v2/login`)'
tls: {}
services:
zitadel:
loadBalancer:
serversTransport: "zitadel"
passHostHeader: true
servers:
- url: "https://zitadel-enabled-tls:8080"
login:
loadBalancer:
serversTransport: "login"
passHostHeader: true
servers:
- url: "http://login-enabled-tls:3000"
serversTransports:
zitadel:
insecureSkipVerify: true
login:
insecureSkipVerify: true
tls:
stores:
default:
defaultCertificate:
certFile: /etc/certs/selfsigned.crt
keyFile: /etc/certs/selfsigned.key
@@ -0,0 +1,39 @@
log:
level: "DEBUG"
providers:
file:
filename: "/etc/traefik/traefik.yaml"
entrypoints:
web:
address: ":443"
http:
routers:
zitadel:
entryPoints:
- "web"
service: "zitadel"
rule: '!PathPrefix(`/ui/v2/login`)'
tls: {}
login:
entryPoints:
- "web"
service: "login"
rule: 'PathPrefix(`/ui/v2/login`)'
tls: {}
services:
zitadel:
loadBalancer:
passHostHeader: true
servers:
- url: "h2c://zitadel-external-tls:8080"
login:
loadBalancer:
passHostHeader: true
servers:
- url: "http://login-external-tls:3000"
tls:
stores:
default:
defaultCertificate:
certFile: /etc/certs/selfsigned.crt
keyFile: /etc/certs/selfsigned.key
@@ -0,0 +1,16 @@
---
title: Front Zitadel Cloud with a CDN, WAF or Reverse Proxy
sidebar_label: Fronting Zitadel Cloud
---
## Fronting Zitadel Cloud
You can use your reverseproxy, content delivery network (CDN) or web application firewall (WAF) to front Zitadel Cloud.
However we currently do not recommend this for production settings.
To configure your service that fronts Zitadel please have a look at the vendors in this page.
## Things to look out for when fronting Zitadel Cloud
- Cache-control - Zitadel Cloud uses a CDN to globally distribute data. Please try to avoid overriding this header as it may lead to sideeffects
- Rate Limits - Zitadel Cloud uses a combination of static and dynamic rate limits. If you receive occasional 429 headers you are rate limited.