How to Sync Docs from GitHub to Confluence Automatically

10 Jun 2026

5 min read

How to Sync Docs from GitHub to Confluence Automatically

Documentation drift is one of the most common problems in engineering teams. The markdown files in your GitHub repo are accurate — they're updated as part of the same PR that changes the code. The Confluence pages that were supposed to mirror them were last updated six months ago by someone who has since left the team.

This guide shows how to wire GitHub and Confluence together so your repo is the single source of truth and Confluence stays in sync automatically, without anyone manually copying content between the two.


The Setup

The approach uses the Markdown Importer for Confluence REST API combined with a GitHub Actions workflow. When a push lands on your main branch, the workflow calls the API to import the updated markdown files into the right Confluence pages.

You need:

  • Markdown Importer for Confluence installed on your Confluence Cloud instance
  • A Confluence API token
  • A GitHub repository with markdown documentation

Step 1 — Generate a Confluence API Token

  1. Go to id.atlassian.com/manage-profile/security/api-tokens
  2. Click Create API token
  3. Give it a name ("GitHub Actions — Confluence Sync")
  4. Copy the token immediately — it's only shown once

Store it as a GitHub Actions secret: CONFLUENCE_API_TOKEN. Also store your Confluence email as CONFLUENCE_EMAIL and your Confluence base URL as CONFLUENCE_BASE_URL.


Step 2 — Find the Target Page ID

Each import needs a target Confluence page ID — the parent page where the markdown will be created or updated.

To find a page ID:

  1. Open the target Confluence page
  2. Click •••Page Information
  3. The ID is in the URL: ...confluence/pages/edit-v2/123456789

Or use the Confluence REST API:

GET https://your-instance.atlassian.net/wiki/rest/api/content?title=Your+Page+Name&spaceKey=YOURSPACE

Store the page ID as a GitHub Actions variable (CONFLUENCE_PAGE_ID).


Step 3 — Create the GitHub Actions Workflow

Create .github/workflows/sync-docs-to-confluence.yml:

name: Sync Docs to Confluence

on:
  push:
    branches:
      - main
    paths:
      - 'docs/**'

jobs:
  sync:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Zip docs folder
        run: zip -r docs.zip docs/

      - name: Import to Confluence
        run: |
          curl -X POST \
            "https://your-instance.atlassian.net/wiki/rest/api/content/${{ vars.CONFLUENCE_PAGE_ID }}/child/page" \
            -u "${{ secrets.CONFLUENCE_EMAIL }}:${{ secrets.CONFLUENCE_API_TOKEN }}" \
            -H "Content-Type: multipart/form-data" \
            -F "[email protected]" \
            -F "pageHierarchy=true" \
            -F "overwrite=true"

The paths filter means the workflow only triggers when files inside docs/ change — not on every push to main.


Step 4 — Use the Markdown Importer REST API Directly

The Markdown Importer REST API gives you more control than the UI. The key endpoint for automation:

POST /wiki/rest/atlassian-connect/1/addons/com.yamuno.markdown-importer/resources/api/import

Request body (multipart/form-data):

Field Value
file ZIP archive of markdown files
parentPageId Target parent page ID
spaceKey Target Confluence space key
pageHierarchy true to preserve folder structure as page hierarchy
overwrite true to update existing pages instead of creating duplicates

Authentication: HTTP Basic — email + API token.

A more complete workflow that handles individual files:

#!/bin/bash
# sync-to-confluence.sh

BASE_URL="${CONFLUENCE_BASE_URL}"
EMAIL="${CONFLUENCE_EMAIL}"
TOKEN="${CONFLUENCE_API_TOKEN}"
PAGE_ID="${CONFLUENCE_PAGE_ID}"
SPACE_KEY="${CONFLUENCE_SPACE_KEY}"

# Create ZIP from docs folder
zip -r docs.zip docs/

# Call the import API
curl -s -X POST \
  "${BASE_URL}/wiki/rest/atlassian-connect/1/addons/com.yamuno.markdown-importer/resources/api/import" \
  -u "${EMAIL}:${TOKEN}" \
  -F "[email protected]" \
  -F "parentPageId=${PAGE_ID}" \
  -F "spaceKey=${SPACE_KEY}" \
  -F "pageHierarchy=true" \
  -F "overwrite=true" \
  | jq '.'

Step 5 — Preserve Folder Structure as Confluence Hierarchy

If your docs folder looks like this:

docs/
├── getting-started.md
├── installation.md
├── how-to/
│   ├── first-steps.md
│   └── advanced-usage.md
└── reference/
    ├── api.md
    └── config.md

With pageHierarchy=true, the importer creates a matching Confluence page tree:

Getting Started
Installation
How To
  ├── First Steps
  └── Advanced Usage
Reference
  ├── API
  └── Config

Each folder becomes a parent page. The markdown file names become page titles (with hyphens replaced by spaces and the first letter capitalized).


Step 6 — Handle Frontmatter

Markdown Importer reads YAML frontmatter and uses it to set page metadata:

---
title: "API Reference"
---

If a title field is present, the importer uses it as the Confluence page title instead of the filename. This lets you have api.md in the repo but "API Reference" as the Confluence page title.

Other frontmatter fields are ignored for the import but preserved if you export back to markdown.


Testing the Workflow

Push a small change to a doc file on main and watch the Actions tab in GitHub. The workflow should:

  1. Trigger on the push event
  2. Zip the docs folder
  3. Call the import API
  4. Return a 200 with the list of pages created or updated

Check Confluence to confirm the pages reflect the changes. The first import creates the pages; subsequent imports update them in place (because overwrite=true).


Common Patterns

Sync only changed files: Use git diff --name-only HEAD~1 HEAD -- docs/ in the workflow to identify which files changed, then import just those files instead of the full folder. Faster and avoids unnecessary Confluence page updates.

Multiple doc sources: Run the import step multiple times with different source folders and target page IDs. You can sync docs/public/ to a customer-facing space and docs/internal/ to an internal space in the same workflow.

Preview before merging: Add the sync workflow to pull request branches targeting a staging Confluence space. Reviewers can check how the docs render in Confluence before the PR merges.


Result

Once the workflow is running, your documentation process is:

  1. Edit markdown in the repo
  2. Open a PR, get it reviewed, merge
  3. Confluence updates automatically within minutes

No manual copying. No drift. The engineers writing the code are the same people keeping the docs current — because it's the same commit.

Install Markdown Importer for Confluence →

Read the REST API documentation →

Featured App

Markdown Importer

Convert Between Markdown Files and Confluence Pages Effortlessly

Stay in the loop

Get product updates and tips straight to your inbox.

No spam, ever.

Related Articles

View all →
How to Bulk Import Markdown Files into Confluence
23 Jul 2026

How to Bulk Import Markdown Files into Confluence

Importing one markdown file at a time into Confluence is tedious. Here's how to import dozens or hundreds of markdown files at once — with folder hierarchy, images, and frontmatter preserved.

Read more
How to Build a Custom Status Dashboard in Confluence
18 Jul 2026

How to Build a Custom Status Dashboard in Confluence

Confluence doesn't have a built-in project status dashboard. Here's how to build one that shows RAG indicators, progress bars, countdown timers, and key metrics — using HTML Macro for Confluence.

Read more
Jira Reporting for Product Managers: Charts, Metrics, and Dashboards
14 Jul 2026

Jira Reporting for Product Managers: Charts, Metrics, and Dashboards

Product managers need a different view of Jira than engineering teams do. Here's how to set up charts and dashboards that track the metrics that actually matter for product decisions.

Read more