A self-hosted, responsive dashboard for your services. Fully configurable from the browser — no code editing needed.
Ctrl+K)http://, domains get https://No build step — Pure HTML/CSS/JS frontend, Node.js backend
Widgets — Clock, Weather, Bookmarks, Text, Image, Iframe, Countdown, Separator, System monitor

Main dashboard — organize your self-hosted services by category, with live health checks and custom tags.

Inline configuration — add, edit and reorder services directly from the browser, no config file editing needed.

Multi-server support — click any service to choose which server to open.

Backup manager — manual backups on demand and automatic weekly backups, restorable with a single click.
Note: This project was built with AI assistance (vibe-coded). It works well for personal homelab use, but review the code with your own judgment before deploying in sensitive environments.
1. Create a folder for Roampage and navigate into it:
mkdir roampage && cd roampage
2. Create the docker-compose.yml file:
nano docker-compose.yml
Paste the following content, then save with Ctrl+O, Enter, and exit with Ctrl+X:
services:
roampage:
image: anagraphy/roampage:latest
container_name: roampage
ports:
- "3046:3000"
volumes:
- roampage_data:/data
restart: unless-stopped
volumes:
roampage_data:
3. Start the container:
docker compose up -d
4. Open your browser and go to:
http://localhost:3046
Click Config in the top-right corner to start adding your services.
If you prefer to build the image yourself instead of pulling it:
git clone https://github.com/Anagraphy/roampage.git
cd roampage
docker compose up -d --build
All configuration is done through the web UI. Click the Config button in the top-right corner.
You can also manually edit the config file at the mounted volume path /data/config.json inside the container.
| Variable | Default | Description |
|---|---|---|
PORT |
3000 |
Internal port the server listens on |
CONFIG_PATH |
/data/config.json |
Path to the config file |
ENCRYPTION_KEY |
(auto) | AES-256-GCM key for config & backups — see below |
Config and backups are encrypted at rest with AES-256-GCM. Three modes are available:
ENCRYPTION_KEY value |
Behaviour |
|---|---|
| (not set) | A random key is generated on first run and saved to ./data/.roampage.key. Survives container restarts as long as the volume is intact, but backups become unrestorable if the key file is lost. |
"64-hex-chars" |
Fixed key, stays valid across rebuilds and volume migrations. Recommended for reliability. Generate once with openssl rand -hex 32. |
"none" |
Encryption disabled — config and backups stored as plain JSON. Backups are always restorable; no key to manage. |
Migration note: switching from encrypted to
none(or vice-versa) requires exporting your config from the UI first, then deletingdata/config.jsonand any old backups before restarting with the new mode.
ports:
- "8080:3000"
volumes:
- ./data:/data
Warning: With a bind mount, make sure the
volumes:section is always present in yourdocker-compose.yml. If it is missing, data is written inside the container and will be lost on the nextdocker compose down.
docker compose pull && docker compose up -d
Your data is never affected by updates as long as the volume is correctly mounted.
/data/config.json/data/backups//data/wallpapers/All data is persisted via the Docker volume and is never touched by image updates.
Roampage is designed for trusted local networks (home LAN, self-hosted VPN). Its security model assumes that anyone who can reach the server is allowed to use it — there is no built-in login.
Anyone who can reach Roampage can read and write your full configuration.
If you expose Roampage outside your local network (public internet, shared VPN, etc.), add authentication at the reverse-proxy layer before doing so. Tools like Authelia, Authentik, or nginx basic auth work well for this.
| Protection | Details |
|---|---|
| Config & backups at rest | AES-256-GCM encryption (see Encryption) |
| Security headers | CSP, HSTS (HTTPS only), X-Content-Type-Options, Referrer-Policy |
| Rate limiting | All API endpoints are rate-limited per IP |
| SSRF | Loopback and cloud metadata IPs are blocked; DNS rebinding mitigated |
| File uploads | Extension + magic-byte validation, 10 MB size limit |
$ claude mcp add roampage \
-- python -m otcore.mcp_server <graph>