Migration guide

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.

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.

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.

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.

  • 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 _attachments and 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.

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.

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 tags in 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.

Migrate once, never 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.