Skip to content
Strynal, Digital Agency

Engineering 6 min read

CI/CD for Websites

How to set up a CI/CD pipeline for your website: what each stage does, which tools to reach for, and the trade-offs worth knowing before you commit to a setup.

By Strynal Team

Deploying a website by hand works fine until it doesn’t. A forgotten step, a stale environment, or a last-minute edit that never made it to production becomes the most expensive thing in the sprint. CI/CD replaces that process with a repeatable pipeline: every push triggers the same sequence, and what ships is exactly what was reviewed. For most websites today, setting one up is far simpler than teams expect.

What CI/CD actually means for a website

CI stands for continuous integration: every code change is built and tested automatically before it lands on the main branch. CD stands for continuous delivery (or deployment): once a build passes, the output is automatically deployed to a target environment, whether staging, preview, or production.

For an app with a test suite and many contributors, CI/CD is infrastructure. For a marketing site or a content-heavy Astro build, it is still worth having, even without an extensive test suite. The value is consistency: the build that runs locally will also run on the server, on every branch, every time. No “it works on my machine.”

The build that passed on your machine is not the build your visitors get. The pipeline is.

Picking a pipeline: what you’re really choosing

There are two broad options: a general-purpose CI service wired to your host, or a platform that handles build and deploy together.

General-purpose CI (GitHub Actions, GitLab CI, Bitbucket Pipelines) gives you a YAML file that runs whatever you specify on a trigger. You control the Node version, the install command, the build command, and where the artifact goes. This fits well when you have unusual build requirements, a non-standard deploy target, or a test suite that must pass before any deploy fires. The cost is configuration: you write and maintain the YAML.

Platform-native pipelines (Vercel, Netlify, Cloudflare Pages) detect your framework, infer the build command, and deploy on push. For most Astro, Next, or Nuxt sites targeting these platforms, you connect the repo and it works. The trade-off is reduced flexibility: the deploy target is the platform, and the build environment is what the platform provides. If your stack is standard, that is fine. If it isn’t, you’ll be working against the defaults.

The choice isn’t permanent. Teams often start on a platform-native pipeline for speed, then move to GitHub Actions as build requirements get more specific. This decision also overlaps with where you’re hosting, so it’s worth reading a comparison of hosting options if you’re deciding on infrastructure at the same time.

The basic pipeline: build, test, deploy

Whether you’re writing YAML or configuring a dashboard, a website pipeline follows roughly the same stages.

1. Install dependencies

Use a lockfile and cache the result. pnpm install --frozen-lockfile or npm ci both guarantee the installed tree matches what was committed. Caching the package store between runs cuts minutes from every run. Most CI providers support this with a single cache key on the lockfile.

2. Run checks

This is where you catch problems before they reach a server. Linting, type-checking, and any unit tests belong here. For a content site, even a simple check (do all internal links resolve? does the build produce output?) adds real value. If the checks stage passes, the reviewer doesn’t have to manually verify what the machine can test. That’s the entire point.

3. Build

Run the production build. For frameworks like Astro or Next, keep the build output deterministic: commit the lockfile, pin a Node version in the CI environment, and avoid relying on global tooling that isn’t declared as a dependency. If the same inputs sometimes produce different outputs, you’ll chase phantom failures that only appear on CI.

4. Deploy to preview, then production

The pattern that pays the most dividends is deploying every branch (not just main) to its own preview URL. Code reviews happen in the browser against the real build, not in a diff. When a branch merges, that same artifact (or a clean re-run on main) deploys to production.

On Vercel or Netlify, branch previews are built in. With GitHub Actions and a separate host, you’ll wire this up using the host’s deploy command or a published action. Either way, the principle holds: what gets reviewed is what gets shipped, and nothing ships from a local terminal.

Environment variables and secrets

This is where most pipelines fail in production but pass locally. Every secret (API keys, webhook tokens, anything you’d never commit) goes into the CI provider’s secrets store and is injected at build time as an environment variable. Never hardcode credentials in YAML.

A practical pattern: commit a .env.example file listing every required variable without values. New contributors and CI environments both use it as a contract. When a required variable is missing, the build should fail loudly rather than silently produce a broken site.

For static site generators, know which variables are baked in at build time versus available at runtime. An Astro or Eleventy site has no runtime: everything is resolved during the build. This is one of several reasons the Figma-to-production workflow emphasises choosing a rendering strategy early. That decision determines which environment variable patterns apply and how secrets flow through the build.

Branch protection and merge gates

Once you have a pipeline, use it as a gate. On GitHub, branch protection rules let you require a passing status check before a PR can merge. A broken build cannot land on main. The configuration takes a few minutes and prevents a whole category of incidents.

The same gate applies to code quality. If you’re using a utility-first CSS workflow, enforce class ordering in CI. A guide to Tailwind CSS in production covers the formatting and purge configuration worth running as checks rather than relying on developer discipline alone.

The right time to add a pipeline

Most teams add CI/CD after the first bad deploy rather than before. Setting one up at project start costs an hour and compounds every week after. The more contributors touch a codebase, the more the pipeline earns its place. Even a solo developer benefits: the CI environment catches the package that was installed globally on one laptop and never committed to the lockfile. That silent discrepancy is exactly how “it works on my machine” happens.

The question isn’t whether to have a pipeline. It’s how much to put in it at the start, and that answer is: more than you think, because adding a check later means someone already shipped the thing it would have caught.

How Strynal approaches CI/CD

Every build we ship through our websites and apps practice starts with a pipeline as part of the project scaffolding, not as a post-launch addition.

For most projects, that means GitHub Actions wired to the deploy target, with branch preview URLs on every PR and a protected main branch that requires a passing build before merge. For clients on Vercel or Cloudflare Pages, the platform handles the deploy and we configure the checks that gate it. For builds with more complex requirements (multi-environment secrets, pre-deploy database migrations, content validation), we layer in the YAML stages that cover those cases.

The details vary by project. The principle doesn’t. A site that can only be deployed from one person’s terminal isn’t finished.