GREEN · RED · BLUE PACKAGE SKILL · VULTR · NEON

Neon Package Skill

A reproducible Green, Red, and Blue workflow for self-hosted Neon — serverless Postgres with storage and compute separated, its layers and WAL durably in Cloudflare R2 — on one guarded, loopback-only Vultr instance.

The R2 prefix is the database. The pageserver uploads layers and the safekeeper offloads WAL to <profile>/data/ in R2, and the tenant and timeline identities live in colors.yml — so a rebuilt host re-attaches the same identities and rehydrates from R2. Two-phase ownership markers make sure convergence never attaches a foreign or half-initialized prefix.

Quick start

npx skills add getcolors/neon
cp .agents/skills/package-neon-green/green ./green
chmod +x green
./green build
./green create --dry-run

Equivalent Red and Blue skills ship red and blue launchers.

Build renders reproducible output and dry-run walks the workflow without provider side effects. Real creation and deletion require explicit authorization.

Architecture

Compute

One Vultr instance and a firewall opening 22 — the only open port. In keygen mode the account SSH key resource is named after the profile and lives in this deployment's state.

Storage tier

Docker Compose: the storage broker, the pageserver, and one safekeeper, every image pinned by tag and digest, all bound to loopback, all writing to R2 under the deployment's own prefix.

Compute node

Postgres under compute_ctl, its roles provisioned from a spec whose SCRAM verifiers are generated on the host — recreate-only by doctrine, disposable by design.

Acceptance

Server-side: the SQL round-trip, three auth refusals, and R2 object evidence. Workstation-side: the SSH tunnel through the generated alias, with and without the password.

Connecting

There is no DNS record and no public database port — the supported client path is the tunnel:

ssh -L 55433:127.0.0.1:55433 <profile>
psql 'postgresql://neon@127.0.0.1:55433/neondb'

The role's password is generated on the server (ssh <profile> cat /etc/neon/secrets/neon_role_password); neon-status summarizes the deployment and neon-rotate rotates the password atomically, with rollback.

The machine keypair

The deployment owns its SSH key, following the workspace SSH Keypair Standard. Leave vultr-ssh-keys out of colors.yml and the first real create generates ~/.ssh/<profile>, registers it at Vultr under the profile name, and a successful delete removes it last — never before.

The key lives outside the checkout. Cloning a deployment repository does not carry machine access with it; copy ~/.ssh/<profile> deliberately. A key with no state is never overwritten, and a Vultr key named after the profile that this deployment's state does not own stops the run — if its fingerprint is not yours, do not delete it.

Supplying vultr-ssh-keys opts out entirely: the package then generates, validates and deletes no key material. Rotation is a rebuild, because Vultr key lists are ForceNew.

The SSH config block

Convergence also writes one ~/.ssh/config block, following the workspace SSH Config Standard, so ssh <profile> reaches the host with no address, user or -i flag — and the acceptance step rides exactly that alias, so the block is proven, not assumed. Delete removes the block before destroying the instance, the reverse of the keypair: a block that outlives its host is stale but harmless, while a key removed early locks you out of a machine that still exists.

Exposure

PortExposurePurpose
22vultr-ssh-sourcesKey-only SSH: convergence, recovery, and the client tunnel
55433 / 3080loopbackPostgres and the compute API, reached through the tunnel
9898 / 6400loopbackPageserver HTTP and page service
7676 / 5454 / 50051loopbackSafekeeper and the storage broker

Durability, honestly

Layers and WAL survive the host in R2, with an honest bound: commit acknowledgement requires the host's disk, uploads are asynchronous, and a fresh safekeeper cannot serve offloaded WAL back — so a full host loss recovers what the pageserver had uploaded, and the single-node RPO is the activity since its last checkpoint upload. A wiped storage tier on a surviving host loses nothing; both cases were rehearsed against the live deployment. Never configure R2 lifecycle rules on the bucket; they would delete live layers and WAL.

Safety

Source on GitHub · Configuration reference