deployment-netlify-deployment

Deploying static sites, JAMstack apps, or frontend frameworks to Netlify

Netlify Deployment

Scope: Netlify site deployment, build settings, continuous deployment, and configuration Lines: ~320 Last Updated: 2025-10-18

When to Use This Skill

Activate this skill when:

Core Concepts

Deployment Workflow

Git-based deployment:

Deploy contexts:

Build Configuration

Three ways to configure:

  1. netlify.toml (recommended): Version-controlled config file
  2. Netlify UI: Web dashboard settings
  3. Netlify CLI: Command-line configuration

Build settings hierarchy:

Site Configuration

Required settings:

Framework detection:


Patterns

Pattern 1: Basic netlify.toml Configuration

# netlify.toml - Basic configuration
[build]
  # Base directory for monorepos
  base = "apps/web"

  # Build command
  command = "npm run build"

  # Publish directory (relative to base)
  publish = "dist"

  # Functions directory
  functions = "netlify/functions"

# Environment variables (non-sensitive only)
[build.environment]
  NODE_VERSION = "20"
  NPM_FLAGS = "--legacy-peer-deps"

# Production context
[context.production]
  command = "npm run build:prod"

[context.production.environment]
  NEXT_PUBLIC_API_URL = "https://api.example.com"

# Deploy preview context (PRs)
[context.deploy-preview]
  command = "npm run build:preview"

[context.deploy-preview.environment]
  NEXT_PUBLIC_API_URL = "https://preview-api.example.com"

# Branch deploy context
[context.branch-deploy]
  command = "npm run build:dev"

When to use:

Pattern 2: Framework-Specific Configurations

# Next.js App Router
[build]
  command = "npm run build"
  publish = ".next"

[build.environment]
  NODE_VERSION = "20"
  NEXT_PRIVATE_TARGET = "server"

# Astro
[build]
  command = "npm run build"
  publish = "dist"

# SvelteKit
[build]
  command = "npm run build"
  publish = "build"

[build.environment]
  NODE_VERSION = "20"

# Vite + React
[build]
  command = "npm run build"
  publish = "dist"

# Nuxt 3
[build]
  command = "npm run build"
  publish = ".output/public"

Framework-specific gotchas:

Pattern 3: Redirects and Rewrites

# netlify.toml redirects
[[redirects]]
  from = "/old-path/*"
  to = "/new-path/:splat"
  status = 301
  force = true

[[redirects]]
  from = "/api/*"
  to = "https://api.example.com/:splat"
  status = 200
  force = true

[[redirects]]
  from = "/blog/:slug"
  to = "/articles/:slug"
  status = 301

# SPA fallback (catch-all)
[[redirects]]
  from = "/*"
  to = "/index.html"
  status = 200

# Redirect with query params
[[redirects]]
  from = "/search"
  to = "/search-results?q=:query"
  query = {query = ":query"}
  status = 301

# Proxy API requests (no CORS)
[[redirects]]
  from = "/api-proxy/*"
  to = "https://external-api.com/:splat"
  status = 200
  headers = {X-Custom-Header = "value"}

Alternative: _redirects file:

# public/_redirects
/old-path/* /new-path/:splat 301
/api/* https://api.example.com/:splat 200
/* /index.html 200

When to use:

Pattern 4: Custom Headers

# netlify.toml headers
[[headers]]
  for = "/*"
  [headers.values]
    X-Frame-Options = "DENY"
    X-XSS-Protection = "1; mode=block"
    X-Content-Type-Options = "nosniff"
    Referrer-Policy = "strict-origin-when-cross-origin"
    Permissions-Policy = "camera=(), microphone=(), geolocation=()"

[[headers]]
  for = "/assets/*"
  [headers.values]
    Cache-Control = "public, max-age=31536000, immutable"

[[headers]]
  for = "/*.js"
  [headers.values]
    Cache-Control = "public, max-age=31536000, immutable"

[[headers]]
  for = "/*.css"
  [headers.values]
    Cache-Control = "public, max-age=31536000, immutable"

[[headers]]
  for = "/api/*"
  [headers.values]
    Access-Control-Allow-Origin = "https://example.com"
    Access-Control-Allow-Methods = "GET, POST, PUT, DELETE"
    Access-Control-Allow-Headers = "Content-Type, Authorization"

[[headers]]
  for = "/index.html"
  [headers.values]
    Cache-Control = "public, max-age=0, must-revalidate"

When to use:

Pattern 5: Netlify CLI Deployment

# Install Netlify CLI
npm install -g netlify-cli

# Login to Netlify
netlify login

# Initialize new site
netlify init

# Link existing site
netlify link

# Deploy to draft URL (preview)
netlify deploy

# Deploy to production
netlify deploy --prod

# Deploy specific directory
netlify deploy --dir=dist --prod

# Open site in browser
netlify open

# View deployment status
netlify status

# Stream build logs
netlify watch

# Run dev server with Netlify features
netlify dev

When to use:

Pattern 6: Environment Variables

# Set via CLI
netlify env:set API_KEY "secret-key"
netlify env:set API_URL "https://api.example.com" --context production
netlify env:set DEBUG_MODE "true" --context deploy-preview

# List environment variables
netlify env:list

# Import from .env file
netlify env:import .env.production

# Remove environment variable
netlify env:unset API_KEY

In netlify.toml (non-sensitive only):

[build.environment]
  NODE_VERSION = "20"
  NEXT_PUBLIC_APP_NAME = "My App"

[context.production.environment]
  NEXT_PUBLIC_API_URL = "https://api.example.com"

[context.deploy-preview.environment]
  NEXT_PUBLIC_API_URL = "https://preview-api.example.com"

Best practices:

Pattern 7: Monorepo Deployment

# netlify.toml in repo root
[build]
  base = "apps/marketing"
  command = "npm run build"
  publish = "dist"

# Ignore builds if app didn't change
ignore = "git diff --quiet $CACHED_COMMIT_REF $COMMIT_REF apps/marketing"

Multiple sites from one repo:

# Site 1: apps/marketing/netlify.toml
[build]
  base = "apps/marketing"
  command = "npm run build"
  publish = "dist"

# Site 2: apps/docs/netlify.toml
[build]
  base = "apps/docs"
  command = "npm run build"
  publish = "dist"

When to use:


Quick Reference

Common Build Commands

Framework       | Build Command           | Publish Directory
----------------|-------------------------|------------------
Next.js         | npm run build          | .next
Astro           | npm run build          | dist
SvelteKit       | npm run build          | build
Vite            | npm run build          | dist
Nuxt 3          | npm run build          | .output/public
Gatsby          | npm run build          | public
Remix           | npm run build          | build/client
Eleventy        | npx @11ty/eleventy     | _site
Hugo            | hugo                   | public
Jekyll          | jekyll build           | _site

Deploy Contexts

Context          | Trigger                | Use Case
-----------------|------------------------|---------------------------
production       | Push to main branch    | Live site
deploy-preview   | Open pull request      | PR previews
branch-deploy    | Push to other branch   | Feature branch testing

Critical Files

File               | Purpose
-------------------|------------------------------------------
netlify.toml       | Build config, redirects, headers
_redirects         | Redirects (alternative to toml)
_headers           | Headers (alternative to toml)
.nvmrc             | Node version specification
package.json       | Dependencies, build scripts

Deployment Checklist

✅ Build command configured
✅ Publish directory set correctly
✅ Environment variables added (production + preview)
✅ Redirects configured (especially SPA fallback)
✅ Security headers added
✅ Cache headers for static assets
✅ Node version specified (.nvmrc or netlify.toml)
✅ Git LFS configured (if using large files)
✅ Custom domain configured (if applicable)
✅ HTTPS enforced

Anti-Patterns

Wrong publish directory: Build succeeds but site shows 404 ✅ Check framework's output directory (.next, dist, build, etc.)

Missing SPA fallback redirect: Direct routes 404 in SPAs ✅ Add /* /index.html 200 redirect for client-side routing

Hardcoded environment variables: API URLs in source code ✅ Use environment variables, different per deploy context

No cache headers: Static assets re-downloaded on every visit ✅ Set Cache-Control: public, max-age=31536000, immutable for hashed assets

Sensitive keys in netlify.toml: API secrets committed to Git ✅ Use Netlify UI or CLI for sensitive environment variables

Build command missing dependencies: Build fails after npm install ✅ Ensure all dependencies in package.json, test locally with netlify dev

Deploying build artifacts: Committing dist/ or .next/ to Git ✅ Add to .gitignore, let Netlify build on deploy

No base directory in monorepo: Builds entire repo ✅ Set base = "apps/your-app" in netlify.toml


Related Skills


Last Updated: 2025-10-18 Format Version: 1.0 (Atomic)