If you use Obsidian, you’ve probably wanted to share a note with someone without exporting a PDF or pasting into a Google Doc. The Share Note plugin by Alan Grainger does exactly that — one command and you get a clean shareable link with your theme intact. The catch is that the notes get uploaded to note.sx, a server you don’t control. For personal stuff that’s probably fine. For anything you care about, it’s worth ~15 minutes to run your own.
The server component ships as a Docker image and the plugin supports custom server URLs out of the box.
What You Need
A VPS with Docker, a domain, and ~15 minutes. The server is light enough for the cheapest VPS tier anywhere. Mine runs on a Hetzner CX22 alongside PostgreSQL and other services and it barely registers.
I use Caddy as the reverse proxy because it handles TLS automatically — no certbot, no renewal cron. If you’re on Nginx, the proxy config is a one-line adaptation.
A VPS with Docker and Docker Compose installed, SSH access, a domain with DNS you can edit, and Caddy installed on the VPS. If you don’t have Caddy yet, it’s a one-liner on Ubuntu: install from the official Caddy repos.
Setting Up the Server
SSH into your VPS and create a directory for the project. I keep mine in /opt/notesx but anywhere works.
mkdir -p /opt/notesx && cd /opt/notesx
Create the environment file. The only values that really matter are BASE_WEB_URL and HASH_SALT. The salt can be any random string, and the base URL is whatever subdomain you’re going to point at this server.
cat > .env << 'EOF'
BASE_WEB_URL=https://notes.yourdomain.com
HASH_SALT=your-random-string-here
FOLDER_PREFIX=1
MAXIMUM_UPLOAD_SIZE_MB=10
ALLOW_NEW_USERS=false
EOF
Generate a proper salt instead of typing something predictable:
openssl rand -hex 32
Now create the Docker Compose file. The key detail here is binding to 127.0.0.1:3000 instead of 0.0.0.0:3000. You don’t want the server directly exposed to the internet. Caddy handles all the public-facing traffic.
# docker-compose.yml
services:
notesx-server:
image: ghcr.io/note-sx/server:latest
container_name: notesx-server
ports:
- '127.0.0.1:3000:3000'
restart: always
env_file:
- .env
volumes:
- ./data/db:/notesx/db
- ./data/files:/notesx/userfiles
healthcheck:
test: wget --no-verbose --tries=1 --spider http://localhost:3000/v1/ping || exit 1
interval: 30s
timeout: 10s
retries: 3
start_period: 10s
Start it up:
docker compose up -d
Verify it’s running:
curl http://localhost:3000/v1/ping
You should get back ok. If you don’t, check docker logs notesx-server for errors.
DNS and Caddy
Point your subdomain at your VPS. Add an A record in your DNS provider pointing to your server’s IP. If you’re using Cloudflare, make sure the proxy is off (gray cloud, DNS only). Caddy needs direct access to the server to provision TLS certificates via Let’s Encrypt, and Cloudflare’s proxy gets in the way of that.
If you leave Cloudflare’s proxy enabled (orange cloud), Caddy can’t complete the ACME challenge for your TLS certificate. Set it to DNS only. Caddy’s auto-TLS handles everything from there.
Edit your Caddyfile (usually at /etc/caddy/Caddyfile) to reverse proxy to the Docker container:
notes.yourdomain.com {
header {
X-Content-Type-Options nosniff
X-Frame-Options DENY
Referrer-Policy strict-origin-when-cross-origin
}
reverse_proxy localhost:3000
}
If you already have other sites in your Caddyfile, just add this block alongside them. Caddy handles multiple sites in a single file without any extra configuration.
Open your firewall for HTTP and HTTPS if you haven’t already:
ufw allow 80/tcp
ufw allow 443/tcp
Reload Caddy:
systemctl reload caddy
Within seconds, Caddy will provision a Let’s Encrypt certificate for your subdomain. You can verify by checking the logs:
journalctl -u caddy -n 20 --no-pager
Look for certificate obtained successfully. If you see DNS errors, your A record hasn’t propagated yet. Give it a few minutes and Caddy will automatically retry.
Connecting the Obsidian Plugin
The Share Note plugin stores its configuration in your vault at .obsidian/plugins/share-note/data.json. Open that file and change the server field to your domain. You also need to clear the existing uid and apiKey so the plugin registers a fresh account on your server instead of trying to authenticate against note.sx.
{
"server": "https://notes.yourdomain.com",
"uid": "",
"apiKey": "",
...
}
Before you restart Obsidian, temporarily enable registration on your server. The ALLOW_NEW_USERS flag controls whether the server accepts new accounts. You set it to false earlier, which is the right default, but you need it open for your initial registration.
sed -i 's/ALLOW_NEW_USERS=false/ALLOW_NEW_USERS=true/' /opt/notesx/.env
cd /opt/notesx && docker compose down && docker compose up -d
Running docker compose restart does not reload environment variables. You need docker compose down && docker compose up -d for .env changes to take effect. This one will bite you if you forget it.
Now restart Obsidian (or reload the Share Note plugin) and try sharing a note. The plugin will automatically register with your server and generate a new API key. Your shared note URL will use your custom domain instead of note.sx. If you’ve set up everything correctly, the link should work immediately.
Once you’ve confirmed it works, lock registration back down:
sed -i 's/ALLOW_NEW_USERS=true/ALLOW_NEW_USERS=false/' /opt/notesx/.env
cd /opt/notesx && docker compose down && docker compose up -d
Verify the change took effect inside the container:
docker exec notesx-server env | grep ALLOW
You should see ALLOW_NEW_USERS=false. Now nobody else can create accounts on your server.
What You End Up With
Your shared notes now live on your own server, under your own domain, with TLS certificates that renew themselves. The Docker container handles the note storage and API, Caddy handles TLS and reverse proxying, and the Obsidian plugin works exactly the same as before except the URLs point to your infrastructure.
The data.json config syncs across all your devices through whatever Obsidian sync method you use, so every device will automatically use your self-hosted server. No per-device configuration needed.
If something breaks, the logs are straightforward. docker logs notesx-server shows you the application logs, journalctl -u caddy shows the proxy and TLS logs. Between those two you can diagnose basically anything.
Closing Thoughts
This entire guide is also available as a shared Obsidian note, hosted on the very server it describes. Meta, I know.
Self-hosting isn’t always worth the trouble, but this one is. The setup is a few minutes, Docker auto-restarts, Caddy auto-renews, and the notes + URLs stay on your own infrastructure.
Credit: Alan Grainger wrote both the Share Note plugin and the self-hostable server. Docs at docs.note.sx.
🎯 Quick Reference
- ✓ The note-sx server runs as a single Docker container on any VPS
- ✓ Caddy handles TLS automatically — no certbot, no cron jobs, no thinking about it
- ✓ Bind the container to 127.0.0.1, not 0.0.0.0 — let Caddy handle public traffic
- ✓ Clear your plugin's uid and apiKey when switching to a self-hosted server
- ✓ docker compose restart doesn't reload .env — use down + up instead
- ✓ Lock ALLOW_NEW_USERS to false after registering your account
Enjoyed this article? Share it with others!