Self-hosted server for publishing Obsidian notes to the web. Deploy on your own infrastructure and maintain full control over your data.
This server works with the Obsidian Pub Plugin to enable one-click publishing of your markdown notes. Host on your own domain, protect with passwords, and keep your content private.
See how your published notes look in action:
👉 Example Publication: The Law of Large Numbers
- Self-hosted — Deploy on your VPS, home server, or cloud infrastructure
- Custom domain — Serve notes from your own domain with automatic SSL
- Password protection — Secure individual publications with passwords
- Docker-based — Easy deployment with Docker Compose
- Automatic HTTPS — Let's Encrypt certificates configured automatically
- Rate limiting — Built-in protection against abuse
- Health monitoring — Health check endpoints for uptime monitoring
- Linux server (Ubuntu 20.04+ recommended)
- Docker and Docker Compose (automatically installed if missing)
- Domain name pointing to your server
- Ports 80 and 443 open
# Clone the repository
git clone https://github.com/IvanShishkin/obsidian-pub-server.git
cd obsidian-pub-server
# Run the installer
sudo chmod +x install.sh
sudo ./install.shThe installer will:
- Install Docker and Docker Compose if not present
- Prompt for your domain and email
- Generate secure authentication keys
- Obtain SSL certificate from Let's Encrypt
- Start all services
- Display your SECRET_KEY for plugin configuration
- Clone the repository:
git clone https://github.com/IvanShishkin/obsidian-pub-server.git
cd obsidian-pub-server- Create environment file:
cp .env.example .env- Edit
.envwith your configuration:
# Generate secrets
SECRET_KEY=$(openssl rand -hex 32)
SESSION_SECRET=$(openssl rand -hex 32)
# Set your domain and email
DOMAIN=publish.yourdomain.com
EMAIL=your-email@example.com- Start the services:
docker compose up -dUse the included update script to safely update your installation:
# Make the script executable (first time only)
chmod +x update.sh
# Run interactive update
./update.sh
# Quick update without prompts
./update.sh --quick
# Force update with backup and cleanup
./update.sh --force- Checks for local changes — offers to stash uncommitted modifications
- Pulls latest code — fetches updates from the git repository
- Creates backup — optionally backs up your publications before updating
- Rebuilds containers — rebuilds all Docker images without cache
- Health check — verifies the application starts correctly
- Cleanup — removes old Docker images to free disk space
Backups are stored in the ./backups/ directory with timestamps:
# List backups
ls -la ./backups/
# Restore from backup (if needed)
docker run --rm -v obsidian-pub-server_app_data:/data -v $(pwd)/backups:/backup alpine tar xzf /backup/backup_YYYYMMDD_HHMMSS.tar.gz -C /data| Variable | Description | Default |
|---|---|---|
NODE_ENV |
Environment mode | production |
PORT |
Application port | 3000 |
SECRET_KEY |
API authentication key | required |
DOMAIN |
Your domain name | required |
EMAIL |
Email for Let's Encrypt | required |
SESSION_SECRET |
Session encryption key | required |
MAX_CONTENT_SIZE |
Max upload size in bytes | 10485760 (10MB) |
RATE_LIMIT_WINDOW |
Rate limit window (ms) | 60000 |
RATE_LIMIT_MAX |
Max requests per window | 100 |
RATE_LIMIT_PUBLIC |
Public endpoint limit | 1000 |
RATE_LIMIT_PASSWORD |
Password attempt limit | 5 |
CORS_ORIGINS |
Allowed CORS origins | * |
BCRYPT_ROUNDS |
Password hashing rounds | 12 |
# Generate SECRET_KEY
openssl rand -hex 32
# Generate SESSION_SECRET
openssl rand -hex 32GET /api/health
Returns server status.
GET /api/check/:filename
Authorization: Bearer <SECRET_KEY>
Check if a file has been published.
POST /api/publish
Authorization: Bearer <SECRET_KEY>
Content-Type: application/json
{
"filename": "my-note.md",
"content": "# My Note\n\nContent here...",
"password": "optional-password"
}
Creates a new publication and returns the public URL.
PUT /api/update/:hash
Authorization: Bearer <SECRET_KEY>
Content-Type: application/json
{
"content": "# Updated Content",
"password": "optional-new-password"
}
Updates content while preserving the URL.
DELETE /api/delete/:hash
Authorization: Bearer <SECRET_KEY>
Removes a publication.
After installation, configure the Obsidian plugin:
- Open Obsidian Settings → Web Publish
- Set API URL to
https://your-domain.com/api - Set Secret Key to the key displayed after installation
- Save and test with a sample note
# View logs
docker compose logs -f
# View specific service logs
docker compose logs -f app
docker compose logs -f nginx
# Restart services
docker compose restart
# Stop services
docker compose down
# Start services
docker compose up -d
# Check service status
docker compose ps
# Rebuild after changes
docker compose build
docker compose up -dCertificates auto-renew via the certbot container. To manually renew:
docker compose run --rm certbot renew
docker compose restart nginxPublications are stored in Docker volumes:
app_data— Published notes and metadatacertbot_conf— SSL certificatescertbot_www— ACME challenge files
To backup your data:
docker run --rm -v obsidian-pub-server_app_data:/data -v $(pwd):/backup alpine tar czf /backup/publications-backup.tar.gz -C /data .- Keep your
SECRET_KEYprivate and secure - Use strong passwords for protected publications
- Regularly update Docker images
- Monitor access logs for suspicious activity
- Consider firewall rules to restrict access
- Back up your data regularly
If SSL certificate fails to obtain:
- Verify DNS points to your server:
dig +short your-domain.com - Check ports 80/443 are open:
sudo ufw status - View certbot logs:
docker compose logs certbot
If the server is unreachable:
- Check nginx is running:
docker compose ps nginx - View nginx logs:
docker compose logs nginx - Test locally:
curl http://localhost:3000/api/health
If publications fail:
- Check app logs:
docker compose logs app - Verify SECRET_KEY matches plugin configuration
- Ensure content size is within limits
Contributions are welcome! Please:
- Fork the repository
- Create a feature branch
- Submit a pull request
- Server issues — Open an issue in this repository
- Plugin issues — Visit obsidian-pub-plugin
MIT License
Enjoy publishing your notes with full control over your data! ❤️