Joplin to Obsidian Migration Tool 🔄
A Python tool to convert Joplin notebook exports (markdown + front matter format) into a format suitable for Obsidian vaults. It reorganizes resources, cleans up file names, and removes Joplin-specific metadata.
Migration Workflow
- Export from Joplin: Create an export using the "Markdown + Front Matter" format
- Process with this tool: Run the migration tool against your Joplin export
- Import to Obsidian: Import the processed files into your Obsidian vault
⚠️ Important Disclaimer
This tool modifies your files and directories! Always create a backup before running. The changes are irreversible. Use --dry-run to preview changes first.
What This Tool Does
| Step | Operation | Subcommand |
|---|---|---|
| 1 | Move resources from global _resources to local folders |
move-resources |
| 2 | Remove trailing underscores/spaces from files & folders | — |
| 3 | Remove empty _resources directories |
cleanup-files |
| 4 | Remove location data (latitude, longitude, altitude) from YAML front matter | cleanup-location |
Installation
pip
pip install joplin-to-obsidian
uv
uv tool install joplin-to-obsidian
Docker
docker pull ghcr.io/hugobatista/joplin-to-obsidian:latest
Usage
Run all steps (with confirmation prompt)
joplin-to-obsidian run /path/to/export
Preview changes (dry run)
joplin-to-obsidian --dry-run run /path/to/export
Shows exactly what would happen without modifying any files:
Step 1: Moving resources to _resources folders
would copy 3 resource(s):
- WebClipper.png
would save 1 file(s):
- Welcome!/4. Tips.md
Step 2: Removing trailing underscores
Would rename: /path/export/note_.md -> /path/export/note.md
Step 4: Removing location data from YAML front matter
Would remove location data from 5 files in Welcome!/:
- 1. Welcome to Joplin!.md
- 2. Importing and exporting notes.md
- 3. Synchronising your notes.md
- 4. Tips.md
- 5. Joplin Privacy Policy.md
Run individual steps
joplin-to-obsidian move-resources /path/to/export
joplin-to-obsidian cleanup-files /path/to/export
joplin-to-obsidian cleanup-location /path/to/export
Each subcommand also supports --dry-run:
joplin-to-obsidian --dry-run move-resources /path/to/export
Show version
joplin-to-obsidian --version
Docker
# Run all steps
docker run -it --rm -v /path/to/export:/data ghcr.io/hugobatista/joplin-to-obsidian:latest run /data
# Build from source
docker build -t joplin-to-obsidian .
docker run -it --rm -v /path/to/export:/data joplin-to-obsidian run /data
Input Structure
your-export-folder/
├── _resources/
│ ├── image1.png
│ ├── document.pdf
│ └── attachment.jpg
├── Note 1.md
├── Note 2.md
├── Subfolder/
│ ├── Note 3.md
│ └── Note 4.md
└── ...
Output Structure
your-export-folder/
├── Note 1.md
├── _resources/
│ └── image1.png
├── Note 2.md
├── _resources/
│ └── document.pdf
├── Subfolder/
│ ├── Note 3.md
│ ├── _resources/
│ │ └── attachment.jpg
│ └── Note 4.md
└── ...
Technical Details
- Language: Python 3.10+
- Dependencies: Typer (for CLI), otherwise standard library
- File Encoding: UTF-8
Troubleshooting
"Resource not found" errors
Markdown files may reference resources that don't exist in the _resources directory. The tool skips these and continues.
Permission errors
Ensure you have write permissions for the directory and all its contents.
File conflicts
If cleanup creates duplicate names, the tool automatically appends numbers to avoid conflicts.
Validation
After running, verify that:
- All markdown files still open correctly
- Images and attachments are still accessible
- No important data was lost
Contributing
See CONTRIBUTING.md for guidelines.
License
MIT — see LICENSE.
Release files for joplin-to-obsidian 0.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| joplin_to_obsidian-0.2.0.tar.gz | 83.1 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| joplin_to_obsidian-0.2.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 94.3 kB
Release files / joplin_to_obsidian-0.2.0.tar.gz
| Download URL | joplin_to_obsidian-0.2.0.tar.gz |
|---|---|
| Size | 83.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
45ee682a13adf7343fbf83024b2f208ed706b652a1ac3801859268cb6697cabf
|
|
BLAKE2b-256 checksum How to use checksums |
c6d972d65910e4ab33904005b6012f87a7f5a437db3eb52124b08620eb6a1cd8
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.13
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 7, 2026.
Transparency logRelease files / joplin_to_obsidian-0.2.0-py3-none-any.whl
| Download URL | joplin_to_obsidian-0.2.0-py3-none-any.whl |
|---|---|
| Size | 11.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
60dd1694c8194417f5f500b7ec30d36eb245fc22f3562f0f1dbd08402cc8cff4
|
|
BLAKE2b-256 checksum How to use checksums |
eec31a0ab0fa2ab2393bbebd4d416ebfe4968c3670f3bbcd60fc7267b3a4624a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.13
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 7, 2026.
Transparency log