Compare commits

..

96 Commits

Author SHA1 Message Date
a31a07bd16 Backup: remove old config
Some checks failed
Linting / YAML Lint (push) Failing after 8s
Linting / Ansible Lint (push) Successful in 16s
2025-12-21 10:13:05 +03:00
54a951b96a Backup: refactor notifications 2025-12-21 10:10:12 +03:00
e1379bc480 Backup: roots parameter 2025-12-20 21:33:21 +03:00
037e0cab9b Backup: restic backup refactoring 2025-12-20 21:31:39 +03:00
2655869814 Backup: support for multiple storages
Some checks failed
Linting / YAML Lint (push) Failing after 9s
Linting / Ansible Lint (push) Successful in 19s
2025-12-20 21:19:06 +03:00
0e96b5030d Backup: refactoring 2025-12-20 21:04:54 +03:00
a217c79e7d Backup: extract restic storage into separate class 2025-12-20 21:03:32 +03:00
6a16ebf084 Backup: parse config to dataclasses
Some checks failed
Linting / YAML Lint (push) Failing after 9s
Linting / Ansible Lint (push) Successful in 16s
2025-12-20 17:44:02 +03:00
2617aa2bd2 Backup: support multiple roots 2025-12-20 17:27:29 +03:00
b686e4da4d Backup: change config format to toml
With support of multiple config values
2025-12-20 17:13:35 +03:00
439c239ac8 Lefthook: fix python format hook 2025-12-20 11:55:13 +03:00
acf599f905 Lefthook: check py files with mypy
Some checks failed
Linting / YAML Lint (push) Failing after 8s
Linting / Ansible Lint (push) Successful in 18s
2025-12-20 11:38:14 +03:00
eae4f5e27b Lefthook: format py files on commit 2025-12-20 11:35:54 +03:00
4fbe9bd5de Backups: skip system dir lost+found
Some checks failed
Linting / YAML Lint (push) Failing after 8s
Linting / Ansible Lint (push) Successful in 15s
2025-12-20 11:22:24 +03:00
dcc4970b20 Add owner and group to backup-targets files 2025-12-20 11:18:37 +03:00
2eac1362b5 Wanderer: backup all data with restic 2025-12-20 11:18:11 +03:00
e3d8479397 Memos: exclude media files from gobackup
Backup media files with backup-targets
2025-12-20 11:06:56 +03:00
91c5eab236 Gramps: exclude media files from gobackup
Backup media files with backup-targets
2025-12-20 11:04:50 +03:00
ca7f089fe6 Backups: use dataclass Application for app info 2025-12-20 10:48:40 +03:00
479e256b1e Backups: use constants for file names 2025-12-20 10:36:19 +03:00
11e5b5752e Backups: add backup-targets file support 2025-12-20 10:32:00 +03:00
392938d0fb Gitea: upgrade to 1.25.3
Some checks failed
Linting / YAML Lint (push) Failing after 10s
Linting / Ansible Lint (push) Successful in 19s
2025-12-19 20:35:53 +03:00
2cc059104e Netdata: upgrade to 2.8.4
Some checks failed
Linting / YAML Lint (push) Failing after 10s
Linting / Ansible Lint (push) Successful in 20s
2025-12-17 20:18:12 +03:00
d09a26b73a Gramps: update vars
Some checks failed
Linting / YAML Lint (push) Failing after 9s
Linting / Ansible Lint (push) Successful in 17s
2025-12-16 20:36:51 +03:00
097676f569 Gramps: move assets to local storage 2025-12-16 20:34:25 +03:00
e878661cb3 Specify secret files to ignore 2025-12-16 19:43:40 +03:00
cb50c1c515 Docker: prune images every night
Some checks failed
Linting / YAML Lint (push) Failing after 10s
Linting / Ansible Lint (push) Successful in 18s
2025-12-16 19:34:31 +03:00
33de71a087 Add agents.md file for ai agents 2025-12-16 19:29:52 +03:00
fbd5fa5faa Memos: upgrade to 0.25.3
Some checks failed
Linting / YAML Lint (push) Failing after 10s
Linting / Ansible Lint (push) Successful in 18s
2025-12-16 11:51:21 +03:00
faf7d58f78 Netdata: update config
Some checks failed
Linting / YAML Lint (push) Failing after 9s
Linting / Ansible Lint (push) Successful in 17s
map /etc/hostname config into container
2025-12-14 21:22:00 +03:00
0a75378bbc Remove old ports config
Some checks failed
Linting / YAML Lint (push) Failing after 9s
Linting / Ansible Lint (push) Successful in 16s
2025-12-14 19:25:33 +03:00
bdd74bdf2e Authelia: add backup for storage database
Some checks failed
Linting / YAML Lint (push) Failing after 9s
Linting / Ansible Lint (push) Successful in 19s
2025-12-14 18:10:59 +03:00
78bee84061 Outline: add postgres health check 2025-12-14 17:58:04 +03:00
7b81858af6 Miniflux: change file names 2025-12-14 17:51:06 +03:00
08fda17561 Gramps: move cache to separate dir 2025-12-13 15:40:56 +03:00
841bd38807 Update valkey to 9.0 2025-12-13 15:35:43 +03:00
fb1fd711c2 Dozzle: upgrade to 8.14.11
All checks were successful
Linting / YAML Lint (push) Successful in 9s
Linting / Ansible Lint (push) Successful in 17s
2025-12-13 14:57:41 +03:00
ecf714eda7 Gramps: reduce celery workers to 1
And update valkey to 9
2025-12-13 14:57:23 +03:00
81f693938e Netdata: upgrade to 2.8.2
All checks were successful
Linting / YAML Lint (push) Successful in 9s
Linting / Ansible Lint (push) Successful in 17s
Tune config, setup update every 10s instead of 1s
2025-12-13 14:46:15 +03:00
10d67861a0 Netdata: revert to 2.7.3
All checks were successful
Linting / YAML Lint (push) Successful in 10s
Linting / Ansible Lint (push) Successful in 17s
High cpu usage for dockerd and containerd
2025-12-13 13:29:40 +03:00
3f5befb44d Netdata: upgrade to 2.8.2
All checks were successful
Linting / YAML Lint (push) Successful in 9s
Linting / Ansible Lint (push) Successful in 17s
2025-12-13 09:52:08 +03:00
1b75ddaef2 Disable python docker package 2025-12-13 09:51:49 +03:00
7d6ef77e64 Authelia: fix run-app behavior 2025-12-13 09:51:35 +03:00
ae7c20a7aa Add mount configuration 2025-12-13 09:03:29 +03:00
67df03efca Add combined application playbook
All checks were successful
Linting / YAML Lint (push) Successful in 11s
Linting / Ansible Lint (push) Successful in 21s
2025-12-13 08:46:34 +03:00
48bb8c9d33 Add combined system playbook 2025-12-13 08:38:12 +03:00
5b53cb30ac Add tag 'run-app' for application run
Useful skip run when configure app
2025-12-13 08:22:11 +03:00
f2bc221663 Outline: change postgres owner
All checks were successful
Linting / YAML Lint (push) Successful in 9s
Linting / Ansible Lint (push) Successful in 16s
2025-12-11 11:26:13 +03:00
b41a50006b Exclude home dir from backups 2025-12-11 10:59:27 +03:00
c2ea2cdb39 Fix app user and group uid and gid
All checks were successful
Linting / YAML Lint (push) Successful in 9s
Linting / Ansible Lint (push) Successful in 16s
Prepare for system upgrade
2025-12-11 10:52:27 +03:00
7e67409393 Applications: move to new base directory
All checks were successful
Linting / YAML Lint (push) Successful in 10s
Linting / Ansible Lint (push) Successful in 21s
2025-12-11 10:14:27 +03:00
6882d61f8e Gitea: move to new app directory
All checks were successful
Linting / YAML Lint (push) Successful in 9s
Linting / Ansible Lint (push) Successful in 17s
2025-12-07 17:53:08 +03:00
47a63202b8 Fix spaces in file 2025-12-07 17:52:48 +03:00
af289f1e28 Configure stateless apps for new storage
Some checks failed
Linting / YAML Lint (push) Failing after 9s
Linting / Ansible Lint (push) Successful in 18s
2025-12-07 17:40:32 +03:00
b08f681c92 Exclude lost+found dir from applications 2025-12-07 17:40:08 +03:00
8dfd061991 Backup apps from /mnt/applications 2025-12-07 17:16:43 +03:00
306d4bf8d0 Install dust for space usage check 2025-12-07 17:13:20 +03:00
dbd679aa8b Gramps: move to new app directory 2025-12-07 16:48:25 +03:00
47ed9c11c1 Add apps data directory (external drive) 2025-12-07 16:47:59 +03:00
f9ad08fd09 Simplify owner setup
All checks were successful
Linting / YAML Lint (push) Successful in 9s
Linting / Ansible Lint (push) Successful in 19s
2025-12-07 15:56:16 +03:00
4c7338f857 Update eget tools
All checks were successful
Linting / YAML Lint (push) Successful in 9s
Linting / Ansible Lint (push) Successful in 21s
2025-12-07 15:39:21 +03:00
a95da35389 Backups: move secrets to config file
Some checks failed
Linting / YAML Lint (push) Has been cancelled
Linting / Ansible Lint (push) Has been cancelled
Allow run backup script with sudo
2025-12-07 15:14:55 +03:00
c74683cfe7 Grams: upgrade to 25.11.2
All checks were successful
Linting / YAML Lint (push) Successful in 44s
Linting / Ansible Lint (push) Successful in 1m8s
2025-12-01 09:18:54 +03:00
9dff413867 Fix linting
All checks were successful
Linting / YAML Lint (push) Successful in 9s
Linting / Ansible Lint (push) Successful in 19s
2025-11-28 20:37:39 +03:00
23a2bae7ec Fix linting
Some checks failed
Linting / YAML Lint (push) Failing after 8s
Linting / Ansible Lint (push) Successful in 18s
2025-11-28 20:13:58 +03:00
942bb7d999 Fix editorconfig for yaml files
Some checks failed
Linting / YAML Lint (push) Failing after 8s
Linting / Ansible Lint (push) Failing after 19s
2025-11-28 18:30:23 +03:00
6ff7a7e3b4 Add lint workflow 2025-11-28 18:27:34 +03:00
8ae28e64f4 Gitea: upgrade to 1.25.2 2025-11-27 11:13:14 +03:00
f7e8248cac Dozzle: upgrade to 8.14.8 2025-11-18 11:36:51 +03:00
2af9066dec Gramps: upgrade to 25.11.0 2025-11-18 11:36:35 +03:00
e3b2e064c0 Outline: upgrade to 1.1.0 2025-11-18 11:36:15 +03:00
380a54cb25 Authelia: upgrade to 4.39.14 2025-11-18 11:36:02 +03:00
d5078186e7 Gitea: upgrade to 1.25.1 2025-11-05 09:34:07 +03:00
57bb696e6e Memos: configure local storage 2025-11-04 17:19:29 +03:00
c6cc7d4c6c Wakapi: upgrade to 2.16.1 2025-11-04 14:15:44 +03:00
90abc6c833 Gitea: upgrade to 1.25.0 2025-11-04 14:15:31 +03:00
395203f236 Memos: install 0.25 2025-11-04 14:10:39 +03:00
57cc639cc8 Outline: upgrade to 1.0.1 2025-10-29 09:44:15 +03:00
1405a2364e Netdata: upgrade to 2.7.3 2025-10-29 09:44:01 +03:00
b165899f25 Gramps: upgrade to 2.10.1 2025-10-27 11:20:21 +03:00
86147d0103 Authelia: add redis (valkey) as session storage 2025-10-26 20:13:31 +03:00
2c9ade0a8e Tools: add args to secret generation tasks 2025-10-26 20:05:06 +03:00
35c1f727f6 Wakapi: try to setup oidc, but failed
https://github.com/muety/wakapi/issues/856
2025-10-25 15:13:01 +03:00
b7d2fca2f2 Gitea: upgrade to 1.24.7 2025-10-25 13:37:31 +03:00
725c4c02cc Dozzle: upgrade to 8.14.6 2025-10-25 13:37:09 +03:00
328256c6be Transcriber: release transcriber:2fc5a56-1761210134 2025-10-23 12:03:51 +03:00
b08dc862c9 Transcriber: release transcriber:ec637c0-1761209409 2025-10-23 11:51:45 +03:00
0810c6c099 Transcriber: release transcriber:822e168-1761208842 2025-10-23 11:42:10 +03:00
dd6b34e983 Transcriber: release transcriber:822e168-1761208664 2025-10-23 11:39:35 +03:00
6fd6d76594 Transcriber: rewrite deploy
Same as homepage deploy

Prepare for two-step deploy:
- local build and fix local tag
- deploy to remote server
2025-10-23 11:37:24 +03:00
61e2431975 Homepage: release homepage-nginx:f797e17-1761204003 2025-10-23 10:20:14 +03:00
9a23e35126 Homepage: rewrite deploy
Prepare for two-step deploy:
- local build and fix local tag
- deploy to remote server
2025-10-23 10:10:06 +03:00
f2a9e660ed Wakapi: upgrade to 2.16.0 2025-10-21 12:02:41 +03:00
bd5f5ca452 Dozzle: upgrade to 8.14.5 2025-10-21 12:02:18 +03:00
860cfd0450 Authelia: upgrade to 4.39.13 2025-10-21 12:01:50 +03:00
884553892b Update tools: rclone 2025-10-21 12:01:14 +03:00
72 changed files with 1874 additions and 945 deletions

View File

@@ -1,5 +1,6 @@
--- ---
exclude_paths: exclude_paths:
- ".ansible/" - ".ansible/"
- ".gitea/"
- "galaxy.roles/" - "galaxy.roles/"
- "Taskfile.yml" - "Taskfile.yml"

4
.crushignore Normal file
View File

@@ -0,0 +1,4 @@
ansible-vault-password-file
*secrets.yml
*secrets.toml

View File

@@ -6,14 +6,9 @@ insert_final_newline = true
indent_style = space indent_style = space
indent_size = 4 indent_size = 4
[*.yml] [*.{yml,yaml,yml.j2}]
indent_size = 2
[*.yml.j2]
indent_size = 2 indent_size = 2
trim_trailing_whitespace = true
[Vagrantfile] [Vagrantfile]
indent_size = 2 indent_size = 2
[Makefile]
indent_style = tab

50
.gitea/workflows/lint.yml Normal file
View File

@@ -0,0 +1,50 @@
name: Linting
on: push
jobs:
yamllint:
name: YAML Lint
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.x'
- name: Install yamllint
run: pip install yamllint
- name: Run yamllint
run: yamllint . --format colored
ansible-lint:
name: Ansible Lint
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.x'
- name: Install dependencies
run: pip install ansible ansible-lint
# Создаем пустой vault password file если он указан в конфиге, но отсутствует
- name: Fix vault issue
run: |
if grep -q "vault_password_file" ansible.cfg && [ ! -f ansible-vault-password-file ]; then
echo "Creating empty vault password file for CI..."
echo "foobar" > ansible-vault-password-file
fi
- name: Run ansible-lint
run: ansible-lint .

2
.gitignore vendored
View File

@@ -7,3 +7,5 @@
/ansible-vault-password-file /ansible-vault-password-file
/temp /temp
*.retry *.retry
__pycache__

25
.yamllint.yml Normal file
View File

@@ -0,0 +1,25 @@
extends: default
ignore:
- ".ansible/"
- "galaxy.roles/"
rules:
# Правила, требуемые ansible-lint
comments:
min-spaces-from-content: 1
comments-indentation: false
braces:
max-spaces-inside: 1
octal-values:
forbid-implicit-octal: true
forbid-explicit-octal: true
# Дополнительные настройки (опционально)
line-length:
max: 120
allow-non-breakable-words: true
allow-non-breakable-inline-mappings: true
document-start: disable # Не требовать --- в начале файла
truthy:
level: warning

69
AGENTS.md Normal file
View File

@@ -0,0 +1,69 @@
# AGENTS GUIDE
## Overview
Ansible-based server automation for personal services. Playbooks provision Dockerized apps (e.g., gitea, authelia, homepage, miniflux, wakapi, memos) via per-app users, Caddy proxy, and Yandex Docker Registry. Secrets are managed with Ansible Vault.
## Project Layout
- Playbooks: `playbook-*.yml` (per service), `playbook-all-*.yml` for grouped actions.
- Inventory: `production.yml` (ungrouped host `server`).
- Variables: `vars/*.yml` (app configs, images), secrets in `vars/secrets.yml` (vault-encrypted).
- Roles: custom roles under `roles/` (e.g., `eget`, `owner`, `secrets`) plus galaxy roles fetched to `galaxy.roles/`.
- Files/templates: service docker-compose and backup templates under `files/`, shared templates under `templates/`.
- Scripts: helper Python scripts in `scripts/` (SMTP utilities) and `files/backups/backup-all.py`.
- CI: `.gitea/workflows/lint.yml` runs yamllint and ansible-lint.
- Hooks: `lefthook.yml` references local hooks in `/home/av/projects/private/git-hooks` (gitleaks, vault check).
- Formatting: `.editorconfig` enforces LF, trailing newline, 4-space indent; YAML/Jinja use 2-space indent.
## Setup
- Copy vault password sample: `cp ansible-vault-password-file.dist ansible-vault-password-file` (needed for ansible and CI).
- Install galaxy roles: `ansible-galaxy role install --role-file requirements.yml --force` (or `task install-roles`).
- Ensure `yq`, `task`, `ansible` installed per README requirements.
## Tasks (taskfile)
- `task install-roles` — install galaxy roles into `galaxy.roles/`.
- `task ssh` — SSH to target using inventory (`production.yml`).
- `task btop` — run `btop` on remote.
- `task encrypt|decrypt -- <files>` — ansible-vault helpers.
- Authelia helpers:
- `task authelia-cli -- <args>` — run authelia CLI in docker.
- `task authelia-validate-config` — render `files/authelia/configuration.template.yml` with secrets and validate via authelia docker image.
- `task authelia-gen-random-string LEN=64` — generate random string.
- `task authelia-gen-secret-and-hash LEN=72` — generate hashed secret.
- `task format-py-files` — run Black via docker (pyfound/black).
## Ansible Usage
- Inventory: `production.yml` with `server` host. `ansible.cfg` points to `./ansible-vault-password-file` and `./galaxy.roles` for roles path.
- Typical deploy example (from README): `ansible-playbook -i production.yml --diff playbook-gitea.yml`.
- Per-app playbooks: `playbook-<app>.yml`; grouped runs: `playbook-all-setup.yml`, `playbook-all-applications.yml`, `playbook-upgrade.yml`, etc.
- Secrets: encrypted `vars/secrets.yml`; additional `files/<app>/secrets.yml` used for templating (e.g., Authelia). Respect `.crushignore` ignoring vault files.
- Templates: many `docker-compose.template.yml` and `*.template.sh` files under `files/*` plus shared `templates/env.j2`. Use `vars/*.yml` to supply values.
- Custom roles:
- `roles/eget`: installs `eget` tool; see defaults/vars for version/source.
- `roles/owner`: manages user/group and env template.
- `roles/secrets`: manages vault-related items.
## Linting & CI
- Local lint configs: `.yamllint.yml`, `.ansible-lint.yml` (excludes `.ansible/`, `.gitea/`, `galaxy.roles/`, `Taskfile.yml`).
- CI (.gitea/workflows/lint.yml) installs `yamllint` and `ansible-lint` and runs `yamllint .` then `ansible-lint .`; creates dummy vault file if missing.
- Pre-commit via lefthook (local hooks path): runs `gitleaks git --staged` and secret-file vault check script.
## Coding/Templating Conventions
- Indentation: 2 spaces for YAML/Jinja (`.editorconfig`), 4 spaces default elsewhere.
- End-of-line: LF; ensure final newline.
- Template suffixes `.template.yml`, `.yml.j2`, `.template.sh` are rendered via Ansible `template` module.
- Avoid committing real secrets; `.crushignore` excludes `ansible-vault-password-file` and `*secrets.yml`.
- Service directories under `files/` hold docker-compose and backup templates; ensure per-app users and registry settings align with `vars/*.yml`.
## Testing/Validation
- YAML lint: `yamllint .` (CI default).
- Ansible lint: `ansible-lint .` (CI default).
- Authelia config validation: `task authelia-validate-config` (renders with secrets then validates via docker).
- Black formatting for Python helpers: `task format-py-files`.
- Python types validation with mypy: `mypy <file.py>`.
## Operational Notes
- Deployments rely on `production.yml` inventory and per-app playbooks; run with `--diff` for visibility.
- Yandex Docker Registry auth helper: `files/yandex-docker-registry-auth.sh`.
- Backups: templates and scripts under `files/backups/` per service; `backup-all.py` orchestrates.
- Home network/DNS reference in README (Yandex domains).
- Ensure `ansible-vault-password-file` present for vault operations and CI.

View File

@@ -27,13 +27,13 @@ tasks:
cmds: cmds:
- ssh {{.REMOTE_USER}}@{{.REMOTE_HOST}} -t btop - ssh {{.REMOTE_USER}}@{{.REMOTE_HOST}} -t btop
vars-decrypt: encrypt:
cmds: cmds:
- ansible-vault decrypt vars/vars.yml - ansible-vault encrypt {{.CLI_ARGS}}
vars-encrypt: decrypt:
cmds: cmds:
- ansible-vault encrypt vars/vars.yml - ansible-vault decrypt {{.CLI_ARGS}}
authelia-cli: authelia-cli:
cmds: cmds:
@@ -55,16 +55,24 @@ tasks:
validate-config --config /data/{{.DEST_FILE}} validate-config --config /data/{{.DEST_FILE}}
authelia-gen-random-string: authelia-gen-random-string:
summary: |
Generate random string.
Usage example:
task authelia-gen-random-string LEN=64
vars:
LEN: '{{ .LEN | default 10 }}'
cmds: cmds:
- > - >
{{.AUTHELIA_DOCKER}} {{.AUTHELIA_DOCKER}}
crypto rand --length 32 --charset alphanumeric crypto rand --length {{.LEN}} --charset alphanumeric
authelia-gen-secret-and-hash: authelia-gen-secret-and-hash:
vars:
LEN: '{{ .LEN | default 72 }}'
cmds: cmds:
- > - >
{{.AUTHELIA_DOCKER}} {{.AUTHELIA_DOCKER}}
crypto hash generate pbkdf2 --variant sha512 --random --random.length 72 --random.charset rfc3986 crypto hash generate pbkdf2 --variant sha512 --random --random.length {{.LEN}} --random.charset rfc3986
format-py-files: format-py-files:
cmds: cmds:

View File

@@ -0,0 +1,10 @@
#!/usr/bin/env bash
set -eu
set -o pipefail
echo "{{ app_name }}: backup data with gobackups"
(cd "{{ base_dir }}" && gobackup perform --config "{{ gobackup_config }}")
echo "{{ app_name }}: done."

View File

@@ -895,9 +895,9 @@ session:
## ##
## Important: Kubernetes (or HA) users must read https://www.authelia.com/t/statelessness ## Important: Kubernetes (or HA) users must read https://www.authelia.com/t/statelessness
## ##
# redis: redis:
# host: '127.0.0.1' host: 'authelia_redis'
# port: 6379 port: 6379
## Use a unix socket instead ## Use a unix socket instead
# host: '/var/run/redis/redis.sock' # host: '/var/run/redis/redis.sock'
@@ -1026,7 +1026,7 @@ storage:
## ##
local: local:
## Path to the SQLite3 Database. ## Path to the SQLite3 Database.
path: '/config/authelia_storage.sqlite3' path: '/data/authelia_storage.sqlite3'
## ##
## MySQL / MariaDB (Storage Provider) ## MySQL / MariaDB (Storage Provider)
@@ -1292,7 +1292,8 @@ identity_providers:
## configured has the RS256 algorithm. For RSA keys (RS or PS) the minimum is a 2048 bit key. ## configured has the RS256 algorithm. For RSA keys (RS or PS) the minimum is a 2048 bit key.
jwks: jwks:
- -
## Key ID embedded into the JWT header for key matching. Must be an alphanumeric string with 7 or less characters. ## Key ID embedded into the JWT header for key matching.
## Must be an alphanumeric string with 7 or less characters.
## This value is automatically generated if not provided. It's recommended to not configure this. ## This value is automatically generated if not provided. It's recommended to not configure this.
# key_id: 'example' # key_id: 'example'
@@ -1399,6 +1400,24 @@ identity_providers:
userinfo_signed_response_alg: 'none' userinfo_signed_response_alg: 'none'
token_endpoint_auth_method: 'client_secret_basic' token_endpoint_auth_method: 'client_secret_basic'
-
client_name: 'Wakapi'
client_id: '{{ oidc__wakapi__client_id }}'
client_secret: '{{ oidc__wakapi__client_secret }}'
redirect_uris:
- 'https://wakapi.vakhrushev.me/oidc/authelia/callback'
scopes:
- 'openid'
- 'profile'
- 'email'
# response_types:
# - 'code'
# grant_types:
# - 'authorization_code'
# access_token_signed_response_alg: 'none'
# userinfo_signed_response_alg: 'none'
# token_endpoint_auth_method: 'client_secret_basic'
- -
## The description to show to users when they end up on the consent screen. Defaults to the ID above. ## The description to show to users when they end up on the consent screen. Defaults to the ID above.
client_name: 'Outline' client_name: 'Outline'

View File

@@ -0,0 +1,26 @@
services:
authelia_app:
container_name: 'authelia_app'
image: 'docker.io/authelia/authelia:4.39.14'
user: '{{ user_create_result.uid }}:{{ user_create_result.group }}'
restart: 'unless-stopped'
networks:
- "web_proxy_network"
- "monitoring_network"
volumes:
- "{{ config_dir }}:/config"
- "{{ data_dir }}:/data"
authelia_redis:
image: valkey/valkey:9.0-alpine
container_name: authelia_redis
restart: unless-stopped
networks:
- "monitoring_network"
networks:
web_proxy_network:
external: true
monitoring_network:
external: true

View File

@@ -1,15 +0,0 @@
services:
authelia_app:
container_name: 'authelia_app'
image: 'docker.io/authelia/authelia:4.39.11'
user: '{{ user_create_result.uid }}:{{ user_create_result.group }}'
restart: 'unless-stopped'
networks:
- "web_proxy_network"
volumes:
- "{{ config_dir }}:/config"
networks:
web_proxy_network:
external: true

View File

@@ -0,0 +1,16 @@
# https://gobackup.github.io/configuration
models:
authelia:
compress_with:
type: 'tgz'
storages:
local:
type: 'local'
path: '{{ backups_dir }}'
keep: 3
databases:
users:
type: sqlite
path: "{{ (data_dir, 'authelia_storage.sqlite3') | path_join }}"

View File

@@ -1,136 +1,147 @@
$ANSIBLE_VAULT;1.1;AES256 $ANSIBLE_VAULT;1.1;AES256
37373465363866623436393966626530656465653837363463323664383666663164363233623738 65666236353432623164363161633732363265343935316466326332323638626138633130666663
3233383234343332623065386134643161346132653431350a303935373631656366633339663333 3566393731353063393466663831623537393939663838640a303935383565376430646431613464
32353263346437626633346263323533313238613462613334353334643236343438306630333037 66363161373765663665316433393835323236363561663633623533363166323533623137646437
6435313930313262310a386662336637623461303636633337303531353261343861313966383764 3438383538613832660a373337323633666535656439383438653132646235623834643730323762
32353439333364353434653164666434326232383562363063313433373137383138396266383134 64656336323037653937393836623061343563303337633830346164613731633761376138313666
36613538653531346232353236313262313138656234626638623034363436303337313961333536 33343265343830306139386164353861633230326333616539313032323935313435623762316163
66366666383363333439333439623931626662383764393463663733333034636633353538656137 63353535663533656166333034636462663266343061626637613830313962333531396339393334
62386263613533343963396166666532313862366433636536613266353064633932323765336362 39336233623462303137343264373631396464303038346131633333333033343839393438653330
34643634643962333563346633306665313765393663306364363362333536646635343832333634 61646166313762633333353233366430333764646261356438336532653463646264643562346262
30383361653063396333616433323235663338346439303465323135626639646166303164643339 32653339353861653964663935393638656563373239633234656131633133643366653833376233
31616534633034393339373934346531633433323433646436333863306566356462613531663136 34643033393039616363656330323765346336643664393934376138353266303731613066626231
61343561616434306163616130626338663737633866646537323263316636626137366361363963 30313362663430343337663663373132383831333662353034316134623639393938306237396230
31303361366365616335363230343239663038623830303232376236393639663232333764643064 65633565643861366434336135313465623537616166356637346137323566333239376361633930
34666439316430356664313531333363626562633636326463313765343263393636333465386339 66356664633433306134613166353032363234633961623862646537376365383535623033356639
33623037343134633535303863626564373630656463336330396336303462373735346331616663 34336433646430323630313464333631663739343832316362666165623039313535306433643761
63666161356565643539343431386231396162323030383836366161303634626266663934356362 61376264616462333839326233613764333633643233663236666562306666353934323636353262
61623833613734333661613338373663663230363331373236323166636534613962613763343663 32386333623632326332373438373236646138326537343835383766663463643535383534633633
31666534303965333466653335646263343764346465373461326166666266303138363933653566 34343166343362343165333837313362666533353939336533326138386538323961323530626663
37636530306632346636626336616536346236663664383864623863653835366133633635613861 63633334663561396565656662363830656133323364383536346163316333356661643261633635
63303634333962343039646564353534313063383434386462366333386331303433366665623734 37323536653664613564616466633631616465656266373830373964326566623733396130303031
61353039313762383664626330663230656237373061616132376564323763393632356665306633 31663166323863353565353235323235363161366534663539343338633434626232336261356438
34643865333165616664376162306634366532386437383461396163376366363832363834356164 38373339383361356565366436376266336234356436363562623431666436626562353065356230
64376637373135383539353636346461353761366561303530326363366238393932333039313264 37333737623161303431393461383438643137613962613838386537663564646333356538393337
34316539626365306461323336396631633532306637306331373863613531656565366236656338 34373136343361333738396664616436363066323030306438666437333839653336316264343139
30306237626561613561353265643137353965313033313939643161613163643566663632663964 38656234623363386561393961363262316535373232353132616136633339613533633333376235
61623134656238363134626530363933623930346532336366393163363562386265626233393139 35656632643363386133656438616366643630303337383130386330326532623930346561383739
63633333313531666335376538613765663933626533636137306564616333373766613665613332 65636138313566363539323663303631663862323034313062353633646638633463653263333035
65643331626361626136623432346233633364343963653932306632646436626433653337326665 66646539393639643834353966396132343437666435626537633336393864326630623961303338
39343033353030616630663865613630613032333831626538323461383264636633623439393765 36616663383262363138346331386239653634373135616232656462353562373939356631633433
65343866323237386336613764386439313830646239613135636161333138646664666339626137 64356461653336353039643065663538373363666339363231373762363163653762363832373238
36616433393339346139323333363030613731313236636464393864616135346234643664343533 37386638373261383533633937613561613961353765363864613031356334303138613766383631
31396661306664343031393865306533373762663962623730313261353231363661306134623934 36666433636663343931643462383338613662333233666337363038666531633864333436383737
64613931356164386431663536363361386566353361333861666365636564643536306638376238 36363562323936313764386336656639633638643465636131373532313238376565353933336133
37633865303063643962346664346366346362313463386432376637663934363165343537323532 37663961623937336230636466643531316463323733626134663331323135396637323231383231
63363431623663656163316662343435636165306134373839613731326139636337343862326338 33643736316537646264353662643261666165366562393031613565623630336333353961353661
61326433343631343065303735663434316330303139303834316137663330363762666664393062 62633362613863303936623436616136616139363861653233313865343532366465373937306139
30653236636538396234313735613365386635323062666236656164633136313362643834336339 61316665653234363033396566316331316164346461353438633864333334653730333065376631
32623834343334613839313138313462376237666238636663343333643533643537376261666433 66616238663062666139653062383036636366646364346632396239623233356533343038363733
64323933323134393461623034623563316135333566326135326434613237363830623063626535 33316165306130616665326364616231613830313334383961633333303261656131333161323237
66323533356366346130626530633337393263663664353430646330353339313534396434653137 38616435633334646533653830393739336363653664373235363863396262623736626435313735
36663737396261653162663337663338373433653233616363626130333833323533303363643730 39353065643033343062616137346361646136313265653965313133666130393361306430303638
66613135633761346433626164643130353963313762643361306537653639373934333565373439 64643364663335343961373865653564366362396138626531613232313461376463336437336666
31636133383866373032373562653933316163353936386339326266363233663633623437346665 30373766666231646264356663626335393233333465386164313630613137303066336430653662
66393630346434356563393039626537336537313930393437663562303031626338616266363361 35336531633039633938363430363239653065356230313538323630316561643033623833656164
65653033363539633364326531653563383634303830386362303665306438663035373831646562 65366435653063616361366666373561663538373363346264386331316531376262663663383266
30356564623733623939663332393463663730643533666134636361316263633166626566333831 38346163653439366430656536666631366534663163396230353531396335663638386261613832
66376461383139626230343136373437393464646331633139633435303236623132343035373037 31336339336465393333383761623663383563613930663430626166666635393164366562663063
39363131623330376262326235646633303232623139626239326361313236316665316464616265 34323031333939656161643139386532666361663630346632383333373261326134636564393233
35303166333561626130323864363430663332306338353731383139386131346132653632633132 38363630633435353730383234663536623166373533333639353963613665353339383837626138
37643865393462623831623435633838323664666264623232326561653866626437373864666232 38653730316538626662303636363664613566383033323661363533333032306362346562316464
64393466326162323236353539326364336238643031313434346566316434383733663663356334 36303730666531396531386331653466396233623138393763653965323239393237636237303237
62343337386532393236353432653239643735323531306337373739343839306264356666636635 66323366343036643765646539366261613062646532306265353430636332386330613962666131
35626665646634653766323939633434303238633564613962643364356631623539623032363039 33383063343638616338326533646162306438616434316139313433303636366665336364656534
31336535333763323236633531616661313834636231363362376661313931623131343364356364 66353634646563373463633637383766343332346530653033663937613135363233386138306565
35653539393265323636303930633639316139643631386632646139643266303531653865623664 39386262663939346432383134333661623637396162623336626137316166613035333138653632
31303930613561323330356337396138633033616265356137353336613638656161633063663964 34653364333732366231396637353939653262323934366333373130613932366533346632366164
33373965383532656634333863643131333461376135646635323035316230393439386130663036 31313663303034306436393763323361616434306134336231383639346261376439643162643539
37373331356364343433316435666130373031303038643063313131653835333365366138656238 35323534366435393531613665333337633365353831326534363737396463363666316639373233
38643437376234316332306434633039346564643863656461353364346335353839303734366565 38386431336631363831366261373439376231326465323736356331636136393762383331336265
32613364356532623231623632363637373664393764636262346264383134366439373238623032 64356363336639356361326539396234626566653334656561386431616139323433376563303132
62326163306532356262393565373937316530623963313266373736356632313831313465666663 32363864663062643065643534623933653165636264653461303262313662663165616463353965
61303962333836373832383236663532376130316465393039326366636133323233316134646430 36613134343634393066613533343362393137356464303530653964363031653231663962303037
63313437663662353962633561643535396332303533343962643038393165373239336431336664 38633730633766306264613865373736346661623531616563316232393235643931353663383066
65356663393565383263613530663762643731356463653538316439303863643363303261363838 39386633383930383732343266326161303663646636383735646332623661303433366161363635
33353739306337326665393164366232393665363465343537373866396136346164333663393738 31313839396539343063613166616134636134333639363362343566356435663934646263653061
31316335366238316537386236393461656266323566636364343139393665616138663432333564 31616137363031333561656134396534333430613637376465363633663861666262616137336337
37653837646666376530373530636164343633653162343131373034633432333138613138346339 38396639333465316431303433653338653338303031313566633330656535666531316235393138
32303332626338653561323835343266353633613434346465616162326162393733643837663230 64383332323466653065343765346162343532386438326362626637656130613433393339323564
62333630333464633362656661336139393639623863343036636534323637336561333734373262 39633262393336383932306632636563313663336337653164306434393661663265306638636265
36373365306531383830383361303566626239343062316166303636323539373966626336613638 37326335616666393262356663326665663561633038333237363234303838636135653861373032
31623863643632653036323834346362663834666431643637666137376139386666643834323465 62643332376364386537326336663531613164376261653938333165656632613434343063613565
63626264643337376663633335666631653637626364653866353131393336303937633430366430 62623262303632633335303430343164343433646238366137623030323233303661373434666234
38383066313831346461643862333838386566613661623130313038386137323331373434363033 62626265623239343634363732623739383536646132656564313032663061383939616162366538
34613537303134343532643430383532353934623066383530653435626566333239333162316435 61373738626362386265623739333139613531373738333862396430356635626130663633666439
30386361346336666665656336646633353663376337326131663435636533646162616332306530 63333836346364313262613331656531623831646165313036393138636162336138613365396331
30373263376437646639623039343234393537303931346461643966383732336366363331643135 63323333366232326365333965363734396565343334613733396437393932616637313738633765
65383462353034643464366334636136373035313437366639336338366133663765313735353366 61373437313636666566303032343366376166643864333639326564363935623534386164346365
62386239326134343761313464383239316465633932363862303536353365376338643863643834 65373538303765386337383964303937343038663832326665356666646336633835653530396337
32633233336262626336383061316137646431353766303930336562386136383530613538383837 63316631333231623861373330303033396138613834623265633263653061623132366235336661
66336235643437636138646663393565383466636232366133343232306563363635316561346565 31376633396534613161663462343365653262383133636238613166343366653464636137353165
36633637303163303963396132653731663134666238363939666663303033316564356364633162 33623539366339353064373238663838303237343737376131306532333134313561323234336530
31376233306138666131333634336639643163666562653934336162323964613863646564616361 64653335653262613738623335343361386261633636616663393035633066653735346431643663
33666264303163326664633839303562333664383130356134383836313635376239353137313363 63626331633337363231313835376239636136633262393463643539343333373139616330373634
66613132663137383737373530326131663861303935663635373464306334363962333566663261 63366439383232363165356166656664633133313533353236393637386535616536613630616137
64616361366137626163626139333630653331383763353632396130306231376662666363353962 37316538653839353932363264663934613936346661343835333666303536363332363234653262
66343064313932386631303663373432646135353438313632316634336235316139313237666362 33303530343764316462336634613661663532393864626437353764656564343435613131663339
31383638393362663038363765303634363366326265643332393165643635643339343137373930 31616236376563623762326566333933303432646465613138373733363263346337633165616563
37633031626365353033333938623466663963376366353561303166373164386132376365643630 61613836363231333966336165393961313930313934323536333334363763393438636138396361
32333134316464643564373537643734353534366563666435663663616331383039393862613838 61373264626531393165376538336533303861643663333439653732313337613362346162373931
63633962316533386337366263636265646334636235323430383832363964343939633264343338 35333330653531623134396134333938616538666661363737633639643462313034356531633033
64366539383831373636343330356537323662666533323935646634363466663239663362326531 37613834356563303639356134323231646461356262636237323061386339306462343035336164
66363863383762303539373636336330353834303239376330623964393439356130646166663332 63376664386663306135333635323030396639326639656131333564353265316631336261373562
65326162336366363466646230656362653531386162356235326235333866373966636434326537 35663639306361373433633530616162636434373533333263303936343539386439303130366439
38653139393563373337346636333337373039343439343139313366316264613763663664633037 37656263363839363339333236333835386537343232636461343338356234616332383330373161
32313237653239643635393363316465346561613331623033323137653865396239633639323534 66646533393037353030616264623461626339613538306133393337623264326535343836363165
61326262643365363737393031383461626530636266393836663937666135363662353665376362 39626263396162336434323437323133653735663136616266376331633665626234386131393433
63643039373931316439303731663762393237623065643236303737643966393836646335626132 34663236623636656237666531353763333861646264646538313964663130353836396236336564
36376665656662373437653933356330636638626162666564393636633630663562303839326662 31333132396535653064366430306464363634633032383666313738396265343335663664613662
35626261616534386361373539633636356136616137323737393466633364653730356138343638 34626331303834346637383932633832343962666633323838383132323239363965313139373762
36663537663361393566616365383161366236646630653737643765666638346531376136643163 36306266313337393235303834356435336138373437306661636535333836396366386336613937
37643530373330353238323431343761653633306464643835343333623837313135303031666535 37386136623965373439373433353264393363653534306662316132643761353138303538353037
30326538636432303363666131376334393361333232313834623230306630373834633265333237 62316636323338353633313461346161343461613465653463393931363661336638346435666134
34323731333835363863643031346166636464653731636636313161643265613861336638313338 38303533633530383466623766393138653564373065373261656165313763663361373235356531
32383438353763343933383537636464666466386131363566306562333136356538326239656232 66346663343636633961656639316365396265303632626465666532313338373336376135366138
33343631613134616265393232613063643561633335323665383133313536313364343066366665 61303865356436616139373064393939356364623461316266316537363963396263613562613363
63636439376436393162306638303062616435333039343566613961626434303766616535623364 37386466633934623062333634303335646232613039633839336365613634613561373436393733
32643866393430303137366264306262643365663034613965666332313430366630653736303537 61316162623063636365346236323164333933643662366463313138623561633533653932663065
39663832326132393066373166613161613130363033363633366563353461663435393565326362 64363734636663613831663630666432396237303630623234376432316532626165373464333134
37616462393933303937643664343663373234663066633834626164303866323835363333366266 30646433646438373961333230343430383232316431313465646136643139353937313761333731
36356133336165613032646436306162663534306239313330353935336332643637653534626233 66653335343935636530393361306162616232333935393135623235626238323238303339643863
38303965646361316434343131653461353234396163613736333235656639326231353734636266 35373366353365613965656562633633303330383631383736306535366137393638313330343636
61613566336437666265366637336363326266383666383165343661333766303830633633393664 35383039313866303239623564666461633161373231303534313466353137643666396133363265
34613061396564616337643032666561633038663062336233666263306132663139396565323035 36373133323162666363303862313566613132333739333164663166666565303032306633636632
33353438633338363263313630393239376162366461383265386633613939663461616233396334 63366539353663666162633832373264306135646266656563373433376438616530626530393131
35616433663862616530636362396333343464393339396538333861303763393066626439396361 36376630306539303865613639643538333134666533643261356662386433626265613431613334
32303732393062383662633937653531653933333463366638613035333832636235346233653866 35353634663930653537326666653763393831363637613639653862323730633266323234656662
35656664636636326163353439626538343463613465613634656530366566323165623162303565 35666433623739323435626536303561386332313838383432303437633731666435393331353139
31303139343138616132383731323061346431336133643735356532373838663761313139663361 32376638343939626237346363623236653639356234633366323464663763366339663536386162
33656365636261303532333131346633373732643232303139353431663132346532616334613034 62623831336165393630363263396466643563383330353232373435613364316538613835313332
30363137613133396335343162643936623330393834356365663932626262313366616534663033 30623136316437333462333864663164356539656436666539313536653065346337353565656138
37376132656233633361623733356334636266383361656437613331306636656333623139303661 30663737333035336137313034626339613631396237326163366364366634346438643831376166
38353639346266333833663533366661633136313262396465633738373438623262306637643336 33616238643033313662353031306534666161636133653531303932633231326139326161356564
34656136343139663461336264346666333537633065343766316630626566363761396537643334 65323231663562323430633561363838663030346432313930323165313835616230663463316161
30323766633664666639363965363138396334343365346333663035323839613030626533303830 63326364376461643035333564643964303030306131396233333439393131336435323134663064
31333734386565383831373939306265636432386332313531623638333663643162623339613366 31306162383766333636386633393863663035376562633965666635353939653936626631346534
34363935636266313736366639373833636230633661323935646331376336623937353039343561 64656462393335653332646562373361613132643034653536303435343833626433613137346232
39623865663462663431643738653663663733663765383663623437383163613232336332653531 65643465323733336162366261336636326136616532343939613363663537336365363966373437
64663133353934313436336633666435343162316135303663636130353936363936363032313263 62626566303435356237333238353736383262313933656139326634663934343864373131646461
65376436316237663434323736663263376164346139616465663737323963316361373438633339 62653662336162313739663961636430346130666130646364393034636464616362636533353262
31323261343635633338613636643232616537653331326331353161396331633461643861323466 62346162386561363239666232306432646336346434636335366638633762666634623737663866
64633033623537386263376263346666633939336133616234363964363339616331636464326163 32343339646330353837323665626430646432316163656361323139633336646363643434363731
63633862373030323132613439343431333938343864383637613435323732356234613965666364 36616534663330376333623631313332616462383936316238363032363762356531393332343430
37343765353735633737393664306533633262353562323565306537646534663833343430643662 34613735633732333762306331666332316265333962343935613936613438353164623031346432
39326134353335653938396532363136376332306162613836663464636233383436333735663731 66396264366666383835333538616430326161653839663838313764663664316266623762653463
313461396466396230323561646662653063 31383235326363646234306636636564366238663965663331623965373139353064363733363339
34383937303033386566633939366331353333373935353263376235623430336236396135303233
61666434613136396338656334306463666535373364373130343161373866333339333436643036
64346162353530353334343438623835363664396265353762663832303366623735636238313039
37353136626563393231353662633031343435636261616131323833613062643834663634353537
30343430396135363466346236373462356366643539363665373663363932316163346665663935
61343434303039623139323265623538366563373633623065353862303935393434663566303232
37653033343230613766306334316464666533326566386633363835373466326263323861636635
36396437373161346636616664313734343565643330376431633238396462633764386531313165
62666538353239396361653035393636633263613639623038383734326564346261666335383234
36333663303365313066616533333336306639363239663339313766356431316562353836303464
3534373436363831616163383134383266636130316433633635

488
files/backups/backup-all.py Normal file
View File

@@ -0,0 +1,488 @@
#!/usr/bin/env python3
"""
Backup script for all applications
Automatically discovers and runs backup scripts for all users,
then creates restic backups and sends notifications.
"""
import itertools
import os
import sys
import subprocess
import logging
import pwd
from abc import ABC
from dataclasses import dataclass
from pathlib import Path
from typing import Dict, List, Optional, Any
import requests
import tomllib
# Default config path
CONFIG_PATH = Path("/etc/backup/config.toml")
# File name to store directories and files to back up
BACKUP_TARGETS_FILE = "backup-targets"
# Default directory fo backups (relative to app dir)
# Used when backup-targets file not exists
BACKUP_DEFAULT_DIR = "backups"
# Configure logging
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s - %(levelname)s - %(message)s",
handlers=[
logging.StreamHandler(sys.stdout),
logging.FileHandler("/var/log/backup-all.log"),
],
)
logger = logging.getLogger(__name__)
@dataclass
class Config:
host_name: str
roots: List[Path]
@dataclass
class Application:
path: Path
owner: str
class Storage(ABC):
def backup(self, backup_dirs: List[str]) -> bool:
"""Backup directories"""
raise NotImplementedError()
class ResticStorage(Storage):
TYPE_NAME = "restic"
def __init__(self, name: str, params: Dict[str, Any]):
self.name = name
self.restic_repository = str(params.get("restic_repository", ""))
self.restic_password = str(params.get("restic_password", ""))
self.aws_access_key_id = str(params.get("aws_access_key_id", ""))
self.aws_secret_access_key = str(params.get("aws_secret_access_key", ""))
self.aws_default_region = str(params.get("aws_default_region", ""))
if not all(
[
self.restic_repository,
self.restic_password,
self.aws_access_key_id,
self.aws_secret_access_key,
self.aws_default_region,
]
):
raise ValueError(
f"Missing storage configuration values for backend ResticStorage: '{self.name}'"
)
def backup(self, backup_dirs: List[str]) -> bool:
if not backup_dirs:
logger.warning("No backup directories found")
return True
try:
return self.__backup_internal(backup_dirs)
except Exception as exc: # noqa: BLE001
logger.error("Restic backup process failed: %s", exc)
return False
def __backup_internal(self, backup_dirs: List[str]) -> bool:
logger.info("Starting restic backup")
logger.info("Destination: %s", self.restic_repository)
env = os.environ.copy()
env.update(
{
"RESTIC_REPOSITORY": self.restic_repository,
"RESTIC_PASSWORD": self.restic_password,
"AWS_ACCESS_KEY_ID": self.aws_access_key_id,
"AWS_SECRET_ACCESS_KEY": self.aws_secret_access_key,
"AWS_DEFAULT_REGION": self.aws_default_region,
}
)
backup_cmd = ["restic", "backup", "--verbose"] + backup_dirs
result = subprocess.run(backup_cmd, env=env, capture_output=True, text=True)
if result.returncode != 0:
logger.error("Restic backup failed: %s", result.stderr)
return False
logger.info("Restic backup completed successfully")
check_cmd = ["restic", "check"]
result = subprocess.run(check_cmd, env=env, capture_output=True, text=True)
if result.returncode != 0:
logger.error("Restic check failed: %s", result.stderr)
return False
logger.info("Restic check completed successfully")
forget_cmd = [
"restic",
"forget",
"--compact",
"--prune",
"--keep-daily",
"90",
"--keep-monthly",
"36",
]
result = subprocess.run(forget_cmd, env=env, capture_output=True, text=True)
if result.returncode != 0:
logger.error("Restic forget/prune failed: %s", result.stderr)
return False
logger.info("Restic forget/prune completed successfully")
result = subprocess.run(check_cmd, env=env, capture_output=True, text=True)
if result.returncode != 0:
logger.error("Final restic check failed: %s", result.stderr)
return False
logger.info("Final restic check completed successfully")
return True
class Notifier(ABC):
def send(self, html_message: str):
raise NotImplementedError()
class TelegramNotifier(Notifier):
TYPE_NAME = "telegram"
def __init__(self, name: str, params: Dict[str, Any]):
self.name = name
self.telegram_bot_token = str(params.get("telegram_bot_token", ""))
self.telegram_chat_id = str(params.get("telegram_chat_id", ""))
if not all(
[
self.telegram_bot_token,
self.telegram_chat_id,
]
):
raise ValueError(
f"Missing notification configuration values for backend {name}"
)
def send(self, html_message: str):
url = f"https://api.telegram.org/bot{self.telegram_bot_token}/sendMessage"
data = {
"chat_id": self.telegram_chat_id,
"parse_mode": "HTML",
"text": html_message,
}
response = requests.post(url, data=data, timeout=30)
if response.status_code == 200:
logger.info("Telegram notification sent successfully")
else:
logger.error(
f"Failed to send Telegram notification: {response.status_code} - {response.text}"
)
class BackupManager:
def __init__(
self,
config: Config,
roots: List[Path],
storages: List[Storage],
notifiers: List[Notifier],
):
self.errors: List[str] = []
self.warnings: List[str] = []
self.successful_backups: List[str] = []
self.config = config
self.roots: List[Path] = roots
self.storages = storages
self.notifiers = notifiers
def find_applications(self) -> List[Application]:
"""Get all application directories and their owners."""
applications: List[Application] = []
source_dirs = itertools.chain(*(root.iterdir() for root in self.roots))
for app_dir in source_dirs:
if "lost+found" in str(app_dir):
continue
if app_dir.is_dir():
try:
stat_info = app_dir.stat()
owner = pwd.getpwuid(stat_info.st_uid).pw_name
applications.append(Application(path=app_dir, owner=owner))
except (KeyError, OSError) as e:
logger.warning(f"Could not get owner for {app_dir}: {e}")
return applications
def find_backup_script(self, app_dir: str) -> Optional[str]:
"""Find backup script in user's home directory"""
possible_scripts = [
os.path.join(app_dir, "backup.sh"),
os.path.join(app_dir, "backup"),
]
for script_path in possible_scripts:
if os.path.exists(script_path):
# Check if file is executable
if os.access(script_path, os.X_OK):
return script_path
else:
logger.warning(
f"Backup script {script_path} exists but is not executable"
)
return None
def run_app_backup(self, script_path: str, app_dir: str, username: str) -> bool:
"""Run backup script as the specified user"""
try:
logger.info(f"Running backup script {script_path} (user {username})")
# Use su to run the script as the user
cmd = ["su", "--login", username, "--command", script_path]
result = subprocess.run(
cmd,
cwd=app_dir,
capture_output=True,
text=True,
timeout=3600, # 1 hour timeout
)
if result.returncode == 0:
logger.info(f"Backup script for {username} completed successfully")
self.successful_backups.append(username)
return True
else:
error_msg = f"Backup script {script_path} failed with return code {result.returncode}"
if result.stderr:
error_msg += f": {result.stderr}"
logger.error(error_msg)
self.errors.append(f"App {username}: {error_msg}")
return False
except subprocess.TimeoutExpired:
error_msg = f"Backup script {script_path} timed out"
logger.error(error_msg)
self.errors.append(f"App {username}: {error_msg}")
return False
except Exception as e:
error_msg = f"Failed to run backup script {script_path}: {str(e)}"
logger.error(error_msg)
self.errors.append(f"App {username}: {error_msg}")
return False
def get_backup_directories(self) -> List[str]:
"""Collect backup targets according to backup-targets rules"""
backup_dirs: List[str] = []
applications = self.find_applications()
def parse_targets_file(targets_file: Path) -> List[str]:
"""Parse backup-targets file, skipping comments and empty lines."""
targets: List[str] = []
try:
for raw_line in targets_file.read_text(encoding="utf-8").splitlines():
line = raw_line.strip()
if not line or line.startswith("#"):
continue
targets.append(line)
except OSError as e:
warning_msg = f"Could not read backup targets file {targets_file}: {e}"
logger.warning(warning_msg)
self.warnings.append(warning_msg)
return targets
for app in applications:
app_dir = app.path
targets_file = app_dir / BACKUP_TARGETS_FILE
resolved_targets: List[Path] = []
if targets_file.exists():
# Read custom targets defined by the application.
for target_line in parse_targets_file(targets_file):
target_path = Path(target_line)
if not target_path.is_absolute():
target_path = (app_dir / target_path).resolve()
else:
target_path = target_path.resolve()
if target_path.exists():
resolved_targets.append(target_path)
else:
warning_msg = (
f"Backup target does not exist for {app_dir}: {target_path}"
)
logger.warning(warning_msg)
self.warnings.append(warning_msg)
else:
# Fallback to default backups directory when no list is provided.
default_target = (app_dir / BACKUP_DEFAULT_DIR).resolve()
if default_target.exists():
resolved_targets.append(default_target)
else:
warning_msg = f"Default backup path does not exist for {app_dir}: {default_target}"
logger.warning(warning_msg)
self.warnings.append(warning_msg)
for target in resolved_targets:
target_str = str(target)
if target_str not in backup_dirs:
backup_dirs.append(target_str)
return backup_dirs
def send_notification(self, success: bool) -> None:
"""Send notification to Notifiers"""
if success and not self.errors:
message = f"<b>{self.config.host_name}</b>: бекап успешно завершен!"
if self.successful_backups:
message += f"\n\nУспешные бекапы: {', '.join(self.successful_backups)}"
else:
message = f"<b>{self.config.host_name}</b>: бекап завершен с ошибками!"
if self.successful_backups:
message += (
f"\n\n✅ Успешные бекапы: {', '.join(self.successful_backups)}"
)
if self.warnings:
message += f"\n\n⚠️ Предупреждения:\n" + "\n".join(self.warnings)
if self.errors:
message += f"\n\n❌ Ошибки:\n" + "\n".join(self.errors)
for notificator in self.notifiers:
try:
notificator.send(message)
except Exception as e:
logger.error(f"Failed to send notification: {str(e)}")
def run_backup_process(self) -> bool:
"""Main backup process"""
logger.info("Starting backup process")
# Get all home directories
applications = self.find_applications()
logger.info(f"Found {len(applications)} application directories")
# Process each user's backup
for app in applications:
app_dir = str(app.path)
username = app.owner
logger.info(f"Processing backup for app: {app_dir} (user {username})")
# Find backup script
backup_script = self.find_backup_script(app_dir)
if backup_script is None:
warning_msg = (
f"No backup script found for app: {app_dir} (user {username})"
)
logger.warning(warning_msg)
self.warnings.append(warning_msg)
continue
self.run_app_backup(backup_script, app_dir, username)
# Get backup directories
backup_dirs = self.get_backup_directories()
logger.info(f"Found backup directories: {backup_dirs}")
overall_success = True
for storage in self.storages:
backup_result = storage.backup(backup_dirs)
if not backup_result:
self.errors.append("Restic backup failed")
# Determine overall success
overall_success = overall_success and backup_result
# Send notification
self.send_notification(overall_success)
logger.info("Backup process completed")
if self.errors:
logger.error(f"Backup completed with {len(self.errors)} errors")
return False
elif self.warnings:
logger.warning(f"Backup completed with {len(self.warnings)} warnings")
return True
else:
logger.info("Backup completed successfully")
return True
def initialize(config_path: Path) -> BackupManager:
try:
with config_path.open("rb") as config_file:
raw_config = tomllib.load(config_file)
except OSError as e:
logger.error(f"Failed to read config file {config_path}: {e}")
raise
host_name = str(raw_config.get("host_name", "unknown"))
roots_raw = raw_config.get("roots") or []
if not isinstance(roots_raw, list) or not roots_raw:
raise ValueError("roots must be a non-empty list of paths in config.toml")
roots = [Path(root) for root in roots_raw]
storage_raw = raw_config.get("storage") or {}
storages: List[Storage] = []
for name, params in storage_raw.items():
if not isinstance(params, dict):
raise ValueError(f"Storage config for {name} must be a table")
storage_type = params.get("type", "")
if storage_type == ResticStorage.TYPE_NAME:
storages.append(ResticStorage(name, params))
if not storages:
raise ValueError("At least one storage backend must be configured")
notifications_raw = raw_config.get("notifier") or {}
notifiers: List[Notifier] = []
for name, params in notifications_raw.items():
if not isinstance(params, dict):
raise ValueError(f"Notificator config for {name} must be a table")
notifier_type = params.get("type", "")
if notifier_type == TelegramNotifier.TYPE_NAME:
notifiers.append(TelegramNotifier(name, params))
if not notifiers:
raise ValueError("At least one notification backend must be configured")
config = Config(host_name=host_name, roots=roots)
return BackupManager(
config=config, roots=roots, storages=storages, notifiers=notifiers
)
def main():
try:
backup_manager = initialize(CONFIG_PATH)
success = backup_manager.run_backup_process()
if not success:
sys.exit(1)
except KeyboardInterrupt:
logger.info("Backup process interrupted by user")
sys.exit(130)
except Exception as e:
logger.error(f"Unexpected error in backup process: {str(e)}")
sys.exit(1)
if __name__ == "__main__":
main()

View File

@@ -1,326 +0,0 @@
#!/usr/bin/env python3
"""
Backup script for all applications
Automatically discovers and runs backup scripts for all users,
then creates restic backups and sends notifications.
"""
import os
import sys
import subprocess
import logging
import pwd
from pathlib import Path
from typing import List, Tuple, Optional
import requests
# Configure logging
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s - %(levelname)s - %(message)s",
handlers=[
logging.StreamHandler(sys.stdout),
logging.FileHandler("/var/log/backup-all.log"),
],
)
logger = logging.getLogger(__name__)
# Configuration from Ansible template variables
RESTIC_REPOSITORY = "{{ restic_repository }}"
RESTIC_PASSWORD = "{{ restic_password }}"
AWS_ACCESS_KEY_ID = "{{ restic_s3_access_key }}"
AWS_SECRET_ACCESS_KEY = "{{ restic_s3_access_secret }}"
AWS_DEFAULT_REGION = "{{ restic_s3_region }}"
TELEGRAM_BOT_TOKEN = "{{ notifications_tg_bot_token }}"
TELEGRAM_CHAT_ID = "{{ notifications_tg_chat_id }}"
NOTIFICATIONS_NAME = "{{ notifications_name }}"
class BackupManager:
def __init__(self):
self.errors = []
self.warnings = []
self.successful_backups = []
def get_home_directories(self) -> List[Tuple[str, str]]:
"""Get all home directories and their owners"""
home_dirs = []
home_path = Path("/home")
if not home_path.exists():
logger.error("/home directory does not exist")
return home_dirs
for user_dir in home_path.iterdir():
if user_dir.is_dir():
try:
# Get the owner of the directory
stat_info = user_dir.stat()
owner = pwd.getpwuid(stat_info.st_uid).pw_name
home_dirs.append((str(user_dir), owner))
except (KeyError, OSError) as e:
logger.warning(f"Could not get owner for {user_dir}: {e}")
return home_dirs
def find_backup_script(self, home_dir: str) -> Optional[str]:
"""Find backup script in user's home directory"""
possible_scripts = [
os.path.join(home_dir, "backup.sh"),
os.path.join(home_dir, "backup"),
]
for script_path in possible_scripts:
if os.path.exists(script_path):
# Check if file is executable
if os.access(script_path, os.X_OK):
return script_path
else:
logger.warning(
f"Backup script {script_path} exists but is not executable"
)
return None
def run_user_backup(self, script_path: str, username: str) -> bool:
"""Run backup script as the specified user"""
try:
logger.info(f"Running backup script {script_path} as user {username}")
# Use su to run the script as the user
cmd = ["su", "--login", username, "--command", script_path]
result = subprocess.run(
cmd, capture_output=True, text=True, timeout=3600 # 1 hour timeout
)
if result.returncode == 0:
logger.info(f"Backup script for {username} completed successfully")
self.successful_backups.append(username)
return True
else:
error_msg = f"Backup script for {username} failed with return code {result.returncode}"
if result.stderr:
error_msg += f": {result.stderr}"
logger.error(error_msg)
self.errors.append(f"User {username}: {error_msg}")
return False
except subprocess.TimeoutExpired:
error_msg = f"Backup script for {username} timed out"
logger.error(error_msg)
self.errors.append(f"User {username}: {error_msg}")
return False
except Exception as e:
error_msg = f"Failed to run backup script for {username}: {str(e)}"
logger.error(error_msg)
self.errors.append(f"User {username}: {error_msg}")
return False
def get_backup_directories(self) -> List[str]:
"""Get all backup directories that exist"""
backup_dirs = []
home_dirs = self.get_home_directories()
for home_dir, _ in home_dirs:
backup_path = os.path.join(home_dir, "backups")
if os.path.exists(backup_path) and os.path.isdir(backup_path):
backup_dirs.append(backup_path)
return backup_dirs
def run_restic_backup(self, backup_dirs: List[str]) -> bool:
"""Run restic backup for all backup directories"""
if not backup_dirs:
logger.warning("No backup directories found")
return True
try:
logger.info("Starting restic backup")
# Set environment variables for restic
env = os.environ.copy()
env.update(
{
"RESTIC_REPOSITORY": RESTIC_REPOSITORY,
"RESTIC_PASSWORD": RESTIC_PASSWORD,
"AWS_ACCESS_KEY_ID": AWS_ACCESS_KEY_ID,
"AWS_SECRET_ACCESS_KEY": AWS_SECRET_ACCESS_KEY,
"AWS_DEFAULT_REGION": AWS_DEFAULT_REGION,
}
)
# Run backup
backup_cmd = ["restic", "backup", "--verbose"] + backup_dirs
result = subprocess.run(backup_cmd, env=env, capture_output=True, text=True)
if result.returncode != 0:
error_msg = f"Restic backup failed: {result.stderr}"
logger.error(error_msg)
self.errors.append(f"Restic backup: {error_msg}")
return False
logger.info("Restic backup completed successfully")
# Run check
check_cmd = ["restic", "check"]
result = subprocess.run(check_cmd, env=env, capture_output=True, text=True)
if result.returncode != 0:
error_msg = f"Restic check failed: {result.stderr}"
logger.error(error_msg)
self.errors.append(f"Restic check: {error_msg}")
return False
logger.info("Restic check completed successfully")
# Run forget and prune
forget_cmd = [
"restic",
"forget",
"--compact",
"--prune",
"--keep-daily",
"90",
"--keep-monthly",
"36",
]
result = subprocess.run(forget_cmd, env=env, capture_output=True, text=True)
if result.returncode != 0:
error_msg = f"Restic forget/prune failed: {result.stderr}"
logger.error(error_msg)
self.errors.append(f"Restic forget/prune: {error_msg}")
return False
logger.info("Restic forget/prune completed successfully")
# Final check
result = subprocess.run(check_cmd, env=env, capture_output=True, text=True)
if result.returncode != 0:
error_msg = f"Final restic check failed: {result.stderr}"
logger.error(error_msg)
self.errors.append(f"Final restic check: {error_msg}")
return False
logger.info("Final restic check completed successfully")
return True
except Exception as e:
error_msg = f"Restic backup process failed: {str(e)}"
logger.error(error_msg)
self.errors.append(f"Restic: {error_msg}")
return False
def send_telegram_notification(self, success: bool) -> None:
"""Send notification to Telegram"""
try:
if success and not self.errors:
message = f"<b>{NOTIFICATIONS_NAME}</b>: бекап успешно завершен!"
if self.successful_backups:
message += (
f"\n\nУспешные бекапы: {', '.join(self.successful_backups)}"
)
else:
message = f"<b>{NOTIFICATIONS_NAME}</b>: бекап завершен с ошибками!"
if self.successful_backups:
message += (
f"\n\n✅ Успешные бекапы: {', '.join(self.successful_backups)}"
)
if self.warnings:
message += f"\n\n⚠️ Предупреждения:\n" + "\n".join(self.warnings)
if self.errors:
message += f"\n\n❌ Ошибки:\n" + "\n".join(self.errors)
url = f"https://api.telegram.org/bot{TELEGRAM_BOT_TOKEN}/sendMessage"
data = {"chat_id": TELEGRAM_CHAT_ID, "parse_mode": "HTML", "text": message}
response = requests.post(url, data=data, timeout=30)
if response.status_code == 200:
logger.info("Telegram notification sent successfully")
else:
logger.error(
f"Failed to send Telegram notification: {response.status_code} - {response.text}"
)
except Exception as e:
logger.error(f"Failed to send Telegram notification: {str(e)}")
def run_backup_process(self) -> bool:
"""Main backup process"""
logger.info("Starting backup process")
# Get all home directories
home_dirs = self.get_home_directories()
logger.info(f"Found {len(home_dirs)} home directories")
# Process each user's backup
for home_dir, username in home_dirs:
logger.info(f"Processing backup for user: {username} ({home_dir})")
# Find backup script
backup_script = self.find_backup_script(home_dir)
if backup_script is None:
warning_msg = (
f"No backup script found for user {username} in {home_dir}"
)
logger.warning(warning_msg)
self.warnings.append(warning_msg)
continue
# Run backup script
self.run_user_backup(backup_script, username)
# Get backup directories
backup_dirs = self.get_backup_directories()
logger.info(f"Found backup directories: {backup_dirs}")
# Run restic backup
restic_success = self.run_restic_backup(backup_dirs)
# Determine overall success
overall_success = restic_success and len(self.errors) == 0
# Send notification
self.send_telegram_notification(overall_success)
logger.info("Backup process completed")
if self.errors:
logger.error(f"Backup completed with {len(self.errors)} errors")
return False
elif self.warnings:
logger.warning(f"Backup completed with {len(self.warnings)} warnings")
return True
else:
logger.info("Backup completed successfully")
return True
def main():
"""Main entry point"""
try:
backup_manager = BackupManager()
success = backup_manager.run_backup_process()
if success:
sys.exit(0)
else:
sys.exit(1)
except KeyboardInterrupt:
logger.info("Backup process interrupted by user")
sys.exit(130)
except Exception as e:
logger.error(f"Unexpected error in backup process: {str(e)}")
sys.exit(1)
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,18 @@
host_name = "{{ notifications_name }}"
roots = [
"{{ application_dir }}"
]
[storage.yandex_cloud_s3]
type = "restic"
restic_repository = "{{ restic_repository }}"
restic_password = "{{ restic_password }}"
aws_access_key_id = "{{ restic_s3_access_key }}"
aws_secret_access_key = "{{ restic_s3_access_secret }}"
aws_default_region = "{{ restic_s3_region }}"
[notifier.server_notifications_channel]
type = "telegram"
telegram_bot_token = "{{ notifications_tg_bot_token }}"
telegram_chat_id = "{{ notifications_tg_chat_id }}"

View File

@@ -89,6 +89,14 @@ wanderer.vakhrushev.me {
} }
memos.vakhrushev.me { memos.vakhrushev.me {
tls anwinged@ya.ru
reverse_proxy {
to memos_app:5230
}
}
wanderbase.vakhrushev.me {
tls anwinged@ya.ru tls anwinged@ya.ru
forward_auth authelia_app:9091 { forward_auth authelia_app:9091 {

View File

@@ -1,7 +1,7 @@
services: services:
dozzle_app: dozzle_app:
image: amir20/dozzle:v8.14.4 image: amir20/dozzle:v8.14.11
container_name: dozzle_app container_name: dozzle_app
restart: unless-stopped restart: unless-stopped
volumes: volumes:

View File

@@ -1,11 +1,10 @@
services: services:
gitea_app: gitea_app:
image: gitea/gitea:1.24.6 image: gitea/gitea:1.25.3
restart: unless-stopped restart: unless-stopped
container_name: gitea_app container_name: gitea_app
ports: ports:
- "127.0.0.1:{{ gitea_port }}:3000"
- "2222:22" - "2222:22"
volumes: volumes:
- {{ data_dir }}:/data - {{ data_dir }}:/data

View File

@@ -3,7 +3,7 @@
services: services:
gramps_app: &gramps_app gramps_app: &gramps_app
image: ghcr.io/gramps-project/grampsweb:25.9.0 image: ghcr.io/gramps-project/grampsweb:25.11.2
container_name: gramps_app container_name: gramps_app
depends_on: depends_on:
- gramps_redis - gramps_redis
@@ -15,10 +15,10 @@ services:
- "{{ (data_dir, 'gramps_db') | path_join }}:/root/.gramps/grampsdb" # persist Gramps database - "{{ (data_dir, 'gramps_db') | path_join }}:/root/.gramps/grampsdb" # persist Gramps database
- "{{ (data_dir, 'gramps_users') | path_join }}:/app/users" # persist user database - "{{ (data_dir, 'gramps_users') | path_join }}:/app/users" # persist user database
- "{{ (data_dir, 'gramps_index') | path_join }}:/app/indexdir" # persist search index - "{{ (data_dir, 'gramps_index') | path_join }}:/app/indexdir" # persist search index
- "{{ (data_dir, 'gramps_thumb_cache') | path_join }}:/app/thumbnail_cache" # persist thumbnails
- "{{ (data_dir, 'gramps_cache') | path_join }}:/app/cache" # persist export and report caches
- "{{ (data_dir, 'gramps_secret') | path_join }}:/app/secret" # persist flask secret - "{{ (data_dir, 'gramps_secret') | path_join }}:/app/secret" # persist flask secret
- "{{ (data_dir, 'gramps_media') | path_join }}:/app/media" # persist media files - "{{ (cache_dir, 'gramps_thumb_cache') | path_join }}:/app/thumbnail_cache" # persist thumbnails
- "{{ (cache_dir, 'gramps_cache') | path_join }}:/app/cache" # persist export and report caches
- "{{ media_dir }}:/app/media" # persist media files
environment: environment:
GRAMPSWEB_TREE: "Gramps" # will create a new tree if not exists GRAMPSWEB_TREE: "Gramps" # will create a new tree if not exists
GRAMPSWEB_SECRET_KEY: "{{ gramps_secret_key }}" GRAMPSWEB_SECRET_KEY: "{{ gramps_secret_key }}"
@@ -37,12 +37,8 @@ services:
GRAMPSWEB_EMAIL_USE_TLS: "false" GRAMPSWEB_EMAIL_USE_TLS: "false"
GRAMPSWEB_DEFAULT_FROM_EMAIL: "gramps@vakhrushev.me" GRAMPSWEB_DEFAULT_FROM_EMAIL: "gramps@vakhrushev.me"
# media storage at s3 # media storage
GRAMPSWEB_MEDIA_BASE_DIR: "s3://av-gramps-media-storage" GRAMPSWEB_MEDIA_BASE_DIR: "/app/media"
AWS_ENDPOINT_URL: "{{ gramps_s3_endpoint }}"
AWS_ACCESS_KEY_ID: "{{ gramps_s3_access_key_id }}"
AWS_SECRET_ACCESS_KEY: "{{ gramps_s3_secret_access_key }}"
AWS_DEFAULT_REGION: "{{ gramps_s3_region }}"
gramps_celery: gramps_celery:
<<: *gramps_app # YAML merge key copying the entire grampsweb service config <<: *gramps_app # YAML merge key copying the entire grampsweb service config
@@ -53,10 +49,10 @@ services:
ports: [] ports: []
networks: networks:
- "gramps_network" - "gramps_network"
command: celery -A gramps_webapi.celery worker --loglevel=INFO --concurrency=2 command: celery -A gramps_webapi.celery worker --loglevel=INFO --concurrency=1
gramps_redis: gramps_redis:
image: valkey/valkey:8.1.1-alpine image: valkey/valkey:9.0-alpine
container_name: gramps_redis container_name: gramps_redis
restart: unless-stopped restart: unless-stopped
networks: networks:

View File

@@ -23,10 +23,3 @@ models:
undo: undo:
type: sqlite type: sqlite
path: "{{ (data_dir, 'gramps_db/59a0f3d6-1c3d-4410-8c1d-1c9c6689659f/undo.db') | path_join }}" path: "{{ (data_dir, 'gramps_db/59a0f3d6-1c3d-4410-8c1d-1c9c6689659f/undo.db') | path_join }}"
archive:
includes:
- "{{ data_dir }}"
excludes:
- "{{ (data_dir, 'gramps_cache') | path_join }}"
- "{{ (data_dir, 'gramps_thumb_cache') | path_join }}"
- "{{ (data_dir, 'gramps_tmp') | path_join }}"

65
files/gramps/gramps_rename.py Executable file
View File

@@ -0,0 +1,65 @@
#!/usr/bin/env python3.12
import argparse
import sys
from pathlib import Path
def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(
description="Rename Gramps document files by appending extensions from a list."
)
parser.add_argument("directory", type=Path, help="Directory containing hashed files")
parser.add_argument("names_file", type=Path, help="Text file with target names")
return parser.parse_args()
def read_names(path: Path) -> list[str]:
if not path.is_file():
raise FileNotFoundError(f"Names file not found: {path}")
names = []
for line in path.read_text(encoding="utf-8").splitlines():
name = line.strip()
if name:
names.append(name)
return names
def rename_files(directory: Path, names: list[str]) -> None:
if not directory.is_dir():
raise NotADirectoryError(f"Directory not found: {directory}")
for name in names:
hash_part, dot, _ = name.partition(".")
if not dot:
print(f"Skipping invalid entry (missing extension): {name}", file=sys.stderr)
continue
source = directory / hash_part
target = directory / name
if target.exists():
print(f"Target already exists, skipping: {target}", file=sys.stderr)
continue
if not source.exists():
print(f"Source not found, skipping: {source}", file=sys.stderr)
continue
source.rename(target)
print(f"Renamed {source.name} -> {target.name}")
def main() -> None:
args = parse_args()
try:
names = read_names(args.names_file)
rename_files(args.directory, names)
except Exception as exc: # noqa: BLE001
print(str(exc), file=sys.stderr)
sys.exit(1)
if __name__ == "__main__":
main()

View File

@@ -1,14 +1,13 @@
services: services:
homepage_app: homepage_app:
image: "{{ registry_homepage_web_image }}" # noinspection ComposeUnknownValues
image: "{{ registry_homepage_nginx_image }}"
container_name: homepage_app container_name: homepage_app
restart: unless-stopped restart: unless-stopped
ports:
- "127.0.0.1:{{ homepage_port }}:80"
networks: networks:
- "web_proxy_network" - "web_proxy_network"
networks: networks:
web_proxy_network: web_proxy_network:
external: true external: true

10
files/memos/backup.sh.j2 Normal file
View File

@@ -0,0 +1,10 @@
#!/usr/bin/env bash
set -eu
set -o pipefail
echo "{{ app_name }}: backup data with gobackups"
(cd "{{ base_dir }}" && gobackup perform --config "{{ gobackup_config }}")
echo "{{ app_name }}: done."

View File

@@ -0,0 +1,23 @@
# See versions: https://github.com/gramps-project/gramps-web/pkgs/container/grampsweb
services:
memos_app:
image: neosmemo/memos:0.25.3
container_name: memos_app
restart: unless-stopped
user: "{{ user_create_result.uid }}:{{ user_create_result.group }}"
networks:
- "web_proxy_network"
volumes:
- "{{ data_dir }}:/var/opt/memos"
environment:
- MEMOS_MODE=prod
- MEMOS_PORT=5230
- MEMOS_STORAGE_TYPE=local
- MEMOS_STORAGE_PATH=assets/{uuid}
- MEMOS_MAX_FILE_SIZE=52428800
networks:
web_proxy_network:
external: true

View File

@@ -0,0 +1,16 @@
# https://gobackup.github.io/configuration
models:
memos:
compress_with:
type: 'tgz'
storages:
local:
type: 'local'
path: '{{ backups_dir }}'
keep: 3
databases:
users:
type: sqlite
path: "{{ (data_dir, 'memos_prod.db') | path_join }}"

View File

@@ -1,8 +1,7 @@
services: services:
netdata: netdata:
image: netdata/netdata:v2.7.1 image: netdata/netdata:v2.8.4
container_name: netdata container_name: netdata
restart: unless-stopped restart: unless-stopped
cap_add: cap_add:
@@ -17,12 +16,16 @@ services:
- "{{ config_dir }}:/etc/netdata" - "{{ config_dir }}:/etc/netdata"
- "{{ (data_dir, 'lib') | path_join }}:/var/lib/netdata" - "{{ (data_dir, 'lib') | path_join }}:/var/lib/netdata"
- "{{ (data_dir, 'cache') | path_join }}:/var/cache/netdata" - "{{ (data_dir, 'cache') | path_join }}:/var/cache/netdata"
# Netdata system volumes # Netdata system volumes
- "/:/host/root:ro,rslave" - "/:/host/root:ro,rslave"
- "/etc/group:/host/etc/group:ro" - "/etc/group:/host/etc/group:ro"
- "/etc/hostname:/host/etc/hostname:ro"
- "/etc/localtime:/etc/localtime:ro" - "/etc/localtime:/etc/localtime:ro"
- "/etc/os-release:/host/etc/os-release:ro" - "/etc/os-release:/host/etc/os-release:ro"
- "/etc/passwd:/host/etc/passwd:ro" - "/etc/passwd:/host/etc/passwd:ro"
- "/proc:/host/proc:ro" - "/proc:/host/proc:ro"
- "/run/dbus:/run/dbus:ro" - "/run/dbus:/run/dbus:ro"
- "/sys:/host/sys:ro" - "/sys:/host/sys:ro"

View File

@@ -1,3 +1,3 @@
jobs: jobs:
- name: fail2ban - name: fail2ban
update_every: 15 # Collect Fail2Ban jails statistics every 15 seconds update_every: 60 # Collect Fail2Ban jails statistics every N seconds

View File

@@ -1,4 +1,4 @@
update_every: 15 update_every: 60
jobs: jobs:

View File

@@ -19,7 +19,7 @@
# cpu cores = 2 # cpu cores = 2
# libuv worker threads = 16 # libuv worker threads = 16
# profile = standalone # profile = standalone
hostname = {{ host_name }} # hostname = rivendell-v2
# glibc malloc arena max for plugins = 1 # glibc malloc arena max for plugins = 1
# glibc malloc arena max for netdata = 1 # glibc malloc arena max for netdata = 1
# crash reports = all # crash reports = all
@@ -30,12 +30,15 @@
# has unstable connection = no # has unstable connection = no
[db] [db]
#| >>> [db].update every <<<
#| datatype: duration (seconds), default value: 1s
update every = 10s
# enable replication = yes # enable replication = yes
# replication period = 1d # replication period = 1d
# replication step = 1h # replication step = 1h
# replication threads = 1 # replication threads = 1
# replication prefetch = 10 # replication prefetch = 10
# update every = 1s
# db = dbengine # db = dbengine
# memory deduplication (ksm) = auto # memory deduplication (ksm) = auto
# cleanup orphan hosts after = 1h # cleanup orphan hosts after = 1h
@@ -47,7 +50,7 @@
# dbengine extent cache size = off # dbengine extent cache size = off
# dbengine enable journal integrity check = no # dbengine enable journal integrity check = no
# dbengine use all ram for caches = no # dbengine use all ram for caches = no
# dbengine out of memory protection = 391.99MiB # dbengine out of memory protection = 391.49MiB
# dbengine use direct io = yes # dbengine use direct io = yes
# dbengine journal v2 unmount time = 2m # dbengine journal v2 unmount time = 2m
# dbengine pages per extent = 109 # dbengine pages per extent = 109
@@ -96,9 +99,6 @@
# PYTHONPATH = # PYTHONPATH =
# TZ = :/etc/localtime # TZ = :/etc/localtime
[host labels]
# name = value
[cloud] [cloud]
# conversation log = no # conversation log = no
# scope = full # scope = full
@@ -107,15 +107,15 @@
[ml] [ml]
# enabled = auto # enabled = auto
# maximum num samples to train = 21600 # training window = 6h
# minimum num samples to train = 900 # min training window = 15m
# max training vectors = 1440
# max samples to smooth = 3
# train every = 3h # train every = 3h
# number of models per dimension = 18 # number of models per dimension = 18
# delete models older than = 7d # delete models older than = 7d
# num samples to diff = 1 # num samples to diff = 1
# num samples to smooth = 3
# num samples to lag = 5 # num samples to lag = 5
# random sampling ratio = 0.20000
# maximum number of k-means iterations = 1000 # maximum number of k-means iterations = 1000
# dimension anomaly score threshold = 0.99000 # dimension anomaly score threshold = 0.99000
# host anomaly rate threshold = 1.00000 # host anomaly rate threshold = 1.00000
@@ -181,7 +181,7 @@
# gzip compression level = 3 # gzip compression level = 3
# ssl skip certificate verification = no # ssl skip certificate verification = no
# web server threads = 6 # web server threads = 6
# web server max sockets = 262144 # web server max sockets = 131072
[registry] [registry]
# enabled = no # enabled = no
@@ -191,7 +191,7 @@
# registry expire idle persons = 1y # registry expire idle persons = 1y
# registry domain = # registry domain =
# registry to announce = https://registry.my-netdata.io # registry to announce = https://registry.my-netdata.io
# registry hostname = 7171b7f9fc69 # registry hostname = rivendell-v2
# verify browser cookies support = yes # verify browser cookies support = yes
# enable cookies SameSite and Secure = yes # enable cookies SameSite and Secure = yes
# max URL length = 1024 # max URL length = 1024
@@ -202,9 +202,29 @@
[pulse] [pulse]
# extended = no # extended = no
# update every = 1s # update every = 10s
[plugins] [plugins]
#| >>> [plugins].perf <<<
#| datatype: yes or no, default value: yes
perf = no
#| >>> [plugins].python.d <<<
#| datatype: yes or no, default value: yes
python.d = no
#| >>> [plugins].charts.d <<<
#| datatype: yes or no, default value: yes
charts.d = no
#| >>> [plugins].otel <<<
#| datatype: yes or no, default value: yes
otel = no
#| >>> [plugins].statsd <<<
#| datatype: yes or no, default value: yes
statsd = no
# idlejitter = yes # idlejitter = yes
# netdata pulse = yes # netdata pulse = yes
# profile = no # profile = no
@@ -213,23 +233,20 @@
# proc = yes # proc = yes
# cgroups = yes # cgroups = yes
# timex = yes # timex = yes
# statsd = yes
# enable running new plugins = yes # enable running new plugins = yes
# check for new plugins every = 1m # check for new plugins every = 1m
# slabinfo = no # slabinfo = no
# freeipmi = no # freeipmi = no
# python.d = yes
# go.d = yes
# apps = yes
# systemd-journal = yes
# network-viewer = yes
# charts.d = yes
# debugfs = yes # debugfs = yes
# perf = yes
# ioping = yes # ioping = yes
# network-viewer = yes
# apps = yes
# go.d = yes
# systemd-units = yes
# systemd-journal = yes
[statsd] [statsd]
# update every (flushInterval) = 1s # update every (flushInterval) = 10s
# udp messages to process at once = 10 # udp messages to process at once = 10
# create private charts for metrics matching = * # create private charts for metrics matching = *
# max private charts hard limit = 1000 # max private charts hard limit = 1000
@@ -247,10 +264,7 @@
# gaps on histograms (deleteHistograms) = no # gaps on histograms (deleteHistograms) = no
# gaps on timers (deleteTimers) = no # gaps on timers (deleteTimers) = no
# gaps on dictionaries (deleteDictionaries) = no # gaps on dictionaries (deleteDictionaries) = no
# statsd server max TCP sockets = 262144 # statsd server max TCP sockets = 131072
# listen backlog = 4096
# default port = 8125
# bind to = udp:localhost tcp:localhost
[plugin:idlejitter] [plugin:idlejitter]
# loop time = 20ms # loop time = 20ms
@@ -300,8 +314,14 @@
# /sys/class/drm = yes # /sys/class/drm = yes
[plugin:cgroups] [plugin:cgroups]
# update every = 1s #| >>> [plugin:cgroups].update every <<<
# check for new cgroups every = 10s #| datatype: duration (seconds), default value: 10s
update every = 20s
#| >>> [plugin:cgroups].check for new cgroups every <<<
#| datatype: duration (seconds), default value: 10s
check for new cgroups every = 20s
# use unified cgroups = auto # use unified cgroups = auto
# max cgroups to allow = 1000 # max cgroups to allow = 1000
# max cgroups depth to monitor = 0 # max cgroups depth to monitor = 0
@@ -314,8 +334,11 @@
# cgroups to match as systemd services = !/system.slice/*/*.service /system.slice/*.service # cgroups to match as systemd services = !/system.slice/*/*.service /system.slice/*.service
[plugin:proc:diskspace] [plugin:proc:diskspace]
#| >>> [plugin:proc:diskspace].update every <<<
#| datatype: duration (seconds), default value: 10s
update every = 1m
# remove charts of unmounted disks = yes # remove charts of unmounted disks = yes
# update every = 1s
# check for new mount points every = 15s # check for new mount points every = 15s
# exclude space metrics on paths = /dev /dev/shm /proc/* /sys/* /var/run/user/* /run/lock /run/user/* /snap/* /var/lib/docker/* /var/lib/containers/storage/* /run/credentials/* /run/containerd/* /rpool /rpool/* # exclude space metrics on paths = /dev /dev/shm /proc/* /sys/* /var/run/user/* /run/lock /run/user/* /snap/* /var/lib/docker/* /var/lib/containers/storage/* /run/credentials/* /run/containerd/* /rpool /rpool/*
# exclude space metrics on filesystems = *gvfs *gluster* *s3fs *ipfs *davfs2 *httpfs *sshfs *gdfs *moosefs fusectl autofs cgroup cgroup2 hugetlbfs devtmpfs fuse.lxcfs # exclude space metrics on filesystems = *gvfs *gluster* *s3fs *ipfs *davfs2 *httpfs *sshfs *gdfs *moosefs fusectl autofs cgroup cgroup2 hugetlbfs devtmpfs fuse.lxcfs
@@ -326,40 +349,28 @@
[plugin:tc] [plugin:tc]
# script to run to get tc values = /usr/libexec/netdata/plugins.d/tc-qos-helper.sh # script to run to get tc values = /usr/libexec/netdata/plugins.d/tc-qos-helper.sh
[plugin:python.d]
# update every = 1s
# command options =
[plugin:go.d] [plugin:go.d]
# update every = 1s # update every = 10s
# command options = # command options =
[plugin:apps] [plugin:apps]
# update every = 1s # update every = 10s
# command options = # command options =
[plugin:systemd-journal] [plugin:systemd-journal]
# update every = 1s # update every = 10s
# command options = # command options =
[plugin:network-viewer] [plugin:network-viewer]
# update every = 1s # update every = 10s
# command options =
[plugin:charts.d]
# update every = 1s
# command options = # command options =
[plugin:debugfs] [plugin:debugfs]
# update every = 1s # update every = 10s
# command options =
[plugin:perf]
# update every = 1s
# command options = # command options =
[plugin:ioping] [plugin:ioping]
# update every = 1s # update every = 10s
# command options = # command options =
[plugin:proc:/proc/net/dev] [plugin:proc:/proc/net/dev]
@@ -635,7 +646,7 @@
# preferred disk ids = * # preferred disk ids = *
# exclude disks = loop* ram* # exclude disks = loop* ram*
# filename to monitor = /host/proc/diskstats # filename to monitor = /host/proc/diskstats
# performance metrics for disks with major 252 = yes # performance metrics for disks with major 253 = yes
[plugin:proc:/proc/mdstat] [plugin:proc:/proc/mdstat]
# faulty devices = yes # faulty devices = yes
@@ -685,3 +696,7 @@
[plugin:proc:/sys/class/drm] [plugin:proc:/sys/class/drm]
# directory to monitor = /host/sys/class/drm # directory to monitor = /host/sys/class/drm
[plugin:systemd-units]
# update every = 10s
# command options =

View File

@@ -3,14 +3,12 @@ services:
# See sample https://github.com/outline/outline/blob/main/.env.sample # See sample https://github.com/outline/outline/blob/main/.env.sample
outline_app: outline_app:
image: outlinewiki/outline:0.87.4 image: outlinewiki/outline:1.1.0
container_name: outline_app container_name: outline_app
restart: unless-stopped restart: unless-stopped
depends_on: depends_on:
- outline_postgres - outline_postgres
- outline_redis - outline_redis
ports:
- "127.0.0.1:{{ outline_port }}:3000"
networks: networks:
- "outline_network" - "outline_network"
- "web_proxy_network" - "web_proxy_network"
@@ -20,7 +18,7 @@ services:
FORCE_HTTPS: 'true' FORCE_HTTPS: 'true'
SECRET_KEY: '{{ outline_secret_key }}' SECRET_KEY: '{{ outline_secret_key }}'
UTILS_SECRET: '{{ outline_utils_secret }}' UTILS_SECRET: '{{ outline_utils_secret }}'
DATABASE_URL: 'postgres://{{ outline_postgres_user }}:{{ outline_postgres_password }}@outline_postgres:5432/{{ outline_postgres_database }}' DATABASE_URL: 'postgres://{{ outline_postgres_user }}:{{ outline_postgres_password }}@outline_postgres:5432/{{ outline_postgres_database }}' # yamllint disable-line rule:line-length
PGSSLMODE: 'disable' PGSSLMODE: 'disable'
REDIS_URL: 'redis://outline_redis:6379' REDIS_URL: 'redis://outline_redis:6379'
@@ -54,7 +52,7 @@ services:
SMTP_SECURE: 'false' SMTP_SECURE: 'false'
outline_redis: outline_redis:
image: valkey/valkey:8.1.1-alpine image: valkey/valkey:9.0-alpine
container_name: outline_redis container_name: outline_redis
restart: unless-stopped restart: unless-stopped
networks: networks:
@@ -64,16 +62,22 @@ services:
outline_postgres: outline_postgres:
image: postgres:16.3-bookworm image: postgres:16.3-bookworm
container_name: outline_postgres container_name: outline_postgres
user: "{{ user_create_result.uid }}:{{ user_create_result.group }}"
restart: unless-stopped restart: unless-stopped
volumes: volumes:
- {{ postgres_data_dir }}:/var/lib/postgresql/data - "/etc/passwd:/etc/passwd:ro"
networks: - "{{ postgres_data_dir }}:/var/lib/postgresql/data"
- "outline_network"
- "monitoring_network"
environment: environment:
POSTGRES_USER: '{{ outline_postgres_user }}' POSTGRES_USER: '{{ outline_postgres_user }}'
POSTGRES_PASSWORD: '{{ outline_postgres_password }}' POSTGRES_PASSWORD: '{{ outline_postgres_password }}'
POSTGRES_DB: '{{ outline_postgres_database }}' POSTGRES_DB: '{{ outline_postgres_database }}'
networks:
- "outline_network"
- "monitoring_network"
healthcheck:
test: ["CMD", "pg_isready", "--username={{ outline_postgres_user }}", "--dbname={{ outline_postgres_database }}"]
interval: 10s
start_period: 30s
networks: networks:
outline_network: outline_network:

View File

@@ -1,9 +1,10 @@
services: services:
transcriber_app: transcriber_app:
# noinspection ComposeUnknownValues
image: "{{ registry_transcriber_image }}" image: "{{ registry_transcriber_image }}"
container_name: transcriber_app container_name: transcriber_app
user: '{{ user_create_result.uid }}:{{ user_create_result.group }}' user: "{{ user_create_result.uid }}:{{ user_create_result.group }}"
restart: unless-stopped restart: unless-stopped
volumes: volumes:
- "{{ config_file }}:/config/config.toml:ro" - "{{ config_file }}:/config/config.toml:ro"

View File

@@ -3,7 +3,7 @@
services: services:
wakapi_app: wakapi_app:
image: ghcr.io/muety/wakapi:2.15.0 image: ghcr.io/muety/wakapi:2.16.1
container_name: wakapi_app container_name: wakapi_app
restart: unless-stopped restart: unless-stopped
user: '{{ user_create_result.uid }}:{{ user_create_result.group }}' user: '{{ user_create_result.uid }}:{{ user_create_result.group }}'
@@ -17,6 +17,11 @@ services:
WAKAPI_ALLOW_SIGNUP: "false" WAKAPI_ALLOW_SIGNUP: "false"
WAKAPI_DISABLE_FRONTPAGE: "true" WAKAPI_DISABLE_FRONTPAGE: "true"
WAKAPI_COOKIE_MAX_AGE: 31536000 WAKAPI_COOKIE_MAX_AGE: 31536000
# OIDC
# WAKAPI_OIDC_PROVIDER_NAME: "authelia"
# WAKAPI_OIDC_PROVIDER_CLIENT_ID: "{{ wakapi_oidc_client_id }}"
# WAKAPI_OIDC_PROVIDER_CLIENT_SECRET: "{{ wakapi_oidc_client_secret }}"
# WAKAPI_OIDC_PROVIDER_ENDPOINT: "https://auth.vakhrushev.me/.well-known/openid-configuration"
# Mail # Mail
WAKAPI_MAIL_SENDER: "Wakapi <wakapi@vakhrushev.me>" WAKAPI_MAIL_SENDER: "Wakapi <wakapi@vakhrushev.me>"
WAKAPI_MAIL_PROVIDER: "smtp" WAKAPI_MAIL_PROVIDER: "smtp"
@@ -26,7 +31,6 @@ services:
WAKAPI_MAIL_SMTP_PASS: "{{ postbox_pass }}" WAKAPI_MAIL_SMTP_PASS: "{{ postbox_pass }}"
WAKAPI_MAIL_SMTP_TLS: "false" WAKAPI_MAIL_SMTP_TLS: "false"
networks: networks:
web_proxy_network: web_proxy_network:
external: true external: true

View File

@@ -1,6 +1,8 @@
# Refer for explanation to following link: # Refer for explanation to following link:
# https://lefthook.dev/configuration/ # https://lefthook.dev/configuration/
glob_matcher: doublestar
templates: templates:
av-hooks-dir: "/home/av/projects/private/git-hooks" av-hooks-dir: "/home/av/projects/private/git-hooks"
@@ -12,3 +14,12 @@ pre-commit:
- name: "check secret files" - name: "check secret files"
run: "python3 {av-hooks-dir}/pre-commit/check-secrets-encrypted-with-ansible-vault.py" run: "python3 {av-hooks-dir}/pre-commit/check-secrets-encrypted-with-ansible-vault.py"
- name: "format python"
glob: "**/*.py"
run: "black --quiet {staged_files}"
stage_fixed: true
- name: "mypy"
glob: "**/*.py"
run: "mypy {staged_files}"

View File

@@ -0,0 +1,48 @@
---
- name: 'Configure netdata'
ansible.builtin.import_playbook: playbook-netdata.yml
#
- name: 'Configure dozzle'
ansible.builtin.import_playbook: playbook-dozzle.yml
- name: 'Configure gitea'
ansible.builtin.import_playbook: playbook-gitea.yml
- name: 'Configure gramps'
ansible.builtin.import_playbook: playbook-gramps.yml
- name: 'Configure memos'
ansible.builtin.import_playbook: playbook-memos.yml
- name: 'Configure miniflux'
ansible.builtin.import_playbook: playbook-miniflux.yml
- name: 'Configure outline'
ansible.builtin.import_playbook: playbook-outline.yml
- name: 'Configure rssbridge'
ansible.builtin.import_playbook: playbook-rssbridge.yml
- name: 'Configure wakapi'
ansible.builtin.import_playbook: playbook-wakapi.yml
- name: 'Configure wanderer'
ansible.builtin.import_playbook: playbook-wanderer.yml
#
- name: 'Configure homepage'
ansible.builtin.import_playbook: playbook-homepage.yml
- name: 'Configure transcriber'
ansible.builtin.import_playbook: playbook-transcriber.yml
#
- name: 'Configure authelia'
ansible.builtin.import_playbook: playbook-authelia.yml
- name: 'Configure caddy proxy'
ansible.builtin.import_playbook: playbook-caddyproxy.yml

12
playbook-all-setup.yml Normal file
View File

@@ -0,0 +1,12 @@
---
- name: 'Configure system'
ansible.builtin.import_playbook: playbook-system.yml
- name: 'Configure docker'
ansible.builtin.import_playbook: playbook-docker.yml
- name: 'Configure eget applications'
ansible.builtin.import_playbook: playbook-eget.yml
- name: 'Configure backups'
ansible.builtin.import_playbook: playbook-backups.yml

View File

@@ -3,15 +3,19 @@
hosts: all hosts: all
vars_files: vars_files:
- vars/ports.yml
- vars/secrets.yml - vars/secrets.yml
- files/authelia/secrets.yml - files/authelia/secrets.yml
vars: vars:
app_name: "authelia" app_name: "authelia"
app_user: "{{ app_name }}" app_user: "{{ app_name }}"
base_dir: "/home/{{ app_user }}" app_owner_uid: 1011
app_owner_gid: 1012
base_dir: "{{ (application_dir, app_name) | path_join }}"
data_dir: "{{ (base_dir, 'data') | path_join }}"
config_dir: "{{ (base_dir, 'config') | path_join }}" config_dir: "{{ (base_dir, 'config') | path_join }}"
backups_dir: "{{ (base_dir, 'backups') | path_join }}"
gobackup_config: "{{ (base_dir, 'gobackup.yml') | path_join }}"
tasks: tasks:
- name: "Create user and environment" - name: "Create user and environment"
@@ -19,6 +23,8 @@
name: owner name: owner
vars: vars:
owner_name: "{{ app_user }}" owner_name: "{{ app_user }}"
owner_uid: "{{ app_owner_uid }}"
owner_gid: "{{ app_owner_gid }}"
owner_extra_groups: ["docker"] owner_extra_groups: ["docker"]
- name: "Create internal application directories" - name: "Create internal application directories"
@@ -29,7 +35,10 @@
group: "{{ app_user }}" group: "{{ app_user }}"
mode: "0700" mode: "0700"
loop: loop:
- "{{ base_dir }}"
- "{{ data_dir }}"
- "{{ config_dir }}" - "{{ config_dir }}"
- "{{ backups_dir }}"
- name: "Copy users file" - name: "Copy users file"
ansible.builtin.copy: ansible.builtin.copy:
@@ -39,7 +48,7 @@
group: "{{ app_user }}" group: "{{ app_user }}"
mode: "0600" mode: "0600"
- name: "Copy configuration files (templates)" - name: "Copy configuration file"
ansible.builtin.template: ansible.builtin.template:
src: "files/{{ app_name }}/configuration.template.yml" src: "files/{{ app_name }}/configuration.template.yml"
dest: "{{ (config_dir, 'configuration.yml') | path_join }}" dest: "{{ (config_dir, 'configuration.yml') | path_join }}"
@@ -47,9 +56,25 @@
group: "{{ app_user }}" group: "{{ app_user }}"
mode: "0600" mode: "0600"
- name: "Copy gobackup config"
ansible.builtin.template:
src: "files/{{ app_name }}/gobackup.template.yml"
dest: "{{ gobackup_config }}"
owner: "{{ app_user }}"
group: "{{ app_user }}"
mode: "0640"
- name: "Copy backup script"
ansible.builtin.template:
src: "files/{{ app_name }}/backup.template.sh"
dest: "{{ (base_dir, 'backup.sh') | path_join }}"
owner: "{{ app_user }}"
group: "{{ app_user }}"
mode: "0750"
- name: "Copy docker compose file" - name: "Copy docker compose file"
ansible.builtin.template: ansible.builtin.template:
src: "./files/{{ app_name }}/docker-compose.yml.j2" src: "./files/{{ app_name }}/docker-compose.template.yml"
dest: "{{ base_dir }}/docker-compose.yml" dest: "{{ base_dir }}/docker-compose.yml"
owner: "{{ app_user }}" owner: "{{ app_user }}"
group: "{{ app_user }}" group: "{{ app_user }}"
@@ -60,8 +85,12 @@
project_src: "{{ base_dir }}" project_src: "{{ base_dir }}"
state: "present" state: "present"
remove_orphans: true remove_orphans: true
tags:
- run-app
- name: "Restart application with docker compose" - name: "Restart application with docker compose"
community.docker.docker_compose_v2: community.docker.docker_compose_v2:
project_src: "{{ base_dir }}" project_src: "{{ base_dir }}"
state: "restarted" state: "restarted"
tags:
- run-app

View File

@@ -4,13 +4,39 @@
vars_files: vars_files:
- vars/secrets.yml - vars/secrets.yml
- vars/secrets.yml
vars: vars:
backup_config_dir: "/etc/backup"
backup_config_file: "{{ (backup_config_dir, 'config.toml') | path_join }}"
restic_shell_script: "{{ (bin_prefix, 'restic-shell.sh') | path_join }}" restic_shell_script: "{{ (bin_prefix, 'restic-shell.sh') | path_join }}"
backup_all_script: "{{ (bin_prefix, 'backup-all.py') | path_join }}" backup_all_script: "{{ (bin_prefix, 'backup-all.py') | path_join }}"
tasks: tasks:
- name: "Create backup config directory"
ansible.builtin.file:
path: "{{ backup_config_dir }}"
state: "directory"
owner: root
group: root
mode: "0755"
- name: "Create backup config file"
ansible.builtin.template:
src: "files/backups/config.template.toml"
dest: "{{ backup_config_file }}"
owner: root
group: root
mode: "0640"
- name: "Allow user to run the backup script without a password"
ansible.builtin.lineinfile:
path: /etc/sudoers
state: present
line: "{{ primary_user }} ALL=(ALL) NOPASSWD: {{ backup_all_script }}"
validate: /usr/sbin/visudo -cf %s # ВАЖНО: проверка синтаксиса перед сохранением
create: no # Файл уже должен существовать
- name: "Copy restic shell script" - name: "Copy restic shell script"
ansible.builtin.template: ansible.builtin.template:
src: "files/backups/restic-shell.sh.j2" src: "files/backups/restic-shell.sh.j2"
@@ -20,8 +46,8 @@
mode: "0700" mode: "0700"
- name: "Copy backup all script" - name: "Copy backup all script"
ansible.builtin.template: ansible.builtin.copy:
src: "files/backups/backup-all.template.py" src: "files/backups/backup-all.py"
dest: "{{ backup_all_script }}" dest: "{{ backup_all_script }}"
owner: root owner: root
group: root group: root

View File

@@ -3,13 +3,14 @@
hosts: all hosts: all
vars_files: vars_files:
- vars/ports.yml
- vars/secrets.yml - vars/secrets.yml
vars: vars:
app_name: "caddyproxy" app_name: "caddyproxy"
app_user: "{{ app_name }}" app_user: "{{ app_name }}"
base_dir: "/home/{{ app_user }}" app_owner_uid: 1010
app_owner_gid: 1011
base_dir: "{{ (application_dir, app_name) | path_join }}"
data_dir: "{{ (base_dir, 'data') | path_join }}" data_dir: "{{ (base_dir, 'data') | path_join }}"
config_dir: "{{ (base_dir, 'config') | path_join }}" config_dir: "{{ (base_dir, 'config') | path_join }}"
@@ -23,8 +24,9 @@
name: owner name: owner
vars: vars:
owner_name: "{{ app_user }}" owner_name: "{{ app_user }}"
owner_extra_groups: owner_uid: "{{ app_owner_uid }}"
- "docker" owner_gid: "{{ app_owner_gid }}"
owner_extra_groups: ["docker"]
- name: "Create internal application directories" - name: "Create internal application directories"
ansible.builtin.file: ansible.builtin.file:
@@ -34,6 +36,7 @@
group: "{{ app_user }}" group: "{{ app_user }}"
mode: "0770" mode: "0770"
loop: loop:
- "{{ base_dir }}"
- "{{ data_dir }}" - "{{ data_dir }}"
- "{{ config_dir }}" - "{{ config_dir }}"
- "{{ caddy_file_dir }}" - "{{ caddy_file_dir }}"
@@ -59,14 +62,20 @@
project_src: "{{ base_dir }}" project_src: "{{ base_dir }}"
state: "present" state: "present"
remove_orphans: true remove_orphans: true
tags:
- run-app
# - name: "Reload caddy" # - name: "Reload caddy"
# community.docker.docker_compose_v2_exec: # community.docker.docker_compose_v2_exec:
# project_src: '{{ base_dir }}' # project_src: '{{ base_dir }}'
# service: "{{ service_name }}" # service: "{{ service_name }}"
# command: caddy reload --config /etc/caddy/Caddyfile # command: caddy reload --config /etc/caddy/Caddyfile
# tags:
# - run-app
- name: "Restart application with docker compose" - name: "Restart application with docker compose"
community.docker.docker_compose_v2: community.docker.docker_compose_v2:
project_src: "{{ base_dir }}" project_src: "{{ base_dir }}"
state: "restarted" state: "restarted"
tags:
- run-app

View File

@@ -3,13 +3,12 @@
hosts: all hosts: all
vars_files: vars_files:
- vars/ports.yml
- vars/secrets.yml - vars/secrets.yml
tasks: tasks:
- name: "Install python docker lib from pip" # - name: "Install python docker lib from pip"
ansible.builtin.pip: # ansible.builtin.pip:
name: docker # name: docker
- name: "Install docker" - name: "Install docker"
ansible.builtin.import_role: ansible.builtin.import_role:
@@ -32,3 +31,10 @@
community.docker.docker_network: community.docker.docker_network:
name: "monitoring_network" name: "monitoring_network"
driver: "bridge" driver: "bridge"
- name: "Schedule docker image prune"
ansible.builtin.cron:
name: "docker image prune"
minute: "0"
hour: "3"
job: "/usr/bin/docker image prune -af"

View File

@@ -3,13 +3,14 @@
hosts: all hosts: all
vars_files: vars_files:
- vars/ports.yml
- vars/secrets.yml - vars/secrets.yml
vars: vars:
app_name: "dozzle" app_name: "dozzle"
app_user: "{{ app_name }}" app_user: "{{ app_name }}"
base_dir: "/home/{{ app_user }}" app_owner_uid: 1016
app_owner_gid: 1017
base_dir: "{{ (application_dir, app_name) | path_join }}"
tasks: tasks:
- name: "Create user and environment" - name: "Create user and environment"
@@ -17,11 +18,23 @@
name: owner name: owner
vars: vars:
owner_name: "{{ app_user }}" owner_name: "{{ app_user }}"
owner_uid: "{{ app_owner_uid }}"
owner_gid: "{{ app_owner_gid }}"
owner_extra_groups: ["docker"] owner_extra_groups: ["docker"]
- name: "Create internal application directories"
ansible.builtin.file:
path: "{{ item }}"
state: "directory"
owner: "{{ app_user }}"
group: "{{ app_user }}"
mode: "0770"
loop:
- "{{ base_dir }}"
- name: "Copy docker compose file" - name: "Copy docker compose file"
ansible.builtin.template: ansible.builtin.template:
src: "./files/{{ app_name }}/docker-compose.yml.j2" src: "./files/{{ app_name }}/docker-compose.template.yml"
dest: "{{ base_dir }}/docker-compose.yml" dest: "{{ base_dir }}/docker-compose.yml"
owner: "{{ app_user }}" owner: "{{ app_user }}"
group: "{{ app_user }}" group: "{{ app_user }}"
@@ -32,3 +45,5 @@
project_src: "{{ base_dir }}" project_src: "{{ base_dir }}"
state: "present" state: "present"
remove_orphans: true remove_orphans: true
tags:
- run-app

View File

@@ -3,7 +3,6 @@
hosts: all hosts: all
vars_files: vars_files:
- vars/ports.yml
- vars/secrets.yml - vars/secrets.yml
# See: https://github.com/zyedidia/eget/releases # See: https://github.com/zyedidia/eget/releases
@@ -22,25 +21,42 @@
- name: "Install rclone" - name: "Install rclone"
ansible.builtin.command: ansible.builtin.command:
cmd: "{{ eget_bin_path }} rclone/rclone --quiet --upgrade-only --to {{ eget_install_dir }} --asset zip --tag v1.71.1" cmd: >
changed_when: false {{ eget_bin_path }} rclone/rclone --quiet --upgrade-only --to {{ eget_install_dir }} --asset zip
--tag v1.72.0
- name: "Install btop"
ansible.builtin.command:
cmd: "{{ eget_bin_path }} aristocratos/btop --quiet --upgrade-only --to {{ eget_install_dir }} --tag v1.4.5"
changed_when: false changed_when: false
- name: "Install restic" - name: "Install restic"
ansible.builtin.command: ansible.builtin.command:
cmd: "{{ eget_bin_path }} restic/restic --quiet --upgrade-only --to {{ eget_install_dir }} --tag v0.18.1" cmd: >
{{ eget_bin_path }} restic/restic --quiet --upgrade-only --to {{ eget_install_dir }}
--tag v0.18.1
changed_when: false
- name: "Install btop"
ansible.builtin.command:
cmd: >
{{ eget_bin_path }} aristocratos/btop --quiet --upgrade-only --to {{ eget_install_dir }}
--tag v1.4.5
changed_when: false changed_when: false
- name: "Install gobackup" - name: "Install gobackup"
ansible.builtin.command: ansible.builtin.command:
cmd: "{{ eget_bin_path }} gobackup/gobackup --quiet --upgrade-only --to {{ eget_install_dir }} --tag v2.15.3" cmd: >
{{ eget_bin_path }} gobackup/gobackup --quiet --upgrade-only --to {{ eget_install_dir }}
--tag v2.17.0
changed_when: false changed_when: false
- name: "Install task" - name: "Install task"
ansible.builtin.command: ansible.builtin.command:
cmd: "{{ eget_bin_path }} go-task/task --quiet --upgrade-only --to {{ eget_install_dir }} --asset tar.gz --tag v3.45.4" cmd: >
{{ eget_bin_path }} go-task/task --quiet --upgrade-only --to {{ eget_install_dir }} --asset tar.gz
--tag v3.45.5
changed_when: false
- name: 'Install dust'
ansible.builtin.command:
cmd: >
{{ bin_prefix }}/eget bootandy/dust --quiet --upgrade-only --to {{ bin_prefix }} --asset gnu
--tag v1.2.3
changed_when: false changed_when: false

View File

@@ -3,13 +3,14 @@
hosts: all hosts: all
vars_files: vars_files:
- vars/ports.yml
- vars/secrets.yml - vars/secrets.yml
vars: vars:
app_name: "gitea" app_name: "gitea"
app_user: "{{ app_name }}" app_user: "{{ app_name }}"
base_dir: "/home/{{ app_user }}" app_owner_uid: 1005
app_owner_gid: 1006
base_dir: "{{ (application_dir, app_name) | path_join }}"
data_dir: "{{ (base_dir, 'data') | path_join }}" data_dir: "{{ (base_dir, 'data') | path_join }}"
backups_dir: "{{ (base_dir, 'backups') | path_join }}" backups_dir: "{{ (base_dir, 'backups') | path_join }}"
@@ -19,10 +20,9 @@
name: owner name: owner
vars: vars:
owner_name: "{{ app_user }}" owner_name: "{{ app_user }}"
owner_extra_groups: owner_uid: "{{ app_owner_uid }}"
- "docker" owner_gid: "{{ app_owner_gid }}"
owner_ssh_keys: owner_extra_groups: ["docker"]
- "{{ lookup('file', 'files/av_id_rsa.pub') }}"
- name: "Create internal application directories" - name: "Create internal application directories"
ansible.builtin.file: ansible.builtin.file:
@@ -32,6 +32,7 @@
group: "{{ app_user }}" group: "{{ app_user }}"
mode: "0770" mode: "0770"
loop: loop:
- "{{ base_dir }}"
- "{{ data_dir }}" - "{{ data_dir }}"
- "{{ backups_dir }}" - "{{ backups_dir }}"
@@ -56,3 +57,5 @@
project_src: "{{ base_dir }}" project_src: "{{ base_dir }}"
state: "present" state: "present"
remove_orphans: true remove_orphans: true
tags:
- run-app

View File

@@ -3,14 +3,17 @@
hosts: all hosts: all
vars_files: vars_files:
- vars/ports.yml
- vars/secrets.yml - vars/secrets.yml
vars: vars:
app_name: "gramps" app_name: "gramps"
app_user: "{{ app_name }}" app_user: "{{ app_name }}"
base_dir: "/home/{{ app_user }}" app_owner_uid: 1009
app_owner_gid: 1010
base_dir: "{{ (application_dir, app_name) | path_join }}"
data_dir: "{{ (base_dir, 'data') | path_join }}" data_dir: "{{ (base_dir, 'data') | path_join }}"
media_dir: "{{ (base_dir, 'media') | path_join }}"
cache_dir: "{{ (base_dir, 'cache') | path_join }}"
backups_dir: "{{ (base_dir, 'backups') | path_join }}" backups_dir: "{{ (base_dir, 'backups') | path_join }}"
gobackup_config: "{{ (base_dir, 'gobackup.yml') | path_join }}" gobackup_config: "{{ (base_dir, 'gobackup.yml') | path_join }}"
@@ -20,10 +23,9 @@
name: owner name: owner
vars: vars:
owner_name: "{{ app_user }}" owner_name: "{{ app_user }}"
owner_extra_groups: owner_uid: "{{ app_owner_uid }}"
- "docker" owner_gid: "{{ app_owner_gid }}"
owner_ssh_keys: owner_extra_groups: ["docker"]
- "{{ lookup('file', 'files/av_id_rsa.pub') }}"
- name: "Create application internal directories" - name: "Create application internal directories"
ansible.builtin.file: ansible.builtin.file:
@@ -33,12 +35,15 @@
group: "{{ app_user }}" group: "{{ app_user }}"
mode: "0750" mode: "0750"
loop: loop:
- "{{ base_dir }}"
- "{{ data_dir }}" - "{{ data_dir }}"
- "{{ media_dir }}"
- "{{ cache_dir }}"
- "{{ backups_dir }}" - "{{ backups_dir }}"
- name: "Copy gobackup config" - name: "Copy gobackup config"
ansible.builtin.template: ansible.builtin.template:
src: "./files/{{ app_name }}/gobackup.yml.j2" src: "./files/{{ app_name }}/gobackup.template.yml"
dest: "{{ gobackup_config }}" dest: "{{ gobackup_config }}"
owner: "{{ app_user }}" owner: "{{ app_user }}"
group: "{{ app_user }}" group: "{{ app_user }}"
@@ -46,15 +51,36 @@
- name: "Copy backup script" - name: "Copy backup script"
ansible.builtin.template: ansible.builtin.template:
src: "files/{{ app_name }}/backup.sh.j2" src: "files/{{ app_name }}/backup.template.sh"
dest: "{{ base_dir }}/backup.sh" dest: "{{ base_dir }}/backup.sh"
owner: "{{ app_user }}" owner: "{{ app_user }}"
group: "{{ app_user }}" group: "{{ app_user }}"
mode: "0750" mode: "0750"
- name: "Create backup targets file"
ansible.builtin.lineinfile:
path: "{{ base_dir }}/backup-targets"
line: "{{ item }}"
create: true
owner: "{{ app_user }}"
group: "{{ app_user }}"
mode: "0750"
loop:
- "{{ data_dir }}"
- "{{ media_dir }}"
- "{{ backups_dir }}"
- name: "Copy rename script"
ansible.builtin.copy:
src: "files/{{ app_name }}/gramps_rename.py"
dest: "{{ base_dir }}/gramps_rename.py"
owner: "{{ app_user }}"
group: "{{ app_user }}"
mode: "0750"
- name: "Copy docker compose file" - name: "Copy docker compose file"
ansible.builtin.template: ansible.builtin.template:
src: "./files/{{ app_name }}/docker-compose.yml.j2" src: "./files/{{ app_name }}/docker-compose.template.yml"
dest: "{{ base_dir }}/docker-compose.yml" dest: "{{ base_dir }}/docker-compose.yml"
owner: "{{ app_user }}" owner: "{{ app_user }}"
group: "{{ app_user }}" group: "{{ app_user }}"
@@ -65,3 +91,5 @@
project_src: "{{ base_dir }}" project_src: "{{ base_dir }}"
state: "present" state: "present"
remove_orphans: true remove_orphans: true
tags:
- run-app

View File

@@ -0,0 +1,20 @@
---
- name: "Upload local homepage images to registry"
hosts: all
gather_facts: false
vars_files:
- vars/secrets.yml
- vars/homepage.yml
- vars/homepage.images.yml
tasks:
- name: "Push web service image to remote registry"
community.docker.docker_image:
state: present
source: local
name: "{{ homepage_nginx_image }}"
repository: "{{ registry_homepage_nginx_image }}"
push: true
delegate_to: 127.0.0.1

View File

@@ -1,60 +1,39 @@
--- ---
# Play 1: Setup environment for the application - name: "Setup and deploy homepage service"
- name: "Setup environment for homepage application"
hosts: all hosts: all
vars_files: vars_files:
- vars/ports.yml
- vars/secrets.yml - vars/secrets.yml
- vars/homepage.yml - vars/homepage.yml
tags: - vars/homepage.images.yml
- setup
tasks: tasks:
- name: "Create user and environment" - name: "Create user and environment"
ansible.builtin.import_role: ansible.builtin.import_role:
name: owner name: owner
vars: vars:
owner_name: "{{ app_user }}" owner_name: "{{ app_user }}"
owner_extra_groups: owner_uid: "{{ app_owner_uid }}"
- "docker" owner_gid: "{{ app_owner_gid }}"
owner_ssh_keys: owner_extra_groups: ["docker"]
- "{{ lookup('file', 'files/av_id_rsa.pub') }}"
- name: "Create application internal directories"
ansible.builtin.file:
path: "{{ item }}"
state: "directory"
owner: "{{ app_user }}"
group: "{{ app_user }}"
mode: "0750"
loop:
- "{{ base_dir }}"
- name: "Login to yandex docker registry." - name: "Login to yandex docker registry."
ansible.builtin.script: ansible.builtin.script:
cmd: "files/yandex-docker-registry-auth.sh" cmd: "files/yandex-docker-registry-auth.sh"
# Play 2: Deploy the application
- name: "Deploy homepage application"
hosts: all
vars_files:
- vars/ports.yml
- vars/secrets.yml
- vars/homepage.yml
tags:
- deploy
tasks:
- name: "Check is web service image passed"
ansible.builtin.assert:
that:
- "homepage_web_image is defined"
fail_msg: 'You must pass variable "homepage_web_image"'
- name: "Create full image name with container registry"
ansible.builtin.set_fact:
registry_homepage_web_image: "{{ (docker_registry_prefix, homepage_web_image) | path_join }}"
- name: "Push web service image to remote registry"
community.docker.docker_image:
state: present
source: local
name: "{{ homepage_web_image }}"
repository: "{{ registry_homepage_web_image }}"
push: true
delegate_to: 127.0.0.1
- name: "Copy docker compose file" - name: "Copy docker compose file"
ansible.builtin.template: ansible.builtin.template:
src: "./files/{{ app_name }}/docker-compose.yml.j2" src: "./files/{{ app_name }}/docker-compose.template.yml"
dest: "{{ base_dir }}/docker-compose.yml" dest: "{{ base_dir }}/docker-compose.yml"
owner: "{{ app_user }}" owner: "{{ app_user }}"
group: "{{ app_user }}" group: "{{ app_user }}"
@@ -65,3 +44,5 @@
project_src: "{{ base_dir }}" project_src: "{{ base_dir }}"
state: "present" state: "present"
remove_orphans: true remove_orphans: true
tags:
- run-app

82
playbook-memos.yml Normal file
View File

@@ -0,0 +1,82 @@
---
- name: "Configure memos application"
hosts: all
vars_files:
- vars/secrets.yml
vars:
app_name: "memos"
app_user: "{{ app_name }}"
app_owner_uid: 1019
app_owner_gid: 1020
base_dir: "{{ (application_dir, app_name) | path_join }}"
data_dir: "{{ (base_dir, 'data') | path_join }}"
backups_dir: "{{ (base_dir, 'backups') | path_join }}"
gobackup_config: "{{ (base_dir, 'gobackup.yml') | path_join }}"
tasks:
- name: "Create user and environment"
ansible.builtin.import_role:
name: owner
vars:
owner_name: "{{ app_user }}"
owner_uid: "{{ app_owner_uid }}"
owner_gid: "{{ app_owner_gid }}"
owner_extra_groups: ["docker"]
- name: "Create application internal directories"
ansible.builtin.file:
path: "{{ item }}"
state: "directory"
owner: "{{ app_user }}"
group: "{{ app_user }}"
mode: "0750"
loop:
- "{{ base_dir }}"
- "{{ data_dir }}"
- "{{ backups_dir }}"
- name: "Copy gobackup config"
ansible.builtin.template:
src: "./files/{{ app_name }}/gobackup.yml.j2"
dest: "{{ gobackup_config }}"
owner: "{{ app_user }}"
group: "{{ app_user }}"
mode: "0640"
- name: "Copy backup script"
ansible.builtin.template:
src: "files/{{ app_name }}/backup.sh.j2"
dest: "{{ base_dir }}/backup.sh"
owner: "{{ app_user }}"
group: "{{ app_user }}"
mode: "0750"
- name: "Create backup targets file"
ansible.builtin.lineinfile:
path: "{{ base_dir }}/backup-targets"
line: "{{ item }}"
create: true
owner: "{{ app_user }}"
group: "{{ app_user }}"
mode: "0750"
loop:
- "{{ data_dir }}"
- "{{ backups_dir }}"
- name: "Copy docker compose file"
ansible.builtin.template:
src: "./files/{{ app_name }}/docker-compose.template.yml"
dest: "{{ base_dir }}/docker-compose.yml"
owner: "{{ app_user }}"
group: "{{ app_user }}"
mode: "0640"
- name: "Run application with docker compose"
community.docker.docker_compose_v2:
project_src: "{{ base_dir }}"
state: "present"
remove_orphans: true
tags:
- run-app

View File

@@ -3,13 +3,14 @@
hosts: all hosts: all
vars_files: vars_files:
- vars/ports.yml
- vars/secrets.yml - vars/secrets.yml
vars: vars:
app_name: "miniflux" app_name: "miniflux"
app_user: "{{ app_name }}" app_user: "{{ app_name }}"
base_dir: "/home/{{ app_user }}" app_owner_uid: 1013
app_owner_gid: 1014
base_dir: "{{ (application_dir, app_name) | path_join }}"
data_dir: "{{ (base_dir, 'data') | path_join }}" data_dir: "{{ (base_dir, 'data') | path_join }}"
secrets_dir: "{{ (base_dir, 'secrets') | path_join }}" secrets_dir: "{{ (base_dir, 'secrets') | path_join }}"
postgres_data_dir: "{{ (base_dir, 'data', 'postgres') | path_join }}" postgres_data_dir: "{{ (base_dir, 'data', 'postgres') | path_join }}"
@@ -21,6 +22,8 @@
name: owner name: owner
vars: vars:
owner_name: "{{ app_user }}" owner_name: "{{ app_user }}"
owner_uid: "{{ app_owner_uid }}"
owner_gid: "{{ app_owner_gid }}"
owner_extra_groups: ["docker"] owner_extra_groups: ["docker"]
- name: "Create internal directories" - name: "Create internal directories"
@@ -31,6 +34,10 @@
group: "{{ app_user }}" group: "{{ app_user }}"
mode: "0770" mode: "0770"
loop: loop:
- "{{ base_dir }}"
- "{{ data_dir }}"
- "{{ secrets_dir }}"
- "{{ postgres_data_dir }}"
- "{{ postgres_backups_dir }}" - "{{ postgres_backups_dir }}"
- name: "Copy secrets" - name: "Copy secrets"
@@ -50,7 +57,7 @@
- name: "Copy docker compose file" - name: "Copy docker compose file"
ansible.builtin.template: ansible.builtin.template:
src: "./files/{{ app_name }}/docker-compose.yml.j2" src: "./files/{{ app_name }}/docker-compose.template.yml"
dest: "{{ base_dir }}/docker-compose.yml" dest: "{{ base_dir }}/docker-compose.yml"
owner: "{{ app_user }}" owner: "{{ app_user }}"
group: "{{ app_user }}" group: "{{ app_user }}"
@@ -58,7 +65,7 @@
- name: "Copy backup script" - name: "Copy backup script"
ansible.builtin.template: ansible.builtin.template:
src: "./files/{{ app_name }}/backup.sh.j2" src: "./files/{{ app_name }}/backup.template.sh"
dest: "{{ base_dir }}/backup.sh" dest: "{{ base_dir }}/backup.sh"
owner: "{{ app_user }}" owner: "{{ app_user }}"
group: "{{ app_user }}" group: "{{ app_user }}"
@@ -70,3 +77,5 @@
state: "present" state: "present"
recreate: "always" recreate: "always"
remove_orphans: true remove_orphans: true
tags:
- run-app

View File

@@ -3,13 +3,14 @@
hosts: all hosts: all
vars_files: vars_files:
- vars/ports.yml
- vars/secrets.yml - vars/secrets.yml
vars: vars:
app_name: "netdata" app_name: "netdata"
app_user: "{{ app_name }}" app_user: "{{ app_name }}"
base_dir: "/home/{{ app_user }}" app_owner_uid: 1012
app_owner_gid: 1013
base_dir: "{{ (application_dir, app_name) | path_join }}"
config_dir: "{{ (base_dir, 'config') | path_join }}" config_dir: "{{ (base_dir, 'config') | path_join }}"
config_go_d_dir: "{{ (config_dir, 'go.d') | path_join }}" config_go_d_dir: "{{ (config_dir, 'go.d') | path_join }}"
data_dir: "{{ (base_dir, 'data') | path_join }}" data_dir: "{{ (base_dir, 'data') | path_join }}"
@@ -20,6 +21,8 @@
name: owner name: owner
vars: vars:
owner_name: "{{ app_user }}" owner_name: "{{ app_user }}"
owner_uid: "{{ app_owner_uid }}"
owner_gid: "{{ app_owner_gid }}"
owner_extra_groups: ["docker"] owner_extra_groups: ["docker"]
- name: "Create internal application directories" - name: "Create internal application directories"
@@ -30,13 +33,14 @@
group: "{{ app_user }}" group: "{{ app_user }}"
mode: "0770" mode: "0770"
loop: loop:
- "{{ base_dir }}"
- "{{ data_dir }}"
- "{{ config_dir }}" - "{{ config_dir }}"
- "{{ config_go_d_dir }}" - "{{ config_go_d_dir }}"
- "{{ data_dir }}"
- name: "Copy netdata config file" - name: "Copy netdata config file"
ansible.builtin.template: ansible.builtin.template:
src: "files/{{ app_name }}/netdata.conf.j2" src: "files/{{ app_name }}/netdata.template.conf"
dest: "{{ config_dir }}/netdata.conf" dest: "{{ config_dir }}/netdata.conf"
owner: "{{ app_user }}" owner: "{{ app_user }}"
group: "{{ app_user }}" group: "{{ app_user }}"
@@ -82,7 +86,7 @@
- name: "Copy docker compose file" - name: "Copy docker compose file"
ansible.builtin.template: ansible.builtin.template:
src: "./files/{{ app_name }}/docker-compose.yml.j2" src: "./files/{{ app_name }}/docker-compose.template.yml"
dest: "{{ base_dir }}/docker-compose.yml" dest: "{{ base_dir }}/docker-compose.yml"
owner: "{{ app_user }}" owner: "{{ app_user }}"
group: "{{ app_user }}" group: "{{ app_user }}"
@@ -93,8 +97,12 @@
project_src: "{{ base_dir }}" project_src: "{{ base_dir }}"
state: "present" state: "present"
remove_orphans: true remove_orphans: true
tags:
- run-app
- name: "Restart application with docker compose" - name: "Restart application with docker compose"
community.docker.docker_compose_v2: community.docker.docker_compose_v2:
project_src: "{{ base_dir }}" project_src: "{{ base_dir }}"
state: "restarted" state: "restarted"
tags:
- run-app

View File

@@ -3,13 +3,14 @@
hosts: all hosts: all
vars_files: vars_files:
- vars/ports.yml
- vars/secrets.yml - vars/secrets.yml
vars: vars:
app_name: "outline" app_name: "outline"
app_user: "{{ app_name }}" app_user: "{{ app_name }}"
base_dir: "/home/{{ app_user }}" app_owner_uid: 1007
app_owner_gid: 1008
base_dir: "{{ (application_dir, app_name) | path_join }}"
data_dir: "{{ (base_dir, 'data') | path_join }}" data_dir: "{{ (base_dir, 'data') | path_join }}"
postgres_data_dir: "{{ (base_dir, 'data', 'postgres') | path_join }}" postgres_data_dir: "{{ (base_dir, 'data', 'postgres') | path_join }}"
postgres_backups_dir: "{{ (base_dir, 'backups', 'postgres') | path_join }}" postgres_backups_dir: "{{ (base_dir, 'backups', 'postgres') | path_join }}"
@@ -20,10 +21,9 @@
name: owner name: owner
vars: vars:
owner_name: "{{ app_user }}" owner_name: "{{ app_user }}"
owner_extra_groups: owner_uid: "{{ app_owner_uid }}"
- "docker" owner_gid: "{{ app_owner_gid }}"
owner_ssh_keys: owner_extra_groups: ["docker"]
- "{{ lookup('file', 'files/av_id_rsa.pub') }}"
- name: "Create internal directories" - name: "Create internal directories"
ansible.builtin.file: ansible.builtin.file:
@@ -33,11 +33,14 @@
group: "{{ app_user }}" group: "{{ app_user }}"
mode: "0770" mode: "0770"
loop: loop:
- "{{ base_dir }}"
- "{{ data_dir }}"
- "{{ postgres_data_dir }}"
- "{{ postgres_backups_dir }}" - "{{ postgres_backups_dir }}"
- name: "Copy docker compose file" - name: "Copy docker compose file"
ansible.builtin.template: ansible.builtin.template:
src: "./files/{{ app_name }}/docker-compose.yml.j2" src: "./files/{{ app_name }}/docker-compose.template.yml"
dest: "{{ base_dir }}/docker-compose.yml" dest: "{{ base_dir }}/docker-compose.yml"
owner: "{{ app_user }}" owner: "{{ app_user }}"
group: "{{ app_user }}" group: "{{ app_user }}"
@@ -45,7 +48,7 @@
- name: "Copy backup script" - name: "Copy backup script"
ansible.builtin.template: ansible.builtin.template:
src: "./files/{{ app_name }}/backup.sh.j2" src: "./files/{{ app_name }}/backup.template.sh"
dest: "{{ base_dir }}/backup.sh" dest: "{{ base_dir }}/backup.sh"
owner: "{{ app_user }}" owner: "{{ app_user }}"
group: "{{ app_user }}" group: "{{ app_user }}"
@@ -56,3 +59,5 @@
project_src: "{{ base_dir }}" project_src: "{{ base_dir }}"
state: "present" state: "present"
remove_orphans: true remove_orphans: true
tags:
- run-app

View File

@@ -3,7 +3,6 @@
hosts: all hosts: all
vars_files: vars_files:
- vars/ports.yml
- vars/secrets.yml - vars/secrets.yml
vars: vars:

View File

@@ -3,13 +3,14 @@
hosts: all hosts: all
vars_files: vars_files:
- vars/ports.yml
- vars/secrets.yml - vars/secrets.yml
vars: vars:
app_name: "rssbridge" app_name: "rssbridge"
app_user: "{{ app_name }}" app_user: "{{ app_name }}"
base_dir: "/home/{{ app_user }}" app_owner_uid: 1014
app_owner_gid: 1015
base_dir: "{{ (application_dir, app_name) | path_join }}"
tasks: tasks:
- name: "Create user and environment" - name: "Create user and environment"
@@ -17,8 +18,20 @@
name: owner name: owner
vars: vars:
owner_name: "{{ app_user }}" owner_name: "{{ app_user }}"
owner_uid: "{{ app_owner_uid }}"
owner_gid: "{{ app_owner_gid }}"
owner_extra_groups: ["docker"] owner_extra_groups: ["docker"]
- name: "Create internal application directories"
ansible.builtin.file:
path: "{{ item }}"
state: "directory"
owner: "{{ app_user }}"
group: "{{ app_user }}"
mode: "0770"
loop:
- "{{ base_dir }}"
- name: "Copy docker compose file" - name: "Copy docker compose file"
ansible.builtin.template: ansible.builtin.template:
src: "./files/{{ app_name }}/docker-compose.yml.j2" src: "./files/{{ app_name }}/docker-compose.yml.j2"
@@ -32,3 +45,5 @@
project_src: "{{ base_dir }}" project_src: "{{ base_dir }}"
state: "present" state: "present"
remove_orphans: true remove_orphans: true
tags:
- run-app

View File

@@ -3,7 +3,6 @@
hosts: all hosts: all
vars_files: vars_files:
- vars/ports.yml
- vars/secrets.yml - vars/secrets.yml
vars: vars:
@@ -40,3 +39,20 @@
owner: root owner: root
group: root group: root
mode: "0755" mode: "0755"
- name: 'Create directory for mount'
ansible.builtin.file:
path: '/mnt/applications'
state: 'directory'
mode: '0755'
tags:
- mount-storage
- name: 'Mount external storages'
ansible.posix.mount:
path: '/mnt/applications'
src: 'UUID=3942bffd-8328-4536-8e88-07926fb17d17'
fstype: ext4
state: mounted
tags:
- mount-storage

View File

@@ -0,0 +1,20 @@
---
- name: "Upload local transcriber images to registry"
hosts: all
gather_facts: false
vars_files:
- vars/secrets.yml
- vars/transcriber.yml
- vars/transcriber.images.yml
tasks:
- name: "Push web service image to remote registry"
community.docker.docker_image:
state: present
source: local
name: "{{ transcriber_image }}"
repository: "{{ registry_transcriber_image }}"
push: true
delegate_to: 127.0.0.1

View File

@@ -3,25 +3,9 @@
hosts: all hosts: all
vars_files: vars_files:
- vars/ports.yml
- vars/secrets.yml - vars/secrets.yml
- vars/transcriber.yml
- vars/transcriber.images.yml
vars:
app_name: "transcriber"
app_user: "{{ app_name }}"
base_dir: "/home/{{ app_user }}"
config_dir: "{{ (base_dir, 'config') | path_join }}"
config_file: "{{ (config_dir, 'config.toml') | path_join }}"
data_dir: "{{ (base_dir, 'data') | path_join }}"
backups_dir: "{{ (base_dir, 'backups') | path_join }}"
docker_registry_prefix: "cr.yandex/crplfk0168i4o8kd7ade"
# transcriber_image: "{{ transcriber_image | default(omit) }}"
tasks: tasks:
- name: "Create user and environment" - name: "Create user and environment"
@@ -29,6 +13,8 @@
name: owner name: owner
vars: vars:
owner_name: "{{ app_user }}" owner_name: "{{ app_user }}"
owner_uid: "{{ app_owner_uid }}"
owner_gid: "{{ app_owner_gid }}"
owner_extra_groups: ["docker"] owner_extra_groups: ["docker"]
- name: "Create application internal directories" - name: "Create application internal directories"
@@ -39,6 +25,7 @@
group: "{{ app_user }}" group: "{{ app_user }}"
mode: "0750" mode: "0750"
loop: loop:
- "{{ base_dir }}"
- "{{ config_dir }}" - "{{ config_dir }}"
- "{{ data_dir }}" - "{{ data_dir }}"
- "{{ backups_dir }}" - "{{ backups_dir }}"
@@ -55,31 +42,9 @@
ansible.builtin.script: ansible.builtin.script:
cmd: "files/yandex-docker-registry-auth.sh" cmd: "files/yandex-docker-registry-auth.sh"
- name: "Deploy service"
when: transcriber_image is defined
block:
# - name: "Check is web service image passed"
# ansible.builtin.assert:
# that:
# - "transcriber_image is defined"
# fail_msg: 'You must pass variable "transcriber_image"'
- name: "Create full image name with container registry"
ansible.builtin.set_fact:
registry_transcriber_image: "{{ (docker_registry_prefix, transcriber_image) | path_join }}"
- name: "Push web service image to remote registry"
community.docker.docker_image:
state: present
source: local
name: "{{ transcriber_image }}"
repository: "{{ registry_transcriber_image }}"
push: true
delegate_to: 127.0.0.1
- name: "Copy docker compose file" - name: "Copy docker compose file"
ansible.builtin.template: ansible.builtin.template:
src: "./files/{{ app_name }}/docker-compose.yml.j2" src: "./files/{{ app_name }}/docker-compose.template.yml"
dest: "{{ base_dir }}/docker-compose.yml" dest: "{{ base_dir }}/docker-compose.yml"
owner: "{{ app_user }}" owner: "{{ app_user }}"
group: "{{ app_user }}" group: "{{ app_user }}"
@@ -90,3 +55,5 @@
project_src: "{{ base_dir }}" project_src: "{{ base_dir }}"
state: "present" state: "present"
remove_orphans: true remove_orphans: true
tags:
- run-app

View File

@@ -3,7 +3,6 @@
hosts: all hosts: all
vars_files: vars_files:
- vars/ports.yml
- vars/secrets.yml - vars/secrets.yml
tasks: tasks:

View File

@@ -3,13 +3,14 @@
hosts: all hosts: all
vars_files: vars_files:
- vars/ports.yml
- vars/secrets.yml - vars/secrets.yml
vars: vars:
app_name: "wakapi" app_name: "wakapi"
app_user: "{{ app_name }}" app_user: "{{ app_name }}"
base_dir: "/home/{{ app_user }}" app_owner_uid: 1015
app_owner_gid: 1016
base_dir: "{{ (application_dir, app_name) | path_join }}"
data_dir: "{{ (base_dir, 'data') | path_join }}" data_dir: "{{ (base_dir, 'data') | path_join }}"
backups_dir: "{{ (base_dir, 'backups') | path_join }}" backups_dir: "{{ (base_dir, 'backups') | path_join }}"
gobackup_config: "{{ (base_dir, 'gobackup.yml') | path_join }}" gobackup_config: "{{ (base_dir, 'gobackup.yml') | path_join }}"
@@ -20,6 +21,8 @@
name: owner name: owner
vars: vars:
owner_name: "{{ app_user }}" owner_name: "{{ app_user }}"
owner_uid: "{{ app_owner_uid }}"
owner_gid: "{{ app_owner_gid }}"
owner_extra_groups: ["docker"] owner_extra_groups: ["docker"]
- name: "Create application internal directories" - name: "Create application internal directories"
@@ -30,6 +33,7 @@
group: "{{ app_user }}" group: "{{ app_user }}"
mode: "0750" mode: "0750"
loop: loop:
- "{{ base_dir }}"
- "{{ data_dir }}" - "{{ data_dir }}"
- "{{ backups_dir }}" - "{{ backups_dir }}"
@@ -62,3 +66,5 @@
project_src: "{{ base_dir }}" project_src: "{{ base_dir }}"
state: "present" state: "present"
remove_orphans: true remove_orphans: true
tags:
- run-app

View File

@@ -3,13 +3,14 @@
hosts: all hosts: all
vars_files: vars_files:
- vars/ports.yml
- vars/secrets.yml - vars/secrets.yml
vars: vars:
app_name: "wanderer" app_name: "wanderer"
app_user: "{{ app_name }}" app_user: "{{ app_name }}"
base_dir: "/home/{{ app_user }}" app_owner_uid: 1018
app_owner_gid: 1019
base_dir: "{{ (application_dir, app_name) | path_join }}"
data_dir: "{{ (base_dir, 'data') | path_join }}" data_dir: "{{ (base_dir, 'data') | path_join }}"
backups_dir: "{{ (base_dir, 'backups') | path_join }}" backups_dir: "{{ (base_dir, 'backups') | path_join }}"
gobackup_config: "{{ (base_dir, 'gobackup.yml') | path_join }}" gobackup_config: "{{ (base_dir, 'gobackup.yml') | path_join }}"
@@ -23,6 +24,8 @@
name: owner name: owner
vars: vars:
owner_name: "{{ app_user }}" owner_name: "{{ app_user }}"
owner_uid: "{{ app_owner_uid }}"
owner_gid: "{{ app_owner_gid }}"
owner_extra_groups: ["docker"] owner_extra_groups: ["docker"]
- name: "Create application internal directories" - name: "Create application internal directories"
@@ -33,6 +36,7 @@
group: "{{ app_user }}" group: "{{ app_user }}"
mode: "0750" mode: "0750"
loop: loop:
- "{{ base_dir }}"
- "{{ data_dir }}" - "{{ data_dir }}"
- "{{ (data_dir, 'pb_data') | path_join }}" - "{{ (data_dir, 'pb_data') | path_join }}"
- "{{ (data_dir, 'uploads') | path_join }}" - "{{ (data_dir, 'uploads') | path_join }}"
@@ -47,13 +51,29 @@
group: "{{ app_user }}" group: "{{ app_user }}"
mode: "0640" mode: "0640"
- name: "Copy backup script" # - name: "Copy backup script"
ansible.builtin.template: # ansible.builtin.template:
src: "files/{{ app_name }}/backup.template.sh" # src: "files/{{ app_name }}/backup.template.sh"
# dest: "{{ base_dir }}/backup.sh"
# owner: "{{ app_user }}"
# group: "{{ app_user }}"
# mode: "0750"
- name: "Disable backup script"
ansible.builtin.file:
dest: "{{ base_dir }}/backup.sh" dest: "{{ base_dir }}/backup.sh"
state: absent
- name: "Create backup targets file"
ansible.builtin.lineinfile:
path: "{{ base_dir }}/backup-targets"
line: "{{ item }}"
create: true
owner: "{{ app_user }}" owner: "{{ app_user }}"
group: "{{ app_user }}" group: "{{ app_user }}"
mode: "0750" mode: "0750"
loop:
- "{{ data_dir }}"
- name: "Copy docker compose file" - name: "Copy docker compose file"
ansible.builtin.template: ansible.builtin.template:
@@ -68,3 +88,5 @@
project_src: "{{ base_dir }}" project_src: "{{ base_dir }}"
state: "present" state: "present"
remove_orphans: true remove_orphans: true
tags:
- run-app

View File

@@ -1,7 +1,7 @@
--- ---
# defaults file for eget # defaults file for eget
eget_version: "1.3.4" eget_version: "1.3.4"
eget_download_url: "https://github.com/zyedidia/eget/releases/download/v{{ eget_version }}/eget-{{ eget_version }}-linux_amd64.tar.gz" eget_download_url: "https://github.com/zyedidia/eget/releases/download/v{{ eget_version }}/eget-{{ eget_version }}-linux_amd64.tar.gz" # yamllint disable-line rule:line-length
eget_install_path: "/usr/bin/eget" eget_install_path: "/usr/bin/eget"
eget_download_dest: '/tmp/{{ eget_download_url | split("/") | last }}' eget_download_dest: '/tmp/{{ eget_download_url | split("/") | last }}'

View File

@@ -1,6 +1,8 @@
--- ---
owner_name: "" owner_name: ""
owner_uid: 0
owner_group: "{{ owner_name }}" owner_group: "{{ owner_name }}"
owner_gid: "{{ owner_uid }}"
owner_extra_groups: [] owner_extra_groups: []
owner_ssh_keys: [] owner_ssh_keys: []
owner_env: {} owner_env: {}

View File

@@ -4,9 +4,15 @@
msg: You must set owner name. msg: You must set owner name.
when: not owner_name when: not owner_name
- name: 'Check app requirements for user "{{ owner_name }}".'
ansible.builtin.fail:
msg: You must set owner uid.
when: not owner_uid
- name: 'Create group "{{ owner_group }}".' - name: 'Create group "{{ owner_group }}".'
ansible.builtin.group: ansible.builtin.group:
name: "{{ owner_group }}" name: "{{ owner_group }}"
gid: "{{ owner_gid }}"
state: present state: present
- name: 'Create user "{{ owner_name }}".' - name: 'Create user "{{ owner_name }}".'
@@ -14,6 +20,7 @@
name: "{{ owner_name }}" name: "{{ owner_name }}"
group: "{{ owner_group }}" group: "{{ owner_group }}"
groups: "{{ owner_extra_groups }}" groups: "{{ owner_extra_groups }}"
uid: "{{ owner_uid }}"
shell: /bin/bash shell: /bin/bash
register: user_create_result register: user_create_result

2
vars/homepage.images.yml Normal file
View File

@@ -0,0 +1,2 @@
---
homepage_nginx_image: "homepage-nginx:f797e17-1761204003"

View File

@@ -1,7 +1,10 @@
--- ---
app_name: "homepage" app_name: "homepage"
app_user: "{{ app_name }}" app_user: "{{ app_name }}"
base_dir: "/home/{{ app_user }}" app_owner_uid: 1008
app_owner_gid: 1009
base_dir: "{{ (application_dir, app_name) | path_join }}"
docker_registry_prefix: "cr.yandex/crplfk0168i4o8kd7ade" docker_registry_prefix: "cr.yandex/crplfk0168i4o8kd7ade"
homepage_web_image: "{{ homepage_web_image | default(omit) }}" # Registry images
registry_homepage_nginx_image: "{{ (docker_registry_prefix, homepage_nginx_image) | path_join }}"

View File

@@ -1,7 +0,0 @@
---
base_port: 41080
homepage_port: "{{ base_port + 3 }}"
netdata_port: "{{ base_port + 4 }}"
gitea_port: "{{ base_port + 8 }}"
outline_port: "{{ base_port + 10 }}"
gramps_port: "{{ base_port + 12 }}"

View File

@@ -1,148 +1,162 @@
$ANSIBLE_VAULT;1.1;AES256 $ANSIBLE_VAULT;1.1;AES256
39656637656164376462636533333263363535313061306531333033313833363665623365633537 66333736306664316131646233393639356436323832386638353237393761386631303639396432
6463613166386138623236623038653539353636623837380a626364633339333465323538656336 6266616434663638306637356435393564303633613332640a663366336135373061396239653065
34613732636365633761663565323535323565313533636164383566663661326337323433363662 32343339313734346261363461633735383538613033656138383835653661633334316533613738
3964323430636531340a393365303732656238316632646333313236386664373834383265326539 3730656661393466370a653863353234323739346630323534333334306432646636383664313865
66363366303430303134306437626230626566666539616431663461306561383335346564656262 39666133623032393638633239653665376336626638303334626164376663626666393439346661
33613234396337313435643130353837323136396665346566376438616663343465353862366263 32363364363066653933313135666566346138353163333639353062336331623938353131646334
35363837326266383331386331363336363034323739646231346637376634616533363336366436 32326466323030643364356530653862633332363338333633393730663536353363663165303838
62356631343031386266653037303036306237623136313033613432383064326261363339656235 66643331366231613733316139333233393636626162643861346437613062643133353035353566
35346638366230653939626130306666313865363337306331343539363562353237613664663436 38323062653135393364376265346361613163346337636634383932666137636133363864643264
39343438623635616138653163323239313733303961373136363534646263623637383165626431 39646634363037383662376166633335636365303734646465623833313766656235333835396263
63663935343532623465623862623134636263653638366334343034616232383664613538333732 63326237306337396465343065313263663466613336303934373337623364613839323931623934
61386332396561373638363938633937393137643365663563366436646137313163313062323134 35353665363339646365353139643032366164653133616562303930306264343633343439373830
33316664663334383866386439643534323862396162316165386231343430376331303030383731 66356635343363363361313633353134313837653163346537656465623832633561323833663763
34383536306634306239663766636532326135643038663864393565383531396132323839396533 32323537303931343934376165663466356466333533643237653038373866626439356334663539
64376131376430343938366635313335393764376130626662393231396431626461376264393666 36613336326633306331323763393230653236306337353139353135316566336661623464316237
35656338333739323462333861303061366236363033623930336433663738363965613230373864 37363930666539373837336466333265333866633733363032323862333239643764333766366134
38386531373334346231616561313336393531363734636666303037323763643332333234333033 33396330613463633261626334393262343031663131313737623233323535303965333864366631
61326362373163393635653532303663653665646438373137343262366235316533353232353661 65333535343062393434396361313433386463613630353765663363633431393831316233313162
61386163303062363262633636373164653932353632623336666366616538323637613634323538 39656333306265386437663863343638323262663734623830336562636366333534373735313932
31633730323163356266373762663761613438333039373734653663653338363166626561646535 61653739616635643661306165363364316262386434643033646430366235303334326236373262
63653430653966363533636534346231346564343366653132333661383562313739633036343735 34396261653934633337646338333364326237663739623965313039303134393238653634616431
62323062333833623563303764383166393237383831376431643261386439633432326532656535 39376539323238353033323333643337613935616335336562626361383164616432633331313536
66616434383730616162666636616235626333343066376632663465363535303063616265313335 63373633633966373237393131663064613332316536373163303163653638343264323934393365
35633532626435376333353333343935636363646661396133643637356564636263383461373836 62356636643434646465313734363936346435663237393064663138643135303237633965393965
66313931376234646330366239353631313461653563623737383761386634366136626464323037 31353564616136366237666139646561363364353738633862346262616637663362333137386133
32306637613631306539643530613831336635343939366130313731323331643138373235633636 63383662363866306366326232643462376532313632336662666666386562356436616236663430
31376338636437386132303136306163376562626663383639376566616266316434316535333461 37636661656237646431616331633833306266313434326461353561643835646236346539323337
34613761643932613661343863386631336266336663396232643564383032376535636561343461 30303065313165306233646135363766613931383433313165366662636137326162633363306431
38326330323063643731626334636439353938376432303130656531643164346463656232303763 31626564323566313036363666386338366462613164663735363333313830306238323365363737
64343865303766306138613530616638343836326336396263313233656664346561313265346336 65333137363631623139333964316464376662643661643038326162643630323938626332633130
39613135396465356463653230633436393034353537383466333762616434306662643563356565 62626334666131373731343432303461656537303062396638326134356166663936663364656139
66333239653336306133343333646361633038666563623336643563383363663836623264353065 32333864653231636235663165323936626135313838663866373132376437656236363235353533
32656665336635366235616531376232343733663964356533326363316139366430396164333238 39346532626666656233343433383434613238653833626436643566653462346330316565366564
64643135633264613166393135623836613130383162313738386434386462376237333464346535 33623831643265623966326638656462306161636366303266333734396433653861393363333332
36646330356635353766383532613338373536633636646234396265656266656137633937303733 61616463613931393835613463353039646164383435373330626134376339623861396266623430
37643139616234373665343963636634633932323032613864353266386133356362633837393130 30316365356337356531363263623362633332313536373333653964356534623861613232653932
33313131356139336163623236356565343035343664376139363736323266383930656131313465 35613064336131313632656663303631363664366163343362643365663932356138376235313466
61636433626132306532363265666234626335333138336132333534613137306134633463336531 30353333376461623435363931356665306333623736313562333836336563616137633863346635
31366562616633653036666136386538396232306138653163376335646163643338326432316436 37356137306361383134663535626130646135363661353438343961653766333538353330346236
61363362366233356462616137653735636365366533356665393331363131346161333163336263 64326639383864396336343062663965343964396162386266363639643962666431336237303864
33383539646231323365303061646463623335346230643561366663626635343761613865336461 66663861346139363335663362333032613637336266323439366566373136643133383361323061
66633131303165653237656237303863623666363532623237353537656138633163313634326464 37633134323933613665366665383962373935646137616139353661336665613661623834303465
33323666653934646366306637393364366434316435666132333638633632333834636237623639 30323264333137636261393535376438363134663734313662383533313130623365386335323662
61613836343835623131343633316634613261393963666134303535366639376538313839643164 63366139643238613632326165313835363964336237383936333737646239363365623030666364
64363539623730623130663138653139623538616265306439393431336461663161613632336234 64656432653164643565376666373262333839353139666561623731343234326637316636333765
62343237333531643266323933306564623031303233376261323864333934666163383933343866 63306331343538386433376566326239376232363434343838653864393562383063663566333263
35323033313031303232323139653230343166373835646464656461663666383932383732333066 35326333326462316134383139303534343263646530363266663933353834353138303435646339
34336264633464393264663537356338643030366532656461383535376337656362336137633463 33643361363062373735663430346636333431363736373463326439353437356530373935633962
65346464353165356434316164346630363535353031323236376266353333396236383833383431 63333031376163656565663536366230333731613833396266383465333461386161373337323863
34656663323263313464626638373264653730303332633634343938613232303236643634303066 36313032613534346230636566383930656330656133376431376462656536386263343831393862
37613233623861363632636364613631633637663832316337626166636436386437653331636435 32363365623061653837303736636664663361663862656562393661623330386435646336373531
34666139363535386463396635646466616138343732363036356164353932643932386231386134 35363037356638653831386261646235613337363066343632653632306631633138633235356139
62363366666232623336343133613733363634373533373138303332313563626261363032336466 39663936333262643061646330316538373862353030626632396336393030643239316634343730
62303335353131383436613632643331396666336634666237623866653965636236323661363362 35363564373865376533333466666439646633313932656665383930623531633038316363636332
38373139393162396462356231666361623062343435386533396366643466393834306135306535 37363466643835353132333532646163316636303662646234613038333334626365623964653235
64653339646338343163386531613438643566366462636234376339363538303632303531656564 36393739353164306666313537633538383934363330373235353262616165623132613330303735
39663861316637626635613739333365366464623334383934303339303332376438363331336135 65323362666263653937376562633833653264613439303236373466646362316462386632373038
32633837326234363662393133393266613433663238623533376666386536353331333737663136 35353462633730666430623638626439626364616335643964623933663233376433353233633235
65326539623639656230326530353535616331616631386166383737666634333731303964643735 34616233636335323365646538326639383033643832323139633064616635353331376230663738
61316133376661663934396330303266613136396537326231653062323833336538653764616262 35316665616230633335366233363332353432633937653335316662306166383337633262633634
37613331626334333037333064343330356363373237323564616132653736316663303366643133 35303766653733376362623436383663663437393461643266376530613533383038373035356234
36663839346633386139306438643465363634613466663438646564316132633032643434343131 32343962623930336566363164616361386134376362383138653963366339326431653832393664
34646166633634346466303434623066646238653138646332613630643364656330653637393432 66633566343666326339323536343738396532623735343034646130386332323466346365383135
34343830643935353138643233643761343532313663376432363534613736343838343963383930 33373732363833316138343938633262373066613930343162663163323331373466366631626237
34623962383330393034636531353865646132386330373234303961346133383839633766636430 34623062353139353734653366313363323634346564663861613233366537663732363134643638
32353863333463393665313165333638363738316438313036393866633535306365363730363461 65623964396665303765343430623636376637666364363835346462613763306135326463353631
64636439343338663033316533623136396266363436313661333138363266633561376238656262 33333834333464383437336531616662643461666138383435636530373761646564626438313433
66643061306664363264633836643939666461343764383061343934633837356338323238643233 64643037626637343433366631343266373436343864396663353039333231646462333932353436
63626464636562316365366236323034363734373234313432623865383634336532363531313036 33643837616639666363353237313137353133346636383231623634326335386537363030613537
39316166373964623561306134643066653035323834636632363465623737313364386136366434 33306333363638643533373237633833663333393566656466313832383636663031663433636663
35643532333938666262373331366437323431653066383533623834313339346165323435353061 37313034353734343966613934663530643537366562623137373331623632653466333839376331
39363261646165313233616334613733633362653161313734623334313230633931323163303834 61623866633937666365633763613138346365393934383163623730373134373531616138363733
31323530316465306236643761363865653839326434323237393238393166396232393263633732 65616230303337396561666462653866333438353463316235303331643834653165363033626537
32346363393332616331633831303735376631303763376362636262353761303563613362363064 37386636653337363666393163393031333461383331613965303262616530363133373732313362
37656332646464353866393736393363303761663338616562343339646137646636663738353964 33343033373963656333363064303035666663646536373764323833653037316231316430333634
62666530666633343637363433386135366663626433393466353035643262663332346464303964 65383236663035326636336535636462323438363165633437333337613439326433626466396365
33633434363636303431306536646139396232646532363838333536363065346131346336613437 38393362633266663962646565336539333239346133323434646632643537613435643434623631
37366537353531343966363163396664376438656631643330326665613533333431613664333666 63636536393936343165393966663438343261333966363639323462663566613437393838323036
34363232653361313732653436306332343430376330353762666530623030393062626536356331 33393163323034336533333632646230343138336333353236376136383664646466626666356234
30323830646365346261376435323433383461616530323731363061663261656537343430376335 61653466363933333331613539636431393934393235376433326665643263663638306463393837
62616538616138663064613332316331663064376435326665333434326433643161376562633663 34396339383536636461366230383938386339303334393038343239363361666565336237326465
33393761333166306366656263363237386161313539373938356666626430613236366561613933 63623231663861303436353533663661656431646165646662383065386362636633333631643335
61393564343531393535386163613966636432353434396532616433336538343230316538303536 38373464373432626538616234623638303734626237393566326463633765316365653837303433
39303436386265353139313864373735353039333538623862313637633236663434376265653336 34376438323439633237313733343836343733373930643138636333366166353666373966323231
66616165333432616433633539373465313962643334356333373930336335356231653539636530 63613466306365386137336538613837613264633735393937343166303736396162303230623430
32313830373864383633623064643566333531623535383432616136333431363731313637653432 33363233663761333063363134393935333530343133303138373934666236393732396330643438
38343930623838316234383136386230343633666434356339633364653637656237336539623839 61343231666363323834346132376135303339313766383365363837313163393636333563376436
39626166643666333839373261653939626261373461303962353334373664356632663664613531 64333832393361626663376161343033383763373537396264646239303736333263303739326436
64626131373039376331646665333130346166336636663732303638393833326337633433396130 31393665363734323364643032393031313135623563383639316163616535323938343765356264
63363534323565346235393862633539663362326166303764613037323538663365393133316562 35616433396236396662393930646431643063323163343366313030383766323733333865616235
64323338653964376138306232393365643738333932666166636164636566346638323266616436 65623737663763373363613030326665613333636366393464383139626462346333323162333537
65626636346265643532656164356137326139613630393633343032366665643131663333373832 32386635636532346539623534346364623532656435653638643962353836653330316638633661
62653639373936346464343662633634393330656636646366636131656431663061636663373037 38363330656161393766383237663737663835646565363966373563663832313263633238316165
37313732336539663665663734666138336635346361343262313935373834343762373261633334 64643536336232396436636261303866376132366261373132333631646166393764636130326335
65633230306663303336323237356131663961323137373333346163303332306538373334623535 39613037633239626666396262386533343764623233626335373139663937613433356134633330
64316137626433353433663139396232636438613164333434623137336330343561313032613133 32656138383563343734616536303265306661386362343939626332623763393136323138653937
35353464356132653233303135346631643639316335333536653964343165326661383864663566 39646537366461353364616538663361663465626634356338323166373837323339343061323261
34656263653761383738323331626534343635316364326335353965663366643963663338643533 33393765653034643166313639323962306265383336663265623832393038386334643661336663
30646235663361363437616635616534303531613361656462393464643661613635323866356238 66643330306239383736636330393266346537326436663432653031303765393930633761343363
62356335663630613166613235646335333464383962653736636161666366333834656638356366 61353163323830656538373366323230353830633534326133613861356132623938663565393130
35663962316430373535613531633534616533653166663930646439623666623637323034663730 63333764613761663162633035353036663736656138623234313833326337316438323835313465
64343931363234666431363439386362613537363065373762623361363138393538626563376233 31653136356638623965363266636461646465313065666137313931623063303337393963353437
64383064383465613661363136636466346530646331303338396437336330373835313861613362 39633166326662303834303261353965363235626535643564613064306636366461666263383833
31616133303839643638363236373731396339313637393163613930396338313666656537376661 66363966636538373435323365636630383561336533356631646563646536383333393864613164
37326632623866393262306633333334386331393733636331363334326664343632663631656138 38393638623263666239623835613933393564323132373630623734363334663735633438663137
31363534616433646264393730376664333735633534313363613134313635393462383366613636 35613563356161316632323562626639633931643762643232636136653731323337333236326363
66353233356232376664343435623962663537376437653931633437623537373635623763653031 36633338386230626665373666643831626132323866653430393531336466386463663865393962
38393661326630306165343762343061373930343165316637613836623630336330366538633966 37363633343632653963306531393537633762313565303631643064363531363839643363356139
35623831623334623639396337396433373236313530633634376535616461356136316432646162 33666162306331393237333966643735643731633331373839356637636462633836633762653039
33376438366439633964303035306162316433653563343332366161313766323065356662323832 32633630303437633130613366386633346637623266663361656365633737346432343263646536
32396363633934386139326165326537623132333839333264613562393263613065663733373338 35376433386464383533323231353830666363363437623033643238363438303565616137316461
37616336376164353362626233656632613632393562316466323764363563636434656332333532 64636233643365663666656431613966396561366236383363303135613433626466366437396465
36303935633264346331336435626537363836323434373531326134643332303831646432383963 37303562633336636635363133626238643430663132626331316437626532356237383730626665
65383366653266633363646633613033616232393933663930323539383939626136623033653634 38373633323531346366313631376231353966626637636262333936343066336639396262646436
65383962623130393030333633396163343462303234363239316161613231656334373935396462 31616165626466623066336138653966323334326236663439643561663863323130653631643231
66643639313031636164353531623265346261393861333035333631363038613432616562643138 31396335333066326166383337353033376335376664653162396466346638653531346239353936
39306638643831343862633138656530363962386361663566643032356365336664303437353363 33663862323135623432333036336162353061363537363463366435643461356562323935653962
64363463656239323137326466633234373739646431616534383964656135636433393836333662 62333130616466353032316462356233393037326438303064656235323966306535346132663232
65643765353131663039356361633838616430303936643937323834633732653334663230653034 63393238646534316433643638646536313934666361323061306561396132306431633932313031
66333861376232346661613361383633393537303332373133396634303262343565666439663435 31356430313463613331363064363265646361353430646563396238326330323038666264653935
61653366313733623231313566613765356266383038353462343838663366353438393365366538 63306635323439326665306135643730386239356661303232353737383532353363303163313437
36393764343137376263626635663033343563626165623864396135366564356461646565616465 38623064383035373239623966336563626130636335383833383366343130393939316535333466
37303632323631626538626165613464326232393431303935626264363432653930373432616136 65353062396632373330663365373761656161653837396666376531376663313466393230646461
32383561306234666235353331653837393562626362633638303433663564303466363963616430 62306465363166663962316634663763626666306631363731633834366433363630383833393361
35666636336439383764626465373031653561303561326433373531666638336335333063313662 36393932326138643432666437373463343061313135613163343034373464356666333134366664
34663333386630363438633164383533333333343961623931323438333161666134633330643739 66343461393733343130636266623661323066623332633930326361626436366237323664336637
66353435636563643737663862353966626137356532636534663963303231653839396236396162 61316135613330663835353139613039613264383838313337373432623133363735333732376566
63613666376434366431643564336165393738356230396431626138663265323466333439336536 38633763336438663737653736326466646237313130623934396531306430363461653962336132
37343938356364346336366565623663323535393538373962373535383630626234323031323463 64313437323766613632376439336234393165646338396334373037323237633737393866303231
31303164626634626635623437376633643061663165306538346166393066633834373363343137 33646338333532613432393234316235653466633639306465363062656335353034666665623631
36326564323562333731386261636537646463363762346461363235626662613430386263616237 66386437616462633336636566393537323831313566326637386466616331396464653438396562
37313234633138383535643633616136623838316638643734616236376138333938663334613833 33343132626463393364386133323163393336313065316433613133663961633033613232343337
32333135633837396539656532653966343462393733393734613163303130373164643130663532 37313334386533366465353461633930643662326235366139306335656163313864636161623239
38623563663133633864316662643630643937376265396339663336636537356232663161346332 38336238663630313836363739623334633130616433393536303431333735643565326265613561
65613930363635323266633161616166393031303735613637623934643939303161336363633232 30613762396162633964336165666137626333643735323330646266666563313935623230643262
35666135333566646434383235316233663530353435616261353535383666366162653137323235 30383635663035653437333339303730346366333765353739663231643433353764363966353435
63633030626461306536623964353031333537313862663634373936323534633566383962376665 63663466343864363033646663376261363562636630643038613365333936653165633733623134
39613264396238363532326635356365626439363233396635656436363962306361363731393939 36626135313330303463336535663235323536613661353332653139336165613261316638666563
39353264323962636434373164633137303738633433313436616661663538633262663233616638 63346131306536353630363236613864393935333431333864333464353664366134313861633463
34363630316338323164306361353535343233643364633131633534633466393936333464643164 34336364663030376264663961396235643438653730396661643032623762623965623737326430
63326534356232393333323937383034376531396531363438303439653639646235306435316565 34333236316335373863663232356136333431666233353138353139313466343762356632396561
38386364316331663161366134656638353864326339313338663133393734626132323835393031 37356162303636343833316432353831373532326436363538333466333636306666626465666333
38613066323763393861663761306433306561666131643662363062626230376234386365633565 36653065363538653266643831326234356534303133343564646564323762653238303161336131
61303663646534623733333066386535346163643739343062376533333039643762623066663064 35396235343837333331396535646564656433343766323765333465356134323536636639623962
39373331316463643734366365633738343232633538373165376562643237396461373561643562 65633233376136316334653335623666313666376661326362663338633862643963353536616563
36623834623334343139316165363664666232656435663962386237656662333364306336326132 66323939643332663665306536356566383164356561303935313432373264353738643131653831
613164353236653566363965353539353830 31346231633037616139373330316231363938386536303432386638323139326132663539663738
64636231663538333932363332663137323764346336633336363965616535373030663363363861
65363632653831353761306231663562623732353433656637353966303033636666613739306230
64363465623031386431663565623966643836383532626134366363656265646563343635383538
32346166363832626664613335383731616135376635336266326531663731373633303131373965
66663666333739373033616363313132643938656335353164343764383933636265656665663433
65386235636531653236333465316331353938323331623733623731303462306566333365383134
31356337343732613764633731306335366136346633393265666331636465323566656337323838
37393031316630623466393138636666303065623430656336386430363962336539616262633030
62616365623765366236303539353166306630393362386430363736313438346266366336323839
34376462376633366463636339646337623965633663363862393261373535636230613532386464
34393564383130306138366564366666646132363732653036353135656132656339353932383530
61636234393165326631303731633062333735306635303566373838393164306538303664313266
33346337613836636337316638323631396235643363333836323135303738366235326465376630
3363

View File

@@ -0,0 +1,2 @@
---
transcriber_image: "transcriber:2fc5a56-1761210134"

17
vars/transcriber.yml Normal file
View File

@@ -0,0 +1,17 @@
---
app_name: "transcriber"
app_user: "{{ app_name }}"
app_owner_uid: 1017
app_owner_gid: 1018
base_dir: "{{ (application_dir, app_name) | path_join }}"
config_dir: "{{ (base_dir, 'config') | path_join }}"
config_file: "{{ (config_dir, 'config.toml') | path_join }}"
data_dir: "{{ (base_dir, 'data') | path_join }}"
backups_dir: "{{ (base_dir, 'backups') | path_join }}"
docker_registry_prefix: "cr.yandex/crplfk0168i4o8kd7ade"
# Registry images
registry_transcriber_image: "{{ (docker_registry_prefix, transcriber_image) | path_join }}"