ericrisco/rsc-harness/deployment
- community
Checking that the SKILL.md file is still in the repository.
Use when taking an app from source to live: choosing the deploy target from requirements (Hetzner+Coolify vs Vercel vs a third), then wiring container → CI → registry → host with build secrets, healthchecks and rollback. NOT one platform's mechanics (that is `coolify`, `vercel`, `railway`, `render`), NOT the Dockerfile alone (that is `docker`).
Install
npx vskill@latest install ericrisco/rsc-harness/deploymentSKILL.md
Ship it — Docker, GitHub Actions, and a deploy target (Coolify · Vercel · Hetzner)
Take any app in this repo from source → hardened container → green CI/CD → live on the right host, with secrets that never leak into image layers or logs, and a defined rollback path.
Out of scope — say so and stop: Kubernetes / Helm / ECS / Nomad orchestration; cloud IaC (Terraform, Pulumi, CloudFormation — only the GHA↔cloud OIDC handshake is covered, not provisioning); application runtime code and DB schema/migration logic (the per-stack skills at the bottom own what runs inside the container).
Decision rules
Consult these first. They settle 90% of choices before you write a line.
Table A — Base image by stack
| Stack | Base image | Notes | |
|---|---|---|---|
| FastAPI / Python | gcr.io/distroless/python3-debian12:nonroot (or python:3.13-slim) | UID 65532, no shell | |
| Go | gcr.io/distroless/static-debian12:nonroot | CGO_ENABLED=0 static, ~10 MB | |
| Next.js | node:24-bookworm-slim | Active LTS; output: "standalone" | |
| Flutter web | nginxinc/nginx-unprivileged:1.27-alpine | static SPA + try_files fallback | |
| Postgres | postgres:18-alpine | managed/official — do NOT build a custom image |
Table B — Coolify build pack
| Situation | Pick | |
|---|---|---|
| Repo has a Dockerfile | Dockerfile pack (always — CI/prod parity) | |
| No Dockerfile, standard stack | Nixpacks / Railpack | |
| Static SPA, no server | Static | |
| Multi-service local parity | Docker Compose | |
| CI already builds & pushes | Docker Image (deploy prebuilt ghcr image) |
If it has a Dockerfile, use the Dockerfile pack.
Table C — Deploy strategy
| Change type | Strategy | |
|---|---|---|
| Backward-compatible | Rolling (Coolify default, healthcheck-gated) | |
| Breaking / instant cutover / risky migration | Blue-green: two Coolify resources + domain swap | |
| Want gradual % traffic (canary) | Canary = release to a small subset, watch metrics, then ramp. Vanilla Coolify has no traffic split — emulate with feature flags (in-app % gating) or a blue-green pair behind a flagged path |
Table D — Secret delivery
| Secret kind | Mechanism | |
|---|---|---|
| Build-time non-secret | ARG | |
| Build-time secret (private dep token) | BuildKit --mount=type=secret (NEVER ARG) | |
| Runtime secret | Coolify env (Is Secret) / GHA secrets | |
| Cloud auth | OIDC — never a stored key |
Docker — the canonical multi-stage shape
One process per container: no supervisord-managed bundles, let the orchestrator scale.
→ full per-stack Dockerfiles: references/dockerfiles-by-stack.md · image-authoring depth (shrinking, base-image choice, cache busting): ../docker/SKILL.md
docker-compose for local dev + Postgres
- GOOD: bind-mount source for dev hot reload; BAD: bind-mount source over a prod image (it shadows the baked build).
- GOOD: bind Postgres to
127.0.0.1; BAD: bind it to0.0.0.0in prod (publicly reachable DB).
→ prod overlay + mailpit: references/dockerfiles-by-stack.md
GitHub Actions — least-privilege pipeline
- GOOD: scoped per-job
permissions(onlybuild-pushgetspackages: write/id-token: write). - BAD: blanket
permissions: write-all— any compromised step can push images or mint tokens. - GOOD: third-party actions pinned to a full commit SHA with a version comment (
@<sha> # v0.35.0). In the March 2026trivy-actionsupply-chain incident (GHSA-69fq-xp46-6x23 / CVE-2026-33634), 76 of 77 tags were force-pushed to credential-stealing malware; the advisory's named known-safe ref isv0.35.0(commit57a97c7e7821a5776cebc9bb87c984fa69cba8f1), the one clean tag still pointing at the realmasterHEAD. A moving tag would have pulled the malware; this SHA pin does not. Let Dependabot bump the SHA once upstream re-tags cleanly.
→ matrix, reusable workflows, OIDC-to-cloud, environments/approvals, releases: references/github-actions.md · workflow-syntax depth: ../github-actions/SKILL.md
Choosing a deploy target (3 options)
Never recommend a single host. Gather requirements → recommend exactly three targets with trade-offs, so the choice is made with eyes open. The canonical slate:
- Hetzner VPS + Coolify — cheapest control, EU residency, sustained/always-on/stateful;
you own ops. (The combo references/coolify.md runs on; see below.)
- Vercel — zero-ops serverless/edge, ideal Next.js, scales to zero for spiky traffic;
metered cost climbs at sustained scale, US-default region.
- A third that fits the case's sharpest constraint — Railway (tiny/simple, predictable
bill), Fly.io (true global edge, 30+ regions), or a hyperscaler (enterprise compliance).
Requirements to gather first: expected total/concurrent users · traffic shape (steady vs spiky) · budget ceiling · data region/residency & compliance · team ops comfort · scaling needs (scale-to-zero, global latency) · stateful needs (own DB/queue/websockets).
Quick steer: Next.js + spiky traffic + ops-averse → Vercel. Cost-sensitive / EU-resident / sustained / own stateful services → Hetzner+Coolify. The Dockerfile this skill produces is the escape hatch — start on Vercel, move to Hetzner+Coolify when the bill grows, same artifact.
→ deep coverage (limits, regions, pricing, decision matrix, worked examples): references/hosting-targets.md
Coolify — wiring the chosen target
Only the parts that touch the pipeline; the platform walkthrough lives elsewhere.
- Pick the Dockerfile build pack when a Dockerfile exists — same artifact CI builds, full control, prod/CI parity.
- Set Ports Exposes to the container port your app listens on (e.g.
8000); Traefik routes the domain to it. - Set the Health Check path/port → this is what gates the rolling swap to the new container.
- Mark sensitive env vars Is Secret — encrypted at rest, masked in logs and UI.
- Enable GitHub App auto-deploy on push, OR call the deploy webhook from CI (one or the other, not both).
- Rollback = redeploy a previously stored image in one click; pair with backward-compatible migrations.
→ persistent storage, custom domains + Let's Encrypt, per-PR previews, CPU/memory limits, blue-green: references/coolify.md and ../coolify/SKILL.md
Secrets flow (GitHub → registry → Coolify)
- A secret crosses at most one trust boundary per hop — never forward a GHA secret into the running container; let Coolify inject runtime env.
- Nothing long-lived lives in a workflow file:
GITHUB_TOKENand OIDC tokens are minted per run and expire. ${{ }}secrets are masked in logs, butset -xandecho "$SECRET"defeat the mask — forbid both.
12-factor config & observability
This is the start of the file. Read the full SKILL.md on GitHub.
Security
- Security scanPassed, 95 of 100Pattern checks on the skill's files.
- Model reviewPassed
Quality evaluation
- Task evaluationNot runNo evaluation yet. Run one in Skill Studio.
Source
- SKILL.md in the repositoryCheckingskills/deployment/SKILL.md
Popularity
- Repository stars
- 141
- Repository forks
- 11
- 7-day trend score
- 30
Stars and forks are for the whole GitHub repository, not this skill alone. Not refreshed yet.
Works with
Any agent vskill installs to: 56 targets in all. These 39 have a page on this site.
- Claude Code
- Cursor
- GitHub Copilot
- Windsurf
- Codex
- Gemini CLI
- Cline
- Amp
- Roo Code
- Goose
- Continue
- Kilo Code
- Junie
- Trae
- OpenHands
- Qwen Code
- AI Agent
- Tabnine Chat
- Sourcegraph Cody
- Amazon Q
- Firebase Genkit
- Sweep
- Devin
- Phind
- Mentat
- Claude Desktop
- Aider
- GPT Pilot
- Plandex
- bolt.new
- v0
- Replit Agent
- Anse
- TypingMind
- BetterChatGPT
- LibreChat
- Warp AI
- Pieces
- Fig AI
Embed badge
[](https://verified-skill.com/skills/ericrisco/rsc-harness/deployment)