GitHub Actions can be as small as a PHPCS check or as important as the pipeline that builds and deploys production images. For Drupal, PHP, Node, and Docker-based projects, a good workflow set should answer five questions on every change:
- Does the code meet project standards?
- Did we introduce a security risk?
- Can the application build?
- Does the running site still behave and perform well?
- Is deployment controlled, repeatable, and auditable?

1. Code Quality Workflow
For Drupal projects, the first workflow is usually code quality. It should run on pull requests before code reaches main or develop.
Common checks:
- PHPCS: Drupal coding standards.
- PHPStan: static analysis for PHP mistakes.
- Composer validate: dependency metadata sanity.
- Twig lint: template syntax if your project uses Twig-heavy custom themes.
- ESLint/Stylelint: frontend code quality if custom JavaScript/CSS is present.
A focused Drupal workflow might look like this:
name: Drupal Code Quality
on:
pull_request:
branches: [main, develop]
permissions:
contents: read
jobs:
code-quality:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: shivammathur/setup-php@v2
with:
php-version: '8.3'
extensions: mbstring, intl, pdo, pdo_mysql
- run: composer install --prefer-dist --no-progress --no-interaction --dev
- run: composer validate --strict
- run: vendor/bin/phpcs --standard=phpcs.xml web/modules/custom web/themes/custom
- run: vendor/bin/phpstan analyse web/modules/custom web/themes/custom
Keep this workflow fast. Developers should get feedback within minutes, not after a full deployment pipeline finishes.
2. Unit And Kernel Test Workflow
Static checks catch many issues, but tests prove behavior. In Drupal, this can mean unit tests, kernel tests, browser tests, or a small smoke-test suite.
Good candidates:
- Custom services.
- Custom Drush commands.
- Access checks.
- Form validation.
- Migration process plugins.
- API normalization logic.
Do not start by trying to test the entire site. Start with the custom code that would hurt most if it silently broke.
3. Code Scanning Workflow
Code scanning looks for security issues and unsafe patterns. GitHub CodeQL is the common GitHub-native option. It works best for supported languages and should run on pull requests and on a scheduled cadence.
name: CodeQL
on:
pull_request:
branches: [main, develop]
push:
branches: [main]
schedule:
- cron: '30 3 * * 1'
permissions:
contents: read
security-events: write
jobs:
analyze:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: github/codeql-action/init@v3
with:
languages: javascript-typescript
- uses: github/codeql-action/analyze@v3
For PHP-heavy Drupal projects, pair CodeQL with PHPStan, PHPCS security rules, dependency scanning, and code review. Code scanning is not a substitute for Drupal access-control thinking.
4. Dependency Review And Vulnerability Checks
Dependency changes deserve their own attention. A pull request that changes composer.lock, package-lock.json, or Docker base images can change the risk profile even if application code is untouched.
Useful checks:
- GitHub Dependabot alerts.
- Dependency Review Action on pull requests.
composer audit.npm auditwhere appropriate.- Container image vulnerability scanning with tools such as Trivy or Grype.
name: Dependency Review
on:
pull_request:
permissions:
contents: read
pull-requests: read
jobs:
dependency-review:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/dependency-review-action@v4
For Drupal, also pay attention to contrib module security advisories. A green CI run does not mean a module is safe if you are behind on security releases.
5. Docker Image Build Workflow
If your site runs from a Docker image, the image workflow is one of the most important workflows in the repo. It should build on pull requests but only push from trusted branches, tags, or manual release events.

name: Docker Image
on:
pull_request:
branches: [main, develop]
push:
branches: [main, develop, 'release/**']
workflow_dispatch:
permissions:
contents: read
packages: write
concurrency:
group: docker-image-${{ github.ref }}
cancel-in-progress: true
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: docker/setup-buildx-action@v3
- uses: docker/metadata-action@v5
id: meta
with:
images: ghcr.io/${{ github.repository }}
tags: |
type=ref,event=branch
type=ref,event=pr
type=sha,prefix=sha-
- uses: docker/login-action@v3
if: github.event_name != 'pull_request'
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- uses: docker/build-push-action@v6
with:
context: .
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
Important safety rule: do not publish images or expose registry credentials from untrusted pull request events.
6. Container Scanning Workflow
Building an image is not the same as trusting it. Add a scan step before pushing, or scan the pushed image and fail on unacceptable severity levels.
Typical scan targets:
- OS packages in the base image.
- PHP extensions and system libraries.
- Composer dependencies.
- Node dependencies if built into the image.
- Secrets accidentally copied into the image.
If the scanner is too noisy at first, start by reporting results as artifacts, then tighten failure thresholds once the baseline is under control.
7. Lighthouse Workflow
Lighthouse checks the real user-facing surface: performance, accessibility, SEO, and best practices. For Drupal, this is especially useful after theme changes, image handling changes, cache changes, or JavaScript updates.

Recommended URLs:
- Homepage.
- Article detail page.
- Article listing page.
- Contact form.
- Any high-value landing page.
A Lighthouse workflow usually has four steps:
- Build or start the application.
- Wait until the site responds.
- Run Lighthouse CI.
- Upload the HTML/JSON report artifact.
name: Lighthouse
on:
pull_request:
branches: [main, develop]
permissions:
contents: read
jobs:
lighthouse:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
- run: npm install -g @lhci/cli
- run: lhci autorun
- uses: actions/upload-artifact@v4
if: always()
with:
name: lighthouse-reports
path: .lighthouseci
Performance budgets should be realistic. Start with warnings and trend tracking; make hard failures only when the team agrees on the thresholds.
8. Smoke Test Workflow
A smoke test checks whether the site basically works after build or deploy. It does not replace full tests; it catches obvious breakage quickly.
Examples:
- Homepage returns HTTP 200.
- Important route returns HTTP 200.
- Login page loads.
- Health endpoint responds.
- Drupal bootstrap works with
drush status. - Expected response headers exist.
curl -fsS https://example.com/ >/dev/null
curl -fsS https://example.com/articles >/dev/null
drush status
drush watchdog:show --count=20
9. Deployment Workflow
Deployment should be boring. The workflow should know which image or tag is being deployed, which environment receives it, who approved it, and how to roll back.
Good deployment workflow features:
- Uses protected GitHub environments.
- Requires manual approval for production.
- Deploys immutable tags or image digests, not vague moving state.
- Runs database updates and config import after code is live.
- Runs smoke tests after deployment.
- Posts the result somewhere visible.
For Drupal, a post-deploy command block usually includes:
drush updatedb -y
drush config:import -y
drush cache:rebuild
drush cron
10. Workflow Security Hardening

Security habits that matter:
- Set
permissionsexplicitly for every workflow. - Use least privilege: most PR checks only need
contents: read. - Do not expose secrets to untrusted pull requests.
- Push packages only from protected branches or tags.
- Use environments and required reviewers for production.
- Review changes under
.github/workflowscarefully. - Prefer official or trusted actions.
- Use concurrency controls to prevent overlapping deploys.
- Keep artifacts short-lived if they may contain sensitive logs.
Recommended Workflow Set For A Drupal Project
| Workflow | Trigger | Purpose |
|---|---|---|
| Code Quality | Pull request | PHPCS, PHPStan, Composer validation |
| Tests | Pull request, push to main | Unit/kernel/browser tests |
| Code Scanning | Pull request, push, schedule | Security analysis |
| Dependency Review | Pull request | Risky dependency changes |
| Docker Image | Pull request, push, manual | Build and publish deployable image |
| Container Scan | After image build | Find image vulnerabilities |
| Lighthouse | Pull request or nightly | Performance/accessibility regression checks |
| Deploy | Manual or protected push/tag | Release to live environment |
Common Mistakes
One Giant Workflow
If everything is in one YAML file, failures are harder to understand and slower checks block faster feedback. Split by responsibility.
Publishing From Pull Requests
Build pull requests, but do not push production images or run deployments from untrusted PR contexts.
No Permissions Block
Explicit permissions make workflows safer and easier to audit.
No Concurrency For Deployments
Two deploys running at once can produce strange production state. Use concurrency groups for image builds and deploys.
Lighthouse Without A Stable Environment
Lighthouse scores are noisy if the target site is not stable. Use consistent URLs, cache warmup, and realistic budgets.
Final Takeaway
Good GitHub workflows are not about collecting badges. They create confidence at each stage: code quality before review, security before merge, build before deploy, performance before release, and approval before production.
For a Drupal project, start with PHPCS/PHPStan and Docker image build. Then add dependency review, code scanning, container scanning, Lighthouse, smoke tests, and protected deployment workflows. Keep each workflow small, explicit, and boring enough that the whole team trusts it.