CI/CD for WordPress: automatiser dine utrullinger i 2026

CI/CD for WordPress: automatiser dine utrullinger i 2026

Sist verifisert: 15. oktober 2026
12 min lesetid
Guide
Full-stack-utvikler

Å 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 du ikke bør deploye WordPress via FTP

Tre problemer dukker opp igjen i norske nettbutikker og byråoverleveringer:

  1. Miljødrift - lokalt PHP 8.2 og Composer 2.7, VPS på PHP 8.1 uten intl. Lokal composer install «fungerer»; produksjon dør på class not found.
  2. Ingen revisjonsspor - etter en hendelse kan du ikke si hvilken commit som var live. Hostingpanelet viser en fildato, ikke en Git-SHA.
  3. Hemmeligheter på disk - SFTP-passord i FileZilla, eller en .env committed «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.

#Deploy WordPress med GitHub Actions

Typisk arbeidsflyt for custom-tema pluss Composer-plugins:

  1. Trigger - push til main eller merge av gjennomgått pull request. develop går ofte bare til staging.
  2. Bygg - composer install --no-dev --optimize-autoloader, npm ci, npm run build. Pin PHP og Node i workflow (php-version: '8.3', node-version: '24').
  3. Test - PHPUnit for plugins, eventuelt Playwright på checkout. Fail stopper jobben; ingen deploy.
  4. Artefakt - pakk release uten .git, uten node_modules, med vendor/ og bygd assets/dist/.
  5. Deploy - rsync/SCP til releases/<run_id>/, deretter atomisk symlink-bytte.

Lås versjoner som fortsatt får rettelser. PHP 8.2 får sikkerhetsrettelser bare til 31. desember 2026, PHP 8.3 til 31. desember 2027 (støttede PHP-versjoner). Node.js 20 nådde slutten av levetiden 30. april 2026; Node.js 22 støttes til 30. april 2027 og Node.js 24 til 30. april 2028 (utgivelsesplanen for Node.js). PHP-minorversjonen følger produksjon, ikke nyeste utgivelse.

Kort skisse i GitHub Actions, med hovedversjons-tagger for lesbarhet; hvorfor produksjons-workflows låser commit-SHA, forklarer avsnittet om herding lenger ned:

name: deploy-production
on:
  push:
    branches: [main]
permissions:
  contents: read
concurrency:
  group: deploy-production
  cancel-in-progress: false
jobs:
  build-and-deploy:
    runs-on: ubuntu-latest
    environment: production
    steps:
      - uses: actions/checkout@v7
      - uses: shivammathur/setup-php@v2
        with:
          php-version: '8.3'
      - run: composer install --no-dev --optimize-autoloader
      - uses: actions/setup-node@v7
        with:
          node-version: '24'
          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 -sfn

Full symlink-script ligger i repoet som bin/release.sh - workflowen kaller bare. Samme script fungerer fra laptop når Actions er nede.

#Stopp to samtidige WordPress-deploys i GitHub Actions

To raske merges til main starter to workflow-kjøringer. Uten sperre kjører begge rsync til releases/ og bytter current i en rekkefølge ingen har valgt. En concurrency-gruppe i GitHub Actions tillater høyst én kjørende workflow per gruppe; en nylig køet kjøring venter som pending (GitHub-dokumentasjon: styre samtidighet).

To detaljer teller ved deploy. Som standard holder en gruppe én kjørende og én ventende kjøring, og en nyere kjøring avbryter den eldre ventende, så siste commit vinner og den i midten hoppes over. cancel-in-progress: true ville også avbrutt den kjørende jobben, og det kan stoppe rsync midt i en release-mappe. Behold false for produksjon, og bruk true i test-workflows på pull requests, der bare nyeste commit teller.

concurrency:
  group: deploy-production
  cancel-in-progress: false

Siden symlinken byttes først etter fullført rsync, rører en avbrutt ventende kjøring aldri current.

#Beskytt produksjon med miljøer i GitHub Actions

Legg environment: production på deploy-jobben og konfigurer miljøet i repository-innstillingene. Da gjelder tre beskyttelser (GitHub-dokumentasjon: deployments og miljøer):

  • Påkrevde reviewers: opptil seks brukere eller team, og én godkjenning slipper jobben videre. Med sperre mot selvgodkjenning kan den som startet deployen ikke godkjenne den.
  • Deploy-brancher og -tagger: bare main eller release-tagger kan deploye til production, så en workflow på en feature-branch når ikke produksjonsserveren.
  • Miljøhemmeligheter: tilgjengelige bare for jobber som refererer til miljøet, og med påkrevde reviewers først etter godkjenning. Produksjonens DEPLOY_SSH_KEY hører hjemme her, ikke blant repository-hemmelighetene.

Sjekk planen først. På GitHub Free, Pro og Team virker påkrevde reviewers og ventetid bare i offentlige repositories. Deploy-brancher og miljøhemmeligheter virker i private repositories på Pro og Team, men ikke på Free. Byråers WordPress-repositories er som regel private, så på disse planene finnes ikke godkjenningsporten, og branch-reglene bærer beskyttelsen.

#Herd deploy-workflowen i GitHub Actions

Deploy-jobben har en SSH-nøkkel til produksjon, så workflow-filen fortjener samme gjennomgang som PHP-kode. GitHubs referanse for sikker bruk lister tiltakene som teller her:

  • Lås tredjeparts actions til full commit-SHA. En tagg som @v2 kan flyttes eller slettes. Å peke en SHA om ville kreve en SHA-1-kollisjon. Hent SHA fra actionens eget repository, ikke fra en fork. GitHub nevner nå en låsefil for workflow-avhengigheter som det sterkere alternativet for repositories som tar den i bruk.
  • Gi GITHUB_TOKEN minimum. Sett permissions: contents: read øverst og øk per jobb bare der et steg trenger mer. En rsync-deploy over SSH trenger bare lesetilgang til repositoryet.
  • Hold upålitelig input unna run:. En pull request-tittel eller et branch-navn inne i ${{ }} i et shell-script åpner for skriptinjeksjon. Send verdien gjennom en mellomliggende miljøvariabel.
  • Én hemmelighet per verdi. Strukturerte data som en JSON-blokk kan bryte maskeringen i loggene, så lagre vert, bruker, sti og nøkkel som separate hemmeligheter.

Samme side påpeker at maskering bare gjelder hemmeligheter som brukes i jobben og er tilgjengelige for runneren, så aldri echo avledede verdier som en dekodet nøkkel.

Å 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/current

Nginx 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.

#WordPress staging og deploy-strategier sammenlignet

I 2026 er veien fra feature-branch via pull request og staging til main og produksjon ikke valgfri 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.

StrategiRisikoVedlikeholdNår det passer
Manuell FTPSvært høyBillig start, dyre bruddBare sandbox / læring
Git-hook på serverMiddelsLavLite portfolio, én utvikler
CI + atomisk rsyncLavMiddelsBedrift, WooCommerce, byrå
Blue/green eller to VPSMinimalHøyHø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.

#Automatiserte tester i en CI/CD-pipeline for WordPress

En pipeline uten tester sender bare ødelagt kode ut raskere. Fire lag passer til et WordPress-repository, fra raskest til tregest:

  • Enhetstester med PHPUnit sjekker enkeltfunksjoner i en utvidelse eller et tema uten database. De kjører ved hver push.
  • Integrasjonstester laster WordPress mot en midlertidig testdatabase. wp scaffold plugin-tests genererer grunnmuren: phpunit.xml.dist, bin/install-wp-tests.sh for testpakken og databasen, tests/bootstrap.php, en eksempeltest og et regelsett .phpcs.xml.dist. Standard CI-fil er for CircleCI; --ci=github skriver i stedet en konfigurasjon for GitHub Actions (WP-CLI: wp scaffold plugin-tests).
  • Statisk analyse med PHPStan og PHP_CodeSniffer fanger typefeil og brudd på kodestandarden uten å kjøre koden.
  • Ende-til-ende-tester med Playwright går gjennom ekte brukerflyter: kontaktskjema, WooCommerce-handlekurv og kasse. De er det tregeste laget, så kjør dem på pull requests mot main og mot staging, ikke ved hver commit.

En rød jobb i hvilket som helst lag stopper workflowen før artefakt-steget, så ingenting når serveren.

#Hvordan håndtere hemmeligheter og databasemigrering ved WordPress-deploy

Aldri commit:

  • SSH-nøkler
  • databasepassord
  • AUTH_KEY / SECURE_AUTH_KEY fra wp-config.php
  • API-tokens for betalingsgateways (Vipps, Klarna, Stripe)

I GitHub ligger de under Settings, deretter Secrets and variables, deretter 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 flush ved object cache
  • wp rewrite flush etter CPT-endringer
  • wp plugin list som smoke-linje i loggen

#Kjør WP-CLI på produksjonsserveren fra runneren

Disse kommandoene må kjøre på serveren, mot den nye releasen. WP-CLI gjør det fra runneren med den globale parameteren --ssh ([<scheme>:][<user>@]<host>[:<port>][<path>]) eller med aliaser i prosjektets wp-cli.yml (WP-CLI-håndboken: kjøre kommandoer eksternt):

@staging:
  ssh: [email protected]/var/www/site/current/public
@prod:
  ssh: [email protected]/var/www/site/current/public

Med den filen i repositoryet kjører wp @prod cache flush på produksjon. WP-CLI må fortsatt være installert på serveren og kunne kalles som wp: runneren sender bare kommandoen videre over SSH.

Flere kommandoer verdt å legge i steget etter byttet, alle i WP-CLI-kommandoreferansen:

  • wp core is-installed skriver ingenting og svarer med exit-koden, så en feilet sjekk feiler steget.
  • wp core update-db --dry-run sammenligner databaseversjonene før den ekte wp core update-db etter en kjerneoppdatering.
  • wp core verify-checksums sammenligner kjernefilene med sjekksummene fra WordPress.org og fanger filer endret utenfor Git.
  • wp maintenance-mode activate og deactivate dekker den sjeldne releasen som må endre skjemaet mens ingenting skriver til det.

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.

#Vanlige feil i en WordPress-pipeline

Fra VPS-arbeid (Hetzner, DigitalOcean, OVH) og managed med SSH:

  • .gitignore spiser vendor/ - 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 kall opcache_reset via 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.php 200
  • é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.

#Deploy av blokk-temaer og must-use-plugins med Git

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.json og 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 overskrev plugins/» 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 for CI/CD og kontinuerlig deploy av WordPress

Før du kobler main til produksjon, gå gjennom listen én gang - den sparer en helg:

  1. Privat repo, eller bekreft historikk uten hemmeligheter (git log -p viser ingen gamle .env).
  2. Branch protection: obligatorisk PR, grønn test-workflow.
  3. Separate Secrets: DEPLOY_HOST_STAGING, DEPLOY_HOST_PROD, egne SSH-nøkler.
  4. Staging matcher produksjonens PHP (ikke «vi kjører 8.3, kunden har 8.1»).
  5. Databasebackup før første atomiske bytte - selv når kode-rollback er klar.
  6. Oppetidsovervåking (UptimeRobot, Better Stack e.l.) med SMS/Slack på produksjons-URL.
  7. Én-siders runbook: hvordan ln -sfn manuelt 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.

Neste steg

Gjør artikkelen om til faktisk implementering

Denne blokken styrker intern lenking og sender leseren videre til de mest relevante tjenestene og innholdet.

Vil du få dette implementert på nettstedet ditt?

Hvis du vil gjøre kunnskapen i artikkelen om til konkrete forbedringer, redesign eller en tydelig leveranseplan, kan jeg ta det videre.

Utforsk andre WordPress-tjenester og kunnskapsbase

Styrk virksomheten din med profesjonell teknisk støtte innen kjerneområdene i WordPress-økosystemet.

Ofte stilte spørsmål

Praktiske svar for å bruke temaet i faktisk arbeid.

Gir CI/CD mening for en solo-frilanser?#
Ja. Aleneutviklere taper mest på en glemt PHP-fil eller et composer.lock bygget på en annen PHP-versjon. Pipelinen tvinger samme bygg på hver commit og etterlater en reviderbar logg over hvilken SHA som gikk live.
Hva er zero-downtime-deployment i WordPress?#
Ny kode lander i releases/TIMESTAMP. Først etter vellykket rsync og eventuelt wp cache flush bytter du symlinken current. PHP-FPM eller Nginx serverer via current, så byttet tar millisekunder og trenger ikke vedlikeholdsmodus.
Trenger jeg VPS for CI/CD?#
VPS eller containere gir full kontroll (SSH, symlinker, WP-CLI). På managed hosting står ofte en deploy-API eller SFTP med nøkkel i Secrets igjen - fortsatt bedre enn manuell FileZilla, selv uten ekte blue/green.
Hva gjør jeg hvis deployen feiler?#
Behold N tidligere release-mapper. Hvis smoke-testen returnerer 500 eller vendor/ mangler, pek symlinken tilbake på forrige release. Planlegg databasmigrasjoner separat - kode-rollback angre ikke ALTER TABLE.
Hvor skal temaer med Vite eller webpack bygges?#
På CI-runneren: checkout, sett opp Node, npm ci, npm run build, pakk deretter dist/ inn i artefakten sammen med PHP. Send ikke node_modules eller TypeScript-kilder til produksjon - bare kompilert CSS/JS.

Trenger du FAQ tilpasset bransje og marked? Jeg lager en versjon som støtter dine forretningsmål.

Ta kontakt

Relaterte artikler