39 KiB
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
Architecture Overview
This stack uses a combination of key services for routing, access, and security:
- Tailscale: 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 serveortailscale funnelfeatures. All other services run within Tailscale's network namespace. - Traefik: 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: 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. You can configure which services require authentication via environment variables.
Features
This stack includes the following services, categorized for clarity:
Core Infrastructure:
- Reverse Proxy: Traefik - Manages internal routing and service discovery.
- Secure Remote Access: Tailscale - Provides VPN access and HTTPS.
- Authentication: Authelia & Redis - Single sign-on portal and session management.
- Dashboard: Homepage - Centralized access point (at
/home).
Media Management (*Arr Suite):
- Sonarr: TV show management.
- Radarr: Movie management.
- Lidarr (Optional): Music management.
- Bazarr: Subtitle management.
- Prowlarr: Indexer management for *arr apps.
Download Clients:
- qBittorrent: Bittorrent client.
- SABnzbd (Optional): Usenet download client.
Media Serving & Requests:
- Jellyfin: Media server for streaming.
- Jellyseerr: Media request management.
Utilities:
- Watchtower: Automatic container updates.
- Autoheal: Automatic container restarts on failure.
- Unpackerr: Automated archive extraction.
Optional Services (Enabled via Profiles):
- AdGuard Home: Network-wide ad blocking.
- Calibre-Web: E-book library management.
- Decluttarr: Automated download cleanup.
- Tandoor Recipes: Recipe management.
- Joplin Server: Note-taking synchronization server.
- Home Assistant: Home automation platform.
- Immich: Self-hosted photo and video backup.
- 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 and Docker Compose Install.
- User Permissions: Ability to run
dockercommands (user indockergroup or usesudo). - Basic Linux Knowledge: Familiarity with command line, text editors, and file permissions.
- Tailscale Account: Required for remote access. Sign up here.
- (Optional) SELinux: If enabled, see Troubleshooting.
Required Setup Steps
These steps are mandatory for a working installation. Without properly completing these, the stack will not function correctly.
-
⚠️ Required: System User Information
- Set
USER_IDandGROUP_IDin.env(runid -uandid -gto get yours) - Incorrect values will cause permission errors with mounted volumes
- Set
-
⚠️ Required: Directory Paths
- Set
CONFIG_ROOT(where service configurations will be stored) - Set
DATA_ROOT(where your media libraries will be stored) - Set
DOWNLOAD_ROOT(must be on same filesystem as DATA_ROOT for hardlinks)
- Set
-
⚠️ Required: Tailscale Configuration
- Create a Tailscale account at tailscale.com
- Generate an Auth Key in the Tailscale Admin Console
- Set
TAILSCALE_AUTHKEYin.env - Set
TAILSCALE_TAILNET_DOMAINto your Tailnet domain (e.g.,your-name.ts.net)
-
⚠️ Required: Security Credentials
- Generate four strong random secrets using
openssl rand -hex 32:echo "AUTHELIA_JWT_SECRET=$(openssl rand -hex 32)" echo "AUTHELIA_SESSION_SECRET=$(openssl rand -hex 32)" echo "AUTHELIA_STORAGE_ENCRYPTION_KEY=$(openssl rand -hex 32)" echo "AUTHELIA_REDIS_PASSWORD=$(openssl rand -hex 32)" - Add these to your
.envfile as shown
- Generate four strong random secrets using
-
⚠️ Required: Create Admin Password
- Generate a password hash for Authelia:
docker run --rm authelia/authelia:latest authelia hash-password 'your_secure_password' - Replace the example hash in
authelia/users_database.ymlwith your generated hash
- Generate a password hash for Authelia:
-
⚠️ Optional: Authentication Configuration
- Choose which services require authentication by setting the corresponding variables in your
.envfile:AUTH_JELLYFIN=false # Example: Allow Jellyfin access without authentication AUTH_SONARR=true # Example: Require authentication for Sonarr - By default, all services require authentication if not specified otherwise
- Choose which services require authentication by setting the corresponding variables in your
Quick Start Guide
After completing all Required Setup Steps above, follow these steps to get up and running:
-
Clone Repository:
git clone https://github.com/AdrienPoupa/docker-compose-nas.git cd docker-compose-nas -
Configure Environment:
# Create an .env file from the example cp .env.example .env # Edit the .env file and fill in ALL required values nano .env -
Configure Authelia Admin:
# Generate a password hash docker run --rm authelia/authelia:latest authelia hash-password 'your_secure_password' # Edit the users_database.yml with the generated hash nano authelia/users_database.yml -
Run the Setup Script:
# Make the script executable chmod +x ./update-setup.sh # Run the setup tool ./update-setup.shThis interactive script will guide you through:
- Updating your
.envfile while preserving existing values - Configuring Authelia with your Tailscale domain settings
- Setting up service configurations and retrieving API keys
- Updating your
-
Start the Stack:
# Start containers docker compose up -d(Wait for containers to download and start)
-
Access Your NAS:
- Open
https://<TAILSCALE_HOSTNAME>.<TAILSCALE_TAILNET_DOMAIN>/ - Log in with username
adminand the password you set - After login, you'll land on the Homepage dashboard at
/home
- Open
⚠️ IMPORTANT: If the stack fails to start, check the Troubleshooting section and verify you've properly completed all Required Setup Steps.
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. |
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. 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) |
AUTH_SONARR |
Control whether Sonarr requires authentication (true/false). |
true |
AUTH_RADARR |
Control whether Radarr requires authentication (true/false). |
true |
AUTH_BAZARR |
Control whether Bazarr requires authentication (true/false). |
true |
AUTH_PROWLARR |
Control whether Prowlarr requires authentication (true/false). |
true |
AUTH_JELLYSEERR |
Control whether Jellyseerr requires authentication (true/false). |
true |
AUTH_QBITTORRENT |
Control whether qBittorrent requires authentication (true/false). |
true |
AUTH_LIDARR |
Control whether Lidarr requires authentication (true/false). |
true |
AUTH_JELLYFIN |
Control whether Jellyfin requires authentication (true/false). |
false |
AUTH_HOMEPAGE |
Control whether Homepage requires authentication (true/false). |
true |
AUTH_FLARESOLVERR |
Control whether FlareSolverr requires authentication (true/false). |
true |
AUTH_SABNZBD |
Control whether SABnzbd requires authentication (true/false). |
true |
AUTH_CALIBRE |
Control whether Calibre-Web requires authentication (true/false). |
true |
Note: Authentication variables were introduced to give you fine-grained control over which services require login. Set to
falsefor services you want to access without authentication.
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. | google |
HOMEPAGE_VAR_HEADER_STYLE |
Dashboard header style. Options. | 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. |
(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. | 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. |
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:
- As mentioned in the Quick Start, you must set a strong password for the default
adminuser. - Generate a hash using Docker (replace
'your_secure_password'):docker run --rm authelia/authelia:latest authelia hash-password 'your_secure_password' - Copy the entire output hash (starting with
$argon2id...). - Open
authelia/users_database.ymland replace the examplepassword:value underadmin:with your generated hash. - Ensure the
adminuser belongs to theadminsandusersgroups as shown in the example.
- As mentioned in the Quick Start, you must set a strong password for the default
-
Adding More Users:
- Generate a password hash for the new user as shown above.
- Edit
authelia/users_database.yml. - Add a new entry under
users:, following the format of theadminuser: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 - Save the file. Authelia should pick up the changes automatically (or restart the Authelia container:
docker compose restart authelia).
-
Enabling User Registration (Optional):
- Edit
authelia/configuration.yml. - Find the commented-out
registration:section near the bottom. - Uncomment it and set
enable: true:
becomes:# registration: # enable: false # Set to true to enable registration formregistration: enable: true - Save the file and restart Authelia (
docker compose restart authelia). - A "Register" link will now appear on the Authelia login page.
- Edit
-
Approving Registered Users:
- When a user registers (if enabled), their details are added to
authelia/users_database.ymlbut marked asdisabled: true. - To approve them, edit
authelia/users_database.yml. - Find the new user's entry.
- Change
disabled: truetodisabled: false(or simply remove thedisabled: trueline). - Save the file. The user should now be able to log in.
- When a user registers (if enabled), their details are added to
(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. Authentication is controlled by the AUTH_* environment variables.
- Login Portal:
https://<TAILSCALE_NODE>/(Redirects unauthenticated users here for secured services) - Homepage Dashboard:
https://<TAILSCALE_NODE>/home(Requires login ifAUTH_HOMEPAGE=true) - Sonarr:
https://<TAILSCALE_NODE>/sonarr(Requires login ifAUTH_SONARR=true) - Radarr:
https://<TAILSCALE_NODE>/radarr(Requires login ifAUTH_RADARR=true) - qBittorrent:
https://<TAILSCALE_NODE>/qbittorrent(Requires login ifAUTH_QBITTORRENT=true) - Jellyfin:
https://<TAILSCALE_NODE>/jellyfin(Requires login ifAUTH_JELLYFIN=true, default isfalse) - ...and so on.
Replace <TAILSCALE_NODE> 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://<APP_HOSTNAME>/<service_path>.
Configuring Authentication Per Service
You can control which services require authentication by setting the appropriate variables in your .env file:
# Example: Allow Jellyfin and qBittorrent without authentication, require it for others
AUTH_JELLYFIN=false
AUTH_QBITTORRENT=false
AUTH_SONARR=true
AUTH_RADARR=true
# ...and so on
If a variable is not explicitly set, authentication defaults to true for that service (except for Jellyfin, which defaults to false).
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
COMPOSE_PROFILES=lidarr,sabnzbd
Available Profiles:
lidarr: Music management.sabnzbd: Usenet download client.flaresolverr: Bypasses Cloudflare challenges for Prowlarr.adguardhome: Network-wide ad blocking (seeadguardhome/README.md).calibre-web: E-book library management.decluttarr: Automated download cleanup.tandoor: Recipe management (seetandoor/README.md).joplin: Note-taking server (seejoplin/README.md).homeassistant: Home automation (seehomeassistant/README.md).immich: Photo management (seeimmich/README.md).
Troubleshooting
Middleware Not Found Errors
If you see error messages like middleware "authelia-auth@docker" does not exist in the Traefik logs, it could be due to one of these issues:
-
Docker Network Issue: The Traefik configuration has been updated to fix network discovery issues when running in Tailscale's network namespace. If you're still seeing this error, try restarting the stack with:
docker compose down docker compose up -d -
Authentication Variable Missing: Ensure you have properly configured the
AUTH_*variables in your.envfile for the services you want to control. If not specified, most services default to requiring authentication. -
Docker Socket Permissions: Make sure Traefik can access the Docker socket. See the SELinux Socket Permissions section below for more details.
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:
-
Check Audit Logs: Immediately after seeing the error, check the SELinux audit log on the host:
sudo ausearch -m avc -ts recentLook for lines containing
denied,docker.sock, and the name of the failing service (e.g.,traefik,watchtower). -
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:# 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.ppThis allows the specific actions that were being denied. You might need to repeat this if different denials appear after applying the first policy.
Authelia v4.38+ Configuration
Authelia v4.38+ introduces significant changes to its configuration structure, particularly for session domains and authentication flows. The setup in this repository has been carefully configured to work with these changes:
-
Domain Configuration:
- You must use your specific Tailnet domain (e.g.,
example.ts.net) for cookies, not justts.net - The domain
ts.netis part of the Public Suffix List, which means browsers restrict cookies on it for security reasons - Authelia will refuse to start if you try to use a domain from this list
- You must use your specific Tailnet domain (e.g.,
-
Required Secret Variables: You must set these four variables in your
.envfile:AUTHELIA_JWT_SECRET: Used for password reset tokensAUTHELIA_SESSION_SECRET: Used for session cookie encryptionAUTHELIA_STORAGE_ENCRYPTION_KEY: Used for database encryptionAUTHELIA_REDIS_PASSWORD: Used for Redis authentication
Generate strong random values for these with:
openssl rand -hex 32 -
Automatic Domain Setup: The
update-setup.shscript automatically:- Uses your specific Tailnet domain (e.g.,
example.ts.net) from your.envfile - Configures cookie domains properly to avoid Public Suffix List issues
- Sets up proper access control rules for both your domain and its subdomains
- Uses your specific Tailnet domain (e.g.,
-
File Permissions: The Authelia container runs with your user ID and group ID, preventing permission issues when managing the configuration files with git or other tools.
If you encounter any of these common errors, running the setup script should resolve them:
error: option 'domain' is not a valid cookie domain: the domain is part of the special public suffix list
error: option 'authelia_url' does not share a cookie scope with domain
error: can't be specified at the same time: option 'domain' and option 'cookies'
configuration key 'jwt_secret' is deprecated in 4.38.0
After making changes to the configuration, restart Authelia with:
docker compose restart authelia
See the Authelia documentation for more details on the v4.38+ configuration structure.
Tailscale Issues
- Authentication: Ensure your
TAILSCALE_AUTHKEYin.envis valid and hasn't expired (especially if using ephemeral keys). Check thetailscalecontainer logs (docker compose logs tailscale) for authentication errors. - Connectivity: Verify the
tailscalecontainer 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 funnelortailscale serveis correct.
File Permissions
If services report permission errors when accessing /config or /data directories, double-check that:
- The
USER_IDandGROUP_IDin your.envfile match the owner/group of the correspondingCONFIG_ROOTandDATA_ROOTdirectories on your host. - The host directories have appropriate read/write permissions for that user/group.
- If using SELinux, the
:Zflag on the volume mounts indocker-compose.ymlis 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.