Migrate VPS off IONOS (Hetzner plan)¶
Status: future plan — not started. Keep this page as the source of truth when revisiting cost / hosting.
Audience: humans + agents planning cutover.
Related: Products, domains & legal, Architecture overview, DNS, Woodpecker agents, Portfolio.
No execution yet
Do not provision, flip DNS, or change CI until Phase 0 inventory is done and a cutover window is agreed.
Goal¶
Keep the same platform (Forgejo, Woodpecker, Traefik, docs, Kuma, Netdata, DefectDojo, WearLab, …), spend less than IONOS after promo renewals, and scale later without jumping to AWS/GCP.
Locked decisions¶
| Topic | Choice |
|---|---|
| Target provider | Hetzner Cloud (EU, predictable monthly pricing) |
| Topology | Keep dual VPS — infra + apps (do not merge onto one box) |
| Portfolio | Leave IONOS web hosting — static origin on infra VPS + Cloudflare edge |
| DNS | Move zone to Cloudflare (free); domain registration may stay at IONOS short-term |
| Big cloud (AWS/GCP/Azure) | Out of scope |
Suggested starting sizes¶
Tune after measuring current IONOS RAM/CPU (Netdata / free -h):
| Role | Rough class | Why |
|---|---|---|
| Infra | Mid (≈4–8 vCPU, 8–16 GB) | Forgejo + Woodpecker server + Traefik + docs + Kuma + Netdata parent + DefectDojo |
| Apps | Smaller/mid (≈3–4 vCPU, 4–8 GB) | WearLab + Traefik + room for 1–2 more apps |
| Backups | Hetzner Storage Box (or B2) | Off-site; today backups are mostly local-only on each VPS |
Current map (today)¶
| Piece | Where | Notes |
|---|---|---|
| Infra VPS | IONOS 85.215.32.166 |
Platform subdomains |
| Apps VPS | IONOS 82.165.177.110 |
wearlab, future apps |
| Portfolio | IONOS web hosting | Woodpecker SFTP to public_html |
| DNS | IONOS | All A records — see DNS |
Woodpecker apps agent reaches infra gRPC via SSH tunnel (IONOS only allows 22/80/443 between VPS). Plan the same pattern on Hetzner unless ports are deliberately opened.
flowchart TB
dns[DNS today IONOS]
ionosWeb[IONOS web hosting portfolio]
infra[Infra VPS 85.215.32.166]
apps[Apps VPS 82.165.177.110]
dns --> infra
dns --> apps
dns --> ionosWeb
ci[Woodpecker] --> infra
ci -->|SFTP| ionosWeb
ci -->|SSH agent| apps
Target map (after cutover)¶
flowchart TB
cf[Cloudflare DNS free]
infraH[Hetzner infra VPS]
appsH[Hetzner apps VPS]
cf --> infraH
cf --> appsH
portfolio[Portfolio static on infra]
infraH --> portfolio
ci2[Woodpecker] --> infraH
ci2 -->|SSH deploy| appsH
ci2 -->|rsync or SSH to infra| portfolio
Portfolio: leave IONOS hosting?¶
Yes (recommended).
| Option | Pros | Cons |
|---|---|---|
| Keep on IONOS hosting | Familiar; isolated from VPS moves | Extra bill; weaker TLS; second deploy path |
| Move to infra + Cloudflare | Drop a line item; one deploy style; better edge TLS/HTTP3 | Small static load on infra |
Domain registration alone is cheap; the expensive parts are usually VPS renewals and web hosting.
Phases¶
Phase 0 — Inventory (zero downtime)¶
- [ ] List IONOS invoice lines (2× VPS, web hosting, domain, add-ons)
- [ ] Per VPS: disk use, RAM peaks, Docker stacks, Forgejo/volume sizes
- [ ] Locate SOPS age key, Woodpecker secrets, SSH keys, Traefik ACME data
- [ ] List every DNS name to move (
git,ci,docs,wearlab, apex,www, …) - [ ] Grep hardcoded IPs (
85.215.32.166,82.165.177.110) in secrets, Kuma, scripts, local SSH - [ ] Freeze: no new permanent DNS during migration week
- [ ] Agree cutover window; open Plane ticket when Plane is healthy
Phase 1 — Provision Hetzner (old stack stays live)¶
- [ ] Create infra + apps servers; firewall 22/80/443
- [ ]
deployuser + SSH keys (reuse existing bootstrap patterns) - [ ] Optional: floating IPs
- [ ] Storage Box; test write/read backup
- [ ] Do not point public DNS yet
Phase 2 — Infra “dark” (private verify)¶
- [ ] Bootstrap
/opt/devopson new infra - [ ] Restore Forgejo + Woodpecker data/config
- [ ] Bring up Traefik, docs, Kuma, Netdata parent, DefectDojo
- [ ] Keep Plane off until stable (
ENABLE_PLANE_STACK=false) - [ ] Private checks: clone from new Forgejo; test pipeline
- [ ] Public
git/cistill on old IONOS
Phase 3 — Apps “dark”¶
- [ ] Bootstrap Traefik +
/opt/appson new apps VPS - [ ] Deploy WearLab (existing secrets /
run-deploy.sh) - [ ] Rebuild Woodpecker apps-agent tunnel to new infra
- [ ] Private Host-header verify for
wearlab - [ ] Temporary Kuma monitor; do not flip public DNS yet
Phase 4 — Portfolio off IONOS web hosting¶
- [ ] Static origin on infra (Traefik/nginx vhost for apex/
www) - [ ] Change portfolio CI: SFTP → SSH/rsync to infra (same idea as docs deploy)
- [ ] Cloudflare: orange-cloud apex +
www; SSL Full / Full (strict) when origin ready - [ ] Point apex/
wwwaway from IONOS web hosting - [ ] After 1–2 stable weeks: cancel IONOS web hosting
Phase 5 — Public DNS cutover¶
Prep: Cloudflare zone, TTL 60–300s, full record checklist from DNS.
Suggested order:
- App hosts (
wearlab, …) — easy rollback - Low-risk platform (
docs,status,metrics,security) git+ci— short pipeline freeze, then flip
Also:
- [ ] Update Woodpecker
deploy_ssh_host, apps SSH secrets, Kuma, Netdata tunnel, local SSH aliases - [ ] Keep old IONOS VPS powered ~7 days for rollback
- [ ] Update docs (overview, DNS, products-and-brands, onboarding) with new IPs
Phase 6 — Decommission IONOS compute¶
- [ ] Final Forgejo + volume backup to Storage Box; test restore once
- [ ] Power off old VPS → cancel
- [ ] Confirm web hosting already cancelled
- [ ] Optional later: transfer domain to Cloudflare Registrar
Scaling after the move¶
Stay on Hetzner VPS; grow in layers — do not rewrite onto big cloud for this workload.
| If this hurts | Then do this |
|---|---|
| Infra RAM / CI slow | Vertical resize infra |
| WearLab / app CPU | Resize apps, then split a heavy app to its own CX |
| Many small apps | Keep multi-app on apps VPS + Traefik (current model) |
| CI queue backlog | Extra small agent box, or more agent capacity |
| Static sites | Cloudflare cache in front of infra (portfolio + docs) |
| Disk / disaster fear | Daily Storage Box + restic; quarterly restore test |
| Multi-region / compliance | Only then managed DB or second region — not now |
Anti-patterns: one mega shared VPS “to save money”; Kubernetes; full AWS migration for this stack.
Risks¶
- Secrets/monitors still pointing at old IPs
- Let’s Encrypt rate limits if DNS is flipped repeatedly (Cloudflare proxy helps)
- Woodpecker agent tunnel must be rebuilt to new infra
- Don’t block the whole migration on Plane being up
Before execution — answer these¶
- Rough monthly IONOS total (or product list)
- Confirm Hetzner (vs Contabo/Netcup) — this plan assumes Hetzner
- Preferred cutover window
- Whether apex must be near-zero downtime
Explicitly out of scope (for now)¶
- Implementing CI/template changes in this repo until Phase 4 is scheduled
- Buying extra domains for hosting
- Collapsing infra + apps onto a single VPS