Automate deployments to Focal Deploy with GitLab Pipelines
GitLab Repository
Your application code hosted on GitLab.com or self-hosted GitLab
Focal Deploy Account & Project
An existing project deployed at least once manually (follow this guide)
Focal Deploy API Key
Generate from Dashboard → API Keys
Navigate to Dashboard → API Keys
Click "Generate New API Key"
Give it a descriptive name like "GitLab CI - My Project"
Copy the generated API key - you'll need it in the next step
Important: The API key will only be shown once. Store it securely!
Go to your GitLab project → Settings → CI/CD → Variables
Click "Add variable" and create the following variables:
Variable #1:
Key:
FOCAL_DEPLOY_API_KEYValue:
Your API key from Step 1
✅ Check "Protect variable" and "Mask variable"
Variable #2 (Optional):
Key:
FOCAL_DEPLOY_PROJECT_IDValue:
Your project ID from Focal Deploy dashboard
Choose between a simple pipeline or an advanced one with staging, previews, and manual production deployment:
Perfect for getting started. Deploys on every push to main branch.
# Simple GitLab CI/CD for Focal Deploy
stages:
- deploy
deploy:
stage: deploy
image: curlimages/curl:latest
script:
- |
curl -X POST https://api.focuswithfocal.io/api/deployments \
-H "Authorization: Bearer $FOCAL_DEPLOY_API_KEY" \
-H "Content-Type: application/json" \
-d '{"projectName": "my-app"}'
only:
- mainIncludes testing, multiple environments, preview deployments for MRs, and manual production deployment.
# GitLab CI/CD Pipeline for Focal Deploy
stages:
- test
- build
- deploy
variables:
NODE_VERSION: "20"
# Cache node_modules for faster builds
cache:
paths:
- node_modules/
# Run tests
test:
stage: test
image: node:${NODE_VERSION}
script:
- npm ci
- npm run lint
- npm test
only:
- merge_requests
- main
- develop
# Build application
build:
stage: build
image: node:${NODE_VERSION}
script:
- npm ci
- npm run build
artifacts:
paths:
- build/
- dist/
- .next/
expire_in: 1 hour
only:
- main
- develop
# Deploy to production
deploy_production:
stage: deploy
image: curlimages/curl:latest
script:
- |
curl -X POST https://api.focuswithfocal.io/api/deployments \
-H "Authorization: Bearer $FOCAL_DEPLOY_API_KEY" \
-H "Content-Type: application/json" \
-d '{"projectId": "'"$FOCAL_DEPLOY_PROJECT_ID"'", "environment": "production", "branch": "'"$CI_COMMIT_REF_NAME"'"}'
environment:
name: production
url: https://${FOCAL_DEPLOY_PROJECT_ID}.focuswithfocal.com
only:
- main
when: manual
# Deploy to staging automatically
deploy_staging:
stage: deploy
image: curlimages/curl:latest
script:
- |
curl -X POST https://api.focuswithfocal.io/api/deployments \
-H "Authorization: Bearer $FOCAL_DEPLOY_API_KEY" \
-H "Content-Type: application/json" \
-d '{"projectId": "'"$FOCAL_DEPLOY_PROJECT_ID"'", "environment": "staging", "branch": "'"$CI_COMMIT_REF_NAME"'"}'
environment:
name: staging
url: https://staging-${FOCAL_DEPLOY_PROJECT_ID}.focuswithfocal.com
only:
- develop
# Preview deployments for merge requests
deploy_preview:
stage: deploy
image: curlimages/curl:latest
script:
- |
curl -X POST https://api.focuswithfocal.io/api/deployments \
-H "Authorization: Bearer $FOCAL_DEPLOY_API_KEY" \
-H "Content-Type: application/json" \
-d '{"projectId": "'"$FOCAL_DEPLOY_PROJECT_ID"'", "environment": "preview-mr-'"$CI_MERGE_REQUEST_IID"'", "branch": "'"$CI_COMMIT_REF_NAME"'"}'
- echo "Preview URL https://mr-$CI_MERGE_REQUEST_IID-${FOCAL_DEPLOY_PROJECT_ID}.focuswithfocal.com"
environment:
name: preview/mr-$CI_MERGE_REQUEST_IID
url: https://mr-$CI_MERGE_REQUEST_IID-${FOCAL_DEPLOY_PROJECT_ID}.focuswithfocal.com
on_stop: stop_preview
only:
- merge_requests
# Stop preview deployments when MR is closed
stop_preview:
stage: deploy
image: curlimages/curl:latest
script:
- curl -X DELETE "https://api.focuswithfocal.io/api/deployments/preview-mr-$CI_MERGE_REQUEST_IID" -H "Authorization: Bearer $FOCAL_DEPLOY_API_KEY"
environment:
name: preview/mr-$CI_MERGE_REQUEST_IID
action: stop
when: manual
only:
- merge_requests📝 To add this pipeline:
.gitlab-ci.ymlPush a commit to your main branch (or create a merge request if using the advanced pipeline)
Go to your repository → CI/CD → Pipelines to watch the pipeline run
Click on the pipeline to see the job logs and track progress
If using the advanced pipeline, manually trigger the production deployment from the pipeline view
Verify your deployment in the Focal Deploy dashboard
Success!
Your pipeline is now active. Every push will trigger automated tests, builds, and deployments!
Trigger deployments only when you create a release tag:
deploy:
stage: deploy
script:
- curl -X POST https://api.focuswithfocal.io/api/deployments -H "Authorization: Bearer $API_KEY"
only:
- tags # Runs only on git tags
except:
- branchesSpeed up testing by running tests in parallel:
test:
stage: test
parallel: 3 # Run 3 instances in parallel
script:
- npm run test:$CI_NODE_INDEXKeep build artifacts for debugging:
build:
stage: build
script:
- npm run build
artifacts:
paths:
- dist/
- coverage/
expire_in: 1 weekGet notified on Slack when deployments complete:
deploy:
stage: deploy
script:
- curl -X POST https://api.focuswithfocal.io/api/deployments -H "Authorization: Bearer $API_KEY"
after_script:
- 'curl -X POST -H "Content-type: application/json" --data "{\"text\":\"Deployment $CI_JOB_STATUS\"}" $SLACK_WEBHOOK'GitLab's environment tracking lets you see deployment history and URLs:
Restrict who can deploy to production by setting up protected environments in Settings → CI/CD → Protected Environments
Require approvals before deploying to production by configuring merge request approval rules
Create temporary environments for each merge request that auto-delete when merged:
environment: name: review/$CI_COMMIT_REF_SLUG url: https://$CI_COMMIT_REF_SLUG.focuswithfocal.com on_stop: stop_review auto_stop_in: 1 day
Always use masked and protected variables
Enable "Mask variable" and "Protect variable" for all sensitive values
Use manual deployments for production
Add when: manual to production jobs to prevent accidental deployments
Limit variable scope
Set variables to specific environments when possible (production, staging, etc.)
Enable protected branches
Protect main/production branches and require merge requests for all changes
Check that:
Verify .gitlab-ci.yml syntax at CI/CD → Editor → Validate
Check the only and except rules match your branch or tag
Ensure the build job completes successfully and artifacts haven't expired
Need Help? Check GitLab CI/CD documentation or contact Focal Deploy support