gitlab-backup: Back Up and Restore GitLab Projects from the Command Line
Source code: github.com/sgaunet/gitlab-backup
When you self-host GitLab or rely heavily on gitlab.com for your projects, having an independent backup strategy is essential. GitLab’s own backup mechanism works well at the instance level, but sometimes you need something more surgical: back up a single project, or an entire group, on demand, and store the result locally or in S3. That’s what gitlab-backup does.
What started as a quick ops script has grown into a fully-featured CLI suite with backup and restore capabilities, S3 storage support, pre/post hooks, rate limiting, and concurrent group exports.
Features
- Export a single GitLab project or an entire group recursively
- Uses GitLab’s native Export/Import API — archives include the full project (repository, wiki, issues, merge requests, labels, and more)
- Two storage backends: local filesystem or S3-compatible storage
- Pre/post backup hooks for custom logic (notifications, encryption, etc.)
- Configurable rate limiting to stay within GitLab API quotas
- Concurrent exports when backing up a group
- Companion
gitlab-restoretool to restore archives back to GitLab - Three configuration methods: YAML config file, CLI flags, or environment variables — with a clear precedence order
- Distributed as a single binary for Linux, macOS, and Windows
Installation
From GitHub Releases
Download the latest binary for your platform from the releases page.
Homebrew
brew tap sgaunet/homebrew-tools
brew install sgaunet/tools/gitlab-backup
brew install sgaunet/tools/gitlab-restore
Configuration
gitlab-backup supports three configuration methods. Settings are applied in this priority order:
- CLI flags (highest priority)
- YAML configuration file
- Environment variables
Configuration File
The most flexible approach for repeated use. Create a config.yaml:
# debuglevel: "info"
gitlabGroupID: 123 # backup an entire group
# gitlabProjectID: 456 # or a single project
localpath: "/backup"
gitlabtoken: # or use the GITLAB_TOKEN env var
# gitlaburi: https://gitlab.com
# tmpdir: /tmp
# exportTimeoutMins: 10 # increase for large projects
hooks:
prebackup: ""
postbackup: ""
# Optional: store backups in S3 instead of localpath
s3cfg:
endpoint: "https://s3.amazonaws.com"
bucketName: "my-gitlab-backups"
bucketPath: "backups"
region: "eu-west-1"
accesskey: ""
secretkey: ""
Then run:
gitlab-backup -c config.yaml
CLI Flags
Useful for one-off backups or scripting without a config file:
# Back up a single project
export GITLAB_TOKEN=glpat-xxxxx
gitlab-backup --project-id 123 --output /backup
# Back up an entire group
gitlab-backup --group-id 456 --output /backup/groups
# Use a config file but override the timeout
gitlab-backup --config production.yaml --timeout 30
# Back up from a self-hosted GitLab instance
gitlab-backup \
--project-id 123 \
--output /backup \
--gitlab-url https://gitlab.mycompany.com \
--timeout 60
Available Flags
| Flag | Description | Default |
|---|---|---|
--config, -c | Path to YAML config file | (optional) |
--group-id | GitLab group ID to back up | 0 |
--project-id | GitLab project ID to back up | 0 |
--output | Output directory for local storage | "" |
--timeout | Export timeout in minutes | 10 |
--tmpdir | Temporary directory | /tmp |
--gitlab-url | GitLab API base URL | https://gitlab.com |
--version, -v | Show version and exit | |
--cfg | Print resolved configuration and exit |
Environment Variables
All settings can also be provided via environment variables:
GITLAB_TOKEN # GitLab personal access token (required)
GITLAB_URI # GitLab base URL (default: https://gitlab.com)
GITLABGROUPID # Group ID
GITLABPROJECTID # Project ID
LOCALPATH # Output directory
EXPORT_TIMEOUT_MIN # Export timeout in minutes (default: 10)
TMPDIR # Temp directory (default: /tmp)
PREBACKUP # Pre-backup hook command
POSTBACKUP # Post-backup hook command
S3ENDPOINT # S3 endpoint URL
S3BUCKETNAME # S3 bucket name
S3BUCKETPATH # S3 bucket path prefix
S3REGION # S3 region
AWS_ACCESS_KEY_ID # AWS/S3 access key
AWS_SECRET_ACCESS_KEY # AWS/S3 secret key
Archive Format
Each backup produces a standard tar.gz archive named {projectName}-{projectID}.tar.gz. The archive is generated by GitLab’s native export API and contains the complete project state: repository, wiki, issues, merge requests, labels, and all associated metadata.
Restoring a Backup
The gitlab-restore companion tool restores archives created by gitlab-backup. It validates that the target project is empty before proceeding, then imports the full project via GitLab’s Import/Export API.
Restore from a Local Archive
gitlab-restore \
--config config.yaml \
--archive /backup/myproject-123.tar.gz \
--namespace mygroup \
--project restored-project
Restore from S3
gitlab-restore \
--config config.yaml \
--archive s3://my-bucket/backups/myproject-123.tar.gz \
--namespace mygroup \
--project restored-project
Force-Overwrite an Existing Project
gitlab-restore \
--config config.yaml \
--archive /backup/myproject-123.tar.gz \
--namespace mygroup \
--project existing-project \
--overwrite # ⚠️ skips the emptiness check
Restore Process
The restore operation goes through these phases:
- Validation — checks that the target project is empty (unless
--overwriteis set) - Download — fetches the archive from S3 (if applicable)
- Extraction — unpacks the archive to a temp directory
- Import — imports the project via GitLab’s Import/Export API
- Cleanup — removes temporary files
Requirements:
- The target GitLab project must already exist (create it via the UI or API first)
- The token must have Maintainer or Owner permissions on the target project
- For S3 restores: AWS credentials with read access to the bucket
Going Further: Encrypted S3 Backups
If you need to encrypt your archives before uploading them to S3, the companion project gitlab-backup2s3 combines gitlab-backup with gocrypt to produce encrypted, S3-stored backups in a single workflow.
Development
The project uses Task as its build tool and goreleaser for releases.
task build # Build the binary
task snapshot # Create a snapshot release
task release # Create a tagged release
task doc # Start a local godoc server
task image # Build and push the Docker image