# VPS Server Structure — Context Prompt for Deployments *Paste this at the start of any new chat where you're deploying or configuring something on the VPS.* --- ## Machine IONOS VPS, type VPS 6-8-240 (6 vCore, 8 GB RAM, 240 GB NVMe) OS: Debian 13 (trixie) User: `patsy` Domain: `bujour.de` / `bujour.info` ## Directory layout ``` /home/patsy/ ├── stacks/ # Docker Compose stacks for infrastructure │ ├── caddy/ # Reverse proxy │ │ ├── compose.yaml │ │ └── Caddyfile │ └── forgejo/ # Git server └── apps/ # Application source / runtime dirs └── checkpoint/ # CheckPoint Ehrenamt app ``` ## Running containers | Container | Image | Ports | Network | |------------|-------------------------------|------------------------------|---------| | caddy | caddy:2 | 80, 443, 443/udp | proxy | | forgejo | codeberg.org/forgejo/forgejo:10 | 3000 (internal), 2222 (SSH) | proxy | | checkpoint | checkpoint-checkpoint | 8000 (internal) | proxy | ## Networking - External Docker network: `proxy` — all containers that need to be reachable via Caddy must join this network - Caddy is the only container with public-facing ports (80/443) - App containers expose ports internally only (no `host:container` mapping needed) ## Caddy routing (~/stacks/caddy/Caddyfile) ``` git.bujour.de { reverse_proxy forgejo:3000 } cpe.bujour.info { reverse_proxy checkpoint:8000 } ``` ## Adding a new app — the pattern 1. Create `~/apps//` for app source/config 2. Create `~/stacks//compose.yaml` — join network `proxy`, expose port internally only 3. Add a block to `~/stacks/caddy/Caddyfile`: `subdomain.domain.tld { reverse_proxy : }` 4. Reload Caddy: `docker exec caddy caddy reload --config /etc/caddy/Caddyfile` 5. Restart new stack: `cd ~/stacks/ && docker compose up -d` ## Caddy compose (~/stacks/caddy/compose.yaml) ```yaml services: caddy: image: caddy:2 container_name: caddy restart: unless-stopped ports: - "80:80" - "443:443" - "443:443/udp" volumes: - ./Caddyfile:/etc/caddy/Caddyfile:ro - caddy_data:/data - caddy_config:/config networks: - proxy volumes: caddy_data: caddy_config: networks: proxy: external: true ``` ## Shell & scripting rules - Interactive: Bash + fzf (fuzzy history: Ctrl+R, files: Ctrl+T, dirs: Alt+C) - All scripts touching the VPS: Bash/POSIX only — never Fish - `sudo` required for most system commands (patsy is not root) - `ufw` requires `sudo` — `/usr/sbin/` not in default PATH by design ## Firewall - UFW active on VPS - IONOS hardware firewall also configured (both must be opened for new ports) - IONOS web console = emergency recovery path — always keep it accessible ## Hard rules (learned the hard way) - Always seed the DB and create the first admin user before considering a deploy done - Always verify SSH key access before applying any hardening - New ports need opening in **both** UFW and the IONOS firewall panel