# 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` ## 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` and `https://swave.lol/gerrit` ### 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 - `--prefix=/jenkins` set via JENKINS_OPTS for path-based access - Accessible at `https://jenkins.swave.lol` and `https://swave.lol/jenkins` - Ports: 8080 (web UI), 50000 (agent communication via JNLP, used by remote build agents) - Volume: `/var/jenkins_home` - 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) ### Nginx (reverse proxy) - Dir: `nginx/` - Compose: `nginx/docker-compose.yaml` - Network: joins `git-network` (IP 172.22.0.254), `ghost_network`, `jenkins_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 ## Startup Order 1. Portainer (standalone) 2. Git Server (creates git-network) 3. Ghost (creates ghost_network) 4. Jenkins (creates jenkins_network) 5. Gerrit (joins git-network + jenkins_network, both must exist) 6. 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 - `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 (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 (mentioned in Readme but not yet set up) - Firewall rules (allow only 80, 443, 22, 8080, 8000, 9000, 9418, 50000)