Properties2
| Type | Practice |
| Note created | Apr 22, 2026 |
Overview
This documents setting up a headless Obsidian instance on a Linux server for vault syncing via Obsidian Sync, mostly based on this guide.
The setup uses:
- Xvfb: Virtual framebuffer (fake display)
- Openbox: Minimal window manager
- x11vnc: VNC server for remote access
- Obsidian: Running with GPU disabled
Installation
Dependencies
sudo apt-get install -y openbox xvfb python3-xdg x11vnc xdg-utils \
libnotify4 libnss3 libsecret-1-0 libasound2Obsidian
# Download latest version (check https://obsidian.md/download for current release)
wget https://github.com/obsidianmd/obsidian-releases/releases/download/v1.11.5/obsidian_1.11.5_amd64.deb
sudo dpkg -i obsidian_1.11.5_amd64.debConfiguration
VNC Password
# Set VNC password (interactive)
x11vnc -storepasswd
chmod 600 ~/.vnc/passwd
# Restart x11vnc after changing password
sudo systemctl restart obsidian-headless-x11vnc.serviceSystemd Services
Four services work together to run Obsidian headlessly.
Xvfb (Virtual Framebuffer)
File: /etc/systemd/system/obsidian-headless-xvfb.service
[Unit]
Description=Headless Xvfb for Obsidian GUI
[Service]
User=<YOUR_USER>
Group=<YOUR_USER>
WorkingDirectory=/home/<YOUR_USER>/
ExecStart=/usr/bin/Xvfb :5 -extension GLX -screen 0 800x600x16
KillSignal=SIGINT
Environment="PATH=/usr/local/bin:/usr/bin:/bin"
Type=exec
Restart=on-failure
RestartSec=30s
[Install]
WantedBy=multi-user.targetOpenbox (Window Manager)
File: /etc/systemd/system/obsidian-headless-openboxsession.service
[Unit]
Description=Headless openbox-session for Obsidian GUI
After=obsidian-headless-xvfb.service
[Service]
User=<YOUR_USER>
Group=<YOUR_USER>
WorkingDirectory=/home/<YOUR_USER>/
ExecStart=/usr/bin/openbox-session
KillSignal=SIGINT
Environment="DISPLAY=:5"
Environment="PATH=/usr/local/bin:/usr/bin:/bin"
Type=exec
Restart=on-failure
RestartSec=30s
[Install]
WantedBy=multi-user.targetx11vnc (VNC Server)
File: /etc/systemd/system/obsidian-headless-x11vnc.service
[Unit]
Description=Headless x11vnc for Obsidian GUI
After=obsidian-headless-openboxsession.service
[Service]
User=<YOUR_USER>
Group=<YOUR_USER>
WorkingDirectory=/home/<YOUR_USER>/
ExecStart=/usr/bin/x11vnc -rfbport 5900 -display :5 -rfbauth /home/<YOUR_USER>/.vnc/passwd -forever -shared -noxdamage
KillSignal=SIGINT
Environment="PATH=/usr/local/bin:/usr/bin:/bin"
Type=exec
Restart=on-failure
RestartSec=30s
[Install]
WantedBy=multi-user.targetNote: The -forever -shared -noxdamage flags are important:
-forever: Keep listening after client disconnects-shared: Allow multiple simultaneous connections-noxdamage: Disable X DAMAGE extension (fixes issues with some compositors)
Obsidian
File: /etc/systemd/system/obsidian-headless.service
[Unit]
Description=Headless Obsidian
After=obsidian-headless-xvfb.service
[Service]
User=<YOUR_USER>
Group=<YOUR_USER>
WorkingDirectory=/home/<YOUR_USER>/
ExecStart=/usr/bin/obsidian --no-sandbox --disable-gpu --disable-software-rasterizer --disable-gpu-compositing
KillSignal=SIGINT
Environment="DISPLAY=:5"
Environment="XDG_RUNTIME_DIR=/run/user/1003"
Environment="PATH=/usr/local/bin:/usr/bin:/bin"
Type=exec
Restart=on-failure
RestartSec=30s
[Install]
WantedBy=multi-user.targetNote: GPU flags are required because Xvfb doesn’t have GPU/GLX support:
--disable-gpu: Disable GPU hardware acceleration--disable-software-rasterizer: Disable software GL fallback--disable-gpu-compositing: Disable GPU compositing
XDG_RUNTIME_DIR is required (Obsidian 1.12.7+)
As of Obsidian 1.12.7, the single-instance IPC socket was moved from $HOME/obsidian-cli.sock to $XDG_RUNTIME_DIR/.obsidian-cli.sock. Systemd services do not inherit XDG_RUNTIME_DIR by default, so without this variable set to the user’s runtime dir (/run/user/<uid>), the running Obsidian instance creates the socket in its working directory (falling back to $HOME), while the CLI invoked from any SSH session looks in /run/user/<uid> and fails with Unable to connect to main process — silently launching a fresh Electron instance that then hangs on Xvfb.
Replace 1003 with the UID of the service user (id -u <user>). loginctl enable-linger <user> is required so /run/user/<uid> persists without an active login session (loginctl show-user <user> should show Linger=yes).
Global Display Configuration
The Obsidian CLI (v1.12+) uses Electron’s single-instance IPC to communicate with the running headless instance. This requires both DISPLAY and (on 1.12.7+) XDG_RUNTIME_DIR to be set, even for non-interactive SSH commands like gcloud compute ssh ... -- obsidian help.
Shell profile files (.profile, .bashrc) are not sourced for non-interactive SSH sessions, so setting these variables there is not sufficient. Instead, set them in /etc/environment, which is read by PAM for all session types:
File: /etc/environment
DISPLAY=:5XDG_RUNTIME_DIR is typically populated by systemd-logind for interactive sessions (SSH logins get it automatically). For non-interactive invocations, either pass it explicitly (env XDG_RUNTIME_DIR=/run/user/$(id -u) obsidian ...) or set it alongside DISPLAY in /etc/environment.
Service Management
# Enable all services (auto-start on boot)
sudo systemctl enable obsidian-headless-xvfb obsidian-headless-openboxsession
sudo systemctl enable obsidian-headless-x11vnc obsidian-headless
# Start all services
sudo systemctl start obsidian-headless-xvfb
sudo systemctl start obsidian-headless-openboxsession
sudo systemctl start obsidian-headless-x11vnc
sudo systemctl start obsidian-headless
# Check status
sudo systemctl status obsidian-headless-xvfb obsidian-headless-openboxsession
sudo systemctl status obsidian-headless-x11vnc obsidian-headless
# View logs
sudo journalctl -u obsidian-headless -f
sudo journalctl -u obsidian-headless-x11vnc -fConnecting via VNC
Port Forwarding
Forward port 5900 from the remote server to connect locally.
Standard SSH:
ssh -L 5900:localhost:5900 <user>@<host>Google Cloud:
gcloud compute ssh <instance-name> --zone=<zone> -- -L 5900:localhost:5900VNC Client Options (macOS)
TigerVNC (Recommended):
# Install
brew install tiger-vnc
# Connect (use -SecurityTypes VncAuth for reliable authentication)
vncviewer -SecurityTypes VncAuth localhost:5900macOS built-in Screen Sharing:
- Finder →
⌘+K→vnc://localhost:5900 - Note: May have compatibility issues with x11vnc; TigerVNC is more reliable
Troubleshooting VNC
If connection hangs or fails:
- Reset the VNC password on the server:
x11vnc -storepasswd sudo systemctl restart obsidian-headless-x11vnc.service - Use
-SecurityTypes VncAuthflag with TigerVNC - Check x11vnc logs:
sudo journalctl -u obsidian-headless-x11vnc.service -f
CLI
The Obsidian CLI (v1.12+) can be enabled in Settings > General > Advanced. Once enabled, obsidian help lists all available commands.
The CLI works by briefly launching an Electron process that connects to the already-running headless instance via single-instance IPC. It sends the command, pipes the output back, and exits. This means:
- The headless Obsidian service must be running for the CLI to work
- The
DISPLAYvariable must point to the Xvfb display (:5) — see Global Display Configuration - CLI commands can be run non-interactively over SSH, e.g.:
gcloud compute ssh <instance> --zone=<zone> -- obsidian vault