Compare commits

..

No commits in common. "master" and "fa6f3ba4b1da35d11ea3bbda9411336b6898c786" have entirely different histories.

63 changed files with 390 additions and 1302 deletions

1
.gitignore vendored
View File

@ -1,2 +1 @@
.vault_password .vault_password
albums.txt

View File

@ -2,7 +2,6 @@
``` ```
pip install -r requirements.txt pip install -r requirements.txt
ansible-galaxy install -r requirements.yml
``` ```
### Ansible Vault ### Ansible Vault
@ -12,13 +11,13 @@ To manage secrets, this repository use Ansible Vault.
Create a secret Create a secret
``` ```
ansible-vault create inv/host_vars/chosto.me/secrets.yml ansible-vault create inv/host_vars/new.chosto.me/secrets.yml
``` ```
Edit a secret Edit a secret
``` ```
ansible-vault edit inv/host_vars/chosto.me/secrets.yml ansible-vault edit inv/host_vars/new.chosto.me/secrets.yml
``` ```
### Server ### Server

74
all.yml
View File

@ -3,58 +3,28 @@
become: yes become: yes
roles: roles:
- role: base - role: base
tags: base tags: ["base"]
- role: cron - role: cron
tags: cron tags: ["cron"]
- role: ufw - role: ufw
tags: ufw tags: ["ufw"]
- role: fail2ban - role: fail2ban
tags: fail2ban tags: ["fail2ban"]
- role: node-exporter - role: "node-exporter"
tags: node-exporter tags: ["node-exporter"]
- role: docker - role: "docker"
tags: docker tags: ["docker"]
- role: traefik - role: "traefik"
tags: tags: ["docker", "traefik"]
- docker - role: "grav"
- traefik tags: ["docker", "grav"]
- role: lychee - role: "lychee"
tags: tags: ["docker", "lychee"]
- docker - role: "web"
- lychee tags: ["docker", "web"]
- role: web - role: "gitea"
tags: tags: ["docker", "gitea"]
- docker - role: "nextcloud"
- web tags: ["nextcloud", "docker"]
- role: gitea - role: "funkwhale"
tags: tags: ["funkwhale", "docker"]
- docker
- gitea
- role: nextcloud
tags:
- nextcloud
- docker
- role: music
tags:
- funwkhale
- music
- docker
- role: hugo
tags:
- docker
- hugo
- role: peertube
tags:
- docker
- peertube
- role: couchdb
tags:
- docker
- couchdb
- role: minecraft
tags:
- docker
- minecraft
- role: restic
tags:
- restic

View File

@ -3,7 +3,7 @@
# No cows because I am not a funny person # No cows because I am not a funny person
nocows = 1 nocows = 1
force_color = True force_color = True
#stdout_callback = unixy stdout_callback = unixy
# Default inventory file, override with -i # Default inventory file, override with -i
inventory = ./inv/static.yml inventory = ./inv/static.yml

View File

@ -1,73 +0,0 @@
$ANSIBLE_VAULT;1.1;AES256
32633937663864373934613132653334336361653961666636666261326163393961386465306565
6138373562366164343962616562373134366332656235320a326535343938363934303764306330
30356434353266663930373765356130313730663835656262323964353532303962356265343666
3432633637643938660a613863636664326232366539313036333933376138303030353439333961
65386466626435353966613534613330363737366436656632373037653665346137366337616133
30383332313636643534643838356537656437346331613235326264633832306262666439373237
35323337666437353764346163373833396232353839643766653132356264333034363834313638
66333839653462653137646133363639313866653531306234336236313966366230353630666230
63343766376466643635333133306265636162636135353838303734393231323063626635383939
61623237343037633239666462303337333739373130633764636336613231643061626661343234
61656337396137333435326564643463396663366561383838363339346430336662346633643865
32626331383061356332313963633136643237643065393236393332356334653761386165323937
31396263386630316334393337393061633232376337383231623566336136313263613566353234
62653433623837316463353264366462613033396535343261356437396631323730653738616234
62343164643762323566656532303638666133316566316563316233333232353661386562663362
31656262386334333034626233653363323061376537626137626536323063613233343637336634
61386565336336643763323932323362633631393365326132656266303164643331623430623062
66373435386530393532343033623930386434656139633336646636353861346332646537393633
35626230333364316133326461353838343632343537613237313463303633393066643762653933
63333765666430646339323436306161663433623465623132643062656665343234346531303061
61643866393937383436613265643934393863613437313763613765656230316265376365353866
66326664613965646231366162376465323364373033373036383036396139663632376561396432
63393138373966386535353164626539313132376537336538373866343932383537346239626439
38353931343864653935306438613562396263373536643433396234636161343162623261346139
33346536386139353163346264666533653238383562666364353162303965356430343736313333
32356365346331396363336531316135343161306565393936396262306332663639646431386265
64306565353363343162666563626462636639316661373465656363636237356135623339366637
32363830666562646132653034326635303166333732613566343633643133386433623836623635
39623936316538303261383135343738306231643935356230313032616337313364346165653238
34373364656436346334373334646263666231633461396337653630626231313338336438373166
30663738303065663566333962396263303634353966663130623237666536666538323464396339
36323634396263353362636336633735373032333666393163393762393930386530363130383432
64383766623861636165326363303937353165656363323666633138326364616333646632626233
63626266343664346331363363366533323466313935356564616432366533313035653264343263
33346137353462653038633937353732333461646535383262633966353339656365383666363839
35303566393863353637333934313135626165643032356161313839353665313237383432656231
33373064666161613762393036663835336361623464386537393361326337356230316232633964
35306262643830353066366264323764373063373038323762346265666530613166373031333936
34353164333239633836653661613664313364343061613663623663376264303364303137333966
32613064336661356138393862653532656134353861383866373438653964626365636239613464
66643635316464663365633864336266306163306261393139633631346264383133616534306630
39653766613931356332306235323533653365323766356433323632343631383465393135303836
33636137393461613761396135333265393531306233633034656439633433366563623832613033
35326533316436313839663336336461613534643764663436636632656164623637356634623533
33613037376531356437333133646264643837316664663465333165623439633039356163303538
36633930316437343138353332376132636262613432623031313463623032653266346464386630
32333363366263303734643930656536616265613133663034646232666238343533613731393031
33326566326236363838623938356236353265653565626561373032303966643137623334386362
66616335326364323365313561353762303230376465306134643735323931626164386130636561
37313131656165313231393433386133613137623061383962336665653137353034343333376138
34356335303931373936336363373638323164643361343433383966613034353837656664646165
63373432653334363463356537356436616533633763353038313638653932313863643930316635
63343739316562636632383036653835646330343865356466346531386166333535306165383364
61316336333939373335633931313137363463366438323463613039666264613831383935376563
63663035313134336263376464356131306539613532316132346339393139653235353336376235
35333861386262373464326338616330393265653532643732633665303666623236373663323732
66326566623039646536313036626232636361626136616237383634653530666463333939363538
34613964333262326632333237333939373733633639363261343866313165333065643862386462
37373431636535373366333034343035326364646632336362376265356331363033383866326536
31323236363262393938613362643365303536373233646263353831663766633164653438376638
34626662626266626436336265643439313035316166633936656539623838383938656538383637
39323963633566623961653764333636393562333362313233363433313133356430313965313562
33366463623937356331306233326233653132323361356362666237353433646436653939656131
38613938666430623564393964393132626438303864343363323731333931346534356336363766
33303761353934633130656634336462626238623932623464656437383235313636316163313431
66353964326261316165613430656337333635393331656131303565356263323839346363653262
34383733626237633263613666616464653363343866353532343830656633323864323032616536
34346464663833353238393764656634353438383930666665363934323135303337626565343363
62353235643931366234303236336461646233636631373838383266633138356662633862336534
30363562366136393065333765393137356365303262626331313238303337663233316439346165
64613836376466313465653830363639326138306133656133383132633361333164396331373961
66336666353739383436

View File

@ -0,0 +1,34 @@
$ANSIBLE_VAULT;1.1;AES256
34363462333030653462383364323934653331333861333732303365626439666666393232376139
6161356563623135646365323133326333383734383136340a643335623334363066353930303638
38653862376330353361613661383330343338633963333538623934396537356137643833663262
3431653035643063330a383634633966643133386236303064663666303935333636386532363363
33646631343761363133646635663836313832616264313134616635373230393935396330373936
36656666623631636230356665366532613230396565336136316530633432326665366135376238
34633666623063383632663333366137666265363663323264643631323463633865336635636435
35616631623532303536613064353135353034333739656432393835303839333165633135663934
31663233656137653230343036666336386361393937383636336536396539303131393133653234
30343030373863636232643635656664643561383264643465363163656131323731326361623639
31663362363337306238616564336330303462346537393336363266323031653166323366333466
36376433373663666535623864303533353837663064623432306363356638363634323831663437
31663462323666633835663831653439306438376662343762663136613532366136636661383166
33303563613436323334366532316336346635356433663766363831646336336665653365616663
32303165313935326462393833363563313235386637353761306262353733316265383133303037
35373338653931383463323533646262653066323164313939336336376262353066363339653938
62383035653333663663336364646634336563366131653665373033333365386562333966353063
36383964633561326262616439383739343736343362363264393137366662306630656364333532
63346331636266626637666264343263303534313038386263666634353330643938393236336361
34356661343334316162313030636533643064383531653836356366623432383066333033663536
32656639323030653635636265343731336531646539356261383139663261386439376237396536
62666130353038386635333265376630376165376433336436636331316531663935663339356436
35303765303031323564333232363335643235376366613931653035313035663737353937393737
66353663643735623762303234663762356136326133656338656664313637346136376266383636
36386637326430626264666362643639636533373530366337373561643335363236646237636338
62393531643663646433303233366233366536373865613331383539616238303135383665343930
33303930633533333637343634393038356235646533613766623436306666306166383632303233
38343063636236663432333336393838373637633737363865373261343965623736326433313937
34323037326362323032356232373065666639616362393536653663316439376662636431626238
32353838666535633831353538306634636562343633656663343131386462656536633663333235
38386435313336613962313665616132323431356333353861386663313562373837663966623532
65363438643666326163393761626231386331343435636562336363643733353439326230326637
61633531316335396662663539366264633034373333336638323734336364323038

View File

@ -1,18 +1,15 @@
firewall_in_ports: firewall_in_ports:
- "80" - "80"
- "443" - "443"
- "25565"
- "{{ ssh_port }}" - "{{ ssh_port }}"
hostname: "{{ base_user_name }}" hostname: "{{ base_user_name }}"
timezone: Europe/Paris
ssh_port: "2220" ssh_port: "2220"
prometheus_server_ip: "51.178.182.35" prometheus_server_ip: "51.178.182.35"
docker_files: "/home/{{ base_user_name }}/docker" docker_files: "/home/{{ base_user_name }}/docker"
compose_version: "3.7" compose_version: "3.7"
traefik_network: proxy traefik_network: proxy
@ -20,7 +17,3 @@ traefik_network: proxy
domain_name: chosto.me domain_name: chosto.me
letsencrypt_email: quentinduchemin@tuta.io letsencrypt_email: quentinduchemin@tuta.io
# When importing to Funkwhale, you must choose a library
# Create a library, get the ID in URL and put it there
funkwhale_import_library_id: 3e772648-0ce1-4dc1-be59-39e8e6f409d6

View File

@ -1,7 +1,6 @@
all: all:
hosts: hosts:
chosto.me: new.chosto.me:
ansible_host: 51.159.149.245
ansible_port: 2220 ansible_port: 2220
ansible_user: chosto ansible_user: chosto
ansible_ssh_private_key_file: ~/.ssh/scaleway ansible_ssh_private_key_file: ~/.ssh/scaleway

View File

@ -1,25 +0,0 @@
---
- hosts: all
become: yes
tasks:
- name: Install and configure Funkwhale, deemix and beets
include_role:
name: music
tasks_from: main
apply:
tags: install
tags: install
- name: Download submitted list of albums
include_role:
name: music
tasks_from: download_music
apply:
tags: download
tags: download
- name: Import music into Funkwhale
include_role:
name: music
tasks_from: import_music
apply:
tags: import
tags: import

View File

@ -1,4 +0,0 @@
collections:
- community.general
- community.docker
- ansible.posix

View File

@ -4,5 +4,3 @@ ihl_base_apt_cache_time: 3600
ihl_base_additional_groups: [] ihl_base_additional_groups: []
ihl_base_users: [] ihl_base_users: []
ihl_base_ssh_users: [] ihl_base_ssh_users: []
timezone: Europe/Paris

View File

@ -1,24 +1,15 @@
- include_tasks: - include: apt.yml
file: apt.yml
tags: tags:
- apt - apt
- include_tasks: - include: users.yml
file: timezone.yml
tags:
- timezone
- include_tasks:
file: users.yml
tags: tags:
- users - users
- include_tasks: - include: hostname.yml
file: hostname.yml
tags: tags:
- hostname - hostname
- include_tasks: - include: ssh.yml
file: ssh.yml
tags: tags:
- ssh - ssh

View File

@ -1,3 +0,0 @@
- name: Set correct timezone
community.general.timezone:
name: "{{ timezone }}"

View File

@ -3,13 +3,11 @@ ihl_base_apt_packages:
- ca-certificates - ca-certificates
- curl - curl
- dnsutils - dnsutils
- ffmpeg
- git - git
- htop - htop
- jq - jq
- less - less
- lm-sensors - lm-sensors
- mlocate
- python3 - python3
- python3-pip - python3-pip
- python3-setuptools - python3-setuptools
@ -17,6 +15,3 @@ ihl_base_apt_packages:
- sudo - sudo
- nano - nano
- rsync - rsync
- sshfs
- tmux
- unzip

View File

@ -1,24 +0,0 @@
---
- name: Create CouchDB directory
file:
path: "{{ couchdb_folder_name }}"
state: directory
owner: "{{ base_user_name }}"
group: "{{ base_user_name }}"
mode: 0755
- name: Copy CouchDB Compose file
template:
src: docker-compose.yml.j2
dest: "{{ couchdb_folder_name }}/docker-compose.yml"
owner: "{{ base_user_name }}"
group: "{{ base_user_name }}"
mode: 0644
- name: Ensure container is up to date
community.docker.docker_compose:
project_src: "{{ couchdb_folder_name }}"
remove_orphans: yes
pull: yes
recreate: smart
state: present

View File

@ -1,35 +0,0 @@
version: "{{ compose_version }}"
networks:
proxy:
name: "{{ traefik_network }}"
volumes:
db:
name: couchdb
services:
couchdb:
image: "couchdb:{{ couchdb_version }}"
container_name: couchdb
networks:
- proxy
volumes:
- db:/opt/couchdb/data
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
environment:
COUCHDB_USER: "{{ couchdb_user }}"
COUCHDB_PASSWORD: "{{ couchdb_password }}"
labels:
traefik.http.routers.couchdb.entrypoints: websecure
traefik.http.routers.couchdb.rule: "Host(`{{ couchdb_subdomain }}.{{ domain_name }}`)"
traefik.http.routers.couchdb.middlewares: cors@docker
traefik.http.services.couchdb.loadbalancer.server.port: 5984
traefik.http.middlewares.cors.headers.accessControlAllowOriginList: https://tempo.agate.blue
traefik.http.middlewares.cors.headers.accessControlAllowCredentials: true
# Cannot use wildcards with creds, see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Headers
traefik.http.middlewares.cors.headers.accessControlAllowHeaders: "Content-Type"
traefik.http.middlewares.cors.headers.accessControlAllowMethods: GET, OPTIONS, POST, PUT, DELETE
traefik.enable: true
restart: unless-stopped

View File

@ -1,5 +0,0 @@
couchdb_version: "3.2.1"
couchdb_folder_name: "{{ docker_files }}/couchdb"
couchdb_subdomain: couchdb
couchdb_user: "couchdb"
couchdb_password: "{{ couchdb_db_password }}"

View File

@ -0,0 +1,19 @@
# use this one if you put the nginx container behind another proxy
# you will have to set some headers on this proxy as well to ensure
# everything works correctly, you can use the ones from the funkwhale_proxy.conf file
# at https://dev.funkwhale.audio/funkwhale/funkwhale/blob/develop/deploy/funkwhale_proxy.conf
# your proxy will also need to support websockets
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto;
proxy_set_header X-Forwarded-Host $http_x_forwarded_host;
proxy_set_header X-Forwarded-Port $http_x_forwarded_port;
proxy_redirect off;
# websocket support
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;

View File

@ -1,28 +1,4 @@
--- ---
- name: Install deemix and beets
pip:
name: "{{ item }}"
state: present
loop:
- deemix
- beets
- pexpect
- pylast
- name: Create deemix and beets directories
file:
path: "{{ item }}"
state: directory
owner: "{{ base_user_name }}"
group: "{{ base_user_name }}"
mode: 0755
recurse: yes
loop:
- "{{ deemix_folder_path }}"
- "{{ deemix_folder_path }}/config"
- "{{ deemix_songs_path }}"
- "{{ beets_config_folder }}"
- name: Create Funkwhale directory - name: Create Funkwhale directory
file: file:
path: "{{ funkwhale_folder_name }}" path: "{{ funkwhale_folder_name }}"
@ -31,7 +7,7 @@
group: "{{ base_user_name }}" group: "{{ base_user_name }}"
mode: 0755 mode: 0755
- name: Copy Funkwhale templates (nginx conf and Compose) - name: Copy Traefik templates (nginx conf and Compose)
template: template:
src: "{{ item }}" src: "{{ item }}"
# Remove .j2 extension # Remove .j2 extension
@ -42,6 +18,7 @@
loop: loop:
- docker-compose.yml.j2 - docker-compose.yml.j2
- conf.env.j2 - conf.env.j2
- nginx.conf.j2
- name: Copy nginx proxy file - name: Copy nginx proxy file
copy: copy:

View File

@ -4,9 +4,9 @@ FUNKWHALE_WEB_WORKERS=4
FUNKWHALE_HOSTNAME={{ funkwhale_subdomain }}.{{ domain_name }} FUNKWHALE_HOSTNAME={{ funkwhale_subdomain }}.{{ domain_name }}
FUNKWHALE_PROTOCOL=https FUNKWHALE_PROTOCOL=https
EMAIL_CONFIG=smtp+tls://{{ funkwhale_email_user }}:{{ funkwhale_mail_password }}@ssl0.ovh.net:587 EMAIL_CONFIG=smtp+tls://{{ funkwhale_subdomain }}@{{ domain_name }}:mD32H&Y2X$9XPFQtS!tq@mail.gandi.net:587
DEFAULT_FROM_EMAIL={{ funkwhale_email_user }} DEFAULT_FROM_EMAIL={{ funkwhale_subdomain }}@{{ domain_name }}
ACCOUNT_EMAIL_VERIFICATION_ENFORCE=false
DATABASE_URL=postgresql://funkwhale:{{ funkwhale_db_password }}@funkwhale_postgres:5432/funkwhale DATABASE_URL=postgresql://funkwhale:{{ funkwhale_db_password }}@funkwhale_postgres:5432/funkwhale
REVERSE_PROXY_TYPE=nginx REVERSE_PROXY_TYPE=nginx
@ -15,8 +15,7 @@ CACHE_URL=redis://funkwhale_redis:6379/0
STATIC_ROOT={{ funkwhale_static_root }} STATIC_ROOT={{ funkwhale_static_root }}
MUSIC_DIRECTORY_PATH={{ funkwhale_import_music_directory }} MUSIC_DIRECTORY_PATH={{ funkwhale_import_music_directory }}
# Dummy value for front container ; we have S3 FUNKWHALE_FRONTEND_PATH={{ funkwhale_frontend }}
MEDIA_ROOT=/media
DJANGO_SETTINGS_MODULE=config.settings.production DJANGO_SETTINGS_MODULE=config.settings.production
DJANGO_SECRET_KEY={{ funkwhale_secret_key }} DJANGO_SECRET_KEY={{ funkwhale_secret_key }}
@ -26,19 +25,5 @@ NGINX_MAX_BODY_SIZE={{ nginx_max_body_size}}
AWS_ACCESS_KEY_ID={{ scaleway_s3_id }} AWS_ACCESS_KEY_ID={{ scaleway_s3_id }}
AWS_SECRET_ACCESS_KEY={{ scaleway_s3_key }} AWS_SECRET_ACCESS_KEY={{ scaleway_s3_key }}
AWS_STORAGE_BUCKET_NAME=celiglyphe AWS_STORAGE_BUCKET_NAME=celiglyphe
# URL used to make changes
AWS_S3_ENDPOINT_URL=https://s3.fr-par.scw.cloud AWS_S3_ENDPOINT_URL=https://s3.fr-par.scw.cloud
# Base URL used to construct listening URLs (acts like a CDN, see Scaleway Edge Services)
# ⚠️ Scheme is https by default + no trailing slash
AWS_S3_CUSTOM_DOMAIN=files.chosto.me
AWS_S3_REGION_NAME=fr-par
# My bucket is public
AWS_QUERYSTRING_AUTH=false
AWS_DEFAULT_ACL=public-read
PROXY_MEDIA=false PROXY_MEDIA=false
EXTERNAL_MEDIA_PROXY_ENABLED=false
THROTTLING_ENABLED=false
EXTERNAL_REQUESTS_TIMEOUT=120
NGINX_MAX_BODY_SIZE=500M

View File

@ -11,12 +11,14 @@ volumes:
name: funkwhale_redis name: funkwhale_redis
db: db:
name: funkwhale_db name: funkwhale_db
frontend:
name: funkwhale_frontend
static: static:
name: funkwhale_static name: funkwhale_static
services: services:
celeryworker: celeryworker:
image: "funkwhale/api:{{ funkwhale_version }}" image: "funkwhale/funkwhale:{{ funkwhale_version }}"
container_name: funkwhale_celeryworker container_name: funkwhale_celeryworker
env_file: env_file:
- ./conf.env - ./conf.env
@ -24,32 +26,29 @@ services:
- C_FORCE_ROOT=true - C_FORCE_ROOT=true
volumes: volumes:
- "{{ funkwhale_import_music_directory_host }}:{{ funkwhale_import_music_directory }}:ro" - "{{ funkwhale_import_music_directory_host }}:{{ funkwhale_import_music_directory }}:ro"
- /etc/timezone:/etc/timezone:ro command: celery -A funkwhale_api.taskapp worker -l INFO
- /etc/localtime:/etc/localtime:ro
command: celery -A funkwhale_api.taskapp worker -l INFO --concurrency=10
networks: networks:
- db - db
restart: unless-stopped restart: unless-stopped
celerybeat: celerybeat:
image: "funkwhale/api:{{ funkwhale_version }}" image: "funkwhale/funkwhale:{{ funkwhale_version }}"
container_name: funkwhale_celerybeat container_name: funkwhale_celerybeat
env_file: ./conf.env env_file: ./conf.env
command: celery -A funkwhale_api.taskapp beat -l INFO command: celery -A funkwhale_api.taskapp beat --pidfile= -l INFO
networks: networks:
- db - db
restart: unless-stopped restart: unless-stopped
api: api:
image: "funkwhale/api:{{ funkwhale_version }}" image: "funkwhale/funkwhale:{{ funkwhale_version }}"
container_name: funkwhale_api container_name: funkwhale_api
env_file: env_file:
- ./conf.env - ./conf.env
volumes: volumes:
- "{{ funkwhale_import_music_directory_host }}:{{ funkwhale_import_music_directory }}:ro" - "{{ funkwhale_import_music_directory_host }}:{{ funkwhale_import_music_directory }}:ro"
- "static:{{ funkwhale_static_root }}" - "static:{{ funkwhale_static_root }}"
- /etc/timezone:/etc/timezone:ro - "frontend:{{ funkwhale_frontend }}"
- /etc/localtime:/etc/localtime:ro
labels: labels:
traefik.http.routers.funkwhale_api.entrypoints: websecure traefik.http.routers.funkwhale_api.entrypoints: websecure
traefik.http.routers.funkwhale_api.rule: "Host(`api.{{ funkwhale_subdomain }}.{{ domain_name }}`)" traefik.http.routers.funkwhale_api.rule: "Host(`api.{{ funkwhale_subdomain }}.{{ domain_name }}`)"
@ -60,15 +59,16 @@ services:
- db - db
restart: unless-stopped restart: unless-stopped
front: nginx:
image: funkwhale/front:{{ funkwhale_version }} image: nginx
container_name: funkwhale_front container_name: funkwhale_nginx
env_file: ./conf.env env_file: ./conf.env
volumes: volumes:
- ./nginx.conf:/etc/nginx/conf.d/default.conf:ro
- ./funkwhale_proxy.conf:/etc/nginx/funkwhale_proxy.conf:ro
- "{{ funkwhale_import_music_directory_host }}:{{ funkwhale_import_music_directory }}:ro" - "{{ funkwhale_import_music_directory_host }}:{{ funkwhale_import_music_directory }}:ro"
- "static:/usr/share/nginx/html/staticfiles:ro" - "static:{{ funkwhale_static_root }}"
- /etc/timezone:/etc/timezone:ro - "frontend:{{ funkwhale_frontend }}"
- /etc/localtime:/etc/localtime:ro
labels: labels:
traefik.http.routers.funkwhale.entrypoints: websecure traefik.http.routers.funkwhale.entrypoints: websecure
traefik.http.routers.funkwhale.rule: "Host(`{{ funkwhale_subdomain }}.{{ domain_name }}`)" traefik.http.routers.funkwhale.rule: "Host(`{{ funkwhale_subdomain }}.{{ domain_name }}`)"
@ -84,8 +84,6 @@ services:
env_file: ./conf.env env_file: ./conf.env
volumes: volumes:
- redis:/data - redis:/data
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
networks: networks:
- db - db
restart: unless-stopped restart: unless-stopped
@ -97,10 +95,6 @@ services:
POSTGRES_USER: funkwhale POSTGRES_USER: funkwhale
POSTGRES_DB: funkwhale POSTGRES_DB: funkwhale
POSTGRES_PASSWORD: "{{ funkwhale_db_password }}" POSTGRES_PASSWORD: "{{ funkwhale_db_password }}"
TZ: Europe/Paris
PGTZ: Europe/Paris
# Don't mount /etc/localtime, it screws with pg_timezone_names
# TZ and PGTZ environment are sufficient.
volumes: volumes:
- db:/var/lib/postgresql/data - db:/var/lib/postgresql/data
networks: networks:

View File

@ -0,0 +1,98 @@
upstream funkwhale-api {
# depending on your setup, you may want to update this
server funkwhale_api:{{ funkwhale_api_port }};
}
# required for websocket support
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
server {
listen {{ funkwhale_nginx_port }};
server_name {{ funkwhale_subdomain }}.{{ domain_name }};
# TLS
# Feel free to use your own configuration for SSL here or simply remove the
# lines and move the configuration to the previous server block if you
# don't want to run funkwhale behind https (this is not recommended)
# have a look here for let's encrypt configuration:
# https://certbot.eff.org/all-instructions/#debian-9-stretch-nginx
root {{ funkwhale_frontend }};
# If you are using S3 to host your files, remember to add your S3 URL to the
# media-src and img-src headers (e.g. img-src 'self' https://<your-S3-URL> data:)
add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' https://s3.fr-par.scw.cloud data:; font-src 'self' data:; object-src 'none'; media-src 'self' https://s3.fr-par.scw.cloud data:";
add_header Referrer-Policy "strict-origin-when-cross-origin";
location / {
include /etc/nginx/funkwhale_proxy.conf;
# this is needed if you have file import via upload enabled
client_max_body_size {{ nginx_max_body_size }};
proxy_pass http://funkwhale-api/;
}
location /front/ {
add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self' data:; object-src 'none'; media-src 'self' data:";
add_header Referrer-Policy "strict-origin-when-cross-origin";
add_header Service-Worker-Allowed "/";
add_header X-Frame-Options "ALLOW";
alias /frontend/;
expires 30d;
add_header Pragma public;
add_header Cache-Control "public, must-revalidate, proxy-revalidate";
}
location /front/embed.html {
add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self' data:; object-src 'none'; media-src 'self' data:";
add_header Referrer-Policy "strict-origin-when-cross-origin";
add_header X-Frame-Options "ALLOW";
alias /frontend/embed.html;
expires 30d;
add_header Pragma public;
add_header Cache-Control "public, must-revalidate, proxy-revalidate";
}
location /federation/ {
include /etc/nginx/funkwhale_proxy.conf;
proxy_pass http://funkwhale-api/federation/;
}
# You can comment this if you do not plan to use the Subsonic API
location /rest/ {
include /etc/nginx/funkwhale_proxy.conf;
proxy_pass http://funkwhale-api/api/subsonic/rest/;
}
location /.well-known/ {
include /etc/nginx/funkwhale_proxy.conf;
proxy_pass http://funkwhale-api/.well-known/;
}
location ~ /_protected/media/(.+) {
internal;
# Needed to ensure DSub auth isn't forwarded to S3/Minio, see #932
proxy_set_header Authorization "";
proxy_pass $1;
}
location /_protected/music {
# this is an internal location that is used to serve
# audio files once correct permission / authentication
# has been checked on API side
# Set this to the same value as your MUSIC_DIRECTORY_PATH setting
internal;
alias {{ funkwhale_import_music_directory }};
}
location /staticfiles/ {
# django static files
alias {{ funkwhale_static_root }}}/;
}
}

View File

@ -1,15 +1,12 @@
funkwhale_version: 1.4.0 funkwhale_version: 1.1.1
funkwhale_api_port: 5000 funkwhale_api_port: 5000
funkwhale_nginx_port: 80 funkwhale_nginx_port: 80
funkwhale_static_root: /static funkwhale_static_root: /static
funkwhale_import_music_directory: /import funkwhale_import_music_directory: /import
funkwhale_import_music_directory_host: "{{ funkwhale_folder_name }}/import" funkwhale_import_music_directory_host: "{{ funkwhale_folder_name }}/import"
funkwhale_folder_name: "{{ docker_files }}/funkwhale" funkwhale_folder_name: "{{ docker_files }}/funkwhale"
funkwhale_frontend: /frontend
funkwhale_subdomain: music funkwhale_subdomain: music
nginx_max_body_size: 100M nginx_max_body_size: 100M
postgres_version: 15 postgres_version: 13
redis_version: 6 redis_version: 6
deemix_folder_path: /home/{{ base_user_name }}/deemix
deemix_songs_path: "{{ deemix_folder_path }}/songs"
beets_config_folder: "/home/{{ base_user_name }}/.config/beets"
beets_log_file: "/var/log/beets.log"

View File

@ -19,18 +19,17 @@ services:
environment: environment:
USER_UID: 1000 USER_UID: 1000
USER_GID: 1000 USER_GID: 1000
# See https://docs.gitea.com/installation/install-with-docker#managing-deployments-with-environment-variables DB_TYPE: postgres
GITEA__database__DB_TYPE: postgres DB_HOST: db:5432
GITEA__database__DB_HOST: db:5432 APP_NAME: {{ gitea_name }}
GITEA__database__NAME: gitea RUN_MODE: prod
GITEA__database__USER: gitea DOMAIN: {{ gitea_subdomain }}.{{ domain_name }}
GITEA__database__PASSWD: "{{ gitea_db_password }}" SSH_DOMAIN: {{ gitea_subdomain }}.{{ domain_name }}
GITEA__DEFAULT__APP_NAME: {{ gitea_name }} ROOT_URL: https://{{ gitea_subdomain }}.{{ domain_name }}
GITEA__DEFAULT__RUN_MODE: prod DISABLE_REGISTRATION: "true"
GITEA__server__DOMAIN: {{ gitea_subdomain }}.{{ domain_name }} DB_NAME: gitea
GITEA__server__SSH_DOMAIN: {{ gitea_subdomain }}.{{ domain_name }} DB_USER: gitea
GITEA__server__ROOT_URL: https://{{ gitea_subdomain }}.{{ domain_name }} DB_PASSWD: "{{ gitea_db_password }}"
GITEA__service__DISABLE_REGISTRATION: "true"
networks: networks:
- proxy - proxy
- db - db

View File

@ -1,4 +1,4 @@
gitea_version: "1.20" gitea_version: "1.14.1"
gitea_folder_name: "{{ docker_files }}/gitea" gitea_folder_name: "{{ docker_files }}/gitea"
postgres_version: "13" postgres_version: "13"
gitea_name: Chostea gitea_name: Chostea

View File

@ -0,0 +1,7 @@
#!/bin/sh
set -e
echo "Launching supercronic..."
supercronic /var/www/crontab &
echo "Launching Grav..."
exec $@

View File

@ -0,0 +1,41 @@
---
- name: Create Grav directory
file:
path: "{{ grav_folder_name }}"
state: directory
owner: "{{ base_user_name }}"
group: "{{ base_user_name }}"
mode: 0755
- name: Copy Grav templates
template:
src: "{{ item }}"
# Remove .j2 extension
dest: "{{ grav_folder_name }}/{{ (item | splitext)[0] }}"
owner: "{{ base_user_name }}"
group: "{{ base_user_name }}"
mode: 0644
loop:
- docker-compose.yml.j2
- Dockerfile.j2
- name: Copy Grav entrypoint
copy:
src: entrypoint.sh
dest: "{{ grav_folder_name }}/entrypoint.sh"
owner: "{{ base_user_name }}"
group: "{{ base_user_name }}"
mode: 0644
- name: Build Grav
community.docker.docker_compose:
project_src: "{{ grav_folder_name }}"
build: yes
- name: Ensure container is up to date
community.docker.docker_compose:
project_src: "{{ traefik_folder_name }}"
remove_orphans: yes
pull: yes
recreate: smart
state: present

View File

@ -0,0 +1,92 @@
FROM php:7.4-apache
LABEL maintainer="Andy Miller <rhuk@getgrav.org> (@rhukster)"
# Enable Apache Rewrite + Expires Module
RUN a2enmod rewrite expires && \
sed -i 's/ServerTokens OS/ServerTokens ProductOnly/g' \
/etc/apache2/conf-available/security.conf
# Install dependencies
RUN apt-get update && apt-get install -y --no-install-recommends \
unzip \
libfreetype6-dev \
libjpeg62-turbo-dev \
libpng-dev \
libyaml-dev \
libzip4 \
libzip-dev \
zlib1g-dev \
libicu-dev \
g++ \
git \
cron \
vim \
&& docker-php-ext-install opcache \
&& docker-php-ext-configure intl \
&& docker-php-ext-install intl \
&& docker-php-ext-configure gd --with-freetype=/usr/include/ --with-jpeg=/usr/include/ \
&& docker-php-ext-install -j$(nproc) gd \
&& docker-php-ext-install zip \
&& rm -rf /var/lib/apt/lists/*
# set recommended PHP.ini settings
# see https://secure.php.net/manual/en/opcache.installation.php
RUN { \
echo 'opcache.memory_consumption=128'; \
echo 'opcache.interned_strings_buffer=8'; \
echo 'opcache.max_accelerated_files=4000'; \
echo 'opcache.revalidate_freq=2'; \
echo 'opcache.fast_shutdown=1'; \
echo 'opcache.enable_cli=1'; \
echo 'upload_max_filesize=128M'; \
echo 'post_max_size=128M'; \
echo 'expose_php=off'; \
} > /usr/local/etc/php/conf.d/php-recommended.ini
RUN pecl install apcu \
&& pecl install yaml-2.0.4 \
&& docker-php-ext-enable apcu yaml
# Install Supercronic
ENV SUPERCRONIC_URL=https://github.com/aptible/supercronic/releases/download/v0.1.12/supercronic-linux-amd64 \
SUPERCRONIC=supercronic-linux-amd64 \
SUPERCRONIC_SHA1SUM=048b95b48b708983effb2e5c935a1ef8483d9e3e
RUN curl -fsSLO "$SUPERCRONIC_URL" \
&& echo "${SUPERCRONIC_SHA1SUM} ${SUPERCRONIC}" | sha1sum -c - \
&& chmod +x "$SUPERCRONIC" \
&& mv "$SUPERCRONIC" "/usr/local/bin/${SUPERCRONIC}" \
&& ln -s "/usr/local/bin/${SUPERCRONIC}" /usr/local/bin/supercronic
# Create cron job for Grav maintenance scripts
RUN echo "*/30 * * * * cd /var/www/html;/usr/local/bin/php bin/grav scheduler 1>> /dev/null 2>&1" > /var/www/crontab
COPY entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh
RUN sed -i s/80/{{ grav_internal_port }}/g /etc/apache2/sites-enabled/000-default.conf /etc/apache2/ports.conf
# Set user to www-data
RUN chown www-data:www-data /var/www
USER www-data
# Set Grav version
ARG GRAV_VERSION={{ grav_version }}
# Install grav
WORKDIR /var/www
RUN curl -o grav-admin.zip -SL https://getgrav.org/download/core/grav-admin/${GRAV_VERSION} && \
unzip grav-admin.zip && \
mv -T /var/www/grav-admin /var/www/html && \
rm grav-admin.zip
# Install plugins
RUN cd html && \
bin/gpm install admin
# provide container inside image for data persistance
VOLUME ["/var/www/html"]
ENTRYPOINT ["/entrypoint.sh"]
CMD ["apache2-foreground"]

View File

@ -0,0 +1,25 @@
version: "{{ compose_version }}"
networks:
proxy:
name: "{{ traefik_network }}"
volumes:
grav_lola:
name: grav_lola
services:
grav_lola:
image: grav:{{ grav_version }}
build: .
container_name: grav_lola
volumes:
- grav_lola:/var/www/html
networks:
- proxy
labels:
traefik.http.routers.grav.entrypoints: websecure
traefik.http.routers.grav.rule: "Host(`blog.leaula.me`)"
traefik.http.services.grav.loadbalancer.server.port: "{{ grav_internal_port }}"
traefik.enable: true
restart: unless-stopped

View File

@ -0,0 +1,3 @@
grav_internal_port: 8080
grav_version: 1.7.13
grav_folder_name: "{{ docker_files }}/grav"

View File

@ -1,37 +0,0 @@
---
- name: Create Hugo directory
file:
path: "{{ hugo_folder_name }}"
state: directory
owner: "{{ base_user_name }}"
group: "{{ base_user_name }}"
mode: 0755
- name: Copy Hugo Compose file
template:
src: docker-compose.yml.j2
# Remove .j2 extension
dest: "{{ hugo_folder_name }}/docker-compose.yml"
owner: "{{ base_user_name }}"
group: "{{ base_user_name }}"
mode: 0644
- name: Clone blog
ansible.builtin.git:
repo: "{{ repository_url }}"
dest: "{{ hugo_website }}"
force: yes
ignore_errors: yes
- name: Pull new stuff
shell:
cmd: git pull
chdir: "{{ hugo_website }}"
- name: Ensure container is up to date
community.docker.docker_compose:
project_src: "{{ hugo_folder_name }}"
remove_orphans: yes
pull: yes
recreate: smart
state: present

View File

@ -1,54 +0,0 @@
version: "{{ compose_version }}"
networks:
proxy:
name: "{{ traefik_network }}"
# Use a bind mount for Hugo data, easier to pull new versions of blog
volumes:
website_files:
driver: local
driver_opts:
type: none
device: "{{ hugo_website }}"
o: bind
website_public:
driver: local
driver_opts:
type: none
device: "{{ hugo_website }}/public"
o: bind
services:
builder:
container_name: hugo_builder
image: "klakegg/hugo:{{ hugo_version }}"
volumes:
- website_files:/src
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
# include content with publishdate in the future
command: --buildFuture
# Hugo will build only
# when triggered
restart: on-failure
front:
container_name: hugo_front
image: nginx:alpine
volumes:
- website_public:/usr/share/nginx/html:ro
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
labels:
traefik.http.routers.hugo.entrypoints: websecure
traefik.http.routers.hugo.rule: "Host(`blog.{{ domain_name }}`)"
traefik.http.services.hugo.loadbalancer.server.port: 80
traefik.enable: true
networks:
- proxy
read_only: true
tmpfs:
- /var/cache/nginx
- /run
restart: unless-stopped

View File

@ -1,6 +0,0 @@
hugo_folder_name: "{{ docker_files }}/hugo"
hugo_website: "{{ hugo_folder_name }}/website"
# Use extended edition with Git inside
# to read git info (useful for lastmod)
hugo_version: 0.105.0-ext-alpine
repository_url: https://git.chosto.me/Chosto/blog.git

View File

@ -18,8 +18,6 @@ services:
image: "lycheeorg/lychee:{{ lychee_version }}" image: "lycheeorg/lychee:{{ lychee_version }}"
volumes: volumes:
- uploads:/uploads - uploads:/uploads
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
labels: labels:
traefik.http.routers.lychee.entrypoints: websecure traefik.http.routers.lychee.entrypoints: websecure
traefik.http.routers.lychee.rule: "Host(`pic.{{ domain_name }}`)" traefik.http.routers.lychee.rule: "Host(`pic.{{ domain_name }}`)"
@ -38,9 +36,7 @@ services:
APP_NAME: Lychee APP_NAME: Lychee
APP_ENV: production APP_ENV: production
APP_DEBUG: "false" APP_DEBUG: "false"
APP_URL: "https://pic.{{ domain_name }}"
STARTUP_DELAY: 5 STARTUP_DELAY: 5
TRUSTED_PROXIES: "*"
networks: networks:
- proxy - proxy
- db - db
@ -51,7 +47,6 @@ services:
container_name: lychee_db container_name: lychee_db
volumes: volumes:
- db:/var/lib/postgresql/data - db:/var/lib/postgresql/data
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro - /etc/localtime:/etc/localtime:ro
environment: environment:
POSTGRES_USER: lychee POSTGRES_USER: lychee

View File

@ -1,3 +1,3 @@
lychee_folder_name: "{{ docker_files }}/lychee" lychee_folder_name: "{{ docker_files }}/lychee"
lychee_version: v5.5.1 lychee_version: v4.3.0
postgres_version: 13 postgres_version: 13

View File

@ -1,26 +0,0 @@
---
- name: Create minecraft directory
file:
path: "{{ minecraft_folder_name }}"
state: directory
owner: "{{ base_user_name }}"
group: "{{ base_user_name }}"
mode: 0755
- name: Copy minecraft Compose file
template:
src: docker-compose.yml.j2
# Remove .j2 extension
dest: "{{ minecraft_folder_name }}/docker-compose.yml"
owner: "{{ base_user_name }}"
group: "{{ base_user_name }}"
mode: 0644
- name: Ensure container is up to date
community.docker.docker_compose:
project_src: "{{ minecraft_folder_name }}"
remove_orphans: yes
pull: yes
recreate: smart
state: present
stopped: true

View File

@ -1,25 +0,0 @@
version: "{{ compose_version }}"
volumes:
data:
name: minecraft_data
services:
lychee:
container_name: minecraft
image: "itzg/minecraft-server"
environment:
TYPE: "PAPER"
EULA: "TRUE"
SNOOPER_ENABLED: "false"
DIFFICULTY: "normal"
MOTD: "Le gentil serveur de Momo Pierre et Quentin"
WHITELIST: "Joyau,MissPlumelle,XxGasKanxX"
# Ultra SetHome,ActionBar
SPIGET_RESOURCES: 96934,2661
ports:
- "25565:25565"
volumes:
- data:/data
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro

View File

@ -1 +0,0 @@
minecraft_folder_name: "{{ docker_files }}/minecraft"

View File

@ -1,13 +0,0 @@
# global proxy conf
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $host:$server_port;
proxy_set_header X-Forwarded-Port $server_port;
proxy_redirect off;
# websocket support
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;

View File

@ -1,38 +0,0 @@
- name: Update deemix configuration file
template:
src: deemix_config.json.j2
dest: "{{ deemix_folder_path }}/config/config.json"
owner: "{{ base_user_name }}"
group: "{{ base_user_name }}"
mode: 0644
- name: Update ARL token file
template:
src: arl.j2
dest: "{{ deemix_folder_path }}/config/.arl"
owner: "{{ base_user_name }}"
group: "{{ base_user_name }}"
mode: 0644
- name: Filter non-existing albums
shell:
chdir: ~/documents/code/funkwhale-playlist-import
cmd: ./exclude-existing-albums.py -s deezer > /tmp/unique_albums.txt
stdin: "{{ lookup('file', 'files/albums.txt') }}"
register: unique_albums
delegate_to: localhost
become: false
- name: Download required albums
# So that files are written with base user perms
become: true
become_user: "{{ base_user_name }}"
shell:
cmd: "deemix --portable -p {{ deemix_songs_path }} {{ item }}"
chdir: "{{ deemix_folder_path }}"
with_items: "{{ lookup('file', '/tmp/unique_albums.txt').splitlines() }}"
register: output_deemix
- name: Show download state
debug:
msg: "{{ output_deemix }}"

View File

@ -1,33 +0,0 @@
- name: Update beets configuration file
template:
src: beets_config.yaml.j2
dest: "{{ beets_config_folder }}/config.yaml"
owner: "{{ base_user_name }}"
group: "{{ base_user_name }}"
mode: 0644
- name: Make sure logs are writable
file:
path: "{{ beets_log_file }}"
state: touch
owner: "{{ base_user_name }}"
group: "{{ base_user_name }}"
mode: 0644
- name: Tag music (auto-tag when confidence > 90%, use as-is otherwise)
# So that files are written with user perms
become: yes
become_user: "{{ base_user_name }}"
shell:
# Quiet mode = do not ask anything to the user
# Default are in configuration file
cmd: "beet -c {{ beets_config_folder }}/config.yaml import -q {{ deemix_songs_path }}"
- name: Import music into Funkwhale
shell:
cmd: "docker-compose exec -T api funkwhale-manage import_files {{ funkwhale_import_library_id }} {{ funkwhale_import_music_directory }} --recursive --noinput --prune"
chdir: "{{ funkwhale_folder_name }}"
- name: Delete files once imported
shell:
cmd: "rm -rf {{ funkwhale_import_music_directory_host }}/*"

View File

@ -1 +0,0 @@
{{ arl_deezer_token }}

View File

@ -1,45 +0,0 @@
directory: {{ funkwhale_import_music_directory_host }}
threaded: yes
plugins: ftintitle embedart duplicates fetchart lastgenre acousticbrainz
match:
# Allow 90% confidence for auto-tagging
strong_rec_thresh: 0.10
max_rec:
media: strong
label: strong
year: strong
preferred:
# I have only a few physical CD
media: ['Digital Media']
discogs:
user_token: {{ discogs_user_token }}
acoustid:
apikey: {{ acoustid_api_key }}
ui:
color: yes
import:
# Always move files to Funkwhale import directory
move: yes
# Previous import interrupted, start for the begining
# Should not really change because files are deleted after import
resume: no
# Merge albums if they look the same
duplicate_action: merge
# Use-as-is if no release found (could be then added to MusicBrainz)
# Reasonable because Deezer metadata is good enough in most cases
quiet_fallback: asis
log: {{ beets_log_file }}
# Preferred languages for aliases (in case of foreign artist with another alphabet for example)
languages: fr en
lastgenre:
canonical: yes
count: 10
force: yes
source: album

View File

@ -1,78 +0,0 @@
{
"downloadLocation": "{{ deemix_songs_path }}",
"tracknameTemplate": "%artist% - %title%",
"albumTracknameTemplate": "%tracknumber% - %title%",
"playlistTracknameTemplate": "%position% - %artist% - %title%",
"createPlaylistFolder": true,
"playlistNameTemplate": "%playlist%",
"createArtistFolder": false,
"artistNameTemplate": "%artist%",
"createAlbumFolder": true,
"albumNameTemplate": "%artist% - %album%",
"createCDFolder": true,
"createStructurePlaylist": false,
"createSingleFolder": false,
"padTracks": true,
"paddingSize": "0",
"illegalCharacterReplacer": "_",
"queueConcurrency": 3,
"maxBitrate": "3",
"fallbackBitrate": true,
"fallbackSearch": false,
"logErrors": true,
"logSearched": false,
"overwriteFile": "n",
"createM3U8File": false,
"playlistFilenameTemplate": "playlist",
"syncedLyrics": false,
"embeddedArtworkSize": 800,
"embeddedArtworkPNG": false,
"localArtworkSize": 1400,
"localArtworkFormat": "jpg",
"saveArtwork": true,
"coverImageTemplate": "cover",
"saveArtworkArtist": false,
"artistImageTemplate": "folder",
"jpegImageQuality": 80,
"dateFormat": "Y-M-D",
"albumVariousArtists": true,
"removeAlbumVersion": false,
"removeDuplicateArtists": false,
"featuredToTitle": "0",
"titleCasing": "nothing",
"artistCasing": "nothing",
"executeCommand": "",
"tags": {
"title": true,
"artist": true,
"album": true,
"cover": true,
"trackNumber": true,
"trackTotal": false,
"discNumber": true,
"discTotal": false,
"albumArtist": true,
"genre": true,
"year": true,
"date": true,
"explicit": false,
"isrc": true,
"length": true,
"barcode": true,
"bpm": true,
"replayGain": false,
"label": true,
"lyrics": false,
"syncedLyrics": false,
"copyright": false,
"composer": false,
"involvedPeople": false,
"source": false,
"rating": false,
"savePlaylistAsCompilation": false,
"useNullSeparator": false,
"saveID3v1": true,
"multiArtistSeparator": "default",
"singleAlbumArtist": false,
"coverDescriptionUTF8": false
}

View File

@ -23,4 +23,3 @@
pull: yes pull: yes
recreate: smart recreate: smart
state: present state: present
ignore_errors: yes

View File

@ -15,16 +15,11 @@ services:
container_name: nextcloud container_name: nextcloud
volumes: volumes:
- nextcloud:/var/www/html - nextcloud:/var/www/html
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
labels: labels:
traefik.http.routers.cloud.entrypoints: websecure traefik.http.routers.cloud.entrypoints: websecure
traefik.http.routers.cloud.rule: "Host(`cloud.{{ domain_name }}`)" traefik.http.routers.cloud.rule: "Host(`cloud1.{{ domain_name }}`)"
traefik.http.services.cloud.loadbalancer.server.port: 80 traefik.http.services.cloud.loadbalancer.server.port: 80
traefik.enable: true traefik.enable: true
environment:
OVERWRITECLIURL: https://cloud.chosto.me
OVERWRITEPROTOCOL: https
networks: networks:
- proxy - proxy
restart: unless-stopped restart: unless-stopped

View File

@ -1,2 +1,2 @@
nextcloud_version: 25 nextcloud_version: 21
nextcloud_folder_name: "{{ docker_files }}/nextcloud" nextcloud_folder_name: "{{ docker_files }}/nextcloud"

View File

@ -1,29 +0,0 @@
---
- name: Create Peertube directory
file:
path: "{{ peertube_folder_name }}"
state: directory
owner: "{{ base_user_name }}"
group: "{{ base_user_name }}"
mode: 0755
- name: Copy Peertube templates (env file and Compose)
template:
src: "{{ item }}"
# Remove .j2 extension
dest: "{{ peertube_folder_name }}/{{ (item | splitext)[0] }}"
owner: "{{ base_user_name }}"
group: "{{ base_user_name }}"
mode: 0644
loop:
- docker-compose.yml.j2
- conf.env.j2
- peertube.conf.j2
- name: Ensure container is up to date
community.docker.docker_compose:
project_src: "{{ peertube_folder_name }}"
remove_orphans: yes
pull: yes
recreate: smart
state: present

View File

@ -1,36 +0,0 @@
# Database / Postgres service configuration
POSTGRES_USER=peertube
POSTGRES_PASSWORD={{ peertube_db_password }}
# Postgres database name "peertube"
POSTGRES_DB=peertube
PEERTUBE_DB_USERNAME=peertube
PEERTUBE_DB_PASSWORD={{ peertube_db_password }}
PEERTUBE_DB_SSL=false
PEERTUBE_DB_HOSTNAME={{ peertube_db_container_name }}
PEERTUBE_SECRET={{ peertube_secret }}
# Server configuration
PEERTUBE_WEBSERVER_HOSTNAME={{ peertube_subdomain }}.{{ domain_name }}
PEERTUBE_WEBSERVER_PORT=9000
PEERTUBE_WEBSERVER_HTTPS=false
# If you need more than one IP as trust_proxy
# pass them as a comma separated array:
PEERTUBE_TRUST_PROXY=["127.0.0.1", "loopback", "172.18.0.0/16"]
# E-mail configuration
# If you use a Custom SMTP server
PEERTUBE_SMTP_USERNAME={{ peertube_subdomain }}
PEERTUBE_SMTP_PASSWORD={{ peertube_mail_password }}
PEERTUBE_SMTP_HOSTNAME=mail.gandi.net
PEERTUBE_SMTP_PORT=587
PEERTUBE_SMTP_FROM={{ peertube_subdomain }}@{{ domain_name }}
PEERTUBE_SMTP_TLS=true
PEERTUBE_SMTP_DISABLE_STARTTLS=false
PEERTUBE_ADMIN_EMAIL=quentinduchemin@tuta.io
# /!\ Prefer to use the PeerTube admin interface to set the following configurations /!\
#PEERTUBE_SIGNUP_ENABLED=true
#PEERTUBE_TRANSCODING_ENABLED=true
#PEERTUBE_CONTACT_FORM_ENABLED=true
PEERTUBE_REDIS_HOSTNAME={{ peertube_redis_container }}

View File

@ -1,75 +0,0 @@
version: "{{ compose_version }}"
networks:
proxy:
name: "{{ traefik_network }}"
db:
name: peertube_db
redis:
name: peertube_redis
volumes:
db:
name: peertube_db
assets:
name: peertube_assets
redis:
name: peertube_redis
data:
name: peertube_data
config:
name: peertube_config
services:
# You can comment this webserver section if you want to use another webserver/proxy or test PeerTube in local
webserver:
image: chocobozzz/peertube-webserver:latest
volumes:
- ./peertube.conf:/etc/nginx/conf.d/peertube.template
- assets:/var/www/peertube/peertube-latest/client/dist:ro
- data:/var/www/peertube/storage
env_file: conf.env
labels:
traefik.http.routers.peertube.entrypoints: websecure
traefik.http.routers.peertube.rule: "Host(`{{ peertube_subdomain }}.{{ domain_name }}`)"
traefik.http.services.peertube.loadbalancer.server.port: 80
traefik.enable: true
networks:
- proxy
restart: unless-stopped
app:
image: "chocobozzz/peertube:{{ peertube_version }}-bookworm"
container_name: peertube
networks:
- proxy
- db
- redis
volumes:
- assets:/app/client/dist
- data:/data
- config:/config
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
env_file: conf.env
restart: unless-stopped
db:
image: "postgres:{{ postgres_version }}"
container_name: "{{ peertube_db_container_name }}"
env_file: conf.env
volumes:
- db:/var/lib/postgresql/data
networks:
- db
restart: unless-stopped
redis:
image: "redis:{{ redis_version }}"
container_name: "{{ peertube_redis_container }}"
volumes:
- redis:/data
networks:
- db
restart: unless-stopped

View File

@ -1,213 +0,0 @@
# Minimum Nginx version required: 1.13.0 (released Apr 25, 2017)
# Please check your Nginx installation features the following modules via 'nginx -V':
# STANDARD HTTP MODULES: Core, Proxy, Rewrite, Access, Gzip, Headers, HTTP/2, Log, Real IP, SSL, Thread Pool, Upstream, AIO Multithreading.
# THIRD PARTY MODULES: None.
upstream backend {
server peertube:9000;
}
server {
listen 80;
listen [::]:80;
server_name tube.chosto.me;
##
# Application
##
location @api {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
client_max_body_size 10G; # default is 1M
proxy_connect_timeout 10m;
proxy_send_timeout 10m;
proxy_read_timeout 10m;
send_timeout 10m;
proxy_pass http://backend;
}
location / {
try_files /dev/null @api;
}
location = /api/v1/videos/upload-resumable {
client_max_body_size 0;
proxy_request_buffering off;
try_files /dev/null @api;
}
location ~ ^/api/v1/videos/(upload|([^/]+/studio/edit))$ {
limit_except POST HEAD { deny all; }
# This is the maximum upload size, which roughly matches the maximum size of a video file.
# Note that temporary space is needed equal to the total size of all concurrent uploads.
# This data gets stored in /var/lib/nginx by default, so you may want to put this directory
# on a dedicated filesystem.
client_max_body_size 12G; # default is 1M
add_header X-File-Maximum-Size 8G always; # inform backend of the set value in bytes before mime-encoding (x * 1.4 >= client_max_body_size)
try_files /dev/null @api;
}
location ~ ^/api/v1/runners/jobs/[^/]+/(update|success)$ {
client_max_body_size 12G; # default is 1M
add_header X-File-Maximum-Size 8G always; # inform backend of the set value in bytes before mime-encoding (x * 1.4 >= client_max_body_size)
try_files /dev/null @api;
}
location ~ ^/api/v1/(videos|video-playlists|video-channels|users/me) {
client_max_body_size 12G; # default is 1M
add_header X-File-Maximum-Size 12G always; # inform backend of the set value in bytes before mime-encoding (x * 1.4 >= client_max_body_size)
try_files /dev/null @api;
}
##
# Websocket
##
location @api_websocket {
proxy_http_version 1.1;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_pass http://backend;
}
location /socket.io {
try_files /dev/null @api_websocket;
}
location /tracker/socket {
# Peers send a message to the tracker every 15 minutes
# Don't close the websocket before then
proxy_read_timeout 15m; # default is 60s
try_files /dev/null @api_websocket;
}
# Plugin websocket routes
location ~ ^/plugins/[^/]+(/[^/]+)?/ws/ {
try_files /dev/null @api_websocket;
}
##
# Performance optimizations
# For extra performance please refer to https://github.com/denji/nginx-tuning
##
root /var/www/peertube/storage;
# Enable compression for JS/CSS/HTML, for improved client load times.
# It might be nice to compress JSON/XML as returned by the API, but
# leaving that out to protect against potential BREACH attack.
gzip on;
gzip_vary on;
gzip_types # text/html is always compressed by HttpGzipModule
text/css
application/javascript
font/truetype
font/opentype
application/vnd.ms-fontobject
image/svg+xml;
gzip_min_length 1000; # default is 20 bytes
gzip_buffers 16 8k;
gzip_comp_level 2; # default is 1
client_body_timeout 30s; # default is 60
client_header_timeout 10s; # default is 60
send_timeout 10s; # default is 60
keepalive_timeout 10s; # default is 75
resolver_timeout 10s; # default is 30
reset_timedout_connection on;
proxy_ignore_client_abort on;
tcp_nopush on; # send headers in one piece
tcp_nodelay on; # don't buffer data sent, good for small data bursts in real time
# If you have a small /var/lib partition, it could be interesting to store temp nginx uploads in a different place
# See https://nginx.org/en/docs/http/ngx_http_core_module.html#client_body_temp_path
#client_body_temp_path /var/www/peertube/storage/nginx/;
# Bypass PeerTube for performance reasons. Optional.
# Should be consistent with client-overrides assets list in client.ts server controller
location ~ ^/client/(assets/images/(icons/icon-36x36\.png|icons/icon-48x48\.png|icons/icon-72x72\.png|icons/icon-96x96\.png|icons/icon-144x144\.png|icons/icon-192x192\.png|icons/icon-512x512\.png|logo\.svg|favicon\.png|default-playlist\.jpg|default-avatar-account\.png|default-avatar-account-48x48\.png|default-avatar-video-channel\.png|default-avatar-video-channel-48x48\.png))$ {
add_header Cache-Control "public, max-age=31536000, immutable"; # Cache 1 year
root /var/www/peertube;
try_files /storage/client-overrides/$1 /peertube-latest/client/dist/$1 @api;
}
# Bypass PeerTube for performance reasons. Optional.
location ~ ^/client/(.*\.(js|css|png|svg|woff2|otf|ttf|woff|eot))$ {
add_header Cache-Control "public, max-age=31536000, immutable"; # Cache 1 year
alias /var/www/peertube/peertube-latest/client/dist/$1;
}
location ~ ^(/static/(webseed|web-videos|streaming-playlists)/private/)|^/download {
# We can't rate limit a try_files directive, so we need to duplicate @api
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_limit_rate 5M;
proxy_pass http://backend;
}
# Bypass PeerTube for performance reasons. Optional.
location ~ ^/static/(webseed|web-videos|redundancy|streaming-playlists)/ {
limit_rate_after 5M;
set $peertube_limit_rate 5M;
# Use this line with nginx >= 1.17.0
limit_rate $peertube_limit_rate;
# Or this line with nginx < 1.17.0
# set $limit_rate $peertube_limit_rate;
if ($request_method = 'OPTIONS') {
add_header Access-Control-Allow-Origin '*';
add_header Access-Control-Allow-Methods 'GET, OPTIONS';
add_header Access-Control-Allow-Headers 'Range,DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type';
add_header Access-Control-Max-Age 1728000; # Preflight request can be cached 20 days
add_header Content-Type 'text/plain charset=UTF-8';
add_header Content-Length 0;
return 204;
}
if ($request_method = 'GET') {
add_header Access-Control-Allow-Origin '*';
add_header Access-Control-Allow-Methods 'GET, OPTIONS';
add_header Access-Control-Allow-Headers 'Range,DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type';
# Don't spam access log file with byte range requests
access_log off;
}
# Enabling the sendfile directive eliminates the step of copying the data into the buffer
# and enables direct copying data from one file descriptor to another.
sendfile on;
sendfile_max_chunk 1M; # prevent one fast connection from entirely occupying the worker process. should be > 800k.
aio threads;
# web-videos is the name of the directory mapped to the `storage.web_videos` key in your PeerTube configuration
rewrite ^/static/webseed/(.*)$ /web-videos/$1 break;
rewrite ^/static/(.*)$ /$1 break;
try_files $uri @api;
}
}

View File

@ -1,8 +0,0 @@
peertube_version: "v6.0.3"
peertube_folder_name: "{{ docker_files }}/peertube"
peertube_subdomain: tube
peertube_db_container_name: "peertube_db"
postgres_version: "13"
redis_version: "6"
peertube_redis_container: "peertube_redis"
peertube_instance_name: "Babil"

View File

@ -1,10 +0,0 @@
min_cryptography_lib: 1.2.3
autorestic_base: /var/lib/autorestic
autorestic_config_path: "{{ autorestic_base }}/autorestic.yml"
autorestic_version: 1.7.7
autorestic_path: /usr/local/bin/autorestic
repository_path: /data
dbdumps_path: /dbdumps
# Default password to derive encryption key for repository (confidentiality)
restic_password: "{{ restic_password }}"

View File

@ -1,78 +0,0 @@
- name: Ensure necessary directories exists
file:
path: "{{ item }}"
state: directory
loop:
- "{{ repository_path }}"
- "{{ dbdumps_path }}"
- "{{ autorestic_base }}"
- name: Download and install restic
apt:
name: restic
update_cache: yes
- name: Install bzip2
apt:
name: bzip2
update_cache: yes
no_log: true
- name: Download autorestic
get_url:
url: "https://github.com/cupcakearmy/autorestic/releases/download/v{{ autorestic_version }}/autorestic_{{ autorestic_version }}_linux_amd64.bz2"
dest: /tmp/autorestic.bz2
- name: Extract and install autorestic executable
shell: "bzcat /tmp/autorestic.bz2 > {{ autorestic_path }}"
- name: Ensure autorestic has executable bit
file:
path: "{{ autorestic_path }}"
mode: '0755'
- name: Copy configuration
template:
src: "autorestic.yml"
dest: "{{ autorestic_config_path }}"
- name: Copy scripts
template:
src: "{{ item }}"
dest: "{{ autorestic_base }}"
mode: 0755
loop:
- backup_db.sh
- start_backup.sh
- name: Ensure scripts are executable
file:
path: "{{ autorestic_base }}/{{ item }}"
mode: 0755
loop:
- backup_db.sh
- start_backup.sh
- name: Generate systemd timer and service
template:
src: "{{ item }}"
dest: "/etc/systemd/system"
loop:
- autorestic.service
- autorestic.timer
# Remove when PR #197 is merged
- name: Initialize Restic Rest repository
shell: "RESTIC_PASSWORD='{{ restic_password }}' restic -r {{ repository_path }} init"
failed_when: false
# Waiting for PR #197 to be merged
- name: Check configuration file is correct and create repositories if needed
shell: "autorestic -c {{ autorestic_config_path }} check"
- name: Ensure timer is activated
systemd:
name: autorestic.timer
enabled: true
state: started
daemon_reload: true

View File

@ -1,10 +0,0 @@
[Unit]
Description=Backups yay
[Service]
Type=oneshot
ExecStart={{ autorestic_base }}/start_backup.sh
# fail if backup takes more than 1 day
TimeoutStartSec=86400
IPAccounting=yes
MemoryAccounting=yes

View File

@ -1,9 +0,0 @@
[Unit]
Description=Backups with autorestic
[Timer]
# Trigger the autorestic cron's check every 10 minutes
OnCalendar=*:0/10:0
[Install]
WantedBy=timers.target

View File

@ -1,26 +0,0 @@
version: 2
global:
forget:
keep-hourly: 24
keep-daily: 7
keep-weekly: 4
keep-monthly: 12
backends:
pica03:
type: local
path: {{ repository_path }}
key: {{ restic_password }}
locations:
funkwhale:
from:
- /var/lib/docker/volumes/funkwhale_static
- {{ dbdumps_path }}/funkwhale_postgres
to: pica03
cron: 0 3 * * *
forget: "yes"
hooks:
before:
- {{ autorestic_base }}/backup_db.sh funkwhale_postgres postgresql

View File

@ -1,61 +0,0 @@
#!/usr/bin/env bash
# usage: <script> <container-name> <database-type>
#
# exports the database of a running docker container in a dump in $BACKUP_DIR/$CONTAINER_NAME/
BACKUP_DIR={{ dbdumps_path }}
# Check container existence
CONTAINER="$1"
if ! docker ps | grep -q "$CONTAINER"
then
echo "The container $CONTAINER doesn't exist or doesn't run"
exit 1
fi
# Check database type
TYPE="$2"
COMMAND=""
case "$TYPE" in
postgresql)
POSTGRES_USER=$(docker exec "$CONTAINER" env | grep POSTGRES_USER | cut -d= -f2)
COMMAND="pg_dumpall -c -U $POSTGRES_USER"
EXTENSION=sql
;;
mariadb)
MARIADB_USER=$(docker exec "$CONTAINER" env | grep MYSQL_USER | cut -d= -f2)
MARIADB_PASSWORD=$(docker exec "$CONTAINER" env | grep MYSQL_PASSWORD | cut -d= -f2)
COMMAND="mysqldump -u $MARIADB_USER --password=$MARIADB_PASSWORD --all-databases"
EXTENSION=sql
;;
mongodb)
COMMAND="mongodump --archive"
EXTENSION=mongodump
;;
ldap-config)
COMMAND="slapcat -n 0"
EXTENSION=config.ldif
;;
ldap-content)
COMMAND="slapcat -n 1"
EXTENSION=content.ldif
;;
*)
echo "I don't know $TYPE database type."
exit 1
esac
# Ensure directory exists
mkdir -p "$BACKUP_DIR/$CONTAINER"
# Export database
docker exec "$CONTAINER" $COMMAND > "$BACKUP_DIR/$CONTAINER/dump.$EXTENSION"
exit $?

View File

@ -1,25 +0,0 @@
#!/usr/bin/env sh
if [ ! -f /tmp/last_autorestic_check_date ]
then
touch /tmp/last_autorestic_check_date
fi
current_date=$(date +"%D")
last_autorestic_check_date=$(cat /tmp/last_autorestic_check_date)
{{ autorestic_path }} -c {{ autorestic_config_path }} --ci exec -av -- unlock
#Check only one time a day
if [ "$current_date" != "$last_autorestic_check_date" ]
then
#todo: use exec -- check when PR #253 is merged (more verbose)
{{ autorestic_path }} -c {{ autorestic_config_path }} check
if [ $? -ne 0 ]
then
exit
fi
echo $current_date > /tmp/last_autorestic_check_date
fi
{{ autorestic_path }} -vvv -c {{ autorestic_config_path }} --ci cron

View File

@ -12,7 +12,6 @@ services:
- "{{ traefik_http_port }}:80" - "{{ traefik_http_port }}:80"
- "{{ traefik_https_port}}:443" - "{{ traefik_https_port}}:443"
volumes: volumes:
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro - /etc/localtime:/etc/localtime:ro
- /var/run/docker.sock:/var/run/docker.sock - /var/run/docker.sock:/var/run/docker.sock
- ./traefik.toml:/traefik.toml - ./traefik.toml:/traefik.toml
@ -33,12 +32,7 @@ services:
traefik.http.middlewares.traefik-api-auth.basicauth.users: "dashboard:{{ traefik_dashboard_htpasswd | replace("$", "$$") }}" traefik.http.middlewares.traefik-api-auth.basicauth.users: "dashboard:{{ traefik_dashboard_htpasswd | replace("$", "$$") }}"
traefik.enable: true traefik.enable: true
environment: environment:
OVH_APPLICATION_KEY: "{{ ovh_app_key }}" GANDIV5_API_KEY: "{{ gandi_api_key }}"
OVH_APPLICATION_SECRET: "{{ ovh_app_secret }}"
OVH_CONSUMER_KEY: "{{ ovh_consumer_key }}"
OVH_ENDPOINT: ovh-eu
OVH_POLLING_INTERVAL: 0
OVH_TTL: 3600
networks: networks:
- proxy - proxy
restart: unless-stopped restart: unless-stopped

View File

@ -42,7 +42,7 @@
email = "{{ letsencrypt_email }}" email = "{{ letsencrypt_email }}"
storage = "/certs/acme.json" storage = "/certs/acme.json"
[certificatesResolvers.letsencrypt.acme.dnsChallenge] [certificatesResolvers.letsencrypt.acme.dnsChallenge]
provider = "ovh" provider = "gandiv5"
delayBeforeCheck = 10 delayBeforeCheck = 10
[metrics] [metrics]

View File

@ -14,8 +14,6 @@ services:
volumes: volumes:
- {{ websites_basepath }}/{{ website.name }}:/var/www/html:ro - {{ websites_basepath }}/{{ website.name }}:/var/www/html:ro
- {{ websites_basepath }}/{{ website.name }}.conf:/etc/nginx/conf.d/default.conf:ro - {{ websites_basepath }}/{{ website.name }}.conf:/etc/nginx/conf.d/default.conf:ro
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
labels: labels:
traefik.http.routers.{{ website.name }}.entrypoints: websecure traefik.http.routers.{{ website.name }}.entrypoints: websecure
traefik.http.routers.{{ website.name }}.rule: "Host(`{{ website.name }}.{{ domain_name }}`)" traefik.http.routers.{{ website.name }}.rule: "Host(`{{ website.name }}.{{ domain_name }}`)"
@ -45,7 +43,5 @@ services:
- {{ websites_basepath }}/{{ website.name }}:/var/www/html/{{ website.name }}:ro - {{ websites_basepath }}/{{ website.name }}:/var/www/html/{{ website.name }}:ro
{% endif %} {% endif %}
{% endfor %} {% endfor %}
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
restart: unless-stopped restart: unless-stopped
{% endif %} {% endif %}