64 Commits

Author SHA1 Message Date
dbdd2cfe11 authentik, minio: initial integration with blueprints for admin policy 2024-12-21 00:42:22 -08:00
1be4868f09 authentik: refactor oauth apps blueprints with group policies 2024-12-20 21:51:20 -08:00
ee24d69906 authentik: refactor groups and proxied apps blueprints 2024-12-20 21:37:19 -08:00
f0a10cc1d2 authentik: refactor oauth2 app blueprints 2024-12-20 19:57:59 -08:00
263f7eea17 updates: nextcloud 2024-12-20 15:25:42 -08:00
3006e3e424 monitoring: container, node dashboard improvements; separate common.py logic 2024-10-30 18:24:34 -07:00
0e43a68754 monitoring: add grafanalib, containers dashboard 2024-10-30 18:24:28 -07:00
fc6e485a61 refactor: separate templating for j2 and normal files 2024-10-30 18:11:26 -07:00
dd0330c85a monitoring: loki, cadvisor config optimizations 2024-10-23 15:35:54 -07:00
40fbdc414e monitoring: update loki schema 2024-10-22 23:16:41 -07:00
aaca0f94f8 monitoring: remove jaeger
Possible to add it back with a proper database, but all-in-one keeps everything in memory.
2024-07-25 13:16:52 -07:00
97b812eb10 updates: nextcloud refactor to apache image instead of nginx 2024-06-30 18:57:41 -07:00
97d1db61d8 updates: postgres upgrade, data migration process 2024-06-30 13:53:46 -07:00
a8bc344aa2 updates: traefik to v3 2024-06-29 17:28:17 -07:00
f9d590170d refactor: switch deployment from command to community.docker.docker_compose_v2 2024-06-28 22:55:05 -07:00
e1f3a22a23 refactor: simplify stack templates, move vars into group_vars/alpina
this vault setup for injective sensitive variables uses the approach described in https://docs.ansible.com/ansible/10/tips_tricks/ansible_tips_tricks.html#keep-vaulted-variables-safely-visible
2024-06-28 22:39:49 -07:00
f3c6c61130 updates: prepare for upgrade to debian 12 2024-06-27 16:37:50 -07:00
ef40b1ba7d updates: ansible, gluetun config 2024-06-26 21:16:46 -07:00
2e22d4c7f2 monitoring: increase prometheus tsdb retention time from 15d to 30d 2024-03-31 12:27:07 -07:00
bc963f28ee arrstack: gluetun config updates 2024-03-31 12:21:41 -07:00
5b1e346282 updates: nextcloud, gitea 2023-12-22 13:07:21 -08:00
8061a2773f fix: add alpina/__init__.py to stop poetry from complaining 2023-12-22 13:05:05 -08:00
3dba4e6fe9 authentik, monitoring: add uptime-kuma with authentik proxy blueprint 2023-11-23 18:53:42 -08:00
d54193e0d5 refactor: add clean_desired flag to Makefile for site.yml playbook 2023-11-23 18:53:36 -08:00
6c05da2b58 authentik, monitoring: add grafana oauth blueprint and provisioning 2023-11-19 00:44:35 -08:00
50ca5d6d0d monitoring: add jaeger tracing all-in-one 2023-11-17 22:35:41 -08:00
3d1a509681 authentik: default auth flow modifications, add passwordless flow 2023-11-15 14:58:22 -08:00
be14ddd5fc authentik: add icon urls to apps and arrstack 2023-11-15 14:58:21 -08:00
b15b1fca40 arrstack: minor gluetun setup fixes 2023-11-15 14:58:21 -08:00
eed1b78625 minio: remove broken healthcheck 2023-11-09 21:30:09 -08:00
405d837407 arrstack: change VPN Provider ProtonVPN -> AirVPN 2023-11-09 21:29:03 -08:00
18c1d96b0a refactor: qbit custom wireguard config replaced with gluetun 2023-11-09 21:26:48 -08:00
74679ed8e5 fix: traefik secrets 2023-10-26 00:12:35 -07:00
d2401f11f4 updates: nextcloud, gitea 2023-10-07 18:52:01 -07:00
30a2744830 monitoring: add traefik to prometheus 2023-07-09 17:57:16 -07:00
de37042061 updates: authentik, nextcloud, gitea, jellyfin 2023-07-05 17:58:16 -07:00
6a92053232 monitoring: add cadvisor to prometheus 2023-07-05 17:56:52 -07:00
1ef7f1cf6e monitoring: add initial implementation of influxdb 2023-04-15 23:15:14 -07:00
9844a1ab5c monitoring: add initial implementation of prometheus 2023-04-15 23:15:02 -07:00
bd8e1b8e38 monitoring: add initial stack with grafana, loki, and promtail 2023-04-07 19:52:20 -07:00
40964c0f60 add basic minio stack 2023-04-05 04:42:48 -07:00
145c5db29f refactor: move all service roles into a single alpina role 2023-04-04 23:14:06 -07:00
ec335e5d3c authentik: add support for basic http auth on arrstack 2023-04-04 02:46:51 -07:00
659d5ffebc initial ipv6 deployment 2023-04-04 01:54:45 -07:00
40de9b87a1 prepare for ipv6 deployment 2023-04-03 17:07:46 -07:00
20ce67472d fix: add authentik blueprints for partly automated configuration 2023-04-03 01:41:34 -07:00
7e94d27824 add authentik blueprints for partly automated configuration 2023-04-02 16:19:56 -07:00
de566c0395 add authentik with basic configuration 2023-04-02 16:19:33 -07:00
a5370f49f5 add prowlarr to replace jackett 2023-03-31 01:24:32 -07:00
c3f6bd2ea9 refactor: add https and acme to traefik 2023-03-31 01:24:28 -07:00
ed426593d4 refactor: move to Debian on truenas scale 2023-03-31 01:23:32 -07:00
a6c370b85a refactor: static internal traefik network config 2023-03-31 01:22:27 -07:00
a0d52958e8 fix: remove quotes from docker .env files because they are not parsed properly 2023-03-24 03:11:28 -07:00
d42dc7b5e4 fix: basic pipeline and makefile cleanup 2023-03-06 19:06:36 -08:00
07bc176758 updates: nextcloud, gitea 2023-03-06 18:22:21 -08:00
14f79e9990 new: jackett for sonarr & radarr 2023-02-18 18:51:19 -08:00
f951a5ff43 updates: sonarr & radarr 2023-02-18 18:50:23 -08:00
2a686f9372 updates: gitea 2023-02-18 18:49:40 -08:00
0f1ca446cd refactor: add poetry and make scripts for simpler usage 2023-02-18 18:46:55 -08:00
1fdfe560aa refactor: split basic ansible task list for compose services into contrib/docker_compose_template_task_list.yml 2023-02-18 18:45:24 -08:00
13de80c361 fix: nextcloud trusted domains use ansible variable 2022-11-07 10:22:53 -08:00
8267a66def fix: enable docker service after install 2022-11-07 10:22:14 -08:00
ff123ee762 jellyfin updates 2022-11-07 10:20:02 -08:00
33678b8b07 traefik updates 2022-11-07 10:17:21 -08:00
89 changed files with 3164 additions and 656 deletions

5
.idea/alpina.iml generated
View File

@@ -23,9 +23,8 @@
<option name="TEMPLATE_CONFIGURATION" value="Jinja2" />
<option name="TEMPLATE_FOLDERS">
<list>
<option value="$MODULE_DIR$/roles/traefik/templates" />
<option value="$MODULE_DIR$/roles/gitea/templates" />
<option value="$MODULE_DIR$/roles/nextcloud/templates" />
<option value="$MODULE_DIR$/roles/docker_host/templates" />
<option value="$MODULE_DIR$/roles/alpina/templates" />
</list>
</option>
</component>

123
.idea/jsonSchemas.xml generated
View File

@@ -3,6 +3,58 @@
<component name="JsonSchemaMappingsProjectConfiguration">
<state>
<map>
<entry key="Ansible Tasks File">
<value>
<SchemaInfo>
<option name="name" value="Ansible Tasks File" />
<option name="relativePathToSchema" value="https://raw.githubusercontent.com/ansible/ansible-lint/main/src/ansiblelint/schemas/ansible.json#/$defs/tasks" />
<option name="applicationDefined" value="true" />
<option name="patterns">
<list>
<Item>
<option name="pattern" value="true" />
<option name="path" value="*/tasks/*.yml" />
<option name="mappingKind" value="Pattern" />
</Item>
</list>
</option>
</SchemaInfo>
</value>
</entry>
<entry key="Authentik Blueprint">
<value>
<SchemaInfo>
<option name="generatedName" value="New Schema" />
<option name="name" value="Authentik Blueprint" />
<option name="relativePathToSchema" value="https://goauthentik.io/blueprints/schema.json" />
<option name="patterns">
<list>
<Item>
<option name="directory" value="true" />
<option name="path" value="roles/alpina/templates/services/authentik/blueprints" />
<option name="mappingKind" value="Directory" />
</Item>
</list>
</option>
</SchemaInfo>
</value>
</entry>
<entry key="Loki">
<value>
<SchemaInfo>
<option name="name" value="Loki" />
<option name="relativePathToSchema" value="https://json.schemastore.org/loki.json" />
<option name="applicationDefined" value="true" />
<option name="patterns">
<list>
<Item>
<option name="path" value="roles/alpina/templates/services/monitoring/loki_config/loki-config.yaml.j2" />
</Item>
</list>
</option>
</SchemaInfo>
</value>
</entry>
<entry key="Traefik v2">
<value>
<SchemaInfo>
@@ -12,7 +64,76 @@
<option name="patterns">
<list>
<Item>
<option name="path" value="roles/traefik/templates/traefik.yml.j2" />
<option name="pattern" value="true" />
<option name="path" value="*/traefik.yml.j2" />
<option name="mappingKind" value="Pattern" />
</Item>
</list>
</option>
</SchemaInfo>
</value>
</entry>
<entry key="Traefik v2 File Provider">
<value>
<SchemaInfo>
<option name="generatedName" value="New Schema" />
<option name="name" value="Traefik v2 File Provider" />
<option name="relativePathToSchema" value="https://json.schemastore.org/traefik-v2-file-provider.json" />
<option name="patterns">
<list>
<Item>
<option name="path" value="file://$APPLICATION_CONFIG_DIR$/scratches/scratch.yml" />
</Item>
<Item>
<option name="path" value="file://$APPLICATION_CONFIG_DIR$/scratches/scratch_1.yml" />
</Item>
<Item>
<option name="path" value="file:///run/user/1000/kio-fuse-kipURF/sftp/root@debbi.lab.home/mnt/dock/traefik/rules/hello-world.yml" />
</Item>
<Item>
<option name="path" value="roles/alpina/templates/services/traefik/rules/traefik-dash.yml.j2" />
</Item>
</list>
</option>
</SchemaInfo>
</value>
</entry>
<entry key="docker-compose.yml">
<value>
<SchemaInfo>
<option name="name" value="docker-compose.yml" />
<option name="relativePathToSchema" value="https://raw.githubusercontent.com/compose-spec/compose-spec/master/schema/compose-spec.json" />
<option name="applicationDefined" value="true" />
<option name="patterns">
<list>
<Item>
<option name="pattern" value="true" />
<option name="path" value="*/docker-compose.yml" />
<option name="mappingKind" value="Pattern" />
</Item>
<Item>
<option name="pattern" value="true" />
<option name="path" value="*/docker-compose.yml.j2" />
<option name="mappingKind" value="Pattern" />
</Item>
</list>
</option>
</SchemaInfo>
</value>
</entry>
<entry key="prometheus.json">
<value>
<SchemaInfo>
<option name="name" value="prometheus.json" />
<option name="relativePathToSchema" value="https://json.schemastore.org/prometheus.json" />
<option name="applicationDefined" value="true" />
<option name="patterns">
<list>
<Item>
<option name="path" value="roles/alpina/collections/services/monitoring/templates/prometheus_config/prometheus.yml.j2" />
</Item>
<Item>
<option name="path" value="roles/alpina/templates/services/monitoring/prometheus_config/prometheus.yml.j2" />
</Item>
</list>
</option>

5
.idea/misc.xml generated
View File

@@ -1,4 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.10 (alpina)" project-jdk-type="Python SDK" />
<component name="Black">
<option name="sdkName" value="Poetry (alpina) (2)" />
</component>
<component name="ProjectRootManager" version="2" project-jdk-name="Poetry (alpina)" project-jdk-type="Python SDK" />
</project>

19
Makefile Normal file
View File

@@ -0,0 +1,19 @@
.POSIX:
.PHONY: *
.EXPORT_ALL_VARIABLES:
env ?= staging
vault_id ?= alpina@contrib/rbw-client.sh
clean_desired ?= false
all: site
setup:
poetry install --quiet
site: setup
poetry run ansible-playbook --vault-id ${vault_id} -i inventories/${env} --extra-vars "clean_desired_arg=${clean_desired}" site.yml
services: setup
poetry run ansible-playbook --vault-id ${vault_id} -i inventories/${env} services.yml

75
README.md Normal file
View File

@@ -0,0 +1,75 @@
# Alpina
A home for configuring all of my homelab containers on a Debian Linux machine.
This assumes a Debian Linux machine with Docker and Docker Compose installed.
My particular setup is based on a [jailmaker](https://github.com/Jip-Hop/jailmaker) container
running on top of TrueNAS SCALE, separating all the docker stuff from the appliance.
# Notes
## Monitoring
The monitoring stack is set up to monitor all the containers and the host.
This is a work in progress, Grafana is set up with grafanalib, a Python library that generates Grafana dashboards.
The dashboards are generated from Python scripts in
[grafana_config/dashboards](roles/alpina/templates/services/monitoring/grafana_config/dashboards).
This requires a custom grafana image, which is built from the
[Dockerfile](roles/alpina/templates/services/monitoring/Dockerfile).
This also means it has to be manually rebuilt whenever the dashboards are updated.
From the services/monitoring directory, run:
```bash
docker compose up -d --build --force-recreate grafana
```
## IPv6
The current configuration is designed to work with IPv6.
However, because of how (not properly) I'm doing the subnetting
from the host's network, NDP doesn't work.
This means that container IPs are not accessible from other hosts on the local network.
I simply have a static route on my router to the container subnet,
that uses the IP of this host as the gateway.
This is a limitation of my current ISP, I only have a single /64 subnet for my lab network.
I'd like to get a /56 or /48, perhaps using Hurricane Electric's tunnel broker.
*Sigh* ISPs being stingy with the 2^48 prefixes they're afraid of running out of.
## Upgrading Postgres
Upgrading the postgres container for a given stack requires a dump and restore.
After making a snapshot or backup of postgres data directory,
in the compose directory for a given stack, run the following commands:
```bash
docker compose down
docker compose up -d <db_service>
docker compose exec -it <db_service> pg_dumpall -U <db_user> | tee /tmp/dump.sql
docker compose down
rm -r <postgres_data_dir>/* # as root
# Edit the docker-compose.yml file to use the new postgres image
docker compose up -d <db_service>
# For some reason, compose exec doesn't like the input redirection
docker exec -i <db_container_name> psql -U <db_user> < /tmp/dump.sql
docker compose up -d
rm /tmp/dump.sql
```
Additionally, if upgrading from postgres <= 13, it is necessary to upgrade the
password hashes. This can be done by running the following command:
```bash
docker compose exec -it <db_service> psql -U <db_user> -c "\password"
```
## Nextcloud
Nextcloud requires some additional work to set up notify_push.
- Initially, comment out the notify_push service in the docker compose.
- Set up nextcloud and install the Client Push (notify_push) app.
- Uncomment the notify_push service in the docker compose and `up -d` the stack.
- ```bash
docker compose exec app ./occ notify_push:setup https://nc.<domain>/push
```
I should probably get around to automating this at some point.

View File

@@ -0,0 +1,28 @@
{% macro default_network(subnet_index) %}
default:
enable_ipv6: true
ipam:
config:
- subnet: {{ docker_ipv6_subnet | ansible.utils.ipsubnet(80, subnet_index) }}
{% endmacro %}
{% macro traefik_labels(host, service="", port="", auth=false) %}
traefik.enable=true
- traefik.http.routers.{{ host }}.rule=Host(`{{ host }}.{{ domain }}`)
- traefik.http.routers.{{ host }}.entrypoints=web
- traefik.http.routers.{{ host }}-tls.rule=Host(`{{ host }}.{{ domain }}`)
- traefik.http.routers.{{ host }}-tls.entrypoints=websecure
- traefik.http.routers.{{ host }}-tls.tls=true
- traefik.http.routers.{{ host }}-tls.tls.certresolver=letsencrypt
- traefik.http.routers.{{ host }}-tls.tls.domains.0.main={{ domain }}
- traefik.http.routers.{{ host }}-tls.tls.domains.0.sans=*.{{ domain }}
{% if service -%}
- traefik.http.routers.{{ host }}.service={{ service }}
{% endif %}
{% if port -%}
- traefik.http.services.{{ host }}.loadbalancer.server.port={{ port }}
{% endif %}
{% if auth -%}
- traefik.http.routers.{{ host }}-tls.middlewares=authentik@docker
{% endif %}
{% endmacro %}

View File

@@ -0,0 +1,51 @@
# Shared variables between environments
---
alpina_svc_path: ~/alpina
base_volume_path: /mnt/dock
media_volume_path: /mnt/media
traefik_subnet: 172.16.122.0
# Authentik
authentik_db_password: "{{ vault_authentik_db_password }}"
authentik_secret_key: "{{ vault_authentik_secret_key }}"
authentik_sendgrid_api_key: "{{ vault_authentik_sendgrid_api_key }}"
auth_grafana_client_secret: "{{ vault_auth_grafana_client_secret }}"
auth_gitea_client_secret: "{{ vault_auth_gitea_client_secret }}"
auth_nextcloud_client_secret: "{{ vault_auth_nextcloud_client_secret }}"
auth_minio_client_secret: "{{ vault_auth_minio_client_secret }}"
arrstack_password: "{{ vault_arrstack_password }}"
# Minio
minio_password: "{{ vault_minio_password }}"
# Monitoring
## auth_grafana_client_secret:
influxdb_admin_password: "{{ vault_influxdb_admin_password }}"
influxdb_admin_token: "{{ vault_influxdb_admin_token }}"
# Traefik
acme_email: "{{ vault_acme_email }}"
cloudflare_api_token: "{{ vault_cloudflare_api_token }}"
# Arrstack
wg_peer_pubkey: "{{ vault_wg_peer_pubkey }}"
vpn_server_names: "{{ vault_vpn_server_names }}"
# Gitea
gitea_db_password: "{{ vault_gitea_db_password }}"
gitea_sendgrid_api_key: "{{ vault_gitea_sendgrid_api_key }}"
## Security
secret_key: "{{ vault_secret_key }}"
internal_token: "{{ vault_internal_token }}"
jwt_secret: "{{ vault_jwt_secret }}"
# Jellyfin
# Nextcloud
nextcloud_db_password: "{{ vault_nextcloud_db_password }}"
redis_password: "{{ vault_redis_password }}"
nextcloud_sendgrid_api_key: "{{ vault_nextcloud_sendgrid_api_key }}"

113
group_vars/alpina/vault.yml Normal file
View File

@@ -0,0 +1,113 @@
$ANSIBLE_VAULT;1.1;AES256
62376365353162306161343336623464386634383663663165393632366666633530373636633032
6536633438613664316163613236663334663635363665630a666135396430306536646534616535
65383432356339643063373232393861333366393038666134346363646130626130633861646536
3134613738333465300a363031626561376533343730353361646462306434663564336538666565
38643166326439356138653163323030626539393265613833303661313036336562373938323663
39336533383636626464343461653836313734393430306238336561323038306238646236393835
62643638636137646162616239636561666432376561393338336663366438346530346666396662
63626432326263383561633532613039643862303135643262383636666161663539643465616566
66303364333133393932643666656263613063373162373265353433616337636337363363353938
31613638633462383031356433393765353439373434356366336234316361393862343763643333
61623233633664396564376462336131353061303831316466306632663261666161323137333633
39623938633861356136636532373139356339636334373137303034646431363438613936636438
38363463386664643439313564313364613962346631343663633837326532613933336462636265
32616161663065316661313335373234353161653732303965613731633665646532386139383732
32363834636532363262646433616563363232643864653365643736353434346130383963393564
34333861326633393763653639663666333061613161393864323165303638353962333531333661
36316534303365626562643366393836356337303533313237613534313565643832373438373530
32393065653538393762333232636235316439653935663437616236326162313464323037336630
39323262333530363230353334356461343866346438626533633339386162336337623137393366
32373361393231343134626237323062663634323939613461633866353561636334613234336532
61306235363037306466656463653836396434313830333031366630373364343637376662346663
65663132346239343937636261643238623364633062356163323364363466666661346364356239
32653266303837663237333136316464626161626136336333363964636461616138323962313166
64643930333964303639393439666432366435386464326561323165353333623765653132383636
34326633663331376563613766383734613762653834356561616461303361373662653337623863
37633135393861366137613137633265306137326536363632373962353233373735663065653534
37333038363330633931353233623236313332336234393333616238353137656363643230633966
32636336663762636130343933373834386465396536316439386465623130396266393438396262
63636561623533366166393831383035373935643037326265636634646339336264383937366334
37373961663330326131343531356238363632663861376362643561643966636364653235303032
33363861396336666332356130353638373135376336373236383730373665623336373830643137
35613234343966383264643834353162353533373939346561363438376339656239323364353036
63623630643930363739326236653435613538393438326331383366666332383763356631356533
39393363366261393231386239363161313939396431323630323062393962313933633462303439
35623831356638333431313430343832616438343134613538343064323535613539663431643830
32623363343733623837366236393136393864353332316538306463346337363264613763326463
65366536326463303062663262636563306565323861666661376338633334383138626364333039
34333734656331346334316465333339333535333632383963663633383361383661643235383866
32326634643633366566306137383066653334323935363066316366313934373663383234316438
35346139633239323431386536656464666161656434316238356333323665333661623364653865
33636139333866356630323031323162323834303062363637313430313164326636383436383465
35333434613632353265633935343164613266383463633631323763633565353039306134656431
37616430633736326139366438613666346434646363313032366231616436616535393334613264
34646132303061383034363139613362626235383938393535626339353438626635396561346166
36666530613634336666653638353734323336366639626465346135323838343565383335313233
62356631666135666434363061666234396337323838303866343839383164643939323862616632
34646433333031653939313434613435623036346631643265643663613537323061343733326534
64626663306338623533333132613333386562306162343438653266356666663535623036616666
64613866663261386233343236353931353766323833623631373438353664393137613032366461
63623164353435336564613739353863383037326465363462376536663934626362393132313465
66353965643763656564366630353131313465656265613434363538343331313666613564313036
35396436633233623261323432666237303335333339393363636362376536343837346264383935
30346163353338336661646536643536623262343762303766393438343666623063326463346566
34663538656133353639333830316562376137643666323832363666623766366131303830626531
62313832316533663261353365343733636236643333396561333636303065653732646665386136
31386535663732386165623037373763333731343461393431306339393634346130646462646661
61646539613964666437623631643333333435353039633531313364366338316365396131346331
30363963633236653364643061316237326362653462656563656165346134656338383738613932
65333432393534643331396563643865656435373563613939616234636533383731336561623037
61373839343132376465343332343165316361383831333538313531333063633632643832633536
33313464643239323963346338386566313031306233336562616638353365666237346262666134
33646134393531346637376133393039326638316334626333363162313239393239663865323730
30343731363031303565643833313135643036666461366666376132663433343662333730373137
65636236313561613637343262653833666135653832363466613138363332393061653032333933
66376263663830333937336566333461333431393336333161623233353332396437396664316137
64363737323036366635613938346261383634353237346337613933303334623434623439616533
32353465336237396133643039613730646661643039363836333733353033343236373864626634
37666562653233336464633337353963363361646334373863653032353137363738613561613135
66356132393630613031316466663837633633383033633064326565303837633062336531373866
34666537303033323362363163353666383962333536303135363666653930326166323637636266
34306537343238353833313635306663643737653531313435383064383133366364646331306261
66363763353534643833316533383364353632343439393032313437633734323031383438633333
31616362343332373333626135396435366235313465346639326564353265643133313339376639
63333233653833653333373162633033623035633832333566653536343832373035636664643839
38393864666430313162366337653836333135333738653763653261343233663666373865383366
65343038646166343934376633613337306436336130626363396339313236653731653265383661
34633332343639333533316631643763363664666563353137383639616132313363383137383132
33343635386139366230363464363731383166393430396533613438366661353439353537346530
62366461653534333834386637363364346432333964306639376339313531383431323930333530
37383665373937303732643636383539393039663363623337663938303139663039366536323031
66613036326263316239646535656163626232626130336465303166336336316435343262373631
39613536336366366435326230653339356635636432303862306636613935306432323966313234
65623938316162393931343337326334666235666362313739343564633339653962313062393431
35373338306332326133333638636137386337343261386663333261333030343635336532373134
38626136383936393339613534386539663035316335656566656639613837313239626431386362
62643733326636323635373363333964643132323562633430626666616531656639383231336432
61653439376663613161396465343638623639653135363863336363343230636336346434326234
32343962666337646435653035333431333632363239616535333835393761353366386561356366
37356530333763346137653566643134376136656638386334343038376439643037623338643333
66626537633931333465383062303766333436346433636434653139333966613865656234346539
36376239393632653536306363313633636464343366373862343039306235303766623462633932
32313537306530343032663365626330363838396566356534343766383865653231613538323461
37303439393733376539613061663937633665663963613236323764653835656563346565636531
30363239376139343166346664306234363031623031663266643966636265666163353536346132
65623638323065633361373330386334636332306634636336613365663133373835666135396230
38373939366534663336376135646237633232646261383964383735353533303862623064313333
33633533653537376138623635663465336131383838663237653933623634343761623731366335
64653233366335656365656336303862656663303138643531356661373831633062633734363661
39306633323337356366383863643034656135393432386638353761323337373631353436383664
34623631306663636439376464383831323566666266613536613661633266343732646264306162
36353030343538316330313831626232353165323038363034666161336338316536353832353966
35336365393563643733363535393763613865663436616130343066303638353431653039356661
34393936363764393032646133326432656230353232623339646165663932366130363734663762
34303433376666383639663661356334653939663739643139363237623031666632623239343562
30656438623236616637643132613666343133393436346635316638633664316363323832393862
39643831363633643562323664613666393033656132333964643639333230353763383330343835
64383530373332343838666536303363313033303931646232343037303863343835366139326135
34336330343365663837396134653566633536643832373433393035366531323035616462363639
66336133346139336264346636643735383136343336303133313031653230366166396239303335
64656535326465363563396532376538336434643964336264303061393139656139376635633730
62326664613766393435383464363538393937313236363630656337356264633134353464393835
32653133383732656235

View File

@@ -1,2 +0,0 @@
---
my_svc_path: ~/services

View File

@@ -0,0 +1,14 @@
# Environment specific variables (prod)
---
docker_ipv6_index: 255
# Arrstack VPN
wg_privkey: "{{ vault_wg_privkey }}"
wg_psk: "{{ vault_wg_psk }}"
wg_addresses: "{{ vault_wg_addresses }}"
fw_vpn_input_ports: "{{ vault_fw_vpn_input_ports }}"
# Authentik GitHub OAuth
github_consumer_key: 32d5cae58d744c56fcc9
github_consumer_secret: "{{ vault_github_consumer_secret }}"

View File

@@ -0,0 +1,21 @@
$ANSIBLE_VAULT;1.1;AES256
61656162363565633436373135333536623561663136303736393865623830633539376362363363
3938333137343336626634346262363964316563643261310a366538363037343965363766646535
61636239326464373039333462653562373933396665393039633266326234663335363337666439
6137323332303533640a383062383135633762323561313666636566306531306636633466316536
66623731626266333731303336323733343336626366343833633365616330343565363035323039
35313961383131616133386663376331336639633137383137346164353632653939363266613562
36316631366661353632386230306532633862393963663465383862653964646462666334396666
66626636353539316266343937623662613336616331626439306538363764636366656635356639
30663535393366383261333832356237373230663037373638303161303534636230616464636265
37623938303638646233346338616239393838396433313063343065386666323264646461373032
63376661646139316430303533643063336634333364643231336130613638626431623732646434
63643833353164313465633333646232653761356333323933396666323837656334343866363762
39646263653137356632323534356631366531636530613736343438393136363835373435636230
30313163386335353935663432323033326235653963653930396235373863373232666334326661
34336632666365666563326366376461386130343965363832343430396537323734363533353065
64313837623366356261383437306465633730353332636561333462356363326132313933653234
66363634333664333433613466396639306436353035346134373430663532373934343861323262
30666664336336393835346234316238613839326436363162626439376530306133343530303365
65393030633237333166336637363435646435323736353461333932366638333264333239373733
30623062643336643431

View File

@@ -1,2 +1,2 @@
[docker_hosts]
root@alpina.lab.home
[alpina]
debbi.lab.home

View File

@@ -0,0 +1,14 @@
# Environment specific variables (staging)
---
docker_ipv6_index: 254
# Arrstack VPN
wg_privkey: "{{ vault_wg_privkey }}"
wg_psk: "{{ vault_wg_psk }}"
wg_addresses: "{{ vault_wg_addresses }}"
fw_vpn_input_ports: "{{ vault_fw_vpn_input_ports }}"
# Authentik GitHub OAuth
github_consumer_key: dbacb8621c37320eb745
github_consumer_secret: "{{ vault_github_consumer_secret }}"

View File

@@ -0,0 +1,21 @@
$ANSIBLE_VAULT;1.1;AES256
63633535633462326534626562373461373363643166383961303861623531663263323534366537
3263633238646439306430356365623233313838326639350a386633363434623737313565316535
33393734633937333637373432366132323366343836393538366339626235613937323066613666
3737393262646333390a623331333461373563313166323232343234616538623433376166313532
32323834346336336164343938303062336438643566343866316164643535663039326331646465
36666162393365323633646635333666613030386265306238633434303234336439646663356363
63323638373035326465633934326363316364616539613462653232393465633233366666373664
66616361646564303530356331323864343966633736643434653237316236363063613634646438
35303238646632616465643264316164363139393834626362326538613033656464323435396638
31346631653764303332386331663361623766333332366537313634636333346538653537346631
62363438303036386530633236376633326162336434343861346261373835653735323161323965
62353965373164616537346134303232363033323134323130316439386339613966646330666533
65346239383230646565346133663530613462363532663562326136376233303638323332326630
35656432363563653663616236393932663637323139666664636237336136366438656666633865
66353162656364356638313236643131613830393838636264663833343461373963613431656364
32303331623033303433333631313038316336653638656638373031653234356164333363336532
37316334353463376562643138346633613633353536653939376564333166323931353634333736
63616133663266383339323562343265613461623865623263623139396163343065623264366230
32633362336335396562366563363830636133376238646433386236666461333731353337386333
61323931643766326338

View File

@@ -1,2 +1,2 @@
[docker_hosts]
root@etapp.lab.home
[alpina]
etappi.lab.home

442
poetry.lock generated Normal file
View File

@@ -0,0 +1,442 @@
# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand.
[[package]]
name = "ansible"
version = "10.5.0"
description = "Radically simple IT automation"
optional = false
python-versions = ">=3.10"
files = [
{file = "ansible-10.5.0-py3-none-any.whl", hash = "sha256:1d10bddba58f1edd0fe0b8e0387e0fafc519535066bb3c919c33b6ea3ec32a0f"},
{file = "ansible-10.5.0.tar.gz", hash = "sha256:ba2045031a7d60c203b6e5fe1f8eaddd53ae076f7ada910e636494384135face"},
]
[package.dependencies]
ansible-core = ">=2.17.5,<2.18.0"
[[package]]
name = "ansible-core"
version = "2.17.5"
description = "Radically simple IT automation"
optional = false
python-versions = ">=3.10"
files = [
{file = "ansible_core-2.17.5-py3-none-any.whl", hash = "sha256:10f165b475cf2bc8d886e532cadb32c52ee6a533649793101d3166bca9bd3ea3"},
{file = "ansible_core-2.17.5.tar.gz", hash = "sha256:ae7f51fd13dc9d57c9bcd43ef23f9c255ca8f18f4b5c0011a4f9b724d92c5a8e"},
]
[package.dependencies]
cryptography = "*"
jinja2 = ">=3.0.0"
packaging = "*"
PyYAML = ">=5.1"
resolvelib = ">=0.5.3,<1.1.0"
[[package]]
name = "ansible-vault"
version = "2.1.0"
description = "R/W an ansible-vault yaml file"
optional = false
python-versions = "*"
files = [
{file = "ansible-vault-2.1.0.tar.gz", hash = "sha256:5ce8fdb5470f1449b76bf07ae2abc56480dad48356ae405c85b686efb64dbd5e"},
]
[package.dependencies]
ansible = "*"
setuptools = "*"
[package.extras]
dev = ["black", "flake8", "isort[pyproject]", "pytest"]
release = ["twine"]
[[package]]
name = "attrs"
version = "24.2.0"
description = "Classes Without Boilerplate"
optional = false
python-versions = ">=3.7"
files = [
{file = "attrs-24.2.0-py3-none-any.whl", hash = "sha256:81921eb96de3191c8258c199618104dd27ac608d9366f5e35d011eae1867ede2"},
{file = "attrs-24.2.0.tar.gz", hash = "sha256:5cfb1b9148b5b086569baec03f20d7b6bf3bcacc9a42bebf87ffaaca362f6346"},
]
[package.extras]
benchmark = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-codspeed", "pytest-mypy-plugins", "pytest-xdist[psutil]"]
cov = ["cloudpickle", "coverage[toml] (>=5.3)", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"]
dev = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pre-commit", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"]
docs = ["cogapp", "furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier (<24.7)"]
tests = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"]
tests-mypy = ["mypy (>=1.11.1)", "pytest-mypy-plugins"]
[[package]]
name = "cffi"
version = "1.17.1"
description = "Foreign Function Interface for Python calling C code."
optional = false
python-versions = ">=3.8"
files = [
{file = "cffi-1.17.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:df8b1c11f177bc2313ec4b2d46baec87a5f3e71fc8b45dab2ee7cae86d9aba14"},
{file = "cffi-1.17.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8f2cdc858323644ab277e9bb925ad72ae0e67f69e804f4898c070998d50b1a67"},
{file = "cffi-1.17.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:edae79245293e15384b51f88b00613ba9f7198016a5948b5dddf4917d4d26382"},
{file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45398b671ac6d70e67da8e4224a065cec6a93541bb7aebe1b198a61b58c7b702"},
{file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ad9413ccdeda48c5afdae7e4fa2192157e991ff761e7ab8fdd8926f40b160cc3"},
{file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5da5719280082ac6bd9aa7becb3938dc9f9cbd57fac7d2871717b1feb0902ab6"},
{file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bb1a08b8008b281856e5971307cc386a8e9c5b625ac297e853d36da6efe9c17"},
{file = "cffi-1.17.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:045d61c734659cc045141be4bae381a41d89b741f795af1dd018bfb532fd0df8"},
{file = "cffi-1.17.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6883e737d7d9e4899a8a695e00ec36bd4e5e4f18fabe0aca0efe0a4b44cdb13e"},
{file = "cffi-1.17.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6b8b4a92e1c65048ff98cfe1f735ef8f1ceb72e3d5f0c25fdb12087a23da22be"},
{file = "cffi-1.17.1-cp310-cp310-win32.whl", hash = "sha256:c9c3d058ebabb74db66e431095118094d06abf53284d9c81f27300d0e0d8bc7c"},
{file = "cffi-1.17.1-cp310-cp310-win_amd64.whl", hash = "sha256:0f048dcf80db46f0098ccac01132761580d28e28bc0f78ae0d58048063317e15"},
{file = "cffi-1.17.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a45e3c6913c5b87b3ff120dcdc03f6131fa0065027d0ed7ee6190736a74cd401"},
{file = "cffi-1.17.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:30c5e0cb5ae493c04c8b42916e52ca38079f1b235c2f8ae5f4527b963c401caf"},
{file = "cffi-1.17.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f75c7ab1f9e4aca5414ed4d8e5c0e303a34f4421f8a0d47a4d019ceff0ab6af4"},
{file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a1ed2dd2972641495a3ec98445e09766f077aee98a1c896dcb4ad0d303628e41"},
{file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:46bf43160c1a35f7ec506d254e5c890f3c03648a4dbac12d624e4490a7046cd1"},
{file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a24ed04c8ffd54b0729c07cee15a81d964e6fee0e3d4d342a27b020d22959dc6"},
{file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:610faea79c43e44c71e1ec53a554553fa22321b65fae24889706c0a84d4ad86d"},
{file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a9b15d491f3ad5d692e11f6b71f7857e7835eb677955c00cc0aefcd0669adaf6"},
{file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:de2ea4b5833625383e464549fec1bc395c1bdeeb5f25c4a3a82b5a8c756ec22f"},
{file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fc48c783f9c87e60831201f2cce7f3b2e4846bf4d8728eabe54d60700b318a0b"},
{file = "cffi-1.17.1-cp311-cp311-win32.whl", hash = "sha256:85a950a4ac9c359340d5963966e3e0a94a676bd6245a4b55bc43949eee26a655"},
{file = "cffi-1.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:caaf0640ef5f5517f49bc275eca1406b0ffa6aa184892812030f04c2abf589a0"},
{file = "cffi-1.17.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:805b4371bf7197c329fcb3ead37e710d1bca9da5d583f5073b799d5c5bd1eee4"},
{file = "cffi-1.17.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:733e99bc2df47476e3848417c5a4540522f234dfd4ef3ab7fafdf555b082ec0c"},
{file = "cffi-1.17.1-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1257bdabf294dceb59f5e70c64a3e2f462c30c7ad68092d01bbbfb1c16b1ba36"},
{file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da95af8214998d77a98cc14e3a3bd00aa191526343078b530ceb0bd710fb48a5"},
{file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d63afe322132c194cf832bfec0dc69a99fb9bb6bbd550f161a49e9e855cc78ff"},
{file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f79fc4fc25f1c8698ff97788206bb3c2598949bfe0fef03d299eb1b5356ada99"},
{file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b62ce867176a75d03a665bad002af8e6d54644fad99a3c70905c543130e39d93"},
{file = "cffi-1.17.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:386c8bf53c502fff58903061338ce4f4950cbdcb23e2902d86c0f722b786bbe3"},
{file = "cffi-1.17.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4ceb10419a9adf4460ea14cfd6bc43d08701f0835e979bf821052f1805850fe8"},
{file = "cffi-1.17.1-cp312-cp312-win32.whl", hash = "sha256:a08d7e755f8ed21095a310a693525137cfe756ce62d066e53f502a83dc550f65"},
{file = "cffi-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:51392eae71afec0d0c8fb1a53b204dbb3bcabcb3c9b807eedf3e1e6ccf2de903"},
{file = "cffi-1.17.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f3a2b4222ce6b60e2e8b337bb9596923045681d71e5a082783484d845390938e"},
{file = "cffi-1.17.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0984a4925a435b1da406122d4d7968dd861c1385afe3b45ba82b750f229811e2"},
{file = "cffi-1.17.1-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d01b12eeeb4427d3110de311e1774046ad344f5b1a7403101878976ecd7a10f3"},
{file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:706510fe141c86a69c8ddc029c7910003a17353970cff3b904ff0686a5927683"},
{file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de55b766c7aa2e2a3092c51e0483d700341182f08e67c63630d5b6f200bb28e5"},
{file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c59d6e989d07460165cc5ad3c61f9fd8f1b4796eacbd81cee78957842b834af4"},
{file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd398dbc6773384a17fe0d3e7eeb8d1a21c2200473ee6806bb5e6a8e62bb73dd"},
{file = "cffi-1.17.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:3edc8d958eb099c634dace3c7e16560ae474aa3803a5df240542b305d14e14ed"},
{file = "cffi-1.17.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:72e72408cad3d5419375fc87d289076ee319835bdfa2caad331e377589aebba9"},
{file = "cffi-1.17.1-cp313-cp313-win32.whl", hash = "sha256:e03eab0a8677fa80d646b5ddece1cbeaf556c313dcfac435ba11f107ba117b5d"},
{file = "cffi-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:f6a16c31041f09ead72d69f583767292f750d24913dadacf5756b966aacb3f1a"},
{file = "cffi-1.17.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:636062ea65bd0195bc012fea9321aca499c0504409f413dc88af450b57ffd03b"},
{file = "cffi-1.17.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c7eac2ef9b63c79431bc4b25f1cd649d7f061a28808cbc6c47b534bd789ef964"},
{file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e221cf152cff04059d011ee126477f0d9588303eb57e88923578ace7baad17f9"},
{file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:31000ec67d4221a71bd3f67df918b1f88f676f1c3b535a7eb473255fdc0b83fc"},
{file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6f17be4345073b0a7b8ea599688f692ac3ef23ce28e5df79c04de519dbc4912c"},
{file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e2b1fac190ae3ebfe37b979cc1ce69c81f4e4fe5746bb401dca63a9062cdaf1"},
{file = "cffi-1.17.1-cp38-cp38-win32.whl", hash = "sha256:7596d6620d3fa590f677e9ee430df2958d2d6d6de2feeae5b20e82c00b76fbf8"},
{file = "cffi-1.17.1-cp38-cp38-win_amd64.whl", hash = "sha256:78122be759c3f8a014ce010908ae03364d00a1f81ab5c7f4a7a5120607ea56e1"},
{file = "cffi-1.17.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b2ab587605f4ba0bf81dc0cb08a41bd1c0a5906bd59243d56bad7668a6fc6c16"},
{file = "cffi-1.17.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:28b16024becceed8c6dfbc75629e27788d8a3f9030691a1dbf9821a128b22c36"},
{file = "cffi-1.17.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1d599671f396c4723d016dbddb72fe8e0397082b0a77a4fab8028923bec050e8"},
{file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca74b8dbe6e8e8263c0ffd60277de77dcee6c837a3d0881d8c1ead7268c9e576"},
{file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f7f5baafcc48261359e14bcd6d9bff6d4b28d9103847c9e136694cb0501aef87"},
{file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98e3969bcff97cae1b2def8ba499ea3d6f31ddfdb7635374834cf89a1a08ecf0"},
{file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cdf5ce3acdfd1661132f2a9c19cac174758dc2352bfe37d98aa7512c6b7178b3"},
{file = "cffi-1.17.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9755e4345d1ec879e3849e62222a18c7174d65a6a92d5b346b1863912168b595"},
{file = "cffi-1.17.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f1e22e8c4419538cb197e4dd60acc919d7696e5ef98ee4da4e01d3f8cfa4cc5a"},
{file = "cffi-1.17.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:c03e868a0b3bc35839ba98e74211ed2b05d2119be4e8a0f224fba9384f1fe02e"},
{file = "cffi-1.17.1-cp39-cp39-win32.whl", hash = "sha256:e31ae45bc2e29f6b2abd0de1cc3b9d5205aa847cafaecb8af1476a609a2f6eb7"},
{file = "cffi-1.17.1-cp39-cp39-win_amd64.whl", hash = "sha256:d016c76bdd850f3c626af19b0542c9677ba156e4ee4fccfdd7848803533ef662"},
{file = "cffi-1.17.1.tar.gz", hash = "sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824"},
]
[package.dependencies]
pycparser = "*"
[[package]]
name = "cryptography"
version = "43.0.1"
description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers."
optional = false
python-versions = ">=3.7"
files = [
{file = "cryptography-43.0.1-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:8385d98f6a3bf8bb2d65a73e17ed87a3ba84f6991c155691c51112075f9ffc5d"},
{file = "cryptography-43.0.1-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:27e613d7077ac613e399270253259d9d53872aaf657471473ebfc9a52935c062"},
{file = "cryptography-43.0.1-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:68aaecc4178e90719e95298515979814bda0cbada1256a4485414860bd7ab962"},
{file = "cryptography-43.0.1-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:de41fd81a41e53267cb020bb3a7212861da53a7d39f863585d13ea11049cf277"},
{file = "cryptography-43.0.1-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:f98bf604c82c416bc829e490c700ca1553eafdf2912a91e23a79d97d9801372a"},
{file = "cryptography-43.0.1-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:61ec41068b7b74268fa86e3e9e12b9f0c21fcf65434571dbb13d954bceb08042"},
{file = "cryptography-43.0.1-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:014f58110f53237ace6a408b5beb6c427b64e084eb451ef25a28308270086494"},
{file = "cryptography-43.0.1-cp37-abi3-win32.whl", hash = "sha256:2bd51274dcd59f09dd952afb696bf9c61a7a49dfc764c04dd33ef7a6b502a1e2"},
{file = "cryptography-43.0.1-cp37-abi3-win_amd64.whl", hash = "sha256:666ae11966643886c2987b3b721899d250855718d6d9ce41b521252a17985f4d"},
{file = "cryptography-43.0.1-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:ac119bb76b9faa00f48128b7f5679e1d8d437365c5d26f1c2c3f0da4ce1b553d"},
{file = "cryptography-43.0.1-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1bbcce1a551e262dfbafb6e6252f1ae36a248e615ca44ba302df077a846a8806"},
{file = "cryptography-43.0.1-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58d4e9129985185a06d849aa6df265bdd5a74ca6e1b736a77959b498e0505b85"},
{file = "cryptography-43.0.1-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:d03a475165f3134f773d1388aeb19c2d25ba88b6a9733c5c590b9ff7bbfa2e0c"},
{file = "cryptography-43.0.1-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:511f4273808ab590912a93ddb4e3914dfd8a388fed883361b02dea3791f292e1"},
{file = "cryptography-43.0.1-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:80eda8b3e173f0f247f711eef62be51b599b5d425c429b5d4ca6a05e9e856baa"},
{file = "cryptography-43.0.1-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:38926c50cff6f533f8a2dae3d7f19541432610d114a70808f0926d5aaa7121e4"},
{file = "cryptography-43.0.1-cp39-abi3-win32.whl", hash = "sha256:a575913fb06e05e6b4b814d7f7468c2c660e8bb16d8d5a1faf9b33ccc569dd47"},
{file = "cryptography-43.0.1-cp39-abi3-win_amd64.whl", hash = "sha256:d75601ad10b059ec832e78823b348bfa1a59f6b8d545db3a24fd44362a1564cb"},
{file = "cryptography-43.0.1-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:ea25acb556320250756e53f9e20a4177515f012c9eaea17eb7587a8c4d8ae034"},
{file = "cryptography-43.0.1-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:c1332724be35d23a854994ff0b66530119500b6053d0bd3363265f7e5e77288d"},
{file = "cryptography-43.0.1-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:fba1007b3ef89946dbbb515aeeb41e30203b004f0b4b00e5e16078b518563289"},
{file = "cryptography-43.0.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:5b43d1ea6b378b54a1dc99dd8a2b5be47658fe9a7ce0a58ff0b55f4b43ef2b84"},
{file = "cryptography-43.0.1-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:88cce104c36870d70c49c7c8fd22885875d950d9ee6ab54df2745f83ba0dc365"},
{file = "cryptography-43.0.1-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:9d3cdb25fa98afdd3d0892d132b8d7139e2c087da1712041f6b762e4f807cc96"},
{file = "cryptography-43.0.1-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:e710bf40870f4db63c3d7d929aa9e09e4e7ee219e703f949ec4073b4294f6172"},
{file = "cryptography-43.0.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:7c05650fe8023c5ed0d46793d4b7d7e6cd9c04e68eabe5b0aeea836e37bdcec2"},
{file = "cryptography-43.0.1.tar.gz", hash = "sha256:203e92a75716d8cfb491dc47c79e17d0d9207ccffcbcb35f598fbe463ae3444d"},
]
[package.dependencies]
cffi = {version = ">=1.12", markers = "platform_python_implementation != \"PyPy\""}
[package.extras]
docs = ["sphinx (>=5.3.0)", "sphinx-rtd-theme (>=1.1.1)"]
docstest = ["pyenchant (>=1.6.11)", "readme-renderer", "sphinxcontrib-spelling (>=4.0.1)"]
nox = ["nox"]
pep8test = ["check-sdist", "click", "mypy", "ruff"]
sdist = ["build"]
ssh = ["bcrypt (>=3.1.5)"]
test = ["certifi", "cryptography-vectors (==43.0.1)", "pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-xdist"]
test-randomorder = ["pytest-randomly"]
[[package]]
name = "grafanalib"
version = "0.7.1"
description = "Library for building Grafana dashboards"
optional = false
python-versions = "*"
files = [
{file = "grafanalib-0.7.1-py3-none-any.whl", hash = "sha256:6fab5d7b837a1f2d1322ef762cd52e565ec0422707a7512765c59f668bdceb58"},
{file = "grafanalib-0.7.1.tar.gz", hash = "sha256:3d92bb4e92ae78fe4e21c5b252ab51f4fdcacd8523ba5a44545b897b2a375b83"},
]
[package.dependencies]
attrs = ">=15.2.0"
[package.extras]
dev = ["flake8", "pytest"]
[[package]]
name = "jinja2"
version = "3.1.4"
description = "A very fast and expressive template engine."
optional = false
python-versions = ">=3.7"
files = [
{file = "jinja2-3.1.4-py3-none-any.whl", hash = "sha256:bc5dd2abb727a5319567b7a813e6a2e7318c39f4f487cfe6c89c6f9c7d25197d"},
{file = "jinja2-3.1.4.tar.gz", hash = "sha256:4a3aee7acbbe7303aede8e9648d13b8bf88a429282aa6122a993f0ac800cb369"},
]
[package.dependencies]
MarkupSafe = ">=2.0"
[package.extras]
i18n = ["Babel (>=2.7)"]
[[package]]
name = "markupsafe"
version = "3.0.1"
description = "Safely add untrusted strings to HTML/XML markup."
optional = false
python-versions = ">=3.9"
files = [
{file = "MarkupSafe-3.0.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:db842712984e91707437461930e6011e60b39136c7331e971952bb30465bc1a1"},
{file = "MarkupSafe-3.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3ffb4a8e7d46ed96ae48805746755fadd0909fea2306f93d5d8233ba23dda12a"},
{file = "MarkupSafe-3.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:67c519635a4f64e495c50e3107d9b4075aec33634272b5db1cde839e07367589"},
{file = "MarkupSafe-3.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:48488d999ed50ba8d38c581d67e496f955821dc183883550a6fbc7f1aefdc170"},
{file = "MarkupSafe-3.0.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f31ae06f1328595d762c9a2bf29dafd8621c7d3adc130cbb46278079758779ca"},
{file = "MarkupSafe-3.0.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:80fcbf3add8790caddfab6764bde258b5d09aefbe9169c183f88a7410f0f6dea"},
{file = "MarkupSafe-3.0.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:3341c043c37d78cc5ae6e3e305e988532b072329639007fd408a476642a89fd6"},
{file = "MarkupSafe-3.0.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:cb53e2a99df28eee3b5f4fea166020d3ef9116fdc5764bc5117486e6d1211b25"},
{file = "MarkupSafe-3.0.1-cp310-cp310-win32.whl", hash = "sha256:db15ce28e1e127a0013dfb8ac243a8e392db8c61eae113337536edb28bdc1f97"},
{file = "MarkupSafe-3.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:4ffaaac913c3f7345579db4f33b0020db693f302ca5137f106060316761beea9"},
{file = "MarkupSafe-3.0.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:26627785a54a947f6d7336ce5963569b5d75614619e75193bdb4e06e21d447ad"},
{file = "MarkupSafe-3.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b954093679d5750495725ea6f88409946d69cfb25ea7b4c846eef5044194f583"},
{file = "MarkupSafe-3.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:973a371a55ce9ed333a3a0f8e0bcfae9e0d637711534bcb11e130af2ab9334e7"},
{file = "MarkupSafe-3.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:244dbe463d5fb6d7ce161301a03a6fe744dac9072328ba9fc82289238582697b"},
{file = "MarkupSafe-3.0.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d98e66a24497637dd31ccab090b34392dddb1f2f811c4b4cd80c230205c074a3"},
{file = "MarkupSafe-3.0.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:ad91738f14eb8da0ff82f2acd0098b6257621410dcbd4df20aaa5b4233d75a50"},
{file = "MarkupSafe-3.0.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:7044312a928a66a4c2a22644147bc61a199c1709712069a344a3fb5cfcf16915"},
{file = "MarkupSafe-3.0.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a4792d3b3a6dfafefdf8e937f14906a51bd27025a36f4b188728a73382231d91"},
{file = "MarkupSafe-3.0.1-cp311-cp311-win32.whl", hash = "sha256:fa7d686ed9883f3d664d39d5a8e74d3c5f63e603c2e3ff0abcba23eac6542635"},
{file = "MarkupSafe-3.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:9ba25a71ebf05b9bb0e2ae99f8bc08a07ee8e98c612175087112656ca0f5c8bf"},
{file = "MarkupSafe-3.0.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:8ae369e84466aa70f3154ee23c1451fda10a8ee1b63923ce76667e3077f2b0c4"},
{file = "MarkupSafe-3.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40f1e10d51c92859765522cbd79c5c8989f40f0419614bcdc5015e7b6bf97fc5"},
{file = "MarkupSafe-3.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5a4cb365cb49b750bdb60b846b0c0bc49ed62e59a76635095a179d440540c346"},
{file = "MarkupSafe-3.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ee3941769bd2522fe39222206f6dd97ae83c442a94c90f2b7a25d847d40f4729"},
{file = "MarkupSafe-3.0.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:62fada2c942702ef8952754abfc1a9f7658a4d5460fabe95ac7ec2cbe0d02abc"},
{file = "MarkupSafe-3.0.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4c2d64fdba74ad16138300815cfdc6ab2f4647e23ced81f59e940d7d4a1469d9"},
{file = "MarkupSafe-3.0.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:fb532dd9900381d2e8f48172ddc5a59db4c445a11b9fab40b3b786da40d3b56b"},
{file = "MarkupSafe-3.0.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0f84af7e813784feb4d5e4ff7db633aba6c8ca64a833f61d8e4eade234ef0c38"},
{file = "MarkupSafe-3.0.1-cp312-cp312-win32.whl", hash = "sha256:cbf445eb5628981a80f54087f9acdbf84f9b7d862756110d172993b9a5ae81aa"},
{file = "MarkupSafe-3.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:a10860e00ded1dd0a65b83e717af28845bb7bd16d8ace40fe5531491de76b79f"},
{file = "MarkupSafe-3.0.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:e81c52638315ff4ac1b533d427f50bc0afc746deb949210bc85f05d4f15fd772"},
{file = "MarkupSafe-3.0.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:312387403cd40699ab91d50735ea7a507b788091c416dd007eac54434aee51da"},
{file = "MarkupSafe-3.0.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2ae99f31f47d849758a687102afdd05bd3d3ff7dbab0a8f1587981b58a76152a"},
{file = "MarkupSafe-3.0.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c97ff7fedf56d86bae92fa0a646ce1a0ec7509a7578e1ed238731ba13aabcd1c"},
{file = "MarkupSafe-3.0.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a7420ceda262dbb4b8d839a4ec63d61c261e4e77677ed7c66c99f4e7cb5030dd"},
{file = "MarkupSafe-3.0.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:45d42d132cff577c92bfba536aefcfea7e26efb975bd455db4e6602f5c9f45e7"},
{file = "MarkupSafe-3.0.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:4c8817557d0de9349109acb38b9dd570b03cc5014e8aabf1cbddc6e81005becd"},
{file = "MarkupSafe-3.0.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6a54c43d3ec4cf2a39f4387ad044221c66a376e58c0d0e971d47c475ba79c6b5"},
{file = "MarkupSafe-3.0.1-cp313-cp313-win32.whl", hash = "sha256:c91b394f7601438ff79a4b93d16be92f216adb57d813a78be4446fe0f6bc2d8c"},
{file = "MarkupSafe-3.0.1-cp313-cp313-win_amd64.whl", hash = "sha256:fe32482b37b4b00c7a52a07211b479653b7fe4f22b2e481b9a9b099d8a430f2f"},
{file = "MarkupSafe-3.0.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:17b2aea42a7280db02ac644db1d634ad47dcc96faf38ab304fe26ba2680d359a"},
{file = "MarkupSafe-3.0.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:852dc840f6d7c985603e60b5deaae1d89c56cb038b577f6b5b8c808c97580f1d"},
{file = "MarkupSafe-3.0.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0778de17cff1acaeccc3ff30cd99a3fd5c50fc58ad3d6c0e0c4c58092b859396"},
{file = "MarkupSafe-3.0.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:800100d45176652ded796134277ecb13640c1a537cad3b8b53da45aa96330453"},
{file = "MarkupSafe-3.0.1-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d06b24c686a34c86c8c1fba923181eae6b10565e4d80bdd7bc1c8e2f11247aa4"},
{file = "MarkupSafe-3.0.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:33d1c36b90e570ba7785dacd1faaf091203d9942bc036118fab8110a401eb1a8"},
{file = "MarkupSafe-3.0.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:beeebf760a9c1f4c07ef6a53465e8cfa776ea6a2021eda0d0417ec41043fe984"},
{file = "MarkupSafe-3.0.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:bbde71a705f8e9e4c3e9e33db69341d040c827c7afa6789b14c6e16776074f5a"},
{file = "MarkupSafe-3.0.1-cp313-cp313t-win32.whl", hash = "sha256:82b5dba6eb1bcc29cc305a18a3c5365d2af06ee71b123216416f7e20d2a84e5b"},
{file = "MarkupSafe-3.0.1-cp313-cp313t-win_amd64.whl", hash = "sha256:730d86af59e0e43ce277bb83970530dd223bf7f2a838e086b50affa6ec5f9295"},
{file = "MarkupSafe-3.0.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:4935dd7883f1d50e2ffecca0aa33dc1946a94c8f3fdafb8df5c330e48f71b132"},
{file = "MarkupSafe-3.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e9393357f19954248b00bed7c56f29a25c930593a77630c719653d51e7669c2a"},
{file = "MarkupSafe-3.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40621d60d0e58aa573b68ac5e2d6b20d44392878e0bfc159012a5787c4e35bc8"},
{file = "MarkupSafe-3.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f94190df587738280d544971500b9cafc9b950d32efcb1fba9ac10d84e6aa4e6"},
{file = "MarkupSafe-3.0.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b6a387d61fe41cdf7ea95b38e9af11cfb1a63499af2759444b99185c4ab33f5b"},
{file = "MarkupSafe-3.0.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:8ad4ad1429cd4f315f32ef263c1342166695fad76c100c5d979c45d5570ed58b"},
{file = "MarkupSafe-3.0.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:e24bfe89c6ac4c31792793ad9f861b8f6dc4546ac6dc8f1c9083c7c4f2b335cd"},
{file = "MarkupSafe-3.0.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:2a4b34a8d14649315c4bc26bbfa352663eb51d146e35eef231dd739d54a5430a"},
{file = "MarkupSafe-3.0.1-cp39-cp39-win32.whl", hash = "sha256:242d6860f1fd9191aef5fae22b51c5c19767f93fb9ead4d21924e0bcb17619d8"},
{file = "MarkupSafe-3.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:93e8248d650e7e9d49e8251f883eed60ecbc0e8ffd6349e18550925e31bd029b"},
{file = "markupsafe-3.0.1.tar.gz", hash = "sha256:3e683ee4f5d0fa2dde4db77ed8dd8a876686e3fc417655c2ece9a90576905344"},
]
[[package]]
name = "netaddr"
version = "1.3.0"
description = "A network address manipulation library for Python"
optional = false
python-versions = ">=3.7"
files = [
{file = "netaddr-1.3.0-py3-none-any.whl", hash = "sha256:c2c6a8ebe5554ce33b7d5b3a306b71bbb373e000bbbf2350dd5213cc56e3dbbe"},
{file = "netaddr-1.3.0.tar.gz", hash = "sha256:5c3c3d9895b551b763779ba7db7a03487dc1f8e3b385af819af341ae9ef6e48a"},
]
[package.extras]
nicer-shell = ["ipython"]
[[package]]
name = "packaging"
version = "24.1"
description = "Core utilities for Python packages"
optional = false
python-versions = ">=3.8"
files = [
{file = "packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124"},
{file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"},
]
[[package]]
name = "pycparser"
version = "2.22"
description = "C parser in Python"
optional = false
python-versions = ">=3.8"
files = [
{file = "pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc"},
{file = "pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6"},
]
[[package]]
name = "pyyaml"
version = "6.0.2"
description = "YAML parser and emitter for Python"
optional = false
python-versions = ">=3.8"
files = [
{file = "PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086"},
{file = "PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf"},
{file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237"},
{file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b"},
{file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed"},
{file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180"},
{file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68"},
{file = "PyYAML-6.0.2-cp310-cp310-win32.whl", hash = "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99"},
{file = "PyYAML-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e"},
{file = "PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774"},
{file = "PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee"},
{file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c"},
{file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317"},
{file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85"},
{file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4"},
{file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e"},
{file = "PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5"},
{file = "PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44"},
{file = "PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab"},
{file = "PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725"},
{file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5"},
{file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425"},
{file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476"},
{file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48"},
{file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b"},
{file = "PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4"},
{file = "PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8"},
{file = "PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba"},
{file = "PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1"},
{file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133"},
{file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484"},
{file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5"},
{file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc"},
{file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652"},
{file = "PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183"},
{file = "PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563"},
{file = "PyYAML-6.0.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:24471b829b3bf607e04e88d79542a9d48bb037c2267d7927a874e6c205ca7e9a"},
{file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7fded462629cfa4b685c5416b949ebad6cec74af5e2d42905d41e257e0869f5"},
{file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d84a1718ee396f54f3a086ea0a66d8e552b2ab2017ef8b420e92edbc841c352d"},
{file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9056c1ecd25795207ad294bcf39f2db3d845767be0ea6e6a34d856f006006083"},
{file = "PyYAML-6.0.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:82d09873e40955485746739bcb8b4586983670466c23382c19cffecbf1fd8706"},
{file = "PyYAML-6.0.2-cp38-cp38-win32.whl", hash = "sha256:43fa96a3ca0d6b1812e01ced1044a003533c47f6ee8aca31724f78e93ccc089a"},
{file = "PyYAML-6.0.2-cp38-cp38-win_amd64.whl", hash = "sha256:01179a4a8559ab5de078078f37e5c1a30d76bb88519906844fd7bdea1b7729ff"},
{file = "PyYAML-6.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d"},
{file = "PyYAML-6.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f"},
{file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290"},
{file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12"},
{file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19"},
{file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e"},
{file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725"},
{file = "PyYAML-6.0.2-cp39-cp39-win32.whl", hash = "sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631"},
{file = "PyYAML-6.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8"},
{file = "pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e"},
]
[[package]]
name = "resolvelib"
version = "1.0.1"
description = "Resolve abstract dependencies into concrete ones"
optional = false
python-versions = "*"
files = [
{file = "resolvelib-1.0.1-py2.py3-none-any.whl", hash = "sha256:d2da45d1a8dfee81bdd591647783e340ef3bcb104b54c383f70d422ef5cc7dbf"},
{file = "resolvelib-1.0.1.tar.gz", hash = "sha256:04ce76cbd63fded2078ce224785da6ecd42b9564b1390793f64ddecbe997b309"},
]
[package.extras]
examples = ["html5lib", "packaging", "pygraphviz", "requests"]
lint = ["black", "flake8", "isort", "mypy", "types-requests"]
release = ["build", "towncrier", "twine"]
test = ["commentjson", "packaging", "pytest"]
[[package]]
name = "setuptools"
version = "75.1.0"
description = "Easily download, build, install, upgrade, and uninstall Python packages"
optional = false
python-versions = ">=3.8"
files = [
{file = "setuptools-75.1.0-py3-none-any.whl", hash = "sha256:35ab7fd3bcd95e6b7fd704e4a1539513edad446c097797f2985e0e4b960772f2"},
{file = "setuptools-75.1.0.tar.gz", hash = "sha256:d59a21b17a275fb872a9c3dae73963160ae079f1049ed956880cd7c09b120538"},
]
[package.extras]
check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)", "ruff (>=0.5.2)"]
core = ["importlib-metadata (>=6)", "importlib-resources (>=5.10.2)", "jaraco.collections", "jaraco.functools", "jaraco.text (>=3.7)", "more-itertools", "more-itertools (>=8.8)", "packaging", "packaging (>=24)", "platformdirs (>=2.6.2)", "tomli (>=2.0.1)", "wheel (>=0.43.0)"]
cover = ["pytest-cov"]
doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier", "towncrier (<24.7)"]
enabler = ["pytest-enabler (>=2.2)"]
test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "jaraco.test", "packaging (>=23.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-home (>=0.5)", "pytest-perf", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel (>=0.44.0)"]
type = ["importlib-metadata (>=7.0.2)", "jaraco.develop (>=7.21)", "mypy (==1.11.*)", "pytest-mypy"]
[metadata]
lock-version = "2.0"
python-versions = "^3.10"
content-hash = "334448cb0c7d192f0e10987a995ecefca5e136733cce4dd15dcc2238f1c371c8"

20
pyproject.toml Normal file
View File

@@ -0,0 +1,20 @@
[tool.poetry]
name = "alpina"
version = "0.1.0"
description = ""
authors = ["Iurii Tatishchev <itatishch@gmail.com>"]
readme = "README.md"
[tool.poetry.dependencies]
python = "^3.10"
ansible = "^10.1.0"
ansible-vault = "^2.1.0"
netaddr = "^1.3.0"
[tool.poetry.dev-dependencies]
grafanalib = "^0.7.1"
[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"

View File

@@ -1 +0,0 @@
ansible==6.0.0

View File

@@ -0,0 +1,15 @@
- name: Ensure {{ collection }} collection directory exists
file:
path: "{{ alpina_svc_path }}/{{ collection }}"
state: directory
mode: "700"
- name: Deploy docker compose stacks for {{ collection }}
vars:
current_stack_name: "{{ stack }}"
current_stack_dest: "{{ alpina_svc_path }}/{{ collection }}/{{ stack }}"
current_stack_source: "{{ role_path }}/templates/{{ collection }}/{{ stack }}"
include_tasks: deploy_compose_stack.yml
loop: "{{ stacks }}"
loop_control:
loop_var: stack

View File

@@ -0,0 +1,42 @@
# https://stackoverflow.com/questions/41667864/can-the-templates-module-handle-multiple-templates-directories
- name: Ensure {{ stack }} stack directory exists
file:
path: "{{ current_stack_dest }}"
state: directory
mode: "700"
- name: Ensure directory structure exists
file:
path: "{{ current_stack_dest }}/{{ item.path }}"
state: directory
mode: "755"
loop: "{{ lookup('community.general.filetree', current_stack_source) }}"
when: item.state == "directory"
- name: Generate {{ current_stack_name }} deployment from templates
template:
src: "{{ item.src }}"
dest: "{{ current_stack_dest }}/{{ item.path | regex_replace('\\.j2$', '') }}"
mode: "644"
loop: "{{ lookup('community.general.filetree', current_stack_source) }}"
when: item.state == "file" and item.path | regex_search('\\.j2$')
- name: Generate {{ current_stack_name }} deployment from static files
copy:
src: "{{ item.src }}"
dest: "{{ current_stack_dest }}/{{ item.path }}"
mode: "644"
loop: "{{ lookup('community.general.filetree', current_stack_source) }}"
when: item.state == "file" and not item.path | regex_search('\\.j2$')
- name: Deploy docker-compose for {{ current_stack_name }}
community.docker.docker_compose_v2:
project_src: "{{ current_stack_dest }}"
state: present
pull: always
remove_orphans: yes
register: docker_compose_output
# - debug:
# var: docker_compose_output

View File

@@ -0,0 +1,34 @@
- name: Register uid of remote user
command: id -u
register: remote_uid_command
changed_when: false
- name: Set fact for uid
set_fact:
remote_uid: "{{ remote_uid_command.stdout }}"
- name: Ensure alpina directory exists
file:
state: directory
path: "{{ alpina_svc_path }}"
mode: "700"
- name: Deploy collection services
vars:
collection: services
stacks:
- traefik
- monitoring
- authentik
- minio
import_tasks: deploy_collection.yml
- name: Deploy collection apps
vars:
collection: apps
stacks:
- gitea
- nextcloud
- jellyfin
- arrstack
import_tasks: deploy_collection.yml

View File

@@ -0,0 +1,33 @@
## ProtonVPN OpenVPN
#VPN_SERVICE_PROVIDER=protonvpn
#OPENVPN_USER=+pmp
#OPENVPN_PASSWORD=
#SERVER_HOSTNAMES=node-us-160.protonvpn.net,node-us-161.protonvpn.net
#VPN_PORT_FORWARDING=on
## ProtonVPN WireGuard
#VPN_SERVICE_PROVIDER=custom
#VPN_TYPE=wireguard
#VPN_ENDPOINT_IP=
#VPN_ENDPOINT_PORT=
#WIREGUARD_PUBLIC_KEY=
#WIREGUARD_PRIVATE_KEY=
#WIREGUARD_PRESHARED_KEY=
#WIREGUARD_ADDRESSES=
#VPN_DNS_ADDRESS=
#VPN_PORT_FORWARDING=on
#VPN_PORT_FORWARDING_PROVIDER=protonvpn
## AirVPN
VPN_SERVICE_PROVIDER=airvpn
VPN_TYPE=wireguard
SERVER_NAMES={{ vpn_server_names }}
WIREGUARD_PUBLIC_KEY={{ wg_peer_pubkey }}
WIREGUARD_PRIVATE_KEY={{ wg_privkey }}
WIREGUARD_PRESHARED_KEY={{ wg_psk }}
WIREGUARD_ADDRESSES={{ wg_addresses }}
FIREWALL_VPN_INPUT_PORTS={{ fw_vpn_input_ports }}
UPDATER_PERIOD=24h
#FIREWALL_OUTBOUND_SUBNETS=10.0.0.0/8,{{ docker_ipv6_subnet }}

View File

@@ -0,0 +1,88 @@
{% import 'contrib/compose_helpers.j2' as helpers with context %}
networks:
{{ helpers.default_network(249) | indent(2) }}
traefik_traefik:
external: true
services:
gluetun:
image: qmcgaw/gluetun:latest
container_name: gluetun
cap_add:
- NET_ADMIN
sysctls:
- net.ipv6.conf.all.disable_ipv6=0
env_file:
- .env.gluetun
restart: unless-stopped
networks:
- default
- traefik_traefik
volumes:
- {{ base_volume_path }}/arrstack/gluetun:/gluetun
qbittorrent:
image: linuxserver/qbittorrent:latest
container_name: qbittorrent
labels:
- {{ helpers.traefik_labels('qbit', port='8080', auth=true) | indent(6) }}
restart: unless-stopped
environment:
{# Keeping this for debugging purposes -#}
- DOCKER_MODS=linuxserver/mods:universal-package-install
network_mode: service:gluetun
volumes:
- {{ base_volume_path }}/arrstack/config/qbittorrent:/config
- {{ base_volume_path }}/arrstack/downloads:/downloads
- {{ media_volume_path }}/Plex:/media/Plex
- {{ media_volume_path }}/iso-img:/media/iso-img
depends_on:
gluetun:
condition: service_healthy
prowlarr:
image: linuxserver/prowlarr:latest
container_name: prowlarr
labels:
- {{ helpers.traefik_labels('prowlarr', port='9696', auth=true) | indent(6) }}
restart: unless-stopped
depends_on:
- qbittorrent
networks:
- default
- traefik_traefik
volumes:
- {{ base_volume_path }}/arrstack/config/prowlarr:/config
sonarr:
image: linuxserver/sonarr:latest
container_name: sonarr
labels:
- {{ helpers.traefik_labels('sonarr', port='8989', auth=true) | indent(6) }}
restart: unless-stopped
depends_on:
- qbittorrent
networks:
- default
- traefik_traefik
volumes:
- {{ base_volume_path }}/arrstack/config/sonarr:/config
- {{ base_volume_path }}/arrstack/downloads:/downloads
- {{ media_volume_path }}/Plex:/media/Plex
radarr:
image: linuxserver/radarr:latest
container_name: radarr
labels:
- {{ helpers.traefik_labels('radarr', port='7878', auth=true) | indent(6) }}
restart: unless-stopped
depends_on:
- qbittorrent
networks:
- default
- traefik_traefik
volumes:
- {{ base_volume_path }}/arrstack/config/radarr:/config
- {{ base_volume_path }}/arrstack/downloads:/downloads
- {{ media_volume_path }}/Plex:/media/Plex

View File

@@ -0,0 +1,3 @@
POSTGRES_USER=gitea
POSTGRES_DB=gitea
POSTGRES_PASSWORD={{ gitea_db_password }}

View File

@@ -0,0 +1,29 @@
GITEA____APP_NAME=CazGitea
# Database
GITEA__database__DB_TYPE=postgres
GITEA__database__HOST=db:5432
GITEA__database__NAME=gitea
GITEA__database__USER=gitea
GITEA__database__PASSWD={{ gitea_db_password }}
# Server
GITEA__server__ROOT_URL=https://gitea.{{ domain }}/
GITEA__server__DISABLE_SSH=true
# Mail
GITEA__mailer__ENABLED=true
GITEA__mailer__SMTP_ADDR=smtp.sendgrid.net
GITEA__mailer__SMTP_PORT=587
GITEA__mailer__FROM=gitea@cazzzer.com
GITEA__mailer__USER=apikey
GITEA__mailer__PASSWD={{ gitea_sendgrid_api_key }}
# Security
GITEA__security__SECRET_KEY={{ secret_key }}
GITEA__security__INTERNAL_TOKEN={{ internal_token }}
GITEA__oauth2__JWT_SECRET={{ jwt_secret }}
# Indexer
GITEA__indexer__REPO_INDEXER_ENABLED=true

View File

@@ -1,22 +1,16 @@
version: "3.9"
{% import 'contrib/compose_helpers.j2' as helpers with context %}
networks:
default:
{{ helpers.default_network(199) | indent(2) }}
traefik_traefik:
external: true
volumes:
gitea:
postgres:
services:
server:
image: gitea/gitea:1.17.2
image: gitea/gitea:1.22
container_name: gitea_server
labels:
- traefik.enable=true
- traefik.http.routers.gitea.rule=Host(`gitea.{{ domain }}`)
- traefik.http.services.gitea.loadbalancer.server.port=3000
- {{ helpers.traefik_labels('gitea', port='3000') | indent(6) }}
restart: unless-stopped
env_file:
- .env.gitea
@@ -24,11 +18,11 @@ services:
- default
- traefik_traefik
volumes:
- gitea:/data
- {{ base_volume_path }}/gitea/gitea:/data
depends_on:
- db
db:
image: postgres:14-alpine
image: postgres:16-alpine
container_name: gitea_db
restart: unless-stopped
env_file:
@@ -36,4 +30,4 @@ services:
networks:
- default
volumes:
- postgres:/var/lib/postgresql/data
- {{ base_volume_path }}/gitea/postgres:/var/lib/postgresql/data

View File

@@ -0,0 +1 @@
JELLYFIN_PublishedServerUrl=https://jellyfin.{{ domain }}

View File

@@ -0,0 +1,28 @@
{% import 'contrib/compose_helpers.j2' as helpers with context %}
networks:
{{ helpers.default_network(197) | indent(2) }}
traefik_traefik:
external: true
services:
jellyfin:
image: jellyfin/jellyfin:latest
container_name: jellyfin_jellyfin
labels:
- {{ helpers.traefik_labels('jellyfin', port='8096') | indent(6) }}
restart: unless-stopped
env_file:
- .env.jellyfin
networks:
- default
- traefik_traefik
volumes:
- {{ base_volume_path }}/jellyfin/config:/config
- {{ base_volume_path }}/jellyfin/cache:/cache
- {{ media_volume_path }}/Plex/media:/data/media:ro
- {{ media_volume_path }}/other_videos:/data/other_videos:ro
tmpfs:
- /tmp/transcodes
devices:
- /dev/dri:/dev/dri

View File

@@ -1,3 +1,3 @@
POSTGRES_USER=nextcloud
POSTGRES_DB=nextcloud
POSTGRES_PASSWORD="{{ db_password }}"
POSTGRES_PASSWORD={{ nextcloud_db_password }}

View File

@@ -0,0 +1 @@
NEXTCLOUD_VERSION=30-apache

View File

@@ -0,0 +1,24 @@
POSTGRES_DB=nextcloud
POSTGRES_USER=nextcloud
POSTGRES_PASSWORD={{ nextcloud_db_password }}
POSTGRES_HOST=db
NEXTCLOUD_TRUSTED_DOMAINS=nc.{{ domain }}
REDIS_HOST=redis
REDIS_HOST_PASSWORD={{ redis_password }}
SMTP_HOST=smtp.sendgrid.net
SMTP_SECURE=tls
SMTP_PORT=587
SMTP_AUTHTYPE=LOGIN
SMTP_NAME=apikey
SMTP_PASSWORD={{ nextcloud_sendgrid_api_key }}
MAIL_FROM_ADDRESS=nc
MAIL_DOMAIN=cazzzer.com
# host IPv4 and IPv6 addresses, loopback for notify_push
TRUSTED_PROXIES={{ hostvars[inventory_hostname]['ansible_default_ipv4']['address'] }} {{ hostvars[inventory_hostname]['ansible_default_ipv6']['address'] }} 127.0.0.1 ::1
OVERWRITEHOST=nc.{{ domain }}
OVERWRITEPROTOCOL=https
OVERWRITECLIURL=https://nc.{{ domain }}

View File

@@ -0,0 +1,4 @@
DATABASE_URL=postgres://nextcloud:{{ nextcloud_db_password }}@db/nextcloud
DATABASE_PREFIX=oc_
REDIS_URL=redis://:{{ redis_password }}@redis
NEXTCLOUD_URL=http://localhost

View File

@@ -0,0 +1 @@
REDIS_PASSWORD={{ redis_password }}

View File

@@ -0,0 +1,87 @@
{% import 'contrib/compose_helpers.j2' as helpers with context %}
networks:
{{ helpers.default_network(198) | indent(2) }}
traefik_traefik:
external: true
services:
app:
image: nextcloud:${NEXTCLOUD_VERSION}
container_name: nextcloud_app
labels:
- {{ helpers.traefik_labels('nc', port='80') | indent(6) }}
restart: unless-stopped
depends_on:
- db
- redis
env_file:
- .env.nextcloud
networks:
- default
volumes:
- {{ base_volume_path }}/nextcloud/nextcloud:/var/www/html
- {{ base_volume_path }}/nextcloud/nextcloud_config:/var/www/html/config
- {{ base_volume_path }}/nextcloud/nextcloud_data:/var/www/html/data
cron:
image: nextcloud:${NEXTCLOUD_VERSION}
container_name: nextcloud_cron
restart: unless-stopped
depends_on:
- app
entrypoint: /cron.sh
networks:
- default
volumes:
- {{ base_volume_path }}/nextcloud/nextcloud:/var/www/html
- {{ base_volume_path }}/nextcloud/nextcloud_config:/var/www/html/config
- {{ base_volume_path }}/nextcloud/nextcloud_data:/var/www/html/data
notify_push:
image: nextcloud:${NEXTCLOUD_VERSION}
container_name: nextcloud_notify_push
{# TODO: Refactor this and minio -#}
labels:
- traefik.enable=true
- traefik.http.routers.nc-notify.rule=Host(`nc.{{ domain }}`) && PathPrefix(`/push`)
- traefik.http.routers.nc-notify.entrypoints=websecure
- traefik.http.routers.nc-notify.tls=true
- traefik.http.routers.nc-notify.tls.certresolver=letsencrypt
- traefik.http.routers.nc-notify.tls.domains.0.main={{ domain }}
- traefik.http.routers.nc-notify.tls.domains.0.sans=*.{{ domain }}
- traefik.http.services.nc-notify.loadbalancer.server.port=7867
restart: unless-stopped
user: www-data
env_file:
- .env.notify_push
network_mode: service:app
entrypoint:
- /var/www/html/custom_apps/notify_push/bin/x86_64/notify_push
volumes:
- {{ base_volume_path }}/nextcloud/nextcloud:/var/www/html
db:
image: postgres:16-alpine
container_name: nextcloud_db
restart: unless-stopped
env_file:
- .env.db
networks:
- default
volumes:
- {{ base_volume_path }}/nextcloud/db:/var/lib/postgresql/data
redis:
image: redis:alpine
container_name: nextcloud_redis
restart: unless-stopped
env_file:
- .env.redis
networks:
- default
command:
- sh
- -c
- redis-server --requirepass $$REDIS_PASSWORD

View File

@@ -0,0 +1,21 @@
AUTHENTIK_ERROR_REPORTING__ENABLED=true
AUTHENTIK_REDIS__HOST=redis
AUTHENTIK_POSTGRESQL__HOST=postgres
AUTHENTIK_POSTGRESQL__USER=authentik
AUTHENTIK_POSTGRESQL__NAME=authentik
AUTHENTIK_POSTGRESQL__PASSWORD={{ authentik_db_password }}
AUTHENTIK_SECRET_KEY={{ authentik_secret_key }}
AUTHENTIK_EMAIL__HOST=smtp.sendgrid.net
AUTHENTIK_EMAIL__PORT=587
AUTHENTIK_EMAIL__USERNAME=apikey
AUTHENTIK_EMAIL__PASSWORD={{ authentik_sendgrid_api_key }}
AUTHENTIK_EMAIL__USE_TLS=true
AUTHENTIK_EMAIL__TIMEOUT=10
AUTHENTIK_EMAIL__FROM=auth@cazzzer.com
AUTHENTIK_DEFAULT_USER_CHANGE_EMAIL=false
AUTHENTIK_DEFAULT_USER_CHANGE_USERNAME=false

View File

@@ -0,0 +1,3 @@
POSTGRES_USER=authentik
POSTGRES_DB=authentik
POSTGRES_PASSWORD={{ authentik_db_password }}

View File

@@ -0,0 +1,84 @@
version: 1
metadata:
labels:
blueprints.goauthentik.io/instantiate: "true"
name: Alpina - OAuth2 Apps
entries:
{% set apps = {
"Grafana": {
"redirect_uri": "https://grafana."~ domain ~"/login/generic_oauth",
"icon": "https://grafana."~ domain ~"/public/img/grafana_icon.svg",
"client_secret": auth_grafana_client_secret,
"ui_group": "Services",
"allowed_for_groups": ["admins"],
},
"Minio": {
"redirect_uri": "https://minio."~ domain ~"/oauth_callback",
"icon": "https://minio."~ domain ~"/logo192.png",
"client_secret": auth_minio_client_secret,
"ui_group": "Services",
"allowed_for_groups": ["admins"],
},
"Gitea": {
"redirect_uri": "https://gitea."~ domain ~"/user/oauth2/Authentik/callback",
"icon": "https://gitea."~ domain ~"/assets/img/logo.svg",
"client_secret": auth_gitea_client_secret,
"ui_group": "Apps",
"allowed_for_groups": ["admins", "users"],
},
"Nextcloud": {
"redirect_uri": "https://nc."~ domain ~"/apps/sociallogin/custom_oidc/authentik",
"icon": "https://nc."~ domain ~"/apps/theming/favicon",
"client_secret": auth_nextcloud_client_secret,
"ui_group": "Apps",
"allowed_for_groups": ["admins", "users"],
},
} -%}
{% for app in apps.keys() -%}
- identifiers:
name: {{ app }}
model: authentik_providers_oauth2.oauth2provider
id: {{ app }}
attrs:
authorization_flow: !Find [authentik_flows.flow, [slug, default-provider-authorization-implicit-consent]]
invalidation_flow: !Find [authentik_flows.flow, [slug, default-provider-invalidation-flow]]
client_type: confidential
client_id: {{ app | lower }}
client_secret: {{ apps[app]["client_secret"] }}
property_mappings:
- !Find [authentik_providers_oauth2.scopemapping, [scope_name, openid]]
- !Find [authentik_providers_oauth2.scopemapping, [scope_name, email]]
- !Find [authentik_providers_oauth2.scopemapping, [scope_name, profile]]
{% if app == "Minio" -%}
- !Find [authentik_providers_oauth2.scopemapping, [scope_name, minio]]
{%- endif %}
redirect_uris:
- matching_mode: strict
url: {{ apps[app]["redirect_uri"] }}
# Necessary for JWKS to be generated correctly
signing_key: !Find [authentik_crypto.certificatekeypair, [name, "authentik Self-signed Certificate"]]
- identifiers:
slug: {{ app | lower }}
model: authentik_core.application
id: app-{{ app }}
attrs:
name: {{ app }}
group: "{{ apps[app]["ui_group"] }}"
meta_description: "Hello, I'm {{ app }}!"
meta_publisher: Alpina
icon: "{{ apps[app]["icon"] }}"
open_in_new_tab: true
provider: !KeyOf {{ app }}
{% for group in apps[app]["allowed_for_groups"] -%}
- identifiers:
group: !Find [authentik_core.group, [name, {{ group }}]]
target: !KeyOf app-{{ app }}
model: authentik_policies.policybinding
attrs:
order: 10
{% endfor %}
{% endfor %}

View File

@@ -0,0 +1,88 @@
version: 1
metadata:
labels:
blueprints.goauthentik.io/instantiate: "true"
name: Alpina - Proxied Apps
entries:
# TODO: Possibly refactor this into a jinja macro (?)
{% set apps = {
"Uptime Kuma": {
"host": "uptime",
"icon": "https://uptime."~ domain ~"/icon.svg",
"unauthenticated_paths": "^/icon.svg$",
"ui_group": "Services",
"allowed_for_groups": ["admins"],
},
"qBit": {
"host": "qbit",
"icon": "https://qbit."~ domain ~"/images/qbittorrent-tray.svg",
"unauthenticated_paths": "^/images/qbittorrent-tray.svg$",
"ui_group": "Arrstack",
"allowed_for_groups": ["arrstack"],
},
"Prowlarr": {
"host": "prowlarr",
"icon": "https://prowlarr."~ domain ~"/Content/Images/logo.svg",
"unauthenticated_paths": "^/Content/Images/logo.svg$",
"ui_group": "Arrstack",
"allowed_for_groups": ["arrstack"],
},
"Sonarr": {
"host": "sonarr",
"icon": "https://sonarr."~ domain ~"/Content/Images/logo.svg",
"unauthenticated_paths": "^/Content/Images/logo.svg$",
"ui_group": "Arrstack",
"allowed_for_groups": ["arrstack"],
},
"Radarr": {
"host": "radarr",
"icon": "https://radarr."~ domain ~"/Content/Images/logo.svg",
"unauthenticated_paths": "^/Content/Images/logo.svg$",
"ui_group": "Arrstack",
"allowed_for_groups": ["arrstack"],
},
} -%}
{% for app in apps.keys() -%}
- identifiers:
name: {{ app }}
model: authentik_providers_proxy.proxyprovider
id: {{ app }}
attrs:
authorization_flow: !Find [authentik_flows.flow, [ slug, default-provider-authorization-implicit-consent ] ]
mode: forward_single
external_host: "https://{{ apps[app]["host"] }}.{{ domain }}/"
skip_path_regex: "{{ apps[app]["unauthenticated_paths"] }}"
- identifiers:
slug: {{ app | lower | replace(" ", "-") }}
model: authentik_core.application
id: app-{{ app }}
attrs:
name: {{ app }}
group: {{ apps[app]["ui_group"] }}
meta_description: "Hello, I'm {{ app }}!"
meta_publisher: Alpina
icon: "{{ apps[app]["icon"] }}"
open_in_new_tab: true
provider: !KeyOf {{ app }}
{% for group in apps[app]["allowed_for_groups"] -%}
- identifiers:
group: !Find [authentik_core.group, [name, {{ group }}]]
target: !KeyOf app-{{ app }}
model: authentik_policies.policybinding
attrs:
order: 10
{% endfor %}
{% endfor %}
- identifiers:
managed: goauthentik.io/outposts/embedded
name: authentik Embedded Outpost
model: authentik_outposts.outpost
attrs:
providers:
{% for app in apps.keys() -%}
- !KeyOf {{ app }}
{% endfor %}

View File

@@ -0,0 +1,58 @@
version: 1
metadata:
labels:
blueprints.goauthentik.io/instantiate: "true"
name: Alpina - Default Authentication Modifications
entries:
# Add a new flow for passwordless WebAuthn authentication
- identifiers:
slug: authentication-passwordless-flow
model: authentik_flows.flow
attrs:
designation: authentication
name: WebAuthn Authentication Flow
title: Sign in with a passkey
# Add a new stage to the flow to validate just WebAuthn devices
- identifiers:
name: webauthn-validation
model: authentik_stages_authenticator_validate.authenticatorvalidatestage
attrs:
device_classes:
- webauthn
not_configured_action: deny
webauthn_user_verification: required
# Stage bindings for passwordless flow,
# 1. Validate WebAuthn key
- identifiers:
order: 10
stage: !Find [authentik_stages_authenticator_validate.authenticatorvalidatestage, [name, webauthn-validation]]
target: !Find [authentik_flows.flow, [slug, authentication-passwordless-flow]]
model: authentik_flows.flowstagebinding
# 2. Finish authenticating user
- identifiers:
order: 100
stage: !Find [authentik_stages_user_login.userloginstage, [name, default-authentication-login]]
target: !Find [authentik_flows.flow, [slug, authentication-passwordless-flow]]
model: authentik_flows.flowstagebinding
# Some modifications to the default identification stage
- identifiers:
name: default-authentication-identification
model: authentik_stages_identification.identificationstage
attrs:
# Allow username and password fields to be on the same page
password_stage: !Find [authentik_stages_password.passwordstage, [name, default-authentication-password]]
# Add a button to use the passwordless WebAuthn flow
passwordless_flow: !Find [authentik_flows.flow, [slug, authentication-passwordless-flow]]
sources:
- !Find [authentik_core.source, [slug, authentik-built-in]]
- !Find [authentik_sources_oauth.oauthsource, [slug, github]]
# Enable compatibility mode for the default authentication flow for better autofill support
- identifiers:
slug: default-authentication-flow
model: authentik_flows.flow
attrs:
compatibility_mode: true

View File

@@ -0,0 +1,40 @@
version: 1
metadata:
labels:
blueprints.goauthentik.io/instantiate: "true"
name: Alpina - Default Groups
entries:
- identifiers:
name: "admins"
model: authentik_core.group
id: "admins"
attrs:
is_superuser: true
- identifiers:
name: "users"
model: authentik_core.group
id: "users"
- identifiers:
name: "arrstack"
model: authentik_core.group
id: "arrstack"
attrs:
arrstack_username: "arr"
arrstack_password: "{{ arrstack_password }}"
- identifiers:
scope_name: "minio"
model: authentik_providers_oauth2.scopemapping
id: "scope-minio"
attrs:
name: "Minio Policy"
expression: |
policy = "default"
if ak_is_group_member(request.user, name="admins"):
policy = "consoleAdmin"
return {
"policy": policy,
}

View File

@@ -0,0 +1,25 @@
version: 1
metadata:
labels:
blueprints.goauthentik.io/instantiate: "true"
name: Alpina - GitHub OAuth
entries:
- identifiers:
slug: github
model: authentik_sources_oauth.oauthsource
attrs:
name: GitHub
slug: github
access_token_url: https://github.com/login/oauth/access_token
additional_scopes: openid read:org
authentication_flow: !Find [authentik_flows.flow, [slug, default-source-authentication]]
authorization_url: https://github.com/login/oauth/authorize
consumer_key: {{ github_consumer_key }}
consumer_secret: {{ github_consumer_secret }}
enabled: true
enrollment_flow: !Find [authentik_flows.flow, [slug, default-source-enrollment]]
policy_engine_mode: any
profile_url: https://api.github.com/user
provider_type: github
user_matching_mode: email_link
user_path_template: goauthentik.io/sources/%(slug)s

View File

@@ -0,0 +1,66 @@
{% import 'contrib/compose_helpers.j2' as helpers with context %}
networks:
{{ helpers.default_network(253) | indent(2) }}
traefik_traefik:
external: true
services:
server:
image: ghcr.io/goauthentik/server:latest
container_name: authentik_server
labels:
- {{ helpers.traefik_labels('auth', port='9000') | indent(6) }}
- traefik.http.middlewares.authentik.forwardauth.address=http://localhost:9000/outpost.goauthentik.io/auth/traefik
- traefik.http.middlewares.authentik.forwardauth.trustForwardHeader=true
- traefik.http.middlewares.authentik.forwardauth.authResponseHeaders=Authorization,X-authentik-username,X-authentik-groups,X-authentik-email,X-authentik-name,X-authentik-uid,X-authentik-jwt,X-authentik-meta-jwks,X-authentik-meta-outpost,X-authentik-meta-provider,X-authentik-meta-app,X-authentik-meta-version
restart: unless-stopped
# Port forward is needed because traefik can't resolve the container name from the host network
ports:
- "9000:9000"
command: server
env_file:
- .env.authentik
networks:
- default
- traefik_traefik
worker:
image: ghcr.io/goauthentik/server:latest
container_name: authentik_worker
restart: unless-stopped
command: worker
env_file:
- .env.authentik
volumes:
- ./blueprints:/blueprints/alpina
- {{ base_volume_path }}/authentik/certs:/certs
postgres:
image: postgres:16-alpine
container_name: authentik_postgres
restart: unless-stopped
env_file:
- .env.db
healthcheck:
test: ["CMD-SHELL", "pg_isready -d $${POSTGRES_DB} -U $${POSTGRES_USER}"]
start_period: 20s
interval: 30s
retries: 5
timeout: 5s
volumes:
- {{ base_volume_path }}/authentik/postgres:/var/lib/postgresql/data
redis:
image: redis:alpine
container_name: authentik_redis
restart: unless-stopped
command: --save 60 1 --loglevel warning
healthcheck:
test: ["CMD-SHELL", "redis-cli ping | grep PONG"]
start_period: 20s
interval: 30s
retries: 5
timeout: 3s
volumes:
- {{ base_volume_path }}/authentik/redis:/data

View File

@@ -0,0 +1,20 @@
MINIO_ROOT_USER=minio
MINIO_ROOT_PASSWORD={{ minio_password }}
MINIO_DOMAIN=s3.{{ domain }}
MINIO_SERVER_URL=https://s3.{{ domain }}
MINIO_BROWSER_REDIRECT_URL=https://minio.{{ domain }}
# https://min.io/docs/minio/linux/reference/minio-server/settings/iam/openid.html
MINIO_IDENTITY_OPENID_CONFIG_URL=https://auth.{{ domain }}/application/o/minio/.well-known/openid-configuration
MINIO_IDENTITY_OPENID_CLIENT_ID=minio
MINIO_IDENTITY_OPENID_CLIENT_SECRET={{ auth_minio_client_secret }}
# defaults to "policy"
#MINIO_IDENTITY_OPENID_CLAIM_NAME=policy
MINIO_IDENTITY_OPENID_DISPLAY_NAME=Authentik
# no need to specify scopes,
# as it defaults to the ones advertised at the discovery url
#MINIO_IDENTITY_OPENID_SCOPES=openid,profile,email,minio
#MINIO_IDENTITY_OPENID_REDIRECT_URI_DYNAMIC=off
#MINIO_IDENTITY_OPENID_CLAIM_USERINFO=on
#MINIO_IDENTITY_OPENID_COMMENT=

View File

@@ -0,0 +1,32 @@
{% import 'contrib/compose_helpers.j2' as helpers with context %}
networks:
{{ helpers.default_network(252) | indent(2) }}
traefik_traefik:
external: true
services:
minio:
image: minio/minio:latest
container_name: minio
labels:
- {{ helpers.traefik_labels('minio', port='9090') | indent(6) }}
- traefik.http.routers.minio.service=minio
- traefik.http.routers.minio-tls.service=minio
- traefik.http.routers.minio-s3.rule=Host(`s3.{{ domain }}`) || HostRegexp(`^.+[.]s3[.]{{ domain }}`)
- traefik.http.routers.minio-s3.entrypoints=websecure
- traefik.http.routers.minio-s3.tls=true
- traefik.http.routers.minio-s3.tls.certresolver=letsencrypt
- traefik.http.routers.minio-s3.tls.domains.0.main=s3.{{ domain }}
- traefik.http.routers.minio-s3.tls.domains.0.sans=*.s3.{{ domain }}
- traefik.http.routers.minio-s3.service=minio-s3
- traefik.http.services.minio-s3.loadbalancer.server.port=9000
restart: unless-stopped
command: server --console-address ":9090" /data
env_file:
- .env.minio
networks:
- default
- traefik_traefik
volumes:
- {{ base_volume_path }}/minio/data:/data

View File

@@ -0,0 +1,7 @@
DOCKER_INFLUXDB_INIT_MODE=setup
DOCKER_INFLUXDB_INIT_ORG=home
DOCKER_INFLUXDB_INIT_BUCKET=influx
DOCKER_INFLUXDB_INIT_USERNAME=CaZzzer
DOCKER_INFLUXDB_INIT_PASSWORD={{ influxdb_admin_password }}
DOCKER_INFLUXDB_INIT_ADMIN_TOKEN={{ influxdb_admin_token }}
DOCKER_INFLUXDB_INIT_RETENTION=1w

View File

@@ -0,0 +1,17 @@
FROM python:3-alpine AS builder
RUN pip install grafanalib
COPY ./grafana_config/dashboards /dashboards
# Required for grafanalib to find the shared python files like common.py
# https://github.com/weaveworks/grafanalib/issues/58
ENV PYTHONPATH=/dashboards
RUN generate-dashboards /dashboards/*.dashboard.py
FROM grafana/grafana:latest
#COPY ./grafana_config /etc/grafana
COPY ./grafana_config/dashboards/*.yaml /etc/grafana/provisioning/dashboards
COPY --from=builder /dashboards/*.json /etc/grafana/provisioning/dashboards

View File

@@ -0,0 +1,119 @@
{% import 'contrib/compose_helpers.j2' as helpers with context %}
networks:
{{ helpers.default_network(251) | indent(2) }}
traefik_traefik:
external: true
services:
grafana:
{# image: grafana/grafana:latest#}
build:
context: .
dockerfile: Dockerfile
container_name: grafana
labels:
- {{ helpers.traefik_labels('grafana', port='3000') | indent(6) }}
restart: unless-stopped
# Needed to make config files readable (not anymore, TODO: remove)
user: "{{ remote_uid }}"
networks:
- default
- traefik_traefik
volumes:
- {{ base_volume_path }}/monitoring/grafana:/var/lib/grafana
- ./grafana_config/grafana.ini:/etc/grafana/grafana.ini:ro
- ./grafana_config/datasources:/etc/grafana/provisioning/datasources:ro
{# - ./grafana_config:/etc/grafana:ro#}
loki:
image: grafana/loki:latest
container_name: loki
restart: unless-stopped
# Needed to make config files readable (not anymore, TODO: remove)
user: "{{ remote_uid }}"
command:
- -config.file=/etc/loki/loki-config.yaml
# Port forward is needed because not possible to resolve the container name from the host network
ports:
- 3100:3100
volumes:
- {{ base_volume_path }}/monitoring/loki:/loki
- ./loki_config:/etc/loki:ro
tmpfs:
- /tmp/loki
promtail:
image: grafana/promtail:latest
container_name: promtail
restart: unless-stopped
command:
- -config.file=/etc/promtail/promtail-config.yaml
ports:
- 514:514
volumes:
- ./promtail_config:/etc/promtail:ro
- /var/log:/var/log:ro
tmpfs:
- /tmp
prometheus:
image: prom/prometheus:latest
container_name: prometheus
restart: unless-stopped
# Needed to make config files readable (not anymore, TODO: remove)
user: "{{ remote_uid }}"
command:
- --config.file=/etc/prometheus/prometheus.yml
- --storage.tsdb.retention.time=30d
volumes:
- ./prometheus_config:/etc/prometheus:ro
- {{ base_volume_path }}/monitoring/prometheus_configs:/etc/prometheus/extra:ro
- {{ base_volume_path }}/monitoring/prometheus:/prometheus
node-exporter:
image: prom/node-exporter:latest
container_name: node-exporter
restart: unless-stopped
network_mode: host
pid: host
volumes:
- /:/host:ro,rslave
cadvisor:
image: gcr.io/cadvisor/cadvisor:latest
container_name: cadvisor
restart: unless-stopped
command:
- --docker_only=true
- --store_container_labels=false
- --whitelisted_container_labels=com.docker.compose.project,com.docker.compose.service
- --enable_metrics=cpu,cpuLoad,diskIO,memory,network,oom_event,process
volumes:
- /:/rootfs:ro
- /var/run:/var/run:rw
- /sys:/sys:ro
- /var/lib/docker/:/var/lib/docker:ro
influxdb:
image: influxdb:2.7-alpine
container_name: influxdb
labels:
- {{ helpers.traefik_labels('influxdb', port='8086') | indent(6) }}
restart: unless-stopped
env_file:
- .env.influxdb
networks:
- default
- traefik_traefik
volumes:
- {{ base_volume_path }}/monitoring/influxdb:/var/lib/influxdb2
uptime-kuma:
image: louislam/uptime-kuma:1
container_name: uptime-kuma
labels:
- {{ helpers.traefik_labels('uptime', port='3001', auth=true) | indent(6) }}
restart: unless-stopped
volumes:
- {{ base_volume_path }}/monitoring/uptime-kuma:/app/data

View File

@@ -0,0 +1,9 @@
apiVersion: 1
providers:
- name: "Grafana"
org_id: 1
folder: "Alpina"
type: "file"
options:
path: "/etc/grafana/provisioning/dashboards"

View File

@@ -0,0 +1,27 @@
from grafanalib.core import Template
# TODO: consider default params for common params like line width, show points, tooltip
PrometheusTemplate = Template(
name='datasource',
type='datasource',
label='Prometheus',
query='prometheus',
)
# TODO: this slightly less (clown emoji), normal Target gave me errors in grafana
class LokiTarget(object):
def __init__(self, loki_datasource, expr, legendFormat, refId):
self.loki_datasource = loki_datasource
self.expr = expr
self.legendFormat = legendFormat
self.refId = refId
def to_json_data(self):
return {
'datasource': self.loki_datasource,
'expr': self.expr,
'legendFormat': self.legendFormat,
'refId': self.refId,
'queryType': 'range',
}

View File

@@ -0,0 +1,129 @@
from grafanalib.core import (
Dashboard, TimeSeries,
Target, GridPos,
Templating, Template, REFRESH_ON_TIME_RANGE_CHANGE, Logs
)
from grafanalib.formatunits import BYTES_IEC, SECONDS, BYTES_SEC_IEC
from common import LokiTarget, PrometheusTemplate
prom_datasource='${datasource}'
loki_datasource='loki'
dashboard = Dashboard(
title='Containers',
uid='containers',
description='Data for compose projects from default Prometheus datasource collected by Cadvisor',
tags=[
'linux',
'docker',
],
templating=Templating(list=[
PrometheusTemplate,
Template(
name='compose_project',
label='Compose Project',
dataSource=prom_datasource,
query='label_values({__name__=~"container.*"}, container_label_com_docker_compose_project)',
includeAll=True,
multi=True,
refresh=REFRESH_ON_TIME_RANGE_CHANGE,
),
Template(
name='container_name',
label='Container',
dataSource=prom_datasource,
query='label_values({__name__=~"container.*", container_label_com_docker_compose_project=~"$compose_project"}, name)',
includeAll=True,
multi=True,
refresh=REFRESH_ON_TIME_RANGE_CHANGE,
),
Template(
name='logs_query',
label='Log Search',
query='',
type='textbox',
),
]),
timezone='browser',
panels=[
TimeSeries(
title='Container Memory Usage',
unit=BYTES_IEC,
gridPos=GridPos(h=8, w=12, x=0, y=0),
lineWidth=2,
fillOpacity=10,
showPoints='never',
stacking={'mode': 'normal'},
tooltipMode='all',
tooltipSort='desc',
targets=[
Target(
datasource=prom_datasource,
expr='max by (name) (container_memory_usage_bytes{name=~"$container_name", container_label_com_docker_compose_project=~"$compose_project"})',
legendFormat='{{ name }}',
refId='A',
),
],
),
TimeSeries(
title='Container CPU Usage',
unit=SECONDS,
gridPos=GridPos(h=8, w=12, x=12, y=0),
lineWidth=2,
fillOpacity=10,
showPoints='never',
tooltipMode='all',
tooltipSort='desc',
targets=[
Target(
datasource=prom_datasource,
expr='max by (name) (rate(container_cpu_usage_seconds_total{name=~"$container_name", container_label_com_docker_compose_project=~"$compose_project"}[$__rate_interval]))',
legendFormat='{{ name }}',
refId='A',
),
],
),
TimeSeries(
title='Container Network Traffic',
unit=BYTES_SEC_IEC,
gridPos=GridPos(h=8, w=12, x=0, y=8),
lineWidth=2,
fillOpacity=10,
showPoints='never',
tooltipMode='all',
tooltipSort='desc',
targets=[
Target(
datasource=prom_datasource,
expr='max by (name) (rate(container_network_receive_bytes_total{name=~"$container_name", container_label_com_docker_compose_project=~"$compose_project"}[$__rate_interval]))',
legendFormat="rx {{ name }}",
refId='A',
),
Target(
datasource=prom_datasource,
expr='-max by (name) (rate(container_network_transmit_bytes_total{name=~"$container_name", container_label_com_docker_compose_project=~"$compose_project"}[$__rate_interval]))',
legendFormat="tx {{ name }}",
refId='B',
),
],
),
Logs(
title='',
gridPos=GridPos(h=8, w=12, x=12, y=8),
showLabels=True,
showCommonLabels=True,
wrapLogMessages=True,
prettifyLogMessage=True,
dedupStrategy='numbers',
targets=[
LokiTarget(
loki_datasource=loki_datasource,
expr='{compose_project=~"$compose_project", container_name=~"$container_name"} |= `$logs_query`',
legendFormat='{{ container_name }}',
refId='A',
),
],
),
],
).auto_panel_ids()

View File

@@ -0,0 +1,139 @@
from grafanalib.core import Dashboard, Templating, Template, TimeSeries, PERCENT_UNIT_FORMAT, GridPos, Target
from grafanalib.formatunits import BYTES_IEC
from common import PrometheusTemplate
from node_consts import CPU_BASIC_COLORS, MEMORY_BASIC_COLORS
dashboard = Dashboard(
title='Node Exporter',
uid='node',
description='Node Exporter (not quite full)',
tags=[
'linux',
],
timezone='browser',
templating=Templating(list=[
# Datasource
PrometheusTemplate,
# Job
Template(
name='job',
label='Job',
dataSource='${datasource}',
query='label_values(node_uname_info, job)',
),
# Instance
Template(
name='instance',
label='Instance',
dataSource='${datasource}',
query='label_values(node_uname_info{job="$job"}, instance)',
),
]),
panels=[
# CPU Basic
TimeSeries(
title='CPU Basic',
description='Basic CPU usage info',
unit=PERCENT_UNIT_FORMAT,
gridPos=GridPos(h=8, w=12, x=0, y=0),
lineWidth=1,
fillOpacity=30,
showPoints='never',
stacking={'mode': 'percent', 'group': 'A'},
tooltipMode='all',
tooltipSort='desc',
targets=[
Target(
datasource='${datasource}',
expr='sum(irate(node_cpu_seconds_total{instance="$instance",job="$job", mode="system"}[$__rate_interval])) / scalar(count(count(node_cpu_seconds_total{instance="$instance",job="$job"}) by (cpu)))',
legendFormat='Busy System',
refId='A',
),
Target(
datasource='${datasource}',
expr='sum(irate(node_cpu_seconds_total{instance="$instance",job="$job", mode="user"}[$__rate_interval])) / scalar(count(count(node_cpu_seconds_total{instance="$instance",job="$job"}) by (cpu)))',
legendFormat='Busy User',
refId='B',
),
Target(
datasource='${datasource}',
expr='sum(irate(node_cpu_seconds_total{instance="$instance",job="$job", mode="iowait"}[$__rate_interval])) / scalar(count(count(node_cpu_seconds_total{instance="$instance",job="$job"}) by (cpu)))',
legendFormat='Busy Iowait',
refId='C',
),
Target(
datasource='${datasource}',
expr='sum(irate(node_cpu_seconds_total{instance="$instance",job="$job", mode=~".*irq"}[$__rate_interval])) / scalar(count(count(node_cpu_seconds_total{instance="$instance",job="$job"}) by (cpu)))',
legendFormat='Busy IRQs',
refId='D',
),
Target(
datasource='${datasource}',
expr='sum(irate(node_cpu_seconds_total{instance="$instance",job="$job", mode!="idle",mode!="user",mode!="system",mode!="iowait",mode!="irq",mode!="softirq"}[$__rate_interval])) / scalar(count(count(node_cpu_seconds_total{instance="$instance",job="$job"}) by (cpu)))',
legendFormat='Busy Other',
refId='E',
),
Target(
datasource='${datasource}',
expr='sum(irate(node_cpu_seconds_total{instance="$instance",job="$job", mode="idle"}[$__rate_interval])) / scalar(count(count(node_cpu_seconds_total{instance="$instance",job="$job"}) by (cpu)))',
legendFormat='Idle',
refId='F',
),
],
# Extra JSON for the colors
extraJson=CPU_BASIC_COLORS,
),
# Memory Basic
TimeSeries(
title='Memory Basic',
description='Basic memory usage',
unit=BYTES_IEC,
gridPos=GridPos(h=8, w=12, x=12, y=0),
lineWidth=1,
fillOpacity=30,
showPoints='never',
stacking={'mode': 'normal', 'group': 'A'},
tooltipMode='all',
tooltipSort='desc',
targets=[
Target(
datasource='${datasource}',
expr='node_memory_MemTotal_bytes{instance="$instance",job="$job"}',
format='time_series',
legendFormat='RAM Total',
refId='A',
),
Target(
datasource='${datasource}',
expr='node_memory_MemTotal_bytes{instance="$instance",job="$job"} - node_memory_MemFree_bytes{instance="$instance",job="$job"} - (node_memory_Cached_bytes{instance="$instance",job="$job"} + node_memory_Buffers_bytes{instance="$instance",job="$job"} + node_memory_SReclaimable_bytes{instance="$instance",job="$job"})',
format='time_series',
legendFormat='RAM Used',
refId='B',
),
Target(
datasource='${datasource}',
expr='node_memory_Cached_bytes{instance="$instance",job="$job"} + node_memory_Buffers_bytes{instance="$instance",job="$job"} + node_memory_SReclaimable_bytes{instance="$instance",job="$job"}',
legendFormat='RAM Cache + Buffer',
refId='C',
),
Target(
datasource='${datasource}',
expr='node_memory_MemFree_bytes{instance="$instance",job="$job"}',
legendFormat='RAM Free',
refId='D',
),
Target(
datasource='${datasource}',
expr='(node_memory_SwapTotal_bytes{instance="$instance",job="$job"} - node_memory_SwapFree_bytes{instance="$instance",job="$job"})',
legendFormat='SWAP Used',
refId='E',
),
],
# Extra JSON for the colors
extraJson=MEMORY_BASIC_COLORS,
),
# TODO: Network Basic
# TODO: Disk Basic
],
).auto_panel_ids()

View File

@@ -0,0 +1,487 @@
# TODO: Question life decisions (I'm not sure if this is good)
CPU_BASIC_COLORS = {
"fieldConfig": {
"overrides": [
{
"matcher": {
"id": "byName",
"options": "Busy Iowait"
},
"properties": [
{
"id": "color",
"value": {
"fixedColor": "#890F02",
"mode": "fixed"
}
}
]
},
{
"matcher": {
"id": "byName",
"options": "Idle"
},
"properties": [
{
"id": "color",
"value": {
"fixedColor": "#052B51",
"mode": "fixed"
}
}
]
},
{
"matcher": {
"id": "byName",
"options": "Busy Iowait"
},
"properties": [
{
"id": "color",
"value": {
"fixedColor": "#890F02",
"mode": "fixed"
}
}
]
},
{
"matcher": {
"id": "byName",
"options": "Idle"
},
"properties": [
{
"id": "color",
"value": {
"fixedColor": "#7EB26D",
"mode": "fixed"
}
}
]
},
{
"matcher": {
"id": "byName",
"options": "Busy System"
},
"properties": [
{
"id": "color",
"value": {
"fixedColor": "#EAB839",
"mode": "fixed"
}
}
]
},
{
"matcher": {
"id": "byName",
"options": "Busy User"
},
"properties": [
{
"id": "color",
"value": {
"fixedColor": "#0A437C",
"mode": "fixed"
}
}
]
},
{
"matcher": {
"id": "byName",
"options": "Busy Other"
},
"properties": [
{
"id": "color",
"value": {
"fixedColor": "#6D1F62",
"mode": "fixed"
}
}
]
}
]
},
}
MEMORY_BASIC_COLORS = {
"fieldConfig": {
"overrides": [
{
"matcher": {
"id": "byName",
"options": "Apps"
},
"properties": [
{
"id": "color",
"value": {
"fixedColor": "#629E51",
"mode": "fixed"
}
}
]
},
{
"matcher": {
"id": "byName",
"options": "Buffers"
},
"properties": [
{
"id": "color",
"value": {
"fixedColor": "#614D93",
"mode": "fixed"
}
}
]
},
{
"matcher": {
"id": "byName",
"options": "Cache"
},
"properties": [
{
"id": "color",
"value": {
"fixedColor": "#6D1F62",
"mode": "fixed"
}
}
]
},
{
"matcher": {
"id": "byName",
"options": "Cached"
},
"properties": [
{
"id": "color",
"value": {
"fixedColor": "#511749",
"mode": "fixed"
}
}
]
},
{
"matcher": {
"id": "byName",
"options": "Committed"
},
"properties": [
{
"id": "color",
"value": {
"fixedColor": "#508642",
"mode": "fixed"
}
}
]
},
{
"matcher": {
"id": "byName",
"options": "Free"
},
"properties": [
{
"id": "color",
"value": {
"fixedColor": "#0A437C",
"mode": "fixed"
}
}
]
},
{
"matcher": {
"id": "byName",
"options": "Hardware Corrupted - Amount of RAM that the kernel identified as corrupted / not working"
},
"properties": [
{
"id": "color",
"value": {
"fixedColor": "#CFFAFF",
"mode": "fixed"
}
}
]
},
{
"matcher": {
"id": "byName",
"options": "Inactive"
},
"properties": [
{
"id": "color",
"value": {
"fixedColor": "#584477",
"mode": "fixed"
}
}
]
},
{
"matcher": {
"id": "byName",
"options": "PageTables"
},
"properties": [
{
"id": "color",
"value": {
"fixedColor": "#0A50A1",
"mode": "fixed"
}
}
]
},
{
"matcher": {
"id": "byName",
"options": "Page_Tables"
},
"properties": [
{
"id": "color",
"value": {
"fixedColor": "#0A50A1",
"mode": "fixed"
}
}
]
},
{
"matcher": {
"id": "byName",
"options": "RAM_Free"
},
"properties": [
{
"id": "color",
"value": {
"fixedColor": "#E0F9D7",
"mode": "fixed"
}
}
]
},
{
"matcher": {
"id": "byName",
"options": "SWAP Used"
},
"properties": [
{
"id": "color",
"value": {
"fixedColor": "#BF1B00",
"mode": "fixed"
}
}
]
},
{
"matcher": {
"id": "byName",
"options": "Slab"
},
"properties": [
{
"id": "color",
"value": {
"fixedColor": "#806EB7",
"mode": "fixed"
}
}
]
},
{
"matcher": {
"id": "byName",
"options": "Slab_Cache"
},
"properties": [
{
"id": "color",
"value": {
"fixedColor": "#E0752D",
"mode": "fixed"
}
}
]
},
{
"matcher": {
"id": "byName",
"options": "Swap"
},
"properties": [
{
"id": "color",
"value": {
"fixedColor": "#BF1B00",
"mode": "fixed"
}
}
]
},
{
"matcher": {
"id": "byName",
"options": "Swap Used"
},
"properties": [
{
"id": "color",
"value": {
"fixedColor": "#BF1B00",
"mode": "fixed"
}
}
]
},
{
"matcher": {
"id": "byName",
"options": "Swap_Cache"
},
"properties": [
{
"id": "color",
"value": {
"fixedColor": "#C15C17",
"mode": "fixed"
}
}
]
},
{
"matcher": {
"id": "byName",
"options": "Swap_Free"
},
"properties": [
{
"id": "color",
"value": {
"fixedColor": "#2F575E",
"mode": "fixed"
}
}
]
},
{
"matcher": {
"id": "byName",
"options": "Unused"
},
"properties": [
{
"id": "color",
"value": {
"fixedColor": "#EAB839",
"mode": "fixed"
}
}
]
},
{
"matcher": {
"id": "byName",
"options": "RAM Total"
},
"properties": [
{
"id": "color",
"value": {
"fixedColor": "#E0F9D7",
"mode": "fixed"
}
},
{
"id": "custom.fillOpacity",
"value": 0
},
{
"id": "custom.stacking",
"value": {
"group": False,
"mode": "normal"
}
}
]
},
{
"matcher": {
"id": "byName",
"options": "RAM Cache + Buffer"
},
"properties": [
{
"id": "color",
"value": {
"fixedColor": "#052B51",
"mode": "fixed"
}
}
]
},
{
"matcher": {
"id": "byName",
"options": "RAM Free"
},
"properties": [
{
"id": "color",
"value": {
"fixedColor": "#7EB26D",
"mode": "fixed"
}
}
]
},
{
"matcher": {
"id": "byName",
"options": "Available"
},
"properties": [
{
"id": "color",
"value": {
"fixedColor": "#DEDAF7",
"mode": "fixed"
}
},
{
"id": "custom.fillOpacity",
"value": 0
},
{
"id": "custom.stacking",
"value": {
"group": False,
"mode": "normal"
}
}
]
}
]
}
}

View File

@@ -0,0 +1,29 @@
apiVersion: 1
datasources:
- name: Loki
type: loki
access: proxy
uid: loki
url: http://loki:3100
editable: false
- name: Prometheus
type: prometheus
access: proxy
uid: prometheus
url: http://prometheus:9090
editable: false
- name: InfluxDB
type: influxdb
access: proxy
uid: influxdb
url: http://influxdb:8086
jsonData:
version: Flux
organization: home
defaultBucket: influx
secureJsonData:
token: {{ influxdb_admin_token }}
editable: false

View File

@@ -0,0 +1,34 @@
[server]
domain = grafana.{{ domain }}
root_url = https://%(domain)s/
;[security]
;admin_user =
;admin_email =
;admin_password =
; https://grafana.com/docs/grafana/latest/setup-grafana/configure-security/configure-authentication/generic-oauth/
[auth]
disable_login_form = true
signout_redirect_url = https://auth.{{ domain }}/application/o/grafana/end-session/
[auth.generic_oauth]
name = Authentik
enabled = true
allow_sign_up = true
client_id = grafana
client_secret = {{ auth_grafana_client_secret }}
scopes = openid profile email
auth_url = https://auth.{{ domain }}/application/o/authorize/
token_url = https://auth.{{ domain }}/application/o/token/
api_url = https://auth.{{ domain }}/application/o/userinfo/
email_attribute_path = email
login_attribute_path = preferred_username
name_attribute_path = name
# Optionally map user groups to Grafana roles
allow_assign_grafana_admin = true
role_attribute_path = contains(groups[*], 'Grafana Admins') && 'GrafanaAdmin' || 'Viewer'

View File

@@ -0,0 +1,30 @@
auth_enabled: false
server:
http_listen_port: 3100
common:
path_prefix: /loki
# TODO: Consider setting up S3 for storage
storage:
filesystem:
chunks_directory: /loki/chunks
rules_directory: /loki/rules
replication_factor: 1
ring:
kvstore:
store: inmemory
schema_config:
configs:
- from: 2024-10-18
index:
period: 24h
prefix: index_
object_store: filesystem
schema: v13
store: tsdb
# TODO: Figure this out
# ruler:
# alertmanager_url: http://localhost:9093

View File

@@ -0,0 +1,39 @@
global:
scrape_interval: 15s
evaluation_interval: 15s
external_labels:
monitor: "{{ ansible_host }}"
scrape_configs:
- job_name: "prometheus"
static_configs:
- targets: ["localhost:9090"]
- job_name: "node"
static_configs:
- targets: ["{{ ansible_host }}:9100"]
- job_name: "cadvisor"
static_configs:
- targets: ["cadvisor:8080"]
- job_name: "traefik"
static_configs:
- targets: ["{{ ansible_host }}:8082"]
- job_name: "loki"
static_configs:
- targets: ["loki:3100"]
- job_name: "promtail"
static_configs:
- targets: ["promtail:9080"]
rule_files:
- "/etc/prometheus/extra/rules/*.yml"
- "/etc/prometheus/extra/rules/*.json"
scrape_config_files:
- "/etc/prometheus/extra/scrape_configs/*.yml"
- "/etc/prometheus/extra/scrape_configs/*.json"

View File

@@ -0,0 +1,48 @@
server:
http_listen_port: 9080
grpc_listen_port: 0
positions:
filename: /tmp/positions.yaml
clients:
- url: http://loki:3100/loki/api/v1/push
scrape_configs:
# local machine logs
- job_name: local
static_configs:
- targets:
- localhost
labels:
job: varlogs
__path__: /var/log/*log
# syslog target
- job_name: syslog
syslog:
listen_address: "0.0.0.0:514" # make sure you also expose this port on the container
idle_timeout: 60s
label_structured_data: yes
labels:
job: "syslog"
relabel_configs:
- source_labels: ['__syslog_message_hostname']
target_label: 'host'
- source_labels: ['__syslog_message_facility']
target_label: 'facility'
- source_labels: ['__syslog_message_severity']
target_label: 'severity'
- source_labels: ['__syslog_message_app_name']
target_label: 'app_name'
## docker logs
#- job_name: docker
# pipeline_stages:
# - docker: {}
# static_configs:
# - labels:
# job: docker
# __path__: /var/lib/docker/containers/*/*-json.log

View File

@@ -0,0 +1 @@
CF_DNS_API_TOKEN={{ cloudflare_api_token }}

View File

@@ -0,0 +1,36 @@
{% import 'contrib/compose_helpers.j2' as helpers with context %}
networks:
traefik:
internal: true
enable_ipv6: true
ipam:
config:
# TODO: Consider removing traefik network, it shouldn't be needed with host networking
- subnet: {{ traefik_subnet }}/24
- subnet: {{ docker_ipv6_subnet | ansible.utils.ipsubnet(80, 255) }}
services:
traefik:
image: traefik:v3.0
container_name: traefik
restart: unless-stopped
env_file:
- .env.traefik
network_mode: host
volumes:
- ./traefik.yml:/etc/traefik/traefik.yml:ro
- ./rules:/rules:ro
- /var/run/docker.sock:/var/run/docker.sock:ro
- {{ base_volume_path }}/traefik/rules:/rules/extra:ro
- {{ base_volume_path }}/traefik/logs:/logs
- {{ base_volume_path }}/traefik/acme:/acme
# This is mostly just so that the traefik network gets created
whoami:
image: containous/whoami
container_name: whoami
labels:
- {{ helpers.traefik_labels('whoami', port=80) | indent(6) }}
networks:
- traefik

View File

@@ -0,0 +1,25 @@
http:
routers:
traefik-dash:
rule: "Host(`traefik.{{ domain }}`)"
entryPoints:
- web
service: traefik-dash
traefik-dash-tls:
rule: "Host(`traefik.{{ domain }}`)"
entryPoints:
- websecure
service: traefik-dash
tls:
certResolver: letsencrypt
domains:
- main: "{{ domain }}"
sans:
- "*.{{ domain }}"
services:
traefik-dash:
loadBalancer:
servers:
- url: "http://localhost:8080"

View File

@@ -0,0 +1,45 @@
api:
insecure: true
log:
filePath: /logs/traefik.log
level: INFO
accessLog:
filePath: /logs/access.log
bufferingSize: 100
entryPoints:
web:
address: ":80"
websecure:
address: ":443"
http3:
advertisedPort: 443
metrics:
address: ":8082"
metrics:
prometheus:
entryPoint: metrics
certificatesResolvers:
letsencrypt:
acme:
email: {{ acme_email }}
storage: "/acme/acme.json"
keyType: "EC384"
dnsChallenge:
provider: "cloudflare"
delayBeforeCheck: 10
resolvers:
- 1.1.1.1
- 8.8.8.8
- 9.9.9.9
providers:
docker:
exposedByDefault: false
network: traefik_traefik
file:
directory: /rules
watch: true

View File

@@ -1,43 +1,70 @@
- name: Upgrade alpine packages
community.general.apk:
upgrade: yes
update_cache: yes
register: apk_upgrades
- name: Install alpine packages
community.general.apk:
- name: Install Debian packages
become: yes
ansible.builtin.apt:
name:
- qemu-guest-agent
- dhcpcd
- python3
- fish
- docker
- docker-compose
- docker-fish-completion
- docker-compose-fish-completion
- zfs
- docker-ce
- docker-compose-plugin
- firewalld
state: latest
- name: Upgrade Debian packages
become: yes
ansible.builtin.apt:
upgrade: dist
update_cache: yes
register: apk_installs
cache_valid_time: 3600
autoremove: yes
state: latest
register: apt_upgrades
- name: Enable qemu-guest-agent service
- name: Ensure firewalld is running
become: yes
service:
name: qemu-guest-agent
runlevel: boot
name: firewalld
state: started
enabled: yes
- name: Enable zfs-import service
service:
name: zfs-import
runlevel: sysinit
enabled: yes
- name: Allow SSH
become: yes
firewalld:
service: ssh
permanent: yes
state: enabled
immediate: yes
- name: Enable zfs-mount service
service:
name: zfs-mount
runlevel: sysinit
enabled: yes
- name: Allow Web
become: yes
firewalld:
service: http
permanent: yes
state: disabled
immediate: yes
- name: Allow Web Secure
become: yes
firewalld:
service: https
permanent: yes
state: enabled
immediate: yes
- name: Allow 443 udp for http3
become: yes
firewalld:
port: 443/udp
permanent: yes
state: enabled
immediate: yes
- name: Allow 514 tcp for syslog
become: yes
firewalld:
port: 514/tcp
permanent: yes
state: enabled
immediate: yes
- name: Reboot if needed
reboot:
when: apk_upgrades.changed or apk_installs.changed
become: yes
ansible.builtin.reboot:
when: apt_upgrades.changed

View File

@@ -1,5 +1,70 @@
- name: Create my service directory
- name: Get IPv6 subnet for Docker
set_fact:
docker_ipv6_subnet: "{{ \
ansible_default_ipv6.address \
| ansible.utils.ipsubnet(64) \
| ansible.utils.ipsubnet(72, docker_ipv6_index) \
}}"
- debug:
var: docker_ipv6_subnet
- name: Configure Docker daemon
become: yes
template:
src: "daemon.json.j2"
dest: "/etc/docker/daemon.json"
owner: root
group: root
mode: "0644"
register: docker_daemon_config
- name: Install Docker loki plugin for logs
community.docker.docker_plugin:
plugin_name: grafana/loki-docker-driver:latest
alias: loki
state: enable
- name: Remove docker0 from firewalld trusted zone
become: yes
firewalld:
zone: trusted
interface: docker0
permanent: yes
immediate: yes
state: disabled
register: docker0_firewalld
- name: Get list of running Docker containers
docker_host_info:
containers: yes
register: docker_container_list
when: clean_desired is true
- name: Stop all running Docker containers
docker_container:
name: "{{ item }}"
state: stopped
loop: "{{ docker_container_list.containers | map(attribute='Id') | list }}"
async: 300
poll: 0
when: clean_desired is true and docker_container_list.containers | length > 0
- name: Prune all Docker containers and networks
docker_prune:
containers: yes
networks: yes
when: clean_desired is true
- name: Clean alpina directory
file:
state: directory
path: "{{ my_svc_path }}"
mode: "700"
path: "{{ alpina_svc_path }}"
state: absent
when: clean_desired is true
- name: Restart Docker daemon
become: yes
service:
name: docker
state: restarted
when: docker_daemon_config.changed or docker0_firewalld.changed

View File

@@ -0,0 +1,10 @@
{
"ipv6": true,
"fixed-cidr-v6": "{{ docker_ipv6_subnet | ansible.utils.ipsubnet(80, 0) }}",
"log-driver": "loki",
"log-opts": {
"loki-url": "http://localhost:3100/loki/api/v1/push",
"loki-batch-size": "400",
"loki-retries": "5"
}
}

View File

@@ -1,33 +0,0 @@
# https://stackoverflow.com/questions/41667864/can-the-templates-module-handle-multiple-templates-directories
- name: Ensure service directory exists
file:
path: "{{ current_svc_path }}"
state: directory
mode: "500"
- name: Ensure directory structure exists
file:
path: "{{ current_svc_path }}/{{ item.path }}"
state: directory
mode: "500"
with_community.general.filetree: "{{ templates_source }}"
when: item.state == "directory"
- name: Include app config variables
include_vars:
file: "{{ role_path }}/vars/app_config.yml"
- name: Generate {{ current_svc_name }} deployment from templates
template:
src: "{{ item.src }}"
dest: "{{ current_svc_path }}/{{ item.path | regex_replace('\\.j2$', '') }}"
mode: "400"
with_community.general.filetree: "{{ templates_source }}"
when: item.state == "file"
- name: Deploy docker-compose for {{ current_svc_name }}
community.docker.docker_compose:
project_src: "{{ current_svc_path }}"
state: present

View File

@@ -1,3 +0,0 @@
POSTGRES_USER=gitea
POSTGRES_DB=gitea
POSTGRES_PASSWORD="{{ db_password }}"

View File

@@ -1,25 +0,0 @@
GITEA____APP_NAME=CazGitea
# Database
GITEA__database__DB_TYPE=postgres
GITEA__database__HOST=db:5432
GITEA__database__NAME="{{ db_user }}"
GITEA__database__USER="{{ db_name }}"
GITEA__database__PASSWD="{{ db_password }}"
# Server
GITEA__server__ROOT_URL=https://gitea.cazzzer.com/
GITEA__server__DISABLE_SSH=true
# Mail
GITEA__mailer__ENABLED=true
GITEA__mailer__HOST=smtp.sendgrid.net:587
GITEA__mailer__FROM=gitea@cazzzer.com
GITEA__mailer__USER=apikey
GITEA__mailer__PASSWD="{{ sendgrid_api_key }}"
# Security
GITEA__security__SECRET_KEY="{{ secret_key }}"
GITEA__security__INTERNAL_TOKEN="{{ internal_token }}"
GITEA__oauth2__JWT_SECRET="{{ jwt_secret }}"

View File

@@ -1,27 +0,0 @@
$ANSIBLE_VAULT;1.2;AES256;alpina
35303032386566343430633238343936366234333434343763666231666232633539303232383534
3035346233346162373939333531613535353232626531640a646537616163353736653161326265
31336530316335623335353661373834613264326436303933326135396166346562343136353931
6439383039346465300a366266393130356630316630333336616565366562613038393239623738
65626664643630353236333932373337333363626337386163613464306638633964663264363964
30373661393531306662323134626664656233323762393037356434353066343830333033316365
65616636613437663737306263373066306361376630616331663031346434336663393862316464
62343339663461353934323063653566303932656264363562333136353665336263646230323832
35376666303531383961646234663230663634393135326664386665633538616233613866373965
64363361313232316336376631646662376565353536316438306361306261663532386564616566
61663534393035343233326562303863646165346538393761326335376165623964396130393831
64333665313461666335383134613831376138393061343238643661366439636534626265323865
35393035336632653038623438626366373733626331633866373935616531623664303063376562
31356332346164663364636235333461383437623161343338643839323765336237633266633864
64363234646533616439313638363865373364623637636537623666383664656630333533303233
64383734366666633832393230663739333435666138636462336332373061346239306136336263
39643666303863303035313738343664636536663939616335303834333834363739303938646665
66303637633239373461393434313036316563313132356432633337666537616363373830313034
61313538633663653230643262613333306361666131663036643162343966313365653566393235
36623832663034373734653664613038363137366437326565373761663963636336393536386435
30393831326134376639366661653439616138643438646363343632346131306532663439396534
32383661306539306635336262383563376561303862396532633362666266313562623336383235
36366565633734633639653239306331333237353233326563653930653739316230666362323931
39663931376562653530323434656436353166393836643238643632396430353034333034333665
62323338373839383132323537353431636537616366393965643463316164323034316536383961
6164333537633631646663333463306236613038326339643439

View File

@@ -1,5 +0,0 @@
---
current_svc_name: gitea
current_svc_path: "{{ my_svc_path }}/{{ current_svc_name }}"
templates_source: "{{ role_path }}/templates"

View File

@@ -1,33 +0,0 @@
# https://stackoverflow.com/questions/41667864/can-the-templates-module-handle-multiple-templates-directories
- name: Ensure service directory exists
file:
path: "{{ current_svc_path }}"
state: directory
mode: "500"
- name: Ensure directory structure exists
file:
path: "{{ current_svc_path }}/{{ item.path }}"
state: directory
mode: "500"
with_community.general.filetree: "{{ templates_source }}"
when: item.state == "directory"
- name: Include app config variables
include_vars:
file: "{{ role_path }}/vars/app_config.yml"
- name: Generate {{ current_svc_name }} deployment from templates
template:
src: "{{ item.src }}"
dest: "{{ current_svc_path }}/{{ item.path | regex_replace('\\.j2$', '') }}"
mode: "400"
with_community.general.filetree: "{{ templates_source }}"
when: item.state == "file"
- name: Deploy docker-compose for {{ current_svc_name }}
community.docker.docker_compose:
project_src: "{{ current_svc_path }}"
state: present

View File

@@ -1 +0,0 @@
JELLYFIN_PublishedServerUrl="https://jellyfin.{{ domain }}"

View File

@@ -1,35 +0,0 @@
version: "3.9"
networks:
default:
traefik_traefik:
external: true
volumes:
config:
cache:
media:
driver: local
driver_opts:
type: nfs
o: "addr=truenas.lab.home,nfsvers=4,ro,noatime"
device: ":/mnt/Mass-Storage-New/JailStorage/Plex"
services:
jellyfin:
image: jellyfin/jellyfin:10.8.5
container_name: jellyfin_jellyfin
labels:
- traefik.enable=true
- traefik.http.routers.jellyfin.rule=Host(`jellyfin.{{ domain }}`)
- traefik.http.services.jellyfin.loadbalancer.server.port=8096
restart: unless-stopped
env_file:
- .env.jellyfin
networks:
- default
- traefik_traefik
volumes:
- config:/config
- cache:/cache
- media:/data/media

View File

@@ -1,6 +0,0 @@
$ANSIBLE_VAULT;1.2;AES256;alpina
61626665353536663033663661393434616339396434383530306265363837313839303939623465
3634333839333530383464613966326238363738663637360a343837623832343232316565346131
66663831356162653363383131396665326531363430656539333866313031306537343864343262
3730643765633232620a643734623336646565663266656262343162613239306166386665333139
6366

View File

@@ -1,5 +0,0 @@
---
current_svc_name: jellyfin
current_svc_path: "{{ my_svc_path }}/{{ current_svc_name }}"
templates_source: "{{ role_path }}/templates"

View File

@@ -1,33 +0,0 @@
# https://stackoverflow.com/questions/41667864/can-the-templates-module-handle-multiple-templates-directories
- name: Ensure service directory exists
file:
path: "{{ current_svc_path }}"
state: directory
mode: "500"
- name: Ensure directory structure exists
file:
path: "{{ current_svc_path }}/{{ item.path }}"
state: directory
mode: "500"
with_community.general.filetree: "{{ templates_source }}"
when: item.state == "directory"
- name: Include app config variables
include_vars:
file: "{{ role_path }}/vars/app_config.yml"
- name: Generate {{ current_svc_name }} deployment from templates
template:
src: "{{ item.src }}"
dest: "{{ current_svc_path }}/{{ item.path | regex_replace('\\.j2$', '') }}"
mode: "400"
with_community.general.filetree: "{{ templates_source }}"
when: item.state == "file"
- name: Deploy docker-compose for {{ current_svc_name }}
community.docker.docker_compose:
project_src: "{{ current_svc_path }}"
state: present

View File

@@ -1,18 +0,0 @@
POSTGRES_DB=nextcloud
POSTGRES_USER=nextcloud
POSTGRES_PASSWORD="{{ db_password }}"
POSTGRES_HOST=db
NEXTCLOUD_TRUSTED_DOMAINS=nc.cazzzer.com
REDIS_HOST=redis
REDIS_HOST_PASSWORD="{{ redis_password }}"
SMTP_HOST=smtp.sendgrid.net
SMTP_SECURE=tls
SMTP_PORT=587
SMTP_AUTHTYPE=LOGIN
SMTP_NAME=apikey
SMTP_PASSWORD="{{ sendgrid_api_key }}"
MAIL_FROM_ADDRESS=nc
MAIL_DOMAIN=cazzzer.com

View File

@@ -1 +0,0 @@
REDIS_PASSWORD="{{ redis_password }}"

View File

@@ -1,96 +0,0 @@
version: "3.9"
networks:
default:
traefik_traefik:
external: true
volumes:
nextcloud:
nextcloud_config:
nextcloud_data:
db:
services:
app:
image: nextcloud:24-fpm-alpine
container_name: nextcloud_app
restart: unless-stopped
depends_on:
- db
- redis
env_file:
- .env.nextcloud
networks:
- default
volumes:
- nextcloud:/var/www/html
- nextcloud_config:/var/www/html/config
- nextcloud_data:/var/www/html/data
cron:
image: nextcloud:24-fpm-alpine
container_name: nextcloud_cron
restart: unless-stopped
depends_on:
- app
entrypoint: /cron.sh
networks:
- default
volumes_from:
- app
notify_push:
image: nextcloud:24-fpm-alpine
container_name: nextcloud_notify_push
restart: unless-stopped
depends_on:
- app
entrypoint:
- /var/www/html/custom_apps/notify_push/bin/x86_64/notify_push
- /var/www/html/config/config.php
networks:
- default
volumes_from:
- app
db:
image: postgres:13-alpine
container_name: nextcloud_db
restart: unless-stopped
env_file:
- .env.db
networks:
- default
volumes:
- db:/var/lib/postgresql/data
redis:
image: redis:7-alpine
container_name: nextcloud_redis
restart: unless-stopped
env_file:
- .env.redis
networks:
- default
command:
- sh
- -c
- redis-server --requirepass $$REDIS_PASSWORD
web:
image: nginx:1.23-alpine
container_name: nextcloud_web
labels:
- traefik.enable=true
- traefik.http.routers.nextcloud.rule=Host(`nc.{{ domain }}`)
restart: unless-stopped
links:
- app
networks:
- traefik_traefik
- default
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf
volumes_from:
- app

View File

@@ -1,182 +0,0 @@
# https://github.com/nextcloud/docker/blob/master/.examples/docker-compose/with-nginx-proxy/postgres/fpm/web/nginx.conf
worker_processes auto;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
#tcp_nopush on;
# Prevent nginx HTTP Server Detection
server_tokens off;
keepalive_timeout 65;
upstream php-handler {
server app:9000;
}
server {
listen 80;
# HSTS settings
# WARNING: Only add the preload option once you read about
# the consequences in https://hstspreload.org/. This option
# will add the domain to a hardcoded list that is shipped
# in all major browsers and getting removed from this list
# could take several months.
#add_header Strict-Transport-Security "max-age=15768000; includeSubDomains; preload;" always;
# set max upload size
client_max_body_size 512M;
fastcgi_buffers 64 4K;
# Enable gzip but do not remove ETag headers
gzip on;
gzip_vary on;
gzip_comp_level 4;
gzip_min_length 256;
gzip_proxied expired no-cache no-store private no_last_modified no_etag auth;
gzip_types application/atom+xml application/javascript application/json application/ld+json application/manifest+json application/rss+xml application/vnd.geo+json application/vnd.ms-fontobject application/x-font-ttf application/x-web-app-manifest+json application/xhtml+xml application/xml font/opentype image/bmp image/svg+xml image/x-icon text/cache-manifest text/css text/plain text/vcard text/vnd.rim.location.xloc text/vtt text/x-component text/x-cross-domain-policy;
# Pagespeed is not supported by Nextcloud, so if your server is built
# with the `ngx_pagespeed` module, uncomment this line to disable it.
#pagespeed off;
# HTTP response headers borrowed from Nextcloud `.htaccess`
add_header Referrer-Policy "no-referrer" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-Download-Options "noopen" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Permitted-Cross-Domain-Policies "none" always;
add_header X-Robots-Tag "none" always;
add_header X-XSS-Protection "1; mode=block" always;
# Remove X-Powered-By, which is an information leak
fastcgi_hide_header X-Powered-By;
# Path to the root of your installation
root /var/www/html;
# Specify how to handle directories -- specifying `/index.php$request_uri`
# here as the fallback means that Nginx always exhibits the desired behaviour
# when a client requests a path that corresponds to a directory that exists
# on the server. In particular, if that directory contains an index.php file,
# that file is correctly served; if it doesn't, then the request is passed to
# the front-end controller. This consistent behaviour means that we don't need
# to specify custom rules for certain paths (e.g. images and other assets,
# `/updater`, `/ocm-provider`, `/ocs-provider`), and thus
# `try_files $uri $uri/ /index.php$request_uri`
# always provides the desired behaviour.
index index.php index.html /index.php$request_uri;
# Rule borrowed from `.htaccess` to handle Microsoft DAV clients
location = / {
if ( $http_user_agent ~ ^DavClnt ) {
return 302 /remote.php/webdav/$is_args$args;
}
}
location = /robots.txt {
allow all;
log_not_found off;
access_log off;
}
# Make a regex exception for `/.well-known` so that clients can still
# access it despite the existence of the regex rule
# `location ~ /(\.|autotest|...)` which would otherwise handle requests
# for `/.well-known`.
location ^~ /.well-known {
# The rules in this block are an adaptation of the rules
# in `.htaccess` that concern `/.well-known`.
location = /.well-known/carddav { return 301 /remote.php/dav/; }
location = /.well-known/caldav { return 301 /remote.php/dav/; }
location /.well-known/acme-challenge { try_files $uri $uri/ =404; }
location /.well-known/pki-validation { try_files $uri $uri/ =404; }
# Let Nextcloud's API for `/.well-known` URIs handle all other
# requests by passing them to the front-end controller.
return 301 /index.php$request_uri;
}
# Rules borrowed from `.htaccess` to hide certain paths from clients
location ~ ^/(?:build|tests|config|lib|3rdparty|templates|data)(?:$|/) { return 404; }
location ~ ^/(?:\.|autotest|occ|issue|indie|db_|console) { return 404; }
# Ensure this block, which passes PHP files to the PHP process, is above the blocks
# which handle static assets (as seen below). If this block is not declared first,
# then Nginx will encounter an infinite rewriting loop when it prepends `/index.php`
# to the URI, resulting in a HTTP 500 error response.
location ~ \.php(?:$|/) {
# Required for legacy support
rewrite ^/(?!index|remote|public|cron|core\/ajax\/update|status|ocs\/v[12]|updater\/.+|oc[ms]-provider\/.+|.+\/richdocumentscode\/proxy) /index.php$request_uri;
fastcgi_split_path_info ^(.+?\.php)(/.*)$;
set $path_info $fastcgi_path_info;
try_files $fastcgi_script_name =404;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $path_info;
#fastcgi_param HTTPS on;
fastcgi_param modHeadersAvailable true; # Avoid sending the security headers twice
fastcgi_param front_controller_active true; # Enable pretty urls
fastcgi_pass php-handler;
fastcgi_intercept_errors on;
fastcgi_request_buffering off;
}
location ~ \.(?:css|js|svg|gif)$ {
try_files $uri /index.php$request_uri;
expires 6M; # Cache-Control policy borrowed from `.htaccess`
access_log off; # Optional: Don't log access to assets
}
location ~ \.woff2?$ {
try_files $uri /index.php$request_uri;
expires 7d; # Cache-Control policy borrowed from `.htaccess`
access_log off; # Optional: Don't log access to assets
}
# Rule borrowed from `.htaccess`
location /remote {
return 301 /remote.php$request_uri;
}
location / {
try_files $uri $uri/ /index.php$request_uri;
}
location ^~ /push/ {
proxy_pass http://notify_push:7867/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
}

View File

@@ -1,14 +0,0 @@
$ANSIBLE_VAULT;1.2;AES256;alpina
65313636646233613364363933616361346639653939346337303832646339316632383966666237
3766396134383434613534373937663162393134306536300a626139373732393037346630333838
63663439353238643532316231623866396434303034313130386635623363353263626362376334
3933346434633662320a386432373465646432343338666561366161646335636232353133393933
65313364666564353039626238383033343765323730316633356139326666623135326131353864
32386237643538636538356261393164633137636235346564393930346539623731386633336339
31303466653936343166366164383134306232613236663735623834393963306331376435616365
31313866383730393063353335626164303632636331303830636530656131636139376633623439
63663639323964623231343066373538633336353561646230363363643762393634643435306164
31366364326237636365336363343264343562353337303235633034383635373934376334353336
61373065386639643064303431623162373665363937353832313561386134613834613935653964
64656339316165313936333736643030356366663162316462636662326134396539356262666536
64336133393937396330353234316563356337623733326264363333373536633833

View File

@@ -1,6 +0,0 @@
---
# vars file for roles/nextcloud/
current_svc_name: nextcloud
current_svc_path: "{{ my_svc_path }}/{{ current_svc_name }}"
templates_source: "{{ role_path }}/templates"

View File

@@ -1,33 +0,0 @@
# https://stackoverflow.com/questions/41667864/can-the-templates-module-handle-multiple-templates-directories
- name: Ensure service directory exists
file:
path: "{{ current_svc_path }}"
state: directory
mode: "500"
- name: Ensure directory structure exists
file:
path: "{{ current_svc_path }}/{{ item.path }}"
state: directory
mode: "500"
with_community.general.filetree: "{{ templates_source }}"
when: item.state == "directory"
#- name: Include app config variables
# include_vars:
# file: "{{ role_path }}/vars/app_config.yml"
- name: Generate {{ current_svc_name }} deployment from templates
template:
src: "{{ item.src }}"
dest: "{{ current_svc_path }}/{{ item.path | regex_replace('\\.j2$', '') }}"
mode: "400"
with_community.general.filetree: "{{ templates_source }}"
when: item.state == "file"
- name: Deploy docker-compose for {{ current_svc_name }}
community.docker.docker_compose:
project_src: "{{ current_svc_path }}"
state: present

View File

@@ -1,20 +0,0 @@
version: "3.9"
networks:
traefik:
services:
traefik:
image: traefik:v2.8
container_name: traefik
restart: unless-stopped
ports:
- "80:80"
- "8080:8080"
env_file:
- .env.traefik
networks:
- traefik
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- ./traefik.yml:/etc/traefik/traefik.yml:ro

View File

@@ -1,7 +0,0 @@
api:
insecure: true
providers:
docker:
exposedByDefault: false
network: traefik_traefik

View File

@@ -1,5 +0,0 @@
---
current_svc_name: traefik
current_svc_path: "{{ my_svc_path }}/{{ current_svc_name }}"
templates_source: "{{ role_path }}/templates"

View File

@@ -1,7 +1,15 @@
- hosts: docker_hosts
- hosts: alpina
roles:
- docker_host
- traefik
- nextcloud
- role: gitea
- alpina
post_tasks:
- name: Docker prune objects
docker_prune:
containers: true
# Keep images for building grafana
images: true
images_filters:
until: "720h"
networks: true
volumes: true
builder_cache: false

View File

@@ -1,6 +1,12 @@
- hosts: all
roles:
- common
pre_tasks:
- name: Set fact for clean desired of docker objects and compose files
set_fact:
# clean_desired_arg is an extra variable passed to the playbook
clean_desired: "{{ clean_desired_arg | bool }}"
- name: Install services
import_playbook: services.yml