
By Yamuno Team
25 Mar 2026
Engineering teams often end up with documentation scattered across two places: a docs/ folder in the repo, and a Confluence space that slowly falls behind. This guide shows you how to close that gap — whether you want a one-time migration, a bulk import, or a fully automated sync that keeps Confluence updated on every push.
Keeping docs in Git is great for developers, but it creates friction for everyone else. Product managers, support teams, and stakeholders usually live in Confluence — not in GitHub. When your README and architecture docs live in a repo, they become invisible to half the company.
At the same time, manually copy-pasting markdown into Confluence is tedious and breaks formatting. The goal is to do this once, correctly, and then keep it in sync automatically.
Best for: migrating a single README, architecture doc, or changelog.
.md or .mdx fileYour markdown — including headers, tables, code blocks, and images — is converted to native Confluence content.
Best for: migrating an entire docs/ folder from a repo in one shot.
git clone https://github.com/your-org/your-repo.git
cd your-repo
zip -r docs-export.zip docs/
For GitLab, it's the same process — or use the GitLab UI: Repository → Files → docs/ → Download this directory.
docs-export.zipThe importer converts your folder structure into a Confluence page tree. A structure like:
docs/
getting-started/
installation.md
quick-start.md
api/
overview.md
authentication.md
…becomes parent/child pages in Confluence exactly as you'd expect — no manual reorganisation needed.
If your markdown references local images (e.g. ), include the assets folder in the ZIP. The importer uploads them as Confluence attachments and rewires the image references automatically.
Best for: keeping Confluence permanently in sync with your repo. Every push updates the docs without anyone touching Confluence manually.
You'll need two things from the Markdown Importer app:
ci-cd-pipeline), set an expiration, and copy it immediatelyhttps://your-site.atlassian.net/wiki/apps/<app-id>/<env-id>/webtrigger
Settings → Secrets → CONFLUENCE_API_ENDPOINT + CONFLUENCE_API_TOKENSettings → CI/CD → Variables → same two variablesYou'll also need your Confluence space ID and parent page ID — both are visible in the app's space and page selectors.
Create .github/workflows/sync-docs.yml:
name: Sync docs to Confluence
on:
push:
branches: [main]
paths:
- "docs/**"
jobs:
sync:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Import README to Confluence
run: |
CONTENT=$(cat docs/README.md | jq -Rs .)
curl -X POST "${{ secrets.CONFLUENCE_API_ENDPOINT }}" \
-H "Authorization: Bearer ${{ secrets.CONFLUENCE_API_TOKEN }}" \
-H "Content-Type: application/json" \
-d "{\"spaceId\":\"DOCS\",\"parentId\":\"123456789\",\"pageTitle\":\"README\",\"content\":$CONTENT,\"overwrite\":true}"
- name: Import API docs
run: |
CONTENT=$(cat docs/api.md | jq -Rs .)
curl -X POST "${{ secrets.CONFLUENCE_API_ENDPOINT }}" \
-H "Authorization: Bearer ${{ secrets.CONFLUENCE_API_TOKEN }}" \
-H "Content-Type: application/json" \
-d "{\"spaceId\":\"DOCS\",\"parentId\":\"123456789\",\"pageTitle\":\"API Reference\",\"content\":$CONTENT,\"overwrite\":true}"
The API sends one page per request as JSON — add a step per file you want to keep in sync. The workflow only triggers when files under docs/ change.
Add to .gitlab-ci.yml:
stages:
- deploy-docs
deploy-confluence:
stage: deploy-docs
only:
- main
script:
- |
curl -X POST "$CONFLUENCE_API_ENDPOINT" \
-H "Authorization: Bearer $CONFLUENCE_API_TOKEN" \
-H "Content-Type: application/json" \
-d "{
\"spaceId\": \"DOCS\",
\"parentId\": \"123456789\",
\"pageTitle\": \"API Documentation\",
\"content\": $(cat docs/api.md | jq -Rs .),
\"overwrite\": true
}"
Store CONFLUENCE_API_ENDPOINT, CONFLUENCE_API_TOKEN as masked CI/CD variables in GitLab.
Organise before you import. Rename files with a numeric prefix if you care about order (01-overview.md, 02-installation.md). The importer preserves file order when importing via the UI.
Handle frontmatter. If your docs use YAML frontmatter (like many Docusaurus or MkDocs setups), the importer reads title from frontmatter and uses it as the Confluence page title. No need to strip it out.
Check internal links. Links between pages (e.g. [see here](../api/overview.md)) are resolved relative to the page hierarchy during import. Cross-check a few after importing to confirm they resolve correctly.
Run a test import first. Pick a small subfolder, import it into a sandbox space, and confirm the output looks right before running the full migration.
Install Markdown Exporter & Importer for Confluence from the Atlassian Marketplace — it's free to try.
For the CI/CD approach, check out the REST API documentation for the full list of parameters, authentication details, and code examples in Node.js, Python, and Go.
Questions or edge cases? Reach out via our support portal — we're happy to help with migration planning.
Get product updates and tips straight to your inbox.
No spam, ever.
A practical guide for Jira admins, Confluence admins, and IT/security teams to evaluate Atlassian Marketplace apps without slowing down adoption.
Read moreMigrate thousands of LaTeX equations between Confluence math apps with one click. No manual work, no lost formulas, no downtime.
Read moreLaTeX Math for Confluence is here! Create stunning mathematical equations with professional LaTeX rendering, live preview, and enterprise-grade security.
Read more