# Bastion — Claude Code Context ## Project Overview Bastion is a self-hosted server system on Debian that runs multiple services in Docker containers. All containers are managed via Portainer. Nginx acts as reverse proxy sitting on all service networks. ## Domain - Primary domain: `swave.lol` - Subdomains: `blog.swave.lol`, `ghost.swave.lol`, `jenkins.swave.lol`, `cgit.swave.lol`, `gerrit.swave.lol`, `nexus.swave.lol`, `registry.swave.lol`, `auth.swave.lol` ## Services / Stacks ### Portainer - Standalone `docker run`, not in a compose file - Web UI on port 9000 - Used to manage all other containers/stacks ### Ghost (blog) - Dir: `ghost/` - Compose: `ghost/compose.yml`, env: `ghost/stack.env` (for Portainer) or `.env` (for local) - Network: `ghost_network` - Services: ghost (port 2368), ghost-db (MySQL 8), activitypub (optional, profile-gated) - Accessible at `https://swave.lol`, `https://blog.swave.lol`, and `https://ghost.swave.lol` - URL must be configured as `https://` in compose.yml (Ghost caches URL config internally) - DOMAIN in stack.env must be the bare domain (e.g., `swave.lol`) without a port number ### Git Server - Dir: `git-server/` - Compose: `git-server/server.yaml`, needs `docker build -t git-server .` first - Networks: `git-network` (172.22.0.0/16, static IP 172.22.0.2) and `jenkins_network` (172.23.0.0/16, static IP 172.23.0.3) - Ports: 22 (SSH), 9418 (git daemon) - Volumes: `/var/git_ssh_keys` (public keys), `/var/git/repos` (repositories) - Host SSH runs on a non-standard port, so port 22 is free for git ### Cgit (git web interface) - Dir: `git-server/cgit/` - Builds from `git-server/cgit/Dockerfile` (Alpine + cgit + fcgiwrap + nginx) - Part of `git-server/server.yaml` compose stack - Network: `git-network` - Mounts `/var/git/repos:/repos:ro` (read-only, shared with git-server) - Auto-discovers all bare repos via `scan-path=/repos` - No host ports (accessed only via main nginx reverse proxy) - Accessible at `https://cgit.swave.lol` and `https://swave.lol/cgit` ### Gerrit (code review) - Part of `git-server/server.yaml` compose stack - Image: `gerritcodereview/gerrit:latest` (no custom Dockerfile) - Networks: `git-network` (static IP 172.22.0.4) and `jenkins_network` (static IP 172.23.0.4) - Web UI on port 8080 internally, SSH on 29418 - No host ports (accessed only via main nginx reverse proxy) - Volumes: `/var/gerrit` (etc, git, db, index, cache), shared `/var/git/repos` - Accessible at `https://gerrit.swave.lol` (swave.lol/gerrit redirects to subdomain) - Auth: HTTP header auth (`X-Forwarded-User` from Authelia), anonymous read allowed - nginx auth_request only on `/login/` path — Gerrit manages its own session cookie after login - `loginUrl = https://gerrit.swave.lol/login/` in gerrit.config triggers Authelia SSO via nginx - Config template: `git-server/gerrit.config.example` (real config gitignored, lives at `/var/gerrit/etc/gerrit.config`) ### Jenkins - Dir: `jenkins/` - Compose: `jenkins/docker-compose.yaml`, builds from `jenkins/Dockerfile` - Network: `jenkins_network` (172.23.0.0/16, static IP 172.23.0.2) - Uses Docker-out-of-Docker (DooD): mounts `/var/run/docker.sock` to spawn sibling build containers - Dockerfile extends `jenkins/jenkins:lts` with Docker CLI and `docker-workflow` plugin - No path prefix (JENKINS_OPTS is empty); accessible at `https://jenkins.swave.lol/` - `swave.lol/jenkins` redirects to `jenkins.swave.lol/` (strips the prefix) - Ports: 8080 (web UI), 50000 (agent communication via JNLP, used by remote build agents) - Volume: `/var/jenkins_home` - Auth: Authelia OIDC (`oic-auth` plugin) — `authorization_policy: one_factor` required in Authelia client config - OIDC redirect URI: `https://jenkins.swave.lol/securityRealm/finishLogin` (must bypass nginx auth_request) - Username field: `preferred_username` (maps Authelia username to Jenkins user) - Jenkins URL setting must be `https://jenkins.swave.lol/` - Pipeline durability: set to "Performance-optimized" to prevent flow execution corruption - Startup script: `jenkins/init.groovy.d/clear-stuck-builds.groovy` auto-cleans stuck builds on restart - Currently runs all builds on the controller via DooD; supports adding permanent SSH agents for scaling (see setup guide 6.6) ### Nexus (artifact repository & Docker registry) - Dir: `nexus/` - Compose: `nexus/docker-compose.yaml` - Image: `sonatype/nexus3:latest` (no custom Dockerfile) - Network: `nexus_network` (172.25.0.0/16, static IP 172.25.0.2) - Nexus web UI on port 8081 internally, Docker registry API on port 5000 - No host ports (accessed only via main nginx reverse proxy) - Volume: `/var/nexus-data` (must be owned by UID 200) - Accessible at `https://nexus.swave.lol` and `https://swave.lol/nexus` - Docker registry accessible at `https://registry.swave.lol` - Docker registry requires manual setup after first login: create a "docker (hosted)" repository with HTTP connector on port 5000 ### Authelia (SSO / authentication) - Dir: `authelia/` - Compose: `authelia/docker-compose.yaml` - Image: `authelia/authelia:latest` - Network: `authelia_network` (172.26.0.0/16, static IP 172.26.0.2) - Services: authelia (port 9091), authelia-redis (session store) - Volumes: `/var/authelia/config`, `/var/authelia/data`, `/var/authelia/secrets` (ro), `/var/authelia/redis` - Secrets loaded from files in `/var/authelia/secrets/` (JWT, session, storage encryption, OIDC HMAC, OIDC RSA key) - Accessible at `https://auth.swave.lol` - Forward-auth for: Gerrit, Jenkins, Nexus, Netdata, Cockpit - OIDC provider for Jenkins (full SSO) - HTTP header injection for Gerrit (`X-Forwarded-User`) - Ghost and Cgit remain public (no auth) - Setup guide: `authelia/setup.md` ### Nginx (reverse proxy) - Dir: `nginx/` - Compose: `nginx/docker-compose.yaml` - Network: joins `git-network` (IP 172.22.0.254), `ghost_network`, `jenkins_network`, `nexus_network`, `authelia_network` - Ports: 80 (HTTP, redirects to HTTPS), 443 (HTTPS) - Config mounted from `/var/nginx/conf/` on host - SSL: Let's Encrypt certs via `run_certbot.sh`, dhparam at `/var/dh_param/` - SSL protocols: TLSv1.2 + TLSv1.3 only - Default catch-all server blocks return 404 error page for unknown subdomains - Error page: `nginx/error-pages/unknown-host.html` - IMPORTANT: SSL server blocks must be commented out for first run (before certs exist) ## Networking Each stack has its own Docker network. Nginx joins all of them to reverse proxy: - `git-network` — git-server + cgit + gerrit + nginx - `ghost_network` — ghost + ghost-db + nginx - `jenkins_network` — jenkins + git-server + gerrit + nginx - `nexus_network` — nexus + nginx - `authelia_network` — authelia + authelia-redis + nginx ## Startup Order 1. Portainer (standalone) 2. Git Server (creates git-network) 3. Ghost (creates ghost_network) 4. Jenkins (creates jenkins_network) 5. Nexus (creates nexus_network) 6. Authelia (creates authelia_network) 7. Gerrit (joins git-network + jenkins_network, both must exist) 8. Nginx (joins all networks, must be last) ## Key Files - `doc/setup-guide.md` — full build instructions from fresh Debian - `doc/Jenkinsfile.example` — example DooD pipeline - `doc/Jenkinsfile.kernel-example` — example kernel build pipeline using kernel-builder image - `doc/jenkins-cpp-build.md` — guide for building C++ apps in Jenkins with Docker agent - `jenkins/kernel-builder/Dockerfile` — custom Docker image for building the Linux kernel - `jenkins/init.groovy.d/clear-stuck-builds.groovy` — startup script to clean stuck pipeline executions - `nexus/docker-compose.yaml` — Nexus Repository Manager + Docker registry - `doc/Jenkinsfile.nexus-example` — example pipelines for uploading artifacts and Docker images to Nexus - `authelia/setup.md` — Authelia SSO setup guide (secrets, OIDC, Gerrit HTTP auth, Jenkins OIDC) - `authelia/nginx-authelia.conf` — nginx server block snippet for auth.swave.lol - `authelia/config/configuration.yml` — Authelia config (OIDC_RSA_KEY_PLACEHOLDER replaced by awk on server) - `authelia/config/users_database.yml.example` — user database template (real file gitignored) - `git-server/gerrit.config.example` — Gerrit config template (real file gitignored, lives at `/var/gerrit/etc/gerrit.config`) - `ghost/.env.example` — template for Ghost env vars - `doc/git-performance.md` — git server performance tuning guide - `nginx/error-pages/unknown-host.html` — 404 page for unknown subdomains - `.gitignore` — excludes `.env` files and other secrets ## Conventions - Each service lives in its own directory with its own compose file - Compose files are named `docker-compose.yaml` or `compose.yml` or `server.yaml` (inconsistent, historical) - Environment variables with secrets go in `.env`/`stack.env` files (gitignored) - Nginx config is a single `nginx.conf` with multiple server blocks - All services use `restart: always` ## TODOs (from Readme.md) - Automatic certificate renewal for Let's Encrypt - Better organization of nginx configs (split per service?) - ~~Cgit web interface for git repos~~ (done) - ~~Nexus~~ (done) - Firewall rules (allow only 80, 443, 22, 8080, 8000, 9000, 9418, 50000)