How to Set Up a CI/CD Pipeline for OpenSEO: A Complete GitHub Actions and Cloudflare Guide
You can set up a CI/CD pipeline for OpenSEO by configuring GitHub Actions workflows for linting, testing, and building, then automating deployments to Cloudflare Workers using Alchemy, with secure secret management and isolated PR preview stages.
OpenSEO (every-app/open-seo) uses a modern CI/CD architecture built around pnpm, GitHub Actions, and Cloudflare Workers. The pipeline ensures code quality through automated checks while enabling safe, previewable deployments for every pull request. This guide walks through the exact configuration files and commands used in the repository to replicate this setup.
Continuous Integration Pipeline
The foundation of the OpenSEO CI/CD pipeline is the GitHub Actions workflow defined in .github/workflows/ci.yml. This workflow runs on every push and pull request to validate code integrity before it reaches production.
The CI Workflow Structure
The ci job executes on ubuntu-latest and follows a deterministic sequence of steps:
- Environment setup – Checks out the exact commit using
actions/checkout@v4, then installs pnpm viapnpm/action-setup@v4and Node.js 22 usingactions/setup-node@v4 - Dependency installation – Runs
pnpm install --frozen-lockfileto ensure reproducible builds using the locked dependency tree - Static analysis – Executes
pnpm run ci:checkwhich runs linting (oxlint) and dead code detection (knip) - Test execution – Runs
pnpm run test:cito execute the full test suite - Bundle verification – Builds the worker with
pnpm vite build, which includes a guard that fails if deny-listed packages (such asdataforseo-client) enter the eager startup graph - Web UI validation – Installs web dependencies with
pnpm --dir web install, runs TypeScript checks viapnpm --dir web run types:check, and builds the static site usingpnpm --dir web run build
All steps are configured to fail fast, ensuring that lint errors, type mismatches, or test failures block the pipeline immediately.
Local CI Verification
Developers can replicate the CI environment locally before pushing changes:
# Enable corepack for the exact pnpm version specified in package.json
corepack enable
# Install dependencies with frozen lockfile
pnpm install --frozen-lockfile
# Run the full CI check suite
pnpm run ci:check
# Execute tests
pnpm run test:ci
# Verify worker bundle (includes dependency guard)
pnpm vite build
Continuous Delivery and Deployment
OpenSEO implements a multi-stage delivery process that includes Docker image builds for self-hosting and Cloudflare Worker deployments for the hosted version.
Docker Image Build
The .github/workflows/docker-image.yml workflow produces a self-hostable container using Dockerfile.selfhost:
- Uses
docker/build-push-action@v6with GitHub Actions cache (type=gha) to speed up subsequent builds - Targets
linux/amd64architecture for broad compatibility - Loads the image locally for verification rather than pushing to a registry automatically
To build the image manually:
docker build -f Dockerfile.selfhost -t openseo:selfhost .
PR Preview Deployments
The PR preview workflow in .github/workflows/pr-preview.yml creates isolated Cloudflare Worker stages for every pull request, enabling safe testing of changes without affecting production.
The workflow executes these critical steps:
- Environment preparation – Writes the secret
ENV_PREVIEWto.env.previewusingprintf '%s' "$ENV_PREVIEW" > .env.preview - URL derivation – Extracts
WORKERS_SUBDOMAINfrom the environment file to construct the preview URL:https://open-seo-<stage>.<subdomain> - Deployment – Runs
pnpm deploy:preview --stage "$STAGE" --yes, which triggers a Vite build and Alchemy deployment - Security verification – Uses
curlto verify the preview URL redirects to Cloudflare Access authentication (https://*.cloudflareaccess.com/...), failing the job if the stage is publicly accessible - PR integration – Posts the preview URL as a comment using the
ghCLI - Cleanup – When the PR closes, automatically runs
pnpm destroy:preview --stage "$STAGE" --yesto remove the temporary infrastructure
Required repository secrets for this workflow:
CLOUDFLARE_API_TOKENCLOUDFLARE_ACCOUNT_IDENV_PREVIEW(contains the full.env.previewfile contents)
Production Deployment
Production deployments use the same Alchemy tooling but target a fixed stage name hosted-prod. Unlike the automated PR previews, production pushes are typically performed manually to prevent accidental secret exposure:
# Run database migrations and deploy to production
pnpm deploy:postgres
This command reads from .env.production and executes the full deployment stack.
Required Secrets and Configuration
To set up the complete CI/CD pipeline, configure these GitHub Actions secrets in your repository settings:
| Secret | Purpose |
|---|---|
CLOUDFLARE_API_TOKEN |
Authenticates with Cloudflare API for Worker deployments |
CLOUDFLARE_ACCOUNT_ID |
Identifies the target Cloudflare account |
ENV_PREVIEW |
Base64-encoded or raw contents of .env.preview containing WORKERS_SUBDOMAIN and other preview-stage variables |
The pipeline strictly validates secret presence early in each workflow run. If ENV_PREVIEW is missing or malformed, the PR preview job aborts immediately to prevent deployment failures.
For local preview deployments (mirroring CI behavior), authenticate once with Alchemy:
pnpm alchemy login
Then create your local environment file:
cp .env.preview.example .env.preview
# Edit .env.preview with your values
pnpm preview:access # Sets up the shared Cloudflare Access gate (idempotent)
pnpm deploy:preview --stage my-feature --yes
Summary
- OpenSEO's CI pipeline uses
.github/workflows/ci.ymlto run linting, type-checking, tests, and bundle verification on every push - The delivery process combines Docker image builds (
docker-image.yml) with Cloudflare Worker deployments via Alchemy - PR previews create isolated, Access-protected stages for each pull request using
.github/workflows/pr-preview.yml - Security is enforced through deny-list bundle guards, mandatory Access authentication on previews, and strict secret management via GitHub Actions
- Local development can replicate all CI steps using pnpm scripts defined in
package.json
Frequently Asked Questions
What Node.js version does OpenSEO require?
OpenSEO requires Node.js 22, as specified in the GitHub Actions workflows using actions/setup-node@v4. The CI pipeline uses pnpm 9.x (managed via corepack) for deterministic dependency resolution across all environments.
How does OpenSEO prevent heavy dependencies from bloating the worker bundle?
The build process includes a bundle guard in the Vite build step (pnpm vite build) that explicitly checks for deny-listed packages like dataforseo-client. If any forbidden package enters the worker's eager startup graph, the build fails immediately, preventing accidental inclusion of heavy dependencies that would degrade cold-start performance.
Can I deploy OpenSEO without using Cloudflare?
Yes. While the hosted version uses Cloudflare Workers, the repository includes a Dockerfile.selfhost and a docker-image.yml workflow for building self-hostable containers. You can run the Docker image in any container runtime (Kubernetes, AWS ECS, etc.) by providing the necessary environment variables manually rather than using Alchemy.
Why are production deployments manual rather than automated?
Production deployments use pnpm deploy:postgres with a fixed hosted-prod stage but are intentionally excluded from automatic GitHub Actions triggers to prevent accidental exposure of production secrets or erroneous pushes. This manual gate ensures that production credentials remain isolated from CI environments and that deploys require explicit human verification.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →