Migrate your Obsidian vault to AppFlowy — structure, images, and tables included
Project description
A CLI tool for migrating Obsidian vaults into AppFlowy. It walks your vault, converts markdown to AppFlowy blocks, and uploads everything through the AppFlowy API — folders become nested pages, images get re-hosted, wikilinks become page links.
Built for one-time migrations. Your Obsidian files are never modified.
Before you start
- Python 3.11+ —
pip install obsidian-to-appflowy - An AppFlowy account on AppFlowy Cloud or your own deployment
- Your vault folder — the directory you opened in Obsidian
⚠️ Run
--dry-runfirst to preview without touching anything.
What gets migrated
| Obsidian | AppFlowy |
|---|---|
| Folders | Nested pages |
Headings # ## … |
Heading blocks (levels 1–6) |
| Bullet and numbered lists (nested) | List blocks with children |
Blockquotes > |
Quote blocks |
| Fenced code blocks | Code blocks with language |
Horizontal rules --- |
Divider blocks |
| Markdown tables | simple_table blocks |
bold, italic, code, ==highlight==, [links] |
Inline formatting |
HTML inline tags <u>, <b>, <i>, <s>, <mark>, <a href>, <br> |
Mapped to AppFlowy formatting (underline / bold / italic / strikethrough / highlight / link / space) |
Frontmatter --- ... --- |
Preserved as a YAML code block at the top |
Wikilinks [[Note]] / [[Note|Alias]] |
Page links (clicking navigates to the page) |
![[img.png]],  |
Uploaded image blocks |
- [ ] / - [x] tasks |
Todo blocks (checked state preserved) |
Try the example vault first
Not sure what to expect? Clone the repo and run try a dry-run on the included vault — it covers every supported block type (headings, lists, tables, code, images, wikilinks, tasks…):
git clone https://github.com/vsgusev/obsidian-to-appflowy
cd obsidian-to-appflowy
obsidian-to-appflowy --vault example_vault --dry-run
Or import it to AppFlowy to see the real result before touching your own notes:
obsidian-to-appflowy --vault example_vault --url https://cloud.appflowy.io --email you@example.com
Usage
Preview locally (no account needed):
# replace ~/Documents/MyVault with the absolute path to your vault
obsidian-to-appflowy --vault ~/Documents/MyVault --dry-run
Import to AppFlowy Cloud:
# for self-hosted, swap --url for your gateway, e.g. http://192.168.1.10:8800
obsidian-to-appflowy --vault ~/Documents/MyVault --url https://cloud.appflowy.io --email you@example.com
Password is prompted if not passed via --password or $APPFLOWY_PASSWORD.
Options
| Flag | Default | |
|---|---|---|
--vault PATH |
required | Obsidian vault root |
--url URL |
required for import | AppFlowy API base URL |
--email EMAIL |
$APPFLOWY_EMAIL |
Account email |
--password PASS |
$APPFLOWY_PASSWORD / prompt |
Account password |
--space NAME |
Obsidian |
Space name in AppFlowy |
--space-color HEX |
#00BCF0 |
Space icon color |
--skip-images |
off | Text-only import, no uploads |
--dry-run |
off | Preview without API calls |
Limitations
- Tags —
#tagstays as plain text - Callouts —
> [!NOTE]becomes a plain quote block - Plugins — Dataview, Kanban, Excalidraw and other plugin syntax appear as plain text
- HTML — only inline tags listed in the migration table are mapped; block-level HTML (
<details>,<table>,<iframe>, etc.) and unsupported inline tags (<sup>,<sub>,<span>) keep their content but lose styling - Duplicate image filenames — if two images share a name, only one is used; a warning prints to stderr
- Edge cases:
- Duplicate page names — wikilinks resolve by filename stem, so
[[Note]]is ambiguous if twoNote.mdexist in different folders (the first one wins) [[Note#Section]]and[[Note^block]]link to the page itself; the section/block target is dropped- YAML frontmatter with a
---line inside a value may be only partially captured - Fenced code indented under a list bullet is not detected as a code block
- Duplicate page names — wikilinks resolve by filename stem, so
Development
pip install -e ".[dev]"
pytest
Contributing
Open an issue before sending a PR. No AI-generated PRs.
License
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file obsidian_to_appflowy-1.2.0.tar.gz.
File metadata
- Download URL: obsidian_to_appflowy-1.2.0.tar.gz
- Upload date:
- Size: 23.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
31a785f5290faafe368936feada77b6c8cafae5fffde5c743a1991df25b6000b
|
|
| MD5 |
2ef55fe4545538bc3a9fa6ed8759c241
|
|
| BLAKE2b-256 |
a23bd1c3f630522f484dceb26f54a394cc63351257afc0017a479a2c7a642ae4
|
File details
Details for the file obsidian_to_appflowy-1.2.0-py3-none-any.whl.
File metadata
- Download URL: obsidian_to_appflowy-1.2.0-py3-none-any.whl
- Upload date:
- Size: 24.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2780c972131a2f5ec114c07aca5bb5cc7e2c75e3f89af69042aec50db88481f6
|
|
| MD5 |
7f881cb9d0bdbf37a7a66b323740a49c
|
|
| BLAKE2b-256 |
57d8ff02a36e8733680d9cc6973ed709c041fa6e62098cd0764248a6b8f64552
|