Compare commits

..

52 Commits

Author SHA1 Message Date
Lino Silva 4a11a4ce8e feat: Sparky fitness 2026-05-31 12:14:58 +01:00
Lino Silva 844cef4bef chore: Change endpoint for price-scraper 2026-05-29 14:24:54 +01:00
Lino Silva 3ed21badb3 fix: Runner config path 2026-05-29 13:11:46 +01:00
Lino Silva bab98a020c feat: Forgejo runner 2026-05-29 13:09:40 +01:00
Lino Silva 3148136c22 feat: Forgejo reverse proxy 2026-05-29 13:09:35 +01:00
Lino Silva fe618ac8e4 fix: Forgejo version 2026-05-29 12:24:29 +01:00
Lino Silva d04a68b4df fix: Forgejo mirror 2026-05-29 12:23:08 +01:00
Lino Silva 6ed68a36be feat: Forgejo 2026-05-29 12:20:55 +01:00
Lino Silva 0eb4e4483c feat: Open wealthfolio 2026-05-28 12:55:03 +01:00
Lino Silva 3032dd0882 feat: Jellyfin reverse-proxy 2026-05-22 14:07:53 +01:00
Lino Silva 0ee5f37b54 feat: KVM reverse proxy 2026-05-22 09:26:38 +01:00
Lino Silva 24bd4ec162 chore: Remove env 2026-05-22 09:26:33 +01:00
Lino Silva cec78f8229 fix: Dahua2Mqtt docker compose 2026-05-19 21:31:06 +01:00
Lino Silva 234d9edc68 fix: Dahua2MQTT env file 2026-05-19 21:25:07 +01:00
Lino Silva 59c003f619 feat: Dahua2MQTT 2026-05-19 18:53:48 +01:00
Lino Silva 98c88759be feat: Speedtest 2026-05-19 18:41:09 +01:00
Lino Silva 8173b7cbc1 feat: Expose gitea 2026-05-19 10:13:23 +01:00
Lino Silva 886c6f559b fix: Price tracker reverse proxy 2026-05-12 22:32:32 +01:00
Lino Silva a05b2ea9ea chore: Renaming 2026-05-12 22:28:16 +01:00
Lino Silva ce197c3299 feat: Price tracker 2026-05-12 22:28:10 +01:00
Lino Silva 94984f0635 fix: Homelable images 2026-05-04 10:39:04 +01:00
Lino Silva e1500967cf feat: Homelable
Co-authored-by: Copilot <copilot@github.com>
2026-05-04 10:33:18 +01:00
Lino Silva 9e4a1f7836 feat: Unmanic endpoint 2026-04-21 12:15:16 +01:00
Lino Silva a286b755fe feat: Added apps-2 VM 2026-04-20 16:25:30 +01:00
Lino Silva e7bb6eadb8 feat: New endpoint 2026-04-14 00:56:44 +01:00
Lino Silva c5421a4af4 fix: Typo 2026-04-13 10:06:04 +01:00
Lino Silva fb7e101d6f fix: Subdomain for trek 2026-04-13 09:39:49 +01:00
Lino Silva 9ffae8989a fix: Remove env file 2026-04-12 23:11:14 +01:00
Lino Silva e44dafb0e2 fix: Dummy 2026-04-12 23:04:01 +01:00
Lino Silva 68d5178ab4 fix: Env var naming 2026-04-12 23:01:56 +01:00
Lino Silva 714f2449f6 fix: Revert 2026-04-12 22:44:22 +01:00
Lino Silva 5030362ba4 fix: Try new interpolation 2026-04-12 22:42:52 +01:00
Lino Silva 8fb291704e fix: Spacing? 2026-04-12 22:32:59 +01:00
Lino Silva f0b7351f64 fix: OIDC discovery url 2026-04-12 22:28:42 +01:00
Lino Silva f111fdfcb7 fix: Reverse proxy port 2026-04-12 22:28:31 +01:00
Lino Silva 07ac4dfec4 fix: Port 2026-04-12 22:22:37 +01:00
Lino Silva 0cc349765e feat: Trek compose files 2026-04-12 22:18:24 +01:00
Lino Silva 7ad7b26385 feat: Trek storage 2026-04-12 22:18:17 +01:00
Lino Silva 48ef6eda37 feat: Remove bpi scraper 2026-04-11 00:40:45 +01:00
Lino Silva 0c90b7a155 fix: Update port 2026-04-10 23:42:26 +01:00
Lino Silva ea9ad9eb30 feat: BPI price scraper app 2026-04-10 23:32:00 +01:00
Lino Silva 13022f4c34 feat: Local ip auth override 2026-04-10 23:09:08 +01:00
Lino Silva 57847ca4fe fix: Only deploy stacks if they didn't exist previously 2026-04-10 15:49:20 +01:00
Lino Silva 7a211b2d4b feat: More fake stacks 2026-04-10 15:29:48 +01:00
Lino Silva c3ecd9449e feat: Auto deploy stacks on komodo (no storage yet) 2026-04-10 15:27:40 +01:00
Lino Silva 82b8bb2e2a fix: Ports 2026-04-10 13:45:35 +01:00
Lino Silva a00d3d6ee3 fix: Renaming files 2026-04-10 13:45:03 +01:00
Lino Silva c3ad0b889d fix: Compose names 2026-04-10 13:35:37 +01:00
Lino Silva df602c0832 feat: Hello world docker composes 2026-04-10 13:02:31 +01:00
Lino Silva 13539eebc6 fix: Access komodo locally 2026-04-10 13:02:05 +01:00
Lino Silva a96ddd3ddf chore: Update vault 2026-04-10 12:43:09 +01:00
Lino Silva f60c5caa0a fix: Revert local ip override 2026-04-10 12:33:41 +01:00
31 changed files with 1054 additions and 137 deletions
@@ -11,30 +11,21 @@ app_data_disks:
disk_id: scsi1
mount_point: /data/pocket-id
device: /dev/sdb
komodo:
vm: infra-core-1
vmid: 410
node: purah
size: "20"
storage: purah-mirror-860gb
disk_id: scsi2
mount_point: /data/komodo
device: /dev/sdc
komodo-periphery-media:
vm: media-1
vmid: 420
node: purah
size: "20"
storage: purah-mirror-860gb
disk_id: scsi1
mount_point: /data/komodo-periphery
device: /dev/sdb
komodo-periphery-apps:
trek:
vm: apps-1
vmid: 430
node: yunobo
size: "20"
size: "10"
storage: nvme-2tb
disk_id: scsi1
mount_point: /data/komodo-periphery
mount_point: /data/trek
device: /dev/sdb
forgejo:
vm: apps-1
vmid: 430
node: yunobo
size: "50"
storage: nvme-2tb
disk_id: scsi2
mount_point: /data/forgejo
device: /dev/sdc
+78 -47
View File
@@ -26,200 +26,231 @@ auto_configure_traefik:
subdomain: "sonarr"
host: "10.0.2.25"
port: 8989
auth_required: true
internal: true
radarr:
subdomain: "radarr"
host: "10.0.2.25"
port: 7878
auth_required: true
internal: true
lidarr:
subdomain: "lidarr"
host: "10.0.2.25"
port: 8686
auth_required: true
internal: true
transmission:
subdomain: "transmission"
host: "10.0.2.25"
port: 9091
auth_required: true
tdarr:
subdomain: "tdarr"
internal: true
unmanic:
subdomain: "unmanic"
host: "10.0.2.25"
port: 8265
auth_required: true
port: 8888
internal: true
bazarr:
subdomain: "bazarr"
host: "10.0.2.25"
port: 6767
auth_required: true
internal: true
seerr:
subdomain: "overseerr"
host: "10.0.2.25"
port: 5055
auth_required: false
internal: false
prowlarr:
subdomain: "prowlarr"
host: "10.0.2.25"
port: 9696
auth_required: true
internal: true
unpackerr:
subdomain: "unpackerr"
host: "10.0.2.25"
port: 5656
auth_required: true
internal: true
questarr:
subdomain: "questarr"
host: "10.0.2.25"
port: 5000
internal: true
# infra
komodo:
subdomain: "komodo"
host: "10.0.4.10"
port: 9120
auth_required: true
internal: true
homeassistant:
subdomain: "homeassistant"
host: "10.0.2.100"
port: 8123
auth_required: false
internal: false
# media
plex:
subdomain: "plex"
host: "10.0.2.10"
port: 32400
auth_required: false
internal: false
tracearr:
subdomain: "tracearr"
host: "10.0.2.21"
port: 3000
auth_required: true
internal: true
jellyfin:
subdomain: "jellyfin"
host: "10.0.2.11"
port: 8096
internal: true
vaultwarden:
subdomain: "pwds"
host: "10.0.2.27"
port: 8004
auth_required: false
internal: false
changedetection:
subdomain: "changedetection"
host: "10.0.2.24"
port: 5000
auth_required: true
internal: true
nextcloud:
subdomain: "cloud"
host: "10.0.2.30"
port: 8001
auth_required: false
internal: false
convertx:
subdomain: "convertx"
host: "10.0.2.43"
port: 3000
auth_required: true
internal: true
dawarich:
subdomain: "places"
host: "10.0.2.48"
port: 3000
auth_required: false
internal: false
frigate:
subdomain: "frigate"
host: "10.0.2.14"
port: 5000
auth_required: true
internal: true
droposs:
subdomain: "games"
host: "10.0.2.46"
port: 3000
auth_required: false
# geoguessr:
# subdomain: "geoguessr"
# host: "10.0.2.39"
# port: 8080
# auth_required: true
internal: false
gitea:
subdomain: "gitea"
host: "10.0.2.28"
port: 3000
auth_required: true
internal: false
forgejo:
subdomain: "git"
host: "10.0.4.30"
port: 8086
internal: false
immich:
subdomain: "immich"
host: "10.0.2.18"
port: 2283
auth_required: false
internal: false
mastodon:
subdomain: "social"
host: "10.0.2.20"
port: 80
auth_required: false
internal: false
forward_https: true
matrix:
subdomain: "chat"
host: "10.0.2.20"
port: 8008
auth_required: false
internal: false
mealie:
subdomain: "recipes"
host: "10.0.2.26"
port: 9000
auth_required: false
internal: false
truenas:
subdomain: "nas"
host: "10.0.2.200"
port: 80
auth_required: true
internal: true
paperless:
subdomain: "paperless"
host: "10.0.2.29"
port: 8003
auth_required: true
internal: true
pbs:
subdomain: "pbs"
host: "10.0.2.104"
port: 8007
https: true
auth_required: true
internal: true
# pinchflat:
# subdomain: "youtube"
# host: "10.0.2.23"
# port: 8081
# auth_required: true
# internal: true
proxmox:
subdomain: "proxmox"
host: "10.0.2.2"
port: 8006
https: true
auth_required: true
internal: true
resume:
subdomain: "resume"
host: "10.0.2.53"
port: 3000
auth_required: true
internal: true
auth_bypass_paths:
- /lino
- /assets
- /api
speedtest-tracker:
subdomain: "fast"
host: "10.0.2.15"
port: 8765
auth_required: true
host: "10.0.4.30"
port: 8085
internal: true
stocks:
subdomain: "stocks"
host: "10.0.2.40"
port: 3333
auth_required: false
internal: false
super-productivity:
subdomain: "tasks"
host: "10.0.2.45"
port: 80
auth_required: true
internal: true
uptime-kuma:
subdomain: "uptime"
host: "10.0.2.203"
port: 3001
auth_required: true
internal: true
wealthfolio:
subdomain: "wealth"
host: "10.0.2.40"
port: 8088
auth_required: true
internal: false
trek:
subdomain: "trips"
host: "10.0.4.30"
port: 8083
internal: true
homelable:
subdomain: "infra"
host: "10.0.4.30"
port: 8084
internal: true
price-tracker:
subdomain: "prices"
host: "10.0.4.30"
port: 3000
internal: true
kvm:
subdomain: "kvm"
host: "10.0.3.1"
port: 80
internal: true
forward_https: true
# Auth services configuration
pocketid_host: 10.0.4.10
@@ -242,4 +273,4 @@ komodo_db_password: "{{ vault_komodo_db_password }}"
komodo_webhook_secret: "{{ vault_komodo_webhook_secret }}"
komodo_jwt_secret: "{{ vault_komodo_jwt_secret }}"
komodo_onboarding_key: "{{ vault_komodo_onboarding_key }}"
komodo_core_address: "komodo.lino.cooking"
komodo_core_host: "10.0.4.10:9120"
+52 -45
View File
@@ -1,46 +1,53 @@
$ANSIBLE_VAULT;1.1;AES256
32666164303261303561346338303530366438613032393865383765643635386564383762343730
3733623235313239653631623739393638396436323863630a643738343132313766626133663932
64373061613562343864313735383730383362643332636630343665353130383165363437323832
6565636230646430370a643833656235646262323032393933323734336434653432616430376233
33336263323533636134633439323536643231396435623362633832643738313538353331313661
61666133386331396132346336623337323662636566303262666238356438623930333339303430
36653839383336613466313833343830346535363665643233646239623730386539613934323430
62316333376164363839376330653333353339343135303837356466396266373639323533353064
38646533656638623039633137386366643866336436373432333339313336653866346639303665
35353162356165366637323031333133343561663534376665636263633739376437333834373233
35616538383466656133663332656361653139313835323266626631636432373230323561616166
62393031653035646132356562366133353839613137323465376266326130613439393664323335
34663965396638613131626534643864373331656435386265303463656636373630643361346365
64353861643231326466343262636664336532653338373866663462636239336166653261646437
35343062303832633439393439643833303166663464303934613030666664363461376333353835
34613361333932623864373037643630396635656138643666336263633431633839643937343630
38343439326237313431626265623161313433636233326335623231376261396236383366653737
62366365666465393132303130646661303765346535666132626562313761623630323936333562
66343331363832393032353631393037616566343238363165663836396635656530356366363066
66343435303037316362623265316566333938643439393937383839363235363131323030303465
37303266303833353236363566383238353039623638376265353332323535353862373864356634
34303939633836643336663236306639393163373264353565643031623336383965316333393862
35633763653665383438333165626561663838336533633865623339363761333430356434373465
37346634616638333738626630346538366631653237626538373864383837666535663931326361
30643031366230653038623732623265383231656633393934323263303930666434623861326630
30626136306431653539353135336237376432393638663961396431376434386438343633666261
32653063373630303263646232626637313163366436363862346263343365363362393833303339
30343437646466313566383238353632653361316461323331633438346562363038376639633932
61653039626431366564643938336135353230636336306639373062666362666463656164363136
61343434623636636136636334313865333864353061623363663865646364653238363337333439
35353539343632653339336133313066626565333533336334303834653930346362333538363164
34303334366562626665653630666539623735626661663435643236626537356630666433393835
31383131333066616365363463333937636162633133343732343530336565333334373338323033
36636439323766373563636664346433643263356466313566313662633438623462383334393438
30383031376362376639346362386334333131343361646338623034356265373262643561656335
31636265333833653839313830653230383733663635356562323062373365336137623738336530
66636564663339626464356135646666376432313464346535636636613165353836386365333738
62323739383939313561396235353537343637636262343338666332656238393566313231396135
63393331326136656461316363346465633435653863663065633633663737616534353064643934
34666565343437333839346235613766623334393166666162366430616439343364323661396230
34393438663163313762653263653537376462316561306634666637356465646139323831343337
32613736396437343064626233353532376263663338366337303832656166343063666231643037
39303239633731306465303036356266353035626566313466623866346635363464333133346262
30313235336231666363346361393064346432353533363937366663373536306632343439616133
3365
63666330376531356135326331306565336237626132383934313666636134366436383831393730
3330643462346434663536383231646364613963373833360a386237306230636131313464363636
66383366616436633538363430383530613635333462383431643438643962633833363632366438
3063323965363031360a353662366435323263356235333633306161363664373738313830333562
36383637363465396161313332316262383132313061356461626331623134653136353566333539
39626662663539303337306662666131653738633666393434336338616561353061316132653666
39313537636561396466646366653366663134386564643036613465623061323261343532373037
39663931326339636533323631333661643736663066643262383766623163333234336364626165
36333536313332613966303838393262616335343563643965363664653334613330356134353362
30643636323661666463393564393434383436346636616333626464306261346337623063306136
33353034386566323366343739626533323735333638313261336666663964633665313962383364
65326536333462303637376265366532623435336363346230366631313663366234616436363161
65663932303138623865306163656464613039643936313532396135393966333632333235636461
65656636666339303365663766323434633032376561333939306336643563653832306362313134
64616438393235623964356132626231646539333065663334346139353232663263323331643064
35636563613836376666643664636265626530363235373265306163386331633631313161346133
64633038306335373236646632356238343763636433623938373064383834346361316232643830
64316663653638386466313336666164343036373231623066663033336365303934626236633238
62643032303863366436313934316563346364383536636236633232656164643032653530376665
30326639646632346638636232626431396536346165656539343535313266666665336366373333
31333930656330653031656236316261663366396434353063663831643464663134623063386333
37386431393336396362373834303135663631343366666333613331333136303134656231343530
64316562663362656662613932393136383837666666316438393235316666323063373732666334
38356162396630363430633166306566626131643734636536633264336632323734333163306535
36633965376362373733616635656565636164636562386566643231363835656634343235373838
34333730353935643935626366656133353330316165633835626333663539623665653662353138
61363230303634653131356563376263363339613265373534383561313932616635643038363266
31663835643830666363646635346538656665363730396366643439326537386264663666363332
64363561623031626531663934636634313536653435663431666463386362306331353137396362
65653039623636316338326335613164376366613266316134386163626436383539393932666436
33633264653230663065393632653935633833313233373933326464316331646363613832633330
30353263343433366362323365386536313535396430373966323337623830373465356437643734
32313038323664376566656666633364363634613033376636326635343233666134363864343634
65386538663835663539353533633931323837373738393463336337353865616439336537353933
32353162363166353265316534643764616132656534666463363439643237396166303833303038
66383236363738336363643832666130633464333536623134616265316663333237383739336537
37376331326139633764353166656634323339656435663531313934376565393639316531663237
64303931386461346632646161393061376534393539663966373562646432386134616435363261
37356466303165356336626134313135353830316536323165323233613235643733643333613636
63666139323931616166346566396162373637336331393034623631346437643632643938323834
35383862653464643533663333616263633465663939633064653666353363363830393565666236
31333663663332363533336132346666643734623131643339643865633565393262643966393734
36663533313638386166653334623832323363636262373931356664323766666538343838313039
39393765343430633336373966346161393130386539393366363366366163316363613038613962
36646336396333333863326565303666373963326163393434313534333533323739623035323137
32346535383263316332653239306633343935383166653139323036616133656638643532646634
64333466666533363932323366353762653461336538303166626330333336616437613037363139
63303764313330656366363630636463303461383565346262656136326362623839366531326663
34663534343564313030613366626337336331343637343564386638653530663631613234636237
37373064303137666263666133363865386135333038653231346562333131313233643062376265
34646635663961393837396637383834376136323864646166656436383338623364613861663537
39313230306465626630
@@ -33,6 +33,17 @@ vms:
network_bridge: "vmbr2"
storage: nvme-2tb
apps-2:
vmid: 431
node: yuga
template_vmid: 9004
cores: 5
memory: 12288
disk: 30G
ip: 10.0.4.31
network_bridge: "vmbr0"
storage: media
edge-1:
vmid: 401
node: mipha
@@ -6,5 +6,3 @@ keepalived_interface: eth0
keepalived_router_id: 51
keepalived_vip: 10.0.4.254
keepalived_password: "{{ vault_keepalived_password | default('changeme') }}"
komodo_core_address: "10.0.4.10:9120"
@@ -6,5 +6,3 @@ keepalived_interface: eth0
keepalived_router_id: 51
keepalived_vip: 10.0.4.254
keepalived_password: "{{ vault_keepalived_password | default('changeme') }}"
komodo_core_address: "10.0.4.10:9120"
+2
View File
@@ -22,3 +22,5 @@ all:
hosts:
apps-1:
ansible_host: 10.0.4.30
apps-2:
ansible_host: 10.0.4.31
+14 -1
View File
@@ -1,4 +1,8 @@
---
# NOTE: Adding a komodo-periphery role to an edge means you need to
# create an onboarding key on Komodo and change its value on
# the vault
- hosts: infra
become: yes
roles:
@@ -25,7 +29,16 @@
- docker
- komodo-periphery
- hosts: apps
- hosts: apps-1
become: yes
roles:
- base
- docker
- komodo-periphery
- trek
- forgejo
- hosts: apps-2
become: yes
roles:
- base
@@ -0,0 +1,45 @@
---
# Playbook to provision Komodo stacks from git repository
# Automatically discovers stacks from docker-compose folder structure
# Structure: docker-compose/{server}/{app}/compose.yaml
# Usage: ansible-playbook -i inventories/production.yml playbooks/provision_komodo_stacks.yml
- name: Provision Komodo stacks
hosts: localhost
connection: local
gather_facts: false
vars:
docker_compose_root: "{{ playbook_dir }}/../../docker-compose"
tasks:
- name: Find all compose.yaml files in docker-compose directory
find:
paths: "{{ docker_compose_root }}"
patterns: "compose.yaml"
recurse: true
register: compose_files
- name: Build stack list from discovered compose files
set_fact:
komodo_stacks: "{{ komodo_stacks | default([]) + [{'server': path_parts[0], 'app': path_parts[1]}] }}"
loop: "{{ compose_files.files }}"
vars:
path_parts: "{{ item.path | regex_replace('^.*/docker-compose/', '') | split('/') }}"
loop_control:
label: "{{ path_parts[0] }}/{{ path_parts[1] }}"
- name: Display discovered stacks
debug:
msg: "Found {{ komodo_stacks | length }} stacks: {{ komodo_stacks | map(attribute='app') | list | join(', ') }}"
- name: Create Komodo stacks
include_role:
name: komodo_stack
vars:
komodo_stack_name: "{{ item.app }}"
komodo_server_name: "{{ item.server }}"
komodo_run_directory: "docker-compose/{{ item.server }}/{{ item.app }}"
loop: "{{ komodo_stacks }}"
loop_control:
label: "{{ item.app }} (on {{ item.server }})"
+53
View File
@@ -0,0 +1,53 @@
---
- name: Add data disk to VM for forgejo
community.proxmox.proxmox_disk:
api_host: "{{ proxmox_api_host }}"
api_user: "{{ proxmox_api_user }}"
api_token_id: "{{ proxmox_api_token_id }}"
api_token_secret: "{{ proxmox_api_token_secret }}"
vmid: "{{ app_data_disks.forgejo.vmid }}"
disk: "{{ app_data_disks.forgejo.disk_id }}"
storage: "{{ app_data_disks.forgejo.storage }}"
size: "{{ app_data_disks.forgejo.size }}"
state: present
delegate_to: localhost
become: no
run_once: true
ignore_errors: yes
register: disk_result
- name: Display disk creation result
debug:
var: disk_result
- name: Wait for data disk to be available
wait_for:
path: "{{ app_data_disks.forgejo.device }}"
state: present
timeout: 30
- name: Check if data disk is formatted
command: "blkid {{ app_data_disks.forgejo.device }}"
register: disk_formatted
failed_when: false
changed_when: false
- name: Format data disk with ext4
filesystem:
fstype: ext4
dev: "{{ app_data_disks.forgejo.device }}"
when: disk_formatted.rc != 0
- name: Create forgejo data mount point
file:
path: "{{ app_data_disks.forgejo.mount_point }}"
state: directory
mode: "0755"
- name: Mount data disk
mount:
path: "{{ app_data_disks.forgejo.mount_point }}"
src: "{{ app_data_disks.forgejo.device }}"
fstype: ext4
state: mounted
opts: defaults
@@ -5,7 +5,7 @@ services:
container_name: komodo-periphery
restart: unless-stopped
environment:
PERIPHERY_CORE_ADDRESS: {{ komodo_core_address }}
PERIPHERY_CORE_ADDRESS: {{ komodo_core_host }}
PERIPHERY_CONNECT_AS: {{ inventory_hostname }}
PERIPHERY_CORE_PUBLIC_KEYS: file:/config/keys/core.pub
PERIPHERY_ROOT_DIRECTORY: /etc/komodo
+86
View File
@@ -0,0 +1,86 @@
# Komodo Stack Role
This role automates the creation of stacks in Komodo via its REST API.
## Quick Start
The included `provision_komodo_stacks.yml` playbook automatically discovers all stacks from your `docker-compose/` folder structure and creates them in Komodo:
```bash
ansible-playbook -i inventories/production.yml playbooks/provision_komodo_stacks.yml
```
This will scan for all `compose.yaml` files in the structure: `docker-compose/{server}/{app}/compose.yaml` and create corresponding stacks in Komodo.
## Requirements
- A running Komodo instance with API access
- Valid Komodo API token stored in `vault_komodo_api_token`
- Existing server and repository configured in Komodo
## Role Variables
### Required Variables
- `komodo_stack_name`: Name for the stack (typically the app name, e.g., "changedetection")
- `komodo_server_name`: Name of the server in Komodo where the stack should deploy
- `komodo_repo_name`: Name of the repository in Komodo containing the stack definition
- `komodo_run_directory`: Path within the repository to the compose.yaml file (e.g., `docker-compose/apps-1/changedetection`)
### Optional Variables
- `komodo_host`: Komodo instance URL (default: `https://komodo.{{ domain }}`)
- `komodo_api_token`: API token for authentication (default: `{{ vault_komodo_api_token }}`)
## Example Usage
### Automatic Discovery (Recommended)
Use the provided playbook to automatically discover and create all stacks from your repository:
```bash
ansible-playbook -i inventories/production.yml playbooks/provision_komodo_stacks.yml
```
This scans the `docker-compose/` directory and creates stacks for each app found in the structure:
- `docker-compose/{server}/{app}/compose.yaml`
Example structure:
```
docker-compose/
apps-1/
changedetection/
compose.yaml
turn/
compose.yaml
media-1/
arr/
compose.yaml
```
## How It Works
1. Validates all required variables are provided
2. Fetches list of servers from Komodo API and finds the server ID by name
3. Fetches list of repositories from Komodo API and finds the repo ID by name
4. Checks if a stack with the same name already exists
5. Creates the stack if it doesn't exist (skips if it does)
## API Token Setup
Store your Komodo API token in an Ansible vault file:
```yaml
# inventories/group_vars/all/vault.yml
vault_komodo_api_token: 'your-api-token-here'
```
Generate an API token in Komodo: Settings → API Keys → Create New Token
## Notes
- The role is idempotent - it won't create duplicate stacks
- Server and repo must already exist in Komodo before running this role
- Uses retry logic for API calls to handle temporary network issues
@@ -0,0 +1,13 @@
---
# Komodo API configuration
komodo_core_address: "http://{{ komodo_core_host }}"
komodo_api_key: "{{ vault_komodo_api_key }}"
komodo_api_secret: "{{ vault_komodo_api_secret }}"
# Stack configuration
komodo_repo_name: "homelab"
# Optional stack configuration
komodo_stack_environment: []
komodo_stack_labels: {}
+129
View File
@@ -0,0 +1,129 @@
---
- name: Validate required variables
assert:
that:
- komodo_stack_name is defined and komodo_stack_name | length > 0
- komodo_server_name is defined and komodo_server_name | length > 0
- komodo_repo_name is defined and komodo_repo_name | length > 0
- komodo_run_directory is defined and komodo_run_directory | length > 0
- komodo_api_key is defined and komodo_api_key | length > 0
- komodo_api_secret is defined and komodo_api_secret | length > 0
fail_msg: "Missing required variables: komodo_stack_name, komodo_server_name, komodo_repo_name, komodo_run_directory, komodo_api_key, or komodo_api_secret"
- name: Get Komodo server by name
uri:
url: "{{ komodo_core_address }}/read/GetServer"
method: POST
headers:
"X-Api-Key": "{{ komodo_api_key }}"
"X-Api-Secret": "{{ komodo_api_secret }}"
"Content-Type": "application/json"
body_format: json
body:
server: "{{ komodo_server_name }}"
status_code: 200
validate_certs: false
register: komodo_server
retries: 3
delay: 2
- name: Extract server ID
set_fact:
komodo_server_id: "{{ komodo_server.json._id['$oid'] }}"
- name: Debug server response
debug:
msg: "Server ID: {{ komodo_server_id }}"
- name: Get Komodo repo by name
uri:
url: "{{ komodo_core_address }}/read/GetRepo"
method: POST
headers:
"X-Api-Key": "{{ komodo_api_key }}"
"X-Api-Secret": "{{ komodo_api_secret }}"
"Content-Type": "application/json"
body_format: json
body:
repo: "{{ komodo_repo_name }}"
status_code: 200
validate_certs: false
register: komodo_repo
retries: 3
delay: 2
- name: Extract repo ID
set_fact:
komodo_repo_id: "{{ komodo_repo.json._id['$oid'] }}"
- name: Debug repo response
debug:
msg: "Repo ID: {{ komodo_repo_id }}"
- name: Check if stack already exists
uri:
url: "{{ komodo_core_address }}/read/GetStack"
method: POST
headers:
"X-Api-Key": "{{ komodo_api_key }}"
"X-Api-Secret": "{{ komodo_api_secret }}"
"Content-Type": "application/json"
body_format: json
body:
stack: "{{ komodo_stack_name }}"
status_code: [200, 500]
validate_certs: false
register: existing_stack
retries: 3
delay: 2
failed_when: false
- name: Create Komodo stack
uri:
url: "{{ komodo_core_address }}/write/CreateStack"
method: POST
headers:
"X-Api-Key": "{{ komodo_api_key }}"
"X-Api-Secret": "{{ komodo_api_secret }}"
"Content-Type": "application/json"
body_format: json
body:
name: "{{ komodo_stack_name }}"
config:
server_id: "{{ komodo_server_id }}"
linked_repo: "{{ komodo_repo_id }}"
run_directory: "{{ komodo_run_directory }}"
status_code: [200, 201]
validate_certs: false
register: stack_result
when: existing_stack.status == 500
retries: 3
delay: 2
- name: Display stack creation result
debug:
msg: "Stack '{{ komodo_stack_name }}' created successfully with ID: {{ stack_result.json._id }}"
when: existing_stack.status == 500 and stack_result is succeeded
- name: Display stack already exists message
debug:
msg: "Stack '{{ komodo_stack_name }}' already exists with ID: {{ existing_stack.json._id }}"
when: existing_stack.status == 200
- name: Deploy stack
uri:
url: "{{ komodo_core_address }}/execute/DeployStack"
method: POST
headers:
"X-Api-Key": "{{ komodo_api_key }}"
"X-Api-Secret": "{{ komodo_api_secret }}"
"Content-Type": "application/json"
body_format: json
body:
stack: "{{ komodo_stack_name }}"
status_code: 200
validate_certs: false
register: start_result
retries: 3
delay: 2
when: existing_stack.status == 500 and stack_result is succeeded
@@ -36,6 +36,16 @@ http:
insecureSkipVerify: true
routers:
# Local IP bypass - HTTPS (higher priority, no auth)
traefik-secure-local:
rule: "Host(`traefik.{{ domain }}`) && (ClientIP(`192.168.0.0/16`) || ClientIP(`10.0.0.0/8`) || ClientIP(`172.16.0.0/12`))"
entryPoints:
- https
priority: 200
service: api@internal
tls:
certResolver: cloudflare
# Static services - HTTPS
traefik-secure:
rule: "Host(`traefik.{{ domain }}`)"
@@ -106,6 +116,21 @@ http:
# Auto-configured services - HTTPS
{% for service_name, config in auto_configure_traefik.items() %}
{% if config.internal | default(true) %}
# {{ service_name }} - local IP bypass (no auth)
{{ service_name }}-local:
rule: "Host(`{{ config.subdomain }}.{{ domain }}`) && (ClientIP(`192.168.0.0/16`) || ClientIP(`10.0.0.0/8`) || ClientIP(`172.16.0.0/12`))"
entryPoints:
- https
priority: 200
{% if config.forward_https | default(false) %}
middlewares:
- {{ service_name }}-https-headers
{% endif %}
service: {{ service_name }}
tls:
certResolver: cloudflare
{% endif %}
{% if config.auth_bypass_paths is defined %}
# {{ service_name }} - bypass paths (no auth)
{% for path in config.auth_bypass_paths %}
@@ -122,31 +147,15 @@ http:
tls:
certResolver: cloudflare
{% endfor %}
{% endif %}
{% if config.auth_required | default(true) %}
# {{ service_name }} - internal network (no auth)
{{ service_name }}-int:
rule: "Host(`{{ config.subdomain }}.{{ domain }}`) && ClientIP(`10.0.0.0/21`)"
entryPoints:
- https
priority: 50
{% if config.forward_https | default(false) %}
middlewares:
- {{ service_name }}-https-headers
{% endif %}
service: {{ service_name }}
tls:
certResolver: cloudflare
{% endif %}
# {{ service_name }} - default path (with auth if required)
{{ service_name }}:
rule: "Host(`{{ config.subdomain }}.{{ domain }}`)"
entryPoints:
- https
priority: 1
{% if config.auth_required | default(true) or config.forward_https | default(false) %}
{% if config.internal | default(true) or config.forward_https | default(false) %}
middlewares:
{% if config.auth_required | default(true) %}
{% if config.internal | default(true) %}
- pocketid-auth
{% endif %}
{% if config.forward_https | default(false) %}
@@ -156,6 +165,24 @@ http:
service: {{ service_name }}
tls:
certResolver: cloudflare
{% else %}
{{ service_name }}:
rule: "Host(`{{ config.subdomain }}.{{ domain }}`)"
entryPoints:
- https
{% if config.internal | default(true) or config.forward_https | default(false) %}
middlewares:
{% if config.internal | default(true) %}
- pocketid-auth
{% endif %}
{% if config.forward_https | default(false) %}
- {{ service_name }}-https-headers
{% endif %}
{% endif %}
service: {{ service_name }}
tls:
certResolver: cloudflare
{% endif %}
{% endfor %}
# Auto-configured services - HTTP to HTTPS redirect
+53
View File
@@ -0,0 +1,53 @@
---
- name: Add data disk to VM for trek
community.proxmox.proxmox_disk:
api_host: "{{ proxmox_api_host }}"
api_user: "{{ proxmox_api_user }}"
api_token_id: "{{ proxmox_api_token_id }}"
api_token_secret: "{{ proxmox_api_token_secret }}"
vmid: "{{ app_data_disks.trek.vmid }}"
disk: "{{ app_data_disks.trek.disk_id }}"
storage: "{{ app_data_disks.trek.storage }}"
size: "{{ app_data_disks.trek.size }}"
state: present
delegate_to: localhost
become: no
run_once: true
ignore_errors: yes
register: disk_result
- name: Display disk creation result
debug:
var: disk_result
- name: Wait for data disk to be available
wait_for:
path: "{{ app_data_disks.trek.device }}"
state: present
timeout: 30
- name: Check if data disk is formatted
command: "blkid {{ app_data_disks.trek.device }}"
register: disk_formatted
failed_when: false
changed_when: false
- name: Format data disk with ext4
filesystem:
fstype: ext4
dev: "{{ app_data_disks.trek.device }}"
when: disk_formatted.rc != 0
- name: Create trek data mount point
file:
path: "{{ app_data_disks.trek.mount_point }}"
state: directory
mode: "0755"
- name: Mount data disk
mount:
path: "{{ app_data_disks.trek.mount_point }}"
src: "{{ app_data_disks.trek.device }}"
fstype: ext4
state: mounted
opts: defaults
@@ -0,0 +1,8 @@
services:
dahuavto2mqtt:
image: "registry.gitlab.com/elad.bar/dahuavto2mqtt:latest"
container_name: "dahua2mqtt"
hostname: "dahua2mqtt"
restart: "unless-stopped"
env_file:
- .env
@@ -0,0 +1,38 @@
services:
server:
image: codeberg.org/forgejo/forgejo:15
container_name: forgejo
environment:
- USER_UID=1000
- USER_GID=1000
restart: always
volumes:
- /data/forgejo:/data
- /etc/localtime:/etc/localtime:ro
ports:
- "8086:3000"
- "222:22"
docker-in-docker:
image: docker:dind
container_name: "docker_dind"
privileged: "true"
command: ["dockerd", "-H", "tcp://0.0.0.0:2375", "--tls=false"]
restart: "unless-stopped"
runner:
image: "data.forgejo.org/forgejo/runner:12"
links:
- docker-in-docker
depends_on:
docker-in-docker:
condition: service_started
container_name: "runner"
environment:
DOCKER_HOST: tcp://docker-in-docker:2375
# User without root privileges, but with access to `./data`.
user: 1001:1001
volumes:
- /data/forgejo/runner:/data
restart: "unless-stopped"
command: "forgejo-runner daemon --config /data/runner-config.yml"
@@ -0,0 +1,36 @@
services:
backend:
image: ghcr.io/pouzor/homelable-backend:latest
restart: unless-stopped
env_file:
- .env
environment:
# Override env_file: SQLite path must point inside the container volume
SQLITE_PATH: /app/data/homelab.db
volumes:
- /data/homelable:/app/data
networks:
- homelable
# Required for ping-based status checks
cap_add:
- NET_RAW
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8000/api/v1/health"]
interval: 10s
timeout: 5s
retries: 6
start_period: 15s
frontend:
image: ghcr.io/pouzor/homelable-frontend:latest
restart: unless-stopped
ports:
- "8084:80"
depends_on:
- backend
networks:
- homelable
networks:
homelable:
driver: bridge
@@ -0,0 +1,7 @@
services:
webserver:
image: git.lino.cooking/lino/bpi-stock-price-scraper:latest
container_name: price-tracker
ports:
- "3000:3000"
restart: unless-stopped
+210
View File
@@ -0,0 +1,210 @@
# SparkyFitness Environment Variables
# Copy this file to .env in the root directory and fill in your own values before running 'docker-compose up'.
# --- PostgreSQL Database Settings ---
# These values should match the ones used by your PostgreSQL container.
# For local development (running Node.js directly), use 'localhost' or '127.0.0.1' if PostgreSQL is on your host.
SPARKY_FITNESS_DB_NAME=sparkyfitness_db
#SPARKY_FITNESS_DB_USER is super user for DB initialization and migrations.
SPARKY_FITNESS_DB_USER=sparky
SPARKY_FITNESS_DB_PASSWORD=changeme_db_password
# Application database user with limited privileges. it can be changed any time after initialization.
SPARKY_FITNESS_APP_DB_USER=sparky_app
SPARKY_FITNESS_APP_DB_PASSWORD=password
# For Docker Compose deployments, SPARKY_FITNESS_DB_HOST will be the service name (e.g., 'sparkyfitness-db').
#SPARKY_FITNESS_DB_HOST=sparkyfitness-db
# SPARKY_FITNESS_DB_PORT controls the HOST port for external database access (e.g., pgAdmin, DBeaver).
# To use this, you must also uncomment the 'ports' section under sparkyfitness-db in docker-compose.prod.yml.
# Inside Docker, containers always communicate on port 5432 (the internal PostgreSQL port).
# Changing this value will NOT affect container-to-container communication.
#SPARKY_FITNESS_DB_PORT=5432
# --- Backend Server Settings ---
# The hostname or IP address of the backend server.
# For Docker Compose, this is typically the service name (e.g., 'sparkyfitness-server').
# For local development or other deployments, this might be 'localhost' or a specific IP.
SPARKY_FITNESS_SERVER_HOST=sparkyfitness-server
# The external port the server will be exposed on.
SPARKY_FITNESS_SERVER_PORT=3010
# The public URL of your frontend (e.g., https://fitness.example.com). This is crucial for CORS security.
# For local development, use http://localhost:8080. For production, use your domain with https.
SPARKY_FITNESS_FRONTEND_URL=http://localhost:3004
# Allow CORS requests from private network addresses (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, localhost, etc.)
# SECURITY WARNING: Only enable this if you are running on a private/self-hosted network.
# Do NOT enable on shared hosting or cloud environments where other users might access your network.
# Default: false (secure default - only the configured SPARKY_FITNESS_FRONTEND_URL is allowed)
#ALLOW_PRIVATE_NETWORK_CORS=false
# A comma-separated list of additional URLs that Better Auth should trust.
# This is useful when accessing the app from a specific local IP on your network.
# Example: SPARKY_FITNESS_EXTRA_TRUSTED_ORIGINS=http://192.168.1.175:8080,http://10.0.0.5:8080
# SPARKY_FITNESS_EXTRA_TRUSTED_ORIGINS=
# Logging level for the server (e.g., INFO, DEBUG, WARN, ERROR)
SPARKY_FITNESS_LOG_LEVEL=ERROR
# Node.js environment mode (e.g., development, production, test)
# Set to 'production' for deployment to ensure optimal performance and security.
NODE_ENV=production
# Server timezone. Use a TZ database name (e.g., 'America/New_York', 'Etc/UTC').
# This affects how dates/times are handled by the server if not explicitly managed in code.
TZ=Europe/Lisbon
# --- Security Settings ---
# A 64-character hex string for data encryption.
# You can generate a secure key with the following command:
# openssl rand -hex 32
# or
# node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
# Changing this will invalidate existing encrypted data. You will need to delete and add External Data sources again.
SPARKY_FITNESS_API_ENCRYPTION_KEY=b65743a2f9946be8b7f14083f59183f26bc07c9d2d352a96b2d186a06907e111
# For Docker Swarm/Kubernetes secrets, you can use a file-based secret:
# SPARKY_FITNESS_API_ENCRYPTION_KEY_FILE=/run/secrets/sparkyfitness_api_key
# BETTER_AUTH_SECRET is used to sign sessions and encrypt 2FA/TOTP data.
# CRITICAL: If you change this after users have enabled 2FA, they will be LOCKED OUT of their accounts.
# Ensure this is set to a strong, persistent value during initial setup and is never changed.
# If you MUST change it, all users must disable Two-Factor Authentication (TOTP) first.
BETTER_AUTH_SECRET=297f9b19e4f13b3ce45af8f1fcc8234264223b6c2052e1f51ea6869583325ecc
# For Docker Swarm/Kubernetes secrets, you can use a file-based secret:
# BETTER_AUTH_SECRET_FILE=/run/secrets/sparkyfitness_better_auth_secret
# --- Signup Settings ---
# Set to 'true' to disable new user registrations.
SPARKY_FITNESS_DISABLE_SIGNUP=false
# --- Admin Settings ---
# Set the email of a user to automatically grant admin privileges on server startup.
# This is useful for development or initial setup.
# Example: SPARKY_FITNESS_ADMIN_EMAIL=admin@example.com
# Optional. If not set, no admin user will be created automatically.
SPARKY_FITNESS_ADMIN_EMAIL=sparkyfitness@lino.cooking
# --- OIDC Authentication Configuration ---
# Set to 'true' to disable email/password login on the login page (overridden by SPARKY_FITNESS_FORCE_EMAIL_LOGIN).
SPARKY_FITNESS_DISABLE_EMAIL_LOGIN=true
# Set to 'true' to enable OIDC login. When set, overrides the database value from Admin > Authentication Settings.
SPARKY_FITNESS_OIDC_AUTH_ENABLED=true
# Display name and provider slug (URL-safe id) for the ENV-configured OIDC provider.
SPARKY_FITNESS_OIDC_PROVIDER_SLUG=pocket-id
SPARKY_FITNESS_OIDC_PROVIDER_NAME=Pocket ID
SPARKY_FITNESS_OIDC_AUTO_REGISTER=true
SPARKY_FITNESS_OIDC_LOGO_URL=https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/pocket-id.svg
# OIDC issuer URL (e.g. https://example.com). Discovery URL is derived as issuer + /.well-known/openid-configuration.
# When set with CLIENT_ID, CLIENT_SECRET and PROVIDER_SLUG, an ENV-configured OIDC provider is upserted at startup.
SPARKY_FITNESS_OIDC_ISSUER_URL=https://auth.lino.cooking/application/o/{{slug}}/
# SPARKY_FITNESS_OIDC_DOMAIN=example.com
# OIDC client credentials from your IdP.
# SPARKY_FITNESS_OIDC_CLIENT_ID=
# SPARKY_FITNESS_OIDC_CLIENT_SECRET=
# SPARKY_FITNESS_OIDC_SCOPE=openid email profile
# Set to 'true' to allow auto-redirect to the single OIDC provider when email login is disabled.
SPARKY_FITNESS_OIDC_AUTO_REDIRECT=true
# Group/claim values from your IdP for role mapping (admin vs user). Configure your IdP to send these in token claims.
# SPARKY_FITNESS_OIDC_ADMIN_GROUP=Admin
# --- Advanced OIDC Settings ---
# SPARKY_FITNESS_OIDC_TOKEN_AUTH_METHOD=client_secret_post
# SPARKY_FITNESS_OIDC_ID_TOKEN_SIGNED_ALG=RS256
# SPARKY_FITNESS_OIDC_USERINFO_SIGNED_ALG=none
# SPARKY_FITNESS_OIDC_TIMEOUT=30000
# Set custom uploads and backups directory. Only needed for standalone installation
# SPARKY_FITNESS_CUSTOM_UPLOADS_DIRECTORY=
# SPARKY_FITNESS_CUSTOM_BACKUP_DIRECTORY=
#
# --- Login Management Fail-Safe ---
# Set to 'true' to force email/password login to be enabled, overriding any in-app settings.
# This is a fail-safe to prevent being locked out if OIDC is misconfigured.
# SPARKY_FITNESS_FORCE_EMAIL_LOGIN=true
# --- Email Settings (Optional) ---
# Configure these variables if you want to enable email notifications (e.g., for password resets).
# If not configured, email functionality will be disabled.
# SPARKY_FITNESS_EMAIL_HOST=smtp.example.com
# SPARKY_FITNESS_EMAIL_PORT=587
# SPARKY_FITNESS_EMAIL_SECURE=true # Use 'true' for TLS/SSL, 'false' for plain text
# SPARKY_FITNESS_EMAIL_USER=your_email@example.com
# SPARKY_FITNESS_EMAIL_PASS=your_email_password
# SPARKY_FITNESS_EMAIL_FROM=no-reply@example.com
# --- Volume Paths (Optional) ---
# These paths define where Docker volumes will store persistent data on your host.
# If not set, Docker will manage volumes automatically in its default location.
DB_PATH=/data/sparky-fitness/postgresql # Path for PostgreSQL database data
SERVER_BACKUP_PATH=/data/sparky-fitness/backup # Path for server backups
SERVER_UPLOADS_PATH=/data/sparky-fitness/uploads # Path for profile pictures and exercise images
# --- API Key Rate Limiting (Optional) ---
# Override the default rate limit for API key authentication (used by automation tools like n8n).
# Defaults to 100 requests per 60-second window if not set.
#SPARKY_FITNESS_API_KEY_RATELIMIT_WINDOW_MS=60000
#SPARKY_FITNESS_API_KEY_RATELIMIT_MAX_REQUESTS=100
# --- Start of Garmin Integration Settings ---
#Below variables are needed only for Garmin integration. If you don't use Garmin integration, you can remove them in your .env file.
# The URL for the Garmin microservice.
# For Docker Compose, this would typically be the service name and port (e.g., 'http://sparkyfitness-garmin:8000').
# For local development, use 'http://localhost:8000' or the port you've configured.
# GARMIN_MICROSERVICE_URL=http://sparkyfitness-garmin:8000
# This is used for Garmin Connect synchronization.
# If you are not using Garmin integration, you don't need this. Make sure this matches with GARMIN_MICROSERVICE_URL.
# GARMIN_SERVICE_PORT=8000
# set to true for China region. Everything else should be false. Optional - defaults to false
# GARMIN_SERVICE_IS_CN=false
# --- End of Garmin Integration Settings ---
# --- MCP Server Settings ---
# The port the MCP server will listen on.
# SPARKY_FITNESS_MCP_PORT=3001
# Vision API Settings (for sparky_analyze_food_image and sparky_scan_label)
# Supported providers: gemini, openai, anthropic
# VISION_API_PROVIDER=gemini
# VISION_API_KEY=
# Set to 'true' to enable developer tools (e.g., sparky_inspect_schema)
# Requires the authenticated user to have the 'admin' role.
# DEV_TOOLS_ENABLED=false
# ----- Developers Section -----
# Data source for external integrations (fitbit, garmin, withings).
# By default, these use live APIs. Set to 'local' to use mock data from the mock_data directory.
# To use these variables, you will also need to pass to Server container. For Garmin, pass to Garmin container.
#SPARKY_FITNESS_FITBIT_DATA_SOURCE=local
#SPARKY_FITNESS_WITHINGS_DATA_SOURCE=local
#SPARKY_FITNESS_GARMIN_DATA_SOURCE=local
#SPARKY_FITNESS_POLAR_DATA_SOURCE=local
#SPARKY_FITNESS_HEVY_DATA_SOURCE=local
# Set to 'true' to capture live API responses into mock data JSON files. Defaults to false.
#SPARKY_FITNESS_SAVE_MOCK_DATA=false
#-----------------------------
@@ -0,0 +1,70 @@
services:
sparkyfitness-db:
image: postgres:18.3-alpine
container_name: sparkyfitness-db
restart: always
# Uncomment below to expose PostgreSQL to the host (e.g., for pgAdmin, DBeaver).
# ports:
# - "${SPARKY_FITNESS_DB_PORT:-5432}:5432"
environment:
POSTGRES_DB: ${SPARKY_FITNESS_DB_NAME:?Variable is required and must be set}
POSTGRES_USER: ${SPARKY_FITNESS_DB_USER:?Variable is required and must be set}
POSTGRES_PASSWORD: ${SPARKY_FITNESS_DB_PASSWORD:?Variable is required and must be set}
PUID: 1000
GUID: 1000
volumes:
- ${DB_PATH:-./postgresql}:/var/lib/postgresql
sparkyfitness-server:
image: codewithcj/sparkyfitness_server:latest # Use pre-built image
environment:
SPARKY_FITNESS_LOG_LEVEL: ${SPARKY_FITNESS_LOG_LEVEL}
ALLOW_PRIVATE_NETWORK_CORS: ${ALLOW_PRIVATE_NETWORK_CORS:-false}
SPARKY_FITNESS_EXTRA_TRUSTED_ORIGINS: ${SPARKY_FITNESS_EXTRA_TRUSTED_ORIGINS:-}
SPARKY_FITNESS_DB_USER: ${SPARKY_FITNESS_DB_USER:-sparky}
SPARKY_FITNESS_DB_HOST: ${SPARKY_FITNESS_DB_HOST:-sparkyfitness-db} # Use the service name 'sparkyfitness-db' for inter-container communication
SPARKY_FITNESS_DB_NAME: ${SPARKY_FITNESS_DB_NAME}
SPARKY_FITNESS_DB_PASSWORD: ${SPARKY_FITNESS_DB_PASSWORD:?Variable is required and must be set}
SPARKY_FITNESS_APP_DB_USER: ${SPARKY_FITNESS_APP_DB_USER:-sparkyapp}
SPARKY_FITNESS_APP_DB_PASSWORD: ${SPARKY_FITNESS_APP_DB_PASSWORD:?Variable is required and must be set}
SPARKY_FITNESS_DB_PORT: 5432 # Uses internal container port. Do NOT change this if SPARKY_FITNESS_DB_HOST is using container name.
SPARKY_FITNESS_API_ENCRYPTION_KEY: ${SPARKY_FITNESS_API_ENCRYPTION_KEY:?Variable is required and must be set}
# Uncomment the line below and comment the line above to use a file-based secret
# SPARKY_FITNESS_API_ENCRYPTION_KEY_FILE: /run/secrets/sparkyfitness_api_key
BETTER_AUTH_SECRET: ${BETTER_AUTH_SECRET:?Variable is required and must be set}
# Uncomment the line below and comment the line above to use a file-based secret
# BETTER_AUTH_SECRET_FILE: /run/secrets/sparkyfitness_better_auth_secret
SPARKY_FITNESS_FRONTEND_URL: ${SPARKY_FITNESS_FRONTEND_URL:-http://0.0.0.0:3004}
SPARKY_FITNESS_DISABLE_SIGNUP: ${SPARKY_FITNESS_DISABLE_SIGNUP}
SPARKY_FITNESS_ADMIN_EMAIL: ${SPARKY_FITNESS_ADMIN_EMAIL} #User with this email can access the admin panel
SPARKY_FITNESS_EMAIL_HOST: ${SPARKY_FITNESS_EMAIL_HOST}
SPARKY_FITNESS_EMAIL_PORT: ${SPARKY_FITNESS_EMAIL_PORT}
SPARKY_FITNESS_EMAIL_SECURE: ${SPARKY_FITNESS_EMAIL_SECURE}
SPARKY_FITNESS_EMAIL_USER: ${SPARKY_FITNESS_EMAIL_USER}
SPARKY_FITNESS_EMAIL_PASS: ${SPARKY_FITNESS_EMAIL_PASS}
SPARKY_FITNESS_EMAIL_FROM: ${SPARKY_FITNESS_EMAIL_FROM}
GARMIN_MICROSERVICE_URL: http://sparkyfitness-garmin:8000 # Add Garmin microservice URL
PUID: 1000
GUID: 1000
restart: always
depends_on:
- sparkyfitness-db # Backend depends on the database being available
volumes:
- ${SERVER_BACKUP_PATH:-./backup}:/app/SparkyFitnessServer/backup # Mount volume for backups
- ${SERVER_UPLOADS_PATH:-./uploads}:/app/SparkyFitnessServer/uploads # Mount volume for Profile pictures and excercise images
sparkyfitness-frontend:
image: codewithcj/sparkyfitness:latest # Use pre-built image
ports:
- "8087:80" # Map host port 8087 to container port 80 (Nginx)
environment:
SPARKY_FITNESS_FRONTEND_URL: ${SPARKY_FITNESS_FRONTEND_URL}
SPARKY_FITNESS_SERVER_HOST: sparkyfitness-server # Internal Docker service name for the backend
SPARKY_FITNESS_SERVER_PORT: 3010 # Port the backend server listens on
PUID: 1000
GUID: 1000
restart: always
depends_on:
- sparkyfitness-server # Frontend depends on the server
#- sparkyfitness-garmin # Frontend depends on Garmin microservice. Enable if you are using Garmin Connect features.
@@ -0,0 +1,19 @@
services:
speedtest:
container_name: speedtest
image: henrywhitaker3/speedtest-tracker
ports:
- 8085:80
volumes:
- /data/speedtest:/config
environment:
- TZ=Europe/Lisbon
- PGID=1000
- PUID=1000
- OOKLA_EULA_GDPR=true
logging:
driver: "json-file"
options:
max-file: "10"
max-size: "200k"
restart: unless-stopped
+44
View File
@@ -0,0 +1,44 @@
services:
app:
image: mauriceboe/trek:latest
container_name: trek
read_only: true
security_opt:
- no-new-privileges:true
cap_drop:
- ALL
cap_add:
- CHOWN
- SETUID
- SETGID
tmpfs:
- /tmp:noexec,nosuid,size=64m
ports:
- "8083:3000"
environment:
- NODE_ENV=production
- PORT=3000
- ENCRYPTION_KEY=${ENCRYPTION_KEY}
- TZ=Europe/Lisbon
- LOG_LEVEL=info
- ALLOWED_ORIGINS=https://trips.lino.cooking
- FORCE_HTTPS=true
- TRUST_PROXY=1
- ALLOW_INTERNAL_NETWORK=true
- APP_URL=https://trips.lino.cooking
- OIDC_ISSUER=https://auth.lino.cooking
- OIDC_CLIENT_ID=652278a5-b695-4589-9d51-d23cfb2e15dd
- OIDC_CLIENT_SECRET=${OIDC_CLIENT_SECRET}
- OIDC_DISPLAY_NAME=PocketID
- OIDC_ONLY=true
- OIDC_DISCOVERY_URL=https://auth.lino.cooking/.well-known/openid-configuration
volumes:
- /data/trek/app:/app/data
- /data/trek/uploads:/app/uploads
restart: unless-stopped
healthcheck:
test: ["CMD", "wget", "-qO-", "http://localhost:3000/api/health"]
interval: 30s
timeout: 15s
retries: 3
start_period: 15s
+7
View File
@@ -0,0 +1,7 @@
services:
webserver:
image: nginx:alpine
container_name: hello-webserver
ports:
- "8082:80"
restart: unless-stopped
+7
View File
@@ -0,0 +1,7 @@
services:
webserver:
image: nginx:alpine
container_name: hello-webserver
ports:
- "8080:80"
restart: unless-stopped
@@ -0,0 +1,7 @@
services:
webserver:
image: nginx:alpine
container_name: hello-webserver
ports:
- "8081:80"
restart: unless-stopped
@@ -0,0 +1,7 @@
services:
webserver:
image: nginx:alpine
container_name: hello-webserver
ports:
- "8082:80"
restart: unless-stopped