# docker-mailserver "fake VPS" (one directory, nothing else) A copy-paste setup that gives an operator (or an agent) an **interactive SSH shell that feels like a VPS** — but the only real host data on it is your `docker-mailserver` directory. The rest of the server's filesystem isn't mounted, so there's nothing else to see. Contrast with the sibling [`../docker-mailserver/`](../docker-mailserver/) example: that one is **broker mode** — a fixed *menu* of `setup email …` commands, no shell. This one is the opposite — a **full shell**, scoped to one directory, for when you want to edit configs and drive `docker compose` yourself. ## What you get ``` ssh -p 2222 agent@your-host └─ bastion-vps key auth; forces every session into the jail └─ docker exec -it dms-jail bash └─ dms-jail disposable alpine + docker/compose/editors/git ONLY real mount: your docker-mailserver directory you land in it, read-write ``` Inside the shell you can `vim mailserver.env`, `docker compose up -d`, `docker exec mailserver setup email add …`, `git pull`, etc. ## Setup 1. Paths are preset to `/srv/docker-mailserver` (host and in-jail, identical). If your directory is elsewhere, edit the **two** paths in [`docker-compose.yml`](docker-compose.yml) and keep both sides of the `:` identical — a different in-jail path still lets you read/edit files, but `docker compose` from the jail would resolve the stack's bind mounts to a host path that doesn't exist and bring the mailserver up with empty data. 2. Add your key: ```bash mkdir -p docker-data/bastion/users.d cp ~/.ssh/id_ed25519.pub docker-data/bastion/users.d/me.pub ``` 3. Launch: ```bash docker compose up -d --build ssh -p 2222 agent@your-host ``` ## Security note — the docker-socket tradeoff To let you run `docker compose` / restart the mailserver from the shell, the jail is given the host docker socket. **A docker socket is host-root equivalent** — from inside the shell, `docker run -v /:/host alpine sh` would expose the whole host. So this is a *practical* one-directory VPS for **trusted operators**, not a hard sandbox an adversary can't escape. Want a real "only this directory exists" boundary instead? Remove the `/var/run/docker.sock` mount from **both** services in the compose file. You lose in-shell `docker compose`/restart (manage the stack from a separate broker-mode bastion — see [`../docker-mailserver/`](../docker-mailserver/)), but the shell then genuinely cannot reach anything but the mounted directory. The auth boundary is your SSH key (`users.d/*.pub`) plus the bastion's `ForceCommand`, which clients cannot bypass — a session can only ever become the jail shell, never anything else.