image

25 Mar 2026

ConfluenceAtlassianMarkdownGitHubGitLabDocumentationDevOpsMigrationCI/CDTechnical Writing

How to Migrate Your GitHub or GitLab Docs to Confluence

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.

Why Teams Move Docs to Confluence

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.


What You Need


Option 1 — Import a Single File

Best for: migrating a single README, architecture doc, or changelog.

  1. Open the target Confluence page where you want the content to live
  2. Click the ••• (More actions) menu → Markdown Importer & Exporter
  3. Select Import and choose your .md or .mdx file
  4. Preview the output, then click Import

Your markdown — including headers, tables, code blocks, and images — is converted to native Confluence content.


Option 2 — Bulk Import from a ZIP Archive

Best for: migrating an entire docs/ folder from a repo in one shot.

Step 1 — Clone and zip your docs folder

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.

Step 2 — Import the ZIP into Confluence

  1. In Confluence, navigate to the space or page where you want the docs imported
  2. Open the ••• menu → Markdown Importer & Exporter
  3. Select Import and upload your docs-export.zip
  4. Review the file tree — the importer shows all files and their resolved hierarchy before importing

Step 3 — Confirm hierarchy

The 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.

Step 4 — Handle images and attachments

If your markdown references local images (e.g. ![diagram](./assets/arch.png)), include the assets folder in the ZIP. The importer uploads them as Confluence attachments and rewires the image references automatically.


Option 3 — Automate with CI/CD (GitHub Actions / GitLab CI)

Best for: keeping Confluence permanently in sync with your repo. Every push updates the docs without anyone touching Confluence manually.

Prerequisites

You'll need two things from the Markdown Importer app:

  1. In Confluence, go to Settings → Apps → Markdown Importer for Confluence → API tab
  2. Click Create New Token, give it a label (e.g. ci-cd-pipeline), set an expiration, and copy it immediately
  3. On the same page, copy your unique API endpoint URL — it looks like:
    https://your-site.atlassian.net/wiki/apps/<app-id>/<env-id>/webtrigger
    
  4. Store both as secrets in your repo:
    • GitHub: Settings → SecretsCONFLUENCE_API_ENDPOINT + CONFLUENCE_API_TOKEN
    • GitLab: Settings → CI/CD → Variables → same two variables

You'll also need your Confluence space ID and parent page ID — both are visible in the app's space and page selectors.


GitHub Actions Workflow

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.


GitLab CI Pipeline

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.


Tips for a Clean Migration

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.


Getting Started

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.

Stay in the loop

Get product updates and tips straight to your inbox.

No spam, ever.

Related Articles

View all →
Forge-native vs Externally Hosted Atlassian Apps: A Practical Security Checklist
05 Mar 2026

Forge-native vs Externally Hosted Atlassian Apps: A Practical Security Checklist

A practical guide for Jira admins, Confluence admins, and IT/security teams to evaluate Atlassian Marketplace apps without slowing down adoption.

Read more
Switching LaTeX Math Apps in Confluence? We Made It Painless.
16 Feb 2026

Switching LaTeX Math Apps in Confluence? We Made It Painless.

Migrate thousands of LaTeX equations between Confluence math apps with one click. No manual work, no lost formulas, no downtime.

Read more
Beautiful Mathematical Equations in Confluence – Introducing LaTeX Math for Confluence!
07 Nov 2025

Beautiful Mathematical Equations in Confluence – Introducing LaTeX Math for Confluence!

LaTeX Math for Confluence is here! Create stunning mathematical equations with professional LaTeX rendering, live preview, and enterprise-grade security.

Read more