diff --git a/README.md b/README.md index 50ae765..20b5044 100644 --- a/README.md +++ b/README.md @@ -4,38 +4,25 @@ **[codeberg.org/opennomad/linderhof](https://codeberg.org/opennomad/linderhof)** -a self-hosting stack based on ansible and docker compose that comes with +a self-hosting stack based on ansible and docker compose that comes with email, web server, git hosting, matrix, monitoring, web analytics, calendar & contacts, backups, overlay networking, and intrusion prevention — no databases, no external services. -- email - - [docker-mailserver](https://github.com/docker-mailserver/docker-mailserver) - - [rainloop](https://www.rainloop.net/) -- web server - - [caddy](https://caddyserver.com/) -- git server - - [forgejo](https://forgejo.org/) -- matrix homeserver - - [tuwunel](https://github.com/matrix-construct/tuwunel) -- monitoring - - [alloy](https://github.com/grafana/alloy) - - [grafana](https://grafana.com/) - - [prometheus](https://prometheus.io/) - - [loki](https://github.com/grafana/loki) -- web analytics - - [goaccess](https://goaccess.io/) -- calendar & contacts - - [radicale](https://radicale.org/) -- backups - - [restic](https://github.com/restic/restic) -- overlay network - - [nebula](https://github.com/slackhq/nebula) -- docker image update notifications - - [diun](https://github.com/crazy-max/diun) -- intrusion prevention - - [fail2ban](https://github.com/fail2ban/fail2ban) +set `enable_: false` in `config.yml` to disable any service — DNS records, Docker networks, and deployment tasks are all skipped automatically. -other features include: -- runs on opensource -- no databases / no external services +| service | toggle | default | powered by | +|---|---|---|---| +| web server | `enable_caddy` | on | [caddy](https://caddyserver.com/) | +| email | `enable_mail` | on | [docker-mailserver](https://github.com/docker-mailserver/docker-mailserver), [rainloop](https://www.rainloop.net/) | +| git hosting | `enable_forgejo` | on | [forgejo](https://forgejo.org/) | +| matrix homeserver | `enable_tuwunel` | on | [tuwunel](https://github.com/matrix-construct/tuwunel) | +| monitoring | `enable_monitoring` | on | [prometheus](https://prometheus.io/), [grafana](https://grafana.com/), [loki](https://github.com/grafana/loki), [alloy](https://github.com/grafana/alloy) | +| web analytics | `enable_goaccess` | on | [goaccess](https://goaccess.io/) | +| calendar & contacts | `enable_radicale` | on | [radicale](https://radicale.org/) | +| backups | `enable_restic` | **off** | [restic](https://github.com/restic/restic) | +| overlay network | `enable_nebula` | on | [nebula](https://github.com/slackhq/nebula) | +| image update alerts | `enable_diun` | on | [diun](https://github.com/crazy-max/diun) | +| intrusion prevention | `enable_fail2ban` | on | [fail2ban](https://github.com/fail2ban/fail2ban) | + +> **restic** is off by default — it requires a [Hetzner Storage Box](https://www.hetzner.com/storage/storage-box/) for its backup target. enable it and configure `restic_repository` in `config.yml` once you have one. ## what you need @@ -78,41 +65,27 @@ ansible-galaxy collection install -r requirements.yml ## deploy -### provision a server (Hetzner) +full deployment order for a fresh server: ```bash -ansible-playbook playbooks/provision.yml +ansible-playbook playbooks/provision.yml # create server, writes IP to stack config +ansible-playbook playbooks/dns.yml # create DNS zones and records +ansible-playbook playbooks/site.yml --tags bootstrap # users, SSH hardening, packages, Docker +ansible-playbook playbooks/site.yml # deploy all services +ansible-playbook playbooks/dkim_sync.yml # generate DKIM keys and publish to DNS ``` -creates the server, registers your SSH key, and writes the IP to your stack config automatically. default type is `cx23` (2 vCPU, 4 GB); override with `-e hcloud_server_type=cx33`. +**provision** creates the server on Hetzner, registers your SSH key, and writes the IP to your stack config automatically. default type is `cx23` (2 vCPU, 4 GB); override with `-e hcloud_server_type=cx33`. -### update DNS +**dns** creates all zones and records conditional on your `enable_*` settings — disabled services get no DNS entries. -```bash -ansible-playbook playbooks/dns.yml -``` +**bootstrap** connects as `root` (the only user on a fresh server), creates your admin user with passwordless sudo, hardens SSH, and installs base packages including Docker. -creates all DNS zones and records for your domain. records are conditional on your `enable_*` settings — disabled services won't get DNS entries. +**site.yml** deploys all enabled services. subsequent runs are idempotent — safe to re-run to apply config changes. -### bootstrap the server +> **note:** on first deployment, the mail role briefly stops Caddy to acquire a Let's Encrypt certificate for the mail hostname via certbot standalone. Caddy is restarted immediately after. this only happens once. -first-time setup of the server (users, SSH hardening, packages, Docker): - -```bash -ansible-playbook playbooks/bootstrap.yml -``` - -this connects as `root` (the only user on a fresh server), creates your admin user with passwordless sudo, sets passwords for `root` and the admin user, hardens SSH, and installs base packages. - -### deploy services - -```bash -ansible-playbook playbooks/site.yml -``` - -deploys all enabled services. subsequent runs are idempotent — safe to re-run to apply config changes. - -> **note:** on first deployment, the mail role briefly stops Caddy to acquire a Let's Encrypt certificate for the mail hostname via certbot standalone. Caddy is restarted immediately after. this only happens once — subsequent runs detect the existing certificate and skip it. +**dkim_sync** generates DKIM keys for all mail domains, writes them to your stack config, and publishes the `mail._domainkey` DNS records. safe to re-run. ## bring your own server @@ -120,7 +93,7 @@ deploys all enabled services. subsequent runs are idempotent — safe to re-run if you already have an Ubuntu server with SSH access: 1. run `./setup.sh` — enter the server's existing hostname and IP when prompted -2. ensure your SSH key is authorized for the admin user and they have passwordless sudo — or run `bootstrap.yml` first if starting from root access +2. ensure your SSH key is authorized for the admin user and they have passwordless sudo — or run `ansible-playbook playbooks/site.yml --tags bootstrap` first if starting from root access 3. skip `provision.yml` and `dns.yml` if you're managing DNS elsewhere 4. run `ansible-playbook playbooks/site.yml` @@ -157,24 +130,6 @@ stack config lives at `$XDG_CONFIG_HOME/linderhof//`: ``` -## service toggles - -set `enable_: false` in `config.yml` to disable a service. DNS records, Docker networks, and deployment tasks for that service will all be skipped automatically. - -| variable | service | -|---|---| -| `enable_mail` | email (docker-mailserver + rainloop) | -| `enable_forgejo` | git hosting | -| `enable_tuwunel` | Matrix homeserver | -| `enable_monitoring` | Prometheus, Grafana, Loki, Alloy | -| `enable_goaccess` | web analytics | -| `enable_goaccess_sync` | rsync analytics reports to a remote host (off by default) | -| `enable_radicale` | CalDAV/CardDAV | -| `enable_restic` | encrypted backups (requires a Hetzner Storage Box — off by default) | -| `enable_nebula` | overlay network | -| `enable_diun` | Docker image update notifications | -| `enable_fail2ban` | intrusion prevention | - ## overriding variables