Arishawke's Homelab
A browser-sized desktop about my homelab. Everything on it is curated and simulated. Nothing here is live.
About
I build and run a small homelab, and this is the tour. Take a look around!
What I build
- a high fantasy world (Aevren)
- a private Android calendar (Asala)
- a color theme used everywhere (Fadetouched)
- this desktop
Find me
This desktop is a simulation: hand-written data, a window manager built for the browser, and not one packet to the real lab. The real one is air-gapped from your browser, the way we both prefer it.
Terminal
Services
status: onlinemaintenanceprivatepublic
- AdGuard Homedns and ad blockingonline
- Caddyreverse proxyonline
- Homepagethis dashboardonline
- Immichphoto managementonline
- Jellyfinmedia serveronline
- Mealierecipe boxonline
- Navidromemusic serveronline
- Uptime Kumaservice monitoringonline
- LRCGETsynced lyricsmaintenance
- Actual Budgetbudgetingprivate
- Backrestbackupsprivate
- Baikalcalendar and contactsprivate
- BentoPDFpdf toolboxprivate
- Courierparcel intakeprivate
- Dockhanddocker managementprivate
- FileBrowser Quantumfile managementprivate
- Gluetunvpn tunnelprivate
- Kavitaebook libraryprivate
- Komfmetadata fetcherprivate
- OmniToolsutility beltprivate
- Paperless-ngxdocument managementprivate
- Scrutinydrive healthprivate
- Shelfmarkreading trackerprivate
- SillyTavernai chat frontendprivate
- Suwayomimanga readerprivate
- Syncthingfile syncprivate
- The Stacksthe acquisitions departmentprivate
- Forgejogit forgepublic
Projects
Aevren
coming soonA high fantasy world: regions, bestiary, and the book that holds them.
- Astro
- vanilla JS
- hand-drawn SVG
Asala
stableA private, open-source calendar for Android. No account, no cloud.
- Kotlin
- Room
- Material 3
Fadetouched
stableA dark, earthy color theme, ported to every tool I touch.
- OKLCH
- design tokens
LuxeCafe
stableA dashboard for dialing in espresso shots on the Ninja Luxe.
- TypeScript
- charts
This desktop
activeThe site you are on: a browser window manager built by hand.
- Astro
- vanilla JS
- bespoke CSS
Lab Dashboard
Machine
- Beelink Me ProIntel N150 . 16GB LPDDR5-4800 . 1TB SSDOne box now. It does not miss being three.
Drives
- docker1TB . SSDBoot drive. Containers and apps live here.
- storage16TB . IronWolf Pro HDDMedia, books, whatever earns a permanent home.
- backup16TB . IronWolf Pro HDDMirrors storage by hand. Not a backup strategy on its own.
Systems
- Compute1 machine4 cores, 16GB RAM, no failover
- Storage9.8 TiBof about 30 TiB raw across the SSD and two IronWolf Pros
- Network2 VLANslab and life, firewalled apart
- Containers28 running2 one-shots exited clean, 0 on fire
- Backupsnightlyverified weekly, restored quarterly
- Uptime247 days, 3 power cuts survivedthe counter resets, the lab does not
Firefox

Good evening
28 Jul 2026 at 02:23
Core
- Caddy
Reverse proxy
- AdGuard Home
DNS and ad blocking
7,513,697queries1,003,146blocked0filtered0.501 mslatency - Gluetun
VPN tunnel
- Dockhand
Docker management
- Uptime Kuma
Service monitoring
22sites up4sites down85%uptime - Scrutiny
Drive health
- Backrest
Backups
- Syncthing
File sync
Media
- Jellyfin
Media server
64movies64series3,545episodes0songs - Navidrome
Music server
0active streams - Suwayomi
Manga reader
4,095read7,498unread0downloaded11,593sources - Kavita
Ebook library
- Komf
Metadata fetcher
- LRCGET
Synced lyrics
- The Stacks
The acquisitions department
32wanted0queue235shelved2requests - Courier
Parcel intake
0leech7seed0 B/sdown0.7 MiB/sup
Library
- Paperless-ngx
Document management
- Shelfmark
Reading tracker
- BentoPDF
PDF toolbox
- FileBrowser Quantum
File management
Life
- Immich
Photo management
1users1,712photos34videos3.6 GiBstorage - Mealie
Recipe box
- Actual Budget
Budgeting
- Baikal
Calendar and contacts
Tools
- Forgejo
Git forge
- OmniTools
Utility belt
- SillyTavern
AI chat frontend
- Homepage
This dashboard
Files
/
home
ari
.bashrc
# ~/.bashrc: interactive shell bits only, batch jobs never read this export EDITOR=nvim export PATH="$HOME/.local/bin:$PATH" alias ll='ls -lah' alias gs='git status' alias dc='docker compose' alias stacks='cd /opt/stacks' # the day I forget this alias is the day I start typing docker compose in full alias dcl='docker compose logs -f' PS1='\u@\h:\w\$ '
.gitconfig
[user] name = ari email = ari@home.lan [core] editor = nvim [init] defaultBranch = main [alias] st = status co = checkout last = log -1 HEAD
notes.md
# notes - scrutiny says the backup drive is approaching end of life again. it said that in 2025 too. still spinning. - remember: gluetun forwards a port, the port changes, nothing downstream ever remembers to check. - baikal calendar sync broke for a week and nobody noticed until a birthday went uncelebrated. calendars are the loneliest self hosted app. - if paperless ever misfiles another utility bill under "correspondence: the void", start the sweep by hand. - the stacks card catalog is fiction, but the shelves are not. mostly. - reminder to self: this file, like the rest of this desktop, is decorative. the real notes live somewhere less browsable.
opt
stacks
core
.env
# core stack, synthetic env only TZ=Etc/UTC PUID=1000 PGID=1000
compose.yaml
services:
caddy:
image: caddy:2-alpine
container_name: caddy
ports:
- "80:80/tcp"
- "443:443/tcp"
restart: unless-stopped
volumes:
- /opt/stacks/core/caddy/config:/config
- /srv/data/core/caddy:/data
adguardhome:
image: adguard/adguardhome:latest
container_name: adguardhome
ports:
- "53:53/udp"
- "53:53/tcp"
- "3053:3000/tcp"
restart: unless-stopped
volumes:
- /opt/stacks/core/adguardhome/config:/config
- /srv/data/core/adguardhome:/data
dockhand:
image: fnsys/dockhand:latest
container_name: dockhand
ports:
- "8081:3000/tcp"
restart: unless-stopped
volumes:
- /opt/stacks/core/dockhand/config:/config
- /srv/data/core/dockhand:/data
uptime-kuma:
image: louislam/uptime-kuma:1
container_name: uptime-kuma
ports:
- "3001:3001/tcp"
restart: unless-stopped
volumes:
- /opt/stacks/core/uptime-kuma/config:/config
- /srv/data/core/uptime-kuma:/data
scrutiny:
image: ghcr.io/analogj/scrutiny:latest-omnibus
container_name: scrutiny
ports:
- "8087:8080/tcp"
restart: unless-stopped
volumes:
- /opt/stacks/core/scrutiny/config:/config
- /srv/data/core/scrutiny:/data
backrest:
image: garethgeorge/backrest:latest
container_name: backrest
ports:
- "9898:9898/tcp"
restart: unless-stopped
volumes:
- /opt/stacks/core/backrest/config:/config
- /srv/data/core/backrest:/data
syncthing:
image: syncthing/syncthing:latest
container_name: syncthing
ports:
- "8384:8384/tcp"
- "22000:22000/tcp"
restart: unless-stopped
volumes:
- /opt/stacks/core/syncthing/config:/config
- /srv/data/core/syncthing:/datadownloads
.env
# downloads stack, synthetic env only TZ=Etc/UTC PUID=1000 PGID=1000
compose.yaml
services:
gluetun:
image: qmcgaw/gluetun:latest
container_name: gluetun
ports:
- "8080:8080/tcp"
restart: unless-stopped
volumes:
- /opt/stacks/downloads/gluetun/config:/config
- /srv/data/downloads/gluetun:/data
courier:
image: forge.arishawke.com/lab/courier:latest
container_name: courier
restart: unless-stopped
volumes:
- /opt/stacks/downloads/courier/config:/config
- /srv/data/downloads/courier:/data
the-stacks:
image: forge.arishawke.com/lab/the-stacks:latest
container_name: the-stacks
ports:
- "2500:2500/tcp"
restart: unless-stopped
volumes:
- /opt/stacks/downloads/the-stacks/config:/config
- /srv/data/downloads/the-stacks:/datalife
.env
# life stack, synthetic env only TZ=Etc/UTC PUID=1000 PGID=1000
compose.yaml
services:
paperless:
image: ghcr.io/paperless-ngx/paperless-ngx:latest
container_name: paperless
ports:
- "8000:8000/tcp"
restart: unless-stopped
volumes:
- /opt/stacks/life/paperless/config:/config
- /srv/data/life/paperless:/data
mealie:
image: ghcr.io/mealie-recipes/mealie:latest
container_name: mealie
ports:
- "9925:9000/tcp"
restart: unless-stopped
volumes:
- /opt/stacks/life/mealie/config:/config
- /srv/data/life/mealie:/data
actual-budget:
image: actualbudget/actual-server:latest
container_name: actual-budget
ports:
- "5006:5006/tcp"
restart: unless-stopped
volumes:
- /opt/stacks/life/actual-budget/config:/config
- /srv/data/life/actual-budget:/data
baikal:
image: ckulka/baikal:nginx
container_name: baikal
ports:
- "8800:80/tcp"
restart: unless-stopped
volumes:
- /opt/stacks/life/baikal/config:/config
- /srv/data/life/baikal:/data
shelfmark:
image: ghcr.io/calibrain/shelfmark:latest
container_name: shelfmark
ports:
- "8085:8084/tcp"
restart: unless-stopped
volumes:
- /opt/stacks/life/shelfmark/config:/config
- /srv/data/life/shelfmark:/data
bentopdf:
image: ghcr.io/alam00000/bentopdf-simple:latest
container_name: bentopdf
ports:
- "8082:8080/tcp"
restart: unless-stopped
volumes:
- /opt/stacks/life/bentopdf/config:/config
- /srv/data/life/bentopdf:/datamedia
.env
# media stack, synthetic env only TZ=Etc/UTC PUID=1000 PGID=1000
compose.yaml
services:
jellyfin:
image: jellyfin/jellyfin:10.10.7
container_name: jellyfin
ports:
- "8096:8096/tcp"
restart: unless-stopped
volumes:
- /opt/stacks/media/jellyfin/config:/config
- /srv/data/media/jellyfin:/data
navidrome:
image: deluan/navidrome:latest
container_name: navidrome
ports:
- "4533:4533/tcp"
restart: unless-stopped
volumes:
- /opt/stacks/media/navidrome/config:/config
- /srv/data/media/navidrome:/data
suwayomi:
image: ghcr.io/suwayomi/suwayomi-server:stable
container_name: suwayomi
ports:
- "4567:4567/tcp"
restart: unless-stopped
volumes:
- /opt/stacks/media/suwayomi/config:/config
- /srv/data/media/suwayomi:/data
kavita:
image: jvmilazz0/kavita:latest
container_name: kavita
ports:
- "5000:5000/tcp"
restart: unless-stopped
volumes:
- /opt/stacks/media/kavita/config:/config
- /srv/data/media/kavita:/data
komf:
image: sndxr/komf:latest
container_name: komf
ports:
- "8086:8085/tcp"
restart: unless-stopped
volumes:
- /opt/stacks/media/komf/config:/config
- /srv/data/media/komf:/data
lrcget:
image: gtt1229/docker-lrcget:latest
container_name: lrcget
ports:
- "5800:5800/tcp"
restart: unless-stopped
volumes:
- /opt/stacks/media/lrcget/config:/config
- /srv/data/media/lrcget:/dataphotos
.env
# photos stack, synthetic env only TZ=Etc/UTC PUID=1000 PGID=1000
compose.yaml
services:
immich:
image: ghcr.io/immich-app/immich-server:release
container_name: immich
ports:
- "2283:2283/tcp"
restart: unless-stopped
volumes:
- /opt/stacks/photos/immich/config:/config
- /srv/data/photos/immich:/datatools
.env
# tools stack, synthetic env only TZ=Etc/UTC PUID=1000 PGID=1000
compose.yaml
services:
forgejo:
image: codeberg.org/forgejo/forgejo:15
container_name: forgejo
ports:
- "3300:3000/tcp"
- "2222:22/tcp"
restart: unless-stopped
volumes:
- /opt/stacks/tools/forgejo/config:/config
- /srv/data/tools/forgejo:/data
filebrowser-quantum:
image: ghcr.io/gtsteffaniak/filebrowser:latest
container_name: filebrowser-quantum
ports:
- "8090:80/tcp"
restart: unless-stopped
volumes:
- /opt/stacks/tools/filebrowser-quantum/config:/config
- /srv/data/tools/filebrowser-quantum:/data
omni-tools:
image: iib0011/omni-tools:latest
container_name: omni-tools
ports:
- "8092:80/tcp"
restart: unless-stopped
volumes:
- /opt/stacks/tools/omni-tools/config:/config
- /srv/data/tools/omni-tools:/data
sillytavern:
image: ghcr.io/sillytavern/sillytavern:latest
container_name: sillytavern
ports:
- "8008:8000/tcp"
restart: unless-stopped
volumes:
- /opt/stacks/tools/sillytavern/config:/config
- /srv/data/tools/sillytavern:/data
homepage:
image: ghcr.io/gethomepage/homepage:latest
container_name: homepage
ports:
- "3000:3000/tcp"
restart: unless-stopped
volumes:
- /opt/stacks/tools/homepage/config:/config
- /srv/data/tools/homepage:/datasrv
media
Books
Movies
Music
Shows
3 items (0 folders, 3 files)This filesystem is decorative.
