fix: user systemd
This commit is contained in:
parent
1cdad04a93
commit
229f9f6b5d
1
.gitignore
vendored
1
.gitignore
vendored
@ -4,6 +4,7 @@ inventory/*
|
|||||||
inventory/host_vars/*
|
inventory/host_vars/*
|
||||||
!inventory/host_vars/example.yml
|
!inventory/host_vars/example.yml
|
||||||
inventory_data/
|
inventory_data/
|
||||||
|
playbook.yml
|
||||||
playbooks/*
|
playbooks/*
|
||||||
!playbooks/example.yml
|
!playbooks/example.yml
|
||||||
TODO.md
|
TODO.md
|
||||||
|
|||||||
@ -3,11 +3,15 @@
|
|||||||
ansible.builtin.systemd:
|
ansible.builtin.systemd:
|
||||||
daemon_reload: true
|
daemon_reload: true
|
||||||
|
|
||||||
|
- name: Reload systemd user
|
||||||
|
ansible.builtin.command: "systemctl --user daemon-reload"
|
||||||
|
become: true
|
||||||
|
become_user: "{{ ansible_user }}"
|
||||||
|
|
||||||
- name: Restart gitea
|
- name: Restart gitea
|
||||||
ansible.builtin.systemd:
|
ansible.builtin.command: "systemctl --user restart gitea.service"
|
||||||
name: gitea
|
become: true
|
||||||
state: restarted
|
become_user: "{{ ansible_user }}"
|
||||||
daemon_reload: true
|
|
||||||
|
|
||||||
- name: Reload nginx
|
- name: Reload nginx
|
||||||
ansible.builtin.systemd:
|
ansible.builtin.systemd:
|
||||||
|
|||||||
@ -82,21 +82,39 @@
|
|||||||
mode: "0644"
|
mode: "0644"
|
||||||
notify: Restart gitea
|
notify: Restart gitea
|
||||||
|
|
||||||
- name: Create systemd service for Gitea
|
- name: Get home directory for {{ ansible_user }}
|
||||||
|
ansible.builtin.getent:
|
||||||
|
database: passwd
|
||||||
|
key: "{{ ansible_user }}"
|
||||||
|
|
||||||
|
- name: Set user home directory fact
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
user_home_dir: "{{ getent_passwd[ansible_user][4] }}"
|
||||||
|
|
||||||
|
- name: Create systemd user directory for Gitea
|
||||||
|
ansible.builtin.file:
|
||||||
|
path: "{{ user_home_dir }}/.config/systemd/user"
|
||||||
|
state: directory
|
||||||
|
owner: "{{ ansible_user }}"
|
||||||
|
group: "{{ ansible_user }}"
|
||||||
|
mode: "0755"
|
||||||
|
|
||||||
|
- name: Create systemd service for Gitea (user scope)
|
||||||
ansible.builtin.template:
|
ansible.builtin.template:
|
||||||
src: gitea.service.j2
|
src: gitea.service.j2
|
||||||
dest: /etc/systemd/system/gitea.service
|
dest: "{{ user_home_dir }}/.config/systemd/user/gitea.service"
|
||||||
owner: root
|
owner: "{{ ansible_user }}"
|
||||||
group: root
|
group: "{{ ansible_user }}"
|
||||||
mode: "0644"
|
mode: "0644"
|
||||||
notify: Reload systemd
|
notify: Reload systemd user
|
||||||
|
|
||||||
- name: Enable and start Gitea service
|
- name: Enable lingering for user {{ ansible_user }}
|
||||||
ansible.builtin.systemd:
|
ansible.builtin.command: "loginctl enable-linger {{ ansible_user }}"
|
||||||
name: gitea
|
when: ansible_user != 'root'
|
||||||
enabled: true
|
|
||||||
state: started
|
- name: Enable and start Gitea service (user scope)
|
||||||
daemon_reload: true
|
ansible.builtin.command: "systemctl --user enable --now gitea.service"
|
||||||
|
become_user: "{{ ansible_user }}"
|
||||||
|
|
||||||
- name: Deploy nginx vhost configuration for Gitea
|
- name: Deploy nginx vhost configuration for Gitea
|
||||||
ansible.builtin.template:
|
ansible.builtin.template:
|
||||||
|
|||||||
@ -1,13 +1,9 @@
|
|||||||
[Unit]
|
[Unit]
|
||||||
Description=Gitea Git Service
|
Description=Gitea Git Service
|
||||||
Requires=network-online.target
|
|
||||||
After=network-online.target
|
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
Type=oneshot
|
Type=oneshot
|
||||||
RemainAfterExit=true
|
RemainAfterExit=true
|
||||||
User={{ ansible_user }}
|
|
||||||
Group={{ ansible_user }}
|
|
||||||
WorkingDirectory={{ podman_projects_dir | default('/opt/podman') }}/gitea
|
WorkingDirectory={{ podman_projects_dir | default('/opt/podman') }}/gitea
|
||||||
ExecStart=/usr/bin/podman play kube --replace gitea.yaml
|
ExecStart=/usr/bin/podman play kube --replace gitea.yaml
|
||||||
ExecStop=/usr/bin/podman play kube --down gitea.yaml
|
ExecStop=/usr/bin/podman play kube --down gitea.yaml
|
||||||
@ -15,4 +11,4 @@ Restart=on-failure
|
|||||||
RestartSec=10
|
RestartSec=10
|
||||||
|
|
||||||
[Install]
|
[Install]
|
||||||
WantedBy=multi-user.target
|
WantedBy=default.target
|
||||||
|
|||||||
@ -9,6 +9,7 @@ See `defaults/main.yml` for all available variables and their default values.
|
|||||||
### Required Passwords
|
### Required Passwords
|
||||||
|
|
||||||
Both passwords must be set in your inventory (min 12 characters):
|
Both passwords must be set in your inventory (min 12 characters):
|
||||||
|
|
||||||
- `immich_postgres_password` - PostgreSQL database password
|
- `immich_postgres_password` - PostgreSQL database password
|
||||||
- `immich_valkey_password` - Valkey/Redis password
|
- `immich_valkey_password` - Valkey/Redis password
|
||||||
|
|
||||||
@ -17,6 +18,7 @@ Both passwords must be set in your inventory (min 12 characters):
|
|||||||
### Valkey ACL Issues
|
### Valkey ACL Issues
|
||||||
|
|
||||||
**Test Immich user credentials:**
|
**Test Immich user credentials:**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
valkey-cli
|
valkey-cli
|
||||||
AUTH immich <immich_valkey_password>
|
AUTH immich <immich_valkey_password>
|
||||||
|
|||||||
@ -3,11 +3,15 @@
|
|||||||
ansible.builtin.systemd:
|
ansible.builtin.systemd:
|
||||||
daemon_reload: true
|
daemon_reload: true
|
||||||
|
|
||||||
|
- name: Reload systemd user
|
||||||
|
ansible.builtin.command: "systemctl --user daemon-reload"
|
||||||
|
become: true
|
||||||
|
become_user: "{{ ansible_user }}"
|
||||||
|
|
||||||
- name: Restart Immich
|
- name: Restart Immich
|
||||||
ansible.builtin.systemd:
|
ansible.builtin.command: "systemctl --user restart immich.service"
|
||||||
name: immich
|
become: true
|
||||||
state: restarted
|
become_user: "{{ ansible_user }}"
|
||||||
daemon_reload: true
|
|
||||||
|
|
||||||
- name: Reload nginx
|
- name: Reload nginx
|
||||||
ansible.builtin.systemd:
|
ansible.builtin.systemd:
|
||||||
|
|||||||
@ -89,21 +89,39 @@
|
|||||||
mode: "0644"
|
mode: "0644"
|
||||||
notify: Restart Immich
|
notify: Restart Immich
|
||||||
|
|
||||||
- name: Create systemd service for Immich
|
- name: Get home directory for {{ ansible_user }}
|
||||||
|
ansible.builtin.getent:
|
||||||
|
database: passwd
|
||||||
|
key: "{{ ansible_user }}"
|
||||||
|
|
||||||
|
- name: Set user home directory fact
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
user_home_dir: "{{ getent_passwd[ansible_user][4] }}"
|
||||||
|
|
||||||
|
- name: Create systemd user directory for Immich
|
||||||
|
ansible.builtin.file:
|
||||||
|
path: "{{ user_home_dir }}/.config/systemd/user"
|
||||||
|
state: directory
|
||||||
|
owner: "{{ ansible_user }}"
|
||||||
|
group: "{{ ansible_user }}"
|
||||||
|
mode: "0755"
|
||||||
|
|
||||||
|
- name: Create systemd service for Immich (user scope)
|
||||||
ansible.builtin.template:
|
ansible.builtin.template:
|
||||||
src: immich.service.j2
|
src: immich.service.j2
|
||||||
dest: /etc/systemd/system/immich.service
|
dest: "{{ user_home_dir }}/.config/systemd/user/immich.service"
|
||||||
owner: root
|
owner: "{{ ansible_user }}"
|
||||||
group: root
|
group: "{{ ansible_user }}"
|
||||||
mode: "0644"
|
mode: "0644"
|
||||||
notify: Reload systemd
|
notify: Reload systemd user
|
||||||
|
|
||||||
- name: Enable and start Immich service
|
- name: Enable lingering for user {{ ansible_user }}
|
||||||
ansible.builtin.systemd:
|
ansible.builtin.command: "loginctl enable-linger {{ ansible_user }}"
|
||||||
name: immich
|
when: ansible_user != 'root'
|
||||||
enabled: true
|
|
||||||
state: started
|
- name: Enable and start Immich service (user scope)
|
||||||
daemon_reload: true
|
ansible.builtin.command: "systemctl --user enable --now immich.service"
|
||||||
|
become_user: "{{ ansible_user }}"
|
||||||
|
|
||||||
- name: Deploy nginx vhost configuration for Immich
|
- name: Deploy nginx vhost configuration for Immich
|
||||||
ansible.builtin.template:
|
ansible.builtin.template:
|
||||||
|
|||||||
@ -1,13 +1,9 @@
|
|||||||
[Unit]
|
[Unit]
|
||||||
Description=Immich Media Server
|
Description=Immich Media Server
|
||||||
Requires=network-online.target
|
|
||||||
After=network-online.target
|
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
Type=oneshot
|
Type=oneshot
|
||||||
RemainAfterExit=true
|
RemainAfterExit=true
|
||||||
User={{ ansible_user }}
|
|
||||||
Group={{ ansible_user }}
|
|
||||||
WorkingDirectory={{ podman_projects_dir | default('/opt/podman') }}/immich
|
WorkingDirectory={{ podman_projects_dir | default('/opt/podman') }}/immich
|
||||||
ExecStart=/usr/bin/podman play kube --replace immich.yaml
|
ExecStart=/usr/bin/podman play kube --replace immich.yaml
|
||||||
ExecStop=/usr/bin/podman play kube --down immich.yaml
|
ExecStop=/usr/bin/podman play kube --down immich.yaml
|
||||||
@ -15,4 +11,4 @@ Restart=on-failure
|
|||||||
RestartSec=10
|
RestartSec=10
|
||||||
|
|
||||||
[Install]
|
[Install]
|
||||||
WantedBy=multi-user.target
|
WantedBy=default.target
|
||||||
|
|||||||
@ -17,19 +17,20 @@ Installs and configures Nginx as a reverse proxy for web applications with modul
|
|||||||
Each service role should deploy its own vhost config:
|
Each service role should deploy its own vhost config:
|
||||||
|
|
||||||
**In service role tasks:**
|
**In service role tasks:**
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- name: Deploy nginx vhost
|
- name: Deploy nginx vhost
|
||||||
ansible.builtin.template:
|
ansible.builtin.template:
|
||||||
src: nginx-vhost.conf.j2
|
src: nginx-vhost.conf.j2
|
||||||
dest: /etc/nginx/conf.d/myservice.conf
|
dest: /etc/nginx/conf.d/myservice.conf
|
||||||
validate: nginx -t
|
validate: nginx -t
|
||||||
when: myservice_nginx_enabled
|
when: myservice_nginx_enabled
|
||||||
notify: Reload nginx
|
notify: Reload nginx
|
||||||
|
|
||||||
- name: Remove nginx vhost when disabled
|
- name: Remove nginx vhost when disabled
|
||||||
ansible.builtin.file:
|
ansible.builtin.file:
|
||||||
path: /etc/nginx/conf.d/myservice.conf
|
path: /etc/nginx/conf.d/myservice.conf
|
||||||
state: absent
|
state: absent
|
||||||
when: not myservice_nginx_enabled
|
when: not myservice_nginx_enabled
|
||||||
notify: Reload nginx
|
notify: Reload nginx
|
||||||
```
|
```
|
||||||
@ -39,15 +40,17 @@ Each service role should deploy its own vhost config:
|
|||||||
Forward TCP traffic from this Nginx instance to services on other hosts using the `stream` module (layer 4 proxy).
|
Forward TCP traffic from this Nginx instance to services on other hosts using the `stream` module (layer 4 proxy).
|
||||||
|
|
||||||
**Configuration:**
|
**Configuration:**
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
nginx_forwarder:
|
nginx_forwarder:
|
||||||
"blog.hello.com":
|
"blog.hello.com":
|
||||||
forward_to: "my.host.lan"
|
forward_to: "my.host.lan"
|
||||||
http: true # Forward port 80 (default: true)
|
http: true # Forward port 80 (default: true)
|
||||||
https: true # Forward port 443 (default: true)
|
https: true # Forward port 443 (default: true)
|
||||||
```
|
```
|
||||||
|
|
||||||
**How it works:**
|
**How it works:**
|
||||||
|
|
||||||
- **Stream-based TCP proxy** (layer 4, not HTTP layer 7)
|
- **Stream-based TCP proxy** (layer 4, not HTTP layer 7)
|
||||||
- No protocol inspection - just forwards raw TCP packets
|
- No protocol inspection - just forwards raw TCP packets
|
||||||
- **HTTPS passes through encrypted** - backend host handles TLS termination
|
- **HTTPS passes through encrypted** - backend host handles TLS termination
|
||||||
@ -56,6 +59,7 @@ nginx_forwarder:
|
|||||||
**Use case:** Omega (gateway) forwards all traffic to Andromeda (internal server) that handles its own TLS certificates.
|
**Use case:** Omega (gateway) forwards all traffic to Andromeda (internal server) that handles its own TLS certificates.
|
||||||
|
|
||||||
**Important notes:**
|
**Important notes:**
|
||||||
|
|
||||||
- Stream configs deployed to `/etc/nginx/streams.d/`
|
- Stream configs deployed to `/etc/nginx/streams.d/`
|
||||||
- No HTTP logging (stream doesn't understand HTTP protocol)
|
- No HTTP logging (stream doesn't understand HTTP protocol)
|
||||||
- No X-Forwarded-For headers (transparent TCP forwarding)
|
- No X-Forwarded-For headers (transparent TCP forwarding)
|
||||||
@ -64,10 +68,12 @@ nginx_forwarder:
|
|||||||
## Logging Backends
|
## Logging Backends
|
||||||
|
|
||||||
**journald (default):**
|
**journald (default):**
|
||||||
|
|
||||||
- Logs sent to systemd journal via syslog
|
- Logs sent to systemd journal via syslog
|
||||||
- View: `journalctl -u nginx -f`
|
- View: `journalctl -u nginx -f`
|
||||||
|
|
||||||
**file:**
|
**file:**
|
||||||
|
|
||||||
- Traditional `/var/log/nginx/*.log` files
|
- Traditional `/var/log/nginx/*.log` files
|
||||||
- Automatic logrotate configuration
|
- Automatic logrotate configuration
|
||||||
|
|
||||||
|
|||||||
@ -5,6 +5,7 @@ Deploys [ntfy](https://ntfy.sh/) - a simple HTTP-based pub-sub notification serv
|
|||||||
## Security Model
|
## Security Model
|
||||||
|
|
||||||
**Secure by default:**
|
**Secure by default:**
|
||||||
|
|
||||||
- `auth-default-access: deny-all` - No anonymous access
|
- `auth-default-access: deny-all` - No anonymous access
|
||||||
- `enable-signup: false` - No public registration
|
- `enable-signup: false` - No public registration
|
||||||
- `enable-login: true` - Authentication required
|
- `enable-login: true` - Authentication required
|
||||||
@ -19,7 +20,7 @@ All notifications require authentication to send or receive.
|
|||||||
Set in inventory or vault:
|
Set in inventory or vault:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
ntfy_admin_password: "your-secure-password-here" # Min 12 chars
|
ntfy_admin_password: "your-secure-password-here" # Min 12 chars
|
||||||
```
|
```
|
||||||
|
|
||||||
### Optional Variables
|
### Optional Variables
|
||||||
@ -44,21 +45,25 @@ ntfy_nginx_hostname: ntfy.nas.local
|
|||||||
### Managing Users
|
### Managing Users
|
||||||
|
|
||||||
List users:
|
List users:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
podman exec ntfy ntfy user list
|
podman exec ntfy ntfy user list
|
||||||
```
|
```
|
||||||
|
|
||||||
Add user:
|
Add user:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
podman exec ntfy ntfy user add <username>
|
podman exec ntfy ntfy user add <username>
|
||||||
```
|
```
|
||||||
|
|
||||||
Change password:
|
Change password:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
podman exec -i ntfy ntfy user change-pass <username>
|
podman exec -i ntfy ntfy user change-pass <username>
|
||||||
```
|
```
|
||||||
|
|
||||||
Remove user:
|
Remove user:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
podman exec ntfy ntfy user remove <username>
|
podman exec ntfy ntfy user remove <username>
|
||||||
```
|
```
|
||||||
@ -66,6 +71,7 @@ podman exec ntfy ntfy user remove <username>
|
|||||||
### Managing Topic Access
|
### Managing Topic Access
|
||||||
|
|
||||||
Grant access to topic:
|
Grant access to topic:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
podman exec ntfy ntfy access <username> <topic> <permission>
|
podman exec ntfy ntfy access <username> <topic> <permission>
|
||||||
```
|
```
|
||||||
@ -73,6 +79,7 @@ podman exec ntfy ntfy access <username> <topic> <permission>
|
|||||||
Permissions: `read-write`, `read-only`, `write-only`, `deny`
|
Permissions: `read-write`, `read-only`, `write-only`, `deny`
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Allow user to publish and subscribe to "alerts" topic
|
# Allow user to publish and subscribe to "alerts" topic
|
||||||
podman exec ntfy ntfy access alice alerts read-write
|
podman exec ntfy ntfy access alice alerts read-write
|
||||||
@ -82,6 +89,7 @@ podman exec ntfy ntfy access bob monitoring write-only
|
|||||||
```
|
```
|
||||||
|
|
||||||
List access control:
|
List access control:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
podman exec ntfy ntfy access
|
podman exec ntfy ntfy access
|
||||||
```
|
```
|
||||||
@ -89,11 +97,13 @@ podman exec ntfy ntfy access
|
|||||||
### Publishing Notifications
|
### Publishing Notifications
|
||||||
|
|
||||||
Using curl with authentication:
|
Using curl with authentication:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl -u admin:password -d "Backup completed" http://localhost:8080/backups
|
curl -u admin:password -d "Backup completed" http://localhost:8080/backups
|
||||||
```
|
```
|
||||||
|
|
||||||
Using ntfy CLI:
|
Using ntfy CLI:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
ntfy publish --token <access-token> ntfy.nas.local mytopic "Hello World"
|
ntfy publish --token <access-token> ntfy.nas.local mytopic "Hello World"
|
||||||
```
|
```
|
||||||
@ -103,6 +113,7 @@ ntfy publish --token <access-token> ntfy.nas.local mytopic "Hello World"
|
|||||||
Web UI: https://ntfy.nas.local (if nginx enabled)
|
Web UI: https://ntfy.nas.local (if nginx enabled)
|
||||||
|
|
||||||
CLI:
|
CLI:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
ntfy subscribe --token <access-token> ntfy.nas.local mytopic
|
ntfy subscribe --token <access-token> ntfy.nas.local mytopic
|
||||||
```
|
```
|
||||||
|
|||||||
@ -23,9 +23,9 @@ ntfy_timezone: UTC
|
|||||||
# Server configuration
|
# Server configuration
|
||||||
ntfy_base_url: http://localhost:{{ ntfy_port }}
|
ntfy_base_url: http://localhost:{{ ntfy_port }}
|
||||||
ntfy_behind_proxy: false
|
ntfy_behind_proxy: false
|
||||||
ntfy_enable_signup: false # Disable public signup for security
|
ntfy_enable_signup: false # Disable public signup for security
|
||||||
ntfy_enable_login: true # Enable authentication
|
ntfy_enable_login: true # Enable authentication
|
||||||
ntfy_enable_reservations: true # Only authenticated users can reserve topics
|
ntfy_enable_reservations: true # Only authenticated users can reserve topics
|
||||||
|
|
||||||
# Nginx reverse proxy configuration
|
# Nginx reverse proxy configuration
|
||||||
ntfy_nginx_enabled: false
|
ntfy_nginx_enabled: false
|
||||||
|
|||||||
@ -3,11 +3,15 @@
|
|||||||
ansible.builtin.systemd:
|
ansible.builtin.systemd:
|
||||||
daemon_reload: true
|
daemon_reload: true
|
||||||
|
|
||||||
|
- name: Reload systemd user
|
||||||
|
ansible.builtin.command: "systemctl --user daemon-reload"
|
||||||
|
become: true
|
||||||
|
become_user: "{{ ansible_user }}"
|
||||||
|
|
||||||
- name: Restart ntfy
|
- name: Restart ntfy
|
||||||
ansible.builtin.systemd:
|
ansible.builtin.command: "systemctl --user restart ntfy.service"
|
||||||
name: ntfy
|
become: true
|
||||||
state: restarted
|
become_user: "{{ ansible_user }}"
|
||||||
daemon_reload: true
|
|
||||||
|
|
||||||
- name: Reload nginx
|
- name: Reload nginx
|
||||||
ansible.builtin.systemd:
|
ansible.builtin.systemd:
|
||||||
|
|||||||
@ -46,21 +46,39 @@
|
|||||||
mode: "0644"
|
mode: "0644"
|
||||||
notify: Restart ntfy
|
notify: Restart ntfy
|
||||||
|
|
||||||
- name: Create systemd service for ntfy
|
- name: Get home directory for {{ ansible_user }}
|
||||||
|
ansible.builtin.getent:
|
||||||
|
database: passwd
|
||||||
|
key: "{{ ansible_user }}"
|
||||||
|
|
||||||
|
- name: Set user home directory fact
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
user_home_dir: "{{ getent_passwd[ansible_user][4] }}"
|
||||||
|
|
||||||
|
- name: Create systemd user directory for ntfy
|
||||||
|
ansible.builtin.file:
|
||||||
|
path: "{{ user_home_dir }}/.config/systemd/user"
|
||||||
|
state: directory
|
||||||
|
owner: "{{ ansible_user }}"
|
||||||
|
group: "{{ ansible_user }}"
|
||||||
|
mode: "0755"
|
||||||
|
|
||||||
|
- name: Create systemd service for ntfy (user scope)
|
||||||
ansible.builtin.template:
|
ansible.builtin.template:
|
||||||
src: ntfy.service.j2
|
src: ntfy.service.j2
|
||||||
dest: /etc/systemd/system/ntfy.service
|
dest: "{{ user_home_dir }}/.config/systemd/user/ntfy.service"
|
||||||
owner: root
|
owner: "{{ ansible_user }}"
|
||||||
group: root
|
group: "{{ ansible_user }}"
|
||||||
mode: "0644"
|
mode: "0644"
|
||||||
notify: Reload systemd
|
notify: Reload systemd user
|
||||||
|
|
||||||
- name: Enable and start ntfy service
|
- name: Enable lingering for user {{ ansible_user }}
|
||||||
ansible.builtin.systemd:
|
ansible.builtin.command: "loginctl enable-linger {{ ansible_user }}"
|
||||||
name: ntfy
|
when: ansible_user != 'root'
|
||||||
enabled: true
|
|
||||||
state: started
|
- name: Enable and start ntfy service (user scope)
|
||||||
daemon_reload: true
|
ansible.builtin.command: "systemctl --user enable --now ntfy.service"
|
||||||
|
become_user: "{{ ansible_user }}"
|
||||||
|
|
||||||
- name: Wait for ntfy to be ready
|
- name: Wait for ntfy to be ready
|
||||||
ansible.builtin.wait_for:
|
ansible.builtin.wait_for:
|
||||||
|
|||||||
@ -1,13 +1,9 @@
|
|||||||
[Unit]
|
[Unit]
|
||||||
Description=Ntfy Notification Service
|
Description=Ntfy Notification Service
|
||||||
Requires=network-online.target
|
|
||||||
After=network-online.target
|
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
Type=oneshot
|
Type=oneshot
|
||||||
RemainAfterExit=true
|
RemainAfterExit=true
|
||||||
User={{ ansible_user }}
|
|
||||||
Group={{ ansible_user }}
|
|
||||||
WorkingDirectory={{ podman_projects_dir | default('/opt/podman') }}/ntfy
|
WorkingDirectory={{ podman_projects_dir | default('/opt/podman') }}/ntfy
|
||||||
ExecStart=/usr/bin/podman play kube --replace ntfy.yaml
|
ExecStart=/usr/bin/podman play kube --replace ntfy.yaml
|
||||||
ExecStop=/usr/bin/podman play kube --down ntfy.yaml
|
ExecStop=/usr/bin/podman play kube --down ntfy.yaml
|
||||||
@ -15,4 +11,4 @@ Restart=on-failure
|
|||||||
RestartSec=10
|
RestartSec=10
|
||||||
|
|
||||||
[Install]
|
[Install]
|
||||||
WantedBy=multi-user.target
|
WantedBy=default.target
|
||||||
|
|||||||
@ -12,10 +12,12 @@ Installs and configures Podman for container management with support for Docker
|
|||||||
## Container Logging
|
## Container Logging
|
||||||
|
|
||||||
**journald (default):**
|
**journald (default):**
|
||||||
|
|
||||||
- Logs sent to systemd journal
|
- Logs sent to systemd journal
|
||||||
- View: `journalctl CONTAINER_NAME=<name> -f`
|
- View: `journalctl CONTAINER_NAME=<name> -f`
|
||||||
|
|
||||||
**k8s-file:**
|
**k8s-file:**
|
||||||
|
|
||||||
- Logs stored as JSON files with automatic rotation
|
- Logs stored as JSON files with automatic rotation
|
||||||
- Configured via `podman_log_max_size` and `podman_log_max_files`
|
- Configured via `podman_log_max_size` and `podman_log_max_files`
|
||||||
|
|
||||||
@ -53,4 +55,4 @@ podman pod ls
|
|||||||
|
|
||||||
- [Podman Documentation](https://docs.podman.io/)
|
- [Podman Documentation](https://docs.podman.io/)
|
||||||
- [Podman Logging](https://docs.podman.io/en/latest/markdown/podman-run.1.html#log-driver-driver)
|
- [Podman Logging](https://docs.podman.io/en/latest/markdown/podman-run.1.html#log-driver-driver)
|
||||||
- [containers.conf(5)](https://github.com/containers/common/blob/main/docs/containers.conf.5.md)
|
- [containers.conf(5)](https://github.com/containers/common/blob/main/docs/containers.conf.5.md)
|
||||||
|
|||||||
@ -14,6 +14,7 @@ Installs and configures PostgreSQL as a shared database service for multiple app
|
|||||||
## Architecture Pattern
|
## Architecture Pattern
|
||||||
|
|
||||||
**Decentralized database management:**
|
**Decentralized database management:**
|
||||||
|
|
||||||
- PostgreSQL role: Installs and configures the server
|
- PostgreSQL role: Installs and configures the server
|
||||||
- Service roles: Create their own databases/users (e.g., immich, nextcloud)
|
- Service roles: Create their own databases/users (e.g., immich, nextcloud)
|
||||||
- Isolation: Each service user can only access their own database
|
- Isolation: Each service user can only access their own database
|
||||||
@ -29,9 +30,10 @@ PostgreSQL binds to `127.0.0.1` by default (secure, localhost-only).
|
|||||||
Containers can reach PostgreSQL via Pasta's `--map-host-loopback` feature, which routes container's `127.0.0.1` to the host's `127.0.0.1`.
|
Containers can reach PostgreSQL via Pasta's `--map-host-loopback` feature, which routes container's `127.0.0.1` to the host's `127.0.0.1`.
|
||||||
|
|
||||||
In docker-compose files, use:
|
In docker-compose files, use:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
extra_hosts:
|
extra_hosts:
|
||||||
- "postgres.local:127.0.0.1"
|
- "postgres.local:127.0.0.1"
|
||||||
```
|
```
|
||||||
|
|
||||||
No additional bind addresses or firewall rules needed!
|
No additional bind addresses or firewall rules needed!
|
||||||
@ -39,10 +41,12 @@ No additional bind addresses or firewall rules needed!
|
|||||||
## Logging Backends
|
## Logging Backends
|
||||||
|
|
||||||
**journald (default):**
|
**journald (default):**
|
||||||
|
|
||||||
- Logs via stderr → systemd journal
|
- Logs via stderr → systemd journal
|
||||||
- View: `journalctl -u postgresql -f`
|
- View: `journalctl -u postgresql -f`
|
||||||
|
|
||||||
**file:**
|
**file:**
|
||||||
|
|
||||||
- Logs to data directory or `/var/log/postgresql/`
|
- Logs to data directory or `/var/log/postgresql/`
|
||||||
- Automatic logrotate configuration
|
- Automatic logrotate configuration
|
||||||
|
|
||||||
@ -86,4 +90,4 @@ sudo -u postgres psql -c "SHOW effective_cache_size;"
|
|||||||
- [PostgreSQL Documentation](https://www.postgresql.org/docs/current/)
|
- [PostgreSQL Documentation](https://www.postgresql.org/docs/current/)
|
||||||
- [PostgreSQL Logging](https://www.postgresql.org/docs/current/runtime-config-logging.html)
|
- [PostgreSQL Logging](https://www.postgresql.org/docs/current/runtime-config-logging.html)
|
||||||
- [PostgreSQL Performance Tuning](https://wiki.postgresql.org/wiki/Tuning_Your_PostgreSQL_Server)
|
- [PostgreSQL Performance Tuning](https://wiki.postgresql.org/wiki/Tuning_Your_PostgreSQL_Server)
|
||||||
- [pg_hba.conf Documentation](https://www.postgresql.org/docs/current/auth-pg-hba-conf.html)
|
- [pg_hba.conf Documentation](https://www.postgresql.org/docs/current/auth-pg-hba-conf.html)
|
||||||
|
|||||||
@ -49,6 +49,7 @@ uptime_kuma_nginx_hostname: uptime.nas.local
|
|||||||
### Adding Monitors
|
### Adding Monitors
|
||||||
|
|
||||||
Web UI → Add New Monitor:
|
Web UI → Add New Monitor:
|
||||||
|
|
||||||
- **Monitor Type:** HTTP(s), TCP Port, Ping, DNS, etc.
|
- **Monitor Type:** HTTP(s), TCP Port, Ping, DNS, etc.
|
||||||
- **Friendly Name:** Display name
|
- **Friendly Name:** Display name
|
||||||
- **URL/Host:** Target to monitor
|
- **URL/Host:** Target to monitor
|
||||||
@ -59,6 +60,7 @@ Web UI → Add New Monitor:
|
|||||||
### Notification Endpoints
|
### Notification Endpoints
|
||||||
|
|
||||||
Web UI → Settings → Notifications:
|
Web UI → Settings → Notifications:
|
||||||
|
|
||||||
- Email (SMTP)
|
- Email (SMTP)
|
||||||
- Discord, Slack, Telegram
|
- Discord, Slack, Telegram
|
||||||
- ntfy (recommended for local notifications)
|
- ntfy (recommended for local notifications)
|
||||||
|
|||||||
@ -3,11 +3,15 @@
|
|||||||
ansible.builtin.systemd:
|
ansible.builtin.systemd:
|
||||||
daemon_reload: true
|
daemon_reload: true
|
||||||
|
|
||||||
|
- name: Reload systemd user
|
||||||
|
ansible.builtin.command: "systemctl --user daemon-reload"
|
||||||
|
become: true
|
||||||
|
become_user: "{{ ansible_user }}"
|
||||||
|
|
||||||
- name: Restart uptime-kuma
|
- name: Restart uptime-kuma
|
||||||
ansible.builtin.systemd:
|
ansible.builtin.command: "systemctl --user restart uptime-kuma.service"
|
||||||
name: uptime-kuma
|
become: true
|
||||||
state: restarted
|
become_user: "{{ ansible_user }}"
|
||||||
daemon_reload: true
|
|
||||||
|
|
||||||
- name: Reload nginx
|
- name: Reload nginx
|
||||||
ansible.builtin.systemd:
|
ansible.builtin.systemd:
|
||||||
|
|||||||
@ -24,21 +24,39 @@
|
|||||||
mode: "0644"
|
mode: "0644"
|
||||||
notify: Restart uptime-kuma
|
notify: Restart uptime-kuma
|
||||||
|
|
||||||
- name: Create systemd service for uptime-kuma
|
- name: Get home directory for {{ ansible_user }}
|
||||||
|
ansible.builtin.getent:
|
||||||
|
database: passwd
|
||||||
|
key: "{{ ansible_user }}"
|
||||||
|
|
||||||
|
- name: Set user home directory fact
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
user_home_dir: "{{ getent_passwd[ansible_user][4] }}"
|
||||||
|
|
||||||
|
- name: Create systemd user directory for uptime-kuma
|
||||||
|
ansible.builtin.file:
|
||||||
|
path: "{{ user_home_dir }}/.config/systemd/user"
|
||||||
|
state: directory
|
||||||
|
owner: "{{ ansible_user }}"
|
||||||
|
group: "{{ ansible_user }}"
|
||||||
|
mode: "0755"
|
||||||
|
|
||||||
|
- name: Create systemd service for uptime-kuma (user scope)
|
||||||
ansible.builtin.template:
|
ansible.builtin.template:
|
||||||
src: uptime-kuma.service.j2
|
src: uptime-kuma.service.j2
|
||||||
dest: /etc/systemd/system/uptime-kuma.service
|
dest: "{{ user_home_dir }}/.config/systemd/user/uptime-kuma.service"
|
||||||
owner: root
|
owner: "{{ ansible_user }}"
|
||||||
group: root
|
group: "{{ ansible_user }}"
|
||||||
mode: "0644"
|
mode: "0644"
|
||||||
notify: Reload systemd
|
notify: Reload systemd user
|
||||||
|
|
||||||
- name: Enable and start uptime-kuma service
|
- name: Enable lingering for user {{ ansible_user }}
|
||||||
ansible.builtin.systemd:
|
ansible.builtin.command: "loginctl enable-linger {{ ansible_user }}"
|
||||||
name: uptime-kuma
|
when: ansible_user != 'root'
|
||||||
enabled: true
|
|
||||||
state: started
|
- name: Enable and start uptime-kuma service (user scope)
|
||||||
daemon_reload: true
|
ansible.builtin.command: "systemctl --user enable --now uptime-kuma.service"
|
||||||
|
become_user: "{{ ansible_user }}"
|
||||||
|
|
||||||
- name: Deploy nginx vhost configuration for uptime-kuma
|
- name: Deploy nginx vhost configuration for uptime-kuma
|
||||||
ansible.builtin.template:
|
ansible.builtin.template:
|
||||||
|
|||||||
@ -1,13 +1,9 @@
|
|||||||
[Unit]
|
[Unit]
|
||||||
Description=Uptime Kuma Monitoring Service
|
Description=Uptime Kuma Monitoring Service
|
||||||
Requires=network-online.target
|
|
||||||
After=network-online.target
|
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
Type=oneshot
|
Type=oneshot
|
||||||
RemainAfterExit=true
|
RemainAfterExit=true
|
||||||
User={{ ansible_user }}
|
|
||||||
Group={{ ansible_user }}
|
|
||||||
WorkingDirectory={{ podman_projects_dir | default('/opt/podman') }}/uptime-kuma
|
WorkingDirectory={{ podman_projects_dir | default('/opt/podman') }}/uptime-kuma
|
||||||
ExecStart=/usr/bin/podman play kube --replace uptime-kuma.yaml
|
ExecStart=/usr/bin/podman play kube --replace uptime-kuma.yaml
|
||||||
ExecStop=/usr/bin/podman play kube --down uptime-kuma.yaml
|
ExecStop=/usr/bin/podman play kube --down uptime-kuma.yaml
|
||||||
@ -15,4 +11,4 @@ Restart=on-failure
|
|||||||
RestartSec=10
|
RestartSec=10
|
||||||
|
|
||||||
[Install]
|
[Install]
|
||||||
WantedBy=multi-user.target
|
WantedBy=default.target
|
||||||
|
|||||||
@ -9,6 +9,7 @@ The role also performs required kernel tuning for optimal Valkey performance.
|
|||||||
Valkey is a high-performance key/value datastore and a drop-in replacement for Redis. It was created as a community-driven fork after Redis changed its license from BSD to proprietary licenses (RSALv2 and SSPLv1) in March 2024.
|
Valkey is a high-performance key/value datastore and a drop-in replacement for Redis. It was created as a community-driven fork after Redis changed its license from BSD to proprietary licenses (RSALv2 and SSPLv1) in March 2024.
|
||||||
|
|
||||||
**Key points:**
|
**Key points:**
|
||||||
|
|
||||||
- Valkey is 100% API-compatible with Redis
|
- Valkey is 100% API-compatible with Redis
|
||||||
- Backed by the Linux Foundation
|
- Backed by the Linux Foundation
|
||||||
- Uses permissive open-source license (BSD 3-Clause)
|
- Uses permissive open-source license (BSD 3-Clause)
|
||||||
@ -16,6 +17,7 @@ Valkey is a high-performance key/value datastore and a drop-in replacement for R
|
|||||||
- Same commands, same protocol, same performance
|
- Same commands, same protocol, same performance
|
||||||
|
|
||||||
**Distribution support:**
|
**Distribution support:**
|
||||||
|
|
||||||
- **Arch Linux**: Installs Valkey (redis package replaced in April 2024)
|
- **Arch Linux**: Installs Valkey (redis package replaced in April 2024)
|
||||||
- **Debian/Ubuntu**: Installs Valkey from official repositories
|
- **Debian/Ubuntu**: Installs Valkey from official repositories
|
||||||
|
|
||||||
@ -58,9 +60,10 @@ Valkey binds to `127.0.0.1` by default (secure, localhost-only).
|
|||||||
Containers can reach Valkey via Pasta's `--map-host-loopback` feature, which routes container's `127.0.0.1` to the host's `127.0.0.1`.
|
Containers can reach Valkey via Pasta's `--map-host-loopback` feature, which routes container's `127.0.0.1` to the host's `127.0.0.1`.
|
||||||
|
|
||||||
In docker-compose files, use:
|
In docker-compose files, use:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
extra_hosts:
|
extra_hosts:
|
||||||
- "host.containers.internal:127.0.0.1"
|
- "host.containers.internal:127.0.0.1"
|
||||||
```
|
```
|
||||||
|
|
||||||
No additional bind addresses needed!
|
No additional bind addresses needed!
|
||||||
@ -78,8 +81,8 @@ None.
|
|||||||
- hosts: servers
|
- hosts: servers
|
||||||
become: true
|
become: true
|
||||||
roles:
|
roles:
|
||||||
- role: valkey
|
- role: valkey
|
||||||
- role: immich # Will connect to system Valkey
|
- role: immich # Will connect to system Valkey
|
||||||
```
|
```
|
||||||
|
|
||||||
### Custom Configuration with ACL Users
|
### Custom Configuration with ACL Users
|
||||||
@ -89,25 +92,26 @@ None.
|
|||||||
- hosts: servers
|
- hosts: servers
|
||||||
become: true
|
become: true
|
||||||
roles:
|
roles:
|
||||||
- role: valkey
|
- role: valkey
|
||||||
vars:
|
vars:
|
||||||
valkey_admin_password: "{{ vault_valkey_password }}"
|
valkey_admin_password: "{{ vault_valkey_password }}"
|
||||||
valkey_maxmemory: 512mb
|
valkey_maxmemory: 512mb
|
||||||
valkey_maxmemory_policy: volatile-lru
|
valkey_maxmemory_policy: volatile-lru
|
||||||
valkey_acl_users:
|
valkey_acl_users:
|
||||||
- username: immich
|
- username: immich
|
||||||
password: "{{ immich_valkey_password }}"
|
password: "{{ immich_valkey_password }}"
|
||||||
keypattern: "immich_bull* immich_channel*"
|
keypattern: "immich_bull* immich_channel*"
|
||||||
commands: "&* -@dangerous +@read +@write +@pubsub +select +auth +ping +info +eval +evalsha"
|
commands: "&* -@dangerous +@read +@write +@pubsub +select +auth +ping +info +eval +evalsha"
|
||||||
- username: nextcloud
|
- username: nextcloud
|
||||||
password: "{{ nextcloud_valkey_password }}"
|
password: "{{ nextcloud_valkey_password }}"
|
||||||
keypattern: "nextcloud*"
|
keypattern: "nextcloud*"
|
||||||
commands: "+@read +@write -@dangerous +auth +ping +info"
|
commands: "+@read +@write -@dangerous +auth +ping +info"
|
||||||
```
|
```
|
||||||
|
|
||||||
## How Services Connect
|
## How Services Connect
|
||||||
|
|
||||||
Services running on the same host can connect to Valkey at:
|
Services running on the same host can connect to Valkey at:
|
||||||
|
|
||||||
- **Host**: `localhost` or `127.0.0.1`
|
- **Host**: `localhost` or `127.0.0.1`
|
||||||
- **Port**: `6379` (default)
|
- **Port**: `6379` (default)
|
||||||
|
|
||||||
@ -116,6 +120,7 @@ Services running on the same host can connect to Valkey at:
|
|||||||
Containers need special handling to reach the host's Valkey:
|
Containers need special handling to reach the host's Valkey:
|
||||||
|
|
||||||
**Use `host.containers.internal`:**
|
**Use `host.containers.internal`:**
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
REDIS_HOSTNAME: host.containers.internal
|
REDIS_HOSTNAME: host.containers.internal
|
||||||
REDIS_PORT: 6379
|
REDIS_PORT: 6379
|
||||||
@ -135,6 +140,7 @@ This special DNS name resolves to the host machine from inside containers.
|
|||||||
### ACL-Based Authentication
|
### ACL-Based Authentication
|
||||||
|
|
||||||
This role uses Valkey's ACL (Access Control List) system for fine-grained security. Each service gets:
|
This role uses Valkey's ACL (Access Control List) system for fine-grained security. Each service gets:
|
||||||
|
|
||||||
- **Dedicated credentials**: Unique username and password
|
- **Dedicated credentials**: Unique username and password
|
||||||
- **Key pattern restrictions**: Can only access specific key patterns
|
- **Key pattern restrictions**: Can only access specific key patterns
|
||||||
- **Command restrictions**: Limited to required commands only
|
- **Command restrictions**: Limited to required commands only
|
||||||
@ -149,25 +155,26 @@ Define ACL users in your inventory or host_vars:
|
|||||||
valkey_admin_password: "your-strong-admin-password"
|
valkey_admin_password: "your-strong-admin-password"
|
||||||
|
|
||||||
valkey_acl_users:
|
valkey_acl_users:
|
||||||
- username: immich
|
- username: immich
|
||||||
password: "{{ immich_valkey_password }}"
|
password: "{{ immich_valkey_password }}"
|
||||||
keypattern: "immich_bull* immich_channel*"
|
keypattern: "immich_bull* immich_channel*"
|
||||||
commands: "&* -@dangerous +@read +@write +@pubsub +select +auth +ping +info +eval +evalsha"
|
commands: "&* -@dangerous +@read +@write +@pubsub +select +auth +ping +info +eval +evalsha"
|
||||||
|
|
||||||
- username: nextcloud
|
- username: nextcloud
|
||||||
password: "{{ nextcloud_valkey_password }}"
|
password: "{{ nextcloud_valkey_password }}"
|
||||||
keypattern: "nextcloud*"
|
keypattern: "nextcloud*"
|
||||||
commands: "+@read +@write -@dangerous +auth +ping +info"
|
commands: "+@read +@write -@dangerous +auth +ping +info"
|
||||||
|
|
||||||
- username: gitea
|
- username: gitea
|
||||||
password: "{{ gitea_valkey_password }}"
|
password: "{{ gitea_valkey_password }}"
|
||||||
keypattern: "gitea*"
|
keypattern: "gitea*"
|
||||||
commands: "+@read +@write -@dangerous +auth +ping +info +select"
|
commands: "+@read +@write -@dangerous +auth +ping +info +select"
|
||||||
```
|
```
|
||||||
|
|
||||||
### ACL Configuration Guide
|
### ACL Configuration Guide
|
||||||
|
|
||||||
**Key Pattern (`keypattern`):**
|
**Key Pattern (`keypattern`):**
|
||||||
|
|
||||||
- Single pattern: `"myservice*"` - matches keys starting with `myservice`
|
- Single pattern: `"myservice*"` - matches keys starting with `myservice`
|
||||||
- Multiple patterns: `"pattern1* pattern2*"` - space-separated (automatically converted to `~pattern1* ~pattern2*` in ACL file)
|
- Multiple patterns: `"pattern1* pattern2*"` - space-separated (automatically converted to `~pattern1* ~pattern2*` in ACL file)
|
||||||
- All keys: `"*"` - not recommended for security
|
- All keys: `"*"` - not recommended for security
|
||||||
@ -179,17 +186,21 @@ valkey_acl_users:
|
|||||||
The role automatically configures kernel parameters required by Valkey (see `tasks/kernel-tuning.yml`):
|
The role automatically configures kernel parameters required by Valkey (see `tasks/kernel-tuning.yml`):
|
||||||
|
|
||||||
**1. Memory Overcommit:**
|
**1. Memory Overcommit:**
|
||||||
|
|
||||||
```
|
```
|
||||||
vm.overcommit_memory = 1
|
vm.overcommit_memory = 1
|
||||||
```
|
```
|
||||||
|
|
||||||
- Required for background saves and replication
|
- Required for background saves and replication
|
||||||
- Configured via `/etc/sysctl.conf`
|
- Configured via `/etc/sysctl.conf`
|
||||||
- Applied immediately and persists across reboots
|
- Applied immediately and persists across reboots
|
||||||
|
|
||||||
**2. Transparent Huge Pages (THP):**
|
**2. Transparent Huge Pages (THP):**
|
||||||
|
|
||||||
```
|
```
|
||||||
transparent_hugepage=madvise
|
transparent_hugepage=madvise
|
||||||
```
|
```
|
||||||
|
|
||||||
- Reduces latency and memory usage issues
|
- Reduces latency and memory usage issues
|
||||||
- Safely appended to existing GRUB kernel parameters (does not overwrite)
|
- Safely appended to existing GRUB kernel parameters (does not overwrite)
|
||||||
- Only adds parameter if `transparent_hugepage=` is not already present
|
- Only adds parameter if `transparent_hugepage=` is not already present
|
||||||
@ -202,6 +213,7 @@ These settings are required to eliminate Valkey startup warnings and ensure opti
|
|||||||
**Note:** The role preserves existing GRUB parameters. If you have `GRUB_CMDLINE_LINUX_DEFAULT="loglevel=3 quiet"`, it will become `GRUB_CMDLINE_LINUX_DEFAULT="loglevel=3 quiet transparent_hugepage=madvise"`.
|
**Note:** The role preserves existing GRUB parameters. If you have `GRUB_CMDLINE_LINUX_DEFAULT="loglevel=3 quiet"`, it will become `GRUB_CMDLINE_LINUX_DEFAULT="loglevel=3 quiet transparent_hugepage=madvise"`.
|
||||||
|
|
||||||
**Commands (`commands`):**
|
**Commands (`commands`):**
|
||||||
|
|
||||||
- `&*` - Allow all pub/sub channels (required for job queues like BullMQ)
|
- `&*` - Allow all pub/sub channels (required for job queues like BullMQ)
|
||||||
- `+allchannels` - Alternative to `&*`
|
- `+allchannels` - Alternative to `&*`
|
||||||
- `+@read` - Allow all read commands (GET, MGET, etc.)
|
- `+@read` - Allow all read commands (GET, MGET, etc.)
|
||||||
@ -213,14 +225,15 @@ These settings are required to eliminate Valkey startup warnings and ensure opti
|
|||||||
|
|
||||||
**Common Command Sets:**
|
**Common Command Sets:**
|
||||||
|
|
||||||
| Service Type | Recommended Commands |
|
| Service Type | Recommended Commands |
|
||||||
|-------------|---------------------|
|
| ---------------------- | --------------------------------------------------------------------------------- |
|
||||||
| **Simple cache** | `+@read +@write -@dangerous +auth +ping +info` |
|
| **Simple cache** | `+@read +@write -@dangerous +auth +ping +info` |
|
||||||
| **Session store** | `+@read +@write -@dangerous +auth +ping +info +select` |
|
| **Session store** | `+@read +@write -@dangerous +auth +ping +info +select` |
|
||||||
| **Job queue (BullMQ)** | `&* -@dangerous +@read +@write +@pubsub +select +auth +ping +info +eval +evalsha` |
|
| **Job queue (BullMQ)** | `&* -@dangerous +@read +@write +@pubsub +select +auth +ping +info +eval +evalsha` |
|
||||||
| **Pub/sub** | `+@pubsub +@read +@write -@dangerous +auth +ping +info` |
|
| **Pub/sub** | `+@pubsub +@read +@write -@dangerous +auth +ping +info` |
|
||||||
|
|
||||||
**Security Best Practices:**
|
**Security Best Practices:**
|
||||||
|
|
||||||
- Always include `-@dangerous` to prevent accidental data loss
|
- Always include `-@dangerous` to prevent accidental data loss
|
||||||
- Use specific key patterns to isolate services
|
- Use specific key patterns to isolate services
|
||||||
- Only grant `+eval` and `+evalsha` if required (job queues)
|
- Only grant `+eval` and `+evalsha` if required (job queues)
|
||||||
@ -244,12 +257,12 @@ Add encrypted values to your inventory:
|
|||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
valkey_admin_password: !vault |
|
valkey_admin_password: !vault |
|
||||||
$ANSIBLE_VAULT;1.1;AES256
|
$ANSIBLE_VAULT;1.1;AES256
|
||||||
...
|
...
|
||||||
|
|
||||||
immich_valkey_password: !vault |
|
immich_valkey_password: !vault |
|
||||||
$ANSIBLE_VAULT;1.1;AES256
|
$ANSIBLE_VAULT;1.1;AES256
|
||||||
...
|
...
|
||||||
```
|
```
|
||||||
|
|
||||||
## Service Management
|
## Service Management
|
||||||
@ -275,6 +288,7 @@ valkey-cli # Also available on Arch Linux
|
|||||||
## Persistence
|
## Persistence
|
||||||
|
|
||||||
Valkey is configured with RDB persistence:
|
Valkey is configured with RDB persistence:
|
||||||
|
|
||||||
- Save after 900 seconds if at least 1 key changed
|
- Save after 900 seconds if at least 1 key changed
|
||||||
- Save after 300 seconds if at least 10 keys changed
|
- Save after 300 seconds if at least 10 keys changed
|
||||||
- Save after 60 seconds if at least 10000 keys changed
|
- Save after 60 seconds if at least 10000 keys changed
|
||||||
@ -293,6 +307,7 @@ When `valkey_maxmemory` is reached, Valkey will behave based on `valkey_maxmemor
|
|||||||
|
|
||||||
**Important for Immich and BullMQ:**
|
**Important for Immich and BullMQ:**
|
||||||
Services using BullMQ for job queues (like Immich) require `noeviction` policy. Evicting job queue data can cause:
|
Services using BullMQ for job queues (like Immich) require `noeviction` policy. Evicting job queue data can cause:
|
||||||
|
|
||||||
- Lost background tasks
|
- Lost background tasks
|
||||||
- Failed job processing
|
- Failed job processing
|
||||||
- Data corruption
|
- Data corruption
|
||||||
@ -302,6 +317,7 @@ Only use eviction policies (`allkeys-lru`, etc.) for pure caching use cases wher
|
|||||||
## Monitoring
|
## Monitoring
|
||||||
|
|
||||||
Check Valkey info (authenticate as admin):
|
Check Valkey info (authenticate as admin):
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
redis-cli
|
redis-cli
|
||||||
AUTH default <valkey_admin_password>
|
AUTH default <valkey_admin_password>
|
||||||
@ -311,6 +327,7 @@ INFO stats
|
|||||||
```
|
```
|
||||||
|
|
||||||
Check connected clients:
|
Check connected clients:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
redis-cli
|
redis-cli
|
||||||
AUTH default <valkey_admin_password>
|
AUTH default <valkey_admin_password>
|
||||||
@ -318,6 +335,7 @@ CLIENT LIST
|
|||||||
```
|
```
|
||||||
|
|
||||||
View ACL configuration:
|
View ACL configuration:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
redis-cli
|
redis-cli
|
||||||
AUTH default <valkey_admin_password>
|
AUTH default <valkey_admin_password>
|
||||||
@ -328,6 +346,7 @@ ACL CAT # List all command categories
|
|||||||
```
|
```
|
||||||
|
|
||||||
Check generated ACL file:
|
Check generated ACL file:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cat /etc/valkey/users.acl
|
cat /etc/valkey/users.acl
|
||||||
# Example output:
|
# Example output:
|
||||||
@ -339,12 +358,14 @@ cat /etc/valkey/users.acl
|
|||||||
## Troubleshooting
|
## Troubleshooting
|
||||||
|
|
||||||
### Check if Valkey is running
|
### Check if Valkey is running
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
systemctl status valkey # Arch Linux
|
systemctl status valkey # Arch Linux
|
||||||
systemctl status valkey-server # Debian/Ubuntu
|
systemctl status valkey-server # Debian/Ubuntu
|
||||||
```
|
```
|
||||||
|
|
||||||
### Test admin connection
|
### Test admin connection
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# With authentication (default user)
|
# With authentication (default user)
|
||||||
redis-cli
|
redis-cli
|
||||||
@ -354,6 +375,7 @@ PING
|
|||||||
```
|
```
|
||||||
|
|
||||||
### Test service user connection
|
### Test service user connection
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Test Immich user
|
# Test Immich user
|
||||||
redis-cli
|
redis-cli
|
||||||
@ -368,6 +390,7 @@ FLUSHDB
|
|||||||
```
|
```
|
||||||
|
|
||||||
### View ACL configuration
|
### View ACL configuration
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Check ACL file
|
# Check ACL file
|
||||||
cat /etc/valkey/users.acl
|
cat /etc/valkey/users.acl
|
||||||
@ -380,6 +403,7 @@ ACL GETUSER immich
|
|||||||
```
|
```
|
||||||
|
|
||||||
### Debug permission issues
|
### Debug permission issues
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Monitor all commands (useful for debugging)
|
# Monitor all commands (useful for debugging)
|
||||||
redis-cli
|
redis-cli
|
||||||
@ -390,6 +414,7 @@ MONITOR
|
|||||||
```
|
```
|
||||||
|
|
||||||
### View configuration
|
### View configuration
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
redis-cli
|
redis-cli
|
||||||
AUTH default <valkey_admin_password>
|
AUTH default <valkey_admin_password>
|
||||||
@ -397,6 +422,7 @@ CONFIG GET "*"
|
|||||||
```
|
```
|
||||||
|
|
||||||
### Check memory usage
|
### Check memory usage
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
redis-cli
|
redis-cli
|
||||||
AUTH default <valkey_admin_password>
|
AUTH default <valkey_admin_password>
|
||||||
@ -406,25 +432,30 @@ INFO memory
|
|||||||
### Common ACL Errors
|
### Common ACL Errors
|
||||||
|
|
||||||
**"NOAUTH Authentication required"**
|
**"NOAUTH Authentication required"**
|
||||||
|
|
||||||
- Client didn't authenticate
|
- Client didn't authenticate
|
||||||
- Service needs to set `REDIS_USERNAME` and `REDIS_PASSWORD`
|
- Service needs to set `REDIS_USERNAME` and `REDIS_PASSWORD`
|
||||||
|
|
||||||
**"WRONGPASS invalid username-password pair"**
|
**"WRONGPASS invalid username-password pair"**
|
||||||
|
|
||||||
- Incorrect username or password
|
- Incorrect username or password
|
||||||
- Verify ACL user exists: `ACL GETUSER username`
|
- Verify ACL user exists: `ACL GETUSER username`
|
||||||
- Check password in inventory matches service configuration
|
- Check password in inventory matches service configuration
|
||||||
|
|
||||||
**"NOPERM No permissions to run the 'command' command"**
|
**"NOPERM No permissions to run the 'command' command"**
|
||||||
|
|
||||||
- Command not allowed in ACL
|
- Command not allowed in ACL
|
||||||
- Check ACL: `ACL GETUSER username`
|
- Check ACL: `ACL GETUSER username`
|
||||||
- Add required command to `commands:` in inventory
|
- Add required command to `commands:` in inventory
|
||||||
|
|
||||||
**"NOPERM No permissions to access a key"**
|
**"NOPERM No permissions to access a key"**
|
||||||
|
|
||||||
- Key doesn't match allowed patterns
|
- Key doesn't match allowed patterns
|
||||||
- Check key pattern: `ACL GETUSER username`
|
- Check key pattern: `ACL GETUSER username`
|
||||||
- Verify service is using correct key prefix
|
- Verify service is using correct key prefix
|
||||||
|
|
||||||
**"NOPERM No permissions to access a channel"**
|
**"NOPERM No permissions to access a channel"**
|
||||||
|
|
||||||
- Pub/sub channel not allowed
|
- Pub/sub channel not allowed
|
||||||
- Add `&*` or `+allchannels` to ACL commands
|
- Add `&*` or `+allchannels` to ACL commands
|
||||||
- Required for BullMQ and other job queues
|
- Required for BullMQ and other job queues
|
||||||
@ -434,18 +465,20 @@ INFO memory
|
|||||||
For high-traffic services, consider:
|
For high-traffic services, consider:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
valkey_maxmemory: 1gb # Increase memory limit
|
valkey_maxmemory: 1gb # Increase memory limit
|
||||||
valkey_maxmemory_policy: noeviction # No eviction (for job queues)
|
valkey_maxmemory_policy: noeviction # No eviction (for job queues)
|
||||||
# Or for pure caching:
|
# Or for pure caching:
|
||||||
# valkey_maxmemory_policy: allkeys-lru # LRU eviction
|
# valkey_maxmemory_policy: allkeys-lru # LRU eviction
|
||||||
```
|
```
|
||||||
|
|
||||||
**Kernel Tuning (automatically configured):**
|
**Kernel Tuning (automatically configured):**
|
||||||
The role automatically sets optimal kernel parameters:
|
The role automatically sets optimal kernel parameters:
|
||||||
|
|
||||||
- Memory overcommit enabled (`vm.overcommit_memory=1`)
|
- Memory overcommit enabled (`vm.overcommit_memory=1`)
|
||||||
- Transparent Huge Pages set to `madvise`
|
- Transparent Huge Pages set to `madvise`
|
||||||
|
|
||||||
To verify kernel settings:
|
To verify kernel settings:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Check memory overcommit
|
# Check memory overcommit
|
||||||
sysctl vm.overcommit_memory
|
sysctl vm.overcommit_memory
|
||||||
@ -469,23 +502,27 @@ Created for managing shared Valkey instances in NAS/homelab environments.
|
|||||||
This role implements **defense-in-depth** with three isolation layers:
|
This role implements **defense-in-depth** with three isolation layers:
|
||||||
|
|
||||||
### 1. ACL Users (Primary Isolation)
|
### 1. ACL Users (Primary Isolation)
|
||||||
|
|
||||||
Each service gets its own user with restricted permissions:
|
Each service gets its own user with restricted permissions:
|
||||||
|
|
||||||
- Unique credentials
|
- Unique credentials
|
||||||
- Key pattern restrictions
|
- Key pattern restrictions
|
||||||
- Command restrictions
|
- Command restrictions
|
||||||
|
|
||||||
### 2. Database Numbers (Secondary Isolation)
|
### 2. Database Numbers (Secondary Isolation)
|
||||||
|
|
||||||
Valkey provides 16 logical databases (0-15) for additional isolation:
|
Valkey provides 16 logical databases (0-15) for additional isolation:
|
||||||
|
|
||||||
| Service | Database | Key Pattern | ACL User |
|
| Service | Database | Key Pattern | ACL User |
|
||||||
|---------|----------|-------------|----------|
|
| --------- | -------- | -------------------------------- | ----------- |
|
||||||
| Immich | 0 | `immich_bull*` `immich_channel*` | `immich` |
|
| Immich | 0 | `immich_bull*` `immich_channel*` | `immich` |
|
||||||
| Nextcloud | 1 | `nextcloud*` | `nextcloud` |
|
| Nextcloud | 1 | `nextcloud*` | `nextcloud` |
|
||||||
| Gitea | 2 | `gitea*` | `gitea` |
|
| Gitea | 2 | `gitea*` | `gitea` |
|
||||||
| Grafana | 3 | `grafana*` | `grafana` |
|
| Grafana | 3 | `grafana*` | `grafana` |
|
||||||
| Custom | 4-15 | Custom | Custom |
|
| Custom | 4-15 | Custom | Custom |
|
||||||
|
|
||||||
### 3. Key Prefixes (Tertiary Isolation)
|
### 3. Key Prefixes (Tertiary Isolation)
|
||||||
|
|
||||||
Services use unique key prefixes enforced by ACL patterns.
|
Services use unique key prefixes enforced by ACL patterns.
|
||||||
|
|
||||||
### Testing Isolation
|
### Testing Isolation
|
||||||
@ -517,23 +554,23 @@ FLUSHDB
|
|||||||
valkey_admin_password: "{{ vault_valkey_admin_password }}"
|
valkey_admin_password: "{{ vault_valkey_admin_password }}"
|
||||||
|
|
||||||
valkey_acl_users:
|
valkey_acl_users:
|
||||||
# Immich - Photo management (needs BullMQ job queue)
|
# Immich - Photo management (needs BullMQ job queue)
|
||||||
- username: immich
|
- username: immich
|
||||||
password: "{{ vault_immich_valkey_password }}"
|
password: "{{ vault_immich_valkey_password }}"
|
||||||
keypattern: "immich_bull* immich_channel*"
|
keypattern: "immich_bull* immich_channel*"
|
||||||
commands: "&* -@dangerous +@read +@write +@pubsub +select +auth +ping +info +eval +evalsha"
|
commands: "&* -@dangerous +@read +@write +@pubsub +select +auth +ping +info +eval +evalsha"
|
||||||
|
|
||||||
# Nextcloud - Simple caching
|
# Nextcloud - Simple caching
|
||||||
- username: nextcloud
|
- username: nextcloud
|
||||||
password: "{{ vault_nextcloud_valkey_password }}"
|
password: "{{ vault_nextcloud_valkey_password }}"
|
||||||
keypattern: "nextcloud*"
|
keypattern: "nextcloud*"
|
||||||
commands: "+@read +@write -@dangerous +auth +ping +info +select"
|
commands: "+@read +@write -@dangerous +auth +ping +info +select"
|
||||||
|
|
||||||
# Gitea - Session store
|
# Gitea - Session store
|
||||||
- username: gitea
|
- username: gitea
|
||||||
password: "{{ vault_gitea_valkey_password }}"
|
password: "{{ vault_gitea_valkey_password }}"
|
||||||
keypattern: "gitea*"
|
keypattern: "gitea*"
|
||||||
commands: "+@read +@write -@dangerous +auth +ping +info +select"
|
commands: "+@read +@write -@dangerous +auth +ping +info +select"
|
||||||
|
|
||||||
# Service variables
|
# Service variables
|
||||||
immich_valkey_db: 0
|
immich_valkey_db: 0
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user