Reorganize repos into kitestacks-homelab + plain-English doc rewrite
- Rewrote RUNBOOK.md and DEBUG-DOCUMENTATION.md in simple 5th-grade language with real-world analogies for every technical concept - Updated README.md with current service inventory and folder map - Added cloud-migration/ subdirectory (from kitestacks-cloud-migration repo) - Added autosync/ subdirectory (from kitestacks-homelab-autosync-test repo) - Added osticket/ subdirectory (from OSTicketSystem repo) - Added cloud/ placeholder for future cloud configs - Excluded binary DB/postgres files from autosync subdirectory Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
f79478158d
commit
fb822d5142
75 changed files with 11711 additions and 338 deletions
|
|
@ -1,121 +1,120 @@
|
|||
# KiteStacks Homelab — Debug Documentation
|
||||
# KiteStacks Homelab — Problems We've Seen and How We Fixed Them
|
||||
|
||||
All known incidents, root causes, and fixes. Most recent first.
|
||||
Newest problems at the top.
|
||||
|
||||
---
|
||||
|
||||
## 2026-06-18 — kscloud1 SSH Key Lost / Cannot SSH
|
||||
## 2026-06-18 — Can't SSH into kscloud1
|
||||
|
||||
**Symptom:** `Permission denied (publickey,password)` connecting to kscloud1.
|
||||
**What happened:** Trying to connect to the cloud machine (kscloud1) gave a
|
||||
"Permission denied" error. The SSH key was missing from the machine.
|
||||
|
||||
**Root cause:** SSH public key was removed from kscloud1's `authorized_keys`.
|
||||
**How we found it:** The error message said `publickey,password` — meaning it tried
|
||||
the SSH key first and then tried a password, both failed.
|
||||
|
||||
**Fix:**
|
||||
1. Open Hetzner Cloud console → VNC terminal → log in as `root`
|
||||
2. On monk, serve the public key temporarily:
|
||||
**How we fixed it:**
|
||||
1. Used Hetzner's browser console (like a TV remote for the server) to log in as root
|
||||
2. Served the SSH public key from monk as a temporary download:
|
||||
```bash
|
||||
# On monk — share the key file over a mini web server
|
||||
cat ~/.ssh/id_ed25519_kscloud1.pub > ~/key.txt
|
||||
python3 -m http.server 7777 --directory ~/
|
||||
```
|
||||
3. In Hetzner console, type:
|
||||
3. Downloaded it from the Hetzner console:
|
||||
```bash
|
||||
curl http://<MONK_TAILSCALE_IP>:7777/key.txt > /root/.ssh/authorized_keys
|
||||
curl http://MONK_TAILSCALE_IP:7777/key.txt > /root/.ssh/authorized_keys
|
||||
```
|
||||
4. If root SSH login was disabled:
|
||||
4. If the machine had root SSH login disabled:
|
||||
```bash
|
||||
sed -i 's/^#*PermitRootLogin.*/PermitRootLogin prohibit-password/' /etc/ssh/sshd_config
|
||||
systemctl restart ssh
|
||||
```
|
||||
|
||||
**Note:** Hetzner VNC console does not support clipboard paste for long strings.
|
||||
Serving the key via HTTP from monk's Tailscale IP is the reliable workaround.
|
||||
**Why this works:** The Hetzner console bypasses SSH entirely — it's like plugging a
|
||||
keyboard and monitor directly into the server. So even when SSH is broken, you can still
|
||||
type commands.
|
||||
|
||||
---
|
||||
|
||||
## 2026-06-18 — BookStack SSO "An Error Occurred / An unknown error occurred"
|
||||
## 2026-06-18 — BookStack Login Said "An Error Occurred"
|
||||
|
||||
**Symptom:** Clicking "Login with authentik" on BookStack shows a generic error page.
|
||||
No stack trace even with `APP_DEBUG=true`. `laravel.log` is 0 bytes.
|
||||
**What happened:** Clicking "Login with Authentik" on the wiki showed a generic error.
|
||||
No details, no clues — just "An unknown error occurred."
|
||||
|
||||
**Root cause (3 compounding issues):**
|
||||
**Why it happened (three problems at once):**
|
||||
|
||||
**Issue 1 — Wrong `OIDC_ISSUER_DISCOVER` default**
|
||||
BookStack defaults to `OIDC_ISSUER_DISCOVER=false`. Without it set to `true`, BookStack
|
||||
does not auto-discover endpoints from Authentik and cannot verify JWT tokens.
|
||||
**Problem 1 — Missing setting in BookStack**
|
||||
BookStack needs `OIDC_ISSUER_DISCOVER=true` to automatically find all the login
|
||||
endpoints from Authentik. Without it, BookStack can't verify login tokens.
|
||||
|
||||
**Issue 2 — Authentik `issuer_mode=global` breaks discovery**
|
||||
When `OIDC_ISSUER=https://auth.kitestacks.com/` (the global URL), BookStack tries to
|
||||
fetch the discovery doc at `https://auth.kitestacks.com/.well-known/openid-configuration`.
|
||||
Authentik's global URL returns an HTML login page, not JSON.
|
||||
The app crashes silently trying to parse HTML as JSON.
|
||||
**Problem 2 — Authentik was using the wrong login URL format**
|
||||
Authentik can either use one shared URL for all apps or a unique URL per app.
|
||||
BookStack expects a per-app URL. When the wrong type was set, BookStack tried to
|
||||
download login instructions from a URL that returned an HTML page instead of data,
|
||||
and then crashed trying to read it.
|
||||
|
||||
**Issue 3 — Root-owned cache directory blocks write**
|
||||
Running `php artisan` commands inside the container as root creates cache subdirectories
|
||||
owned by `root:root`. BookStack's PHP process runs as `abc` (UID 1000) and cannot write
|
||||
to these directories, causing a `Permission denied` on the first OIDC login attempt.
|
||||
This exception is caught by BookStack's generic handler → "An unknown error occurred".
|
||||
**Problem 3 — File permission error hidden by BookStack**
|
||||
Running a setup command inside the BookStack container as root created some folders
|
||||
that only root could write to. When the normal BookStack process tried to save
|
||||
a login session, it couldn't — and BookStack showed a generic error instead of
|
||||
the real one.
|
||||
|
||||
**Fix:**
|
||||
**How we fixed it:**
|
||||
|
||||
Step 1 — Change Authentik bookstack provider to `per_provider` issuer mode:
|
||||
Step 1 — Change Authentik to use per-app URLs (run this once):
|
||||
```bash
|
||||
docker run --rm --network host \
|
||||
-e PGPASSWORD="<REDACTED>" \
|
||||
postgres:16 psql -h <KSCLOUD1_TAILSCALE_IP> -U authentik authentik -c \
|
||||
"UPDATE authentik_providers_oauth2_oauth2provider SET issuer_mode='per_provider' WHERE provider_ptr_id=<ID>;"
|
||||
-e PGPASSWORD="YOUR_DB_PASSWORD" \
|
||||
postgres:16 psql -h KSCLOUD1_TAILSCALE_IP -U authentik authentik -c \
|
||||
"UPDATE authentik_providers_oauth2_oauth2provider SET issuer_mode='per_provider' WHERE provider_ptr_id=PROVIDER_ID;"
|
||||
```
|
||||
|
||||
Step 2 — Update BookStack compose env vars:
|
||||
```yaml
|
||||
- OIDC_ISSUER=https://auth.kitestacks.com/application/o/bookstack/
|
||||
- OIDC_ISSUER_DISCOVER=true
|
||||
Step 2 — Make sure BookStack's settings include:
|
||||
```
|
||||
OIDC_ISSUER=https://auth.kitestacks.com/application/o/bookstack/
|
||||
OIDC_ISSUER_DISCOVER=true
|
||||
```
|
||||
|
||||
Step 3 — Fix cache permissions:
|
||||
Step 3 — Fix the file permission problem:
|
||||
```bash
|
||||
docker exec bookstack chown -R abc:users /config/www/framework/cache/
|
||||
```
|
||||
|
||||
Step 4 — Restart BookStack and test:
|
||||
Step 4 — Restart BookStack:
|
||||
```bash
|
||||
docker compose up -d
|
||||
# Verify OIDC redirect works
|
||||
curl -sc /tmp/c.txt http://localhost:6875/login -o /tmp/l.html
|
||||
CSRF=$(grep -oP 'name="_token" value="\K[^"]+' /tmp/l.html | head -1)
|
||||
curl -v -b /tmp/c.txt -X POST http://localhost:6875/oidc/login -d "_token=$CSRF" --max-redirs 0 2>&1 | grep "Location:"
|
||||
# Should show: Location: https://auth.kitestacks.com/application/o/authorize/?...
|
||||
```
|
||||
|
||||
**Key insight:** When Authentik's `issuer_mode=per_provider`, the discovery doc at
|
||||
`https://auth.kitestacks.com/application/o/bookstack/.well-known/openid-configuration`
|
||||
returns `issuer: https://auth.kitestacks.com/application/o/bookstack/` — this must match
|
||||
`OIDC_ISSUER` exactly for JWT validation to pass.
|
||||
|
||||
---
|
||||
|
||||
## 2026-06-18 — Portainer OAuth Users Can't See Environments
|
||||
## 2026-06-18 — Portainer OAuth Login Couldn't See Any Servers
|
||||
|
||||
**Symptom:** After logging in via Authentik SSO, Portainer shows no environments.
|
||||
**What happened:** Logged in through Authentik, got into Portainer, but no environments
|
||||
(no servers, nothing to manage) were visible.
|
||||
|
||||
**Root cause:** Portainer CE creates OAuth users as Role:2 (regular user). Regular users
|
||||
have no access to environments by default — only admins do.
|
||||
**Why it happened:** Portainer creates new SSO users as "regular users." Regular users
|
||||
can't see environments — only admins can. The fix is to create the user as an admin
|
||||
**before** they log in for the first time.
|
||||
|
||||
**Fix:** Pre-create the OAuth user as Role:1 (admin) via API *before* their first login:
|
||||
**How we fixed it:**
|
||||
|
||||
Create the user as admin before first login:
|
||||
```bash
|
||||
# Get a temporary auth token
|
||||
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'])")
|
||||
-d '{"username":"admin","password":"YOUR_PASSWORD"}' | python3 -c "import sys,json; print(json.load(sys.stdin)['jwt'])")
|
||||
|
||||
# Note: do NOT include "Password" field for OAuth users
|
||||
# Create the user with admin role (role 1 = admin)
|
||||
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}'
|
||||
```
|
||||
|
||||
If the user already logged in as Role:2, promote them via API:
|
||||
If they already logged in as a regular user, promote them:
|
||||
```bash
|
||||
curl -sk -X PUT "https://portainer.kitestacks.com/api/users/<USER_ID>" \
|
||||
curl -sk -X PUT "https://portainer.kitestacks.com/api/users/USER_ID" \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"role":1}'
|
||||
|
|
@ -123,110 +122,88 @@ curl -sk -X PUT "https://portainer.kitestacks.com/api/users/<USER_ID>" \
|
|||
|
||||
---
|
||||
|
||||
## 2026-06-17 — Cloudflare Tunnel Phantom 3rd Connector
|
||||
## 2026-06-17 — Three Cloudflare Connectors Instead of Two
|
||||
|
||||
**Symptom:** `cloudflared tunnel info` shows 3 connectors instead of 2.
|
||||
Authentik OAuth codes fail with `invalid_grant` intermittently.
|
||||
**What happened:** The Cloudflare dashboard was showing 3 tunnel connectors when there
|
||||
should only be 2 (one from monk, one from kscloud1). This caused Authentik logins to
|
||||
fail randomly — about half the time, the code from the login form would reach the wrong
|
||||
connector and get rejected.
|
||||
|
||||
**Root cause:** The native cloudflared systemd service on monk was running alongside
|
||||
the Docker container — two connectors from the same host, causing session/auth split.
|
||||
**Why it happened:** The system's built-in cloudflared service was still running on monk,
|
||||
alongside the Docker container version. So monk was connecting to Cloudflare twice.
|
||||
|
||||
**Fix:**
|
||||
**How we fixed it:**
|
||||
```bash
|
||||
sudo systemctl disable --now cloudflared
|
||||
```
|
||||
|
||||
Verify only 2 connectors remain in Cloudflare Zero Trust → Networks → Tunnels.
|
||||
That stopped the duplicate. Now only the Docker container runs.
|
||||
|
||||
**Also fixed:** Authentik OAuth2 code TTL bumped from 1 min → 10 min to tolerate
|
||||
reconnect windows when monk comes back online.
|
||||
After fixing: verified only 2 connectors in Cloudflare Zero Trust → Networks → Tunnels.
|
||||
|
||||
---
|
||||
|
||||
## 2026-06-17 — BookStack MariaDB Crash Loop ("Table 'mysql.db' doesn't exist")
|
||||
## 2026-06-17 — BookStack Database Kept Crashing
|
||||
|
||||
**Symptom:** `bookstack-db` container in crash loop, logs show:
|
||||
`Table 'mysql.db' doesn't exist`
|
||||
**What happened:** The BookStack database container (bookstack-db) kept restarting
|
||||
and never stayed running. Logs showed: `Table 'mysql.db' doesn't exist`
|
||||
|
||||
**Root cause:** Stale/corrupt data in `./db/` from a previous partial MariaDB initialization.
|
||||
**Why it happened:** The database's data folder had leftover files from a previous
|
||||
incomplete setup. When MariaDB started, it saw partial old data and crashed trying
|
||||
to use it.
|
||||
|
||||
**Fix:** Wipe the data directory (files are root-owned inside the container):
|
||||
**How we fixed it:**
|
||||
```bash
|
||||
# Wipe the broken database files (they're owned by root inside the container)
|
||||
docker run --rm -v $(pwd)/db:/db alpine sh -c 'rm -rf /db/*'
|
||||
|
||||
# Start fresh
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2026-06-17 — BookStack "Name does not resolve" for bookstack-db
|
||||
## 2026-06-17 — BookStack Said It Couldn't Find the Database
|
||||
|
||||
**Symptom:** BookStack Laravel log shows DB hostname resolution failure on first boot.
|
||||
**What happened:** BookStack started but immediately errored saying it couldn't connect
|
||||
to the database (bookstack-db).
|
||||
|
||||
**Root cause:** Race condition — BookStack ran DB migrations before MariaDB was fully
|
||||
initialized and registered with Docker's embedded DNS (127.0.0.11).
|
||||
**Why it happened:** BookStack was too fast. It started before the database was fully
|
||||
ready, and when it tried to find `bookstack-db` on the internal network, Docker hadn't
|
||||
finished registering it yet.
|
||||
|
||||
**Fix:** Wait for `bookstack-db` to be healthy, then restart the BookStack container:
|
||||
**How we fixed it:**
|
||||
```bash
|
||||
# Just wait a few seconds and restart BookStack
|
||||
docker restart bookstack
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2026-06-09 — Root CHANGELOG Permission Issue
|
||||
|
||||
**Symptom:** CHANGELOG.md could not be read/written by the normal user.
|
||||
|
||||
**Root cause:** CHANGELOG.md was owned by root with 600 permissions.
|
||||
|
||||
**Fix:**
|
||||
```bash
|
||||
sudo chown kenpat:kenpat CHANGELOG.md
|
||||
chmod 644 CHANGELOG.md
|
||||
```
|
||||
That's it — the database had finished starting up by then.
|
||||
|
||||
---
|
||||
|
||||
## 2026-06-09 — Repo Folder Ownership Issue
|
||||
|
||||
**Symptom:** Could not create new files in the kitestacks-homelab repo directory.
|
||||
|
||||
**Root cause:** Repo root folder was owned by root.
|
||||
|
||||
**Fix:**
|
||||
```bash
|
||||
sudo chown -R kenpat:kenpat /opt/kitestacks-autosync/kitestacks-homelab
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Diagnostic Quick Reference
|
||||
## Quick Diagnostic Commands
|
||||
|
||||
```bash
|
||||
# Check which container is causing issues
|
||||
# See which containers are running (and which are crashing)
|
||||
docker ps --format "table {{.Names}}\t{{.Status}}"
|
||||
|
||||
# Tail any service log
|
||||
docker logs <container> --tail 50 -f
|
||||
# Follow the live logs of any service
|
||||
docker logs CONTAINER_NAME --tail 50 -f
|
||||
|
||||
# BookStack PHP log
|
||||
# Read BookStack's PHP error log
|
||||
docker exec bookstack cat /app/www/storage/logs/laravel.log | tail -50
|
||||
|
||||
# Test BookStack OIDC flow directly
|
||||
# Test if BookStack's login redirect works
|
||||
curl -sc /tmp/c.txt http://localhost:6875/login -o /tmp/l.html && \
|
||||
CSRF=$(grep -oP 'name="_token" value="\K[^"]+' /tmp/l.html | head -1) && \
|
||||
curl -v -b /tmp/c.txt -X POST http://localhost:6875/oidc/login \
|
||||
-d "_token=$CSRF" --max-redirs 0 2>&1 | grep -E "HTTP|Location"
|
||||
# Should show: Location: https://auth.kitestacks.com/application/o/authorize/?...
|
||||
|
||||
# Test Authentik discovery document
|
||||
curl -s https://auth.kitestacks.com/application/o/<slug>/.well-known/openid-configuration | python3 -m json.tool
|
||||
|
||||
# Check Cloudflare tunnel connector count
|
||||
docker exec cloudflared cloudflared tunnel info <TUNNEL_ID>
|
||||
|
||||
# Check Tailscale connectivity
|
||||
# Check Tailscale connections between machines
|
||||
tailscale status
|
||||
|
||||
# PostgreSQL connectivity check (from monk)
|
||||
docker run --rm --network host -e PGPASSWORD="<REDACTED>" \
|
||||
postgres:16 psql -h <KSCLOUD1_TAILSCALE_IP> -U authentik authentik -c "\l"
|
||||
# See if both Cloudflare connectors are working
|
||||
docker exec cloudflared cloudflared tunnel info TUNNEL_ID
|
||||
```
|
||||
|
|
|
|||
70
README.md
70
README.md
|
|
@ -1,37 +1,55 @@
|
|||
# KiteStacks Homelab
|
||||
|
||||
<!-- version: 1.3.922 -->
|
||||
Everything needed to run, fix, and understand the KiteStacks homelab lives here.
|
||||
|
||||
Private GitOps repository for the KiteStacks homelab.
|
||||
## What is KiteStacks?
|
||||
|
||||
## Cluster
|
||||
KiteStacks is a personal homelab — a set of useful web apps that run on two computers
|
||||
(monk at home, kscloud1 in Germany). All the websites are accessible over the internet
|
||||
through Cloudflare without exposing any home IP addresses.
|
||||
|
||||
- K3s
|
||||
- FluxCD (planned)
|
||||
- Longhorn (planned)
|
||||
## How to Read This Repo
|
||||
|
||||
## Applications
|
||||
| File / Folder | What it is |
|
||||
|--------------|------------|
|
||||
| `RUNBOOK.md` | **Start here.** Plain-English guide to how everything works and how to do common tasks |
|
||||
| `DEBUG-DOCUMENTATION.md` | Every problem we've hit and how we solved it |
|
||||
| `docs/` | Detailed setup guides for specific services (Authentik SSO, etc.) |
|
||||
| `apps/` | Docker Compose files for each service |
|
||||
| `clusters/` | Infrastructure-level configs |
|
||||
| `projects/` | Active project notes |
|
||||
| `cloud/` | Cloud-specific configurations (kscloud1) |
|
||||
| `cloud-migration/` | Archive of cloud migration work and volume backups |
|
||||
| `autosync/` | Auto-sync scripts that keep the repo up to date automatically |
|
||||
| `osticket/` | OSTicket help-desk project notes |
|
||||
|
||||
- Homepage
|
||||
- Authentik (SSO identity provider)
|
||||
- Grafana
|
||||
- Open WebUI (Kite AI)
|
||||
- Forgejo
|
||||
- BookStack
|
||||
- OpenProject
|
||||
- Kavita
|
||||
- Raindrop.io (cloud, bookmark manager)
|
||||
- Uptime Kuma
|
||||
- LiteLLM
|
||||
- Linkding
|
||||
- Prometheus (monitoring, no SSO)
|
||||
- Portainer (admin, no SSO)
|
||||
## Services Running Right Now
|
||||
|
||||
## SSO
|
||||
| Service | Website | What it does |
|
||||
|---------|---------|--------------|
|
||||
| Authentik | auth.kitestacks.com | Single login for all services |
|
||||
| Portainer | portainer.kitestacks.com | Manage all Docker containers |
|
||||
| Forgejo | gitforge.kitestacks.com | Git server (code + scripts) |
|
||||
| BookStack | wiki.kitestacks.com | Wiki and notes |
|
||||
| Grafana | grafana.kitestacks.com | Server health charts |
|
||||
| Karakeep | links.kitestacks.com | Bookmark manager |
|
||||
| Kavita | kavita.kitestacks.com | Ebook reader |
|
||||
| OSTicket | tasks.kitestacks.com | Help desk / ticket system |
|
||||
| Open WebUI | ai.kitestacks.com | AI chat (GPT, Claude, local) |
|
||||
| Uptime Kuma | status.kitestacks.com | Service monitor |
|
||||
| Portal | www.kitestacks.com | Homepage |
|
||||
|
||||
All services use [Authentik](https://auth.kitestacks.com) as the identity provider.
|
||||
Setup guide: [docs/authentik-sso-setup.md](docs/authentik-sso-setup.md)
|
||||
## Quick Reference
|
||||
|
||||
## Documentation
|
||||
```bash
|
||||
# Check all running containers
|
||||
docker ps --format "table {{.Names}}\t{{.Status}}"
|
||||
|
||||
docs/KiteStacks-Homelab-Documentation-v1.3.884.md
|
||||
# Restart a service
|
||||
cd ~/kitestacks-live/docker/SERVICE_NAME && docker compose restart
|
||||
|
||||
# View live logs
|
||||
docker logs CONTAINER_NAME --tail 50 -f
|
||||
```
|
||||
|
||||
All usernames and passwords go through Authentik at `https://auth.kitestacks.com`.
|
||||
|
|
|
|||
357
RUNBOOK.md
357
RUNBOOK.md
|
|
@ -1,221 +1,174 @@
|
|||
# KiteStacks Homelab — Complete Setup Runbook
|
||||
# KiteStacks Homelab — How Everything Works
|
||||
|
||||
**Last Updated:** 2026-06-18
|
||||
**Status:** Production (monk primary, kscloud1 Hetzner cloud replica)
|
||||
**Maintainer:** kenpat
|
||||
**Status:** Up and running
|
||||
**Owner:** kenpat
|
||||
|
||||
---
|
||||
|
||||
## Architecture Overview
|
||||
## The Big Picture
|
||||
|
||||
KiteStacks is a personal homelab — a small set of programs (called "services") that run
|
||||
on two computers. One computer sits at home (called **monk**), and one rents space in
|
||||
a data center in Germany (called **kscloud1**).
|
||||
|
||||
People on the internet can reach every website without knowing where the computers are,
|
||||
because all traffic goes through **Cloudflare** — a free service that acts like a secret
|
||||
post-office. Cloudflare knows the address; the rest of the world doesn't.
|
||||
|
||||
```
|
||||
Internet
|
||||
│
|
||||
└── Cloudflare (DNS + Tunnel)
|
||||
│ Active-Active across 2 connectors
|
||||
├── cloudflared on monk (primary home machine, Docker container)
|
||||
└── cloudflared on kscloud1 (Hetzner VPS, <KSCLOUD1_PUBLIC_IP>)
|
||||
|
||||
Tailscale overlay network (VPN mesh):
|
||||
monk <MONK_TAILSCALE_IP>
|
||||
kscloud1 <KSCLOUD1_TAILSCALE_IP> ← hosts shared Authentik Postgres + Redis
|
||||
You (browser)
|
||||
│
|
||||
└─► Cloudflare (the post office)
|
||||
│
|
||||
├─► monk (home machine, runs most services)
|
||||
└─► kscloud1 (cloud backup machine in Germany)
|
||||
```
|
||||
|
||||
**Public subdomains** route through the same Cloudflare Tunnel token.
|
||||
Both monk and kscloud1 are connectors so the site stays up if either goes offline.
|
||||
|
||||
| Subdomain | Service | Port |
|
||||
|-----------|---------|------|
|
||||
| auth.kitestacks.com | Authentik | 9000 |
|
||||
| portainer.kitestacks.com | Portainer | 9443 |
|
||||
| wiki.kitestacks.com | BookStack | 6875 (monk) / 6877 (kscloud1) |
|
||||
| grafana.kitestacks.com | Grafana | 3000 |
|
||||
| gitforge.kitestacks.com | Forgejo | 3006 |
|
||||
| links.kitestacks.com | Karakeep | 3100 |
|
||||
| status.kitestacks.com | Uptime Kuma | 3001 |
|
||||
| tasks.kitestacks.com | OSTicket | 8080 |
|
||||
| flux.kitestacks.com | FluxCD | — |
|
||||
If monk goes offline, kscloud1 keeps serving the sites — Cloudflare automatically
|
||||
switches traffic over. This is called **active-active** (both doors are always open).
|
||||
|
||||
---
|
||||
|
||||
## Service Inventory
|
||||
## What Runs Where
|
||||
|
||||
### Running on monk
|
||||
authentik, authentik-worker, authentik-ldap, authentik-ldap-proxy,
|
||||
bookstack, bookstack-db, cloudflared, flux, forgejo, grafana,
|
||||
karakeep, karakeep-chrome, karakeep-meilisearch, kavita,
|
||||
kite-litellm, kite-openwebui, kitestacks-metrics-api, kitestacks-portal,
|
||||
node-exporter, ntfy, osticket, osticket-app, osticket-db,
|
||||
portainer, prometheus, uptime-kuma, blackbox-exporter
|
||||
### Services on monk
|
||||
| What it is | What it does | Website |
|
||||
|------------|--------------|---------|
|
||||
| Authentik | Login manager — handles all usernames and passwords | auth.kitestacks.com |
|
||||
| Portainer | Dashboard to manage all the Docker containers | portainer.kitestacks.com |
|
||||
| Forgejo | Git — stores all the code and scripts | gitforge.kitestacks.com |
|
||||
| BookStack | Wiki — where all the notes and guides live | wiki.kitestacks.com |
|
||||
| Grafana | Charts showing how healthy the servers are | grafana.kitestacks.com |
|
||||
| Karakeep | Saves and organizes bookmarks | links.kitestacks.com |
|
||||
| Kavita | Reads ebooks and manga | kavita.kitestacks.com |
|
||||
| OSTicket | Help-desk ticket system | tasks.kitestacks.com |
|
||||
| Open WebUI | Chat with AI models (GPT, Claude, local models) | ai.kitestacks.com |
|
||||
| Uptime Kuma | Watches every service and alerts if one goes down | status.kitestacks.com |
|
||||
| KiteStacks Portal | The main homepage with links to everything | www.kitestacks.com |
|
||||
|
||||
### Running on kscloud1 (extras)
|
||||
bookstack, bookstack-db-ks, kite-monitor, osticket-app-118,
|
||||
osticket-db-118, www-backup, homepage-backup, cloudflared,
|
||||
authentik-postgresql, authentik-redis
|
||||
|
||||
### Shared infrastructure on kscloud1
|
||||
- PostgreSQL `:5432` — Authentik DB used by both hosts (Tailscale only)
|
||||
- Redis `:6379` — Authentik session cache (Tailscale only)
|
||||
### Services on kscloud1 (cloud backup)
|
||||
- A copy of BookStack
|
||||
- A copy of the main Portal
|
||||
- The login database (PostgreSQL) and session memory (Redis) that Authentik uses
|
||||
- The Cloudflare connector (so the site keeps running if monk is off)
|
||||
|
||||
---
|
||||
|
||||
## Cloudflare Tunnel
|
||||
## Cloudflare Tunnel (the secret post office)
|
||||
|
||||
### How it works
|
||||
Both monk and kscloud1 run `cloudflared` as Docker containers using the **same tunnel token**. Cloudflare load-balances across both connectors (active-active). The tunnel token is stored in:
|
||||
- monk: `~/kitestacks-live/docker/cloudflared/.env` → `TUNNEL_TOKEN`
|
||||
- kscloud1: `/opt/kitestacks/docker/cloudflared/.env` → `TUNNEL_TOKEN`
|
||||
### Why it exists
|
||||
Normal websites need a router setting called "port forwarding" and a fixed home IP address.
|
||||
Cloudflare Tunnel removes both requirements — monk connects **out** to Cloudflare, and
|
||||
Cloudflare forwards visitor traffic back in. Your home address is never exposed.
|
||||
|
||||
### Fix: Phantom 3rd Replica
|
||||
If `cloudflared tunnel info` shows 3 connectors instead of 2, the native cloudflared systemd service on monk is running alongside the Docker container.
|
||||
### How to check it's healthy
|
||||
Go to Cloudflare Zero Trust → Networks → Tunnels. You should see **2 healthy connectors**
|
||||
(one from monk, one from kscloud1).
|
||||
|
||||
```bash
|
||||
# Check systemd cloudflared on monk
|
||||
systemctl status cloudflared
|
||||
# Disable it — Docker container is the correct one
|
||||
sudo systemctl disable --now cloudflared
|
||||
```
|
||||
|
||||
### Adding a new hostname route
|
||||
### Adding a new website
|
||||
In Cloudflare Zero Trust → Networks → Tunnels → your tunnel → Edit → Public Hostname:
|
||||
- Subdomain: `newservice`
|
||||
- Domain: `kitestacks.com`
|
||||
- Service: `http://container-name:port`
|
||||
- Service URL: `http://container-name:port`
|
||||
|
||||
Both monk and kscloud1 must have the container running on the same port.
|
||||
Both monk and kscloud1 need to be running that container on the same port.
|
||||
|
||||
---
|
||||
### Fix: If you see 3 connectors instead of 2
|
||||
The old cloudflared system service on monk is probably running alongside the Docker one.
|
||||
Run this on monk to fix it:
|
||||
|
||||
## Authentik SSO
|
||||
|
||||
### Architecture
|
||||
Authentik uses a **shared database** hosted on kscloud1. monk's Authentik containers connect via Tailscale.
|
||||
|
||||
- monk containers: `authentik`, `authentik-worker`, `authentik-ldap`, `authentik-ldap-proxy`
|
||||
- DB: PostgreSQL on kscloud1 at `<KSCLOUD1_TAILSCALE_IP>:5432`
|
||||
- Redis: kscloud1 at `<KSCLOUD1_TAILSCALE_IP>:6379`
|
||||
|
||||
### Adding OIDC SSO for a new app
|
||||
|
||||
1. In Authentik admin (`https://auth.kitestacks.com/if/admin/`):
|
||||
- **Providers** → Create → OAuth2/OpenID Provider
|
||||
- Name the provider after the app (e.g. `bookstack`)
|
||||
- Set `issuer_mode` based on the app's requirements (see Debug doc)
|
||||
- Note the Client ID and Client Secret
|
||||
|
||||
2. **Application** → Create → link to the provider
|
||||
|
||||
3. **Policy Binding** → bind the `default-authentication-flow` to the application
|
||||
|
||||
4. Configure the app with:
|
||||
- `OIDC_ISSUER` = discovery base URL
|
||||
- `OIDC_CLIENT_ID` / `OIDC_CLIENT_SECRET`
|
||||
- Callback URL = `https://yourapp.kitestacks.com/auth/callback`
|
||||
|
||||
### Checking OIDC discovery URL
|
||||
```bash
|
||||
# Per-provider (issuer_mode=per_provider)
|
||||
curl -s https://auth.kitestacks.com/application/o/<slug>/.well-known/openid-configuration | python3 -m json.tool
|
||||
|
||||
# Global (issuer_mode=global)
|
||||
# Note: global issuer URL does NOT serve a JSON discovery doc at /.well-known/
|
||||
# Use per-provider mode for apps that auto-discover endpoints (BookStack, etc.)
|
||||
```
|
||||
|
||||
### Changing provider issuer_mode via SQL
|
||||
```bash
|
||||
docker run --rm --network host \
|
||||
-e PGPASSWORD="<REDACTED>" \
|
||||
postgres:16 psql -h <KSCLOUD1_TAILSCALE_IP> -U authentik authentik -c \
|
||||
"UPDATE authentik_providers_oauth2_oauth2provider SET issuer_mode='per_provider' WHERE provider_ptr_id=<ID>;"
|
||||
sudo systemctl disable --now cloudflared
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Portainer
|
||||
## Authentik (the login manager)
|
||||
|
||||
### OAuth setup (Authentik)
|
||||
Portainer CE uses AuthenticationMethod=3 (OAuth). Configured via the BoltDB.
|
||||
### What it does
|
||||
Every website on KiteStacks uses Authentik for login. Instead of each website having its
|
||||
own username and password, Authentik is the one source of truth. You log in once and
|
||||
all the websites trust that login. This system is called **SSO** (Single Sign-On).
|
||||
|
||||
Key settings:
|
||||
- `OAuthLoginURI`: `https://auth.kitestacks.com/application/o/authorize/`
|
||||
- `OAuthTokenURI`: `https://auth.kitestacks.com/application/o/token/`
|
||||
- `OAuthUserURI`: `https://auth.kitestacks.com/application/o/userinfo/`
|
||||
- `OAuthClientID`: `portainer`
|
||||
- `OAuthRedirectURI`: `https://portainer.kitestacks.com`
|
||||
- `OAuthAutoCreateUsers`: `true`
|
||||
- `OAuthDefaultTeamID`: `0`
|
||||
### Where the database lives
|
||||
Authentik's user database lives on **kscloud1** (not on monk). Both machines share it
|
||||
through a private encrypted network called **Tailscale**.
|
||||
|
||||
### Pre-creating an admin user before first OAuth login
|
||||
OAuth auto-created users default to Role:2 (regular user) and can't see environments.
|
||||
Pre-create them as Role:1 (admin) via the API before they log in:
|
||||
### Adding a new app to SSO
|
||||
|
||||
1. Go to `https://auth.kitestacks.com/if/admin/`
|
||||
2. **Providers** → Create → OAuth2/OpenID Provider
|
||||
3. Name it after the app (e.g., `myapp`)
|
||||
4. Note the Client ID and Client Secret
|
||||
5. **Application** → Create → link it to the provider
|
||||
6. Set up the app with:
|
||||
- Login URL (your app's OIDC issuer URL)
|
||||
- Client ID and Client Secret
|
||||
- Callback URL: `https://yourapp.kitestacks.com/auth/callback`
|
||||
|
||||
---
|
||||
|
||||
## Portainer (the container dashboard)
|
||||
|
||||
### What it does
|
||||
Portainer is a web dashboard that shows all running Docker containers. Think of Docker
|
||||
containers like small self-contained boxes — each one runs one program. Portainer lets
|
||||
you start, stop, restart, and view logs for all the boxes without typing commands.
|
||||
|
||||
### If you get locked out
|
||||
```bash
|
||||
# Stop Portainer
|
||||
docker stop portainer
|
||||
|
||||
# Reset the password (the command will print a new temporary password)
|
||||
docker run --rm -v portainer_data:/data portainer/helper-reset-password
|
||||
|
||||
# Start it again
|
||||
docker start portainer
|
||||
```
|
||||
|
||||
### First-time OAuth login issue
|
||||
When someone logs into Portainer through Authentik for the first time, they get created
|
||||
as a regular user (not admin). They won't be able to see any servers. To fix this,
|
||||
create their account as admin **before** their first login:
|
||||
|
||||
```bash
|
||||
# Get auth token
|
||||
# Step 1: Get a login token
|
||||
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'])")
|
||||
-d '{"username":"admin","password":"YOUR_PASSWORD"}' | python3 -c "import sys,json; print(json.load(sys.stdin)['jwt'])")
|
||||
|
||||
# Create user as admin (Role:1), no password needed for OAuth users
|
||||
# Step 2: Create the user as admin (role 1 = admin)
|
||||
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}'
|
||||
```
|
||||
|
||||
### Reset admin password (if locked out)
|
||||
```bash
|
||||
# Stop Portainer
|
||||
docker stop portainer
|
||||
|
||||
# Reset password (shows new temp password)
|
||||
docker run --rm -v portainer_data:/data portainer/helper-reset-password
|
||||
|
||||
# Restart
|
||||
docker start portainer
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## BookStack
|
||||
## BookStack (the wiki)
|
||||
|
||||
### Setup (both monk and kscloud1)
|
||||
Location:
|
||||
- monk: `~/kitestacks-live/docker/bookstack/docker-compose.yml`
|
||||
- kscloud1: `/opt/kitestacks/docker/bookstack/docker-compose.yml`
|
||||
### What it does
|
||||
BookStack is a self-hosted wiki — like a private Wikipedia just for this homelab.
|
||||
All notes, runbooks, and guides live here.
|
||||
|
||||
Key environment variables:
|
||||
```yaml
|
||||
- APP_URL=https://wiki.kitestacks.com
|
||||
- DB_HOST=bookstack-db
|
||||
- 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>
|
||||
```
|
||||
### Important settings
|
||||
BookStack uses Authentik for login. Two settings must be correct:
|
||||
|
||||
### Generate APP_KEY
|
||||
```bash
|
||||
docker run --rm --entrypoint /bin/bash lscr.io/linuxserver/bookstack:latest appkey
|
||||
```
|
||||
- `OIDC_ISSUER_DISCOVER=true` — tells BookStack to automatically find all login endpoints
|
||||
- `OIDC_ISSUER` — must point to the per-app Authentik URL, like:
|
||||
`https://auth.kitestacks.com/application/o/bookstack/`
|
||||
|
||||
### OIDC Configuration
|
||||
BookStack uses `OIDC_ISSUER_DISCOVER=true` to auto-discover all endpoints from Authentik.
|
||||
The `OIDC_ISSUER` must match the per-app discovery URL base (not the global Authentik URL).
|
||||
### Fix: If cache breaks after running a PHP command
|
||||
Sometimes running admin commands inside the container breaks file permissions:
|
||||
|
||||
The Authentik bookstack provider must have `issuer_mode='per_provider'` so its discovery
|
||||
document returns the correct per-app issuer URL. See Debug doc for full troubleshooting.
|
||||
|
||||
### Fix cache permissions after artisan runs
|
||||
Running `php artisan` as root creates root-owned cache dirs that block the app:
|
||||
```bash
|
||||
docker exec bookstack chown -R abc:users /config/www/framework/cache/
|
||||
```
|
||||
|
||||
### Clear Laravel config/cache
|
||||
### Clear BookStack's config cache
|
||||
```bash
|
||||
docker exec bookstack php /app/www/artisan config:clear
|
||||
docker exec bookstack php /app/www/artisan cache:clear
|
||||
|
|
@ -223,27 +176,27 @@ docker exec bookstack php /app/www/artisan cache:clear
|
|||
|
||||
---
|
||||
|
||||
## kscloud1 Access
|
||||
## kscloud1 (the cloud backup machine)
|
||||
|
||||
### SSH
|
||||
### SSH access
|
||||
```bash
|
||||
ssh -i ~/.ssh/id_ed25519_kscloud1 root@<KSCLOUD1_TAILSCALE_IP>
|
||||
ssh -i ~/.ssh/id_ed25519_kscloud1 root@KSCLOUD1_TAILSCALE_IP
|
||||
```
|
||||
|
||||
### If SSH key is lost / not working
|
||||
1. Open Hetzner Cloud console: `console.hetzner.cloud` → your server → Console tab
|
||||
2. Log in as `root` (Linux user password)
|
||||
3. Serve the key from monk over Tailscale:
|
||||
### If you can't SSH in (key was lost)
|
||||
|
||||
1. Open Hetzner Cloud console → your server → **Console** tab (this is like a TV remote for the server)
|
||||
2. Log in as `root` using the Linux root password
|
||||
3. On monk, share your public SSH key temporarily:
|
||||
```bash
|
||||
# On monk — start temporary HTTP server
|
||||
cat ~/.ssh/id_ed25519_kscloud1.pub > ~/key.txt
|
||||
python3 -m http.server 7777 --directory ~/
|
||||
```
|
||||
4. In Hetzner console, type:
|
||||
4. In the Hetzner console, type:
|
||||
```bash
|
||||
curl http://<MONK_TAILSCALE_IP>:7777/key.txt > /root/.ssh/authorized_keys
|
||||
curl http://MONK_TAILSCALE_IP:7777/key.txt > /root/.ssh/authorized_keys
|
||||
```
|
||||
5. Enable root SSH (if needed):
|
||||
5. If root SSH is disabled:
|
||||
```bash
|
||||
sed -i 's/^#*PermitRootLogin.*/PermitRootLogin prohibit-password/' /etc/ssh/sshd_config
|
||||
systemctl restart ssh
|
||||
|
|
@ -251,43 +204,67 @@ ssh -i ~/.ssh/id_ed25519_kscloud1 root@<KSCLOUD1_TAILSCALE_IP>
|
|||
|
||||
---
|
||||
|
||||
## OSTicket SMTP
|
||||
## OSTicket (help desk)
|
||||
|
||||
**Config:** smtp.gmail.com:587, STARTTLS
|
||||
**From:** `kitestacks.helpdesk@gmail.com` (app password stored in DB)
|
||||
OSTicket is the ticket/task system at `tasks.kitestacks.com`.
|
||||
Emails sent to `kitestacks.helpdesk@gmail.com` become tickets automatically.
|
||||
|
||||
To test email delivery: Admin Panel → Diagnostics → Send Test Email
|
||||
To test that email is working: Admin Panel → Diagnostics → Send Test Email
|
||||
|
||||
---
|
||||
|
||||
## Forgejo
|
||||
## Forgejo (code storage)
|
||||
|
||||
Runs on monk at `localhost:3006` (port 2222 for SSH git).
|
||||
Forgejo is the Git server — all scripts, configs, and docs live here.
|
||||
|
||||
### Generate API token for automation
|
||||
### Create an API token for automation
|
||||
```bash
|
||||
docker exec -u git forgejo forgejo admin user generate-access-token \
|
||||
--username kenpat --token-name "my-token" --raw \
|
||||
--username kenpat \
|
||||
--token-name "my-token" \
|
||||
--raw \
|
||||
--scopes "read:user,write:user,read:repository,write:repository"
|
||||
```
|
||||
|
||||
Note: SSH to gitforge.kitestacks.com only works from inside the local network,
|
||||
not through Cloudflare (Cloudflare blocks non-HTTPS ports).
|
||||
For git operations from monk, use `ssh://git@localhost:2222/kenpat/repo.git`.
|
||||
|
||||
---
|
||||
|
||||
## Common Docker Operations
|
||||
## Everyday Docker Commands
|
||||
|
||||
```bash
|
||||
# View logs for a service
|
||||
docker logs <container> --tail 50 -f
|
||||
# See all running containers and their status
|
||||
docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"
|
||||
|
||||
# View recent logs for a service
|
||||
docker logs CONTAINER_NAME --tail 50 -f
|
||||
|
||||
# Restart a service
|
||||
cd ~/kitestacks-live/docker/<service> && docker compose restart
|
||||
cd ~/kitestacks-live/docker/SERVICE_NAME
|
||||
docker compose restart
|
||||
|
||||
# Full stack restart
|
||||
# Stop and restart a service (harder reset)
|
||||
docker compose down && docker compose up -d
|
||||
|
||||
# Update a container image
|
||||
# Pull latest image and restart
|
||||
docker compose pull && docker compose up -d
|
||||
|
||||
# Check all running containers
|
||||
docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Tailscale (the private tunnel between machines)
|
||||
|
||||
Tailscale creates an encrypted private network between monk and kscloud1.
|
||||
Nothing on this network is visible to the public internet.
|
||||
|
||||
Used for:
|
||||
- monk connecting to kscloud1's PostgreSQL and Redis (for Authentik)
|
||||
- SSH from monk to kscloud1
|
||||
- Prometheus on monk scraping metrics from kscloud1
|
||||
|
||||
To check connection status:
|
||||
```bash
|
||||
tailscale status
|
||||
```
|
||||
|
|
|
|||
9
autosync/.gitignore
vendored
Normal file
9
autosync/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
apps/authentik/postgres/
|
||||
apps/forgejo/data/gitea/gitea.db
|
||||
apps/kavita/config/*.db
|
||||
apps/kavita/config/*.db-wal
|
||||
apps/kavita/config/*.db-shm
|
||||
apps/linkding/
|
||||
*.sqlite3
|
||||
*.sqlite3-wal
|
||||
*.sqlite3-shm
|
||||
78
autosync/CHANGELOG.md
Normal file
78
autosync/CHANGELOG.md
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
# Changelog
|
||||
|
||||
All notable changes to KiteStacks Homelab are documented here.
|
||||
|
||||
## [v1.3.7] — 2026-06-06 05:08:38
|
||||
|
||||
### Changed
|
||||
- [autosync] 2026-06-06 05:08:38 INFO Synced: apps/forgejo/data/gitea/gitea.db
|
||||
- [autosync] 2026-06-06 05:08:38 INFO Removed: apps/forgejo/data/gitea/gitea.db-journal
|
||||
- apps/forgejo/data/gitea/gitea.db
|
||||
- DELETED: apps/forgejo/data/gitea/gitea.db-journal
|
||||
|
||||
## [v1.3.6] — 2026-06-06 05:08:20
|
||||
|
||||
### Changed
|
||||
- [autosync] 2026-06-06 05:08:20 INFO Synced: apps/forgejo/data/gitea/gitea.db
|
||||
- [autosync] 2026-06-06 05:08:20 INFO Removed: apps/kavita/config/kavita.db-shm
|
||||
- [autosync] 2026-06-06 05:08:20 INFO Synced: apps/kavita/config/kavita.db
|
||||
- [autosync] 2026-06-06 05:08:20 INFO Removed: apps/kavita/config/kavita.db-wal
|
||||
- [autosync] 2026-06-06 05:08:20 INFO Removed: apps/forgejo/data/gitea/gitea.db-journal
|
||||
- [autosync] 2026-06-06 05:08:20 INFO Synced: apps/linkding/data/tasks.sqlite3-wal
|
||||
- apps/forgejo/data/gitea/gitea.db
|
||||
- DELETED: apps/kavita/config/kavita.db-shm
|
||||
- apps/kavita/config/kavita.db
|
||||
- DELETED: apps/kavita/config/kavita.db-wal
|
||||
- DELETED: apps/forgejo/data/gitea/gitea.db-journal
|
||||
- apps/linkding/data/tasks.sqlite3-wal
|
||||
|
||||
## [v1.3.5] — 2026-06-06 05:07:39
|
||||
|
||||
### Changed
|
||||
- [autosync] 2026-06-06 05:07:39 INFO Synced: apps/forgejo/data/gitea/gitea.db
|
||||
- [autosync] 2026-06-06 05:07:39 INFO Removed: apps/forgejo/data/gitea/gitea.db-journal
|
||||
- apps/forgejo/data/gitea/gitea.db
|
||||
- DELETED: apps/forgejo/data/gitea/gitea.db-journal
|
||||
|
||||
## [v1.3.4] — 2026-06-06 05:07:19
|
||||
|
||||
### Changed
|
||||
- [autosync] 2026-06-06 05:07:19 INFO Synced: apps/forgejo/data/gitea/gitea.db
|
||||
- [autosync] 2026-06-06 05:07:19 INFO Synced: apps/authentik/postgres/base/16384/17715
|
||||
- [autosync] 2026-06-06 05:07:19 INFO Synced: apps/authentik/postgres/pg_wal/000000010000000000000002
|
||||
- [autosync] 2026-06-06 05:07:19 INFO Synced: apps/authentik/postgres/base/16384/17733
|
||||
- [autosync] 2026-06-06 05:07:19 INFO Synced: apps/authentik/postgres/base/16384/17734
|
||||
- [autosync] 2026-06-06 05:07:19 INFO Synced: apps/authentik/postgres/base/16384/17735
|
||||
- [autosync] 2026-06-06 05:07:19 INFO Synced: apps/authentik/postgres/pg_xact/0000
|
||||
- [autosync] 2026-06-06 05:07:19 INFO Removed: apps/forgejo/data/gitea/gitea.db-journal
|
||||
- [autosync] 2026-06-06 05:07:19 INFO Synced: apps/linkding/data/tasks.sqlite3-wal
|
||||
- [autosync] 2026-06-06 05:07:19 INFO Synced: apps/authentik/postgres/base/16384/17722
|
||||
- [autosync] 2026-06-06 05:07:19 INFO Synced: apps/authentik/postgres/base/16384/17720
|
||||
- [autosync] 2026-06-06 05:07:19 INFO Synced: apps/authentik/postgres/global/pg_control
|
||||
- [autosync] 2026-06-06 05:07:19 INFO Synced: apps/authentik/postgres/pg_logical/replorigin_checkpoint
|
||||
- apps/forgejo/data/gitea/gitea.db
|
||||
- apps/authentik/postgres/base/16384/17715
|
||||
- apps/authentik/postgres/pg_wal/000000010000000000000002
|
||||
- apps/authentik/postgres/base/16384/17733
|
||||
- apps/authentik/postgres/base/16384/17734
|
||||
- apps/authentik/postgres/base/16384/17735
|
||||
- apps/authentik/postgres/pg_xact/0000
|
||||
- DELETED: apps/forgejo/data/gitea/gitea.db-journal
|
||||
- apps/linkding/data/tasks.sqlite3-wal
|
||||
- apps/authentik/postgres/base/16384/17722
|
||||
- apps/authentik/postgres/base/16384/17720
|
||||
- apps/authentik/postgres/global/pg_control
|
||||
- apps/authentik/postgres/pg_logical/replorigin_checkpoint
|
||||
|
||||
## [v1.3.3] — 2026-06-06 04:54:24
|
||||
|
||||
### Changed
|
||||
- [autosync] 2026-06-06 04:54:24 INFO Synced: apps/authentik/postgres/pg_wal/000000010000000000000002
|
||||
- [autosync] 2026-06-06 04:54:24 INFO Synced: apps/kavita/config/kavita.db-wal
|
||||
- [autosync] 2026-06-06 04:54:24 INFO Synced: apps/linkding/data/tasks.sqlite3-wal
|
||||
- [autosync] 2026-06-06 04:54:24 INFO Synced: apps/kavita/config/cache.db-wal
|
||||
- apps/authentik/postgres/pg_wal/000000010000000000000002
|
||||
- apps/kavita/config/kavita.db-wal
|
||||
- apps/linkding/data/tasks.sqlite3-wal
|
||||
- apps/kavita/config/cache.db-wal
|
||||
|
||||
157
autosync/README.md
Normal file
157
autosync/README.md
Normal file
|
|
@ -0,0 +1,157 @@
|
|||
╔══════════════════════════════════════════════════════════════════╗
|
||||
║ KiteStacks Homelab — Auto-Sync Setup Guide ║
|
||||
╚══════════════════════════════════════════════════════════════════╝
|
||||
|
||||
WHAT THIS DOES
|
||||
──────────────
|
||||
Watches your Docker app directories for any file changes.
|
||||
On each change it automatically:
|
||||
|
||||
1. Copies the changed file into the correct repo folder
|
||||
~/docker/homepage/ → apps/homepage/
|
||||
~/docker/<app>/ → apps/<app>/
|
||||
/etc/kitestacks/ → clusters/assassin/
|
||||
|
||||
2. Bumps the version in README.md
|
||||
<!-- version: 1.3.7 -->
|
||||
|
||||
3. Creates a new versioned doc file
|
||||
docs/KiteStacks-Homelab-Documentation-v1.3.3.md
|
||||
|
||||
4. Appends an entry to CHANGELOG.md
|
||||
|
||||
5. Commits with your existing style:
|
||||
"Automated update: 2026-06-06 03:36:00"
|
||||
|
||||
6. Pushes to Forgejo at gitforge.kitestacks.com
|
||||
|
||||
It pushes to a TEST repo first so you can verify before
|
||||
flipping to kenpat/kitestacks-homelab.
|
||||
|
||||
|
||||
FILES
|
||||
─────
|
||||
setup.sh ← Run first (as root)
|
||||
promote.sh ← Run to switch test → main repo
|
||||
status.sh ← Health check
|
||||
config/
|
||||
settings.conf.example
|
||||
settings.conf ← Your config (fill this in first)
|
||||
scripts/
|
||||
kitestacks-watcher.sh ← The daemon (started by systemd)
|
||||
|
||||
|
||||
STEP-BY-STEP
|
||||
════════════
|
||||
|
||||
1. GET A FORGEJO TOKEN
|
||||
gitforge.kitestacks.com → User Settings → Applications
|
||||
→ Generate New Token → name it "autosync"
|
||||
→ Scopes: ✓ repository (read + write)
|
||||
Copy it — you only see it once.
|
||||
|
||||
2. CONFIGURE
|
||||
cp config/settings.conf.example config/settings.conf
|
||||
nano config/settings.conf
|
||||
|
||||
Required changes:
|
||||
FORGEJO_TOKEN="your-token-here"
|
||||
WATCH_DIRS="/home/kenpat/docker /etc/kitestacks"
|
||||
↑ adjust to match where your app files live
|
||||
|
||||
3. RUN SETUP
|
||||
sudo bash setup.sh
|
||||
|
||||
This will:
|
||||
• Install git, inotify-tools, curl, jq
|
||||
• Create kitestacks-homelab-autosync-test on Forgejo
|
||||
• Clone it to /opt/kitestacks-autosync/
|
||||
• Bootstrap README.md and docs/ at v1.3.0
|
||||
• Install and start the systemd service
|
||||
|
||||
4. VERIFY
|
||||
bash status.sh
|
||||
journalctl -u kitestacks-autosync -f
|
||||
|
||||
Trigger a test change:
|
||||
touch /home/kenpat/docker/homepage/test-autosync.txt
|
||||
|
||||
Check gitforge.kitestacks.com/kenpat/kitestacks-homelab-autosync-test
|
||||
You should see:
|
||||
• A new commit: "Automated update: 2026-06-06 ..."
|
||||
• apps/homepage/test-autosync.txt added
|
||||
• README.md version bumped: 1.3.0 → 1.3.1
|
||||
• docs/KiteStacks-Homelab-Documentation-v1.3.1.md created
|
||||
• CHANGELOG.md entry added
|
||||
|
||||
Clean up the test file:
|
||||
rm /home/kenpat/docker/homepage/test-autosync.txt
|
||||
|
||||
5. PROMOTE TO MAIN REPO
|
||||
sudo bash promote.sh
|
||||
|
||||
Type "promote" to confirm. From this point on, all changes
|
||||
go to kenpat/kitestacks-homelab.
|
||||
|
||||
|
||||
HOW THE README.md VERSIONING WORKS
|
||||
════════════════════════════════════
|
||||
The script tracks versions using an HTML comment tag that is
|
||||
invisible when the README renders:
|
||||
|
||||
# KiteStacks Homelab
|
||||
|
||||
<!-- version: 1.3.7 -->
|
||||
|
||||
Private GitOps repository for the KiteStacks homelab.
|
||||
...
|
||||
|
||||
This tag is injected automatically on first run (it won't
|
||||
change how your README looks in the browser).
|
||||
|
||||
The docs/ reference line is also kept current:
|
||||
docs/KiteStacks-Homelab-Documentation-v1.3.4.md
|
||||
|
||||
|
||||
DIRECTORY MAPPING
|
||||
═════════════════
|
||||
When a file changes, it's placed in the repo like this:
|
||||
|
||||
Server path Repo path
|
||||
───────────────────────────────── ─────────────────────────────────
|
||||
~/docker/homepage/config/… apps/homepage/config/…
|
||||
~/docker/kavita-docker-automation/… apps/kavita-docker-automation/…
|
||||
/etc/kitestacks/… clusters/assassin/…
|
||||
|
||||
|
||||
MANAGING THE SERVICE
|
||||
════════════════════
|
||||
bash status.sh # health check
|
||||
journalctl -u kitestacks-autosync -f # live logs
|
||||
sudo systemctl restart kitestacks-autosync # restart
|
||||
sudo systemctl stop kitestacks-autosync # stop
|
||||
|
||||
|
||||
DEBOUNCE
|
||||
════════
|
||||
Default: 15 seconds after the last change before committing.
|
||||
This means if you save 10 files in 5 seconds, you get ONE commit
|
||||
not 10. Adjust DEBOUNCE_SECONDS in settings.conf.
|
||||
|
||||
|
||||
TROUBLESHOOTING
|
||||
═══════════════
|
||||
Push fails with auth error
|
||||
→ Check FORGEJO_TOKEN is correct in settings.conf
|
||||
→ Re-run: sudo bash setup.sh
|
||||
|
||||
"Config not found" on start
|
||||
→ sudo bash setup.sh (it will copy the example for you)
|
||||
|
||||
Service crashes
|
||||
→ journalctl -u kitestacks-autosync -n 50
|
||||
→ Check WATCH_DIRS path(s) exist
|
||||
|
||||
Files not appearing in apps/ subfolder
|
||||
→ Check WATCH_DIRS in settings.conf matches your actual
|
||||
Docker compose directory (e.g. /home/kenpat/docker)
|
||||
20
autosync/apps/homepage/deployment.yaml
Normal file
20
autosync/apps/homepage/deployment.yaml
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: homepage
|
||||
namespace: homepage
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: homepage
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: homepage
|
||||
spec:
|
||||
containers:
|
||||
- name: homepage
|
||||
image: ghcr.io/yourusername/homepage:latest
|
||||
ports:
|
||||
- containerPort: 80
|
||||
13
autosync/apps/homepage/kustomization.yaml
Normal file
13
autosync/apps/homepage/kustomization.yaml
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
apiVersion: kustomize.toolkit.fluxcd.io/v1
|
||||
kind: Kustomization
|
||||
metadata:
|
||||
name: homepage
|
||||
namespace: flux-system
|
||||
spec:
|
||||
interval: 1m
|
||||
path: ./apps/homepage
|
||||
prune: true
|
||||
sourceRef:
|
||||
kind: GitRepository
|
||||
name: kitestacks-homelab
|
||||
targetNamespace: homepage
|
||||
4
autosync/apps/homepage/namespace.yaml
Normal file
4
autosync/apps/homepage/namespace.yaml
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: homepage
|
||||
13
autosync/apps/homepage/service.yaml
Normal file
13
autosync/apps/homepage/service.yaml
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: homepage
|
||||
namespace: homepage
|
||||
spec:
|
||||
selector:
|
||||
app: homepage
|
||||
ports:
|
||||
- protocol: TCP
|
||||
port: 80
|
||||
targetPort: 80
|
||||
type: ClusterIP
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
apiVersion: batch/v1
|
||||
kind: CronJob
|
||||
metadata:
|
||||
name: kavita-docker-auto-restart
|
||||
namespace: automation
|
||||
spec:
|
||||
schedule: "*/15 * * * *"
|
||||
successfulJobsHistoryLimit: 2
|
||||
failedJobsHistoryLimit: 3
|
||||
jobTemplate:
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
restartPolicy: OnFailure
|
||||
containers:
|
||||
- name: restart-kavita
|
||||
image: docker:27-cli
|
||||
command:
|
||||
- /bin/sh
|
||||
- -c
|
||||
- |
|
||||
echo "Restarting Kavita Docker container..."
|
||||
docker restart kavita
|
||||
echo "Kavita restart completed."
|
||||
volumeMounts:
|
||||
- name: docker-sock
|
||||
mountPath: /var/run/docker.sock
|
||||
volumes:
|
||||
- name: docker-sock
|
||||
hostPath:
|
||||
path: /var/run/docker.sock
|
||||
type: Socket
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
|
||||
resources:
|
||||
- namespace.yaml
|
||||
- kavita-docker-restart-cronjob.yaml
|
||||
4
autosync/apps/kavita-docker-automation/namespace.yaml
Normal file
4
autosync/apps/kavita-docker-automation/namespace.yaml
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: automation
|
||||
20
autosync/clusters/assassin/deployment.yaml
Normal file
20
autosync/clusters/assassin/deployment.yaml
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: homepage
|
||||
namespace: homepage
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: homepage
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: homepage
|
||||
spec:
|
||||
containers:
|
||||
- name: homepage
|
||||
image: nginx:latest
|
||||
ports:
|
||||
- containerPort: 80
|
||||
12
autosync/clusters/assassin/flux-system/gitrepository.yaml
Normal file
12
autosync/clusters/assassin/flux-system/gitrepository.yaml
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
apiVersion: source.toolkit.fluxcd.io/v1
|
||||
kind: GitRepository
|
||||
metadata:
|
||||
name: kitestacks-homelab
|
||||
namespace: flux-system
|
||||
spec:
|
||||
interval: 1m
|
||||
url: https://gitforge.kitestacks.com/kenpat/kitestacks-homelab.git
|
||||
ref:
|
||||
branch: main
|
||||
secretRef:
|
||||
name: forgejo-auth
|
||||
13
autosync/clusters/assassin/flux-system/kustomization.yaml
Normal file
13
autosync/clusters/assassin/flux-system/kustomization.yaml
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
apiVersion: kustomize.toolkit.fluxcd.io/v1
|
||||
kind: Kustomization
|
||||
metadata:
|
||||
name: kitestacks-homelab
|
||||
namespace: flux-system
|
||||
spec:
|
||||
interval: 1m
|
||||
path: ./apps/kavita-docker-automation
|
||||
prune: true
|
||||
sourceRef:
|
||||
kind: GitRepository
|
||||
name: kitestacks-homelab
|
||||
targetNamespace: automation
|
||||
13
autosync/clusters/assassin/homepage.yaml
Normal file
13
autosync/clusters/assassin/homepage.yaml
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
apiVersion: kustomize.toolkit.fluxcd.io/v1
|
||||
kind: Kustomization
|
||||
metadata:
|
||||
name: homepage
|
||||
namespace: flux-system
|
||||
spec:
|
||||
interval: 1m
|
||||
path: ./apps/homepage
|
||||
prune: true
|
||||
sourceRef:
|
||||
kind: GitRepository
|
||||
name: kitestacks-homelab
|
||||
|
||||
6
autosync/clusters/assassin/kustomization.yaml
Normal file
6
autosync/clusters/assassin/kustomization.yaml
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
apiVersion: kustomize.config.k8s.io/v1
|
||||
kind: Kustomization
|
||||
resources:
|
||||
- namespace.yaml
|
||||
- deployment.yaml
|
||||
- service.yaml
|
||||
4
autosync/clusters/assassin/namespace.yaml
Normal file
4
autosync/clusters/assassin/namespace.yaml
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: homepage
|
||||
13
autosync/clusters/assassin/service.yaml
Normal file
13
autosync/clusters/assassin/service.yaml
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: homepage
|
||||
namespace: homepage
|
||||
spec:
|
||||
selector:
|
||||
app: homepage
|
||||
ports:
|
||||
- protocol: TCP
|
||||
port: 80
|
||||
targetPort: 80
|
||||
type: ClusterIP
|
||||
32
autosync/docs/KiteStacks-Homelab-Documentation-v1.2.md
Normal file
32
autosync/docs/KiteStacks-Homelab-Documentation-v1.2.md
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
# KiteStacks Homelab Documentation v1.2
|
||||
|
||||
## Current Architecture
|
||||
|
||||
KiteStacks Homelab is transitioning from Docker Compose to Kubernetes using K3s.
|
||||
|
||||
## Current Server
|
||||
|
||||
Hostname: Assassin
|
||||
Role: Homelab server and Kubernetes control plane
|
||||
Kubernetes: K3s
|
||||
Status: Ready
|
||||
|
||||
## Current Docker Services
|
||||
|
||||
- Homepage
|
||||
- Kavita
|
||||
- Forgejo
|
||||
- Linkding
|
||||
- Grafana
|
||||
- Prometheus
|
||||
- Other supporting services
|
||||
|
||||
## Kubernetes Milestone
|
||||
|
||||
K3s has been installed successfully.
|
||||
|
||||
Verified command:
|
||||
|
||||
```bash
|
||||
kubectl get nodes
|
||||
assassin Ready control-plane
|
||||
48
autosync/docs/KiteStacks-Homelab-Documentation-v1.3.3.md
Normal file
48
autosync/docs/KiteStacks-Homelab-Documentation-v1.3.3.md
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
# KiteStacks Homelab Documentation v1.3.3
|
||||
|
||||
**Version:** 1.3.3
|
||||
**Updated:** 2026-06-06 04:54:24
|
||||
**Previous:** [v1.3.2 docs](KiteStacks-Homelab-Documentation-v1.3.2.md)
|
||||
|
||||
---
|
||||
|
||||
## Change Summary
|
||||
|
||||
- [autosync] 2026-06-06 04:54:24 INFO Synced: apps/authentik/postgres/pg_wal/000000010000000000000002
|
||||
- [autosync] 2026-06-06 04:54:24 INFO Synced: apps/kavita/config/kavita.db-wal
|
||||
- [autosync] 2026-06-06 04:54:24 INFO Synced: apps/linkding/data/tasks.sqlite3-wal
|
||||
- [autosync] 2026-06-06 04:54:24 INFO Synced: apps/kavita/config/cache.db-wal
|
||||
- apps/authentik/postgres/pg_wal/000000010000000000000002
|
||||
- apps/kavita/config/kavita.db-wal
|
||||
- apps/linkding/data/tasks.sqlite3-wal
|
||||
- apps/kavita/config/cache.db-wal
|
||||
|
||||
---
|
||||
|
||||
## Cluster
|
||||
|
||||
| Component | Status |
|
||||
|-----------|--------|
|
||||
| K3s | Active |
|
||||
| FluxCD | Planned |
|
||||
| Longhorn | Planned |
|
||||
|
||||
## Applications
|
||||
|
||||
| App | Path |
|
||||
|-----|------|
|
||||
| Homepage | apps/homepage/ |
|
||||
| Kavita | apps/kavita-docker-automation/ |
|
||||
| Linkding | apps/linkding/ |
|
||||
| Forgejo | apps/forgejo/ |
|
||||
| Grafana | apps/grafana/ |
|
||||
| Prometheus | apps/prometheus/ |
|
||||
| Authentik | apps/authentik/ |
|
||||
| Open WebUI | apps/open-webui/ |
|
||||
| LiteLLM | apps/litellm/ |
|
||||
|
||||
## All Documentation Versions
|
||||
|
||||
- [v1.2](KiteStacks-Homelab-Documentation-v1.2.md)
|
||||
- [v1.3](KiteStacks-Homelab-Documentation-v1.3.md)
|
||||
- [v1.3.3](KiteStacks-Homelab-Documentation-v1.3.3.md)
|
||||
67
autosync/docs/KiteStacks-Homelab-Documentation-v1.3.4.md
Normal file
67
autosync/docs/KiteStacks-Homelab-Documentation-v1.3.4.md
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
# KiteStacks Homelab Documentation v1.3.4
|
||||
|
||||
**Version:** 1.3.4
|
||||
**Updated:** 2026-06-06 05:07:19
|
||||
**Previous:** [v1.3.3 docs](KiteStacks-Homelab-Documentation-v1.3.3.md)
|
||||
|
||||
---
|
||||
|
||||
## Change Summary
|
||||
|
||||
- [autosync] 2026-06-06 05:07:19 INFO Synced: apps/forgejo/data/gitea/gitea.db
|
||||
- [autosync] 2026-06-06 05:07:19 INFO Synced: apps/authentik/postgres/base/16384/17715
|
||||
- [autosync] 2026-06-06 05:07:19 INFO Synced: apps/authentik/postgres/pg_wal/000000010000000000000002
|
||||
- [autosync] 2026-06-06 05:07:19 INFO Synced: apps/authentik/postgres/base/16384/17733
|
||||
- [autosync] 2026-06-06 05:07:19 INFO Synced: apps/authentik/postgres/base/16384/17734
|
||||
- [autosync] 2026-06-06 05:07:19 INFO Synced: apps/authentik/postgres/base/16384/17735
|
||||
- [autosync] 2026-06-06 05:07:19 INFO Synced: apps/authentik/postgres/pg_xact/0000
|
||||
- [autosync] 2026-06-06 05:07:19 INFO Removed: apps/forgejo/data/gitea/gitea.db-journal
|
||||
- [autosync] 2026-06-06 05:07:19 INFO Synced: apps/linkding/data/tasks.sqlite3-wal
|
||||
- [autosync] 2026-06-06 05:07:19 INFO Synced: apps/authentik/postgres/base/16384/17722
|
||||
- [autosync] 2026-06-06 05:07:19 INFO Synced: apps/authentik/postgres/base/16384/17720
|
||||
- [autosync] 2026-06-06 05:07:19 INFO Synced: apps/authentik/postgres/global/pg_control
|
||||
- [autosync] 2026-06-06 05:07:19 INFO Synced: apps/authentik/postgres/pg_logical/replorigin_checkpoint
|
||||
- apps/forgejo/data/gitea/gitea.db
|
||||
- apps/authentik/postgres/base/16384/17715
|
||||
- apps/authentik/postgres/pg_wal/000000010000000000000002
|
||||
- apps/authentik/postgres/base/16384/17733
|
||||
- apps/authentik/postgres/base/16384/17734
|
||||
- apps/authentik/postgres/base/16384/17735
|
||||
- apps/authentik/postgres/pg_xact/0000
|
||||
- DELETED: apps/forgejo/data/gitea/gitea.db-journal
|
||||
- apps/linkding/data/tasks.sqlite3-wal
|
||||
- apps/authentik/postgres/base/16384/17722
|
||||
- apps/authentik/postgres/base/16384/17720
|
||||
- apps/authentik/postgres/global/pg_control
|
||||
- apps/authentik/postgres/pg_logical/replorigin_checkpoint
|
||||
|
||||
---
|
||||
|
||||
## Cluster
|
||||
|
||||
| Component | Status |
|
||||
|-----------|--------|
|
||||
| K3s | Active |
|
||||
| FluxCD | Planned |
|
||||
| Longhorn | Planned |
|
||||
|
||||
## Applications
|
||||
|
||||
| App | Path |
|
||||
|-----|------|
|
||||
| Homepage | apps/homepage/ |
|
||||
| Kavita | apps/kavita-docker-automation/ |
|
||||
| Linkding | apps/linkding/ |
|
||||
| Forgejo | apps/forgejo/ |
|
||||
| Grafana | apps/grafana/ |
|
||||
| Prometheus | apps/prometheus/ |
|
||||
| Authentik | apps/authentik/ |
|
||||
| Open WebUI | apps/open-webui/ |
|
||||
| LiteLLM | apps/litellm/ |
|
||||
|
||||
## All Documentation Versions
|
||||
|
||||
- [v1.2](KiteStacks-Homelab-Documentation-v1.2.md)
|
||||
- [v1.3](KiteStacks-Homelab-Documentation-v1.3.md)
|
||||
- [v1.3.3](KiteStacks-Homelab-Documentation-v1.3.3.md)
|
||||
- [v1.3.4](KiteStacks-Homelab-Documentation-v1.3.4.md)
|
||||
46
autosync/docs/KiteStacks-Homelab-Documentation-v1.3.5.md
Normal file
46
autosync/docs/KiteStacks-Homelab-Documentation-v1.3.5.md
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
# KiteStacks Homelab Documentation v1.3.5
|
||||
|
||||
**Version:** 1.3.5
|
||||
**Updated:** 2026-06-06 05:07:39
|
||||
**Previous:** [v1.3.4 docs](KiteStacks-Homelab-Documentation-v1.3.4.md)
|
||||
|
||||
---
|
||||
|
||||
## Change Summary
|
||||
|
||||
- [autosync] 2026-06-06 05:07:39 INFO Synced: apps/forgejo/data/gitea/gitea.db
|
||||
- [autosync] 2026-06-06 05:07:39 INFO Removed: apps/forgejo/data/gitea/gitea.db-journal
|
||||
- apps/forgejo/data/gitea/gitea.db
|
||||
- DELETED: apps/forgejo/data/gitea/gitea.db-journal
|
||||
|
||||
---
|
||||
|
||||
## Cluster
|
||||
|
||||
| Component | Status |
|
||||
|-----------|--------|
|
||||
| K3s | Active |
|
||||
| FluxCD | Planned |
|
||||
| Longhorn | Planned |
|
||||
|
||||
## Applications
|
||||
|
||||
| App | Path |
|
||||
|-----|------|
|
||||
| Homepage | apps/homepage/ |
|
||||
| Kavita | apps/kavita-docker-automation/ |
|
||||
| Linkding | apps/linkding/ |
|
||||
| Forgejo | apps/forgejo/ |
|
||||
| Grafana | apps/grafana/ |
|
||||
| Prometheus | apps/prometheus/ |
|
||||
| Authentik | apps/authentik/ |
|
||||
| Open WebUI | apps/open-webui/ |
|
||||
| LiteLLM | apps/litellm/ |
|
||||
|
||||
## All Documentation Versions
|
||||
|
||||
- [v1.2](KiteStacks-Homelab-Documentation-v1.2.md)
|
||||
- [v1.3](KiteStacks-Homelab-Documentation-v1.3.md)
|
||||
- [v1.3.3](KiteStacks-Homelab-Documentation-v1.3.3.md)
|
||||
- [v1.3.4](KiteStacks-Homelab-Documentation-v1.3.4.md)
|
||||
- [v1.3.5](KiteStacks-Homelab-Documentation-v1.3.5.md)
|
||||
55
autosync/docs/KiteStacks-Homelab-Documentation-v1.3.6.md
Normal file
55
autosync/docs/KiteStacks-Homelab-Documentation-v1.3.6.md
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
# KiteStacks Homelab Documentation v1.3.6
|
||||
|
||||
**Version:** 1.3.6
|
||||
**Updated:** 2026-06-06 05:08:20
|
||||
**Previous:** [v1.3.5 docs](KiteStacks-Homelab-Documentation-v1.3.5.md)
|
||||
|
||||
---
|
||||
|
||||
## Change Summary
|
||||
|
||||
- [autosync] 2026-06-06 05:08:20 INFO Synced: apps/forgejo/data/gitea/gitea.db
|
||||
- [autosync] 2026-06-06 05:08:20 INFO Removed: apps/kavita/config/kavita.db-shm
|
||||
- [autosync] 2026-06-06 05:08:20 INFO Synced: apps/kavita/config/kavita.db
|
||||
- [autosync] 2026-06-06 05:08:20 INFO Removed: apps/kavita/config/kavita.db-wal
|
||||
- [autosync] 2026-06-06 05:08:20 INFO Removed: apps/forgejo/data/gitea/gitea.db-journal
|
||||
- [autosync] 2026-06-06 05:08:20 INFO Synced: apps/linkding/data/tasks.sqlite3-wal
|
||||
- apps/forgejo/data/gitea/gitea.db
|
||||
- DELETED: apps/kavita/config/kavita.db-shm
|
||||
- apps/kavita/config/kavita.db
|
||||
- DELETED: apps/kavita/config/kavita.db-wal
|
||||
- DELETED: apps/forgejo/data/gitea/gitea.db-journal
|
||||
- apps/linkding/data/tasks.sqlite3-wal
|
||||
|
||||
---
|
||||
|
||||
## Cluster
|
||||
|
||||
| Component | Status |
|
||||
|-----------|--------|
|
||||
| K3s | Active |
|
||||
| FluxCD | Planned |
|
||||
| Longhorn | Planned |
|
||||
|
||||
## Applications
|
||||
|
||||
| App | Path |
|
||||
|-----|------|
|
||||
| Homepage | apps/homepage/ |
|
||||
| Kavita | apps/kavita-docker-automation/ |
|
||||
| Linkding | apps/linkding/ |
|
||||
| Forgejo | apps/forgejo/ |
|
||||
| Grafana | apps/grafana/ |
|
||||
| Prometheus | apps/prometheus/ |
|
||||
| Authentik | apps/authentik/ |
|
||||
| Open WebUI | apps/open-webui/ |
|
||||
| LiteLLM | apps/litellm/ |
|
||||
|
||||
## All Documentation Versions
|
||||
|
||||
- [v1.2](KiteStacks-Homelab-Documentation-v1.2.md)
|
||||
- [v1.3](KiteStacks-Homelab-Documentation-v1.3.md)
|
||||
- [v1.3.3](KiteStacks-Homelab-Documentation-v1.3.3.md)
|
||||
- [v1.3.4](KiteStacks-Homelab-Documentation-v1.3.4.md)
|
||||
- [v1.3.5](KiteStacks-Homelab-Documentation-v1.3.5.md)
|
||||
- [v1.3.6](KiteStacks-Homelab-Documentation-v1.3.6.md)
|
||||
48
autosync/docs/KiteStacks-Homelab-Documentation-v1.3.7.md
Normal file
48
autosync/docs/KiteStacks-Homelab-Documentation-v1.3.7.md
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
# KiteStacks Homelab Documentation v1.3.7
|
||||
|
||||
**Version:** 1.3.7
|
||||
**Updated:** 2026-06-06 05:08:38
|
||||
**Previous:** [v1.3.6 docs](KiteStacks-Homelab-Documentation-v1.3.6.md)
|
||||
|
||||
---
|
||||
|
||||
## Change Summary
|
||||
|
||||
- [autosync] 2026-06-06 05:08:38 INFO Synced: apps/forgejo/data/gitea/gitea.db
|
||||
- [autosync] 2026-06-06 05:08:38 INFO Removed: apps/forgejo/data/gitea/gitea.db-journal
|
||||
- apps/forgejo/data/gitea/gitea.db
|
||||
- DELETED: apps/forgejo/data/gitea/gitea.db-journal
|
||||
|
||||
---
|
||||
|
||||
## Cluster
|
||||
|
||||
| Component | Status |
|
||||
|-----------|--------|
|
||||
| K3s | Active |
|
||||
| FluxCD | Planned |
|
||||
| Longhorn | Planned |
|
||||
|
||||
## Applications
|
||||
|
||||
| App | Path |
|
||||
|-----|------|
|
||||
| Homepage | apps/homepage/ |
|
||||
| Kavita | apps/kavita-docker-automation/ |
|
||||
| Linkding | apps/linkding/ |
|
||||
| Forgejo | apps/forgejo/ |
|
||||
| Grafana | apps/grafana/ |
|
||||
| Prometheus | apps/prometheus/ |
|
||||
| Authentik | apps/authentik/ |
|
||||
| Open WebUI | apps/open-webui/ |
|
||||
| LiteLLM | apps/litellm/ |
|
||||
|
||||
## All Documentation Versions
|
||||
|
||||
- [v1.2](KiteStacks-Homelab-Documentation-v1.2.md)
|
||||
- [v1.3](KiteStacks-Homelab-Documentation-v1.3.md)
|
||||
- [v1.3.3](KiteStacks-Homelab-Documentation-v1.3.3.md)
|
||||
- [v1.3.4](KiteStacks-Homelab-Documentation-v1.3.4.md)
|
||||
- [v1.3.5](KiteStacks-Homelab-Documentation-v1.3.5.md)
|
||||
- [v1.3.6](KiteStacks-Homelab-Documentation-v1.3.6.md)
|
||||
- [v1.3.7](KiteStacks-Homelab-Documentation-v1.3.7.md)
|
||||
339
autosync/docs/KiteStacks-Homelab-Documentation-v1.3.md
Normal file
339
autosync/docs/KiteStacks-Homelab-Documentation-v1.3.md
Normal file
|
|
@ -0,0 +1,339 @@
|
|||
# KiteStacks Homelab Documentation v1.3
|
||||
|
||||
## Overview
|
||||
|
||||
KiteStacks Homelab is a self-hosted infrastructure platform designed around Kubernetes, GitOps, observability, AI services, digital libraries, productivity applications, and identity management.
|
||||
|
||||
The homelab is currently operating in a hybrid state:
|
||||
|
||||
* Existing applications continue running in Docker
|
||||
* Kubernetes (K3s) has been deployed successfully
|
||||
* Forgejo serves as the private Git platform
|
||||
* GitOps repository has been established
|
||||
* Migration to Kubernetes will occur incrementally
|
||||
|
||||
---
|
||||
|
||||
# Infrastructure
|
||||
|
||||
## Primary Server
|
||||
|
||||
Hostname:
|
||||
|
||||
```text
|
||||
assassin
|
||||
```
|
||||
|
||||
Role:
|
||||
|
||||
```text
|
||||
Primary Homelab Server
|
||||
Kubernetes Control Plane
|
||||
Docker Host
|
||||
GitOps Management Node
|
||||
```
|
||||
|
||||
Operating System:
|
||||
|
||||
```text
|
||||
Debian 13
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# Kubernetes
|
||||
|
||||
## Distribution
|
||||
|
||||
```text
|
||||
K3s
|
||||
```
|
||||
|
||||
Version:
|
||||
|
||||
```text
|
||||
v1.35.5+k3s1
|
||||
```
|
||||
|
||||
Status:
|
||||
|
||||
```text
|
||||
Ready
|
||||
```
|
||||
|
||||
Verification:
|
||||
|
||||
```bash
|
||||
kubectl get nodes
|
||||
```
|
||||
|
||||
Expected Result:
|
||||
|
||||
```text
|
||||
assassin Ready control-plane
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Kubernetes Components
|
||||
|
||||
Currently Running:
|
||||
|
||||
* CoreDNS
|
||||
* Metrics Server
|
||||
* Local Path Provisioner
|
||||
* Traefik Ingress Controller
|
||||
|
||||
Future Components:
|
||||
|
||||
* FluxCD
|
||||
* Longhorn
|
||||
* Cert-Manager
|
||||
* Cloudflare Tunnel
|
||||
* Authentik
|
||||
* Prometheus Operator
|
||||
|
||||
---
|
||||
|
||||
# GitOps
|
||||
|
||||
## Git Platform
|
||||
|
||||
```text
|
||||
Forgejo
|
||||
```
|
||||
|
||||
Repository:
|
||||
|
||||
```text
|
||||
https://gitforge.kitestacks.com/kenpat/kitestacks-homelab.git
|
||||
```
|
||||
|
||||
Visibility:
|
||||
|
||||
```text
|
||||
Private
|
||||
```
|
||||
|
||||
Purpose:
|
||||
|
||||
* Infrastructure as Code
|
||||
* Kubernetes manifests
|
||||
* Documentation
|
||||
* Application deployment
|
||||
* Disaster recovery
|
||||
|
||||
---
|
||||
|
||||
## Repository Structure
|
||||
|
||||
```text
|
||||
kitestacks-homelab
|
||||
├── apps
|
||||
├── clusters
|
||||
│ └── assassin
|
||||
├── docs
|
||||
├── infrastructure
|
||||
└── media
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# Current Docker Applications
|
||||
|
||||
## Dashboard
|
||||
|
||||
* Homepage
|
||||
|
||||
## Library Services
|
||||
|
||||
* Kavita
|
||||
|
||||
## Knowledge Management
|
||||
|
||||
* Linkding
|
||||
|
||||
## Source Control
|
||||
|
||||
* Forgejo
|
||||
|
||||
## Monitoring
|
||||
|
||||
* Grafana
|
||||
* Prometheus
|
||||
|
||||
---
|
||||
|
||||
# Planned Services
|
||||
|
||||
## Identity
|
||||
|
||||
* Authentik
|
||||
|
||||
## AI
|
||||
|
||||
* Open WebUI
|
||||
* LiteLLM
|
||||
|
||||
## Productivity
|
||||
|
||||
* Plane
|
||||
* OpenProject (evaluation)
|
||||
|
||||
## Media
|
||||
|
||||
* Audiobookshelf
|
||||
|
||||
---
|
||||
|
||||
# Kavita
|
||||
|
||||
## Library Location
|
||||
|
||||
```text
|
||||
/home/kenpat/library/books
|
||||
```
|
||||
|
||||
## Current Status
|
||||
|
||||
Running in Docker.
|
||||
|
||||
Future migration planned to Kubernetes.
|
||||
|
||||
---
|
||||
|
||||
# Cloudflare
|
||||
|
||||
## Domain
|
||||
|
||||
```text
|
||||
kitestacks.com
|
||||
```
|
||||
|
||||
Current usage:
|
||||
|
||||
* Reverse proxy
|
||||
* Public application access
|
||||
* DNS management
|
||||
|
||||
Future usage:
|
||||
|
||||
* Kubernetes ingress
|
||||
* Cloudflare Tunnel
|
||||
* Zero Trust
|
||||
|
||||
---
|
||||
|
||||
# Monitoring Strategy
|
||||
|
||||
Primary Monitoring:
|
||||
|
||||
* Grafana
|
||||
|
||||
Metrics Collection:
|
||||
|
||||
* Prometheus
|
||||
|
||||
Future Monitoring:
|
||||
|
||||
* Kubernetes cluster metrics
|
||||
* Longhorn metrics
|
||||
* Application metrics
|
||||
* Node metrics
|
||||
|
||||
---
|
||||
|
||||
# Migration Roadmap
|
||||
|
||||
## Phase 1 (Completed)
|
||||
|
||||
* Docker Homelab
|
||||
* Forgejo Deployment
|
||||
* K3s Installation
|
||||
* GitOps Repository Creation
|
||||
|
||||
## Phase 2 (Current)
|
||||
|
||||
* FluxCD Installation
|
||||
* GitOps Bootstrap
|
||||
* Longhorn Deployment
|
||||
|
||||
## Phase 3
|
||||
|
||||
* Homepage Migration
|
||||
* Linkding Migration
|
||||
|
||||
## Phase 4
|
||||
|
||||
* Kavita Migration
|
||||
* Forgejo Migration
|
||||
|
||||
## Phase 5
|
||||
|
||||
* Grafana Migration
|
||||
* Prometheus Migration
|
||||
|
||||
## Phase 6
|
||||
|
||||
* Authentik Deployment
|
||||
* Open WebUI Deployment
|
||||
* LiteLLM Deployment
|
||||
|
||||
---
|
||||
|
||||
# Backup Strategy
|
||||
|
||||
Planned:
|
||||
|
||||
* Git repository backups
|
||||
* Application volume backups
|
||||
* Longhorn snapshots
|
||||
* Off-site backups
|
||||
|
||||
---
|
||||
|
||||
# Version History
|
||||
|
||||
## Version 1.1
|
||||
|
||||
Initial Homelab Documentation
|
||||
|
||||
## Version 1.2
|
||||
|
||||
K3s Installation Milestone
|
||||
|
||||
## Version 1.3
|
||||
|
||||
Forgejo GitOps Repository Established
|
||||
K3s Operational
|
||||
Documentation Baseline Created
|
||||
Ready for FluxCD Deployment
|
||||
|
||||
---
|
||||
|
||||
# Current Status
|
||||
|
||||
Homelab State:
|
||||
|
||||
```text
|
||||
Operational
|
||||
```
|
||||
|
||||
Kubernetes State:
|
||||
|
||||
```text
|
||||
Ready
|
||||
```
|
||||
|
||||
GitOps State:
|
||||
|
||||
```text
|
||||
Repository Created
|
||||
Awaiting FluxCD Integration
|
||||
```
|
||||
|
||||
Next Milestone:
|
||||
|
||||
```text
|
||||
FluxCD Installation and Forgejo Integration
|
||||
```
|
||||
102
autosync/promote.sh
Normal file
102
autosync/promote.sh
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
#!/bin/bash
|
||||
# =============================================================================
|
||||
# promote.sh
|
||||
# Flips the watcher from TEST repo → kenpat/kitestacks-homelab (main).
|
||||
# Run after verifying the test repo looks correct in Forgejo.
|
||||
# =============================================================================
|
||||
|
||||
set -e
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
CONFIG_FILE="$SCRIPT_DIR/config/settings.conf"
|
||||
STATE_FILE="/opt/kitestacks-autosync/.active_target"
|
||||
WORK_DIR="/opt/kitestacks-autosync"
|
||||
|
||||
GREEN='\033[0;32m'; YELLOW='\033[1;33m'; RED='\033[0;31m'; NC='\033[0m'
|
||||
info() { echo -e "${GREEN}[INFO]${NC} $1"; }
|
||||
warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
|
||||
error() { echo -e "${RED}[ERROR]${NC} $1"; exit 1; }
|
||||
|
||||
[[ $EUID -ne 0 ]] && error "Run as root: sudo bash promote.sh"
|
||||
source "$CONFIG_FILE"
|
||||
|
||||
CURRENT="$(cat "$STATE_FILE" 2>/dev/null || echo "test")"
|
||||
if [[ "$CURRENT" == "main" ]]; then
|
||||
warn "Already targeting kenpat/kitestacks-homelab. Nothing to do."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo ""
|
||||
warn "┌──────────────────────────────────────────────────────────────┐"
|
||||
warn "│ PROMOTE: test repo → kenpat/kitestacks-homelab │"
|
||||
warn "│ │"
|
||||
warn "│ From: ${FORGEJO_URL}/${FORGEJO_USER}/${TEST_REPO}"
|
||||
warn "│ To : ${FORGEJO_URL}/${FORGEJO_USER}/${MAIN_REPO}"
|
||||
warn "│ │"
|
||||
warn "│ All future auto-commits will go to the MAIN repo. │"
|
||||
warn "└──────────────────────────────────────────────────────────────┘"
|
||||
echo ""
|
||||
read -rp "Have you verified the test repo looks correct? Type 'promote' to confirm: " CONFIRM
|
||||
[[ "$CONFIRM" != "promote" ]] && { info "Aborted."; exit 0; }
|
||||
|
||||
# ── Set up remote prefix ──────────────────────────────────────────────────────
|
||||
if [[ "$AUTH_METHOD" == "ssh" ]]; then
|
||||
REMOTE_BASE="git@gitforge.kitestacks.com:${FORGEJO_USER}"
|
||||
else
|
||||
REMOTE_BASE="https://gitforge.kitestacks.com/${FORGEJO_USER}"
|
||||
fi
|
||||
|
||||
# ── Clone main repo if not already present ────────────────────────────────────
|
||||
MAIN_DIR="$WORK_DIR/$MAIN_REPO"
|
||||
if [[ ! -d "$MAIN_DIR/.git" ]]; then
|
||||
info "Cloning kenpat/kitestacks-homelab..."
|
||||
git clone "${REMOTE_BASE}/${MAIN_REPO}.git" "$MAIN_DIR"
|
||||
fi
|
||||
|
||||
cd "$MAIN_DIR"
|
||||
git config user.email "$GIT_EMAIL"
|
||||
git config user.name "$GIT_NAME"
|
||||
git pull --rebase origin HEAD 2>/dev/null || true
|
||||
|
||||
# ── Add version tag to main README.md if not already there ───────────────────
|
||||
if ! grep -q '<!-- version:' "$MAIN_DIR/README.md" 2>/dev/null; then
|
||||
info "Injecting version tag into main README.md..."
|
||||
|
||||
# Get the latest version from the test repo
|
||||
TEST_VER=$(grep -oP '(?<=version:\s)[\d.]+' "$WORK_DIR/$TEST_REPO/README.md" 2>/dev/null | head -1 || echo "1.3.0")
|
||||
|
||||
# Inject after the first H1
|
||||
sed -i "0,/^# /{s|^# \(.*\)|# \1\n\n<!-- version: $TEST_VER -->|}" "$MAIN_DIR/README.md"
|
||||
|
||||
# Update the docs reference line
|
||||
sed -i "s|docs/KiteStacks-Homelab-Documentation-v.*\.md|docs/KiteStacks-Homelab-Documentation-v${TEST_VER}.md|" "$MAIN_DIR/README.md" || true
|
||||
|
||||
# Copy CHANGELOG.md and docs from test repo if main doesn't have them
|
||||
if [[ ! -f "$MAIN_DIR/CHANGELOG.md" ]]; then
|
||||
cp "$WORK_DIR/$TEST_REPO/CHANGELOG.md" "$MAIN_DIR/CHANGELOG.md" 2>/dev/null || true
|
||||
fi
|
||||
if [[ ! -d "$MAIN_DIR/docs" ]]; then
|
||||
cp -r "$WORK_DIR/$TEST_REPO/docs" "$MAIN_DIR/docs" 2>/dev/null || true
|
||||
fi
|
||||
|
||||
git add -A
|
||||
git commit -m "chore: inject autosync version tracking — promoted from test"
|
||||
git push origin HEAD
|
||||
info "Main repo prepared for autosync."
|
||||
fi
|
||||
|
||||
# ── Flip state ────────────────────────────────────────────────────────────────
|
||||
echo "main" > "$STATE_FILE"
|
||||
info "Active target → main (kenpat/kitestacks-homelab)"
|
||||
|
||||
# ── Restart service ───────────────────────────────────────────────────────────
|
||||
info "Restarting kitestacks-autosync service..."
|
||||
systemctl restart kitestacks-autosync
|
||||
sleep 2
|
||||
systemctl status kitestacks-autosync --no-pager
|
||||
|
||||
echo ""
|
||||
echo -e "${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
||||
info "Promotion complete!"
|
||||
info " Now watching → ${FORGEJO_URL}/${FORGEJO_USER}/${MAIN_REPO}"
|
||||
echo -e "${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
||||
319
autosync/scripts/kitestacks-watcher-sh
Normal file
319
autosync/scripts/kitestacks-watcher-sh
Normal file
|
|
@ -0,0 +1,319 @@
|
|||
#!/bin/bash
|
||||
# =============================================================================
|
||||
# kitestacks-watcher.sh
|
||||
# Runs as a systemd service. Watches configured directories, then on change:
|
||||
# 1. Pulls latest from Forgejo (avoid conflicts)
|
||||
# 2. Copies changed files into the correct apps/ or clusters/ folder
|
||||
# 3. Bumps the version in README.md and the docs/ changelog file
|
||||
# 4. Commits with a descriptive message
|
||||
# 5. Pushes to active repo (test first, then main after promote.sh)
|
||||
# =============================================================================
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
CONFIG_FILE="$SCRIPT_DIR/../config/settings.conf"
|
||||
STATE_FILE="/opt/kitestacks-autosync/.active_target"
|
||||
WORK_DIR="/opt/kitestacks-autosync"
|
||||
|
||||
log() { echo "[autosync] $(date '+%Y-%m-%d %H:%M:%S') INFO $*"; }
|
||||
warn() { echo "[autosync] $(date '+%Y-%m-%d %H:%M:%S') WARN $*"; }
|
||||
err() { echo "[autosync] $(date '+%Y-%m-%d %H:%M:%S') ERROR $*" >&2; }
|
||||
|
||||
# ── Load config ──────────────────────────────────────────────────────────────
|
||||
[[ ! -f "$CONFIG_FILE" ]] && { err "Config not found: $CONFIG_FILE"; exit 1; }
|
||||
source "$CONFIG_FILE"
|
||||
|
||||
# ── Helpers ───────────────────────────────────────────────────────────────────
|
||||
active_target() { cat "$STATE_FILE" 2>/dev/null || echo "test"; }
|
||||
|
||||
active_repo() {
|
||||
[[ "$(active_target)" == "main" ]] && echo "$MAIN_REPO" || echo "$TEST_REPO"
|
||||
}
|
||||
|
||||
repo_dir() { echo "$WORK_DIR/$(active_repo)"; }
|
||||
|
||||
# ── Version management ────────────────────────────────────────────────────────
|
||||
read_version() {
|
||||
local readme="$(repo_dir)/README.md"
|
||||
# Look for a line like: <!-- version: 1.3.2 --> or **Version:** 1.3.2
|
||||
local ver
|
||||
ver=$(grep -oP '(?<=version:\s)[\d.]+' "$readme" 2>/dev/null | head -1)
|
||||
[[ -z "$ver" ]] && ver=$(grep -oP '\d+\.\d+\.\d+' "$readme" 2>/dev/null | head -1)
|
||||
echo "${ver:-$VERSION_SEED}"
|
||||
}
|
||||
|
||||
bump_version() {
|
||||
local ver="$1"
|
||||
IFS='.' read -ra p <<< "$ver"
|
||||
local maj="${p[0]:-1}" min="${p[1]:-3}" pat="${p[2]:-0}"
|
||||
case "${VERSION_BUMP:-patch}" in
|
||||
major) echo "$((maj+1)).0.0" ;;
|
||||
minor) echo "${maj}.$((min+1)).0" ;;
|
||||
*) echo "${maj}.${min}.$((pat+1))" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
# ── README.md update ──────────────────────────────────────────────────────────
|
||||
# Maintains the KiteStacks README.md format with version tracking.
|
||||
update_readme() {
|
||||
local dir="$1" new_ver="$2" old_ver="$3"
|
||||
local readme="$dir/README.md"
|
||||
local ts; ts="$(date '+%Y-%m-%d %H:%M:%S')"
|
||||
|
||||
# If version comment tag exists, update it; otherwise append one after title
|
||||
if grep -q '<!-- version:' "$readme" 2>/dev/null; then
|
||||
sed -i "s|<!-- version:.*-->|<!-- version: $new_ver -->|" "$readme"
|
||||
else
|
||||
# Inject version tag after the first H1 line
|
||||
sed -i "0,/^# /{s|^# \(.*\)|# \1\n\n<!-- version: $new_ver -->|}" "$readme"
|
||||
fi
|
||||
|
||||
log "README.md version tag: $old_ver → $new_ver"
|
||||
}
|
||||
|
||||
# ── Docs changelog update ─────────────────────────────────────────────────────
|
||||
# Updates docs/KiteStacks-Homelab-Documentation-v<version>.md
|
||||
# Creates a new versioned doc file and a CHANGELOG.md entry.
|
||||
update_docs() {
|
||||
local dir="$1" new_ver="$2" changed_files="$3"
|
||||
local ts; ts="$(date '+%Y-%m-%d %H:%M:%S')"
|
||||
local docs_dir="$dir/docs"
|
||||
mkdir -p "$docs_dir"
|
||||
|
||||
# ── New versioned doc file ────────────────────────────────────────────────
|
||||
local doc_file="$docs_dir/KiteStacks-Homelab-Documentation-v${new_ver}.md"
|
||||
cat > "$doc_file" <<EOF
|
||||
# KiteStacks Homelab Documentation v${new_ver}
|
||||
|
||||
**Version:** ${new_ver}
|
||||
**Updated:** ${ts}
|
||||
**Previous:** [v${2%.*}.$(( ${new_ver##*.} - 1 )) docs](KiteStacks-Homelab-Documentation-v${2%.*}.$(( ${new_ver##*.} - 1 )).md)
|
||||
|
||||
---
|
||||
|
||||
## Change Summary
|
||||
|
||||
$(echo "$changed_files" | sed 's/^/- /')
|
||||
|
||||
---
|
||||
|
||||
## Cluster
|
||||
|
||||
| Component | Status |
|
||||
|-----------|--------|
|
||||
| K3s | Active |
|
||||
| FluxCD | Planned |
|
||||
| Longhorn | Planned |
|
||||
|
||||
## Applications
|
||||
|
||||
| App | Path |
|
||||
|-----|------|
|
||||
| Homepage | apps/homepage/ |
|
||||
| Kavita | apps/kavita-docker-automation/ |
|
||||
| Linkding | apps/linkding/ |
|
||||
| Forgejo | apps/forgejo/ |
|
||||
| Grafana | apps/grafana/ |
|
||||
| Prometheus | apps/prometheus/ |
|
||||
| Authentik | apps/authentik/ |
|
||||
| Open WebUI | apps/open-webui/ |
|
||||
| LiteLLM | apps/litellm/ |
|
||||
|
||||
## All Documentation Versions
|
||||
|
||||
$(ls "$docs_dir"/KiteStacks-Homelab-Documentation-v*.md 2>/dev/null \
|
||||
| sort -V \
|
||||
| while read f; do
|
||||
v=$(basename "$f" .md | grep -oP '[\d.]+$')
|
||||
echo "- [v${v}]($(basename "$f"))"
|
||||
done)
|
||||
EOF
|
||||
|
||||
log "Created doc file: $(basename "$doc_file")"
|
||||
|
||||
# ── CHANGELOG.md ─────────────────────────────────────────────────────────
|
||||
local changelog="$dir/CHANGELOG.md"
|
||||
if [[ ! -f "$changelog" ]]; then
|
||||
echo -e "# Changelog\n\nAll notable changes to KiteStacks Homelab are documented here.\n" > "$changelog"
|
||||
fi
|
||||
|
||||
# Prepend new entry after the header (after line 3)
|
||||
local entry
|
||||
entry=$(cat <<EOF
|
||||
|
||||
## [v${new_ver}] — ${ts}
|
||||
|
||||
### Changed
|
||||
$(echo "$changed_files" | sed 's/^/- /')
|
||||
|
||||
EOF
|
||||
)
|
||||
# Insert after line 3 of the changelog
|
||||
local tmp; tmp=$(mktemp)
|
||||
head -3 "$changelog" > "$tmp"
|
||||
echo "$entry" >> "$tmp"
|
||||
tail -n +4 "$changelog" >> "$tmp"
|
||||
mv "$tmp" "$changelog"
|
||||
|
||||
log "CHANGELOG.md updated with v${new_ver}"
|
||||
}
|
||||
|
||||
# ── Map a filesystem path to a repo subfolder ─────────────────────────────────
|
||||
# Files from ~/docker/homepage → apps/homepage/
|
||||
# Files from ~/docker/ → apps/<dirname>/
|
||||
# Files from /etc/kitestacks/ → clusters/assassin/
|
||||
map_to_repo_path() {
|
||||
local src="$1"
|
||||
local rel=""
|
||||
|
||||
for watch in $WATCH_DIRS; do
|
||||
if [[ "$src" == "$watch"* ]]; then
|
||||
rel="${src#$watch/}"
|
||||
# Top-level dir under watch becomes the app folder
|
||||
local top; top=$(echo "$rel" | cut -d'/' -f1)
|
||||
if [[ "$watch" == *docker* ]]; then
|
||||
echo "apps/${rel}"
|
||||
else
|
||||
echo "clusters/assassin/${rel}"
|
||||
fi
|
||||
return
|
||||
fi
|
||||
done
|
||||
# Fallback
|
||||
echo "server-files/${src#/}"
|
||||
}
|
||||
|
||||
# ── Sync changed files into the workspace ─────────────────────────────────────
|
||||
sync_files() {
|
||||
local repo="$1"; shift
|
||||
local files=("$@")
|
||||
local synced=()
|
||||
|
||||
for src in "${files[@]}"; do
|
||||
local dest_rel; dest_rel="$(map_to_repo_path "$src")"
|
||||
local dest="$repo/$dest_rel"
|
||||
mkdir -p "$(dirname "$dest")"
|
||||
|
||||
if [[ -f "$src" ]]; then
|
||||
cp -p "$src" "$dest"
|
||||
synced+=("$dest_rel")
|
||||
log "Synced: $dest_rel"
|
||||
else
|
||||
rm -f "$dest"
|
||||
synced+=("DELETED: $dest_rel")
|
||||
log "Removed: $dest_rel"
|
||||
fi
|
||||
done
|
||||
|
||||
printf '%s\n' "${synced[@]}"
|
||||
}
|
||||
|
||||
# ── Commit and push ───────────────────────────────────────────────────────────
|
||||
commit_and_push() {
|
||||
local dir="$1" version="$2" file_count="$3"
|
||||
cd "$dir"
|
||||
|
||||
git add -A
|
||||
|
||||
if git diff --cached --quiet; then
|
||||
log "Nothing to commit."
|
||||
return 0
|
||||
fi
|
||||
|
||||
local target; target="$(active_target)"
|
||||
local msg="Automated update: $(date '+%Y-%m-%d %H:%M:%S')"
|
||||
# Match the commit style already in your repo: "Automated update: YYYY-MM-DD HH:MM:SS"
|
||||
|
||||
git commit -m "$msg"
|
||||
|
||||
log "Pushing to ${target} repo ($(active_repo))..."
|
||||
if git push origin HEAD 2>&1; then
|
||||
log "✓ Push OK — v${version}"
|
||||
else
|
||||
err "Push failed. Will retry on next change."
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# ── Should this path be skipped? ─────────────────────────────────────────────
|
||||
is_excluded() {
|
||||
local path="$1"
|
||||
for pat in $EXCLUDE_PATTERNS; do
|
||||
case "$path" in $pat) return 0 ;; esac
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
# ════════════════════════════════════════════════════════════════════════════
|
||||
# MAIN
|
||||
# ════════════════════════════════════════════════════════════════════════════
|
||||
mkdir -p "$WORK_DIR"
|
||||
log "KiteStacks AutoSync starting up"
|
||||
log "Active target : $(active_target) → $(active_repo)"
|
||||
log "Watching : $WATCH_DIRS"
|
||||
log "Debounce : ${DEBOUNCE_SECONDS}s"
|
||||
|
||||
# Pull latest before we start watching
|
||||
RDIR="$(repo_dir)"
|
||||
if [[ -d "$RDIR/.git" ]]; then
|
||||
cd "$RDIR"
|
||||
git pull --rebase origin HEAD 2>/dev/null && log "Pulled latest from remote." || warn "Pull failed — continuing anyway."
|
||||
fi
|
||||
|
||||
declare -A PENDING
|
||||
LAST_EVENT=0
|
||||
|
||||
# Build the inotifywait argument list from WATCH_DIRS
|
||||
IFS=' ' read -ra WATCH_ARRAY <<< "$WATCH_DIRS"
|
||||
|
||||
inotifywait -m -r \
|
||||
-e modify,create,delete,moved_to,moved_from \
|
||||
--format '%w%f' \
|
||||
--exclude '\.git' \
|
||||
"${WATCH_ARRAY[@]}" 2>/dev/null |
|
||||
|
||||
while IFS= read -r changed; do
|
||||
|
||||
is_excluded "$changed" && continue
|
||||
PENDING["$changed"]=1
|
||||
LAST_EVENT=$(date +%s)
|
||||
|
||||
# ── Debounce loop ─────────────────────────────────────────────────────────
|
||||
while true; do
|
||||
sleep 1
|
||||
ELAPSED=$(( $(date +%s) - LAST_EVENT ))
|
||||
[[ $ELAPSED -ge ${DEBOUNCE_SECONDS:-15} ]] && break
|
||||
|
||||
# Drain any additional events that arrived during sleep
|
||||
while IFS= read -r -t 0.1 extra; do
|
||||
is_excluded "$extra" && continue
|
||||
PENDING["$extra"]=1
|
||||
LAST_EVENT=$(date +%s)
|
||||
done
|
||||
done
|
||||
|
||||
[[ ${#PENDING[@]} -eq 0 ]] && continue
|
||||
|
||||
log "━━ Batch of ${#PENDING[@]} change(s) detected ━━"
|
||||
|
||||
RDIR="$(repo_dir)"
|
||||
OLD_VER="$(read_version)"
|
||||
NEW_VER="$(bump_version "$OLD_VER")"
|
||||
|
||||
# Pull before committing
|
||||
cd "$RDIR"
|
||||
git pull --rebase origin HEAD 2>/dev/null || warn "Pre-commit pull failed."
|
||||
|
||||
# Sync files and capture the list of repo-relative paths
|
||||
CHANGED_LIST="$(sync_files "$RDIR" "${!PENDING[@]}")"
|
||||
|
||||
update_readme "$RDIR" "$NEW_VER" "$OLD_VER"
|
||||
update_docs "$RDIR" "$NEW_VER" "$CHANGED_LIST"
|
||||
commit_and_push "$RDIR" "$NEW_VER" "${#PENDING[@]}"
|
||||
|
||||
# Clear batch
|
||||
unset PENDING
|
||||
declare -A PENDING
|
||||
|
||||
done
|
||||
244
autosync/setup.sh
Normal file
244
autosync/setup.sh
Normal file
|
|
@ -0,0 +1,244 @@
|
|||
#!/bin/bash
|
||||
# =============================================================================
|
||||
# KiteStacks HomelaB — Auto-Sync Setup
|
||||
# Run once as root: sudo bash setup.sh
|
||||
# =============================================================================
|
||||
|
||||
set -e
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
CONFIG_FILE="$SCRIPT_DIR/config/settings.conf"
|
||||
WORK_DIR="/opt/kitestacks-autosync"
|
||||
STATE_FILE="$WORK_DIR/.active_target"
|
||||
|
||||
GREEN='\033[0;32m'; YELLOW='\033[1;33m'; RED='\033[0;31m'; CYAN='\033[0;36m'; NC='\033[0m'
|
||||
info() { echo -e "${GREEN}[INFO]${NC} $1"; }
|
||||
warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
|
||||
error() { echo -e "${RED}[ERROR]${NC} $1"; exit 1; }
|
||||
step() { echo -e "\n${CYAN}══ $1 ══${NC}"; }
|
||||
|
||||
[[ $EUID -ne 0 ]] && error "Run as root: sudo bash setup.sh"
|
||||
|
||||
# ── Config check ─────────────────────────────────────────────────────────────
|
||||
if [[ ! -f "$CONFIG_FILE" ]]; then
|
||||
info "Copying example config..."
|
||||
cp "$SCRIPT_DIR/config/settings.conf.example" "$CONFIG_FILE"
|
||||
warn "Please edit $CONFIG_FILE and set FORGEJO_TOKEN and WATCH_DIRS, then re-run."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
source "$CONFIG_FILE"
|
||||
|
||||
[[ "$FORGEJO_TOKEN" == "PASTE_YOUR_TOKEN_HERE" ]] && \
|
||||
error "You haven't set FORGEJO_TOKEN in config/settings.conf yet."
|
||||
[[ -z "$FORGEJO_TOKEN" ]] && error "FORGEJO_TOKEN is empty in config/settings.conf."
|
||||
|
||||
# ── Dependencies ─────────────────────────────────────────────────────────────
|
||||
step "Installing dependencies"
|
||||
apt-get update -qq
|
||||
apt-get install -y -qq git inotify-tools curl jq
|
||||
info "Dependencies installed."
|
||||
|
||||
# ── Configure git credentials ─────────────────────────────────────────────────
|
||||
step "Configuring git credentials"
|
||||
if [[ "$AUTH_METHOD" == "ssh" ]]; then
|
||||
if [[ ! -f "$SSH_KEY_PATH" ]]; then
|
||||
info "Generating SSH key at $SSH_KEY_PATH..."
|
||||
ssh-keygen -t ed25519 -C "kitestacks-autosync@$(hostname)" -f "$SSH_KEY_PATH" -N ""
|
||||
echo ""
|
||||
warn "Add this public key to Forgejo:"
|
||||
warn " gitforge.kitestacks.com → User Settings → SSH / GPG Keys → Add Key"
|
||||
echo "────────────────────────────────────────────────────────"
|
||||
cat "${SSH_KEY_PATH}.pub"
|
||||
echo "────────────────────────────────────────────────────────"
|
||||
read -rp "Press Enter after adding the key to Forgejo..."
|
||||
fi
|
||||
REMOTE_BASE="git@gitforge.kitestacks.com:${FORGEJO_USER}"
|
||||
else
|
||||
# HTTPS token auth
|
||||
git config --global credential.helper store
|
||||
# Write credential (idempotent — grep prevents duplicates)
|
||||
local CRED_LINE="https://${FORGEJO_USER}:${FORGEJO_TOKEN}@gitforge.kitestacks.com"
|
||||
grep -qF "$CRED_LINE" ~/.git-credentials 2>/dev/null || echo "$CRED_LINE" >> ~/.git-credentials
|
||||
REMOTE_BASE="https://gitforge.kitestacks.com/${FORGEJO_USER}"
|
||||
fi
|
||||
info "Credentials configured (method: $AUTH_METHOD)"
|
||||
|
||||
# ── Verify Forgejo API ────────────────────────────────────────────────────────
|
||||
step "Verifying Forgejo connectivity"
|
||||
HTTP=$(curl -s -o /dev/null -w "%{http_code}" --max-time 8 \
|
||||
-H "Authorization: token $FORGEJO_TOKEN" \
|
||||
"${FORGEJO_URL}/api/v1/user")
|
||||
[[ "$HTTP" != "200" ]] && error "Forgejo API returned HTTP $HTTP — check FORGEJO_URL and FORGEJO_TOKEN."
|
||||
info "Forgejo API reachable ✓"
|
||||
|
||||
# ── Create test repo on Forgejo if needed ─────────────────────────────────────
|
||||
step "Setting up test repo: $TEST_REPO"
|
||||
HTTP=$(curl -s -o /dev/null -w "%{http_code}" \
|
||||
-H "Authorization: token $FORGEJO_TOKEN" \
|
||||
"${FORGEJO_URL}/api/v1/repos/${FORGEJO_USER}/${TEST_REPO}")
|
||||
|
||||
if [[ "$HTTP" == "404" ]]; then
|
||||
info "Creating test repo on Forgejo..."
|
||||
RESULT=$(curl -s -X POST \
|
||||
-H "Authorization: token $FORGEJO_TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{
|
||||
\"name\": \"${TEST_REPO}\",
|
||||
\"description\": \"AutoSync test — KiteStacks Homelab\",
|
||||
\"private\": true,
|
||||
\"auto_init\": true,
|
||||
\"default_branch\": \"main\"
|
||||
}" \
|
||||
"${FORGEJO_URL}/api/v1/user/repos")
|
||||
URL=$(echo "$RESULT" | jq -r '.html_url // empty')
|
||||
[[ -n "$URL" ]] && info "Test repo created: $URL" || warn "Repo creation response was unexpected — check Forgejo."
|
||||
else
|
||||
info "Test repo already exists."
|
||||
fi
|
||||
|
||||
# ── Clone repos ───────────────────────────────────────────────────────────────
|
||||
step "Cloning repos into $WORK_DIR"
|
||||
mkdir -p "$WORK_DIR"
|
||||
|
||||
# Test repo
|
||||
TEST_DIR="$WORK_DIR/$TEST_REPO"
|
||||
if [[ ! -d "$TEST_DIR/.git" ]]; then
|
||||
info "Cloning test repo..."
|
||||
git clone "${REMOTE_BASE}/${TEST_REPO}.git" "$TEST_DIR"
|
||||
else
|
||||
info "Test repo already cloned."
|
||||
fi
|
||||
|
||||
cd "$TEST_DIR"
|
||||
git config user.email "$GIT_EMAIL"
|
||||
git config user.name "$GIT_NAME"
|
||||
|
||||
# ── Bootstrap README.md from main repo content ───────────────────────────────
|
||||
step "Bootstrapping README.md in test repo"
|
||||
if [[ ! -f "$TEST_DIR/README.md" ]] || ! grep -q 'version:' "$TEST_DIR/README.md" 2>/dev/null; then
|
||||
cat > "$TEST_DIR/README.md" <<'EOF'
|
||||
# KiteStacks Homelab
|
||||
|
||||
<!-- version: 1.3.0 -->
|
||||
|
||||
Private GitOps repository for the KiteStacks homelab.
|
||||
|
||||
## Cluster
|
||||
|
||||
- K3s
|
||||
- FluxCD (planned)
|
||||
- Longhorn (planned)
|
||||
|
||||
## Applications
|
||||
|
||||
- Homepage
|
||||
- Kavita
|
||||
- Linkding
|
||||
- Forgejo
|
||||
- Grafana
|
||||
- Prometheus
|
||||
- Authentik
|
||||
- Open WebUI
|
||||
- LiteLLM
|
||||
|
||||
## Documentation
|
||||
|
||||
docs/KiteStacks-Homelab-Documentation-v1.3.0.md
|
||||
EOF
|
||||
|
||||
mkdir -p "$TEST_DIR/docs"
|
||||
cat > "$TEST_DIR/docs/KiteStacks-Homelab-Documentation-v1.3.0.md" <<'EOF'
|
||||
# KiteStacks Homelab Documentation v1.3.0
|
||||
|
||||
**Version:** 1.3.0
|
||||
**Updated:** Initial autosync bootstrap
|
||||
|
||||
---
|
||||
|
||||
## Cluster
|
||||
|
||||
| Component | Status |
|
||||
|-----------|--------|
|
||||
| K3s | Active |
|
||||
| FluxCD | Planned |
|
||||
| Longhorn | Planned |
|
||||
|
||||
## Applications
|
||||
|
||||
| App | Path |
|
||||
|-----|------|
|
||||
| Homepage | apps/homepage/ |
|
||||
| Kavita | apps/kavita-docker-automation/ |
|
||||
| Linkding | apps/linkding/ |
|
||||
| Forgejo | apps/forgejo/ |
|
||||
| Grafana | apps/grafana/ |
|
||||
| Prometheus | apps/prometheus/ |
|
||||
| Authentik | apps/authentik/ |
|
||||
| Open WebUI | apps/open-webui/ |
|
||||
| LiteLLM | apps/litellm/ |
|
||||
EOF
|
||||
|
||||
cat > "$TEST_DIR/CHANGELOG.md" <<'EOF'
|
||||
# Changelog
|
||||
|
||||
All notable changes to KiteStacks Homelab are documented here.
|
||||
|
||||
## [v1.3.0] — Initial autosync bootstrap
|
||||
|
||||
- Automated sync system installed
|
||||
EOF
|
||||
|
||||
git add -A
|
||||
git commit -m "chore: bootstrap autosync README and docs v1.3.0"
|
||||
git push origin HEAD
|
||||
info "Test repo bootstrapped with v1.3.0 content."
|
||||
else
|
||||
info "README.md already has version tag — skipping bootstrap."
|
||||
fi
|
||||
|
||||
# ── Set initial state to test ─────────────────────────────────────────────────
|
||||
echo "test" > "$STATE_FILE"
|
||||
|
||||
# ── Install systemd service ───────────────────────────────────────────────────
|
||||
step "Installing systemd service"
|
||||
cat > /etc/systemd/system/kitestacks-autosync.service <<EOF
|
||||
[Unit]
|
||||
Description=KiteStacks HomelaB Auto-Sync
|
||||
After=network-online.target
|
||||
Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
ExecStart=/bin/bash ${SCRIPT_DIR}/scripts/kitestacks-watcher.sh
|
||||
Restart=on-failure
|
||||
RestartSec=15
|
||||
StandardOutput=journal
|
||||
StandardError=journal
|
||||
SyslogIdentifier=kitestacks-autosync
|
||||
Environment=HOME=/root
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
|
||||
systemctl daemon-reload
|
||||
systemctl enable kitestacks-autosync
|
||||
systemctl restart kitestacks-autosync
|
||||
sleep 2
|
||||
|
||||
info "Service installed and started."
|
||||
systemctl status kitestacks-autosync --no-pager || true
|
||||
|
||||
echo ""
|
||||
echo -e "${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
||||
info "Setup complete!"
|
||||
echo ""
|
||||
info " Test repo : ${FORGEJO_URL}/${FORGEJO_USER}/${TEST_REPO}"
|
||||
info " Watching : $WATCH_DIRS"
|
||||
info " Live logs : journalctl -u kitestacks-autosync -f"
|
||||
info " Status : bash status.sh"
|
||||
echo ""
|
||||
warn "Next step: trigger a test change, then check the test repo."
|
||||
warn "Once verified, run: sudo bash promote.sh"
|
||||
echo -e "${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
||||
85
autosync/status.sh
Normal file
85
autosync/status.sh
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
#!/bin/bash
|
||||
# =============================================================================
|
||||
# status.sh — KiteStacks AutoSync health check
|
||||
# =============================================================================
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
CONFIG_FILE="$SCRIPT_DIR/config/settings.conf"
|
||||
STATE_FILE="/opt/kitestacks-autosync/.active_target"
|
||||
WORK_DIR="/opt/kitestacks-autosync"
|
||||
|
||||
BOLD='\033[1m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'
|
||||
RED='\033[0;31m'; CYAN='\033[0;36m'; NC='\033[0m'
|
||||
|
||||
ok() { echo -e " ${GREEN}✓${NC} $1"; }
|
||||
fail() { echo -e " ${RED}✗${NC} $1"; }
|
||||
info() { echo -e " ${CYAN}·${NC} $1"; }
|
||||
|
||||
source "$CONFIG_FILE" 2>/dev/null || { echo "Config not found at $CONFIG_FILE"; exit 1; }
|
||||
|
||||
echo -e "\n${BOLD}╔══ KiteStacks AutoSync Status ════════════════════════════════╗${NC}"
|
||||
|
||||
# ── Service ───────────────────────────────────────────────────────────────────
|
||||
echo -e "\n${BOLD}Service${NC}"
|
||||
if systemctl is-active --quiet kitestacks-autosync 2>/dev/null; then
|
||||
ok "kitestacks-autosync is running"
|
||||
else
|
||||
fail "kitestacks-autosync is NOT running"
|
||||
echo " → sudo systemctl start kitestacks-autosync"
|
||||
fi
|
||||
systemctl is-enabled --quiet kitestacks-autosync 2>/dev/null \
|
||||
&& ok "Enabled at boot" || info "Not enabled at boot"
|
||||
|
||||
# ── Active target ─────────────────────────────────────────────────────────────
|
||||
echo -e "\n${BOLD}Active Target${NC}"
|
||||
TARGET="$(cat "$STATE_FILE" 2>/dev/null || echo "test")"
|
||||
if [[ "$TARGET" == "main" ]]; then
|
||||
ok "→ MAIN: ${FORGEJO_URL}/${FORGEJO_USER}/${MAIN_REPO}"
|
||||
else
|
||||
info "→ TEST: ${FORGEJO_URL}/${FORGEJO_USER}/${TEST_REPO}"
|
||||
info " (run 'sudo bash promote.sh' when you're happy with the test repo)"
|
||||
fi
|
||||
|
||||
# ── Workspace ─────────────────────────────────────────────────────────────────
|
||||
echo -e "\n${BOLD}Workspace${NC}"
|
||||
RNAME="$([[ "$TARGET" == "main" ]] && echo "$MAIN_REPO" || echo "$TEST_REPO")"
|
||||
RDIR="$WORK_DIR/$RNAME"
|
||||
|
||||
if [[ -d "$RDIR/.git" ]]; then
|
||||
ok "Cloned: $RDIR"
|
||||
VER=$(grep -oP '(?<=version:\s)[\d.]+' "$RDIR/README.md" 2>/dev/null | head -1 || echo "unknown")
|
||||
info "Current version : $VER"
|
||||
LAST=$(cd "$RDIR" && git log -1 --format="%h %s (%ar)" 2>/dev/null)
|
||||
info "Last commit : $LAST"
|
||||
DOC_COUNT=$(ls "$RDIR/docs/"KiteStacks-Homelab-Documentation-v*.md 2>/dev/null | wc -l)
|
||||
info "Doc versions : $DOC_COUNT file(s) in docs/"
|
||||
else
|
||||
fail "Workspace not found — run setup.sh first"
|
||||
fi
|
||||
|
||||
# ── Forgejo API ───────────────────────────────────────────────────────────────
|
||||
echo -e "\n${BOLD}Forgejo Connectivity${NC}"
|
||||
HTTP=$(curl -s -o /dev/null -w "%{http_code}" --max-time 5 \
|
||||
-H "Authorization: token $FORGEJO_TOKEN" \
|
||||
"${FORGEJO_URL}/api/v1/user" 2>/dev/null)
|
||||
[[ "$HTTP" == "200" ]] && ok "API reachable ($FORGEJO_URL)" || fail "API returned HTTP $HTTP"
|
||||
|
||||
# ── Watch dirs ────────────────────────────────────────────────────────────────
|
||||
echo -e "\n${BOLD}Watched Directories${NC}"
|
||||
for d in $WATCH_DIRS; do
|
||||
if [[ -d "$d" ]]; then
|
||||
COUNT=$(find "$d" -type f 2>/dev/null | wc -l)
|
||||
ok "$d ($COUNT files)"
|
||||
else
|
||||
fail "$d (does not exist)"
|
||||
fi
|
||||
done
|
||||
|
||||
# ── Recent logs ───────────────────────────────────────────────────────────────
|
||||
echo -e "\n${BOLD}Recent Log Entries (last 10)${NC}"
|
||||
journalctl -u kitestacks-autosync -n 10 --no-pager -q 2>/dev/null \
|
||||
| sed 's/^/ /' \
|
||||
|| info "No journal entries yet."
|
||||
|
||||
echo -e "\n${BOLD}╚══════════════════════════════════════════════════════════════╝${NC}"
|
||||
echo -e " Live logs: ${CYAN}journalctl -u kitestacks-autosync -f${NC}\n"
|
||||
2
cloud-migration/.gitattributes
vendored
Normal file
2
cloud-migration/.gitattributes
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
archives/*.tar.gz filter=lfs diff=lfs merge=lfs -text
|
||||
volume-exports/*.tar.gz filter=lfs diff=lfs merge=lfs -text
|
||||
42
cloud-migration/README.md
Normal file
42
cloud-migration/README.md
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
# KiteStacks Cloud Migration Backup
|
||||
|
||||
Created for migrating the current KiteStacks host to Hetzner Cloud.
|
||||
|
||||
## Contents
|
||||
|
||||
- `archives/docker-bind-data.tar.gz` - `/home/kenpat/docker` bind-mounted service folders, including compose files, `.env` files, and bind-mounted app data.
|
||||
- `archives/syncthing-shared.tar.gz` - `/home/kenpat/SyncthingShared`.
|
||||
- `archives/kitestacks-scripts.tar.gz` - local KiteStacks automation/script folders.
|
||||
- `archives/host-etc-subset.tar.gz` - selected host `/etc` configuration needed for migration context.
|
||||
- `volume-exports/*.tar.gz` - Docker named volume exports.
|
||||
- `inventory/*` - Docker, network, disk, config, and host inventory.
|
||||
- `restore/RESTORE.md` - first-pass restore procedure.
|
||||
- `SHA256SUMS` - checksums for all files in this backup.
|
||||
|
||||
## Restoring Claude's Context on a New Machine
|
||||
|
||||
This repo includes a snapshot of Claude Code's persistent memory in `claude-memory/`
|
||||
(taken from `~/.claude/projects/-home-kenpat/memory/` on the original host). It
|
||||
captures the homelab setup, this migration plan/status, and your working
|
||||
preferences, so a fresh Claude session on the new machine doesn't start blank.
|
||||
|
||||
To restore it:
|
||||
|
||||
1. Clone this repo on the new machine.
|
||||
2. Copy the contents of `claude-memory/` into Claude Code's memory directory for
|
||||
your new home path, e.g.:
|
||||
```sh
|
||||
mkdir -p ~/.claude/projects/-home-<youruser>/memory
|
||||
cp claude-memory/*.md ~/.claude/projects/-home-<youruser>/memory/
|
||||
```
|
||||
3. Start a Claude Code session in that project. It will automatically load
|
||||
`MEMORY.md` and the linked memory files, and can pick up the migration
|
||||
(and any other tracked work) where it left off.
|
||||
4. Update `claude-memory/project-cloud-migration.md` as migration phases
|
||||
progress, and re-copy it back into this repo so future restores stay current.
|
||||
|
||||
## Important
|
||||
|
||||
This repository contains secrets: `.env` files, tunnel tokens, app database data, and service credentials. Keep it private.
|
||||
|
||||
The Docker bind-data archive was created from a live host. For databases with strict consistency requirements, prefer restoring service-native dumps when available, or stop services before taking a final cutover backup.
|
||||
31
cloud-migration/SHA256SUMS
Normal file
31
cloud-migration/SHA256SUMS
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
f188190cf1a5655916edc2192ceb778330a041082613ba3f8573a5f3bbfe64a0 ./README.md
|
||||
d2f2128eed132b9e8a43f35f6f4e4bfdba3254c5117a0318a6bf137f55f41b1f ./archives/docker-bind-data.tar.gz
|
||||
f865584c6d3548c073b5299cd6ba41b478610d221683238b4cd83b1420030549 ./archives/host-etc-subset.tar.gz
|
||||
8748291a60e092bd389b8c50d16ae7613b6909138c4bdd06852eb41dc76dc07f ./archives/kitestacks-scripts.tar.gz
|
||||
748e3f8decd1234dfbd1f9f81cb44222c40d3cfd827a9c46d1d3337b1643b58f ./archives/syncthing-shared.tar.gz
|
||||
8a685bef70fd7a71cf439b8088e0930bfab93956a0e8fe9b3d17888c118814c2 ./inventory/config-files.txt
|
||||
189c4fa043c3d7b67d1e2e5c923b10022977c84a751b6526a0b2ab3c89ce5c6b ./inventory/crontab.stderr
|
||||
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 ./inventory/crontab.txt
|
||||
a71267fb60fd62a9924b9eed404e5f2c217cdf4c604962fc195c8db8050ec51d ./inventory/df-h.txt
|
||||
a1f2d23e0f2b0a5b9bb76820ff2dbeaadcd059362d3791a90090fdb624f4a5e8 ./inventory/docker-container-inspect.json
|
||||
15f1abf2e548b2fdce4852ff6da034b75d3664446c23672ddd00135b83a1afce ./inventory/docker-folder-sizes.txt
|
||||
9d28d36f5ae776ad10080aa6b44292500b4963588c901847861890098a997803 ./inventory/docker-networks.txt
|
||||
08939f14b26a7c0918dcc6cf4f852435f19ee05da34bd37a5e04e8c3e87efab3 ./inventory/docker-running.txt
|
||||
0e58c8f5d09d2319ea38eee6b0a6ec923d621f187c51d2cf6381dadcccc8ffe9 ./inventory/docker-volume-inspect.json
|
||||
9d3067d74156ce24c16822686f94c8897ec7d877df25eff643cf19937b89d2e4 ./inventory/docker-volumes.txt
|
||||
659e639b4a93e08e868e133ab7c97898d419aec3056fcda012df686d52231f9e ./inventory/host-etc-tar-warnings.txt
|
||||
dc6d9f9163deacf7ff8ca239c47d039581ef079172577ee418bd6fcb72ddc07a ./inventory/ip-addr.txt
|
||||
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 ./inventory/kitestacks-scripts-tar-warnings.txt
|
||||
00c76796663c6b278a91730c15531a559ed552ff65dea34424129fdb12b92587 ./inventory/uname.txt
|
||||
dd93d026ddf76ea16896bf5faab9a2462275983e8c69ea6c07dc351864dce81c ./restore/RESTORE.md
|
||||
2a5392e54eed3edb677ad52f419a27b1c1de387d3c1c512585c09cbd399ab08b ./volume-exports/95f721b2e8b90b4e17a3675d4905837933bc366e12c15d16bce1bd9d166c43f0.tar.gz
|
||||
d37a2b35bd294d36aa0c3f96e477ee0203bc491f66d6d3cf122c81c29ba4b2c5 ./volume-exports/b11ac7c9fe060195954a46980f7ed85da9a62fea48d7cca5990aa54ec3d4cd8a.tar.gz
|
||||
668ae10a5bb4a44864b788063610b43def6fdad2964181b5838e50388d8b4fbf ./volume-exports/b1a99e9e271f6dbb1e693aabd2508fbae9973a82c5c1778fdad099054754f111.tar.gz
|
||||
44730b582fe275ae4a5daa7a03b5ae6ba77d55022df5b41e1183475d7a6e29fb ./volume-exports/b303d482950f666b37acae69c790f0f37e9d28735a4ea0b98a7961a8a400fa75.tar.gz
|
||||
c492df2ba24d0f77ac6b9684de110a2cc96a843f6b1b4422473af6361407c68b ./volume-exports/kite-ai_open-webui.tar.gz
|
||||
9b6410c6b71cd1f96157f3780de604a9c453abc2e5e23f4e5b2950f87098446e ./volume-exports/openproject_openproject_assets.tar.gz
|
||||
bae148a7b41896039d6cab807d27860b2d8d7333b03f79a6c16f7770179e4a6e ./volume-exports/openproject_openproject_db_data.tar.gz
|
||||
ba708e82a287f5c16f53afc1a885ca9837c0dfc44f92ab5ae2691090222dfdf1 ./volume-exports/openproject_openproject_logs.tar.gz
|
||||
ae85ee17213163cbea636c5270a0a9152361ba2e26bb0781d0274fe9c6379b4f ./volume-exports/openproject_openproject_pgdata.tar.gz
|
||||
7d7039b4dd710016ba2a447cb4ae82b24778544f6bdbd8fff07dc6ba87e5377c ./volume-exports/portainer_data.tar.gz
|
||||
a6fa0fea172711ef20d9ac4bd8362c2a9544f93c5dd435c62abfc7cfe18b88bd ./volume-exports/uptime-kuma.tar.gz
|
||||
BIN
cloud-migration/archives/docker-bind-data.tar.gz
(Stored with Git LFS)
Normal file
BIN
cloud-migration/archives/docker-bind-data.tar.gz
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
cloud-migration/archives/host-etc-subset.tar.gz
(Stored with Git LFS)
Normal file
BIN
cloud-migration/archives/host-etc-subset.tar.gz
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
cloud-migration/archives/kitestacks-scripts.tar.gz
(Stored with Git LFS)
Normal file
BIN
cloud-migration/archives/kitestacks-scripts.tar.gz
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
cloud-migration/archives/syncthing-shared.tar.gz
(Stored with Git LFS)
Normal file
BIN
cloud-migration/archives/syncthing-shared.tar.gz
(Stored with Git LFS)
Normal file
Binary file not shown.
7
cloud-migration/claude-memory/MEMORY.md
Normal file
7
cloud-migration/claude-memory/MEMORY.md
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
# Memory Index
|
||||
|
||||
- [User Profile](user-profile.md) — kenpat, homelab operator, kitestacks.com
|
||||
- [Project: KiteStacks Homelab](project-kitestacks.md) — Docker Compose homelab, Cloudflare tunnels, autosync to Forgejo
|
||||
- [Project: Authentik SSO](project-sso.md) — SSO setup in progress; what's done, what's pending
|
||||
- [Feedback: Tool Confirmation](feedback_tool_confirmation.md) — don't pause for tool confirmation, keep going autonomously
|
||||
- [Project: Cloud Migration](project-cloud-migration.md) — Phase 1 backup→Forgejo (done), Phase 2 Oracle VPS (planned), LFS/Cloudflare 413 gotcha
|
||||
13
cloud-migration/claude-memory/feedback_tool_confirmation.md
Normal file
13
cloud-migration/claude-memory/feedback_tool_confirmation.md
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
---
|
||||
name: feedback_tool_confirmation
|
||||
description: "User preference: don't pause for tool confirmation — keep going autonomously"
|
||||
metadata:
|
||||
node_type: memory
|
||||
type: feedback
|
||||
originSessionId: c435573b-6aff-4e43-a184-c17aa96ce348
|
||||
---
|
||||
|
||||
Don't pause and wait for the user to hit enter or confirm tool calls. Keep executing until the task is complete.
|
||||
|
||||
**Why:** User wants uninterrupted autonomous execution.
|
||||
**How to apply:** Only stop when the user must do something manually (UI action, testing) or when a decision genuinely requires their input. Never pause mid-task just to confirm a command.
|
||||
28
cloud-migration/claude-memory/project-cloud-migration.md
Normal file
28
cloud-migration/claude-memory/project-cloud-migration.md
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
---
|
||||
name: project-cloud-migration
|
||||
description: "KiteStacks server migration plan — Phase 1 (Forgejo backup, restore to new desktop) and Phase 2 (Oracle Cloud VPS, always-on)"
|
||||
metadata:
|
||||
node_type: memory
|
||||
type: project
|
||||
originSessionId: 1d92780e-77c5-41ac-8887-daca0ea55e8b
|
||||
---
|
||||
|
||||
**Phase 1 — STATUS: backup pushed & verified, restore not yet performed**
|
||||
|
||||
Goal: be able to pull the backup repo onto a new (desktop) machine and physically relaunch the homelab from it.
|
||||
|
||||
- Full server backup lives in Forgejo repo `kenpat/kitestacks-cloud-migration` (private), clone url `http://100.90.13.55:3006/kenpat/kitestacks-cloud-migration.git` (also reachable at `gitforge.kitestacks.com` for small files).
|
||||
- Source backup: `/home/kenpat/kitestacks-cloud` (git repo, 1 commit `6ffcbea`, ~4.3GB). Contains `archives/*.tar.gz` (docker bind-data, syncthing-shared, scripts, host /etc subset), `volume-exports/*.tar.gz` (named Docker volumes), `inventory/`, `restore/RESTORE.md`, `SHA256SUMS`.
|
||||
- 14 large files (~2.3GB) are tracked via **Git LFS** — required `apt-get install git-lfs` (no passwordless sudo on this host, user ran it manually) and `git lfs migrate import --include="archives/*.tar.gz,volume-exports/*.tar.gz"`.
|
||||
- **Known gotcha**: Forgejo's public hostname `gitforge.kitestacks.com` (Cloudflare Tunnel) returns HTTP 413 for any LFS object >~100MB. The 3 big files (docker-bind-data.tar.gz ~967MB, kite-ai_open-webui.tar.gz ~963MB, syncthing-shared.tar.gz ~165MB) had to be PUT directly to `http://100.90.13.55:3006/kenpat/kitestacks-cloud-migration.git/info/lfs/objects/<oid>/<size>` (local Tailscale IP, bypasses Cloudflare). `git lfs pull` for these 3 files will need the same workaround — clone/pull via the local IP, not the public hostname.
|
||||
- Verified: local HEAD == remote HEAD, all 15/15 LFS objects present server-side with sizes matching `SHA256SUMS` OIDs.
|
||||
- Documented in `/home/kenpat/forgejo-repos/kitestacks-homelab/docs/KiteStacks-Homelab-Documentation-v1.3.922.md`.
|
||||
|
||||
**Phase 1 remaining work**: actual restore-to-new-desktop has NOT been done/tested yet. When the user pulls a Claude session in on the new desktop, the workflow is: clone `kitestacks-cloud-migration` (via local IP for LFS), follow `restore/RESTORE.md`, restore Docker volumes from `volume-exports/`, restore bind-mounts from `archives/docker-bind-data.tar.gz`, bring stacks up via the compose files, re-point Cloudflare Tunnel.
|
||||
|
||||
**Why**: User wants to migrate the KiteStacks homelab off the current desktop onto new hardware first (Phase 1), then to an always-on Oracle Cloud VPS (Phase 2) so services don't go down when the desktop sleeps/is off.
|
||||
|
||||
**How to apply**: When the user says they're moving to a new desktop / starting the restore, walk through `restore/RESTORE.md` using this memory's notes (especially the LFS/Cloudflare gotcha). Once services are confirmed running on the new desktop, mark Phase 1 complete in this file, notify the user explicitly, and begin Phase 2 (Oracle Cloud VPS setup) — update this file with Phase 2 progress the same way.
|
||||
|
||||
**Phase 2 — STATUS: not started**
|
||||
Plan: provision an Oracle Cloud VPS, migrate the same stack there for always-on hosting (independent of desktop power state). No infra details yet — to be filled in once started.
|
||||
19
cloud-migration/claude-memory/project-kitestacks.md
Normal file
19
cloud-migration/claude-memory/project-kitestacks.md
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
---
|
||||
name: project-kitestacks
|
||||
description: "KiteStacks homelab infrastructure — Docker Compose services, networking, autosync, Forgejo repo"
|
||||
metadata:
|
||||
node_type: memory
|
||||
type: project
|
||||
originSessionId: 301d23e2-6920-42b0-a27d-eba4e667b7f7
|
||||
---
|
||||
|
||||
All services run as Docker Compose stacks in `/home/kenpat/docker/<app>/`.
|
||||
All containers join the `kitestacks` external Docker bridge network (172.18.0.0/16) — cloudflared uses container names as hostnames to route traffic.
|
||||
Cloudflare Tunnel (token-based) routes `*.kitestacks.com` → containers on the kitestacks network. Tunnel ingress rules are configured in the Cloudflare Zero Trust dashboard (not in local files).
|
||||
|
||||
**Autosync system:** A systemd service (`kitestacks-autosync`) watches `/home/kenpat/docker/` via inotify and pushes any file change to the Forgejo repo `kenpat/kitestacks-homelab` at `gitforge.kitestacks.com`. Working repo lives at `/opt/kitestacks-autosync/kitestacks-homelab/`. User's local clone is at `/home/kenpat/forgejo-repos/kitestacks-homelab/` (remote: `http://100.90.13.55:3006/kenpat/kitestacks-homelab.git`). Both point to the same Forgejo repo.
|
||||
|
||||
**Versioned docs:** Autosync auto-creates `docs/KiteStacks-Homelab-Documentation-v1.3.NNN.md` on each change. Manual/feature docs use the next version number. Current version as of 2026-06-08: 1.3.884.
|
||||
|
||||
**Why:** User always wants changes documented and pushed to Forgejo at the end of any task.
|
||||
**How to apply:** After making file changes, write docs to `/home/kenpat/forgejo-repos/kitestacks-homelab/docs/`, update CHANGELOG.md and README.md version tag, commit, and push. Use direct IP remote (no TLS issues).
|
||||
32
cloud-migration/claude-memory/project-sso.md
Normal file
32
cloud-migration/claude-memory/project-sso.md
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
---
|
||||
name: project-sso
|
||||
description: "Authentik SSO setup status for kitestacks.com — what's done vs pending"
|
||||
metadata:
|
||||
node_type: memory
|
||||
type: project
|
||||
originSessionId: 301d23e2-6920-42b0-a27d-eba4e667b7f7
|
||||
---
|
||||
|
||||
Authentik SSO configured 2026-06-08 to cover all kitestacks.com services.
|
||||
Full reference: `docs/authentik-sso-setup.md` in the Forgejo repo.
|
||||
|
||||
**Config files updated (done):**
|
||||
- `apps/authentik/docker-compose.yml` — kitestacks network declared
|
||||
- `apps/kavita/config/appsettings.json` — OIDC enabled, Authority set
|
||||
- BookStack retired — not used, all books on Kavita
|
||||
- `apps/openproject/docker-compose.yml` — OIDC env vars + network
|
||||
- `apps/openproject/.env` — OPENPROJECT_OIDC_SECRET placeholder
|
||||
- Grafana and OpenWebUI already had OIDC env vars (just need Authentik apps created)
|
||||
|
||||
**Pending manual steps:**
|
||||
1. Create Authentik OAuth2/OIDC providers + applications in admin UI for: Grafana, OpenWebUI, Kavita, OpenProject, Forgejo
|
||||
2. Create Authentik Proxy Providers for: Shaarli, Uptime Kuma, LiteLLM; assign to Embedded Outpost
|
||||
3. Configure Forgejo OAuth2 source via Forgejo admin UI (Site Admin → Auth Sources)
|
||||
4. Fill client secrets in `.env` files and restart containers
|
||||
5. Update Cloudflare tunnel routes: links.kitestacks.com → authentik:9000, status.kitestacks.com → authentik:9000, llm.kitestacks.com → authentik:9000
|
||||
6. After OpenProject container recreation (v13→v15 upgrade), update tunnel: tasks.kitestacks.com → openproject:80
|
||||
|
||||
**Excluded from SSO:** Portainer, Prometheus, Node Exporter, OpenRouter, BookStack (retired)
|
||||
|
||||
**Why:** User requested Authentik SSO for all services; OpenRouter/Prometheus/node-exporter/Portainer excluded by user request.
|
||||
**How to apply:** When user asks about SSO, check this memory for current status before suggesting next steps.
|
||||
10
cloud-migration/claude-memory/user-profile.md
Normal file
10
cloud-migration/claude-memory/user-profile.md
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
---
|
||||
name: user-profile
|
||||
description: "kenpat's role, preferences, and homelab context"
|
||||
metadata:
|
||||
node_type: memory
|
||||
type: user
|
||||
originSessionId: 301d23e2-6920-42b0-a27d-eba4e667b7f7
|
||||
---
|
||||
|
||||
kenpat runs a self-hosted homelab at kitestacks.com. All services exposed via Cloudflare Tunnels. Comfortable with Docker Compose, Git/Forgejo. Wants Claude to document changes and push them to Forgejo as part of completing any task.
|
||||
57
cloud-migration/inventory/config-files.txt
Normal file
57
cloud-migration/inventory/config-files.txt
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
/home/kenpat/docker/kitestacks-portal/nginx.conf
|
||||
/home/kenpat/docker/kitestacks-portal/docker-compose.yml
|
||||
/home/kenpat/docker/openproject/docker-compose.yml
|
||||
/home/kenpat/docker/openproject/.env
|
||||
/home/kenpat/docker/authentik/docker-compose.yml
|
||||
/home/kenpat/docker/authentik/.env
|
||||
/home/kenpat/docker/cloudflared/docker-compose.yml
|
||||
/home/kenpat/docker/prometheus/prometheus.yml
|
||||
/home/kenpat/docker/prometheus/docker-compose.yml
|
||||
/home/kenpat/docker/zammad/docker-compose.old.yml
|
||||
/home/kenpat/docker/bookstack/docker-compose.yml
|
||||
/home/kenpat/docker/kavita/config/appsettings.json
|
||||
/home/kenpat/docker/kite-ai/docker-compose.yml
|
||||
/home/kenpat/docker/kite-ai/litellm_config.yaml
|
||||
/home/kenpat/docker/kite-ai/.env
|
||||
/home/kenpat/docker/kitestacks-portal-test/nginx.conf
|
||||
/home/kenpat/docker/kitestacks-portal-test/docker-compose.yml
|
||||
/home/kenpat/docker/grafana/docker-compose.yml
|
||||
/home/kenpat/docker/grafana/grafana-networkpolicy.yaml
|
||||
/home/kenpat/docker/grafana/.env
|
||||
/home/kenpat/docker/homepage/config-test/kubernetes.yaml
|
||||
/home/kenpat/docker/homepage/config-test/settings.yaml
|
||||
/home/kenpat/docker/karakeep/docker-compose.yml
|
||||
/home/kenpat/docker/karakeep/.env
|
||||
/home/kenpat/docker/forgejo/cronjob.yaml
|
||||
/home/kenpat/docker/forgejo/configmap.yaml
|
||||
/home/kenpat/docker/forgejo/docker-compose.yml
|
||||
/home/kenpat/docker/forgejo/uptime-kuma/kitestacks-monitors.json
|
||||
/home/kenpat/docker/forgejo/uptime-kuma/uptime-kuma-deployment.yaml
|
||||
/home/kenpat/docker/forgejo/uptime-kuma/kitestacks-cron.yaml
|
||||
/home/kenpat/docker/forgejo/uptime-kuma/uptime-kuma-ingress.yaml
|
||||
/home/kenpat/docker/forgejo/uptime-kuma/configmap.yaml
|
||||
/home/kenpat/docker/forgejo/uptime-kuma/gitrepository.yaml
|
||||
/home/kenpat/docker/forgejo/gitrepository.yaml
|
||||
/home/kenpat/docker/homepage-archived-2026-06-07/config/kubernetes.yaml
|
||||
/home/kenpat/docker/homepage-archived-2026-06-07/config/bookmarks.yaml
|
||||
/home/kenpat/docker/homepage-archived-2026-06-07/config/proxmox.yaml
|
||||
/home/kenpat/docker/homepage-archived-2026-06-07/config/services.yaml
|
||||
/home/kenpat/docker/homepage-archived-2026-06-07/config/settings.yaml
|
||||
/home/kenpat/docker/homepage-archived-2026-06-07/config/widgets-live.yaml
|
||||
/home/kenpat/docker/homepage-archived-2026-06-07/config/settings-live.yaml
|
||||
/home/kenpat/docker/homepage-archived-2026-06-07/config/services-live.yaml
|
||||
/home/kenpat/docker/homepage-archived-2026-06-07/config/widgets.yaml
|
||||
/home/kenpat/docker/homepage-archived-2026-06-07/config/docker.yaml
|
||||
/home/kenpat/docker/homepage-archived-2026-06-07/docker-compose.test.yml
|
||||
/home/kenpat/docker/homepage-archived-2026-06-07/docker-compose.yml
|
||||
/home/kenpat/docker/homepage-archived-2026-06-07/services.yaml
|
||||
/home/kenpat/docker/homepage-archived-2026-06-07/config-test/kubernetes.yaml
|
||||
/home/kenpat/docker/homepage-archived-2026-06-07/config-test/bookmarks.yaml
|
||||
/home/kenpat/docker/homepage-archived-2026-06-07/config-test/proxmox.yaml
|
||||
/home/kenpat/docker/homepage-archived-2026-06-07/config-test/services.yaml
|
||||
/home/kenpat/docker/homepage-archived-2026-06-07/config-test/settings.yaml
|
||||
/home/kenpat/docker/homepage-archived-2026-06-07/config-test/widgets-live.yaml
|
||||
/home/kenpat/docker/homepage-archived-2026-06-07/config-test/settings-live.yaml
|
||||
/home/kenpat/docker/homepage-archived-2026-06-07/config-test/services-live.yaml
|
||||
/home/kenpat/docker/homepage-archived-2026-06-07/config-test/widgets.yaml
|
||||
/home/kenpat/docker/homepage-archived-2026-06-07/config-test/docker.yaml
|
||||
1
cloud-migration/inventory/crontab.stderr
Normal file
1
cloud-migration/inventory/crontab.stderr
Normal file
|
|
@ -0,0 +1 @@
|
|||
crontabs/kenpat/: fopen: Permission denied
|
||||
0
cloud-migration/inventory/crontab.txt
Normal file
0
cloud-migration/inventory/crontab.txt
Normal file
17
cloud-migration/inventory/df-h.txt
Normal file
17
cloud-migration/inventory/df-h.txt
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
Filesystem Size Used Avail Use% Mounted on
|
||||
/dev/nvme0n1p2 226G 69G 146G 33% /
|
||||
tmpfs 3.8G 0 3.8G 0% /dev
|
||||
tmpfs 761M 6.5M 755M 1% /run
|
||||
tmpfs 5.0M 12K 5.0M 1% /run/lock
|
||||
tmpfs 1.0M 0 1.0M 0% /run/credentials/systemd-journald.service
|
||||
tmpfs 761M 4.1M 757M 1% /run/user/1000
|
||||
efivarfs 246K 130K 112K 54% /sys/firmware/efi/efivars
|
||||
tmpfs 3.8G 952K 3.8G 1% /tmp
|
||||
/dev/nvme0n1p1 975M 8.8M 966M 1% /boot/efi
|
||||
/dev/nvme0n1p2 226G 69G 146G 33% /home/kenpat
|
||||
udev 3.7G 0 3.7G 0% /dev/tty
|
||||
tmpfs 3.8G 0 3.8G 0% /tmp/.git
|
||||
tmpfs 3.8G 0 3.8G 0% /tmp/.agents
|
||||
tmpfs 3.8G 0 3.8G 0% /tmp/.codex
|
||||
tmpfs 3.8G 0 3.8G 0% /home/kenpat/.git
|
||||
tmpfs 3.8G 0 3.8G 0% /home/kenpat/.agents
|
||||
8324
cloud-migration/inventory/docker-container-inspect.json
Normal file
8324
cloud-migration/inventory/docker-container-inspect.json
Normal file
File diff suppressed because it is too large
Load diff
22
cloud-migration/inventory/docker-folder-sizes.txt
Normal file
22
cloud-migration/inventory/docker-folder-sizes.txt
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
4.0K /home/kenpat/docker/audiobookshelf
|
||||
4.0K /home/kenpat/docker/plane
|
||||
4.0K /home/kenpat/docker/portainer
|
||||
4.0K /home/kenpat/docker/postgres
|
||||
4.0K /home/kenpat/docker/uptime-kuma
|
||||
8.0K /home/kenpat/docker/zammad
|
||||
12K /home/kenpat/docker/openproject
|
||||
16K /home/kenpat/docker/kite-ai
|
||||
16K /home/kenpat/docker/prometheus
|
||||
20K /home/kenpat/docker/cloudflared
|
||||
24K /home/kenpat/docker/homepage
|
||||
32K /home/kenpat/docker/authentik
|
||||
2.5M /home/kenpat/docker/homepage-backup-pre-cyberpunk-2026-06-07-0152.tar.gz
|
||||
2.7M /home/kenpat/docker/homepage-archived-2026-06-07
|
||||
3.1M /home/kenpat/docker/kitestacks-portal
|
||||
3.2M /home/kenpat/docker/kitestacks-portal-test
|
||||
4.4M /home/kenpat/docker/linkding
|
||||
42M /home/kenpat/docker/kavita
|
||||
63M /home/kenpat/docker/karakeep
|
||||
155M /home/kenpat/docker/bookstack
|
||||
156M /home/kenpat/docker/grafana
|
||||
839M /home/kenpat/docker/forgejo
|
||||
19
cloud-migration/inventory/docker-networks.txt
Normal file
19
cloud-migration/inventory/docker-networks.txt
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
NAME DRIVER SCOPE
|
||||
authentik_default bridge local
|
||||
bookstack_default bridge local
|
||||
bridge bridge local
|
||||
calibre-web_default bridge local
|
||||
cloudflared_default bridge local
|
||||
forgejo_default bridge local
|
||||
grafana_default bridge local
|
||||
homepage_default bridge local
|
||||
host host local
|
||||
karakeep_internal bridge local
|
||||
kite-ai_default bridge local
|
||||
kitestacks bridge local
|
||||
kitestacks-portal-test_default bridge local
|
||||
kitestacks-portal_default bridge local
|
||||
none null local
|
||||
openproject_default bridge local
|
||||
openproject_openproject-net bridge local
|
||||
prometheus_default bridge local
|
||||
25
cloud-migration/inventory/docker-running.txt
Normal file
25
cloud-migration/inventory/docker-running.txt
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
NAMES IMAGE STATUS PORTS
|
||||
karakeep ghcr.io/karakeep-app/karakeep:release Up About an hour (healthy) 3000/tcp
|
||||
karakeep-meilisearch getmeili/meilisearch:v1.41.0 Up 2 hours 7700/tcp
|
||||
karakeep-chrome gcr.io/zenika-hub/alpine-chrome:124 Up 2 hours
|
||||
openproject openproject/openproject:15 Up 18 hours 0.0.0.0:80->80/tcp, :::80->80/tcp
|
||||
forgejo codeberg.org/forgejo/forgejo:11 Up 16 hours 0.0.0.0:2222->22/tcp, :::2222->22/tcp, 0.0.0.0:3006->3000/tcp, :::3006->3000/tcp
|
||||
kite-openwebui ghcr.io/open-webui/open-webui:main Up 32 hours (healthy) 0.0.0.0:3100->8080/tcp, :::3100->8080/tcp
|
||||
grafana grafana/grafana-oss Up 32 hours 0.0.0.0:3150->3000/tcp, :::3150->3000/tcp
|
||||
cloudflared cloudflare/cloudflared:latest Up 32 hours
|
||||
kitestacks-metrics-api kitestacks-portal-test-metrics-api:latest Up 32 hours
|
||||
homepage nginx:alpine Up 32 hours 80/tcp, 0.0.0.0:3005->3000/tcp, :::3005->3000/tcp
|
||||
kitestacks-portal-test nginx:alpine Up 32 hours 0.0.0.0:3008->80/tcp, :::3008->80/tcp
|
||||
homepage-test ghcr.io/gethomepage/homepage:latest Up 32 hours (healthy) 0.0.0.0:3007->3000/tcp, :::3007->3000/tcp
|
||||
kite-litellm ghcr.io/berriai/litellm:main-latest Up 32 hours 0.0.0.0:4000->4000/tcp, :::4000->4000/tcp
|
||||
bookstack lscr.io/linuxserver/bookstack:latest Up 32 hours 443/tcp, 0.0.0.0:6875->80/tcp, :::6875->80/tcp
|
||||
bookstack-db mariadb:11 Up 32 hours 3306/tcp
|
||||
authentik ghcr.io/goauthentik/server:latest Up 32 hours (healthy) 0.0.0.0:9001->9000/tcp, :::9001->9000/tcp
|
||||
authentik-worker ghcr.io/goauthentik/server:latest Up 32 hours (healthy)
|
||||
authentik-postgres postgres:16-alpine Up 32 hours 5432/tcp
|
||||
authentik-redis redis:alpine Up 32 hours 6379/tcp
|
||||
kavita ghcr.io/kareadita/kavita:latest Up 8 minutes (healthy) 0.0.0.0:5000->5000/tcp, :::5000->5000/tcp
|
||||
portainer portainer/portainer-ce:latest Up 32 hours 8000/tcp, 9000/tcp, 0.0.0.0:9443->9443/tcp, :::9443->9443/tcp
|
||||
prometheus prom/prometheus Up 32 hours 0.0.0.0:9090->9090/tcp, :::9090->9090/tcp
|
||||
node-exporter prom/node-exporter Up 32 hours 0.0.0.0:9100->9100/tcp, :::9100->9100/tcp
|
||||
uptime-kuma louislam/uptime-kuma:latest Up 32 hours (healthy) 0.0.0.0:3001->3001/tcp, :::3001->3001/tcp
|
||||
439
cloud-migration/inventory/docker-volume-inspect.json
Normal file
439
cloud-migration/inventory/docker-volume-inspect.json
Normal file
|
|
@ -0,0 +1,439 @@
|
|||
[
|
||||
{
|
||||
"CreatedAt": "2026-06-09T13:36:40-05:00",
|
||||
"Driver": "local",
|
||||
"Labels": {
|
||||
"com.docker.volume.anonymous": ""
|
||||
},
|
||||
"Mountpoint": "/var/lib/docker/volumes/4e935c6eb337c5ace7ae2d4ac76bf4cd7fba2d6733b7a3ecd647e53052965524/_data",
|
||||
"Name": "4e935c6eb337c5ace7ae2d4ac76bf4cd7fba2d6733b7a3ecd647e53052965524",
|
||||
"Options": null,
|
||||
"Scope": "local"
|
||||
},
|
||||
{
|
||||
"CreatedAt": "2026-06-06T02:50:40-05:00",
|
||||
"Driver": "local",
|
||||
"Labels": {
|
||||
"com.docker.volume.anonymous": ""
|
||||
},
|
||||
"Mountpoint": "/var/lib/docker/volumes/95f721b2e8b90b4e17a3675d4905837933bc366e12c15d16bce1bd9d166c43f0/_data",
|
||||
"Name": "95f721b2e8b90b4e17a3675d4905837933bc366e12c15d16bce1bd9d166c43f0",
|
||||
"Options": null,
|
||||
"Scope": "local"
|
||||
},
|
||||
{
|
||||
"CreatedAt": "2026-06-04T11:22:48-05:00",
|
||||
"Driver": "local",
|
||||
"Labels": {
|
||||
"com.docker.volume.anonymous": ""
|
||||
},
|
||||
"Mountpoint": "/var/lib/docker/volumes/b1a99e9e271f6dbb1e693aabd2508fbae9973a82c5c1778fdad099054754f111/_data",
|
||||
"Name": "b1a99e9e271f6dbb1e693aabd2508fbae9973a82c5c1778fdad099054754f111",
|
||||
"Options": null,
|
||||
"Scope": "local"
|
||||
},
|
||||
{
|
||||
"CreatedAt": "2026-06-06T02:50:40-05:00",
|
||||
"Driver": "local",
|
||||
"Labels": {
|
||||
"com.docker.volume.anonymous": ""
|
||||
},
|
||||
"Mountpoint": "/var/lib/docker/volumes/b11ac7c9fe060195954a46980f7ed85da9a62fea48d7cca5990aa54ec3d4cd8a/_data",
|
||||
"Name": "b11ac7c9fe060195954a46980f7ed85da9a62fea48d7cca5990aa54ec3d4cd8a",
|
||||
"Options": null,
|
||||
"Scope": "local"
|
||||
},
|
||||
{
|
||||
"CreatedAt": "2026-06-05T14:51:34-05:00",
|
||||
"Driver": "local",
|
||||
"Labels": {
|
||||
"com.docker.volume.anonymous": ""
|
||||
},
|
||||
"Mountpoint": "/var/lib/docker/volumes/b303d482950f666b37acae69c790f0f37e9d28735a4ea0b98a7961a8a400fa75/_data",
|
||||
"Name": "b303d482950f666b37acae69c790f0f37e9d28735a4ea0b98a7961a8a400fa75",
|
||||
"Options": null,
|
||||
"Scope": "local"
|
||||
},
|
||||
{
|
||||
"CreatedAt": "2026-06-04T23:47:29-05:00",
|
||||
"Driver": "local",
|
||||
"Labels": {
|
||||
"com.docker.compose.project": "kite-ai",
|
||||
"com.docker.compose.version": "2.26.1",
|
||||
"com.docker.compose.volume": "open-webui"
|
||||
},
|
||||
"Mountpoint": "/var/lib/docker/volumes/kite-ai_open-webui/_data",
|
||||
"Name": "kite-ai_open-webui",
|
||||
"Options": null,
|
||||
"Scope": "local"
|
||||
},
|
||||
{
|
||||
"CreatedAt": "2026-06-04T21:54:45-05:00",
|
||||
"Driver": "local",
|
||||
"Labels": {
|
||||
"com.docker.compose.project": "openproject",
|
||||
"com.docker.compose.version": "2.26.1",
|
||||
"com.docker.compose.volume": "openproject_assets"
|
||||
},
|
||||
"Mountpoint": "/var/lib/docker/volumes/openproject_openproject_assets/_data",
|
||||
"Name": "openproject_openproject_assets",
|
||||
"Options": null,
|
||||
"Scope": "local"
|
||||
},
|
||||
{
|
||||
"CreatedAt": "2026-06-05T14:38:15-05:00",
|
||||
"Driver": "local",
|
||||
"Labels": {
|
||||
"com.docker.compose.project": "openproject",
|
||||
"com.docker.compose.version": "2.26.1",
|
||||
"com.docker.compose.volume": "openproject_db_data"
|
||||
},
|
||||
"Mountpoint": "/var/lib/docker/volumes/openproject_openproject_db_data/_data",
|
||||
"Name": "openproject_openproject_db_data",
|
||||
"Options": null,
|
||||
"Scope": "local"
|
||||
},
|
||||
{
|
||||
"CreatedAt": "2026-06-05T14:38:15-05:00",
|
||||
"Driver": "local",
|
||||
"Labels": {
|
||||
"com.docker.compose.project": "openproject",
|
||||
"com.docker.compose.version": "2.26.1",
|
||||
"com.docker.compose.volume": "openproject_logs"
|
||||
},
|
||||
"Mountpoint": "/var/lib/docker/volumes/openproject_openproject_logs/_data",
|
||||
"Name": "openproject_openproject_logs",
|
||||
"Options": null,
|
||||
"Scope": "local"
|
||||
},
|
||||
{
|
||||
"CreatedAt": "2026-06-04T21:54:45-05:00",
|
||||
"Driver": "local",
|
||||
"Labels": {
|
||||
"com.docker.compose.project": "openproject",
|
||||
"com.docker.compose.version": "2.26.1",
|
||||
"com.docker.compose.volume": "openproject_pgdata"
|
||||
},
|
||||
"Mountpoint": "/var/lib/docker/volumes/openproject_openproject_pgdata/_data",
|
||||
"Name": "openproject_openproject_pgdata",
|
||||
"Options": null,
|
||||
"Scope": "local"
|
||||
},
|
||||
{
|
||||
"CreatedAt": "2026-06-04T11:27:46-05:00",
|
||||
"Driver": "local",
|
||||
"Labels": null,
|
||||
"Mountpoint": "/var/lib/docker/volumes/portainer_data/_data",
|
||||
"Name": "portainer_data",
|
||||
"Options": null,
|
||||
"Scope": "local"
|
||||
},
|
||||
{
|
||||
"Id": "9a8ff52b78b4009bfd1871f7ecabc87a03048027aeb928a93795c49cc63ecc8c",
|
||||
"Created": "2026-06-04T15:51:50.956146487Z",
|
||||
"Path": "/usr/bin/dumb-init",
|
||||
"Args": [
|
||||
"--",
|
||||
"extra/entrypoint.sh",
|
||||
"node",
|
||||
"server/server.js"
|
||||
],
|
||||
"State": {
|
||||
"Status": "running",
|
||||
"Running": true,
|
||||
"Paused": false,
|
||||
"Restarting": false,
|
||||
"OOMKilled": false,
|
||||
"Dead": false,
|
||||
"Pid": 5263,
|
||||
"ExitCode": 0,
|
||||
"Error": "",
|
||||
"StartedAt": "2026-06-08T10:20:52.463566079Z",
|
||||
"FinishedAt": "2026-06-08T10:20:43.666763869Z",
|
||||
"Health": {
|
||||
"Status": "healthy",
|
||||
"FailingStreak": 0,
|
||||
"Log": [
|
||||
{
|
||||
"Start": "2026-06-09T13:32:39.230864414-05:00",
|
||||
"End": "2026-06-09T13:32:39.293596386-05:00",
|
||||
"ExitCode": 0,
|
||||
"Output": "2026/06/09 18:32:39 Checking http://127.0.0.1:3001\n2026/06/09 18:32:39 Health Check OK [Res Code: 200]\n"
|
||||
},
|
||||
{
|
||||
"Start": "2026-06-09T13:33:39.294974636-05:00",
|
||||
"End": "2026-06-09T13:33:39.386020711-05:00",
|
||||
"ExitCode": 0,
|
||||
"Output": "2026/06/09 18:33:39 Checking http://127.0.0.1:3001\n2026/06/09 18:33:39 Health Check OK [Res Code: 200]\n"
|
||||
},
|
||||
{
|
||||
"Start": "2026-06-09T13:34:39.387516537-05:00",
|
||||
"End": "2026-06-09T13:34:39.582395564-05:00",
|
||||
"ExitCode": 0,
|
||||
"Output": "2026/06/09 18:34:39 Checking http://127.0.0.1:3001\n2026/06/09 18:34:39 Health Check OK [Res Code: 200]\n"
|
||||
},
|
||||
{
|
||||
"Start": "2026-06-09T13:35:39.596511098-05:00",
|
||||
"End": "2026-06-09T13:35:39.727123444-05:00",
|
||||
"ExitCode": 0,
|
||||
"Output": "2026/06/09 18:35:39 Checking http://127.0.0.1:3001\n2026/06/09 18:35:39 Health Check OK [Res Code: 200]\n"
|
||||
},
|
||||
{
|
||||
"Start": "2026-06-09T13:36:39.728420401-05:00",
|
||||
"End": "2026-06-09T13:36:39.827964251-05:00",
|
||||
"ExitCode": 0,
|
||||
"Output": "2026/06/09 18:36:39 Checking http://127.0.0.1:3001\n2026/06/09 18:36:39 Health Check OK [Res Code: 200]\n"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"Image": "sha256:f48d816cb7460cd3b7bb15ed393968b0ae0da4c690443b778b6a5db6b09f527e",
|
||||
"ResolvConfPath": "/var/lib/docker/containers/9a8ff52b78b4009bfd1871f7ecabc87a03048027aeb928a93795c49cc63ecc8c/resolv.conf",
|
||||
"HostnamePath": "/var/lib/docker/containers/9a8ff52b78b4009bfd1871f7ecabc87a03048027aeb928a93795c49cc63ecc8c/hostname",
|
||||
"HostsPath": "/var/lib/docker/containers/9a8ff52b78b4009bfd1871f7ecabc87a03048027aeb928a93795c49cc63ecc8c/hosts",
|
||||
"LogPath": "/var/lib/docker/containers/9a8ff52b78b4009bfd1871f7ecabc87a03048027aeb928a93795c49cc63ecc8c/9a8ff52b78b4009bfd1871f7ecabc87a03048027aeb928a93795c49cc63ecc8c-json.log",
|
||||
"Name": "/uptime-kuma",
|
||||
"RestartCount": 0,
|
||||
"Driver": "overlay2",
|
||||
"Platform": "linux",
|
||||
"MountLabel": "",
|
||||
"ProcessLabel": "",
|
||||
"AppArmorProfile": "docker-default",
|
||||
"ExecIDs": null,
|
||||
"HostConfig": {
|
||||
"Binds": [
|
||||
"uptime-kuma:/app/data"
|
||||
],
|
||||
"ContainerIDFile": "",
|
||||
"LogConfig": {
|
||||
"Type": "json-file",
|
||||
"Config": {}
|
||||
},
|
||||
"NetworkMode": "bridge",
|
||||
"PortBindings": {
|
||||
"3001/tcp": [
|
||||
{
|
||||
"HostIp": "",
|
||||
"HostPort": "3001"
|
||||
}
|
||||
]
|
||||
},
|
||||
"RestartPolicy": {
|
||||
"Name": "unless-stopped",
|
||||
"MaximumRetryCount": 0
|
||||
},
|
||||
"AutoRemove": false,
|
||||
"VolumeDriver": "",
|
||||
"VolumesFrom": null,
|
||||
"ConsoleSize": [
|
||||
26,
|
||||
80
|
||||
],
|
||||
"CapAdd": null,
|
||||
"CapDrop": null,
|
||||
"CgroupnsMode": "private",
|
||||
"Dns": [],
|
||||
"DnsOptions": [],
|
||||
"DnsSearch": [],
|
||||
"ExtraHosts": null,
|
||||
"GroupAdd": null,
|
||||
"IpcMode": "private",
|
||||
"Cgroup": "",
|
||||
"Links": null,
|
||||
"OomScoreAdj": 0,
|
||||
"PidMode": "",
|
||||
"Privileged": false,
|
||||
"PublishAllPorts": false,
|
||||
"ReadonlyRootfs": false,
|
||||
"SecurityOpt": null,
|
||||
"UTSMode": "",
|
||||
"UsernsMode": "",
|
||||
"ShmSize": 67108864,
|
||||
"Runtime": "runc",
|
||||
"Isolation": "",
|
||||
"CpuShares": 0,
|
||||
"Memory": 0,
|
||||
"NanoCpus": 0,
|
||||
"CgroupParent": "",
|
||||
"BlkioWeight": 0,
|
||||
"BlkioWeightDevice": [],
|
||||
"BlkioDeviceReadBps": [],
|
||||
"BlkioDeviceWriteBps": [],
|
||||
"BlkioDeviceReadIOps": [],
|
||||
"BlkioDeviceWriteIOps": [],
|
||||
"CpuPeriod": 0,
|
||||
"CpuQuota": 0,
|
||||
"CpuRealtimePeriod": 0,
|
||||
"CpuRealtimeRuntime": 0,
|
||||
"CpusetCpus": "",
|
||||
"CpusetMems": "",
|
||||
"Devices": [],
|
||||
"DeviceCgroupRules": null,
|
||||
"DeviceRequests": null,
|
||||
"MemoryReservation": 0,
|
||||
"MemorySwap": 0,
|
||||
"MemorySwappiness": null,
|
||||
"OomKillDisable": null,
|
||||
"PidsLimit": null,
|
||||
"Ulimits": [],
|
||||
"CpuCount": 0,
|
||||
"CpuPercent": 0,
|
||||
"IOMaximumIOps": 0,
|
||||
"IOMaximumBandwidth": 0,
|
||||
"MaskedPaths": [
|
||||
"/proc/asound",
|
||||
"/proc/acpi",
|
||||
"/proc/kcore",
|
||||
"/proc/keys",
|
||||
"/proc/latency_stats",
|
||||
"/proc/timer_list",
|
||||
"/proc/timer_stats",
|
||||
"/proc/sched_debug",
|
||||
"/proc/scsi",
|
||||
"/sys/firmware",
|
||||
"/sys/devices/virtual/powercap"
|
||||
],
|
||||
"ReadonlyPaths": [
|
||||
"/proc/bus",
|
||||
"/proc/fs",
|
||||
"/proc/irq",
|
||||
"/proc/sys",
|
||||
"/proc/sysrq-trigger"
|
||||
]
|
||||
},
|
||||
"GraphDriver": {
|
||||
"Data": {
|
||||
"LowerDir": "/var/lib/docker/overlay2/a9ccf55f6f463815b92b71dcf70142bc29e46f15a9f379793794f9e2dc9202e2-init/diff:/var/lib/docker/overlay2/484ebda4c7653a5aece44f036a066cf6a8eb822181ba22bae6908d16a629819c/diff:/var/lib/docker/overlay2/6f26b9d89fa702369832639624a9ed0ea1c5b47ca6fb349bc8c401df7eaf6d54/diff:/var/lib/docker/overlay2/5fece3c11be6f920ed0ac2bca96519fcc6b37a8c7e1d4012a9aef5989df017ee/diff:/var/lib/docker/overlay2/59e052c7119808723ca72a872dc04903cf46eae560e6cb078abeb99cb09cde7b/diff:/var/lib/docker/overlay2/a15d41f7bc54190c511f40a5677a79744aafe316725054e6dbf25a52372b756b/diff:/var/lib/docker/overlay2/252278bc5048f2b29878b86d0f585a399f13abec6902c3399d50f40245ce458e/diff:/var/lib/docker/overlay2/8b63000845eefb70e35349605be767cc7bc122d530605a042caabb249a61c16a/diff:/var/lib/docker/overlay2/83089265605c9e5dc2cee6afb5c6d65fb018f3b7b699e4a88c874b29d8582394/diff:/var/lib/docker/overlay2/5e82af76259ba2a3d365c0a46804925e2cbe0bfdaa4bf7c6bd174c6c95748321/diff:/var/lib/docker/overlay2/43d5816b7d853b6c652fdf2805bae68e7cc3881058052a3a8ca2a0ea912decf3/diff:/var/lib/docker/overlay2/3f2031c2ab4999a3c459c490542ef5bb9de6568f6ee4ba3d2a4e951e5627ecba/diff:/var/lib/docker/overlay2/9bbabaccb55a7612c7d2c275644d80565cb7f20b22f9243a78b3732d002a1b60/diff:/var/lib/docker/overlay2/6360813a2de43dd1d216d9a17945d9836403a018bdeb91754458768731788736/diff",
|
||||
"MergedDir": "/var/lib/docker/overlay2/a9ccf55f6f463815b92b71dcf70142bc29e46f15a9f379793794f9e2dc9202e2/merged",
|
||||
"UpperDir": "/var/lib/docker/overlay2/a9ccf55f6f463815b92b71dcf70142bc29e46f15a9f379793794f9e2dc9202e2/diff",
|
||||
"WorkDir": "/var/lib/docker/overlay2/a9ccf55f6f463815b92b71dcf70142bc29e46f15a9f379793794f9e2dc9202e2/work"
|
||||
},
|
||||
"Name": "overlay2"
|
||||
},
|
||||
"Mounts": [
|
||||
{
|
||||
"Type": "volume",
|
||||
"Name": "uptime-kuma",
|
||||
"Source": "/var/lib/docker/volumes/uptime-kuma/_data",
|
||||
"Destination": "/app/data",
|
||||
"Driver": "local",
|
||||
"Mode": "z",
|
||||
"RW": true,
|
||||
"Propagation": ""
|
||||
}
|
||||
],
|
||||
"Config": {
|
||||
"Hostname": "9a8ff52b78b4",
|
||||
"Domainname": "",
|
||||
"User": "",
|
||||
"AttachStdin": false,
|
||||
"AttachStdout": false,
|
||||
"AttachStderr": false,
|
||||
"ExposedPorts": {
|
||||
"3001/tcp": {}
|
||||
},
|
||||
"Tty": false,
|
||||
"OpenStdin": false,
|
||||
"StdinOnce": false,
|
||||
"Env": [
|
||||
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
|
||||
"NODE_VERSION=18.20.3",
|
||||
"YARN_VERSION=1.22.19",
|
||||
"UPTIME_KUMA_IS_CONTAINER=1"
|
||||
],
|
||||
"Cmd": [
|
||||
"node",
|
||||
"server/server.js"
|
||||
],
|
||||
"Healthcheck": {
|
||||
"Test": [
|
||||
"CMD-SHELL",
|
||||
"extra/healthcheck"
|
||||
],
|
||||
"Interval": 60000000000,
|
||||
"Timeout": 30000000000,
|
||||
"StartPeriod": 180000000000,
|
||||
"Retries": 5
|
||||
},
|
||||
"Image": "louislam/uptime-kuma:latest",
|
||||
"Volumes": {
|
||||
"/app/data": {}
|
||||
},
|
||||
"WorkingDir": "/app",
|
||||
"Entrypoint": [
|
||||
"/usr/bin/dumb-init",
|
||||
"--",
|
||||
"extra/entrypoint.sh"
|
||||
],
|
||||
"OnBuild": null,
|
||||
"Labels": {}
|
||||
},
|
||||
"NetworkSettings": {
|
||||
"Bridge": "",
|
||||
"SandboxID": "be33e50dd494b136bd0c4093411afca8264ab29e20b3c9bde92c87db15cb5383",
|
||||
"SandboxKey": "/var/run/docker/netns/be33e50dd494",
|
||||
"Ports": {
|
||||
"3001/tcp": [
|
||||
{
|
||||
"HostIp": "0.0.0.0",
|
||||
"HostPort": "3001"
|
||||
},
|
||||
{
|
||||
"HostIp": "::",
|
||||
"HostPort": "3001"
|
||||
}
|
||||
]
|
||||
},
|
||||
"HairpinMode": false,
|
||||
"LinkLocalIPv6Address": "",
|
||||
"LinkLocalIPv6PrefixLen": 0,
|
||||
"SecondaryIPAddresses": null,
|
||||
"SecondaryIPv6Addresses": null,
|
||||
"EndpointID": "febf33e9a0277267dae9c51c79c42e824dbb520c8fe0a471374398d6024788bb",
|
||||
"Gateway": "172.17.0.1",
|
||||
"GlobalIPv6Address": "",
|
||||
"GlobalIPv6PrefixLen": 0,
|
||||
"IPAddress": "172.17.0.3",
|
||||
"IPPrefixLen": 16,
|
||||
"IPv6Gateway": "",
|
||||
"MacAddress": "02:42:ac:11:00:03",
|
||||
"Networks": {
|
||||
"bridge": {
|
||||
"IPAMConfig": null,
|
||||
"Links": null,
|
||||
"Aliases": null,
|
||||
"MacAddress": "02:42:ac:11:00:03",
|
||||
"NetworkID": "f4d4e2fb72e14fd40db0f04cfe43e79b4afdd228a2593c24a0e36c832349ba48",
|
||||
"EndpointID": "febf33e9a0277267dae9c51c79c42e824dbb520c8fe0a471374398d6024788bb",
|
||||
"Gateway": "172.17.0.1",
|
||||
"IPAddress": "172.17.0.3",
|
||||
"IPPrefixLen": 16,
|
||||
"IPv6Gateway": "",
|
||||
"GlobalIPv6Address": "",
|
||||
"GlobalIPv6PrefixLen": 0,
|
||||
"DriverOpts": null,
|
||||
"DNSNames": null
|
||||
},
|
||||
"kitestacks": {
|
||||
"IPAMConfig": {},
|
||||
"Links": null,
|
||||
"Aliases": [],
|
||||
"MacAddress": "02:42:ac:12:00:0d",
|
||||
"NetworkID": "72143bca8d6382130e8e1698bf60fb53ff93f944102372d2cc7dc8e32aee0758",
|
||||
"EndpointID": "6c0340601e5b36aa8af525c8a8dd1048be951be5cec6f802273dd3d13f8e2470",
|
||||
"Gateway": "172.18.0.1",
|
||||
"IPAddress": "172.18.0.13",
|
||||
"IPPrefixLen": 16,
|
||||
"IPv6Gateway": "",
|
||||
"GlobalIPv6Address": "",
|
||||
"GlobalIPv6PrefixLen": 0,
|
||||
"DriverOpts": {},
|
||||
"DNSNames": [
|
||||
"uptime-kuma",
|
||||
"9a8ff52b78b4"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
11
cloud-migration/inventory/docker-volumes.txt
Normal file
11
cloud-migration/inventory/docker-volumes.txt
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
95f721b2e8b90b4e17a3675d4905837933bc366e12c15d16bce1bd9d166c43f0
|
||||
b1a99e9e271f6dbb1e693aabd2508fbae9973a82c5c1778fdad099054754f111
|
||||
b11ac7c9fe060195954a46980f7ed85da9a62fea48d7cca5990aa54ec3d4cd8a
|
||||
b303d482950f666b37acae69c790f0f37e9d28735a4ea0b98a7961a8a400fa75
|
||||
kite-ai_open-webui
|
||||
openproject_openproject_assets
|
||||
openproject_openproject_db_data
|
||||
openproject_openproject_logs
|
||||
openproject_openproject_pgdata
|
||||
portainer_data
|
||||
uptime-kuma
|
||||
2
cloud-migration/inventory/host-etc-tar-warnings.txt
Normal file
2
cloud-migration/inventory/host-etc-tar-warnings.txt
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
tar: etc/systemd/system/k3s.service.env: Cannot open: Permission denied
|
||||
tar: Exiting with failure status due to previous errors
|
||||
350
cloud-migration/inventory/ip-addr.txt
Normal file
350
cloud-migration/inventory/ip-addr.txt
Normal file
|
|
@ -0,0 +1,350 @@
|
|||
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
|
||||
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
|
||||
inet 127.0.0.1/8 scope host lo
|
||||
valid_lft forever preferred_lft forever
|
||||
inet6 ::1/128 scope host noprefixroute
|
||||
valid_lft forever preferred_lft forever
|
||||
2: enp0s31f6: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc fq_codel state DOWN group default qlen 1000
|
||||
link/ether 54:05:db:c5:c4:4a brd ff:ff:ff:ff:ff:ff
|
||||
altname enx5405dbc5c44a
|
||||
3: wlp0s20f3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
|
||||
link/ether 8c:8d:28:c4:e1:9f brd ff:ff:ff:ff:ff:ff
|
||||
altname wlx8c8d28c4e19f
|
||||
inet 192.168.1.205/24 brd 192.168.1.255 scope global dynamic noprefixroute wlp0s20f3
|
||||
valid_lft 57571sec preferred_lft 57571sec
|
||||
inet6 2600:1702:5730:50b0:af17:2be8:9ef7:f714/64 scope global temporary dynamic
|
||||
valid_lft 3523sec preferred_lft 3523sec
|
||||
inet6 2600:1702:5730:50b0::40/128 scope global dynamic noprefixroute
|
||||
valid_lft 2878sec preferred_lft 2878sec
|
||||
inet6 2600:1702:5730:50b0:9c92:ac42:9751:b5c0/64 scope global temporary deprecated dynamic
|
||||
valid_lft 3523sec preferred_lft 0sec
|
||||
inet6 2600:1702:5730:50b0:8e8d:28ff:fec4:e19f/64 scope global dynamic mngtmpaddr noprefixroute
|
||||
valid_lft 3523sec preferred_lft 3523sec
|
||||
inet6 fe80::8e8d:28ff:fec4:e19f/64 scope link noprefixroute
|
||||
valid_lft forever preferred_lft forever
|
||||
4: tailscale0: <POINTOPOINT,MULTICAST,NOARP,UP,LOWER_UP> mtu 1280 qdisc fq_codel state UNKNOWN group default qlen 500
|
||||
link/none
|
||||
inet 100.90.13.55/32 scope global tailscale0
|
||||
valid_lft forever preferred_lft forever
|
||||
inet6 fd7a:115c:a1e0::b139:d38/128 scope global
|
||||
valid_lft forever preferred_lft forever
|
||||
inet6 fe80::3a3d:6f43:b3d1:6e3a/64 scope link stable-privacy proto kernel_ll
|
||||
valid_lft forever preferred_lft forever
|
||||
5: br-5a4a35faa802: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
|
||||
link/ether 02:42:19:cd:38:a4 brd ff:ff:ff:ff:ff:ff
|
||||
inet 192.168.160.1/20 brd 192.168.175.255 scope global br-5a4a35faa802
|
||||
valid_lft forever preferred_lft forever
|
||||
inet6 fe80::42:19ff:fecd:38a4/64 scope link proto kernel_ll
|
||||
valid_lft forever preferred_lft forever
|
||||
6: br-9e267d3240c9: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
|
||||
link/ether 02:42:bc:f6:1a:bd brd ff:ff:ff:ff:ff:ff
|
||||
inet 172.22.0.1/16 brd 172.22.255.255 scope global br-9e267d3240c9
|
||||
valid_lft forever preferred_lft forever
|
||||
inet6 fe80::42:bcff:fef6:1abd/64 scope link proto kernel_ll
|
||||
valid_lft forever preferred_lft forever
|
||||
7: br-fa41b0d19f8e: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
|
||||
link/ether 02:42:56:3c:ac:24 brd ff:ff:ff:ff:ff:ff
|
||||
inet 192.168.48.1/20 brd 192.168.63.255 scope global br-fa41b0d19f8e
|
||||
valid_lft forever preferred_lft forever
|
||||
inet6 fe80::42:56ff:fe3c:ac24/64 scope link proto kernel_ll
|
||||
valid_lft forever preferred_lft forever
|
||||
8: br-70ba1bd90717: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
|
||||
link/ether 02:42:ac:13:4a:a6 brd ff:ff:ff:ff:ff:ff
|
||||
inet 172.26.0.1/16 brd 172.26.255.255 scope global br-70ba1bd90717
|
||||
valid_lft forever preferred_lft forever
|
||||
inet6 fe80::42:acff:fe13:4aa6/64 scope link proto kernel_ll
|
||||
valid_lft forever preferred_lft forever
|
||||
9: br-72143bca8d63: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
|
||||
link/ether 02:42:91:a3:2d:1f brd ff:ff:ff:ff:ff:ff
|
||||
inet 172.18.0.1/16 brd 172.18.255.255 scope global br-72143bca8d63
|
||||
valid_lft forever preferred_lft forever
|
||||
inet6 fe80::42:91ff:fea3:2d1f/64 scope link proto kernel_ll
|
||||
valid_lft forever preferred_lft forever
|
||||
11: br-bffe9441e56d: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
|
||||
link/ether 02:42:66:ef:dd:4f brd ff:ff:ff:ff:ff:ff
|
||||
inet 172.19.0.1/16 brd 172.19.255.255 scope global br-bffe9441e56d
|
||||
valid_lft forever preferred_lft forever
|
||||
inet6 fe80::42:66ff:feef:dd4f/64 scope link proto kernel_ll
|
||||
valid_lft forever preferred_lft forever
|
||||
12: br-2a6267f1d942: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
|
||||
link/ether 02:42:7b:5b:ed:9e brd ff:ff:ff:ff:ff:ff
|
||||
inet 172.21.0.1/16 brd 172.21.255.255 scope global br-2a6267f1d942
|
||||
valid_lft forever preferred_lft forever
|
||||
inet6 fe80::42:7bff:fe5b:ed9e/64 scope link proto kernel_ll
|
||||
valid_lft forever preferred_lft forever
|
||||
13: br-7530729ebfae: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
|
||||
link/ether 02:42:b7:e0:17:4f brd ff:ff:ff:ff:ff:ff
|
||||
inet 192.168.32.1/20 brd 192.168.47.255 scope global br-7530729ebfae
|
||||
valid_lft forever preferred_lft forever
|
||||
inet6 fe80::42:b7ff:fee0:174f/64 scope link proto kernel_ll
|
||||
valid_lft forever preferred_lft forever
|
||||
14: br-a8e208276d2f: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
|
||||
link/ether 02:42:59:9f:c6:22 brd ff:ff:ff:ff:ff:ff
|
||||
inet 172.20.0.1/16 brd 172.20.255.255 scope global br-a8e208276d2f
|
||||
valid_lft forever preferred_lft forever
|
||||
15: br-bb1a116b6f77: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
|
||||
link/ether 02:42:69:eb:0c:b9 brd ff:ff:ff:ff:ff:ff
|
||||
inet 172.25.0.1/16 brd 172.25.255.255 scope global br-bb1a116b6f77
|
||||
valid_lft forever preferred_lft forever
|
||||
inet6 fe80::42:69ff:feeb:cb9/64 scope link proto kernel_ll
|
||||
valid_lft forever preferred_lft forever
|
||||
17: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
|
||||
link/ether 02:42:37:8a:ab:2f brd ff:ff:ff:ff:ff:ff
|
||||
inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
|
||||
valid_lft forever preferred_lft forever
|
||||
inet6 fe80::42:37ff:fe8a:ab2f/64 scope link proto kernel_ll
|
||||
valid_lft forever preferred_lft forever
|
||||
18: br-be1a91b20dc8: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
|
||||
link/ether 02:42:cd:7e:51:db brd ff:ff:ff:ff:ff:ff
|
||||
inet 172.29.0.1/16 brd 172.29.255.255 scope global br-be1a91b20dc8
|
||||
valid_lft forever preferred_lft forever
|
||||
inet6 fe80::42:cdff:fe7e:51db/64 scope link proto kernel_ll
|
||||
valid_lft forever preferred_lft forever
|
||||
19: br-30777ab296b8: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
|
||||
link/ether 02:42:af:5f:6f:f2 brd ff:ff:ff:ff:ff:ff
|
||||
inet 172.27.0.1/16 brd 172.27.255.255 scope global br-30777ab296b8
|
||||
valid_lft forever preferred_lft forever
|
||||
inet6 fe80::42:afff:fe5f:6ff2/64 scope link proto kernel_ll
|
||||
valid_lft forever preferred_lft forever
|
||||
20: br-49d3d5da502c: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
|
||||
link/ether 02:42:9f:ae:96:94 brd ff:ff:ff:ff:ff:ff
|
||||
inet 172.30.0.1/16 brd 172.30.255.255 scope global br-49d3d5da502c
|
||||
valid_lft forever preferred_lft forever
|
||||
inet6 fe80::42:9fff:feae:9694/64 scope link proto kernel_ll
|
||||
valid_lft forever preferred_lft forever
|
||||
24: vethf4816dc@if23: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br-fa41b0d19f8e state UP group default
|
||||
link/ether b6:a6:ed:49:bd:fe brd ff:ff:ff:ff:ff:ff link-netnsid 16
|
||||
inet6 fe80::b4a6:edff:fe49:bdfe/64 scope link proto kernel_ll
|
||||
valid_lft forever preferred_lft forever
|
||||
26: veth1987749@if25: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br-9e267d3240c9 state UP group default
|
||||
link/ether 36:c0:50:3d:b1:96 brd ff:ff:ff:ff:ff:ff link-netnsid 13
|
||||
inet6 fe80::34c0:50ff:fe3d:b196/64 scope link proto kernel_ll
|
||||
valid_lft forever preferred_lft forever
|
||||
30: veth85fe678@if29: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br-30777ab296b8 state UP group default
|
||||
link/ether 8a:f3:42:ef:e1:6b brd ff:ff:ff:ff:ff:ff link-netnsid 4
|
||||
inet6 fe80::88f3:42ff:feef:e16b/64 scope link proto kernel_ll
|
||||
valid_lft forever preferred_lft forever
|
||||
32: vethd5b37cc@if31: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br-bffe9441e56d state UP group default
|
||||
link/ether 3e:99:51:e7:ec:ef brd ff:ff:ff:ff:ff:ff link-netnsid 10
|
||||
inet6 fe80::3c99:51ff:fee7:ecef/64 scope link proto kernel_ll
|
||||
valid_lft forever preferred_lft forever
|
||||
34: vetha6e4b9a@if33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br-5a4a35faa802 state UP group default
|
||||
link/ether ba:e5:82:e3:18:d4 brd ff:ff:ff:ff:ff:ff link-netnsid 21
|
||||
inet6 fe80::b8e5:82ff:fee3:18d4/64 scope link proto kernel_ll
|
||||
valid_lft forever preferred_lft forever
|
||||
36: veth58ed7ba@if35: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default
|
||||
link/ether 2a:09:5b:4b:75:ef brd ff:ff:ff:ff:ff:ff link-netnsid 0
|
||||
inet6 fe80::2809:5bff:fe4b:75ef/64 scope link proto kernel_ll
|
||||
valid_lft forever preferred_lft forever
|
||||
38: vethec42d32@if37: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br-be1a91b20dc8 state UP group default
|
||||
link/ether 3e:47:cf:ef:85:dd brd ff:ff:ff:ff:ff:ff link-netnsid 3
|
||||
inet6 fe80::3c47:cfff:feef:85dd/64 scope link proto kernel_ll
|
||||
valid_lft forever preferred_lft forever
|
||||
40: vethbfda3d6@if39: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br-70ba1bd90717 state UP group default
|
||||
link/ether 8e:b3:04:4c:20:24 brd ff:ff:ff:ff:ff:ff link-netnsid 9
|
||||
inet6 fe80::8cb3:4ff:fe4c:2024/64 scope link proto kernel_ll
|
||||
valid_lft forever preferred_lft forever
|
||||
42: veth3b113fe@if41: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br-49d3d5da502c state UP group default
|
||||
link/ether 02:10:8b:04:57:f2 brd ff:ff:ff:ff:ff:ff link-netnsid 1
|
||||
inet6 fe80::10:8bff:fe04:57f2/64 scope link proto kernel_ll
|
||||
valid_lft forever preferred_lft forever
|
||||
44: vethfaaa84d@if43: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br-7530729ebfae state UP group default
|
||||
link/ether 32:39:e5:7c:f2:c8 brd ff:ff:ff:ff:ff:ff link-netnsid 2
|
||||
inet6 fe80::3039:e5ff:fe7c:f2c8/64 scope link proto kernel_ll
|
||||
valid_lft forever preferred_lft forever
|
||||
46: veth34136f6@if45: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br-49d3d5da502c state UP group default
|
||||
link/ether 2e:db:db:40:b3:f3 brd ff:ff:ff:ff:ff:ff link-netnsid 15
|
||||
inet6 fe80::2cdb:dbff:fe40:b3f3/64 scope link proto kernel_ll
|
||||
valid_lft forever preferred_lft forever
|
||||
48: vethd05d87a@if47: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br-be1a91b20dc8 state UP group default
|
||||
link/ether 4e:8c:a7:c5:c2:86 brd ff:ff:ff:ff:ff:ff link-netnsid 14
|
||||
inet6 fe80::4c8c:a7ff:fec5:c286/64 scope link proto kernel_ll
|
||||
valid_lft forever preferred_lft forever
|
||||
50: vethd0baa49@if49: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default
|
||||
link/ether 06:13:ff:e9:a6:60 brd ff:ff:ff:ff:ff:ff link-netnsid 19
|
||||
inet6 fe80::413:ffff:fee9:a660/64 scope link proto kernel_ll
|
||||
valid_lft forever preferred_lft forever
|
||||
52: vethdceb901@if51: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br-be1a91b20dc8 state UP group default
|
||||
link/ether 5e:4a:e7:57:f9:74 brd ff:ff:ff:ff:ff:ff link-netnsid 6
|
||||
inet6 fe80::5c4a:e7ff:fe57:f974/64 scope link proto kernel_ll
|
||||
valid_lft forever preferred_lft forever
|
||||
54: vethff5574b@if53: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br-5a4a35faa802 state UP group default
|
||||
link/ether 56:35:0f:70:e5:bf brd ff:ff:ff:ff:ff:ff link-netnsid 18
|
||||
inet6 fe80::5435:fff:fe70:e5bf/64 scope link proto kernel_ll
|
||||
valid_lft forever preferred_lft forever
|
||||
62: vethce624b6@if61: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br-72143bca8d63 state UP group default
|
||||
link/ether ce:38:5d:fb:bc:ce brd ff:ff:ff:ff:ff:ff link-netnsid 8
|
||||
inet6 fe80::cc38:5dff:fefb:bcce/64 scope link proto kernel_ll
|
||||
valid_lft forever preferred_lft forever
|
||||
66: vethe7b822b@if65: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br-72143bca8d63 state UP group default
|
||||
link/ether 2a:eb:3b:03:d5:35 brd ff:ff:ff:ff:ff:ff link-netnsid 12
|
||||
inet6 fe80::28eb:3bff:fe03:d535/64 scope link proto kernel_ll
|
||||
valid_lft forever preferred_lft forever
|
||||
70: vethb0d7c1a@if69: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br-bffe9441e56d state UP group default
|
||||
link/ether 16:32:ce:d0:92:8d brd ff:ff:ff:ff:ff:ff link-netnsid 8
|
||||
inet6 fe80::1432:ceff:fed0:928d/64 scope link proto kernel_ll
|
||||
valid_lft forever preferred_lft forever
|
||||
72: vethdf62a27@if71: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br-72143bca8d63 state UP group default
|
||||
link/ether e2:f4:b0:c1:a2:49 brd ff:ff:ff:ff:ff:ff link-netnsid 10
|
||||
inet6 fe80::e0f4:b0ff:fec1:a249/64 scope link proto kernel_ll
|
||||
valid_lft forever preferred_lft forever
|
||||
74: vethb919b92@if73: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br-72143bca8d63 state UP group default
|
||||
link/ether 62:7b:50:e0:bb:ea brd ff:ff:ff:ff:ff:ff link-netnsid 9
|
||||
inet6 fe80::607b:50ff:fee0:bbea/64 scope link proto kernel_ll
|
||||
valid_lft forever preferred_lft forever
|
||||
76: veth0737587@if75: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br-be1a91b20dc8 state UP group default
|
||||
link/ether ae:a1:02:ef:6e:09 brd ff:ff:ff:ff:ff:ff link-netnsid 12
|
||||
inet6 fe80::aca1:2ff:feef:6e09/64 scope link proto kernel_ll
|
||||
valid_lft forever preferred_lft forever
|
||||
78: veth8613011@if77: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br-72143bca8d63 state UP group default
|
||||
link/ether 3a:54:4d:77:fa:63 brd ff:ff:ff:ff:ff:ff link-netnsid 13
|
||||
inet6 fe80::3854:4dff:fe77:fa63/64 scope link proto kernel_ll
|
||||
valid_lft forever preferred_lft forever
|
||||
80: veth6c24951@if79: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br-72143bca8d63 state UP group default
|
||||
link/ether 9a:4e:c2:9d:5b:5d brd ff:ff:ff:ff:ff:ff link-netnsid 14
|
||||
inet6 fe80::984e:c2ff:fe9d:5b5d/64 scope link proto kernel_ll
|
||||
valid_lft forever preferred_lft forever
|
||||
82: veth621784b@if81: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br-72143bca8d63 state UP group default
|
||||
link/ether 76:b9:09:4c:ea:5b brd ff:ff:ff:ff:ff:ff link-netnsid 15
|
||||
inet6 fe80::74b9:9ff:fe4c:ea5b/64 scope link proto kernel_ll
|
||||
valid_lft forever preferred_lft forever
|
||||
84: vethb8d844d@if83: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br-72143bca8d63 state UP group default
|
||||
link/ether 26:5b:4a:c1:06:99 brd ff:ff:ff:ff:ff:ff link-netnsid 16
|
||||
inet6 fe80::245b:4aff:fec1:699/64 scope link proto kernel_ll
|
||||
valid_lft forever preferred_lft forever
|
||||
86: vethe73ee17@if85: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br-72143bca8d63 state UP group default
|
||||
link/ether 32:3c:06:a1:7b:c3 brd ff:ff:ff:ff:ff:ff link-netnsid 21
|
||||
inet6 fe80::303c:6ff:fea1:7bc3/64 scope link proto kernel_ll
|
||||
valid_lft forever preferred_lft forever
|
||||
88: vetheb0e29b@if87: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br-72143bca8d63 state UP group default
|
||||
link/ether 2a:ed:74:96:5b:5b brd ff:ff:ff:ff:ff:ff link-netnsid 19
|
||||
inet6 fe80::28ed:74ff:fe96:5b5b/64 scope link proto kernel_ll
|
||||
valid_lft forever preferred_lft forever
|
||||
90: veth5614959@if89: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br-72143bca8d63 state UP group default
|
||||
link/ether 6e:ad:c1:cb:fd:62 brd ff:ff:ff:ff:ff:ff link-netnsid 18
|
||||
inet6 fe80::6cad:c1ff:fecb:fd62/64 scope link proto kernel_ll
|
||||
valid_lft forever preferred_lft forever
|
||||
95: flannel.1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UNKNOWN group default
|
||||
link/ether 86:22:0a:4d:90:00 brd ff:ff:ff:ff:ff:ff
|
||||
inet 10.42.0.0/32 scope global flannel.1
|
||||
valid_lft forever preferred_lft forever
|
||||
inet6 fe80::8422:aff:fe4d:9000/64 scope link proto kernel_ll
|
||||
valid_lft forever preferred_lft forever
|
||||
96: cni0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UP group default qlen 1000
|
||||
link/ether 12:c6:0d:7d:dd:9c brd ff:ff:ff:ff:ff:ff
|
||||
inet 10.42.0.1/24 brd 10.42.0.255 scope global cni0
|
||||
valid_lft forever preferred_lft forever
|
||||
inet6 fe80::10c6:dff:fe7d:dd9c/64 scope link proto kernel_ll
|
||||
valid_lft forever preferred_lft forever
|
||||
97: veth445f8110@if2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue master cni0 state UP group default
|
||||
link/ether 4a:31:e4:e9:f7:a3 brd ff:ff:ff:ff:ff:ff link-netns cni-81ae1f26-1017-c5d6-4493-ed9bb16fb501
|
||||
inet6 fe80::4831:e4ff:fee9:f7a3/64 scope link proto kernel_ll
|
||||
valid_lft forever preferred_lft forever
|
||||
98: veth736cde43@if2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue master cni0 state UP group default
|
||||
link/ether 02:df:78:e8:c2:27 brd ff:ff:ff:ff:ff:ff link-netns cni-c2221a39-e9f7-e155-eeb0-39b9fd4c1d66
|
||||
inet6 fe80::df:78ff:fee8:c227/64 scope link proto kernel_ll
|
||||
valid_lft forever preferred_lft forever
|
||||
99: vethc04aa9e2@if2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue master cni0 state UP group default
|
||||
link/ether fa:33:2f:6a:c4:f2 brd ff:ff:ff:ff:ff:ff link-netns cni-6c852b61-9f8e-638e-1071-aef368406551
|
||||
inet6 fe80::f833:2fff:fe6a:c4f2/64 scope link proto kernel_ll
|
||||
valid_lft forever preferred_lft forever
|
||||
100: veth033b45e8@if2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue master cni0 state UP group default
|
||||
link/ether de:90:ff:6b:d3:94 brd ff:ff:ff:ff:ff:ff link-netns cni-e2f9d769-bab1-5b91-4110-f56e53d38d37
|
||||
inet6 fe80::dc90:ffff:fe6b:d394/64 scope link proto kernel_ll
|
||||
valid_lft forever preferred_lft forever
|
||||
101: veth4ad49488@if2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue master cni0 state UP group default
|
||||
link/ether 56:33:f1:1d:fd:48 brd ff:ff:ff:ff:ff:ff link-netns cni-39c366c4-cbab-952a-bb8b-b611d518dab2
|
||||
inet6 fe80::5433:f1ff:fe1d:fd48/64 scope link proto kernel_ll
|
||||
valid_lft forever preferred_lft forever
|
||||
102: vethbf1d0e0b@if2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue master cni0 state UP group default
|
||||
link/ether ae:05:8b:90:7d:d7 brd ff:ff:ff:ff:ff:ff link-netns cni-1f683513-3a91-70e3-87a9-c4292acaedb9
|
||||
inet6 fe80::ac05:8bff:fe90:7dd7/64 scope link proto kernel_ll
|
||||
valid_lft forever preferred_lft forever
|
||||
103: vethbdf19ef0@if2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue master cni0 state UP group default
|
||||
link/ether 26:07:7e:9c:d3:77 brd ff:ff:ff:ff:ff:ff link-netns cni-a42e0f60-d58d-f377-8811-001b64d3726d
|
||||
inet6 fe80::2407:7eff:fe9c:d377/64 scope link proto kernel_ll
|
||||
valid_lft forever preferred_lft forever
|
||||
105: veth7feaf739@if2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue master cni0 state UP group default
|
||||
link/ether 26:7d:bc:ba:a8:ca brd ff:ff:ff:ff:ff:ff link-netns cni-41f4ceb5-6cb8-8f92-953b-95864e9e7cf1
|
||||
inet6 fe80::247d:bcff:feba:a8ca/64 scope link proto kernel_ll
|
||||
valid_lft forever preferred_lft forever
|
||||
106: veth0d598c9d@if2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue master cni0 state UP group default
|
||||
link/ether be:31:2d:cb:aa:84 brd ff:ff:ff:ff:ff:ff link-netns cni-ec30b8aa-5ede-4f67-9c31-77aebc74579c
|
||||
inet6 fe80::bc31:2dff:fecb:aa84/64 scope link proto kernel_ll
|
||||
valid_lft forever preferred_lft forever
|
||||
108: veth612e159c@if2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue master cni0 state UP group default
|
||||
link/ether 6e:ec:c7:4c:0d:e3 brd ff:ff:ff:ff:ff:ff link-netns cni-0ec5bdaa-3575-8063-bb3b-08ac819b3189
|
||||
inet6 fe80::6cec:c7ff:fe4c:de3/64 scope link proto kernel_ll
|
||||
valid_lft forever preferred_lft forever
|
||||
109: veth93eeb514@if2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue master cni0 state UP group default
|
||||
link/ether 4e:79:3f:e1:81:48 brd ff:ff:ff:ff:ff:ff link-netns cni-cb3547f5-7c92-bb53-0589-3f4e3d74bd27
|
||||
inet6 fe80::4c79:3fff:fee1:8148/64 scope link proto kernel_ll
|
||||
valid_lft forever preferred_lft forever
|
||||
110: veth1bd4228f@if2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue master cni0 state UP group default
|
||||
link/ether 1a:29:c8:84:d9:c5 brd ff:ff:ff:ff:ff:ff link-netns cni-ff5b8f46-6411-8fd9-b1e6-512e439595df
|
||||
inet6 fe80::1829:c8ff:fe84:d9c5/64 scope link proto kernel_ll
|
||||
valid_lft forever preferred_lft forever
|
||||
111: veth18e1f9d7@if2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue master cni0 state UP group default
|
||||
link/ether b2:d2:3c:7f:a8:8c brd ff:ff:ff:ff:ff:ff link-netns cni-279ef32e-3c53-9c4f-519d-47333ad178fc
|
||||
inet6 fe80::b0d2:3cff:fe7f:a88c/64 scope link proto kernel_ll
|
||||
valid_lft forever preferred_lft forever
|
||||
572: br-2c51db061b99: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
|
||||
link/ether 02:42:9e:6a:3e:9b brd ff:ff:ff:ff:ff:ff
|
||||
inet 172.24.0.1/16 brd 172.24.255.255 scope global br-2c51db061b99
|
||||
valid_lft forever preferred_lft forever
|
||||
inet6 fe80::42:9eff:fe6a:3e9b/64 scope link proto kernel_ll
|
||||
valid_lft forever preferred_lft forever
|
||||
574: vethdb48521@if573: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br-72143bca8d63 state UP group default
|
||||
link/ether 36:6b:7a:5b:6f:ad brd ff:ff:ff:ff:ff:ff link-netnsid 5
|
||||
inet6 fe80::346b:7aff:fe5b:6fad/64 scope link proto kernel_ll
|
||||
valid_lft forever preferred_lft forever
|
||||
576: vethdb77b7c@if575: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br-2c51db061b99 state UP group default
|
||||
link/ether 9e:93:de:92:ff:8b brd ff:ff:ff:ff:ff:ff link-netnsid 5
|
||||
inet6 fe80::9c93:deff:fe92:ff8b/64 scope link proto kernel_ll
|
||||
valid_lft forever preferred_lft forever
|
||||
636: veth77e5e8e@if635: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br-72143bca8d63 state UP group default
|
||||
link/ether 76:bd:07:8c:8d:f1 brd ff:ff:ff:ff:ff:ff link-netnsid 11
|
||||
inet6 fe80::74bd:7ff:fe8c:8df1/64 scope link proto kernel_ll
|
||||
valid_lft forever preferred_lft forever
|
||||
638: veth01be747@if637: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br-bb1a116b6f77 state UP group default
|
||||
link/ether 12:3c:22:cc:04:d7 brd ff:ff:ff:ff:ff:ff link-netnsid 11
|
||||
inet6 fe80::103c:22ff:fecc:4d7/64 scope link proto kernel_ll
|
||||
valid_lft forever preferred_lft forever
|
||||
1100: br-fefc2ce50c94: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
|
||||
link/ether 02:42:03:65:11:bf brd ff:ff:ff:ff:ff:ff
|
||||
inet 172.28.0.1/16 brd 172.28.255.255 scope global br-fefc2ce50c94
|
||||
valid_lft forever preferred_lft forever
|
||||
inet6 fe80::42:3ff:fe65:11bf/64 scope link proto kernel_ll
|
||||
valid_lft forever preferred_lft forever
|
||||
1102: veth5d48e8b@if1101: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br-fefc2ce50c94 state UP group default
|
||||
link/ether 2e:36:d3:de:71:70 brd ff:ff:ff:ff:ff:ff link-netnsid 32
|
||||
inet6 fe80::2c36:d3ff:fede:7170/64 scope link proto kernel_ll
|
||||
valid_lft forever preferred_lft forever
|
||||
1106: veth813ef74@if1105: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br-fefc2ce50c94 state UP group default
|
||||
link/ether ca:bd:7b:23:ee:7d brd ff:ff:ff:ff:ff:ff link-netnsid 29
|
||||
inet6 fe80::c8bd:7bff:fe23:ee7d/64 scope link proto kernel_ll
|
||||
valid_lft forever preferred_lft forever
|
||||
1160: vethe7aa1c2@if1159: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br-72143bca8d63 state UP group default
|
||||
link/ether 46:c8:27:6d:21:aa brd ff:ff:ff:ff:ff:ff link-netnsid 7
|
||||
inet6 fe80::44c8:27ff:fe6d:21aa/64 scope link proto kernel_ll
|
||||
valid_lft forever preferred_lft forever
|
||||
1162: veth5b67937@if1161: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br-fefc2ce50c94 state UP group default
|
||||
link/ether 02:4a:ca:26:ff:10 brd ff:ff:ff:ff:ff:ff link-netnsid 7
|
||||
inet6 fe80::4a:caff:fe26:ff10/64 scope link proto kernel_ll
|
||||
valid_lft forever preferred_lft forever
|
||||
1208: veth3c71fd4@if1207: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default
|
||||
link/ether b6:43:00:76:43:22 brd ff:ff:ff:ff:ff:ff link-netnsid 17
|
||||
inet6 fe80::b443:ff:fe76:4322/64 scope link proto kernel_ll
|
||||
valid_lft forever preferred_lft forever
|
||||
1210: veth8f5c212@if1209: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br-72143bca8d63 state UP group default
|
||||
link/ether f6:b0:30:80:4e:52 brd ff:ff:ff:ff:ff:ff link-netnsid 17
|
||||
inet6 fe80::f4b0:30ff:fe80:4e52/64 scope link proto kernel_ll
|
||||
valid_lft forever preferred_lft forever
|
||||
1215: veth2cd00be8@if2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue master cni0 state UP group default
|
||||
link/ether de:83:da:54:0d:ad brd ff:ff:ff:ff:ff:ff link-netns cni-0b8ed978-d1dc-2353-3e39-95be7b63e661
|
||||
inet6 fe80::dc83:daff:fe54:dad/64 scope link proto kernel_ll
|
||||
valid_lft forever preferred_lft forever
|
||||
1225: veth0e83a05@if1224: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default
|
||||
link/ether 6e:ce:17:da:09:69 brd ff:ff:ff:ff:ff:ff link-netnsid 20
|
||||
inet6 fe80::6cce:17ff:feda:969/64 scope link proto kernel_ll
|
||||
valid_lft forever preferred_lft forever
|
||||
1
cloud-migration/inventory/uname.txt
Normal file
1
cloud-migration/inventory/uname.txt
Normal file
|
|
@ -0,0 +1 @@
|
|||
Linux Assassin 6.12.90+deb13.1-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.12.90-2 (2026-05-27) x86_64 GNU/Linux
|
||||
77
cloud-migration/restore/RESTORE.md
Normal file
77
cloud-migration/restore/RESTORE.md
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
# Restore Outline
|
||||
|
||||
This is the high-level restore path for a new Hetzner Cloud host.
|
||||
|
||||
## 1. Prepare Host
|
||||
|
||||
Install Docker and Docker Compose plugin.
|
||||
|
||||
```bash
|
||||
sudo apt update
|
||||
sudo apt install -y docker.io docker-compose-plugin git tar gzip
|
||||
sudo usermod -aG docker "$USER"
|
||||
```
|
||||
|
||||
Log out and back in after adding the Docker group.
|
||||
|
||||
## 2. Clone Backup
|
||||
|
||||
```bash
|
||||
git clone https://gitforge.kitestacks.com/kenpat/kitestacks-cloud.git
|
||||
cd kitestacks-cloud
|
||||
sha256sum -c SHA256SUMS
|
||||
```
|
||||
|
||||
## 3. Restore Bind-Mounted Data
|
||||
|
||||
```bash
|
||||
mkdir -p /home/kenpat
|
||||
tar -xzf archives/docker-bind-data.tar.gz -C /home/kenpat
|
||||
tar -xzf archives/syncthing-shared.tar.gz -C /home/kenpat
|
||||
tar -xzf archives/kitestacks-scripts.tar.gz -C /home/kenpat
|
||||
```
|
||||
|
||||
## 4. Restore Named Docker Volumes
|
||||
|
||||
Create the external network used by the stack:
|
||||
|
||||
```bash
|
||||
docker network create kitestacks
|
||||
```
|
||||
|
||||
For each file in `volume-exports`, create and restore the volume:
|
||||
|
||||
```bash
|
||||
for archive in volume-exports/*.tar.gz; do
|
||||
volume="$(basename "$archive" .tar.gz)"
|
||||
docker volume create "$volume"
|
||||
docker run --rm \
|
||||
-v "$volume:/volume" \
|
||||
-v "$PWD/volume-exports:/backup:ro" \
|
||||
alpine \
|
||||
sh -c "tar -xzf /backup/$volume.tar.gz -C /volume"
|
||||
done
|
||||
```
|
||||
|
||||
## 5. Start Services
|
||||
|
||||
Start core services in dependency order. Example:
|
||||
|
||||
```bash
|
||||
cd /home/kenpat/docker/authentik && docker compose up -d
|
||||
cd /home/kenpat/docker/forgejo && docker compose up -d
|
||||
cd /home/kenpat/docker/cloudflared && docker compose up -d
|
||||
```
|
||||
|
||||
Then start the remaining service folders under `/home/kenpat/docker`.
|
||||
|
||||
## 6. DNS and Tunnel Cutover
|
||||
|
||||
Review:
|
||||
|
||||
- `inventory/docker-running.txt`
|
||||
- `inventory/docker-networks.txt`
|
||||
- `/home/kenpat/docker/cloudflared/docker-compose.yml`
|
||||
- Cloudflare tunnel credentials under the restored cloudflared folder
|
||||
|
||||
Move DNS/tunnel targets to the Hetzner host after services are healthy.
|
||||
BIN
cloud-migration/volume-exports/95f721b2e8b90b4e17a3675d4905837933bc366e12c15d16bce1bd9d166c43f0.tar.gz
(Stored with Git LFS)
Normal file
BIN
cloud-migration/volume-exports/95f721b2e8b90b4e17a3675d4905837933bc366e12c15d16bce1bd9d166c43f0.tar.gz
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
cloud-migration/volume-exports/b11ac7c9fe060195954a46980f7ed85da9a62fea48d7cca5990aa54ec3d4cd8a.tar.gz
(Stored with Git LFS)
Normal file
BIN
cloud-migration/volume-exports/b11ac7c9fe060195954a46980f7ed85da9a62fea48d7cca5990aa54ec3d4cd8a.tar.gz
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
cloud-migration/volume-exports/b1a99e9e271f6dbb1e693aabd2508fbae9973a82c5c1778fdad099054754f111.tar.gz
(Stored with Git LFS)
Normal file
BIN
cloud-migration/volume-exports/b1a99e9e271f6dbb1e693aabd2508fbae9973a82c5c1778fdad099054754f111.tar.gz
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
cloud-migration/volume-exports/b303d482950f666b37acae69c790f0f37e9d28735a4ea0b98a7961a8a400fa75.tar.gz
(Stored with Git LFS)
Normal file
BIN
cloud-migration/volume-exports/b303d482950f666b37acae69c790f0f37e9d28735a4ea0b98a7961a8a400fa75.tar.gz
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
cloud-migration/volume-exports/kite-ai_open-webui.tar.gz
(Stored with Git LFS)
Normal file
BIN
cloud-migration/volume-exports/kite-ai_open-webui.tar.gz
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
cloud-migration/volume-exports/openproject_openproject_assets.tar.gz
(Stored with Git LFS)
Normal file
BIN
cloud-migration/volume-exports/openproject_openproject_assets.tar.gz
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
cloud-migration/volume-exports/openproject_openproject_db_data.tar.gz
(Stored with Git LFS)
Normal file
BIN
cloud-migration/volume-exports/openproject_openproject_db_data.tar.gz
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
cloud-migration/volume-exports/openproject_openproject_logs.tar.gz
(Stored with Git LFS)
Normal file
BIN
cloud-migration/volume-exports/openproject_openproject_logs.tar.gz
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
cloud-migration/volume-exports/openproject_openproject_pgdata.tar.gz
(Stored with Git LFS)
Normal file
BIN
cloud-migration/volume-exports/openproject_openproject_pgdata.tar.gz
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
cloud-migration/volume-exports/portainer_data.tar.gz
(Stored with Git LFS)
Normal file
BIN
cloud-migration/volume-exports/portainer_data.tar.gz
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
cloud-migration/volume-exports/uptime-kuma.tar.gz
(Stored with Git LFS)
Normal file
BIN
cloud-migration/volume-exports/uptime-kuma.tar.gz
(Stored with Git LFS)
Normal file
Binary file not shown.
5
cloud/README.md
Normal file
5
cloud/README.md
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
# KiteStacks Cloud
|
||||
|
||||
This folder is reserved for cloud-specific configurations.
|
||||
|
||||
For the main homelab setup, see the root of this repository.
|
||||
7
osticket/CHANGELOG.md
Normal file
7
osticket/CHANGELOG.md
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
# Changelog
|
||||
|
||||
This file explains what changed in simple words.
|
||||
|
||||
## 2026-06-08
|
||||
- Started Phase 1: OSTicketSystem.
|
||||
- Created separate files for runbook, debug notes, and change history.
|
||||
3
osticket/DEBUG-DOCUMENTATION.md
Normal file
3
osticket/DEBUG-DOCUMENTATION.md
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
# Debug Documentation
|
||||
|
||||
This file is for errors, fixes, broken commands, and troubleshooting notes.
|
||||
5
osticket/README.md
Normal file
5
osticket/README.md
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
# OSTicketSystem
|
||||
|
||||
This is Phase 1 of the training project.
|
||||
|
||||
Goal: Build a custom ticket system where classmates can log in, claim tickets, complete tasks, and close tickets.
|
||||
5
osticket/RUNBOOK.md
Normal file
5
osticket/RUNBOOK.md
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
# OSTicketSystem Runbook
|
||||
|
||||
This file explains how to build and use the project from start to finish.
|
||||
|
||||
No debugging notes go here.
|
||||
Loading…
Add table
Add a link
Reference in a new issue