CI/CD dla WordPress: automatyzacja wdrożeń w 2026 roku

CI/CD dla WordPress: automatyzacja wdrożeń w 2026 roku

Ostatnio zweryfikowano: 15 października 2026
12 min czytania
Przewodnik
Full-stack developer

Ręczne przeciąganie motywów przez FTP w 2026 roku kończy się zwykle tak samo: brakuje jednego pliku z inc/, composer.lock jest z innego laptopa, a produkcja leży, bo ktoś nadpisał wp-config.php. Continuous Integration i Continuous Deployment nie są fanaberią enterprise - to sposób, by każda zmiana w Gicie przeszła ten sam build, te same testy i ten sam sposób wgrania na serwer.

Ten przewodnik dotyczy WordPressa z motywem lub wtyczką w repozytorium (Composer, npm, ewent. Docker), nie „strony klikanej wyłącznie w panelu”. Jeśli cały kod żyje tylko w bazie i w mediach, najpierw przenieś motywy i custom code do Gita - bez tego CI/CD nie ma czego budować.

Oficjalna dokumentacja runnerów: GitHub Actions. Twierdzenie WordPressa o hardeningu serwera warto czytać równolegle: Hardening WordPress.

#Dlaczego nie wgrywać WordPressa przez FTP

Trzy problemy wracają w audytach polskich sklepów WooCommerce i stron agencji:

  1. Niespójność środowiska - lokalnie PHP 8.2 i Composer 2.7, na VPS PHP 8.1 bez rozszerzenia intl. Lokalny composer install „działa”, produkcja pada na class not found.
  2. Brak audytu - po incydencie nie da się powiedzieć, który commit był na żywo. Panel hostingu pokazuje datę pliku, nie SHA z Gita.
  3. Sekrety na dysku - zapisane hasła SFTP w FileZilli albo w .env commitowanym „na chwilę” to klasyczna droga do wycieku.

CI/CD rozwiązuje to mechanicznie: build na znanym obrazie (np. shivammathur/setup-php albo własny Docker), artefakt wersjonowany SHA, deploy po SSH kluczem trzymanym w Secrets.

#Deploy WordPressa przez GitHub Actions krok po kroku

Typowy workflow dla motywu custom + wtyczek z Composer:

  1. Trigger - push do main albo merge Pull Requestu po review. Gałąź develop często idzie tylko na staging.
  2. Build - composer install --no-dev --optimize-autoloader, npm ci, npm run build. Wersje PHP i Node pinujesz w workflow (php-version: '8.3', node-version: '24').
  3. Test - PHPUnit dla wtyczek, ewent. Playwright na krytyczny flow koszyka. Fail = stop, zero deployu.
  4. Artefakt - pakujesz katalog release bez .git, bez node_modules, z vendor/ i zbudowanym assets/dist/.
  5. Deploy - rsync/SCP do releases/<run_id>/, potem atomowe przełączenie symlinku.

Pinuj wersje, które jeszcze dostają poprawki. PHP 8.2 ma poprawki bezpieczeństwa tylko do 31 grudnia 2026, PHP 8.3 do 31 grudnia 2027 (wspierane wersje PHP). Node.js 20 stracił wsparcie 30 kwietnia 2026; Node.js 22 jest wspierany do 30 kwietnia 2027, a Node.js 24 do 30 kwietnia 2028 (harmonogram wydań Node.js). Wersję minor PHP dobierasz do produkcji, nie do najnowszego wydania.

Fragment idei (skrócony) w GitHub Actions, z głównymi tagami dla czytelności; dlaczego produkcyjny workflow pinuje SHA commitów, wyjaśnia sekcja o zabezpieczeniu workflow niżej:

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: |
          # zapisz klucz, rsync do releases/$GITHUB_SHA, ln -sfn

Pełny skrypt symlinku trzymasz w repo jako bin/release.sh - w workflow tylko go wywołujesz. Dzięki temu ten sam skrypt działa z laptopa w awarii, gdy Actions leży.

#Jak zablokować dwa równoległe deploye WordPressa w GitHub Actions

Dwa szybkie merge’e do main uruchamiają dwa przebiegi workflow. Bez blokady oba robią rsync do releases/ i przełączają current w kolejności, której nikt nie wybrał. Grupa concurrency w GitHub Actions pozwala na najwyżej jeden działający przebieg w grupie, a nowo zakolejkowany czeka jako pending (dokumentacja GitHub: kontrola współbieżności).

Przy deployu liczą się dwa szczegóły. Domyślnie grupa trzyma jeden działający i jeden oczekujący przebieg, a nowszy anuluje starszy oczekujący, więc wygrywa ostatni commit, a ten ze środka jest pomijany. cancel-in-progress: true anulowałoby też działający job, co potrafi przerwać rsync w połowie katalogu release. Na produkcji zostaw false, a true ustawiaj w workflow testowych na Pull Requestach, gdzie liczy się tylko najnowszy commit.

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

Symlink przełącza się dopiero po pełnym rsync, więc anulowany przebieg oczekujący nigdy nie dotyka current.

#Ochrona produkcji przez środowiska GitHub Actions

Dodaj environment: production do joba deploy i skonfiguruj to środowisko w ustawieniach repozytorium. Działają wtedy trzy zabezpieczenia (dokumentacja GitHub: deploymenty i środowiska):

  • Wymagani recenzenci: do sześciu osób lub zespołów, a jedna akceptacja zwalnia job. Z włączoną blokadą samoakceptacji osoba, która uruchomiła deploy, nie może go zatwierdzić.
  • Gałęzie i tagi deployu: tylko main albo tagi wydań mogą wdrażać na production, więc workflow z gałęzi feature nie dosięgnie serwera produkcyjnego.
  • Sekrety środowiska: dostępne tylko dla jobów, które wskazują środowisko, a przy wymaganych recenzentach dopiero po akceptacji. Produkcyjny DEPLOY_SSH_KEY trzymasz tutaj, nie w sekretach repozytorium.

Sprawdź plan, zanim na tym oprzesz proces. W GitHub Free, Pro i Team wymagani recenzenci i wait timer działają tylko w repozytoriach publicznych. Gałęzie deployu i sekrety środowiska działają w repozytoriach prywatnych na Pro i Team, ale nie na Free. Repozytoria agencji WordPress są zwykle prywatne, więc na tych planach bramki akceptacji nie ma i ochronę niosą reguły gałęzi.

#Jak zabezpieczyć workflow deployu w GitHub Actions

Job deployu trzyma klucz SSH do produkcji, więc plik workflow zasługuje na takie samo review jak kod PHP. Wytyczne GitHub o bezpiecznym użyciu Actions wymieniają środki, które mają tu znaczenie:

  • Pinuj zewnętrzne akcje do pełnego SHA commita. Tag typu @v2 da się przesunąć albo usunąć. Przepięcie SHA wymagałoby kolizji SHA-1. SHA bierzesz z repozytorium samej akcji, nie z forka. GitHub wskazuje dziś plik blokady zależności workflow jako mocniejszą opcję dla repozytoriów, które go wdrożą.
  • Daj GITHUB_TOKEN minimum. Ustaw permissions: contents: read na górze i podnoś uprawnienia per job tylko tam, gdzie krok tego wymaga. Deploy przez rsync i SSH potrzebuje wyłącznie odczytu repozytorium.
  • Nie wstawiaj niezaufanych danych prosto do run:. Tytuł Pull Requestu albo nazwa gałęzi wewnątrz ${{ }} w skrypcie shell to droga do wstrzyknięcia kodu. Przekaż wartość przez pośrednią zmienną środowiskową.
  • Jeden sekret na jedną wartość. Dane strukturalne, np. blok JSON, potrafią zepsuć maskowanie w logach, więc host, użytkownik, ścieżka i klucz to osobne sekrety.

Ta sama strona zaznacza, że maskowanie obejmuje tylko sekrety użyte w jobie i dostępne dla runnera, więc nigdy nie rób echo wartości pochodnych, np. zdekodowanego klucza.

Nadpisywanie plików „w miejscu” na public_html oznacza, że przez kilka sekund połowa requestów widzi stary functions.php i nowy template. Przy WooCommerce to potrafi zepsuć sesję koszyka.

Model atomowy:

  • katalog bazowy: /var/www/site/
  • releases: /var/www/site/releases/20260920-142211/
  • shared: uploads, wp-config.php, cache object - poza release
  • current: symlink do aktywnego release

Po udanym rsync:

ln -sfn /var/www/site/releases/20260920-142211 /var/www/site/current

Nginx root wskazuje na .../current/public (albo odpowiednik). Przełączenie jest natychmiastowe. Stare release kasujesz po N dniach albo zostawiasz ostatnie trzy na rollback.

WordPress uploads muszą być poza release - inaczej każdy deploy kasuje media albo wymaga kopiowania gigabajtów. Klasyczny układ: shared/uploads linkowany do wp-content/uploads w każdym release.

#Staging WordPress i porównanie strategii wdrożeń

W 2026 roku ścieżka od gałęzi feature przez Pull Request i staging do main i produkcji nie jest opcjonalna dla sklepów i stron leadowych. Staging powinien mieć:

  • tę samą wersję PHP i rozszerzeń co produkcja
  • kopię bazy (anonimizowaną, jeśli są dane osobowe RODO)
  • te same sekrety płatności w trybie sandbox

Preview per PR (tymczasowy URL) ma sens przy redesignie i przy zmianach CSS, mniej przy migracjach schematu bazy - te i tak testujesz na stagingu z prawdziwą kopią.

StrategiaRyzykoKoszt utrzymaniaKiedy ma sens
Ręczne FTPBardzo wysokieNiski start, wysoki koszt awariiTylko sandbox / nauka
Git hook na serwerzeŚrednieNiskiMałe portfolio, jeden deweloper
CI + rsync atomowyNiskieŚredniBiznes, WooCommerce, agencje
Blue/green lub dwa VPSMinimalneWysokiDuży ruch, SLA, płatności

Blue/green (dwa pełne środowiska, przełączenie load balancera) rzadko opłaca się poniżej kilkuset równoległych sesji. Dla większości polskich sklepów wystarcza atomowy symlink + rollback katalogu.

#Testy automatyczne w potoku CI/CD WordPressa

Potok bez testów tylko szybciej wysyła zepsuty kod. W repozytorium WordPress sprawdzają się cztery warstwy, od najszybszej do najwolniejszej:

  • Testy jednostkowe w PHPUnit sprawdzają pojedyncze funkcje wtyczki lub motywu bez bazy danych. Biegną przy każdym pushu.
  • Testy integracyjne ładują WordPressa z tymczasową bazą testową. wp scaffold plugin-tests generuje szkielet: phpunit.xml.dist, bin/install-wp-tests.sh do instalacji pakietu testów i bazy, tests/bootstrap.php, przykładowy test i zestaw reguł .phpcs.xml.dist. Domyślny plik CI jest dla CircleCI; --ci=github zapisuje zamiast niego konfigurację GitHub Actions (WP-CLI: wp scaffold plugin-tests).
  • Analiza statyczna w PHPStan i PHP_CodeSniffer łapie błędy typów i naruszenia standardu kodowania bez uruchamiania kodu.
  • Testy end-to-end w Playwright przechodzą realne ścieżki użytkownika: formularz kontaktowy, koszyk i checkout WooCommerce. To najwolniejsza warstwa, więc uruchamiasz ją na Pull Requestach do main i na stagingu, nie przy każdym commicie.

Czerwony job w dowolnej warstwie zatrzymuje workflow przed krokiem artefaktu, więc nic nie trafia na serwer.

#Sekrety i migracje bazy przy wdrożeniu WordPress

Nigdy nie commituj:

  • kluczy SSH
  • haseł bazy
  • AUTH_KEY / SECURE_AUTH_KEY z wp-config.php
  • tokenów API bramek płatności

W GitHubie leżą w Settings, dalej Secrets and variables, dalej Actions. Wstrzykujesz je jako env w kroku deploy. Na serwerze wp-config.php leży w shared/ i nie jest częścią artefaktu z CI.

WP-CLI w potoku po przełączeniu symlinku:

  • wp cache flush (jeśli object cache)
  • wp rewrite flush po zmianie CPT
  • wp plugin list jako smoke w logu

#Jak uruchomić WP-CLI na serwerze produkcyjnym z runnera

Te komendy muszą działać na serwerze, na nowym release. WP-CLI robi to z runnera przez globalny parametr --ssh ([<scheme>:][<user>@]<host>[:<port>][<path>]) albo przez aliasy w projektowym wp-cli.yml (podręcznik WP-CLI: zdalne uruchamianie komend):

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

Z tym plikiem w repozytorium wp @prod cache flush wykonuje się na produkcji. WP-CLI nadal musi być zainstalowane na serwerze i dostępne jako wp: runner tylko przekazuje komendę przez SSH.

Kolejne komendy warte kroku po przełączeniu, wszystkie w dokumentacji komend WP-CLI:

  • wp core is-installed nic nie wypisuje i odpowiada kodem wyjścia, więc nieudane sprawdzenie wywraca krok.
  • wp core update-db --dry-run porównuje wersje bazy przed właściwym wp core update-db po aktualizacji rdzenia.
  • wp core verify-checksums porównuje pliki rdzenia z sumami kontrolnymi WordPress.org i wyłapuje pliki zmienione poza Gitem.
  • wp maintenance-mode activate i deactivate obsługują rzadki release, który musi zmienić schemat bazy, gdy nic do niej nie zapisuje.

Migracje bazy (custom tabele, ACF JSON już w repo) planuj osobno od deployu plików. Rollback symlinku nie cofa ALTER TABLE. Jeśli migracja jest destrukcyjna, najpierw backup (wp db export na stagingu i produkcji), potem migracja, potem dopiero przełączenie ruchu na kod, który jej wymaga - albo feature flag w PHP.

Dokumentacja instalacji zależności PHP: Composer install - w CI zawsze --no-dev na produkcji i zablokowany composer.lock.

#Najczęstsze błędy w potoku CI/CD WordPress

Z praktyki wdrożeń na VPS (Hetzner, DigitalOcean, własne OVH) i na managed z SSH:

  • .gitignore zjada vendor/ - produkcja dostaje motyw bez autoloadera. Albo budujesz vendor na runnerze i wrzucasz do artefaktu, albo uruchamiasz Composer na serwerze w kroku po rsync (wtedy serwer musi mieć Composer i sieć do Packagist).
  • różne ABSPATH / ścieżki - skrypt zakłada /var/www/html, a hosting ma /home/user/domains/.... Pinuj ścieżki w Secrets per środowisko.
  • opcache trzyma stary kod - po ln -sfn wywołaj reload PHP-FPM albo opcache_reset przez WP-CLI/mu-plugin tylko dla deploy usera.
  • cron i kolejki - Action Scheduler WooCommerce może odpalać joby na starym kodzie przez kilka minut; po dużym release warto chwilę obserwować failed jobs.

Smoke test po deployu (curl z runnera albo osobny job):

  • strona główna HTTP 200
  • /wp-login.php 200
  • jeden krytyczny URL produktu lub landing z formularzem
  • opcjonalnie HEAD do CDN, jeśli assety mają hash w nazwie

Jeśli smoke padnie, automatyczny rollback symlinku jest tańszy niż telefon od klienta o 23:00.

#Motywy blokowe i mu-pluginy w Gicie

Nie cały WordPress nadaje się do tego samego potoku. Treści w bazie (wpisy, ACF field groups jeśli nie eksportujesz JSON, Elementor CSS) i tak żyją poza release. CI/CD chroni kod, nie zastępuje backupu bazy.

Praktyczne podziały:

  • Motyw klasyczny lub hybrydowy z Vite - pełny build w CI, artefakt z style.css + dist/.
  • Block theme (FSE) - trzymasz theme.json i szablony HTML w Gicie; eksport zmian z Site Editora wraca do repo przez PR, inaczej produkcja i Git rozjeżdżają się po pierwszym kliknięciu „Zapisz”.
  • Must-use pluginy - wgrywane razem z release; nie aktualizujesz ich z panelu. To dobre miejsce na healthcheck deployu i wyłączenie file editora.
  • Wtyczki z wordpress.org - albo pinujesz wersje w Composer (wpackagist), albo aktualizujesz je osobnym jobem z review. Mieszanie „panel kliknął Update” z „CI nadpisał katalog” kończy się losowym stanem plugins/.

Na stronach agencji w PL często widzę hybrydę: custom motyw + WooCommerce z wpackagist + dwie wtyczki premium wgrane jako zip do private/ i instalowane Composerem z path repository. Potok musi znać wszystkie trzy źródła, inaczej staging i produkcja różnią się jednym ZIP-em sprzed pół roku.

#Checklista przed wdrożeniem WordPressa z GitHub Actions

Zanim podłączysz main do produkcji, przejdź tę listę raz - oszczędza weekend:

  1. Repozytorium prywatne albo bez sekretów w historii (git log -p nie pokazuje starych .env).
  2. Branch protection: wymagany PR, wymagany zielony workflow test.
  3. Osobne Secrets: DEPLOY_HOST_STAGING, DEPLOY_HOST_PROD, osobne klucze SSH.
  4. Staging zsynchronizowany z produkcją co do wersji PHP (nie „u nas działa 8.3, u klienta 8.1”).
  5. Backup bazy przed pierwszym atomowym przełączeniem - nawet jeśli rollback kodu jest gotowy.
  6. Monitoring uptime (UptimeRobot, Better Stack, cokolwiek) z alertem SMS/Slack na URL produkcyjny.
  7. Runbook na jednej stronie: jak ręcznie ln -sfn na poprzedni release, kto ma dostęp do serwera.

Dopiero potem włączasz auto-deploy z main. Wcześniej wystarczy workflow „build + test” na PR i ręczny workflow_dispatch na staging.

#Podsumowanie

CI/CD dla WordPressa to nie „magiczny YAML”, tylko powtarzalny kontrakt: ten sam PHP, ten sam lockfile, ten sam artefakt, przełączenie bez okna z połową plików. Zaczynasz od Gita i stagingu, dokładasz Actions z buildem Composer/npm, kończysz atomowym current i smoke testem. FTP zostaje narzędziem awaryjnym, nie procesem.

Jeśli budujesz taki potok pod sklep lub stronę leadową i potrzebujesz kogoś, kto utrzyma go razem z kodem motywu, napisz przez formularz kontaktowy - opisuję zakres wdrożenia i utrzymania CI/CD przy realnym stacku WordPress.

Następny krok

Przekuj artykuł w realne wdrożenie

Pod tym wpisem dokładam linki, które domykają intencję użytkownika i prowadzą dalej w strukturze serwisu.

Chcesz wdrożyć ten temat na swojej stronie?

Jeśli chcesz przełożyć wiedzę z artykułu na działającą stronę, sklep albo przebudowę serwisu, przygotuję konkretny zakres prac.

Sprawdź inne usługi WordPress i bazę wiedzy

Wzmocnij swój biznes dzięki profesjonalnemu wsparciu technicznemu w kluczowych obszarach ekosystemu WordPress.

Często zadawane pytania

Najważniejsze odpowiedzi, które pomagają wdrożyć temat w praktyce.

Czy CI/CD ma sens dla jednego freelancera?#
Tak. Samotny deweloper najbardziej traci na zapomnianym pliku PHP albo na composer.lock zbudowanym lokalnie na innej wersji PHP. Potok wymusza ten sam build na każdym commitcie i zostawia audytowalny log, kto (i z którego SHA) wdrożył produkcję.
Co to jest wdrożenie zero-downtime w WordPressie?#
Nowa wersja kodu ląduje w katalogu releases/TIMESTAMP, a dopiero po udanym rsync i ewentualnym wp cache flush przełączasz symlink current. PHP-FPM czy Nginx serwują ścieżkę przez current, więc przełączenie trwa milisekundy i nie wymaga trybu konserwacji.
Czy potrzebuję VPS, żeby mieć CI/CD?#
VPS lub kontenery dają pełną kontrolę (SSH, symlinki, WP-CLI). Na hostingu zarządzanym często zostaje API deployu albo SFTP z kluczem w Secrets - nadal lepsze niż ręczne przeciąganie z FileZilli, choć bez prawdziwego blue/green.
Co robić, gdy deploy się wywali?#
Trzymasz N poprzednich katalogów releases. Jeśli smoke test po przełączeniu zwraca 500 albo brakuje vendor/, przestawiasz symlink z powrotem na poprzedni release. Migracje bazy planuj osobno - rollback kodu nie cofa ALTER TABLE.
Gdzie budować motywy z Vite albo webpackiem?#
Na runnerze CI: checkout, setup Node, npm ci, npm run build, potem pakujesz dist/ do artefaktu razem z PHP. Na serwer produkcyjny nie wgrywasz node_modules ani źródeł TS - tylko skompilowane CSS/JS.

Potrzebujesz FAQ dopasowanego do branży i rynku? Przygotuję wersję pod Twoje cele biznesowe.

Porozmawiajmy

Polecane artykuły