Confluence to Markdown in Git, step by step.
Moving out of Confluence is mostly mechanical, and the mechanical parts are well trodden: export, convert, reshape, commit. What trips teams up is the expectation that everything survives. It does not, and knowing exactly what you lose before you start is the difference between a weekend and a quarter. This guide covers both.
Do one space first. Pick the space people actually read, migrate it end to end, and publish it. Everything you learn there changes how you do the rest — and quite often the rest turns out not to be worth migrating at all.
A Confluence importer is on the roadmap. DocuCommit already ships an importer for Google Sites exports that turns a zip into a structured project in one step, and a Confluence equivalent is planned on the same foundation — it would collapse steps 01 to 03 below into a single action in the editor. It is not built yet and there is no date, so treat the manual route on this page as the real answer: it works today, and it is what we would use ourselves.
Get the content out of Confluence.
Export the space as HTML. It preserves rendered content — tables, headings, and images as files — which is what converts cleanly. XML export is a full backup intended for re-importing into Confluence, and PDF is a dead end for migration.
The menu path differs by product. On Confluence Data Center and Server it is Space tools → Content tools → Export. On Confluence Cloud it is under Space settings → Export space, and a full HTML export requires admin rights. Either way you get a zip containing one HTML file per page plus an attachments directory.
Unzip it somewhere outside your repository. You will be reshaping this material, and you want the original untouched to go back to.
HTML to Markdown with pandoc.
Pandoc handles this well and is the standard tool for the job. Target GitHub-flavoured Markdown, and turn off line wrapping so future diffs stay readable — a re-wrapped paragraph shows up as a whole changed block.
# One HTML page to GitHub-flavoured Markdown
pandoc -f html -t gfm --wrap=none -o overview.md overview.html
# The whole export directory, preserving names
find . -name '*.html' -print0 | while IFS= read -r -d '' f; do
pandoc -f html -t gfm --wrap=none -o "${f%.html}.md" "$f"
done Convert a handful of pages first and read the output before running it across hundreds. The quality of the export decides everything downstream, and it is far cheaper to adjust the command than to fix the results by hand.
Folders, frontmatter, attachments.
DocuCommit reads a directory of Markdown files. Folders become sections,
an index.md is that section's own landing page, and a
_attachments directory holds images and files without being
treated as pages. Numeric prefixes on folders keep the order obvious in
any file browser.
docs/
├── 01-getting-started/
│ ├── index.md # the section's own landing page
│ ├── overview.md
│ └── install.md
├── 02-guides/
│ ├── index.md
│ └── basics.md
└── _attachments/ # images and files, never treated as pages
Each page carries a small YAML frontmatter block. title is
the display name, sort orders pages within a section, and
tags feed search filters. A slug can pin the
URL when you need to preserve an existing link.
---
title: Getting Started
sort: 1
tags:
- onboarding
- internal
---
# Getting Started
The body is ordinary Markdown from here down. Confluence exports flat filenames; the hierarchy lives in the navigation, not the file system. Rebuilding that hierarchy as folders is the one genuinely manual step, and it is worth doing deliberately rather than scripting — it is the moment you get to drop the pages nobody has opened since 2019.
The predictable breakage, in the order you will hit it.
- Macros. Info, note, and warning panels convert to plain blockquotes or stray divs. Decide on one replacement convention and apply it everywhere.
- Table of contents macros. Delete them. The rendered docs generate navigation themselves.
- Internal links. Confluence links point at page IDs or titles. They need rewriting to relative file paths, and this is where a scripted find-and-replace earns its keep.
- Attachment paths. Point every image at
_attachmentsand delete the copies pandoc leaves scattered around. - Include and excerpt macros. These have no equivalent. Inline the content — usually a relief, since transclusion is why nobody could find anything.
- Jira and dynamic macros. Nothing to convert. Replace with a link to the live system.
- Code blocks. Usually survive, but language hints are frequently lost. Worth a pass if your docs are code-heavy.
Make Git the source of truth.
Once the tree looks right, initialise the repository. From this commit onwards, history is Git history: every later edit is a commit with an author and a message, and a clone is a complete backup.
cd docs
git init
git add .
git commit -m "Import documentation from Confluence"
git remote add origin git@github.com:your-org/docs.git
git push -u origin main Then open the repository in the DocuCommit editor and read through it. Point the read-only server at the same repository when you are happy, and the whole organisation can read the result without installing anything.
What does not come across.
No migration path moves these, including this one. Plan around them rather than discovering them halfway:
- Page history. You get one import commit, not fifteen years of Confluence revisions. If that history is legally or operationally important, keep Confluence in read-only mode for as long as you need it rather than trying to convert it.
- Comments. Confluence comments do not export in usable form. Where a comment thread holds a real decision, promote it into the page body before you migrate — that is where it belonged anyway.
- Labels. These map to
tagsin frontmatter, but not automatically — it is a scripted pass if you want to keep them. - Anything dynamic. Live Jira queries, report macros, and user-directory widgets become static text or links.
- Page restrictions do not translate. DocuCommit's server has no per-page read control; if some spaces must stay private, give them their own repository and server instance.
- Attachment volume matters. Years of Confluence attachments land in Git history permanently, and DocuCommit does not support Git LFS — prune what you export, and keep originals of large media outside the docs repository.
The trade you are making is a one-time loss of trail in exchange for content that is plain files from then on — readable, diffable, greppable, and never needing migration again.
Land it as Markdown you own.
Start a 14-day trial, no card required. After the import, every page is a plain Markdown file in your own Git repo — including if you leave.