GitLab CI/CD ¶
GitLab CI/CD provides integrated continuous integration and deployment directly within GitLab. This guide covers deploying markata-go sites to GitLab Pages and other platforms.
Quick Start ¶ #
Create .gitlab-ci.yml in your repository root:
image: alpine:latest
stages:
- build
- deploy
variables:
MARKATA_VERSION: "0.1.0"
build:
stage: build
before_script:
- apk add --no-cache wget tar
- wget -qO- "https://github.com/WaylonWalker/markata-go/releases/download/v${MARKATA_VERSION}/markata-go_${MARKATA_VERSION}_linux_x86_64.tar.gz" | tar xz
- mv markata-go /usr/local/bin/
script:
- markata-go build --clean
artifacts:
paths:
- public/
expire_in: 1 hour
pages:
stage: deploy
script:
- echo "Deploying to GitLab Pages..."
artifacts:
paths:
- public/
only:
- main
environment:
name: production
url: https://$CI_PROJECT_NAMESPACE.gitlab.io/$CI_PROJECT_NAME
Push to your repository, and GitLab will automatically build and deploy your site.
GitLab Pages ¶ #
Basic Deployment ¶ #
GitLab Pages requires a job named pages that produces artifacts in the public/ directory:
pages:
stage: deploy
script:
- echo "Deploying to GitLab Pages"
artifacts:
paths:
- public/
only:
- main
The key requirements:
- Job must be named
pages - Artifacts must include
public/directory - Only runs on your default branch
With Site URL Configuration ¶ #
variables:
MARKATA_VERSION: "0.1.0"
SITE_URL: "https://mygroup.gitlab.io/myproject"
build:
stage: build
image: alpine:latest
before_script:
- apk add --no-cache wget tar
- wget -qO- "https://github.com/WaylonWalker/markata-go/releases/download/v${MARKATA_VERSION}/markata-go_${MARKATA_VERSION}_linux_x86_64.tar.gz" | tar xz
- mv markata-go /usr/local/bin/
script:
- MARKATA_GO_URL=$SITE_URL markata-go build --clean
artifacts:
paths:
- public/
expire_in: 1 hour
pages:
stage: deploy
dependencies:
- build
script:
- echo "Deploying to GitLab Pages"
artifacts:
paths:
- public/
only:
- main
Custom Domain ¶ #
- Go to Settings > Pages > New Domain
- Add your domain and verify ownership
- Update your configuration:
variables:
SITE_URL: "https://example.com"
- Configure DNS:
- Add a
CNAMErecord pointing to<namespace>.gitlab.io - Or use
Arecords for apex domains
- Add a
Access Control ¶ #
Control who can view your GitLab Pages site:
- Settings > General > Visibility
- Choose visibility level for Pages
For private Pages with authentication:
pages:
stage: deploy
script:
- echo "Deploying private site"
artifacts:
paths:
- public/
only:
- main
Then enable Pages Access Control in project settings.
Complete Pipeline ¶ #
A full-featured pipeline with testing and multiple environments:
image: alpine:latest
stages:
- validate
- build
- test
- deploy
variables:
MARKATA_VERSION: "0.1.0"
# Cache configuration
GOPATH: ${CI_PROJECT_DIR}/.go
# Global cache for all jobs
cache:
key: ${CI_COMMIT_REF_SLUG}
paths:
- .go/
# Reusable template for markata-go setup
.markata-setup: &markata-setup
before_script:
- apk add --no-cache wget tar
- |
if [ ! -f /usr/local/bin/markata-go ]; then
wget -qO- "https://github.com/WaylonWalker/markata-go/releases/download/v${MARKATA_VERSION}/markata-go_${MARKATA_VERSION}_linux_x86_64.tar.gz" | tar xz
mv markata-go /usr/local/bin/
fi
# Validate configuration
validate:
stage: validate
<<: *markata-setup
script:
- markata-go config validate
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
# Build the site
build:
stage: build
<<: *markata-setup
script:
- |
if [ "$CI_COMMIT_BRANCH" == "$CI_DEFAULT_BRANCH" ]; then
export MARKATA_GO_URL="https://${CI_PROJECT_NAMESPACE}.gitlab.io/${CI_PROJECT_NAME}"
else
export MARKATA_GO_URL=""
fi
- markata-go build --clean -v
artifacts:
paths:
- public/
expire_in: 1 day
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
- if: $CI_COMMIT_BRANCH
# Test HTML output
test:html:
stage: test
image: node:20-alpine
dependencies:
- build
script:
- npm install -g html-validate
- html-validate "public/**/*.html" || true
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
allow_failure: true
# Test for broken links
test:links:
stage: test
image: node:20-alpine
dependencies:
- build
script:
- npm install -g linkinator
- linkinator public --recurse --skip "^(?!https?://)" || true
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
allow_failure: true
# Deploy to GitLab Pages
pages:
stage: deploy
dependencies:
- build
script:
- echo "Deploying to GitLab Pages"
artifacts:
paths:
- public/
environment:
name: production
url: https://${CI_PROJECT_NAMESPACE}.gitlab.io/${CI_PROJECT_NAME}
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
Multi-Environment Deployments ¶ #
Deploy to staging and production environments:
stages:
- build
- deploy
variables:
MARKATA_VERSION: "0.1.0"
.build-template: &build-template
stage: build
image: alpine:latest
before_script:
- apk add --no-cache wget tar
- wget -qO- "https://github.com/WaylonWalker/markata-go/releases/download/v${MARKATA_VERSION}/markata-go_${MARKATA_VERSION}_linux_x86_64.tar.gz" | tar xz
- mv markata-go /usr/local/bin/
script:
- markata-go build --clean
# Build for staging
build:staging:
<<: *build-template
variables:
MARKATA_GO_URL: "https://staging.example.com"
artifacts:
paths:
- public/
expire_in: 1 day
rules:
- if: $CI_COMMIT_BRANCH == "develop"
# Build for production
build:production:
<<: *build-template
variables:
MARKATA_GO_URL: "https://example.com"
artifacts:
paths:
- public/
expire_in: 1 day
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
# Deploy to staging environment
deploy:staging:
stage: deploy
dependencies:
- build:staging
script:
- echo "Deploying to staging..."
# Add your staging deployment commands here
environment:
name: staging
url: https://staging.example.com
rules:
- if: $CI_COMMIT_BRANCH == "develop"
# Deploy to production (manual approval)
deploy:production:
stage: deploy
dependencies:
- build:production
script:
- echo "Deploying to production..."
# For GitLab Pages, this happens automatically
environment:
name: production
url: https://example.com
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
when: manual # Require manual approval
Review Apps (Preview Deployments) ¶ #
Create dynamic preview environments for merge requests:
stages:
- build
- review
- deploy
- cleanup
variables:
MARKATA_VERSION: "0.1.0"
# Build for merge request review
build:review:
stage: build
image: alpine:latest
before_script:
- apk add --no-cache wget tar
- wget -qO- "https://github.com/WaylonWalker/markata-go/releases/download/v${MARKATA_VERSION}/markata-go_${MARKATA_VERSION}_linux_x86_64.tar.gz" | tar xz
- mv markata-go /usr/local/bin/
script:
- MARKATA_GO_URL="https://${CI_PROJECT_NAMESPACE}.gitlab.io/-/${CI_PROJECT_NAME}/-/jobs/${CI_JOB_ID}/artifacts/public" markata-go build --clean
artifacts:
paths:
- public/
expire_in: 1 week
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
# Deploy review app
review:
stage: review
dependencies:
- build:review
script:
- echo "Review app deployed"
artifacts:
paths:
- public/
environment:
name: review/$CI_COMMIT_REF_SLUG
url: https://${CI_PROJECT_NAMESPACE}.gitlab.io/-/${CI_PROJECT_NAME}/-/jobs/${CI_JOB_ID}/artifacts/public/index.html
on_stop: stop:review
auto_stop_in: 1 week
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
# Stop review app
stop:review:
stage: cleanup
script:
- echo "Stopping review app"
environment:
name: review/$CI_COMMIT_REF_SLUG
action: stop
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
when: manual
allow_failure: true
For more robust review apps, consider deploying to external services like Netlify or Vercel.
Caching Strategies ¶ #
Cache markata-go Binary ¶ #
variables:
MARKATA_VERSION: "0.1.0"
cache:
key: markata-go-${MARKATA_VERSION}
paths:
- .cache/markata-go
build:
stage: build
image: alpine:latest
before_script:
- mkdir -p .cache
- |
if [ -f .cache/markata-go ]; then
cp .cache/markata-go /usr/local/bin/
else
apk add --no-cache wget tar
wget -qO- "https://github.com/WaylonWalker/markata-go/releases/download/v${MARKATA_VERSION}/markata-go_${MARKATA_VERSION}_linux_x86_64.tar.gz" | tar xz
cp markata-go .cache/
mv markata-go /usr/local/bin/
fi
script:
- markata-go build --clean
Cache with Go Install ¶ #
If you prefer building from source:
variables:
GOPATH: ${CI_PROJECT_DIR}/.go
cache:
key: ${CI_COMMIT_REF_SLUG}
paths:
- .go/pkg/mod/
- .go/bin/
build:
stage: build
image: golang:1.22
script:
- |
if [ ! -f ${GOPATH}/bin/markata-go ]; then
go install github.com/WaylonWalker/markata-go/cmd/markata-go@latest
fi
- ${GOPATH}/bin/markata-go build --clean
Per-Branch Cache ¶ #
cache:
key: ${CI_COMMIT_REF_SLUG}
paths:
- .cache/
# Fallback to default branch cache if branch cache doesn't exist
cache:
key:
files:
- go.sum
prefix: ${CI_COMMIT_REF_SLUG}
paths:
- .go/pkg/mod/
policy: pull-push
Scheduled Pipelines ¶ #
Rebuild your site on a schedule:
- Go to CI/CD > Schedules
- Create a new schedule
- Set the interval (e.g., daily at midnight)
- Target the main branch
Or configure in .gitlab-ci.yml:
pages:
stage: deploy
script:
- echo "Deploying to GitLab Pages"
artifacts:
paths:
- public/
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
- if: $CI_PIPELINE_SOURCE == "schedule"
Deploying to External Services ¶ #
Netlify ¶ #
deploy:netlify:
stage: deploy
image: node:20-alpine
dependencies:
- build
before_script:
- npm install -g netlify-cli
script:
- netlify deploy --dir=public --prod
environment:
name: production
url: https://example.netlify.app
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
variables:
NETLIFY_AUTH_TOKEN: ${NETLIFY_AUTH_TOKEN}
NETLIFY_SITE_ID: ${NETLIFY_SITE_ID}
Add NETLIFY_AUTH_TOKEN and NETLIFY_SITE_ID as CI/CD variables in Settings > CI/CD > Variables.
AWS S3 ¶ #
deploy:s3:
stage: deploy
image: amazon/aws-cli:latest
dependencies:
- build
script:
- aws s3 sync ./public s3://${S3_BUCKET} --delete
- |
if [ -n "${CLOUDFRONT_DISTRIBUTION_ID}" ]; then
aws cloudfront create-invalidation --distribution-id ${CLOUDFRONT_DISTRIBUTION_ID} --paths "/*"
fi
environment:
name: production
url: https://example.com
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
variables:
AWS_ACCESS_KEY_ID: ${AWS_ACCESS_KEY_ID}
AWS_SECRET_ACCESS_KEY: ${AWS_SECRET_ACCESS_KEY}
AWS_DEFAULT_REGION: us-east-1
Using Docker Images ¶ #
Pre-built Image with markata-go ¶ #
Create a custom Docker image for faster builds:
# Dockerfile
FROM alpine:latest
ARG MARKATA_VERSION=0.1.0
RUN apk add --no-cache wget tar ca-certificates \
&& wget -qO- "https://github.com/WaylonWalker/markata-go/releases/download/v${MARKATA_VERSION}/markata-go_${MARKATA_VERSION}_linux_x86_64.tar.gz" | tar xz \
&& mv markata-go /usr/local/bin/ \
&& apk del wget tar
ENTRYPOINT ["markata-go"]
Use in your pipeline:
build:
stage: build
image: registry.gitlab.com/mygroup/myproject/markata-go:latest
script:
- markata-go build --clean
Go Image ¶ #
build:
stage: build
image: golang:1.22-alpine
before_script:
- go install github.com/WaylonWalker/markata-go/cmd/markata-go@latest
script:
- markata-go build --clean
Troubleshooting ¶ #
Pipeline Not Running ¶ #
- Check
.gitlab-ci.ymlsyntax: CI/CD > Editor - Verify the file is in the repository root
- Check branch rules match your branch
Pages Not Updating ¶ #
- Verify the
pagesjob completed successfully - Check artifacts include
public/directory - Wait a few minutes for propagation
- Check Settings > Pages for status
404 Error on Pages ¶ #
- Ensure
public/index.htmlexists - Check the URL matches your project path
- Verify GitLab Pages is enabled
Build Fails with Memory Error ¶ #
Increase job resources using tags:
build:
tags:
- high-memory
script:
- markata-go build --clean
Or contact your GitLab administrator to configure runner resources.
Permission Denied ¶ #
Check that protected variables are available:
- Settings > CI/CD > Variables
- Uncheck “Protected” or run on protected branches
Environment Variables Reference ¶ #
| Variable | Purpose | Example |
|---|---|---|
MARKATA_GO_URL |
Site base URL | https://example.com |
MARKATA_GO_OUTPUT_DIR |
Output directory | dist |
MARKATA_GO_TITLE |
Site title override | My Blog |
CI_PROJECT_NAMESPACE |
GitLab group/user | mygroup |
CI_PROJECT_NAME |
Repository name | myproject |
CI_COMMIT_BRANCH |
Current branch | main |
CI_DEFAULT_BRANCH |
Default branch | main |
CI_PIPELINE_SOURCE |
Pipeline trigger | push, merge_request_event |
Next Steps ¶ #
- GitHub Actions Guide - GitHub Actions workflows
- Deployment Guide - Manual deployment options
- GitLab CI/CD Documentation - Official docs