Markopolis
Stack
Markopolis is like the self hosted version of Obsidian Publish.
Image details
Source details
Configuration
TypeComposelinuxghcr.io/rishikanthc/markopolis:2.0.081080:80/app/markdown : /portainer/Files/AppData/Config/markopolisMARKOPOLIS_DOMAIN=""MARKOPOLIS_FRONTEND_URL = ""MARKOPOLIS_TITLE="Awesome Notes"MARKOPOLIS_MD_PATH=/app/markdownMARKOPOLIS_API_KEY=<really long random alpha-numeric string>unless-stoppedStandalone Install
Select an install method, to see config/commands for deploying Markopolis
Install on Portainer
Import all app templates into your Portainer instance, for easy 1-click deploys
- Ensure both Docker and Portainer are installed, and up-to-date
- Log into your Portainer web UI
- Under Settings → App Templates, paste the below URL
- Head to Home → App Templates, and the list of apps will show up
- Select Markopolis, fill in any config options, and hit Deploy
Template Import URL
https://raw.githubusercontent.com/Lissy93/portainer-templates/main/templates.json
Show Me
Original stackfile
The compose file this template deploys, straight from its repo:
services:
markopolis:
image: ghcr.io/rishikanthc/markopolis:2.0.0
ports:
- "81080:80"
environment:
- MARKOPOLIS_DOMAIN=""
- MARKOPOLIS_FRONTEND_URL = ""
- MARKOPOLIS_TITLE="Awesome Notes"
- MARKOPOLIS_MD_PATH=/app/markdown
- MARKOPOLIS_API_KEY=<really long random alpha-numeric string>
volumes:
- /portainer/Files/AppData/Config/markopolis:/app/markdown
restart: unless-stopped
volumes:
markopolis_data:
driver: local
Or deploy it directly from the source:
git clone https://github.com/xneo1/portainer_templates
cd portainer_templates
docker compose -f Template/Stack/markopolis.yml up -dMore install options in our documentation, or see rishikanthc/markopolis for app-specific guidance.
Introduction
Hi, I’m Rishikanth, and I’m excited to introduce you to Markopolis! It’s a web app and API server I built that lets you easily share your Markdown notes as websites while giving you full control to interact with and manage your Markdown files via a powerful API. Just point Markopolis to a folder with your Markdown files, and it’ll handle the rest. The idea is to help you create your own tools and features around your notes without being tied down by proprietary systems. It’s completely open-source and free under the MIT License. Check out the GitHub repo and start exploring!TLDR: Self-hosted Obsidian publish with an API to extend functionality.
Features
- Easy setup Extremely simple to deploy and use
- Easy publish Publish notes online with a single command
- Markdown API interface Interact with aspecs of markdown using REST APIs
- Extensible Extendable using exposed APIs
- Develop your own frontend You can use the api calls to get every section of markdown files to design your own frontend
- Instand rendering Article is available online as soon as ypu publish
- Full text search Fuzzy search across your entire notes vault
- Obsidian markdown flavor Maintains compatibility with obsidian markdown syntax. Supports
- Dark & Light modes Supports toggling between light and dark themes
- Easy maintenance Requires very little to no maintenance
- Docker support Available as docker images to self host
and lots more to come. Checkout the roadmap page for planned features.
Demo
The documentation website is hosted using Markopolis and is a live demo. These notes are used to demonstrate the various aspects of Markopolis. Checkout the Markdown Syntax page for a full showcase of all supported markdown syntax.Thank you for considering Markopolis for your Markdown note-sharing needs! If you like the project considering starring the repository.
Versioning
I try to follow semantic versioning as much as possible. However, I have still not streamlined the process yet, so please bear with me if there are any mishaps. v2.0.0 achieves code separation between backend and frontend because of which I had to fast forward the docker versioning to match the python package. Going forward I'll try to avoid such mishaps and I'll be maintaining a detailed changelog at changelog.This is my first open-source project and I'm excited to scale it well. I started building this mostly out of my personal need, but if there's public interest I'm more than happy to accept feature requests and contributions. Any and all feedback is welcome. This project will always be open-source and maintained as I rely on it for my own notes system.
If you like the project please don't forget to star the github repo.
Installation
Installing Markopolis involves two steps. First deploying the server. Second installing the CLI tool. The CLI tool provides a utility command to upload your markdown files to the server. The articles are published as soon as this command is run.Step 1: Server installation
We will be using Docker for deploying Markopolis. Create a docker-compose and configure environment variables. Make sure to generate and add a secureAPI_KEY.
Allocate persistent storage for the Markdown files.Next create a
docker-compose.yaml file with the following:version: '3.8'
services:
markopolis:
image: ghcr.io/rishikanthc/markopolis:latest
ports:
- "8080:8080"
- "3000:3000"
environment:
- POCKETBASE_URL=http://127.0.0.1:8080
- API_KEY=test
- [email protected]
- POCKETBASE_ADMIN_PASSWORD=password
- TITLE=Markopolis
- CAP1=caption1
- CAP2=caption2
- CAP3=caption3
volumes:
- ./pb_data:/app/dbNow you can deploy Markopolis by running
docker-compse up -dParameter | Description -- | -- POCKETBASEURL | DO NOT Change this POCKETBASEADMINEMAIL | The admin account email for the database POCKETBASEADMINPASSWORD | The admin account password TITLE | SITE TITLE APIKEY | For security, most of the API endpoints are protected by an API key. Make sure to use a secure API key and don't share it publicly. CAP1 | Caption 1, text that appears below the site title CAP2 | Caption 2 CAP3 | Caption 3
STEP 2: Local installation
I highly recommend configuring a virtual environment for python to keep your environment clean and and prevent any dependency issues. Below I detail the steps to do this using Conda or pip. If you are familar with this feel free to skip to the package installation section.!info You need to have python version >= 3.12
Setting up a virtual environment
You can use eitherpip or conda to do this. If you are using pip simply run
python3.12 -m venv <name>Replace
<name> with your desired virtual environment name. You can then activate the virtual environment
using:
source <name>For conda, you can use
conda create -n <name> python==3.12and activate it with
conda activate <name>Package installation
Simply install the markopolis python package using your preferred package manager.pip:
pip install markopolisConfiguration
Set the environment variablesMARKOPOLIS_DOMAIN and MARKOPOLIS_APIbash or zsh (temporarily for current session)
export MARKOPOLIS_DOMAIN=https://markopolis.example.combash or zsh (permanently for all sessions)
echo 'export MARKOPOLIS_DOMAIN=https://markopolis.example.com' >> ~/.zshrc
echo 'export MARKOPOLIS_DOMAIN=https://markopolis.example.com' >> ~/.bashrc
source ~/.zshrc
source ~/.bashrcfish (temporarily for current session)
set -x MARKOPOLIS_DOMAIN https://markopolis.example.comfish (permanently for all sessions)
echo 'set -x MARKOPOLIS_DOMAIN "https://markopolis.example.com"' >> ~/.config/fish/config.fish
source ~/.config/fish/config.fishFor more information on how to use Markopolis checkout the Markopolis website. If you like this project please considering starring it.
Serve Markopolis on your own domain behind Caddy, Nginx or Traefik. Fill in your domain and copy the result. It's a starting point, some apps need their own base URL or extra headers set too.
Proxying markopolis.example.com to http://markopolis:80
Add this to your Caddyfile
markopolis.example.com {
reverse_proxy http://markopolis:80
}Check the logs first
Nine times out of ten the logs tell you exactly what went wrong.
- In Portainer, go to Containers, click the container, then Logs. Or run
docker logs <container> - Exit codes help too:
137means killed, usually out of memory.126or127means the command inside the image is broken.
Port already in use
If deployment fails with "Bind for 0.0.0.0:81080 failed: port is already allocated", something else on your server is using that port.
- Find what's using it:
sudo ss -tlnp | grep :81080 - Stop the other service, or pick a different host port. In
81080:80only the left number is yours to change, the right one belongs to the app.
Running but the page won't load
The container is up but nothing appears in your browser.
- Use your server's real IP:
http://your-server-ip:81080. The 0.0.0.0 link Portainer shows isn't a real address. - Give it a minute after first deploy, markopolis can take a while to initialise.
- Make sure your firewall allows the port, e.g.
sudo ufw allow 81080
Permission denied on volumes
If the logs show "permission denied", the app can't write to its data folder on the host.
- Fix the ownership:
sudo chown -R 1000:1000 /portainer/Files/AppData/Config/markopolis
Image won't pull
Test the pull directly on the host: docker pull ghcr.io/rishikanthc/markopolis:2.0.0
- "manifest unknown" means the tag no longer exists.
- "toomanyrequests" is the Docker Hub rate limit. Log in with
docker loginto raise it. - "no space left on device" means a full disk. Reclaim space with
docker system prune
"exec format error"
This means the image was built for a different CPU architecture than your server.
- This image supports:
amd64, arm64 - Check yours with
uname -m: x86_64 is amd64, aarch64 is arm64. Raspberry Pi and other ARM boards are the usual culprits.
Container keeps restarting
The unless-stopped restart policy relaunches the app after every crash, so the real error can scroll past.
- Check the logs right after a restart, the last few lines before it died are the useful ones.
- Get the exit code with
docker inspect <container> --format '{{.State.ExitCode}}' - Still stuck? Redeploy once with the restart policy set to
noso the failure stays visible.
Stack won't deploy
Compose stacks fail fast on small mistakes, and Portainer shows the reason just above the editor.
- YAML only accepts spaces for indentation, a single tab breaks the whole file.
Raise an issue
Found something which isn't working as it should? Here's how to report it.
- Bug within the app: Open an issue on rishikanthc/markopolis
- Template not working: Open an issue on xneo1/portainer_templates
- This website not working: Open an issue on lissy93/portainer-templates
A Compose stack
Markopolis is a Compose stack, a set of containers defined in one file and brought up together by Portainer, then started and stopped as a single app.
The app image
An image is the app packed up ready to go, everything Markopolis needs bundled into one download. This template pulls ghcr.io/rishikanthc/markopolis:2.0.0, which Docker fetches once (about 321 MB) and then starts your own copy from.
Where the image comes from
Docker pulls its images from registries, public libraries of ready-built apps. Markopolis's comes from the GitHub Container Registry, published by rishikanthc.
Version tags
The bit after the colon in the image name is the version tag. This one pins 2.0.0, so every redeploy gives you that exact build until you bump it yourself.
Which machines it runs on
Every image is built for particular CPU types. This one ships for amd64, arm64, so it runs on both regular x86 servers and ARM boards like a Raspberry Pi.
Ports
A port is the door the app answers on. A mapping like 81080:80 means it's reachable on port 81080 of your server, where the left number is yours to change and the right one belongs to the app. Once it's running, open http://your-server-ip:81080 in a browser. It opens:
81080:80, likely the web interface
Volumes
A volume is where Markopolis keeps its files so they survive an update or a restart. Without one, anything it saves would sit inside the container and vanish the moment it's recreated. This template mounts:
/app/markdownfrom/portainer/Files/AppData/Config/markopolison the host
Environment variables
Environment variables are the settings you hand over when you deploy, things like a password or a timezone. Markopolis takes 5 of them, all with defaults you can leave alone or tweak:
MARKOPOLIS_DOMAIN, defaults to""MARKOPOLIS_FRONTEND_URL, defaults to""MARKOPOLIS_TITLE, defaults to"Awesome Notes"MARKOPOLIS_MD_PATH, defaults to/app/markdownMARKOPOLIS_API_KEY, defaults to<really long random alpha-numeric string>
Restart policy
The restart policy here is unless-stopped, so Docker restarts Markopolis after a crash or reboot, but leaves it off when you stop it on purpose. You can change this on the deploy screen. The choices are no (never restart), on-failure (only after a crash), unless-stopped (restart unless you stop it), and always (bring it back no matter what).
Networking
Nothing custom is set, so Markopolis sits on Docker's default bridge network: its own private space that reaches the outside world only through the ports it publishes.
Container name
Once it's deployed, Portainer names the container markopolis. That's what you'll spot in the containers list and use in commands like docker logs markopolis.
Platform
The platform is linux, the kind of system the container is built to run on. Docker and Portainer handle this on a normal Linux server.
Open source license
Markopolis is open source, released under the MIT license. In plain terms the code is out in the open, so you're free to run it and change it to fit what you need.
Portainer app templates
Zooming out, this whole page comes from a Portainer app template: a short recipe telling Portainer how to set Markopolis up. Add the template list to Portainer once, then deploying Markopolis is a click rather than a wall of config.