5.4 KiB
Installation Guide
Prerequisites
- Docker (or Docker Compose)
- A Linux host (x86_64 or ARM64, e.g. Synology NAS)
Quick Install
# Clone the repo
git clone https://git.aholck.net/anders-pub/markdown-hub.git
cd markdown-hub
# Build the image
docker build -t markdownhub .
# Run
docker run -d \
--name markdownhub \
--restart unless-stopped \
-p 8080:8080 \
-v /path/to/data:/app/data \
-e MH_SECRET="$(openssl rand -hex 32)" \
-e MH_ADMIN_EMAIL="you@example.com" \
-e MH_ADMIN_PASSWORD="changeme" \
markdownhub
Open http://your-host:8080 and log in with the admin credentials above.
Change your password immediately in ⚙️ Preferences after first login.
Environment Variables
| Variable | Required | Default | Description |
|---|---|---|---|
MH_SECRET |
Yes | dev-secret-change-me |
JWT signing key. Generate with openssl rand -hex 32 |
MH_ADMIN_EMAIL |
No | admin@localhost |
Admin email (only used on first run) |
MH_ADMIN_PASSWORD |
No | admin |
Admin password (only used on first run) |
MH_PORT |
No | 8080 |
Internal port (change the -p mapping instead) |
MH_DATA_DIR |
No | /app/data |
Data directory inside container |
MH_AI_ENDPOINT |
No | — | LiteLLM/OpenAI-compatible API URL for AI features |
MH_AI_API_KEY |
No | — | API key for AI endpoint |
MH_AI_MODEL |
No | gpt-4 |
Default model name |
MH_LDAP_URL |
No | — | LDAP server URL (e.g. ldap://ldap.example.com:389) |
MH_LDAP_BIND_DN |
No | — | Service account DN for user search |
MH_LDAP_BIND_PASS |
No | — | Service account password |
MH_LDAP_BASE_DN |
No | — | Base DN for user search (e.g. dc=example,dc=com) |
MH_LDAP_USER_FILTER |
No | (&(objectClass=inetOrgPerson)(uid=%s)) |
LDAP search filter (%s = username) |
MH_LDAP_SKIP_TLS |
No | false |
Skip TLS verification (for self-signed certs) |
LDAP Notes
- Use
ldaps://host:636for SLDAP (LDAP over TLS) - LDAP settings can also be configured from the Admin panel (⚙️ → 👤) without restarting
- If a required group is set, only users who are members of that group can log in
- On first LDAP login, a local user is auto-created (for preferences, file storage, etc.)
- Local accounts still work alongside LDAP — local auth is tried first
Data Volume
Mount a persistent volume to /app/data. This contains:
/app/data/
├── db/
│ └── markdownhub.db # SQLite database (users, permissions, jobs)
└── files/
└── {user-id}/ # Each user's markdown files + git repo
Reverse Proxy (HTTPS)
Put behind nginx/Caddy/Traefik for HTTPS. Example nginx config:
server {
listen 443 ssl;
server_name md.example.com;
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
location / {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
# WebSocket for real-time collab
location /ws/ {
proxy_pass http://127.0.0.1:8080;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
}
}
Synology NAS
- Install Docker from Package Center
- SSH into your NAS or use Task Scheduler
- Run the commands from Quick Install above
- Use
/volume1/docker/markdownhub/dataas your data path
For ARM-based Synology (e.g. DS124), build with:
docker build --platform linux/arm64 -t markdownhub .
Updating
cd markdown-hub
git pull
docker build -t markdownhub .
docker rm -f markdownhub
docker run -d \
--name markdownhub \
--restart unless-stopped \
-p 8080:8080 \
-v /path/to/data:/app/data \
-e MH_SECRET="your-existing-secret" \
markdownhub
Your data persists in the volume — only the app binary and frontend are replaced.
Backup
# Stop (optional, for consistency)
docker stop markdownhub
# Copy data
cp -r /path/to/data /path/to/backup/markdownhub-$(date +%Y%m%d)
# Restart
docker start markdownhub
Restore
docker stop markdownhub
rm -rf /path/to/data
cp -r /path/to/backup/markdownhub-YYYYMMDD /path/to/data
docker start markdownhub
Build Daemon (Optional)
For the AI build pipeline, run the daemon on a machine with Pi installed:
export MH_URL=https://md.example.com
export MH_DAEMON_TOKEN=your-api-token
export MH_WORKSPACE=~/builds
python3 daemon/build_daemon.py
Or run in screen/tmux:
screen -dmS mh-daemon python3 daemon/build_daemon.py
CLI Tool (Optional)
Build the mdsync CLI:
cd markdown-hub
go build -o mdsync ./cmd/mdsync
sudo mv mdsync /usr/local/bin/
Usage:
mdsync login https://md.example.com you@example.com yourpassword
mdsync pull . # Download all files to current directory
mdsync push . # Upload local .md files to server
mdsync status # Check git sync status
Troubleshooting
Can't login: Check MH_SECRET hasn't changed between restarts. If it has, existing tokens are invalid — just login again.
Port in use: Change the host port: -p 9090:8080
Permission denied on data volume: Ensure the directory is writable: chmod 777 /path/to/data
WebSocket not connecting (collab): Make sure your reverse proxy forwards /ws/ with upgrade headers (see nginx example above).