# Bastion Server — Setup Guide Full instruction to build the server from a fresh Debian installation. ## Prerequisites - Fresh Debian server - Root access - Domain `swave.lol` with DNS A records pointing to the server: - `swave.lol` - `blog.swave.lol` - `jenkins.swave.lol` - `cgit.swave.lol` ## 1. Install Docker Based on https://docs.docker.com/engine/install/debian/#install-using-the-repository ```bash # Install prerequisites apt-get update apt-get install -y ca-certificates curl gnupg # Add Docker GPG key install -m 0755 -d /etc/apt/keyrings curl -fsSL https://download.docker.com/linux/debian/gpg -o /etc/apt/keyrings/docker.asc chmod a+r /etc/apt/keyrings/docker.asc # Add Docker repository echo \ "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/debian \ $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \ tee /etc/apt/sources.list.d/docker.list > /dev/null # Install Docker apt-get update apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin # Verify docker run hello-world ``` ## 2. Clone the bastion repository ```bash cd /root/Projects git clone bastion cd bastion ``` ## 3. Start Portainer Portainer manages all other containers and stacks via web UI. ```bash docker run -d \ --name="portainer" \ --restart on-failure \ -p 9000:9000 \ -p 8000:8000 \ -v /var/run/docker.sock:/var/run/docker.sock \ -v portainer_data:/data \ portainer/portainer-ce:latest ``` Access Portainer at `http://:9000` and create admin account. ## 4. Git Server ### 4.1 Create host directories ```bash # Directory for SSH public keys (for git access) mkdir -p /var/git_ssh_keys # Directory for git repositories mkdir -p /var/git/repos # Copy your public SSH keys for git access (use ed25519, RSA is not supported) # cp ~/.ssh/id_ed25519.pub /var/git_ssh_keys/mykey.pub ``` ### 4.2 Build and start ```bash cd /root/Projects/bastion/git-server # Build the git-server image docker build -t git-server . # Start (this also creates the git-network) docker compose -f server.yaml up -d ``` ### 4.3 Create a test repository ```bash # On the server cd /var/git/repos git init --bare test.git chown -R 1000:1000 test.git # git user inside container # From a client machine git clone ssh://git@/repos/test.git or git clone git:///test.git ``` ## 5. Ghost (Blog) ### 5.1 Prepare environment ```bash cd /root/Projects/bastion/ghost # Copy the mysql init script to the expected location mkdir -p /var/lib/docker/mysql-init-script cp mysql-init-script/create-multiple-databases.sh /var/lib/docker/mysql-init-script/ # Edit stack.env (or .env for non-Portainer use) with your credentials # IMPORTANT: Change default passwords before first run! ``` Key settings in `stack.env`: - `DOMAIN` — your domain (e.g. `swave.lol`) - `DATABASE_ROOT_PASSWORD` — MySQL root password - `DATABASE_USER` / `DATABASE_PASSWORD` — Ghost database credentials - `UPLOAD_LOCATION` — where Ghost stores content ### 5.2 Start Ghost ```bash cd /root/Projects/bastion/ghost docker compose up -d ``` Ghost will be available at `http://:2368` for testing before Nginx is configured. To start with ActivityPub support (optional): ```bash docker compose --profile activitypub up -d ``` ## 6. Jenkins ### 6.1 Create host directories ```bash mkdir -p /var/jenkins_home ``` ### 6.2 Build and start ```bash cd /root/Projects/bastion/jenkins # Build Jenkins image (includes Docker CLI and docker-workflow plugin) docker compose build # Start Jenkins docker compose up -d ``` ### 6.3 Get initial admin password ```bash docker exec jenkins cat /var/jenkins_home/secrets/initialAdminPassword ``` Jenkins will be available at `http://:8080/jenkins` for initial setup. ### 6.4 Jenkins build setup (DooD) Jenkins uses Docker-out-of-Docker: it spawns sibling containers on the host for builds. Each project defines its own build image in the Jenkinsfile: ```groovy pipeline { agent { docker { image 'node:20' } } stages { stage('Build') { steps { sh 'npm install && npm run build' } } } } ``` See `doc/Jenkinsfile.example` for a full multi-stage example. ## 7. Nginx (Reverse Proxy + SSL) Nginx must be started AFTER Git Server, Ghost, and Jenkins, because it joins their networks as external. ### 7.1 Create host directories ```bash # Nginx config directory mkdir -p /var/nginx/conf # Copy nginx.conf cp /root/Projects/bastion/nginx/nginx.conf /var/nginx/conf/ # DH parameters (this takes a few minutes) mkdir -p /var/dh_param openssl dhparam -out /var/dh_param/dhparam-2048.pem 2048 # Let's Encrypt directories mkdir -p /var/letsencrypt/etc mkdir -p /var/letsencrypt/lts_site # Copy the ACME challenge placeholder page cp /root/Projects/bastion/nginx/letsencrypt/index.html /var/letsencrypt/lts_site/ ``` ### 7.2 First run — HTTP only (no SSL yet) Before we have SSL certificates, we need to temporarily disable the SSL server blocks so Nginx can start and serve the ACME challenge for certbot. ```bash # Edit the nginx.conf copy on the host to comment out SSL server blocks # Keep only the port 80 server block nano /var/nginx/conf/nginx.conf ``` Comment out everything from `# Ghost — swave.lol` to the end of file. Keep only the `listen 80` server block. ```bash cd /root/Projects/bastion/nginx docker compose up -d ``` ### 7.3 Obtain SSL certificates ```bash cd /root/Projects/bastion/nginx bash run_certbot.sh ``` This requests certificates for: - `swave.lol` - `blog.swave.lol` - `jenkins.swave.lol` - `cgit.swave.lol` IMPORTANT: All DNS records must be pointing to the server before running certbot. ### 7.4 Enable SSL ```bash # Restore full nginx.conf with SSL blocks cp /root/Projects/bastion/nginx/nginx.conf /var/nginx/conf/ # Restart Nginx cd /root/Projects/bastion/nginx docker compose restart ``` ### 7.5 Verify All services should now be accessible: | URL | Service | |-----|---------| | `https://swave.lol` | Ghost (blog) | | `https://blog.swave.lol` | Ghost (blog, alias) | | `https://swave.lol/jenkins` | Jenkins (path-based) | | `https://jenkins.swave.lol` | Jenkins (subdomain) | | `https://swave.lol/cgit` | Cgit (path-based) | | `https://cgit.swave.lol` | Cgit (subdomain) | | `http://:9000` | Portainer | | `ssh://git@/repos/.git` | Git (SSH) | | `git:///.git` | Git (daemon, read-only) | ## Network Architecture ``` git-network (172.22.0.0/16) ├── git-server (172.22.0.2) ├── cgit └── nginx (172.22.0.254) ghost_network ├── ghost ├── ghost-db ├── activitypub (optional) └── nginx jenkins_network ├── jenkins └── nginx ``` Nginx sits on all networks so it can reverse proxy to every service. ## Startup Order The correct order to start all services: ```bash # 1. Portainer (standalone, no dependencies) docker run -d --name="portainer" --restart on-failure \ -p 9000:9000 -p 8000:8000 \ -v /var/run/docker.sock:/var/run/docker.sock \ -v portainer_data:/data portainer/portainer-ce:latest # 2. Git Server (creates git-network) docker compose -f /root/Projects/bastion/git-server/server.yaml up -d # 3. Ghost (creates ghost_network) docker compose -f /root/Projects/bastion/ghost/compose.yml up -d # 4. Jenkins (creates jenkins_network) docker compose -f /root/Projects/bastion/jenkins/docker-compose.yaml up -d # 5. Nginx (joins all networks — must be last) docker compose -f /root/Projects/bastion/nginx/docker-compose.yaml up -d ``` ## Ports Summary | Port | Service | Protocol | |------|---------|----------| | 22 | Git Server (SSH) | TCP | | 80 | Nginx (HTTP, redirects to 443) | TCP | | 443 | Nginx (HTTPS) | TCP | | 2368 | Ghost (direct, for testing) | TCP | | 8080 | Jenkins (direct, for testing) | TCP | | 9000 | Portainer (web UI) | TCP | | 9418 | Git Server (git daemon) | TCP | | 50000 | Jenkins (agent communication) | TCP |