Skip to main content

CI/CD Integration

Integrate FireBackup into your CI/CD pipelines to automate backup verification, pre-deployment snapshots, and disaster recovery testing.

Overview

CI/CD integration enables:

  • Pre-deployment backups - Automatic snapshots before deployments
  • Backup verification - Validate backup integrity in pipelines
  • Database seeding - Restore backups to staging/test environments
  • Disaster recovery testing - Automated DR drills
  • Compliance automation - Scheduled backup audits

API Authentication

Generate API Key

  1. Go to SettingsAPI Keys
  2. Click Create API Key
  3. Set appropriate permissions:
    • backups:read - List and view backups
    • backups:write - Create backups
    • restores:write - Initiate restores
  4. Copy the API key

Using API Keys

Include in request headers:

curl -H "Authorization: Bearer YOUR_API_KEY" \
https://api.firebackup.io/api/v1/backups

Store securely in CI/CD secrets:

  • GitHub Actions: Repository secrets
  • GitLab CI: CI/CD variables
  • CircleCI: Environment variables
  • Jenkins: Credentials store

GitHub Actions

Pre-Deployment Backup

Create a backup before deploying to production:

# .github/workflows/deploy.yml
name: Deploy with Backup

on:
push:
branches: [main]

jobs:
backup-and-deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Create Pre-Deployment Backup
id: backup
run: |
RESPONSE=$(curl -s -X POST \
-H "Authorization: Bearer ${{ secrets.FIREBACKUP_API_KEY }}" \
-H "Content-Type: application/json" \
-d '{"projectId": "proj_abc123", "type": "full", "tags": ["pre-deploy", "github-actions"]}' \
https://api.firebackup.io/api/v1/backups)

BACKUP_ID=$(echo $RESPONSE | jq -r '.id')
echo "backup_id=$BACKUP_ID" >> $GITHUB_OUTPUT

- name: Wait for Backup Completion
run: |
BACKUP_ID=${{ steps.backup.outputs.backup_id }}
while true; do
STATUS=$(curl -s \
-H "Authorization: Bearer ${{ secrets.FIREBACKUP_API_KEY }}" \
https://api.firebackup.io/api/v1/backups/$BACKUP_ID | jq -r '.status')

echo "Backup status: $STATUS"

if [ "$STATUS" = "completed" ]; then
echo "Backup completed successfully"
break
elif [ "$STATUS" = "failed" ]; then
echo "Backup failed"
exit 1
fi

sleep 10
done

- name: Deploy to Production
run: |
# Your deployment steps
echo "Deploying..."

- name: Tag Backup as Deployed
if: success()
run: |
curl -X PATCH \
-H "Authorization: Bearer ${{ secrets.FIREBACKUP_API_KEY }}" \
-H "Content-Type: application/json" \
-d '{"tags": ["pre-deploy", "deployed", "${{ github.sha }}"]}' \
https://api.firebackup.io/api/v1/backups/${{ steps.backup.outputs.backup_id }}

Backup Verification

Verify backup integrity after creation:

# .github/workflows/verify-backups.yml
name: Verify Backups

on:
schedule:
- cron: '0 6 * * *' # Daily at 6 AM

jobs:
verify:
runs-on: ubuntu-latest
steps:
- name: Get Recent Backups
id: backups
run: |
BACKUPS=$(curl -s \
-H "Authorization: Bearer ${{ secrets.FIREBACKUP_API_KEY }}" \
"https://api.firebackup.io/api/v1/backups?limit=5&status=completed")

echo "backups=$BACKUPS" >> $GITHUB_OUTPUT

- name: Verify Backup Integrity
run: |
echo '${{ steps.backups.outputs.backups }}' | jq -r '.backups[] | .id' | while read BACKUP_ID; do
echo "Verifying backup: $BACKUP_ID"

VERIFY=$(curl -s -X POST \
-H "Authorization: Bearer ${{ secrets.FIREBACKUP_API_KEY }}" \
https://api.firebackup.io/api/v1/backups/$BACKUP_ID/verify)

VALID=$(echo $VERIFY | jq -r '.valid')

if [ "$VALID" != "true" ]; then
echo "Backup verification failed for $BACKUP_ID"
exit 1
fi

echo "Backup $BACKUP_ID verified successfully"
done

- name: Notify on Failure
if: failure()
uses: slackapi/slack-github-action@v1
with:
payload: |
{
"text": "⚠️ Backup verification failed",
"blocks": [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "Backup verification failed. Check the workflow logs."
}
}
]
}
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }}

Reusable Workflow

Create a reusable workflow for backups:

# .github/workflows/backup.yml
name: FireBackup

on:
workflow_call:
inputs:
project_id:
required: true
type: string
backup_type:
required: false
type: string
default: 'full'
wait_for_completion:
required: false
type: boolean
default: true
secrets:
api_key:
required: true
outputs:
backup_id:
description: 'Created backup ID'
value: ${{ jobs.backup.outputs.backup_id }}

jobs:
backup:
runs-on: ubuntu-latest
outputs:
backup_id: ${{ steps.create.outputs.backup_id }}
steps:
- name: Create Backup
id: create
run: |
RESPONSE=$(curl -s -X POST \
-H "Authorization: Bearer ${{ secrets.api_key }}" \
-H "Content-Type: application/json" \
-d '{"projectId": "${{ inputs.project_id }}", "type": "${{ inputs.backup_type }}"}' \
https://api.firebackup.io/api/v1/backups)

BACKUP_ID=$(echo $RESPONSE | jq -r '.id')
echo "backup_id=$BACKUP_ID" >> $GITHUB_OUTPUT

- name: Wait for Completion
if: ${{ inputs.wait_for_completion }}
run: |
# Wait logic here

Use in other workflows:

jobs:
backup:
uses: ./.github/workflows/backup.yml
with:
project_id: proj_abc123
secrets:
api_key: ${{ secrets.FIREBACKUP_API_KEY }}

deploy:
needs: backup
runs-on: ubuntu-latest
steps:
- run: echo "Backup ID: ${{ needs.backup.outputs.backup_id }}"

GitLab CI

Pre-Deployment Backup

# .gitlab-ci.yml
stages:
- backup
- deploy

variables:
FIREBACKUP_API_URL: https://api.firebackup.io/api/v1
PROJECT_ID: proj_abc123

backup:
stage: backup
image: curlimages/curl:latest
script:
- |
RESPONSE=$(curl -s -X POST \
-H "Authorization: Bearer $FIREBACKUP_API_KEY" \
-H "Content-Type: application/json" \
-d "{\"projectId\": \"$PROJECT_ID\", \"type\": \"full\", \"tags\": [\"gitlab-ci\", \"$CI_COMMIT_SHA\"]}" \
$FIREBACKUP_API_URL/backups)

BACKUP_ID=$(echo $RESPONSE | jq -r '.id')
echo "BACKUP_ID=$BACKUP_ID" >> backup.env

# Wait for completion
while true; do
STATUS=$(curl -s \
-H "Authorization: Bearer $FIREBACKUP_API_KEY" \
$FIREBACKUP_API_URL/backups/$BACKUP_ID | jq -r '.status')

if [ "$STATUS" = "completed" ]; then
echo "Backup completed"
break
elif [ "$STATUS" = "failed" ]; then
echo "Backup failed"
exit 1
fi

sleep 10
done
artifacts:
reports:
dotenv: backup.env
rules:
- if: $CI_COMMIT_BRANCH == "main"

deploy:
stage: deploy
needs:
- job: backup
artifacts: true
script:
- echo "Deploying with backup $BACKUP_ID"
# Your deployment steps
rules:
- if: $CI_COMMIT_BRANCH == "main"

Scheduled Backup Verification

# .gitlab-ci.yml
verify-backups:
stage: test
image: curlimages/curl:latest
script:
- |
# Get recent backups
BACKUPS=$(curl -s \
-H "Authorization: Bearer $FIREBACKUP_API_KEY" \
"$FIREBACKUP_API_URL/backups?limit=5&status=completed")

# Verify each backup
echo $BACKUPS | jq -r '.backups[].id' | while read BACKUP_ID; do
VERIFY=$(curl -s -X POST \
-H "Authorization: Bearer $FIREBACKUP_API_KEY" \
$FIREBACKUP_API_URL/backups/$BACKUP_ID/verify)

VALID=$(echo $VERIFY | jq -r '.valid')
if [ "$VALID" != "true" ]; then
echo "Verification failed for $BACKUP_ID"
exit 1
fi
done
rules:
- if: $CI_PIPELINE_SOURCE == "schedule"

CircleCI

Workflow Configuration

# .circleci/config.yml
version: 2.1

orbs:
slack: circleci/slack@4.12.0

commands:
create-backup:
parameters:
project_id:
type: string
steps:
- run:
name: Create FireBackup
command: |
RESPONSE=$(curl -s -X POST \
-H "Authorization: Bearer $FIREBACKUP_API_KEY" \
-H "Content-Type: application/json" \
-d '{"projectId": "<< parameters.project_id >>", "type": "full"}' \
https://api.firebackup.io/api/v1/backups)

echo $RESPONSE | jq -r '.id' > /tmp/backup_id

wait-for-backup:
steps:
- run:
name: Wait for Backup
command: |
BACKUP_ID=$(cat /tmp/backup_id)
while true; do
STATUS=$(curl -s \
-H "Authorization: Bearer $FIREBACKUP_API_KEY" \
https://api.firebackup.io/api/v1/backups/$BACKUP_ID | jq -r '.status')

if [ "$STATUS" = "completed" ]; then
exit 0
elif [ "$STATUS" = "failed" ]; then
exit 1
fi
sleep 10
done

jobs:
backup-and-deploy:
docker:
- image: cimg/base:stable
steps:
- checkout
- create-backup:
project_id: proj_abc123
- wait-for-backup
- run:
name: Deploy
command: echo "Deploying..."
- slack/notify:
event: fail
template: basic_fail_1

workflows:
deploy:
jobs:
- backup-and-deploy:
filters:
branches:
only: main

Jenkins

Pipeline Script

// Jenkinsfile
pipeline {
agent any

environment {
FIREBACKUP_API_KEY = credentials('firebackup-api-key')
PROJECT_ID = 'proj_abc123'
}

stages {
stage('Pre-Deployment Backup') {
steps {
script {
def response = httpRequest(
url: 'https://api.firebackup.io/api/v1/backups',
httpMode: 'POST',
customHeaders: [
[name: 'Authorization', value: "Bearer ${FIREBACKUP_API_KEY}"],
[name: 'Content-Type', value: 'application/json']
],
requestBody: """{"projectId": "${PROJECT_ID}", "type": "full", "tags": ["jenkins", "${BUILD_NUMBER}"]}"""
)

def json = readJSON text: response.content
env.BACKUP_ID = json.id
echo "Created backup: ${env.BACKUP_ID}"
}
}
}

stage('Wait for Backup') {
steps {
script {
def completed = false
while (!completed) {
def response = httpRequest(
url: "https://api.firebackup.io/api/v1/backups/${env.BACKUP_ID}",
customHeaders: [
[name: 'Authorization', value: "Bearer ${FIREBACKUP_API_KEY}"]
]
)

def json = readJSON text: response.content

if (json.status == 'completed') {
completed = true
echo "Backup completed successfully"
} else if (json.status == 'failed') {
error "Backup failed"
} else {
sleep 10
}
}
}
}
}

stage('Deploy') {
steps {
echo "Deploying with backup ${env.BACKUP_ID}"
// Your deployment steps
}
}
}

post {
failure {
slackSend(
color: 'danger',
message: "Deployment failed. Backup ID: ${env.BACKUP_ID}"
)
}
}
}

Use Cases

Pre-Deployment Snapshots

Create a backup before every production deployment:

#!/bin/bash
# pre-deploy-backup.sh

set -e

PROJECT_ID=$1
API_KEY=$FIREBACKUP_API_KEY

echo "Creating pre-deployment backup..."

# Create backup
BACKUP=$(curl -s -X POST \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d "{\"projectId\": \"$PROJECT_ID\", \"type\": \"full\", \"tags\": [\"pre-deploy\"]}" \
https://api.firebackup.io/api/v1/backups)

BACKUP_ID=$(echo $BACKUP | jq -r '.id')
echo "Backup ID: $BACKUP_ID"

# Wait for completion
while true; do
STATUS=$(curl -s \
-H "Authorization: Bearer $API_KEY" \
https://api.firebackup.io/api/v1/backups/$BACKUP_ID | jq -r '.status')

case $STATUS in
completed)
echo "Backup completed successfully"
echo $BACKUP_ID
exit 0
;;
failed)
echo "Backup failed"
exit 1
;;
*)
echo "Status: $STATUS"
sleep 10
;;
esac
done

Staging Environment Seeding

Restore production data to staging:

#!/bin/bash
# seed-staging.sh

set -e

PROD_PROJECT_ID=$1
STAGING_PROJECT_ID=$2
API_KEY=$FIREBACKUP_API_KEY

echo "Getting latest production backup..."

# Get latest backup
LATEST_BACKUP=$(curl -s \
-H "Authorization: Bearer $API_KEY" \
"https://api.firebackup.io/api/v1/backups?projectId=$PROD_PROJECT_ID&status=completed&limit=1" \
| jq -r '.backups[0].id')

echo "Latest backup: $LATEST_BACKUP"

echo "Restoring to staging..."

# Restore to staging
RESTORE=$(curl -s -X POST \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d "{\"targetProjectId\": \"$STAGING_PROJECT_ID\", \"options\": {\"clearExisting\": true}}" \
https://api.firebackup.io/api/v1/backups/$LATEST_BACKUP/restore)

RESTORE_ID=$(echo $RESTORE | jq -r '.id')
echo "Restore ID: $RESTORE_ID"

# Wait for completion
# ... similar wait logic

Disaster Recovery Testing

Automated DR drill:

# .github/workflows/dr-drill.yml
name: Disaster Recovery Drill

on:
schedule:
- cron: '0 2 1 * *' # Monthly at 2 AM

jobs:
dr-drill:
runs-on: ubuntu-latest
steps:
- name: Get Random Production Backup
id: backup
run: |
BACKUP=$(curl -s \
-H "Authorization: Bearer ${{ secrets.FIREBACKUP_API_KEY }}" \
"https://api.firebackup.io/api/v1/backups?status=completed&limit=10" \
| jq -r '.backups[0]')

BACKUP_ID=$(echo $BACKUP | jq -r '.id')
echo "backup_id=$BACKUP_ID" >> $GITHUB_OUTPUT

- name: Restore to DR Environment
run: |
curl -X POST \
-H "Authorization: Bearer ${{ secrets.FIREBACKUP_API_KEY }}" \
-H "Content-Type: application/json" \
-d '{"targetProjectId": "proj_dr_test", "options": {"clearExisting": true}}' \
https://api.firebackup.io/api/v1/backups/${{ steps.backup.outputs.backup_id }}/restore

- name: Validate Restored Data
run: |
# Run validation tests against DR environment
npm run test:dr-validation

- name: Report Results
run: |
curl -X POST ${{ secrets.SLACK_WEBHOOK }} \
-H "Content-Type: application/json" \
-d '{
"text": "✅ DR Drill completed successfully",
"attachments": [{
"fields": [
{"title": "Backup ID", "value": "${{ steps.backup.outputs.backup_id }}"},
{"title": "Restore Time", "value": "'$(date)'"}
]
}]
}'

Best Practices

Security

  • Store API keys in CI/CD secrets, never in code
  • Use scoped keys with minimal permissions
  • Rotate keys periodically
  • Audit key usage via API logs

Reliability

  • Always wait for backup completion before deploying
  • Implement timeouts to avoid hanging pipelines
  • Add notifications for failures
  • Tag backups with commit SHA for traceability

Performance

  • Run backups in parallel when possible
  • Use incremental backups for faster completion
  • Cache backup status to reduce API calls