Initial VPS operations handbook
This commit is contained in:
@@ -0,0 +1,138 @@
|
||||
# hk2.chans.xyz
|
||||
|
||||
| Item | Value |
|
||||
|------|--------|
|
||||
| Role | PowerDNS Authoritative (ns1) + Poweradmin / Traefik |
|
||||
| SSH | `ssh -4 windy@hk2.chans.xyz` (prefer IPv4 from WSL) |
|
||||
| IPv4 | `154.36.174.161` |
|
||||
| Hostname (OS) | `C20210714091792` (Debian 13 trixie) |
|
||||
| Also DNS | `ns1.wsvc.info` → this host |
|
||||
| Secondary NS | `ns2.wsvc.info` → `202.91.35.141` (`us1.wsvc.info`, PowerDNS Auth **5.0.5**) |
|
||||
| Install path | `/opt/pdns` |
|
||||
| Compose | `/opt/pdns/compose.yml` |
|
||||
| Env file | `/opt/pdns/.env` (secrets — **never commit**) |
|
||||
| TZ | `Asia/Shanghai` |
|
||||
|
||||
Upstream docs / config checklist: [docs/pdns-upstream.md](../docs/pdns-upstream.md)
|
||||
|
||||
## PowerDNS Authoritative
|
||||
|
||||
**Status: operational** (auth + db healthy, API **5.0.6**, DNS on :53 — last config audit 2026-08-01 21:40 CST).
|
||||
|
||||
| Item | Value |
|
||||
|------|--------|
|
||||
| Image | `powerdns/pdns-auth-50:5.0.6` (pinned) |
|
||||
| Container | `pdns-auth` |
|
||||
| Config | `/opt/pdns/auth/pdns.conf` + `auth/templates.d/secrets.j2` → `pdns.d/secrets.conf` |
|
||||
| Backend | PostgreSQL 16 (`launch=gpgsql`, DB name `pdns`) |
|
||||
| DNS ports | host `53/tcp` + `53/udp` |
|
||||
| API | host `127.0.0.1:8081` only; `webserver-allow-from` private ranges |
|
||||
| Mode | `primary=yes`; `also-notify` / AXFR ACL → `202.91.35.141`; `only-notify=` empty |
|
||||
| DNSSEC | `gpgsql-dnssec=yes`; CSK ECDSAP256SHA256 |
|
||||
| SOA-EDIT | `default-soa-edit(-signed)=INCEPTION-INCREMENT` |
|
||||
| Poweradmin | `PDNS_VERSION=50`; Traefik Host(`pdns.wsvc.info`) |
|
||||
| Advisories | 2026-06 + 2026-07 cleared by 5.0.6 |
|
||||
|
||||
### Zones (authoritative)
|
||||
|
||||
| Zone | Kind | DNSSEC | Zone metadata |
|
||||
|------|------|--------|---------------|
|
||||
| `windy.me` | Master | yes | `ALLOW-AXFR-FROM` + `TSIG-ALLOW-AXFR=mykey.` |
|
||||
| `wsvc.info` | Master | yes | same |
|
||||
| `chans.xyz` | Master | yes | same |
|
||||
|
||||
NS set: `ns1.wsvc.info` / `ns2.wsvc.info`. Dig SOA may differ from backend serial (SOA-EDIT) — expected.
|
||||
|
||||
### Intended auth config (sanitized)
|
||||
|
||||
See full shape in [docs/pdns-upstream.md](../docs/pdns-upstream.md). Live secrets only in `.env` / generated drop-in on the server.
|
||||
|
||||
### Stack
|
||||
|
||||
| Container | Service | Status | Image |
|
||||
|-----------|---------|--------|-------|
|
||||
| `pdns-auth` | auth | Up (healthy) | `powerdns/pdns-auth-50:5.0.6` |
|
||||
| `pdns-db` | db | Up (healthy) | `postgres:16` |
|
||||
| `poweradmin` | poweradmin | Up (healthy) | `poweradmin/poweradmin:stable` |
|
||||
| `pdns_pgweb` | pgweb | Up | `sosedoff/pgweb:0.16.2` |
|
||||
| `pdns-backup` | backup | Up | `postgres:16` (scheduler) |
|
||||
| `powerdns-admin` | *(orphan)* | Exited | legacy PDA UI — not in active compose |
|
||||
|
||||
### Network model
|
||||
|
||||
| Network | Purpose |
|
||||
|---------|---------|
|
||||
| `backend` | Internal (`internal: true`) — DB + service API |
|
||||
| `edge` | Host-published DNS `:53` and loopback API `:8081` |
|
||||
| `frontend` (`traefik`) | External Traefik network for web UIs |
|
||||
|
||||
### Public / management URLs
|
||||
|
||||
| URL | Backend | Notes |
|
||||
|-----|---------|-------|
|
||||
| https://pdns.wsvc.info | `poweradmin` via Traefik | DNS admin UI |
|
||||
| https://pgweb.wsvc.info | `pgweb` via Traefik | DB browser (HTTP basic) |
|
||||
| DNS `ns1.wsvc.info:53` | `pdns-auth` | Public authoritative |
|
||||
|
||||
## Config audit (vs official docs)
|
||||
|
||||
**Functional primary/secondary config: OK** after 5.0.6 upgrade + `only-notify=` / `domains.master` cleanup.
|
||||
|
||||
| Item | Verdict |
|
||||
|------|---------|
|
||||
| `primary` + MASTER zones + gpgsql DNSSEC | OK |
|
||||
| `also-notify` + empty `only-notify` | OK (docs-recommended pattern) |
|
||||
| API bind / host port publish | OK |
|
||||
| SOA-EDIT with PowerDNS secondary | OK |
|
||||
| API key / DB password hygiene | **Deferred** — weak, plaintext, reused |
|
||||
| TSIG `mykey` | **Deferred** — any holder can AXFR (bypasses IP ACL) |
|
||||
|
||||
## Backup
|
||||
|
||||
| Item | Value |
|
||||
|------|--------|
|
||||
| Service | `pdns-backup` |
|
||||
| Schedule | `CRON_SCHEDULE=0 3 * * *` (03:00 Asia/Shanghai) |
|
||||
| Retention | `RETENTION_DAYS=7`, `MAX_BACKUPS=3` |
|
||||
| Output | `/opt/pdns/backup/pdns_*.sql.gz` (+ optional `roles_*.sql`) |
|
||||
| Scheduler | `scripts/backup-scheduler.sh` (daily `M H * * *` only) |
|
||||
| Manual | `cd /opt/pdns && ./scripts/manual-backup.sh` |
|
||||
|
||||
**Quirk:** `backend` is internal — backup must not use Alpine + runtime `apk`/`crond`. Uses `postgres:16` + `backup-scheduler.sh` (fixed 2026-08-01).
|
||||
|
||||
## Other software on this host (stubs)
|
||||
|
||||
`/opt/traefik`, `/opt/adguard`, `/opt/remark42`, `/opt/rustdesk`, `/opt/nginx-manager`, …
|
||||
|
||||
## Ops / runbooks
|
||||
|
||||
- [pdns-health](../runbooks/pdns-health.md)
|
||||
- [pdns-upstream docs](../docs/pdns-upstream.md)
|
||||
|
||||
```bash
|
||||
ssh -4 windy@hk2.chans.xyz
|
||||
cd /opt/pdns && docker compose ps -a
|
||||
docker compose logs -f auth poweradmin backup
|
||||
./scripts/manual-backup.sh
|
||||
# after pdns.conf / secrets.j2 / auth env changes:
|
||||
docker compose up -d --force-recreate auth
|
||||
```
|
||||
|
||||
```bash
|
||||
dig @154.36.174.161 NS windy.me +short
|
||||
dig @154.36.174.161 SOA wsvc.info +short
|
||||
# compare secondary:
|
||||
dig @202.91.35.141 SOA wsvc.info +short
|
||||
```
|
||||
|
||||
On-server docs: `/opt/pdns/README.md`, `CHANGELOG.md`.
|
||||
|
||||
## Verified
|
||||
|
||||
Last checked: **2026-08-01 21:40 CST** — operational; docs audit recorded.
|
||||
|
||||
- Auth **5.0.6** healthy; no Mandatory security banner
|
||||
- Zones Master + DNSSEC; SOA in sync with ns2 (served serial)
|
||||
- `only-notify=` + `also-notify=202.91.35.141`; MASTER `domains.master` cleared
|
||||
- https://pdns.wsvc.info → **302**; https://pgweb.wsvc.info → **401**
|
||||
- Hardening backlog: API/DB credential rotation + TSIG rotate (see upstream doc)
|
||||
@@ -0,0 +1,54 @@
|
||||
# mx2.windy.me
|
||||
|
||||
| Item | Value |
|
||||
|------|--------|
|
||||
| Role | Mail server (mailcow) |
|
||||
| SSH | `ssh -4 windy@mx2.windy.me` (pubkey; root denied; prefer IPv4 from WSL) |
|
||||
| Install path | `/opt/mail` |
|
||||
| Compose project | `cow` (`COMPOSE_PROJECT_NAME=cow`) |
|
||||
| Compose file | `/opt/mail/docker-compose.yml` |
|
||||
| Config | `/opt/mail/mailcow.conf` (`.env` → symlink) |
|
||||
| Hostname | `MAILCOW_HOSTNAME=mx2.windy.me` |
|
||||
| TZ | `Asia/Shanghai` |
|
||||
| IPv4 | `194.163.160.244` |
|
||||
| IPv6 | `2a02:c207:2284:8258::1` |
|
||||
| Docker network | `IPV4_NETWORK=172.22.1` |
|
||||
| UI | https://mx2.windy.me |
|
||||
|
||||
## Ports (host-published)
|
||||
|
||||
`25`, `465`, `587` (SMTP), `110`/`995` (POP), `143`/`993` (IMAP), `4190` (Sieve), `80`/`443` (UI)
|
||||
|
||||
## Domain & mailboxes
|
||||
|
||||
- Domain: `windy.me` (primary on this host)
|
||||
- MX for `windy.me`: `mx2.windy.me` (prio 20), `mx.windy.me` (prio 30)
|
||||
- Mailboxes (examples): `zhiqiang@windy.me`, `claw@windy.me`, `hermes@windy.me`
|
||||
- ACME / Let’s Encrypt enabled (`SKIP_LETS_ENCRYPT=n`); cert SAN includes `mx2`, `autoconfig`, `autodiscover`
|
||||
- PTR (v4/v6) → `mx2.windy.me`
|
||||
|
||||
## Ops notes
|
||||
|
||||
- Manage with: `cd /opt/mail && docker compose …`
|
||||
- `SKIP_CLAMD=y` may still leave `clamd` running after updates; recreate if you need it truly skipped
|
||||
- Host has ~8G RAM, no swap; watch memory under load
|
||||
- Unrelated stopped containers may exist (`zc-*` monitoring stack) — not part of mailcow
|
||||
|
||||
## Client access (SMTP / IMAP)
|
||||
|
||||
See [mailcow-smtp-client](../runbooks/mailcow-smtp-client.md).
|
||||
|
||||
- Send: `mx2.windy.me:587` STARTTLS (or `:465` SSL), auth = full email + mailbox password
|
||||
- IMAP: `mx2.windy.me:993` · POP: `:995`
|
||||
- Webmail: https://mx2.windy.me
|
||||
|
||||
## Runbooks
|
||||
|
||||
- [mailcow-health](../runbooks/mailcow-health.md)
|
||||
- [mailcow-update](../runbooks/mailcow-update.md)
|
||||
- [mailcow-smtp-client](../runbooks/mailcow-smtp-client.md)
|
||||
- Script: [`../scripts/check-mx2.sh`](../scripts/check-mx2.sh)
|
||||
|
||||
## Verified
|
||||
|
||||
Last checked: 2026-08-01 — `./scripts/check-mx2.sh` exit 0 (compose up, watchdog 100%, empty queue, TLS LE OK, SMTP Postcow, A/AAAA/PTR/MX/SPF OK).
|
||||
@@ -0,0 +1,100 @@
|
||||
# synapse.chans.xyz — Matrix homeserver (ESS)
|
||||
|
||||
## Role
|
||||
|
||||
Matrix homeserver running **Element Server Suite (ESS) Community**: Synapse + Matrix Authentication Service (MAS) + Element Web + Element Admin.
|
||||
|
||||
## SSH
|
||||
|
||||
```
|
||||
ssh -4 windy@synapse.chans.xyz
|
||||
```
|
||||
|
||||
- User: `windy` (sudo via Ansible)
|
||||
- IPv4: `169.58.86.13`
|
||||
- OS: Ubuntu 26.04
|
||||
|
||||
## Stack
|
||||
|
||||
| Layer | Detail |
|
||||
|-------|--------|
|
||||
| Runtime | K3s v1.36.2+k3s1 (single-node) |
|
||||
| Ingress | K3s built-in Traefik (hostPort `:80` / `:443`) |
|
||||
| TLS | cert-manager v1.19.3 + `letsencrypt-prod` ClusterIssuer (HTTP-01) |
|
||||
| DB | ESS embedded PostgreSQL 17 (PVC 20Gi, local-path) |
|
||||
| Cache | ESS embedded Redis (PVC 2Gi) |
|
||||
| Chart | `oci://ghcr.io/element-hq/ess-helm/matrix-stack`, version `26.7.2` |
|
||||
|
||||
### Matrix service endpoints
|
||||
|
||||
| URL | Component |
|
||||
|-----|-----------|
|
||||
| https://chat.chans.xyz | Element Web |
|
||||
| https://synapse.chans.xyz | Synapse Client-Server / Federation API |
|
||||
| https://account.chans.xyz | Matrix Authentication Service |
|
||||
| https://admin.chans.xyz | Element Admin console |
|
||||
| https://chans.xyz/.well-known/matrix/server | Federation discovery → `synapse.chans.xyz:443` |
|
||||
| https://chans.xyz/.well-known/matrix/client | Client discovery → `https://synapse.chans.xyz` |
|
||||
| `mrtc.chans.xyz` | Reserved for MatrixRTC (not deployed) |
|
||||
|
||||
## Public ports
|
||||
|
||||
| Port | Protocol | Service |
|
||||
|------|----------|---------|
|
||||
| 22/tcp | SSH | OpenSSH |
|
||||
| 80/tcp | HTTP | Let's Encrypt HTTP-01 / Traefik redirect |
|
||||
| 443/tcp | HTTPS | Matrix Client-Server, Federation, Element, MAS, Admin |
|
||||
|
||||
All other ports internal only (no K3s API, no database, no Redis exposed).
|
||||
|
||||
## Namespace layout
|
||||
|
||||
- `ess` — all ESS workloads (Synapse, MAS, Element, Postgres, Redis, HAProxy)
|
||||
- `matrix-system` — cluster base resources (ResourceQuota, LimitRange, mrtc-placeholder)
|
||||
- `cert-manager` — cert-manager
|
||||
|
||||
## Local backup
|
||||
|
||||
| Item | Detail |
|
||||
|------|--------|
|
||||
| Path | `/var/backups/matrix` (root:root, 0700) |
|
||||
| Schedule | **Paused** — `matrix-backup.timer` is disabled pending a reviewed backup design |
|
||||
| Retention | 7 days |
|
||||
| Disk warning | 80% (healthcheck), 90% (backup stops) |
|
||||
| Content | Planned: PostgreSQL `synapse` + `mas` logical dumps, media store archive, `/etc/matrix-bootstrap` |
|
||||
| Status | **Not operational** — no current Matrix backup or recovery tier |
|
||||
|
||||
## Health checks
|
||||
|
||||
Daily via `matrix-k3s-healthcheck.timer`, outputs to `/var/lib/vps-health/latest.json`. Checks:
|
||||
|
||||
- K3s API / namespace health
|
||||
- ESS pod restart counts
|
||||
- Backup freshness
|
||||
- Root disk usage
|
||||
- Certificate expiry
|
||||
- External HTTPS endpoints
|
||||
|
||||
## Bootstrap secrets
|
||||
|
||||
Path: `/etc/matrix-bootstrap/` (root:root, 0700) — individual files per secret key (0600). SMTP password, DB passwords, signing key etc. Included in local backup.
|
||||
|
||||
## Identity
|
||||
|
||||
- **Server name**: `chans.xyz` (user IDs: `@user:chans.xyz`)
|
||||
- **Deployment type**: New instance (no recovery from previous homeserver)
|
||||
- **Public registration**: Disabled
|
||||
- **Authentication**: MAS local passwords
|
||||
|
||||
## Ansible
|
||||
|
||||
Inventory group: `matrix_production` (but NOT in `managed` which expects Docker Compose).
|
||||
|
||||
Playbooks in `ansible/playbooks/matrix-*.yml`.
|
||||
|
||||
## Known limitations
|
||||
|
||||
- MatrixRTC / Element Call / LiveKit / Coturn not deployed (`mrtc.chans.xyz` reserved only)
|
||||
- SMTP email not yet configured (requires manual bootstrap + Helm upgrade)
|
||||
- No off-site Restic backup
|
||||
- Single-node K3s (no HA for control plane)
|
||||
@@ -0,0 +1,91 @@
|
||||
# us2.wsvc.info
|
||||
|
||||
| Item | Value |
|
||||
|------|--------|
|
||||
| Role | Multi-service VPS (Vaultwarden, Traefik, Soft Serve, …) |
|
||||
| SSH | `ssh -4 windy@us2.wsvc.info` (prefer IPv4 from WSL) |
|
||||
| IPv4 | `193.9.44.165` |
|
||||
| Also DNS | `auth.wsvc.info` → this host; `repo.windy.me` → this host (Soft Serve) |
|
||||
| Public HTTPS | Traefik on `:80` / `:443` (`/opt/traefik`) |
|
||||
|
||||
## Vaultwarden (Bitwarden-compatible)
|
||||
|
||||
**Status: operational** (Postgres live, HTTPS 200, healthy containers, SMTP AUTH OK — last probe 2026-08-01 18:55 CST).
|
||||
|
||||
Upstream docs: [docs/vaultwarden-upstream.md](../docs/vaultwarden-upstream.md)
|
||||
|
||||
| Item | Value |
|
||||
|------|--------|
|
||||
| Install path | `/opt/vaultwarden` |
|
||||
| Compose | `/opt/vaultwarden/docker-compose.yml` |
|
||||
| Env file | `/opt/vaultwarden/.env` |
|
||||
| Admin overrides | `/opt/vaultwarden/vw-data/config.json` (**wins over env**) |
|
||||
| Public URL / `DOMAIN` | `https://auth.wsvc.info` |
|
||||
| Image | `vaultwarden/server:1.37.1` (pinned) |
|
||||
| Live DB | **Postgres 16** (`vw-db` / service `pg`) via compose `DATABASE_URL` |
|
||||
| Data (probe) | users=1, ciphers=1327 |
|
||||
| Cold SQLite | `backups/sqlite-cold/db.sqlite3.pre-pg-20260801` (not used live) |
|
||||
| Pre-migrate backup | `backups/pre-pg-migrate-20260801_161204/` |
|
||||
| Data dir | `./vw-data` → `/data` (attachments, rsa keys, `config.json`) |
|
||||
| DB backups | `vaultwarden-backup` → `pg_dump` daily 02:00 (`scripts/backup.sh`); latest e.g. `backups/vaultwarden_2026-08-01_16-23-03.sql.gz` |
|
||||
| Reverse proxy | Traefik (`vw-net`) → Host(`auth.wsvc.info`), LE (`auth.wsvc.info`, valid → 2026-10-08) |
|
||||
| SMTP | `mx2.windy.me:587` STARTTLS (`extra_hosts` → 194.163.160.244); from `zhiqiang@windy.me` |
|
||||
| SMTP secrets | Keep **`.env`**, **`vw-data/config.json`**, and **`.smtp-credentials`** in sync — `config.json` wins; drift breaks mail |
|
||||
| IP header | `X-Forwarded-For` |
|
||||
| Signups | disabled (`signups_allowed=false`); invitations allowed |
|
||||
| pgweb | compose profile `debug` (stopped by default) |
|
||||
| Secrets | `.env`, `.admin-token`, `.smtp-credentials` — **never commit** |
|
||||
|
||||
### Official documentation
|
||||
|
||||
| Topic | Link |
|
||||
|-------|------|
|
||||
| Config overview | https://github.com/dani-garcia/vaultwarden/wiki/Configuration-overview |
|
||||
| Postgres / migration | https://github.com/dani-garcia/vaultwarden/wiki/Using-the-PostgreSQL-Backend |
|
||||
| SMTP | https://github.com/dani-garcia/vaultwarden/wiki/SMTP-configuration |
|
||||
| Proxy / Traefik | https://github.com/dani-garcia/vaultwarden/wiki/Proxy-examples |
|
||||
| Full index | [docs/vaultwarden-upstream.md](../docs/vaultwarden-upstream.md) |
|
||||
|
||||
### Stack
|
||||
|
||||
| Container | Status |
|
||||
|-----------|--------|
|
||||
| `vaultwarden` | Up (healthy), `vaultwarden/server:1.37.1` |
|
||||
| `vw-db` | Up (healthy) — **live** Postgres |
|
||||
| `vaultwarden-backup` | Up (`pg_dump`) |
|
||||
| `vaultwarden-pgweb` | Exited (profile `debug`) |
|
||||
|
||||
Effective `config.json`: domain `https://auth.wsvc.info`, SMTP 587/starttls to `mx2.windy.me` as `zhiqiang@windy.me`, `ip_header=X-Forwarded-For`. Password matched to `.env` (fixed 2026-08-01).
|
||||
|
||||
### Client use
|
||||
|
||||
- Server URL: `https://auth.wsvc.info`
|
||||
- Admin: `https://auth.wsvc.info/admin`
|
||||
|
||||
### Ops / runbooks
|
||||
|
||||
- [vaultwarden-health](../runbooks/vaultwarden-health.md)
|
||||
- [vaultwarden-sqlite-to-postgres](../runbooks/vaultwarden-sqlite-to-postgres.md)
|
||||
- [vaultwarden-upstream docs](../docs/vaultwarden-upstream.md)
|
||||
|
||||
```bash
|
||||
ssh -4 windy@us2.wsvc.info
|
||||
cd /opt/vaultwarden && docker compose ps -a
|
||||
# after recreating vaultwarden, if auth 404s:
|
||||
docker restart traefik
|
||||
```
|
||||
|
||||
## Other software on this host (stubs)
|
||||
|
||||
`/opt/traefik`, `soft-serve`, `authelia`, `dendrite`/`conduit`, `mastodon`, `rustdesk`, `zitadel`, etc.
|
||||
|
||||
## Verified
|
||||
|
||||
Last checked: **2026-08-01 18:55 CST** — operational.
|
||||
|
||||
- `vaultwarden` + `vw-db` healthy; `DATABASE_URL` → `pg:5432/vaultwarden`
|
||||
- `https://auth.wsvc.info/` **200**, `/admin` **200**, `/api/config` OK (`disableUserRegistration: true`)
|
||||
- Identity wrong-password → **400** business error (DB readable, not 500)
|
||||
- SMTP: container → `mx2:587` OK; STARTTLS cert CN=`mx2.windy.me`; **AUTH OK** with effective `config.json` password (synced with `.env` / `.smtp-credentials`)
|
||||
- LE cert CN=`auth.wsvc.info`
|
||||
- PG counts: users=1, ciphers=1327
|
||||
Reference in New Issue
Block a user