Comprehensive toolkit for generating best practice GitLab CI/CD pipelines and configurations following current standards and conventions. Use this skill when creating new GitLab CI/CD resources, implementing CI/CD pipelines, or building GitLab pipelines from scratch.
Overall
score
93%
Does it follow best practices?
Validation for skill structure
Comprehensive security guidelines for creating secure GitLab CI/CD pipelines. Follow these practices to protect your code, credentials, and infrastructure.
❌ BAD:
deploy:
script:
- deploy --api-key sk_live_abc123xyz
- mysql -u admin -ppassword123 -h db.example.com✅ GOOD:
deploy:
script:
- deploy --api-key $API_KEY
- mysql -u $DB_USER -p$DB_PASSWORD -h $DB_HOSTSettings for sensitive variables:
Example configuration in GitLab UI:
Settings → CI/CD → Variables
Key: API_KEY
Value: sk_live_abc123xyz
Flags: [x] Mask variable
[x] Protect variable
[ ] Expand variable reference (disable for JSON/special chars)# Environment-specific variables
deploy-staging:
environment:
name: staging
variables:
API_ENDPOINT: https://api-staging.example.com
script:
- deploy --endpoint $API_ENDPOINT
deploy-production:
environment:
name: production
variables:
API_ENDPOINT: https://api.example.com
script:
- deploy --endpoint $API_ENDPOINTdeploy:
secrets:
DATABASE_PASSWORD:
vault: production/db/password@secret
token: $VAULT_TOKEN
script:
- deploy --db-password $DATABASE_PASSWORDbuild:
script:
- make build
artifacts:
paths:
- dist/
exclude:
- "**/*.env"
- "**/*.pem"
- "**/*.key"
- "**/credentials.*"
- "**/.env.*"
- "**/config.production.*"❌ BAD:
test:
image: node:latest # Unpredictable, security risk✅ GOOD:
test:
image: node:20.11-alpine3.19 # Pinned versionBest practice:
# ✅ GOOD: Official images
build:
image: node:20-alpine # Official Node.js image
test:
image: postgres:15-alpine # Official PostgreSQL image
# ⚠️ CAUTION: Third-party images (verify trust)
scan:
image: aquasec/trivy:0.49.0 # Verify publisher reputationstages:
- build
- scan
build-image:
stage: build
script:
- docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA .
scan-image:
stage: scan
image: aquasec/trivy:latest
script:
- trivy image --severity HIGH,CRITICAL --exit-code 1 $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA
needs: [build-image]
allow_failure: false # Fail pipeline on vulnerabilities# ✅ GOOD: Alpine-based images (smaller attack surface)
FROM node:20-alpine
# ⚠️ LARGER: Full images have more packages
FROM node:20-bookworm# Dockerfile best practice
FROM node:20-alpine
# Create non-root user
RUN addgroup -g 1001 -S nodejs && adduser -S nodejs -u 1001
# Change ownership
COPY --chown=nodejs:nodejs . .
# Switch to non-root user
USER nodejs
CMD ["node", "server.js"]❌ DANGEROUS PATTERNS:
# Piping to bash
install:
script:
- curl https://install.sh | bash # ❌ Dangerous
# Using eval
deploy:
script:
- eval "$COMMAND" # ❌ Code injection risk
# Overly permissive permissions
setup:
script:
- chmod 777 /app # ❌ Security risk✅ SECURE PATTERNS:
# Download and verify before execution
install:
script:
- curl -o install.sh https://install.sh
- sha256sum -c install.sh.sha256 # Verify integrity
- bash install.sh
# Avoid eval, use explicit commands
deploy:
script:
- ./deploy.sh $ENVIRONMENT
# Minimal permissions
setup:
script:
- chmod 755 /app
- chmod 600 /app/config.ymldeploy:
script:
- |
# Validate branch name
if [[ ! "$CI_COMMIT_BRANCH" =~ ^[a-zA-Z0-9_-]+$ ]]; then
echo "Invalid branch name"
exit 1
fi
# Validate version format
if [[ ! "$VERSION" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "Invalid version format"
exit 1
fi
./deploy.sh "$CI_COMMIT_BRANCH" "$VERSION"set -e for Error Handlingdeploy:
script:
- |
set -e # Exit on first error
set -o pipefail # Exit on pipe failures
echo "Starting deployment"
./build.sh
./test.sh
./deploy.sh# ❌ BAD: Secret might appear in logs
deploy:
script:
- echo "Deploying with token $API_TOKEN"
# ✅ GOOD: Don't echo secrets
deploy:
script:
- echo "Starting deployment"
- deploy --token $API_TOKEN # Token won't appear if masked
# ✅ GOOD: Use GitLab's masking
deploy:
before_script:
- echo "::add-mask::$API_TOKEN" # Mask custom secrets
script:
- deploy --token $API_TOKEN# ❌ BAD: Debug mode exposes information
deploy-production:
variables:
CI_DEBUG_TRACE: "true" # ❌ Don't use in production
script:
- deploy production
# ✅ GOOD: Only debug in non-production
test:
variables:
CI_DEBUG_TRACE: "true" # ✅ OK for testing
script:
- npm test❌ BAD:
build:
artifacts:
paths:
- ./** # Includes everything, might expose secrets✅ GOOD:
build:
artifacts:
paths:
- dist/
- build/
exclude:
- "**/*.env"
- "**/*.pem"
- "**/*.key"
- "**/node_modules/"# Short-lived artifacts for builds
build:
artifacts:
paths:
- dist/
expire_in: 1 hour # ✅ Minimize exposure window
# Longer retention for releases
release:
artifacts:
paths:
- release/
expire_in: 1 month # ✅ Appropriate for releasesbuild:
artifacts:
paths:
- dist/
exclude:
- node_modules/ # ✅ Don't include dependencies
- vendor/ # ✅ Don't include vendor code
- .git/ # ✅ Don't include git historyConfigure in Settings → CI/CD → General pipelines:
deploy:
script:
# ✅ HTTPS
- curl -X POST https://api.example.com/deploy
# ❌ HTTP (only for local development)
# - curl -X POST http://api.example.com/deploy❌ BAD:
test:
script:
- curl -k https://api.example.com # ❌ Disables verification
- git config --global http.sslVerify false # ❌ Dangerous✅ GOOD:
test:
script:
- curl --cacert /etc/ssl/certs/ca-bundle.crt https://api.example.com
- git config --global http.sslCAInfo /etc/ssl/certs/ca-bundle.crtvariables:
# ✅ Use private registry for internal images
INTERNAL_REGISTRY: registry.internal.example.com
build:
image: $INTERNAL_REGISTRY/build-tools:latest
script:
- make build# Configure runner to limit outbound connections
# Only allow specific domains/IPs in firewall rules
deploy:
script:
# Whitelist specific endpoints
- curl https://api.allowed-service.com/deploySettings → Repository → Protected branches:
main and production branchesdeploy-production:
script:
- deploy production
rules:
- if: $CI_COMMIT_BRANCH == "main" # Only on protected branch
when: manualSettings → CI/CD → Environments:
deploy-production:
environment:
name: production # Protected environment
url: https://example.com
script:
- deploy production
when: manual # Requires manual approvalrelease:
rules:
- if: $CI_COMMIT_TAG =~ /^v\d+\.\d+\.\d+$/ # Only on version tags
script:
- make release
when: manualSettings → CI/CD → Runners:
deploy-production:
tags:
- production-runner # Specific runner for production
- secured
script:
- deploy productiondeploy-production:
resource_group: production # ✅ Prevents concurrent deployments
script:
- deploy production
deploy-staging:
resource_group: staging # ✅ Independent resource group
script:
- deploy staging# ✅ GOOD: Lock file ensures reproducibility
build:
script:
- npm ci # Uses package-lock.json
# - npm install # ❌ Don't use in CIbuild:
script:
- npm ci --audit # Check for vulnerabilities
- npm audit --audit-level=high # Fail on high severity issuesinclude:
- template: Security/Dependency-Scanning.gitlab-ci.yml
gemnasium-dependency_scanning:
variables:
DS_EXCLUDED_PATHS: "node_modules,vendor"
artifacts:
reports:
dependency_scanning: gl-dependency-scanning-report.jsoninclude:
- template: Security/Secret-Detection.gitlab-ci.yml
secret_detection:
variables:
SECRET_DETECTION_EXCLUDED_PATHS: "tests/"sbom-generation:
image: anchore/syft:latest
script:
- syft packages dir:. -o cyclonedx-json > sbom.json
artifacts:
paths:
- sbom.json
expire_in: 1 yearinclude:
- template: 'Workflows/MergeRequest-Pipelines.gitlab-ci.yml'
- template: Security/SAST.gitlab-ci.yml
- template: Security/Dependency-Scanning.gitlab-ci.yml
- template: Security/Secret-Detection.gitlab-ci.yml
- template: Security/Container-Scanning.gitlab-ci.yml
compliance-check:
stage: .pre
script:
- echo "Running compliance checks"
- ./scripts/compliance-audit.sh
allow_failure: false# Different teams/roles for different stages
deploy-staging:
rules:
- if: $CI_COMMIT_BRANCH == "develop"
# Can be triggered by developers
deploy-production:
rules:
- if: $CI_COMMIT_TAG =~ /^v\d+/
when: manual # Requires ops team approval
environment:
name: productiondeploy:
before_script:
- echo "Deploy initiated by $GITLAB_USER_LOGIN at $(date)"
- echo "Commit: $CI_COMMIT_SHORT_SHA"
- echo "Branch: $CI_COMMIT_BRANCH"
script:
- deploy production
after_script:
- echo "Deploy completed at $(date)"
- ./scripts/log-audit-event.shWhen creating or reviewing GitLab CI/CD pipelines, ensure:
.gitlab-ci.ymlcurl | bash patternseval with external inputset -e)./**)Immediate Actions:
Investigation:
Remediation:
.gitlab-ci.yml with proper secret managementPrevention:
Always prioritize security in your GitLab CI/CD pipelines. When in doubt, choose the more secure option.
Install with Tessl CLI
npx tessl i pantheon-ai/gitlab-ci-generator@0.1.0