A fully self-hosted homeserver stack built on Docker Compose, covering media streaming, personal cloud, photo backup, system utilities, and storage management β all manageable through a built-in web portal. No cloud dependency. No subscriptions. Everything on your own hardware.
- What's Included
- Architecture Overview
- Quick Start (Fresh Server)
- Service Directory & Ports
- Configuration Reference (.env)
- Management Portal (WebUI)
- setup.sh CLI Reference
- Directory Structure
- Hardware Recommendations
- Updating Services
- Backing Up
The suite is organized into five independent Docker Compose stacks. Each runs in isolation and can be deployed or stopped individually.
| Suite | Purpose |
|---|---|
| π¬ Media | Movie & TV automation, music server, streaming |
| βοΈ Nextcloud | Self-hosted cloud drive & calendar |
| πΈ Immich | Google Photos replacement with ML features |
| ποΈ Storage | File browser, backup engines |
| π οΈ Utility | Password manager, PDF tools, sync, monitoring |
| π Dashboard | Landing portal with live service metrics |
Your Laptop / Phone
β
β (Local network or Tailscale VPN)
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Ubuntu Server β
β β
β :80 Dashboard (Homepage) β
β :8888 Management WebUI βββ You control everything β
β from here β
β ββββββββββββββ ββββββββββββββ ββββββββββββββββ β
β β Media β β Cloud β β Utility β β
β β Stack β β Stack β β Stack β β
β ββββββββββββββ ββββββββββββββ ββββββββββββββββ β
β ββββββββββββββ ββββββββββββββ β
β β Storage β β Dashboard β β
β β Stack β β Stack β β
β ββββββββββββββ ββββββββββββββ β
β β
β /mnt/hdd/media (your media drives) β
β /mnt/hdd/photos (photo backup location) β
β ./appdata/ (all service config data) β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Requirements: Ubuntu 20.04+ or Debian 11+ server β headless is fine, no desktop needed. Run all commands over SSH from your laptop.
This single command installs Node.js, downloads the suite, and starts the WebUI as a system service. It does not install Docker or launch any containers β that's done through the WebUI.
Option A β Short URL (quick)
curl -fsSL https://tinyurl.com/22qdg5nm | sudo bashOption B β Full GitHub URL (recommended if you want to verify the source)
# Inspect the script first (always good practice before piping to bash)
curl -fsSL https://raw.githubusercontent.com/arunkarshan/HomeServerConfiguration/main/bootstrap.sh | less
# Then run it once you're satisfied
curl -fsSL https://raw.githubusercontent.com/arunkarshan/HomeServerConfiguration/main/bootstrap.sh | sudo bashBoth URLs point to the exact same script. Option A redirects to Option B via TinyURL. If you are security-conscious (and you should be), use Option B so you can see exactly what you're running before it executes. The script source is fully readable at bootstrap.sh in this repository.
What it does:
- Installs
curl,unzip, and Node.js LTS via NodeSource - Downloads the suite as a zip from GitHub (no
gitrequired) - Extracts to
/opt/homeserver - Creates and enables a systemd service (
homeserver-webui) that survives reboots - Prints the URL to open in your browser
When it finishes, you'll see:
β WebUI is live and running!
Open this in your browser (from your laptop):
http://192.168.1.x:8888
On your laptop's browser, navigate to the URL printed above. From here, everything else is managed through the portal β no more terminal needed.
In the sidebar, go to Documentation & Guides β Install Docker Engine. Click Install Docker Engine. The portal runs the official Docker convenience installer and streams live output.
Go to Deploy & Setup β Install (From scratch). Fill in your server IP, timezone, user IDs, and storage paths. Select the suites you want and click deploy.
media/docker-compose.yml
| Service | Container | Port | Description |
|---|---|---|---|
| Jellyfin | media_jellyfin |
8096 |
Media streaming server (movies, TV, music) |
| qBittorrent | media_qbittorrent |
8085 |
BitTorrent client with web UI |
| Radarr | media_radarr |
7878 |
Automated movie download manager |
| Sonarr | media_sonarr |
8989 |
Automated TV show download manager |
| Prowlarr | media_prowlarr |
9696 |
Indexer manager for Radarr & Sonarr |
| Flaresolverr | media_flaresolverr |
β | Cloudflare bypass proxy for indexers |
| Jellyseerr | media_jellyseerr |
5055 |
Content request portal for users |
| Bazarr | media_bazarr |
6767 |
Automatic subtitle downloader |
| Navidrome | media_navidrome |
4533 |
Self-hosted music server (Subsonic API) |
| MeTube | media_metube |
8087 |
yt-dlp web UI for YouTube downloads |
Note: Jellyfin is configured with Intel QuickSync / VAAPI hardware acceleration via
/dev/dri. Disable thedevicesblock if your server has no iGPU.
nextcloud/docker-compose.yml
| Service | Container | Port | Description |
|---|---|---|---|
| Nextcloud | nextcloud_app |
8080 |
Self-hosted cloud drive (Google Drive replacement) |
| Nextcloud Cron | nextcloud_cron |
β | Background job scheduler for Nextcloud |
| PostgreSQL 16 | nextcloud_postgres |
β | Database backend for Nextcloud |
Nextcloud is connected to the shared Redis instance from the Utility stack to reduce memory overhead.
immich/docker-compose.yml
| Service | Container | Port | Description |
|---|---|---|---|
| Immich Server | immich_server |
2283 |
Photo/video backup & management (Google Photos replacement) |
| Immich ML | immich_machine_learning |
β | Face recognition, CLIP search, object classification |
| Valkey (Redis) | immich_redis |
β | In-memory cache for Immich |
| PostgreSQL + pgvecto.rs | immich_postgres |
β | Vector-capable database for Immich ML search |
Immich is configured with Intel GPU hardware transcoding via
hwaccel.transcoding.yml. Remove theextendsblock if not needed.
storage/docker-compose.yml
| Service | Container | Port | Description |
|---|---|---|---|
| FileBrowser | storage_filebrowser |
8082 |
Web-based file manager for your media drives |
| Kopia | storage_kopia |
51515 |
Fast, encrypted, deduplicated backup engine |
| Backrest | storage_backrest |
9898 |
Web UI and scheduler for Restic backups |
utility/docker-compose.yml
| Service | Container | Port | Description |
|---|---|---|---|
| Vaultwarden | utility_vaultwarden |
8086 |
Bitwarden-compatible password manager |
| Stirling-PDF | utility_stirling_pdf |
8083 |
PDF processing: split, merge, OCR, compress |
| IT-Tools | utility_it_tools |
8084 |
Developer utilities: encoders, converters, regex |
| Uptime Kuma | utility_uptime_kuma |
3001 |
Service monitoring & downtime alerts |
| Syncthing | utility_syncthing |
8384 |
Peer-to-peer file sync across devices |
| Pairdrop | utility_pairdrop |
3010 |
LAN file sharing (AirDrop for any device) |
| Paperless-ngx | utility_paperless_web |
8010 |
Document scanning, OCR, and archiving |
| Paperless Redis | utility_paperless_redis |
β | Shared Redis cache (also used by Nextcloud) |
| Radicale | utility_radicale |
5232 |
CalDAV/CardDAV server (calendar & contacts sync) |
| Baikal | utility_baikal |
8088 |
Alternative CalDAV/CardDAV server |
| Cronicle | utility_cronicle |
3012 |
Cron job scheduler with web UI |
| Ofelia | utility_ofelia |
β | Docker-native job scheduler |
dashboard/docker-compose.yml
| Service | Container | Port | Description |
|---|---|---|---|
| Homepage | dashboard_homepage |
80 |
Live metrics dashboard (bound to root port) |
| Heimdall | dashboard_heimdall |
8081 |
Visual app launcher portal |
Homepage reads live container metrics via the Docker socket and is configured via
appdata/homepage/.
All stacks share a single .env file in the project root. It is auto-generated and managed by the WebUI β you rarely need to edit it manually.
| Variable | Default | Description |
|---|---|---|
TZ |
Etc/UTC |
Timezone for all containers (e.g. Asia/Kolkata) |
PUID |
1000 |
User ID that owns media files |
PGID |
1000 |
Group ID that owns media files |
SERVER_IP |
β | Local IP of your server (e.g. 192.168.1.10) |
SYSTEM_DATA_DIR |
./appdata |
Where all service config data is stored |
MEDIA_DIR |
/mnt/hdd/media |
Root path of your media drive |
UPLOAD_LOCATION |
/mnt/hdd/immich/photos |
Immich photo upload destination |
NEXTCLOUD_DATA_LOCATION |
/mnt/hdd/nextcloud/data |
Nextcloud user data directory |
DB_DATA_LOCATION |
./appdata/immich/postgres |
Immich database storage path |
GITHUB_REPO |
β | Your GitHub repo for config backups |
GITHUB_TOKEN |
β | GitHub personal access token for git push/pull |
HOMEPAGE_VAR_QBITTORRENT_PASSWORD |
β | qBittorrent WebUI password (for Homepage metrics) |
HOMEPAGE_VAR_PAPERLESS_USERNAME |
β | Paperless username (for Homepage metrics) |
HOMEPAGE_VAR_PAPERLESS_PASSWORD |
β | Paperless password (for Homepage metrics) |
HOMEPAGE_VAR_IMMICH_API_KEY |
β | Immich API key (for Homepage metrics) |
.envis listed in.gitignoreand never committed to version control. Secrets stay on your machine.
The WebUI is a lightweight, responsive SPA (Single Page Application) that runs at http://<your-server-ip>:8888. It is started automatically on boot via a systemd unit.
systemctl status homeserver-webui # check status of portal
systemctl restart homeserver-webui # restart portal
journalctl -u homeserver-webui -f # stream live portal logsThe WebUI is structured into distinct pages accessible via the left sidebar:
- Ecosystem Vitals: Displays real-time aggregate widgets for total containers, running containers, stopped containers, and containers in error state.
- System Bulletin: A centralized notice board showing warning alerts if containers are stopped or unhealthy, or general notices if all services are healthy.
- System Vitals Mini-Card: Quick status snapshot of CPU, RAM, Temperature, and Storage. Clicking it navigates directly to the System Admin panel.
- Container Cards: Displays all containers with their real-time state, custom-mapped service icons, and direct links to view log outputs.
- Container Inspector: Click on any service card to open its detailed metadata viewer:
- Service group classification, linked backend containers, and active image tags.
- Interactive Volume Mounts Map (mapping host directories to container mount points).
- Docker Compose Viewer: Displays the raw Docker Compose configuration block with a copy-to-clipboard button.
The System Administration view manages host level services independent of the Docker daemon:
- Vitals Tab: Real-time polling charts and grids showing exact CPU load, RAM allocation, CPU thermal temperature, and an interactive storage disk capacity table.
- Power Tab: Configures automated server shutdown and wakeup schedules:
- Auto Shutdown: Configurable time (e.g.
23:30) and schedule selection (Everyday, Weekdays, Weekends, or specific days of the week) mapped to system crontabs. - Auto Wakeup: Configured to write to the motherboard's Real Time Clock (RTC) wake alarms (
rtcwake), letting the hardware boot up automatically at the specified time.
- Auto Shutdown: Configurable time (e.g.
- Network Tab: Edits host interface settings. Easily configure DHCP or switch to a Static IP (specifying Address, Gateway, Primary & Secondary DNS) with automated validation.
- Maintenance Tab: Actions to update host system packages, prune unused Docker images/volumes (Garbage Collection), and restart the WebUI process.
- Consolidated Terminal: A single, clean terminal log viewer embedded at the bottom of the System page that streams output from system upgrades, Netplan updates, and Docker pruning.
setup.sh is the underlying bash engine. The WebUI calls it internally, but it can also be run directly from the terminal for scripting or manual administration.
# First-time interactive setup
sudo ./setup.sh
# Non-interactive actions (called by WebUI backend)
sudo ./setup.sh --install-docker # Install Docker Engine & Compose V2
sudo ./setup.sh --update <services> # Pull & redeploy selected services (e.g., 'all' or 'media_jellyfin')
sudo ./setup.sh --restart <services> # Restart selected containers
sudo ./setup.sh --reconfigure <services> # Recreate containers applying latest configuration
sudo ./setup.sh --nuke <services> # Tear down containers and wipe their local configurations
sudo ./setup.sh --prune # Prune unused Docker images, containers, networks, and volumes
sudo ./setup.sh --tailscale # Deploy and authenticate Tailscale VPN
sudo ./setup.sh --install-samba # Install Samba daemon and Cockpit administration GUI
sudo ./setup.sh --samba-info # Retrieve configured Samba shares and active users
sudo ./setup.sh --samba-add-user <user> <pass> # Add a user to the Samba registry
sudo ./setup.sh --samba-remove-user <user> # Delete a Samba user
sudo ./setup.sh --samba-add-share <name> <path> # Register a new folder share in smb.conf
sudo ./setup.sh --samba-remove-share <name> # Delete a share from smb.conf
sudo ./setup.sh --sys-maintenance # Run OS upgrades and clean package caches
sudo ./setup.sh --backup # Compress and archive .env and appdata/ configs
sudo ./setup.sh --git-push # Commit and push configs to GITHUB_REPO
sudo ./setup.sh --sync # Sync latest configs from GITHUB_REPO zipball
sudo ./setup.sh --check-updates # Check for newer tags of running Docker images
sudo ./setup.sh --set-static-ip <iface> <ip> <gw> <dns1> <dns2> # Configure static IP via Netplan
sudo ./setup.sh --set-dhcp <iface> # Revert interface to DHCP auto configuration
sudo ./setup.sh --schedule-power <shutdown_time> <shutdown_days> <wakeup_time> <wakeup_days> <enable_shutdown> <enable_wakeup> # Configure auto power schedulerThe project directory structure is modular and separates frontend assets, backend router, and compose suites:
HomeServerConfiguration/
β
βββ bootstrap.sh # One-command installer (installs Node.js & spawns WebUI)
βββ setup.sh # Core bash orchestrator (handles all system and docker actions)
βββ configure_services.py # Python config compiler
βββ configure_homepage.sh # Homepage dashboard config writer
βββ docker-compose.yml # Shared internal networks definition
βββ .env # Global environment and secrets configuration (gitignored)
βββ .gitignore
β
βββ webui/ # WebUI Node.js Server Project
β βββ server.js # Lightweight web server entry point
β βββ index.html # Core HTML structure & SPA shell
β β
β βββ public/ # Static Frontend Assets
β β βββ app.js # SPA logic, real-time stats polling, routes, events
β β βββ styles.css # Modular CSS design system, cards, terminals, layout
β β
β βββ src/ # Backend Module files
β βββ config.js # Configuration parser and env writer
β βββ runner.js # Secure terminal execution and spawn processor
β βββ routes.js # HTTP router and SSE real-time stream endpoints
β
βββ media/ # π¬ Media suite docker-compose
βββ immich/ # πΈ Photo backup suite docker-compose
βββ nextcloud/ # βοΈ Cloud drive suite docker-compose
βββ storage/ # ποΈ Storage management suite docker-compose
βββ utility/ # π οΈ Utility & admin suite docker-compose
βββ dashboard/ # π Homepage/Heimdall launcher docker-compose
β
βββ appdata/ # Services configuration directories (gitignored)
βββ homepage/ # Config files for the gethomepage dashboard (committed)
βββ ... # Application DBs, configuration files, and state data
| Component | Minimum | Recommended |
|---|---|---|
| CPU | Intel Core i3 (8th gen+) | Intel Core i5/i7 with QuickSync iGPU |
| RAM | 8 GB | 16β32 GB |
| Boot drive | 120 GB SSD | 256 GB SSD |
| Media storage | 2 TB HDD | 4β8 TB HDD (or dedicated storage array) |
| OS | Ubuntu Server 22.04 LTS | Ubuntu Server 24.04 LTS |
| Network | 100 Mbps LAN | Gigabit LAN |
Hardware Accel Tip: An Intel iGPU (8th gen+) enables hardware-accelerated transcoding in both Jellyfin and Immich via QuickSync/VAAPI with zero configuration.
All container updates are managed through the WebUI under Check & Pull Updates or Selective Update. This pulls the latest image tag for each selected service, stops the old container, and restarts it with identical configurations.
To update everything at once from the terminal:
sudo ./setup.sh --update allThe WebUI Backup Configurations action archives all appdata/ config directories and the .env file. The Push Configs to Git action commits and pushes the project (excluding secrets) to your GitHub repository for version control.
To restore on a new machine: bootstrap β git pull β redeploy.
# Full restore workflow
curl -fsSL https://raw.githubusercontent.com/arunkarshan/HomeServerConfiguration/main/bootstrap.sh | sudo bash
# Then in WebUI: Fetch Configs from Git -> Install (From scratch)Tailscale is built into the stack. Once configured (via Configure Tailscale VPN in the WebUI), all your services are accessible from anywhere using your Tailscale IP β without opening any ports on your router.
Built for personal use. Contributions and issues welcome.