Skip to content

Deployment

This guide covers how code moves from a feature branch to production on Kinsta, including the CI/CD pipeline, environment mapping, and the push workflow.

Environments

Environment URL Branch Trigger
Production naluma.app main Push to main (merge PR)
Staging Kinsta staging subdomain staging Push to staging
Local localhost:8080 Feature branches Manual (docker compose up)

Kinsta runs Nginx (no .htaccess), manages server-side caching, and provides a CDN with automatic WebP conversion via Cloudflare Polish.

Branch-to-environment flow

All work follows the same pattern: branch from main, open a PR, squash merge.

  1. Create a feature branch from main:

    git checkout main && git pull
    git checkout -b feature/my-change
    
  2. Commit locally using conventional commits (feat:, fix:, chore:, docs:). Do not push after every commit -- each push triggers CI.

  3. When ready, push and open a PR targeting main:

    git -c http.postBuffer=524288000 push -u origin feature/my-change
    
  4. CI runs the quality gate automatically (see below).

  5. Squash merge the PR. The repository is configured for squash-only merges with the PR title as the commit message. The branch is auto-deleted after merge.

  6. Merging to main triggers the production deployment pipeline.

Always use the extended push command

The repository includes a large packages/ directory required by Kinsta. The default Git HTTP buffer is too small, so every push must use:

git -c http.postBuffer=524288000 push

Without this flag, pushes may fail silently or time out.

CI/CD pipelines

Four GitHub Actions workflows handle quality checks and deployment.

php.yml -- PR quality gate

Runs on every pull request targeting main. This is the gatekeeper -- PRs cannot merge until it passes.

Quality job steps:

  • Pint formatting check (composer lint)
  • WordPress Coding Standards (composer lint:phpcs)
  • PHPStan static analysis (composer analyse)
  • ESLint for JavaScript (npm run lint:js)
  • Stylelint for SCSS (npm run lint:scss)
  • CSS build (npm run build) with a git diff --exit-code check to verify compiled CSS is committed
  • Translation string check (scripts/check-translations.sh)

PHPUnit job steps:

  • Spins up a MariaDB 10.6 service container
  • Runs unit tests (composer test:unit)
  • Runs integration tests against the test database

deploy.yml -- reusable deployment workflow

A workflow_call workflow that both staging and production pipelines invoke. It accepts an environment input and runs two jobs:

  1. Lint & Analyse -- identical to the quality gate (Pint, phpcs, PHPStan).

  2. Deploy -- builds production assets and rsyncs to Kinsta:

    • Installs production Composer dependencies (--no-dev --optimize-autoloader)
    • Builds the design system CSS (npm run build)
    • Connects to Kinsta via SSH
    • Rsyncs the project, excluding development-only files (.git/, .github/, docs/, node_modules/, SCSS source, config files, etc.)

The deploy job uses GitHub Environment secrets scoped per environment: KINSTA_SSH_PRIVATE_KEY, KINSTA_SSH_HOST, KINSTA_SSH_PORT, KINSTA_SSH_USER, KINSTA_REMOTE_DIR.

Concurrency is enforced per environment -- a new deploy cancels any in-progress deploy for the same target:

concurrency:
  group: deploy-${{ inputs.environment }}
  cancel-in-progress: true

cd-production.yml -- production deploy

Triggers on push to main and calls deploy.yml with environment: production:

on:
  push:
    branches:
      - main

jobs:
  deploy:
    uses: ./.github/workflows/deploy.yml
    with:
      environment: production
    secrets: inherit

cd-staging.yml -- staging deploy

Identical structure, triggers on push to staging with environment: staging.

What gets deployed

The rsync step deploys the full project minus development artifacts. Key exclusions:

  • .git/, .github/, .claude/ -- version control and CI config
  • .env, .env.* -- environment secrets (managed on Kinsta directly)
  • docs/, openspec/, scripts/ -- documentation and tooling
  • node_modules/, package.json, package-lock.json -- frontend build tooling
  • web/app/themes/naluma-theme/assets/scss/ -- SCSS source (compiled CSS is deployed)
  • web/app/uploads/, web/app/cache/, web/app/languages/ -- runtime directories managed by Kinsta

Compiled CSS must be committed

The CI quality gate verifies that style.css is up to date with the SCSS source. Always run npm run build locally before committing if you change any SCSS files.

Troubleshooting

Push fails or times out. Ensure you are using git -c http.postBuffer=524288000 push. This is required for every push, not just the first one.

CI fails on CSS diff. Run npm run build locally, commit the updated style.css, and push again.

Deploy succeeds but changes are not visible. Kinsta caches aggressively. Clear the site cache from the Kinsta dashboard or wait for the CDN TTL to expire.