Go-Live: DNS, Render, Vercel, Recast & United Domains
Automation for Custom Domains — without data loss (only additive DB fields and optional API steps).
Hosting paths (overview):
| Path | Typical for | DNS target |
|---|---|---|
| Render (Cockpit multi-tenant) | Non-MEC / preview | Render LB + CNAME |
| Vercel (v0 shared/dedicated) | v0 without Recast | Vercel (A / cname.vercel-dns.com) |
| Recast (Docker/GHCR) | MEC: one GitHub repo per template, Recast pull | A record → Recast server IP |
Sections below on Vercel Go-Live and UD automation mainly apply to Render/Vercel. For MEC on Recast, see MEC on Recast.
Requirements (IT)
Dashboard environment (Render/Production):
| Variable | Purpose |
|---|---|
RENDER_API_KEY + RENDER_FRONTEND_SERVICE_ID | Cockpit template on Render |
VERCEL_API_TOKEN | optional VERCEL_TEAM_ID |
UD_RESELLING_LOGIN + UD_RESELLING_PASSWORD | DNS at UD Reseller (domainreselling.de) |
UD_DNS_API_KEY | DNS at UD customer account (DNS API add-on, format prefix.secret) |
COCKPIT_DISABLE_DNS_PROVIDER_WRITE=1 | Emergency: UD read-only, no DNS write |
Migration: packages/database/migrations/20260531120000_add_hosting_dns_vercel_ud_SAFE.sql
v0 Team Template (recommended)
Once: Golden Project + v0 Team Template + Vercel Shared Env (COCKPIT_DASHBOARD_URL, REVALIDATION_SECRET, COCKPIT_FRONTEND_CHANNEL).
Per Center: only COCKPIT_REGISTER_TOKEN + COCKPIT_CENTER_SLUG — Cockpit checks that the token and slug match.
→ Step-by-step: Set up v0 Team Template
Center ↔ Vercel Project
| Mode | Storage Location | When |
|---|---|---|
| Dedicated (1 Center = 1 Project) | ShoppingCenter.vercelProjectId | Standard v0 per Center |
| Shared (Multi-Center, one layout) | Organization.vercelProjectsByTemplate[websiteTemplate] → fallback vercelSharedProjectId + vercelProjectMode=shared | Part G / one Vercel project per template family |
IT maintains shared projects once under Dashboard → Organizations → Hosting tab (e.g. /dashboard/organizations/mec?tab=hosting). One prj_… per MEC website template — in the UI e.g. MEC Template Rot (mec-template-a), MEC Template Hybrid (mec-template-d), MEC Grooß (mec-template-grooss), MEC 2 (mec-template-c), MEC Grün (mec-template-green). Centers in shared mode do not need their own projectId — Go-Live resolves from the center's websiteTemplate.
Resolution order for shared:
- Optional:
ShoppingCenter.vercelProjectId(center override) Organization.vercelProjectsByTemplate[websiteTemplate]- Fallback:
Organization.vercelSharedProjectId
Automatically during deploy registration:
- Vercel Env:
VERCEL_PROJECT_ID, optionalCOCKPIT_VERCEL_PROJECT_MODE=shared - Route
POST /api/cockpit-register→ Cockpit storesvercelProjectIdin Meta and at the Center
Manually: Website Management → Frontend Channels (v0) or Tab Center → Website → Activation (Block “Go-Live”).
MEC on Recast (Docker / GHCR)
Context: For MEC we run one v0 GitHub repo per website template (e.g. smg-mec-template-a, smg-mec-template-d, smg-mec-template-grooss). Recast hosts the built Docker images from GHCR — no Vercel project per center, no new repo per center.
This matches Part G (Multi-Center) in the v0 instructions: one layout per template family, many domains; content per center from the Cockpit API (by-domain).
Architecture (DNS vs. app)
DNS (per center domain) Recast server Cockpit
───────────────────── ───────────── ───────
center-a.de ──A──► Recast IP ──► Nginx ──► Container template-a ──► by-domain?domain=center-a.de
center-b.de ──A──► Recast IP ──► Nginx ──► Container template-a ──► Center B (same image)
center-c.de ──A──► Recast IP ──► Nginx ──► Container template-d ──► Center C (Hybrid image)
- DNS only decides: domain → Recast server IP (not Vercel, not Render).
- Which center loads is resolved by
GET …/api/centers/by-domain?domain={host}(Host header must be forwarded by Nginx). - Cockpit
websiteTemplatemust match the template repo/container (e.g. Rot centers only on themec-template-astack).
Target DNS per center domain (Recast)
| Record | Name | Value |
|---|---|---|
| A | @ | Recast root server IP |
| A | www | same IP (or CNAME www → apex — per provider) |
Not required: Vercel CNAME (cname.vercel-dns.com), Render CNAME, Vercel prj_… registration.
Who does what?
| Step | Who | Action |
|---|---|---|
| 1 | IT / editorial | customDomain (+ optional customDomains) in Cockpit — Activation tab |
| 2 | Domain owner (MEC, center, UD) | Set A record to Recast IP |
| 3 | Recast (hosting partner) | SSL: certbot --nginx -d domain.de -d www.domain.de |
| 4 | Recast (if multiple template containers) | Nginx: domain → correct container port (see below) |
| 5 | Template repo (v0) | resolveCenter() / middleware — host → by-domain |
New MEC center (same template): domain + Cockpit data + DNS + SSL — no new GitHub repo, no new GHCR build. Code update for all centers in a family: one push in the template repo → GHCR → Recast pull.
Nginx on Recast
Two common variants:
Variant A — one container per template (recommended with multiple repos):
- e.g.
mec-template-a→127.0.0.1:3001,mec-template-d→127.0.0.1:3002 - Per domain (or map file): Nginx routes to the matching port — based on the center's
websiteTemplatein Cockpit - Optional in container:
EXPECTED_WEBSITE_TEMPLATE=mec-template-a— wrong routing → 404 instead of wrong layout
Variant B — one multi-tenant container (Dockerfile.center-website):
- All domains → one port (e.g.
3000), Nginxserver_name _;withproxy_set_header Host $host; - See also
SETUP-NOTES-for-hoster.mdin the repo root
Deploy package for v0 repos (one repo per template): deployment-templates/v0-recast/ — details: v0 on Recast (Docker).
Env on Recast (per template container)
For multi-center per template repo (Part G):
| Variable | Required | Note |
|---|---|---|
NEXT_PUBLIC_DASHBOARD_URL | yes | API base for browser + server |
REVALIDATION_SECRET | yes | must match Cockpit dashboard |
EXPECTED_WEBSITE_TEMPLATE | recommended | e.g. mec-template-a |
COCKPIT_CENTER_SLUG | no (production) | preview/default only — otherwise one center “wins” |
COCKPIT_REGISTER_TOKEN | no (production shared) | register/URL per center via Cockpit/MCP, not as global container env |
COCKPIT_CENTER_SLUG + COCKPIT_REGISTER_TOKEN per stack apply to dedicated Recast (one center = one compose stack) — not MEC “one repo per template, many domains”.
Cockpit Go-Live / UD automation vs. Recast
| Feature | Render / Vercel | Recast (MEC) |
|---|---|---|
| Go-Live button registers domain at Vercel/Render | yes | no (no Vercel prj_…) |
| UD automation sets CNAME/A to Vercel/Render target | yes | manual: A to Recast IP (UD zone can still be updated by hand/API — Cockpit target is currently Render/Vercel) |
customDomain in Cockpit for by-domain | yes | yes — required |
cockpit_dns_status (read current DNS) | yes | yes — checks reachability |
| Revalidate after publish | yes | yes — if websitePublicUrl + REVALIDATION_SECRET on Recast |
In practice: save domain in Cockpit, point DNS at Recast IP, SSL on Recast, test under Frontend Channels. Optional MCP: cockpit_register_frontend_deployment with origin=https://www.center.de (AgencyOS key).
MEC template ↔ repo (reference)
| Display name | websiteTemplate | Typical repo / GHCR image |
|---|---|---|
| MEC Template Rot | mec-template-a | smg-mec-template-a |
| MEC Template Hybrid | mec-template-d | smg-mec-template-d |
| MEC Grooß | mec-template-grooss | smg-mec-template-grooss |
| MEC 2 | mec-template-c | smg-mec-template-c |
| MEC Grün | mec-template-green | smg-mec-template-green |
Org Hosting tab (vercelProjectsByTemplate) is for Vercel shared — irrelevant for Recast; GHCR image mapping on the server matters instead.
See also: v0 Recast Deploy, v0 Instructions Part G, SETUP-NOTES-for-hoster.md.
Preview vs. Live (Website URLs)
| Field | Purpose | UD / Go-Live |
|---|---|---|
websitePublicUrlStaging | Preview / Staging (e.g. *.vercel.app, staging.…) | No — only revalidate & connection test |
websitePublicUrlProduction | Live origin (Custom Domain on Vercel) | Yes — often set automatically after Go Live |
websitePublicUrl | Legacy (Production → Staging) | Backward compatible |
Deploy register: Body environment: preview | production (Vercel: VERCEL_ENV is sent).
Without environment, the origin counts as staging when the hostname is e.g. *.vercel.app, staging.…, preview.…, or v0.… — otherwise use the Preview / Staging field in the dashboard when setting URLs manually.
Migration: 20260601120000_add_website_url_staging_production_SAFE.sql (existing websitePublicUrl is split, not deleted).
Center Detail (Tab “Website Technology”), Preview and SEO Tabs: Links to the live website use the same logic — Production URL from Cockpit (websitePublicUrlProduction, Custom Domain, or Legacy websitePublicUrl). Without an entry: https://{center-slug}.cockpit-os.de (no longer mallos-center-website.onrender.com/{slug}).
Go-Live Procedure
- Enter and save Custom Domain(s) in the Activation tab.
- Choose Hosting: Cockpit Template (Render) or v0/Vercel; set Project ID / Mode in Vercel.
- DNS Preview (Dry-Run) — shows planned steps without changes.
- Execute Go Live — registers Domain at Render/Vercel, replaces conflicting A/AAAA/CNAME at UD (
delrr+addrr), checks status.
UD: automatic DNS migration
Go-Live supports two United Domains APIs (auto-detected per domain):
| API | Env | When |
|---|---|---|
| Reseller | UD_RESELLING_* | Domain/DNS zone in reseller portal (domainreselling.de) |
| Customer DNS API | UD_DNS_API_KEY | Domain in normal UD portfolio with DNS API product |
Priority: check reseller zone first, then retail portfolio. Go-Live reads the current zone and compares it to the target (Render or Vercel):
| Action | When |
|---|---|
Delete (delrr) | Old A, AAAA, or CNAME on @ / www that does not match the Cockpit target |
Add (addrr) | Missing target records (Render: A @ → 216.24.57.1, CNAME www → mallos-center-website.onrender.com) |
| Untouched | MX, TXT, NS, SOA — mail and verification records stay |
Dry-run lists exactly which records would be removed and added.
GET …/dns-status exposes pendingSync for what Go-Live would change.
Domains not at UD (other nameserver): manual steps in the Activation tab — UD automation does not apply.
API (Session):
GET /api/centers/{centerId}/dns-statusPOST /api/centers/{centerId}/go-live-domain— Body:{ "dryRun": false, "applyUdDns": true, "domains": ["example.com"] }(optionaldomainsfor preview before save; otherwise only persisted DB domains)
Expected client errors (not HTTP 500):
| HTTP | Code | Meaning |
|---|---|---|
| 400 | NO_CUSTOM_DOMAINS | No domain saved — fill Activation tab and save website config |
| 400 | VERCEL_PROJECT_MISSING | Vercel mode without projectId |
| 404 | CENTER_NOT_FOUND | Unknown center ID |
| 503 | VERCEL_NOT_CONFIGURED | VERCEL_API_TOKEN missing in dashboard env |
Troubleshooting (Go-Live steps)
| Step | Message | Cause / fix |
|---|---|---|
render:… | 401 Unauthorized | Invalid or expired RENDER_API_KEY on the dashboard service |
render:… | service not found | Wrong RENDER_FRONTEND_SERVICE_ID (placeholder) — copy real ID from mallos-center-website → Settings (srv_…) |
render:… | Key missing | Set RENDER_API_KEY and RENDER_FRONTEND_SERVICE_ID, or add domain manually in Render |
ud:… | Zone not found | Domain not in UD reseller or customer DNS API portfolio |
ud:… | fetch failed | UD API unreachable from dashboard server |
ud:… | CNAME is not allowed for a zonename | Apex needs A @ → 216.24.57.1, not CNAME @ — re-run Go-Live after dashboard deploy |
verify | DNS/HTTPS active | Domain works — Render/UD errors affect automation only, not necessarily the live site |
AgencyOS / MCP:
cockpit_dns_statuscockpit_go_live_domaincockpit_register_frontend_deployment(withvercelProjectId,vercelProjectMode)
Org Shared Project (Super Admin):
- UI: Organization → Hosting tab (e.g.
/dashboard/organizations/mec?tab=hosting) GET/PATCH /api/organizations/{slug}/hosting— body e.g.:({"vercelProjectsByTemplate": {"mec-template-green": "prj_…","mec-template-grooss": "prj_…"},"vercelSharedProjectId": "prj_…"}vercelSharedProjectIdoptional as fallback; slug or Org UUID)
Editorial: Website Live (v0 + MCP)
-
Enter the Domain in Cockpit under Center → Website → Activation — or via MCP:
cockpit_update_center_website_configwith top-levelcustomDomain: "example.de"(notcontentConfig). For go-live, you can also passcustomDomaindirectly tocockpit_publish_website_live. -
Build in v0; when finished, in the chat (with Cockpit MCP):
“The website is ready and should go live. Domain: example.de — please check UD and make it live.”
-
Assistant calls
cockpit_publish_website_live(dryRun: true, thenfalse).
In the Dashboard under Frontend Channels (v0), there is the copyable chat text and a compact status (UD/DNS).
Connection Overview (Dashboard)
In the Tab Center → Website → Activation and under Website Management → Frontend Channels (v0), the Connection Overview displays at a glance:
- Cockpit website active, layout template, Cockpit preview
- Custom Domain / DNS / SSL (Live)
- Hosting Render vs. Vercel, Preview URL, Live URL
- Auto-Update (Revalidate) after Publish
Button Test checks reachability and revalidate — without database changes.
Section “DNS: where does the domain point?” (read-only, GET …/dns-status):
| Source | What is read |
|---|---|
| Public | DNS Lookup + HTTPS — shows, e.g., on Render or Vercel |
| Vercel API | Domain in the project? verified? |
| UD Reselling | QueryDNSZoneRRList — Actual records vs. Desired (Render/Vercel) |
IT requirement: VERCEL_API_TOKEN, UD_RESELLING_* and/or UD_DNS_API_KEY on the Dashboard (Render).
Revalidate: Render (Multi-Center) vs. Dedicated v0 Live Domain
The Dashboard invalidates in parallel all configured targets (CENTER_WEBSITE_URL + Staging/Production URL of the Center).
| Target | Path Mode | Example |
|---|---|---|
Cockpit Center Website on Render (mallos-center-website…) | /{centerSlug}/… | /allen-center-trier/shops |
| Own v0/Vercel Live Domain (only one Center, Root Routing) | /shops, /news, ... | /shops on alleencenter.com |
| Recast / v0 template repo (MEC multi-center) | /shops, /news, … (root) | /shops on center-a.de — center via by-domain |
If the Live Domain does not have a route POST /api/revalidate with the same REVALIDATION_SECRET, this target is logged as skipped — the Render instance will still be updated. For auto-update on the real domain: Implement the Cockpit revalidate route from the Monorepo (apps/center-website/app/api/revalidate/route.ts) into the v0 project and set Secret on Vercel.
Notes
- UD API only updates zones hosted at UD (domain nameservers at United Domains). Conflicting A/AAAA/CNAME on
@/wwware replaced on Go-Live; MX/TXT are kept. - Domains at other providers: continue manual guidance in the Activation tab.
- Apex Domains (.de): often A-Record instead of CNAME `@ — adopt Vercel/Render guidelines from API.
Nutzungsstatistik: Seitenaufrufe werden anonymisiert erfasst. Im Umami-Dashboard nach diesem Pfad filtern: /en/dashboard/go-live-dns-vercel-ud