Compare commits
52 Commits
4855448bed
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 4a11a4ce8e | |||
| 844cef4bef | |||
| 3ed21badb3 | |||
| bab98a020c | |||
| 3148136c22 | |||
| fe618ac8e4 | |||
| d04a68b4df | |||
| 6ed68a36be | |||
| 0eb4e4483c | |||
| 3032dd0882 | |||
| 0ee5f37b54 | |||
| 24bd4ec162 | |||
| cec78f8229 | |||
| 234d9edc68 | |||
| 59c003f619 | |||
| 98c88759be | |||
| 8173b7cbc1 | |||
| 886c6f559b | |||
| a05b2ea9ea | |||
| ce197c3299 | |||
| 94984f0635 | |||
| e1500967cf | |||
| 9e4a1f7836 | |||
| a286b755fe | |||
| e7bb6eadb8 | |||
| c5421a4af4 | |||
| fb7e101d6f | |||
| 9ffae8989a | |||
| e44dafb0e2 | |||
| 68d5178ab4 | |||
| 714f2449f6 | |||
| 5030362ba4 | |||
| 8fb291704e | |||
| f0b7351f64 | |||
| f111fdfcb7 | |||
| 07ac4dfec4 | |||
| 0cc349765e | |||
| 7ad7b26385 | |||
| 48ef6eda37 | |||
| 0c90b7a155 | |||
| ea9ad9eb30 | |||
| 13022f4c34 | |||
| 57847ca4fe | |||
| 7a211b2d4b | |||
| c3ecd9449e | |||
| 82b8bb2e2a | |||
| a00d3d6ee3 | |||
| c3ad0b889d | |||
| df602c0832 | |||
| 13539eebc6 | |||
| a96ddd3ddf | |||
| f60c5caa0a |
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -22,3 +22,5 @@ all:
|
||||
hosts:
|
||||
apps-1:
|
||||
ansible_host: 10.0.4.30
|
||||
apps-2:
|
||||
ansible_host: 10.0.4.31
|
||||
|
||||
@@ -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 }})"
|
||||
@@ -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
|
||||
|
||||
@@ -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: {}
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -0,0 +1,7 @@
|
||||
services:
|
||||
webserver:
|
||||
image: nginx:alpine
|
||||
container_name: hello-webserver
|
||||
ports:
|
||||
- "8082:80"
|
||||
restart: unless-stopped
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user