Automate your Astro blog’s build, test, and deploy workflow with GitLab CI/CD and Cloudflare Pages.
What is GitLab CI/CD?
GitLab’s built-in CI/CD automates your development workflows. For an Astro blog:
- Automated Builds: Build on every push
- Automated Tests: Verify your site builds correctly
- Automated Deployments: Ship to Cloudflare Pages hands-free
- Pipeline Visualization: Real-time build status
- Artifact Storage: Keep outputs for debugging and rollbacks
Key Components:
| Component | Purpose |
|---|---|
| Pipeline | Full workflow from push to deployment |
| Jobs | Individual tasks (build, test, deploy) |
| Stages | Sequential job groups (build → test → deploy) |
| Runners | Servers that execute jobs |
| Artifacts | Job outputs (built site, test reports) |
| Pipeline Editor | Web IDE for .gitlab-ci.yml |
| Pipeline Schedules | Cron-like periodic builds |
Why Use GitLab CI/CD for Your Astro Blog?
Benefits for Your Blog:
- Zero Manual Deployment: Push and forget
- Build Verification: Catch errors before production
- Consistent Builds: Same environment every time
- Fast Rollbacks: Previous build artifacts always available
- Team Collaboration: Multiple authors, no deployment conflicts
- Security: Secrets stored securely in GitLab
Real-World Scenarios:
- Typo Fix: Push → build → deploy in 2 minutes
- New Post: Add post → pipeline runs → live in minutes
- Dependency Updates: Update Astro → pipeline tests it → safe deploy
- Multiple Environments: Stage first, then production
When Should You Use GitLab CI/CD?
Perfect For:
-
Frequent Updates: Regular publishing
-
Multiple Contributors: Team-driven content
-
Quality Assurance: Build verification before deploy
-
Professional Workflow: Production-grade deployment
-
Complex Builds: MDX, TypeScript, custom processing
-
Scheduled Deployments: Time-based publishing
Not Necessary For:
-
Static Rarely-Updated Sites: Monthly updates don’t justify the setup
-
Solo Developer Learning: Start with simple uploads first
-
No Build Step: Pure HTML/CSS with nothing to compile
How to Set Up GitLab CI/CD
Prerequisites
You need:
- Self-hosted GitLab instance with your Astro blog repo
- GitLab Runner configured (shared or project-specific)
- Cloudflare Pages project created
- Cloudflare API token with Pages edit permissions
- Basic YAML knowledge
Architecture Overview
Step 1: Create GitLab CI/CD Configuration
Create .gitlab-ci.yml in your repo root:
# .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 credentials securely in GitLab.
Navigate to Settings:
- Go to your GitLab project
- Click Settings → CI/CD
- Expand Variables section
- Click Add Variable
Add Cloudflare Credentials:
| Variable Name | Value | Protected | Masked | Description |
|---|---|---|---|---|
CLOUDFLARE_API_TOKEN | Your API token | ✅ Yes | ✅ Yes | Pages edit permissions token |
CLOUDFLARE_ACCOUNT_ID | Your account ID | ✅ Yes | ❌ No | From Cloudflare dashboard URL |
How to Get Cloudflare API Token:
- Go to Cloudflare Dashboard
- Click My Profile → API Tokens
- Click Create Token
- Use template: Edit Cloudflare Workers
- Or create a custom token with:
- Account - Cloudflare Pages: Edit
- Copy the token immediately (shown only once)
- Add it to GitLab CI/CD variables
Step 3: Configure GitLab Runner
Option A: Use Shared Runners (Easier)
If your instance has shared runners:
- Go to Settings → CI/CD → Runners
- Enable Shared runners
- Done
Option B: Set Up Project Runner (More Control)
For 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
Add wrangler.toml to 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:
-
Commit and push:
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 -
Watch the pipeline: Go to CI/CD → Pipelines, click the running pipeline, and follow each job live.
-
Read the results:
- ✅ Green = Success
- ❌ Red = Failed (click for logs)
- 🔵 Blue = Running
- ⏸️ Gray = Manual action needed
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)
Rebuild your blog on a schedule. Useful for posts with future publish dates — they need a rebuild to go live.
- Go to CI/CD → Schedules
- Click New schedule
- Configure:
- Description:
Nightly rebuild for scheduled posts - Interval pattern:
0 2 * * *(2 AM daily) - Cron timezone: Your timezone
- Target branch:
main - Variables: (none needed)
- Description:
- Click Save pipeline schedule
Common Cron Patterns:
| Pattern | Description |
|---|---|
0 2 * * * | Daily at 2:00 AM |
0 */6 * * * | Every 6 hours |
0 0 * * 0 | Weekly on Sunday at midnight |
0 9 * * 1-5 | Weekdays at 9:00 AM |
Step 7: Understanding Pipeline Artifacts
What Are Artifacts?
Files generated by pipeline jobs, kept for later use:
- Build artifacts: The
dist/folder with your built site - Test reports: Automated test results
- Logs: Build and deployment logs
Accessing Artifacts:
- Go to CI/CD → Pipelines
- Click a successful pipeline
- Click Download next to the job
- Select artifacts to download
Artifact Benefits:
- Debugging: Inspect failed builds locally
- Rollbacks: Redeploy previous versions
- Auditing: Track what was deployed and when
- Testing: Test builds locally before promoting
Advanced Configuration
Parallel Jobs for Faster Builds
Run tests simultaneously:
# 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 relevant 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 on pipeline failures:
# 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:
- Verify
CLOUDFLARE_API_TOKENis set correctly - Confirm token has Pages edit permissions
- Check token hasn’t expired
Issue 3: Out of CI/CD Minutes
Error: Quota exceeded for runner
Solution:
- Check Settings → Usage Quotas
- Optimize cache to cut build times
- Use a 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 passing CI before merge:
- Go to Settings → Repository → Branch protection
- Add rule for
mainbranch:- ✅ Require approval from code owners
- ✅ Require pipelines to succeed
- ✅ Prevent force push
2. Tag Production Releases
Tag each production deploy:
# 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
Check Analytics → CI/CD Analytics for:
- Pipeline success rate
- Average duration
- Most failing jobs
4. Use Pipeline Templates
Extract reusable job definitions:
# .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:
- Fix failed pipelines
- Check CI/CD usage quota
- Address dependency security alerts
- Clean up old artifacts
Monthly:
- Update GitLab Runner
- Optimize cache config
- Verify Cloudflare API token expiration
- Bump Node.js version if needed
Cost Considerations
GitLab CI/CD Free Tier:
- 400 minutes/month CI/CD minutes
- 10 GB artifact storage
- 5 concurrent jobs
Your Blog’s Usage:
A typical pipeline takes ~2 minutes.
- 10 posts/month: ~20 min (5% of quota)
- 50 posts/month: ~100 min (25% of quota)
- Daily rebuilds: ~60 min (15% of quota)
At ~40% of free quota, this costs $0/month.
If You Exceed Free Tier:
Option 1: Self-hosted runner (unlimited, free) Option 2: GitLab Premium ($29/user/month, 10,000 min) Option 3: Optimize pipeline to reduce minutes
Conclusion
Your pipeline now:
- ✅ Builds on every commit
- ✅ Tests to catch errors early
- ✅ Deploys previews for feature branches
- ✅ Requires manual approval for production
- ✅ Stores artifacts for debugging and rollbacks
- ✅ Provides full deployment history
Every deploy is tested and validated automatically.
Next Steps
Enhance your pipeline with:
- Automated Testing: Link checking, accessibility, performance
- SEO Validation: Meta tag and sitemap verification
- Image Optimization: Automatic compression during build
- Content Validation: Spelling, grammar, broken link checks
- Analytics Integration: Deployment success rate tracking