# Docker Compose NAS This project provides a comprehensive, self-hosted media and utility server setup using Docker Compose. It aims to replicate and enhance the functionality of a typical NAS using containerized applications on a standard Linux host. The core idea is to manage media libraries (movies, TV shows, music), automate downloads securely, provide easy access via a dashboard, and enable remote access through Tailscale. ## Table of Contents - [Docker Compose NAS](#docker-compose-nas) - [Table of Contents](#table-of-contents) - [Architecture Overview](#architecture-overview) - [Features](#features) - [Prerequisites](#prerequisites) - [Quick Start Guide](#quick-start-guide) - [Configuration (`.env` Variables)](#configuration-env-variables) - [Core System \& Paths](#core-system--paths) - [Networking \& Access (Tailscale)](#networking--access-tailscale) - [Authentication (Authelia)](#authentication-authelia) - [Service Credentials](#service-credentials) - [Homepage Customization \& Widgets](#homepage-customization--widgets) - [Optional Features \& Services](#optional-features--services) - [Detailed Setup \& Usage](#detailed-setup--usage) - [Authelia User Management](#authelia-user-management) - [(Optional) VPN Configuration](#optional-vpn-configuration) - [(Optional) Traefik DNS Challenge](#optional-traefik-dns-challenge) - [Service Access](#service-access) - [Optional Services](#optional-services) - [Troubleshooting](#troubleshooting) - [SELinux Socket Permissions (Docker)](#selinux-socket-permissions-docker) - [Tailscale Issues](#tailscale-issues) - [File Permissions](#file-permissions) - [Advanced Topics](#advanced-topics) ## Architecture Overview This stack uses a combination of key services for routing, access, and security: * **[Tailscale](https://tailscale.com):** Provides a secure overlay network (WireGuard-based VPN) connecting your devices. It allows access to the NAS services from anywhere without opening firewall ports and handles HTTPS termination via its built-in `tailscale serve` or `tailscale funnel` features. All other services run within Tailscale's network namespace. * **[Traefik](https://traefik.io):** Acts as a reverse proxy *within* the Tailscale network. It discovers services via Docker labels and routes incoming requests (from Tailscale) to the appropriate container based on paths (e.g., `/sonarr`, `/radarr`). * **[Authelia](https://www.authelia.com):** Serves as the authentication gateway. Traefik forwards requests to Authelia for verification. If a user isn't logged in, they are redirected to the Authelia portal (`/`). Once authenticated, Authelia sets a session cookie (stored in Redis), and Traefik allows access to the requested service. ## Features This stack includes the following services, categorized for clarity: **Core Infrastructure:** * **Reverse Proxy:** [Traefik](https://traefik.io) - Manages internal routing and service discovery. * **Secure Remote Access:** [Tailscale](https://tailscale.com) - Provides VPN access and HTTPS. * **Authentication:** [Authelia](https://www.authelia.com) & [Redis](https://redis.io) - Single sign-on portal and session management. * **Dashboard:** [Homepage](https://gethomepage.dev) - Centralized access point (at `/home`). **Media Management (\*Arr Suite):** * [Sonarr](https://sonarr.tv): TV show management. * [Radarr](https://radarr.video): Movie management. * [Lidarr](https://lidarr.audio) (Optional): Music management. * [Bazarr](https://www.bazarr.media/): Subtitle management. * [Prowlarr](https://github.com/Prowlarr/Prowlarr): Indexer management for *arr apps. **Download Clients:** * [qBittorrent](https://www.qbittorrent.org): Bittorrent client. * [SABnzbd](https://sabnzbd.org/) (Optional): Usenet download client. **Media Serving & Requests:** * [Jellyfin](https://jellyfin.org): Media server for streaming. * [Jellyseerr](https://github.com/FallenBagel/jellyseerr): Media request management. **Utilities:** * [Watchtower](https://containrrr.dev/watchtower/): Automatic container updates. * [Autoheal](https://github.com/willfarrell/docker-autoheal/): Automatic container restarts on failure. * [Unpackerr](https://unpackerr.zip): Automated archive extraction. **Optional Services (Enabled via Profiles):** * [AdGuard Home](https://adguard.com/en/adguard-home/overview.html): Network-wide ad blocking. * [Calibre-Web](https://github.com/janeczku/calibre-web): E-book library management. * [Decluttarr](https://github.com/manimatter/decluttarr): Automated download cleanup. * [Tandoor Recipes](https://docs.tandoor.dev/): Recipe management. * [Joplin Server](https://joplinapp.org/): Note-taking synchronization server. * [Home Assistant](https://www.home-assistant.io/): Home automation platform. * [Immich](https://immich.app/): Self-hosted photo and video backup. * [FlareSolverr](https://github.com/FlareSolverr/FlareSolverr): Bypasses Cloudflare challenges (e.g., for Prowlarr). ## Prerequisites * **Linux Host:** A system capable of running Docker (e.g., Ubuntu, Debian, Fedora). * **Docker & Docker Compose:** Latest versions installed. See [Docker Engine Install](https://docs.docker.com/engine/install/) and [Docker Compose Install](https://docs.docker.com/compose/install/). * **User Permissions:** Ability to run `docker` commands (user in `docker` group or use `sudo`). * **Basic Linux Knowledge:** Familiarity with command line, text editors, and file permissions. * **Tailscale Account:** Required for remote access. [Sign up here](https://tailscale.com/login). * **(Optional) SELinux:** If enabled, see [Troubleshooting](#selinux-socket-permissions). ## Quick Start Guide This guide gets the core stack running quickly. **Read the full [Configuration](#configuration-env-variables) section afterwards for details.** 1. **Clone Repository:** ```bash git clone https://github.com/AdrienPoupa/docker-compose-nas.git cd docker-compose-nas ``` 2. **Create `.env` File:** ```bash cp .env.example .env ``` 3. **Critical `.env` Edits:** Open `.env` and set **at minimum**: * `USER_ID` & `GROUP_ID`: Run `id -u` and `id -g` on your host to find these. * `TIMEZONE`: Your local timezone (e.g., `Asia/Manila`). * `TAILSCALE_AUTHKEY`: Generate an auth key in your [Tailscale Admin Console](https://login.tailscale.com/admin/settings/keys). * `TAILSCALE_TAILNET_DOMAIN`: Your unique Tailnet domain (e.g., `your-name.ts.net`). * `AUTHELIA_JWT_SECRET`, `AUTHELIA_SESSION_SECRET`, `AUTHELIA_STORAGE_ENCRYPTION_KEY`, `AUTHELIA_REDIS_PASSWORD`: **Generate unique, strong secrets** for these (e.g., using `openssl rand -hex 32`). **Do not use the examples!** 4. **Set Authelia Admin Password:** * Generate a password hash (replace `'your_secure_password'`): ```bash docker run --rm authelia/authelia:latest authelia hash-password 'your_secure_password' ``` * Copy the **entire output hash** (starting with `$argon2id...`). * Open `authelia/users_database.yml` and replace the example `password:` value under `admin:` with your generated hash. 5. **Start the Stack:** ```bash # Make helper script executable (if needed) chmod +x ./update-config.sh # Start containers docker compose up -d ``` *(Wait for containers to download and start)* 6. **(Optional) Run Config Script:** This attempts to set API keys/URLs in *arr apps based on `.env`. May require manual adjustments in apps later. ```bash ./update-config.sh ``` 7. **Access:** Navigate to `https://./`. You should be redirected to the Authelia login. Use `admin` and the password you set in step 4. After login, you'll land on the Homepage dashboard at `/home`. ## Configuration (`.env` Variables) This file controls essential settings. Copy `.env.example` to `.env` and modify the values. **Bold variables** are critical for initial setup. #### Core System & Paths | Variable | Description | Default | | :----------------------- | :---------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------- | | **`USER_ID`** | Linux user ID for container permissions. Find with `id -u`. | `1000` | | **`GROUP_ID`** | Linux group ID for container permissions. Find with `id -g`. | `1000` | | **`TIMEZONE`** | Your local timezone (e.g., `America/New_York`, `Asia/Manila`). [List](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones). | `America/New_York` | | `CONFIG_ROOT` | Host base directory for service configurations. `.` = project subdirs. | `.` | | `DATA_ROOT` | Host directory for media libraries (movies, TV, music, etc.). | `/mnt/data` | | `DOWNLOAD_ROOT` | Host directory for downloads (in progress/completed). **Must be on same filesystem as `DATA_ROOT` for hardlinks.** | `/mnt/data/torrents` | | `IMMICH_UPLOAD_LOCATION` | Host path for Immich uploads (if `immich` profile enabled). | `/mnt/data/photos` | #### Networking & Access (Tailscale) | Variable | Description | Default | | :-------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------- | | **`TAILSCALE_AUTHKEY`** | **Required.** Auth key from [Tailscale Admin Console](https://login.tailscale.com/admin/settings/keys). Use reusable or ephemeral. | *(None)* | | **`TAILSCALE_TAILNET_DOMAIN`**| **Required.** Your unique Tailnet domain (e.g., `your-name.ts.net`). | `your-tailnet.ts.net` | | `TAILSCALE_HOSTNAME` | Desired hostname for this NAS within Tailscale. | `tailscale-nas` | | `TAILSCALE_TAGS` | Optional tags for the Tailscale node (e.g., `tag:nas`). | `tag:nas` | | `ENABLE_FUNNEL_HTTPS` | Use Tailscale Funnel (`true` = public access via Tailscale domain) or Serve (`false` = Tailnet-only access, recommended). | `false` | | `APP_HOSTNAME` | Primary hostname used by Traefik/Authelia. Defaults to Tailscale FQDN. Renamed from `HOSTNAME` to avoid host system conflicts. Can be overridden if using custom DNS. | `${TAILSCALE_HOSTNAME}.${TAILSCALE_TAILNET_DOMAIN}` | #### Authentication (Authelia) | Variable | Description | Default | | :---------------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------- | | **`AUTHELIA_JWT_SECRET`** | **Required.** Random secret for Authelia (used for password reset JWT). **Generate your own!** | *(None - Example in file)* | | **`AUTHELIA_SESSION_SECRET`** | **Required.** Random secret for session cookies. **Generate your own!** | *(None - Example in file)* | | **`AUTHELIA_STORAGE_ENCRYPTION_KEY`** | **Required.** Random secret for encrypting data at rest (e.g., SQLite DB). **Generate your own!** | *(None - Example in file)* | | **`AUTHELIA_REDIS_PASSWORD`** | **Required.** Password for the Redis database (used for session storage). **Generate your own!** | *(None - Example in file)* | | `AUTHELIA_SESSION_DOMAIN` | *Deprecated.* Domain for session cookies. Should match `APP_HOSTNAME`. (Handled within `authelia/configuration.yml` in v4.38+) | `${APP_HOSTNAME}` | | `AUTHELIA_DEFAULT_REDIRECT_URL` | *Deprecated.* Where users land after login. (Handled within `authelia/configuration.yml` in v4.38+) | `https://${APP_HOSTNAME}/home` | #### Service Credentials | Variable | Description | Default | | :----------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------- | | `QBITTORRENT_USERNAME` | Username for qBittorrent Web UI. | `admin` | | `QBITTORRENT_PASSWORD` | Password for qBittorrent Web UI. **Change default!** (May need to use temp password from logs on first run, then change in UI & `.env`). | `adminadmin` | | `CALIBRE_USERNAME` | Username for Calibre-Web (if `calibre-web` profile enabled). | `admin` | | `CALIBRE_PASSWORD` | Password for Calibre-Web (if `calibre-web` profile enabled). | `admin123` | | `IMMICH_DB_PASSWORD` | Password for Immich's internal database (if `immich` profile enabled). | `postgres` | | `ADGUARD_USERNAME` | Username for AdGuard Home (if `adguardhome` profile enabled). | *(None)* | | `ADGUARD_PASSWORD` | Password for AdGuard Home (if `adguardhome` profile enabled). | *(None)* | #### Homepage Customization & Widgets | Variable | Description | Default | | :------------------------------ | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :-------------------- | | `HOMEPAGE_VAR_TITLE` | Title shown on the dashboard. | `Docker-Compose NAS` | | `HOMEPAGE_VAR_SEARCH_PROVIDER` | Default search engine. [Options](https://gethomepage.dev/en/widgets/search/). | `google` | | `HOMEPAGE_VAR_HEADER_STYLE` | Dashboard header style. [Options](https://gethomepage.dev/en/configs/settings/#header-style). | `boxed` | | `HOMEPAGE_VAR_WEATHER_CITY` | City for weather widget. | *(None)* | | `HOMEPAGE_VAR_WEATHER_LAT` | Latitude for weather widget. | *(None)* | | `HOMEPAGE_VAR_WEATHER_LONG` | Longitude for weather widget. | *(None)* | | `HOMEPAGE_VAR_WEATHER_UNIT` | Weather units (`metric` or `imperial`). | `metric` | | `SONARR_API_KEY` | API Keys for various services, primarily used for Homepage widgets. Find keys in each app's settings. | *(None)* | | `RADARR_API_KEY` | " | *(None)* | | `LIDARR_API_KEY` | " (if `lidarr` profile enabled) | *(None)* | | `BAZARR_API_KEY` | " | *(None)* | | `PROWLARR_API_KEY` | " | *(None)* | | `JELLYFIN_API_KEY` | " | *(None)* | | `JELLYSEERR_API_KEY` | " | *(None)* | | `SABNZBD_API_KEY` | " (if `sabnzbd` profile enabled) | *(None)* | | `IMMICH_API_KEY` | " (if `immich` profile enabled) | *(None)* | | `HOMEASSISTANT_ACCESS_TOKEN` | " (if `homeassistant` profile enabled) | *(None)* | #### Optional Features & Services | Variable | Description | Default | | :---------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------- | | `COMPOSE_PROFILES` | Comma-separated list of optional service profiles to enable (e.g., `lidarr,sabnzbd`). See [Optional Services](#optional-services). | *(None)* | | `COMPOSE_PATH_SEPARATOR` | Path separator for `COMPOSE_FILE` (use `;` for Windows). | `:` | | `COMPOSE_FILE` | Colon-separated list of compose files to use. Allows extending base config. | `docker-compose.yml:...` (See `.env.example`) | | `DECLUTTARR_TEST_RUN` | Run Decluttarr in test mode (`True`/`False`)? (if `decluttarr` profile enabled). | `True` | | `DECLUTTARR_...` | Other Decluttarr settings (see `.env.example`). | *(Varies)* | | `PIA_USER` / `PIA_PASS` | Credentials for PIA VPN (if using default VPN setup for qBittorrent). | *(None)* | | `PIA_LOCATION` | PIA server location (if using default VPN). [List](https://serverlist.piaservers.net/vpninfo/servers/v6). | `ca` | | `PIA_LOCAL_NETWORK` | Your local network CIDR (e.g., `192.168.1.0/24`) to allow local access to VPN'd containers. | `192.168.0.0/16` | | `DNS_CHALLENGE` | Enable Traefik DNS challenge for Let's Encrypt (`true`/`false`). **Not needed if using Tailscale for HTTPS.** | `true` | | `DNS_CHALLENGE_PROVIDER` | Your DNS provider (e.g., `cloudflare`). [Providers](https://doc.traefik.io/traefik/https/acme/#providers). | `cloudflare` | | `LETS_ENCRYPT_EMAIL` | Email for Let's Encrypt (if using DNS challenge). | *(None)* | | `LETS_ENCRYPT_CA_SERVER` | Let's Encrypt server URL (if using DNS challenge). | `https://acme-v02.api.letsencrypt.org/directory` | | `CLOUDFLARE_...` / `PROVIDER_...` | DNS provider API credentials (if using DNS challenge). | *(None)* | | `HOMEASSISTANT_HOSTNAME` | Specific hostname for Home Assistant (if `homeassistant` profile enabled). | *(None)* | | `IMMICH_HOSTNAME` | Specific hostname for Immich (if `immich` profile enabled). | *(None)* | | `ADGUARD_HOSTNAME` | Specific hostname for AdGuard Home (if `adguardhome` profile enabled). | *(None)* | ## Detailed Setup & Usage ### Authelia User Management Authelia uses the `authelia/users_database.yml` file to manage users. * **Setting the Initial Admin Password:** 1. As mentioned in the Quick Start, you **must** set a strong password for the default `admin` user. 2. Generate a hash using Docker (replace `'your_secure_password'`): ```bash docker run --rm authelia/authelia:latest authelia hash-password 'your_secure_password' ``` 3. Copy the **entire output hash** (starting with `$argon2id...`). 4. Open `authelia/users_database.yml` and replace the example `password:` value under `admin:` with your generated hash. 5. Ensure the `admin` user belongs to the `admins` and `users` groups as shown in the example. * **Adding More Users:** 1. Generate a password hash for the new user as shown above. 2. Edit `authelia/users_database.yml`. 3. Add a new entry under `users:`, following the format of the `admin` user: ```yaml users: admin: # ... (admin details) ... newuser: displayname: "New User Name" password: "paste_generated_hash_here" email: newuser@example.com groups: - users # Add to 'admins' group if needed ``` 4. Save the file. Authelia should pick up the changes automatically (or restart the Authelia container: `docker compose restart authelia`). * **Enabling User Registration (Optional):** 1. Edit `authelia/configuration.yml`. 2. Find the commented-out `registration:` section near the bottom. 3. Uncomment it and set `enable: true`: ```yaml # registration: # enable: false # Set to true to enable registration form ``` becomes: ```yaml registration: enable: true ``` 4. Save the file and restart Authelia (`docker compose restart authelia`). 5. A "Register" link will now appear on the Authelia login page. * **Approving Registered Users:** 1. When a user registers (if enabled), their details are added to `authelia/users_database.yml` but marked as `disabled: true`. 2. To approve them, edit `authelia/users_database.yml`. 3. Find the new user's entry. 4. Change `disabled: true` to `disabled: false` (or simply remove the `disabled: true` line). 5. Save the file. The user should now be able to log in. ### (Optional) VPN Configuration *(Details about configuring the PIA VPN or other VPN setups could go here if needed.)* ### (Optional) Traefik DNS Challenge *(Details about setting up DNS provider credentials for Let's Encrypt could go here if needed.)* ## Service Access With the default Tailscale setup and Authelia enabled, services are securely accessible via HTTPS using your Tailscale node's name or IP. Access requires authentication via Authelia. * **Login Portal:** `https:///` (Redirects unauthenticated users here) * **Homepage Dashboard:** `https:///home` (Accessible after login) * **Sonarr:** `https:///sonarr` (Requires login) * **Radarr:** `https:///radarr` (Requires login) * **qBittorrent:** `https:///qbittorrent` * **Jellyfin:** `https:///jellyfin` * ...and so on. Replace `` with your Tailscale device name (e.g., `tailscale-nas.your-tailnet.ts.net`) or its Tailscale IP address. If you configure DNS for your `APP_HOSTNAME` variable to point to the Tailscale IP, you can use `https:///`. ## Optional Services Several services are included but disabled by default. Enable them by adding their profile name to the `COMPOSE_PROFILES` variable in your `.env` file (separate multiple profiles with commas). Example: Enable Lidarr and SABnzbd ```dotenv COMPOSE_PROFILES=lidarr,sabnzbd ``` Available Profiles: * `lidarr`: Music management. * `sabnzbd`: Usenet download client. * `flaresolverr`: Bypasses Cloudflare challenges for Prowlarr. * `adguardhome`: Network-wide ad blocking (see `adguardhome/README.md`). * `calibre-web`: E-book library management. * `decluttarr`: Automated download cleanup. * `tandoor`: Recipe management (see `tandoor/README.md`). * `joplin`: Note-taking server (see `joplin/README.md`). * `homeassistant`: Home automation (see `homeassistant/README.md`). * `immich`: Photo management (see `immich/README.md`). ## Troubleshooting ### SELinux Socket Permissions (Docker) If you are running Docker on a host with SELinux enabled (like Fedora, CentOS, RHEL) and services like Traefik, Watchtower, or Autoheal fail with "permission denied" errors when trying to access `/var/run/docker.sock`: 1. **Check Audit Logs:** Immediately after seeing the error, check the SELinux audit log on the host: ```bash sudo ausearch -m avc -ts recent ``` Look for lines containing `denied`, `docker.sock`, and the name of the failing service (e.g., `traefik`, `watchtower`). 2. **Generate Custom Policy:** If denials are found, you may need to create a custom SELinux policy module using `audit2allow`. Pipe the denial messages into it: ```bash # Generate policy files (my-dockersock.te and my-dockersock.pp) sudo ausearch -m avc -ts recent | audit2allow -M my-dockersock # Install the policy module sudo semodule -i my-dockersock.pp ``` This allows the specific actions that were being denied. You might need to repeat this if different denials appear after applying the first policy. ### Tailscale Issues * **Authentication:** Ensure your `TAILSCALE_AUTHKEY` in `.env` is valid and hasn't expired (especially if using ephemeral keys). Check the `tailscale` container logs (`docker compose logs tailscale`) for authentication errors. * **Connectivity:** Verify the `tailscale` container is running and connected to your Tailnet (`docker compose exec tailscale tailscale status`). * **Funnel/Serve Command:** If you modified the Tailscale command, ensure the syntax for `tailscale funnel` or `tailscale serve` is correct. ### File Permissions If services report permission errors when accessing `/config` or `/data` directories, double-check that: * The `USER_ID` and `GROUP_ID` in your `.env` file match the owner/group of the corresponding `CONFIG_ROOT` and `DATA_ROOT` directories on your host. * The host directories have appropriate read/write permissions for that user/group. * If using SELinux, the `:Z` flag on the volume mounts in `docker-compose.yml` is correctly applied to allow the container to write to the host paths. ## Advanced Topics *(Relevant sections like Synology Quirks, NFS Share, Static IP, etc., can be kept here, but review them to ensure they align with a standard Docker setup rather than Podman specifics where applicable.)* *(Example: Synology section should focus on Docker package setup, port conflicts, user IDs, etc., relevant to DSM.)* *(Example: Remove Podman-specific commands or troubleshooting steps from these sections.)* --- *Self-hosted media stack powered by Docker, Traefik, Tailscale, and the \*arr suite.*