Å dra temafiler over FTP i 2026 ender vanligvis likt: én fil fra inc/ mangler, composer.lock kom fra en annen laptop, og produksjon ligger nede fordi noen overskrev wp-config.php. Continuous Integration og Continuous Deployment er ikke enterprise-teater - det er måten hver Git-endring går gjennom samme bygg, samme tester og samme vei inn på serveren.
Denne guiden gjelder WordPress med tema eller plugin i repoet (Composer, npm, eventuelt Docker), ikke nettsteder som bare lever i admin. Hvis egendefinert kode fortsatt bare finnes i database og media, flytt temaer og custom code til Git først - uten det har CI/CD ingenting å bygge.
Dokumentasjon for runners: GitHub Actions. Les også parallelt: Hardening WordPress. For kommandoer etter deploy: WP-CLI.
Hvorfor FTP og «bare last opp» feiler i revisjoner
Tre problemer dukker opp igjen i norske nettbutikker og byråoverleveringer:
- Miljødrift - lokalt PHP 8.2 og Composer 2.7, VPS på PHP 8.1 uten
intl. Lokalcomposer install«fungerer»; produksjon dør på class not found. - Ingen revisjonsspor - etter en hendelse kan du ikke si hvilken commit som var live. Hostingpanelet viser en fildato, ikke en Git-SHA.
- Hemmeligheter på disk - SFTP-passord i FileZilla, eller en
.envcommitted «bare et øyeblikk», er fortsatt klassisk lekkasjevei - særlig når persondata under GDPR ligger i staging-kopier.
CI/CD fikser dette mekanisk: bygg på kjent image (shivammathur/setup-php eller egen Docker), versjoner artefakt etter SHA, deploy over SSH med nøkkel i Secrets.
Minimal pipeline: push, bygg, test, deploy
Typisk arbeidsflyt for custom-tema pluss Composer-plugins:
- Trigger - push til
maineller merge av gjennomgått pull request.developgår ofte bare til staging. - Bygg -
composer install --no-dev --optimize-autoloader,npm ci,npm run build. Pin PHP og Node i workflow (php-version: '8.2',node-version: '20'). - Test - PHPUnit for plugins, eventuelt Playwright på checkout. Fail stopper jobben; ingen deploy.
- Artefakt - pakk release uten
.git, utennode_modules, medvendor/og bygdassets/dist/. - Deploy - rsync/SCP til
releases/<run_id>/, deretter atomisk symlink-bytte.
Kort skisse i GitHub Actions:
name: deploy-production
on:
push:
branches: [main]
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: shivammathur/setup-php@v2
with:
php-version: '8.2'
- run: composer install --no-dev --optimize-autoloader
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: npm
- run: npm ci && npm run build
- name: rsync release
env:
SSH_KEY: ${{ secrets.DEPLOY_SSH_KEY }}
run: |
# skriv nøkkel, rsync til releases/$GITHUB_SHA, ln -sfnFull symlink-script ligger i repoet som bin/release.sh - workflowen kaller bare. Samme script fungerer fra laptop når Actions er nede.
Atomiske deploys i stedet for å overskrive filer
Å overskrive filer «på plass» under public_html betyr at noen requests i noen sekunder ser gammel functions.php og ny template. I WooCommerce under Black Friday-kampanjer kan det ødelegge handlekurvsesjoner.
Atomisk oppsett:
- base:
/var/www/site/ - releases:
/var/www/site/releases/20260920-142211/ - shared: uploads,
wp-config.php, object cache - utenfor releasen current→ symlink til aktiv release
Etter vellykket rsync:
ln -sfn /var/www/site/releases/20260920-142211 /var/www/site/currentNginx root peker på .../current/public (eller tilsvarende). Byttene er umiddelbare. Slett gamle releases etter N dager, eller behold de tre siste for rollback.
WordPress-uploads må ligge utenfor releasen - ellers sletter hver deploy media eller kopierer gigabytes. Klassisk mønster: shared/uploads linket inn i wp-content/uploads i hver release.
Staging, forhåndsvisning og strategimatrise
I 2026 er feature branch → PR → staging → main/produksjon ikke valgfritt for butikker og lead-sider. Staging bør ha:
- samme PHP-versjon og utvidelser som produksjon
- databasekopi (anonymisert når persondata er i spill under GDPR)
- betalinghemmeligheter kun i sandbox
Preview-URL per PR hjelper redesign og CSS mer enn skjemamigrasjoner - de trenger fortsatt staging med ekte dump.
| Strategi | Risiko | Vedlikehold | Når det passer |
|---|---|---|---|
| Manuell FTP | Svært høy | Billig start, dyre brudd | Bare sandbox / læring |
| Git-hook på server | Middels | Lav | Lite portfolio, én utvikler |
| CI + atomisk rsync | Lav | Middels | Bedrift, WooCommerce, byrå |
| Blue/green eller to VPS | Minimal | Høy | Høy trafikk, SLA, betalinger |
Blue/green (to fulle miljøer, load balancer-bytte) lønner sjelden under hundrevis av samtidige sesjoner. De fleste norske nettbutikker klarer seg med atomisk symlink pluss mappe-rollback - typisk på Hetzner, Linode eller managed med SSH. Vipps-sandbox i staging og ekte Vipps-nøkler bare i produksjons-Secrets er et mønster som sparer mange feilaktige testkjøp.
Hemmeligheter, WP-CLI og database
Aldri commit:
- SSH-nøkler
- databasepassord
AUTH_KEY/SECURE_AUTH_KEYfrawp-config.php- API-tokens for betalingsgateways (Vipps, Klarna, Stripe)
I GitHub: Settings → Secrets and variables → Actions. Injiser som env i deploy-steget. På serveren ligger wp-config.php under shared/ og er ikke del av CI-artefakten.
WP-CLI etter symlink-bytte:
wp cache flushved object cachewp rewrite flushetter CPT-endringerwp plugin listsom smoke-linje i loggen
Planlegg databasmigrasjoner adskilt fra fil-deploy. Symlink-rollback angrer ikke ALTER TABLE. Ved destruktive migrasjoner: backup først (wp db export på staging og produksjon), deretter migrasjon, deretter trafikk til kode som trenger skjemaet - eller feature-flag i PHP.
PHP-avhengigheter: Composer install - i CI alltid --no-dev for produksjon og låst composer.lock.
Hva som vanligvis knuser den første WordPress-pipelinen
Fra VPS-arbeid (Hetzner, DigitalOcean, OVH) og managed med SSH:
.gitignorespiservendor/- produksjon får tema uten autoloader. Enten bygg vendor på runneren og legg den i artefakten, eller kjør Composer på serveren etter rsync (da trenger serveren Composer og Packagist-tilgang).- Feil stier - scriptet antar
/var/www/html, hosten bruker/home/user/domains/.... Pin stier i Secrets per miljø. - Opcache holder gammel kode - etter
ln -sfn, reload PHP-FPM eller kallopcache_resetvia WP-CLI/mu-plugin bare for deploy-brukeren. - Cron og køer - WooCommerce Action Scheduler kan kjøre jobber på gammel kode i noen minutter; etter store releases, se litt på failed jobs.
Smoke-test etter deploy (curl fra runner eller egen jobb):
- forsiden HTTP 200
/wp-login.php200- én kritisk produkt- eller skjemalanding
- valgfri HEAD til CDN hvis assets er content-hashed
Hvis smoke feiler, er automatisk symlink-rollback billigere enn kundeoppringing kl. 23:00.
Blokk-temaer, must-use-plugins og Git-unntak
Ikke hele WordPress passer i samme pipeline. Innhold i databasen (innlegg, ACF-feltgrupper uten JSON-eksport, Elementor-CSS) lever fortsatt utenfor releasen. CI/CD beskytter kode; det erstatter ikke databasebackup.
Praktiske skiller:
- Klassisk eller hybrid-tema med Vite - fullt CI-bygg, artefakt med
style.css+dist/. - Blokk-tema (FSE) - hold
theme.jsonog HTML-maler i Git; endringer fra Site Editor tilbake via PR, ellers divergerer produksjon og Git etter første «Lagre». - Must-use-plugins - rulles ut med releasen; oppdater dem ikke fra admin. Bra sted for deploy-healthchecks og å slå av file editor.
- Plugins fra wordpress.org - pin versjoner via Composer (
wpackagist) eller oppdater i en gjennomgått jobb. Å blande «Update i admin» med «CI overskrevplugins/» gir et tilfeldig plugin-tre.
Byrå-stacks i Norden blander ofte custom-tema, WooCommerce fra wpackagist og to premium-zip via Composer path repository. Pipelinen må kjenne alle tre kilder, ellers skiller staging og produksjon seg med én zip fra for et halvt år siden.
Sjekkliste før første Actions-deploy
Før du kobler main til produksjon, gå gjennom listen én gang - den sparer en helg:
- Privat repo, eller bekreft historikk uten hemmeligheter (
git log -pviser ingen gamle.env). - Branch protection: obligatorisk PR, grønn
test-workflow. - Separate Secrets:
DEPLOY_HOST_STAGING,DEPLOY_HOST_PROD, egne SSH-nøkler. - Staging matcher produksjonens PHP (ikke «vi kjører 8.3, kunden har 8.1»).
- Databasebackup før første atomiske bytte - selv når kode-rollback er klar.
- Oppetidsovervåking (UptimeRobot, Better Stack e.l.) med SMS/Slack på produksjons-URL.
- Én-siders runbook: hvordan
ln -sfnmanuelt til forrige release, hvem har servertilgang.
Først da: auto-deploy fra main. Fram til da holder «bygg + test» på PR-er og manuell workflow_dispatch til staging.
Oppsummering
CI/CD for WordPress er ikke «magisk YAML». Det er en gjentakbar kontrakt: samme PHP, samme lockfile, samme artefakt, bytte uten vindu med halvveis opplastede filer. Start med Git og staging, legg til Actions for Composer/npm-bygg, avslutt med atomisk current og smoke-test. FTP blir nødverktøy, ikke prosess.
Trenger du en WordPress-utvikler som holder pipelinen sammen med temakoden for butikk eller lead-side, jobber WPPoland på ekte stack - ikke lysark.







