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:
commit
53695f4fff
411 files changed
+7817
No files matched your search
+15
@@ -0,0 +1,15 @@
|
||||
*.retry
|
||||
.ansible/
|
||||
.cache/
|
||||
.vault-pass
|
||||
*.log
|
||||
*.pem
|
||||
*.key
|
||||
*.crt
|
||||
*.csr
|
||||
*.p12
|
||||
*.env
|
||||
secrets.yml
|
||||
vault.yml
|
||||
|
||||
TODO.md
|
||||
@@ -0,0 +1,278 @@
|
||||
# nbops-ansible
|
||||
|
||||
## nbops
|
||||
#### Infrastructure as Code for notBoris' Homelab
|
||||
|
||||
nbops is the central operations repository for my hosted infrastructure, containing everything required to provision, configure, and maintain my virtual machines and services. The goal is to treat my infrastructure as code for reproducibility and automation from source control, while having minimal manual intervention.
|
||||
|
||||
## About
|
||||
|
||||
nbops-ansible contains the configuration management layer of the nbops platform.
|
||||
|
||||
Every virtual machine begins life as a standardised Debian 13 GenericCloud template, provisioned by Proxmox and bootstrapped with Cloud-Init. Once the operating system has completed its initial configuration, the host runs ansible-pull against this repository to configure itself according to its assigned role.
|
||||
|
||||
A freshly created virtual machine should require little more than a hostname, network configuration and SSH key before automatically configuring itself into a production-ready host.
|
||||
|
||||
|
||||
# Design Philosophy
|
||||
|
||||
This repository follows a layered approach where each component has a clearly defined responsibility.
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A[GitHub: nbops-ansible] --> B[Proxmox VE]
|
||||
B --> C[Debian 13 GenericCloud Template]
|
||||
C --> D[Clone VM]
|
||||
D --> E[Cloud-Init Bootstrap]
|
||||
E --> F[ansible-pull]
|
||||
F --> G[Apply Base Roles]
|
||||
G --> H[Apply Service Roles]
|
||||
H --> I[Docker Compose]
|
||||
I --> J[Running Services]
|
||||
```
|
||||
|
||||
## Layer Responsibilities
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
P[Proxmox VE<br/>VM lifecycle<br/>storage<br/>networking]
|
||||
D[Debian 13<br/>GenericCloud<br/>minimal OS]
|
||||
C[Cloud-Init<br/>identity<br/>network<br/>bootstrap]
|
||||
A[Ansible<br/>desired state<br/>roles<br/>configuration]
|
||||
DC[Docker Compose<br/>service runtime]
|
||||
S[Applications<br/>actual workloads]
|
||||
|
||||
P --> D --> C --> A --> DC --> S
|
||||
```
|
||||
|
||||
# Repository Structure
|
||||
|
||||
.
|
||||
├── inventories/
|
||||
│ ├── production/
|
||||
│ └── development/
|
||||
│
|
||||
├── playbooks/
|
||||
│
|
||||
├── roles/
|
||||
│ ├── base/
|
||||
│ ├── docker/
|
||||
│ ├── ssh/
|
||||
│ ├── ufw/
|
||||
│ ├── mtls/
|
||||
│ ├── monitoring/
|
||||
│ ├── traefik/
|
||||
│ ├── authentik/
|
||||
│ ├── immich/
|
||||
│ ├── gitea/
|
||||
│ ├── arr/
|
||||
│ ├── ocis/
|
||||
│ └── pterodactyl/
|
||||
│
|
||||
├── group_vars/
|
||||
├── host_vars/
|
||||
├── templates/
|
||||
├── files/
|
||||
├── scripts/
|
||||
└── docs/
|
||||
|
||||
As the infrastructure grows, additional functionality should be implemented as new roles rather than modifying existing ones wherever practical.
|
||||
|
||||
|
||||
# Configuration Workflow
|
||||
|
||||
Cloud-Init is intentionally lightweight.
|
||||
|
||||
Its responsibility is limited to preparing the operating system by configuring:
|
||||
|
||||
* Hostname
|
||||
* Networking
|
||||
* DNS
|
||||
* Users
|
||||
* SSH
|
||||
* Docker
|
||||
* Firewall
|
||||
* Internal CA trust
|
||||
* Base packages
|
||||
|
||||
Once complete, Cloud-Init hands over to Ansible.
|
||||
|
||||
Ansible is responsible for configuring and maintaining the desired state of every machine for the remainder of its lifecycle.
|
||||
|
||||
# Roles
|
||||
|
||||
The repository is built around reusable Ansible roles; every server receives a common baseline before additional service-specific roles are applied.
|
||||
|
||||
## Core Roles
|
||||
|
||||
* base
|
||||
* docker
|
||||
* ssh
|
||||
* ufw
|
||||
* mtls
|
||||
* monitoring
|
||||
* updates
|
||||
|
||||
## Service Roles
|
||||
|
||||
* traefik
|
||||
* authentik
|
||||
* gitea
|
||||
* Pelican
|
||||
* etc..
|
||||
|
||||
This approach allows new services to inherit the same security, logging and operating system configuration while remaining independent from one another.
|
||||
|
||||
# Operating System Standard
|
||||
|
||||
Unless otherwise required, every virtual machine deployed by nbops follows the same baseline.
|
||||
|
||||
* Debian 13 (Trixie)
|
||||
* GenericCloud image
|
||||
* UEFI (OVMF)
|
||||
* Q35 machine type
|
||||
* VirtIO networking
|
||||
* VirtIO SCSI storage
|
||||
* QEMU Guest Agent
|
||||
* Docker Engine
|
||||
* Docker Compose Plugin
|
||||
* Chrony
|
||||
* UFW
|
||||
* SSH key authentication only
|
||||
|
||||
Standardising the operating system significantly reduces maintenance overhead and configuration drift across the environment.
|
||||
|
||||
# Infrastructure Principles
|
||||
|
||||
The following principles guide every decision made within this repository.
|
||||
|
||||
* Everything should be reproducible.
|
||||
* Everything should be version controlled.
|
||||
* Manual configuration should be avoided.
|
||||
* Infrastructure should be disposable rather than unique.
|
||||
* Security should be built into the baseline.
|
||||
* Applications should remain isolated from the operating system wherever possible.
|
||||
* Configuration should be modular and reusable.
|
||||
|
||||
# Future Goals
|
||||
|
||||
Planned work includes:
|
||||
|
||||
* Automated Debian template creation
|
||||
* Cloud-Init template generation
|
||||
* Automated certificate management
|
||||
* Secret management
|
||||
* Monitoring and alerting
|
||||
* Backup automation
|
||||
* Configuration validation
|
||||
* Disaster recovery testing
|
||||
* Multi-node infrastructure support
|
||||
|
||||
# Contributing
|
||||
|
||||
This repository is primarily maintained for my own homelab, but the techniques and playbooks may be useful to others building similar self-hosted environments.
|
||||
|
||||
Suggestions, improvements and ideas are always welcome.
|
||||
|
||||
# License
|
||||
|
||||
Unless otherwise stated, all code within this repository is released under the MIT License.
|
||||
# nbops-ansible
|
||||
|
||||
Ansible repository for a reproducible Proxmox-based Infrastructure as Code
|
||||
platform using Debian 13 GenericCloud, Cloud-Init, Docker Engine, Docker Compose,
|
||||
and `ansible-pull`.
|
||||
|
||||
## Model
|
||||
|
||||
```text
|
||||
Proxmox
|
||||
-> Debian 13 GenericCloud template
|
||||
-> clone
|
||||
-> Cloud-Init
|
||||
-> ansible-pull
|
||||
-> base roles
|
||||
-> service roles
|
||||
-> Docker Compose
|
||||
```
|
||||
|
||||
Cloud-Init handles Day 0 bootstrap only. Ansible owns Day 1 onward state.
|
||||
|
||||
## Layout
|
||||
|
||||
- `inventories/`: production and development inventories
|
||||
- `playbooks/`: site and pull entrypoints
|
||||
- `roles/`: reusable core and application roles
|
||||
- `group_vars/`: shared defaults
|
||||
- `host_vars/`: per-VM overrides
|
||||
- `templates/`: Cloud-Init and shared templates
|
||||
- `scripts/`: local helper scripts
|
||||
- `docs/`: operational notes
|
||||
|
||||
## Roles
|
||||
|
||||
Core roles:
|
||||
|
||||
- `base`
|
||||
- `ssh`
|
||||
- `ufw`
|
||||
- `updates`
|
||||
- `docker`
|
||||
- `mtls`
|
||||
- `monitoring`
|
||||
|
||||
Application roles:
|
||||
|
||||
- `traefik`
|
||||
- `authentik`
|
||||
- `immich`
|
||||
- `gitea`
|
||||
- `ocis`
|
||||
- `pterodactyl`
|
||||
- `arr`
|
||||
|
||||
Enable roles per host with `nbops_roles`.
|
||||
|
||||
```yaml
|
||||
nbops_roles:
|
||||
- base
|
||||
- ssh
|
||||
- ufw
|
||||
- updates
|
||||
- docker
|
||||
- mtls
|
||||
- monitoring
|
||||
- traefik
|
||||
```
|
||||
|
||||
## VM Topology
|
||||
|
||||
Every VM is expected to run Docker. Put `docker` in `nbops_roles` for each VM.
|
||||
|
||||
The edge VM normally runs `traefik` and `authentik`, with Traefik publishing
|
||||
ports `80` and `443`. Downstream VMs can run their own internal Traefik and set
|
||||
`nbops_service_routing_mode: traefik` so applications register with Traefik
|
||||
instead of exposing individual host ports.
|
||||
|
||||
See `host_vars/edge.yml.example`, `host_vars/downstream.yml.example`, and
|
||||
`docs/topology.md`. The full Docker service list is in `docs/service-catalog.md`.
|
||||
|
||||
## Bootstrap
|
||||
|
||||
Render `templates/cloud-init-user-data.yml.j2` with host variables and pass it to
|
||||
Proxmox as user-data. The template installs baseline packages, Docker from
|
||||
Docker's official Debian repository, configures UFW and SSH, creates `/srv` and
|
||||
PKI directories, and executes `ansible-pull`.
|
||||
|
||||
## Secrets
|
||||
|
||||
Secrets are intentionally excluded. Do not commit passwords, API keys,
|
||||
certificates, private keys, or generated `.env` files. Use Ansible Vault, SOPS,
|
||||
Vault, or Smallstep-backed issuance when introducing real secret material.
|
||||
|
||||
## Validation
|
||||
|
||||
```sh
|
||||
ansible-galaxy collection install -r requirements.yml
|
||||
scripts/check.sh
|
||||
```
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
[defaults]
|
||||
inventory = inventories/production/hosts.yml
|
||||
roles_path = roles
|
||||
collections_path = .ansible/collections:~/.ansible/collections:/usr/share/ansible/collections
|
||||
stdout_callback = yaml
|
||||
bin_ansible_callbacks = True
|
||||
host_key_checking = True
|
||||
retry_files_enabled = False
|
||||
interpreter_python = auto_silent
|
||||
fact_caching = jsonfile
|
||||
fact_caching_connection = .ansible/facts
|
||||
fact_caching_timeout = 86400
|
||||
|
||||
[privilege_escalation]
|
||||
become = True
|
||||
become_method = sudo
|
||||
become_ask_pass = False
|
||||
|
||||
[ssh_connection]
|
||||
pipelining = True
|
||||
ssh_args = -o ControlMaster=auto -o ControlPersist=60s
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
# Operations
|
||||
|
||||
Install collections:
|
||||
|
||||
```sh
|
||||
ansible-galaxy collection install -r requirements.yml
|
||||
```
|
||||
|
||||
Run a syntax check:
|
||||
|
||||
```sh
|
||||
ansible-playbook --syntax-check playbooks/site.yml
|
||||
```
|
||||
|
||||
Run locally:
|
||||
|
||||
```sh
|
||||
ansible-playbook -i inventories/development/hosts.yml playbooks/site.yml
|
||||
```
|
||||
|
||||
Run selected tags:
|
||||
|
||||
```sh
|
||||
ansible-playbook -i inventories/production/hosts.yml playbooks/site.yml --tags base,docker,traefik
|
||||
```
|
||||
|
||||
Run with ansible-pull:
|
||||
|
||||
```sh
|
||||
REPO_URL=git@github.com:example/nbops-ansible.git scripts/ansible-pull.sh
|
||||
```
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
# Proxmox Debian 13 GenericCloud Template
|
||||
|
||||
Expected VM defaults:
|
||||
|
||||
- Debian 13 Trixie GenericCloud image
|
||||
- Q35 machine type
|
||||
- UEFI / OVMF
|
||||
- VirtIO SCSI controller
|
||||
- VirtIO network device
|
||||
- CPU type `host`
|
||||
- QEMU Guest Agent enabled
|
||||
- Cloud-Init drive attached
|
||||
- 2 vCPU, 2 GB RAM, 32 GB disk unless a service needs more
|
||||
|
||||
Cloud-Init is only responsible for Day 0 bootstrap: user creation, SSH
|
||||
hardening, Docker installation, baseline packages, firewall defaults, directory
|
||||
layout, PKI directory preparation, and `ansible-pull`.
|
||||
|
||||
Applications and durable service configuration belong in Ansible roles.
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
# Secrets
|
||||
|
||||
Do not commit passwords, API keys, issued certificates, private keys, or `.env`
|
||||
files containing secrets.
|
||||
|
||||
This repository is ready for these future integrations:
|
||||
|
||||
- Ansible Vault
|
||||
- SOPS
|
||||
- HashiCorp Vault
|
||||
- Smallstep certificates
|
||||
|
||||
Role defaults deliberately use `CHANGE_ME_IN_VAULT` placeholders for required
|
||||
application secrets. Override those values in encrypted inventory data before
|
||||
deploying real services.
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
# Service Catalog
|
||||
|
||||
These Docker service roles are available through `nbops_roles`.
|
||||
|
||||
Media and downloads:
|
||||
|
||||
- `jellyfin`
|
||||
- `jellyseerr`
|
||||
- `qbittorrent`
|
||||
- `transmission`
|
||||
- `wizarr`
|
||||
- `flaresolverr`
|
||||
- `cleanuparr`
|
||||
- `pinchflat`
|
||||
- `kavita`
|
||||
|
||||
Identity and access:
|
||||
|
||||
- `authentik`
|
||||
- `authentik_ldap`
|
||||
- `authentik_proxy`
|
||||
- `authentik_rac`
|
||||
- `vaultwarden`
|
||||
|
||||
Dashboards and file tools:
|
||||
|
||||
- `homarr`
|
||||
- `nginx_content`
|
||||
- `code_server`
|
||||
- `filebrowser_quantum`
|
||||
- `kiwix_serve`
|
||||
- `requarks`
|
||||
- `fiveetools`
|
||||
- `ntfy`
|
||||
|
||||
Infrastructure:
|
||||
|
||||
- `pelican_panel`
|
||||
- `pelican_server`
|
||||
- `gitea`
|
||||
- `gitea_act_runner`
|
||||
- `postgres`
|
||||
- `php_fpm`
|
||||
- `valkey`
|
||||
- `syncthing`
|
||||
- `ipv6nat`
|
||||
|
||||
Monitoring:
|
||||
|
||||
- `grafana`
|
||||
- `loki`
|
||||
- `alloy`
|
||||
- `grafana_renderer`
|
||||
- `prometheus`
|
||||
- `uptime_kuma`
|
||||
|
||||
Most roles use the shared `docker_app` implementation and support:
|
||||
|
||||
- `host` routing mode: publish ports directly and open UFW rules.
|
||||
- `traefik` routing mode: attach to the proxy network and render Traefik labels.
|
||||
|
||||
Override role defaults in `host_vars` or `group_vars`; do not edit generated
|
||||
Compose files on the VM.
|
||||
@@ -0,0 +1,59 @@
|
||||
# Topology
|
||||
|
||||
Every VM is a Docker host. Proxmox and Cloud-Init only create the machine and
|
||||
bootstrap Ansible; service placement is controlled by inventory.
|
||||
|
||||
## Edge VM
|
||||
|
||||
The edge VM runs:
|
||||
|
||||
- Docker
|
||||
- Traefik
|
||||
- authentik IAM
|
||||
|
||||
It publishes Traefik entrypoints on host ports `80` and `443`. authentik can run
|
||||
behind the same local Traefik instance by setting:
|
||||
|
||||
```yaml
|
||||
nbops_service_routing_mode: traefik
|
||||
authentik_domain: auth.example.invalid
|
||||
```
|
||||
|
||||
## Downstream VMs
|
||||
|
||||
Downstream VMs also run Docker. Instead of publishing each application port on
|
||||
the host, run Traefik on the VM and attach application containers to the shared
|
||||
Docker proxy network.
|
||||
|
||||
```yaml
|
||||
nbops_roles:
|
||||
- base
|
||||
- ssh
|
||||
- ufw
|
||||
- updates
|
||||
- docker
|
||||
- traefik
|
||||
- gitea
|
||||
- immich
|
||||
|
||||
nbops_service_routing_mode: traefik
|
||||
nbops_traefik_entrypoint: web
|
||||
nbops_traefik_tls: false
|
||||
```
|
||||
|
||||
In this mode application roles do not publish their HTTP ports. They render
|
||||
Traefik labels and join `nbops_proxy_network`, which defaults to `proxy`.
|
||||
|
||||
Only Traefik entrypoints should be exposed on the downstream host firewall. Use
|
||||
`traefik_bind_address` to bind those entrypoints to the VM's internal address.
|
||||
|
||||
## Routing Modes
|
||||
|
||||
Application roles support:
|
||||
|
||||
- `host`: publish application ports directly and open those ports in UFW.
|
||||
- `traefik`: do not publish HTTP ports; attach to Traefik and render labels.
|
||||
|
||||
Gitea is special: HTTP can be routed through Traefik while SSH remains published
|
||||
with `gitea_publish_ssh: true`.
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
# Group Variables
|
||||
|
||||
Group variables define shared defaults for every VM. Override them in environment
|
||||
or host-specific files rather than editing role tasks.
|
||||
|
||||
Secrets, private keys, API tokens, and issued certificates must not be committed.
|
||||
Use Ansible Vault, SOPS, Vault, or Smallstep-backed workflows when those values
|
||||
are introduced.
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
---
|
||||
nbops_environment: production
|
||||
nbops_admin_user: admin
|
||||
nbops_admin_groups:
|
||||
- sudo
|
||||
nbops_hostname: "{{ inventory_hostname }}"
|
||||
nbops_fqdn: "{{ inventory_hostname }}"
|
||||
nbops_timezone: Etc/UTC
|
||||
nbops_locale: en_GB.UTF-8
|
||||
nbops_ssh_port: 22
|
||||
nbops_dns_servers:
|
||||
- 1.1.1.1
|
||||
- 9.9.9.9
|
||||
|
||||
nbops_standard_directories:
|
||||
- /srv/docker
|
||||
- /srv/config
|
||||
- /srv/backups
|
||||
- /srv/data
|
||||
- /srv/media
|
||||
- /etc/pki/internal
|
||||
- /etc/pki/internal/private
|
||||
- /etc/pki/internal/certs
|
||||
- /usr/local/share/ca-certificates/internal
|
||||
|
||||
nbops_base_packages:
|
||||
- acl
|
||||
- apt-transport-https
|
||||
- bash-completion
|
||||
- ca-certificates
|
||||
- chrony
|
||||
- curl
|
||||
- git
|
||||
- gnupg
|
||||
- htop
|
||||
- jq
|
||||
- locales
|
||||
- lsb-release
|
||||
- needrestart
|
||||
- qemu-guest-agent
|
||||
- rsync
|
||||
- sudo
|
||||
- ufw
|
||||
- unattended-upgrades
|
||||
- vim
|
||||
|
||||
nbops_sysctl:
|
||||
net.ipv4.ip_forward: "1"
|
||||
net.ipv6.conf.all.forwarding: "1"
|
||||
fs.inotify.max_user_watches: "1048576"
|
||||
fs.inotify.max_user_instances: "1024"
|
||||
|
||||
docker_users:
|
||||
- "{{ nbops_admin_user }}"
|
||||
|
||||
ufw_allowed_tcp_ports:
|
||||
- "{{ nbops_ssh_port }}"
|
||||
ufw_allowed_udp_ports: []
|
||||
|
||||
monitoring_agent: none
|
||||
monitoring_packages: []
|
||||
|
||||
mtls_ca_certificates: []
|
||||
mtls_host_certificates: []
|
||||
mtls_private_keys: []
|
||||
|
||||
nbops_proxy_network: proxy
|
||||
nbops_service_routing_mode: host
|
||||
nbops_service_domain_suffix: example.invalid
|
||||
nbops_traefik_entrypoint: websecure
|
||||
nbops_traefik_tls: true
|
||||
@@ -0,0 +1,7 @@
|
||||
# Host Variables
|
||||
|
||||
Create one file per VM, for example `host_vars/media-01.yml`.
|
||||
|
||||
Use host variables for hostnames, service enablement, storage paths, and port
|
||||
assignments. Keep credentials and TLS private material outside Git.
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
---
|
||||
nbops_hostname: cloud
|
||||
nbops_fqdn: cloud.example.internal
|
||||
nbops_roles:
|
||||
- base
|
||||
- ssh
|
||||
- ufw
|
||||
- updates
|
||||
- docker
|
||||
- mtls
|
||||
- monitoring
|
||||
- traefik
|
||||
- ocis
|
||||
|
||||
nbops_service_routing_mode: traefik
|
||||
nbops_traefik_entrypoint: web
|
||||
nbops_traefik_tls: false
|
||||
|
||||
# Bind this to the VM's internal/LAN address in production.
|
||||
traefik_bind_address: "0.0.0.0"
|
||||
traefik_publish_entrypoints: true
|
||||
traefik_domain: apps-01.example.internal
|
||||
traefik_forwarded_headers_trusted_ips:
|
||||
- 10.0.0.0/8
|
||||
- 172.16.0.0/12
|
||||
- 192.168.0.0/16
|
||||
|
||||
ocis_url: https://files.example.internal
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
---
|
||||
nbops_hostname: edge
|
||||
nbops_fqdn: edge.example.invalid
|
||||
nbops_roles:
|
||||
- base
|
||||
- ssh
|
||||
- ufw
|
||||
- updates
|
||||
- docker
|
||||
- mtls
|
||||
- monitoring
|
||||
- traefik
|
||||
- authentik
|
||||
- authentik_rac
|
||||
- authentik_ldap
|
||||
- authentik_worker
|
||||
|
||||
nbops_service_routing_mode: traefik
|
||||
nbops_service_domain_suffix: example.invalid
|
||||
nbops_traefik_entrypoint: websecure
|
||||
nbops_traefik_tls: true
|
||||
|
||||
traefik_bind_address: "0.0.0.0"
|
||||
traefik_publish_entrypoints: true
|
||||
traefik_domain: example.invalid
|
||||
traefik_acme_email: admin@example.invalid
|
||||
|
||||
authentik_domain: auth.example.invalid
|
||||
authentik_http_port: 9000
|
||||
authentik_https_port: 9443
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
---
|
||||
nbops_hostname: apps-01
|
||||
nbops_fqdn: apps-01.example.internal
|
||||
nbops_roles:
|
||||
- base
|
||||
- ssh
|
||||
- ufw
|
||||
- updates
|
||||
- docker
|
||||
- mtls
|
||||
- monitoring
|
||||
- traefik
|
||||
- gitea
|
||||
- immich
|
||||
- ocis
|
||||
- arr
|
||||
|
||||
nbops_service_routing_mode: traefik
|
||||
nbops_traefik_entrypoint: web
|
||||
nbops_traefik_tls: false
|
||||
|
||||
# Bind this to the VM's internal/LAN address in production.
|
||||
traefik_bind_address: "0.0.0.0"
|
||||
traefik_publish_entrypoints: true
|
||||
traefik_domain: apps-01.example.internal
|
||||
traefik_forwarded_headers_trusted_ips:
|
||||
- 10.0.0.0/8
|
||||
- 172.16.0.0/12
|
||||
- 192.168.0.0/16
|
||||
|
||||
gitea_domain: git.example.internal
|
||||
gitea_publish_ssh: true
|
||||
immich_domain: photos.example.internal
|
||||
ocis_url: https://files.example.internal
|
||||
|
||||
arr_services:
|
||||
prowlarr:
|
||||
image: lscr.io/linuxserver/prowlarr:latest
|
||||
port: 9696
|
||||
domain: prowlarr.example.internal
|
||||
sonarr:
|
||||
image: lscr.io/linuxserver/sonarr:latest
|
||||
port: 8989
|
||||
domain: sonarr.example.internal
|
||||
radarr:
|
||||
image: lscr.io/linuxserver/radarr:latest
|
||||
port: 7878
|
||||
domain: radarr.example.internal
|
||||
lidarr:
|
||||
image: lscr.io/linuxserver/lidarr:latest
|
||||
port: 8686
|
||||
domain: lidarr.example.internal
|
||||
bazarr:
|
||||
image: lscr.io/linuxserver/bazarr:latest
|
||||
port: 6767
|
||||
domain: bazarr.example.internal
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
---
|
||||
nbops_hostname: pelican-node-01
|
||||
nbops_fqdn: pelican-node-01.example.internal
|
||||
nbops_roles:
|
||||
- base
|
||||
- ssh
|
||||
- ufw
|
||||
- updates
|
||||
- docker
|
||||
- pelican_server
|
||||
|
||||
pelican_server_daemon_port: 8080
|
||||
pelican_server_sftp_port: 2022
|
||||
pelican_server_allocation_ports:
|
||||
- { port: "25565:25575", proto: tcp }
|
||||
@@ -0,0 +1,29 @@
|
||||
---
|
||||
nbops_hostname: cloud
|
||||
nbops_fqdn: cloud.example.internal
|
||||
nbops_roles:
|
||||
- base
|
||||
- ssh
|
||||
- ufw
|
||||
- updates
|
||||
- docker
|
||||
- mtls
|
||||
- monitoring
|
||||
- traefik
|
||||
- ocis
|
||||
|
||||
nbops_service_routing_mode: traefik
|
||||
nbops_traefik_entrypoint: web
|
||||
nbops_traefik_tls: false
|
||||
|
||||
# Bind this to the VM's internal/LAN address in production.
|
||||
traefik_bind_address: "0.0.0.0"
|
||||
traefik_publish_entrypoints: true
|
||||
traefik_domain: apps-01.example.internal
|
||||
traefik_forwarded_headers_trusted_ips:
|
||||
- 10.0.0.0/8
|
||||
- 172.16.0.0/12
|
||||
- 192.168.0.0/16
|
||||
|
||||
ocis_url: https://files.example.internal
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
---
|
||||
all:
|
||||
children:
|
||||
nbops:
|
||||
hosts:
|
||||
localhost:
|
||||
ansible_connection: local
|
||||
ansible_python_interpreter: /usr/bin/python3
|
||||
nbops_environment: development
|
||||
nbops_roles:
|
||||
- base
|
||||
- ssh
|
||||
- ufw
|
||||
- updates
|
||||
- docker
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
---
|
||||
all:
|
||||
children:
|
||||
nbops:
|
||||
hosts:
|
||||
localhost:
|
||||
ansible_connection: local
|
||||
ansible_python_interpreter: /usr/bin/python3
|
||||
nbops_roles:
|
||||
- base
|
||||
- ssh
|
||||
- ufw
|
||||
- updates
|
||||
- docker
|
||||
- mtls
|
||||
- monitoring
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
---
|
||||
- name: Local ansible-pull entrypoint
|
||||
ansible.builtin.import_playbook: site.yml
|
||||
|
||||
@@ -0,0 +1,167 @@
|
||||
---
|
||||
- name: Converge nbops host
|
||||
hosts: all
|
||||
become: true
|
||||
gather_facts: true
|
||||
|
||||
pre_tasks:
|
||||
- name: Validate supported operating system
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- ansible_distribution == "Debian"
|
||||
- ansible_distribution_major_version | int >= 13
|
||||
fail_msg: "nbops targets Debian 13 Trixie or newer."
|
||||
tags:
|
||||
- always
|
||||
|
||||
roles:
|
||||
- role: base
|
||||
tags: [base]
|
||||
when: "'base' in nbops_roles | default([])"
|
||||
- role: ssh
|
||||
tags: [ssh]
|
||||
when: "'ssh' in nbops_roles | default([])"
|
||||
- role: ufw
|
||||
tags: [ufw]
|
||||
when: "'ufw' in nbops_roles | default([])"
|
||||
- role: updates
|
||||
tags: [updates]
|
||||
when: "'updates' in nbops_roles | default([])"
|
||||
- role: docker
|
||||
tags: [docker]
|
||||
when: "'docker' in nbops_roles | default([])"
|
||||
- role: mtls
|
||||
tags: [mtls]
|
||||
when: "'mtls' in nbops_roles | default([])"
|
||||
- role: monitoring
|
||||
tags: [monitoring]
|
||||
when: "'monitoring' in nbops_roles | default([])"
|
||||
- role: traefik
|
||||
tags: [traefik]
|
||||
when: "'traefik' in nbops_roles | default([])"
|
||||
- role: authentik
|
||||
tags: [authentik]
|
||||
when: "'authentik' in nbops_roles | default([])"
|
||||
- role: immich
|
||||
tags: [immich]
|
||||
when: "'immich' in nbops_roles | default([])"
|
||||
- role: gitea
|
||||
tags: [gitea]
|
||||
when: "'gitea' in nbops_roles | default([])"
|
||||
- role: ocis
|
||||
tags: [ocis]
|
||||
when: "'ocis' in nbops_roles | default([])"
|
||||
- role: pterodactyl
|
||||
tags: [pterodactyl]
|
||||
when: "'pterodactyl' in nbops_roles | default([])"
|
||||
- role: arr
|
||||
tags: [arr]
|
||||
when: "'arr' in nbops_roles | default([])"
|
||||
- role: jellyfin
|
||||
tags: [jellyfin]
|
||||
when: "'jellyfin' in nbops_roles | default([])"
|
||||
- role: pelican_panel
|
||||
tags: [pelican_panel]
|
||||
when: "'pelican_panel' in nbops_roles | default([])"
|
||||
- role: pelican_server
|
||||
tags: [pelican_server]
|
||||
when: "'pelican_server' in nbops_roles | default([])"
|
||||
- role: authentik_ldap
|
||||
tags: [authentik_ldap]
|
||||
when: "'authentik_ldap' in nbops_roles | default([])"
|
||||
- role: authentik_proxy
|
||||
tags: [authentik_proxy]
|
||||
when: "'authentik_proxy' in nbops_roles | default([])"
|
||||
- role: authentik_rac
|
||||
tags: [authentik_rac]
|
||||
when: "'authentik_rac' in nbops_roles | default([])"
|
||||
- role: jellyseerr
|
||||
tags: [jellyseerr]
|
||||
when: "'jellyseerr' in nbops_roles | default([])"
|
||||
- role: qbittorrent
|
||||
tags: [qbittorrent]
|
||||
when: "'qbittorrent' in nbops_roles | default([])"
|
||||
- role: transmission
|
||||
tags: [transmission]
|
||||
when: "'transmission' in nbops_roles | default([])"
|
||||
- role: wizarr
|
||||
tags: [wizarr]
|
||||
when: "'wizarr' in nbops_roles | default([])"
|
||||
- role: homarr
|
||||
tags: [homarr]
|
||||
when: "'homarr' in nbops_roles | default([])"
|
||||
- role: flaresolverr
|
||||
tags: [flaresolverr]
|
||||
when: "'flaresolverr' in nbops_roles | default([])"
|
||||
- role: cleanuparr
|
||||
tags: [cleanuparr]
|
||||
when: "'cleanuparr' in nbops_roles | default([])"
|
||||
- role: pinchflat
|
||||
tags: [pinchflat]
|
||||
when: "'pinchflat' in nbops_roles | default([])"
|
||||
- role: nginx_content
|
||||
tags: [nginx_content]
|
||||
when: "'nginx_content' in nbops_roles | default([])"
|
||||
- role: code_server
|
||||
tags: [code_server]
|
||||
when: "'code_server' in nbops_roles | default([])"
|
||||
- role: filebrowser_quantum
|
||||
tags: [filebrowser_quantum]
|
||||
when: "'filebrowser_quantum' in nbops_roles | default([])"
|
||||
- role: kiwix_serve
|
||||
tags: [kiwix_serve]
|
||||
when: "'kiwix_serve' in nbops_roles | default([])"
|
||||
- role: ipv6nat
|
||||
tags: [ipv6nat]
|
||||
when: "'ipv6nat' in nbops_roles | default([])"
|
||||
- role: php_fpm
|
||||
tags: [php_fpm]
|
||||
when: "'php_fpm' in nbops_roles | default([])"
|
||||
- role: valkey
|
||||
tags: [valkey]
|
||||
when: "'valkey' in nbops_roles | default([])"
|
||||
- role: syncthing
|
||||
tags: [syncthing]
|
||||
when: "'syncthing' in nbops_roles | default([])"
|
||||
- role: paperless_ngx
|
||||
tags: [paperless_ngx]
|
||||
when: "'paperless_ngx' in nbops_roles | default([])"
|
||||
- role: kavita
|
||||
tags: [kavita]
|
||||
when: "'kavita' in nbops_roles | default([])"
|
||||
- role: gitea_act_runner
|
||||
tags: [gitea_act_runner]
|
||||
when: "'gitea_act_runner' in nbops_roles | default([])"
|
||||
- role: vaultwarden
|
||||
tags: [vaultwarden]
|
||||
when: "'vaultwarden' in nbops_roles | default([])"
|
||||
- role: grafana
|
||||
tags: [grafana]
|
||||
when: "'grafana' in nbops_roles | default([])"
|
||||
- role: loki
|
||||
tags: [loki]
|
||||
when: "'loki' in nbops_roles | default([])"
|
||||
- role: alloy
|
||||
tags: [alloy]
|
||||
when: "'alloy' in nbops_roles | default([])"
|
||||
- role: grafana_renderer
|
||||
tags: [grafana_renderer]
|
||||
when: "'grafana_renderer' in nbops_roles | default([])"
|
||||
- role: prometheus
|
||||
tags: [prometheus]
|
||||
when: "'prometheus' in nbops_roles | default([])"
|
||||
- role: uptime_kuma
|
||||
tags: [uptime_kuma]
|
||||
when: "'uptime_kuma' in nbops_roles | default([])"
|
||||
- role: requarks
|
||||
tags: [requarks]
|
||||
when: "'requarks' in nbops_roles | default([])"
|
||||
- role: postgres
|
||||
tags: [postgres]
|
||||
when: "'postgres' in nbops_roles | default([])"
|
||||
- role: fiveetools
|
||||
tags: [fiveetools]
|
||||
when: "'fiveetools' in nbops_roles | default([])"
|
||||
- role: ntfy
|
||||
tags: [ntfy]
|
||||
when: "'ntfy' in nbops_roles | default([])"
|
||||
@@ -0,0 +1,6 @@
|
||||
---
|
||||
collections:
|
||||
- name: ansible.posix
|
||||
- name: community.docker
|
||||
- name: community.general
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
# alloy
|
||||
|
||||
Deploys Grafana Alloy with Docker Compose.
|
||||
|
||||
@@ -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 }
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -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 }}"
|
||||
|
||||
@@ -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 %}
|
||||
|
||||
@@ -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`.
|
||||
|
||||
@@ -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 }}"
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
---
|
||||
- name: restart arr
|
||||
community.docker.docker_compose_v2:
|
||||
project_src: "{{ arr_dir }}"
|
||||
state: restarted
|
||||
|
||||
@@ -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]
|
||||
@@ -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 %}
|
||||
@@ -0,0 +1,4 @@
|
||||
PUID={{ arr_puid }}
|
||||
PGID={{ arr_pgid }}
|
||||
TZ={{ arr_timezone }}
|
||||
|
||||
@@ -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`.
|
||||
|
||||
@@ -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 }}"
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
---
|
||||
- name: restart authentik
|
||||
community.docker.docker_compose_v2:
|
||||
project_src: "{{ authentik_dir }}"
|
||||
state: restarted
|
||||
|
||||
@@ -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]
|
||||
@@ -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 %}
|
||||
@@ -0,0 +1,5 @@
|
||||
PG_PASS={{ authentik_postgres_password }}
|
||||
{% for key, value in authentik_env.items() %}
|
||||
{{ key }}={{ value }}
|
||||
{% endfor %}
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
# authentik_ldap
|
||||
|
||||
Deploys an authentik LDAP outpost.
|
||||
|
||||
@@ -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 }
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -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 }}"
|
||||
|
||||
@@ -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 %}
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
# authentik_proxy
|
||||
|
||||
Deploys an authentik proxy outpost.
|
||||
|
||||
@@ -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 }
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -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 }}"
|
||||
|
||||
@@ -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 %}
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
# authentik_rac
|
||||
|
||||
Deploys an authentik RAC outpost.
|
||||
|
||||
@@ -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 }
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -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 }}"
|
||||
|
||||
@@ -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 %}
|
||||
|
||||
@@ -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`.
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
{% for name, command in base_aliases.items() %}
|
||||
alias {{ name }}='{{ command }}'
|
||||
{% endfor %}
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
# cleanuparr
|
||||
|
||||
Deploys Cleanuparr with Docker Compose.
|
||||
|
||||
@@ -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 }
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -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 }}"
|
||||
|
||||
@@ -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 %}
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
# code_server
|
||||
|
||||
Deploys code-server with Docker Compose.
|
||||
|
||||
@@ -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 }
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -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 }}"
|
||||
|
||||
@@ -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 %}
|
||||
|
||||
@@ -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`.
|
||||
|
||||
@@ -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
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
---
|
||||
- name: restart docker
|
||||
ansible.builtin.service:
|
||||
name: docker
|
||||
state: restarted
|
||||
|
||||
@@ -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]
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -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`.
|
||||
|
||||
@@ -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
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
Loaded 100 of 411 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user