Compare commits

..

7 Commits

Author SHA1 Message Date
aki
09b20f71fc fix(authelia): Add user configuration for Authelia container
Some checks failed
/ validate-docker-compose (push) Has been cancelled
2025-04-26 00:08:32 +08:00
aki
afbffb97e3 fix(authelia): Update configuration for v4.38+ with required variables and improved domain handling
Some checks failed
/ validate-docker-compose (push) Has been cancelled
2025-04-26 00:04:49 +08:00
aki
1c5959cafb fix(env): Enhance environment variable tracking by adding current key presence check
Some checks failed
/ validate-docker-compose (push) Has been cancelled
2025-04-25 23:52:33 +08:00
aki
73e40af91a feat: Add environment update script to manage .env variables and preserve existing values
Some checks failed
/ validate-docker-compose (push) Has been cancelled
2025-04-25 23:48:09 +08:00
aki
461a0dc110 fix(authelia): Update configuration for v4.38+ compatibility and remove deprecated variables
Some checks failed
/ validate-docker-compose (push) Has been cancelled
2025-04-25 23:40:25 +08:00
aki
91873062c9 fix(env): Rename HOSTNAME to APP_HOSTNAME to avoid conflicts and update related configurations
Some checks failed
/ validate-docker-compose (push) Has been cancelled
2025-04-25 21:52:38 +08:00
aki
8a52e6894f feat!: Add Authelia for authentication and Redis for session storage
Some checks failed
/ validate-docker-compose (push) Has been cancelled
- Introduced Redis service for session management with health checks.
- Added Authelia service for user authentication with necessary environment variables.
- Configured Traefik to use Authelia as middleware for various services.
- Created Authelia configuration file with session, storage, and access control settings.
- Added user database for Authelia with an example admin user.
2025-04-25 17:33:09 +08:00
9 changed files with 501 additions and 1852 deletions

3
.gitignore vendored
View File

@@ -1,9 +1,6 @@
*.env *.env
*.bak
.idea .idea
docker-compose.override.yml docker-compose.override.yml
/authelia/*.yml
!/authelia/*.example.yml
/homepage/logs /homepage/logs
/homepage/*.yaml /homepage/*.yaml
/homepage/*.css /homepage/*.css

358
README.md
View File

@@ -11,7 +11,6 @@ The core idea is to manage media libraries (movies, TV shows, music), automate d
- [Architecture Overview](#architecture-overview) - [Architecture Overview](#architecture-overview)
- [Features](#features) - [Features](#features)
- [Prerequisites](#prerequisites) - [Prerequisites](#prerequisites)
- [Required Setup Steps](#required-setup-steps)
- [Quick Start Guide](#quick-start-guide) - [Quick Start Guide](#quick-start-guide)
- [Configuration (`.env` Variables)](#configuration-env-variables) - [Configuration (`.env` Variables)](#configuration-env-variables)
- [Core System \& Paths](#core-system--paths) - [Core System \& Paths](#core-system--paths)
@@ -25,11 +24,8 @@ The core idea is to manage media libraries (movies, TV shows, music), automate d
- [(Optional) VPN Configuration](#optional-vpn-configuration) - [(Optional) VPN Configuration](#optional-vpn-configuration)
- [(Optional) Traefik DNS Challenge](#optional-traefik-dns-challenge) - [(Optional) Traefik DNS Challenge](#optional-traefik-dns-challenge)
- [Service Access](#service-access) - [Service Access](#service-access)
- [Setup Script Commands (`update-setup.sh`)](#setup-script-commands-update-setupsh)
- [Managing Service Authentication](#managing-service-authentication-authelia-policies)
- [Optional Services](#optional-services) - [Optional Services](#optional-services)
- [Troubleshooting](#troubleshooting) - [Troubleshooting](#troubleshooting)
- [Middleware Not Found Errors](#middleware-not-found-errors)
- [SELinux Socket Permissions (Docker)](#selinux-socket-permissions-docker) - [SELinux Socket Permissions (Docker)](#selinux-socket-permissions-docker)
- [Authelia v4.38+ Configuration](#authelia-v438-configuration) - [Authelia v4.38+ Configuration](#authelia-v438-configuration)
- [Tailscale Issues](#tailscale-issues) - [Tailscale Issues](#tailscale-issues)
@@ -40,175 +36,104 @@ The core idea is to manage media libraries (movies, TV shows, music), automate d
This stack uses a combination of key services for routing, access, and security: 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. * **[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`). * **[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. You can configure which services require authentication via environment variables. * **[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 ## Features
This stack includes the following services, categorized for clarity: This stack includes the following services, categorized for clarity:
**Core Infrastructure:** **Core Infrastructure:**
* **Reverse Proxy:** [Traefik](https://traefik.io) - Manages internal routing and service discovery.
- **Reverse Proxy:** [Traefik](https://traefik.io) - Manages internal routing and service discovery. * **Secure Remote Access:** [Tailscale](https://tailscale.com) - Provides VPN access and HTTPS.
- **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.
- **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`).
- **Dashboard:** [Homepage](https://gethomepage.dev) - Centralized access point (at `/home`).
**Media Management (\*Arr Suite):** **Media Management (\*Arr Suite):**
* [Sonarr](https://sonarr.tv): TV show management.
- [Sonarr](https://sonarr.tv): TV show management. * [Radarr](https://radarr.video): Movie management.
- [Radarr](https://radarr.video): Movie management. * [Lidarr](https://lidarr.audio) (Optional): Music management.
- [Lidarr](https://lidarr.audio) (Optional): Music management. * [Bazarr](https://www.bazarr.media/): Subtitle management.
- [Bazarr](https://www.bazarr.media/): Subtitle management. * [Prowlarr](https://github.com/Prowlarr/Prowlarr): Indexer management for *arr apps.
- [Prowlarr](https://github.com/Prowlarr/Prowlarr): Indexer management for *arr apps.
**Download Clients:** **Download Clients:**
* [qBittorrent](https://www.qbittorrent.org): Bittorrent client.
- [qBittorrent](https://www.qbittorrent.org): Bittorrent client. * [SABnzbd](https://sabnzbd.org/) (Optional): Usenet download client.
- [SABnzbd](https://sabnzbd.org/) (Optional): Usenet download client.
**Media Serving & Requests:** **Media Serving & Requests:**
* [Jellyfin](https://jellyfin.org): Media server for streaming.
- [Jellyfin](https://jellyfin.org): Media server for streaming. * [Jellyseerr](https://github.com/FallenBagel/jellyseerr): Media request management.
- [Jellyseerr](https://github.com/FallenBagel/jellyseerr): Media request management.
**Utilities:** **Utilities:**
* [Watchtower](https://containrrr.dev/watchtower/): Automatic container updates.
- [Watchtower](https://containrrr.dev/watchtower/): Automatic container updates. * [Autoheal](https://github.com/willfarrell/docker-autoheal/): Automatic container restarts on failure.
- [Autoheal](https://github.com/willfarrell/docker-autoheal/): Automatic container restarts on failure. * [Unpackerr](https://unpackerr.zip): Automated archive extraction.
- [Unpackerr](https://unpackerr.zip): Automated archive extraction.
**Optional Services (Enabled via Profiles):** **Optional Services (Enabled via Profiles):**
* [AdGuard Home](https://adguard.com/en/adguard-home/overview.html): Network-wide ad blocking.
- [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.
- [Calibre-Web](https://github.com/janeczku/calibre-web): E-book library management. * [Decluttarr](https://github.com/manimatter/decluttarr): Automated download cleanup.
- [Decluttarr](https://github.com/manimatter/decluttarr): Automated download cleanup. * [Tandoor Recipes](https://docs.tandoor.dev/): Recipe management.
- [Tandoor Recipes](https://docs.tandoor.dev/): Recipe management. * [Joplin Server](https://joplinapp.org/): Note-taking synchronization server.
- [Joplin Server](https://joplinapp.org/): Note-taking synchronization server. * [Home Assistant](https://www.home-assistant.io/): Home automation platform.
- [Home Assistant](https://www.home-assistant.io/): Home automation platform. * [Immich](https://immich.app/): Self-hosted photo and video backup.
- [Immich](https://immich.app/): Self-hosted photo and video backup. * [FlareSolverr](https://github.com/FlareSolverr/FlareSolverr): Bypasses Cloudflare challenges (e.g., for Prowlarr).
- [FlareSolverr](https://github.com/FlareSolverr/FlareSolverr): Bypasses Cloudflare challenges (e.g., for Prowlarr).
## Prerequisites ## Prerequisites
- **Linux Host:** A system capable of running Docker (e.g., Ubuntu, Debian, Fedora). * **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/). * **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`). * **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. * **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). * **Tailscale Account:** Required for remote access. [Sign up here](https://tailscale.com/login).
- **(Optional) SELinux:** If enabled, see [Troubleshooting](#selinux-socket-permissions-docker). * **(Optional) SELinux:** If enabled, see [Troubleshooting](#selinux-socket-permissions).
## Required Setup Steps
These steps are **mandatory** for a working installation. Without properly completing these, the stack will not function correctly.
1. **⚠️ Required: System User Information**
- Set `USER_ID` and `GROUP_ID` in `.env` (run `id -u` and `id -g` to get yours)
- Incorrect values will cause permission errors with mounted volumes
2. **⚠️ 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)
3. **⚠️ Required: Tailscale Configuration**
- Create a Tailscale account at [tailscale.com](https://tailscale.com)
- Generate an Auth Key in the [Tailscale Admin Console](https://login.tailscale.com/admin/settings/keys)
- Set `TAILSCALE_AUTHKEY` in `.env`
- Set `TAILSCALE_TAILNET_DOMAIN` to your Tailnet domain (e.g., `your-name.ts.net`)
4. **⚠️ Required: Security Credentials**
- Generate four strong random secrets using `openssl rand -hex 32`:
```bash
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 `.env` file as shown
5. **⚠️ Required: Create Authelia Account**
- Create an Authelia account (only for yourself and those you trust!)
```bash
docker run --rm authelia/authelia:latest authelia hash-password 'your_secure_password'
```
6. ** Optional: Set up**
- Generate a password hash for Authelia:
```bash
docker run --rm authelia/authelia:latest authelia hash-password 'your_secure_password'
```
- Replace the example hash in `authelia/users_database.yml` with your generated hash
## Quick Start Guide ## Quick Start Guide
After completing all [Required Setup Steps](#required-setup-steps) above, follow these steps to get up and running: This guide gets the core stack running quickly. **Read the full [Configuration](#configuration-env-variables) section afterwards for details.**
1. **Clone Repository:** 1. **Clone Repository:**
```bash ```bash
git clone https://github.com/AdrienPoupa/docker-compose-nas.git git clone https://github.com/AdrienPoupa/docker-compose-nas.git
cd docker-compose-nas cd docker-compose-nas
``` ```
2. **Configure Environment:** 2. **Create `.env` File:**
```bash ```bash
# Create an .env file from the example
cp .env.example .env cp .env.example .env
# Edit the .env file and fill in ALL required values
nano .env
``` ```
3. **Configure Authelia Admin:** 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 ```bash
# Generate a password hash
docker run --rm authelia/authelia:latest authelia hash-password 'your_secure_password' 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
``` ```
* Copy the **entire output hash** (starting with `$argon2id...`).
4. **Run the Setup Script:** * Open `authelia/users_database.yml` and replace the example `password:` value under `admin:` with your generated hash.
```bash
# Make the script executable
chmod +x ./update-setup.sh
# Run the setup tool (use 'all' for initial setup)
./update-setup.sh all
```
This script will:
- Update your `.env` file while preserving existing values (`update-env`).
- Configure Authelia with your Tailscale domain settings (`update-authelia`).
- Set up service configurations and retrieve API keys (`update-services`).
You can also run individual commands like `./update-setup.sh update-authelia`. Run `./update-setup.sh help` for all options.
5. **Start the Stack:** 5. **Start the Stack:**
```bash ```bash
# Make helper script executable (if needed)
chmod +x ./update-config.sh
# Start containers # Start containers
docker compose up -d docker compose up -d
``` ```
*(Wait for containers to download and start)* *(Wait for containers to download and start)*
6. **Access Your NAS:** 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.
- Open `https://<TAILSCALE_HOSTNAME>.<TAILSCALE_TAILNET_DOMAIN>/` ```bash
- Log in with username `admin` and the password you set ./update-config.sh
- After login, you'll land on the Homepage dashboard at `/home` ```
> ⚠️ **IMPORTANT:** If the stack fails to start, check the [Troubleshooting](#troubleshooting) section and verify you've properly completed all [Required Setup Steps](#required-setup-steps). 7. **Access:** Navigate to `https://<TAILSCALE_HOSTNAME>.<TAILSCALE_TAILNET_DOMAIN>/`. 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) ## Configuration (`.env` Variables)
@@ -245,6 +170,8 @@ This file controls essential settings. Copy `.env.example` to `.env` and modify
| **`AUTHELIA_SESSION_SECRET`** | **Required.** Random secret for session cookies. **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_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_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 #### Service Credentials
@@ -307,23 +234,20 @@ This file controls essential settings. Copy `.env.example` to `.env` and modify
Authelia uses the `authelia/users_database.yml` file to manage users. Authelia uses the `authelia/users_database.yml` file to manage users.
- **Setting the Initial Admin Password:** * **Setting the Initial Admin Password:**
1. As mentioned in the Quick Start, you **must** set a strong password for the default `admin` user. 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'`): 2. Generate a hash using Docker (replace `'your_secure_password'`):
```bash ```bash
docker run --rm authelia/authelia:latest authelia hash-password 'your_secure_password' docker run --rm authelia/authelia:latest authelia hash-password 'your_secure_password'
``` ```
3. Copy the **entire output hash** (starting with `$argon2id...`). 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. 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. 5. Ensure the `admin` user belongs to the `admins` and `users` groups as shown in the example.
- **Adding More Users:** * **Adding More Users:**
1. Generate a password hash for the new user as shown above. 1. Generate a password hash for the new user as shown above.
2. Edit `authelia/users_database.yml`. 2. Edit `authelia/users_database.yml`.
3. Add a new entry under `users:`, following the format of the `admin` user: 3. Add a new entry under `users:`, following the format of the `admin` user:
```yaml ```yaml
users: users:
admin: admin:
@@ -335,26 +259,25 @@ Authelia uses the `authelia/users_database.yml` file to manage users.
groups: groups:
- users # Add to 'admins' group if needed - 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`).
4. Save the file and restart Authelia: `docker compose restart authelia`. * **Enabling User Registration (Optional):**
- **Adding/Updating Users (Recommended Method):**
Use the setup script's interactive tool:
```bash
./update-setup.sh manage-accounts
```
This script handles password hashing and file formatting, reducing the chance of errors. It will prompt you for the username, display name, email, and groups, then generate a secure password hash.
- **Enabling User Registration (Optional):**
1. Edit `authelia/configuration.yml`. 1. Edit `authelia/configuration.yml`.
2. Find the commented-out `registration:` section near the bottom. 2. Find the commented-out `registration:` section near the bottom.
3. Uncomment it and set `enable: true`. 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`). 4. Save the file and restart Authelia (`docker compose restart authelia`).
5. A "Register" link will now appear on the Authelia login page. 5. A "Register" link will now appear on the Authelia login page.
- **Approving Registered Users:** * **Approving Registered Users:**
1. When a user registers (if enabled), their details are added to `authelia/users_database.yml` but marked as `disabled: true`. 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`. 2. To approve them, edit `authelia/users_database.yml`.
3. Find the new user's entry. 3. Find the new user's entry.
@@ -371,103 +294,54 @@ Authelia uses the `authelia/users_database.yml` file to manage users.
## Service Access ## 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 included `update-setup.sh` script. 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://<TAILSCALE_NODE>/` (Redirects unauthenticated users here for secured services) * **Login Portal:** `https://<TAILSCALE_NODE>/` (Redirects unauthenticated users here)
- **Homepage Dashboard:** `https://<TAILSCALE_NODE>/home` (Requires login by default) * **Homepage Dashboard:** `https://<TAILSCALE_NODE>/home` (Accessible after login)
- **Sonarr:** `https://<TAILSCALE_NODE>/sonarr` (Requires login by default) * **Sonarr:** `https://<TAILSCALE_NODE>/sonarr` (Requires login)
- **Radarr:** `https://<TAILSCALE_NODE>/radarr` (Requires login by default) * **Radarr:** `https://<TAILSCALE_NODE>/radarr` (Requires login)
- **qBittorrent:** `https://<TAILSCALE_NODE>/qbittorrent` (Requires login by default) * **qBittorrent:** `https://<TAILSCALE_NODE>/qbittorrent`
- **Jellyfin:** `https://<TAILSCALE_NODE>/jellyfin` (Requires login by default) * **Jellyfin:** `https://<TAILSCALE_NODE>/jellyfin`
- ...and so on. * ...and so on.
Replace `<TAILSCALE_NODE>` with your Tailscale device name (e.g., `tailscale-nas.your-tailnet.ts.net`) or its Tailscale IP address. 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>`. If you configure DNS for your `APP_HOSTNAME` variable to point to the Tailscale IP, you can use `https://<APP_HOSTNAME>/<service_path>`.
### Setup Script Commands (`update-setup.sh`)
The `update-setup.sh` script provides various commands to manage your configuration. Run `./update-setup.sh help` to see all options.
**Core Setup & Updates:**
- `./update-setup.sh update-env`: Updates `.env` from `.env.example`, preserving existing values and highlighting new/deprecated keys.
- `./update-setup.sh update-authelia`: Updates `authelia/configuration.yml` from the example, applying domain settings from `.env` and attempting to preserve secrets (uses `yq` if available).
- `./update-setup.sh update-services`: Updates configurations for running *arr/qBittorrent/Bazarr containers (sets URL base, extracts API keys to `.env`). Restarts affected containers.
- `./update-setup.sh all`: Runs `update-env`, `update-authelia`, and `update-services` sequentially. Recommended for initial setup and major updates.
**Authelia Policy Management:**
- `./update-setup.sh manage-policies`: Starts an interactive menu to list or set Authelia access policies (`one_factor`, `two_factor`, `bypass`, `deny`) for specific services defined in `authelia/configuration.yml`.
- `./update-setup.sh list-policies`: Lists services defined in `authelia/configuration.yml` and their current access policy.
- `./update-setup.sh set-policy <service> <policy>`: Directly sets the Authelia access policy for the specified `<service>` to the given `<policy>` (e.g., `one_factor`, `two_factor`, `bypass`, `deny`).
> **Important:** After changing Authelia policies using `manage-policies` or `set-policy`, you **must** restart Authelia for the changes to take effect:
>
> ```bash
> docker compose restart authelia
> ```
**User & File Management:**
- `./update-setup.sh manage-accounts`: Starts an interactive tool to add or update users in `authelia/users_database.yml`. It generates password hashes and prompts for user details.
- `./update-setup.sh cleanup`: Interactively finds and deletes old backup files (`.bak`) created by the script. Allows keeping the most recent backup of each type.
**Help:**
- `./update-setup.sh help`: Displays the full list of commands and usage instructions.
### Managing Service Authentication (Authelia Policies)
Use the `update-setup.sh` script to easily control which services require Authelia login and what level of authentication is needed. This is done by managing *access control rules* within Authelia's configuration (`authelia/configuration.yml`).
See the `Authelia Policy Management` commands in the [Setup Script Commands](#setup-script-commands-update-setupsh) section above for details on how to list and set policies like `one_factor`, `two_factor`, `bypass`, or `deny` for each service.
## Optional Services ## 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). 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 Example: Enable Lidarr and SABnzbd
```dotenv ```dotenv
COMPOSE_PROFILES=lidarr,sabnzbd COMPOSE_PROFILES=lidarr,sabnzbd
``` ```
Available Profiles: Available Profiles:
* `lidarr`: Music management.
- `lidarr`: Music management. * `sabnzbd`: Usenet download client.
- `sabnzbd`: Usenet download client. * `flaresolverr`: Bypasses Cloudflare challenges for Prowlarr.
- `flaresolverr`: Bypasses Cloudflare challenges for Prowlarr. * `adguardhome`: Network-wide ad blocking (see `adguardhome/README.md`).
- `adguardhome`: Network-wide ad blocking (see `adguardhome/README.md`). * `calibre-web`: E-book library management.
- `calibre-web`: E-book library management. * `decluttarr`: Automated download cleanup.
- `decluttarr`: Automated download cleanup. * `tandoor`: Recipe management (see `tandoor/README.md`).
- `tandoor`: Recipe management (see `tandoor/README.md`). * `joplin`: Note-taking server (see `joplin/README.md`).
- `joplin`: Note-taking server (see `joplin/README.md`). * `homeassistant`: Home automation (see `homeassistant/README.md`).
- `homeassistant`: Home automation (see `homeassistant/README.md`). * `immich`: Photo management (see `immich/README.md`).
- `immich`: Photo management (see `immich/README.md`).
## Troubleshooting ## Troubleshooting
### Middleware Not Found Errors
If you see error messages like `middleware "authelia-auth@docker" does not exist` in the Traefik logs, please check Authelia logs for any fatal errors. It is likely due to a misconfigured `configuration.yml` in `authelia/configuration.yml`
Make sure Traefik can access the Docker socket. See the [SELinux Socket Permissions](#selinux-socket-permissions-docker) section below for more details.
### SELinux Socket Permissions (Docker) ### 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`: 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: 1. **Check Audit Logs:** Immediately after seeing the error, check the SELinux audit log on the host:
```bash ```bash
sudo ausearch -m avc -ts recent sudo ausearch -m avc -ts recent
``` ```
Look for lines containing `denied`, `docker.sock`, and the name of the failing service (e.g., `traefik`, `watchtower`). 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: 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 ```bash
# Generate policy files (my-dockersock.te and my-dockersock.pp) # Generate policy files (my-dockersock.te and my-dockersock.pp)
sudo ausearch -m avc -ts recent | audit2allow -M my-dockersock sudo ausearch -m avc -ts recent | audit2allow -M my-dockersock
@@ -475,61 +349,43 @@ If you are running Docker on a host with SELinux enabled (like Fedora, CentOS, R
# Install the policy module # Install the policy module
sudo semodule -i my-dockersock.pp 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. This 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+ 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: Authelia v4.38+ introduces significant changes to its configuration structure, particularly for session domains and authentication flows. This Docker Compose template has been updated to support these changes, making it easier to set up:
1. **Domain Configuration**: 1. **Centralized Configuration**: All required Authelia settings are defined in the `.env` file - you don't need to manually edit the Authelia YAML configuration.
- You must use your specific Tailnet domain (e.g., `example.ts.net`) for cookies, not just `ts.net`
- The domain `ts.net` is part of the [Public Suffix List](https://publicsuffix.org/), 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
2. **Required Secret Variables**: You must set these four variables in your `.env` file: 2. **Domain Handling**: The system now uses wildcard domain matching (`*.ts.net`) to handle Tailscale domains, eliminating environment variable expansion issues that could cause errors like:
- `AUTHELIA_JWT_SECRET`: Used for password reset tokens ```
- `AUTHELIA_SESSION_SECRET`: Used for session cookie encryption error decoding 'session.cookies[0].authelia_url': could not decode 'https://${APP_HOSTNAME}'
- `AUTHELIA_STORAGE_ENCRYPTION_KEY`: Used for database encryption
- `AUTHELIA_REDIS_PASSWORD`: Used for Redis authentication
Generate strong random values for these with: `openssl rand -hex 32`
3. **Automatic Domain Setup**: The `update-setup.sh` script automatically:
- Uses your specific Tailnet domain (e.g., `example.ts.net`) from your `.env` file
- Configures cookie domains properly to avoid Public Suffix List issues
- Sets up proper access control rules for both your domain and its subdomains
If you encounter any of these common errors:
```log
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
``` ```
Running the setup script should resolve them. After making changes to the configuration, restart Authelia with: 3. **Required Variables**: You only need to set these variables in your `.env` file:
- `AUTHELIA_JWT_SECRET`: For password reset tokens
- `AUTHELIA_SESSION_SECRET`: For session cookie encryption
- `AUTHELIA_STORAGE_ENCRYPTION_KEY`: For database encryption
- `AUTHELIA_REDIS_PASSWORD`: For Redis authentication
```bash Generate these with: `openssl rand -hex 32`
docker compose restart authelia
```
See the [Authelia documentation](https://www.authelia.com/configuration/session/introduction/) for more details on the v4.38+ configuration structure. 4. **Domain Configuration**: Your Tailscale domain is automatically set using `APP_HOSTNAME=${TAILSCALE_HOSTNAME}.${TAILSCALE_TAILNET_DOMAIN}` in the `.env` file.
If you still encounter issues, make sure your `TAILSCALE_TAILNET_DOMAIN` ends with `.ts.net` for proper domain matching, or edit `authelia/configuration.yml` to match your specific domain pattern.
### Tailscale Issues ### 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. * **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`). * **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. * **Funnel/Serve Command:** If you modified the Tailscale command, ensure the syntax for `tailscale funnel` or `tailscale serve` is correct.
### File Permissions ### File Permissions
If services report permission errors when accessing `/config` or `/data` directories, double-check that: 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 `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.
- 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.
- 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 ## Advanced Topics

View File

@@ -1,140 +0,0 @@
# Authelia Configuration File v4.38+
# Documentation: https://www.authelia.com/configuration/
# Server settings
server:
address: 'tcp://0.0.0.0:9091'
# Logging configuration
log:
level: info
format: text
# Session configuration for v4.38+
session:
name: authelia_session
secret: ${AUTHELIA_SESSION_SECRET}
expiration: 1h
inactivity: 5m
redis:
host: redis
port: 6379
password: ${AUTHELIA_SESSION_REDIS_PASSWORD}
database_index: 0
cookies:
# Using your specific Tailscale domain (e.g. example.ts.net) not just ts.net
- domain: 'your-tailnet.ts.net'
authelia_url: 'https://tailscale-nas.your-tailnet.ts.net'
default_redirection_url: 'https://tailscale-nas.your-tailnet.ts.net/home'
same_site: lax
# Regulation (brute force protection)
regulation:
max_retries: 3
find_time: 2m
ban_time: 5m
# Storage (for user preferences, etc. - encrypted using storage key)
storage:
encryption_key: ${AUTHELIA_STORAGE_ENCRYPTION_KEY}
local:
path: /config/db.sqlite3
# Authentication backend (using file-based user database)
authentication_backend:
file:
path: /config/users_database.yml
password:
algorithm: argon2id
iterations: 1
memory: 1024
parallelism: 8
salt_length: 16
key_length: 32
# Access control rules
access_control:
default_policy: deny # Deny access by default
rules:
# Rules are processed in order. First match wins.
# It's recommended to put more specific rules first.
# 1. Bypass rules (No authentication required)
# Allow access to Authelia's own endpoints
- domain: '*.your-tailnet.ts.net'
path_regex: '^/auth.*' # Match /auth and anything after it
policy: bypass
# Allow access to the root path (will be redirected by Traefik later)
- domain: '*.your-tailnet.ts.net'
path: '/'
policy: bypass
# Allow access to API endpoints (as requested, review security implications)
- domain: '*.your-tailnet.ts.net'
path_regex: '^/api.*' # Match /api and anything after it
policy: bypass
# 2. One-Factor Authentication Rules (Requires login)
# Add rules for each service you want to protect.
# The domain should match your Tailscale domain.
# The path should match the Traefik PathPrefix for the service.
- domain: '*.your-tailnet.ts.net'
path_regex: '^/sonarr.*'
policy: one_factor
- domain: '*.your-tailnet.ts.net'
path_regex: '^/radarr.*'
policy: one_factor
- domain: '*.your-tailnet.ts.net'
path_regex: '^/lidarr.*'
policy: one_factor
- domain: '*.your-tailnet.ts.net'
path_regex: '^/bazarr.*'
policy: one_factor
- domain: '*.your-tailnet.ts.net'
path_regex: '^/qbittorrent.*'
policy: one_factor
- domain: '*.your-tailnet.ts.net'
path_regex: '^/sabnzbd.*'
policy: one_factor
- domain: '*.your-tailnet.ts.net'
path_regex: '^/calibre.*'
policy: one_factor
- domain: '*.your-tailnet.ts.net'
path_regex: '^/home.*' # Protect the homepage
policy: one_factor
- domain: '*.your-tailnet.ts.net'
path_regex: '^/jellyseerr.*'
policy: one_factor
- domain: '*.your-tailnet.ts.net'
path_regex: '^/prowlarr.*'
policy: one_factor
- domain: '*.your-tailnet.ts.net'
path_regex: '^/flaresolverr.*'
policy: one_factor
# Add other services here following the pattern:
# - domain: '*.your-tailnet.ts.net'
# path_regex: '^/<service_path>.*'
# policy: one_factor
# 3. Default rule for the domain (optional, if you want a catch-all)
# This rule will apply if no path-specific rule above matches.
# You might want to deny or require one_factor for unmatched paths.
# Example: Deny any other path on the domain
# - domain: '*.your-tailnet.ts.net'
# policy: deny
# Example: Require login for any other path
# - domain: '*.your-tailnet.ts.net'
# policy: one_factor
# Notifier configuration
notifier:
filesystem:
filename: /config/notification.txt
# Identity Validation (includes JWT secret for password reset)
identity_validation:
reset_password:
jwt_secret: ${AUTHELIA_JWT_SECRET}
# Identity Providers
identity_providers:
oidc: null

View File

@@ -0,0 +1,73 @@
# Authelia Configuration File v4.38+
# Documentation: https://www.authelia.com/configuration/
# Server settings using the new 'address' format
server:
address: 'tcp://0.0.0.0:9091' # Listen on all interfaces, port 9091
# Logging configuration
log:
level: info
format: text # or json
# Session configuration for v4.38+
session:
name: authelia_session
secret: ${AUTHELIA_SESSION_SECRET}
expiration: 1h
inactivity: 5m
redis:
host: redis
port: 6379
password: ${AUTHELIA_SESSION_REDIS_PASSWORD}
database_index: 0
cookies:
# Use the wildcard domain pattern to match all subdomains
- domain: ${AUTHELIA_SESSION_DOMAIN}
authelia_url: ${AUTHELIA_DEFAULT_REDIRECTION_URL}
default_redirection_url: ${AUTHELIA_DEFAULT_REDIRECTION_URL}
# Regulation (brute force protection)
regulation:
max_retries: 3
find_time: 2m
ban_time: 5m
# Storage (for user preferences, etc. - encrypted using storage key)
storage:
encryption_key: ${AUTHELIA_STORAGE_ENCRYPTION_KEY}
local:
path: /config/db.sqlite3
# Authentication backend (using file-based user database)
authentication_backend:
file:
path: /config/users_database.yml
password:
algorithm: argon2id # Recommended hashing algorithm
iterations: 1
memory: 1024 # MiB
parallelism: 8
salt_length: 16
key_length: 32
# Access control rules
access_control:
default_policy: deny # Deny access by default
rules:
- domain: ${AUTHELIA_SESSION_DOMAIN}
policy: one_factor # Requires username/password
# Notifier configuration
notifier:
filesystem:
filename: /config/notification.txt
# Identity Validation (includes JWT secret for password reset)
identity_validation:
reset_password:
jwt_secret: ${AUTHELIA_JWT_SECRET}
# Identity Providers
identity_providers:
oidc: null # Explicitly disable OIDC

View File

@@ -13,11 +13,14 @@ services:
- --experimental.plugins.rewrite-body.version=v1.2.0 - --experimental.plugins.rewrite-body.version=v1.2.0
- --experimental.plugins.rewriteHeaders.modulename=github.com/XciD/traefik-plugin-rewrite-headers - --experimental.plugins.rewriteHeaders.modulename=github.com/XciD/traefik-plugin-rewrite-headers
- --experimental.plugins.rewriteHeaders.version=v0.0.3 - --experimental.plugins.rewriteHeaders.version=v0.0.3
- --providers.docker.network=docker-compose-nas network_mode: service:tailscale # Add this line
- --providers.docker.endpoint=unix:///var/run/docker.sock # ports: # Remove this section
network_mode: service:tailscale # - "80:80"
# - "443:443"
volumes: volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro - /var/run/docker.sock:/var/run/docker.sock:ro
# extra_hosts: # Remove this section
# - host.docker.internal:172.17.0.1
healthcheck: healthcheck:
test: ["CMD", "traefik", "healthcheck", "--ping"] test: ["CMD", "traefik", "healthcheck", "--ping"]
interval: 30s interval: 30s
@@ -28,11 +31,11 @@ services:
restart: always restart: always
environment: environment:
- REDIS_PASSWORD=${AUTHELIA_REDIS_PASSWORD} - REDIS_PASSWORD=${AUTHELIA_REDIS_PASSWORD}
command: ["redis-server", "--requirepass", "${AUTHELIA_REDIS_PASSWORD}"] command: ["redis-server", "--requirepass", "$$REDIS_PASSWORD"] # Use $$ to escape $ for compose
volumes: volumes:
- ${CONFIG_ROOT:-.}/redis:/data:Z - ${CONFIG_ROOT:-.}/redis:/data:Z
healthcheck: healthcheck:
test: ["CMD", "redis-cli", "-a", "${AUTHELIA_REDIS_PASSWORD}", "ping"] test: ["CMD", "redis-cli", "-a", "$$REDIS_PASSWORD", "ping"] # Use $$ to escape $ for compose
interval: 5s interval: 5s
timeout: 3s timeout: 3s
retries: 5 retries: 5
@@ -44,25 +47,33 @@ services:
volumes: volumes:
- ${CONFIG_ROOT:-.}/authelia:/config:Z - ${CONFIG_ROOT:-.}/authelia:/config:Z
environment: environment:
# Core secrets and keys
- AUTHELIA_JWT_SECRET=${AUTHELIA_JWT_SECRET}
- AUTHELIA_SESSION_SECRET=${AUTHELIA_SESSION_SECRET} - AUTHELIA_SESSION_SECRET=${AUTHELIA_SESSION_SECRET}
- AUTHELIA_STORAGE_ENCRYPTION_KEY=${AUTHELIA_STORAGE_ENCRYPTION_KEY} - AUTHELIA_STORAGE_ENCRYPTION_KEY=${AUTHELIA_STORAGE_ENCRYPTION_KEY}
- AUTHELIA_SESSION_REDIS_PASSWORD=${AUTHELIA_REDIS_PASSWORD} - AUTHELIA_SESSION_REDIS_PASSWORD=${AUTHELIA_REDIS_PASSWORD}
- AUTHELIA_IDENTITY_VALIDATION_RESET_PASSWORD_JWT_SECRET=${AUTHELIA_JWT_SECRET} - AUTHELIA_IDENTITY_VALIDATION_RESET_PASSWORD_JWT_SECRET=${AUTHELIA_JWT_SECRET}
# Domain settings extracted from APP_HOSTNAME
- AUTHELIA_SESSION_DOMAIN=${APP_HOSTNAME}
- AUTHELIA_SERVER_DOMAIN=${APP_HOSTNAME}
- AUTHELIA_DEFAULT_REDIRECTION_URL=https://${APP_HOSTNAME}/home
- TZ=${TIMEZONE} - TZ=${TIMEZONE}
labels: labels:
- traefik.enable=true - traefik.enable=true
- traefik.http.routers.authelia.rule=Host(`${APP_HOSTNAME}`) && PathPrefix(`/auth`) # Changed rule # Rule for Authelia portal itself (handles internal paths like /api, /logout etc.)
- traefik.http.routers.authelia.rule=PathPrefix(`/`)
- traefik.http.routers.authelia.entrypoints=web - traefik.http.routers.authelia.entrypoints=web
# - traefik.http.routers.authelia.priority=100 # Removed priority - traefik.http.routers.authelia.priority=100 # High priority to catch root path
- traefik.http.services.authelia.loadbalancer.server.port=9091 - traefik.http.services.authelia.loadbalancer.server.port=9091
- traefik.http.middlewares.authelia-auth.forwardAuth.address=http://authelia:9091/api/verify # Simplified forwardAuth address # Define the forwardAuth middleware
- traefik.http.routers.authelia.middlewares=https-proto@docker - traefik.http.middlewares.authelia-auth.forwardAuth.address=http://authelia:9091/api/verify?rd=https://${APP_HOSTNAME}/
- traefik.http.middlewares.authelia-auth.forwardAuth.trustForwardHeader=true - traefik.http.middlewares.authelia-auth.forwardAuth.trustForwardHeader=true
- traefik.http.middlewares.authelia-auth.forwardAuth.authResponseHeaders=Remote-User,Remote-Groups,Remote-Name,Remote-Email - traefik.http.middlewares.authelia-auth.forwardAuth.authResponseHeaders=Remote-User,Remote-Groups,Remote-Name,Remote-Email
# Homepage labels for Authelia itself
- homepage.group=Security - homepage.group=Security
- homepage.name=Authelia - homepage.name=Authelia
- homepage.icon=authelia.png - homepage.icon=authelia.png
- homepage.href=/auth # Updated href - homepage.href=https://${APP_HOSTNAME}/
- homepage.description=Authentication Portal - homepage.description=Authentication Portal
sonarr: sonarr:
image: lscr.io/linuxserver/sonarr image: lscr.io/linuxserver/sonarr
@@ -81,9 +92,9 @@ services:
retries: 10 retries: 10
labels: labels:
- traefik.enable=true - traefik.enable=true
- traefik.http.routers.sonarr.rule=Host(`${APP_HOSTNAME}`) && PathPrefix(`/sonarr`) # Added Host check - traefik.http.routers.sonarr.rule=PathPrefix(`/sonarr`)
- traefik.http.routers.sonarr.entrypoints=web - traefik.http.routers.sonarr.entrypoints=web
- traefik.http.routers.sonarr.middlewares=https-proto@docker,authelia-auth@docker - traefik.http.routers.sonarr.middlewares=authelia-auth@docker
- traefik.http.services.sonarr.loadbalancer.server.port=8989 - traefik.http.services.sonarr.loadbalancer.server.port=8989
- homepage.group=Media - homepage.group=Media
- homepage.name=Sonarr - homepage.name=Sonarr
@@ -111,9 +122,9 @@ services:
retries: 10 retries: 10
labels: labels:
- traefik.enable=true - traefik.enable=true
- traefik.http.routers.radarr.rule=Host(`${APP_HOSTNAME}`) && PathPrefix(`/radarr`) # Added Host check - traefik.http.routers.radarr.rule=PathPrefix(`/radarr`)
- traefik.http.routers.radarr.entrypoints=web - traefik.http.routers.radarr.entrypoints=web
- traefik.http.routers.radarr.middlewares=https-proto@docker,authelia-auth@docker - traefik.http.routers.radarr.middlewares=authelia-auth@docker
- traefik.http.services.radarr.loadbalancer.server.port=7878 - traefik.http.services.radarr.loadbalancer.server.port=7878
- homepage.group=Media - homepage.group=Media
- homepage.name=Radarr - homepage.name=Radarr
@@ -141,9 +152,9 @@ services:
retries: 10 retries: 10
labels: labels:
- traefik.enable=true - traefik.enable=true
- traefik.http.routers.lidarr.rule=Host(`${APP_HOSTNAME}`) && PathPrefix(`/lidarr`) # Added Host check - traefik.http.routers.lidarr.rule=PathPrefix(`/lidarr`)
- traefik.http.routers.lidarr.entrypoints=web - traefik.http.routers.lidarr.entrypoints=web
- traefik.http.routers.lidarr.middlewares=https-proto@docker,authelia-auth@docker - traefik.http.routers.lidarr.middlewares=authelia-auth@docker
- traefik.http.services.lidarr.loadbalancer.server.port=8686 - traefik.http.services.lidarr.loadbalancer.server.port=8686
- homepage.group=Media - homepage.group=Media
- homepage.name=Lidarr - homepage.name=Lidarr
@@ -173,9 +184,9 @@ services:
retries: 10 retries: 10
labels: labels:
- traefik.enable=true - traefik.enable=true
- traefik.http.routers.bazarr.rule=Host(`${APP_HOSTNAME}`) && PathPrefix(`/bazarr`) # Added Host check - traefik.http.routers.bazarr.rule=PathPrefix(`/bazarr`)
- traefik.http.routers.bazarr.entrypoints=web - traefik.http.routers.bazarr.entrypoints=web
- traefik.http.routers.bazarr.middlewares=https-proto@docker,authelia-auth@docker - traefik.http.routers.bazarr.middlewares=authelia-auth@docker
- traefik.http.services.bazarr.loadbalancer.server.port=6767 - traefik.http.services.bazarr.loadbalancer.server.port=6767
- homepage.group=Download - homepage.group=Download
- homepage.name=Bazarr - homepage.name=Bazarr
@@ -208,10 +219,10 @@ services:
retries: 10 retries: 10
labels: labels:
- traefik.enable=true - traefik.enable=true
- traefik.http.routers.jellyseerr.rule=Host(`${APP_HOSTNAME}`) && PathPrefix(`/jellyseerr`) # Added Host check - traefik.http.routers.jellyseerr.rule=PathPrefix(`/jellyseerr`)
- traefik.http.routers.jellyseerr.entrypoints=web - traefik.http.routers.jellyseerr.entrypoints=web
- traefik.http.services.jellyseerr.loadbalancer.server.port=5055 - traefik.http.services.jellyseerr.loadbalancer.server.port=5055
- traefik.http.routers.jellyseerr.middlewares=https-proto@docker,jellyseerr-stripprefix,jellyseerr-rewrite,jellyseerr-rewriteHeaders,authelia-auth@docker - traefik.http.routers.jellyseerr.middlewares=jellyseerr-stripprefix,jellyseerr-rewrite,jellyseerr-rewriteHeaders,authelia-auth@docker
- traefik.http.middlewares.jellyseerr-stripprefix.stripPrefix.prefixes=/jellyseerr - traefik.http.middlewares.jellyseerr-stripprefix.stripPrefix.prefixes=/jellyseerr
- traefik.http.middlewares.jellyseerr-rewriteHeaders.plugin.rewriteHeaders.rewrites[0].header=location - traefik.http.middlewares.jellyseerr-rewriteHeaders.plugin.rewriteHeaders.rewrites[0].header=location
- traefik.http.middlewares.jellyseerr-rewriteHeaders.plugin.rewriteHeaders.rewrites[0].regex=^/(.+)$ - traefik.http.middlewares.jellyseerr-rewriteHeaders.plugin.rewriteHeaders.rewrites[0].regex=^/(.+)$
@@ -282,9 +293,9 @@ services:
retries: 10 retries: 10
labels: labels:
- traefik.enable=true - traefik.enable=true
- traefik.http.routers.prowlarr.rule=Host(`${APP_HOSTNAME}`) && PathPrefix(`/prowlarr`) # Added Host check - traefik.http.routers.prowlarr.rule=PathPrefix(`/prowlarr`)
- traefik.http.routers.prowlarr.entrypoints=web - traefik.http.routers.prowlarr.entrypoints=web
- traefik.http.routers.prowlarr.middlewares=https-proto@docker,authelia-auth@docker - traefik.http.routers.prowlarr.middlewares=authelia-auth@docker
- traefik.http.services.prowlarr.loadbalancer.server.port=9696 - traefik.http.services.prowlarr.loadbalancer.server.port=9696
- homepage.group=Download - homepage.group=Download
- homepage.name=Prowlarr - homepage.name=Prowlarr
@@ -306,9 +317,9 @@ services:
- TZ=${TIMEZONE} - TZ=${TIMEZONE}
labels: labels:
- traefik.enable=true - traefik.enable=true
- traefik.http.routers.flaresolverr.rule=Host(`${APP_HOSTNAME}`) && PathPrefix(`/flaresolverr`) # Added Host check - traefik.http.routers.flaresolverr.rule=PathPrefix(`/flaresolverr`)
- traefik.http.routers.flaresolverr.entrypoints=web - traefik.http.routers.flaresolverr.entrypoints=web
- traefik.http.routers.flaresolverr.middlewares=https-proto@docker,authelia-auth@docker - traefik.http.routers.flaresolverr.middlewares=authelia-auth@docker
- traefik.http.services.flaresolverr.loadbalancer.server.port=8191 - traefik.http.services.flaresolverr.loadbalancer.server.port=8191
profiles: profiles:
- flaresolverr - flaresolverr
@@ -326,20 +337,25 @@ services:
- ${DOWNLOAD_ROOT}:/data/torrents:Z - ${DOWNLOAD_ROOT}:/data/torrents:Z
restart: always restart: always
healthcheck: healthcheck:
# Container may fail if the PIA's token expired, so mark as unhealthy when there is no internet connection
# see: https://github.com/qdm12/gluetun/issues/641#issuecomment-933856220
test: test:
["CMD", "curl", "--fail", "http://127.0.0.1:8080", "https://google.com"] ["CMD", "curl", "--fail", "http://127.0.0.1:8080", "https://google.com"]
interval: 30s interval: 30s
retries: 10 retries: 10
labels: labels:
- traefik.enable=true - traefik.enable=true
- traefik.http.routers.qbittorrent.rule=Host(`${APP_HOSTNAME}`) && PathPrefix(`/qbittorrent`) # Added Host check - traefik.http.routers.qbittorrent.rule=PathPrefix(`/qbittorrent`)
- traefik.http.routers.qbittorrent.entrypoints=web - traefik.http.routers.qbittorrent.entrypoints=web
- traefik.http.services.qbittorrent.loadbalancer.server.port=8080 - traefik.http.services.qbittorrent.loadbalancer.server.port=8080
- traefik.http.routers.qbittorrent.middlewares=https-proto@docker,qbittorrent-strip-slash,qbittorrent-stripprefix,authelia-auth@docker - traefik.http.routers.qbittorrent.middlewares=qbittorrent-strip-slash,qbittorrent-stripprefix,authelia-auth@docker
# https://github.com/qbittorrent/qBittorrent/issues/5693#issuecomment-552146296
- traefik.http.middlewares.qbittorrent-stripprefix.stripPrefix.prefixes=/qbittorrent - traefik.http.middlewares.qbittorrent-stripprefix.stripPrefix.prefixes=/qbittorrent
# https://community.traefik.io/t/middleware-to-add-the-if-needed/1895/19
- traefik.http.middlewares.qbittorrent-strip-slash.redirectregex.regex=(^.*\/qbittorrent$$) - traefik.http.middlewares.qbittorrent-strip-slash.redirectregex.regex=(^.*\/qbittorrent$$)
- traefik.http.middlewares.qbittorrent-strip-slash.redirectregex.replacement=$$1/ - traefik.http.middlewares.qbittorrent-strip-slash.redirectregex.replacement=$$1/
- traefik.http.middlewares.qbittorrent-strip-slash.redirectregex.permanent=false - traefik.http.middlewares.qbittorrent-strip-slash.redirectregex.permanent=false
#- com.centurylinklabs.watchtower.depends-on=/vpn
- homepage.group=Download - homepage.group=Download
- homepage.name=qBittorrent - homepage.name=qBittorrent
- homepage.icon=qbittorrent.png - homepage.icon=qbittorrent.png
@@ -378,9 +394,9 @@ services:
restart: always restart: always
labels: labels:
- traefik.enable=true - traefik.enable=true
- traefik.http.routers.sabnzbd.rule=Host(`${APP_HOSTNAME}`) && PathPrefix(`/sabnzbd`) # Added Host check - traefik.http.routers.sabnzbd.rule=PathPrefix(`/sabnzbd`) # Simplified rule
- traefik.http.routers.sabnzbd.entrypoints=web - traefik.http.routers.sabnzbd.entrypoints=web
- traefik.http.routers.sabnzbd.middlewares=https-proto@docker,authelia-auth@docker - traefik.http.routers.sabnzbd.middlewares=authelia-auth@docker
- traefik.http.services.sabnzbd.loadbalancer.server.port=8080 - traefik.http.services.sabnzbd.loadbalancer.server.port=8080
- homepage.group=Download - homepage.group=Download
- homepage.name=Sabnzbd - homepage.name=Sabnzbd
@@ -400,7 +416,7 @@ services:
- PUID=${USER_ID} - PUID=${USER_ID}
- PGID=${GROUP_ID} - PGID=${GROUP_ID}
- TZ=${TIMEZONE} - TZ=${TIMEZONE}
- JELLYFIN_PublishedServerUrl=https://${TAILSCALE_HOSTNAME}.${TAILSCALE_TAILNET_DOMAIN}/jellyfin - JELLYFIN_PublishedServerUrl=${TAILSCALE_HOSTNAME}.${TAILSCALE_TAILNET_DOMAIN}/jellyfin
volumes: volumes:
- ${CONFIG_ROOT:-.}/jellyfin:/config:Z - ${CONFIG_ROOT:-.}/jellyfin:/config:Z
- ${DATA_ROOT}:/data:Z - ${DATA_ROOT}:/data:Z
@@ -414,9 +430,9 @@ services:
retries: 10 retries: 10
labels: labels:
- traefik.enable=true - traefik.enable=true
- traefik.http.routers.jellyfin.rule=Host(`${APP_HOSTNAME}`) && PathPrefix(`/jellyfin`) # Added Host check - traefik.http.routers.jellyfin.rule=PathPrefix(`/jellyfin`)
- traefik.http.routers.jellyfin.entrypoints=web - traefik.http.routers.jellyfin.entrypoints=web
- traefik.http.routers.jellyfin.middlewares=https-proto@docker # Only HTTPS, no auth - traefik.http.routers.jellyfin.middlewares=authelia-auth@docker
- traefik.http.services.jellyfin.loadbalancer.server.port=8096 - traefik.http.services.jellyfin.loadbalancer.server.port=8096
- homepage.group=Media - homepage.group=Media
- homepage.name=Jellyfin - homepage.name=Jellyfin
@@ -445,8 +461,8 @@ services:
- traefik.http.middlewares.calibre-headers.headers.customRequestHeaders.X-Scheme=https - traefik.http.middlewares.calibre-headers.headers.customRequestHeaders.X-Scheme=https
- traefik.http.middlewares.calibre-headers.headers.customRequestHeaders.X-Script-Name=/calibre - traefik.http.middlewares.calibre-headers.headers.customRequestHeaders.X-Script-Name=/calibre
- traefik.http.middlewares.calibre-stripprefixregex.stripPrefixRegex.regex=/calibre - traefik.http.middlewares.calibre-stripprefixregex.stripPrefixRegex.regex=/calibre
- traefik.http.routers.calibre.middlewares=https-proto@docker,calibre-headers,calibre-stripprefixregex,authelia-auth@docker - traefik.http.routers.calibre.middlewares=calibre-headers,calibre-stripprefixregex,authelia-auth@docker
- traefik.http.routers.calibre.rule=Host(`${APP_HOSTNAME}`) && PathPrefix(`/calibre`) # Added Host check - traefik.http.routers.calibre.rule=PathPrefix(`/calibre`)
- traefik.http.routers.calibre.entrypoints=web - traefik.http.routers.calibre.entrypoints=web
- traefik.http.services.calibre.loadbalancer.server.port=8083 - traefik.http.services.calibre.loadbalancer.server.port=8083
- homepage.group=Media - homepage.group=Media
@@ -509,6 +525,7 @@ services:
- HOMEPAGE_VAR_WEATHER_LONG=${HOMEPAGE_VAR_WEATHER_LONG} - HOMEPAGE_VAR_WEATHER_LONG=${HOMEPAGE_VAR_WEATHER_LONG}
- HOMEPAGE_VAR_WEATHER_TIME=${TIMEZONE} - HOMEPAGE_VAR_WEATHER_TIME=${TIMEZONE}
- HOMEPAGE_VAR_WEATHER_UNIT=${HOMEPAGE_VAR_WEATHER_UNIT} - HOMEPAGE_VAR_WEATHER_UNIT=${HOMEPAGE_VAR_WEATHER_UNIT}
# Explicitly allow the hostname constructed from Tailscale variables
- HOMEPAGE_ALLOWED_HOSTS=${TAILSCALE_HOSTNAME}.${TAILSCALE_TAILNET_DOMAIN} - HOMEPAGE_ALLOWED_HOSTS=${TAILSCALE_HOSTNAME}.${TAILSCALE_TAILNET_DOMAIN}
volumes: volumes:
- ${CONFIG_ROOT:-.}/homepage:/app/config:Z - ${CONFIG_ROOT:-.}/homepage:/app/config:Z
@@ -519,16 +536,17 @@ services:
[sh, -c, "cp -n /app/config/tpl/*.yaml /app/config && node server.js"] [sh, -c, "cp -n /app/config/tpl/*.yaml /app/config && node server.js"]
labels: labels:
- traefik.enable=true - traefik.enable=true
- traefik.http.routers.homepage.rule=Host(`${APP_HOSTNAME}`) && PathPrefix(`/home`) # Changed rule to root # Rule for homepage, now at /home, needs auth
- traefik.http.routers.homepage.rule=PathPrefix(`/home`)
- traefik.http.routers.homepage.entrypoints=web - traefik.http.routers.homepage.entrypoints=web
# - traefik.http.routers.homepage.priority=10 # Removed priority - traefik.http.routers.homepage.priority=10 # Lower priority than Authelia's root rule
# Global middleware for setting HTTPS header - traefik.http.middlewares.homepage-stripprefix.stripPrefix.prefixes=/home
- traefik.http.middlewares.https-proto.headers.customrequestheaders.X-Forwarded-Proto=https - traefik.http.routers.homepage.middlewares=homepage-stripprefix,authelia-auth@docker
- traefik.http.routers.homepage.middlewares=https-proto@docker,authelia-auth@docker # Homepage's own labels for discovery (unchanged)
- homepage.group=Dashboard - homepage.group=Dashboard
- homepage.name=Homepage - homepage.name=Homepage
- homepage.icon=homepage.png - homepage.icon=homepage.png
- homepage.href=/ # Updated href - homepage.href=/home # Update link to new path
- homepage.description=Service Dashboard - homepage.description=Service Dashboard
watchtower: watchtower:
image: ghcr.io/containrrr/watchtower:latest image: ghcr.io/containrrr/watchtower:latest
@@ -549,22 +567,23 @@ services:
tailscale: tailscale:
image: tailscale/tailscale:latest image: tailscale/tailscale:latest
container_name: tailscale container_name: tailscale
hostname: ${TAILSCALE_HOSTNAME:-tailscale-nas} hostname: ${TAILSCALE_HOSTNAME:-tailscale-nas} # Hostname for Tailscale access
environment: environment:
TS_AUTHKEY: ${TAILSCALE_AUTHKEY} TS_AUTHKEY: ${TAILSCALE_AUTHKEY} # Needs to be set in .env
TS_EXTRA_ARGS: "--advertise-tags=${TAILSCALE_TAGS:-tag:nas}" TS_EXTRA_ARGS: "--advertise-tags=${TAILSCALE_TAGS:-tag:nas}" # Keep tags if desired
TS_STATE_DIR: "/var/lib/tailscale" TS_STATE_DIR: "/var/lib/tailscale"
TS_USERSPACE: "false" TS_USERSPACE: "false"
# Switch to enable Funnel (public access) or Serve (Tailnet only)
ENABLE_FUNNEL_HTTPS: ${ENABLE_FUNNEL_HTTPS:-false} ENABLE_FUNNEL_HTTPS: ${ENABLE_FUNNEL_HTTPS:-false}
volumes: volumes:
- ${CONFIG_ROOT:-.}/tailscale/state:/var/lib/tailscale:Z - ${CONFIG_ROOT:-.}/tailscale/state:/var/lib/tailscale:Z # Persist state
- /var/run/docker.sock:/var/run/docker.sock - /var/run/docker.sock:/var/run/docker.sock # Optional, keep if needed
devices: devices:
- /dev/net/tun:/dev/net/tun - /dev/net/tun:/dev/net/tun
cap_add: cap_add:
- NET_ADMIN - NET_ADMIN
- NET_RAW - NET_RAW
extra_hosts: extra_hosts: # Add this section
- host.docker.internal:172.17.0.1 - host.docker.internal:172.17.0.1
restart: always restart: always
command: command:
@@ -588,6 +607,8 @@ services:
done done
echo " Tailscaled is running." echo " Tailscaled is running."
# --- Start Tailscale Funnel/Serve ---
# Check the ENABLE_FUNNEL_HTTPS variable
if [ "${ENABLE_FUNNEL_HTTPS}" = "true" ]; then if [ "${ENABLE_FUNNEL_HTTPS}" = "true" ]; then
echo "ENABLE_FUNNEL_HTTPS is true. Setting up Funnel -> http://localhost:80..." echo "ENABLE_FUNNEL_HTTPS is true. Setting up Funnel -> http://localhost:80..."
tailscale funnel --bg http://localhost:80 tailscale funnel --bg http://localhost:80
@@ -597,9 +618,10 @@ services:
tailscale serve --bg http://localhost:80 tailscale serve --bg http://localhost:80
echo "Tailscale Serve configured." echo "Tailscale Serve configured."
fi fi
# --- End Tailscale Funnel/Serve ---
echo "Tailscale forwarding configured. Container will remain running." echo "Tailscale forwarding configured. Container will remain running."
wait wait # Wait indefinitely for background processes
networks: networks:
default: default:

49
update-config.sh Executable file
View File

@@ -0,0 +1,49 @@
#!/bin/bash
# See https://stackoverflow.com/a/44864004 for the sed GNU/BSD compatible hack
function update_arr_config {
echo "Updating ${container} configuration..."
until [ -f "${CONFIG_ROOT:-.}"/"$container"/config.xml ]; do sleep 1; done
sed -i.bak "s/<UrlBase><\/UrlBase>/<UrlBase>\/$1<\/UrlBase>/" "${CONFIG_ROOT:-.}"/"$container"/config.xml && rm "${CONFIG_ROOT:-.}"/"$container"/config.xml.bak
CONTAINER_NAME_UPPER=$(echo "$container" | tr '[:lower:]' '[:upper:]')
sed -i.bak 's/^'"${CONTAINER_NAME_UPPER}"'_API_KEY=.*/'"${CONTAINER_NAME_UPPER}"'_API_KEY='"$(sed -n 's/.*<ApiKey>\(.*\)<\/ApiKey>.*/\1/p' "${CONFIG_ROOT:-.}"/"$container"/config.xml)"'/' .env && rm .env.bak
echo "Update of ${container} configuration complete, restarting..."
docker compose restart "$container"
}
function update_qbittorrent_config {
echo "Updating ${container} configuration..."
docker compose stop "$container"
until [ -f "${CONFIG_ROOT:-.}"/"$container"/qBittorrent/qBittorrent.conf ]; do sleep 1; done
sed -i.bak '/WebUI\\ServerDomains=*/a WebUI\\Password_PBKDF2="@ByteArray(ARQ77eY1NUZaQsuDHbIMCA==:0WMRkYTUWVT9wVvdDtHAjU9b3b7uB8NR1Gur2hmQCvCDpm39Q+PsJRJPaCU51dEiz+dTzh8qbPsL8WkFljQYFQ==)"' "${CONFIG_ROOT:-.}"/"$container"/qBittorrent/qBittorrent.conf && rm "${CONFIG_ROOT:-.}"/"$container"/qBittorrent/qBittorrent.conf.bak
echo "Update of ${container} configuration complete, restarting..."
docker compose start "$container"
}
function update_bazarr_config {
echo "Updating ${container} configuration..."
until [ -f "${CONFIG_ROOT:-.}"/"$container"/config/config/config.yaml ]; do sleep 1; done
sed -i.bak "s/base_url: ''/base_url: '\/$container'/" "${CONFIG_ROOT:-.}"/"$container"/config/config/config.yaml && rm "${CONFIG_ROOT:-.}"/"$container"/config/config/config.yaml.bak
sed -i.bak "s/use_radarr: false/use_radarr: true/" "${CONFIG_ROOT:-.}"/"$container"/config/config/config.yaml && rm "${CONFIG_ROOT:-.}"/"$container"/config/config/config.yaml.bak
sed -i.bak "s/use_sonarr: false/use_sonarr: true/" "${CONFIG_ROOT:-.}"/"$container"/config/config/config.yaml && rm "${CONFIG_ROOT:-.}"/"$container"/config/config/config.yaml.bak
until [ -f "${CONFIG_ROOT:-.}"/sonarr/config.xml ]; do sleep 1; done
SONARR_API_KEY=$(sed -n 's/.*<ApiKey>\(.*\)<\/ApiKey>.*/\1/p' "${CONFIG_ROOT:-.}"/sonarr/config.xml)
sed -i.bak "/sonarr:/,/^radarr:/ { s/apikey: .*/apikey: $SONARR_API_KEY/; s/base_url: .*/base_url: \/sonarr/; s/ip: .*/ip: sonarr/ }" "${CONFIG_ROOT:-.}"/"$container"/config/config/config.yaml && rm "${CONFIG_ROOT:-.}"/"$container"/config/config/config.yaml.bak
until [ -f "${CONFIG_ROOT:-.}"/radarr/config.xml ]; do sleep 1; done
RADARR_API_KEY=$(sed -n 's/.*<ApiKey>\(.*\)<\/ApiKey>.*/\1/p' "${CONFIG_ROOT:-.}"/radarr/config.xml)
sed -i.bak "/radarr:/,/^sonarr:/ { s/apikey: .*/apikey: $RADARR_API_KEY/; s/base_url: .*/base_url: \/radarr/; s/ip: .*/ip: radarr/ }" "${CONFIG_ROOT:-.}"/"$container"/config/config/config.yaml && rm "${CONFIG_ROOT:-.}"/"$container"/config/config/config.yaml.bak
sed -i.bak 's/^BAZARR_API_KEY=.*/BAZARR_API_KEY='"$(sed -n 's/.*apikey: \(.*\)*/\1/p' "${CONFIG_ROOT:-.}"/"$container"/config/config/config.yaml | head -n 1)"'/' .env && rm .env.bak
echo "Update of ${container} configuration complete, restarting..."
docker compose restart "$container"
}
for container in $(docker ps --format '{{.Names}}'); do
if [[ "$container" =~ ^(radarr|sonarr|lidarr|prowlarr)$ ]]; then
update_arr_config "$container"
elif [[ "$container" =~ ^(bazarr)$ ]]; then
update_bazarr_config "$container"
elif [[ "$container" =~ ^(qbittorrent)$ ]]; then
update_qbittorrent_config "$container"
fi
done

162
update-env.sh Executable file
View File

@@ -0,0 +1,162 @@
#!/bin/bash
# env-update.sh - Update environment variables from .env.example to .env
# Preserves existing values while updating structure and highlighting changes
# Created: April 25, 2025
set -e
# Color definitions
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
BLUE='\033[0;34m'
MAGENTA='\033[0;35m'
CYAN='\033[0;36m'
BOLD='\033[1m'
NC='\033[0m' # No Color
# Files
ENV_FILE=".env"
ENV_EXAMPLE=".env.example"
TIMESTAMP=$(date +"%Y%m%d-%H%M%S")
ENV_BACKUP=".env.${TIMESTAMP}.bak"
# Check if files exist
if [ ! -f "$ENV_FILE" ]; then
echo -e "${RED}Error: $ENV_FILE doesn't exist${NC}"
echo -e "Creating a new $ENV_FILE from $ENV_EXAMPLE"
cp "$ENV_EXAMPLE" "$ENV_FILE"
echo -e "${GREEN}Done! Please review and fill in required values in $ENV_FILE${NC}"
exit 0
fi
if [ ! -f "$ENV_EXAMPLE" ]; then
echo -e "${RED}Error: $ENV_EXAMPLE doesn't exist${NC}"
exit 1
fi
echo -e "${CYAN}${BOLD}Environment File Update Tool${NC}"
echo -e "${CYAN}============================${NC}"
echo -e "${BLUE}This will update your $ENV_FILE based on the structure in $ENV_EXAMPLE${NC}"
echo -e "${BLUE}Your existing values will be preserved where possible${NC}"
echo -e "${BLUE}Backup will be created as: $ENV_BACKUP${NC}"
echo -e "${YELLOW}Continue? [y/N]:${NC}"
read -r answer
if [[ ! "$answer" =~ ^[Yy]$ ]]; then
echo -e "${RED}Cancelled.${NC}"
exit 0
fi
# Create backup of current .env
echo -e "${BLUE}Creating backup of current $ENV_FILE as $ENV_BACKUP...${NC}"
cp "$ENV_FILE" "$ENV_BACKUP"
# Store current env values
echo -e "${BLUE}Reading current environment values...${NC}"
declare -A current_values
declare -A current_keys_present
while IFS='=' read -r key value; do
# Skip comments and empty lines
if [[ ! "$key" =~ ^#.*$ ]] && [[ ! -z "$key" ]]; then
# Clean up any comments after the value
value=$(echo "$value" | sed 's/[[:space:]]*#.*$//')
# Trim leading/trailing whitespace
key=$(echo "$key" | xargs)
value=$(echo "$value" | xargs)
# Store in associative array if key is not empty
if [[ ! -z "$key" ]]; then
current_values["$key"]="$value"
# Track that this key existed in original file, regardless of value
current_keys_present["$key"]=1
fi
fi
done < "$ENV_FILE"
# Create new env file from example
echo -e "${BLUE}Creating new $ENV_FILE from $ENV_EXAMPLE...${NC}"
cp "$ENV_EXAMPLE" "$ENV_FILE.new"
# Track which keys from the current env have been used
declare -A used_keys
# Track new keys that need attention
new_keys=()
# Track keys with special warnings
special_keys=()
# Process the template and fill in values from current env
while IFS= read -r line; do
if [[ "$line" =~ ^([A-Za-z0-9_]+)=(.*)$ ]]; then
key="${BASH_REMATCH[1]}"
default_value="${BASH_REMATCH[2]}"
# Mark the key as used if it exists in the original file
if [[ -n "${current_keys_present[$key]}" ]]; then
used_keys["$key"]=1
# Replace the line with the current value if one exists
if [[ -n "${current_values[$key]}" ]]; then
sed -i "s|^$key=.*$|$key=${current_values[$key]}|" "$ENV_FILE.new"
fi
# If key doesn't exist in original file and has empty/placeholder value
elif [[ -z "$default_value" ]] || [[ "$default_value" == '""' ]] || [[ "$default_value" == "''" ]]; then
new_keys+=("$key")
# Special attention for Authelia keys
if [[ "$key" == AUTHELIA_*_SECRET* ]] || [[ "$key" == AUTHELIA_*_KEY* ]]; then
special_keys+=("$key")
fi
fi
fi
done < "$ENV_FILE.new"
# Create section for unused/deprecated keys at the bottom of the file
echo -e "\n\n# --- DEPRECATED OR UNUSED KEYS (Kept for Reference) ---" >> "$ENV_FILE.new"
echo -e "# Keys below were in your original .env but aren't in the current .env.example" >> "$ENV_FILE.new"
echo -e "# They may be deprecated or renamed. Review and remove if no longer needed\n" >> "$ENV_FILE.new"
unused_keys_count=0
for key in "${!current_values[@]}"; do
if [[ -z "${used_keys[$key]}" ]]; then
echo "$key=${current_values[$key]} # DEPRECATED/UNUSED - Review" >> "$ENV_FILE.new"
unused_keys_count=$((unused_keys_count + 1))
fi
done
# Replace the old file with the new one
mv "$ENV_FILE.new" "$ENV_FILE"
# Generate summary
echo -e "\n${GREEN}${BOLD}Update Complete!${NC}"
echo -e "${BLUE}Summary:${NC}"
echo -e " - ${CYAN}Original config backed up to: $ENV_BACKUP${NC}"
echo -e " - ${CYAN}Updated .env structure to match .env.example${NC}"
echo -e " - ${CYAN}Preserved ${#used_keys[@]} existing values${NC}"
if [[ $unused_keys_count -gt 0 ]]; then
echo -e " - ${YELLOW}Found $unused_keys_count deprecated/unused keys${NC}"
echo -e " ${YELLOW}These have been moved to the bottom of the file with warnings${NC}"
fi
if [[ ${#new_keys[@]} -gt 0 ]]; then
echo -e "\n${YELLOW}${BOLD}NEW KEYS NEEDING ATTENTION:${NC}"
echo -e "${YELLOW}The following keys are new and may need values set:${NC}"
for key in "${new_keys[@]}"; do
echo -e " - ${MAGENTA}$key${NC}"
done
fi
if [[ ${#special_keys[@]} -gt 0 ]]; then
echo -e "\n${RED}${BOLD}IMPORTANT SECURITY KEYS:${NC}"
echo -e "${RED}The following keys require secure values:${NC}"
for key in "${special_keys[@]}"; do
echo -e " - ${MAGENTA}$key${NC}"
# Specific advice for Authelia keys
if [[ "$key" == AUTHELIA_*_SECRET* ]] || [[ "$key" == AUTHELIA_*_KEY* ]]; then
echo -e " ${CYAN}Generate with: ${GREEN}openssl rand -hex 32${NC}"
fi
done
fi
echo -e "\n${BLUE}Review your updated $ENV_FILE file and adjust any values as needed.${NC}"

File diff suppressed because it is too large Load Diff