Jira Project Backup & Restore
Backup and restore individual Jira Cloud projects via REST API — issues, comments, worklogs, attachments, boards, and sprints. Fully resumable, with an interactive menu and CLI mode.
Why?
Jira Cloud has no built-in per-project backup/restore. The only native option was the full-instance Backup Manager, which Atlassian deprecated in March 2026. This tool fills the gap using standard REST API v3 endpoints.
Features
| Feature | Description |
|---|---|
| Full project backup | Metadata, components, versions, roles, issues (all fields + changelog), worklogs, attachments, agile boards, sprints |
| 5-phase restore | Issues (epics first, subtasks last), links, comments, worklogs, attachments |
| Multi-project | Backup dozens of projects in a single run |
| Skip existing | --skip-existing skips projects that already have a complete backup |
| Auto-cleanup | Incomplete/partial backup folders are automatically removed before each run |
| Resumable | Safely re-run after interruption — already-processed items are skipped |
| Memory efficient | Issues stream directly to disk — backup 18 000+ issues on a 1 GB host without OOM |
| Verified backups | Detects silent truncation (count check), records SHA-256 checksums, and validates on demand |
| Dry-run mode | Preview all restore actions without making any API calls |
| Rate-limit aware | Exponential backoff with 429 / Retry-After detection |
| CSV export | Export backup data to CSV files for reporting, auditing, and sharing |
| Backup inspection | Detailed breakdown of issue types, statuses, priorities, and assignees |
| Connection test | Pre-flight check: authentication, project access, and server info |
| Interactive menu | Guided workflow with organised sections and post-operation summaries |
| CLI mode | --backup / --restore / --export-csv flags for scripted or cron use |
Quick Start
1. Install
Via PyPI (recommended):
pip install jira-project-backup-restore
Or clone for development:
git clone https://github.com/davidmalko87/jira-project-backup-restore.git
cd jira-project-backup-restore
pip install -r requirements.txt
2. Configure
cp .env.example .env
Edit .env with your Jira Cloud credentials:
JIRA_URL=https://your-domain.atlassian.net
JIRA_EMAIL=you@example.com
JIRA_API_TOKEN=your-api-token
Generate an API token at id.atlassian.com/manage-api-tokens
3. Run
Interactive menu:
python main.py
==================================================
Jira Backup & Restore Tool v1.5.0
==================================================
Instance : https://your-domain.atlassian.net
Auth : API Token
Backups : ./backups
--- Backup & Restore ---
1) Backup project(s)
2) Restore project from backup
--- Browse & Analyze ---
3) List existing backups
4) Validate backup integrity
5) Upload attachments only
6) Export backup to CSV
7) Inspect backup details
--- Settings & Tools ---
8) Test Jira connection
9) Show current configuration
10) Cleanup incomplete backups
0) Exit
CLI — backup:
python main.py --backup PROJ
python main.py --backup PROJ1,PROJ2
python main.py --backup PROJ1,PROJ2 --skip-existing
CLI — restore:
python main.py --restore backups/PROJ_20260322_143000 --target NEWPROJ
python main.py --restore backups/PROJ_20260322_143000 --target NEWPROJ --dry-run
CLI — export to CSV:
python main.py --export-csv backups/PROJ_20260322_143000
python main.py --export-csv backups/PROJ_20260322_143000 --output-dir /tmp/report
CLI — validate a backup (exits non-zero on problems, for scripts/cron):
python main.py --validate backups/PROJ_20260322_143000
What Gets Backed Up
| File | Contents |
|---|---|
project_meta.json |
Project config — name, lead, category, permission scheme |
components.json |
All project components |
versions.json |
All fix versions |
roles.json |
Role-to-member mappings |
issues.json |
All issues — every field, changelog history |
worklogs/worklogs.json |
Time tracking entries per issue |
attachments/<KEY>/ |
Binary attachment files, streamed to disk |
boards.json |
Agile board list |
board_<id>_config.json |
Board columns and swimlanes |
board_<id>_sprints.json |
Sprint history |
manifest.json |
File index, SHA-256 checksums, completeness counts, and a complete flag |
Restore Phases
Each phase can be toggled individually and is fully resumable via restore_progress.json:
| Phase | What happens | Endpoint |
|---|---|---|
| 1 | Create issues — epics first, then regular, then subtasks | POST /rest/api/3/issue |
| 2 | Restore issue links (outward only, no duplicates) | POST /rest/api/3/issueLink |
| 3 | Add comments — original author and date prepended as text | POST /rest/api/3/issue/{key}/comment |
| 4 | Add worklogs — original author prepended as text | POST /rest/api/3/issue/{key}/worklog |
| 5 | Upload attachments — skips duplicates by filename | POST /rest/api/3/issue/{key}/attachments |
| 6 | Restore statuses (opt-in, best-effort) — single workflow transition to the original status | POST /rest/api/3/issue/{key}/transitions |
Phases 1–5 run by default. Phase 6 is opt-in (it fires workflow rules and notifications): enable it with --with-statuses on the CLI, or by selecting 6 in the interactive phase prompt.
Issue key mapping between source and target is saved in key_mapping.json inside the backup directory.
✅ Round-trip Verified
The backup→restore round-trip has been proven end-to-end against a live Jira Cloud site: a project was backed up, restored into a fresh project, and diffed via the API — issue count, types, hierarchy (epics/subtasks), ADF bodies, labels, links (no duplicates), comments, worklogs, and attachment bytes (SHA-256) all matched. A backup is only proven once it has been restored end-to-end and verified; structural checks alone are necessary but not sufficient.
Known Limitations
These are Jira Cloud REST API constraints — not tool limitations:
| Data | Status | Notes |
|---|---|---|
| Timestamps (created/updated) | Not restorable | Cloud API blocks setting these fields |
| Changelog / history | Backup only | No write endpoint exists |
| Comment / worklog author | Text attribution | [Originally by Name on Date] prepended; original ADF body preserved |
| Reporter / Assignee | Conditional | Restored only if the user's email exists in the target instance |
| Issue status | Best-effort (opt-in) | Phase 6 restores it via a single workflow transition; statuses needing a multi-step path or a required-field screen stay at the project default |
Issue keys (e.g. KEY-123) |
Reassigned | Cloud assigns new keys; old→new mapping is saved |
Project Structure
jira-project-backup-restore/
├── main.py # Entry point — interactive menu + CLI flags
├── .env.example # Configuration template
├── requirements.txt # Python dependencies
│
├── jira_tool/
│ ├── config.py # .env loader and validation
│ ├── auth.py # Session builder (API token / cookie auth)
│ ├── api_client.py # HTTP client with retry and rate-limit handling
│ ├── backup.py # BackupManager — orchestrates full project backup
│ ├── restore.py # RestoreManager — 5-phase restore
│ ├── export.py # CSV export and backup statistics
│ ├── attachments.py # Standalone attachment uploader
│ ├── adf.py # Atlassian Document Format helpers
│ ├── progress.py # Resumability tracker
│ ├── utils.py # Logging, JSON I/O, utilities
│ ├── cli.py # Console-script entry point
│ └── menu.py # Interactive CLI menu
│
└── backups/ # Backup output directory (gitignored)
└── PROJ_20260322_143000/
├── manifest.json # Completion marker + file index
├── issues.json
├── attachments/
└── ...
Configuration Reference
All settings live in .env (copy from .env.example):
| Variable | Required | Default | Description |
|---|---|---|---|
JIRA_URL |
Yes | — | Jira Cloud base URL (no trailing slash) |
JIRA_EMAIL |
Yes* | — | Account email for API token auth |
JIRA_API_TOKEN |
Yes* | — | API token — generate here |
JIRA_COOKIE_HEADER |
Alt* | — | Full Cookie: header value for SSO auth |
JIRA_VERIFY_SSL |
No | true |
Set false to skip SSL verification |
BACKUP_ROOT |
No | ./backups |
Directory where backups are written |
PAGE_SIZE |
No | 100 |
Issues per API page (max 100) |
MAX_RETRIES |
No | 3 |
Retry count on transient failures |
READ_TIMEOUT |
No | 30 |
HTTP read timeout in seconds |
API_DELAY |
No | 0.2 |
Seconds to wait between API calls |
INCLUDE_ATTACHMENTS |
No | true |
Download attachment binary files |
INCLUDE_CHANGELOG |
No | true |
Include field change history in issues |
INCLUDE_WORKLOGS |
No | true |
Include time tracking entries |
LEGACY_KEY_JQL_TEMPLATE |
No | — | JQL template for standalone attachment upload |
* Either
JIRA_EMAIL+JIRA_API_TOKENorJIRA_COOKIE_HEADERis required.
Requirements
- Python 3.10+
requests>= 2.28python-dotenv>= 1.0
Changelog
See CHANGELOG.md for the full version history.
Contributing
See CONTRIBUTING.md for the versioning policy and how to bump the version when making changes.
License
Metadata
Release files for jira-project-backup-restore 1.5.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| jira_project_backup_restore-1.5.0.tar.gz | 41.4 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| jira_project_backup_restore-1.5.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 85.1 kB
Release files / jira_project_backup_restore-1.5.0.tar.gz
| Download URL | jira_project_backup_restore-1.5.0.tar.gz |
|---|---|
| Size | 41.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
413dd1cf316de1fcc0ed515dcf5f60b6717053ebd766b6336dc103aa393d82b6
|
|
BLAKE2b-256 checksum How to use checksums |
e37ce7f755bb56ccd46ccc52ca002cf2a8b2603f8493c40bb1e230010b01a629
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jun 2, 2026.
Transparency logRelease files / jira_project_backup_restore-1.5.0-py3-none-any.whl
| Download URL | jira_project_backup_restore-1.5.0-py3-none-any.whl |
|---|---|
| Size | 43.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
9606ed2e6445bb1ca979ab1880dedcf1c45e9bb6bb27e084a992a8a9966aaf62
|
|
BLAKE2b-256 checksum How to use checksums |
e46dc0aa13b4e78a927988e09f65a07633d0cf20c0cb53f33fd328d08a9fe850
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jun 2, 2026.
Transparency log