Automated setup scripts for Ubuntu home server.
curl -fsSL https://raw.githubusercontent.com/seigiard/homelab/main/scripts/setup.sh | bashThe script will:
- Install git and clone repository to
/opt/homelab - Update system (apt update/upgrade)
- Install packages (zsh, micro, zoxide, htop, mc, jq, ffmpeg, mediainfo, chafa, etc.)
- Setup Oh-My-Zsh with plugins
- Configure git
- Setup Avahi (mDNS)
- Apply dotfiles
- Install Docker
- Generate SSH key for GitHub (interactive step)
homelab/
├── scripts/
│ ├── setup.sh # Entry point (curl | bash)
│ ├── bootstrap.sh # Docker, directories, permissions
│ ├── lib/
│ │ ├── config.sh # Shared variables
│ │ └── tui.sh # TUI library
│ └── setup/
│ ├── --init.sh # Orchestrator
│ ├── 00-update-system.sh
│ ├── 01-install-packages.sh
│ ├── 02-setup-zsh.sh
│ ├── 03-setup-git.sh
│ ├── 04-setup-avahi.sh
│ ├── 05-apply-dotfiles.sh
│ ├── 06-run-bootstrap.sh
│ ├── 07-setup-ssh-key.sh
│ └── 08-show-summary.sh
├── dotfiles/ # Symlinked to ~
├── services/ # Docker services
│ ├── traefik/ # Reverse proxy + avahi-helper (*.home.local)
│ └── homepage/ # Dashboard with Docker auto-discovery
└── tests/ # Docker testing
./scripts/docker/deploy.sh # Deploy all services
./scripts/docker/stop.sh # Stop all
./scripts/docker/status.sh # Container status
./scripts/docker/rebuild.sh # Rebuild (pull + restart)
./scripts/docker/deploy.sh traefik # Deploy single serviceAfter first deployment, containers auto-start on reboot (restart: unless-stopped).
| Service | Local HTTP (backup) | Local HTTPS (primary) | Description |
|---|---|---|---|
| Homepage | http://home.local | https://1218217.xyz | Dashboard with Docker auto-discovery |
| Traefik | http://traefik.home.local | https://traefik.1218217.xyz | Reverse proxy dashboard |
| Authelia | http://auth.home.local | https://auth.1218217.xyz | SSO authentication (forwardAuth) |
| Dozzle | http://dozzle.home.local | https://dozzle.1218217.xyz | Docker logs viewer |
| Glances | http://glances.home.local | https://glances.1218217.xyz | System monitoring |
| FileBrowser | http://files.home.local | https://files.1218217.xyz | Web file manager |
| OPDS Generator | http://opds.home.local | https://opds.1218217.xyz | E-book OPDS catalog |
| Backrest | http://backup.home.local | https://backup.1218217.xyz | Backup management (restic + rclone) |
| Jellyfin | http://movies.home.local | https://movies.1218217.xyz | Media streaming server |
| AdGuard Home | http://dns.home.local | https://dns.1218217.xyz | DNS & Ad Blocker |
| Transmission | http://torrent.home.local | https://torrent.1218217.xyz | Public torrent client |
| Transmission OMG | http://ptorrent.home.local | https://ptorrent.1218217.xyz | Private torrent client |
| PriceBuddy | http://prices.home.local | https://prices.1218217.xyz | Price tracker |
| Cloudflared | — | *.1218217.xyz (external) | Cloudflare Tunnel (external access) |
| Samba | — | — | SMB file shares (ports 139, 445) |
Note: Local HTTPS requires split-horizon DNS (AdGuard Home) to resolve
*.1218217.xyzto local IP. External access via Cloudflare Tunnel continues to work independently.
- Create
services/myservice/docker-compose.yml - Add Traefik labels for routing
- Add Homepage labels for auto-discovery
- Run
./scripts/docker/deploy.sh myservice
Example labels:
labels:
# Traefik - HTTP (local backup)
- traefik.enable=true
- traefik.http.routers.myservice.rule=Host(`myservice.${LOCAL_DOMAIN:-home.local}`)
- traefik.http.routers.myservice.entrypoints=web
- traefik.http.services.myservice.loadbalancer.server.port=8080
# Traefik - HTTPS (local primary)
- traefik.http.routers.myservice-secure.rule=Host(`myservice.${EXTERNAL_DOMAIN:-1218217.xyz}`)
- traefik.http.routers.myservice-secure.entrypoints=websecure
- traefik.http.routers.myservice-secure.tls=true
- traefik.http.routers.myservice-secure.tls.certresolver=cloudflare
# Authelia protection (optional - for services requiring auth)
- traefik.http.routers.myservice-secure.middlewares=authelia@docker
# Homepage
- homepage.group=Services
- homepage.name=My Service
- homepage.icon=myservice
- homepage.href=https://myservice.${EXTERNAL_DOMAIN:-1218217.xyz}Verify system state after installation:
cd ~/homelab
./scripts/healthcheck.shChecks:
- Installed packages (zsh, git, jq, micro, zoxide, etc.)
- User shell (zsh)
- SSH key
- Git config
- Hostname
- Dotfiles (symlinks)
- Docker (daemon, compose, traefik-net network)
After installation, you can re-run any step:
cd ~/homelab
./scripts/setup/07-setup-ssh-key.shSafari uses the system mDNS resolver and works immediately. Chrome and Firefox use their own DNS resolvers which may cache failed requests to .local domains.
Solution — clear browser DNS cache:
Chrome:
chrome://net-internals/#dns → Clear host cache
Firefox:
about:networking → DNS → Clear DNS Cache
After clearing cache, *.home.local should work in all browsers.
Main variables in scripts/lib/config.sh:
GITHUB_USER="seigiard"
GITHUB_EMAIL="seigiard@gmail.com"
INSTALL_PATH="/opt/homelab"
HOSTNAME="home"rclone is installed automatically. Configure a remote for backups:
rclone configExample: Add Google Drive remote named gdrive:
- Choose
n(new remote) - Name:
gdrive - Storage:
drive(Google Drive) - Follow OAuth flow in browser
Verify configuration:
rclone listremotes # Should show: gdrive:
rclone lsd gdrive: # List foldersAfter deploying Backrest (./scripts/docker/deploy.sh backrest):
- Open http://backup.home.local
- Add Repository:
- URI:
rclone:gdrive:backups/homelab - Password: create a strong encryption password (save it!)
- URI:
- Add Backup Plans:
- Path:
/backup/appdata→ container configs - Path:
/backup/users→ user data - Schedule:
0 3 * * *(daily at 3 AM)
- Path:
- Test: Run backup manually, verify in Google Drive
Backrest uses restic for encrypted, deduplicated backups.
For local HTTPS access to *.1218217.xyz, configure Let's Encrypt certificates via Cloudflare DNS challenge.
- Go to Cloudflare Dashboard → My Profile → API Tokens
- Create Token with permissions:
Zone:DNS:Editfor zone1218217.xyz - Save the token
Add to your .env:
ACME_EMAIL=your-email@example.com
CF_DNS_API_TOKEN=your-cloudflare-api-tokentouch services/traefik/data/acme.json
chmod 600 services/traefik/data/acme.json./scripts/docker/rebuild.sh traefikIn AdGuard Home (http://dns.home.local), add DNS rewrites:
*.1218217.xyz → 192.168.1.41 (your server IP)
This makes local devices resolve *.1218217.xyz to the local server while external access continues via Cloudflare Tunnel.
# Check certificate issuance
docker logs traefik 2>&1 | grep -i "acme\|certificate"
# Test HTTPS locally
curl -v https://traefik.1218217.xyz