Initialise repository structure and placeholder files. Generic functionality works i guess, need to come up with a better solution for compose templates

This commit is contained in:
boris committed 2026-07-08 10:29:51 +01:00
commit 53695f4fff
411 files changed
+7817

No files matched your search

+4
View File
@@ -0,0 +1,4 @@
# alloy
Deploys Grafana Alloy with Docker Compose.
+17
View File
@@ -0,0 +1,17 @@
---
alloy_name: alloy
alloy_dir: /srv/docker/alloy
alloy_image: grafana/alloy:latest
alloy_routing_mode: host
alloy_command: "run --server.http.listen-addr=0.0.0.0:12345 /etc/alloy/config.alloy"
alloy_env: {}
alloy_directories:
- "{{ alloy_dir }}"
- /srv/config/alloy
alloy_volumes:
- /srv/config/alloy:/etc/alloy
- /var/log:/var/log:ro
- /var/run/docker.sock:/var/run/docker.sock:ro
alloy_ports:
- { host: 12345, container: 12345, protocol: tcp }
+1
View File
@@ -0,0 +1 @@
+1
View File
@@ -0,0 +1 @@
+17
View File
@@ -0,0 +1,17 @@
---
- name: Deploy Grafana Alloy
ansible.builtin.include_role:
name: docker_app
vars:
docker_app_env_template_src: "{{ playbook_dir }}/../roles/alloy/templates/env.j2"
docker_app_compose_template_src: "{{ playbook_dir }}/../roles/alloy/templates/compose.yaml.j2"
docker_app_name: "{{ alloy_name }}"
docker_app_dir: "{{ alloy_dir }}"
docker_app_image: "{{ alloy_image }}"
docker_app_command: "{{ alloy_command }}"
docker_app_env: "{{ alloy_env }}"
docker_app_directories: "{{ alloy_directories }}"
docker_app_volumes: "{{ alloy_volumes }}"
docker_app_ports: "{{ alloy_ports }}"
docker_app_routing_mode: "{{ alloy_routing_mode }}"
+1
View File
@@ -0,0 +1 @@
+90
View File
@@ -0,0 +1,90 @@
services:
{{ docker_app_name }}:
image: {{ docker_app_image }}
restart: {{ docker_app_restart }}
{% if docker_app_command | length > 0 %}
command: {{ docker_app_command | to_json }}
{% endif %}
{% if docker_app_user | length > 0 %}
user: "{{ docker_app_user }}"
{% endif %}
{% if docker_app_network_mode | length > 0 %}
network_mode: {{ docker_app_network_mode }}
{% endif %}
{% if docker_app_privileged | bool %}
privileged: true
{% endif %}
{% if docker_app_env | length > 0 %}
env_file: .env
{% endif %}
{% if docker_app_routing_mode == "host" and docker_app_ports | length > 0 %}
ports:
{% for port in docker_app_ports %}
- "{{ port.bind | default('0.0.0.0') }}:{{ port.host | default(port.container) }}:{{ port.container }}{% if port.protocol | default('tcp') != 'tcp' %}/{{ port.protocol }}{% endif %}"
{% endfor %}
{% endif %}
{% if docker_app_routing_mode == "traefik" and docker_app_service_port %}
# internal service port: {{ docker_app_service_port }}; not published on the Docker host
labels:
- traefik.enable=true
- traefik.http.routers.{{ docker_app_name }}.rule={{ docker_app_traefik_rule }}
- traefik.http.routers.{{ docker_app_name }}.entrypoints={{ docker_app_traefik_entrypoint }}
- traefik.http.routers.{{ docker_app_name }}.tls={{ docker_app_traefik_tls | bool | ternary('true', 'false') }}
{% if docker_app_traefik_middlewares | length > 0 %}
- traefik.http.routers.{{ docker_app_name }}.middlewares={{ docker_app_traefik_middlewares | join(',') }}
{% endif %}
- traefik.http.services.{{ docker_app_name }}.loadbalancer.server.port={{ docker_app_service_port }}
{% for key, value in docker_app_labels.items() %}
- {{ key }}={{ value }}
{% endfor %}
{% elif docker_app_labels | length > 0 %}
labels:
{% for key, value in docker_app_labels.items() %}
- {{ key }}={{ value }}
{% endfor %}
{% endif %}
{% if docker_app_volumes | length > 0 %}
volumes:
{% for volume in docker_app_volumes %}
- {{ volume }}
{% endfor %}
{% endif %}
{% if docker_app_devices | length > 0 %}
devices:
{% for device in docker_app_devices %}
- {{ device }}
{% endfor %}
{% endif %}
{% if docker_app_cap_add | length > 0 %}
cap_add:
{% for capability in docker_app_cap_add %}
- {{ capability }}
{% endfor %}
{% endif %}
{% if docker_app_group_add | length > 0 %}
group_add:
{% for group in docker_app_group_add %}
- "{{ group }}"
{% endfor %}
{% endif %}
{% if docker_app_extra_hosts | length > 0 %}
extra_hosts:
{% for host in docker_app_extra_hosts %}
- {{ host }}
{% endfor %}
{% endif %}
{% if docker_app_depends_on | length > 0 %}
depends_on:
{% for dependency in docker_app_depends_on %}
- {{ dependency }}
{% endfor %}
{% endif %}
{% if docker_app_routing_mode == "traefik" and docker_app_network_mode | length == 0 %}
networks:
- {{ docker_app_traefik_network }}
{% endif %}
{% if docker_app_routing_mode == "traefik" and docker_app_network_mode | length == 0 %}
networks:
{{ docker_app_traefik_network }}:
external: true
{% endif %}
+4
View File
@@ -0,0 +1,4 @@
{% for key, value in docker_app_env.items() %}
{{ key }}={{ value }}
{% endfor %}
+7
View File
@@ -0,0 +1,7 @@
# arr
Deploys a LinuxServer.io Arr stack with Prowlarr, Sonarr, Radarr, Lidarr, and
Bazarr. Override `arr_services` to add or remove services.
Tags: `arr`, `config`, `compose`, `firewall`.
+34
View File
@@ -0,0 +1,34 @@
---
arr_dir: /srv/docker/arr
arr_puid: "1000"
arr_pgid: "1000"
arr_timezone: "{{ nbops_timezone }}"
arr_config_root: /srv/config/arr
arr_media_root: /srv/media
arr_downloads_root: /srv/data/downloads
arr_routing_mode: "{{ nbops_service_routing_mode }}"
arr_traefik_network: "{{ nbops_proxy_network }}"
arr_traefik_entrypoint: "{{ nbops_traefik_entrypoint }}"
arr_traefik_tls: "{{ nbops_traefik_tls }}"
arr_services:
prowlarr:
image: lscr.io/linuxserver/prowlarr:latest
port: 9696
domain: prowlarr.example.invalid
sonarr:
image: lscr.io/linuxserver/sonarr:latest
port: 8989
domain: sonarr.example.invalid
radarr:
image: lscr.io/linuxserver/radarr:latest
port: 7878
domain: radarr.example.invalid
lidarr:
image: lscr.io/linuxserver/lidarr:latest
port: 8686
domain: lidarr.example.invalid
bazarr:
image: lscr.io/linuxserver/bazarr:latest
port: 6767
domain: bazarr.example.invalid
arr_tcp_ports: "{{ arr_services | dict2items | map(attribute='value.port') | list }}"
+1
View File
@@ -0,0 +1 @@
+6
View File
@@ -0,0 +1,6 @@
---
- name: restart arr
community.docker.docker_compose_v2:
project_src: "{{ arr_dir }}"
state: restarted
+65
View File
@@ -0,0 +1,65 @@
---
- name: Create Arr stack directories
ansible.builtin.file:
path: "{{ item }}"
state: directory
owner: root
group: root
mode: "0755"
loop:
- "{{ arr_dir }}"
- "{{ arr_config_root }}"
- "{{ arr_media_root }}"
- "{{ arr_downloads_root }}"
tags: [arr]
- name: Create Arr application config directories
ansible.builtin.file:
path: "{{ arr_config_root }}/{{ item.key }}"
state: directory
owner: root
group: root
mode: "0755"
loop: "{{ arr_services | dict2items }}"
tags: [arr, config]
- name: Render Arr environment
ansible.builtin.template:
src: env.j2
dest: "{{ arr_dir }}/.env"
owner: root
group: root
mode: "0644"
tags: [arr, config]
- name: Ensure Arr proxy network exists
community.docker.docker_network:
name: "{{ arr_traefik_network }}"
state: present
when: arr_routing_mode == "traefik"
tags: [arr, docker]
- name: Render Arr Compose file
ansible.builtin.template:
src: compose.yaml.j2
dest: "{{ arr_dir }}/compose.yaml"
owner: root
group: root
mode: "0644"
notify: restart arr
tags: [arr, compose]
- name: Allow Arr ports
community.general.ufw:
rule: allow
port: "{{ item }}"
proto: tcp
loop: "{{ arr_tcp_ports }}"
when: arr_routing_mode == "host"
tags: [arr, firewall]
- name: Start Arr stack
community.docker.docker_compose_v2:
project_src: "{{ arr_dir }}"
state: present
tags: [arr, compose]
+31
View File
@@ -0,0 +1,31 @@
services:
{% for name, service in arr_services.items() %}
{{ name }}:
image: {{ service.image }}
restart: unless-stopped
env_file: .env
{% if arr_routing_mode == "host" %}
ports:
- "{{ service.port }}:{{ service.port }}"
{% endif %}
{% if arr_routing_mode == "traefik" %}
# internal service port: {{ service.port }}; not published on the Docker host
labels:
- traefik.enable=true
- traefik.http.routers.{{ name }}.rule=Host(`{{ service.domain }}`)
- traefik.http.routers.{{ name }}.entrypoints={{ arr_traefik_entrypoint }}
- traefik.http.routers.{{ name }}.tls={{ arr_traefik_tls | bool | ternary('true', 'false') }}
- traefik.http.services.{{ name }}.loadbalancer.server.port={{ service.port }}
networks:
- {{ arr_traefik_network }}
{% endif %}
volumes:
- {{ arr_config_root }}/{{ name }}:/config
- {{ arr_media_root }}:/media
- {{ arr_downloads_root }}:/downloads
{% endfor %}
{% if arr_routing_mode == "traefik" %}
networks:
{{ arr_traefik_network }}:
external: true
{% endif %}
+4
View File
@@ -0,0 +1,4 @@
PUID={{ arr_puid }}
PGID={{ arr_pgid }}
TZ={{ arr_timezone }}
+7
View File
@@ -0,0 +1,7 @@
# authentik
Deploys authentik with PostgreSQL and Redis. Override secret values from Vault,
SOPS, or Ansible Vault before production use.
Tags: `authentik`, `config`, `compose`, `firewall`.
+24
View File
@@ -0,0 +1,24 @@
---
authentik_dir: /srv/docker/authentik
authentik_image: ghcr.io/goauthentik/server:2025.6
authentik_postgres_image: docker.io/library/postgres:16-alpine
authentik_redis_image: docker.io/library/redis:7-alpine
authentik_http_port: 9000
authentik_https_port: 9443
authentik_domain: auth.example.invalid
authentik_routing_mode: "{{ nbops_service_routing_mode }}"
authentik_traefik_network: "{{ nbops_proxy_network }}"
authentik_traefik_entrypoint: "{{ nbops_traefik_entrypoint }}"
authentik_traefik_tls: "{{ nbops_traefik_tls }}"
authentik_secret_key: "{{ vault_authentik_secret_key | default('CHANGE_ME_IN_VAULT') }}"
authentik_postgres_password: "{{ vault_authentik_postgres_password | default('CHANGE_ME_IN_VAULT') }}"
authentik_env:
AUTHENTIK_SECRET_KEY: "{{ authentik_secret_key }}"
AUTHENTIK_POSTGRESQL__HOST: postgresql
AUTHENTIK_POSTGRESQL__USER: authentik
AUTHENTIK_POSTGRESQL__NAME: authentik
AUTHENTIK_POSTGRESQL__PASSWORD: "{{ authentik_postgres_password }}"
AUTHENTIK_REDIS__HOST: redis
authentik_tcp_ports:
- "{{ authentik_http_port }}"
- "{{ authentik_https_port }}"
+1
View File
@@ -0,0 +1 @@
+6
View File
@@ -0,0 +1,6 @@
---
- name: restart authentik
community.docker.docker_compose_v2:
project_src: "{{ authentik_dir }}"
state: restarted
+51
View File
@@ -0,0 +1,51 @@
---
- name: Create authentik directory
ansible.builtin.file:
path: "{{ authentik_dir }}"
state: directory
owner: root
group: root
mode: "0755"
tags: [authentik]
- name: Render authentik environment
ansible.builtin.template:
src: env.j2
dest: "{{ authentik_dir }}/.env"
owner: root
group: root
mode: "0600"
no_log: true
tags: [authentik, config]
- name: Ensure authentik proxy network exists
community.docker.docker_network:
name: "{{ authentik_traefik_network }}"
state: present
when: authentik_routing_mode == "traefik"
tags: [authentik, docker]
- name: Render authentik Compose file
ansible.builtin.template:
src: compose.yaml.j2
dest: "{{ authentik_dir }}/compose.yaml"
owner: root
group: root
mode: "0644"
notify: restart authentik
tags: [authentik, compose]
- name: Allow authentik ports
community.general.ufw:
rule: allow
port: "{{ item }}"
proto: tcp
loop: "{{ authentik_tcp_ports }}"
when: authentik_routing_mode == "host"
tags: [authentik, firewall]
- name: Start authentik
community.docker.docker_compose_v2:
project_src: "{{ authentik_dir }}"
state: present
tags: [authentik, compose]
+51
View File
@@ -0,0 +1,51 @@
services:
postgresql:
image: {{ authentik_postgres_image }}
restart: unless-stopped
environment:
POSTGRES_DB: authentik
POSTGRES_USER: authentik
POSTGRES_PASSWORD: ${PG_PASS}
volumes:
- ./postgresql:/var/lib/postgresql/data
redis:
image: {{ authentik_redis_image }}
restart: unless-stopped
server:
image: {{ authentik_image }}
restart: unless-stopped
command: server
env_file: .env
{% if authentik_routing_mode == "host" %}
ports:
- "{{ authentik_http_port }}:9000"
- "{{ authentik_https_port }}:9443"
{% endif %}
{% if authentik_routing_mode == "traefik" %}
# internal HTTP service port: 9000; not published on the Docker host
labels:
- traefik.enable=true
- traefik.http.routers.authentik.rule=Host(`{{ authentik_domain }}`)
- traefik.http.routers.authentik.entrypoints={{ authentik_traefik_entrypoint }}
- traefik.http.routers.authentik.tls={{ authentik_traefik_tls | bool | ternary('true', 'false') }}
- traefik.http.services.authentik.loadbalancer.server.port=9000
networks:
- default
- {{ authentik_traefik_network }}
{% endif %}
depends_on:
- postgresql
- redis
worker:
image: {{ authentik_image }}
restart: unless-stopped
command: worker
env_file: .env
depends_on:
- postgresql
- redis
{% if authentik_routing_mode == "traefik" %}
networks:
{{ authentik_traefik_network }}:
external: true
{% endif %}
+5
View File
@@ -0,0 +1,5 @@
PG_PASS={{ authentik_postgres_password }}
{% for key, value in authentik_env.items() %}
{{ key }}={{ value }}
{% endfor %}
+4
View File
@@ -0,0 +1,4 @@
# authentik_ldap
Deploys an authentik LDAP outpost.
+15
View File
@@ -0,0 +1,15 @@
---
authentik_ldap_name: authentik-ldap
authentik_ldap_dir: /srv/docker/authentik-ldap
authentik_ldap_image: ghcr.io/goauthentik/ldap:2025.6
authentik_ldap_routing_mode: host
authentik_ldap_env:
AUTHENTIK_HOST: https://auth.example.invalid
AUTHENTIK_TOKEN: "{{ vault_authentik_ldap_token | default('CHANGE_ME_IN_VAULT') }}"
authentik_ldap_directories:
- "{{ authentik_ldap_dir }}"
authentik_ldap_volumes: []
authentik_ldap_ports:
- { host: 3389, container: 3389, protocol: tcp }
- { host: 6636, container: 6636, protocol: tcp }
+1
View File
@@ -0,0 +1 @@
+1
View File
@@ -0,0 +1 @@
+16
View File
@@ -0,0 +1,16 @@
---
- name: Deploy authentik LDAP outpost
ansible.builtin.include_role:
name: docker_app
vars:
docker_app_env_template_src: "{{ playbook_dir }}/../roles/authentik_ldap/templates/env.j2"
docker_app_compose_template_src: "{{ playbook_dir }}/../roles/authentik_ldap/templates/compose.yaml.j2"
docker_app_name: "{{ authentik_ldap_name }}"
docker_app_dir: "{{ authentik_ldap_dir }}"
docker_app_image: "{{ authentik_ldap_image }}"
docker_app_env: "{{ authentik_ldap_env }}"
docker_app_directories: "{{ authentik_ldap_directories }}"
docker_app_volumes: "{{ authentik_ldap_volumes }}"
docker_app_ports: "{{ authentik_ldap_ports }}"
docker_app_routing_mode: "{{ authentik_ldap_routing_mode }}"
+1
View File
@@ -0,0 +1 @@
@@ -0,0 +1,90 @@
services:
{{ docker_app_name }}:
image: {{ docker_app_image }}
restart: {{ docker_app_restart }}
{% if docker_app_command | length > 0 %}
command: {{ docker_app_command | to_json }}
{% endif %}
{% if docker_app_user | length > 0 %}
user: "{{ docker_app_user }}"
{% endif %}
{% if docker_app_network_mode | length > 0 %}
network_mode: {{ docker_app_network_mode }}
{% endif %}
{% if docker_app_privileged | bool %}
privileged: true
{% endif %}
{% if docker_app_env | length > 0 %}
env_file: .env
{% endif %}
{% if docker_app_routing_mode == "host" and docker_app_ports | length > 0 %}
ports:
{% for port in docker_app_ports %}
- "{{ port.bind | default('0.0.0.0') }}:{{ port.host | default(port.container) }}:{{ port.container }}{% if port.protocol | default('tcp') != 'tcp' %}/{{ port.protocol }}{% endif %}"
{% endfor %}
{% endif %}
{% if docker_app_routing_mode == "traefik" and docker_app_service_port %}
# internal service port: {{ docker_app_service_port }}; not published on the Docker host
labels:
- traefik.enable=true
- traefik.http.routers.{{ docker_app_name }}.rule={{ docker_app_traefik_rule }}
- traefik.http.routers.{{ docker_app_name }}.entrypoints={{ docker_app_traefik_entrypoint }}
- traefik.http.routers.{{ docker_app_name }}.tls={{ docker_app_traefik_tls | bool | ternary('true', 'false') }}
{% if docker_app_traefik_middlewares | length > 0 %}
- traefik.http.routers.{{ docker_app_name }}.middlewares={{ docker_app_traefik_middlewares | join(',') }}
{% endif %}
- traefik.http.services.{{ docker_app_name }}.loadbalancer.server.port={{ docker_app_service_port }}
{% for key, value in docker_app_labels.items() %}
- {{ key }}={{ value }}
{% endfor %}
{% elif docker_app_labels | length > 0 %}
labels:
{% for key, value in docker_app_labels.items() %}
- {{ key }}={{ value }}
{% endfor %}
{% endif %}
{% if docker_app_volumes | length > 0 %}
volumes:
{% for volume in docker_app_volumes %}
- {{ volume }}
{% endfor %}
{% endif %}
{% if docker_app_devices | length > 0 %}
devices:
{% for device in docker_app_devices %}
- {{ device }}
{% endfor %}
{% endif %}
{% if docker_app_cap_add | length > 0 %}
cap_add:
{% for capability in docker_app_cap_add %}
- {{ capability }}
{% endfor %}
{% endif %}
{% if docker_app_group_add | length > 0 %}
group_add:
{% for group in docker_app_group_add %}
- "{{ group }}"
{% endfor %}
{% endif %}
{% if docker_app_extra_hosts | length > 0 %}
extra_hosts:
{% for host in docker_app_extra_hosts %}
- {{ host }}
{% endfor %}
{% endif %}
{% if docker_app_depends_on | length > 0 %}
depends_on:
{% for dependency in docker_app_depends_on %}
- {{ dependency }}
{% endfor %}
{% endif %}
{% if docker_app_routing_mode == "traefik" and docker_app_network_mode | length == 0 %}
networks:
- {{ docker_app_traefik_network }}
{% endif %}
{% if docker_app_routing_mode == "traefik" and docker_app_network_mode | length == 0 %}
networks:
{{ docker_app_traefik_network }}:
external: true
{% endif %}
+4
View File
@@ -0,0 +1,4 @@
{% for key, value in docker_app_env.items() %}
{{ key }}={{ value }}
{% endfor %}
+4
View File
@@ -0,0 +1,4 @@
# authentik_proxy
Deploys an authentik proxy outpost.
+17
View File
@@ -0,0 +1,17 @@
---
authentik_proxy_name: authentik-proxy
authentik_proxy_dir: /srv/docker/authentik-proxy
authentik_proxy_image: ghcr.io/goauthentik/proxy:2025.6
authentik_proxy_domain: proxy.example.invalid
authentik_proxy_service_port: 9000
authentik_proxy_routing_mode: "{{ nbops_service_routing_mode }}"
authentik_proxy_env:
AUTHENTIK_HOST: https://auth.example.invalid
AUTHENTIK_TOKEN: "{{ vault_authentik_proxy_token | default('CHANGE_ME_IN_VAULT') }}"
authentik_proxy_directories:
- "{{ authentik_proxy_dir }}"
authentik_proxy_volumes: []
authentik_proxy_ports:
- { host: 9001, container: 9000, protocol: tcp }
- { host: 9444, container: 9443, protocol: tcp }
+1
View File
@@ -0,0 +1 @@
+1
View File
@@ -0,0 +1 @@
+18
View File
@@ -0,0 +1,18 @@
---
- name: Deploy authentik proxy outpost
ansible.builtin.include_role:
name: docker_app
vars:
docker_app_env_template_src: "{{ playbook_dir }}/../roles/authentik_proxy/templates/env.j2"
docker_app_compose_template_src: "{{ playbook_dir }}/../roles/authentik_proxy/templates/compose.yaml.j2"
docker_app_name: "{{ authentik_proxy_name }}"
docker_app_dir: "{{ authentik_proxy_dir }}"
docker_app_image: "{{ authentik_proxy_image }}"
docker_app_env: "{{ authentik_proxy_env }}"
docker_app_directories: "{{ authentik_proxy_directories }}"
docker_app_volumes: "{{ authentik_proxy_volumes }}"
docker_app_ports: "{{ authentik_proxy_ports }}"
docker_app_routing_mode: "{{ authentik_proxy_routing_mode }}"
docker_app_domain: "{{ authentik_proxy_domain }}"
docker_app_service_port: "{{ authentik_proxy_service_port }}"
+1
View File
@@ -0,0 +1 @@
@@ -0,0 +1,90 @@
services:
{{ docker_app_name }}:
image: {{ docker_app_image }}
restart: {{ docker_app_restart }}
{% if docker_app_command | length > 0 %}
command: {{ docker_app_command | to_json }}
{% endif %}
{% if docker_app_user | length > 0 %}
user: "{{ docker_app_user }}"
{% endif %}
{% if docker_app_network_mode | length > 0 %}
network_mode: {{ docker_app_network_mode }}
{% endif %}
{% if docker_app_privileged | bool %}
privileged: true
{% endif %}
{% if docker_app_env | length > 0 %}
env_file: .env
{% endif %}
{% if docker_app_routing_mode == "host" and docker_app_ports | length > 0 %}
ports:
{% for port in docker_app_ports %}
- "{{ port.bind | default('0.0.0.0') }}:{{ port.host | default(port.container) }}:{{ port.container }}{% if port.protocol | default('tcp') != 'tcp' %}/{{ port.protocol }}{% endif %}"
{% endfor %}
{% endif %}
{% if docker_app_routing_mode == "traefik" and docker_app_service_port %}
# internal service port: {{ docker_app_service_port }}; not published on the Docker host
labels:
- traefik.enable=true
- traefik.http.routers.{{ docker_app_name }}.rule={{ docker_app_traefik_rule }}
- traefik.http.routers.{{ docker_app_name }}.entrypoints={{ docker_app_traefik_entrypoint }}
- traefik.http.routers.{{ docker_app_name }}.tls={{ docker_app_traefik_tls | bool | ternary('true', 'false') }}
{% if docker_app_traefik_middlewares | length > 0 %}
- traefik.http.routers.{{ docker_app_name }}.middlewares={{ docker_app_traefik_middlewares | join(',') }}
{% endif %}
- traefik.http.services.{{ docker_app_name }}.loadbalancer.server.port={{ docker_app_service_port }}
{% for key, value in docker_app_labels.items() %}
- {{ key }}={{ value }}
{% endfor %}
{% elif docker_app_labels | length > 0 %}
labels:
{% for key, value in docker_app_labels.items() %}
- {{ key }}={{ value }}
{% endfor %}
{% endif %}
{% if docker_app_volumes | length > 0 %}
volumes:
{% for volume in docker_app_volumes %}
- {{ volume }}
{% endfor %}
{% endif %}
{% if docker_app_devices | length > 0 %}
devices:
{% for device in docker_app_devices %}
- {{ device }}
{% endfor %}
{% endif %}
{% if docker_app_cap_add | length > 0 %}
cap_add:
{% for capability in docker_app_cap_add %}
- {{ capability }}
{% endfor %}
{% endif %}
{% if docker_app_group_add | length > 0 %}
group_add:
{% for group in docker_app_group_add %}
- "{{ group }}"
{% endfor %}
{% endif %}
{% if docker_app_extra_hosts | length > 0 %}
extra_hosts:
{% for host in docker_app_extra_hosts %}
- {{ host }}
{% endfor %}
{% endif %}
{% if docker_app_depends_on | length > 0 %}
depends_on:
{% for dependency in docker_app_depends_on %}
- {{ dependency }}
{% endfor %}
{% endif %}
{% if docker_app_routing_mode == "traefik" and docker_app_network_mode | length == 0 %}
networks:
- {{ docker_app_traefik_network }}
{% endif %}
{% if docker_app_routing_mode == "traefik" and docker_app_network_mode | length == 0 %}
networks:
{{ docker_app_traefik_network }}:
external: true
{% endif %}
+4
View File
@@ -0,0 +1,4 @@
{% for key, value in docker_app_env.items() %}
{{ key }}={{ value }}
{% endfor %}
+4
View File
@@ -0,0 +1,4 @@
# authentik_rac
Deploys an authentik RAC outpost.
+16
View File
@@ -0,0 +1,16 @@
---
authentik_rac_name: authentik-rac
authentik_rac_dir: /srv/docker/authentik-rac
authentik_rac_image: ghcr.io/goauthentik/rac:2025.6
authentik_rac_domain: rac.example.invalid
authentik_rac_service_port: 3389
authentik_rac_routing_mode: host
authentik_rac_env:
AUTHENTIK_HOST: https://auth.example.invalid
AUTHENTIK_TOKEN: "{{ vault_authentik_rac_token | default('CHANGE_ME_IN_VAULT') }}"
authentik_rac_directories:
- "{{ authentik_rac_dir }}"
authentik_rac_volumes: []
authentik_rac_ports:
- { host: 3390, container: 3389, protocol: tcp }
+1
View File
@@ -0,0 +1 @@
+1
View File
@@ -0,0 +1 @@
+18
View File
@@ -0,0 +1,18 @@
---
- name: Deploy authentik RAC outpost
ansible.builtin.include_role:
name: docker_app
vars:
docker_app_env_template_src: "{{ playbook_dir }}/../roles/authentik_rac/templates/env.j2"
docker_app_compose_template_src: "{{ playbook_dir }}/../roles/authentik_rac/templates/compose.yaml.j2"
docker_app_name: "{{ authentik_rac_name }}"
docker_app_dir: "{{ authentik_rac_dir }}"
docker_app_image: "{{ authentik_rac_image }}"
docker_app_env: "{{ authentik_rac_env }}"
docker_app_directories: "{{ authentik_rac_directories }}"
docker_app_volumes: "{{ authentik_rac_volumes }}"
docker_app_ports: "{{ authentik_rac_ports }}"
docker_app_routing_mode: "{{ authentik_rac_routing_mode }}"
docker_app_domain: "{{ authentik_rac_domain }}"
docker_app_service_port: "{{ authentik_rac_service_port }}"
+1
View File
@@ -0,0 +1 @@
@@ -0,0 +1,90 @@
services:
{{ docker_app_name }}:
image: {{ docker_app_image }}
restart: {{ docker_app_restart }}
{% if docker_app_command | length > 0 %}
command: {{ docker_app_command | to_json }}
{% endif %}
{% if docker_app_user | length > 0 %}
user: "{{ docker_app_user }}"
{% endif %}
{% if docker_app_network_mode | length > 0 %}
network_mode: {{ docker_app_network_mode }}
{% endif %}
{% if docker_app_privileged | bool %}
privileged: true
{% endif %}
{% if docker_app_env | length > 0 %}
env_file: .env
{% endif %}
{% if docker_app_routing_mode == "host" and docker_app_ports | length > 0 %}
ports:
{% for port in docker_app_ports %}
- "{{ port.bind | default('0.0.0.0') }}:{{ port.host | default(port.container) }}:{{ port.container }}{% if port.protocol | default('tcp') != 'tcp' %}/{{ port.protocol }}{% endif %}"
{% endfor %}
{% endif %}
{% if docker_app_routing_mode == "traefik" and docker_app_service_port %}
# internal service port: {{ docker_app_service_port }}; not published on the Docker host
labels:
- traefik.enable=true
- traefik.http.routers.{{ docker_app_name }}.rule={{ docker_app_traefik_rule }}
- traefik.http.routers.{{ docker_app_name }}.entrypoints={{ docker_app_traefik_entrypoint }}
- traefik.http.routers.{{ docker_app_name }}.tls={{ docker_app_traefik_tls | bool | ternary('true', 'false') }}
{% if docker_app_traefik_middlewares | length > 0 %}
- traefik.http.routers.{{ docker_app_name }}.middlewares={{ docker_app_traefik_middlewares | join(',') }}
{% endif %}
- traefik.http.services.{{ docker_app_name }}.loadbalancer.server.port={{ docker_app_service_port }}
{% for key, value in docker_app_labels.items() %}
- {{ key }}={{ value }}
{% endfor %}
{% elif docker_app_labels | length > 0 %}
labels:
{% for key, value in docker_app_labels.items() %}
- {{ key }}={{ value }}
{% endfor %}
{% endif %}
{% if docker_app_volumes | length > 0 %}
volumes:
{% for volume in docker_app_volumes %}
- {{ volume }}
{% endfor %}
{% endif %}
{% if docker_app_devices | length > 0 %}
devices:
{% for device in docker_app_devices %}
- {{ device }}
{% endfor %}
{% endif %}
{% if docker_app_cap_add | length > 0 %}
cap_add:
{% for capability in docker_app_cap_add %}
- {{ capability }}
{% endfor %}
{% endif %}
{% if docker_app_group_add | length > 0 %}
group_add:
{% for group in docker_app_group_add %}
- "{{ group }}"
{% endfor %}
{% endif %}
{% if docker_app_extra_hosts | length > 0 %}
extra_hosts:
{% for host in docker_app_extra_hosts %}
- {{ host }}
{% endfor %}
{% endif %}
{% if docker_app_depends_on | length > 0 %}
depends_on:
{% for dependency in docker_app_depends_on %}
- {{ dependency }}
{% endfor %}
{% endif %}
{% if docker_app_routing_mode == "traefik" and docker_app_network_mode | length == 0 %}
networks:
- {{ docker_app_traefik_network }}
{% endif %}
{% if docker_app_routing_mode == "traefik" and docker_app_network_mode | length == 0 %}
networks:
{{ docker_app_traefik_network }}:
external: true
{% endif %}
+4
View File
@@ -0,0 +1,4 @@
{% for key, value in docker_app_env.items() %}
{{ key }}={{ value }}
{% endfor %}
+9
View File
@@ -0,0 +1,9 @@
# base
Configures the operating system baseline: hostname, locale, timezone, packages,
Chrony, QEMU guest agent, persistent journald, sysctl, aliases, PKI directories,
and `/srv` layout.
Tags: `base`, `hostname`, `packages`, `locale`, `timezone`, `directories`,
`journald`, `sysctl`, `shell`, `services`, `pki`.
+13
View File
@@ -0,0 +1,13 @@
---
base_hostname: "{{ nbops_hostname }}"
base_fqdn: "{{ nbops_fqdn }}"
base_timezone: "{{ nbops_timezone }}"
base_locale: "{{ nbops_locale }}"
base_packages: "{{ nbops_base_packages }}"
base_standard_directories: "{{ nbops_standard_directories }}"
base_sysctl: "{{ nbops_sysctl }}"
base_aliases:
ll: "ls -alF"
la: "ls -A"
l: "ls -CF"
+1
View File
@@ -0,0 +1 @@
+16
View File
@@ -0,0 +1,16 @@
---
- name: restart chrony
ansible.builtin.service:
name: chrony
state: restarted
- name: restart qemu guest agent
ansible.builtin.service:
name: qemu-guest-agent
state: restarted
- name: restart systemd-journald
ansible.builtin.service:
name: systemd-journald
state: restarted
+104
View File
@@ -0,0 +1,104 @@
---
- name: Set hostname
ansible.builtin.hostname:
name: "{{ base_hostname }}"
tags: [base, hostname]
- name: Configure hosts file
ansible.builtin.template:
src: hosts.j2
dest: /etc/hosts
owner: root
group: root
mode: "0644"
tags: [base, hostname]
- name: Install base packages
ansible.builtin.apt:
name: "{{ base_packages }}"
state: present
update_cache: true
cache_valid_time: 3600
tags: [base, packages]
- name: Configure locale
community.general.locale_gen:
name: "{{ base_locale }}"
state: present
tags: [base, locale]
- name: Set system locale
ansible.builtin.copy:
dest: /etc/default/locale
owner: root
group: root
mode: "0644"
content: |
LANG={{ base_locale }}
LC_ALL={{ base_locale }}
tags: [base, locale]
- name: Configure timezone
community.general.timezone:
name: "{{ base_timezone }}"
notify: restart chrony
tags: [base, timezone]
- name: Ensure standard directories exist
ansible.builtin.file:
path: "{{ item }}"
state: directory
owner: root
group: root
mode: "{{ '0700' if item.endswith('/private') else '0755' }}"
loop: "{{ base_standard_directories }}"
tags: [base, directories]
- name: Configure persistent journald storage
ansible.builtin.copy:
dest: /etc/systemd/journald.conf.d/10-nbops.conf
owner: root
group: root
mode: "0644"
content: |
[Journal]
Storage=persistent
Compress=yes
SystemMaxUse=1G
notify: restart systemd-journald
tags: [base, journald]
- name: Configure sysctl
ansible.posix.sysctl:
name: "{{ item.key }}"
value: "{{ item.value }}"
state: present
reload: true
loop: "{{ base_sysctl | dict2items }}"
tags: [base, sysctl]
- name: Configure shell aliases
ansible.builtin.template:
src: nbops-aliases.sh.j2
dest: /etc/profile.d/nbops-aliases.sh
owner: root
group: root
mode: "0644"
tags: [base, shell]
- name: Enable base services
ansible.builtin.service:
name: "{{ item }}"
enabled: true
state: started
loop:
- chrony
- qemu-guest-agent
tags: [base, services]
- name: Refresh CA certificate trust
ansible.builtin.command:
cmd: update-ca-certificates
changed_when: false
tags: [base, pki]
+7
View File
@@ -0,0 +1,7 @@
127.0.0.1 localhost
127.0.1.1 {{ base_fqdn }} {{ base_hostname }}
::1 localhost ip6-localhost ip6-loopback
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
+4
View File
@@ -0,0 +1,4 @@
{% for name, command in base_aliases.items() %}
alias {{ name }}='{{ command }}'
{% endfor %}
+4
View File
@@ -0,0 +1,4 @@
# cleanuparr
Deploys Cleanuparr with Docker Compose.
+18
View File
@@ -0,0 +1,18 @@
---
cleanuparr_name: cleanuparr
cleanuparr_dir: /srv/docker/cleanuparr
cleanuparr_image: ghcr.io/cleanuparr/cleanuparr:latest
cleanuparr_domain: cleanuparr.example.invalid
cleanuparr_service_port: 11011
cleanuparr_routing_mode: "{{ nbops_service_routing_mode }}"
cleanuparr_env:
TZ: "{{ nbops_timezone }}"
cleanuparr_directories:
- "{{ cleanuparr_dir }}"
- /srv/config/cleanuparr
cleanuparr_volumes:
- /srv/config/cleanuparr:/config
- /srv/data/downloads:/downloads
cleanuparr_ports:
- { host: 11011, container: 11011, protocol: tcp }
+1
View File
@@ -0,0 +1 @@
+1
View File
@@ -0,0 +1 @@
+18
View File
@@ -0,0 +1,18 @@
---
- name: Deploy Cleanuparr
ansible.builtin.include_role:
name: docker_app
vars:
docker_app_env_template_src: "{{ playbook_dir }}/../roles/cleanuparr/templates/env.j2"
docker_app_compose_template_src: "{{ playbook_dir }}/../roles/cleanuparr/templates/compose.yaml.j2"
docker_app_name: "{{ cleanuparr_name }}"
docker_app_dir: "{{ cleanuparr_dir }}"
docker_app_image: "{{ cleanuparr_image }}"
docker_app_env: "{{ cleanuparr_env }}"
docker_app_directories: "{{ cleanuparr_directories }}"
docker_app_volumes: "{{ cleanuparr_volumes }}"
docker_app_ports: "{{ cleanuparr_ports }}"
docker_app_routing_mode: "{{ cleanuparr_routing_mode }}"
docker_app_domain: "{{ cleanuparr_domain }}"
docker_app_service_port: "{{ cleanuparr_service_port }}"
+1
View File
@@ -0,0 +1 @@
@@ -0,0 +1,90 @@
services:
{{ docker_app_name }}:
image: {{ docker_app_image }}
restart: {{ docker_app_restart }}
{% if docker_app_command | length > 0 %}
command: {{ docker_app_command | to_json }}
{% endif %}
{% if docker_app_user | length > 0 %}
user: "{{ docker_app_user }}"
{% endif %}
{% if docker_app_network_mode | length > 0 %}
network_mode: {{ docker_app_network_mode }}
{% endif %}
{% if docker_app_privileged | bool %}
privileged: true
{% endif %}
{% if docker_app_env | length > 0 %}
env_file: .env
{% endif %}
{% if docker_app_routing_mode == "host" and docker_app_ports | length > 0 %}
ports:
{% for port in docker_app_ports %}
- "{{ port.bind | default('0.0.0.0') }}:{{ port.host | default(port.container) }}:{{ port.container }}{% if port.protocol | default('tcp') != 'tcp' %}/{{ port.protocol }}{% endif %}"
{% endfor %}
{% endif %}
{% if docker_app_routing_mode == "traefik" and docker_app_service_port %}
# internal service port: {{ docker_app_service_port }}; not published on the Docker host
labels:
- traefik.enable=true
- traefik.http.routers.{{ docker_app_name }}.rule={{ docker_app_traefik_rule }}
- traefik.http.routers.{{ docker_app_name }}.entrypoints={{ docker_app_traefik_entrypoint }}
- traefik.http.routers.{{ docker_app_name }}.tls={{ docker_app_traefik_tls | bool | ternary('true', 'false') }}
{% if docker_app_traefik_middlewares | length > 0 %}
- traefik.http.routers.{{ docker_app_name }}.middlewares={{ docker_app_traefik_middlewares | join(',') }}
{% endif %}
- traefik.http.services.{{ docker_app_name }}.loadbalancer.server.port={{ docker_app_service_port }}
{% for key, value in docker_app_labels.items() %}
- {{ key }}={{ value }}
{% endfor %}
{% elif docker_app_labels | length > 0 %}
labels:
{% for key, value in docker_app_labels.items() %}
- {{ key }}={{ value }}
{% endfor %}
{% endif %}
{% if docker_app_volumes | length > 0 %}
volumes:
{% for volume in docker_app_volumes %}
- {{ volume }}
{% endfor %}
{% endif %}
{% if docker_app_devices | length > 0 %}
devices:
{% for device in docker_app_devices %}
- {{ device }}
{% endfor %}
{% endif %}
{% if docker_app_cap_add | length > 0 %}
cap_add:
{% for capability in docker_app_cap_add %}
- {{ capability }}
{% endfor %}
{% endif %}
{% if docker_app_group_add | length > 0 %}
group_add:
{% for group in docker_app_group_add %}
- "{{ group }}"
{% endfor %}
{% endif %}
{% if docker_app_extra_hosts | length > 0 %}
extra_hosts:
{% for host in docker_app_extra_hosts %}
- {{ host }}
{% endfor %}
{% endif %}
{% if docker_app_depends_on | length > 0 %}
depends_on:
{% for dependency in docker_app_depends_on %}
- {{ dependency }}
{% endfor %}
{% endif %}
{% if docker_app_routing_mode == "traefik" and docker_app_network_mode | length == 0 %}
networks:
- {{ docker_app_traefik_network }}
{% endif %}
{% if docker_app_routing_mode == "traefik" and docker_app_network_mode | length == 0 %}
networks:
{{ docker_app_traefik_network }}:
external: true
{% endif %}
+4
View File
@@ -0,0 +1,4 @@
{% for key, value in docker_app_env.items() %}
{{ key }}={{ value }}
{% endfor %}
+4
View File
@@ -0,0 +1,4 @@
# code_server
Deploys code-server with Docker Compose.
+22
View File
@@ -0,0 +1,22 @@
---
code_server_name: code-server
code_server_dir: /srv/docker/code-server
code_server_image: lscr.io/linuxserver/code-server:latest
code_server_domain: code.example.invalid
code_server_service_port: 8443
code_server_routing_mode: "{{ nbops_service_routing_mode }}"
code_server_env:
PUID: "1000"
PGID: "1000"
TZ: "{{ nbops_timezone }}"
PASSWORD: "{{ vault_code_server_password | default('CHANGE_ME_IN_VAULT') }}"
code_server_directories:
- "{{ code_server_dir }}"
- /srv/config/code-server
- /srv/data/workspace
code_server_volumes:
- /srv/config/code-server:/config
- /srv/data/workspace:/workspace
code_server_ports:
- { host: 8443, container: 8443, protocol: tcp }
+1
View File
@@ -0,0 +1 @@
+1
View File
@@ -0,0 +1 @@
+18
View File
@@ -0,0 +1,18 @@
---
- name: Deploy code-server
ansible.builtin.include_role:
name: docker_app
vars:
docker_app_env_template_src: "{{ playbook_dir }}/../roles/code_server/templates/env.j2"
docker_app_compose_template_src: "{{ playbook_dir }}/../roles/code_server/templates/compose.yaml.j2"
docker_app_name: "{{ code_server_name }}"
docker_app_dir: "{{ code_server_dir }}"
docker_app_image: "{{ code_server_image }}"
docker_app_env: "{{ code_server_env }}"
docker_app_directories: "{{ code_server_directories }}"
docker_app_volumes: "{{ code_server_volumes }}"
docker_app_ports: "{{ code_server_ports }}"
docker_app_routing_mode: "{{ code_server_routing_mode }}"
docker_app_domain: "{{ code_server_domain }}"
docker_app_service_port: "{{ code_server_service_port }}"
+1
View File
@@ -0,0 +1 @@
@@ -0,0 +1,90 @@
services:
{{ docker_app_name }}:
image: {{ docker_app_image }}
restart: {{ docker_app_restart }}
{% if docker_app_command | length > 0 %}
command: {{ docker_app_command | to_json }}
{% endif %}
{% if docker_app_user | length > 0 %}
user: "{{ docker_app_user }}"
{% endif %}
{% if docker_app_network_mode | length > 0 %}
network_mode: {{ docker_app_network_mode }}
{% endif %}
{% if docker_app_privileged | bool %}
privileged: true
{% endif %}
{% if docker_app_env | length > 0 %}
env_file: .env
{% endif %}
{% if docker_app_routing_mode == "host" and docker_app_ports | length > 0 %}
ports:
{% for port in docker_app_ports %}
- "{{ port.bind | default('0.0.0.0') }}:{{ port.host | default(port.container) }}:{{ port.container }}{% if port.protocol | default('tcp') != 'tcp' %}/{{ port.protocol }}{% endif %}"
{% endfor %}
{% endif %}
{% if docker_app_routing_mode == "traefik" and docker_app_service_port %}
# internal service port: {{ docker_app_service_port }}; not published on the Docker host
labels:
- traefik.enable=true
- traefik.http.routers.{{ docker_app_name }}.rule={{ docker_app_traefik_rule }}
- traefik.http.routers.{{ docker_app_name }}.entrypoints={{ docker_app_traefik_entrypoint }}
- traefik.http.routers.{{ docker_app_name }}.tls={{ docker_app_traefik_tls | bool | ternary('true', 'false') }}
{% if docker_app_traefik_middlewares | length > 0 %}
- traefik.http.routers.{{ docker_app_name }}.middlewares={{ docker_app_traefik_middlewares | join(',') }}
{% endif %}
- traefik.http.services.{{ docker_app_name }}.loadbalancer.server.port={{ docker_app_service_port }}
{% for key, value in docker_app_labels.items() %}
- {{ key }}={{ value }}
{% endfor %}
{% elif docker_app_labels | length > 0 %}
labels:
{% for key, value in docker_app_labels.items() %}
- {{ key }}={{ value }}
{% endfor %}
{% endif %}
{% if docker_app_volumes | length > 0 %}
volumes:
{% for volume in docker_app_volumes %}
- {{ volume }}
{% endfor %}
{% endif %}
{% if docker_app_devices | length > 0 %}
devices:
{% for device in docker_app_devices %}
- {{ device }}
{% endfor %}
{% endif %}
{% if docker_app_cap_add | length > 0 %}
cap_add:
{% for capability in docker_app_cap_add %}
- {{ capability }}
{% endfor %}
{% endif %}
{% if docker_app_group_add | length > 0 %}
group_add:
{% for group in docker_app_group_add %}
- "{{ group }}"
{% endfor %}
{% endif %}
{% if docker_app_extra_hosts | length > 0 %}
extra_hosts:
{% for host in docker_app_extra_hosts %}
- {{ host }}
{% endfor %}
{% endif %}
{% if docker_app_depends_on | length > 0 %}
depends_on:
{% for dependency in docker_app_depends_on %}
- {{ dependency }}
{% endfor %}
{% endif %}
{% if docker_app_routing_mode == "traefik" and docker_app_network_mode | length == 0 %}
networks:
- {{ docker_app_traefik_network }}
{% endif %}
{% if docker_app_routing_mode == "traefik" and docker_app_network_mode | length == 0 %}
networks:
{{ docker_app_traefik_network }}:
external: true
{% endif %}
+4
View File
@@ -0,0 +1,4 @@
{% for key, value in docker_app_env.items() %}
{{ key }}={{ value }}
{% endfor %}
+8
View File
@@ -0,0 +1,8 @@
# docker
Installs Docker Engine from Docker's official Debian repository, configures
`/etc/docker/daemon.json`, enables the service, and adds configured users to the
`docker` group.
Tags: `docker`, `packages`, `config`, `users`, `services`.
+18
View File
@@ -0,0 +1,18 @@
---
docker_apt_key_url: https://download.docker.com/linux/debian/gpg
docker_apt_key_path: /etc/apt/keyrings/docker.asc
docker_apt_architecture: "{{ ansible_architecture | regex_replace('x86_64', 'amd64') | regex_replace('aarch64', 'arm64') }}"
docker_apt_uri: https://download.docker.com/linux/debian
docker_packages:
- docker-ce
- docker-ce-cli
- containerd.io
- docker-buildx-plugin
- docker-compose-plugin
docker_daemon_config:
log-driver: local
live-restore: true
storage-driver: overlay2
iptables: true
docker_service_state: started
docker_service_enabled: true
+1
View File
@@ -0,0 +1 @@
+6
View File
@@ -0,0 +1,6 @@
---
- name: restart docker
ansible.builtin.service:
name: docker
state: restarted
+69
View File
@@ -0,0 +1,69 @@
---
- name: Ensure apt keyring directory exists
ansible.builtin.file:
path: /etc/apt/keyrings
state: directory
owner: root
group: root
mode: "0755"
tags: [docker, packages]
- name: Install Docker repository signing key
ansible.builtin.get_url:
url: "{{ docker_apt_key_url }}"
dest: "{{ docker_apt_key_path }}"
owner: root
group: root
mode: "0644"
tags: [docker, packages]
- name: Configure Docker apt repository
ansible.builtin.deb822_repository:
name: docker
types:
- deb
uris:
- "{{ docker_apt_uri }}"
suites:
- "{{ ansible_distribution_release }}"
components:
- stable
architectures:
- "{{ docker_apt_architecture }}"
signed_by: "{{ docker_apt_key_path }}"
state: present
enabled: true
install_python_debian: true
tags: [docker, packages]
- name: Install Docker Engine and plugins
ansible.builtin.apt:
name: "{{ docker_packages }}"
state: present
update_cache: true
tags: [docker, packages]
- name: Configure Docker daemon
ansible.builtin.copy:
dest: /etc/docker/daemon.json
owner: root
group: root
mode: "0644"
content: "{{ docker_daemon_config | to_nice_json }}\n"
notify: restart docker
tags: [docker, config]
- name: Add users to docker group
ansible.builtin.user:
name: "{{ item }}"
groups: docker
append: true
loop: "{{ docker_users }}"
tags: [docker, users]
- name: Enable Docker service
ansible.builtin.service:
name: docker
enabled: "{{ docker_service_enabled }}"
state: "{{ docker_service_state }}"
tags: [docker, services]
+1
View File
@@ -0,0 +1 @@
+10
View File
@@ -0,0 +1,10 @@
# docker_app
Shared implementation role for single-container Docker Compose applications.
Application roles set `docker_app_*` variables and include this role.
It supports direct host-port publishing with UFW rules or Traefik routing via
labels and a shared Docker network.
Tags: `docker_app`, `docker`, `config`, `compose`, `firewall`.
+32
View File
@@ -0,0 +1,32 @@
---
docker_app_name: app
docker_app_dir: "/srv/docker/{{ docker_app_name }}"
docker_app_image: ""
docker_app_restart: unless-stopped
docker_app_command: ""
docker_app_user: ""
docker_app_network_mode: ""
docker_app_privileged: false
docker_app_pull: missing
docker_app_env: {}
docker_app_volumes: []
docker_app_ports: []
docker_app_devices: []
docker_app_cap_add: []
docker_app_group_add: []
docker_app_extra_hosts: []
docker_app_depends_on: []
docker_app_directories:
- "{{ docker_app_dir }}"
docker_app_routing_mode: "{{ nbops_service_routing_mode }}"
docker_app_domain: "{{ docker_app_name }}.{{ nbops_service_domain_suffix }}"
docker_app_service_port: null
docker_app_traefik_network: "{{ nbops_proxy_network }}"
docker_app_traefik_entrypoint: "{{ nbops_traefik_entrypoint }}"
docker_app_traefik_tls: "{{ nbops_traefik_tls }}"
docker_app_traefik_middlewares: []
docker_app_traefik_rule: "Host(`{{ docker_app_domain }}`)"
docker_app_labels: {}
docker_app_ufw_ports: "{{ docker_app_ports }}"
docker_app_env_template_src: env.j2
docker_app_compose_template_src: compose.yaml.j2
+1
View File
@@ -0,0 +1 @@
+6
View File
@@ -0,0 +1,6 @@
---
- name: restart docker app
community.docker.docker_compose_v2:
project_src: "{{ docker_app_dir }}"
state: restarted
+62
View File
@@ -0,0 +1,62 @@
---
- name: Validate Docker app image is configured
ansible.builtin.assert:
that:
- docker_app_image | length > 0
fail_msg: "docker_app_image must be configured for {{ docker_app_name }}."
tags: [docker_app]
- name: Create Docker app directories
ansible.builtin.file:
path: "{{ item }}"
state: directory
owner: root
group: root
mode: "0755"
loop: "{{ docker_app_directories }}"
tags: [docker_app]
- name: Ensure proxy network exists
community.docker.docker_network:
name: "{{ docker_app_traefik_network }}"
state: present
when:
- docker_app_routing_mode == "traefik"
- docker_app_network_mode | length == 0
tags: [docker_app, docker]
- name: Render Docker app environment
ansible.builtin.template:
src: "{{ docker_app_env_template_src }}"
dest: "{{ docker_app_dir }}/.env"
owner: root
group: root
mode: "0600"
no_log: true
tags: [docker_app, config]
- name: Render Docker app Compose file
ansible.builtin.template:
src: "{{ docker_app_compose_template_src }}"
dest: "{{ docker_app_dir }}/compose.yaml"
owner: root
group: root
mode: "0644"
notify: restart docker app
tags: [docker_app, compose]
- name: Allow Docker app host ports
community.general.ufw:
rule: allow
port: "{{ item.host | default(item.container) }}"
proto: "{{ item.protocol | default('tcp') }}"
loop: "{{ docker_app_ufw_ports }}"
when: docker_app_routing_mode == "host"
tags: [docker_app, firewall]
- name: Start Docker app
community.docker.docker_compose_v2:
project_src: "{{ docker_app_dir }}"
state: present
pull: "{{ docker_app_pull }}"
tags: [docker_app, compose]
@@ -0,0 +1,90 @@
services:
{{ docker_app_name }}:
image: {{ docker_app_image }}
restart: {{ docker_app_restart }}
{% if docker_app_command | length > 0 %}
command: {{ docker_app_command | to_json }}
{% endif %}
{% if docker_app_user | length > 0 %}
user: "{{ docker_app_user }}"
{% endif %}
{% if docker_app_network_mode | length > 0 %}
network_mode: {{ docker_app_network_mode }}
{% endif %}
{% if docker_app_privileged | bool %}
privileged: true
{% endif %}
{% if docker_app_env | length > 0 %}
env_file: .env
{% endif %}
{% if docker_app_routing_mode == "host" and docker_app_ports | length > 0 %}
ports:
{% for port in docker_app_ports %}
- "{{ port.bind | default('0.0.0.0') }}:{{ port.host | default(port.container) }}:{{ port.container }}{% if port.protocol | default('tcp') != 'tcp' %}/{{ port.protocol }}{% endif %}"
{% endfor %}
{% endif %}
{% if docker_app_routing_mode == "traefik" and docker_app_service_port %}
# internal service port: {{ docker_app_service_port }}; not published on the Docker host
labels:
- traefik.enable=true
- traefik.http.routers.{{ docker_app_name }}.rule={{ docker_app_traefik_rule }}
- traefik.http.routers.{{ docker_app_name }}.entrypoints={{ docker_app_traefik_entrypoint }}
- traefik.http.routers.{{ docker_app_name }}.tls={{ docker_app_traefik_tls | bool | ternary('true', 'false') }}
{% if docker_app_traefik_middlewares | length > 0 %}
- traefik.http.routers.{{ docker_app_name }}.middlewares={{ docker_app_traefik_middlewares | join(',') }}
{% endif %}
- traefik.http.services.{{ docker_app_name }}.loadbalancer.server.port={{ docker_app_service_port }}
{% for key, value in docker_app_labels.items() %}
- {{ key }}={{ value }}
{% endfor %}
{% elif docker_app_labels | length > 0 %}
labels:
{% for key, value in docker_app_labels.items() %}
- {{ key }}={{ value }}
{% endfor %}
{% endif %}
{% if docker_app_volumes | length > 0 %}
volumes:
{% for volume in docker_app_volumes %}
- {{ volume }}
{% endfor %}
{% endif %}
{% if docker_app_devices | length > 0 %}
devices:
{% for device in docker_app_devices %}
- {{ device }}
{% endfor %}
{% endif %}
{% if docker_app_cap_add | length > 0 %}
cap_add:
{% for capability in docker_app_cap_add %}
- {{ capability }}
{% endfor %}
{% endif %}
{% if docker_app_group_add | length > 0 %}
group_add:
{% for group in docker_app_group_add %}
- "{{ group }}"
{% endfor %}
{% endif %}
{% if docker_app_extra_hosts | length > 0 %}
extra_hosts:
{% for host in docker_app_extra_hosts %}
- {{ host }}
{% endfor %}
{% endif %}
{% if docker_app_depends_on | length > 0 %}
depends_on:
{% for dependency in docker_app_depends_on %}
- {{ dependency }}
{% endfor %}
{% endif %}
{% if docker_app_routing_mode == "traefik" and docker_app_network_mode | length == 0 %}
networks:
- {{ docker_app_traefik_network }}
{% endif %}
{% if docker_app_routing_mode == "traefik" and docker_app_network_mode | length == 0 %}
networks:
{{ docker_app_traefik_network }}:
external: true
{% endif %}
+4
View File
@@ -0,0 +1,4 @@
{% for key, value in docker_app_env.items() %}
{{ key }}={{ value }}
{% endfor %}
+4
View File
@@ -0,0 +1,4 @@
# filebrowser_quantum
Deploys Filebrowser Quantum with Docker Compose.
@@ -0,0 +1,19 @@
---
filebrowser_quantum_name: filebrowser-quantum
filebrowser_quantum_dir: /srv/docker/filebrowser-quantum
filebrowser_quantum_image: gtstef/filebrowser:latest
filebrowser_quantum_domain: files.example.invalid
filebrowser_quantum_service_port: 80
filebrowser_quantum_routing_mode: "{{ nbops_service_routing_mode }}"
filebrowser_quantum_env:
TZ: "{{ nbops_timezone }}"
filebrowser_quantum_directories:
- "{{ filebrowser_quantum_dir }}"
- /srv/config/filebrowser-quantum
- /srv/data/files
filebrowser_quantum_volumes:
- /srv/config/filebrowser-quantum:/config
- /srv/data/files:/srv
filebrowser_quantum_ports:
- { host: 8089, container: 80, protocol: tcp }
+1
View File
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+18
View File
@@ -0,0 +1,18 @@
---
- name: Deploy Filebrowser Quantum
ansible.builtin.include_role:
name: docker_app
vars:
docker_app_env_template_src: "{{ playbook_dir }}/../roles/filebrowser_quantum/templates/env.j2"
docker_app_compose_template_src: "{{ playbook_dir }}/../roles/filebrowser_quantum/templates/compose.yaml.j2"
docker_app_name: "{{ filebrowser_quantum_name }}"
docker_app_dir: "{{ filebrowser_quantum_dir }}"
docker_app_image: "{{ filebrowser_quantum_image }}"
docker_app_env: "{{ filebrowser_quantum_env }}"
docker_app_directories: "{{ filebrowser_quantum_directories }}"
docker_app_volumes: "{{ filebrowser_quantum_volumes }}"
docker_app_ports: "{{ filebrowser_quantum_ports }}"
docker_app_routing_mode: "{{ filebrowser_quantum_routing_mode }}"
docker_app_domain: "{{ filebrowser_quantum_domain }}"
docker_app_service_port: "{{ filebrowser_quantum_service_port }}"
@@ -0,0 +1 @@
@@ -0,0 +1,90 @@
services:
{{ docker_app_name }}:
image: {{ docker_app_image }}
restart: {{ docker_app_restart }}
{% if docker_app_command | length > 0 %}
command: {{ docker_app_command | to_json }}
{% endif %}
{% if docker_app_user | length > 0 %}
user: "{{ docker_app_user }}"
{% endif %}
{% if docker_app_network_mode | length > 0 %}
network_mode: {{ docker_app_network_mode }}
{% endif %}
{% if docker_app_privileged | bool %}
privileged: true
{% endif %}
{% if docker_app_env | length > 0 %}
env_file: .env
{% endif %}
{% if docker_app_routing_mode == "host" and docker_app_ports | length > 0 %}
ports:
{% for port in docker_app_ports %}
- "{{ port.bind | default('0.0.0.0') }}:{{ port.host | default(port.container) }}:{{ port.container }}{% if port.protocol | default('tcp') != 'tcp' %}/{{ port.protocol }}{% endif %}"
{% endfor %}
{% endif %}
{% if docker_app_routing_mode == "traefik" and docker_app_service_port %}
# internal service port: {{ docker_app_service_port }}; not published on the Docker host
labels:
- traefik.enable=true
- traefik.http.routers.{{ docker_app_name }}.rule={{ docker_app_traefik_rule }}
- traefik.http.routers.{{ docker_app_name }}.entrypoints={{ docker_app_traefik_entrypoint }}
- traefik.http.routers.{{ docker_app_name }}.tls={{ docker_app_traefik_tls | bool | ternary('true', 'false') }}
{% if docker_app_traefik_middlewares | length > 0 %}
- traefik.http.routers.{{ docker_app_name }}.middlewares={{ docker_app_traefik_middlewares | join(',') }}
{% endif %}
- traefik.http.services.{{ docker_app_name }}.loadbalancer.server.port={{ docker_app_service_port }}
{% for key, value in docker_app_labels.items() %}
- {{ key }}={{ value }}
{% endfor %}
{% elif docker_app_labels | length > 0 %}
labels:
{% for key, value in docker_app_labels.items() %}
- {{ key }}={{ value }}
{% endfor %}
{% endif %}
{% if docker_app_volumes | length > 0 %}
volumes:
{% for volume in docker_app_volumes %}
- {{ volume }}
{% endfor %}
{% endif %}
{% if docker_app_devices | length > 0 %}
devices:
{% for device in docker_app_devices %}
- {{ device }}
{% endfor %}
{% endif %}
{% if docker_app_cap_add | length > 0 %}
cap_add:
{% for capability in docker_app_cap_add %}
- {{ capability }}
{% endfor %}
{% endif %}
{% if docker_app_group_add | length > 0 %}
group_add:
{% for group in docker_app_group_add %}
- "{{ group }}"
{% endfor %}
{% endif %}
{% if docker_app_extra_hosts | length > 0 %}
extra_hosts:
{% for host in docker_app_extra_hosts %}
- {{ host }}
{% endfor %}
{% endif %}
{% if docker_app_depends_on | length > 0 %}
depends_on:
{% for dependency in docker_app_depends_on %}
- {{ dependency }}
{% endfor %}
{% endif %}
{% if docker_app_routing_mode == "traefik" and docker_app_network_mode | length == 0 %}
networks:
- {{ docker_app_traefik_network }}
{% endif %}
{% if docker_app_routing_mode == "traefik" and docker_app_network_mode | length == 0 %}
networks:
{{ docker_app_traefik_network }}:
external: true
{% endif %}
@@ -0,0 +1,4 @@
{% for key, value in docker_app_env.items() %}
{{ key }}={{ value }}
{% endfor %}
+4
View File
@@ -0,0 +1,4 @@
# fiveetools
Deploys 5etools with Docker Compose.
+14
View File
@@ -0,0 +1,14 @@
---
fiveetools_name: fiveetools
fiveetools_dir: /srv/docker/fiveetools
fiveetools_image: jafner/5etools:latest
fiveetools_domain: 5etools.example.invalid
fiveetools_service_port: 80
fiveetools_routing_mode: "{{ nbops_service_routing_mode }}"
fiveetools_env: {}
fiveetools_directories:
- "{{ fiveetools_dir }}"
fiveetools_volumes: []
fiveetools_ports:
- { host: 8087, container: 80, protocol: tcp }
+1
View File
@@ -0,0 +1 @@
+1
View File
@@ -0,0 +1 @@
+18
View File
@@ -0,0 +1,18 @@
---
- name: Deploy 5etools
ansible.builtin.include_role:
name: docker_app
vars:
docker_app_env_template_src: "{{ playbook_dir }}/../roles/fiveetools/templates/env.j2"
docker_app_compose_template_src: "{{ playbook_dir }}/../roles/fiveetools/templates/compose.yaml.j2"
docker_app_name: "{{ fiveetools_name }}"
docker_app_dir: "{{ fiveetools_dir }}"
docker_app_image: "{{ fiveetools_image }}"
docker_app_env: "{{ fiveetools_env }}"
docker_app_directories: "{{ fiveetools_directories }}"
docker_app_volumes: "{{ fiveetools_volumes }}"
docker_app_ports: "{{ fiveetools_ports }}"
docker_app_routing_mode: "{{ fiveetools_routing_mode }}"
docker_app_domain: "{{ fiveetools_domain }}"
docker_app_service_port: "{{ fiveetools_service_port }}"
+1
View File
@@ -0,0 +1 @@
@@ -0,0 +1,90 @@
services:
{{ docker_app_name }}:
image: {{ docker_app_image }}
restart: {{ docker_app_restart }}
{% if docker_app_command | length > 0 %}
command: {{ docker_app_command | to_json }}
{% endif %}
{% if docker_app_user | length > 0 %}
user: "{{ docker_app_user }}"
{% endif %}
{% if docker_app_network_mode | length > 0 %}
network_mode: {{ docker_app_network_mode }}
{% endif %}
{% if docker_app_privileged | bool %}
privileged: true
{% endif %}
{% if docker_app_env | length > 0 %}
env_file: .env
{% endif %}
{% if docker_app_routing_mode == "host" and docker_app_ports | length > 0 %}
ports:
{% for port in docker_app_ports %}
- "{{ port.bind | default('0.0.0.0') }}:{{ port.host | default(port.container) }}:{{ port.container }}{% if port.protocol | default('tcp') != 'tcp' %}/{{ port.protocol }}{% endif %}"
{% endfor %}
{% endif %}
{% if docker_app_routing_mode == "traefik" and docker_app_service_port %}
# internal service port: {{ docker_app_service_port }}; not published on the Docker host
labels:
- traefik.enable=true
- traefik.http.routers.{{ docker_app_name }}.rule={{ docker_app_traefik_rule }}
- traefik.http.routers.{{ docker_app_name }}.entrypoints={{ docker_app_traefik_entrypoint }}
- traefik.http.routers.{{ docker_app_name }}.tls={{ docker_app_traefik_tls | bool | ternary('true', 'false') }}
{% if docker_app_traefik_middlewares | length > 0 %}
- traefik.http.routers.{{ docker_app_name }}.middlewares={{ docker_app_traefik_middlewares | join(',') }}
{% endif %}
- traefik.http.services.{{ docker_app_name }}.loadbalancer.server.port={{ docker_app_service_port }}
{% for key, value in docker_app_labels.items() %}
- {{ key }}={{ value }}
{% endfor %}
{% elif docker_app_labels | length > 0 %}
labels:
{% for key, value in docker_app_labels.items() %}
- {{ key }}={{ value }}
{% endfor %}
{% endif %}
{% if docker_app_volumes | length > 0 %}
volumes:
{% for volume in docker_app_volumes %}
- {{ volume }}
{% endfor %}
{% endif %}
{% if docker_app_devices | length > 0 %}
devices:
{% for device in docker_app_devices %}
- {{ device }}
{% endfor %}
{% endif %}
{% if docker_app_cap_add | length > 0 %}
cap_add:
{% for capability in docker_app_cap_add %}
- {{ capability }}
{% endfor %}
{% endif %}
{% if docker_app_group_add | length > 0 %}
group_add:
{% for group in docker_app_group_add %}
- "{{ group }}"
{% endfor %}
{% endif %}
{% if docker_app_extra_hosts | length > 0 %}
extra_hosts:
{% for host in docker_app_extra_hosts %}
- {{ host }}
{% endfor %}
{% endif %}
{% if docker_app_depends_on | length > 0 %}
depends_on:
{% for dependency in docker_app_depends_on %}
- {{ dependency }}
{% endfor %}
{% endif %}
{% if docker_app_routing_mode == "traefik" and docker_app_network_mode | length == 0 %}
networks:
- {{ docker_app_traefik_network }}
{% endif %}
{% if docker_app_routing_mode == "traefik" and docker_app_network_mode | length == 0 %}
networks:
{{ docker_app_traefik_network }}:
external: true
{% endif %}
+4
View File
@@ -0,0 +1,4 @@
{% for key, value in docker_app_env.items() %}
{{ key }}={{ value }}
{% endfor %}
+4
View File
@@ -0,0 +1,4 @@
# flaresolverr
Deploys FlareSolverr with Docker Compose.
+16
View File
@@ -0,0 +1,16 @@
---
flaresolverr_name: flaresolverr
flaresolverr_dir: /srv/docker/flaresolverr
flaresolverr_image: ghcr.io/flaresolverr/flaresolverr:latest
flaresolverr_domain: flaresolverr.example.invalid
flaresolverr_service_port: 8191
flaresolverr_routing_mode: "{{ nbops_service_routing_mode }}"
flaresolverr_env:
LOG_LEVEL: info
TZ: "{{ nbops_timezone }}"
flaresolverr_directories:
- "{{ flaresolverr_dir }}"
flaresolverr_volumes: []
flaresolverr_ports:
- { host: 8191, container: 8191, protocol: tcp }
Loaded 100 of 411 files, more files were not shown because too many files have changed in this diff. Show more