← Back to Blog Engineering

Deploying Static Sites to Netlify: Git, CI, and Getting Out of Your Own Way

Last Rev Team Jul 24, 2023 7 min read
Git branch workflow diagram connecting to Netlify deployment pipeline with build status indicators

Deploying a website should not be an event. It should be boring. Push code, build runs, site updates. No SSH. No FTP. No "let me just manually copy these files to the server."

That is the promise of Git-based continuous deployment, and platforms like Netlify have made it dead simple for static sites. Connect a repo. Configure a build command. Every push to main goes live automatically. The whole setup takes less time than reading this article.

But "simple" does not mean there is nothing to think about. The difference between a deployment pipeline that works and one that causes headaches comes down to a handful of decisions you make upfront.

The Git-Based Deployment Model

The core idea is straightforward: your Git repository is the single source of truth. Every deployment is triggered by a Git event... a push, a merge, a pull request. The deployment platform watches your repo and responds.

Here is what that flow looks like in practice:

  1. Developer pushes to a feature branch
  2. Netlify detects the push and runs the build
  3. A preview deployment goes live at a unique URL
  4. Team reviews the preview, approves the PR
  5. PR merges to main
  6. Netlify builds again and deploys to production

Every step is automated. Every change is traceable. Every deployment can be rolled back by reverting a commit. Compare that to the manual deployment workflows that still exist at a surprising number of companies... where deploying means remoting into a server and hoping nothing breaks.

Setting Up the Pipeline

The actual setup on Netlify is minimal. You connect your GitHub (or GitLab or Bitbucket) repo, tell it which branch to watch and what build command to run, and you are done.

For a typical static site generator, the configuration looks like this:

  • Build command: npm run build (or astro build, hugo, eleventy... whatever your generator uses)
  • Publish directory: dist or build or public... wherever your generator outputs the compiled files
  • Production branch: main

That is the entire configuration for most projects. Netlify detects the framework automatically in many cases and suggests the right settings. But here is where teams run into their first mistake: they accept the defaults without understanding them.

The Build Command Matters

Your build command is not just "compile the code." It is the entire pipeline that produces your deployable output. That should include:

  • Linting and type checking (catch errors before they go live)
  • Running tests (if you have them... you should have them)
  • Generating the static output
  • Optimizing images and assets
  • Generating sitemaps, RSS feeds, and other metadata

Wrap all of that into a single npm run build script. If any step fails, the build fails, and the deployment does not happen. That is exactly what you want... a failed build is infinitely better than a broken deployment.

Branch Deploys and Preview URLs

This is the feature that changes how teams work. Every pull request gets its own live URL. Not a local screenshot. Not a staging environment shared by the whole team. A real, live, shareable URL that shows exactly what the changes look like.

Netlify's deploy previews make code review dramatically more effective. A reviewer can click a link, see the actual changes in a browser, and give feedback on the real thing... not a description of the thing.

For teams that include stakeholders in the review process (designers, content editors, product managers), this is transformative. Instead of "here is a Jira ticket describing what changed," it is "here is the actual page; click this link."

Pro tip: name your branches descriptively. feature/update-pricing-page generates a more useful preview URL than fix-123. The URL becomes the context for the review.

Environment Variables and Secrets

Static sites often need API keys at build time... a CMS API key to fetch content, an analytics ID to embed, a feature flag service token. These should never be in your Git repo.

Netlify's environment variable management lets you set secrets that are available during the build but never exposed in the repository. Separate values for production and preview deploys mean your staging builds can hit a staging CMS without any code changes.

The pattern:

  • API keys and secrets go in Netlify's environment variable settings
  • Your build process reads them from process.env
  • Different values for production versus deploy previews
  • .env files for local development, listed in .gitignore

If you see a .env file committed to a repository, that is a security incident. Not a best practice issue... a security incident. GitHub's secret scanning catches some of these, but do not rely on it as your only defense.

Build Caching and Performance

Build times matter more than people think. A 10-minute build means a 10-minute delay between merging a fix and that fix being live. For content updates that need to go out quickly, that is an eternity.

Netlify caches node_modules and build output between deploys. But you can help it by:

  • Using a lock file. package-lock.json or yarn.lock ensures consistent installs and better cache hits.
  • Optimizing images at build time. Sharp, squoosh, or your framework's built-in image optimization should run during the build, not on the client.
  • Avoiding unnecessary rebuilds. If only a blog post changed, you should not be rebuilding your entire component library. Incremental builds are the goal.

Modern static site generators like Astro and Hugo build thousands of pages in seconds. If your build takes minutes, the bottleneck is usually in asset processing or external API calls, not the page generation itself.

Rollbacks and Atomic Deploys

One of the underrated benefits of this model: every deployment is atomic and every previous deployment is available for instant rollback.

An atomic deploy means the entire site updates at once. There is no moment where half the site is the old version and half is the new version. Either the new build succeeds and replaces everything, or it fails and nothing changes.

And because Netlify keeps every successful deploy, rolling back is a one-click operation. Something went wrong? Roll back to the previous deploy in seconds while you figure out what happened. Compare that to debugging a broken production server at 2am while customers see errors.

The Webhook Trigger for CMS Content

Static sites have one apparent limitation: content changes require a rebuild. If your content lives in a headless CMS, how do you get changes live without a developer pushing code?

The answer is webhooks. Your CMS fires a webhook when content is published. Netlify receives it, runs a build, and deploys the updated site. The content editor publishes in the CMS and the site updates within minutes. No developer involved.

This is the pattern that makes static sites viable for content-heavy properties. The editorial experience is the same as any dynamic CMS... publish and it goes live. The difference is invisible to the editor: instead of rendering on demand, the site pre-renders and serves from a CDN.

When to Look Beyond Netlify

Netlify is excellent for static sites and simple serverless functions. But it is worth knowing where the edges are:

  • Build minute limits on lower-tier plans can be a constraint for sites that rebuild frequently
  • Large sites (10,000+ pages) may need more sophisticated incremental build strategies
  • Server-side rendering requirements push you toward platforms like Vercel that are optimized for that use case
  • Enterprise compliance requirements may dictate infrastructure choices

The good news: the Git-based deployment model works the same way regardless of platform. Vercel, Cloudflare Pages, AWS Amplify... the concepts are identical. Learn the pattern once, apply it anywhere.

Getting Started

If you are still deploying manually, here is the fastest path to sanity:

  1. Get your site building with a single command locally
  2. Push to GitHub
  3. Connect the repo to Netlify
  4. Configure your build command and publish directory
  5. Push a change and watch it deploy

The whole process takes 15 minutes. And once it is set up, you never think about deployment again. It just happens.

Need help setting up a deployment pipeline that scales? Let's get your builds automated.

Sources

  1. Netlify -- Official Documentation (2023)
  2. Netlify -- "Deploy Previews" Documentation (2023)
  3. GitHub -- "About Secret Scanning" Documentation (2023)
  4. Astro -- Official Documentation (2023)
  5. Hugo -- Static Site Generator Documentation