Neue Skill: typo3-staging-to-live-cutover

Das Go-live ist eine eigene Aufgabe, nicht der letzte Schritt des
Staging-Deployments: oeffentlich, schwer umkehrbar, anderes Risiko.

Phasen: Cutover-Modell waehlen; aufraeumen, was nicht mit auf live darf
(DB-Dumps, Einmal-Skripte, .bak-Configs, Fehlerlogs); umgebungsabhaengige
Konfiguration (trustedHostsPattern, displayErrors, site base, DB-Zugang,
Mail); Staging-Sperren entfernen (Basic-Auth, noindex, Testkonten);
Redirects/Scheduler/Sprachpakete/SSL; Verifikation und Rueckweg.

Die Beispiele stammen aus einem realen Staging-Account: ~26 MB Dumps und
Ticket-Sicherungen im Home, eine .bak-Config im Projektbaum, Basic-Auth
in public/.htaccess, displayErrors=1 und ein auf die Staging-Domain
gepinntes trustedHostsPattern.

Die konsoleH-Deploy-Skill (jetzt 1.1) verweist am Ende darauf und benennt
die beiden Artefakte, die sie selbst erzeugt und die nicht mitwandern
duerfen.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-06 13:45:32 +02:00
co-authored by Claude Opus 5
parent 674fbc9e46
commit 52df6752d9
2 changed files with 159 additions and 1 deletions
+9 -1
View File
@@ -3,7 +3,7 @@ name: typo3-konsoleh-staging-deploy
description: "Deployt ein lokal mit DDEV gebautes TYPO3-Projekt (v13/v14, Composer) als Staging-Seite auf einen Hetzner-Managed-Webhosting-Server (konsoleH). Der Composer-Build läuft AUF dem Server (kein vorgefertigtes vendor/ wird hochgeladen). Verwenden, wenn eine TYPO3-Staging- oder Vorschau-Umgebung auf Hetzner Managed Hosting (konsoleH), einem *.your-server.de-Host oder dedi*-Server eingerichtet werden soll, oder wenn eine lokale DDEV-TYPO3-Seite per SSH auf ein Managed-/Shared-Hosting übertragen werden soll. Umfasst: SSH-Inventar, per-User-composer.phar, Deploy-Keys für private Git-Repos (gitea) + Registry-Auth, Datenbank + Dokumentstamm über konsoleH, rsync von Quellcode + fileadmin, DB-Import, Staging-additional.php, .htaccess, Sprachpakete, HTTP-Basic-Auth und Let's Encrypt. NICHT für Root-VPS-/Docker-/DDEV-auf-dem-Server-Deployments."
metadata:
author: Wappler
version: "1.0"
version: "1.1"
---
# TYPO3 → Hetzner konsoleH staging deployment
@@ -224,3 +224,11 @@ On subsequent rsyncs, **exclude** the files that hold staging-specific state:
9. `.htpasswd` must be `chmod 644` (Apache auth uid ≠ PHP uid) and live outside the web root.
10. SSL = admin sets Let's Encrypt in the konsoleH SSL-Manager.
11. Always run composer/typo3 with `php -d memory_limit=-1` (128M host limit).
## Danach live gehen
Diese Skill baut und pflegt eine **Staging**-Umgebung. Sie in Produktion zu überführen ist eine eigene
Aufgabe mit anderem Risikoprofil — siehe die Skill **`typo3-staging-to-live-cutover`**. Zwei Dinge, die
diese Skill bewusst erzeugt und die **nicht** auf live gehören: der **Basic-Auth-Block samt
`.htpasswd`** und die **DB-Dumps / Ticket-Sicherungen**, die sich im Home des Accounts ansammeln.
Dumps von Anfang an außerhalb des Docroots ablegen, mit Datum benennen und löschen, sobald die
Änderung abgenommen ist, die sie absichern.
+150
View File
@@ -0,0 +1,150 @@
---
name: typo3-staging-to-live-cutover
description: "Promote an approved TYPO3 staging site to production — the go-live step AFTER a staging environment exists. Use when a TYPO3 site is about to go live, when a staging/preview site must become the public site, when asked to clean up a staging server before launch, or when preparing a launch checklist for a shared/managed host. Covers: what must NOT travel to live (DB dumps, one-off SQL/PHP scripts, .bak files, error logs), the environment-specific config that must change (trustedHostsPattern, displayErrors, site base, DB credentials, mail), removing basic-auth/staging locks, indexing and redirects, scheduler and SSL, plus verification and rollback. Assumes the build already runs on staging (see the staging-deploy skill for that)."
metadata:
author: Wappler
version: "1.0"
---
# TYPO3 — staging → live cutover
Turning an approved staging site into the public site. Different risk profile from building staging:
**public, hard to reverse, and observed by the customer**. Work through the phases in order; do not
start Phase 4 before Phase 13 are verified.
Placeholders: `<SSH_USER>@<HOST>`, `<PROJECT_DIR>` (e.g. `~/public_html/<project>`), `<SITE_ID>`,
`<STAGING_DOMAIN>`, `<LIVE_DOMAIN>`, `<LIVE_DB>`.
---
## Phase 0 — Decide the cutover model FIRST
It determines everything else. Three common shapes:
- **Domain switch in place** — the staging webspace becomes the live one (point `<LIVE_DOMAIN>`'s
document root at the same project). Cheapest; but staging then no longer exists as a separate
environment, so agree with the user where future work is previewed.
- **Copy into the live webspace** — rsync the project + DB into a separate account/space. Keeps
staging alive. Everything in Phase 1 matters twice: you must not copy the junk over.
- **Rebuild from the repo** — `composer install` on the live space, DB imported from staging. Cleanest;
the artefacts in Phase 1 never exist there. **Prefer this when the host allows it.**
Also settle **who owns the DNS/TTL change** and **when** (lower the TTL a day ahead), and confirm the
content freeze: the customer's editors are usually working in staging until the last minute.
---
## Phase 1 — What must NOT travel to live
Staging accumulates working material. Inventory it, then decide per item: delete, or keep outside
the project tree. **Never just leave it.**
```bash
# Dumps, backups, one-off scripts in the account home
ls -lah ~/ | grep -viE '^d|\.bashrc|\.profile|\.ssh'
# Leftovers inside the project (exclude vendor)
find <PROJECT_DIR> -path '*/vendor' -prune -o \
\( -name '*.bak' -o -name '*.bak-*' -o -name '*.orig' -o -name '*.sql' \
-o -name '*.sql.gz' -o -name '*.zip' -o -name '*.tar.gz' \) -print
# Anything executable in the docroot besides the entry point
find <PROJECT_DIR>/public -maxdepth 1 -name '*.php'
```
Typical finds, and why each matters:
- **Database dumps** (`*-db*.sql.gz`, `backup-*.sql`) — often tens of MB and a **full copy of the
production dataset**, including `be_users` hashes and any personal data in content records.
Usually they sit in the account home, i.e. *outside* the docroot and not web-reachable — verify
that rather than assume it (`ls -la ~/public_html` and compare with the domain's document root).
Even when unreachable they must not be copied to live and should not linger indefinitely.
- **Per-ticket safety backups** (`backup-<ticket>-<date>.sql`) and permission exports
(`be_group*.bak`) — the same, in miniature. Delete once the change they protected is accepted.
- **One-off migration/build scripts** (`build-*.php`, `*-debug.php`, `fix-*.php`) — the real hazard:
a script written to run once, left in a web-reachable path, is a remote code path nobody reviews.
**Nothing but the framework's entry point belongs in the docroot.**
- **`*.bak` config files** (e.g. `config/sites/<SITE_ID>/config.yaml.bak-<ticket>`) — these DO sit
inside the project and will be rsynced to live. Remove them; they are noise at best and a
stale-config trap at worst.
- **Empty/failed artefacts** (`dumperr.log`, `importerr.log`, 20-byte `.sql.gz` files from a dump
that failed) — evidence a step went wrong; read them once, then delete.
- **The staging `.htpasswd`** — see Phase 3.
Before deleting a dump, confirm its content is either reproducible or genuinely obsolete. If the user
wants an archive, move it **off the server** (they download it), don't relocate it within the account.
---
## Phase 2 — Environment-specific configuration
Every one of these is pinned to staging and silently wrong on live.
- **`config/system/additional.php`** (or the live equivalent):
- `SYS/trustedHostsPattern` — pinned to `<STAGING_DOMAIN>`; a wrong value gives a hard failure on
the live host. Set it to `<LIVE_DOMAIN>` (escape the dots), or `.*` only if the host terminates
the Host header itself.
- `SYS/displayErrors` — staging typically runs `1`. **Must be `0` (or removed) on live**, otherwise
stack traces with paths and query fragments are public.
- `SYS/devIPmask`, `exceptionalErrors`, `systemLogLevel`, debug/preview flags — reset to production.
- **Application context** — `Production` on live (`TYPO3_CONTEXT`, often via `.htaccess`/vhost).
- **DB credentials** — the live database is a different one. Keep the pattern of reading the password
from a `chmod 600` file outside the docroot rather than inlining it; **generate fresh credentials
for live, do not reuse staging's.**
- **Site configuration** `config/sites/<SITE_ID>/config.yaml``base` is usually `/` or the staging
host. Set the live base (or base variants keyed on the application context, which lets one config
serve both). Check `baseVariants` blocks left over from earlier environments.
- **Mail** — sender address/domain, SMTP credentials, and any staging-wide redirect of outgoing mail
(a "catch all recipients" setting is common on staging and **must** be off on live).
- **File permissions** — `settings.php` holds the `encryptionKey` and is often world-readable (644) on
shared hosting. Tighten what the host allows, and rotate the encryption key if staging was widely
shared.
---
## Phase 3 — Remove the staging locks
- **HTTP basic-auth**: staging is usually protected in `public/.htaccess`
(`AuthType Basic` / `AuthUserFile …/.htpasswd` / `Require valid-user`). Remove that block for live —
and delete the `.htpasswd` file. Verify with `curl -sk -o /dev/null -w '%{http_code}' <LIVE_URL>`:
**200, not 401**.
- **Indexing**: whatever was decided for staging is usually the opposite for live. Check `robots.txt`,
any `X-Robots-Tag` header, and the site's SEO settings for a global `noindex`.
- **Test accounts**: remove backend users created for the launch, reset the install-tool password, and
confirm no `_cli_`/demo account carries an interactive password.
- **Placeholder files** left in the webspace root from before the project (`index.htm`, "coming soon"
pages) — they can shadow the real entry point depending on the host's `DirectoryIndex`.
---
## Phase 4 — Content, SEO, operations
- **Redirects** — a relaunch usually changes slugs. Export the old URL set, map it to the new one, and
load it into `sys_redirect` **before** DNS switches. Check the old site's top URLs from its access
logs or analytics, not from the sitemap alone.
- **Scheduler** — set up the cron entry on the live host (`vendor/bin/typo3 scheduler:run`, typically
every minute); staging often has none.
- **Language packs** — `vendor/bin/typo3 language:update` on the live host; in Composer mode they are
not shipped, and labels silently fall back to English.
- **SSL** — issue/activate the certificate for `<LIVE_DOMAIN>` (on managed panels this is a UI step and
may need a retry before it takes). Verify the chain from outside, not just `curl -k`.
- **Caches and index** — `cache:flush`, clear compiled CSS/JS artefacts, `referenceindex:update`.
- **Search/indexed search** — re-index after the content freeze lifts.
---
## Phase 5 — Verify, then keep a way back
- **Rollback plan before the switch**: a final DB dump plus the previous DNS values, written down.
Store the dump outside the docroot and **delete it once the launch is accepted** — otherwise it
becomes exactly the Phase 1 problem on the live server.
- **Smoke test the live host** over the real domain (loop the slug list, assert 200), including the
backend, a form submission, and one page per template type.
- **Confirm from outside your network** — DNS, SSL and caching behave differently from a machine that
has been talking to the staging IP all week.
- **Re-run the Phase 1 inventory on the live server** after the cutover. If you copied rather than
rebuilt, the junk came along.
---
## Recurring traps
- **Remote DB writes and schema commands are frequently blocked by tooling guards.** Expect it: stage
a reviewed `.sql` file on the server, keep a row-level backup beside it, and hand the user the
one-line command to execute rather than fighting the guard.
- **Editors keep working during the cutover.** A dump taken at 13:00 does not contain the 13:40 edit.
Agree a content freeze, or take the final dump immediately before the switch and re-verify counts.
- **`composer install` ships the locked reference.** If the sitepackage is tracked from a branch, the
lock may point at an older commit — `composer update <TEMPLATE_PACKAGE>` explicitly, else live runs
a template older than staging's.
- **Do not promote by copying the staging DB onto an existing live DB** without checking which side is
the content master. Once real editors touch the live site, the direction reverses permanently.