DOCS: Docker guides

Signed-off-by: jokob-sk <jokob.sk@gmail.com>
This commit is contained in:
jokob-sk
2025-10-30 13:14:06 +11:00
parent 647defb4cc
commit fba5359839
2 changed files with 134 additions and 69 deletions

View File

@@ -1,46 +1,78 @@
# Managing File Permissions for NetAlertX on Nginx with Docker
# Managing File Permissions for NetAlertX on a Read-Only Container
> [!TIP]
> If you are facing permission issues, try to start the container without mapping your volumes. If that works, then the issue is permission related. You can try e.g., the following command:
> ```
> docker run -d --rm --network=host \
> -e TZ=Europe/Berlin \
> -e PUID=200 -e PGID=200 \
> -e PORT=20211 \
> ghcr.io/jokob-sk/netalertx:latest
> ```
NetAlertX runs on an Nginx web server. On Alpine Linux, Nginx operates as the `nginx` user (if PUID and GID environment variables are not specified, nginx user UID will be set to 102, and its supplementary group `www-data` ID to 82). Consequently, files accessed or written by the NetAlertX application are owned by `nginx:www-data`.
> NetAlertX runs in a **secure, read-only Alpine-based container** under a dedicated `netalertx` user (UID 20211, GID 20211). All writable paths are either mounted as **persistent volumes** or **`tmpfs` filesystems**. This ensures consistent file ownership and prevents privilege escalation.
Upon starting, NetAlertX changes nginx user UID and www-data GID to specified values (or defaults), and the ownership of files on the host system mapped to `/app/config` and `/app/db` in the container to `nginx:www-data`. This ensures that Nginx can access and write to these files. Since the user in the Docker container is mapped to a user on the host system by ID:GID, the files in `/app/config` and `/app/db` on the host system are owned by a user with the same ID and GID (defaults are ID 102 and GID 82). On different systems, this ID:GID may belong to different users, or there may not be a group with ID 82 at all.
---
Option to set specific user UID and GID can be useful for host system users needing to access these files (e.g., backup scripts).
## Writable Paths
### Permissions Table for Individual Folders
NetAlertX requires certain paths to be writable at runtime. These paths should be mounted either as **host volumes** or **`tmpfs`** in your `docker-compose.yml` or `docker run` command:
| Folder | User | User ID | Group | Group ID | Permissions | Notes |
|----------------|--------|---------|-----------|----------|-------------|---------------------------------------------------------------------|
| `/app/config` | nginx | PUID (default 102) | www-data | PGID (default 82) | rwxr-xr-x | Ensure `nginx` can read/write; other users can read if in `www-data` |
| `/app/db` | nginx | PUID (default 102) | www-data | PGID (default 82) | rwxr-xr-x | Same as above |
| Path | Purpose | Notes |
| ------------------------------------ | ----------------------------------- | ------------------------------------------------------ |
| `/app/config` | Application configuration | Persistent volume recommended |
| `/app/db` | Database files | Persistent volume recommended |
| `/app/log` | Logs | Can be `tmpfs` for speed or host volume to retain logs |
| `/app/api` | API cache | Use `tmpfs` for faster access |
| `/services/config/nginx/conf.active` | Active nginx configuration override | `tmpfs` recommended or customiozed file mounted |
| `/services/run` | Runtime directories for nginx & PHP | `tmpfs` required |
| `/tmp` | PHP session save directory | `tmpfs` required |
> All these paths will have **UID 20211 / GID 20211** inside the container. Files on the host will appear owned by `20211:20211`.
### Fixing Permission Problems
---
The container fails to start with "Permission Denied" errors. This typically happens when your data volumes (`netalertx_config`, `netalertx_db`) are "owned" by the `root` user (UID 0) from a previous install, but the secure container must run as the `netalertx` user (UID 20211).
## Fixing Permission Problems
Sometimes, permission issues arise if your existing host directories were created by a previous container running as root or another UID. The container will fail to start with "Permission Denied" errors.
### Solution
1. Run the container **once** as the `root` user (`--user "0"`) to trigger the built-in fix-it mode:
1. **Run the container once as root** (`--user "0"`) to allow it to correct permissions automatically:
```bash
docker run -it --rm --name netalertx-fix --user "0" \
-v netalertx_config:/app/config \
-v netalertx_db:/app/db \
netalertx:latest
```
2. Wait for the logs to show a **magenta warning** and confirm permissions are being fixed. The container will then hang (this is intentional).
3. Press **Ctrl+C** to stop the fix-it container.
4. Start your container normally (e.g., with `docker-compose up -d` or your standard `docker run` command).
```bash
docker run -it --rm --name netalertx --user "0" \
-v local/path/config:/app/config \
-v local/path/db:/app/db \
ghcr.io/jokob-sk/netalertx:latest
```
2. Wait for logs showing **permissions being fixed**. The container will then **hang intentionally**.
3. Press **Ctrl+C** to stop the container.
4. Start the container normally with your `docker-compose.yml` or `docker run` command.
> The container startup script detects `root` and runs `chown -R 20211:20211` on all volumes, fixing ownership for the secure `netalertx` user.
---
## Example: docker-compose.yml with `tmpfs`
```yaml
services:
netalertx:
container_name: netalertx
image: "ghcr.io/jokob-sk/netalertx"
network_mode: "host"
cap_add:
- NET_RAW
- NET_ADMIN
- NET_BIND_SERVICE
restart: unless-stopped
volumes:
- local/path/config:/app/config
- local/path/db:/app/db
environment:
- TZ=Europe/Berlin
- PORT=20211
tmpfs:
- "/app/log:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/app/api:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,sync,noatime,nodiratime"
- "/services/config/nginx/conf.active:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/services/run:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/tmp:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
```
> This setup ensures all writable paths are either in `tmpfs` or host-mounted, and the container never writes outside of controlled volumes.
### About This Method
The containers startup script detects it is running as `root` (UID 0). It automatically runs the `chown` command to fix the permissions on your volume files, setting them to the correct `20211` user. It then hangs (`sleep infinity`) to prevent you from *ever* running the application as `root`, forcing you to restart securely.

View File

@@ -1,6 +1,6 @@
# Migration
When upgrading from older versions of NetAlertX (or PiAlert (by jokob-sk)) the following data and setup migration steps need to be followed.
When upgrading from older versions of NetAlertX (or PiAlert by jokob-sk), follow the migration steps below to ensure your data and configuration are properly transferred.
> [!TIP]
> It's always important to have a [backup strategy](./BACKUPS.md) in place.
@@ -27,7 +27,7 @@ You can migrate data manually, for example by exporting and importing devices us
#### STEPS:
The application will automatically migrate the database, configuration, and all device information.
A ticker message will appear at the top of the web UI until you update your Docker mount points.
A banner message will appear at the top of the web UI reminding you to update your Docker mount points.
1. Stop the container
2. [Back up your setup](./BACKUPS.md)
@@ -37,7 +37,7 @@ A ticker message will appear at the top of the web UI until you update your Dock
> [!TIP]
> If you have troubles accessing past backups, config or database files you can copy them into the newly mapped directories, for example by running this command in the container: `cp -r /app/config /home/pi/pialert/config/old_backup_files`. This should create a folder in the `config` directory called `old_backup_files` containing all the files in that location. Another approach is to map the old location and the new one at the same time to copy things over.
> If you have trouble accessing past backups, config or database files you can copy them into the newly mapped directories, for example by running this command in the container: `cp -r /app/config /home/pi/pialert/config/old_backup_files`. This should create a folder in the `config` directory called `old_backup_files` containing all the files in that location. Another approach is to map the old location and the new one at the same time to copy things over.
#### New Docker mount locations
@@ -58,7 +58,7 @@ The internal application path in the container has changed from `/home/pi/pialer
> [!NOTE]
> The application uses symlinks linking the old db and config locations to the new ones, so data loss should not occur. [Backup strategies](./BACKUPS.md) are still recommended to backup your setup.
> The application automatically creates symlinks from the old database and config locations to the new ones, so data loss should not occur. Read the [backup strategies](./BACKUPS.md) guide to backup your setup.
#### Examples
@@ -92,16 +92,16 @@ services:
```yaml
services:
netalertx: # This has changed (🟡optional)
container_name: netalertx # This has changed (🟡optional)
image: "ghcr.io/jokob-sk/netalertx:25.5.24" # This has changed (🟡optional/🔺required in future)
netalertx: # 🆕 This has changed
container_name: netalertx # 🆕 This has changed
image: "ghcr.io/jokob-sk/netalertx:25.5.24" # 🆕 This has changed
network_mode: "host"
restart: unless-stopped
volumes:
- local/path/config:/app/config # This has changed (🔺required)
- local/path/db:/app/db # This has changed (🔺required)
- local/path/config:/app/config # 🆕 This has changed
- local/path/db:/app/db # 🆕 This has changed
# (optional) useful for debugging if you have issues setting up the container
- local/path/logs:/app/log # This has changed (🟡optional)
- local/path/logs:/app/log # 🆕 This has changed
environment:
- TZ=Europe/Berlin
- PORT=20211
@@ -138,16 +138,16 @@ services:
```yaml
services:
netalertx: # This has changed (🟡optional)
container_name: netalertx # This has changed (🟡optional)
image: "ghcr.io/jokob-sk/netalertx:25.5.24" # This has changed (🟡optional/🔺required in future)
netalertx: # 🆕 This has changed
container_name: netalertx # 🆕 This has changed
image: "ghcr.io/jokob-sk/netalertx:25.5.24" # 🆕 This has changed
network_mode: "host"
restart: unless-stopped
volumes:
- local/path/config/app.conf:/app/config/app.conf # This has changed (🔺required)
- local/path/db/app.db:/app/db/app.db # This has changed (🔺required)
- local/path/config/app.conf:/app/config/app.conf # 🆕 This has changed
- local/path/db/app.db:/app/db/app.db # 🆕 This has changed
# (optional) useful for debugging if you have issues setting up the container
- local/path/logs:/app/log # This has changed (🟡optional)
- local/path/logs:/app/log # 🆕 This has changed
environment:
- TZ=Europe/Berlin
- PORT=20211
@@ -156,7 +156,7 @@ services:
### 1.2 Migration from NetAlertX `v25.5.24`
Versions before `v25.10.1` require an intermediate migration through `v25.5.24` to ensure database compatibility.
Versions before `v25.10.1` require an intermediate migration through `v25.5.24` to ensure database compatibility. Skipping this step may cause compatibility issues due to database schema changes introduced after `v25.5.24`.
#### STEPS:
@@ -180,7 +180,7 @@ Examples of docker files with the tagged version.
services:
netalertx:
container_name: netalertx
image: "ghcr.io/jokob-sk/netalertx:25.5.24" # This is important (🔺required)
image: "ghcr.io/jokob-sk/netalertx:25.5.24" # 🆕 This is important
network_mode: "host"
restart: unless-stopped
volumes:
@@ -197,7 +197,7 @@ services:
services:
netalertx:
container_name: netalertx
image: "ghcr.io/jokob-sk/netalertx:25.10.1" # This is important (🔺required)
image: "ghcr.io/jokob-sk/netalertx:25.10.1" # 🆕 This is important
network_mode: "host"
restart: unless-stopped
volumes:
@@ -212,30 +212,19 @@ services:
### 1.3 Migration from NetAlertX `v25.10.1`
> [!WARNING]
> This section is under development. The migration path from `v25.10.1` to future versions (e.g., `v25.11.x` and newer) will be published soon.
Starting from v25.10.1, the container uses a [more secure, read-only runtime environment](./SECURITY_FEATURES.md), which requires all writable paths (e.g., logs, API cache, temporary data) to be mounted as `tmpfs` or permanent writable volumes, with sufficient access [permissions](./FILE_PERMISSIONS.md).
#### STEPS:
1. Stop the container
2. [Back up your setup](./BACKUPS.md)
3. Upgrade to `v25.10.1` by pinning the release version (See Examples below)
4. Start the container and verify everything works as expected.
5. Stop the container
6. 🔻 TBC 🔺 Perform a one-off migration to the `20211` user `docker run -it --rm --name netalertx --user "0" -v netalertx_config:/app/config -v netalertx_db:/app/db netalertx:latest`
7. 🔻 TBC 🔺 Stop the container
8. 🔻 TBC 🔺 Switch to the latest `netalertx` image
9. 🔻 TBC 🔺 Start the container and verify everything works as expected.
##### Example 1: Mapping folders
###### docker-compose.yml changes
3. Upgrade to `v25.10.1` by pinning the release version (See the example below)
```yaml
services:
netalertx:
container_name: netalertx
image: "ghcr.io/jokob-sk/netalertx:25.10.1" # This is important (🔺required)
image: "ghcr.io/jokob-sk/netalertx:25.10.1" # 🆕 This is important
network_mode: "host"
restart: unless-stopped
volumes:
@@ -248,12 +237,56 @@ services:
- PORT=20211
```
🔻 TBC 🔺
4. Start the container and verify everything works as expected.
5. Stop the container.
6. Perform a one-off migration to the latest `netalertx` image and `20211` user:
```bash
> [!NOTE]
> The example below assumes your `/config` and `/db` folders are stored in `local/path`.
> Replace this path with your actual configuration directory. `netalertx` is the container name, whcih might differ from your setup.
```sh
docker run -it --rm --name netalertx --user "0" \
-v netalertx_config:/app/config \
-v netalertx_db:/app/db \
netalertx:latest
-v local/path/config:/app/config \
-v local/path/db:/app/db \
ghcr.io/jokob-sk/netalertx:latest
```
7. Stop the container
8. Update the `docker-compose.yml` as per example below.
```yaml
services:
netalertx:
container_name: netalertx
image: "ghcr.io/jokob-sk/netalertx" # 🆕 This is important
network_mode: "host"
cap_add: # 🆕 New line
- NET_RAW # 🆕 New line
- NET_ADMIN # 🆕 New line
- NET_BIND_SERVICE # 🆕 New line
restart: unless-stopped
volumes:
- local/path/config:/app/config
- local/path/db:/app/db
# (optional) useful for debugging if you have issues setting up the container
#- local/path/logs:/app/log
environment:
- TZ=Europe/Berlin
- PORT=20211
# 🆕 New "tmpfs" section START 🔽
tmpfs:
# Speed up logging. This can be commented out to retain logs between container restarts
- "/app/log:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
# Speed up API access as frontend/backend API is very chatty
- "/app/api:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,sync,noatime,nodiratime"
# Required for customization of the nginx listen addr/port without rebuilding the container
- "/services/config/nginx/conf.active:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
# /services/config/nginx/conf.d is required for nginx and php to start
- "/services/run:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
# /tmp is required by php for session save this should be reworked to /services/run/tmp
- "/tmp:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
# 🆕 New "tmpfs" section END 🔼
```
9. Start the container and verify everything works as expected.