Portainer Templates logo

Portainer Templates

Markopolis

Stack

Notes

Markopolis is like the self hosted version of Obsidian Publish.

Image details

Architecture: amd64, arm64
Image size: 321 MB
User: rishikanthc

Source details

Stars: 180
Forks: 3
Language: TypeScript
License: MIT
Updated: 1 years ago
Website: markopolis.app/

Configuration

Type
Compose
Platform
linux
Image
ghcr.io/rishikanthc/markopolis:2.0.0
Ports
81080:80
Volumes
/app/markdown : /portainer/Files/AppData/Config/markopolis
Env vars
MARKOPOLIS_DOMAIN=""MARKOPOLIS_FRONTEND_URL = ""MARKOPOLIS_TITLE="Awesome Notes"MARKOPOLIS_MD_PATH=/app/markdownMARKOPOLIS_API_KEY=<really long random alpha-numeric string>
Restart
unless-stopped
Source

Template by xneo1·Source

Standalone Install

Select an install method, to see config/commands for deploying Markopolis

Installation method

Install on Portainer

Import all app templates into your Portainer instance, for easy 1-click deploys

  1. Ensure both Docker and Portainer are installed, and up-to-date
  2. Log into your Portainer web UI
  3. Under Settings → App Templates, paste the below URL
  4. Head to Home → App Templates, and the list of apps will show up
  5. 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 demo
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 -d

More 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
callouts, equations, code highlighting etc.
  • 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 secure API_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
      - POCKETBASE_ADMIN_EMAIL=admin@admin.com
      - POCKETBASE_ADMIN_PASSWORD=password
      - TITLE=Markopolis
      - CAP1=caption1
      - CAP2=caption2
      - CAP3=caption3
    volumes:
      - ./pb_data:/app/db

Now you can deploy Markopolis by running docker-compse up -d
Parameter | 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 either pip 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.12

and activate it with
conda activate <name>

Package installation

Simply install the markopolis python package using your preferred package manager.
pip:
pip install markopolis

Configuration

Set the environment variables MARKOPOLIS_DOMAIN and MARKOPOLIS_API
bash or zsh (temporarily for current session)
export MARKOPOLIS_DOMAIN=https://markopolis.example.com

bash 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 ~/.bashrc

fish (temporarily for current session)
set -x MARKOPOLIS_DOMAIN https://markopolis.example.com

fish (permanently for all sessions)
echo 'set -x MARKOPOLIS_DOMAIN "https://markopolis.example.com"' >> ~/.config/fish/config.fish
source ~/.config/fish/config.fish

For 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: 137 means killed, usually out of memory. 126 or 127 means 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:80 only 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 login to 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 no so 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.

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/markdown from /portainer/Files/AppData/Config/markopolis on 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/markdown
  • MARKOPOLIS_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.