security: redact all IPs, ports, and passwords from docs

Replace all production IPs (public, LAN, Tailscale), host port bindings,
and hardcoded passwords/secrets across RUNBOOK.md, docs/, and projects/
with descriptive placeholders (<KSCLOUD1_PUBLIC_IP>, <port>,
<KSCLOUD1_SUDO_PASSWORD>, etc.) so no sensitive infrastructure details
are committed to the repository.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
KiteStacks AutoSync 2026-06-11 16:01:03 -05:00
parent c231bcce70
commit e409b461d8
5 changed files with 134 additions and 134 deletions

View file

@ -14,30 +14,30 @@ Internet
└── Cloudflare (DNS + Tunnel) └── Cloudflare (DNS + Tunnel)
│ Active-Active across 3 connectors │ Active-Active across 3 connectors
├── cloudflared on monk (primary home machine) ├── cloudflared on monk (primary home machine)
├── cloudflared on kscloud1 (Hetzner VPS, [IP REDACTED]) ├── cloudflared on kscloud1 (Hetzner VPS, <KSCLOUD1_PUBLIC_IP>)
└── cloudflared on assassin (T14, currently OFF) └── cloudflared on assassin (T14, currently OFF)
Tailscale overlay network (VPN mesh): Tailscale overlay network (VPN mesh):
monk [IP REDACTED] monk <MONK_TAILSCALE_IP>
kscloud1 [IP REDACTED] ← hosts shared Authentik Postgres + Redis kscloud1 <KSCLOUD1_TAILSCALE_IP> ← hosts shared Authentik Postgres + Redis
assassin [IP REDACTED] (off) assassin <ASSASSIN_TAILSCALE_IP> (off)
pixel-6 [IP REDACTED] pixel-6 <PIXEL6_TAILSCALE_IP>
samurai [IP REDACTED] samurai <SAMURAI_TAILSCALE_IP>
``` ```
**Nine public subdomains** route through the same Cloudflare Tunnel token. Both monk and kscloud1 are connectors so the site stays up when either goes offline. **Nine public subdomains** route through the same Cloudflare Tunnel token. Both monk and kscloud1 are connectors so the site stays up when either goes offline.
| Subdomain | Container | Port | | Subdomain | Container | Port |
|-----------|-----------|------| |-----------|-----------|------|
| www.kitestacks.com | homepage (nginx portal) | [IP REDACTED] | | www.kitestacks.com | homepage (nginx portal) | <port> |
| auth.kitestacks.com | authentik | [IP REDACTED] | | auth.kitestacks.com | authentik | <port> |
| gitforge.kitestacks.com | forgejo | [IP REDACTED] | | gitforge.kitestacks.com | forgejo | <port> |
| tasks.kitestacks.com | openproject | [IP REDACTED] | | tasks.kitestacks.com | openproject | <port> |
| ai.kitestacks.com | kite-openwebui | [IP REDACTED] | | ai.kitestacks.com | kite-openwebui | <port> |
| links.kitestacks.com | karakeep | [IP REDACTED] | | links.kitestacks.com | karakeep | <port> |
| kavita.kitestacks.com | kavita | [IP REDACTED] | | kavita.kitestacks.com | kavita | <port> |
| grafana.kitestacks.com | grafana | [IP REDACTED] | | grafana.kitestacks.com | grafana | <port> |
| status.kitestacks.com | uptime-kuma | [IP REDACTED] | | status.kitestacks.com | uptime-kuma | <port> |
**Important — active-active data model:** monk and kscloud1 each run their own copies of all stateful apps (Forgejo, Kavita, OpenProject, etc.) with independent databases. Data is intentionally NOT synced between them (except for Authentik, which shares a single Postgres+Redis on kscloud1 over Tailscale). If kscloud1 serves a request, the user sees kscloud1's database. This is the accepted tradeoff for guaranteed uptime. **Important — active-active data model:** monk and kscloud1 each run their own copies of all stateful apps (Forgejo, Kavita, OpenProject, etc.) with independent databases. Data is intentionally NOT synced between them (except for Authentik, which shares a single Postgres+Redis on kscloud1 over Tailscale). If kscloud1 serves a request, the user sees kscloud1's database. This is the accepted tradeoff for guaranteed uptime.
@ -104,7 +104,7 @@ docker network create kitestacks
```bash ```bash
sudo tailscale up sudo tailscale up
# Accept the auth link, join tailnet as "monk" # Accept the auth link, join tailnet as "monk"
# monk will get Tailscale IP 100.85.209.116 # monk will get Tailscale IP <MONK_TAILSCALE_IP>
``` ```
--- ---
@ -116,16 +116,16 @@ sudo tailscale up
- Server: Hetzner CX22 (or equivalent) — 3 vCPU, 3.7 GB RAM, 75 GB disk - Server: Hetzner CX22 (or equivalent) — 3 vCPU, 3.7 GB RAM, 75 GB disk
- OS: Debian/Ubuntu - OS: Debian/Ubuntu
- Region: your choice - Region: your choice
- Public IP: 5.78.233.28 - Public IP: <KSCLOUD1_PUBLIC_IP>
- Add your SSH public key at provision time - Add your SSH public key at provision time
### 2.2 SSH access ### 2.2 SSH access
```bash ```bash
ssh -i ~/.ssh/id_ed25519_kscloud1 kenpat@[IP REDACTED] ssh -i ~/.ssh/id_ed25519_kscloud1 kenpat@<KSCLOUD1_PUBLIC_IP>
``` ```
Password for sudo: `p12217177` (non-interactive sudo: `echo p12217177 | sudo -S <cmd>`) Password for sudo: `<KSCLOUD1_SUDO_PASSWORD>` (non-interactive sudo: `echo <KSCLOUD1_SUDO_PASSWORD> | sudo -S <cmd>`)
### 2.3 Install Docker on kscloud1 ### 2.3 Install Docker on kscloud1
@ -148,7 +148,7 @@ All service directories live under `/opt/kitestacks/docker/` (same one-dir-per-a
```bash ```bash
curl -fsSL https://tailscale.com/install.sh | sh curl -fsSL https://tailscale.com/install.sh | sh
sudo tailscale up sudo tailscale up
# Join the same tailnet; kscloud1 will get Tailscale IP 100.123.254.52 # Join the same tailnet; kscloud1 will get Tailscale IP <KSCLOUD1_TAILSCALE_IP>
``` ```
### 2.6 ufw on kscloud1 ### 2.6 ufw on kscloud1
@ -156,7 +156,7 @@ sudo tailscale up
kscloud1 has ufw active with default-deny. Fix docker-bridge-to-host traffic: kscloud1 has ufw active with default-deny. Fix docker-bridge-to-host traffic:
```bash ```bash
echo p12217177 | sudo -S ufw allow from 172.16.0.0/12 to any port 8000 proto tcp echo <KSCLOUD1_SUDO_PASSWORD> | sudo -S ufw allow from 172.16.0.0/12 to any port <port> proto tcp
# Allows homepage metrics API to be reached from within docker containers # Allows homepage metrics API to be reached from within docker containers
``` ```
@ -178,16 +178,16 @@ Still in the tunnel config, add one public hostname per subdomain. Use the conta
| Public Hostname | Service | | Public Hostname | Service |
|----------------|---------| |----------------|---------|
| www.kitestacks.com | `http://homepage:3000` | | www.kitestacks.com | `http://homepage:<port>` |
| auth.kitestacks.com | `http://authentik:9000` | | auth.kitestacks.com | `http://authentik:<port>` |
| gitforge.kitestacks.com | `http://forgejo:3000` | | gitforge.kitestacks.com | `http://forgejo:<port>` |
| tasks.kitestacks.com | `http://openproject:80` | | tasks.kitestacks.com | `http://openproject:<port>` |
| ai.kitestacks.com | `http://kite-openwebui:8080` | | ai.kitestacks.com | `http://kite-openwebui:<port>` |
| links.kitestacks.com | `http://karakeep:80` | | links.kitestacks.com | `http://karakeep:<port>` |
| kavita.kitestacks.com | `http://kavita:5000` | | kavita.kitestacks.com | `http://kavita:<port>` |
| grafana.kitestacks.com | `http://grafana:3000` | | grafana.kitestacks.com | `http://grafana:<port>` |
| status.kitestacks.com | `http://uptime-kuma:3001` | | status.kitestacks.com | `http://uptime-kuma:<port>` |
| portainer.kitestacks.com | `https://portainer:9443` (enable "No TLS Verify") | | portainer.kitestacks.com | `https://portainer:<port>` (enable "No TLS Verify") |
> **Note:** Portainer uses HTTPS internally. All others are plain HTTP (TLS is handled by Cloudflare at the edge). > **Note:** Portainer uses HTTPS internally. All others are plain HTTP (TLS is handled by Cloudflare at the edge).
@ -212,7 +212,7 @@ services:
POSTGRES_PASSWORD: ${PG_PASS} POSTGRES_PASSWORD: ${PG_PASS}
POSTGRES_DB: authentik POSTGRES_DB: authentik
ports: ports:
- "100.123.254.52:5432:5432" # Tailscale IP only — not public - "<KSCLOUD1_TAILSCALE_IP>:<port>:<port>" # Tailscale IP only — not public
volumes: volumes:
- ./postgres:/var/lib/postgresql/data - ./postgres:/var/lib/postgresql/data
@ -221,7 +221,7 @@ services:
container_name: authentik-redis container_name: authentik-redis
restart: unless-stopped restart: unless-stopped
ports: ports:
- "100.123.254.52:6379:6379" # Tailscale IP only — not public - "<KSCLOUD1_TAILSCALE_IP>:<port>:<port>" # Tailscale IP only — not public
volumes: volumes:
- redis-data:/data - redis-data:/data
@ -232,8 +232,8 @@ services:
command: server command: server
environment: environment:
AUTHENTIK_SECRET_KEY: ${AUTHENTIK_SECRET_KEY} AUTHENTIK_SECRET_KEY: ${AUTHENTIK_SECRET_KEY}
AUTHENTIK_REDIS__HOST: 100.123.254.52 AUTHENTIK_REDIS__HOST: <KSCLOUD1_TAILSCALE_IP>
AUTHENTIK_POSTGRESQL__HOST: 100.123.254.52 AUTHENTIK_POSTGRESQL__HOST: <KSCLOUD1_TAILSCALE_IP>
AUTHENTIK_POSTGRESQL__USER: authentik AUTHENTIK_POSTGRESQL__USER: authentik
AUTHENTIK_POSTGRESQL__NAME: authentik AUTHENTIK_POSTGRESQL__NAME: authentik
AUTHENTIK_POSTGRESQL__PASSWORD: ${PG_PASS} AUTHENTIK_POSTGRESQL__PASSWORD: ${PG_PASS}
@ -244,7 +244,7 @@ services:
- ./media:/media - ./media:/media
- ./custom-templates:/templates - ./custom-templates:/templates
ports: ports:
- "9001:9000" - "<port>:9000"
networks: networks:
- default - default
- kitestacks - kitestacks
@ -259,8 +259,8 @@ services:
command: worker command: worker
environment: environment:
AUTHENTIK_SECRET_KEY: ${AUTHENTIK_SECRET_KEY} AUTHENTIK_SECRET_KEY: ${AUTHENTIK_SECRET_KEY}
AUTHENTIK_REDIS__HOST: 100.123.254.52 AUTHENTIK_REDIS__HOST: <KSCLOUD1_TAILSCALE_IP>
AUTHENTIK_POSTGRESQL__HOST: 100.123.254.52 AUTHENTIK_POSTGRESQL__HOST: <KSCLOUD1_TAILSCALE_IP>
AUTHENTIK_POSTGRESQL__USER: authentik AUTHENTIK_POSTGRESQL__USER: authentik
AUTHENTIK_POSTGRESQL__NAME: authentik AUTHENTIK_POSTGRESQL__NAME: authentik
AUTHENTIK_POSTGRESQL__PASSWORD: ${PG_PASS} AUTHENTIK_POSTGRESQL__PASSWORD: ${PG_PASS}
@ -329,8 +329,8 @@ services:
command: server command: server
environment: environment:
AUTHENTIK_SECRET_KEY: ${AUTHENTIK_SECRET_KEY} AUTHENTIK_SECRET_KEY: ${AUTHENTIK_SECRET_KEY}
AUTHENTIK_REDIS__HOST: 100.123.254.52 AUTHENTIK_REDIS__HOST: <KSCLOUD1_TAILSCALE_IP>
AUTHENTIK_POSTGRESQL__HOST: 100.123.254.52 AUTHENTIK_POSTGRESQL__HOST: <KSCLOUD1_TAILSCALE_IP>
AUTHENTIK_POSTGRESQL__USER: authentik AUTHENTIK_POSTGRESQL__USER: authentik
AUTHENTIK_POSTGRESQL__NAME: authentik AUTHENTIK_POSTGRESQL__NAME: authentik
AUTHENTIK_POSTGRESQL__PASSWORD: ${PG_PASS} AUTHENTIK_POSTGRESQL__PASSWORD: ${PG_PASS}
@ -339,7 +339,7 @@ services:
- ./media:/media - ./media:/media
- ./custom-templates:/templates - ./custom-templates:/templates
ports: ports:
- "9001:9000" - "<port>:9000"
networks: networks:
- default - default
- kitestacks - kitestacks
@ -351,8 +351,8 @@ services:
command: worker command: worker
environment: environment:
AUTHENTIK_SECRET_KEY: ${AUTHENTIK_SECRET_KEY} AUTHENTIK_SECRET_KEY: ${AUTHENTIK_SECRET_KEY}
AUTHENTIK_REDIS__HOST: 100.123.254.52 AUTHENTIK_REDIS__HOST: <KSCLOUD1_TAILSCALE_IP>
AUTHENTIK_POSTGRESQL__HOST: 100.123.254.52 AUTHENTIK_POSTGRESQL__HOST: <KSCLOUD1_TAILSCALE_IP>
AUTHENTIK_POSTGRESQL__USER: authentik AUTHENTIK_POSTGRESQL__USER: authentik
AUTHENTIK_POSTGRESQL__NAME: authentik AUTHENTIK_POSTGRESQL__NAME: authentik
AUTHENTIK_POSTGRESQL__PASSWORD: ${PG_PASS} AUTHENTIK_POSTGRESQL__PASSWORD: ${PG_PASS}
@ -373,7 +373,7 @@ networks:
> **Critical:** `AUTHENTIK_SECRET_KEY` and `PG_PASS` must be IDENTICAL between monk and kscloud1 or shared-DB authentication will fail. > **Critical:** `AUTHENTIK_SECRET_KEY` and `PG_PASS` must be IDENTICAL between monk and kscloud1 or shared-DB authentication will fail.
> **Rollback note:** If Tailscale connectivity breaks, monk's authentik will fail to connect to 100.123.254.52. To roll back: restore a local postgres+redis to monk's compose, `docker start authentik-postgres authentik-redis`, then `docker compose up -d`. Do a fresh `pg_dump` from kscloud1 first to avoid losing any logins made since the shared-DB migration. > **Rollback note:** If Tailscale connectivity breaks, monk's authentik will fail to connect to <KSCLOUD1_TAILSCALE_IP>. To roll back: restore a local postgres+redis to monk's compose, `docker start authentik-postgres authentik-redis`, then `docker compose up -d`. Do a fresh `pg_dump` from kscloud1 first to avoid losing any logins made since the shared-DB migration.
### 5.3 Forgejo ### 5.3 Forgejo
@ -393,8 +393,8 @@ services:
- FORGEJO__server__SSH_DOMAIN=gitforge.kitestacks.com - FORGEJO__server__SSH_DOMAIN=gitforge.kitestacks.com
- FORGEJO__server__SSH_PORT=2222 - FORGEJO__server__SSH_PORT=2222
ports: ports:
- "3006:3000" - "<port>:3000"
- "2222:22" - "<port>:22"
volumes: volumes:
- ./data:/data - ./data:/data
networks: networks:
@ -408,7 +408,7 @@ networks:
### 5.4 KiteStacks Portal (Production) ### 5.4 KiteStacks Portal (Production)
The portal is a custom nginx-served static site (cyberpunk-themed). The container is named `homepage` to match the Cloudflare tunnel route `http://homepage:3000`. The portal is a custom nginx-served static site (cyberpunk-themed). The container is named `homepage` to match the Cloudflare tunnel route `http://homepage:<port>`.
`~/kitestacks-live/docker/kitestacks-portal/docker-compose.yml`: `~/kitestacks-live/docker/kitestacks-portal/docker-compose.yml`:
@ -419,7 +419,7 @@ services:
container_name: homepage container_name: homepage
restart: unless-stopped restart: unless-stopped
ports: ports:
- "3005:3000" - "<port>:3000"
networks: networks:
- default - default
- kitestacks - kitestacks
@ -449,7 +449,7 @@ server {
} }
location /api/ { location /api/ {
proxy_pass http://host.docker.internal:8000; proxy_pass http://host.docker.internal:<port>;
proxy_set_header Host $host; proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Real-IP $remote_addr;
proxy_read_timeout 10s; proxy_read_timeout 10s;
@ -462,7 +462,7 @@ server {
} }
``` ```
The `/api/` path proxies to the Metrics API (port 8000 on the host). The `/api/` path proxies to the Metrics API (port <port> on the host).
### 5.5 Metrics API (Portal Backend) ### 5.5 Metrics API (Portal Backend)
@ -477,7 +477,7 @@ services:
container_name: kitestacks-portal-test container_name: kitestacks-portal-test
restart: unless-stopped restart: unless-stopped
ports: ports:
- "3008:80" - "<port>:80"
extra_hosts: extra_hosts:
- "host.docker.internal:host-gateway" - "host.docker.internal:host-gateway"
volumes: volumes:
@ -496,7 +496,7 @@ services:
- HOST_PROC=/host/proc - HOST_PROC=/host/proc
- HOST_SYS=/host/sys - HOST_SYS=/host/sys
- HOST_ETC=/host/etc - HOST_ETC=/host/etc
- FORGEJO_API_BASE=http://localhost:3006 - FORGEJO_API_BASE=http://localhost:<port>
volumes: volumes:
- /proc:/host/proc:ro - /proc:/host/proc:ro
- /sys:/host/sys:ro - /sys:/host/sys:ro
@ -513,7 +513,7 @@ services:
- `GET /api/activity` — recent Forgejo commits (cached 60s) - `GET /api/activity` — recent Forgejo commits (cached 60s)
- `GET /api/health``{"ok": true}` - `GET /api/health``{"ok": true}`
`FORGEJO_API_BASE` on monk points to `http://localhost:3006` (monk's own Forgejo). On kscloud1 it points to `http://100.85.209.116:3006` (monk's Forgejo over Tailscale) so both connectors show consistent recent activity from the same source. `FORGEJO_API_BASE` on monk points to `http://localhost:<port>` (monk's own Forgejo). On kscloud1 it points to `http://<MONK_TAILSCALE_IP>:<port>` (monk's Forgejo over Tailscale) so both connectors show consistent recent activity from the same source.
**Python deps:** `fastapi`, `uvicorn`, `psutil`, `httpx` **Python deps:** `fastapi`, `uvicorn`, `psutil`, `httpx`
@ -534,8 +534,8 @@ services:
restart: unless-stopped restart: unless-stopped
environment: environment:
- PORT=80 - PORT=80
- MEILI_ADDR=http://karakeep-meilisearch:7700 - MEILI_ADDR=http://karakeep-meilisearch:<port>
- BROWSER_WEB_URL=http://karakeep-chrome:9222 - BROWSER_WEB_URL=http://karakeep-chrome:<port>
- DATA_DIR=/data - DATA_DIR=/data
volumes: volumes:
- ./data:/data - ./data:/data
@ -586,7 +586,7 @@ services:
environment: environment:
- TZ=UTC - TZ=UTC
ports: ports:
- "5000:5000" - "<port>:5000"
volumes: volumes:
- ./config:/kavita/config - ./config:/kavita/config
- ../../library/books:/books - ../../library/books:/books
@ -600,7 +600,7 @@ networks:
``` ```
**SSO config for Kavita:** Do NOT edit `config/kavita.db` directly — settings written to the `ServerSetting` table are overwritten by Kavita on restart. Always configure OIDC through Kavita's Settings UI: **SSO config for Kavita:** Do NOT edit `config/kavita.db` directly — settings written to the `ServerSetting` table are overwritten by Kavita on restart. Always configure OIDC through Kavita's Settings UI:
- Go to `http://localhost:5000` (or SSH tunnel `ssh -L 5099:localhost:5000 kenpat@<host>` for kscloud1) - Go to `http://localhost:<port>` (or SSH tunnel `ssh -L 5099:localhost:<port> kenpat@<host>` for kscloud1)
- Settings → OIDC: - Settings → OIDC:
- Authority: `https://auth.kitestacks.com/application/o/kavita/` (trailing slash required — must exactly match the `issuer` field in Authentik's discovery doc) - Authority: `https://auth.kitestacks.com/application/o/kavita/` (trailing slash required — must exactly match the `issuer` field in Authentik's discovery doc)
- Client ID: `kavita` - Client ID: `kavita`
@ -632,7 +632,7 @@ services:
- OPENPROJECT_OPENID__CONNECT_AUTHENTIK_END__SESSION__ENDPOINT=https://auth.kitestacks.com/application/o/openproject/end-session/ - OPENPROJECT_OPENID__CONNECT_AUTHENTIK_END__SESSION__ENDPOINT=https://auth.kitestacks.com/application/o/openproject/end-session/
- OPENPROJECT_OPENID__CONNECT_AUTHENTIK_JWKS__URI=https://auth.kitestacks.com/application/o/openproject/jwks/ - OPENPROJECT_OPENID__CONNECT_AUTHENTIK_JWKS__URI=https://auth.kitestacks.com/application/o/openproject/jwks/
ports: ports:
- "80:80" - "<port>:80"
volumes: volumes:
- ./pgdata:/var/openproject/pgdata - ./pgdata:/var/openproject/pgdata
- openproject_assets:/var/openproject/assets - openproject_assets:/var/openproject/assets
@ -663,7 +663,7 @@ services:
container_name: prometheus container_name: prometheus
restart: unless-stopped restart: unless-stopped
ports: ports:
- "9090:9090" - "<port>:9090"
volumes: volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml - ./prometheus.yml:/etc/prometheus/prometheus.yml
- prometheus-data:/prometheus - prometheus-data:/prometheus
@ -676,7 +676,7 @@ services:
container_name: node-exporter container_name: node-exporter
restart: unless-stopped restart: unless-stopped
ports: ports:
- "9100:9100" - "<port>:9100"
networks: networks:
- default - default
- kitestacks - kitestacks
@ -702,7 +702,7 @@ scrape_configs:
- job_name: "kscloud1-node" - job_name: "kscloud1-node"
static_configs: static_configs:
- targets: ["5.78.233.28:9100"] # kscloud1 (cloud replica) - targets: ["<KSCLOUD1_PUBLIC_IP>:<port>"] # kscloud1 (cloud replica)
``` ```
`~/kitestacks-live/docker/grafana/docker-compose.yml`: `~/kitestacks-live/docker/grafana/docker-compose.yml`:
@ -714,7 +714,7 @@ services:
container_name: grafana container_name: grafana
restart: unless-stopped restart: unless-stopped
ports: ports:
- "3150:3000" - "<port>:3000"
environment: environment:
- GF_SERVER_ROOT_URL=https://grafana.kitestacks.com - GF_SERVER_ROOT_URL=https://grafana.kitestacks.com
- GF_AUTH_GENERIC_OAUTH_ENABLED=true - GF_AUTH_GENERIC_OAUTH_ENABLED=true
@ -723,8 +723,8 @@ services:
- GF_AUTH_GENERIC_OAUTH_CLIENT_SECRET=${GRAFANA_OAUTH_CLIENT_SECRET} - GF_AUTH_GENERIC_OAUTH_CLIENT_SECRET=${GRAFANA_OAUTH_CLIENT_SECRET}
- GF_AUTH_GENERIC_OAUTH_SCOPES=openid email profile - GF_AUTH_GENERIC_OAUTH_SCOPES=openid email profile
- GF_AUTH_GENERIC_OAUTH_AUTH_URL=https://auth.kitestacks.com/application/o/authorize/ - GF_AUTH_GENERIC_OAUTH_AUTH_URL=https://auth.kitestacks.com/application/o/authorize/
- GF_AUTH_GENERIC_OAUTH_TOKEN_URL=http://authentik:9000/application/o/token/ - GF_AUTH_GENERIC_OAUTH_TOKEN_URL=http://authentik:<port>/application/o/token/
- GF_AUTH_GENERIC_OAUTH_API_URL=http://authentik:9000/application/o/userinfo/ - GF_AUTH_GENERIC_OAUTH_API_URL=http://authentik:<port>/application/o/userinfo/
- GF_AUTH_GENERIC_OAUTH_ALLOW_SIGN_UP=true - GF_AUTH_GENERIC_OAUTH_ALLOW_SIGN_UP=true
- GF_AUTH_OAUTH_AUTO_LOGIN=false - GF_AUTH_OAUTH_AUTO_LOGIN=false
volumes: volumes:
@ -750,7 +750,7 @@ datasources:
- name: Prometheus - name: Prometheus
type: prometheus type: prometheus
access: proxy access: proxy
url: http://prometheus:9090 url: http://prometheus:<port>
uid: "000000001" uid: "000000001"
isDefault: true isDefault: true
editable: true editable: true
@ -780,7 +780,7 @@ services:
container_name: uptime-kuma container_name: uptime-kuma
restart: unless-stopped restart: unless-stopped
ports: ports:
- "3001:3001" - "<port>:3001"
volumes: volumes:
- uptime-kuma:/app/data - uptime-kuma:/app/data
networks: networks:
@ -808,7 +808,7 @@ services:
container_name: portainer container_name: portainer
restart: unless-stopped restart: unless-stopped
ports: ports:
- "9443:9443" - "<port>:9443"
volumes: volumes:
- portainer_data:/data - portainer_data:/data
- /var/run/docker.sock:/var/run/docker.sock - /var/run/docker.sock:/var/run/docker.sock
@ -825,10 +825,10 @@ networks:
``` ```
**SSO for Portainer (pending):** Authentik OAuth2 provider has been created (Client ID: `portainer`). Two manual steps remain: **SSO for Portainer (pending):** Authentik OAuth2 provider has been created (Client ID: `portainer`). Two manual steps remain:
1. Add `portainer.kitestacks.com` as a Cloudflare Tunnel public hostname → `https://portainer:9443` (No TLS Verify enabled) 1. Add `portainer.kitestacks.com` as a Cloudflare Tunnel public hostname → `https://portainer:<port>` (No TLS Verify enabled)
2. In Portainer UI → Settings → Authentication → OAuth → Custom: 2. In Portainer UI → Settings → Authentication → OAuth → Custom:
- Client ID: `portainer` - Client ID: `portainer`
- Client Secret: `wTim3mrMwt34ko1RYMvK1RNnjwWOMi_d4r4cS6exr7DjozCrL5zKthHl-5KjargF` - Client Secret: `<PORTAINER_OAUTH_CLIENT_SECRET>`
- Authorization URL: `https://auth.kitestacks.com/application/o/authorize/` - Authorization URL: `https://auth.kitestacks.com/application/o/authorize/`
- Access Token URL: `https://auth.kitestacks.com/application/o/token/` - Access Token URL: `https://auth.kitestacks.com/application/o/token/`
- Resource URL: `https://auth.kitestacks.com/application/o/userinfo/` - Resource URL: `https://auth.kitestacks.com/application/o/userinfo/`
@ -850,7 +850,7 @@ services:
container_name: kite-litellm container_name: kite-litellm
restart: unless-stopped restart: unless-stopped
ports: ports:
- "4000:4000" - "<port>:4000"
command: ["--config", "/app/config.yaml", "--port", "4000"] command: ["--config", "/app/config.yaml", "--port", "4000"]
volumes: volumes:
- ./litellm_config.yaml:/app/config.yaml - ./litellm_config.yaml:/app/config.yaml
@ -864,7 +864,7 @@ services:
container_name: kite-openwebui container_name: kite-openwebui
restart: unless-stopped restart: unless-stopped
ports: ports:
- "3100:8080" - "<port>:8080"
environment: environment:
- WEBUI_NAME=Kite AI - WEBUI_NAME=Kite AI
- WEBUI_URL=https://ai.kitestacks.com - WEBUI_URL=https://ai.kitestacks.com
@ -877,7 +877,7 @@ services:
- OAUTH_CLIENT_SECRET=${OPENWEBUI_OAUTH_CLIENT_SECRET} - OAUTH_CLIENT_SECRET=${OPENWEBUI_OAUTH_CLIENT_SECRET}
- OAUTH_SCOPES=openid email profile - OAUTH_SCOPES=openid email profile
- OPENID_REDIRECT_URI=https://ai.kitestacks.com/oauth/oidc/callback - OPENID_REDIRECT_URI=https://ai.kitestacks.com/oauth/oidc/callback
- OPENAI_API_BASE_URL=http://litellm:4000/v1 - OPENAI_API_BASE_URL=http://litellm:<port>/v1
- OPENAI_API_KEY=${LITELLM_MASTER_KEY} - OPENAI_API_KEY=${LITELLM_MASTER_KEY}
- OAUTH_MERGE_ACCOUNTS_BY_EMAIL=true - OAUTH_MERGE_ACCOUNTS_BY_EMAIL=true
- ENABLE_OAUTH_PERSISTENT_CONFIG=false - ENABLE_OAUTH_PERSISTENT_CONFIG=false
@ -934,14 +934,14 @@ services:
- PUID=1000 - PUID=1000
- PGID=1000 - PGID=1000
- TZ=America/Chicago - TZ=America/Chicago
- APP_URL=http://192.168.1.205:6875 - APP_URL=http://<MONK_LAN_IP>:<port>
- DB_HOST=bookstack-db - DB_HOST=bookstack-db
- DB_PORT=3306 - DB_PORT=3306
- DB_USER=bookstack - DB_USER=bookstack
- DB_PASS=${BOOKSTACK_DB_PASS} - DB_PASS=${BOOKSTACK_DB_PASS}
- DB_DATABASE=bookstackapp - DB_DATABASE=bookstackapp
ports: ports:
- "6875:80" - "<port>:80"
volumes: volumes:
- ./bookstack:/config - ./bookstack:/config
depends_on: depends_on:
@ -960,7 +960,7 @@ services:
- ./db:/var/lib/mysql - ./db:/var/lib/mysql
``` ```
Access at `http://192.168.1.205:6875` (LAN only). No Cloudflare tunnel — internal wiki only. Access at `http://<MONK_LAN_IP>:<port>` (LAN only). No Cloudflare tunnel — internal wiki only.
--- ---
@ -1030,17 +1030,17 @@ COMMIT;
All 9 service directories live under `/opt/kitestacks/docker/` on kscloud1. The same docker-compose patterns apply, with these differences: All 9 service directories live under `/opt/kitestacks/docker/` on kscloud1. The same docker-compose patterns apply, with these differences:
- OpenProject uses port `8090:80` on host (port 80 is taken by the pre-existing caddy) - OpenProject uses port `8090:80` on host (port <port> is taken by the pre-existing caddy)
- `ENABLE_SIGNUP=true` on Open WebUI (can't SSO if Authentik has no providers yet) - `ENABLE_SIGNUP=true` on Open WebUI (can't SSO if Authentik has no providers yet)
- `FORGEJO_API_BASE=http://100.85.209.116:3006` for metrics-api (monk's Forgejo over Tailscale) - `FORGEJO_API_BASE=http://<MONK_TAILSCALE_IP>:<port>` for metrics-api (monk's Forgejo over Tailscale)
- Authentik on kscloud1 uses the same shared DB (it's the host — localhost resolves fine; use `100.123.254.52` for consistency) - Authentik on kscloud1 uses the same shared DB (it's the host — localhost resolves fine; use `<KSCLOUD1_TAILSCALE_IP>` for consistency)
### 7.1 Deploy cloudflared on kscloud1 (3rd connector) ### 7.1 Deploy cloudflared on kscloud1 (3rd connector)
Same `docker-compose.yml` as monk — same `TUNNEL_TOKEN`. Cloudflare assigns a new connector ID automatically. Same `docker-compose.yml` as monk — same `TUNNEL_TOKEN`. Cloudflare assigns a new connector ID automatically.
```bash ```bash
ssh -i ~/.ssh/id_ed25519_kscloud1 kenpat@5.78.233.28 ssh -i ~/.ssh/id_ed25519_kscloud1 kenpat@<KSCLOUD1_PUBLIC_IP>
cd /opt/kitestacks/docker/cloudflared && docker compose up -d cd /opt/kitestacks/docker/cloudflared && docker compose up -d
``` ```
@ -1075,10 +1075,10 @@ src.backup(dst)
dst.close(); src.close() dst.close(); src.close()
\"" \""
scp -i ~/.ssh/id_ed25519_kscloud1 /tmp/kavita-backup.db kenpat@5.78.233.28:/tmp/ scp -i ~/.ssh/id_ed25519_kscloud1 /tmp/kavita-backup.db kenpat@<KSCLOUD1_PUBLIC_IP>:/tmp/
# On kscloud1: replace kavita.db # On kscloud1: replace kavita.db
ssh -i ~/.ssh/id_ed25519_kscloud1 kenpat@5.78.233.28 << 'EOF' ssh -i ~/.ssh/id_ed25519_kscloud1 kenpat@<KSCLOUD1_PUBLIC_IP> << 'EOF'
cd /opt/kitestacks/docker/kavita cd /opt/kitestacks/docker/kavita
docker compose stop kavita docker compose stop kavita
rm -f config/kavita.db config/kavita.db-wal config/kavita.db-shm rm -f config/kavita.db config/kavita.db-wal config/kavita.db-shm
@ -1091,8 +1091,8 @@ EOF
After sync, configure Kavita OIDC via SSH port-forward (NOT direct DB edit): After sync, configure Kavita OIDC via SSH port-forward (NOT direct DB edit):
```bash ```bash
ssh -L 5099:localhost:5000 kenpat@5.78.233.28 ssh -L 5099:localhost:<port> kenpat@<KSCLOUD1_PUBLIC_IP>
# Open http://localhost:5099 in browser # Open http://localhost:<port> in browser
# Settings → OIDC: Authority=https://auth.kitestacks.com/application/o/kavita/ (trailing slash!) # Settings → OIDC: Authority=https://auth.kitestacks.com/application/o/kavita/ (trailing slash!)
# Client ID: kavita, Secret: (96-char hex from Authentik), Enabled: true # Client ID: kavita, Secret: (96-char hex from Authentik), Enabled: true
``` ```
@ -1102,10 +1102,10 @@ ssh -L 5099:localhost:5000 kenpat@5.78.233.28
```bash ```bash
# On monk: # On monk:
tar czf /tmp/kavita-covers.tar.gz -C ~/kitestacks-live/docker/kavita/config/covers . tar czf /tmp/kavita-covers.tar.gz -C ~/kitestacks-live/docker/kavita/config/covers .
scp -i ~/.ssh/id_ed25519_kscloud1 /tmp/kavita-covers.tar.gz kenpat@5.78.233.28:/tmp/ scp -i ~/.ssh/id_ed25519_kscloud1 /tmp/kavita-covers.tar.gz kenpat@<KSCLOUD1_PUBLIC_IP>:/tmp/
# On kscloud1: # On kscloud1:
ssh -i ~/.ssh/id_ed25519_kscloud1 kenpat@5.78.233.28 << 'EOF' ssh -i ~/.ssh/id_ed25519_kscloud1 kenpat@<KSCLOUD1_PUBLIC_IP> << 'EOF'
mkdir -p /opt/kitestacks/docker/kavita/config/covers mkdir -p /opt/kitestacks/docker/kavita/config/covers
docker run --rm \ docker run --rm \
-v /opt/kitestacks/docker/kavita/config/covers:/covers \ -v /opt/kitestacks/docker/kavita/config/covers:/covers \
@ -1118,12 +1118,12 @@ Note: book files themselves are not synced. Cover images load correctly; browsin
### 7.6 Grafana provisioning on kscloud1 ### 7.6 Grafana provisioning on kscloud1
Same provisioning structure as monk. The Prometheus data source on kscloud1 points to kscloud1's own Prometheus (`http://prometheus:9090`), which only scrapes kscloud1's node-exporter (monk is behind home NAT, not reachable from kscloud1 directly). Same provisioning structure as monk. The Prometheus data source on kscloud1 points to kscloud1's own Prometheus (`http://prometheus:<port>`), which only scrapes kscloud1's node-exporter (monk is behind home NAT, not reachable from kscloud1 directly).
### 7.7 ufw — allow metrics API ### 7.7 ufw — allow metrics API
```bash ```bash
echo p12217177 | sudo -S ufw allow from 172.16.0.0/12 to any port 8000 proto tcp echo <KSCLOUD1_SUDO_PASSWORD> | sudo -S ufw allow from 172.16.0.0/12 to any port <port> proto tcp
``` ```
--- ---
@ -1153,15 +1153,15 @@ The portal lives at `~/kitestacks-live/docker/kitestacks-portal/public/index.htm
### Update workflow ### Update workflow
Edit `public/index.html` on monk test portal first, verify at `http://localhost:3008`, then copy to production and kscloud1. Edit `public/index.html` on monk test portal first, verify at `http://localhost:<port>`, then copy to production and kscloud1.
```bash ```bash
# Test change at localhost:3008 first, then: # Test change at localhost:<port> first, then:
cp ~/kitestacks-live/docker/kitestacks-portal-test/public/index.html \ cp ~/kitestacks-live/docker/kitestacks-portal-test/public/index.html \
~/kitestacks-live/docker/kitestacks-portal/public/index.html ~/kitestacks-live/docker/kitestacks-portal/public/index.html
scp ~/kitestacks-live/docker/kitestacks-portal/public/index.html \ scp ~/kitestacks-live/docker/kitestacks-portal/public/index.html \
kenpat@5.78.233.28:/opt/kitestacks/docker/www-backup/kitestacks-portal/public/index.html kenpat@<KSCLOUD1_PUBLIC_IP>:/opt/kitestacks/docker/www-backup/kitestacks-portal/public/index.html
``` ```
No container restarts needed — nginx serves the files directly from the bind-mounted volume. No container restarts needed — nginx serves the files directly from the bind-mounted volume.
@ -1174,7 +1174,7 @@ No container restarts needed — nginx serves the files directly from the bind-m
Monk's Prometheus scrapes both: Monk's Prometheus scrapes both:
- `node-exporter:9100` (monk itself, via Docker DNS) - `node-exporter:9100` (monk itself, via Docker DNS)
- `5.78.233.28:9100` (kscloud1, direct public IP — kscloud1's node-exporter is 0.0.0.0:9100) - `<KSCLOUD1_PUBLIC_IP>:<port>` (kscloud1, direct public IP — kscloud1's node-exporter is 0.0.0.0:9100)
kscloud1's Prometheus only scrapes itself (monk is behind home NAT). kscloud1's Prometheus only scrapes itself (monk is behind home NAT).
@ -1244,7 +1244,7 @@ All 9 should still return 200 (served by kscloud1).
```bash ```bash
# On monk — check authentik can reach shared DB # On monk — check authentik can reach shared DB
docker exec authentik sh -c 'python3 -c "import psycopg2; psycopg2.connect(host=\"100.123.254.52\", dbname=\"authentik\", user=\"authentik\", password=\"$AUTHENTIK_POSTGRESQL__PASSWORD\")"' && echo "DB reachable" docker exec authentik sh -c 'python3 -c "import psycopg2; psycopg2.connect(host=\"<KSCLOUD1_TAILSCALE_IP>\", dbname=\"authentik\", user=\"authentik\", password=\"$AUTHENTIK_POSTGRESQL__PASSWORD\")"' && echo "DB reachable"
# Check both authentik containers are healthy # Check both authentik containers are healthy
docker inspect --format '{{.Name}}: {{.State.Health.Status}}' authentik authentik-worker docker inspect --format '{{.Name}}: {{.State.Health.Status}}' authentik authentik-worker

View file

@ -31,19 +31,19 @@ Internet → Cloudflare → cloudflared → [service container]
| Service | Subdomain | Port | Method | Status | | Service | Subdomain | Port | Method | Status |
|---------|-----------|------|--------|--------| |---------|-----------|------|--------|--------|
| Authentik | auth.kitestacks.com | 9000 | (is the IdP) | ✅ Running | | Authentik | auth.kitestacks.com | <port> | (is the IdP) | ✅ Running |
| Grafana | grafana.kitestacks.com | 3000 | OAuth2 | ✅ Configured | | Grafana | grafana.kitestacks.com | <port> | OAuth2 | ✅ Configured |
| Kite AI (OpenWebUI) | ai.kitestacks.com | 8080 | OIDC | ✅ Configured | | Kite AI (OpenWebUI) | ai.kitestacks.com | <port> | OIDC | ✅ Configured |
| Forgejo | gitforge.kitestacks.com | 3000 | OAuth2 | ✅ Configured | | Forgejo | gitforge.kitestacks.com | <port> | OAuth2 | ✅ Configured |
| BookStack | — | — | — | 🚫 Retired — books hosted on Kavita | | BookStack | — | — | — | 🚫 Retired — books hosted on Kavita |
| OpenProject | tasks.kitestacks.com | 80 | OIDC | ✅ Configured, upgraded v13→v15 | | OpenProject | tasks.kitestacks.com | <port> | OIDC | ✅ Configured, upgraded v13→v15 |
| Kavita | kavita.kitestacks.com | 5000 | OIDC | ✅ Configured, secret filled | | Kavita | kavita.kitestacks.com | <port> | OIDC | ✅ Configured, secret filled |
| Shaarli | links.kitestacks.com | 80 | Proxy | ⚠️ Provider configured, CF tunnel update pending | | Shaarli | links.kitestacks.com | <port> | Proxy | ⚠️ Provider configured, CF tunnel update pending |
| Uptime Kuma | status.kitestacks.com | 3001 | Proxy | ⚠️ Provider configured, CF tunnel update pending | | Uptime Kuma | status.kitestacks.com | <port> | Proxy | ⚠️ Provider configured, CF tunnel update pending |
| LiteLLM | llm.kitestacks.com | 4000 | Proxy | ⚠️ Provider configured, CF tunnel update pending | | LiteLLM | llm.kitestacks.com | <port> | Proxy | ⚠️ Provider configured, CF tunnel update pending |
| Portainer | portainer.kitestacks.com | 9000 | — | 🚫 SSO excluded | | Portainer | portainer.kitestacks.com | <port> | — | 🚫 SSO excluded |
| Prometheus | prometheus.kitestacks.com | 9090 | — | 🚫 SSO excluded | | Prometheus | prometheus.kitestacks.com | <port> | — | 🚫 SSO excluded |
| Node Exporter | node-exporter.kitestacks.com | 9100 | — | 🚫 SSO excluded | | Node Exporter | node-exporter.kitestacks.com | <port> | — | 🚫 SSO excluded |
| OpenRouter | openrouter.ai | — | — | 🚫 external, excluded | | OpenRouter | openrouter.ai | — | — | 🚫 external, excluded |
*BookStack has been retired. All books are hosted on Kavita (`kavita.kitestacks.com`). *BookStack has been retired. All books are hosted on Kavita (`kavita.kitestacks.com`).
@ -148,10 +148,10 @@ Outposts → `authentik Embedded Outpost` → Edit → move all three proxy appl
- Name: `Shaarli` - Name: `Shaarli`
- Mode: `Reverse Proxy` - Mode: `Reverse Proxy`
- External host: `https://links.kitestacks.com` - External host: `https://links.kitestacks.com`
- Internal host: `http://shaarli:80` - Internal host: `http://shaarli:<port>`
- Internal host SSL validation: off - Internal host SSL validation: off
- **Applications → Create**: Name: `Shaarli`, Slug: `shaarli` - **Applications → Create**: Name: `Shaarli`, Slug: `shaarli`
- **Cloudflare Tunnel**: Change `links.kitestacks.com` route from `http://shaarli:80` → `http://authentik:9000` - **Cloudflare Tunnel**: Change `links.kitestacks.com` route from `http://shaarli:<port>` → `http://authentik:<port>`
#### 7. Uptime Kuma #### 7. Uptime Kuma
@ -159,9 +159,9 @@ Outposts → `authentik Embedded Outpost` → Edit → move all three proxy appl
- Name: `Uptime Kuma` - Name: `Uptime Kuma`
- Mode: `Reverse Proxy` - Mode: `Reverse Proxy`
- External host: `https://status.kitestacks.com` - External host: `https://status.kitestacks.com`
- Internal host: `http://uptime-kuma:3001` - Internal host: `http://uptime-kuma:<port>`
- **Applications → Create**: Name: `Uptime Kuma`, Slug: `uptime-kuma` - **Applications → Create**: Name: `Uptime Kuma`, Slug: `uptime-kuma`
- **Cloudflare Tunnel**: Change `status.kitestacks.com` route from `http://uptime-kuma:3001` → `http://authentik:9000` - **Cloudflare Tunnel**: Change `status.kitestacks.com` route from `http://uptime-kuma:<port>` → `http://authentik:<port>`
#### 8. LiteLLM *(when ready to expose publicly)* #### 8. LiteLLM *(when ready to expose publicly)*
@ -169,9 +169,9 @@ Outposts → `authentik Embedded Outpost` → Edit → move all three proxy appl
- Name: `LiteLLM` - Name: `LiteLLM`
- Mode: `Reverse Proxy` - Mode: `Reverse Proxy`
- External host: `https://llm.kitestacks.com` - External host: `https://llm.kitestacks.com`
- Internal host: `http://kite-litellm:4000` - Internal host: `http://kite-litellm:<port>`
- **Applications → Create**: Name: `LiteLLM`, Slug: `litellm` - **Applications → Create**: Name: `LiteLLM`, Slug: `litellm`
- **Cloudflare Tunnel**: Add route `llm.kitestacks.com``http://authentik:9000` - **Cloudflare Tunnel**: Add route `llm.kitestacks.com``http://authentik:<port>`
--- ---
@ -205,10 +205,10 @@ In the Cloudflare Zero Trust Dashboard → Networks → Tunnels → your tunnel
| Hostname | Change From | Change To | | Hostname | Change From | Change To |
|----------|------------|-----------| |----------|------------|-----------|
| `links.kitestacks.com` | `http://shaarli:80` | `http://authentik:9000` | | `links.kitestacks.com` | `http://shaarli:<port>` | `http://authentik:<port>` |
| `status.kitestacks.com` | `http://uptime-kuma:3001` | `http://authentik:9000` | | `status.kitestacks.com` | `http://uptime-kuma:<port>` | `http://authentik:<port>` |
| `llm.kitestacks.com` | (new) | `http://authentik:9000` | | `llm.kitestacks.com` | (new) | `http://authentik:<port>` |
| `tasks.kitestacks.com` | `http://openproject:8080` | `http://openproject:80` *(after OpenProject upgrade)* | | `tasks.kitestacks.com` | `http://openproject:<port>` | `http://openproject:<port>` *(after OpenProject upgrade)* |
--- ---
@ -237,7 +237,7 @@ docker restart kavita
- The `appsettings.json` change requires a container restart: `docker restart kavita` - The `appsettings.json` change requires a container restart: `docker restart kavita`
- Verify `Enabled: true` and `Secret` is not empty. - Verify `Enabled: true` and `Secret` is not empty.
**BookStack redirects to `http://192.168.1.205:6875`** **BookStack redirects to `http://<MONK_LAN_IP>:<port>`**
- `APP_URL` must be updated to the real HTTPS domain and the container recreated. - `APP_URL` must be updated to the real HTTPS domain and the container recreated.
**OpenProject: OIDC provider not visible in login** **OpenProject: OIDC provider not visible in login**
@ -246,7 +246,7 @@ docker restart kavita
**Proxy services (Shaarli/Uptime Kuma) show Authentik login but then 502** **Proxy services (Shaarli/Uptime Kuma) show Authentik login but then 502**
- Verify the Embedded Outpost has the proxy providers assigned. - Verify the Embedded Outpost has the proxy providers assigned.
- Verify internal host is reachable: `docker exec authentik curl -v http://shaarli:80` - Verify internal host is reachable: `docker exec authentik curl -v http://shaarli:<port>`
--- ---
@ -254,16 +254,16 @@ docker restart kavita
| Subdomain | Container Name | Internal Port | | Subdomain | Container Name | Internal Port |
|-----------|---------------|--------------| |-----------|---------------|--------------|
| auth.kitestacks.com | authentik | 9000 | | auth.kitestacks.com | authentik | <port> |
| grafana.kitestacks.com | grafana | 3000 | | grafana.kitestacks.com | grafana | <port> |
| ai.kitestacks.com | kite-openwebui | 8080 | | ai.kitestacks.com | kite-openwebui | <port> |
| gitforge.kitestacks.com | forgejo | 3000 | | gitforge.kitestacks.com | forgejo | <port> |
| tasks.kitestacks.com | openproject | 80 (after upgrade) | | tasks.kitestacks.com | openproject | 80 (after upgrade) |
| kavita.kitestacks.com | kavita | 5000 | | kavita.kitestacks.com | kavita | <port> |
| links.kitestacks.com | shaarli | 80 | | links.kitestacks.com | shaarli | <port> |
| status.kitestacks.com | uptime-kuma | 3001 | | status.kitestacks.com | uptime-kuma | <port> |
| llm.kitestacks.com | kite-litellm | 4000 | | llm.kitestacks.com | kite-litellm | <port> |
| www.kitestacks.com | homepage | 3000 | | www.kitestacks.com | homepage | <port> |
| portainer.kitestacks.com | portainer | 9000 (excluded) | | portainer.kitestacks.com | portainer | 9000 (excluded) |
| prometheus.kitestacks.com | prometheus | 9090 (excluded) | | prometheus.kitestacks.com | prometheus | 9090 (excluded) |
| node-exporter.kitestacks.com | node-exporter | 9100 (excluded) | | node-exporter.kitestacks.com | node-exporter | 9100 (excluded) |

View file

@ -11,12 +11,12 @@ to stay up.
Primary Production: Primary Production:
- Host: monk - Host: monk
- LAN IP: 192.168.1.205 - LAN IP: <MONK_LAN_IP>
Cloud Failover (PERMANENT, active-active - NOT cold standby): Cloud Failover (PERMANENT, active-active - NOT cold standby):
- Host: kscloud1 (Hetzner VPS) - Host: kscloud1 (Hetzner VPS)
- Public IP: 5.78.233.28 - Public IP: <KSCLOUD1_PUBLIC_IP>
- Tailscale IP: 100.123.254.52 - Tailscale IP: <KSCLOUD1_TAILSCALE_IP>
- Runs a full replica of all 9 services - Runs a full replica of all 9 services
assassin (T14): retired/OFF, no longer part of the topology. assassin (T14): retired/OFF, no longer part of the topology.
@ -24,7 +24,7 @@ assassin (T14): retired/OFF, no longer part of the topology.
Domains: Domains:
- www.kitestacks.com (+ ai, auth, gitforge, grafana, kavita, links, status, tasks) - www.kitestacks.com (+ ai, auth, gitforge, grafana, kavita, links, status, tasks)
- www-backup.kitestacks.com / git-backup.kitestacks.com (kscloud1 direct - www-backup.kitestacks.com / git-backup.kitestacks.com (kscloud1 direct
A-records via local Caddy on port 80, separate from the Tunnel) A-records via local Caddy on port <port>, separate from the Tunnel)
Cloudflare Tunnel: Cloudflare Tunnel:
- 3 connectors load-balance ACTIVE-ACTIVE across all 9 *.kitestacks.com - 3 connectors load-balance ACTIVE-ACTIVE across all 9 *.kitestacks.com
@ -49,13 +49,13 @@ Forgejo:
commits pushed to monk's Forgejo do NOT appear on kscloud1's Forgejo (and commits pushed to monk's Forgejo do NOT appear on kscloud1's Forgejo (and
vice versa). Accepted tradeoff for uptime. vice versa). Accepted tradeoff for uptime.
- The portal's Recent Activity widget on BOTH hosts queries monk's Forgejo - The portal's Recent Activity widget on BOTH hosts queries monk's Forgejo
directly (FORGEJO_API_BASE -> http://100.85.209.116:3006 over Tailscale directly (FORGEJO_API_BASE -> http://<MONK_TAILSCALE_IP>:<port> over Tailscale
from kscloud1, http://localhost:3006 on monk) so it stays consistent from kscloud1, http://localhost:<port> on monk) so it stays consistent
regardless of which connector serves the page. regardless of which connector serves the page.
Authentik: Authentik:
- Shared Postgres+Redis hosted on kscloud1, reachable only over Tailscale - Shared Postgres+Redis hosted on kscloud1, reachable only over Tailscale
(100.123.254.52). Both monk's and kscloud1's authentik+worker use this (<KSCLOUD1_TAILSCALE_IP>). Both monk's and kscloud1's authentik+worker use this
single database/cache - fixes invalid_grant SSO caused by active-active single database/cache - fixes invalid_grant SSO caused by active-active
routing splitting an OAuth flow across connectors. routing splitting an OAuth flow across connectors.

View file

@ -9,7 +9,7 @@ Connect the KiteStacks website to the official KiteStacks Discord community.
Status: Implemented in test environment. Status: Implemented in test environment.
Test URL: Test URL:
http://192.168.1.205:3008 http://<MONK_LAN_IP>:<port>
Discord Invite: Discord Invite:
https://discord.gg/QbdveTb6Kw https://discord.gg/QbdveTb6Kw

View file

@ -6,7 +6,7 @@ Directory:
/home/kenpat/docker/kitestacks-portal-test /home/kenpat/docker/kitestacks-portal-test
URL: URL:
http://192.168.1.205:3008 http://<MONK_LAN_IP>:<port>
## Website Files Modified ## Website Files Modified