Repository navigation
Configuration
You need two things from Notion: an integration token and the ID of the page that will be the root of your sync.
- Go to notion.so/my-integrations and create a new internal integration.
- Copy its token. New tokens start with
ntn_; oldersecret_tokens also work.
- Open the page you want to sync under.
- In the page's
•••menu, open Connections and add your integration. Sub-pages inherit the access. - Copy the page ID: the 32-character hex string at the end of the page URL.
Run this in your project folder:
notion-md-sync initIt asks for the token, the parent page ID and a Markdown folder (default ./docs), then creates:
| File | Contents |
|---|---|
config.yaml |
Settings |
.env |
Your token and page ID (written with mode 0600) |
.env.example |
A blank template you can commit |
docs/welcome.md |
A sample page to push |
init needs an interactive terminal. It always asks for the token, even if NOTION_MD_SYNC_NOTION_TOKEN is already set. If a config.yaml already exists it changes nothing.
Put your secrets in a .env file (never commit it):
NOTION_MD_SYNC_NOTION_TOKEN=ntn_your_token_here
NOTION_MD_SYNC_NOTION_PARENT_PAGE_ID=your_page_id_hereAnd create config.yaml. The repo has a template at config/config.template.yaml:
notion:
token: "" # leave empty, set NOTION_MD_SYNC_NOTION_TOKEN
parent_page_id: "" # leave empty, set NOTION_MD_SYNC_NOTION_PARENT_PAGE_ID
directories:
markdown_root: ./docs
excluded_patterns:
- '*.tmp'
- 'node_modules/**'
- '.git/**'
sync:
conflict_resolution: newer # newer, notion_wins, markdown_wins, diffThen check it:
notion-md-sync verifyConfig file, first match wins:
- The path given with
--config/-c ./config.yaml./configs/config.yaml~/.notion-md-sync/config.yaml
A config file is optional if both environment variables are set. The repo's template lives in config/ (singular), which is not searched. Copy it to ./config.yaml or pass -c.
.env file, first match wins: ./.env, then ~/.notion-md-sync/.env, then up to three parent folders.
Environment variables override the config file:
| Variable | Setting |
|---|---|
NOTION_MD_SYNC_NOTION_TOKEN |
notion.token |
NOTION_MD_SYNC_NOTION_PARENT_PAGE_ID |
notion.parent_page_id |
| Key | Default | Meaning |
|---|---|---|
notion.token |
— | Integration token. Required. Prefer the env var. |
notion.parent_page_id |
— | Root page of the sync. Required. |
directories.markdown_root |
./ |
Folder that holds your Markdown tree. |
directories.excluded_patterns |
none | Glob patterns that are never synced. Hidden folders such as .git and .notion-sync are always skipped. |
sync.conflict_resolution |
diff |
What sync bidirectional does when both sides changed. newer keeps whichever side changed last, and falls back to diff when a time is unknown. notion_wins keeps Notion's version. markdown_wins keeps the local file. diff shows both and asks you. |
sync.direction |
push |
Shown by verify. The sync command doesn't read it: pass the direction as an argument. |
mapping.strategy |
filename |
Accepted but not used. Page titles always come from the frontmatter title, or the file name when there is none. |
performance.workers |
0 |
Concurrent workers for pull. 0 picks a count from the number of pages. |
performance.use_multi_client |
false |
Experimental: spread requests over several HTTP clients. |
performance.client_count |
3 |
Number of clients when use_multi_client is on. |
| Flag | Meaning |
|---|---|
-c, --config <path> |
Use this config file |
-v, --verbose |
More output |
--timeout <duration> |
Stop a command after this long (default 30m) |
- Keep the token in
.envor your environment, not inconfig.yaml. - Add
.envand any config containing a token to.gitignore. - Commit
.env.exampleinstead.