A step-by-step guide to setting up GitLab CI/CD pipelines for your Astro blog, automating the build, test, and deployment process to Cloudflare Pages.

What is GitLab CI/CD?

GitLab CI/CD is a built-in continuous integration and continuous deployment tool that automates your software development workflows. For your Astro blog, it means:

  • Automated Builds: Automatically build your blog whenever you push code
  • Automated Tests: Run tests to ensure your blog builds correctly
  • Automated Deployments: Deploy to Cloudflare Pages automatically
  • Pipeline Visualization: See the status of your builds in real-time
  • Artifact Storage: Keep build outputs for debugging and rollbacks

Key Components:

ComponentPurpose
PipelineThe complete workflow from code push to deployment
JobsIndividual tasks within a pipeline (build, test, deploy)
StagesGroups of jobs that run in sequence (build → test → deploy)
RunnersServers that execute your jobs
ArtifactsFiles generated by jobs (built site, test reports)
Pipeline EditorWeb IDE for editing .gitlab-ci.yml
Pipeline SchedulesCron-like scheduling for periodic builds

Why Use GitLab CI/CD for Your Astro Blog?

Benefits for Your Blog:

  1. Zero Manual Deployment: Push code and forget - deployment happens automatically
  2. Build Verification: Catch errors before they reach production
  3. Consistent Builds: Same environment every time, no “works on my machine”
  4. Fast Rollbacks: Artifacts from previous builds allow quick rollbacks
  5. Team Collaboration: Multiple authors can contribute without deployment conflicts
  6. Security: API keys and secrets stored securely in GitLab

Real-World Scenarios:

  • Typo Fix: Push a typo fix → Auto-build → Auto-deploy in 2 minutes
  • New Post: Add a new blog post → Pipeline runs → Live in minutes
  • Dependency Updates: Update Astro version → Pipeline tests it → Safe deployment
  • Multiple Environments: Deploy to staging first, then production

When Should You Use GitLab CI/CD?

Perfect For:

  • Frequent Updates: Publishing blog posts regularly

  • Multiple Contributors: Team members pushing content

  • Quality Assurance: Need to verify builds before deployment

  • Professional Workflow: Want production-grade deployment process

  • Complex Builds: Using MDX, TypeScript, or custom processing

  • Scheduled Deployments: Publishing posts at specific times

Not Necessary For:

  • Static Rarely-Updated Sites: Updating once a month might not justify CI/CD

  • Solo Developer Learning: Simple FTP upload might be easier when learning

  • No Build Step: Pure HTML/CSS sites with no build process

How to Set Up GitLab CI/CD

Prerequisites

Before starting, ensure you have:

  • Self-hosted GitLab instance with your Astro blog repository
  • GitLab Runner configured (shared or project-specific)
  • Cloudflare Pages project created
  • Cloudflare API token with Pages edit permissions
  • Basic understanding of YAML syntax

Architecture Overview

flowchart TD
  subgraph Developer["👨‍💻 Developer Workflow"]
      A[Write Blog Post]
      B[Git Commit]
      C[Git Push]
  end

  subgraph GitLab["🦊 GitLab CI/CD Pipeline"]
      D[Trigger Pipeline]
      E[Install Dependencies]
      F[Build Astro Site]
      G[Run Tests]
      H[Create Artifact]
  end

  subgraph Deployment["☁️ Deployment Decision"]
      I{Branch Check}
  end

  subgraph Staging["📦 Staging"]
      J[Deploy to Preview]
      L[Preview Environment]
  end

  subgraph Production["🚀 Production"]
      K[Deploy to Production]
      M[Live Site]
  end

  A --> B
  B --> C
  C --> D
  D --> E
  E --> F
  F --> G
  G --> H
  H --> I
  I -->|feature branch| J
  J --> L
  I -->|main branch| K
  K --> M

  style Developer fill:#e1f5ff,stroke:#0288d1,stroke-width:2px
  style GitLab fill:#fff3e0,stroke:#ef6c00,stroke-width:2px
  style Deployment fill:#f3e5f5,stroke:#7b1fa2,stroke-width:2px
  style Staging fill:#fff9c4,stroke:#f9a825,stroke-width:2px
  style Production fill:#e8f5e9,stroke:#388e3c,stroke-width:2px

Step 1: Create GitLab CI/CD Configuration

Create a .gitlab-ci.yml file in the root of your repository:

# .gitlab-ci.yml
# GitLab CI/CD pipeline for Astro blog deployment to Cloudflare Pages

# Define the Docker image to use for all jobs
# Using Node.js 20 as Astro requires Node 18+
image: node:20

# Define pipeline stages (run in order)
stages:
  - install # Install dependencies
  - build # Build the Astro site
  - test # Run tests
  - deploy # Deploy to Cloudflare Pages

# Cache node_modules between pipeline runs
# This speeds up subsequent builds
cache:
  key:
    files:
      - package-lock.json # Cache invalidates when package-lock.json changes
  paths:
    - node_modules/
    - .npm/

# Global variables available to all jobs
variables:
  npm_config_cache: '$CI_PROJECT_DIR/.npm'

# ==========================================
# STAGE 1: Install Dependencies
# ==========================================
install_dependencies:
  stage: install
  script:
    - echo "Installing dependencies..."
    - npm ci --prefer-offline --no-audit
  artifacts:
    expire_in: 1 hour
    paths:
      - node_modules/
  only:
    - branches # Run on all branches

# ==========================================
# STAGE 2: Build Astro Site
# ==========================================
build_site:
  stage: build
  dependencies:
    - install_dependencies
  script:
    - echo "Building Astro site..."
    - npm run build
    - echo "Build completed successfully!"
    - ls -lah dist/
  artifacts:
    expire_in: 1 week
    paths:
      - dist/ # Built site
      - package.json # Needed for deployment
  only:
    - branches

# ==========================================
# STAGE 3: Test (Optional but Recommended)
# ==========================================
test_build:
  stage: test
  dependencies:
    - build_site
  script:
    - echo "Running build verification tests..."
    # Check if dist directory exists and has content
    - test -d dist || (echo "dist directory not found!" && exit 1)
    - test -f dist/index.html || (echo "index.html not found!" && exit 1)
    # Check for required files
    - test -d dist/_astro || (echo "Assets directory missing!" && exit 1)
    - echo "Build verification passed!"
  only:
    - branches

# ==========================================
# STAGE 4: Deploy to Cloudflare Pages
# ==========================================

# Deploy to staging (feature branches)
deploy_staging:
  stage: deploy
  dependencies:
    - build_site
  before_script:
    - npm install -g wrangler
  script:
    - echo "Deploying to Cloudflare Pages (Staging)..."
    - |
      wrangler pages deploy dist \
        --project-name=blog-stack101 \
        --branch=$CI_COMMIT_REF_NAME \
        --commit-hash=$CI_COMMIT_SHORT_SHA \
        --commit-message="$CI_COMMIT_MESSAGE"
    - echo "Staging deployment complete!"
    - echo "Preview URL will be displayed above ⬆️"
  environment:
    name: staging/$CI_COMMIT_REF_NAME
    url: https://$CI_COMMIT_REF_SLUG.blog-stack101.pages.dev
  only:
    - branches
  except:
    - main # Don't deploy staging for main branch

# Deploy to production (main branch only)
deploy_production:
  stage: deploy
  dependencies:
    - build_site
  before_script:
    - npm install -g wrangler
  script:
    - echo "Deploying to Cloudflare Pages (Production)..."
    - |
      wrangler pages deploy dist \
        --project-name=blog-stack101 \
        --branch=main \
        --commit-hash=$CI_COMMIT_SHORT_SHA \
        --commit-message="$CI_COMMIT_MESSAGE"
    - echo "Production deployment complete!"
    - echo "Site is live at https://blog.stack101.dev 🚀"
  environment:
    name: production
    url: https://blog.stack101.dev
  only:
    - main # Only deploy to production from main branch
  when: manual # Require manual approval for production deployments

Step 2: Configure GitLab CI/CD Variables

Store sensitive credentials securely in GitLab:

  1. Go to your GitLab project
  2. Click SettingsCI/CD
  3. Expand Variables section
  4. Click Add Variable

Add Cloudflare Credentials:

Variable NameValueProtectedMaskedDescription
CLOUDFLARE_API_TOKENYour API token✅ Yes✅ YesCloudflare API token with Pages edit permissions
CLOUDFLARE_ACCOUNT_IDYour account ID✅ Yes❌ NoFound in Cloudflare dashboard URL

How to Get Cloudflare API Token:

  1. Go to Cloudflare Dashboard
  2. Click My ProfileAPI Tokens
  3. Click Create Token
  4. Use template: Edit Cloudflare Workers
  5. Or create custom token with permissions:
    • Account - Cloudflare Pages: Edit
  6. Copy the token immediately (shown only once)
  7. Add it to GitLab CI/CD variables

Step 3: Configure GitLab Runner

Option A: Use Shared Runners (Easier)

If your GitLab instance has shared runners enabled:

  1. Go to SettingsCI/CDRunners
  2. Enable Shared runners for this project
  3. No additional configuration needed

Option B: Set Up Project Runner (More Control)

If you need a dedicated runner:

# On your GitLab Runner server (can be same as GitLab host)

# Install GitLab Runner
curl -L "https://packages.gitlab.com/install/repositories/runner/gitlab-runner/script.deb.sh" | sudo bash
sudo apt-get install gitlab-runner

# Register the runner
sudo gitlab-runner register

# Follow the prompts:
# GitLab instance URL: https://your-gitlab.com
# Registration token: (from Settings → CI/CD → Runners)
# Description: blog-stack101-runner
# Tags: docker,astro,nodejs
# Executor: docker
# Default Docker image: node:20

Step 4: Create wrangler.toml Configuration

Create wrangler.toml in your project root:

# wrangler.toml
# Cloudflare Pages configuration

name = "blog-stack101"
compatibility_date = "2024-12-06"

# Pages-specific configuration
pages_build_output_dir = "dist"

# Environment variables (non-sensitive)
[env.production]
NODE_VERSION = "20"

[env.preview]
NODE_VERSION = "20"

Step 5: Test Your Pipeline

First Pipeline Run:

  1. Commit and push your changes:

    git add .gitlab-ci.yml wrangler.toml
    git commit -m "feat: add GitLab CI/CD pipeline for automated deployment"
    git push origin feature/ci-cd
  2. Watch the pipeline:

    • Go to CI/CDPipelines in GitLab
    • Click on the running pipeline
    • Watch each job execute in real-time
  3. Check the results:

    • ✅ Green checkmarks = Success
    • ❌ Red X = Failed (click to see logs)
    • 🔵 Blue circle = Running
    • ⏸️ Gray = Manual action required

Expected Pipeline Flow:

┌─────────────────┐
│ install_deps    │  ~30s  ✅
└────────┬────────┘

┌────────▼────────┐
│ build_site      │  ~45s  ✅
└────────┬────────┘

┌────────▼────────┐
│ test_build      │  ~10s  ✅
└────────┬────────┘

┌────────▼────────┐
│ deploy_staging  │  ~20s  ✅
└─────────────────┘

Preview: https://feature-ci-cd.blog-stack101.pages.dev

Step 6: Configure Pipeline Schedules (Optional)

Automatically rebuild your blog at specific times:

Use Case: Scheduled Posts

If you write posts with future publish dates, schedule nightly builds:

  1. Go to CI/CDSchedules
  2. Click New schedule
  3. Configure:
    • Description: Nightly rebuild for scheduled posts
    • Interval pattern: 0 2 * * * (2 AM daily)
    • Cron timezone: Your timezone
    • Target branch: main
    • Variables: (none needed)
  4. Click Save pipeline schedule

Common Cron Patterns:

PatternDescription
0 2 * * *Daily at 2:00 AM
0 */6 * * *Every 6 hours
0 0 * * 0Weekly on Sunday at midnight
0 9 * * 1-5Weekdays at 9:00 AM

Step 7: Understanding Pipeline Artifacts

What Are Artifacts?

Artifacts are files generated by your pipeline jobs that are preserved for later use:

  • Build artifacts: The dist/ folder containing your built site
  • Test reports: Results from automated tests
  • Logs: Build and deployment logs

Accessing Artifacts:

  1. Go to CI/CDPipelines
  2. Click on a successful pipeline
  3. Click the Download icon next to jobs
  4. Select artifacts to download

Artifact Benefits:

  • Debugging: Download and inspect failed builds
  • Rollbacks: Redeploy previous artifacts
  • Auditing: Keep history of what was deployed when
  • Testing: Download and test builds locally

Advanced Configuration

Parallel Jobs for Faster Builds

Speed up your pipeline by running tests in parallel:

# Run multiple test jobs simultaneously
test_links:
  stage: test
  script:
    - npm run test:links # Check for broken links

test_a11y:
  stage: test
  script:
    - npm run test:accessibility # Accessibility tests

test_performance:
  stage: test
  script:
    - npm run test:lighthouse # Lighthouse performance tests

Conditional Deployments

Deploy only when specific files change:

deploy_production:
  stage: deploy
  script:
    - wrangler pages deploy dist --project-name=blog-stack101
  only:
    refs:
      - main
    changes:
      - src/**/*
      - public/**/*
      - package.json
      - astro.config.mjs

Notification Integration

Get notified when pipelines fail:

# Add to .gitlab-ci.yml
notify_failure:
  stage: .post # Runs after all other stages
  script:
    - |
      curl -X POST https://api.telegram.org/bot$TELEGRAM_BOT_TOKEN/sendMessage \
        -d chat_id=$TELEGRAM_CHAT_ID \
        -d text="🚨 Pipeline failed for $CI_PROJECT_NAME"
  when: on_failure # Only run when pipeline fails
  only:
    - main

Troubleshooting Common Issues

Issue 1: Pipeline Fails at Build Stage

Error: npm ERR! missing script: build

Solution:

# Verify package.json has build script
cat package.json | grep -A 5 "scripts"

# Should contain:
# "scripts": {
#   "build": "astro build"
# }

Issue 2: Cloudflare Deployment Fails

Error: Error: Authentication error

Solution:

  • Check CLOUDFLARE_API_TOKEN is set correctly
  • Verify token has Pages edit permissions
  • Ensure token hasn’t expired

Issue 3: Out of CI/CD Minutes

Error: Quota exceeded for runner

Solution:

  • Check SettingsUsage Quotas
  • Optimize cache to reduce build times
  • Consider self-hosted runner for unlimited minutes

Issue 4: Slow Builds

Symptoms: Builds taking >5 minutes

Solutions:

# Add cache for faster installs
cache:
  key: ${CI_COMMIT_REF_SLUG}
  paths:
    - node_modules/
    - .npm/

# Use npm ci instead of npm install
script:
  - npm ci --prefer-offline --no-audit

Best Practices

1. Use Branch Protection

Require CI/CD to pass before merging:

  1. Go to SettingsRepositoryBranch protection
  2. Add rule for main branch:
    • ✅ Require approval from code owners
    • ✅ Require pipelines to succeed
    • ✅ Prevent force push

2. Tag Production Releases

Create git tags for production deployments:

# After successful deployment
git tag -a v1.0.0 -m "Release v1.0.0 - Added CI/CD pipeline"
git push origin v1.0.0

3. Monitor Pipeline Performance

Track metrics in AnalyticsCI/CD Analytics:

  • Pipeline success rate
  • Average pipeline duration
  • Most failing jobs

4. Use Pipeline Templates

Create reusable templates for common jobs:

# .gitlab/ci/templates/deploy.yml
.deploy_template:
  before_script:
    - npm install -g wrangler
  script:
    - wrangler pages deploy dist --project-name=$PROJECT_NAME

# Use in main .gitlab-ci.yml
include:
  - local: '.gitlab/ci/templates/deploy.yml'

deploy_prod:
  extends: .deploy_template
  variables:
    PROJECT_NAME: blog-stack101

Pipeline Workflow Example

Feature Development Workflow:

# 1. Create feature branch
git checkout -b feature/new-blog-post

# 2. Write your blog post
# Edit src/content/blog/my-new-post.mdx

# 3. Commit and push
git add .
git commit -m "feat: add new blog post about GitLab CI/CD"
git push origin feature/new-blog-post

# 4. Pipeline automatically:
#    ✅ Installs dependencies
#    ✅ Builds site
#    ✅ Runs tests
#    ✅ Deploys to staging preview
#    📧 Sends you preview URL

# 5. Review preview:
# https://feature-new-blog-post.blog-stack101.pages.dev

# 6. Create merge request
# GitLab → Merge Requests → New merge request

# 7. After approval and merge to main:
#    ✅ Pipeline runs again on main branch
#    ⏸️ Waits for manual approval
#    ✅ Click "Deploy to Production"
#    🚀 Live at https://blog.stack101.dev

Monitoring and Maintenance

Weekly Checklist:

  • Review failed pipelines and fix recurring issues
  • Check CI/CD usage quota (Settings → Usage Quotas)
  • Update dependencies if security alerts appear
  • Review artifact storage and clean up old artifacts

Monthly Tasks:

  • Update GitLab Runner to latest version
  • Review and optimize cache configuration
  • Check Cloudflare API token expiration
  • Update Node.js version in pipeline if needed

Cost Considerations

GitLab CI/CD Costs (Free Tier):

  • 400 minutes/month free CI/CD minutes
  • 10 GB artifact storage
  • 5 concurrent jobs

Your Blog’s Usage:

Typical pipeline: ~2 minutes per run

  • 10 posts/month: ~20 minutes (5% of quota)
  • 50 posts/month: ~100 minutes (25% of quota)
  • Daily rebuilds: ~60 minutes (15% of quota)

Total estimated usage: 40% of free quota = $0/month

If You Exceed Free Tier:

Option 1: Self-hosted runner (unlimited minutes, $0) Option 2: GitLab Premium ($29/user/month, 10,000 minutes) Option 3: Optimize pipeline to use fewer minutes

Conclusion

You now have a production-ready CI/CD pipeline for your Astro blog that:

  • ✅ Automatically builds on every commit
  • ✅ Runs tests to catch errors early
  • ✅ Deploys preview environments for feature branches
  • ✅ Requires manual approval for production deployments
  • ✅ Stores build artifacts for debugging and rollbacks
  • ✅ Provides full deployment history and audit trail

This setup gives you the confidence to publish content knowing that every deployment has been tested and validated automatically.

Next Steps

Consider enhancing your pipeline with:

  1. Automated Testing: Add link checking, accessibility tests, performance tests
  2. SEO Validation: Verify meta tags and sitemaps before deployment
  3. Image Optimization: Compress images automatically during build
  4. Content Validation: Check for spelling, grammar, and broken links
  5. Analytics Integration: Track deployment success rates and performance

Resources