Skip to content
All posts

Confluence6 min readYamuno Team

How to Migrate GitHub or GitLab Docs to Confluence

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 an 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 and select Markdown Importer & Exporter
  3. Choose your .md file
  4. Preview the output, then click Import

Your Markdown, including headings, tables, code blocks, and images, is converted to native Confluence content.

If your docs use MDX (as Docusaurus sites often do), treat the files as Markdown with extras. Standard Markdown converts, but JSX components and imports are not standard syntax and won't render as components. Rename .mdx files to .md and remove or replace components before importing.


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. You can also open the docs/ folder in the GitLab UI and use the download menu to download just that 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 and select Markdown Importer & Exporter
  3. Upload your docs-export.zip
  4. Preview the content and review any conflicts the importer flags (for example, pages that already exist), then click Import

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 and child pages in Confluence, with each folder as a parent page. Page titles must be unique within a Confluence space, so rename duplicate file names (such as several overview.md files) before importing. The bulk import guide covers this in more detail.

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

On GitHub, the shortest route is the open source confluence-markdown-sync Action, which publishes every Markdown file in a folder under one parent page. The workflows below call the API directly, which suits GitLab or setups that need more control.

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 → Secrets → CONFLUENCE_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.

Tokens expire after at most 30 days, so put a reminder in place to create a new one and update the CI secret. The API handles page content only: local images are not uploaded, so link images by public URL or upload them separately with Confluence's attachment API.


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\":\"98765432\",\"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\":\"98765432\",\"parentId\":\"123456789\",\"pageTitle\":\"API Reference\",\"content\":$CONTENT,\"overwrite\":true}"

The API takes one page per request as JSON, so this simple version has a step per file. The workflow only triggers when files under docs/ change. For a version that finds the changed files with git diff and loops over them, with a parent page per folder, see how to sync GitHub docs to Confluence automatically.


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\": \"98765432\",
          \"parentId\": \"123456789\",
          \"pageTitle\": \"API Documentation\",
          \"content\": $(cat docs/api.md | jq -Rs .),
          \"overwrite\": true
        }"

Store CONFLUENCE_API_ENDPOINT and CONFLUENCE_API_TOKEN as masked CI/CD variables in GitLab.


Tips for a Clean Migration

Organise before you import. Files are imported in the order they are selected, and consistent naming helps keep that order predictable. A numeric prefix (01-overview.md, 02-installation.md) is a simple way to do it.

Handle front matter. If your docs use YAML front matter (like many Docusaurus or MkDocs setups), you don't need to strip it out. The import options include importing front matter as page properties. Check a test import to see how your fields come through.

Check internal links. Relative links between Markdown files (e.g. [see here](../api/overview.md)) may not always resolve to the right Confluence page. Cross-check them after importing and update any that point to the wrong place.

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

Markdown Importer for Confluence Cloud

Import Markdown files into Confluence and export pages back to Markdown

Use caseTechnical documentationMarkdown, docs from Git, code, math and clean PDFs in ConfluenceSee the full setup

Related articles

All posts