---
name: migrate-neon-to-homelab
classification: infra
success_metric: |
  Deck Hearth runs on CT 102 Postgres + MinIO + Redis, app on Dokploy (CT 112);
  Neon and Vercel hosting decommissioned; Vercel AI Gateway retained.
status: shipped
created: 2026-08-15
depends_on: []
skip:
  - ia
  - ui-design
  - ux
  - visual
  - a11y
  - design
  - flag
---

# Convoy: migrate-neon-to-homelab

Move Deck Hearth from Neon (free tier full ~490 MB) to **CT 102 Postgres**
on the axiom homelab. Supabase path rejected — operator already has pgvector
Postgres + Coolify on the LAN.

## Why homelab fits

| Asset | CT 102 |
| --- | --- |
| Postgres | `pgvector/pgvector:pg17`, port 5432 |
| Disk | `/apps` ZFS mirror — not capped at 512 MB |
| CI | `deckhearth_ci` already used by migrate job |
| App host | Dokploy on CT 112 (Traefik on CT 100) |

## Blocker: Vercel ↔ private IP

Vercel cannot connect to `192.168.68.102`. Production cutover requires
**Dokploy deploy** (`deckhearth.stillwell.cloud`) on CT 112.

## Phases

| Phase | Work | Owner | Status |
| --- | --- | --- | --- |
| 1 | `axiom-server`: `ct102/init/03-deckhearth.sql` | operator | ready |
| 2 | `lib/sql.js` + import swap off `@vercel/postgres` | code | done |
| 3 | `docs/HOMELAB_DATABASE.md` runbook | code | done |
| 4 | Operator: provision DB, `npm run migrate up` | operator | done |
| 5 | MinIO + Redis wiring, Dockerfile, Dokploy docs | code | done |
| 6 | `npm run migrate-neon-to-homelab` data copy | operator | pending |
| 7 | Dokploy app + Traefik route | operator | in_progress |
| 8 | Decommission Neon + Vercel | operator | pending |

## Env contract

```bash
POSTGRES_URL=postgresql://deckhearth:…@192.168.68.102:5432/deckhearth
POSTGRES_URL_DIRECT=…   # same on homelab
NEON_DATABASE_URL=…     # one-time source only
```

## axiom-server changes

- `proxmox/ct102/init/03-deckhearth.sql` — `deckhearth` + pgvector + `deckhearth_ci`
- `.cursor/rules/ct102-databases.mdc` — table row

## tcg-vault changes

- `lib/sql.js`, `scripts/migrate-neon-to-homelab.js`
- `docs/HOMELAB_DATABASE.md`

## Risks

| Risk | Mitigation |
| --- | --- |
| LAN-only DB | ✅ RESOLVED — app now on Dokploy CT 112, DB on CT 102 same LAN |
| `deckhearth_ci` password drift | Match `HOMELAB_CI_POSTGRES_PASSWORD` GitHub secret |
| Init SQL on live CT 102 | Manual `docker exec psql` apply, not initdb.d replay |

## As-shipped

**Phase 1–5 (code):** Shipped. CT 102 Postgres + pgvector provisioned; `lib/sql.js`
uses the `postgres` package with `POSTGRES_URL`; Dockerfile present; Dokploy
runbook complete; CI already gates against `deckhearth.stillwell.cloud`.

**Phase 6 (data copy):** Pending operator action. Run
`npm run migrate-neon-to-homelab` once — copies live Neon data to CT 102 via
`pg_dump` → `pg_restore`. Script is production-ready; see `scripts/migrate-neon-to-homelab.js`.
If starting fresh with no Neon data, skip this step.

**Phase 7 (Dokploy app):** Live. Smoke + visual CI workflows hit
`https://deckhearth.stillwell.cloud` and return 200. Dokploy app + Traefik
route confirmed operational.

**Phase 8 (decommission):** Pending operator action. Steps in
`docs/DOKPLOY_DEPLOY.md` § 6. Data copy (phase 6) should run before the
Vercel project is deleted. `vercel.json` and `.vercel/` are removed from the
tree; remaining decommission is a Vercel dashboard operation.
