Add manual build path: step-by-step commands for all services
This commit is contained in:
parent
e5c119c9b4
commit
1d6c25225b
1 changed files with 620 additions and 0 deletions
620
build-guide/without-ai/README.md
Normal file
620
build-guide/without-ai/README.md
Normal file
|
|
@ -0,0 +1,620 @@
|
||||||
|
# Build KiteStacks Manually
|
||||||
|
|
||||||
|
This is the step-by-step, command-by-command build guide. Every file, every command, every setting. No AI. Build this from scratch on a blank Ubuntu 24.04 machine.
|
||||||
|
|
||||||
|
**Convention:** Replace `<REDACTED>` with your actual values. Replace `kitestacks.com` with your own domain.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Step 1 — Docker Engine
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Remove any old Docker installs
|
||||||
|
sudo apt remove docker docker-engine docker.io containerd runc
|
||||||
|
|
||||||
|
# Install dependencies
|
||||||
|
sudo apt update
|
||||||
|
sudo apt install -y ca-certificates curl gnupg lsb-release
|
||||||
|
|
||||||
|
# Add Docker's official GPG key
|
||||||
|
sudo install -m 0755 -d /etc/apt/keyrings
|
||||||
|
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | \
|
||||||
|
sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
|
||||||
|
sudo chmod a+r /etc/apt/keyrings/docker.gpg
|
||||||
|
|
||||||
|
# Add Docker repository
|
||||||
|
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \
|
||||||
|
https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | \
|
||||||
|
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
|
||||||
|
|
||||||
|
# Install Docker Engine + Compose v2
|
||||||
|
sudo apt update
|
||||||
|
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
|
||||||
|
|
||||||
|
# Add your user to docker group (re-login after this)
|
||||||
|
sudo usermod -aG docker $USER
|
||||||
|
|
||||||
|
# Create the shared network for all homelab containers
|
||||||
|
docker network create kitestacks
|
||||||
|
|
||||||
|
# Verify
|
||||||
|
docker run --rm hello-world
|
||||||
|
docker network ls | grep kitestacks
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Step 2 — Tailscale
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -fsSL https://tailscale.com/install.sh | sh
|
||||||
|
sudo tailscale up
|
||||||
|
# Follow the auth link printed in the terminal
|
||||||
|
tailscale status
|
||||||
|
```
|
||||||
|
|
||||||
|
Do the same on the cloud VPS when you get to Step 10.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Step 3 — Project Structure
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mkdir -p ~/kitestacks-live/docker/{authentik,bookstack,cloudflared,forgejo,grafana,karakeep,kavita,kitestacks-portal,osticket,portainer,prometheus}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Step 4 — Cloudflare Tunnel
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mkdir -p ~/kitestacks-live/docker/cloudflared
|
||||||
|
```
|
||||||
|
|
||||||
|
Create `~/kitestacks-live/docker/cloudflared/.env`:
|
||||||
|
```
|
||||||
|
TUNNEL_TOKEN=<REDACTED>
|
||||||
|
```
|
||||||
|
|
||||||
|
Create `~/kitestacks-live/docker/cloudflared/docker-compose.yml`:
|
||||||
|
```yaml
|
||||||
|
services:
|
||||||
|
cloudflared:
|
||||||
|
image: cloudflare/cloudflared:latest
|
||||||
|
container_name: cloudflared
|
||||||
|
restart: unless-stopped
|
||||||
|
command: tunnel --no-autoupdate run
|
||||||
|
environment:
|
||||||
|
- TUNNEL_TOKEN=${TUNNEL_TOKEN}
|
||||||
|
networks:
|
||||||
|
- kitestacks
|
||||||
|
|
||||||
|
networks:
|
||||||
|
kitestacks:
|
||||||
|
external: true
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd ~/kitestacks-live/docker/cloudflared
|
||||||
|
docker compose up -d
|
||||||
|
docker logs cloudflared --tail 20
|
||||||
|
```
|
||||||
|
|
||||||
|
In Cloudflare Zero Trust → Networks → Tunnels → your tunnel → Public Hostnames, add:
|
||||||
|
- `www.kitestacks.com` → `http://kitestacks-portal:80`
|
||||||
|
- `auth.kitestacks.com` → `http://authentik:9000`
|
||||||
|
(Add more subdomains as you deploy each service)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Step 5 — Authentik (SSO)
|
||||||
|
|
||||||
|
Authentik needs PostgreSQL and Redis. In this setup, both live on kscloud1 (Step 10). For a single-machine setup, run them locally.
|
||||||
|
|
||||||
|
Create `~/kitestacks-live/docker/authentik/docker-compose.yml`:
|
||||||
|
```yaml
|
||||||
|
services:
|
||||||
|
postgresql:
|
||||||
|
image: docker.io/library/postgres:16-alpine
|
||||||
|
restart: unless-stopped
|
||||||
|
container_name: authentik-postgresql
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD-SHELL", "pg_isready -d ${POSTGRES_DB} -U ${POSTGRES_USER}"]
|
||||||
|
start_period: 20s
|
||||||
|
interval: 30s
|
||||||
|
retries: 5
|
||||||
|
timeout: 5s
|
||||||
|
volumes:
|
||||||
|
- database:/var/lib/postgresql/data
|
||||||
|
environment:
|
||||||
|
POSTGRES_PASSWORD: ${PG_PASS}
|
||||||
|
POSTGRES_USER: ${PG_USER}
|
||||||
|
POSTGRES_DB: ${PG_DB}
|
||||||
|
networks:
|
||||||
|
- kitestacks
|
||||||
|
|
||||||
|
redis:
|
||||||
|
image: docker.io/library/redis:alpine
|
||||||
|
restart: unless-stopped
|
||||||
|
container_name: authentik-redis
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD-SHELL", "redis-cli ping | grep PONG"]
|
||||||
|
start_period: 20s
|
||||||
|
interval: 30s
|
||||||
|
retries: 5
|
||||||
|
timeout: 3s
|
||||||
|
networks:
|
||||||
|
- kitestacks
|
||||||
|
|
||||||
|
server:
|
||||||
|
image: ghcr.io/goauthentik/server:2024.12.3
|
||||||
|
restart: unless-stopped
|
||||||
|
container_name: authentik
|
||||||
|
command: server
|
||||||
|
environment:
|
||||||
|
AUTHENTIK_REDIS__HOST: redis
|
||||||
|
AUTHENTIK_POSTGRESQL__HOST: postgresql
|
||||||
|
AUTHENTIK_POSTGRESQL__USER: ${PG_USER}
|
||||||
|
AUTHENTIK_POSTGRESQL__NAME: ${PG_DB}
|
||||||
|
AUTHENTIK_POSTGRESQL__PASSWORD: ${PG_PASS}
|
||||||
|
AUTHENTIK_SECRET_KEY: ${AUTHENTIK_SECRET_KEY}
|
||||||
|
volumes:
|
||||||
|
- ./media:/media
|
||||||
|
- ./custom-templates:/templates
|
||||||
|
depends_on:
|
||||||
|
- postgresql
|
||||||
|
- redis
|
||||||
|
networks:
|
||||||
|
- kitestacks
|
||||||
|
|
||||||
|
worker:
|
||||||
|
image: ghcr.io/goauthentik/server:2024.12.3
|
||||||
|
restart: unless-stopped
|
||||||
|
container_name: authentik-worker
|
||||||
|
command: worker
|
||||||
|
environment:
|
||||||
|
AUTHENTIK_REDIS__HOST: redis
|
||||||
|
AUTHENTIK_POSTGRESQL__HOST: postgresql
|
||||||
|
AUTHENTIK_POSTGRESQL__USER: ${PG_USER}
|
||||||
|
AUTHENTIK_POSTGRESQL__NAME: ${PG_DB}
|
||||||
|
AUTHENTIK_POSTGRESQL__PASSWORD: ${PG_PASS}
|
||||||
|
AUTHENTIK_SECRET_KEY: ${AUTHENTIK_SECRET_KEY}
|
||||||
|
volumes:
|
||||||
|
- /var/run/docker.sock:/var/run/docker.sock
|
||||||
|
- ./media:/media
|
||||||
|
- ./custom-templates:/templates
|
||||||
|
depends_on:
|
||||||
|
- postgresql
|
||||||
|
- redis
|
||||||
|
networks:
|
||||||
|
- kitestacks
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
database:
|
||||||
|
|
||||||
|
networks:
|
||||||
|
kitestacks:
|
||||||
|
external: true
|
||||||
|
```
|
||||||
|
|
||||||
|
Create `~/kitestacks-live/docker/authentik/.env`:
|
||||||
|
```
|
||||||
|
PG_PASS=<REDACTED>
|
||||||
|
PG_USER=authentik
|
||||||
|
PG_DB=authentik
|
||||||
|
AUTHENTIK_SECRET_KEY=<REDACTED>
|
||||||
|
```
|
||||||
|
|
||||||
|
Generate the secret key:
|
||||||
|
```bash
|
||||||
|
openssl rand -hex 32
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd ~/kitestacks-live/docker/authentik
|
||||||
|
docker compose up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
Browse to `https://auth.kitestacks.com/if/flow/initial-setup/` to complete setup.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Step 6 — Portainer
|
||||||
|
|
||||||
|
Create `~/kitestacks-live/docker/portainer/docker-compose.yml`:
|
||||||
|
```yaml
|
||||||
|
services:
|
||||||
|
portainer:
|
||||||
|
image: portainer/portainer-ce:latest
|
||||||
|
container_name: portainer
|
||||||
|
restart: unless-stopped
|
||||||
|
volumes:
|
||||||
|
- /var/run/docker.sock:/var/run/docker.sock
|
||||||
|
- portainer_data:/data
|
||||||
|
networks:
|
||||||
|
- kitestacks
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
portainer_data:
|
||||||
|
|
||||||
|
networks:
|
||||||
|
kitestacks:
|
||||||
|
external: true
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd ~/kitestacks-live/docker/portainer
|
||||||
|
docker compose up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
Add Cloudflare Tunnel route: `portainer.kitestacks.com` → `https://portainer:9443`
|
||||||
|
|
||||||
|
In Portainer UI → Settings → Authentication → OAuth:
|
||||||
|
- Client ID: `portainer` (from Authentik provider)
|
||||||
|
- Auth URL: `https://auth.kitestacks.com/application/o/authorize/`
|
||||||
|
- Token URL: `https://auth.kitestacks.com/application/o/token/`
|
||||||
|
- Resource URL: `https://auth.kitestacks.com/application/o/userinfo/`
|
||||||
|
- Redirect URL: `https://portainer.kitestacks.com`
|
||||||
|
|
||||||
|
Pre-create your Authentik user as admin before their first login:
|
||||||
|
```bash
|
||||||
|
TOKEN=$(curl -sk -X POST https://portainer.kitestacks.com/api/auth \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{"username":"admin","password":"<REDACTED>"}' | python3 -c \
|
||||||
|
"import sys,json; print(json.load(sys.stdin)['jwt'])")
|
||||||
|
|
||||||
|
curl -sk -X POST "https://portainer.kitestacks.com/api/users" \
|
||||||
|
-H "Authorization: Bearer $TOKEN" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{"username":"user@example.com","role":1}'
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Step 7 — Forgejo
|
||||||
|
|
||||||
|
Create `~/kitestacks-live/docker/forgejo/docker-compose.yml`:
|
||||||
|
```yaml
|
||||||
|
services:
|
||||||
|
forgejo:
|
||||||
|
image: codeberg.org/forgejo/forgejo:11
|
||||||
|
container_name: forgejo
|
||||||
|
restart: unless-stopped
|
||||||
|
volumes:
|
||||||
|
- ./data:/data
|
||||||
|
- /etc/timezone:/etc/timezone:ro
|
||||||
|
- /etc/localtime:/etc/localtime:ro
|
||||||
|
ports:
|
||||||
|
- "2222:22"
|
||||||
|
- "3006:3000"
|
||||||
|
networks:
|
||||||
|
- kitestacks
|
||||||
|
|
||||||
|
networks:
|
||||||
|
kitestacks:
|
||||||
|
external: true
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd ~/kitestacks-live/docker/forgejo
|
||||||
|
docker compose up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
Browse to `http://localhost:3006` to complete initial setup.
|
||||||
|
|
||||||
|
Add CF Tunnel route: `gitforge.kitestacks.com` → `http://forgejo:3000`
|
||||||
|
|
||||||
|
Set up SSH config (`~/.ssh/config`):
|
||||||
|
```
|
||||||
|
Host gitforge.kitestacks.com
|
||||||
|
HostName gitforge.kitestacks.com
|
||||||
|
Port 2222
|
||||||
|
User git
|
||||||
|
IdentityFile ~/.ssh/id_ed25519
|
||||||
|
```
|
||||||
|
|
||||||
|
Generate API token:
|
||||||
|
```bash
|
||||||
|
docker exec -u git forgejo forgejo admin user generate-access-token \
|
||||||
|
--username <your-username> --token-name "cli-token" --raw \
|
||||||
|
--scopes "read:user,write:user,read:repository,write:repository"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Step 8 — BookStack
|
||||||
|
|
||||||
|
Create `~/kitestacks-live/docker/bookstack/docker-compose.yml`:
|
||||||
|
```yaml
|
||||||
|
services:
|
||||||
|
bookstack:
|
||||||
|
image: lscr.io/linuxserver/bookstack:latest
|
||||||
|
container_name: bookstack
|
||||||
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
- PUID=1000
|
||||||
|
- PGID=1000
|
||||||
|
- APP_URL=https://wiki.kitestacks.com
|
||||||
|
- DB_HOST=bookstack-db
|
||||||
|
- DB_PORT=3306
|
||||||
|
- DB_USER=bookstack
|
||||||
|
- DB_PASS=<REDACTED>
|
||||||
|
- DB_DATABASE=bookstackapp
|
||||||
|
- AUTH_METHOD=oidc
|
||||||
|
- OIDC_ISSUER=https://auth.kitestacks.com/application/o/bookstack/
|
||||||
|
- OIDC_ISSUER_DISCOVER=true
|
||||||
|
- OIDC_CLIENT_ID=bookstack
|
||||||
|
- OIDC_CLIENT_SECRET=<REDACTED>
|
||||||
|
- OIDC_USER_ATTRIBUTE=email
|
||||||
|
- APP_KEY=<REDACTED>
|
||||||
|
volumes:
|
||||||
|
- ./config:/config
|
||||||
|
ports:
|
||||||
|
- "6875:80"
|
||||||
|
depends_on:
|
||||||
|
- bookstack-db
|
||||||
|
networks:
|
||||||
|
- kitestacks
|
||||||
|
|
||||||
|
bookstack-db:
|
||||||
|
image: lscr.io/linuxserver/mariadb:latest
|
||||||
|
container_name: bookstack-db
|
||||||
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
- PUID=1000
|
||||||
|
- PGID=1000
|
||||||
|
- MYSQL_ROOT_PASSWORD=<REDACTED>
|
||||||
|
- MYSQL_DATABASE=bookstackapp
|
||||||
|
- MYSQL_USER=bookstack
|
||||||
|
- MYSQL_PASSWORD=<REDACTED>
|
||||||
|
volumes:
|
||||||
|
- ./db:/config
|
||||||
|
networks:
|
||||||
|
- kitestacks
|
||||||
|
|
||||||
|
networks:
|
||||||
|
kitestacks:
|
||||||
|
external: true
|
||||||
|
```
|
||||||
|
|
||||||
|
Generate APP_KEY:
|
||||||
|
```bash
|
||||||
|
docker run --rm --entrypoint /bin/bash lscr.io/linuxserver/bookstack:latest appkey
|
||||||
|
```
|
||||||
|
|
||||||
|
In Authentik, create an OAuth2 Provider for BookStack:
|
||||||
|
- Name: `bookstack`
|
||||||
|
- Redirect URIs: `https://wiki.kitestacks.com/oidc/callback`
|
||||||
|
- `issuer_mode`: `per_provider`
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd ~/kitestacks-live/docker/bookstack
|
||||||
|
docker compose up -d
|
||||||
|
|
||||||
|
# Fix cache permissions (prevents "unknown error" on OIDC login)
|
||||||
|
docker exec bookstack chown -R abc:users /config/www/framework/cache/
|
||||||
|
```
|
||||||
|
|
||||||
|
Add CF Tunnel route: `wiki.kitestacks.com` → `http://bookstack:80`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Step 9 — Monitoring
|
||||||
|
|
||||||
|
### Prometheus + Node Exporter
|
||||||
|
|
||||||
|
Create `~/kitestacks-live/docker/prometheus/docker-compose.yml`:
|
||||||
|
```yaml
|
||||||
|
services:
|
||||||
|
prometheus:
|
||||||
|
image: prom/prometheus:latest
|
||||||
|
container_name: prometheus
|
||||||
|
restart: unless-stopped
|
||||||
|
volumes:
|
||||||
|
- ./prometheus.yml:/etc/prometheus/prometheus.yml
|
||||||
|
- prometheus_data:/prometheus
|
||||||
|
networks:
|
||||||
|
- kitestacks
|
||||||
|
|
||||||
|
node-exporter:
|
||||||
|
image: prom/node-exporter:latest
|
||||||
|
container_name: node-exporter
|
||||||
|
restart: unless-stopped
|
||||||
|
network_mode: host
|
||||||
|
pid: host
|
||||||
|
volumes:
|
||||||
|
- /:/host:ro,rslave
|
||||||
|
command:
|
||||||
|
- '--path.rootfs=/host'
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
prometheus_data:
|
||||||
|
|
||||||
|
networks:
|
||||||
|
kitestacks:
|
||||||
|
external: true
|
||||||
|
```
|
||||||
|
|
||||||
|
Create `~/kitestacks-live/docker/prometheus/prometheus.yml`:
|
||||||
|
```yaml
|
||||||
|
global:
|
||||||
|
scrape_interval: 15s
|
||||||
|
|
||||||
|
scrape_configs:
|
||||||
|
- job_name: 'node-monk'
|
||||||
|
static_configs:
|
||||||
|
- targets: ['localhost:9100']
|
||||||
|
|
||||||
|
- job_name: 'node-kscloud1'
|
||||||
|
static_configs:
|
||||||
|
- targets: ['<KSCLOUD1_TAILSCALE_IP>:9100']
|
||||||
|
```
|
||||||
|
|
||||||
|
### Grafana
|
||||||
|
|
||||||
|
Create `~/kitestacks-live/docker/grafana/docker-compose.yml`:
|
||||||
|
```yaml
|
||||||
|
services:
|
||||||
|
grafana:
|
||||||
|
image: grafana/grafana:latest
|
||||||
|
container_name: grafana
|
||||||
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
- GF_SERVER_ROOT_URL=https://grafana.kitestacks.com
|
||||||
|
- GF_AUTH_GENERIC_OAUTH_ENABLED=true
|
||||||
|
- GF_AUTH_GENERIC_OAUTH_NAME=Authentik
|
||||||
|
- GF_AUTH_GENERIC_OAUTH_CLIENT_ID=grafana
|
||||||
|
- GF_AUTH_GENERIC_OAUTH_CLIENT_SECRET=<REDACTED>
|
||||||
|
- 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_TOKEN_URL=https://auth.kitestacks.com/application/o/token/
|
||||||
|
- GF_AUTH_GENERIC_OAUTH_API_URL=https://auth.kitestacks.com/application/o/userinfo/
|
||||||
|
volumes:
|
||||||
|
- grafana_data:/var/lib/grafana
|
||||||
|
networks:
|
||||||
|
- kitestacks
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
grafana_data:
|
||||||
|
|
||||||
|
networks:
|
||||||
|
kitestacks:
|
||||||
|
external: true
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd ~/kitestacks-live/docker/grafana
|
||||||
|
docker compose up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
After first login: Configuration → Data Sources → Add Prometheus → URL `http://prometheus:9090`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Step 10 — Cloud Replica (kscloud1)
|
||||||
|
|
||||||
|
### On the VPS
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Install Docker (same commands as Step 1)
|
||||||
|
# Install Tailscale (same as Step 2)
|
||||||
|
# Create project directories
|
||||||
|
mkdir -p /opt/kitestacks/docker/{authentik,bookstack,cloudflared,grafana,portainer}
|
||||||
|
|
||||||
|
# Copy docker-compose files from monk via SCP or git
|
||||||
|
# Example for cloudflared:
|
||||||
|
scp ~/kitestacks-live/docker/cloudflared/docker-compose.yml root@<KSCLOUD1>:/opt/kitestacks/docker/cloudflared/
|
||||||
|
```
|
||||||
|
|
||||||
|
### Move Authentik DB to kscloud1
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# On kscloud1: bring up just postgres and redis from authentik compose
|
||||||
|
cd /opt/kitestacks/docker/authentik
|
||||||
|
docker compose up -d postgresql redis
|
||||||
|
|
||||||
|
# On monk: update Authentik env to point to kscloud1 Tailscale IP
|
||||||
|
# AUTHENTIK_REDIS__HOST: <KSCLOUD1_TAILSCALE_IP>
|
||||||
|
# AUTHENTIK_POSTGRESQL__HOST: <KSCLOUD1_TAILSCALE_IP>
|
||||||
|
docker compose up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
### Add second cloudflared connector on kscloud1
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Same TUNNEL_TOKEN as monk
|
||||||
|
cd /opt/kitestacks/docker/cloudflared
|
||||||
|
docker compose up -d
|
||||||
|
|
||||||
|
# Verify 2 connectors in Cloudflare Zero Trust
|
||||||
|
docker exec cloudflared cloudflared tunnel info <TUNNEL_ID>
|
||||||
|
```
|
||||||
|
|
||||||
|
### Test failover
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Stop all services on monk
|
||||||
|
docker stop $(docker ps -q)
|
||||||
|
|
||||||
|
# Browse to https://wiki.kitestacks.com from a browser — kscloud1 should serve it
|
||||||
|
# Check Cloudflare analytics for which connector is active
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Step 11 — OSTicket (Help Desk)
|
||||||
|
|
||||||
|
Create `~/kitestacks-live/docker/osticket/docker-compose.yml`:
|
||||||
|
```yaml
|
||||||
|
services:
|
||||||
|
osticket-db:
|
||||||
|
image: mariadb:11
|
||||||
|
container_name: osticket-db
|
||||||
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
- MYSQL_ROOT_PASSWORD=<REDACTED>
|
||||||
|
- MYSQL_DATABASE=osticket
|
||||||
|
- MYSQL_USER=osticket
|
||||||
|
- MYSQL_PASSWORD=<REDACTED>
|
||||||
|
volumes:
|
||||||
|
- ./db:/var/lib/mysql
|
||||||
|
networks:
|
||||||
|
- kitestacks
|
||||||
|
|
||||||
|
osticket:
|
||||||
|
image: ghcr.io/tiredofit/docker-osticket:latest
|
||||||
|
container_name: osticket-app
|
||||||
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
- DB_HOST=osticket-db
|
||||||
|
- DB_NAME=osticket
|
||||||
|
- DB_USER=osticket
|
||||||
|
- DB_PASS=<REDACTED>
|
||||||
|
- SMTP_HOST=smtp.gmail.com
|
||||||
|
- SMTP_PORT=587
|
||||||
|
- SMTP_FROM=kitestacks.helpdesk@gmail.com
|
||||||
|
- SMTP_USER=kitestacks.helpdesk@gmail.com
|
||||||
|
- SMTP_PASS=<REDACTED>
|
||||||
|
- SMTP_TLS=true
|
||||||
|
depends_on:
|
||||||
|
- osticket-db
|
||||||
|
networks:
|
||||||
|
- kitestacks
|
||||||
|
|
||||||
|
networks:
|
||||||
|
kitestacks:
|
||||||
|
external: true
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Reference: Useful Commands
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Check all running containers
|
||||||
|
docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"
|
||||||
|
|
||||||
|
# Follow logs for a service
|
||||||
|
docker logs <container> --tail 50 -f
|
||||||
|
|
||||||
|
# Restart a service
|
||||||
|
cd ~/kitestacks-live/docker/<service> && docker compose restart
|
||||||
|
|
||||||
|
# Pull latest image and redeploy
|
||||||
|
docker compose pull && docker compose up -d
|
||||||
|
|
||||||
|
# BookStack: clear config cache
|
||||||
|
docker exec bookstack php /app/www/artisan config:clear
|
||||||
|
docker exec bookstack php /app/www/artisan cache:clear
|
||||||
|
|
||||||
|
# Portainer: reset admin password
|
||||||
|
docker stop portainer
|
||||||
|
docker run --rm -v portainer_data:/data portainer/helper-reset-password
|
||||||
|
docker start portainer
|
||||||
|
|
||||||
|
# Check Tailscale connectivity
|
||||||
|
tailscale status
|
||||||
|
|
||||||
|
# SSH to kscloud1
|
||||||
|
ssh -i ~/.ssh/id_ed25519_kscloud1 root@<KSCLOUD1_TAILSCALE_IP>
|
||||||
|
```
|
||||||
Reference in a new issue