Deploying to Netlify from GitHub Actions
Overview
While Netlify provides built-in CI/CD, deploying from GitHub Actions gives you more control over the build process, allows integration with other CI steps, and enables custom deployment logic.
Why Deploy from GitHub Actions?
Advantages
- Unified CI/CD Pipeline - Run tests, quality checks, and deployment in one workflow
- Custom Build Logic - Complex build steps that Netlify's CI can't handle
- Conditional Deployment - Deploy only after specific checks pass
- Build Artifacts Reuse - Build once, deploy multiple times
- Better Control - Full control over build environment and dependencies
- Integration - Easy integration with other GitHub Actions tools
When to Use
- You need to run extensive tests before deployment
- Your build process requires specific tools or configurations
- You want to deploy to multiple environments
- You need to coordinate deployment with other services
Setup: Disable Netlify's Auto-Deploy
To prevent conflicts, disable Netlify's automatic builds:
Via Netlify Dashboard
- Go to Site Settings → Build & deploy
- Under Continuous deployment, click Edit settings
- Set Build command to empty or a no-op command
- Save changes
Via netlify.toml (Recommended)
Create netlify.toml in your repository root:
[build]
# Disable automatic builds - we deploy from GitHub Actions
command = "echo 'Build disabled - deploying from GitHub Actions'"
publish = "out"
[build.environment]
# Set Node.js version if needed
NODE_VERSION = "20"
This approach is version-controlled and more maintainable.
Implementation
Prerequisites
- Netlify Auth Token
- Go to User settings → Applications → Personal access tokens
- Create new token with "Deploy" permissions
- Add to GitHub secrets as
NETLIFY_AUTH_TOKEN
- Netlify Site ID
- Go to Site settings → General → Site details
- Copy API ID
- Add to GitHub secrets as
NETLIFY_SITE_ID
Basic Deployment Workflow
name: Deploy to Netlify
on:
push:
branches:
- main
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install dependencies
run: npm ci
- name: Build application
run: npm run build
env:
NODE_ENV: production
- name: Deploy to Netlify
uses: nwtgck/actions-netlify@v3.0
with:
publish-dir: ./out
production-deploy: true
github-token: ${{ secrets.GITHUB_TOKEN }}
deploy-message: 'Deploy from GitHub Actions - ${{ github.sha }}'
env:
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }}
timeout-minutes: 5
With Quality Gates
This project's approach - only deploy after all checks pass:
name: Production Deploy
on:
push:
branches:
- main
jobs:
quality-checks:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 10
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'pnpm'
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Type checking
run: pnpm run typecheck
- name: Linting
run: pnpm run lint
- name: Unit tests
run: pnpm run test:run
- name: Build
run: pnpm run build
env:
NODE_ENV: production
deploy:
runs-on: ubuntu-latest
needs: quality-checks
steps:
- uses: actions/checkout@v4
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 10
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'pnpm'
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Build with docs
run: pnpm run build:with-docs
env:
NODE_ENV: production
- name: Deploy to Netlify Production
uses: nwtgck/actions-netlify@v3.0
with:
publish-dir: ./out
production-deploy: true
github-token: ${{ secrets.GITHUB_TOKEN }}
deploy-message: 'Production deploy - ${{ github.sha }}'
env:
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }}
timeout-minutes: 10
Using Netlify CLI Directly
For more control, use Netlify CLI instead of the action:
- name: Install Netlify CLI
run: npm install -g netlify-cli
- name: Deploy to Netlify
run: |
netlify deploy \
--dir=./out \
--site=${{ secrets.NETLIFY_SITE_ID }} \
--auth=${{ secrets.NETLIFY_AUTH_TOKEN }} \
--prod \
--message="Deploy from GitHub Actions - ${{ github.sha }}"
Capturing Deploy URL
- name: Deploy to Netlify
id: netlify-deploy
run: |
OUTPUT=$(netlify deploy \
--dir=./out \
--site=${{ secrets.NETLIFY_SITE_ID }} \
--auth=${{ secrets.NETLIFY_AUTH_TOKEN }} \
--prod \
--json)
DEPLOY_URL=$(echo "$OUTPUT" | jq -r '.deploy_url')
echo "deploy-url=$DEPLOY_URL" >> $GITHUB_OUTPUT
env:
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
- name: Output deployment URL
run: echo "Deployed to ${{ steps.netlify-deploy.outputs.deploy-url }}"
Common Patterns
Deploy Only on Tag
on:
push:
tags:
- 'v*'
Deploy with Environment Variables
- name: Build with environment
run: pnpm run build
env:
NODE_ENV: production
API_URL: ${{ secrets.PROD_API_URL }}
FEATURE_FLAG_X: true
Multi-Environment Deployment
jobs:
deploy-staging:
if: github.ref == 'refs/heads/develop'
# Deploy to staging site
deploy-production:
if: github.ref == 'refs/heads/main'
# Deploy to production site
Troubleshooting
Build Works Locally but Fails in CI
- Check Node.js version matches
- Verify all dependencies are in
package.json - Check for environment-specific code
- Review build logs for missing environment variables
Deployment Timeout
- Increase
timeout-minutesin the deploy step - Check if build artifacts are too large
- Consider using deploy preview to debug
Concurrent Deployments
Netlify queues concurrent deploys. Add concurrency control:
concurrency:
group: netlify-deploy-${{ github.ref }}
cancel-in-progress: true
Best Practices
- Always run quality checks before deploy - Prevent broken deployments
- Use build caching - Speed up CI with dependency and build caching
- Separate build and deploy - Reuse build artifacts when possible
- Add deployment notifications - Post to Slack, Discord, etc.
- Monitor deploy time - Optimize if deploys take too long
- Use deploy previews for PRs - Test before merging
Security Considerations
- Never commit secrets - Use GitHub Secrets for tokens
- Limit token permissions - Use tokens with minimum required permissions
- Rotate tokens regularly - Update tokens periodically
- Use environment-specific secrets - Different tokens for staging/production
Additional Resources
Related
- PR Preview Deployments - Automatic preview URLs for pull requests