aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.ansible-lint3
-rw-r--r--Jenkinsfile23
-rw-r--r--README.md97
-rw-r--r--ansible.cfg8
-rw-r--r--data/authelia/authelia-authrequest.conf25
-rw-r--r--data/authelia/authelia-location.conf36
-rw-r--r--data/authelia/authelia.conf61
-rw-r--r--data/authelia/configuration.yml300
-rw-r--r--data/authelia/proxy.conf35
-rw-r--r--data/bookstack/bookstack.conf.j227
-rw-r--r--data/cadvisor/cadvisor.conf34
-rw-r--r--data/chronyd/chrony.conf59
-rw-r--r--data/docker/daemon.json7
-rw-r--r--data/drawio/drawio.conf34
-rw-r--r--data/firefly/firefly.conf.j275
-rw-r--r--data/freshrss/freshrss.conf38
-rw-r--r--data/game_server/lightdm.conf8
-rw-r--r--data/game_server/sunshine_proxy.conf24
-rw-r--r--data/game_server/xinitrc107
-rw-r--r--data/gitea/app.ini103
-rw-r--r--data/gitea/gitea.conf30
-rw-r--r--data/grafana/grafana.conf134
-rw-r--r--data/grafana/grafana.ini.j21268
-rw-r--r--data/grafana/main.json5176
-rw-r--r--data/home_assistant/configuration.yaml16
-rw-r--r--data/home_assistant/home_assistant.conf.j238
-rw-r--r--data/homer/config.yml194
-rw-r--r--data/homer/homer.conf34
-rw-r--r--data/homer/png/3cx.pngbin0 -> 52040 bytes
-rw-r--r--data/homer/png/SHODAN.jpgbin0 -> 32227 bytes
-rw-r--r--data/homer/png/adguardhome.pngbin0 -> 3511 bytes
-rw-r--r--data/homer/png/adminer.pngbin0 -> 10244 bytes
-rw-r--r--data/homer/png/airsonic.pngbin0 -> 27546 bytes
-rw-r--r--data/homer/png/alarmpi.pngbin0 -> 4569 bytes
-rw-r--r--data/homer/png/alertmanager.pngbin0 -> 30103 bytes
-rw-r--r--data/homer/png/alltube.pngbin0 -> 59953 bytes
-rw-r--r--data/homer/png/amazon.pngbin0 -> 46453 bytes
-rw-r--r--data/homer/png/amd.pngbin0 -> 28457 bytes
-rw-r--r--data/homer/png/amvd.pngbin0 -> 16597 bytes
-rw-r--r--data/homer/png/ansible.pngbin0 -> 9029 bytes
-rw-r--r--data/homer/png/archivebox.pngbin0 -> 4831 bytes
-rw-r--r--data/homer/png/archiveteamwarrior.pngbin0 -> 8742 bytes
-rw-r--r--data/homer/png/argocd.pngbin0 -> 43662 bytes
-rw-r--r--data/homer/png/ariang.pngbin0 -> 13576 bytes
-rw-r--r--data/homer/png/artifactory.pngbin0 -> 2665 bytes
-rw-r--r--data/homer/png/authelia.pngbin0 -> 54179 bytes
-rw-r--r--data/homer/png/avmfritzbox.pngbin0 -> 11242 bytes
-rw-r--r--data/homer/png/awx.pngbin0 -> 81865 bytes
-rw-r--r--data/homer/png/azure.pngbin0 -> 15224 bytes
-rw-r--r--data/homer/png/azuredns.pngbin0 -> 15292 bytes
-rw-r--r--data/homer/png/bacula.pngbin0 -> 6492 bytes
-rw-r--r--data/homer/png/badge.pngbin0 -> 14768 bytes
-rw-r--r--data/homer/png/baikal.pngbin0 -> 1967 bytes
-rw-r--r--data/homer/png/bastillion.pngbin0 -> 4082 bytes
-rw-r--r--data/homer/png/bazarr.pngbin0 -> 15324 bytes
-rw-r--r--data/homer/png/beats.pngbin0 -> 16663 bytes
-rw-r--r--data/homer/png/bithumen.pngbin0 -> 3835 bytes
-rw-r--r--data/homer/png/bitwarden.pngbin0 -> 16971 bytes
-rw-r--r--data/homer/png/blueiris.pngbin0 -> 197462 bytes
-rw-r--r--data/homer/png/booksonic.pngbin0 -> 34768 bytes
-rw-r--r--data/homer/png/bookstack.pngbin0 -> 3245 bytes
-rw-r--r--data/homer/png/box.pngbin0 -> 107979 bytes
-rw-r--r--data/homer/png/brewpi.pngbin0 -> 13019 bytes
-rw-r--r--data/homer/png/buxfer.pngbin0 -> 5995 bytes
-rw-r--r--data/homer/png/cabot.pngbin0 -> 10764 bytes
-rw-r--r--data/homer/png/cadvisor.pngbin0 -> 14106 bytes
-rw-r--r--data/homer/png/calibreweb.pngbin0 -> 1583 bytes
-rw-r--r--data/homer/png/cardigann.pngbin0 -> 1075 bytes
-rw-r--r--data/homer/png/cgit.pngbin0 -> 1366 bytes
-rw-r--r--data/homer/png/checkmk.pngbin0 -> 4192 bytes
-rw-r--r--data/homer/png/chevereto.pngbin0 -> 8380 bytes
-rw-r--r--data/homer/png/chowdown.pngbin0 -> 26579 bytes
-rw-r--r--data/homer/png/chronograf.pngbin0 -> 17902 bytes
-rw-r--r--data/homer/png/chudnick.com.pngbin0 -> 15861 bytes
-rw-r--r--data/homer/png/clarkson.pngbin0 -> 3773 bytes
-rw-r--r--data/homer/png/cloudcmd.pngbin0 -> 18884 bytes
-rw-r--r--data/homer/png/cockpit.pngbin0 -> 10017 bytes
-rw-r--r--data/homer/png/cockpitcms.pngbin0 -> 3738 bytes
-rw-r--r--data/homer/png/code.pngbin0 -> 32885 bytes
-rw-r--r--data/homer/png/codeserver.pngbin0 -> 2465 bytes
-rw-r--r--data/homer/png/codimd.pngbin0 -> 686 bytes
-rw-r--r--data/homer/png/concourse.pngbin0 -> 9329 bytes
-rw-r--r--data/homer/png/couchpotato.pngbin0 -> 42776 bytes
-rw-r--r--data/homer/png/cpanel.pngbin0 -> 2769 bytes
-rw-r--r--data/homer/png/cryptpad.pngbin0 -> 10031 bytes
-rw-r--r--data/homer/png/cyberchef.pngbin0 -> 5970 bytes
-rw-r--r--data/homer/png/deemix.pngbin0 -> 68591 bytes
-rw-r--r--data/homer/png/deluge.pngbin0 -> 4187 bytes
-rw-r--r--data/homer/png/directus.pngbin0 -> 3006 bytes
-rw-r--r--data/homer/png/docker.pngbin0 -> 17182 bytes
-rw-r--r--data/homer/png/docspell.pngbin0 -> 3525 bytes
-rw-r--r--data/homer/png/dokuwiki.pngbin0 -> 16014 bytes
-rw-r--r--data/homer/png/domoticz.pngbin0 -> 8189 bytes
-rw-r--r--data/homer/png/dozzle.pngbin0 -> 2417 bytes
-rw-r--r--data/homer/png/drawio.pngbin0 -> 4944 bytes
-rw-r--r--data/homer/png/drone.pngbin0 -> 50256 bytes
-rw-r--r--data/homer/png/droppy.pngbin0 -> 3173 bytes
-rw-r--r--data/homer/png/duplicacy.pngbin0 -> 19131 bytes
-rw-r--r--data/homer/png/duplicati.pngbin0 -> 7431 bytes
-rw-r--r--data/homer/png/ebay.pngbin0 -> 26348 bytes
-rw-r--r--data/homer/png/elastic.pngbin0 -> 1208 bytes
-rw-r--r--data/homer/png/elasticsearch.pngbin0 -> 33255 bytes
-rw-r--r--data/homer/png/element.pngbin0 -> 26433 bytes
-rw-r--r--data/homer/png/emby.pngbin0 -> 2270 bytes
-rw-r--r--data/homer/png/embystat.pngbin0 -> 6216 bytes
-rw-r--r--data/homer/png/emq.pngbin0 -> 5474 bytes
-rw-r--r--data/homer/png/erste-george.pngbin0 -> 8173 bytes
-rw-r--r--data/homer/png/erste.pngbin0 -> 6102 bytes
-rw-r--r--data/homer/png/esphome.pngbin0 -> 1177 bytes
-rw-r--r--data/homer/png/evebox.pngbin0 -> 9484 bytes
-rw-r--r--data/homer/png/facebook-messenger.pngbin0 -> 7660 bytes
-rw-r--r--data/homer/png/facebook.pngbin0 -> 9674 bytes
-rw-r--r--data/homer/png/filebrowser.pngbin0 -> 14063 bytes
-rw-r--r--data/homer/png/filerun.pngbin0 -> 15814 bytes
-rw-r--r--data/homer/png/firefly.pngbin0 -> 62508 bytes
-rw-r--r--data/homer/png/firefoxsend.pngbin0 -> 13216 bytes
-rw-r--r--data/homer/png/flexget.pngbin0 -> 33135 bytes
-rw-r--r--data/homer/png/flood.pngbin0 -> 16173 bytes
-rw-r--r--data/homer/png/foldingathome.pngbin0 -> 9723 bytes
-rw-r--r--data/homer/png/freeipa.pngbin0 -> 49701 bytes
-rw-r--r--data/homer/png/freenas.pngbin0 -> 2192 bytes
-rw-r--r--data/homer/png/freepbx.pngbin0 -> 51100 bytes
-rw-r--r--data/homer/png/freshrss.pngbin0 -> 12552 bytes
-rw-r--r--data/homer/png/ghost.pngbin0 -> 1819 bytes
-rw-r--r--data/homer/png/gitea.pngbin0 -> 13863 bytes
-rw-r--r--data/homer/png/github.pngbin0 -> 3224 bytes
-rw-r--r--data/homer/png/gitlab.pngbin0 -> 12975 bytes
-rw-r--r--data/homer/png/glances.pngbin0 -> 3123 bytes
-rw-r--r--data/homer/png/gogs.pngbin0 -> 20249 bytes
-rw-r--r--data/homer/png/google-calendar.pngbin0 -> 7040 bytes
-rw-r--r--data/homer/png/google-keep.pngbin0 -> 5640 bytes
-rw-r--r--data/homer/png/google-mail.pngbin0 -> 4858 bytes
-rw-r--r--data/homer/png/googlemaps.pngbin0 -> 65044 bytes
-rw-r--r--data/homer/png/gotify.pngbin0 -> 19188 bytes
-rw-r--r--data/homer/png/grafana.pngbin0 -> 30484 bytes
-rw-r--r--data/homer/png/grav.pngbin0 -> 1360 bytes
-rw-r--r--data/homer/png/graylog.pngbin0 -> 3573 bytes
-rw-r--r--data/homer/png/grocy.pngbin0 -> 2661 bytes
-rw-r--r--data/homer/png/guacamole.pngbin0 -> 10534 bytes
-rw-r--r--data/homer/png/handbrake.pngbin0 -> 18907 bytes
-rw-r--r--data/homer/png/haproxy.pngbin0 -> 12464 bytes
-rw-r--r--data/homer/png/hasura.pngbin0 -> 11153 bytes
-rw-r--r--data/homer/png/hdhomerun.pngbin0 -> 4213 bytes
-rw-r--r--data/homer/png/headphones.pngbin0 -> 8948 bytes
-rw-r--r--data/homer/png/healthchecks.pngbin0 -> 10895 bytes
-rw-r--r--data/homer/png/heimdall.pngbin0 -> 11840 bytes
-rw-r--r--data/homer/png/home-assistant.pngbin0 -> 47969 bytes
-rw-r--r--data/homer/png/homebridge.pngbin0 -> 133806 bytes
-rw-r--r--data/homer/png/homer.pngbin0 -> 88703 bytes
-rw-r--r--data/homer/png/hp.pngbin0 -> 30144 bytes
-rw-r--r--data/homer/png/hubitat.pngbin0 -> 23735 bytes
-rw-r--r--data/homer/png/huginn.pngbin0 -> 51187 bytes
-rw-r--r--data/homer/png/hugo.pngbin0 -> 7993 bytes
-rw-r--r--data/homer/png/hydra.pngbin0 -> 26955 bytes
-rw-r--r--data/homer/png/icecast.pngbin0 -> 8692 bytes
-rw-r--r--data/homer/png/icinga.pngbin0 -> 39686 bytes
-rw-r--r--data/homer/png/idrac.pngbin0 -> 3789 bytes
-rw-r--r--data/homer/png/ilo.pngbin0 -> 5816 bytes
-rw-r--r--data/homer/png/infoblox.pngbin0 -> 3206 bytes
-rw-r--r--data/homer/png/invidious.pngbin0 -> 45432 bytes
-rw-r--r--data/homer/png/invoiceninja.pngbin0 -> 4765 bytes
-rw-r--r--data/homer/png/iobroker.pngbin0 -> 3428 bytes
-rw-r--r--data/homer/png/irc.pngbin0 -> 100138 bytes
-rw-r--r--data/homer/png/jackett.pngbin0 -> 24159 bytes
-rw-r--r--data/homer/png/jaeger.pngbin0 -> 8382 bytes
-rw-r--r--data/homer/png/jdownloader.pngbin0 -> 52501 bytes
-rw-r--r--data/homer/png/jeedom.pngbin0 -> 3623 bytes
-rw-r--r--data/homer/png/jellyfin.pngbin0 -> 21925 bytes
-rw-r--r--data/homer/png/jenkins.pngbin0 -> 6453 bytes
-rw-r--r--data/homer/png/jitsimeet.pngbin0 -> 65889 bytes
-rw-r--r--data/homer/png/joomla.pngbin0 -> 2015 bytes
-rw-r--r--data/homer/png/joplin.pngbin0 -> 22996 bytes
-rw-r--r--data/homer/png/kanboard.pngbin0 -> 2215 bytes
-rw-r--r--data/homer/png/kavita.pngbin0 -> 18275 bytes
-rw-r--r--data/homer/png/keila.pngbin0 -> 39333 bytes
-rw-r--r--data/homer/png/keycloak.pngbin0 -> 30410 bytes
-rw-r--r--data/homer/png/kibana.pngbin0 -> 8839 bytes
-rw-r--r--data/homer/png/kimai.pngbin0 -> 66299 bytes
-rw-r--r--data/homer/png/kitana.pngbin0 -> 5026 bytes
-rw-r--r--data/homer/png/kodi.pngbin0 -> 1716 bytes
-rw-r--r--data/homer/png/komga.pngbin0 -> 43137 bytes
-rw-r--r--data/homer/png/krusader.pngbin0 -> 27708 bytes
-rw-r--r--data/homer/png/kubernetes-dashboard.pngbin0 -> 14851 bytes
-rw-r--r--data/homer/png/kutt.pngbin0 -> 79849 bytes
-rw-r--r--data/homer/png/lazylibrarian.pngbin0 -> 12549 bytes
-rw-r--r--data/homer/png/leantime.pngbin0 -> 7201 bytes
-rw-r--r--data/homer/png/lemonldapng.pngbin0 -> 7322 bytes
-rw-r--r--data/homer/png/letencrypt.pngbin0 -> 21241 bytes
-rw-r--r--data/homer/png/librenms.pngbin0 -> 4046 bytes
-rw-r--r--data/homer/png/librephotos.pngbin0 -> 7461 bytes
-rw-r--r--data/homer/png/librespeed.pngbin0 -> 9466 bytes
-rw-r--r--data/homer/png/lidarr.pngbin0 -> 44323 bytes
-rw-r--r--data/homer/png/listmonk.pngbin0 -> 9000 bytes
-rw-r--r--data/homer/png/logstash.pngbin0 -> 16516 bytes
-rw-r--r--data/homer/png/loki.pngbin0 -> 20829 bytes
-rw-r--r--data/homer/png/longhorn.pngbin0 -> 26391 bytes
-rw-r--r--data/homer/png/lychee.pngbin0 -> 28252 bytes
-rw-r--r--data/homer/png/mailcow.pngbin0 -> 25277 bytes
-rw-r--r--data/homer/png/mailhog.pngbin0 -> 1188 bytes
-rw-r--r--data/homer/png/mainsail.pngbin0 -> 2316 bytes
-rw-r--r--data/homer/png/mak.pngbin0 -> 9755 bytes
-rw-r--r--data/homer/png/mattermost.pngbin0 -> 4945 bytes
-rw-r--r--data/homer/png/mayanedms.pngbin0 -> 2580 bytes
-rw-r--r--data/homer/png/mcmyadmin.pngbin0 -> 8239 bytes
-rw-r--r--data/homer/png/mealie.pngbin0 -> 64032 bytes
-rw-r--r--data/homer/png/mediawiki.pngbin0 -> 9247 bytes
-rw-r--r--data/homer/png/medusa.pngbin0 -> 3715 bytes
-rw-r--r--data/homer/png/meraki.pngbin0 -> 8311 bytes
-rw-r--r--data/homer/png/microsoft-todo.pngbin0 -> 14832 bytes
-rw-r--r--data/homer/png/mikrotik.pngbin0 -> 80148 bytes
-rw-r--r--data/homer/png/mineos.pngbin0 -> 17034 bytes
-rw-r--r--data/homer/png/miniflux.pngbin0 -> 592 bytes
-rw-r--r--data/homer/png/minio.pngbin0 -> 36803 bytes
-rw-r--r--data/homer/png/molecule.pngbin0 -> 35447 bytes
-rw-r--r--data/homer/png/mongodb.pngbin0 -> 87280 bytes
-rw-r--r--data/homer/png/monica.pngbin0 -> 21987 bytes
-rw-r--r--data/homer/png/monit.pngbin0 -> 16823 bytes
-rw-r--r--data/homer/png/motioneye.pngbin0 -> 14671 bytes
-rw-r--r--data/homer/png/mqtt.pngbin0 -> 37463 bytes
-rw-r--r--data/homer/png/mylar.pngbin0 -> 34567 bytes
-rw-r--r--data/homer/png/n8n.pngbin0 -> 6749 bytes
-rw-r--r--data/homer/png/nagios.pngbin0 -> 23818 bytes
-rw-r--r--data/homer/png/navidrome.pngbin0 -> 14638 bytes
-rw-r--r--data/homer/png/ncore.pngbin0 -> 17343 bytes
-rw-r--r--data/homer/png/nessus.pngbin0 -> 2455 bytes
-rw-r--r--data/homer/png/netatmo.pngbin0 -> 12788 bytes
-rw-r--r--data/homer/png/netboot.pngbin0 -> 2986 bytes
-rw-r--r--data/homer/png/netbootxyz.pngbin0 -> 17004 bytes
-rw-r--r--data/homer/png/netbox.pngbin0 -> 29060 bytes
-rw-r--r--data/homer/png/netdata.pngbin0 -> 1913 bytes
-rw-r--r--data/homer/png/nextcloud.pngbin0 -> 6078 bytes
-rw-r--r--data/homer/png/nginx.pngbin0 -> 61836 bytes
-rw-r--r--data/homer/png/nginxproxymanager.pngbin0 -> 9069 bytes
-rw-r--r--data/homer/png/nodered.pngbin0 -> 46516 bytes
-rw-r--r--data/homer/png/nowshowing.pngbin0 -> 11763 bytes
-rw-r--r--data/homer/png/ntop.pngbin0 -> 32436 bytes
-rw-r--r--data/homer/png/nxfilter.pngbin0 -> 1084 bytes
-rw-r--r--data/homer/png/nzbget.pngbin0 -> 4685 bytes
-rw-r--r--data/homer/png/nzbhydra.pngbin0 -> 5372 bytes
-rw-r--r--data/homer/png/octoprint.pngbin0 -> 22247 bytes
-rw-r--r--data/homer/png/omada.pngbin0 -> 38231 bytes
-rw-r--r--data/homer/png/ombi.pngbin0 -> 9264 bytes
-rw-r--r--data/homer/png/omnidb.pngbin0 -> 9648 bytes
-rw-r--r--data/homer/png/onlyoffice.pngbin0 -> 36365 bytes
-rw-r--r--data/homer/png/openhab.pngbin0 -> 4851 bytes
-rw-r--r--data/homer/png/openmaptiler.pngbin0 -> 2753 bytes
-rw-r--r--data/homer/png/openmediavault.pngbin0 -> 2721 bytes
-rw-r--r--data/homer/png/openspeedtest.pngbin0 -> 8144 bytes
-rw-r--r--data/homer/png/opensprinkler.pngbin0 -> 3322 bytes
-rw-r--r--data/homer/png/openvpn.pngbin0 -> 4428 bytes
-rw-r--r--data/homer/png/openwrt.pngbin0 -> 29980 bytes
-rw-r--r--data/homer/png/opnsense.pngbin0 -> 28281 bytes
-rw-r--r--data/homer/png/osticket.pngbin0 -> 4219 bytes
-rw-r--r--data/homer/png/overseerr.pngbin0 -> 36653 bytes
-rw-r--r--data/homer/png/owncloud.pngbin0 -> 5005 bytes
-rw-r--r--data/homer/png/ownphotos.pngbin0 -> 2814 bytes
-rw-r--r--data/homer/png/pagerduty.pngbin0 -> 7505 bytes
-rw-r--r--data/homer/png/paloaltonetworks.pngbin0 -> 3167 bytes
-rw-r--r--data/homer/png/paperless-ng.pngbin0 -> 14675 bytes
-rw-r--r--data/homer/png/papermerge.pngbin0 -> 165076 bytes
-rw-r--r--data/homer/png/partkeepr.pngbin0 -> 3160 bytes
-rw-r--r--data/homer/png/peertube.pngbin0 -> 4178 bytes
-rw-r--r--data/homer/png/pfsense.pngbin0 -> 114048 bytes
-rw-r--r--data/homer/png/pgadmin.pngbin0 -> 9988 bytes
-rw-r--r--data/homer/png/phantombot.pngbin0 -> 40093 bytes
-rw-r--r--data/homer/png/photoprism.pngbin0 -> 21536 bytes
-rw-r--r--data/homer/png/photostructure.pngbin0 -> 96463 bytes
-rw-r--r--data/homer/png/photoview.pngbin0 -> 178807 bytes
-rw-r--r--data/homer/png/phpldapadmin.pngbin0 -> 28909 bytes
-rw-r--r--data/homer/png/phpmyadmin.pngbin0 -> 10147 bytes
-rw-r--r--data/homer/png/piaware.pngbin0 -> 9477 bytes
-rw-r--r--data/homer/png/pihole.pngbin0 -> 67394 bytes
-rw-r--r--data/homer/png/pingdom.pngbin0 -> 22125 bytes
-rw-r--r--data/homer/png/piwigo.pngbin0 -> 29077 bytes
-rw-r--r--data/homer/png/plausible.pngbin0 -> 75359 bytes
-rw-r--r--data/homer/png/pleroma.pngbin0 -> 1144 bytes
-rw-r--r--data/homer/png/plesk.pngbin0 -> 3813 bytes
-rw-r--r--data/homer/png/plex.pngbin0 -> 73866 bytes
-rw-r--r--data/homer/png/plexdrive.pngbin0 -> 20970 bytes
-rw-r--r--data/homer/png/plexrequests.pngbin0 -> 3521 bytes
-rw-r--r--data/homer/png/plume.pngbin0 -> 4382 bytes
-rw-r--r--data/homer/png/podify.pngbin0 -> 37410 bytes
-rw-r--r--data/homer/png/portainer.pngbin0 -> 14272 bytes
-rw-r--r--data/homer/png/portus.pngbin0 -> 29684 bytes
-rw-r--r--data/homer/png/postgres.pngbin0 -> 60635 bytes
-rw-r--r--data/homer/png/printer.pngbin0 -> 2081 bytes
-rw-r--r--data/homer/png/privatebin.pngbin0 -> 5522 bytes
-rw-r--r--data/homer/png/projectsend.pngbin0 -> 3611 bytes
-rw-r--r--data/homer/png/prometheus.pngbin0 -> 44193 bytes
-rw-r--r--data/homer/png/prowlarr.pngbin0 -> 56027 bytes
-rw-r--r--data/homer/png/proxmox.pngbin0 -> 6679 bytes
-rw-r--r--data/homer/png/prtg.pngbin0 -> 5055 bytes
-rw-r--r--data/homer/png/psitransfer.pngbin0 -> 1169 bytes
-rw-r--r--data/homer/png/pterodactyl.pngbin0 -> 110637 bytes
-rw-r--r--data/homer/png/pyload.pngbin0 -> 38040 bytes
-rw-r--r--data/homer/png/qbittorrent.pngbin0 -> 31453 bytes
-rw-r--r--data/homer/png/qnap.pngbin0 -> 3015 bytes
-rw-r--r--data/homer/png/rabbitmq.pngbin0 -> 31047 bytes
-rw-r--r--data/homer/png/radarr.pngbin0 -> 91825 bytes
-rw-r--r--data/homer/png/radicale.pngbin0 -> 9951 bytes
-rw-r--r--data/homer/png/rainloop.pngbin0 -> 4605 bytes
-rw-r--r--data/homer/png/rancher.pngbin0 -> 2177 bytes
-rw-r--r--data/homer/png/raneto.pngbin0 -> 126003 bytes
-rw-r--r--data/homer/png/rclone.pngbin0 -> 4101 bytes
-rw-r--r--data/homer/png/readarr.pngbin0 -> 23550 bytes
-rw-r--r--data/homer/png/recalbox.pngbin0 -> 2380 bytes
-rw-r--r--data/homer/png/redis.pngbin0 -> 37626 bytes
-rw-r--r--data/homer/png/requestrr.pngbin0 -> 9121 bytes
-rw-r--r--data/homer/png/resiliosync.pngbin0 -> 6805 bytes
-rw-r--r--data/homer/png/riot.pngbin0 -> 13534 bytes
-rw-r--r--data/homer/png/rocketchat.pngbin0 -> 5656 bytes
-rw-r--r--data/homer/png/rompya.pngbin0 -> 20866 bytes
-rw-r--r--data/homer/png/rook.pngbin0 -> 23059 bytes
-rw-r--r--data/homer/png/roundcube.pngbin0 -> 45693 bytes
-rw-r--r--data/homer/png/router.pngbin0 -> 4435 bytes
-rw-r--r--data/homer/png/rspamd.pngbin0 -> 8401 bytes
-rw-r--r--data/homer/png/rstudioserver.pngbin0 -> 3385 bytes
-rw-r--r--data/homer/png/rundeck.pngbin0 -> 893 bytes
-rw-r--r--data/homer/png/runeaudio.pngbin0 -> 4978 bytes
-rw-r--r--data/homer/png/rutorrent.pngbin0 -> 5654 bytes
-rw-r--r--data/homer/png/sabnzbd.pngbin0 -> 5287 bytes
-rw-r--r--data/homer/png/scrutiny.pngbin0 -> 44993 bytes
-rw-r--r--data/homer/png/seafile.pngbin0 -> 8980 bytes
-rw-r--r--data/homer/png/searxmetasearchengine.pngbin0 -> 9463 bytes
-rw-r--r--data/homer/png/serviio.pngbin0 -> 3496 bytes
-rw-r--r--data/homer/png/shaarli.pngbin0 -> 6422 bytes
-rw-r--r--data/homer/png/shinobi.pngbin0 -> 24457 bytes
-rw-r--r--data/homer/png/sickbeard.pngbin0 -> 17672 bytes
-rw-r--r--data/homer/png/sickchill.pngbin0 -> 13804 bytes
-rw-r--r--data/homer/png/sickgear.pngbin0 -> 13363 bytes
-rw-r--r--data/homer/png/sinusbot.pngbin0 -> 16451 bytes
-rw-r--r--data/homer/png/slack.pngbin0 -> 27837 bytes
-rw-r--r--data/homer/png/snibox.pngbin0 -> 6092 bytes
-rw-r--r--data/homer/png/sonarqube.pngbin0 -> 3966 bytes
-rw-r--r--data/homer/png/sonarr.pngbin0 -> 35159 bytes
-rw-r--r--data/homer/png/sourcegraph.pngbin0 -> 7500 bytes
-rw-r--r--data/homer/png/splunk.pngbin0 -> 11311 bytes
-rw-r--r--data/homer/png/spotweb.pngbin0 -> 5328 bytes
-rw-r--r--data/homer/png/squidex.pngbin0 -> 8246 bytes
-rw-r--r--data/homer/png/statping.pngbin0 -> 45022 bytes
-rw-r--r--data/homer/png/strapi.pngbin0 -> 5253 bytes
-rw-r--r--data/homer/png/streama.pngbin0 -> 1532 bytes
-rw-r--r--data/homer/png/sunshine.pngbin0 -> 18686 bytes
-rw-r--r--data/homer/png/synclounge.pngbin0 -> 8578 bytes
-rw-r--r--data/homer/png/syncthing.pngbin0 -> 19975 bytes
-rw-r--r--data/homer/png/synology.pngbin0 -> 5217 bytes
-rw-r--r--data/homer/png/taiga.pngbin0 -> 7975 bytes
-rw-r--r--data/homer/png/tandoorrecipes.pngbin0 -> 6676 bytes
-rw-r--r--data/homer/png/tasmoadmin.pngbin0 -> 888 bytes
-rw-r--r--data/homer/png/tasmota.pngbin0 -> 2432 bytes
-rw-r--r--data/homer/png/tautulli.pngbin0 -> 29051 bytes
-rw-r--r--data/homer/png/tdarr.pngbin0 -> 21853 bytes
-rw-r--r--data/homer/png/teedy.pngbin0 -> 26743 bytes
-rw-r--r--data/homer/png/thanos.pngbin0 -> 15265 bytes
-rw-r--r--data/homer/png/theia.pngbin0 -> 13841 bytes
-rw-r--r--data/homer/png/thelounge.pngbin0 -> 4329 bytes
-rw-r--r--data/homer/png/tinytinyrss.pngbin0 -> 12215 bytes
-rw-r--r--data/homer/png/tplink.pngbin0 -> 17219 bytes
-rw-r--r--data/homer/png/traccar.pngbin0 -> 7929 bytes
-rw-r--r--data/homer/png/traefik.pngbin0 -> 37646 bytes
-rw-r--r--data/homer/png/transmission.pngbin0 -> 8313 bytes
-rw-r--r--data/homer/png/trilium.pngbin0 -> 16344 bytes
-rw-r--r--data/homer/png/truenas.pngbin0 -> 3796 bytes
-rw-r--r--data/homer/png/tubearchivist.pngbin0 -> 16951 bytes
-rw-r--r--data/homer/png/tubesync.pngbin0 -> 15953 bytes
-rw-r--r--data/homer/png/tvheadend.pngbin0 -> 3811 bytes
-rw-r--r--data/homer/png/ubooquity.pngbin0 -> 11584 bytes
-rw-r--r--data/homer/png/ultimateguitar.pngbin0 -> 33562 bytes
-rw-r--r--data/homer/png/unifi.pngbin0 -> 186692 bytes
-rw-r--r--data/homer/png/unraid.pngbin0 -> 13669 bytes
-rw-r--r--data/homer/png/updog.pngbin0 -> 10888 bytes
-rw-r--r--data/homer/png/urbackup.pngbin0 -> 2952 bytes
-rw-r--r--data/homer/png/valetudo.pngbin0 -> 79289 bytes
-rw-r--r--data/homer/png/vault.pngbin0 -> 14587 bytes
-rw-r--r--data/homer/png/vikunja.pngbin0 -> 11543 bytes
-rw-r--r--data/homer/png/virtualradarserver.pngbin0 -> 31512 bytes
-rw-r--r--data/homer/png/vmware.pngbin0 -> 8477 bytes
-rw-r--r--data/homer/png/vmwarehorizon.pngbin0 -> 13380 bytes
-rw-r--r--data/homer/png/volumio.pngbin0 -> 9859 bytes
-rw-r--r--data/homer/png/wallabag.pngbin0 -> 18060 bytes
-rw-r--r--data/homer/png/wanikani.pngbin0 -> 19142 bytes
-rw-r--r--data/homer/png/watcher.pngbin0 -> 6418 bytes
-rw-r--r--data/homer/png/watchtower.pngbin0 -> 116149 bytes
-rw-r--r--data/homer/png/webdav.pngbin0 -> 15969 bytes
-rw-r--r--data/homer/png/webmin.pngbin0 -> 9548 bytes
-rw-r--r--data/homer/png/webtools.pngbin0 -> 14342 bytes
-rw-r--r--data/homer/png/wekan.pngbin0 -> 54896 bytes
-rw-r--r--data/homer/png/wetty.pngbin0 -> 24101 bytes
-rw-r--r--data/homer/png/wggenweb.pngbin0 -> 9929 bytes
-rw-r--r--data/homer/png/whoami.pngbin0 -> 9743 bytes
-rw-r--r--data/homer/png/wikijs.pngbin0 -> 12419 bytes
-rw-r--r--data/homer/png/wireguard.pngbin0 -> 73201 bytes
-rw-r--r--data/homer/png/wizarr.pngbin0 -> 37856 bytes
-rw-r--r--data/homer/png/wordpress.pngbin0 -> 14376 bytes
-rw-r--r--data/homer/png/xbrowsersync.pngbin0 -> 48213 bytes
-rw-r--r--data/homer/png/xigmanas.pngbin0 -> 16562 bytes
-rw-r--r--data/homer/png/xteve.pngbin0 -> 3499 bytes
-rw-r--r--data/homer/png/xwiki.pngbin0 -> 6237 bytes
-rw-r--r--data/homer/png/yacht.pngbin0 -> 13391 bytes
-rw-r--r--data/homer/png/ynab.pngbin0 -> 5870 bytes
-rw-r--r--data/homer/png/youtube.pngbin0 -> 15908 bytes
-rw-r--r--data/homer/png/youtubedl.pngbin0 -> 3998 bytes
-rw-r--r--data/homer/png/zabbix.pngbin0 -> 2442 bytes
-rw-r--r--data/homer/png/zigbee2mqtt.pngbin0 -> 25352 bytes
-rw-r--r--data/homer/png/znc.pngbin0 -> 24480 bytes
-rw-r--r--data/homer/png/zoneminder.pngbin0 -> 65487 bytes
-rw-r--r--data/homer/png/zulip.pngbin0 -> 5887 bytes
-rw-r--r--data/homer/png/zwavejs.pngbin0 -> 26171 bytes
-rw-r--r--data/homer/svg/adguardhome.svg99
-rw-r--r--data/homer/svg/adminer.svg217
-rw-r--r--data/homer/svg/bazarr.svg9
-rw-r--r--data/homer/svg/caddy.svg1
-rw-r--r--data/homer/svg/calibreweb.svg9
-rw-r--r--data/homer/svg/changedetection.svg1
-rw-r--r--data/homer/svg/cloudflare.svg10
-rw-r--r--data/homer/svg/discord.svg6
-rw-r--r--data/homer/svg/filebrowser.svg147
-rw-r--r--data/homer/svg/filerun.svg7
-rw-r--r--data/homer/svg/freshrss.svg12
-rw-r--r--data/homer/svg/grocy.svg33
-rw-r--r--data/homer/svg/hedgedoc.svg20
-rw-r--r--data/homer/svg/home-assistant.svg54
-rw-r--r--data/homer/svg/kavita.svg124
-rw-r--r--data/homer/svg/mailcow.svg3
-rw-r--r--data/homer/svg/mealie.svg1148
-rw-r--r--data/homer/svg/mkb_bank.svg11
-rw-r--r--data/homer/svg/ntop.svg26
-rw-r--r--data/homer/svg/overseerr.svg90
-rw-r--r--data/homer/svg/pagerduty.svgbin0 -> 521 bytes
-rw-r--r--data/homer/svg/portainer.svg1
-rw-r--r--data/homer/svg/prowlarr.svg296
-rw-r--r--data/homer/svg/pywttr-docker.svg101
-rw-r--r--data/homer/svg/radicale.svg10
-rw-r--r--data/homer/svg/searxng.svg56
-rw-r--r--data/homer/svg/sonarr.svg9
-rw-r--r--data/homer/svg/text-generation-webui.svg10
-rw-r--r--data/homer/svg/thanos.svg1
-rw-r--r--data/homer/svg/xbrowsersync.svg168
-rw-r--r--data/influxdb/influxdb.conf30
-rw-r--r--data/invidious/invidious.conf.j234
-rw-r--r--data/invidious/invidious.env11
-rw-r--r--data/jellyfin/jellyfin.conf68
-rw-r--r--data/jenkins/configuration.yml.j2163
-rw-r--r--data/jenkins/jenkins.conf85
-rw-r--r--data/kanboard/config.php59
-rw-r--r--data/kanboard/kanboard.conf.j234
-rw-r--r--data/lidarr/lidarr.conf.j236
-rw-r--r--data/loki/config.yml54
-rw-r--r--data/loki/loki.conf21
-rw-r--r--data/msmtp_mta/msmtprc11
-rw-r--r--data/navidrome/navidrome.conf34
-rw-r--r--data/nextcloud/nextcloud.conf45
-rw-r--r--data/photoprism/photoprism.conf41
-rw-r--r--data/pihole-exporter/pihole-exporter.conf27
-rw-r--r--data/pihole/pihole_unbound.conf35
-rw-r--r--data/pihole/setupVars.conf10
-rw-r--r--data/prometheus-blackbox-exporter/blackbox.yml62
-rw-r--r--data/prometheus-nginx-exporter/defaults1
-rw-r--r--data/prometheus-nginx-exporter/metrics.conf14
-rw-r--r--data/prometheus-server/defaults1
-rw-r--r--data/prometheus-server/prometheus.yml168
-rw-r--r--data/promtail/config.yml30
-rw-r--r--data/promtail/config_standard.yml28
-rw-r--r--data/prowlarr/prowlarr.conf.j236
-rw-r--r--data/pywttr_docker/pywttr_docker.conf.j233
-rw-r--r--data/qbittorrent/qbittorrent.conf.j234
-rw-r--r--data/radarr/radarr.conf.j236
-rw-r--r--data/readarr/readarr.conf.j236
-rw-r--r--data/searxng/searxng.conf48
-rw-r--r--data/searxng/settings.yml74
-rw-r--r--data/searxng/uwsgi.ini50
-rw-r--r--data/sonarr/sonarr.conf.j236
-rw-r--r--data/text_generation/text_generation.conf.j237
-rw-r--r--data/vaultwarden/vaultwarden.conf.j239
-rw-r--r--group_vars/all/vars.yml570
-rw-r--r--renovate.json11
-rw-r--r--requirements.yml5
-rw-r--r--roles/linux_base/defaults/main.yml1
-rw-r--r--roles/linux_base/handlers/main.yml16
-rw-r--r--roles/linux_base/tasks/main.yml57
-rw-r--r--roles/proxmox/cloudinit_guest/defaults/main.yml7
-rw-r--r--roles/proxmox/cloudinit_guest/tasks/main.yml80
-rw-r--r--roles/proxmox/debian_cloudinit/defaults/main.yml8
-rw-r--r--roles/proxmox/debian_cloudinit/tasks/main.yml115
-rw-r--r--roles/proxmox/fedora_cloudinit/defaults/main.yml8
-rw-r--r--roles/proxmox/fedora_cloudinit/tasks/main.yml122
-rw-r--r--roles/proxmox/proxmox_backup_server/tasks/main.yml42
-rw-r--r--roles/proxmox/pve_backup/tasks/main.yml17
-rw-r--r--roles/proxmox/system/defaults/main.yml8
-rw-r--r--roles/proxmox/system/tasks/main.yml30
-rw-r--r--roles/proxmox/system/tasks/proxmox_repo.yml8
-rw-r--r--roles/proxmox/system/tasks/user.yml28
-rw-r--r--roles/services/chronyd/handlers/main.yml4
-rw-r--r--roles/services/chronyd/tasks/main.yml30
-rw-r--r--roles/services/containers/arr_stack/handlers/main.yml4
-rw-r--r--roles/services/containers/arr_stack/tasks/gluetun.yml105
-rw-r--r--roles/services/containers/arr_stack/tasks/lidarr.yml93
-rw-r--r--roles/services/containers/arr_stack/tasks/main.yml130
-rw-r--r--roles/services/containers/arr_stack/tasks/prowlarr.yml92
-rw-r--r--roles/services/containers/arr_stack/tasks/qbittorrent.yml94
-rw-r--r--roles/services/containers/arr_stack/tasks/radarr.yml93
-rw-r--r--roles/services/containers/arr_stack/tasks/readarr.yml93
-rw-r--r--roles/services/containers/arr_stack/tasks/sonarr.yml93
-rw-r--r--roles/services/containers/authelia/handlers/main.yml4
-rw-r--r--roles/services/containers/authelia/tasks/main.yml283
-rw-r--r--roles/services/containers/bookstack/handlers/main.yml4
-rw-r--r--roles/services/containers/bookstack/tasks/main.yml118
-rw-r--r--roles/services/containers/cadvisor/handlers/main.yml4
-rw-r--r--roles/services/containers/cadvisor/tasks/main.yml90
-rw-r--r--roles/services/containers/drawio/handlers/main.yml4
-rw-r--r--roles/services/containers/drawio/tasks/main.yml149
-rw-r--r--roles/services/containers/firefly/handlers/main.yml4
-rw-r--r--roles/services/containers/firefly/tasks/main.yml172
-rw-r--r--roles/services/containers/freshrss/handlers/main.yml4
-rw-r--r--roles/services/containers/freshrss/tasks/main.yml101
-rw-r--r--roles/services/containers/gitea/handlers/main.yml4
-rw-r--r--roles/services/containers/gitea/tasks/main.yml171
-rw-r--r--roles/services/containers/home_assistant/handlers/main.yml4
-rw-r--r--roles/services/containers/home_assistant/tasks/main.yml86
-rw-r--r--roles/services/containers/homer/handlers/main.yml4
-rw-r--r--roles/services/containers/homer/tasks/main.yml122
-rw-r--r--roles/services/containers/invidious/handlers/main.yml29
-rw-r--r--roles/services/containers/invidious/tasks/main.yml124
-rw-r--r--roles/services/containers/jellyfin/handlers/main.yml4
-rw-r--r--roles/services/containers/jellyfin/tasks/main.yml159
-rw-r--r--roles/services/containers/kanboard/handlers/main.yml18
-rw-r--r--roles/services/containers/kanboard/tasks/main.yml93
-rw-r--r--roles/services/containers/navidrome/handlers/main.yml4
-rw-r--r--roles/services/containers/navidrome/tasks/main.yml117
-rw-r--r--roles/services/containers/nextcloud/handlers/main.yml4
-rw-r--r--roles/services/containers/nextcloud/tasks/main.yml184
-rw-r--r--roles/services/containers/photoprism/defaults/main.yml10
-rw-r--r--roles/services/containers/photoprism/handlers/main.yml4
-rw-r--r--roles/services/containers/photoprism/tasks/main.yml115
-rw-r--r--roles/services/containers/pihole_exporter/tasks/main.yml97
-rw-r--r--roles/services/containers/pywttr_docker/handlers/main.yml18
-rw-r--r--roles/services/containers/pywttr_docker/tasks/main.yml74
-rw-r--r--roles/services/containers/renovate/tasks/main.yml87
-rw-r--r--roles/services/containers/searxng/handlers/main.yml4
-rw-r--r--roles/services/containers/searxng/tasks/main.yml170
-rw-r--r--roles/services/containers/text_generation/handlers/main.yml29
-rw-r--r--roles/services/containers/text_generation/tasks/main.yml89
-rw-r--r--roles/services/containers/vaultwarden/handlers/main.yml4
-rw-r--r--roles/services/containers/vaultwarden/tasks/main.yml79
-rw-r--r--roles/services/docker_rootless/defaults/main.yml18
-rw-r--r--roles/services/docker_rootless/handlers/main.yml6
-rw-r--r--roles/services/docker_rootless/tasks/main.yml93
-rw-r--r--roles/services/freeipa/client/defaults/main.yml0
-rw-r--r--roles/services/freeipa/client/tasks/main.yml4
-rw-r--r--roles/services/freeipa/server/defaults/main.yml1
-rw-r--r--roles/services/freeipa/server/tasks/main.yml43
-rw-r--r--roles/services/game_server/handlers/main.yml71
-rw-r--r--roles/services/game_server/tasks/main.yml223
-rw-r--r--roles/services/jenkins/handlers/main.yml13
-rw-r--r--roles/services/jenkins/tasks/main.yml184
-rw-r--r--roles/services/monitoring/grafana/defaults/main.yml5
-rw-r--r--roles/services/monitoring/grafana/handlers/main.yml13
-rw-r--r--roles/services/monitoring/grafana/tasks/main.yml125
-rw-r--r--roles/services/monitoring/influxdb/defaults/main.yml6
-rw-r--r--roles/services/monitoring/influxdb/handlers/main.yml4
-rw-r--r--roles/services/monitoring/influxdb/tasks/main.yml19
-rw-r--r--roles/services/monitoring/loki/handlers/main.yml8
-rw-r--r--roles/services/monitoring/loki/tasks/main.yml80
-rw-r--r--roles/services/monitoring/prometheus/blackbox-exporter/tasks/main.yml0
-rw-r--r--roles/services/monitoring/prometheus/nginx_exporter/defaults/main.yml4
-rw-r--r--roles/services/monitoring/prometheus/nginx_exporter/handlers/main.yml9
-rw-r--r--roles/services/monitoring/prometheus/nginx_exporter/tasks/main.yml44
-rw-r--r--roles/services/monitoring/prometheus/node_exporter/defaults/main.yml4
-rw-r--r--roles/services/monitoring/prometheus/node_exporter/tasks/main.yml28
-rw-r--r--roles/services/monitoring/prometheus/server/defaults/main.yml6
-rw-r--r--roles/services/monitoring/prometheus/server/tasks/main.yml79
-rw-r--r--roles/services/monitoring/promtail/handlers/main.yml39
-rw-r--r--roles/services/monitoring/promtail/tasks/main.yml151
-rw-r--r--roles/services/msmtp_mta/tasks/main.yml11
-rw-r--r--roles/services/pihole/handlers/main.yml14
-rw-r--r--roles/services/pihole/tasks/main.yml80
-rw-r--r--roles/services/ssh/tasks/main.yml46
-rw-r--r--roles/services/unattended_upgrades/tasks/main.yml63
-rw-r--r--run.yml89
579 files changed, 18625 insertions, 0 deletions
diff --git a/.ansible-lint b/.ansible-lint
new file mode 100644
index 0000000..e652a03
--- /dev/null
+++ b/.ansible-lint
@@ -0,0 +1,3 @@
1skip_list:
2 - '403'
3 - '204'
diff --git a/Jenkinsfile b/Jenkinsfile
new file mode 100644
index 0000000..3cbfdd0
--- /dev/null
+++ b/Jenkinsfile
@@ -0,0 +1,23 @@
1pipeline {
2 agent any
3 stages {
4 stage('Checkout') {
5 steps {
6 checkout scm
7 }
8 }
9
10 stage('Deploy') {
11 when { branch 'master' }
12 steps {
13 sh 'ansible-galaxy install -r requirements.yml'
14 ansiblePlaybook become: true, credentialsId: 'jenkins_freeipa_ssh', disableHostKeyChecking: true, installation: 'Default', inventory: 'inventory.yml', playbook: 'run.yml', vaultCredentialsId: 'ansible_vault'
15 }
16 }
17 }
18 post {
19 always {
20 recordIssues enabledForFailure: true, tools: [ansibleLint(pattern: '**/run.yml')]
21 }
22 }
23}
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..b948a56
--- /dev/null
+++ b/README.md
@@ -0,0 +1,97 @@
1# homelab\_iac
2
3A complete Ansible infrastructure as code representation of my homelab
4featuring many custom roles and a playbook to tie it all together.
5
6# Using
7
8## Replica
9This repo will not work for you immediately after cloning. Please
10continue reading to understand why. You may clone this repo and then
11use it as is after filling in the missing pieces for your environment
12to achieve a replica of my setup.
13
14## Individual Roles
15Alternatively, if you are only interested in a role or two, you can copy
16the file structure of the role(s) you are interested in to your own project.
17Roles should be entirely self contained other than dependence on variables
18stored in the global variable file.
19
20# Omissions
21In this public mirror I have decided to omit several files either due
22to their sensitivity or their specificity to my environment.
23
24## Commit History
25I did not originally intend for this repo to be public and so
26previous commits contained plaintext sensitive info. Therefore
27this public mirror will not contain my commit history and will instead
28start as a snapshot of my configuration at the time of the initial commit.
29
30## Inventory
31I have not included my inventory.yml file in this mirror.
32Please consult the Ansible docs on creating an inventory file
33before attempting to use this repo.
34
35## Secrets
36I have purposefully not included my Ansible vault containing various secrets
37in this public mirror. So if you clone this repo and attempt to run the playbook
38you will get errors about missing variables. Below is a list of variables
39that will need to be defined in order for the playbook to run properly.
40It is highly advised but not mandatory to keep these variables in an
41Ansible vault.
42
43- proxmox\_password
44- ipabackup\_password
45- ci\_password
46- ipaadmin\_principal
47- ipaadmin\_password
48- ipafulladmin\_password
49- grafana\_password
50- grafana\_smtp\_password
51- influx\_password
52- pihole\_password
53- pihole\_api\_token
54- authelia\_jwt\_secret
55- authelia\_session\_secret
56- authelia\_encryption\_key
57- authelia\_oidc\_hmac
58- authelia\_oidc\_cert
59- authelia\_oidc\_key
60- authelia\_smtp\_password
61- authelia\_ldap\_password
62- gitea\_client\_secret
63- jenkins\_client\_secret
64- nextcloud\_client\_secret
65- jellyfin\_client\_secret
66- bookstack\_client\_secret
67- navidrome\_encryptionkey
68- msmtp\_mta\_email\_password
69- invidious\_postgres\_password
70- gitea\_internal\_token
71- gitea\_lfs\_jwt\_secret
72- jenkins\_ipa\_password
73- docker\_registry\_password
74- pbs\_admin\_password
75- pbs\_password
76- nextcloud\_postgres\_password
77- nextcloud\_admin\_password
78- renovate\_token
79- jenkins\_password
80- jenkins\_apikey
81- jenkins\_privkey
82- jenkins\_vault
83- jenkins\_oic\_secret
84- jenkins\_oic\_escapehatch
85- jenkins\_metrics\_key
86- photoprism\_admin\_password
87- wireguard\_privkey
88- wireguard\_addrs
89- gluetun\_cities
90- nginx\_key
91- bookstack\_mysql\_root\_password
92- bookstack\_mysql\_password
93- bookstack\_oidc\_secret
94- firefly\_postgres\_password
95- firefly\_app\_key
96- firefly\_cron\_token
97- firefly\_access\_token
diff --git a/ansible.cfg b/ansible.cfg
new file mode 100644
index 0000000..91fcbcf
--- /dev/null
+++ b/ansible.cfg
@@ -0,0 +1,8 @@
1[defaults]
2deprecation_warnings=False
3devel_warnings=False
4callback_whitelist = profile_roles, profile_tasks
5default_become=True
6become=True
7host_key_checking=False
8roles_path=roles
diff --git a/data/authelia/authelia-authrequest.conf b/data/authelia/authelia-authrequest.conf
new file mode 100644
index 0000000..8d629bf
--- /dev/null
+++ b/data/authelia/authelia-authrequest.conf
@@ -0,0 +1,25 @@
1## Send a subrequest to Authelia to verify if the user is authenticated and has permission to access the resource.
2auth_request /authelia;
3
4## Set the $target_url variable based on the original request.
5
6## Comment this line if you're using nginx without the http_set_misc module.
7#set_escape_uri $target_url $scheme://$http_host$request_uri;
8
9## Uncomment this line if you're using NGINX without the http_set_misc module.
10set $target_url $scheme://$http_host$request_uri;
11
12## Save the upstream response headers from Authelia to variables.
13auth_request_set $user $upstream_http_remote_user;
14auth_request_set $groups $upstream_http_remote_groups;
15auth_request_set $name $upstream_http_remote_name;
16auth_request_set $email $upstream_http_remote_email;
17
18## Inject the response headers from the variables into the request made to the backend.
19proxy_set_header Remote-User $user;
20proxy_set_header Remote-Groups $groups;
21proxy_set_header Remote-Name $name;
22proxy_set_header Remote-Email $email;
23
24## If the subreqest returns 200 pass to the backend, if the subrequest returns 401 redirect to the portal.
25error_page 401 =302 https://auth.chudnick.com/?rd=$target_url;
diff --git a/data/authelia/authelia-location.conf b/data/authelia/authelia-location.conf
new file mode 100644
index 0000000..43504e9
--- /dev/null
+++ b/data/authelia/authelia-location.conf
@@ -0,0 +1,36 @@
1set $upstream_authelia http://127.0.0.1:9091/api/verify;
2
3## Virtual endpoint created by nginx to forward auth requests.
4location /authelia {
5 ## Essential Proxy Configuration
6 internal;
7 proxy_pass $upstream_authelia;
8
9 ## Headers
10 ## The headers starting with X-* are required.
11 proxy_set_header X-Original-URL $scheme://$http_host$request_uri;
12 proxy_set_header X-Original-Method $request_method;
13 proxy_set_header X-Forwarded-Method $request_method;
14 proxy_set_header X-Forwarded-Proto $scheme;
15 proxy_set_header X-Forwarded-Host $http_host;
16 proxy_set_header X-Forwarded-Uri $request_uri;
17 proxy_set_header X-Forwarded-For $remote_addr;
18 proxy_set_header Content-Length "";
19 proxy_set_header Connection "";
20
21 ## Basic Proxy Configuration
22 proxy_pass_request_body off;
23 proxy_next_upstream error timeout invalid_header http_500 http_502 http_503; # Timeout if the real server is dead
24 proxy_redirect http:// $scheme://;
25 proxy_http_version 1.1;
26 proxy_cache_bypass $cookie_session;
27 proxy_no_cache $cookie_session;
28 proxy_buffers 4 32k;
29 client_body_buffer_size 128k;
30
31 ## Advanced Proxy Configuration
32 send_timeout 5m;
33 proxy_read_timeout 240;
34 proxy_send_timeout 240;
35 proxy_connect_timeout 240;
36}
diff --git a/data/authelia/authelia.conf b/data/authelia/authelia.conf
new file mode 100644
index 0000000..5f3a573
--- /dev/null
+++ b/data/authelia/authelia.conf
@@ -0,0 +1,61 @@
1server {
2 listen 443 ssl;
3 ssl_certificate /etc/letsencrypt/live/chudnick.com/fullchain.pem;
4 ssl_certificate_key /etc/letsencrypt/live/chudnick.com/privkey.pem;
5 ssl_stapling on;
6 ssl_stapling_verify on;
7
8 server_name auth.chudnick.com;
9
10 location / {
11 ## Headers
12 proxy_set_header Host $host;
13 proxy_set_header X-Original-URL $scheme://$http_host$request_uri;
14 proxy_set_header X-Forwarded-Proto $scheme;
15 proxy_set_header X-Forwarded-Host $http_host;
16 proxy_set_header X-Forwarded-Uri $request_uri;
17 proxy_set_header X-Forwarded-Ssl on;
18 proxy_set_header X-Forwarded-For $remote_addr;
19 proxy_set_header X-Real-IP $remote_addr;
20 proxy_set_header Connection "";
21
22 ## Basic Proxy Configuration
23 client_body_buffer_size 128k;
24 proxy_next_upstream error timeout invalid_header http_500 http_502 http_503;
25 proxy_redirect http:// $scheme://;
26 proxy_http_version 1.1;
27 proxy_cache_bypass $cookie_session;
28 proxy_no_cache $cookie_session;
29 proxy_buffers 64 256k;
30
31 ## Trusted Proxies Configuration
32 real_ip_header X-Forwarded-For;
33 real_ip_recursive on;
34
35 ## Advanced Proxy Configuration
36 send_timeout 5m;
37 proxy_read_timeout 360;
38 proxy_send_timeout 360;
39 proxy_connect_timeout 360;
40 proxy_set_header Host $host;
41 proxy_pass http://127.0.0.1:9091;
42 }
43
44 location /metrics {
45 proxy_set_header Host $host;
46 proxy_pass http://127.0.0.1:9959;
47 }
48
49 location /api/verify {
50 proxy_pass http://127.0.0.1:9091;
51 }
52
53}
54
55server {
56 listen 80;
57 listen [::]:80;
58 server_name auth.chudnick.com;
59 return 301 https://$host$request_uri;
60}
61
diff --git a/data/authelia/configuration.yml b/data/authelia/configuration.yml
new file mode 100644
index 0000000..c4fc923
--- /dev/null
+++ b/data/authelia/configuration.yml
@@ -0,0 +1,300 @@
1theme: dark
2default_redirection_url: https://auth.chudnick.com
3
4server:
5 host: 0.0.0.0
6 port: 9091
7 read_buffer_size: 10485760
8
9log:
10 level: info
11 keep_stdout: true
12
13authentication_backend:
14 password_reset:
15 disable: true
16 ldap:
17 implementation: freeipa
18 url: ldap://192.168.20.20
19 timeout: 5s
20 start_tls: false
21 base_dn: DC=home,DC=local
22 user: UID=authelia,CN=users,CN=accounts,DC=home,DC=local
23
24access_control:
25 default_policy: deny
26 rules:
27 - domain: auth.chudnick.com
28 policy: bypass
29
30 # bypass subsonic api endpoint
31 - domain: "music.chudnick.com"
32 resources: "^/rest/.*$"
33 policy: bypass
34
35 # bypass metrics endpoint for monitoring server
36 - domain: "music.chudnick.com"
37 resources: "^/metrics$"
38 networks:
39 - '192.168.20.32'
40 policy: bypass
41
42 - domain: "music.chudnick.com"
43 policy: one_factor
44
45 # bypass mobile client api
46 - domain: "rss.chudnick.com"
47 resources: "/api/.*$"
48 policy: bypass
49
50 - domain: "rss.chudnick.com"
51 resources:
52 - "/"
53 - "/i/.*$"
54 policy: one_factor
55
56 - domain: "invidious.chudnick.com"
57 policy: one_factor
58
59 # bypass grafana connection to prometheus
60 - domain: "monitoring.chudnick.com"
61 resources: "^/prometheus/api.*"
62 networks:
63 - '127.0.0.1'
64 - '192.168.20.32'
65 policy: bypass
66
67 - domain: "monitoring.chudnick.com"
68 resources: "^/prometheus.*"
69 policy: one_factor
70
71 # bypass metrics endpoint for monitoring server
72 - domain: "cadvisor.chudnick.com"
73 resources: "/metrics"
74 networks:
75 - '192.168.20.32'
76 policy: bypass
77
78 - domain: "cadvisor.chudnick.com"
79 policy: one_factor
80
81 - domain: "drawio.chudnick.com"
82 policy: one_factor
83
84 # bypass grafana connection to loki
85 - domain: "logs.chudnick.com"
86 networks:
87 - '127.0.0.1'
88 - '192.168.20.32'
89 policy: bypass
90
91 # bypass loki log push
92 - domain: "logs.chudnick.com"
93 resources: "/loki/api/v1/push"
94 policy: bypass
95
96 - domain: "logs.chudnick.com"
97 policy: one_factor
98
99 - domain: "dashboard.chudnick.com"
100 policy: one_factor
101
102 - domain: "photos.chudnick.com"
103 policy: one_factor
104
105 - domain: "qbittorrent.chudnick.com"
106 policy: one_factor
107
108 - domain: "sonarr.chudnick.com"
109 policy: one_factor
110
111 - domain: "radarr.chudnick.com"
112 policy: one_factor
113
114 - domain: "lidarr.chudnick.com"
115 policy: one_factor
116
117 - domain: "readarr.chudnick.com"
118 policy: one_factor
119
120 - domain: "prowlarr.chudnick.com"
121 policy: one_factor
122
123 - domain: "weather.chudnick.com"
124 policy: one_factor
125
126 - domain: "gpt.chudnick.com"
127 policy: one_factor
128
129 - domain: "tasks.chudnick.com"
130 policy: one_factor
131
132 - domain: "finances.chudnick.com"
133 policy: one_factor
134
135 - domain: "finimporter.chudnick.com"
136 policy: one_factor
137
138 - domain: "homeassistant.chudnick.com"
139 policy: one_factor
140
141 - domain: "vaultwarden.chudnick.com"
142 resources: "^/admin.*$"
143 subject: 'group:vaultwarden-admins'
144 policy: two_factor
145
146totp:
147 issuer: auth.chudnick.com
148 algorithm: sha1
149 digits: 6
150 period: 30
151 skew: 1
152 secret_size: 32
153
154session:
155 name: authelia_session
156 expiration: 3600
157 inactivity: 300
158 domain: "chudnick.com"
159
160 redis:
161 host: redis_authelia
162 port: 6379
163
164regulation:
165 max_retries: 3
166 find_time: 120
167 ban_time: 300
168
169storage:
170 local:
171 path: /config/db.sqlite3
172
173telemetry:
174 metrics:
175 enabled: true
176 address: "tcp://0.0.0.0:9959"
177 buffers:
178 read: 4096
179 write: 4096
180 timeouts:
181 read: 2s
182 write: 2s
183 idle: 30s
184
185notifier:
186 disable_startup_check: false
187 smtp:
188 host: mail.chudnick.com
189 port: 465
190 timeout: 5s
191 username: authelia
192 sender: "Authelia <authelia@chudnick.com>"
193 identifier: "auth.chudnick.com"
194 subject: "[Authelia] {title}"
195 startup_check_address: "sam@chudnick.com"
196
197ntp:
198 address: "netservices.home.local:123"
199
200identity_providers:
201 oidc:
202 clients:
203 - id: gitea
204 description: gitea
205 secret: '$plaintext${{ gitea_client_secret }}'
206 public: false
207 authorization_policy: one_factor
208 redirect_uris:
209 - https://gitea.chudnick.com/user/oauth2/authelia/callback
210 scopes:
211 - openid
212 - profile
213 - email
214 - groups
215 userinfo_signing_algorithm: none
216 pre_configured_consent_duration: 4w
217 grant_types:
218 - refresh_token
219 - authorization_code
220 response_types:
221 - code
222 response_modes:
223 - form_post
224 - query
225 - fragment
226
227 - id: grafana
228 description: grafana
229 secret: '$plaintext${{ grafana_client_secret }}'
230 public: false
231 authorization_policy: one_factor
232 pre_configured_consent_duration: 4w
233 redirect_uris:
234 - https://monitoring.chudnick.com/grafana/login/generic_oauth
235 scopes:
236 - openid
237 - profile
238 - groups
239 - email
240 userinfo_signing_algorithm: none
241
242 - id: nextcloud
243 description: NextCloud
244 secret: '$plaintext${{ nextcloud_client_secret }}'
245 public: false
246 authorization_policy: one_factor
247 pre_configured_consent_duration: 4w
248 redirect_uris:
249 - https://nextcloud.chudnick.com/apps/oidc_login/oidc
250 scopes:
251 - openid
252 - profile
253 - email
254 - groups
255 userinfo_signing_algorithm: none
256
257 - id: jenkins
258 description: Jenkins
259 secret: '$plaintext${{ jenkins_client_secret }}'
260 public: false
261 authorization_policy: one_factor
262 pre_configured_consent_duration: 4w
263 redirect_uris:
264 - https://jenkins.chudnick.com/securityRealm/finishLogin
265 scopes:
266 - openid
267 - profile
268 - email
269 - groups
270 - offline_access
271 userinfo_signing_algorithm: none
272
273 - id: jellyfin
274 description: jellyfin
275 secret: '$plaintext${{ jellyfin_client_secret }}'
276 public: false
277 authorization_policy: one_factor
278 pre_configured_consent_duration: 4w
279 redirect_uris:
280 - https://jellyfin.chudnick.com/sso/OID/r/authelia
281 scopes:
282 - openid
283 - groups
284 - profile
285 userinfo_signing_algorithm: none
286
287 - id: bookstack
288 description: bookstack
289 secret: '$plaintext${{ bookstack_client_secret }}'
290 public: false
291 authorization_policy: one_factor
292 pre_configured_consent_duration: 4w
293 redirect_uris:
294 - https://wiki.chudnick.com/oidc/callback
295 scopes:
296 - openid
297 - groups
298 - profile
299 - email
300 userinfo_signing_algorithm: none
diff --git a/data/authelia/proxy.conf b/data/authelia/proxy.conf
new file mode 100644
index 0000000..4098bb2
--- /dev/null
+++ b/data/authelia/proxy.conf
@@ -0,0 +1,35 @@
1## Headers
2proxy_set_header Host $host;
3proxy_set_header X-Original-URL $scheme://$http_host$request_uri;
4proxy_set_header X-Forwarded-Proto $scheme;
5proxy_set_header X-Forwarded-Host $http_host;
6proxy_set_header X-Forwarded-Uri $request_uri;
7proxy_set_header X-Forwarded-Ssl on;
8proxy_set_header X-Forwarded-For $remote_addr;
9proxy_set_header X-Real-IP $remote_addr;
10proxy_set_header Connection "";
11
12## Basic Proxy Configuration
13client_body_buffer_size 128k;
14proxy_next_upstream error timeout invalid_header http_500 http_502 http_503; ## Timeout if the real server is dead.
15proxy_redirect http:// $scheme://;
16proxy_http_version 1.1;
17proxy_cache_bypass $cookie_session;
18proxy_no_cache $cookie_session;
19proxy_buffers 64 256k;
20
21## Trusted Proxies Configuration
22## Please read the following documentation before configuring this:
23## https://www.authelia.com/integration/proxies/nginx/#trusted-proxies
24# set_real_ip_from 10.0.0.0/8;
25# set_real_ip_from 172.16.0.0/12;
26# set_real_ip_from 192.168.0.0/16;
27# set_real_ip_from fc00::/7;
28real_ip_header X-Forwarded-For;
29real_ip_recursive on;
30
31## Advanced Proxy Configuration
32send_timeout 5m;
33proxy_read_timeout 360;
34proxy_send_timeout 360;
35proxy_connect_timeout 360;
diff --git a/data/bookstack/bookstack.conf.j2 b/data/bookstack/bookstack.conf.j2
new file mode 100644
index 0000000..0dd6f63
--- /dev/null
+++ b/data/bookstack/bookstack.conf.j2
@@ -0,0 +1,27 @@
1server {
2 listen 443 ssl;
3 server_name {{ bookstack_server_name }};
4
5 ssl_certificate /etc/letsencrypt/live/chudnick.com/fullchain.pem;
6 ssl_certificate_key /etc/letsencrypt/live/chudnick.com/privkey.pem;
7 add_header Strict-Transport-Security "max-age=31536000" always;
8 ssl_stapling on;
9 ssl_stapling_verify on;
10
11 # Security / XSS Mitigation Headers
12 add_header X-Frame-Options "SAMEORIGIN";
13 add_header X-XSS-Protection "1; mode=block";
14 add_header X-Content-Type-Options "nosniff";
15
16 location / {
17 proxy_pass http://127.0.0.1:{{ bookstack_external_port }}/;
18 }
19
20}
21
22server {
23 listen 80;
24 listen [::]:80;
25 server_name {{ bookstack_server_name }};
26 return 301 https://$host$request_uri;
27}
diff --git a/data/cadvisor/cadvisor.conf b/data/cadvisor/cadvisor.conf
new file mode 100644
index 0000000..62ffd48
--- /dev/null
+++ b/data/cadvisor/cadvisor.conf
@@ -0,0 +1,34 @@
1server {
2 listen 443 ssl;
3 server_name cadvisor.chudnick.com;
4
5 ssl_certificate /etc/letsencrypt/live/chudnick.com/fullchain.pem;
6 ssl_certificate_key /etc/letsencrypt/live/chudnick.com/privkey.pem;
7 add_header Strict-Transport-Security "max-age=31536000" always;
8 ssl_stapling on;
9 ssl_stapling_verify on;
10
11 # Security / XSS Mitigation Headers
12 add_header X-Frame-Options "SAMEORIGIN";
13 add_header X-XSS-Protection "1; mode=block";
14 add_header X-Content-Type-Options "nosniff";
15
16 # authelia
17 include /etc/nginx/snippets/authelia-location.conf;
18
19 location / {
20 # authelia
21 include /etc/nginx/snippets/proxy.conf;
22 include /etc/nginx/snippets/authelia-authrequest.conf;
23
24 proxy_pass http://127.0.0.1:8004/;
25 }
26
27}
28
29server {
30 listen 80;
31 listen [::]:80;
32 server_name cadvisor.chudnick.com;
33 return 301 https://$host$request_uri;
34}
diff --git a/data/chronyd/chrony.conf b/data/chronyd/chrony.conf
new file mode 100644
index 0000000..59d71f6
--- /dev/null
+++ b/data/chronyd/chrony.conf
@@ -0,0 +1,59 @@
1# Welcome to the chrony configuration file. See chrony.conf(5) for more
2# information about usable directives.
3
4# Include configuration files found in /etc/chrony/conf.d.
5confdir /etc/chrony/conf.d
6
7# Use Debian vendor zone.
8pool 2.debian.pool.ntp.org iburst
9
10# Use time sources from DHCP.
11sourcedir /run/chrony-dhcp
12
13# Use NTP sources found in /etc/chrony/sources.d.
14sourcedir /etc/chrony/sources.d
15
16# This directive specify the location of the file containing ID/key pairs for
17# NTP authentication.
18keyfile /etc/chrony/chrony.keys
19
20# This directive specify the file into which chronyd will store the rate
21# information.
22driftfile /var/lib/chrony/chrony.drift
23
24# Save NTS keys and cookies.
25ntsdumpdir /var/lib/chrony
26
27# Uncomment the following line to turn logging on.
28#log tracking measurements statistics
29
30# Log files location.
31logdir /var/log/chrony
32
33# Stop bad estimates upsetting machine clock.
34maxupdateskew 100.0
35
36# This directive enables kernel synchronisation (every 11 minutes) of the
37# real-time clock. Note that it can’t be used along with the 'rtcfile' directive.
38rtcsync
39
40# Step the system clock instead of slewing it if the adjustment is larger than
41# one second, but only in the first three clock updates.
42makestep 1 3
43
44# Get TAI-UTC offset and leap seconds from the system tz database.
45# This directive must be commented out when using time sources serving
46# leap-smeared time.
47leapsectz right/UTC
48
49# Allow usage as NTP server from local network
50allow 192.168.30.0/24
51allow 192.168.20.0/24
52allow 192.168.10.0/24
53allow 127.0.0.1/8
54
55# Serve time even if not synchronized to an external time source
56local stratum 10
57
58# Require authentication for NTP sources
59#authselectmode require
diff --git a/data/docker/daemon.json b/data/docker/daemon.json
new file mode 100644
index 0000000..ba71dfc
--- /dev/null
+++ b/data/docker/daemon.json
@@ -0,0 +1,7 @@
1{
2 "log-driver": "loki",
3 "log-opts": {
4 "loki-url": "https://logs.chudnick.com/loki/api/v1/push",
5 "loki-batch-size": "400"
6 }
7}
diff --git a/data/drawio/drawio.conf b/data/drawio/drawio.conf
new file mode 100644
index 0000000..3c374cc
--- /dev/null
+++ b/data/drawio/drawio.conf
@@ -0,0 +1,34 @@
1server {
2 listen 443 ssl;
3
4 ssl_certificate /etc/letsencrypt/live/chudnick.com/fullchain.pem;
5 ssl_certificate_key /etc/letsencrypt/live/chudnick.com/privkey.pem;
6 ssl_stapling on;
7 ssl_stapling_verify on;
8
9 server_name drawio.chudnick.com;
10
11 # Security Headers
12 add_header X-Frame-Options "SAMEORIGIN";
13 add_header X-XSS-Protection "1; mode=block";
14 add_header X-Content-Type-Options "nosniff";
15
16 # authelia
17 include /etc/nginx/snippets/authelia-location.conf;
18
19 location / {
20 #authelia
21 include /etc/nginx/snippets/proxy.conf;
22 include /etc/nginx/snippets/authelia-authrequest.conf;
23
24 proxy_pass http://127.0.0.1:8400;
25 }
26}
27
28server {
29 listen 80;
30 listen [::]:80;
31 server_name drawio.chudnick.com;
32 return 301 https://$host$request_uri;
33}
34
diff --git a/data/firefly/firefly.conf.j2 b/data/firefly/firefly.conf.j2
new file mode 100644
index 0000000..d3bc9a1
--- /dev/null
+++ b/data/firefly/firefly.conf.j2
@@ -0,0 +1,75 @@
1server {
2 listen 443 ssl;
3 server_name {{ firefly_server_name }};
4
5 ssl_certificate /etc/letsencrypt/live/chudnick.com/fullchain.pem;
6 ssl_certificate_key /etc/letsencrypt/live/chudnick.com/privkey.pem;
7 add_header Strict-Transport-Security "max-age=31536000" always;
8 ssl_stapling on;
9 ssl_stapling_verify on;
10
11 # Security / XSS Mitigation Headers
12 add_header X-Frame-Options "SAMEORIGIN";
13 add_header X-XSS-Protection "1; mode=block";
14 add_header X-Content-Type-Options "nosniff";
15 add_header 'Access-Control-Allow-Origin' 'https://chudnick.com' always;
16
17 # authelia
18 include /etc/nginx/snippets/authelia-location.conf;
19
20
21 location / {
22 #authelia
23 include /etc/nginx/snippets/proxy.conf;
24 include /etc/nginx/snippets/authelia-authrequest.conf;
25
26 proxy_pass http://127.0.0.1:{{ firefly_external_port }}/;
27 }
28
29}
30
31server {
32 listen 80;
33 listen [::]:80;
34 server_name {{ firefly_server_name }};
35 return 301 https://$host$request_uri;
36}
37
38server {
39 listen 443 ssl;
40 server_name {{ firefly_importer_server_name }};
41
42 ssl_certificate /etc/letsencrypt/live/chudnick.com/fullchain.pem;
43 ssl_certificate_key /etc/letsencrypt/live/chudnick.com/privkey.pem;
44 add_header Strict-Transport-Security "max-age=31536000" always;
45 ssl_stapling on;
46 ssl_stapling_verify on;
47
48 # Security / XSS Mitigation Headers
49 add_header X-Frame-Options "SAMEORIGIN";
50 add_header X-XSS-Protection "1; mode=block";
51 add_header X-Content-Type-Options "nosniff";
52 add_header 'Access-Control-Allow-Origin' 'https://chudnick.com' always;
53
54 # authelia
55 include /etc/nginx/snippets/authelia-location.conf;
56
57 location / {
58 #authelia
59 include /etc/nginx/snippets/proxy.conf;
60 include /etc/nginx/snippets/authelia-authrequest.conf;
61
62 proxy_buffer_size 128k;
63 proxy_busy_buffers_size 256k;
64
65 proxy_pass http://127.0.0.1:{{ firefly_importer_external_port }}/;
66 }
67
68}
69
70server {
71 listen 80;
72 listen [::]:80;
73 server_name {{ firefly_importer_server_name }};
74 return 301 https://$host$request_uri;
75}
diff --git a/data/freshrss/freshrss.conf b/data/freshrss/freshrss.conf
new file mode 100644
index 0000000..eecc2e3
--- /dev/null
+++ b/data/freshrss/freshrss.conf
@@ -0,0 +1,38 @@
1server {
2 listen 443 ssl;
3 server_name rss.chudnick.com;
4
5 ssl_certificate /etc/letsencrypt/live/chudnick.com/fullchain.pem;
6 ssl_certificate_key /etc/letsencrypt/live/chudnick.com/privkey.pem;
7 add_header Strict-Transport-Security "max-age=31536000" always;
8 ssl_stapling on;
9 ssl_stapling_verify on;
10
11 # Security / XSS Mitigation Headers
12 add_header X-Frame-Options "SAMEORIGIN";
13 add_header X-XSS-Protection "1; mode=block";
14 add_header X-Content-Type-Options "nosniff";
15
16 # authelia
17 include /etc/nginx/snippets/authelia-location.conf;
18
19 location / {
20 #authelia
21 include /etc/nginx/snippets/proxy.conf;
22 include /etc/nginx/snippets/authelia-authrequest.conf;
23
24 proxy_pass http://127.0.0.1:8090/;
25
26 # Forward the Authorization header for the Google Reader API.
27 proxy_set_header Authorization $http_authorization;
28 proxy_pass_header Authorization;
29 }
30
31}
32
33server {
34 listen 80;
35 listen [::]:80;
36 server_name rss.chudnick.com;
37 return 301 https://$host$request_uri;
38}
diff --git a/data/game_server/lightdm.conf b/data/game_server/lightdm.conf
new file mode 100644
index 0000000..eaf4d09
--- /dev/null
+++ b/data/game_server/lightdm.conf
@@ -0,0 +1,8 @@
1[LightDM]
2[SeatDefaults]
3autologin-user=gamer
4autologin-user-timeout=0
5user-session=/usr/bin/startxfce4
6[Seat:*]
7[XDMCPServer]
8[VNCServer]
diff --git a/data/game_server/sunshine_proxy.conf b/data/game_server/sunshine_proxy.conf
new file mode 100644
index 0000000..8e3fb69
--- /dev/null
+++ b/data/game_server/sunshine_proxy.conf
@@ -0,0 +1,24 @@
1server {
2 listen 443 ssl;
3 server_name games.chudnick.com;
4
5 ssl_certificate /etc/ssl/certs/ssl-cert-snakeoil.pem;
6 ssl_certificate_key /etc/ssl/private/ssl-cert-snakeoil.key;
7
8 # Security / XSS Mitigation Headers
9 add_header X-Frame-Options "SAMEORIGIN";
10 add_header X-XSS-Protection "1; mode=block";
11 add_header X-Content-Type-Options "nosniff";
12
13 location / {
14 proxy_pass http://127.0.0.1:47990/;
15 }
16
17}
18
19server {
20 listen 80;
21 listen [::]:80;
22 server_name games.chudnick.com;
23 return 301 https://$host$request_uri;
24}
diff --git a/data/game_server/xinitrc b/data/game_server/xinitrc
new file mode 100644
index 0000000..19ac175
--- /dev/null
+++ b/data/game_server/xinitrc
@@ -0,0 +1,107 @@
1#!/bin/sh
2
3# fix broken $UID on some system...
4if test "x$UID" = "x"; then
5 if test -x /usr/xpg4/bin/id; then
6 UID=`/usr/xpg4/bin/id -u`;
7 else
8 UID=`id -u`;
9 fi
10fi
11
12# set $XDG_MENU_PREFIX to "xfce-" so that "xfce-applications.menu" is picked
13# over "applications.menu" in all Xfce applications.
14if test "x$XDG_MENU_PREFIX" = "x"; then
15 XDG_MENU_PREFIX="xfce-"
16 export XDG_MENU_PREFIX
17fi
18
19# set DESKTOP_SESSION so that one can detect easily if an Xfce session is running
20if test "x$DESKTOP_SESSION" = "x"; then
21 DESKTOP_SESSION="xfce"
22 export DESKTOP_SESSION
23fi
24
25# set XDG_CURRENT_DESKTOP so that Qt 5 applications can identify user set Xfce theme
26if test "x$XDG_CURRENT_DESKTOP" = "x"; then
27 XDG_CURRENT_DESKTOP="XFCE"
28 export XDG_CURRENT_DESKTOP
29fi
30
31# $XDG_CONFIG_HOME defines the base directory relative to which user specific
32# configuration files should be stored. If $XDG_CONFIG_HOME is either not set
33# or empty, a default equal to $HOME/.config should be used.
34if test "x$XDG_CONFIG_HOME" = "x" ; then
35 XDG_CONFIG_HOME=$HOME/.config
36fi
37[ -d "$XDG_CONFIG_HOME" ] || mkdir "$XDG_CONFIG_HOME"
38
39# $XDG_CACHE_HOME defines the base directory relative to which user specific
40# non-essential data files should be stored. If $XDG_CACHE_HOME is either not
41# set or empty, a default equal to $HOME/.cache should be used.
42if test "x$XDG_CACHE_HOME" = "x" ; then
43 XDG_CACHE_HOME=$HOME/.cache
44fi
45[ -d "$XDG_CACHE_HOME" ] || mkdir "$XDG_CACHE_HOME"
46
47# set up XDG user directores. see
48# http://freedesktop.org/wiki/Software/xdg-user-dirs
49if command -v xdg-user-dirs-update >/dev/null 2>&1; then
50 xdg-user-dirs-update
51fi
52
53# For now, start with an empty list
54XRESOURCES=""
55
56# Has to go prior to merging Xft.xrdb, as its the "Defaults" file
57test -r "/etc/xdg/xfce4/Xft.xrdb" && XRESOURCES="$XRESOURCES /etc/xdg/xfce4/Xft.xrdb"
58test -r $HOME/.Xdefaults && XRESOURCES="$XRESOURCES $HOME/.Xdefaults"
59
60BASEDIR=$XDG_CONFIG_HOME/xfce4
61if test -r "$BASEDIR/Xft.xrdb"; then
62 XRESOURCES="$XRESOURCES $BASEDIR/Xft.xrdb"
63elif test -r "$XFCE4HOME/Xft.xrdb"; then
64 mkdir -p "$BASEDIR"
65 cp "$XFCE4HOME/Xft.xrdb" "$BASEDIR"/
66 XRESOURCES="$XRESOURCES $BASEDIR/Xft.xrdb"
67fi
68
69# merge in X cursor settings
70test -r "$BASEDIR/Xcursor.xrdb" && XRESOURCES="$XRESOURCES $BASEDIR/Xcursor.xrdb"
71
72# ~/.Xresources contains overrides to the above
73test -r "$HOME/.Xresources" && XRESOURCES="$XRESOURCES $HOME/.Xresources"
74
75# load all X resources (adds /dev/null to avoid an empty list that would hang the process)
76cat /dev/null $XRESOURCES | xrdb -merge -
77
78# load local modmap
79test -r $HOME/.Xmodmap && xmodmap $HOME/.Xmodmap
80
81# if XAUTHLOCALHOSTNAME is not set in systemd user session, starting of xfce4-notifyd, DISPLAY etc. will fail
82if command -v systemctl >/dev/null 2>&1 && systemctl --user list-jobs >/dev/null 2>&1; then # user session is running
83 dbus-update-activation-environment --systemd XAUTHLOCALHOSTNAME=$XAUTHLOCALHOSTNAME
84fi
85
86
87# check if we start xfce4-session with ck-launch-session. this is only
88# required for starting from a console, not a login manager
89if test "x$XFCE4_SESSION_WITH_CK" = "x1"; then
90 if command -v ck-launch-session >/dev/null 2>&1; then
91 exec ck-launch-session xfce4-session
92 else
93 echo
94 echo "You have tried to start Xfce with consolekit support, but"
95 echo "ck-launch-session is not installed."
96 echo "Aborted startup..."
97 echo
98 exit 1
99 fi
100else
101 # start xfce4-session normally
102 systemctl --user start sunshine
103 exec xfce4-session
104fi
105
106# if we got here, then exec failed
107exit 1
diff --git a/data/gitea/app.ini b/data/gitea/app.ini
new file mode 100644
index 0000000..84f9647
--- /dev/null
+++ b/data/gitea/app.ini
@@ -0,0 +1,103 @@
1APP_NAME = Gitea: Git with a cup of tea
2RUN_MODE = prod
3RUN_USER = git
4
5[repository]
6ROOT = /data/git/repositories
7ENABLE_PUSH_CREATE_USER = true
8DEFAULT_PUSH_CREATE_PRIVATE = false
9
10[repository.local]
11LOCAL_COPY_PATH = /data/gitea/tmp/local-repo
12
13[repository.upload]
14TEMP_PATH = /data/gitea/uploads
15
16[server]
17APP_DATA_PATH = /data/gitea
18DOMAIN = gitea.chudnick.com
19SSH_DOMAIN = gitea.chudnick.com
20HTTP_PORT = 3000
21ROOT_URL = https://gitea.chudnick.com/
22DISABLE_SSH = false
23SSH_PORT = 22
24SSH_LISTEN_PORT = 22
25LFS_START_SERVER = true
26LFS_JWT_SECRET =
27OFFLINE_MODE = false
28
29[database]
30PATH = /data/gitea/gitea.db
31DB_TYPE = sqlite3
32HOST = localhost:3306
33NAME = gitea
34USER = root
35PASSWD =
36LOG_SQL = false
37SCHEMA =
38SSL_MODE = disable
39CHARSET = utf8
40
41[indexer]
42ISSUE_INDEXER_PATH = /data/gitea/indexers/issues.bleve
43
44[session]
45PROVIDER_CONFIG = /data/gitea/sessions
46PROVIDER = file
47
48[picture]
49AVATAR_UPLOAD_PATH = /data/gitea/avatars
50REPOSITORY_AVATAR_UPLOAD_PATH = /data/gitea/repo-avatars
51DISABLE_GRAVATAR = false
52ENABLE_FEDERATED_AVATAR = true
53
54[attachment]
55PATH = /data/gitea/attachments
56
57[log]
58MODE = console
59LEVEL = info
60ROUTER = console
61ROOT_PATH = /data/gitea/log
62
63[security]
64INSTALL_LOCK = true
65SECRET_KEY =
66REVERSE_PROXY_LIMIT = 1
67REVERSE_PROXY_TRUSTED_PROXIES = *
68INTERNAL_TOKEN =
69PASSWORD_HASH_ALGO = pbkdf2
70
71[service]
72DISABLE_REGISTRATION = false
73REQUIRE_SIGNIN_VIEW = false
74REGISTER_EMAIL_CONFIRM = false
75ENABLE_NOTIFY_MAIL = false
76ALLOW_ONLY_EXTERNAL_REGISTRATION = false
77ENABLE_CAPTCHA = false
78DEFAULT_KEEP_EMAIL_PRIVATE = false
79DEFAULT_ALLOW_CREATE_ORGANIZATION = true
80DEFAULT_ENABLE_TIMETRACKING = true
81NO_REPLY_ADDRESS = noreply.localhost
82
83[lfs]
84PATH = /data/git/lfs
85
86[mailer]
87ENABLED = false
88
89[openid]
90ENABLE_OPENID_SIGNIN = true
91ENABLE_OPENID_SIGNUP = true
92
93[repository.pull-request]
94DEFAULT_MERGE_STYLE = merge
95
96[repository.signing]
97DEFAULT_TRUST_MODEL = committer
98
99[webhook]
100ALLOWED_HOST_LIST = jenkins.chudnick.com
101
102[metrics]
103ENABLED = true
diff --git a/data/gitea/gitea.conf b/data/gitea/gitea.conf
new file mode 100644
index 0000000..1b862a4
--- /dev/null
+++ b/data/gitea/gitea.conf
@@ -0,0 +1,30 @@
1server {
2 listen 443 ssl;
3 server_name gitea.chudnick.com;
4
5 ssl_certificate /etc/letsencrypt/live/chudnick.com/fullchain.pem;
6 ssl_certificate_key /etc/letsencrypt/live/chudnick.com/privkey.pem;
7 add_header Strict-Transport-Security "max-age=31536000" always;
8 ssl_stapling on;
9 ssl_stapling_verify on;
10
11 # Security / XSS Mitigation Headers
12 add_header X-Frame-Options "SAMEORIGIN";
13 add_header X-XSS-Protection "1; mode=block";
14 add_header X-Content-Type-Options "nosniff";
15
16 location / {
17 proxy_pass http://127.0.0.1:8003/;
18 }
19
20 # for docker image push
21 client_max_body_size 500M;
22
23}
24
25server {
26 listen 80;
27 listen [::]:80;
28 server_name gitea.chudnick.com;
29 return 301 https://$host$request_uri;
30}
diff --git a/data/grafana/grafana.conf b/data/grafana/grafana.conf
new file mode 100644
index 0000000..9fcc1b0
--- /dev/null
+++ b/data/grafana/grafana.conf
@@ -0,0 +1,134 @@
1# this is required to proxy Grafana Live WebSocket connections.
2map $http_upgrade $connection_upgrade {
3 default upgrade;
4 '' close;
5}
6
7upstream grafana {
8 server localhost:3000;
9}
10
11server {
12 listen 443 ssl;
13 server_name monitoring.chudnick.com
14 root /usr/share/nginx/html;
15 index index.html index.htm;
16
17 ssl_certificate "/etc/nginx/tls/fullchain.pem";
18 ssl_certificate_key "/etc/nginx/tls/privkey.pem";
19
20 location /grafana {
21 proxy_set_header Host $http_host;
22 proxy_pass http://localhost:3000;
23 }
24
25 # Proxy Grafana Live WebSocket connections.
26 location /grafana/api/live/ {
27 proxy_http_version 1.1;
28 proxy_set_header Upgrade $http_upgrade;
29 proxy_set_header Connection $connection_upgrade;
30 proxy_set_header Host $http_host;
31 proxy_pass http://localhost:3000;
32 }
33
34 # Restrict access to metrics
35 location /grafana/metrics {
36 allow 127.0.0.1;
37 allow 192.168.20.32;
38 allow 192.168.10.254;
39 deny all;
40 proxy_set_header Host $http_host;
41 proxy_pass http://localhost:3000;
42 }
43
44
45 # Prometheus
46
47 set $upstream_authelia https://auth.chudnick.com/api/verify;
48 resolver 192.168.20.34;
49
50 ## Virtual endpoint created by nginx to forward auth requests.
51 location /authelia {
52 ## Essential Proxy Configuration
53 internal;
54 proxy_pass $upstream_authelia;
55
56 ## Headers
57 ## The headers starting with X-* are required.
58 proxy_set_header X-Original-URL $scheme://$http_host$request_uri;
59 proxy_set_header X-Original-Method $request_method;
60 proxy_set_header X-Forwarded-Method $request_method;
61 proxy_set_header X-Forwarded-Proto $scheme;
62 proxy_set_header X-Forwarded-Host $http_host;
63 proxy_set_header X-Forwarded-Uri $request_uri;
64 proxy_set_header X-Forwarded-For $remote_addr;
65 proxy_set_header Content-Length "";
66 proxy_set_header Connection "";
67
68 ## Basic Proxy Configuration
69 proxy_pass_request_body off;
70 proxy_next_upstream error timeout invalid_header http_500 http_502 http_503;
71 proxy_redirect http:// $scheme://;
72 proxy_http_version 1.1;
73 proxy_cache_bypass $cookie_session;
74 proxy_no_cache $cookie_session;
75 proxy_buffers 4 32k;
76 client_body_buffer_size 128k;
77
78 ## Advanced Proxy Configuration
79 send_timeout 5m;
80 proxy_read_timeout 240;
81 proxy_send_timeout 240;
82 proxy_connect_timeout 240;
83 }
84
85
86 location /prometheus {
87 # Authelia config
88 proxy_set_header Host $host;
89 proxy_set_header X-Original-URL $scheme://$http_host$request_uri;
90 proxy_set_header X-Forwarded-Proto $scheme;
91 proxy_set_header X-Forwarded-Host $http_host;
92 proxy_set_header X-Forwarded-Uri $request_uri;
93 proxy_set_header X-Forwarded-Ssl on;
94 proxy_set_header X-Forwarded-For $remote_addr;
95 proxy_set_header X-Real-IP $remote_addr;
96 proxy_set_header Connection "";
97 client_body_buffer_size 128k;
98 proxy_next_upstream error timeout invalid_header http_500 http_502 http_503;
99 proxy_redirect http:// $scheme://;
100 proxy_http_version 1.1;
101 proxy_cache_bypass $cookie_session;
102 proxy_no_cache $cookie_session;
103 proxy_buffers 64 256k;
104 real_ip_header X-Forwarded-For;
105 real_ip_recursive on;
106 send_timeout 5m;
107 proxy_read_timeout 360;
108 proxy_send_timeout 360;
109 proxy_connect_timeout 360;
110
111 # Authelia request
112 auth_request /authelia;
113 set $target_url $scheme://$http_host$request_uri;
114 auth_request_set $user $upstream_http_remote_user;
115 auth_request_set $groups $upstream_http_remote_groups;
116 auth_request_set $name $upstream_http_remote_name;
117 auth_request_set $email $upstream_http_remote_email;
118 proxy_set_header Remote-User $user;
119 proxy_set_header Remote-Groups $groups;
120 proxy_set_header Remote-Name $name;
121 proxy_set_header Remote-Email $email;
122 error_page 401 =302 https://auth.chudnick.com/?rd=$target_url;
123
124 proxy_pass http://127.0.0.1:9090/prometheus;
125 }
126
127}
128
129server {
130 listen 80;
131 return 301 https://$host$request_uri;
132 server_name monitoring.chudnick.com;
133 return 404;
134}
diff --git a/data/grafana/grafana.ini.j2 b/data/grafana/grafana.ini.j2
new file mode 100644
index 0000000..c24cc6a
--- /dev/null
+++ b/data/grafana/grafana.ini.j2
@@ -0,0 +1,1268 @@
1##################### Grafana Configuration Defaults #####################
2
3# possible values : production, development
4app_mode = production
5
6# instance name, defaults to HOSTNAME environment variable value or hostname if HOSTNAME var is empty
7instance_name = ${HOSTNAME}
8
9# force migration will run migrations that might cause dataloss
10force_migration = false
11
12#################################### Paths ###############################
13[paths]
14# Path to where grafana can store temp files, sessions, and the sqlite3 db (if that is used)
15data = /var/lib/grafana
16
17# Temporary files in `data` directory older than given duration will be removed
18temp_data_lifetime = 24h
19
20# Directory where grafana can store logs
21logs = /var/log/grafana
22
23# Directory where grafana will automatically scan and look for plugins
24plugins = /var/lib/grafana/plugins
25
26# folder that contains provisioning config files that grafana will apply on startup and while running.
27provisioning = /etc/grafana/provisioning
28
29#################################### Server ##############################
30[server]
31# Protocol (http, https, h2, socket)
32protocol = http
33
34# The ip address to bind to, empty will bind to all interfaces
35http_addr =
36
37# The http port to use
38http_port = 3000
39
40# The public facing domain name used to access grafana from a browser
41domain = monitoring.chudnick.com/grafana
42
43# Redirect to correct domain if host header does not match domain
44# Prevents DNS rebinding attacks
45enforce_domain = false
46
47# The full public facing url
48root_url = https://monitoring.chudnick.com/grafana
49
50# Serve Grafana from subpath specified in `root_url` setting. By default it is set to `false` for compatibility reasons.
51serve_from_sub_path = true
52
53# Log web requests
54router_logging = false
55
56# the path relative working path
57static_root_path = public
58
59# enable gzip
60enable_gzip = false
61
62# https certs & key file
63
64# Unix socket path
65socket = /tmp/grafana.sock
66
67# CDN Url
68cdn_url =
69
70# Sets the maximum time in minutes before timing out read of an incoming request and closing idle connections.
71# `0` means there is no timeout for reading the request.
72read_timeout = 0
73
74#################################### Database ############################
75[database]
76# You can configure the database connection by specifying type, host, name, user and password
77# as separate properties or as on string using the url property.
78
79# Either "mysql", "postgres" or "sqlite3", it's your choice
80type = sqlite3
81host = 127.0.0.1:3306
82name = grafana
83user = root
84# If the password contains # or ; you have to wrap it with triple quotes. Ex """#password;"""
85password =
86# Use either URL or the previous fields to configure the database
87# Example: mysql://user:secret@host:port/database
88url =
89
90# Max idle conn setting default is 2
91max_idle_conn = 2
92
93# Max conn setting default is 0 (mean not set)
94max_open_conn =
95
96# Connection Max Lifetime default is 14400 (means 14400 seconds or 4 hours)
97conn_max_lifetime = 14400
98
99# Set to true to log the sql calls and execution times.
100log_queries =
101
102# For "postgres", use either "disable", "require" or "verify-full"
103# For "mysql", use either "true", "false", or "skip-verify".
104ssl_mode = disable
105
106# Database drivers may support different transaction isolation levels.
107# Currently, only "mysql" driver supports isolation levels.
108# If the value is empty - driver's default isolation level is applied.
109# For "mysql" use "READ-UNCOMMITTED", "READ-COMMITTED", "REPEATABLE-READ" or "SERIALIZABLE".
110isolation_level =
111
112ca_cert_path =
113client_key_path =
114client_cert_path =
115server_cert_name =
116
117# For "sqlite3" only, path relative to data_path setting
118path = grafana.db
119
120# For "sqlite3" only. cache mode setting used for connecting to the database
121cache_mode = private
122
123# For "mysql" only if migrationLocking feature toggle is set. How many seconds to wait before failing to lock the database for the migrations, default is 0.
124locking_attempt_timeout_sec = 0
125
126#################################### Cache server #############################
127[remote_cache]
128# Either "redis", "memcached" or "database" default is "database"
129type = database
130
131# cache connectionstring options
132# database: will use Grafana primary database.
133# redis: config like redis server e.g. `addr=127.0.0.1:6379,pool_size=100,db=0,ssl=false`. Only addr is required. ssl may be 'true', 'false', or 'insecure'.
134# memcache: 127.0.0.1:11211
135connstr =
136
137#################################### Data proxy ###########################
138[dataproxy]
139
140# This enables data proxy logging, default is false
141logging = false
142
143# How long the data proxy waits to read the headers of the response before timing out, default is 30 seconds.
144# This setting also applies to core backend HTTP data sources where query requests use an HTTP client with timeout set.
145timeout = 30
146
147# How long the data proxy waits to establish a TCP connection before timing out, default is 10 seconds.
148dialTimeout = 10
149
150# How many seconds the data proxy waits before sending a keepalive request.
151keep_alive_seconds = 30
152
153# How many seconds the data proxy waits for a successful TLS Handshake before timing out.
154tls_handshake_timeout_seconds = 10
155
156# How many seconds the data proxy will wait for a server's first response headers after
157# fully writing the request headers if the request has an "Expect: 100-continue"
158# header. A value of 0 will result in the body being sent immediately, without
159# waiting for the server to approve.
160expect_continue_timeout_seconds = 1
161
162# Optionally limits the total number of connections per host, including connections in the dialing,
163# active, and idle states. On limit violation, dials will block.
164# A value of zero (0) means no limit.
165max_conns_per_host = 0
166
167# The maximum number of idle connections that Grafana will keep alive.
168max_idle_connections = 100
169
170# How many seconds the data proxy keeps an idle connection open before timing out.
171idle_conn_timeout_seconds = 90
172
173# If enabled and user is not anonymous, data proxy will add X-Grafana-User header with username into the request.
174send_user_header = false
175
176# Limit the amount of bytes that will be read/accepted from responses of outgoing HTTP requests.
177response_limit = 0
178
179# Limits the number of rows that Grafana will process from SQL data sources.
180row_limit = 1000000
181
182#################################### Analytics ###########################
183[analytics]
184# Server reporting, sends usage counters to stats.grafana.org every 24 hours.
185# No ip addresses are being tracked, only simple counters to track
186# running instances, dashboard and error counts. It is very helpful to us.
187# Change this option to false to disable reporting.
188reporting_enabled = false
189
190# The name of the distributor of the Grafana instance. Ex hosted-grafana, grafana-labs
191reporting_distributor =
192
193# Set to false to disable all checks to https://grafana.com
194# for new versions of grafana. The check is used
195# in some UI views to notify that a grafana update exists.
196# This option does not cause any auto updates, nor send any information
197# only a GET request to https://raw.githubusercontent.com/grafana/grafana/main/latest.json to get the latest version.
198check_for_updates = false
199
200# Set to false to disable all checks to https://grafana.com
201# for new versions of plugins. The check is used
202# in some UI views to notify that a plugin update exists.
203# This option does not cause any auto updates, nor send any information
204# only a GET request to https://grafana.com to get the latest versions.
205check_for_plugin_updates = false
206
207# Google Analytics universal tracking code, only enabled if you specify an id here
208google_analytics_ua_id =
209
210# Google Tag Manager ID, only enabled if you specify an id here
211google_tag_manager_id =
212
213# Rudderstack write key, enabled only if rudderstack_data_plane_url is also set
214rudderstack_write_key =
215
216# Rudderstack data plane url, enabled only if rudderstack_write_key is also set
217rudderstack_data_plane_url =
218
219# Rudderstack SDK url, optional, only valid if rudderstack_write_key and rudderstack_data_plane_url is also set
220rudderstack_sdk_url =
221
222# Rudderstack Config url, optional, used by Rudderstack SDK to fetch source config
223rudderstack_config_url =
224
225# Application Insights connection string. Specify an URL string to enable this feature.
226application_insights_connection_string =
227
228# Optional. Specifies an Application Insights endpoint URL where the endpoint string is wrapped in backticks ``.
229application_insights_endpoint_url =
230
231# Controls if the UI contains any links to user feedback forms
232feedback_links_enabled = false
233
234#################################### Security ############################
235[security]
236# disable creation of admin user on first start of grafana
237disable_initial_admin_creation = true
238
239# used for signing
240secret_key = SW2YcwTIb9zpOOhoPsMm
241
242# current key provider used for envelope encryption, default to static value specified by secret_key
243encryption_provider = secretKey.v1
244
245# list of configured key providers, space separated (Enterprise only): e.g., awskms.v1 azurekv.v1
246available_encryption_providers =
247
248# disable gravatar profile images
249disable_gravatar = true
250
251# data source proxy whitelist (ip_or_domain:port separated by spaces)
252data_source_proxy_whitelist =
253
254# disable protection against brute force login attempts
255disable_brute_force_login_protection = true
256
257# set to true if you host Grafana behind HTTPS. default is false.
258cookie_secure = true
259
260# set cookie SameSite attribute. defaults to `lax`. can be set to "lax", "strict", "none" and "disabled"
261cookie_samesite = strict
262
263# set to true if you want to allow browsers to render Grafana in a <frame>, <iframe>, <embed> or <object>. default is false.
264allow_embedding = false
265
266# Set to true if you want to enable http strict transport security (HSTS) response header.
267# HSTS tells browsers that the site should only be accessed using HTTPS.
268strict_transport_security = false
269
270# Sets how long a browser should cache HSTS. Only applied if strict_transport_security is enabled.
271strict_transport_security_max_age_seconds = 86400
272
273# Set to true if to enable HSTS preloading option. Only applied if strict_transport_security is enabled.
274strict_transport_security_preload = false
275
276# Set to true if to enable the HSTS includeSubDomains option. Only applied if strict_transport_security is enabled.
277strict_transport_security_subdomains = false
278
279# Set to true to enable the X-Content-Type-Options response header.
280# The X-Content-Type-Options response HTTP header is a marker used by the server to indicate that the MIME types advertised
281# in the Content-Type headers should not be changed and be followed.
282x_content_type_options = true
283
284# Set to true to enable the X-XSS-Protection header, which tells browsers to stop pages from loading
285# when they detect reflected cross-site scripting (XSS) attacks.
286x_xss_protection = true
287
288# Enable adding the Content-Security-Policy header to your requests.
289# CSP allows to control resources the user agent is allowed to load and helps prevent XSS attacks.
290content_security_policy = false
291
292# Set Content Security Policy template used when adding the Content-Security-Policy header to your requests.
293# $NONCE in the template includes a random nonce.
294# $ROOT_PATH is server.root_url without the protocol.
295content_security_policy_template = """script-src 'self' 'unsafe-eval' 'unsafe-inline' 'strict-dynamic' $NONCE;object-src 'none';font-src 'self';style-src 'self' 'unsafe-inline' blob:;img-src * data:;base-uri 'self';connect-src 'self' grafana.com ws://$ROOT_PATH wss://$ROOT_PATH;manifest-src 'self';media-src 'none';form-action 'self';"""
296
297# Controls if old angular plugins are supported or not. This will be disabled by default in future release
298angular_support_enabled = true
299
300[security.encryption]
301# Defines the time-to-live (TTL) for decrypted data encryption keys stored in memory (cache).
302# Please note that small values may cause performance issues due to a high frequency decryption operations.
303data_keys_cache_ttl = 15m
304
305# Defines the frequency of data encryption keys cache cleanup interval.
306# On every interval, decrypted data encryption keys that reached the TTL are removed from the cache.
307data_keys_cache_cleanup_interval = 1m
308
309#################################### Snapshots ###########################
310[snapshots]
311# snapshot sharing options
312external_enabled = false
313external_snapshot_url =
314external_snapshot_name =
315
316# Set to true to enable this Grafana instance act as an external snapshot server and allow unauthenticated requests for
317# creating and deleting snapshots.
318public_mode = false
319
320# remove expired snapshot
321snapshot_remove_expired = true
322
323#################################### Dashboards ##################
324
325[dashboards]
326# Number dashboard versions to keep (per dashboard). Default: 20, Minimum: 1
327versions_to_keep = 20
328
329# Minimum dashboard refresh interval. When set, this will restrict users to set the refresh interval of a dashboard lower than given interval. Per default this is 5 seconds.
330# The interval string is a possibly signed sequence of decimal numbers, followed by a unit suffix (ms, s, m, h, d), e.g. 30s or 1m.
331min_refresh_interval = 5s
332
333# Path to the default home dashboard. If this value is empty, then Grafana uses StaticRootPath + "dashboards/home.json"
334default_home_dashboard_path =
335
336################################### Data sources #########################
337[datasources]
338# Upper limit of data sources that Grafana will return. This limit is a temporary configuration and it will be deprecated when pagination will be introduced on the list data sources API.
339datasource_limit = 5000
340
341#################################### Users ###############################
342[users]
343# disable user signup / registration
344allow_sign_up = false
345
346# Allow non admin users to create organizations
347allow_org_create = false
348
349# Set to true to automatically assign new users to the default organization (id 1)
350auto_assign_org = true
351
352# Set this value to automatically add new users to the provided organization (if auto_assign_org above is set to true)
353auto_assign_org_id = 1
354
355# Default role new users will be automatically assigned (if auto_assign_org above is set to true)
356auto_assign_org_role = Viewer
357
358# Require email validation before sign up completes
359verify_email_enabled = false
360
361# Background text for the user field on the login page
362login_hint = email or username
363password_hint = password
364
365# Default UI theme ("dark" or "light")
366default_theme = dark
367
368# Default locale (supported IETF language tag, such as en-US)
369default_locale = en-US
370
371# Path to a custom home page. Users are only redirected to this if the default home dashboard is used. It should match a frontend route and contain a leading slash.
372home_page =
373
374# External user management
375external_manage_link_url =
376external_manage_link_name =
377external_manage_info =
378
379# Viewers can edit/inspect dashboard settings in the browser. But not save the dashboard.
380viewers_can_edit = false
381
382# Editors can administrate dashboard, folders and teams they create
383editors_can_admin = false
384
385# The duration in time a user invitation remains valid before expiring. This setting should be expressed as a duration. Examples: 6h (hours), 2d (days), 1w (week). Default is 24h (24 hours). The minimum supported duration is 15m (15 minutes).
386user_invite_max_lifetime_duration = 24h
387
388# Enter a comma-separated list of usernames to hide them in the Grafana UI. These users are shown to Grafana admins and to themselves.
389hidden_users =
390
391[auth]
392# Login cookie name
393login_cookie_name = grafana_session
394
395# The maximum lifetime (duration) an authenticated user can be inactive before being required to login at next visit. Default is 7 days (7d). This setting should be expressed as a duration, e.g. 5m (minutes), 6h (hours), 10d (days), 2w (weeks), 1M (month). The lifetime resets at each successful token rotation (token_rotation_interval_minutes).
396login_maximum_inactive_lifetime_duration =
397
398# The maximum lifetime (duration) an authenticated user can be logged in since login time before being required to login. Default is 30 days (30d). This setting should be expressed as a duration, e.g. 5m (minutes), 6h (hours), 10d (days), 2w (weeks), 1M (month).
399login_maximum_lifetime_duration =
400
401# How often should auth tokens be rotated for authenticated users when being active. The default is each 10 minutes.
402token_rotation_interval_minutes = 10
403
404# Set to true to disable (hide) the login form, useful if you use OAuth
405disable_login_form = false
406
407# Set to true to disable the sign out link in the side menu. Useful if you use auth.proxy or auth.jwt.
408disable_signout_menu = false
409
410# URL to redirect the user to after sign out
411signout_redirect_url =
412
413# Set to true to attempt login with OAuth automatically, skipping the login screen.
414# This setting is ignored if multiple OAuth providers are configured.
415oauth_auto_login = false
416
417# OAuth state max age cookie duration in seconds. Defaults to 600 seconds.
418oauth_state_cookie_max_age = 600
419
420# Skip forced assignment of OrgID 1 or 'auto_assign_org_id' for social logins
421oauth_skip_org_role_update_sync = false
422
423# limit of api_key seconds to live before expiration
424api_key_max_seconds_to_live = -1
425
426# Set to true to enable SigV4 authentication option for HTTP-based datasources
427sigv4_auth_enabled = false
428
429# Set to true to enable verbose logging of SigV4 request signing
430sigv4_verbose_logging = false
431
432# Set to true to enable Azure authentication option for HTTP-based datasources
433azure_auth_enabled = false
434
435#################################### Anonymous Auth ######################
436[auth.anonymous]
437# enable anonymous access
438enabled = false
439
440# specify organization name that should be used for unauthenticated users
441org_name = Main Org.
442
443# specify role for unauthenticated users
444org_role = Viewer
445
446# mask the Grafana version number for unauthenticated users
447hide_version = false
448
449#################################### GitHub Auth #########################
450[auth.github]
451enabled = false
452allow_sign_up = true
453client_id = some_id
454client_secret =
455scopes = user:email,read:org
456auth_url = https://github.com/login/oauth/authorize
457token_url = https://github.com/login/oauth/access_token
458api_url = https://api.github.com/user
459allowed_domains =
460team_ids =
461allowed_organizations =
462role_attribute_path =
463role_attribute_strict = false
464
465#################################### GitLab Auth #########################
466[auth.gitlab]
467enabled = false
468allow_sign_up = true
469client_id = some_id
470client_secret =
471scopes = api
472auth_url = https://gitlab.com/oauth/authorize
473token_url = https://gitlab.com/oauth/token
474api_url = https://gitlab.com/api/v4
475allowed_domains =
476allowed_groups =
477role_attribute_path =
478role_attribute_strict = false
479
480#################################### Google Auth #########################
481[auth.google]
482enabled = false
483allow_sign_up = true
484client_id = some_client_id
485client_secret =
486scopes = https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/userinfo.email
487auth_url = https://accounts.google.com/o/oauth2/auth
488token_url = https://accounts.google.com/o/oauth2/token
489api_url = https://www.googleapis.com/oauth2/v1/userinfo
490allowed_domains =
491hosted_domain =
492
493#################################### Grafana.com Auth ####################
494# legacy key names (so they work in env variables)
495[auth.grafananet]
496enabled = false
497allow_sign_up = true
498client_id = some_id
499client_secret =
500scopes = user:email
501allowed_organizations =
502
503[auth.grafana_com]
504enabled = false
505allow_sign_up = true
506client_id = some_id
507client_secret =
508scopes = user:email
509allowed_organizations =
510
511#################################### Azure AD OAuth #######################
512[auth.azuread]
513name = Azure AD
514enabled = false
515allow_sign_up = true
516client_id = some_client_id
517client_secret =
518scopes = openid email profile
519auth_url = https://login.microsoftonline.com/<tenant-id>/oauth2/v2.0/authorize
520token_url = https://login.microsoftonline.com/<tenant-id>/oauth2/v2.0/token
521allowed_domains =
522allowed_groups =
523role_attribute_strict = false
524
525#################################### Okta OAuth #######################
526[auth.okta]
527name = Okta
528icon = okta
529enabled = false
530allow_sign_up = true
531client_id = some_id
532client_secret =
533scopes = openid profile email groups
534auth_url = https://<tenant-id>.okta.com/oauth2/v1/authorize
535token_url = https://<tenant-id>.okta.com/oauth2/v1/token
536api_url = https://<tenant-id>.okta.com/oauth2/v1/userinfo
537allowed_domains =
538allowed_groups =
539role_attribute_path =
540role_attribute_strict = false
541
542#################################### Generic OAuth #######################
543[auth.generic_oauth]
544name = OAuth
545icon = signin
546enabled = true
547allow_sign_up = true
548client_id = grafana
549client_secret = grafana_client_secret
550scopes = openid profile email groups
551empty_scopes = false
552auth_url = https://auth.chudnick.com/api/oidc/authorization
553token_url = https://auth.chudnick.com/api/oidc/token
554api_url = https://auth.chudnick.com/api/oidc/userinfo
555login_attribute_path = preferred_username
556groups_attribute_path = groups
557name_attribute_path = name
558use_pkce = false
559
560email_attribute_name =
561email_attribute_path =
562role_attribute_path =
563role_attribute_strict = false
564id_token_attribute_name =
565team_ids_attribute_path =
566teams_url =
567allowed_domains =
568team_ids =
569allowed_organizations =
570tls_skip_verify_insecure = false
571tls_client_cert =
572tls_client_key =
573tls_client_ca =
574auth_style =
575
576#################################### Basic Auth ##########################
577[auth.basic]
578enabled = true
579
580#################################### Auth Proxy ##########################
581[auth.proxy]
582enabled = false
583header_name = X-WEBAUTH-USER
584header_property = username
585auto_sign_up = true
586sync_ttl = 60
587whitelist =
588headers =
589headers_encoded = false
590enable_login_token = false
591
592#################################### Auth JWT ##########################
593[auth.jwt]
594enabled = false
595enable_login_token = false
596header_name =
597email_claim =
598username_claim =
599jwk_set_url =
600jwk_set_file =
601cache_ttl = 60m
602expected_claims = {}
603key_file =
604auto_sign_up = false
605
606#################################### Auth LDAP ###########################
607[auth.ldap]
608enabled = false
609config_file = /etc/grafana/ldap.toml
610allow_sign_up = true
611
612# LDAP background sync (Enterprise only)
613# At 1 am every day
614sync_cron = "0 1 * * *"
615active_sync_enabled = true
616
617#################################### AWS ###########################
618[aws]
619# Enter a comma-separated list of allowed AWS authentication providers.
620# Options are: default (AWS SDK Default), keys (Access && secret key), credentials (Credentials field), ec2_iam_role (EC2 IAM Role)
621allowed_auth_providers = default,keys,credentials
622
623# Allow AWS users to assume a role using temporary security credentials.
624# If true, assume role will be enabled for all AWS authentication providers that are specified in aws_auth_providers
625assume_role_enabled = true
626
627# Specify max no of pages to be returned by the ListMetricPages API
628list_metrics_page_limit = 500
629
630#################################### Azure ###############################
631[azure]
632# Azure cloud environment where Grafana is hosted
633# Possible values are AzureCloud, AzureChinaCloud, AzureUSGovernment and AzureGermanCloud
634# Default value is AzureCloud (i.e. public cloud)
635cloud = AzureCloud
636
637# Specifies whether Grafana hosted in Azure service with Managed Identity configured (e.g. Azure Virtual Machines instance)
638# If enabled, the managed identity can be used for authentication of Grafana in Azure services
639# Disabled by default, needs to be explicitly enabled
640managed_identity_enabled = false
641
642# Client ID to use for user-assigned managed identity
643# Should be set for user-assigned identity and should be empty for system-assigned identity
644managed_identity_client_id =
645
646#################################### Role-based Access Control ###########
647[rbac]
648# If enabled, cache permissions in a in memory cache (Enterprise only)
649permission_cache = true
650
651#################################### SMTP / Emailing #####################
652[smtp]
653enabled = true
654host = mail.chudnick.com:465
655user = monitoring@chudnick.com
656# If the password contains # or ; you have to wrap it with triple quotes. Ex """#password;"""
657password = {{ grafana_smtp_password }}
658cert_file =
659key_file =
660skip_verify = false
661from_address = monitoring@chudnick.com
662from_name = Monitoring Alerts
663ehlo_identity = monitoring.chudnick.com
664startTLS_policy =
665
666[emails]
667welcome_email_on_sign_up = false
668templates_pattern = emails/*.html, emails/*.txt
669content_types = text/plain
670
671#################################### Logging ##########################
672[log]
673# Either "console", "file", "syslog". Default is console and file
674# Use space to separate multiple modes, e.g. "console file"
675mode = console file
676
677# Either "debug", "info", "warn", "error", "critical", default is "info"
678level = info
679
680# optional settings to set different levels for specific loggers. Ex filters = sqlstore:debug
681filters =
682
683# For "console" mode only
684[log.console]
685level =
686
687# log line format, valid options are text, console and json
688format = console
689
690# For "file" mode only
691[log.file]
692level =
693
694# log line format, valid options are text, console and json
695format = text
696
697# This enables automated log rotate(switch of following options), default is true
698log_rotate = true
699
700# Max line number of single file, default is 1000000
701max_lines = 1000000
702
703# Max size shift of single file, default is 28 means 1 << 28, 256MB
704max_size_shift = 28
705
706# Segment log daily, default is true
707daily_rotate = true
708
709# Expired days of log file(delete after max days), default is 7
710max_days = 7
711
712[log.syslog]
713level =
714
715# log line format, valid options are text, console and json
716format = text
717
718# Syslog network type and address. This can be udp, tcp, or unix. If left blank, the default unix endpoints will be used.
719network =
720address =
721
722# Syslog facility. user, daemon and local0 through local7 are valid.
723facility =
724
725# Syslog tag. By default, the process' argv[0] is used.
726tag =
727
728[log.frontend]
729# Should Sentry javascript agent be initialized
730enabled = false
731
732# Defines which provider to use sentry or grafana
733provider = sentry
734
735# Sentry DSN if you want to send events to Sentry.
736sentry_dsn =
737
738# Custom HTTP endpoint to send events to. Default will log the events to stdout.
739custom_endpoint =
740
741# Rate of events to be reported to Sentry between 0 (none) and 1 (all), float
742sample_rate = 1.0
743
744# Requests per second limit enforced per an extended period, for Grafana backend log ingestion endpoint (/log).
745log_endpoint_requests_per_second_limit = 3
746
747# Max requests accepted per short interval of time for Grafana backend log ingestion endpoint (/log)
748log_endpoint_burst_limit = 15
749
750# Should error instrumentation be enabled, only affects Grafana Javascript Agent
751instrumentations_errors_enabled = true
752
753# Should console instrumentation be enabled, only affects Grafana Javascript Agent
754instrumentations_console_enabled = false
755
756# Should webvitals instrumentation be enabled, only affects Grafana Javascript Agent
757instrumentations_webvitals_enabled = false
758
759# Api Key, only applies to Grafana Javascript Agent provider
760api_key =
761
762#################################### Usage Quotas ########################
763[quota]
764enabled = false
765
766#### set quotas to -1 to make unlimited. ####
767# limit number of users per Org.
768org_user = 10
769
770# limit number of dashboards per Org.
771org_dashboard = 100
772
773# limit number of data_sources per Org.
774org_data_source = 10
775
776# limit number of api_keys per Org.
777org_api_key = 10
778
779# limit number of alerts per Org.
780org_alert_rule = 100
781
782# limit number of orgs a user can create.
783user_org = 10
784
785# Global limit of users.
786global_user = -1
787
788# global limit of orgs.
789global_org = -1
790
791# global limit of dashboards
792global_dashboard = -1
793
794# global limit of api_keys
795global_api_key = -1
796
797# global limit on number of logged in users.
798global_session = -1
799
800# global limit of alerts
801global_alert_rule = -1
802
803# global limit of files uploaded to the SQL DB
804global_file = 1000
805
806#################################### Unified Alerting ####################
807[unified_alerting]
808# Enable the Unified Alerting sub-system and interface. When enabled we'll migrate all of your alert rules and notification channels to the new system. New alert rules will be created and your notification channels will be converted into an Alertmanager configuration. Previous data is preserved to enable backwards compatibility but new data is removed when switching. When this configuration section and flag are not defined, the state is defined at runtime. See the documentation for more details.
809enabled =
810
811# Comma-separated list of organization IDs for which to disable unified alerting. Only supported if unified alerting is enabled.
812disabled_orgs =
813
814# Specify the frequency of polling for admin config changes.
815# The interval string is a possibly signed sequence of decimal numbers, followed by a unit suffix (ms, s, m, h, d), e.g. 30s or 1m.
816admin_config_poll_interval = 60s
817
818# Specify the frequency of polling for Alertmanager config changes.
819# The interval string is a possibly signed sequence of decimal numbers, followed by a unit suffix (ms, s, m, h, d), e.g. 30s or 1m.
820alertmanager_config_poll_interval = 60s
821
822# Listen address/hostname and port to receive unified alerting messages for other Grafana instances. The port is used for both TCP and UDP. It is assumed other Grafana instances are also running on the same port.
823ha_listen_address = "0.0.0.0:9094"
824
825# Explicit address/hostname and port to advertise other Grafana instances. The port is used for both TCP and UDP.
826ha_advertise_address = ""
827
828# Comma-separated list of initial instances (in a format of host:port) that will form the HA cluster. Configuring this setting will enable High Availability mode for alerting.
829ha_peers = ""
830
831# Time to wait for an instance to send a notification via the Alertmanager. In HA, each Grafana instance will
832# be assigned a position (e.g. 0, 1). We then multiply this position with the timeout to indicate how long should
833# each instance wait before sending the notification to take into account replication lag.
834# The interval string is a possibly signed sequence of decimal numbers, followed by a unit suffix (ms, s, m, h, d), e.g. 30s or 1m.
835ha_peer_timeout = 15s
836
837# The interval between sending gossip messages. By lowering this value (more frequent) gossip messages are propagated
838# across cluster more quickly at the expense of increased bandwidth usage.
839# The interval string is a possibly signed sequence of decimal numbers, followed by a unit suffix (ms, s, m, h, d), e.g. 30s or 1m.
840ha_gossip_interval = 200ms
841
842# The interval between gossip full state syncs. Setting this interval lower (more frequent) will increase convergence speeds
843# across larger clusters at the expense of increased bandwidth usage.
844# The interval string is a possibly signed sequence of decimal numbers, followed by a unit suffix (ms, s, m, h, d), e.g. 30s or 1m.
845ha_push_pull_interval = 60s
846
847# Enable or disable alerting rule execution. The alerting UI remains visible. This option has a legacy version in the `[alerting]` section that takes precedence.
848execute_alerts = true
849
850# Alert evaluation timeout when fetching data from the datasource. This option has a legacy version in the `[alerting]` section that takes precedence.
851# The timeout string is a possibly signed sequence of decimal numbers, followed by a unit suffix (ms, s, m, h, d), e.g. 30s or 1m.
852evaluation_timeout = 30s
853
854# Number of times we'll attempt to evaluate an alert rule before giving up on that evaluation. This option has a legacy version in the `[alerting]` section that takes precedence.
855max_attempts = 3
856
857# Minimum interval to enforce between rule evaluations. Rules will be adjusted if they are less than this value or if they are not multiple of the scheduler interval (10s). Higher values can help with resource management as we'll schedule fewer evaluations over time. This option has a legacy version in the `[alerting]` section that takes precedence.
858# The interval string is a possibly signed sequence of decimal numbers, followed by a unit suffix (ms, s, m, h, d), e.g. 30s or 1m.
859min_interval = 10s
860
861[unified_alerting.screenshots]
862# Enable screenshots in notifications. This option requires the Grafana Image Renderer plugin.
863# For more information on configuration options, refer to [rendering].
864capture = false
865
866# The maximum number of screenshots that can be taken at the same time. This option is different from
867# concurrent_render_request_limit as max_concurrent_screenshots sets the number of concurrent screenshots
868# that can be taken at the same time for all firing alerts where as concurrent_render_request_limit sets
869# the total number of concurrent screenshots across all Grafana services.
870max_concurrent_screenshots = 5
871
872# Uploads screenshots to the local Grafana server or remote storage such as Azure, S3 and GCS. Please
873# see [external_image_storage] for further configuration options. If this option is false then
874# screenshots will be persisted to disk for up to temp_data_lifetime.
875upload_external_image_storage = false
876
877[unified_alerting.reserved_labels]
878# Comma-separated list of reserved labels added by the Grafana Alerting engine that should be disabled.
879# For example: `disabled_labels=grafana_folder`
880disabled_labels =
881
882#################################### Alerting ############################
883[alerting]
884# Enable the legacy alerting sub-system and interface. If Unified Alerting is already enabled and you try to go back to legacy alerting, all data that is part of Unified Alerting will be deleted. When this configuration section and flag are not defined, the state is defined at runtime. See the documentation for more details.
885enabled =
886
887# Makes it possible to turn off alert execution but alerting UI is visible
888execute_alerts = true
889
890# Default setting for new alert rules. Defaults to categorize error and timeouts as alerting. (alerting, keep_state)
891error_or_timeout = alerting
892
893# Default setting for how Grafana handles nodata or null values in alerting. (alerting, no_data, keep_state, ok)
894nodata_or_nullvalues = no_data
895
896# Alert notifications can include images, but rendering many images at the same time can overload the server
897# This limit will protect the server from render overloading and make sure notifications are sent out quickly
898concurrent_render_limit = 5
899
900# Default setting for alert calculation timeout. Default value is 30
901evaluation_timeout_seconds = 30
902
903# Default setting for alert notification timeout. Default value is 30
904notification_timeout_seconds = 30
905
906# Default setting for max attempts to sending alert notifications. Default value is 3
907max_attempts = 3
908
909# Makes it possible to enforce a minimal interval between evaluations, to reduce load on the backend
910min_interval_seconds = 1
911
912# Configures for how long alert annotations are stored. Default is 0, which keeps them forever.
913# This setting should be expressed as an duration. Ex 6h (hours), 10d (days), 2w (weeks), 1M (month).
914max_annotation_age =
915
916# Configures max number of alert annotations that Grafana stores. Default value is 0, which keeps all alert annotations.
917max_annotations_to_keep =
918
919#################################### Annotations #########################
920[annotations]
921# Configures the batch size for the annotation clean-up job. This setting is used for dashboard, API, and alert annotations.
922cleanupjob_batchsize = 100
923
924[annotations.dashboard]
925# Dashboard annotations means that annotations are associated with the dashboard they are created on.
926
927# Configures how long dashboard annotations are stored. Default is 0, which keeps them forever.
928# This setting should be expressed as a duration. Examples: 6h (hours), 10d (days), 2w (weeks), 1M (month).
929max_age =
930
931# Configures max number of dashboard annotations that Grafana stores. Default value is 0, which keeps all dashboard annotations.
932max_annotations_to_keep =
933
934[annotations.api]
935# API annotations means that the annotations have been created using the API without any
936# association with a dashboard.
937
938# Configures how long Grafana stores API annotations. Default is 0, which keeps them forever.
939# This setting should be expressed as a duration. Examples: 6h (hours), 10d (days), 2w (weeks), 1M (month).
940max_age =
941
942# Configures max number of API annotations that Grafana keeps. Default value is 0, which keeps all API annotations.
943max_annotations_to_keep =
944
945#################################### Explore #############################
946[explore]
947# Enable the Explore section
948enabled = true
949
950#################################### Help #############################
951[help]
952# Enable the Help section
953enabled = true
954
955#################################### Profile #############################
956[profile]
957# Enable the Profile section
958enabled = true
959
960#################################### Query History #############################
961[query_history]
962# Enable the Query history
963enabled = true
964
965#################################### Internal Grafana Metrics ############
966# Metrics available at HTTP URL /metrics and /metrics/plugins/:pluginId
967[metrics]
968enabled = true
969interval_seconds = 10
970# Disable total stats (stat_totals_*) metrics to be generated
971disable_total_stats = false
972
973#If both are set, basic auth will be required for the metrics endpoints.
974basic_auth_username =
975basic_auth_password =
976
977# Metrics environment info adds dimensions to the `grafana_environment_info` metric, which
978# can expose more information about the Grafana instance.
979[metrics.environment_info]
980#exampleLabel1 = exampleValue1
981#exampleLabel2 = exampleValue2
982
983# Send internal Grafana metrics to graphite
984[metrics.graphite]
985# Enable by setting the address setting (ex localhost:2003)
986address =
987prefix = prod.grafana.%(instance_name)s.
988
989#################################### Grafana.com integration ##########################
990[grafana_net]
991url = https://grafana.com
992
993[grafana_com]
994url = https://grafana.com
995
996#################################### Distributed tracing ############
997# Opentracing is deprecated use opentelemetry instead
998[tracing.jaeger]
999# jaeger destination (ex localhost:6831)
1000address =
1001# tag that will always be included in when creating new spans. ex (tag1:value1,tag2:value2)
1002always_included_tag =
1003# Type specifies the type of the sampler: const, probabilistic, rateLimiting, or remote
1004sampler_type = const
1005# jaeger samplerconfig param
1006# for "const" sampler, 0 or 1 for always false/true respectively
1007# for "probabilistic" sampler, a probability between 0 and 1
1008# for "rateLimiting" sampler, the number of spans per second
1009# for "remote" sampler, param is the same as for "probabilistic"
1010# and indicates the initial sampling rate before the actual one
1011# is received from the mothership
1012sampler_param = 1
1013# sampling_server_url is the URL of a sampling manager providing a sampling strategy.
1014sampling_server_url =
1015# Whether or not to use Zipkin span propagation (x-b3- HTTP headers).
1016zipkin_propagation = false
1017# Setting this to true disables shared RPC spans.
1018# Not disabling is the most common setting when using Zipkin elsewhere in your infrastructure.
1019disable_shared_zipkin_spans = false
1020
1021[tracing.opentelemetry.jaeger]
1022# jaeger destination (ex http://localhost:14268/api/traces)
1023address =
1024# Propagation specifies the text map propagation format: w3c, jaeger
1025propagation =
1026
1027# This is a configuration for OTLP exporter with GRPC protocol
1028[tracing.opentelemetry.otlp]
1029# otlp destination (ex localhost:4317)
1030address =
1031# Propagation specifies the text map propagation format: w3c, jaeger
1032propagation =
1033
1034#################################### External Image Storage ##############
1035[external_image_storage]
1036# Used for uploading images to public servers so they can be included in slack/email messages.
1037# You can choose between (s3, webdav, gcs, azure_blob, local)
1038provider =
1039
1040[external_image_storage.s3]
1041endpoint =
1042path_style_access =
1043bucket_url =
1044bucket =
1045region =
1046path =
1047access_key =
1048secret_key =
1049
1050[external_image_storage.webdav]
1051url =
1052username =
1053password =
1054public_url =
1055
1056[external_image_storage.gcs]
1057key_file =
1058bucket =
1059path =
1060enable_signed_urls = false
1061signed_url_expiration =
1062
1063[external_image_storage.azure_blob]
1064account_name =
1065account_key =
1066container_name =
1067
1068[external_image_storage.local]
1069# does not require any configuration
1070
1071[rendering]
1072# Options to configure a remote HTTP image rendering service, e.g. using https://github.com/grafana/grafana-image-renderer.
1073# URL to a remote HTTP image renderer service, e.g. http://localhost:8081/render, will enable Grafana to render panels and dashboards to PNG-images using HTTP requests to an external service.
1074server_url =
1075# If the remote HTTP image renderer service runs on a different server than the Grafana server you may have to configure this to a URL where Grafana is reachable, e.g. http://grafana.domain/.
1076callback_url =
1077# Concurrent render request limit affects when the /render HTTP endpoint is used. Rendering many images at the same time can overload the server,
1078# which this setting can help protect against by only allowing a certain amount of concurrent requests.
1079concurrent_render_request_limit = 30
1080
1081[panels]
1082# here for to support old env variables, can remove after a few months
1083enable_alpha = false
1084disable_sanitize_html = false
1085
1086[plugins]
1087enable_alpha = false
1088app_tls_skip_verify_insecure = false
1089# Enter a comma-separated list of plugin identifiers to identify plugins to load even if they are unsigned. Plugins with modified signatures are never loaded.
1090allow_loading_unsigned_plugins =
1091# Enable or disable installing / uninstalling / updating plugins directly from within Grafana.
1092plugin_admin_enabled = true
1093plugin_admin_external_manage_enabled = false
1094plugin_catalog_url = https://grafana.com/grafana/plugins/
1095# Enter a comma-separated list of plugin identifiers to hide in the plugin catalog.
1096plugin_catalog_hidden_plugins =
1097
1098#################################### Grafana Live ##########################################
1099[live]
1100# max_connections to Grafana Live WebSocket endpoint per Grafana server instance. See Grafana Live docs
1101# if you are planning to make it higher than default 100 since this can require some OS and infrastructure
1102# tuning. 0 disables Live, -1 means unlimited connections.
1103max_connections = 100
1104
1105# allowed_origins is a comma-separated list of origins that can establish connection with Grafana Live.
1106# If not set then origin will be matched over root_url. Supports wildcard symbol "*".
1107allowed_origins =
1108
1109# engine defines an HA (high availability) engine to use for Grafana Live. By default no engine used - in
1110# this case Live features work only on a single Grafana server.
1111# Available options: "redis".
1112# Setting ha_engine is an EXPERIMENTAL feature.
1113ha_engine =
1114
1115# ha_engine_address sets a connection address for Live HA engine. Depending on engine type address format can differ.
1116# For now we only support Redis connection address in "host:port" format.
1117# This option is EXPERIMENTAL.
1118ha_engine_address = "127.0.0.1:6379"
1119
1120#################################### Grafana Image Renderer Plugin ##########################
1121[plugin.grafana-image-renderer]
1122# Instruct headless browser instance to use a default timezone when not provided by Grafana, e.g. when rendering panel image of alert.
1123# See ICU’s metaZones.txt (https://cs.chromium.org/chromium/src/third_party/icu/source/data/misc/metaZones.txt) for a list of supported
1124# timezone IDs. Fallbacks to TZ environment variable if not set.
1125rendering_timezone =
1126
1127# Instruct headless browser instance to use a default language when not provided by Grafana, e.g. when rendering panel image of alert.
1128# Please refer to the HTTP header Accept-Language to understand how to format this value, e.g. 'fr-CH, fr;q=0.9, en;q=0.8, de;q=0.7, *;q=0.5'.
1129rendering_language =
1130
1131# Instruct headless browser instance to use a default device scale factor when not provided by Grafana, e.g. when rendering panel image of alert.
1132# Default is 1. Using a higher value will produce more detailed images (higher DPI), but will require more disk space to store an image.
1133rendering_viewport_device_scale_factor =
1134
1135# Instruct headless browser instance whether to ignore HTTPS errors during navigation. Per default HTTPS errors are not ignored. Due to
1136# the security risk it's not recommended to ignore HTTPS errors.
1137rendering_ignore_https_errors =
1138
1139# Instruct headless browser instance whether to capture and log verbose information when rendering an image. Default is false and will
1140# only capture and log error messages. When enabled, debug messages are captured and logged as well.
1141# For the verbose information to be included in the Grafana server log you have to adjust the rendering log level to debug, configure
1142# [log].filter = rendering:debug.
1143rendering_verbose_logging =
1144
1145# Instruct headless browser instance whether to output its debug and error messages into running process of remote rendering service.
1146# Default is false. This can be useful to enable (true) when troubleshooting.
1147rendering_dumpio =
1148
1149# Additional arguments to pass to the headless browser instance. Default is --no-sandbox. The list of Chromium flags can be found
1150# here (https://peter.sh/experiments/chromium-command-line-switches/). Multiple arguments is separated with comma-character.
1151rendering_args =
1152
1153# You can configure the plugin to use a different browser binary instead of the pre-packaged version of Chromium.
1154# Please note that this is not recommended, since you may encounter problems if the installed version of Chrome/Chromium is not
1155# compatible with the plugin.
1156rendering_chrome_bin =
1157
1158# Instruct how headless browser instances are created. Default is 'default' and will create a new browser instance on each request.
1159# Mode 'clustered' will make sure that only a maximum of browsers/incognito pages can execute concurrently.
1160# Mode 'reusable' will have one browser instance and will create a new incognito page on each request.
1161rendering_mode =
1162
1163# When rendering_mode = clustered, you can instruct how many browsers or incognito pages can execute concurrently. Default is 'browser'
1164# and will cluster using browser instances.
1165# Mode 'context' will cluster using incognito pages.
1166rendering_clustering_mode =
1167# When rendering_mode = clustered, you can define the maximum number of browser instances/incognito pages that can execute concurrently. Default is '5'.
1168rendering_clustering_max_concurrency =
1169# When rendering_mode = clustered, you can specify the duration a rendering request can take before it will time out. Default is `30` seconds.
1170rendering_clustering_timeout =
1171
1172# Limit the maximum viewport width, height and device scale factor that can be requested.
1173rendering_viewport_max_width =
1174rendering_viewport_max_height =
1175rendering_viewport_max_device_scale_factor =
1176
1177# Change the listening host and port of the gRPC server. Default host is 127.0.0.1 and default port is 0 and will automatically assign
1178# a port not in use.
1179grpc_host =
1180grpc_port =
1181
1182[enterprise]
1183license_path =
1184
1185[feature_toggles]
1186# there are currently two ways to enable feature toggles in the `grafana.ini`.
1187# you can either pass an array of feature you want to enable to the `enable` field or
1188# configure each toggle by setting the name of the toggle to true/false. Toggles set to true/false
1189# will take precedence over toggles in the `enable` list.
1190
1191# enable = feature1,feature2
1192enable =
1193
1194# The new prometheus visual query builder
1195promQueryBuilder = true
1196
1197# The new loki visual query builder
1198lokiQueryBuilder = true
1199
1200# Experimental Explore to Dashboard workflow
1201explore2Dashboard = true
1202
1203# Experimental Command Palette
1204commandPalette = true
1205
1206# Use dynamic labels in CloudWatch datasource
1207cloudWatchDynamicLabels = true
1208
1209# feature1 = true
1210# feature2 = false
1211
1212[date_formats]
1213# For information on what formatting patterns that are supported https://momentjs.com/docs/#/displaying/
1214
1215# Default system date format used in time range picker and other places where full time is displayed
1216full_date = YYYY-MM-DD HH:mm:ss
1217
1218# Used by graph and other places where we only show small intervals
1219interval_second = HH:mm:ss
1220interval_minute = HH:mm
1221interval_hour = MM/DD HH:mm
1222interval_day = MM/DD
1223interval_month = YYYY-MM
1224interval_year = YYYY
1225
1226# Experimental feature
1227use_browser_locale = false
1228
1229# Default timezone for user preferences. Options are 'browser' for the browser local timezone or a timezone name from IANA Time Zone database, e.g. 'UTC' or 'Europe/Amsterdam' etc.
1230default_timezone = browser
1231
1232[expressions]
1233# Enable or disable the expressions functionality.
1234enabled = true
1235
1236[geomap]
1237# Set the JSON configuration for the default basemap
1238default_baselayer_config =
1239
1240# Enable or disable loading other base map layers
1241enable_custom_baselayers = true
1242
1243#################################### Dashboard previews #####################################
1244
1245[dashboard_previews.crawler]
1246# Number of dashboards rendered in parallel. Default is 6.
1247thread_count =
1248
1249# Timeout passed down to the Image Renderer plugin. It is used in two separate places within a single rendering request:
1250# First during the initial navigation to the dashboard and then when waiting for all the panels to load. Default is 20s.
1251# This setting should be expressed as a duration. Examples: 10s (seconds), 1m (minutes).
1252rendering_timeout =
1253
1254# Maximum duration of a single crawl. Default is 1h.
1255# This setting should be expressed as a duration. Examples: 10s (seconds), 1m (minutes).
1256max_crawl_duration =
1257
1258# Minimum interval between two subsequent scheduler runs. Default is 12h.
1259# This setting should be expressed as a duration. Examples: 10s (seconds), 1m (minutes).
1260scheduler_interval =
1261
1262
1263#################################### Storage ################################################
1264
1265[storage]
1266# Allow uploading SVG files without sanitization.
1267allow_unsanitized_svg_upload = false
1268
diff --git a/data/grafana/main.json b/data/grafana/main.json
new file mode 100644
index 0000000..53a1adb
--- /dev/null
+++ b/data/grafana/main.json
@@ -0,0 +1,5176 @@
1{
2 "annotations": {
3 "list": [
4 {
5 "builtIn": 1,
6 "datasource": {
7 "type": "grafana",
8 "uid": "-- Grafana --"
9 },
10 "enable": true,
11 "hide": true,
12 "iconColor": "rgba(0, 211, 255, 1)",
13 "name": "Annotations & Alerts",
14 "target": {
15 "limit": 100,
16 "matchAny": false,
17 "tags": [],
18 "type": "dashboard"
19 },
20 "type": "dashboard"
21 }
22 ]
23 },
24 "editable": true,
25 "fiscalYearStartMonth": 0,
26 "graphTooltip": 0,
27 "id": 19,
28 "links": [],
29 "liveNow": false,
30 "panels": [
31 {
32 "collapsed": false,
33 "gridPos": {
34 "h": 1,
35 "w": 24,
36 "x": 0,
37 "y": 0
38 },
39 "id": 49,
40 "panels": [],
41 "title": "Infrastructure",
42 "type": "row"
43 },
44 {
45 "datasource": {
46 "type": "influxdb",
47 "uid": "1Y6tKgWVz"
48 },
49 "fieldConfig": {
50 "defaults": {
51 "color": {
52 "mode": "thresholds"
53 },
54 "mappings": [
55 {
56 "options": {
57 "match": "null",
58 "result": {
59 "text": "N/A"
60 }
61 },
62 "type": "special"
63 }
64 ],
65 "max": 1,
66 "min": 0,
67 "thresholds": {
68 "mode": "absolute",
69 "steps": [
70 {
71 "color": "#299c46"
72 },
73 {
74 "color": "rgba(237, 129, 40, 0.89)",
75 "value": 0.75
76 },
77 {
78 "color": "#d44a3a",
79 "value": 0.875
80 }
81 ]
82 },
83 "unit": "percentunit"
84 },
85 "overrides": []
86 },
87 "gridPos": {
88 "h": 8,
89 "w": 3,
90 "x": 0,
91 "y": 1
92 },
93 "id": 29,
94 "links": [],
95 "maxDataPoints": 100,
96 "options": {
97 "orientation": "horizontal",
98 "reduceOptions": {
99 "calcs": [
100 "lastNotNull"
101 ],
102 "fields": "",
103 "values": false
104 },
105 "showThresholdLabels": false,
106 "showThresholdMarkers": true
107 },
108 "pluginVersion": "10.0.0",
109 "targets": [
110 {
111 "datasource": {
112 "type": "influxdb",
113 "uid": "1Y6tKgWVz"
114 },
115 "groupBy": [
116 {
117 "params": [
118 "$__interval"
119 ],
120 "type": "time"
121 },
122 {
123 "params": [
124 "null"
125 ],
126 "type": "fill"
127 }
128 ],
129 "measurement": "system",
130 "orderByTime": "ASC",
131 "policy": "default",
132 "query": "SELECT last(\"used\") / last(\"total\") FROM \"system\" WHERE (\"host\" = 'fast-pool') AND $timeFilter GROUP BY time($__interval) fill(null)",
133 "rawQuery": true,
134 "refId": "A",
135 "resultFormat": "time_series",
136 "select": [
137 [
138 {
139 "params": [
140 "used"
141 ],
142 "type": "field"
143 },
144 {
145 "params": [
146 " /"
147 ],
148 "type": "math"
149 }
150 ],
151 [
152 {
153 "params": [
154 "total"
155 ],
156 "type": "field"
157 }
158 ],
159 [
160 {
161 "params": [
162 "used"
163 ],
164 "type": "field"
165 },
166 {
167 "params": [
168 " /"
169 ],
170 "type": "math"
171 }
172 ]
173 ],
174 "tags": [
175 {
176 "key": "host",
177 "operator": "=",
178 "value": "fast-pool"
179 }
180 ]
181 }
182 ],
183 "title": "fast-pool Usage",
184 "transparent": true,
185 "type": "gauge"
186 },
187 {
188 "datasource": {
189 "type": "influxdb",
190 "uid": "1Y6tKgWVz"
191 },
192 "fieldConfig": {
193 "defaults": {
194 "color": {
195 "mode": "thresholds"
196 },
197 "mappings": [
198 {
199 "options": {
200 "match": "null",
201 "result": {
202 "text": "N/A"
203 }
204 },
205 "type": "special"
206 }
207 ],
208 "max": 1,
209 "min": 0,
210 "thresholds": {
211 "mode": "absolute",
212 "steps": [
213 {
214 "color": "#299c46"
215 },
216 {
217 "color": "rgba(237, 129, 40, 0.89)",
218 "value": 0.75
219 },
220 {
221 "color": "#d44a3a",
222 "value": 0.875
223 }
224 ]
225 },
226 "unit": "percentunit"
227 },
228 "overrides": []
229 },
230 "gridPos": {
231 "h": 8,
232 "w": 3,
233 "x": 3,
234 "y": 1
235 },
236 "id": 31,
237 "links": [],
238 "maxDataPoints": 100,
239 "options": {
240 "orientation": "horizontal",
241 "reduceOptions": {
242 "calcs": [
243 "lastNotNull"
244 ],
245 "fields": "",
246 "values": false
247 },
248 "showThresholdLabels": false,
249 "showThresholdMarkers": true
250 },
251 "pluginVersion": "10.0.0",
252 "targets": [
253 {
254 "datasource": {
255 "type": "influxdb",
256 "uid": "1Y6tKgWVz"
257 },
258 "groupBy": [
259 {
260 "params": [
261 "$__interval"
262 ],
263 "type": "time"
264 },
265 {
266 "params": [
267 "null"
268 ],
269 "type": "fill"
270 }
271 ],
272 "orderByTime": "ASC",
273 "policy": "default",
274 "query": "SELECT last(\"used\") / last(\"total\") FROM \"system\" WHERE (\"host\" = 'slow-pool') AND $timeFilter GROUP BY time($__interval) fill(null)",
275 "rawQuery": true,
276 "refId": "A",
277 "resultFormat": "time_series",
278 "select": [
279 [
280 {
281 "params": [
282 "value"
283 ],
284 "type": "field"
285 },
286 {
287 "params": [],
288 "type": "mean"
289 }
290 ]
291 ],
292 "tags": []
293 }
294 ],
295 "title": "slow-pool Usage",
296 "transparent": true,
297 "type": "gauge"
298 },
299 {
300 "datasource": {
301 "type": "influxdb",
302 "uid": "1Y6tKgWVz"
303 },
304 "fieldConfig": {
305 "defaults": {
306 "color": {
307 "mode": "thresholds"
308 },
309 "mappings": [
310 {
311 "options": {
312 "match": "null",
313 "result": {
314 "text": "N/A"
315 }
316 },
317 "type": "special"
318 }
319 ],
320 "thresholds": {
321 "mode": "absolute",
322 "steps": [
323 {
324 "color": "green"
325 },
326 {
327 "color": "red",
328 "value": 80
329 }
330 ]
331 },
332 "unit": "none"
333 },
334 "overrides": []
335 },
336 "gridPos": {
337 "h": 3,
338 "w": 2,
339 "x": 6,
340 "y": 1
341 },
342 "id": 35,
343 "links": [],
344 "maxDataPoints": 100,
345 "options": {
346 "colorMode": "none",
347 "graphMode": "none",
348 "justifyMode": "auto",
349 "orientation": "horizontal",
350 "reduceOptions": {
351 "calcs": [
352 "lastNotNull"
353 ],
354 "fields": "",
355 "values": false
356 },
357 "textMode": "auto"
358 },
359 "pluginVersion": "10.0.0",
360 "targets": [
361 {
362 "datasource": {
363 "type": "influxdb",
364 "uid": "1Y6tKgWVz"
365 },
366 "groupBy": [],
367 "measurement": "cpustat",
368 "orderByTime": "ASC",
369 "policy": "default",
370 "query": "SELECT \"cpus\" FROM \"cpustat\" WHERE (\"host\" = 'proxmox') AND $timeFilter",
371 "rawQuery": true,
372 "refId": "A",
373 "resultFormat": "time_series",
374 "select": [
375 [
376 {
377 "params": [
378 "cpus"
379 ],
380 "type": "field"
381 }
382 ]
383 ],
384 "tags": [
385 {
386 "key": "host",
387 "operator": "=~",
388 "value": "/^$server$/"
389 }
390 ]
391 }
392 ],
393 "title": "Cores",
394 "type": "stat"
395 },
396 {
397 "columns": [],
398 "datasource": {
399 "type": "influxdb",
400 "uid": "1Y6tKgWVz"
401 },
402 "fontSize": "100%",
403 "gridPos": {
404 "h": 8,
405 "w": 4,
406 "x": 8,
407 "y": 1
408 },
409 "id": 39,
410 "interval": "",
411 "links": [],
412 "scroll": true,
413 "showHeader": true,
414 "sort": {
415 "col": 1,
416 "desc": false
417 },
418 "styles": [
419 {
420 "alias": "Time",
421 "align": "auto",
422 "colors": [
423 "rgba(245, 54, 54, 0.9)",
424 "rgba(237, 129, 40, 0.89)",
425 "rgba(50, 172, 45, 0.97)"
426 ],
427 "dateFormat": "YYYY-MM-DD HH:mm:ss",
428 "decimals": 2,
429 "mappingType": 1,
430 "pattern": "Time",
431 "thresholds": [],
432 "type": "hidden",
433 "unit": "short"
434 },
435 {
436 "alias": "Uptime",
437 "align": "auto",
438 "colors": [
439 "rgba(245, 54, 54, 0.9)",
440 "rgba(237, 129, 40, 0.89)",
441 "rgba(50, 172, 45, 0.97)"
442 ],
443 "dateFormat": "YYYY-MM-DD HH:mm:ss",
444 "decimals": 1,
445 "mappingType": 1,
446 "pattern": "uptime",
447 "rangeMaps": [
448 {
449 "from": "",
450 "text": "",
451 "to": ""
452 }
453 ],
454 "thresholds": [],
455 "type": "number",
456 "unit": "dtdurations",
457 "valueMaps": [
458 {
459 "text": "Offline",
460 "value": "0"
461 }
462 ]
463 },
464 {
465 "alias": "VM",
466 "align": "auto",
467 "colors": [
468 "rgba(245, 54, 54, 0.9)",
469 "rgba(237, 129, 40, 0.89)",
470 "rgba(50, 172, 45, 0.97)"
471 ],
472 "dateFormat": "YYYY-MM-DD HH:mm:ss",
473 "decimals": 2,
474 "mappingType": 1,
475 "pattern": "host",
476 "sanitize": true,
477 "thresholds": [],
478 "type": "string",
479 "unit": "short",
480 "valueMaps": []
481 }
482 ],
483 "targets": [
484 {
485 "datasource": {
486 "type": "influxdb",
487 "uid": "1Y6tKgWVz"
488 },
489 "groupBy": [
490 {
491 "params": [
492 "host"
493 ],
494 "type": "tag"
495 }
496 ],
497 "limit": "1",
498 "measurement": "system",
499 "orderByTime": "ASC",
500 "policy": "default",
501 "query": "SELECT last(uptime) AS \"uptime\" FROM \"system\" WHERE (\"object\" = 'qemu') AND time > (now() - 10m) AND uptime > 0 GROUP BY \"host\" limit 1",
502 "rawQuery": true,
503 "refId": "A",
504 "resultFormat": "table",
505 "select": [
506 [
507 {
508 "params": [
509 "uptime"
510 ],
511 "type": "field"
512 },
513 {
514 "params": [
515 "uptime"
516 ],
517 "type": "alias"
518 }
519 ]
520 ],
521 "tags": [
522 {
523 "key": "object",
524 "operator": "=",
525 "value": "qemu"
526 }
527 ]
528 }
529 ],
530 "title": "Running VMs",
531 "transform": "table",
532 "transparent": true,
533 "type": "table-old"
534 },
535 {
536 "datasource": {
537 "type": "prometheus",
538 "uid": "t-6Lw3i4z"
539 },
540 "fieldConfig": {
541 "defaults": {
542 "color": {
543 "mode": "palette-classic"
544 },
545 "custom": {
546 "axisCenteredZero": false,
547 "axisColorMode": "text",
548 "axisLabel": "temperature",
549 "axisPlacement": "auto",
550 "barAlignment": 0,
551 "drawStyle": "line",
552 "fillOpacity": 20,
553 "gradientMode": "none",
554 "hideFrom": {
555 "legend": false,
556 "tooltip": false,
557 "viz": false
558 },
559 "lineInterpolation": "linear",
560 "lineWidth": 1,
561 "pointSize": 5,
562 "scaleDistribution": {
563 "type": "linear"
564 },
565 "showPoints": "never",
566 "spanNulls": false,
567 "stacking": {
568 "group": "A",
569 "mode": "none"
570 },
571 "thresholdsStyle": {
572 "mode": "off"
573 }
574 },
575 "links": [],
576 "mappings": [],
577 "min": 0,
578 "thresholds": {
579 "mode": "absolute",
580 "steps": [
581 {
582 "color": "green"
583 },
584 {
585 "color": "red",
586 "value": 80
587 }
588 ]
589 },
590 "unit": "celsius"
591 },
592 "overrides": [
593 {
594 "matcher": {
595 "id": "byRegexp",
596 "options": "/.*Critical*./"
597 },
598 "properties": [
599 {
600 "id": "color",
601 "value": {
602 "fixedColor": "#E24D42",
603 "mode": "fixed"
604 }
605 },
606 {
607 "id": "custom.fillOpacity",
608 "value": 0
609 }
610 ]
611 },
612 {
613 "matcher": {
614 "id": "byRegexp",
615 "options": "/.*Max*./"
616 },
617 "properties": [
618 {
619 "id": "color",
620 "value": {
621 "fixedColor": "#EF843C",
622 "mode": "fixed"
623 }
624 },
625 {
626 "id": "custom.fillOpacity",
627 "value": 0
628 }
629 ]
630 }
631 ]
632 },
633 "gridPos": {
634 "h": 8,
635 "w": 12,
636 "x": 12,
637 "y": 1
638 },
639 "id": 18,
640 "links": [],
641 "options": {
642 "legend": {
643 "calcs": [
644 "mean",
645 "lastNotNull",
646 "max",
647 "min"
648 ],
649 "displayMode": "table",
650 "placement": "bottom",
651 "showLegend": true
652 },
653 "tooltip": {
654 "mode": "multi",
655 "sort": "none"
656 }
657 },
658 "pluginVersion": "9.1.1",
659 "targets": [
660 {
661 "datasource": {
662 "type": "prometheus",
663 "uid": "t-6Lw3i4z"
664 },
665 "editorMode": "code",
666 "expr": "node_hwmon_temp_celsius{instance=\"proxmox.home.local:9100\"}",
667 "format": "time_series",
668 "interval": "",
669 "intervalFactor": 1,
670 "legendFormat": "{{ chip }} {{ sensor }} temp",
671 "range": true,
672 "refId": "A",
673 "step": 240
674 },
675 {
676 "datasource": {
677 "type": "prometheus",
678 "uid": "t-6Lw3i4z"
679 },
680 "editorMode": "code",
681 "expr": "node_hwmon_temp_crit_alarm_celsius{instance=\"proxmox.home.local:9100\"}",
682 "format": "time_series",
683 "hide": true,
684 "interval": "",
685 "intervalFactor": 1,
686 "legendFormat": "{{ chip }} {{ sensor }} Critical Alarm",
687 "range": true,
688 "refId": "B",
689 "step": 240
690 },
691 {
692 "datasource": {
693 "type": "prometheus",
694 "uid": "t-6Lw3i4z"
695 },
696 "editorMode": "code",
697 "expr": "node_hwmon_temp_crit_celsius{instance=\"proxmox.home.local:9100\"}",
698 "format": "time_series",
699 "interval": "",
700 "intervalFactor": 1,
701 "legendFormat": "{{ chip }} {{ sensor }} Critical",
702 "range": true,
703 "refId": "C",
704 "step": 240
705 },
706 {
707 "datasource": {
708 "type": "prometheus",
709 "uid": "t-6Lw3i4z"
710 },
711 "editorMode": "code",
712 "expr": "node_hwmon_temp_crit_hyst_celsius{instance=\"proxmox.home.local:9100\"}",
713 "format": "time_series",
714 "hide": true,
715 "interval": "",
716 "intervalFactor": 1,
717 "legendFormat": "{{ chip }} {{ sensor }} Critical Historical",
718 "range": true,
719 "refId": "D",
720 "step": 240
721 },
722 {
723 "datasource": {
724 "type": "prometheus",
725 "uid": "t-6Lw3i4z"
726 },
727 "expr": "node_hwmon_temp_max_celsius{instance=\"$node\",job=\"$job\"}",
728 "format": "time_series",
729 "hide": true,
730 "interval": "",
731 "intervalFactor": 1,
732 "legendFormat": "{{ chip }} {{ sensor }} Max",
733 "refId": "E",
734 "step": 240
735 }
736 ],
737 "title": "Hardware temperature monitor",
738 "type": "timeseries"
739 },
740 {
741 "datasource": {
742 "type": "influxdb",
743 "uid": "1Y6tKgWVz"
744 },
745 "fieldConfig": {
746 "defaults": {
747 "color": {
748 "mode": "thresholds"
749 },
750 "mappings": [
751 {
752 "options": {
753 "match": "null",
754 "result": {
755 "text": "N/A"
756 }
757 },
758 "type": "special"
759 }
760 ],
761 "thresholds": {
762 "mode": "absolute",
763 "steps": [
764 {
765 "color": "green"
766 },
767 {
768 "color": "red",
769 "value": 80
770 }
771 ]
772 },
773 "unit": "bytes"
774 },
775 "overrides": []
776 },
777 "gridPos": {
778 "h": 3,
779 "w": 2,
780 "x": 6,
781 "y": 4
782 },
783 "id": 37,
784 "links": [],
785 "maxDataPoints": 100,
786 "options": {
787 "colorMode": "none",
788 "graphMode": "none",
789 "justifyMode": "auto",
790 "orientation": "horizontal",
791 "reduceOptions": {
792 "calcs": [
793 "lastNotNull"
794 ],
795 "fields": "",
796 "values": false
797 },
798 "textMode": "auto"
799 },
800 "pluginVersion": "10.0.0",
801 "targets": [
802 {
803 "datasource": {
804 "type": "influxdb",
805 "uid": "1Y6tKgWVz"
806 },
807 "groupBy": [],
808 "measurement": "memory",
809 "orderByTime": "ASC",
810 "policy": "default",
811 "query": "SELECT \"memtotal\" FROM \"memory\" WHERE (\"host\" = 'proxmox') AND $timeFilter",
812 "rawQuery": true,
813 "refId": "A",
814 "resultFormat": "time_series",
815 "select": [
816 [
817 {
818 "params": [
819 "memtotal"
820 ],
821 "type": "field"
822 }
823 ]
824 ],
825 "tags": [
826 {
827 "key": "host",
828 "operator": "=~",
829 "value": "/^$server$/"
830 }
831 ]
832 }
833 ],
834 "title": "Total Memory",
835 "type": "stat"
836 },
837 {
838 "datasource": {
839 "type": "influxdb",
840 "uid": "1Y6tKgWVz"
841 },
842 "fieldConfig": {
843 "defaults": {
844 "color": {
845 "mode": "palette-classic"
846 },
847 "custom": {
848 "axisCenteredZero": false,
849 "axisColorMode": "text",
850 "axisLabel": "Percent",
851 "axisPlacement": "auto",
852 "barAlignment": 0,
853 "drawStyle": "line",
854 "fillOpacity": 10,
855 "gradientMode": "none",
856 "hideFrom": {
857 "legend": false,
858 "tooltip": false,
859 "viz": false
860 },
861 "lineInterpolation": "linear",
862 "lineWidth": 2,
863 "pointSize": 5,
864 "scaleDistribution": {
865 "type": "linear"
866 },
867 "showPoints": "never",
868 "spanNulls": false,
869 "stacking": {
870 "group": "A",
871 "mode": "none"
872 },
873 "thresholdsStyle": {
874 "mode": "off"
875 }
876 },
877 "mappings": [],
878 "min": 0,
879 "thresholds": {
880 "mode": "absolute",
881 "steps": [
882 {
883 "color": "green"
884 },
885 {
886 "color": "red",
887 "value": 80
888 }
889 ]
890 },
891 "unit": "percent"
892 },
893 "overrides": [
894 {
895 "matcher": {
896 "id": "byName",
897 "options": "I/O Wait"
898 },
899 "properties": [
900 {
901 "id": "color",
902 "value": {
903 "fixedColor": "dark-blue",
904 "mode": "fixed"
905 }
906 }
907 ]
908 },
909 {
910 "matcher": {
911 "id": "byName",
912 "options": "Load Average (1 Min)"
913 },
914 "properties": [
915 {
916 "id": "color",
917 "value": {
918 "fixedColor": "dark-red",
919 "mode": "fixed"
920 }
921 }
922 ]
923 },
924 {
925 "matcher": {
926 "id": "byName",
927 "options": "Load Average (1 Min)"
928 },
929 "properties": [
930 {
931 "id": "unit",
932 "value": "short"
933 },
934 {
935 "id": "max",
936 "value": 10
937 },
938 {
939 "id": "custom.axisLabel",
940 "value": "Load"
941 }
942 ]
943 }
944 ]
945 },
946 "gridPos": {
947 "h": 7,
948 "w": 12,
949 "x": 0,
950 "y": 9
951 },
952 "id": 12,
953 "interval": "",
954 "links": [],
955 "options": {
956 "legend": {
957 "calcs": [
958 "mean",
959 "lastNotNull"
960 ],
961 "displayMode": "list",
962 "placement": "bottom",
963 "showLegend": true
964 },
965 "tooltip": {
966 "mode": "multi",
967 "sort": "none"
968 }
969 },
970 "pluginVersion": "9.1.1",
971 "targets": [
972 {
973 "alias": "CPU Usage",
974 "datasource": {
975 "type": "influxdb",
976 "uid": "1Y6tKgWVz"
977 },
978 "groupBy": [
979 {
980 "params": [
981 "1m"
982 ],
983 "type": "time"
984 },
985 {
986 "params": [
987 "null"
988 ],
989 "type": "fill"
990 }
991 ],
992 "hide": false,
993 "measurement": "cpustat",
994 "orderByTime": "ASC",
995 "policy": "default",
996 "query": "SELECT mean(\"cpu\") * 100 FROM \"cpustat\" WHERE (\"host\" =~ /^$server$/) AND $timeFilter GROUP BY time(1m) fill(null)",
997 "rawQuery": false,
998 "refId": "A",
999 "resultFormat": "time_series",
1000 "select": [
1001 [
1002 {
1003 "params": [
1004 "cpu"
1005 ],
1006 "type": "field"
1007 },
1008 {
1009 "params": [],
1010 "type": "mean"
1011 },
1012 {
1013 "params": [
1014 "* 100"
1015 ],
1016 "type": "math"
1017 }
1018 ]
1019 ],
1020 "tags": [
1021 {
1022 "key": "host",
1023 "operator": "=",
1024 "value": "proxmox"
1025 }
1026 ]
1027 },
1028 {
1029 "alias": "I/O Wait",
1030 "datasource": {
1031 "type": "influxdb",
1032 "uid": "1Y6tKgWVz"
1033 },
1034 "groupBy": [
1035 {
1036 "params": [
1037 "1m"
1038 ],
1039 "type": "time"
1040 },
1041 {
1042 "params": [
1043 "null"
1044 ],
1045 "type": "fill"
1046 }
1047 ],
1048 "measurement": "cpustat",
1049 "orderByTime": "ASC",
1050 "policy": "default",
1051 "query": "SELECT mean(\"wait\") * 100 FROM \"cpustat\" WHERE (\"host\" =~ /^$server$/) AND $timeFilter GROUP BY time(1m) fill(null)",
1052 "rawQuery": false,
1053 "refId": "B",
1054 "resultFormat": "time_series",
1055 "select": [
1056 [
1057 {
1058 "params": [
1059 "wait"
1060 ],
1061 "type": "field"
1062 },
1063 {
1064 "params": [],
1065 "type": "mean"
1066 },
1067 {
1068 "params": [
1069 " * 100"
1070 ],
1071 "type": "math"
1072 }
1073 ]
1074 ],
1075 "tags": [
1076 {
1077 "key": "host",
1078 "operator": "=",
1079 "value": "proxmox"
1080 }
1081 ]
1082 },
1083 {
1084 "alias": "Load Average (1 Min)",
1085 "datasource": {
1086 "type": "influxdb",
1087 "uid": "1Y6tKgWVz"
1088 },
1089 "groupBy": [
1090 {
1091 "params": [
1092 "1m"
1093 ],
1094 "type": "time"
1095 },
1096 {
1097 "params": [
1098 "null"
1099 ],
1100 "type": "fill"
1101 }
1102 ],
1103 "measurement": "cpustat",
1104 "orderByTime": "ASC",
1105 "policy": "default",
1106 "query": "SELECT mean(\"avg1\") FROM \"cpustat\" WHERE (\"host\" =~ /^$server$/) AND $timeFilter GROUP BY time(1m) fill(null)",
1107 "rawQuery": false,
1108 "refId": "C",
1109 "resultFormat": "time_series",
1110 "select": [
1111 [
1112 {
1113 "params": [
1114 "avg1"
1115 ],
1116 "type": "field"
1117 },
1118 {
1119 "params": [],
1120 "type": "mean"
1121 }
1122 ]
1123 ],
1124 "tags": [
1125 {
1126 "key": "host",
1127 "operator": "=",
1128 "value": "proxmox"
1129 }
1130 ]
1131 }
1132 ],
1133 "title": "Host CPU Usage",
1134 "type": "timeseries"
1135 },
1136 {
1137 "datasource": {
1138 "type": "influxdb",
1139 "uid": "1Y6tKgWVz"
1140 },
1141 "fieldConfig": {
1142 "defaults": {
1143 "color": {
1144 "mode": "palette-classic"
1145 },
1146 "custom": {
1147 "axisCenteredZero": false,
1148 "axisColorMode": "text",
1149 "axisLabel": "",
1150 "axisPlacement": "auto",
1151 "barAlignment": 0,
1152 "drawStyle": "line",
1153 "fillOpacity": 10,
1154 "gradientMode": "none",
1155 "hideFrom": {
1156 "legend": false,
1157 "tooltip": false,
1158 "viz": false
1159 },
1160 "lineInterpolation": "linear",
1161 "lineWidth": 2,
1162 "pointSize": 5,
1163 "scaleDistribution": {
1164 "type": "linear"
1165 },
1166 "showPoints": "never",
1167 "spanNulls": false,
1168 "stacking": {
1169 "group": "A",
1170 "mode": "none"
1171 },
1172 "thresholdsStyle": {
1173 "mode": "off"
1174 }
1175 },
1176 "mappings": [],
1177 "max": 100,
1178 "min": 0,
1179 "thresholds": {
1180 "mode": "absolute",
1181 "steps": [
1182 {
1183 "color": "green"
1184 },
1185 {
1186 "color": "red",
1187 "value": 80
1188 }
1189 ]
1190 },
1191 "unit": "percent"
1192 },
1193 "overrides": []
1194 },
1195 "gridPos": {
1196 "h": 7,
1197 "w": 12,
1198 "x": 12,
1199 "y": 9
1200 },
1201 "id": 14,
1202 "links": [],
1203 "options": {
1204 "legend": {
1205 "calcs": [
1206 "mean",
1207 "lastNotNull",
1208 "max",
1209 "min"
1210 ],
1211 "displayMode": "table",
1212 "placement": "bottom",
1213 "showLegend": true
1214 },
1215 "tooltip": {
1216 "mode": "multi",
1217 "sort": "none"
1218 }
1219 },
1220 "pluginVersion": "9.1.1",
1221 "targets": [
1222 {
1223 "alias": "Memory Use",
1224 "datasource": {
1225 "type": "influxdb",
1226 "uid": "1Y6tKgWVz"
1227 },
1228 "groupBy": [
1229 {
1230 "params": [
1231 "$__interval"
1232 ],
1233 "type": "time"
1234 },
1235 {
1236 "params": [
1237 "null"
1238 ],
1239 "type": "fill"
1240 }
1241 ],
1242 "measurement": "memory",
1243 "orderByTime": "ASC",
1244 "policy": "default",
1245 "query": "SELECT mean(\"memused\")/mean(\"memtotal\") * 100 FROM \"memory\" WHERE (\"host\" = 'proxmox') AND $timeFilter GROUP BY time(1m) fill(null)",
1246 "rawQuery": true,
1247 "refId": "A",
1248 "resultFormat": "time_series",
1249 "select": [
1250 [
1251 {
1252 "params": [
1253 "memused"
1254 ],
1255 "type": "field"
1256 },
1257 {
1258 "params": [],
1259 "type": "mean"
1260 }
1261 ]
1262 ],
1263 "tags": [
1264 {
1265 "key": "host",
1266 "operator": "=~",
1267 "value": "/^$server$/"
1268 }
1269 ]
1270 }
1271 ],
1272 "title": "Host Memory Usage",
1273 "type": "timeseries"
1274 },
1275 {
1276 "datasource": {
1277 "type": "prometheus",
1278 "uid": "t-6Lw3i4z"
1279 },
1280 "fieldConfig": {
1281 "defaults": {
1282 "color": {
1283 "mode": "palette-classic"
1284 },
1285 "custom": {
1286 "axisCenteredZero": false,
1287 "axisColorMode": "text",
1288 "axisLabel": "",
1289 "axisPlacement": "left",
1290 "barAlignment": 0,
1291 "drawStyle": "line",
1292 "fillOpacity": 10,
1293 "gradientMode": "opacity",
1294 "hideFrom": {
1295 "legend": false,
1296 "tooltip": false,
1297 "viz": false
1298 },
1299 "lineInterpolation": "linear",
1300 "lineStyle": {
1301 "fill": "solid"
1302 },
1303 "lineWidth": 2,
1304 "pointSize": 5,
1305 "scaleDistribution": {
1306 "type": "linear"
1307 },
1308 "showPoints": "auto",
1309 "spanNulls": false,
1310 "stacking": {
1311 "group": "A",
1312 "mode": "none"
1313 },
1314 "thresholdsStyle": {
1315 "mode": "off"
1316 }
1317 },
1318 "mappings": [],
1319 "thresholds": {
1320 "mode": "absolute",
1321 "steps": [
1322 {
1323 "color": "green"
1324 },
1325 {
1326 "color": "red",
1327 "value": 80
1328 }
1329 ]
1330 },
1331 "unit": "percent"
1332 },
1333 "overrides": []
1334 },
1335 "gridPos": {
1336 "h": 8,
1337 "w": 12,
1338 "x": 0,
1339 "y": 16
1340 },
1341 "id": 135,
1342 "options": {
1343 "legend": {
1344 "calcs": [
1345 "last"
1346 ],
1347 "displayMode": "list",
1348 "placement": "bottom",
1349 "showLegend": true
1350 },
1351 "tooltip": {
1352 "mode": "single",
1353 "sort": "none"
1354 }
1355 },
1356 "targets": [
1357 {
1358 "datasource": {
1359 "type": "prometheus",
1360 "uid": "t-6Lw3i4z"
1361 },
1362 "editorMode": "code",
1363 "expr": "100 - (avg by (instance) (rate(node_cpu_seconds_total{instance!=\"proxmox.home.local:9100\",mode=\"idle\"}[1m])) * 100)",
1364 "instant": false,
1365 "interval": "",
1366 "legendFormat": "{{instance}}",
1367 "range": true,
1368 "refId": "A"
1369 }
1370 ],
1371 "title": "VM CPU Usage",
1372 "type": "timeseries"
1373 },
1374 {
1375 "datasource": {
1376 "type": "prometheus",
1377 "uid": "t-6Lw3i4z"
1378 },
1379 "fieldConfig": {
1380 "defaults": {
1381 "color": {
1382 "mode": "palette-classic"
1383 },
1384 "custom": {
1385 "axisCenteredZero": false,
1386 "axisColorMode": "text",
1387 "axisLabel": "",
1388 "axisPlacement": "left",
1389 "barAlignment": 0,
1390 "drawStyle": "line",
1391 "fillOpacity": 10,
1392 "gradientMode": "opacity",
1393 "hideFrom": {
1394 "legend": false,
1395 "tooltip": false,
1396 "viz": false
1397 },
1398 "lineInterpolation": "linear",
1399 "lineStyle": {
1400 "fill": "solid"
1401 },
1402 "lineWidth": 2,
1403 "pointSize": 5,
1404 "scaleDistribution": {
1405 "type": "linear"
1406 },
1407 "showPoints": "auto",
1408 "spanNulls": false,
1409 "stacking": {
1410 "group": "A",
1411 "mode": "none"
1412 },
1413 "thresholdsStyle": {
1414 "mode": "off"
1415 }
1416 },
1417 "mappings": [],
1418 "thresholds": {
1419 "mode": "absolute",
1420 "steps": [
1421 {
1422 "color": "green"
1423 },
1424 {
1425 "color": "red",
1426 "value": 80
1427 }
1428 ]
1429 },
1430 "unit": "decbytes"
1431 },
1432 "overrides": []
1433 },
1434 "gridPos": {
1435 "h": 8,
1436 "w": 12,
1437 "x": 12,
1438 "y": 16
1439 },
1440 "id": 134,
1441 "options": {
1442 "legend": {
1443 "calcs": [
1444 "last"
1445 ],
1446 "displayMode": "list",
1447 "placement": "bottom",
1448 "showLegend": true
1449 },
1450 "tooltip": {
1451 "mode": "single",
1452 "sort": "none"
1453 }
1454 },
1455 "targets": [
1456 {
1457 "datasource": {
1458 "type": "prometheus",
1459 "uid": "t-6Lw3i4z"
1460 },
1461 "editorMode": "builder",
1462 "expr": "node_memory_MemTotal_bytes{instance!=\"proxmox.home.local:9100\"} - node_memory_MemFree_bytes{instance!=\"proxmox.home.local:9100\"} - (node_memory_Cached_bytes{instance!=\"proxmox.home.local:9100\"} + node_memory_Buffers_bytes{instance!=\"proxmox.home.local:9100\"} + node_memory_SReclaimable_bytes{instance!=\"proxmox.home.local:9100\"})",
1463 "instant": false,
1464 "interval": "",
1465 "legendFormat": "{{instance}}",
1466 "range": true,
1467 "refId": "A"
1468 }
1469 ],
1470 "title": "VM Memory Usage",
1471 "type": "timeseries"
1472 },
1473 {
1474 "datasource": {
1475 "type": "prometheus",
1476 "uid": "t-6Lw3i4z"
1477 },
1478 "description": "Disk space used of all filesystems mounted",
1479 "fieldConfig": {
1480 "defaults": {
1481 "color": {
1482 "mode": "palette-classic"
1483 },
1484 "custom": {
1485 "axisCenteredZero": false,
1486 "axisColorMode": "text",
1487 "axisLabel": "",
1488 "axisPlacement": "auto",
1489 "barAlignment": 0,
1490 "drawStyle": "line",
1491 "fillOpacity": 40,
1492 "gradientMode": "opacity",
1493 "hideFrom": {
1494 "legend": false,
1495 "tooltip": false,
1496 "viz": false
1497 },
1498 "lineInterpolation": "linear",
1499 "lineStyle": {
1500 "fill": "solid"
1501 },
1502 "lineWidth": 2,
1503 "pointSize": 5,
1504 "scaleDistribution": {
1505 "type": "linear"
1506 },
1507 "showPoints": "never",
1508 "spanNulls": true,
1509 "stacking": {
1510 "group": "A",
1511 "mode": "none"
1512 },
1513 "thresholdsStyle": {
1514 "mode": "off"
1515 }
1516 },
1517 "links": [],
1518 "mappings": [],
1519 "max": 100,
1520 "min": 0,
1521 "thresholds": {
1522 "mode": "absolute",
1523 "steps": [
1524 {
1525 "color": "green"
1526 },
1527 {
1528 "color": "red",
1529 "value": 80
1530 }
1531 ]
1532 },
1533 "unit": "percent"
1534 },
1535 "overrides": []
1536 },
1537 "gridPos": {
1538 "h": 7,
1539 "w": 24,
1540 "x": 0,
1541 "y": 24
1542 },
1543 "id": 125,
1544 "links": [],
1545 "options": {
1546 "legend": {
1547 "calcs": [
1548 "lastNotNull"
1549 ],
1550 "displayMode": "table",
1551 "placement": "right",
1552 "showLegend": true,
1553 "sortBy": "Last *",
1554 "sortDesc": true
1555 },
1556 "tooltip": {
1557 "mode": "multi",
1558 "sort": "none"
1559 }
1560 },
1561 "pluginVersion": "9.3.2",
1562 "targets": [
1563 {
1564 "datasource": {
1565 "type": "prometheus",
1566 "uid": "t-6Lw3i4z"
1567 },
1568 "editorMode": "code",
1569 "expr": "100 - ((node_filesystem_avail_bytes{instance=~\".+\",device!~'rootfs'} * 100) / node_filesystem_size_bytes{instance=~\".+\",device!~'rootfs'})",
1570 "format": "time_series",
1571 "interval": "",
1572 "intervalFactor": 1,
1573 "legendFormat": "{{instance}} - {{mountpoint}}",
1574 "range": true,
1575 "refId": "A",
1576 "step": 240
1577 }
1578 ],
1579 "title": "VMs Disk Space Used",
1580 "type": "timeseries"
1581 },
1582 {
1583 "datasource": {
1584 "type": "influxdb",
1585 "uid": "1Y6tKgWVz"
1586 },
1587 "fieldConfig": {
1588 "defaults": {
1589 "color": {
1590 "mode": "palette-classic"
1591 },
1592 "custom": {
1593 "axisCenteredZero": false,
1594 "axisColorMode": "text",
1595 "axisLabel": "",
1596 "axisPlacement": "auto",
1597 "barAlignment": 0,
1598 "drawStyle": "line",
1599 "fillOpacity": 10,
1600 "gradientMode": "none",
1601 "hideFrom": {
1602 "legend": false,
1603 "tooltip": false,
1604 "viz": false
1605 },
1606 "lineInterpolation": "linear",
1607 "lineWidth": 1,
1608 "pointSize": 5,
1609 "scaleDistribution": {
1610 "type": "linear"
1611 },
1612 "showPoints": "never",
1613 "spanNulls": false,
1614 "stacking": {
1615 "group": "A",
1616 "mode": "none"
1617 },
1618 "thresholdsStyle": {
1619 "mode": "off"
1620 }
1621 },
1622 "mappings": [],
1623 "thresholds": {
1624 "mode": "absolute",
1625 "steps": [
1626 {
1627 "color": "green"
1628 },
1629 {
1630 "color": "red",
1631 "value": 80
1632 }
1633 ]
1634 },
1635 "unit": "bps"
1636 },
1637 "overrides": []
1638 },
1639 "gridPos": {
1640 "h": 6,
1641 "w": 12,
1642 "x": 0,
1643 "y": 31
1644 },
1645 "id": 41,
1646 "interval": "",
1647 "links": [],
1648 "options": {
1649 "legend": {
1650 "calcs": [
1651 "mean",
1652 "lastNotNull"
1653 ],
1654 "displayMode": "table",
1655 "placement": "right",
1656 "showLegend": true,
1657 "sortBy": "Mean",
1658 "sortDesc": true
1659 },
1660 "tooltip": {
1661 "mode": "multi",
1662 "sort": "none"
1663 }
1664 },
1665 "pluginVersion": "9.3.2",
1666 "targets": [
1667 {
1668 "alias": "$tag_host",
1669 "datasource": {
1670 "type": "influxdb",
1671 "uid": "1Y6tKgWVz"
1672 },
1673 "groupBy": [
1674 {
1675 "params": [
1676 "$__interval"
1677 ],
1678 "type": "time"
1679 },
1680 {
1681 "params": [
1682 "host"
1683 ],
1684 "type": "tag"
1685 },
1686 {
1687 "params": [
1688 "null"
1689 ],
1690 "type": "fill"
1691 }
1692 ],
1693 "measurement": "system",
1694 "orderByTime": "ASC",
1695 "policy": "default",
1696 "query": "SELECT non_negative_derivative(mean(\"netin\"), 10s) FROM \"system\" WHERE (\"object\" = 'qemu') AND $timeFilter GROUP BY time($__interval), \"host\" fill(null)",
1697 "rawQuery": true,
1698 "refId": "A",
1699 "resultFormat": "time_series",
1700 "select": [
1701 [
1702 {
1703 "params": [
1704 "netin"
1705 ],
1706 "type": "field"
1707 },
1708 {
1709 "params": [],
1710 "type": "mean"
1711 },
1712 {
1713 "params": [
1714 "10s"
1715 ],
1716 "type": "non_negative_derivative"
1717 }
1718 ]
1719 ],
1720 "tags": [
1721 {
1722 "key": "nodename",
1723 "operator": "=~",
1724 "value": "/^$server$/"
1725 },
1726 {
1727 "condition": "AND",
1728 "key": "object",
1729 "operator": "=",
1730 "value": "qemu"
1731 }
1732 ]
1733 }
1734 ],
1735 "title": "VMs Traffic In",
1736 "type": "timeseries"
1737 },
1738 {
1739 "datasource": {
1740 "type": "influxdb",
1741 "uid": "1Y6tKgWVz"
1742 },
1743 "fieldConfig": {
1744 "defaults": {
1745 "color": {
1746 "mode": "palette-classic"
1747 },
1748 "custom": {
1749 "axisCenteredZero": false,
1750 "axisColorMode": "text",
1751 "axisLabel": "",
1752 "axisPlacement": "auto",
1753 "barAlignment": 0,
1754 "drawStyle": "line",
1755 "fillOpacity": 10,
1756 "gradientMode": "none",
1757 "hideFrom": {
1758 "legend": false,
1759 "tooltip": false,
1760 "viz": false
1761 },
1762 "lineInterpolation": "linear",
1763 "lineWidth": 1,
1764 "pointSize": 5,
1765 "scaleDistribution": {
1766 "type": "linear"
1767 },
1768 "showPoints": "never",
1769 "spanNulls": false,
1770 "stacking": {
1771 "group": "A",
1772 "mode": "none"
1773 },
1774 "thresholdsStyle": {
1775 "mode": "off"
1776 }
1777 },
1778 "mappings": [],
1779 "thresholds": {
1780 "mode": "absolute",
1781 "steps": [
1782 {
1783 "color": "green"
1784 },
1785 {
1786 "color": "red",
1787 "value": 80
1788 }
1789 ]
1790 },
1791 "unit": "bps"
1792 },
1793 "overrides": []
1794 },
1795 "gridPos": {
1796 "h": 6,
1797 "w": 12,
1798 "x": 12,
1799 "y": 31
1800 },
1801 "id": 43,
1802 "interval": "",
1803 "links": [],
1804 "options": {
1805 "legend": {
1806 "calcs": [
1807 "mean",
1808 "lastNotNull"
1809 ],
1810 "displayMode": "table",
1811 "placement": "right",
1812 "showLegend": true,
1813 "sortBy": "Mean",
1814 "sortDesc": true
1815 },
1816 "tooltip": {
1817 "mode": "multi",
1818 "sort": "none"
1819 }
1820 },
1821 "pluginVersion": "9.3.2",
1822 "targets": [
1823 {
1824 "alias": "$tag_host",
1825 "datasource": {
1826 "type": "influxdb",
1827 "uid": "1Y6tKgWVz"
1828 },
1829 "groupBy": [
1830 {
1831 "params": [
1832 "$__interval"
1833 ],
1834 "type": "time"
1835 },
1836 {
1837 "params": [
1838 "host"
1839 ],
1840 "type": "tag"
1841 },
1842 {
1843 "params": [
1844 "null"
1845 ],
1846 "type": "fill"
1847 }
1848 ],
1849 "measurement": "system",
1850 "orderByTime": "ASC",
1851 "policy": "default",
1852 "query": "SELECT non_negative_derivative(mean(\"netout\"), 10s) FROM \"system\" WHERE (\"object\" = 'qemu') AND $timeFilter GROUP BY time($__interval), \"host\" fill(null)",
1853 "rawQuery": true,
1854 "refId": "A",
1855 "resultFormat": "time_series",
1856 "select": [
1857 [
1858 {
1859 "params": [
1860 "netout"
1861 ],
1862 "type": "field"
1863 },
1864 {
1865 "params": [],
1866 "type": "mean"
1867 },
1868 {
1869 "params": [
1870 "10s"
1871 ],
1872 "type": "non_negative_derivative"
1873 }
1874 ]
1875 ],
1876 "tags": [
1877 {
1878 "key": "nodename",
1879 "operator": "=~",
1880 "value": "/^$server$/"
1881 },
1882 {
1883 "condition": "AND",
1884 "key": "object",
1885 "operator": "=",
1886 "value": "qemu"
1887 }
1888 ]
1889 }
1890 ],
1891 "title": "VMs Traffic Out",
1892 "type": "timeseries"
1893 },
1894 {
1895 "datasource": {
1896 "type": "prometheus",
1897 "uid": "t-6Lw3i4z"
1898 },
1899 "fieldConfig": {
1900 "defaults": {
1901 "color": {
1902 "mode": "palette-classic"
1903 },
1904 "custom": {
1905 "axisCenteredZero": false,
1906 "axisColorMode": "text",
1907 "axisLabel": "",
1908 "axisPlacement": "auto",
1909 "barAlignment": 0,
1910 "drawStyle": "line",
1911 "fillOpacity": 50,
1912 "gradientMode": "none",
1913 "hideFrom": {
1914 "legend": false,
1915 "tooltip": false,
1916 "viz": false
1917 },
1918 "lineInterpolation": "linear",
1919 "lineWidth": 1,
1920 "pointSize": 5,
1921 "scaleDistribution": {
1922 "type": "linear"
1923 },
1924 "showPoints": "never",
1925 "spanNulls": false,
1926 "stacking": {
1927 "group": "A",
1928 "mode": "normal"
1929 },
1930 "thresholdsStyle": {
1931 "mode": "off"
1932 }
1933 },
1934 "mappings": [],
1935 "thresholds": {
1936 "mode": "absolute",
1937 "steps": [
1938 {
1939 "color": "green"
1940 },
1941 {
1942 "color": "red",
1943 "value": 80
1944 }
1945 ]
1946 },
1947 "unit": "percent"
1948 },
1949 "overrides": []
1950 },
1951 "gridPos": {
1952 "h": 7,
1953 "w": 12,
1954 "x": 0,
1955 "y": 37
1956 },
1957 "id": 25,
1958 "links": [],
1959 "options": {
1960 "legend": {
1961 "calcs": [
1962 "mean",
1963 "last"
1964 ],
1965 "displayMode": "table",
1966 "placement": "right",
1967 "showLegend": true,
1968 "sortBy": "Mean",
1969 "sortDesc": true
1970 },
1971 "tooltip": {
1972 "mode": "multi",
1973 "sort": "none"
1974 }
1975 },
1976 "pluginVersion": "9.3.2",
1977 "targets": [
1978 {
1979 "datasource": {
1980 "type": "prometheus",
1981 "uid": "t-6Lw3i4z"
1982 },
1983 "editorMode": "code",
1984 "expr": "sum(rate(container_cpu_usage_seconds_total{name=~\".+\"}[$__interval])) by (name) * 100",
1985 "hide": false,
1986 "interval": "",
1987 "intervalFactor": 2,
1988 "legendFormat": "{{name}}",
1989 "metric": "",
1990 "range": true,
1991 "refId": "F",
1992 "step": 240
1993 }
1994 ],
1995 "title": "Container CPU Usage",
1996 "type": "timeseries"
1997 },
1998 {
1999 "datasource": {
2000 "type": "prometheus",
2001 "uid": "t-6Lw3i4z"
2002 },
2003 "fieldConfig": {
2004 "defaults": {
2005 "color": {
2006 "mode": "palette-classic"
2007 },
2008 "custom": {
2009 "axisCenteredZero": false,
2010 "axisColorMode": "text",
2011 "axisLabel": "",
2012 "axisPlacement": "auto",
2013 "barAlignment": 0,
2014 "drawStyle": "line",
2015 "fillOpacity": 30,
2016 "gradientMode": "none",
2017 "hideFrom": {
2018 "legend": false,
2019 "tooltip": false,
2020 "viz": false
2021 },
2022 "lineInterpolation": "linear",
2023 "lineWidth": 2,
2024 "pointSize": 5,
2025 "scaleDistribution": {
2026 "type": "linear"
2027 },
2028 "showPoints": "never",
2029 "spanNulls": false,
2030 "stacking": {
2031 "group": "A",
2032 "mode": "normal"
2033 },
2034 "thresholdsStyle": {
2035 "mode": "off"
2036 }
2037 },
2038 "mappings": [],
2039 "thresholds": {
2040 "mode": "absolute",
2041 "steps": [
2042 {
2043 "color": "green"
2044 },
2045 {
2046 "color": "red",
2047 "value": 80
2048 }
2049 ]
2050 },
2051 "unit": "bytes"
2052 },
2053 "overrides": []
2054 },
2055 "gridPos": {
2056 "h": 7,
2057 "w": 12,
2058 "x": 12,
2059 "y": 37
2060 },
2061 "id": 27,
2062 "links": [],
2063 "options": {
2064 "legend": {
2065 "calcs": [
2066 "mean",
2067 "last"
2068 ],
2069 "displayMode": "table",
2070 "placement": "right",
2071 "showLegend": true,
2072 "sortBy": "Last",
2073 "sortDesc": true
2074 },
2075 "tooltip": {
2076 "mode": "multi",
2077 "sort": "none"
2078 }
2079 },
2080 "pluginVersion": "9.3.2",
2081 "targets": [
2082 {
2083 "datasource": {
2084 "type": "prometheus",
2085 "uid": "t-6Lw3i4z"
2086 },
2087 "expr": "sum(container_memory_rss{name=~\".+\"}) by (name)",
2088 "hide": false,
2089 "intervalFactor": 2,
2090 "legendFormat": "{{name}}",
2091 "refId": "A",
2092 "step": 240
2093 },
2094 {
2095 "datasource": {
2096 "type": "prometheus",
2097 "uid": "t-6Lw3i4z"
2098 },
2099 "expr": "container_memory_usage_bytes{name=~\".+\"}",
2100 "hide": true,
2101 "intervalFactor": 2,
2102 "legendFormat": "{{name}}",
2103 "refId": "B",
2104 "step": 240
2105 }
2106 ],
2107 "title": "Container Memory Usage",
2108 "type": "timeseries"
2109 },
2110 {
2111 "datasource": {
2112 "type": "prometheus",
2113 "uid": "t-6Lw3i4z"
2114 },
2115 "fieldConfig": {
2116 "defaults": {
2117 "color": {
2118 "mode": "palette-classic"
2119 },
2120 "custom": {
2121 "axisCenteredZero": false,
2122 "axisColorMode": "text",
2123 "axisLabel": "",
2124 "axisPlacement": "auto",
2125 "barAlignment": 0,
2126 "drawStyle": "line",
2127 "fillOpacity": 10,
2128 "gradientMode": "none",
2129 "hideFrom": {
2130 "legend": false,
2131 "tooltip": false,
2132 "viz": false
2133 },
2134 "lineInterpolation": "linear",
2135 "lineWidth": 2,
2136 "pointSize": 5,
2137 "scaleDistribution": {
2138 "type": "linear"
2139 },
2140 "showPoints": "never",
2141 "spanNulls": false,
2142 "stacking": {
2143 "group": "A",
2144 "mode": "none"
2145 },
2146 "thresholdsStyle": {
2147 "mode": "off"
2148 }
2149 },
2150 "mappings": [],
2151 "thresholds": {
2152 "mode": "absolute",
2153 "steps": [
2154 {
2155 "color": "green"
2156 },
2157 {
2158 "color": "red",
2159 "value": 80
2160 }
2161 ]
2162 },
2163 "unit": "Bps"
2164 },
2165 "overrides": []
2166 },
2167 "gridPos": {
2168 "h": 7,
2169 "w": 12,
2170 "x": 0,
2171 "y": 44
2172 },
2173 "id": 45,
2174 "links": [],
2175 "options": {
2176 "legend": {
2177 "calcs": [
2178 "mean",
2179 "last"
2180 ],
2181 "displayMode": "table",
2182 "placement": "right",
2183 "showLegend": true
2184 },
2185 "tooltip": {
2186 "mode": "multi",
2187 "sort": "none"
2188 }
2189 },
2190 "pluginVersion": "9.3.2",
2191 "targets": [
2192 {
2193 "datasource": {
2194 "type": "prometheus",
2195 "uid": "t-6Lw3i4z"
2196 },
2197 "editorMode": "code",
2198 "expr": "sum(rate(container_network_receive_bytes_total{name=~\".+\"}[$__interval])) by (name)",
2199 "intervalFactor": 2,
2200 "legendFormat": "{{name}}",
2201 "range": true,
2202 "refId": "A",
2203 "step": 240
2204 },
2205 {
2206 "datasource": {
2207 "type": "prometheus",
2208 "uid": "t-6Lw3i4z"
2209 },
2210 "expr": "- rate(container_network_transmit_bytes_total{name=~\".+\"}[$interval])",
2211 "hide": true,
2212 "intervalFactor": 2,
2213 "legendFormat": "{{name}}",
2214 "refId": "B",
2215 "step": 10
2216 }
2217 ],
2218 "title": "Container Traffic In",
2219 "type": "timeseries"
2220 },
2221 {
2222 "datasource": {
2223 "type": "prometheus",
2224 "uid": "t-6Lw3i4z"
2225 },
2226 "fieldConfig": {
2227 "defaults": {
2228 "color": {
2229 "mode": "palette-classic"
2230 },
2231 "custom": {
2232 "axisCenteredZero": false,
2233 "axisColorMode": "text",
2234 "axisLabel": "",
2235 "axisPlacement": "auto",
2236 "barAlignment": 0,
2237 "drawStyle": "line",
2238 "fillOpacity": 10,
2239 "gradientMode": "none",
2240 "hideFrom": {
2241 "legend": false,
2242 "tooltip": false,
2243 "viz": false
2244 },
2245 "lineInterpolation": "linear",
2246 "lineWidth": 2,
2247 "pointSize": 5,
2248 "scaleDistribution": {
2249 "type": "linear"
2250 },
2251 "showPoints": "never",
2252 "spanNulls": false,
2253 "stacking": {
2254 "group": "A",
2255 "mode": "none"
2256 },
2257 "thresholdsStyle": {
2258 "mode": "off"
2259 }
2260 },
2261 "mappings": [],
2262 "thresholds": {
2263 "mode": "absolute",
2264 "steps": [
2265 {
2266 "color": "green"
2267 },
2268 {
2269 "color": "red",
2270 "value": 80
2271 }
2272 ]
2273 },
2274 "unit": "Bps"
2275 },
2276 "overrides": []
2277 },
2278 "gridPos": {
2279 "h": 7,
2280 "w": 12,
2281 "x": 12,
2282 "y": 44
2283 },
2284 "id": 47,
2285 "links": [],
2286 "options": {
2287 "legend": {
2288 "calcs": [
2289 "mean",
2290 "last"
2291 ],
2292 "displayMode": "table",
2293 "placement": "right",
2294 "showLegend": true
2295 },
2296 "tooltip": {
2297 "mode": "multi",
2298 "sort": "none"
2299 }
2300 },
2301 "pluginVersion": "9.3.2",
2302 "targets": [
2303 {
2304 "datasource": {
2305 "type": "prometheus",
2306 "uid": "t-6Lw3i4z"
2307 },
2308 "editorMode": "code",
2309 "expr": "sum(rate(container_network_transmit_bytes_total{name=~\".+\"}[$__interval])) by (name)",
2310 "intervalFactor": 2,
2311 "legendFormat": "{{name}}",
2312 "range": true,
2313 "refId": "A",
2314 "step": 240
2315 },
2316 {
2317 "datasource": {
2318 "type": "prometheus",
2319 "uid": "t-6Lw3i4z"
2320 },
2321 "expr": "rate(container_network_transmit_bytes_total{id=\"/\"}[$interval])",
2322 "hide": true,
2323 "intervalFactor": 2,
2324 "legendFormat": "",
2325 "refId": "B",
2326 "step": 10
2327 }
2328 ],
2329 "title": "Container Traffic Out",
2330 "type": "timeseries"
2331 },
2332 {
2333 "collapsed": true,
2334 "gridPos": {
2335 "h": 1,
2336 "w": 24,
2337 "x": 0,
2338 "y": 51
2339 },
2340 "id": 127,
2341 "panels": [
2342 {
2343 "datasource": {
2344 "type": "loki",
2345 "uid": "AzGHyE5Vz"
2346 },
2347 "gridPos": {
2348 "h": 15,
2349 "w": 24,
2350 "x": 0,
2351 "y": 11
2352 },
2353 "id": 131,
2354 "options": {
2355 "dedupStrategy": "signature",
2356 "enableLogDetails": true,
2357 "prettifyLogMessage": false,
2358 "showCommonLabels": false,
2359 "showLabels": true,
2360 "showTime": false,
2361 "sortOrder": "Ascending",
2362 "wrapLogMessage": false
2363 },
2364 "targets": [
2365 {
2366 "datasource": {
2367 "type": "loki",
2368 "uid": "AzGHyE5Vz"
2369 },
2370 "editorMode": "code",
2371 "expr": "{container_name=~\"$container_name\"}",
2372 "queryType": "range",
2373 "refId": "A"
2374 }
2375 ],
2376 "title": "Container Logs - \"$container_name\"",
2377 "type": "logs"
2378 }
2379 ],
2380 "title": "Container Logs",
2381 "type": "row"
2382 },
2383 {
2384 "collapsed": true,
2385 "gridPos": {
2386 "h": 1,
2387 "w": 24,
2388 "x": 0,
2389 "y": 52
2390 },
2391 "id": 133,
2392 "panels": [
2393 {
2394 "datasource": {
2395 "type": "loki",
2396 "uid": "AzGHyE5Vz"
2397 },
2398 "gridPos": {
2399 "h": 14,
2400 "w": 24,
2401 "x": 0,
2402 "y": 9
2403 },
2404 "id": 130,
2405 "options": {
2406 "dedupStrategy": "signature",
2407 "enableLogDetails": true,
2408 "prettifyLogMessage": true,
2409 "showCommonLabels": false,
2410 "showLabels": false,
2411 "showTime": true,
2412 "sortOrder": "Ascending",
2413 "wrapLogMessage": false
2414 },
2415 "targets": [
2416 {
2417 "datasource": {
2418 "type": "loki",
2419 "uid": "AzGHyE5Vz"
2420 },
2421 "editorMode": "code",
2422 "expr": "{job=\"systemd-journal\",hostname=~\"$vm_name\",unit=~\"$vm_service\"} | logfmt | level = `error`",
2423 "queryType": "range",
2424 "refId": "A"
2425 }
2426 ],
2427 "title": "VM Errors - \"$vm_name\" - \"$vm_service\"",
2428 "type": "logs"
2429 }
2430 ],
2431 "title": "VM Logs",
2432 "type": "row"
2433 },
2434 {
2435 "collapsed": true,
2436 "gridPos": {
2437 "h": 1,
2438 "w": 24,
2439 "x": 0,
2440 "y": 53
2441 },
2442 "id": 51,
2443 "panels": [
2444 {
2445 "datasource": {
2446 "type": "prometheus",
2447 "uid": "t-6Lw3i4z"
2448 },
2449 "fieldConfig": {
2450 "defaults": {
2451 "color": {
2452 "mode": "palette-classic"
2453 },
2454 "custom": {
2455 "axisCenteredZero": false,
2456 "axisColorMode": "text",
2457 "axisLabel": "",
2458 "axisPlacement": "auto",
2459 "barAlignment": 0,
2460 "drawStyle": "line",
2461 "fillOpacity": 10,
2462 "gradientMode": "none",
2463 "hideFrom": {
2464 "legend": false,
2465 "tooltip": false,
2466 "viz": false
2467 },
2468 "lineInterpolation": "linear",
2469 "lineWidth": 1,
2470 "pointSize": 5,
2471 "scaleDistribution": {
2472 "type": "linear"
2473 },
2474 "showPoints": "never",
2475 "spanNulls": false,
2476 "stacking": {
2477 "group": "A",
2478 "mode": "none"
2479 },
2480 "thresholdsStyle": {
2481 "mode": "off"
2482 }
2483 },
2484 "mappings": [],
2485 "thresholds": {
2486 "mode": "absolute",
2487 "steps": [
2488 {
2489 "color": "green"
2490 },
2491 {
2492 "color": "red",
2493 "value": 80
2494 }
2495 ]
2496 },
2497 "unit": "short"
2498 },
2499 "overrides": [
2500 {
2501 "matcher": {
2502 "id": "byName",
2503 "options": "pihole"
2504 },
2505 "properties": [
2506 {
2507 "id": "color",
2508 "value": {
2509 "fixedColor": "super-light-blue",
2510 "mode": "fixed"
2511 }
2512 }
2513 ]
2514 }
2515 ]
2516 },
2517 "gridPos": {
2518 "h": 5,
2519 "w": 8,
2520 "x": 0,
2521 "y": 50
2522 },
2523 "id": 53,
2524 "links": [],
2525 "options": {
2526 "legend": {
2527 "calcs": [],
2528 "displayMode": "list",
2529 "placement": "bottom",
2530 "showLegend": false
2531 },
2532 "tooltip": {
2533 "mode": "multi",
2534 "sort": "none"
2535 }
2536 },
2537 "pluginVersion": "9.3.2",
2538 "targets": [
2539 {
2540 "datasource": {
2541 "type": "prometheus",
2542 "uid": "t-6Lw3i4z"
2543 },
2544 "editorMode": "builder",
2545 "expr": "pihole_dns_queries_all_types{hostname=~\"192.168.20.34\"}",
2546 "interval": "",
2547 "legendFormat": "{{ job }}",
2548 "range": true,
2549 "refId": "A"
2550 }
2551 ],
2552 "title": "DNS queries all of types",
2553 "transparent": true,
2554 "type": "timeseries"
2555 },
2556 {
2557 "datasource": {
2558 "type": "prometheus",
2559 "uid": "t-6Lw3i4z"
2560 },
2561 "fieldConfig": {
2562 "defaults": {
2563 "color": {
2564 "mode": "palette-classic"
2565 },
2566 "custom": {
2567 "axisCenteredZero": false,
2568 "axisColorMode": "text",
2569 "axisLabel": "",
2570 "axisPlacement": "auto",
2571 "barAlignment": 0,
2572 "drawStyle": "line",
2573 "fillOpacity": 10,
2574 "gradientMode": "none",
2575 "hideFrom": {
2576 "legend": false,
2577 "tooltip": false,
2578 "viz": false
2579 },
2580 "lineInterpolation": "linear",
2581 "lineWidth": 1,
2582 "pointSize": 5,
2583 "scaleDistribution": {
2584 "type": "linear"
2585 },
2586 "showPoints": "never",
2587 "spanNulls": false,
2588 "stacking": {
2589 "group": "A",
2590 "mode": "none"
2591 },
2592 "thresholdsStyle": {
2593 "mode": "off"
2594 }
2595 },
2596 "mappings": [],
2597 "thresholds": {
2598 "mode": "absolute",
2599 "steps": [
2600 {
2601 "color": "green"
2602 },
2603 {
2604 "color": "red",
2605 "value": 80
2606 }
2607 ]
2608 },
2609 "unit": "short"
2610 },
2611 "overrides": [
2612 {
2613 "matcher": {
2614 "id": "byName",
2615 "options": "pihole"
2616 },
2617 "properties": [
2618 {
2619 "id": "color",
2620 "value": {
2621 "fixedColor": "light-red",
2622 "mode": "fixed"
2623 }
2624 }
2625 ]
2626 }
2627 ]
2628 },
2629 "gridPos": {
2630 "h": 5,
2631 "w": 8,
2632 "x": 8,
2633 "y": 50
2634 },
2635 "id": 55,
2636 "links": [],
2637 "options": {
2638 "legend": {
2639 "calcs": [],
2640 "displayMode": "list",
2641 "placement": "bottom",
2642 "showLegend": false
2643 },
2644 "tooltip": {
2645 "mode": "multi",
2646 "sort": "none"
2647 }
2648 },
2649 "pluginVersion": "9.3.2",
2650 "targets": [
2651 {
2652 "datasource": {
2653 "type": "prometheus",
2654 "uid": "t-6Lw3i4z"
2655 },
2656 "editorMode": "builder",
2657 "expr": "pihole_ads_blocked_today{hostname=~\"192.168.20.34\"}",
2658 "instant": false,
2659 "interval": "",
2660 "legendFormat": "{{ job }}",
2661 "refId": "A"
2662 }
2663 ],
2664 "title": "Ads blocked (today)",
2665 "transparent": true,
2666 "type": "timeseries"
2667 },
2668 {
2669 "aliasColors": {
2670 "pihole": "super-light-red"
2671 },
2672 "bars": false,
2673 "dashLength": 10,
2674 "dashes": false,
2675 "datasource": {
2676 "type": "prometheus",
2677 "uid": "t-6Lw3i4z"
2678 },
2679 "fill": 1,
2680 "fillGradient": 0,
2681 "gridPos": {
2682 "h": 5,
2683 "w": 8,
2684 "x": 16,
2685 "y": 50
2686 },
2687 "hiddenSeries": false,
2688 "id": 57,
2689 "legend": {
2690 "avg": false,
2691 "current": false,
2692 "max": false,
2693 "min": false,
2694 "show": true,
2695 "total": false,
2696 "values": false
2697 },
2698 "lines": true,
2699 "linewidth": 1,
2700 "links": [],
2701 "nullPointMode": "null",
2702 "options": {
2703 "alertThreshold": true
2704 },
2705 "percentage": false,
2706 "pluginVersion": "9.3.2",
2707 "pointradius": 2,
2708 "points": false,
2709 "renderer": "flot",
2710 "seriesOverrides": [],
2711 "spaceLength": 10,
2712 "stack": false,
2713 "steppedLine": false,
2714 "targets": [
2715 {
2716 "datasource": {
2717 "type": "prometheus",
2718 "uid": "t-6Lw3i4z"
2719 },
2720 "expr": "pihole_ads_percentage_today",
2721 "interval": "",
2722 "legendFormat": "{{ job }}",
2723 "refId": "A"
2724 }
2725 ],
2726 "thresholds": [],
2727 "timeRegions": [],
2728 "title": "% of ads blocked",
2729 "tooltip": {
2730 "shared": true,
2731 "sort": 0,
2732 "value_type": "individual"
2733 },
2734 "transparent": true,
2735 "type": "graph",
2736 "xaxis": {
2737 "mode": "time",
2738 "show": true,
2739 "values": []
2740 },
2741 "yaxes": [
2742 {
2743 "format": "percent",
2744 "logBase": 1,
2745 "show": true
2746 },
2747 {
2748 "format": "short",
2749 "logBase": 1,
2750 "show": true
2751 }
2752 ],
2753 "yaxis": {
2754 "align": false
2755 }
2756 },
2757 {
2758 "aliasColors": {},
2759 "bars": false,
2760 "dashLength": 10,
2761 "dashes": false,
2762 "datasource": {
2763 "type": "prometheus",
2764 "uid": "t-6Lw3i4z"
2765 },
2766 "fill": 1,
2767 "fillGradient": 0,
2768 "gridPos": {
2769 "h": 7,
2770 "w": 8,
2771 "x": 0,
2772 "y": 55
2773 },
2774 "hiddenSeries": false,
2775 "id": 71,
2776 "legend": {
2777 "avg": false,
2778 "current": false,
2779 "max": false,
2780 "min": false,
2781 "show": true,
2782 "total": false,
2783 "values": false
2784 },
2785 "lines": true,
2786 "linewidth": 1,
2787 "links": [],
2788 "nullPointMode": "null",
2789 "options": {
2790 "alertThreshold": true
2791 },
2792 "percentage": false,
2793 "pluginVersion": "9.3.2",
2794 "pointradius": 2,
2795 "points": false,
2796 "renderer": "flot",
2797 "seriesOverrides": [],
2798 "spaceLength": 10,
2799 "stack": false,
2800 "steppedLine": false,
2801 "targets": [
2802 {
2803 "datasource": {
2804 "type": "prometheus",
2805 "uid": "t-6Lw3i4z"
2806 },
2807 "expr": "pihole_querytypes",
2808 "interval": "",
2809 "legendFormat": "{{ type }}",
2810 "refId": "A"
2811 }
2812 ],
2813 "thresholds": [],
2814 "timeRegions": [],
2815 "title": "DNS Query types",
2816 "tooltip": {
2817 "shared": true,
2818 "sort": 0,
2819 "value_type": "individual"
2820 },
2821 "transparent": true,
2822 "type": "graph",
2823 "xaxis": {
2824 "mode": "time",
2825 "show": true,
2826 "values": []
2827 },
2828 "yaxes": [
2829 {
2830 "format": "percent",
2831 "logBase": 1,
2832 "show": true
2833 },
2834 {
2835 "format": "short",
2836 "logBase": 1,
2837 "show": true
2838 }
2839 ],
2840 "yaxis": {
2841 "align": false
2842 }
2843 },
2844 {
2845 "datasource": {
2846 "type": "prometheus",
2847 "uid": "t-6Lw3i4z"
2848 },
2849 "fieldConfig": {
2850 "defaults": {
2851 "color": {
2852 "mode": "palette-classic"
2853 },
2854 "custom": {
2855 "hideFrom": {
2856 "legend": false,
2857 "tooltip": false,
2858 "viz": false
2859 }
2860 },
2861 "mappings": []
2862 },
2863 "overrides": [
2864 {
2865 "__systemRef": "hideSeriesFrom",
2866 "matcher": {
2867 "id": "byNames",
2868 "options": {
2869 "mode": "exclude",
2870 "names": [
2871 "auth.chudnick.com",
2872 "chudnick.com",
2873 "drawio.chudnick.com",
2874 "git.chudnick.com",
2875 "ipasrv.home.local",
2876 "mail.chudnick.com",
2877 "monitoring.chudnick.com",
2878 "music.chudnick.com",
2879 "pihole.chudnick.com",
2880 "searxng.chudnick.com"
2881 ],
2882 "prefix": "All except:",
2883 "readOnly": true
2884 }
2885 },
2886 "properties": [
2887 {
2888 "id": "custom.hideFrom",
2889 "value": {
2890 "legend": false,
2891 "tooltip": false,
2892 "viz": true
2893 }
2894 }
2895 ]
2896 }
2897 ]
2898 },
2899 "gridPos": {
2900 "h": 7,
2901 "w": 4,
2902 "x": 8,
2903 "y": 55
2904 },
2905 "id": 59,
2906 "links": [],
2907 "maxDataPoints": 3,
2908 "options": {
2909 "legend": {
2910 "displayMode": "list",
2911 "placement": "bottom",
2912 "showLegend": true
2913 },
2914 "pieType": "pie",
2915 "reduceOptions": {
2916 "calcs": [
2917 "lastNotNull"
2918 ],
2919 "fields": "",
2920 "values": false
2921 },
2922 "tooltip": {
2923 "mode": "single",
2924 "sort": "none"
2925 }
2926 },
2927 "targets": [
2928 {
2929 "datasource": {
2930 "type": "prometheus",
2931 "uid": "t-6Lw3i4z"
2932 },
2933 "editorMode": "builder",
2934 "expr": "pihole_top_queries{hostname=~\"192.168.20.34\"}",
2935 "instant": true,
2936 "interval": "",
2937 "legendFormat": "{{ domain }}",
2938 "refId": "A"
2939 }
2940 ],
2941 "title": "Top queries by domain",
2942 "transparent": true,
2943 "type": "piechart"
2944 },
2945 {
2946 "datasource": {
2947 "type": "prometheus",
2948 "uid": "t-6Lw3i4z"
2949 },
2950 "fieldConfig": {
2951 "defaults": {
2952 "color": {
2953 "mode": "palette-classic"
2954 },
2955 "custom": {
2956 "hideFrom": {
2957 "legend": false,
2958 "tooltip": false,
2959 "viz": false
2960 }
2961 },
2962 "mappings": []
2963 },
2964 "overrides": []
2965 },
2966 "gridPos": {
2967 "h": 7,
2968 "w": 4,
2969 "x": 12,
2970 "y": 55
2971 },
2972 "id": 61,
2973 "links": [],
2974 "maxDataPoints": 3,
2975 "options": {
2976 "legend": {
2977 "displayMode": "list",
2978 "placement": "bottom",
2979 "showLegend": true
2980 },
2981 "pieType": "pie",
2982 "reduceOptions": {
2983 "calcs": [
2984 "lastNotNull"
2985 ],
2986 "fields": "",
2987 "values": false
2988 },
2989 "tooltip": {
2990 "mode": "single",
2991 "sort": "none"
2992 }
2993 },
2994 "targets": [
2995 {
2996 "datasource": {
2997 "type": "prometheus",
2998 "uid": "t-6Lw3i4z"
2999 },
3000 "editorMode": "builder",
3001 "expr": "pihole_top_ads{hostname=~\"192.168.20.34\"}",
3002 "interval": "",
3003 "legendFormat": "{{ domain }}",
3004 "range": true,
3005 "refId": "A"
3006 }
3007 ],
3008 "title": "Top ads by domain",
3009 "transparent": true,
3010 "type": "piechart"
3011 },
3012 {
3013 "datasource": {
3014 "type": "prometheus",
3015 "uid": "t-6Lw3i4z"
3016 },
3017 "fieldConfig": {
3018 "defaults": {
3019 "color": {
3020 "mode": "palette-classic"
3021 },
3022 "custom": {
3023 "hideFrom": {
3024 "legend": false,
3025 "tooltip": false,
3026 "viz": false
3027 }
3028 },
3029 "mappings": []
3030 },
3031 "overrides": []
3032 },
3033 "gridPos": {
3034 "h": 7,
3035 "w": 4,
3036 "x": 16,
3037 "y": 55
3038 },
3039 "id": 63,
3040 "links": [],
3041 "maxDataPoints": 3,
3042 "options": {
3043 "legend": {
3044 "displayMode": "list",
3045 "placement": "bottom",
3046 "showLegend": true
3047 },
3048 "pieType": "pie",
3049 "reduceOptions": {
3050 "calcs": [
3051 "lastNotNull"
3052 ],
3053 "fields": "",
3054 "values": false
3055 },
3056 "tooltip": {
3057 "mode": "single",
3058 "sort": "none"
3059 }
3060 },
3061 "pluginVersion": "6.1.4",
3062 "targets": [
3063 {
3064 "datasource": {
3065 "type": "prometheus",
3066 "uid": "t-6Lw3i4z"
3067 },
3068 "editorMode": "builder",
3069 "expr": "pihole_reply{hostname=\"192.168.20.34\"}",
3070 "interval": "",
3071 "legendFormat": "{{ type }}",
3072 "range": true,
3073 "refId": "A"
3074 }
3075 ],
3076 "title": "Replies by type",
3077 "transparent": true,
3078 "type": "piechart"
3079 },
3080 {
3081 "datasource": {
3082 "type": "prometheus",
3083 "uid": "t-6Lw3i4z"
3084 },
3085 "fieldConfig": {
3086 "defaults": {
3087 "color": {
3088 "mode": "thresholds"
3089 },
3090 "mappings": [
3091 {
3092 "options": {
3093 "0": {
3094 "text": "Disabled"
3095 },
3096 "1": {
3097 "text": "Enabled"
3098 }
3099 },
3100 "type": "value"
3101 }
3102 ],
3103 "thresholds": {
3104 "mode": "absolute",
3105 "steps": [
3106 {
3107 "color": "#d44a3a"
3108 },
3109 {
3110 "color": "rgba(237, 129, 40, 0.89)",
3111 "value": 0
3112 },
3113 {
3114 "color": "#299c46",
3115 "value": 1
3116 }
3117 ]
3118 },
3119 "unit": "none"
3120 },
3121 "overrides": []
3122 },
3123 "gridPos": {
3124 "h": 2,
3125 "w": 4,
3126 "x": 20,
3127 "y": 55
3128 },
3129 "id": 77,
3130 "links": [],
3131 "maxDataPoints": 100,
3132 "options": {
3133 "colorMode": "background",
3134 "graphMode": "none",
3135 "justifyMode": "auto",
3136 "orientation": "horizontal",
3137 "reduceOptions": {
3138 "calcs": [
3139 "lastNotNull"
3140 ],
3141 "fields": "",
3142 "values": false
3143 },
3144 "textMode": "auto"
3145 },
3146 "pluginVersion": "9.3.2",
3147 "targets": [
3148 {
3149 "datasource": {
3150 "type": "prometheus",
3151 "uid": "t-6Lw3i4z"
3152 },
3153 "editorMode": "builder",
3154 "expr": "pihole_status{hostname=~\"192.168.20.34\"}",
3155 "instant": true,
3156 "interval": "",
3157 "legendFormat": "{{ legend }}",
3158 "refId": "A"
3159 }
3160 ],
3161 "title": "Status",
3162 "transparent": true,
3163 "type": "stat"
3164 },
3165 {
3166 "datasource": {
3167 "type": "prometheus",
3168 "uid": "t-6Lw3i4z"
3169 },
3170 "fieldConfig": {
3171 "defaults": {
3172 "color": {
3173 "mode": "thresholds"
3174 },
3175 "mappings": [
3176 {
3177 "options": {
3178 "match": "null",
3179 "result": {
3180 "text": "N/A"
3181 }
3182 },
3183 "type": "special"
3184 }
3185 ],
3186 "thresholds": {
3187 "mode": "absolute",
3188 "steps": [
3189 {
3190 "color": "green"
3191 },
3192 {
3193 "color": "red",
3194 "value": 80
3195 }
3196 ]
3197 },
3198 "unit": "none"
3199 },
3200 "overrides": []
3201 },
3202 "gridPos": {
3203 "h": 2,
3204 "w": 4,
3205 "x": 20,
3206 "y": 57
3207 },
3208 "id": 67,
3209 "links": [],
3210 "maxDataPoints": 100,
3211 "options": {
3212 "colorMode": "none",
3213 "graphMode": "none",
3214 "justifyMode": "auto",
3215 "orientation": "horizontal",
3216 "reduceOptions": {
3217 "calcs": [
3218 "mean"
3219 ],
3220 "fields": "",
3221 "values": false
3222 },
3223 "textMode": "auto"
3224 },
3225 "pluginVersion": "9.3.2",
3226 "targets": [
3227 {
3228 "datasource": {
3229 "type": "prometheus",
3230 "uid": "t-6Lw3i4z"
3231 },
3232 "editorMode": "builder",
3233 "expr": "pihole_unique_domains{hostname=~\"192.168.20.34\"}",
3234 "interval": "",
3235 "legendFormat": "",
3236 "range": true,
3237 "refId": "A"
3238 }
3239 ],
3240 "title": "Unique domains",
3241 "transparent": true,
3242 "type": "stat"
3243 },
3244 {
3245 "datasource": {
3246 "type": "prometheus",
3247 "uid": "t-6Lw3i4z"
3248 },
3249 "fieldConfig": {
3250 "defaults": {
3251 "color": {
3252 "mode": "thresholds"
3253 },
3254 "mappings": [
3255 {
3256 "options": {
3257 "match": "null",
3258 "result": {
3259 "text": "N/A"
3260 }
3261 },
3262 "type": "special"
3263 }
3264 ],
3265 "thresholds": {
3266 "mode": "absolute",
3267 "steps": [
3268 {
3269 "color": "green"
3270 },
3271 {
3272 "color": "red",
3273 "value": 80
3274 }
3275 ]
3276 },
3277 "unit": "none"
3278 },
3279 "overrides": []
3280 },
3281 "gridPos": {
3282 "h": 2,
3283 "w": 4,
3284 "x": 20,
3285 "y": 59
3286 },
3287 "id": 65,
3288 "links": [],
3289 "maxDataPoints": 100,
3290 "options": {
3291 "colorMode": "none",
3292 "graphMode": "none",
3293 "justifyMode": "auto",
3294 "orientation": "horizontal",
3295 "reduceOptions": {
3296 "calcs": [
3297 "mean"
3298 ],
3299 "fields": "",
3300 "values": false
3301 },
3302 "textMode": "auto"
3303 },
3304 "pluginVersion": "9.3.2",
3305 "targets": [
3306 {
3307 "datasource": {
3308 "type": "prometheus",
3309 "uid": "t-6Lw3i4z"
3310 },
3311 "editorMode": "builder",
3312 "expr": "pihole_domains_being_blocked{hostname=~\"192.168.20.34\"}",
3313 "instant": false,
3314 "interval": "",
3315 "legendFormat": "",
3316 "refId": "A"
3317 }
3318 ],
3319 "title": "Domains being blocked",
3320 "transparent": true,
3321 "type": "stat"
3322 },
3323 {
3324 "datasource": {
3325 "type": "prometheus",
3326 "uid": "t-6Lw3i4z"
3327 },
3328 "fieldConfig": {
3329 "defaults": {
3330 "color": {
3331 "mode": "palette-classic"
3332 },
3333 "custom": {
3334 "axisCenteredZero": false,
3335 "axisColorMode": "text",
3336 "axisLabel": "",
3337 "axisPlacement": "auto",
3338 "barAlignment": 0,
3339 "drawStyle": "line",
3340 "fillOpacity": 10,
3341 "gradientMode": "none",
3342 "hideFrom": {
3343 "legend": false,
3344 "tooltip": false,
3345 "viz": false
3346 },
3347 "lineInterpolation": "linear",
3348 "lineWidth": 1,
3349 "pointSize": 5,
3350 "scaleDistribution": {
3351 "type": "linear"
3352 },
3353 "showPoints": "never",
3354 "spanNulls": false,
3355 "stacking": {
3356 "group": "A",
3357 "mode": "none"
3358 },
3359 "thresholdsStyle": {
3360 "mode": "off"
3361 }
3362 },
3363 "mappings": [],
3364 "thresholds": {
3365 "mode": "absolute",
3366 "steps": [
3367 {
3368 "color": "green"
3369 },
3370 {
3371 "color": "red",
3372 "value": 80
3373 }
3374 ]
3375 },
3376 "unit": "short"
3377 },
3378 "overrides": []
3379 },
3380 "gridPos": {
3381 "h": 5,
3382 "w": 12,
3383 "x": 0,
3384 "y": 62
3385 },
3386 "id": 73,
3387 "links": [],
3388 "options": {
3389 "legend": {
3390 "calcs": [],
3391 "displayMode": "list",
3392 "placement": "bottom",
3393 "showLegend": true
3394 },
3395 "tooltip": {
3396 "mode": "multi",
3397 "sort": "none"
3398 }
3399 },
3400 "pluginVersion": "9.3.2",
3401 "targets": [
3402 {
3403 "datasource": {
3404 "type": "prometheus",
3405 "uid": "t-6Lw3i4z"
3406 },
3407 "editorMode": "builder",
3408 "expr": "pihole_unique_clients{hostname=~\"192.168.20.34\"}",
3409 "interval": "",
3410 "legendFormat": "{{ hostname }}",
3411 "range": true,
3412 "refId": "A"
3413 }
3414 ],
3415 "title": "Unique clients",
3416 "transparent": true,
3417 "type": "timeseries"
3418 },
3419 {
3420 "datasource": {
3421 "type": "prometheus",
3422 "uid": "t-6Lw3i4z"
3423 },
3424 "fieldConfig": {
3425 "defaults": {
3426 "color": {
3427 "mode": "palette-classic"
3428 },
3429 "custom": {
3430 "axisCenteredZero": false,
3431 "axisColorMode": "text",
3432 "axisLabel": "",
3433 "axisPlacement": "auto",
3434 "barAlignment": 0,
3435 "drawStyle": "line",
3436 "fillOpacity": 10,
3437 "gradientMode": "none",
3438 "hideFrom": {
3439 "legend": false,
3440 "tooltip": false,
3441 "viz": false
3442 },
3443 "lineInterpolation": "linear",
3444 "lineWidth": 1,
3445 "pointSize": 5,
3446 "scaleDistribution": {
3447 "type": "linear"
3448 },
3449 "showPoints": "never",
3450 "spanNulls": false,
3451 "stacking": {
3452 "group": "A",
3453 "mode": "none"
3454 },
3455 "thresholdsStyle": {
3456 "mode": "off"
3457 }
3458 },
3459 "mappings": [],
3460 "thresholds": {
3461 "mode": "absolute",
3462 "steps": [
3463 {
3464 "color": "green"
3465 },
3466 {
3467 "color": "red",
3468 "value": 80
3469 }
3470 ]
3471 },
3472 "unit": "percent"
3473 },
3474 "overrides": [
3475 {
3476 "matcher": {
3477 "id": "byName",
3478 "options": "pihole_unique_clients{hostname=\\\"127.0.0.1\\\",instance=\\\"localhost:9311\\\",job=\\\"pihole\\\"}"
3479 },
3480 "properties": [
3481 {
3482 "id": "color",
3483 "value": {
3484 "fixedColor": "#E0B400",
3485 "mode": "fixed"
3486 }
3487 }
3488 ]
3489 }
3490 ]
3491 },
3492 "gridPos": {
3493 "h": 5,
3494 "w": 12,
3495 "x": 12,
3496 "y": 62
3497 },
3498 "id": 75,
3499 "links": [],
3500 "options": {
3501 "legend": {
3502 "calcs": [],
3503 "displayMode": "list",
3504 "placement": "bottom",
3505 "showLegend": false
3506 },
3507 "tooltip": {
3508 "mode": "multi",
3509 "sort": "desc"
3510 }
3511 },
3512 "pluginVersion": "9.3.2",
3513 "targets": [
3514 {
3515 "datasource": {
3516 "type": "prometheus",
3517 "uid": "t-6Lw3i4z"
3518 },
3519 "editorMode": "builder",
3520 "expr": "pihole_forward_destinations{hostname=~\"192.168.20.34\"}",
3521 "interval": "",
3522 "legendFormat": "{{ destination }}",
3523 "range": true,
3524 "refId": "A"
3525 }
3526 ],
3527 "title": "Forward destinations",
3528 "transparent": true,
3529 "type": "timeseries"
3530 }
3531 ],
3532 "title": "Pihole",
3533 "type": "row"
3534 },
3535 {
3536 "collapsed": true,
3537 "gridPos": {
3538 "h": 1,
3539 "w": 24,
3540 "x": 0,
3541 "y": 54
3542 },
3543 "id": 79,
3544 "panels": [
3545 {
3546 "datasource": {
3547 "type": "prometheus",
3548 "uid": "t-6Lw3i4z"
3549 },
3550 "description": "",
3551 "fieldConfig": {
3552 "defaults": {
3553 "color": {
3554 "mode": "palette-classic"
3555 },
3556 "custom": {
3557 "axisCenteredZero": false,
3558 "axisColorMode": "text",
3559 "axisLabel": "Connections (rate)",
3560 "axisPlacement": "auto",
3561 "barAlignment": 0,
3562 "drawStyle": "line",
3563 "fillOpacity": 10,
3564 "gradientMode": "none",
3565 "hideFrom": {
3566 "legend": false,
3567 "tooltip": false,
3568 "viz": false
3569 },
3570 "lineInterpolation": "linear",
3571 "lineWidth": 1,
3572 "pointSize": 5,
3573 "scaleDistribution": {
3574 "type": "linear"
3575 },
3576 "showPoints": "never",
3577 "spanNulls": false,
3578 "stacking": {
3579 "group": "A",
3580 "mode": "none"
3581 },
3582 "thresholdsStyle": {
3583 "mode": "off"
3584 }
3585 },
3586 "decimals": 1,
3587 "mappings": [],
3588 "thresholds": {
3589 "mode": "absolute",
3590 "steps": [
3591 {
3592 "color": "green"
3593 },
3594 {
3595 "color": "red",
3596 "value": 80
3597 }
3598 ]
3599 },
3600 "unit": "short"
3601 },
3602 "overrides": []
3603 },
3604 "gridPos": {
3605 "h": 10,
3606 "w": 12,
3607 "x": 0,
3608 "y": 111
3609 },
3610 "id": 81,
3611 "links": [],
3612 "options": {
3613 "legend": {
3614 "calcs": [],
3615 "displayMode": "list",
3616 "placement": "bottom",
3617 "showLegend": true
3618 },
3619 "tooltip": {
3620 "mode": "multi",
3621 "sort": "none"
3622 }
3623 },
3624 "pluginVersion": "9.3.2",
3625 "targets": [
3626 {
3627 "datasource": {
3628 "type": "prometheus",
3629 "uid": "t-6Lw3i4z"
3630 },
3631 "editorMode": "builder",
3632 "expr": "irate(nginx_connections_accepted{instance=~\".+\"}[5m])",
3633 "format": "time_series",
3634 "instant": false,
3635 "intervalFactor": 1,
3636 "legendFormat": "{{instance}} accepted",
3637 "refId": "A"
3638 },
3639 {
3640 "datasource": {
3641 "type": "prometheus",
3642 "uid": "t-6Lw3i4z"
3643 },
3644 "editorMode": "code",
3645 "expr": "irate(nginx_connections_handled{instance=~\".+\"}[5m])",
3646 "format": "time_series",
3647 "instant": false,
3648 "intervalFactor": 1,
3649 "legendFormat": "{{instance}} handled",
3650 "refId": "B"
3651 }
3652 ],
3653 "title": "Processed connections",
3654 "type": "timeseries"
3655 },
3656 {
3657 "datasource": {
3658 "type": "prometheus",
3659 "uid": "t-6Lw3i4z"
3660 },
3661 "fieldConfig": {
3662 "defaults": {
3663 "color": {
3664 "mode": "palette-classic"
3665 },
3666 "custom": {
3667 "axisCenteredZero": false,
3668 "axisColorMode": "text",
3669 "axisLabel": "Connections",
3670 "axisPlacement": "auto",
3671 "barAlignment": 0,
3672 "drawStyle": "line",
3673 "fillOpacity": 10,
3674 "gradientMode": "none",
3675 "hideFrom": {
3676 "legend": false,
3677 "tooltip": false,
3678 "viz": false
3679 },
3680 "lineInterpolation": "linear",
3681 "lineWidth": 1,
3682 "pointSize": 5,
3683 "scaleDistribution": {
3684 "type": "linear"
3685 },
3686 "showPoints": "never",
3687 "spanNulls": false,
3688 "stacking": {
3689 "group": "A",
3690 "mode": "none"
3691 },
3692 "thresholdsStyle": {
3693 "mode": "off"
3694 }
3695 },
3696 "decimals": 0,
3697 "mappings": [],
3698 "thresholds": {
3699 "mode": "absolute",
3700 "steps": [
3701 {
3702 "color": "green"
3703 },
3704 {
3705 "color": "red",
3706 "value": 80
3707 }
3708 ]
3709 },
3710 "unit": "short"
3711 },
3712 "overrides": []
3713 },
3714 "gridPos": {
3715 "h": 10,
3716 "w": 12,
3717 "x": 12,
3718 "y": 111
3719 },
3720 "id": 83,
3721 "links": [],
3722 "options": {
3723 "legend": {
3724 "calcs": [],
3725 "displayMode": "list",
3726 "placement": "bottom",
3727 "showLegend": true
3728 },
3729 "tooltip": {
3730 "mode": "multi",
3731 "sort": "none"
3732 }
3733 },
3734 "pluginVersion": "9.3.2",
3735 "targets": [
3736 {
3737 "datasource": {
3738 "type": "prometheus",
3739 "uid": "t-6Lw3i4z"
3740 },
3741 "editorMode": "code",
3742 "expr": "nginx_connections_active{instance=~\".+\"}",
3743 "format": "time_series",
3744 "intervalFactor": 1,
3745 "legendFormat": "{{instance}} active",
3746 "range": true,
3747 "refId": "A"
3748 },
3749 {
3750 "datasource": {
3751 "type": "prometheus",
3752 "uid": "t-6Lw3i4z"
3753 },
3754 "editorMode": "code",
3755 "expr": "nginx_connections_reading{instance=~\".+\"}",
3756 "format": "time_series",
3757 "intervalFactor": 1,
3758 "legendFormat": "{{instance}} reading",
3759 "range": true,
3760 "refId": "B"
3761 },
3762 {
3763 "datasource": {
3764 "type": "prometheus",
3765 "uid": "t-6Lw3i4z"
3766 },
3767 "editorMode": "code",
3768 "expr": "nginx_connections_waiting{instance=~\".+\"}",
3769 "format": "time_series",
3770 "intervalFactor": 1,
3771 "legendFormat": "{{instance}} waiting",
3772 "range": true,
3773 "refId": "C"
3774 },
3775 {
3776 "datasource": {
3777 "type": "prometheus",
3778 "uid": "t-6Lw3i4z"
3779 },
3780 "editorMode": "code",
3781 "expr": "nginx_connections_writing{instance=~\".+\"}",
3782 "format": "time_series",
3783 "intervalFactor": 1,
3784 "legendFormat": "{{instance}} writing",
3785 "range": true,
3786 "refId": "D"
3787 }
3788 ],
3789 "title": "Active Connections",
3790 "type": "timeseries"
3791 },
3792 {
3793 "datasource": {
3794 "type": "prometheus",
3795 "uid": "t-6Lw3i4z"
3796 },
3797 "fieldConfig": {
3798 "defaults": {
3799 "color": {
3800 "mode": "palette-classic"
3801 },
3802 "custom": {
3803 "axisCenteredZero": false,
3804 "axisColorMode": "text",
3805 "axisLabel": "",
3806 "axisPlacement": "auto",
3807 "barAlignment": 0,
3808 "drawStyle": "line",
3809 "fillOpacity": 10,
3810 "gradientMode": "none",
3811 "hideFrom": {
3812 "legend": false,
3813 "tooltip": false,
3814 "viz": false
3815 },
3816 "lineInterpolation": "linear",
3817 "lineWidth": 1,
3818 "pointSize": 5,
3819 "scaleDistribution": {
3820 "type": "linear"
3821 },
3822 "showPoints": "never",
3823 "spanNulls": false,
3824 "stacking": {
3825 "group": "A",
3826 "mode": "none"
3827 },
3828 "thresholdsStyle": {
3829 "mode": "off"
3830 }
3831 },
3832 "mappings": [],
3833 "thresholds": {
3834 "mode": "absolute",
3835 "steps": [
3836 {
3837 "color": "green"
3838 },
3839 {
3840 "color": "red",
3841 "value": 80
3842 }
3843 ]
3844 },
3845 "unit": "short"
3846 },
3847 "overrides": []
3848 },
3849 "gridPos": {
3850 "h": 8,
3851 "w": 24,
3852 "x": 0,
3853 "y": 121
3854 },
3855 "id": 85,
3856 "links": [],
3857 "options": {
3858 "legend": {
3859 "calcs": [],
3860 "displayMode": "list",
3861 "placement": "bottom",
3862 "showLegend": true
3863 },
3864 "tooltip": {
3865 "mode": "multi",
3866 "sort": "none"
3867 }
3868 },
3869 "pluginVersion": "9.3.2",
3870 "targets": [
3871 {
3872 "datasource": {
3873 "type": "prometheus",
3874 "uid": "t-6Lw3i4z"
3875 },
3876 "editorMode": "code",
3877 "expr": "irate(nginx_http_requests_total{instance=~\".+\"}[5m])",
3878 "format": "time_series",
3879 "intervalFactor": 1,
3880 "legendFormat": "{{instance}} total requests",
3881 "range": true,
3882 "refId": "A"
3883 }
3884 ],
3885 "title": "Total requests",
3886 "type": "timeseries"
3887 }
3888 ],
3889 "title": "Nginx Monitoring",
3890 "type": "row"
3891 },
3892 {
3893 "collapsed": true,
3894 "gridPos": {
3895 "h": 1,
3896 "w": 24,
3897 "x": 0,
3898 "y": 55
3899 },
3900 "id": 87,
3901 "panels": [
3902 {
3903 "datasource": {
3904 "type": "prometheus",
3905 "uid": "t-6Lw3i4z"
3906 },
3907 "fieldConfig": {
3908 "defaults": {
3909 "mappings": [],
3910 "thresholds": {
3911 "mode": "absolute",
3912 "steps": [
3913 {
3914 "color": "dark-blue"
3915 }
3916 ]
3917 }
3918 },
3919 "overrides": []
3920 },
3921 "gridPos": {
3922 "h": 4,
3923 "w": 6,
3924 "x": 0,
3925 "y": 130
3926 },
3927 "id": 99,
3928 "options": {
3929 "colorMode": "value",
3930 "graphMode": "none",
3931 "justifyMode": "center",
3932 "orientation": "auto",
3933 "reduceOptions": {
3934 "calcs": [
3935 "lastNotNull"
3936 ],
3937 "fields": "",
3938 "values": false
3939 },
3940 "textMode": "auto"
3941 },
3942 "pluginVersion": "9.3.2",
3943 "targets": [
3944 {
3945 "datasource": {
3946 "type": "prometheus",
3947 "uid": "t-6Lw3i4z"
3948 },
3949 "expr": "gitea_accesses",
3950 "interval": "",
3951 "legendFormat": "",
3952 "refId": "A"
3953 }
3954 ],
3955 "title": "Number of Accesses",
3956 "type": "stat"
3957 },
3958 {
3959 "datasource": {
3960 "type": "prometheus",
3961 "uid": "t-6Lw3i4z"
3962 },
3963 "fieldConfig": {
3964 "defaults": {
3965 "mappings": [],
3966 "thresholds": {
3967 "mode": "absolute",
3968 "steps": [
3969 {
3970 "color": "dark-blue"
3971 }
3972 ]
3973 }
3974 },
3975 "overrides": []
3976 },
3977 "gridPos": {
3978 "h": 4,
3979 "w": 6,
3980 "x": 6,
3981 "y": 130
3982 },
3983 "id": 89,
3984 "options": {
3985 "colorMode": "value",
3986 "graphMode": "none",
3987 "justifyMode": "center",
3988 "orientation": "auto",
3989 "reduceOptions": {
3990 "calcs": [
3991 "lastNotNull"
3992 ],
3993 "fields": "",
3994 "values": false
3995 },
3996 "textMode": "auto"
3997 },
3998 "pluginVersion": "9.3.2",
3999 "targets": [
4000 {
4001 "datasource": {
4002 "type": "prometheus",
4003 "uid": "t-6Lw3i4z"
4004 },
4005 "expr": "gitea_repositories",
4006 "interval": "",
4007 "legendFormat": "",
4008 "refId": "A"
4009 }
4010 ],
4011 "title": "Number of Repositories",
4012 "type": "stat"
4013 },
4014 {
4015 "datasource": {
4016 "type": "prometheus",
4017 "uid": "t-6Lw3i4z"
4018 },
4019 "fieldConfig": {
4020 "defaults": {
4021 "mappings": [],
4022 "thresholds": {
4023 "mode": "absolute",
4024 "steps": [
4025 {
4026 "color": "dark-blue"
4027 }
4028 ]
4029 }
4030 },
4031 "overrides": []
4032 },
4033 "gridPos": {
4034 "h": 4,
4035 "w": 6,
4036 "x": 12,
4037 "y": 130
4038 },
4039 "id": 91,
4040 "options": {
4041 "colorMode": "value",
4042 "graphMode": "none",
4043 "justifyMode": "center",
4044 "orientation": "auto",
4045 "reduceOptions": {
4046 "calcs": [
4047 "lastNotNull"
4048 ],
4049 "fields": "",
4050 "values": false
4051 },
4052 "textMode": "auto"
4053 },
4054 "pluginVersion": "9.3.2",
4055 "targets": [
4056 {
4057 "datasource": {
4058 "type": "prometheus",
4059 "uid": "t-6Lw3i4z"
4060 },
4061 "expr": "gitea_users",
4062 "interval": "",
4063 "legendFormat": "",
4064 "refId": "A"
4065 }
4066 ],
4067 "title": "Number of Users",
4068 "type": "stat"
4069 },
4070 {
4071 "datasource": {
4072 "type": "prometheus",
4073 "uid": "t-6Lw3i4z"
4074 },
4075 "fieldConfig": {
4076 "defaults": {
4077 "mappings": [],
4078 "thresholds": {
4079 "mode": "absolute",
4080 "steps": [
4081 {
4082 "color": "dark-blue"
4083 }
4084 ]
4085 }
4086 },
4087 "overrides": []
4088 },
4089 "gridPos": {
4090 "h": 4,
4091 "w": 6,
4092 "x": 18,
4093 "y": 130
4094 },
4095 "id": 101,
4096 "options": {
4097 "colorMode": "value",
4098 "graphMode": "none",
4099 "justifyMode": "center",
4100 "orientation": "auto",
4101 "reduceOptions": {
4102 "calcs": [
4103 "lastNotNull"
4104 ],
4105 "fields": "",
4106 "values": false
4107 },
4108 "textMode": "auto"
4109 },
4110 "pluginVersion": "9.3.2",
4111 "targets": [
4112 {
4113 "datasource": {
4114 "type": "prometheus",
4115 "uid": "t-6Lw3i4z"
4116 },
4117 "expr": "gitea_issues",
4118 "interval": "",
4119 "legendFormat": "",
4120 "refId": "A"
4121 }
4122 ],
4123 "title": "Number of Issues",
4124 "type": "stat"
4125 },
4126 {
4127 "datasource": {
4128 "type": "prometheus",
4129 "uid": "t-6Lw3i4z"
4130 },
4131 "fieldConfig": {
4132 "defaults": {
4133 "mappings": [],
4134 "thresholds": {
4135 "mode": "absolute",
4136 "steps": [
4137 {
4138 "color": "dark-purple"
4139 }
4140 ]
4141 }
4142 },
4143 "overrides": []
4144 },
4145 "gridPos": {
4146 "h": 4,
4147 "w": 4,
4148 "x": 0,
4149 "y": 134
4150 },
4151 "id": 93,
4152 "options": {
4153 "colorMode": "value",
4154 "graphMode": "none",
4155 "justifyMode": "center",
4156 "orientation": "auto",
4157 "reduceOptions": {
4158 "calcs": [
4159 "lastNotNull"
4160 ],
4161 "fields": "",
4162 "values": false
4163 },
4164 "textMode": "auto"
4165 },
4166 "pluginVersion": "9.3.2",
4167 "targets": [
4168 {
4169 "datasource": {
4170 "type": "prometheus",
4171 "uid": "t-6Lw3i4z"
4172 },
4173 "expr": "gitea_webhooks",
4174 "interval": "",
4175 "legendFormat": "",
4176 "refId": "A"
4177 }
4178 ],
4179 "title": "Number of Webhooks",
4180 "type": "stat"
4181 },
4182 {
4183 "datasource": {
4184 "type": "prometheus",
4185 "uid": "t-6Lw3i4z"
4186 },
4187 "fieldConfig": {
4188 "defaults": {
4189 "mappings": [],
4190 "thresholds": {
4191 "mode": "absolute",
4192 "steps": [
4193 {
4194 "color": "dark-purple"
4195 }
4196 ]
4197 }
4198 },
4199 "overrides": []
4200 },
4201 "gridPos": {
4202 "h": 4,
4203 "w": 4,
4204 "x": 4,
4205 "y": 134
4206 },
4207 "id": 103,
4208 "options": {
4209 "colorMode": "value",
4210 "graphMode": "none",
4211 "justifyMode": "center",
4212 "orientation": "auto",
4213 "reduceOptions": {
4214 "calcs": [
4215 "lastNotNull"
4216 ],
4217 "fields": "",
4218 "values": false
4219 },
4220 "textMode": "auto"
4221 },
4222 "pluginVersion": "9.3.2",
4223 "targets": [
4224 {
4225 "datasource": {
4226 "type": "prometheus",
4227 "uid": "t-6Lw3i4z"
4228 },
4229 "editorMode": "code",
4230 "expr": "gitea_hooktasks",
4231 "interval": "",
4232 "legendFormat": "",
4233 "range": true,
4234 "refId": "A"
4235 }
4236 ],
4237 "title": "Number of Webhook Tasks",
4238 "type": "stat"
4239 },
4240 {
4241 "datasource": {
4242 "type": "prometheus",
4243 "uid": "t-6Lw3i4z"
4244 },
4245 "fieldConfig": {
4246 "defaults": {
4247 "mappings": [],
4248 "thresholds": {
4249 "mode": "absolute",
4250 "steps": [
4251 {
4252 "color": "dark-purple"
4253 }
4254 ]
4255 }
4256 },
4257 "overrides": []
4258 },
4259 "gridPos": {
4260 "h": 4,
4261 "w": 4,
4262 "x": 8,
4263 "y": 134
4264 },
4265 "id": 97,
4266 "options": {
4267 "colorMode": "value",
4268 "graphMode": "none",
4269 "justifyMode": "center",
4270 "orientation": "auto",
4271 "reduceOptions": {
4272 "calcs": [
4273 "lastNotNull"
4274 ],
4275 "fields": "",
4276 "values": false
4277 },
4278 "textMode": "auto"
4279 },
4280 "pluginVersion": "9.3.2",
4281 "targets": [
4282 {
4283 "datasource": {
4284 "type": "prometheus",
4285 "uid": "t-6Lw3i4z"
4286 },
4287 "expr": "gitea_releases",
4288 "interval": "",
4289 "legendFormat": "",
4290 "refId": "A"
4291 }
4292 ],
4293 "title": "Number of Releases",
4294 "type": "stat"
4295 },
4296 {
4297 "datasource": {
4298 "type": "prometheus",
4299 "uid": "t-6Lw3i4z"
4300 },
4301 "fieldConfig": {
4302 "defaults": {
4303 "mappings": [],
4304 "thresholds": {
4305 "mode": "absolute",
4306 "steps": [
4307 {
4308 "color": "dark-yellow"
4309 }
4310 ]
4311 }
4312 },
4313 "overrides": []
4314 },
4315 "gridPos": {
4316 "h": 4,
4317 "w": 4,
4318 "x": 12,
4319 "y": 134
4320 },
4321 "id": 95,
4322 "options": {
4323 "colorMode": "value",
4324 "graphMode": "none",
4325 "justifyMode": "center",
4326 "orientation": "auto",
4327 "reduceOptions": {
4328 "calcs": [
4329 "lastNotNull"
4330 ],
4331 "fields": "",
4332 "values": false
4333 },
4334 "textMode": "auto"
4335 },
4336 "pluginVersion": "9.3.2",
4337 "targets": [
4338 {
4339 "datasource": {
4340 "type": "prometheus",
4341 "uid": "t-6Lw3i4z"
4342 },
4343 "expr": "gitea_publickeys",
4344 "interval": "",
4345 "legendFormat": "",
4346 "refId": "A"
4347 }
4348 ],
4349 "title": "Number of PublicKeys",
4350 "type": "stat"
4351 },
4352 {
4353 "datasource": {
4354 "type": "prometheus",
4355 "uid": "t-6Lw3i4z"
4356 },
4357 "fieldConfig": {
4358 "defaults": {
4359 "mappings": [],
4360 "thresholds": {
4361 "mode": "absolute",
4362 "steps": [
4363 {
4364 "color": "dark-yellow"
4365 }
4366 ]
4367 }
4368 },
4369 "overrides": []
4370 },
4371 "gridPos": {
4372 "h": 4,
4373 "w": 4,
4374 "x": 16,
4375 "y": 134
4376 },
4377 "id": 105,
4378 "options": {
4379 "colorMode": "value",
4380 "graphMode": "none",
4381 "justifyMode": "center",
4382 "orientation": "auto",
4383 "reduceOptions": {
4384 "calcs": [
4385 "lastNotNull"
4386 ],
4387 "fields": "",
4388 "values": false
4389 },
4390 "textMode": "auto"
4391 },
4392 "pluginVersion": "9.3.2",
4393 "targets": [
4394 {
4395 "datasource": {
4396 "type": "prometheus",
4397 "uid": "t-6Lw3i4z"
4398 },
4399 "expr": "gitea_organizations",
4400 "interval": "",
4401 "legendFormat": "",
4402 "refId": "A"
4403 }
4404 ],
4405 "title": "Number of Organizations",
4406 "type": "stat"
4407 },
4408 {
4409 "datasource": {
4410 "type": "prometheus",
4411 "uid": "t-6Lw3i4z"
4412 },
4413 "fieldConfig": {
4414 "defaults": {
4415 "mappings": [],
4416 "thresholds": {
4417 "mode": "absolute",
4418 "steps": [
4419 {
4420 "color": "dark-yellow"
4421 }
4422 ]
4423 }
4424 },
4425 "overrides": []
4426 },
4427 "gridPos": {
4428 "h": 4,
4429 "w": 4,
4430 "x": 20,
4431 "y": 134
4432 },
4433 "id": 107,
4434 "options": {
4435 "colorMode": "value",
4436 "graphMode": "none",
4437 "justifyMode": "center",
4438 "orientation": "auto",
4439 "reduceOptions": {
4440 "calcs": [
4441 "lastNotNull"
4442 ],
4443 "fields": "",
4444 "values": false
4445 },
4446 "textMode": "auto"
4447 },
4448 "pluginVersion": "9.3.2",
4449 "targets": [
4450 {
4451 "datasource": {
4452 "type": "prometheus",
4453 "uid": "t-6Lw3i4z"
4454 },
4455 "expr": "gitea_labels",
4456 "interval": "",
4457 "legendFormat": "",
4458 "refId": "A"
4459 }
4460 ],
4461 "title": "Number of Labels",
4462 "type": "stat"
4463 }
4464 ],
4465 "title": "Gitea",
4466 "type": "row"
4467 },
4468 {
4469 "collapsed": true,
4470 "gridPos": {
4471 "h": 1,
4472 "w": 24,
4473 "x": 0,
4474 "y": 56
4475 },
4476 "id": 109,
4477 "panels": [
4478 {
4479 "datasource": {
4480 "type": "prometheus",
4481 "uid": "t-6Lw3i4z"
4482 },
4483 "fieldConfig": {
4484 "defaults": {
4485 "color": {
4486 "mode": "thresholds"
4487 },
4488 "mappings": [
4489 {
4490 "options": {
4491 "match": "null",
4492 "result": {
4493 "index": 0,
4494 "text": "0"
4495 }
4496 },
4497 "type": "special"
4498 }
4499 ],
4500 "thresholds": {
4501 "mode": "absolute",
4502 "steps": [
4503 {
4504 "color": "#37872D"
4505 },
4506 {
4507 "color": "#F2CC0C",
4508 "value": 1
4509 },
4510 {
4511 "color": "#F2CC0C",
4512 "value": 2
4513 }
4514 ]
4515 },
4516 "unit": "none"
4517 },
4518 "overrides": []
4519 },
4520 "gridPos": {
4521 "h": 4,
4522 "w": 4,
4523 "x": 0,
4524 "y": 131
4525 },
4526 "id": 121,
4527 "links": [],
4528 "maxDataPoints": 100,
4529 "options": {
4530 "colorMode": "value",
4531 "graphMode": "area",
4532 "justifyMode": "auto",
4533 "orientation": "horizontal",
4534 "reduceOptions": {
4535 "calcs": [
4536 "lastNotNull"
4537 ],
4538 "fields": "/^__name__$/",
4539 "values": false
4540 },
4541 "textMode": "auto"
4542 },
4543 "pluginVersion": "9.3.2",
4544 "targets": [
4545 {
4546 "datasource": {
4547 "type": "prometheus",
4548 "uid": "t-6Lw3i4z"
4549 },
4550 "editorMode": "code",
4551 "expr": "jenkins_node_builds_count",
4552 "format": "time_series",
4553 "instant": false,
4554 "intervalFactor": 1,
4555 "refId": "A",
4556 "textEditor": false
4557 }
4558 ],
4559 "title": "Total Jobs",
4560 "type": "stat"
4561 },
4562 {
4563 "datasource": {
4564 "type": "prometheus",
4565 "uid": "t-6Lw3i4z"
4566 },
4567 "fieldConfig": {
4568 "defaults": {
4569 "color": {
4570 "mode": "thresholds"
4571 },
4572 "mappings": [
4573 {
4574 "options": {
4575 "0": {
4576 "text": "None!"
4577 }
4578 },
4579 "type": "value"
4580 }
4581 ],
4582 "thresholds": {
4583 "mode": "absolute",
4584 "steps": [
4585 {
4586 "color": "rgba(245, 54, 54, 0.9)"
4587 },
4588 {
4589 "color": "rgba(237, 129, 40, 0.89)",
4590 "value": 0
4591 },
4592 {
4593 "color": "rgba(50, 172, 45, 0.97)",
4594 "value": 0
4595 }
4596 ]
4597 },
4598 "unit": "none"
4599 },
4600 "overrides": []
4601 },
4602 "gridPos": {
4603 "h": 4,
4604 "w": 4,
4605 "x": 4,
4606 "y": 131
4607 },
4608 "id": 111,
4609 "links": [],
4610 "maxDataPoints": 100,
4611 "options": {
4612 "colorMode": "value",
4613 "graphMode": "area",
4614 "justifyMode": "auto",
4615 "orientation": "horizontal",
4616 "reduceOptions": {
4617 "calcs": [
4618 "lastNotNull"
4619 ],
4620 "fields": "",
4621 "values": false
4622 },
4623 "textMode": "auto"
4624 },
4625 "pluginVersion": "9.3.2",
4626 "targets": [
4627 {
4628 "datasource": {
4629 "type": "prometheus",
4630 "uid": "t-6Lw3i4z"
4631 },
4632 "expr": "jenkins_runs_success_total",
4633 "format": "time_series",
4634 "intervalFactor": 1,
4635 "refId": "A",
4636 "textEditor": false
4637 }
4638 ],
4639 "title": "Sucessful Jobs",
4640 "type": "stat"
4641 },
4642 {
4643 "datasource": {
4644 "type": "prometheus",
4645 "uid": "t-6Lw3i4z"
4646 },
4647 "fieldConfig": {
4648 "defaults": {
4649 "color": {
4650 "mode": "thresholds"
4651 },
4652 "mappings": [
4653 {
4654 "options": {
4655 "0": {
4656 "index": 0,
4657 "text": "0"
4658 }
4659 },
4660 "type": "value"
4661 }
4662 ],
4663 "thresholds": {
4664 "mode": "absolute",
4665 "steps": [
4666 {
4667 "color": "#37872D"
4668 },
4669 {
4670 "color": "#C4162A",
4671 "value": 0
4672 },
4673 {
4674 "color": "rgb(217, 213, 213)",
4675 "value": 0
4676 }
4677 ]
4678 },
4679 "unit": "none"
4680 },
4681 "overrides": []
4682 },
4683 "gridPos": {
4684 "h": 4,
4685 "w": 4,
4686 "x": 8,
4687 "y": 131
4688 },
4689 "id": 113,
4690 "links": [],
4691 "maxDataPoints": 100,
4692 "options": {
4693 "colorMode": "value",
4694 "graphMode": "area",
4695 "justifyMode": "auto",
4696 "orientation": "horizontal",
4697 "reduceOptions": {
4698 "calcs": [
4699 "lastNotNull"
4700 ],
4701 "fields": "/^__name__$/",
4702 "values": false
4703 },
4704 "textMode": "auto"
4705 },
4706 "pluginVersion": "9.3.2",
4707 "targets": [
4708 {
4709 "datasource": {
4710 "type": "prometheus",
4711 "uid": "t-6Lw3i4z"
4712 },
4713 "editorMode": "code",
4714 "expr": "rate(jenkins_runs_failure_total[$__rate_interval])",
4715 "format": "time_series",
4716 "instant": false,
4717 "intervalFactor": 1,
4718 "refId": "A",
4719 "textEditor": false
4720 }
4721 ],
4722 "title": "Aborted Jobs",
4723 "type": "stat"
4724 },
4725 {
4726 "datasource": {
4727 "type": "prometheus",
4728 "uid": "t-6Lw3i4z"
4729 },
4730 "fieldConfig": {
4731 "defaults": {
4732 "color": {
4733 "mode": "thresholds"
4734 },
4735 "mappings": [
4736 {
4737 "options": {
4738 "0": {
4739 "text": "None!"
4740 }
4741 },
4742 "type": "value"
4743 }
4744 ],
4745 "thresholds": {
4746 "mode": "absolute",
4747 "steps": [
4748 {
4749 "color": "#37872D"
4750 },
4751 {
4752 "color": "#C4162A",
4753 "value": 0
4754 },
4755 {
4756 "color": "#FA6400",
4757 "value": 0
4758 }
4759 ]
4760 },
4761 "unit": "none"
4762 },
4763 "overrides": []
4764 },
4765 "gridPos": {
4766 "h": 4,
4767 "w": 4,
4768 "x": 12,
4769 "y": 131
4770 },
4771 "id": 115,
4772 "links": [],
4773 "maxDataPoints": 100,
4774 "options": {
4775 "colorMode": "value",
4776 "graphMode": "area",
4777 "justifyMode": "auto",
4778 "orientation": "horizontal",
4779 "reduceOptions": {
4780 "calcs": [
4781 "lastNotNull"
4782 ],
4783 "fields": "/^__name__$/",
4784 "values": false
4785 },
4786 "textMode": "auto"
4787 },
4788 "pluginVersion": "9.3.2",
4789 "targets": [
4790 {
4791 "datasource": {
4792 "type": "prometheus",
4793 "uid": "t-6Lw3i4z"
4794 },
4795 "expr": "jenkins_runs_unstable_total",
4796 "format": "time_series",
4797 "instant": false,
4798 "intervalFactor": 1,
4799 "refId": "A",
4800 "textEditor": false
4801 }
4802 ],
4803 "title": "Unstable Jobs",
4804 "type": "stat"
4805 },
4806 {
4807 "datasource": {
4808 "type": "prometheus",
4809 "uid": "t-6Lw3i4z"
4810 },
4811 "fieldConfig": {
4812 "defaults": {
4813 "color": {
4814 "mode": "thresholds"
4815 },
4816 "mappings": [],
4817 "thresholds": {
4818 "mode": "absolute",
4819 "steps": [
4820 {
4821 "color": "rgba(50, 172, 45, 0.97)"
4822 },
4823 {
4824 "color": "#37872D",
4825 "value": 0
4826 },
4827 {
4828 "color": "rgba(245, 54, 54, 0.9)",
4829 "value": 1
4830 }
4831 ]
4832 },
4833 "unit": "none"
4834 },
4835 "overrides": []
4836 },
4837 "gridPos": {
4838 "h": 4,
4839 "w": 4,
4840 "x": 16,
4841 "y": 131
4842 },
4843 "id": 117,
4844 "links": [],
4845 "maxDataPoints": 100,
4846 "options": {
4847 "colorMode": "value",
4848 "graphMode": "area",
4849 "justifyMode": "auto",
4850 "orientation": "horizontal",
4851 "reduceOptions": {
4852 "calcs": [
4853 "lastNotNull"
4854 ],
4855 "fields": "",
4856 "values": false
4857 },
4858 "textMode": "auto"
4859 },
4860 "pluginVersion": "9.3.2",
4861 "targets": [
4862 {
4863 "datasource": {
4864 "type": "prometheus",
4865 "uid": "t-6Lw3i4z"
4866 },
4867 "expr": "jenkins_runs_failure_total",
4868 "format": "time_series",
4869 "intervalFactor": 1,
4870 "refId": "A",
4871 "textEditor": false
4872 }
4873 ],
4874 "title": "Failed Jobs",
4875 "type": "stat"
4876 },
4877 {
4878 "datasource": {
4879 "type": "prometheus",
4880 "uid": "t-6Lw3i4z"
4881 },
4882 "fieldConfig": {
4883 "defaults": {
4884 "color": {
4885 "mode": "thresholds"
4886 },
4887 "mappings": [
4888 {
4889 "options": {
4890 "match": "null",
4891 "result": {
4892 "text": "None!"
4893 }
4894 },
4895 "type": "special"
4896 }
4897 ],
4898 "thresholds": {
4899 "mode": "absolute",
4900 "steps": [
4901 {
4902 "color": "rgba(50, 172, 45, 0.97)"
4903 },
4904 {
4905 "color": "#37872D",
4906 "value": 0
4907 },
4908 {
4909 "color": "#FA6400",
4910 "value": 1
4911 }
4912 ]
4913 },
4914 "unit": "none"
4915 },
4916 "overrides": []
4917 },
4918 "gridPos": {
4919 "h": 4,
4920 "w": 4,
4921 "x": 20,
4922 "y": 131
4923 },
4924 "id": 119,
4925 "links": [],
4926 "maxDataPoints": 100,
4927 "options": {
4928 "colorMode": "value",
4929 "graphMode": "area",
4930 "justifyMode": "auto",
4931 "orientation": "horizontal",
4932 "reduceOptions": {
4933 "calcs": [
4934 "lastNotNull"
4935 ],
4936 "fields": "",
4937 "values": false
4938 },
4939 "textMode": "auto"
4940 },
4941 "pluginVersion": "9.3.2",
4942 "targets": [
4943 {
4944 "datasource": {
4945 "type": "prometheus",
4946 "uid": "t-6Lw3i4z"
4947 },
4948 "expr": "jenkins_queue_size_value",
4949 "format": "time_series",
4950 "intervalFactor": 1,
4951 "refId": "A",
4952 "textEditor": false
4953 }
4954 ],
4955 "title": "Jenkins queue size",
4956 "type": "stat"
4957 },
4958 {
4959 "aliasColors": {},
4960 "bars": false,
4961 "dashLength": 10,
4962 "dashes": false,
4963 "datasource": {
4964 "type": "prometheus",
4965 "uid": "t-6Lw3i4z"
4966 },
4967 "editable": true,
4968 "error": false,
4969 "fill": 1,
4970 "fillGradient": 0,
4971 "grid": {},
4972 "gridPos": {
4973 "h": 7,
4974 "w": 24,
4975 "x": 0,
4976 "y": 135
4977 },
4978 "hiddenSeries": false,
4979 "id": 123,
4980 "isNew": true,
4981 "legend": {
4982 "alignAsTable": false,
4983 "avg": false,
4984 "current": false,
4985 "hideEmpty": false,
4986 "hideZero": false,
4987 "max": false,
4988 "min": false,
4989 "rightSide": false,
4990 "show": false,
4991 "total": false,
4992 "values": false
4993 },
4994 "lines": true,
4995 "linewidth": 2,
4996 "links": [],
4997 "nullPointMode": "connected",
4998 "options": {
4999 "alertThreshold": true
5000 },
5001 "paceLength": 10,
5002 "percentage": false,
5003 "pluginVersion": "9.3.2",
5004 "pointradius": 5,
5005 "points": false,
5006 "renderer": "flot",
5007 "seriesOverrides": [],
5008 "spaceLength": 10,
5009 "stack": false,
5010 "steppedLine": false,
5011 "targets": [
5012 {
5013 "datasource": {
5014 "type": "prometheus",
5015 "uid": "t-6Lw3i4z"
5016 },
5017 "expr": "default_jenkins_builds_last_build_duration_milliseconds",
5018 "format": "time_series",
5019 "intervalFactor": 1,
5020 "legendFormat": "",
5021 "refId": "A"
5022 }
5023 ],
5024 "thresholds": [],
5025 "timeRegions": [],
5026 "title": "Job Duration",
5027 "tooltip": {
5028 "msResolution": false,
5029 "shared": true,
5030 "sort": 0,
5031 "value_type": "cumulative"
5032 },
5033 "type": "graph",
5034 "xaxis": {
5035 "mode": "time",
5036 "show": true,
5037 "values": []
5038 },
5039 "yaxes": [
5040 {
5041 "format": "ms",
5042 "label": "",
5043 "logBase": 2,
5044 "show": true
5045 },
5046 {
5047 "format": "short",
5048 "label": "",
5049 "logBase": 1,
5050 "show": false
5051 }
5052 ],
5053 "yaxis": {
5054 "align": false
5055 }
5056 }
5057 ],
5058 "title": "Jenkins",
5059 "type": "row"
5060 }
5061 ],
5062 "refresh": "30s",
5063 "schemaVersion": 38,
5064 "style": "dark",
5065 "tags": [],
5066 "templating": {
5067 "list": [
5068 {
5069 "current": {
5070 "selected": true,
5071 "text": [
5072 "All"
5073 ],
5074 "value": [
5075 "$__all"
5076 ]
5077 },
5078 "datasource": {
5079 "type": "loki",
5080 "uid": "AzGHyE5Vz"
5081 },
5082 "definition": "",
5083 "hide": 0,
5084 "includeAll": true,
5085 "multi": true,
5086 "name": "container_name",
5087 "options": [],
5088 "query": {
5089 "label": "container_name",
5090 "refId": "LokiVariableQueryEditor-VariableQuery",
5091 "stream": "",
5092 "type": 1
5093 },
5094 "refresh": 1,
5095 "regex": "",
5096 "skipUrlSync": false,
5097 "sort": 2,
5098 "type": "query"
5099 },
5100 {
5101 "current": {
5102 "selected": true,
5103 "text": [
5104 "All"
5105 ],
5106 "value": [
5107 "$__all"
5108 ]
5109 },
5110 "datasource": {
5111 "type": "loki",
5112 "uid": "AzGHyE5Vz"
5113 },
5114 "definition": "",
5115 "hide": 0,
5116 "includeAll": true,
5117 "multi": true,
5118 "name": "vm_name",
5119 "options": [],
5120 "query": {
5121 "label": "hostname",
5122 "refId": "LokiVariableQueryEditor-VariableQuery",
5123 "stream": "",
5124 "type": 1
5125 },
5126 "refresh": 1,
5127 "regex": "",
5128 "skipUrlSync": false,
5129 "sort": 0,
5130 "type": "query"
5131 },
5132 {
5133 "current": {
5134 "selected": true,
5135 "text": [
5136 "All"
5137 ],
5138 "value": [
5139 "$__all"
5140 ]
5141 },
5142 "datasource": {
5143 "type": "loki",
5144 "uid": "AzGHyE5Vz"
5145 },
5146 "definition": "",
5147 "hide": 0,
5148 "includeAll": true,
5149 "multi": true,
5150 "name": "vm_service",
5151 "options": [],
5152 "query": {
5153 "label": "unit",
5154 "refId": "LokiVariableQueryEditor-VariableQuery",
5155 "stream": "",
5156 "type": 1
5157 },
5158 "refresh": 1,
5159 "regex": "",
5160 "skipUrlSync": false,
5161 "sort": 0,
5162 "type": "query"
5163 }
5164 ]
5165 },
5166 "time": {
5167 "from": "now-6h",
5168 "to": "now"
5169 },
5170 "timepicker": {},
5171 "timezone": "",
5172 "title": "Main",
5173 "uid": "kXjiNPWVk",
5174 "version": 39,
5175 "weekStart": ""
5176}
diff --git a/data/home_assistant/configuration.yaml b/data/home_assistant/configuration.yaml
new file mode 100644
index 0000000..3723739
--- /dev/null
+++ b/data/home_assistant/configuration.yaml
@@ -0,0 +1,16 @@
1default_config:
2
3homeassistant:
4 currency: USD
5 country: US
6 external_url: "https://homeassistant.chudnick.com"
7 auth_providers:
8 - type: trusted_networks
9 trusted_networks:
10 - 192.168.30.0/24
11 allow_bypass_login: true
12 - type: homeassistant
13
14http:
15 use_x_forwarded_for: true
16 trusted_proxies: 172.25.22.0/24
diff --git a/data/home_assistant/home_assistant.conf.j2 b/data/home_assistant/home_assistant.conf.j2
new file mode 100644
index 0000000..9f6be24
--- /dev/null
+++ b/data/home_assistant/home_assistant.conf.j2
@@ -0,0 +1,38 @@
1server {
2 listen 443 ssl;
3 server_name {{ home_assistant_server_name }};
4
5 ssl_certificate /etc/letsencrypt/live/chudnick.com/fullchain.pem;
6 ssl_certificate_key /etc/letsencrypt/live/chudnick.com/privkey.pem;
7 add_header Strict-Transport-Security "max-age=31536000" always;
8 ssl_stapling on;
9 ssl_stapling_verify on;
10
11 # Security / XSS Mitigation Headers
12 add_header X-Frame-Options "SAMEORIGIN";
13 add_header X-XSS-Protection "1; mode=block";
14 add_header X-Content-Type-Options "nosniff";
15 add_header 'Access-Control-Allow-Origin' 'https://chudnick.com' always;
16
17 # authelia
18 include /etc/nginx/snippets/authelia-location.conf;
19
20
21 location / {
22 #authelia
23 include /etc/nginx/snippets/proxy.conf;
24 include /etc/nginx/snippets/authelia-authrequest.conf;
25
26 proxy_set_header Upgrade $http_upgrade;
27 proxy_set_header Connection $connection_upgrade;
28 proxy_pass http://127.0.0.1:{{ home_assistant_external_port }}/;
29 }
30
31}
32
33server {
34 listen 80;
35 listen [::]:80;
36 server_name {{ home_assistant_server_name }};
37 return 301 https://$host$request_uri;
38}
diff --git a/data/homer/config.yml b/data/homer/config.yml
new file mode 100644
index 0000000..ba2313a
--- /dev/null
+++ b/data/homer/config.yml
@@ -0,0 +1,194 @@
1---
2title: "Homelab Dashboard"
3subtitle: "Homer"
4
5header: true
6footer: ""
7
8defaults:
9 layout: rows
10 colorTheme: dark
11
12theme: default
13
14services:
15 - name: Infrastructure
16 icon: "fas fa-server"
17 items:
18 - name: "Proxmox VE"
19 logo: "assets/png/proxmox.png"
20 url: "https://proxmox.chudnick.com:8006/#v1:0:18:4:::::::"
21 target: "_blank"
22 - name: "Proxmox Backup Server"
23 logo: "assets/png/proxmox.png"
24 url: "https://proxmox.chudnick.com:8007"
25 target: "_blank"
26 - name: "Mikrotik hEX Router"
27 logo: "assets/png/mikrotik.png"
28 url: "https://router.chudnick.com"
29 target: "_blank"
30 - name: "Mikrotik cAP Access Point"
31 logo: "assets/png/mikrotik.png"
32 url: "https://ap.chudnick.com"
33 target: "_blank"
34 - name: "FreeIPA"
35 logo: "assets/png/freeipa.png"
36 url: "https://ipasrv.home.local"
37 target: "_blank"
38 - name: "Pihole"
39 logo: "assets/png/pihole.png"
40 url: "https://pihole.chudnick.com/admin"
41 target: "_blank"
42
43 - name: Services
44 icon: "fab fa-docker"
45 items:
46 - name: "Authelia"
47 logo: "assets/png/authelia.png"
48 url: "https://auth.chudnick.com/"
49 target: "_blank"
50 - name: "FreshRSS"
51 logo: "assets/png/freshrss.png"
52 url: "https://rss.chudnick.com/"
53 target: "_blank"
54 - name: "Homer"
55 logo: "assets/png/homer.png"
56 url: "https://dashboard.chudnick.com"
57 target: "_blank"
58 - name: "SearxNG"
59 logo: "assets/svg/searxng.svg"
60 url: "https://searxng.chudnick.com"
61 target: "_blank"
62 - name: "Sunshine"
63 logo: "assets/png/sunshine.png"
64 url: "https://games.home.local:47990"
65 target: "_blank"
66 - name: "Bookstack"
67 logo: "assets/png/bookstack.png"
68 url: "https://wiki.chudnick.com"
69 target: "_blank"
70 - name: "Nextcloud"
71 logo: "assets/png/nextcloud.png"
72 url: "https://nextcloud.chudnick.com"
73 target: "_blank"
74 - name: "Photoprism"
75 logo: "assets/png/photoprism.png"
76 url: "https://photos.chudnick.com"
77 target: "_blank"
78 - name: "pywttr-docker"
79 logo: "assets/svg/pywttr-docker.svg"
80 url: "https://weather.chudnick.com"
81 target: "_blank"
82 - name: "text-generation-webui"
83 logo: "assets/svg/text-generation-webui.svg"
84 url: "https://gpt.chudnick.com"
85 target: "_blank"
86 - name: "Kanboard"
87 logo: "assets/png/kanboard.png"
88 url: "https://tasks.chudnick.com"
89 target: "_blank"
90 - name: "Firefly"
91 logo: "assets/png/firefly.png"
92 url: "https://finances.chudnick.com"
93 target: "_blank"
94 - name: "Firefly Importer"
95 logo: "assets/png/firefly.png"
96 url: "https://finimporter.chudnick.com"
97 target: "_blank"
98 - name: "Home Assistant"
99 logo: "assets/png/home-assistant.png"
100 url: "https://homeassistant.chudnick.com"
101 target: "_blank"
102 - name: "Vaultwarden"
103 logo: "assets/png/bitwarden.png"
104 url: "https://vaultwarden.chudnick.com"
105 target: "_blank"
106
107 - name: Monitoring
108 icon: "fas fa-heartbeat"
109 items:
110 - name: "Grafana"
111 logo: "assets/png/grafana.png"
112 url: "https://monitoring.chudnick.com/grafana"
113 target: "_blank"
114 - name: "Prometheus"
115 logo: "assets/png/prometheus.png"
116 url: "https://monitoring.chudnick.com/prometheus/classic/graph"
117 target: "_blank"
118 - name: "cAdvisor"
119 logo: "assets/png/cadvisor.png"
120 url: "https://cadvisor.chudnick.com"
121 target: "_blank"
122 - name: "Loki"
123 logo: "assets/png/loki.png"
124 url: "https://logs.chudnick.com/metrics"
125 target: "_blank"
126
127 - name: Media
128 icon: "fas fa-tv"
129 items:
130 - name: "Jellyfin"
131 logo: "assets/png/jellyfin.png"
132 url: "https://jellyfin.chudnick.com"
133 target: "_blank"
134 - name: "Navidrome"
135 logo: "assets/png/navidrome.png"
136 url: "https://music.chudnick.com/"
137 target: "_blank"
138 - name: "Invidious"
139 logo: "assets/png/invidious.png"
140 url: "https://invidious.chudnick.com"
141 target: "_blank"
142 - name: "qBittorrent"
143 logo: "assets/png/qbittorrent.png"
144 url: "https://qbittorrent.chudnick.com"
145 target: "_blank"
146 - name: "sonarr"
147 logo: "assets/png/sonarr.png"
148 url: "https://sonarr.chudnick.com"
149 target: "_blank"
150 - name: "radarr"
151 logo: "assets/png/radarr.png"
152 url: "https://radarr.chudnick.com"
153 target: "_blank"
154 - name: "lidarr"
155 logo: "assets/png/lidarr.png"
156 url: "https://lidarr.chudnick.com"
157 target: "_blank"
158 - name: "readarr"
159 logo: "assets/png/readarr.png"
160 url: "https://readarr.chudnick.com"
161 target: "_blank"
162 - name: "prowlarr"
163 logo: "assets/png/prowlarr.png"
164 url: "https://prowlarr.chudnick.com"
165 target: "_blank"
166
167 - name: Development
168 icon: "fas fa-terminal"
169 items:
170 - name: "Gitea"
171 logo: "assets/png/gitea.png"
172 url: "https://gitea.chudnick.com"
173 target: "_blank"
174 - name: "jenkins"
175 logo: "assets/png/jenkins.png"
176 url: "https://jenkins.chudnick.com"
177 target: "_blank"
178 - name: "draw.io"
179 logo: "assets/png/drawio.png"
180 url: "https://drawio.chudnick.com/?offline=1"
181 target: "_blank"
182
183 - name: My Public Sites
184 icon: "fas fa-browser"
185 items:
186 - name: Website
187 logo: "assets/png/chudnick.com.png"
188 url: "https://www.chudnick.com"
189 target: "_blank"
190 - name: Public Git
191 logo: "assets/png/cgit.png"
192 url: "https://git.chudnick.com"
193 target: "_blank"
194
diff --git a/data/homer/homer.conf b/data/homer/homer.conf
new file mode 100644
index 0000000..8670478
--- /dev/null
+++ b/data/homer/homer.conf
@@ -0,0 +1,34 @@
1server {
2 listen 443 ssl;
3 server_name dashboard.chudnick.com;
4
5 ssl_certificate /etc/letsencrypt/live/chudnick.com/fullchain.pem;
6 ssl_certificate_key /etc/letsencrypt/live/chudnick.com/privkey.pem;
7 add_header Strict-Transport-Security "max-age=31536000" always;
8 ssl_stapling on;
9 ssl_stapling_verify on;
10
11 # Security / XSS Mitigation Headers
12 add_header X-Frame-Options "SAMEORIGIN";
13 add_header X-XSS-Protection "1; mode=block";
14 add_header X-Content-Type-Options "nosniff";
15
16 # authelia
17 include /etc/nginx/snippets/authelia-location.conf;
18
19 location / {
20 # authelia
21 include /etc/nginx/snippets/proxy.conf;
22 include /etc/nginx/snippets/authelia-authrequest.conf;
23
24 proxy_pass http://127.0.0.1:8001/;
25 }
26
27}
28
29server {
30 listen 80;
31 listen [::]:80;
32 server_name dashboard.chudnick.com;
33 return 301 https://$host$request_uri;
34}
diff --git a/data/homer/png/3cx.png b/data/homer/png/3cx.png
new file mode 100644
index 0000000..b573dd1
--- /dev/null
+++ b/data/homer/png/3cx.png
Binary files differ
diff --git a/data/homer/png/SHODAN.jpg b/data/homer/png/SHODAN.jpg
new file mode 100644
index 0000000..d3b192d
--- /dev/null
+++ b/data/homer/png/SHODAN.jpg
Binary files differ
diff --git a/data/homer/png/adguardhome.png b/data/homer/png/adguardhome.png
new file mode 100644
index 0000000..a76bd37
--- /dev/null
+++ b/data/homer/png/adguardhome.png
Binary files differ
diff --git a/data/homer/png/adminer.png b/data/homer/png/adminer.png
new file mode 100644
index 0000000..65b3aab
--- /dev/null
+++ b/data/homer/png/adminer.png
Binary files differ
diff --git a/data/homer/png/airsonic.png b/data/homer/png/airsonic.png
new file mode 100644
index 0000000..76056ea
--- /dev/null
+++ b/data/homer/png/airsonic.png
Binary files differ
diff --git a/data/homer/png/alarmpi.png b/data/homer/png/alarmpi.png
new file mode 100644
index 0000000..34d6fe1
--- /dev/null
+++ b/data/homer/png/alarmpi.png
Binary files differ
diff --git a/data/homer/png/alertmanager.png b/data/homer/png/alertmanager.png
new file mode 100644
index 0000000..68bba82
--- /dev/null
+++ b/data/homer/png/alertmanager.png
Binary files differ
diff --git a/data/homer/png/alltube.png b/data/homer/png/alltube.png
new file mode 100644
index 0000000..2a8e0d1
--- /dev/null
+++ b/data/homer/png/alltube.png
Binary files differ
diff --git a/data/homer/png/amazon.png b/data/homer/png/amazon.png
new file mode 100644
index 0000000..bfd82b0
--- /dev/null
+++ b/data/homer/png/amazon.png
Binary files differ
diff --git a/data/homer/png/amd.png b/data/homer/png/amd.png
new file mode 100644
index 0000000..5acf7a7
--- /dev/null
+++ b/data/homer/png/amd.png
Binary files differ
diff --git a/data/homer/png/amvd.png b/data/homer/png/amvd.png
new file mode 100644
index 0000000..878f01e
--- /dev/null
+++ b/data/homer/png/amvd.png
Binary files differ
diff --git a/data/homer/png/ansible.png b/data/homer/png/ansible.png
new file mode 100644
index 0000000..bcabdd2
--- /dev/null
+++ b/data/homer/png/ansible.png
Binary files differ
diff --git a/data/homer/png/archivebox.png b/data/homer/png/archivebox.png
new file mode 100644
index 0000000..7726d0e
--- /dev/null
+++ b/data/homer/png/archivebox.png
Binary files differ
diff --git a/data/homer/png/archiveteamwarrior.png b/data/homer/png/archiveteamwarrior.png
new file mode 100644
index 0000000..b6c1a7d
--- /dev/null
+++ b/data/homer/png/archiveteamwarrior.png
Binary files differ
diff --git a/data/homer/png/argocd.png b/data/homer/png/argocd.png
new file mode 100644
index 0000000..46f71a3
--- /dev/null
+++ b/data/homer/png/argocd.png
Binary files differ
diff --git a/data/homer/png/ariang.png b/data/homer/png/ariang.png
new file mode 100644
index 0000000..a2ed4d4
--- /dev/null
+++ b/data/homer/png/ariang.png
Binary files differ
diff --git a/data/homer/png/artifactory.png b/data/homer/png/artifactory.png
new file mode 100644
index 0000000..04c1a9f
--- /dev/null
+++ b/data/homer/png/artifactory.png
Binary files differ
diff --git a/data/homer/png/authelia.png b/data/homer/png/authelia.png
new file mode 100644
index 0000000..5262f67
--- /dev/null
+++ b/data/homer/png/authelia.png
Binary files differ
diff --git a/data/homer/png/avmfritzbox.png b/data/homer/png/avmfritzbox.png
new file mode 100644
index 0000000..61a30b5
--- /dev/null
+++ b/data/homer/png/avmfritzbox.png
Binary files differ
diff --git a/data/homer/png/awx.png b/data/homer/png/awx.png
new file mode 100644
index 0000000..d5b5b23
--- /dev/null
+++ b/data/homer/png/awx.png
Binary files differ
diff --git a/data/homer/png/azure.png b/data/homer/png/azure.png
new file mode 100644
index 0000000..2c49795
--- /dev/null
+++ b/data/homer/png/azure.png
Binary files differ
diff --git a/data/homer/png/azuredns.png b/data/homer/png/azuredns.png
new file mode 100644
index 0000000..7b49bed
--- /dev/null
+++ b/data/homer/png/azuredns.png
Binary files differ
diff --git a/data/homer/png/bacula.png b/data/homer/png/bacula.png
new file mode 100644
index 0000000..7bc766e
--- /dev/null
+++ b/data/homer/png/bacula.png
Binary files differ
diff --git a/data/homer/png/badge.png b/data/homer/png/badge.png
new file mode 100644
index 0000000..8687ffe
--- /dev/null
+++ b/data/homer/png/badge.png
Binary files differ
diff --git a/data/homer/png/baikal.png b/data/homer/png/baikal.png
new file mode 100644
index 0000000..5eac889
--- /dev/null
+++ b/data/homer/png/baikal.png
Binary files differ
diff --git a/data/homer/png/bastillion.png b/data/homer/png/bastillion.png
new file mode 100644
index 0000000..4ef96cd
--- /dev/null
+++ b/data/homer/png/bastillion.png
Binary files differ
diff --git a/data/homer/png/bazarr.png b/data/homer/png/bazarr.png
new file mode 100644
index 0000000..9bcaa29
--- /dev/null
+++ b/data/homer/png/bazarr.png
Binary files differ
diff --git a/data/homer/png/beats.png b/data/homer/png/beats.png
new file mode 100644
index 0000000..ba6f115
--- /dev/null
+++ b/data/homer/png/beats.png
Binary files differ
diff --git a/data/homer/png/bithumen.png b/data/homer/png/bithumen.png
new file mode 100644
index 0000000..611a1aa
--- /dev/null
+++ b/data/homer/png/bithumen.png
Binary files differ
diff --git a/data/homer/png/bitwarden.png b/data/homer/png/bitwarden.png
new file mode 100644
index 0000000..e8a1c7c
--- /dev/null
+++ b/data/homer/png/bitwarden.png
Binary files differ
diff --git a/data/homer/png/blueiris.png b/data/homer/png/blueiris.png
new file mode 100644
index 0000000..841baef
--- /dev/null
+++ b/data/homer/png/blueiris.png
Binary files differ
diff --git a/data/homer/png/booksonic.png b/data/homer/png/booksonic.png
new file mode 100644
index 0000000..8b29966
--- /dev/null
+++ b/data/homer/png/booksonic.png
Binary files differ
diff --git a/data/homer/png/bookstack.png b/data/homer/png/bookstack.png
new file mode 100644
index 0000000..5791636
--- /dev/null
+++ b/data/homer/png/bookstack.png
Binary files differ
diff --git a/data/homer/png/box.png b/data/homer/png/box.png
new file mode 100644
index 0000000..aaa7996
--- /dev/null
+++ b/data/homer/png/box.png
Binary files differ
diff --git a/data/homer/png/brewpi.png b/data/homer/png/brewpi.png
new file mode 100644
index 0000000..0fbab4e
--- /dev/null
+++ b/data/homer/png/brewpi.png
Binary files differ
diff --git a/data/homer/png/buxfer.png b/data/homer/png/buxfer.png
new file mode 100644
index 0000000..17eaf27
--- /dev/null
+++ b/data/homer/png/buxfer.png
Binary files differ
diff --git a/data/homer/png/cabot.png b/data/homer/png/cabot.png
new file mode 100644
index 0000000..bd0c51b
--- /dev/null
+++ b/data/homer/png/cabot.png
Binary files differ
diff --git a/data/homer/png/cadvisor.png b/data/homer/png/cadvisor.png
new file mode 100644
index 0000000..0f30e05
--- /dev/null
+++ b/data/homer/png/cadvisor.png
Binary files differ
diff --git a/data/homer/png/calibreweb.png b/data/homer/png/calibreweb.png
new file mode 100644
index 0000000..a8424ee
--- /dev/null
+++ b/data/homer/png/calibreweb.png
Binary files differ
diff --git a/data/homer/png/cardigann.png b/data/homer/png/cardigann.png
new file mode 100644
index 0000000..0ced4e0
--- /dev/null
+++ b/data/homer/png/cardigann.png
Binary files differ
diff --git a/data/homer/png/cgit.png b/data/homer/png/cgit.png
new file mode 100644
index 0000000..425528e
--- /dev/null
+++ b/data/homer/png/cgit.png
Binary files differ
diff --git a/data/homer/png/checkmk.png b/data/homer/png/checkmk.png
new file mode 100644
index 0000000..290aea0
--- /dev/null
+++ b/data/homer/png/checkmk.png
Binary files differ
diff --git a/data/homer/png/chevereto.png b/data/homer/png/chevereto.png
new file mode 100644
index 0000000..cbc0240
--- /dev/null
+++ b/data/homer/png/chevereto.png
Binary files differ
diff --git a/data/homer/png/chowdown.png b/data/homer/png/chowdown.png
new file mode 100644
index 0000000..fb57a28
--- /dev/null
+++ b/data/homer/png/chowdown.png
Binary files differ
diff --git a/data/homer/png/chronograf.png b/data/homer/png/chronograf.png
new file mode 100644
index 0000000..3c0d0d1
--- /dev/null
+++ b/data/homer/png/chronograf.png
Binary files differ
diff --git a/data/homer/png/chudnick.com.png b/data/homer/png/chudnick.com.png
new file mode 100644
index 0000000..c3da2ef
--- /dev/null
+++ b/data/homer/png/chudnick.com.png
Binary files differ
diff --git a/data/homer/png/clarkson.png b/data/homer/png/clarkson.png
new file mode 100644
index 0000000..f265690
--- /dev/null
+++ b/data/homer/png/clarkson.png
Binary files differ
diff --git a/data/homer/png/cloudcmd.png b/data/homer/png/cloudcmd.png
new file mode 100644
index 0000000..93d5373
--- /dev/null
+++ b/data/homer/png/cloudcmd.png
Binary files differ
diff --git a/data/homer/png/cockpit.png b/data/homer/png/cockpit.png
new file mode 100644
index 0000000..8efd9b7
--- /dev/null
+++ b/data/homer/png/cockpit.png
Binary files differ
diff --git a/data/homer/png/cockpitcms.png b/data/homer/png/cockpitcms.png
new file mode 100644
index 0000000..5649406
--- /dev/null
+++ b/data/homer/png/cockpitcms.png
Binary files differ
diff --git a/data/homer/png/code.png b/data/homer/png/code.png
new file mode 100644
index 0000000..f6c6e06
--- /dev/null
+++ b/data/homer/png/code.png
Binary files differ
diff --git a/data/homer/png/codeserver.png b/data/homer/png/codeserver.png
new file mode 100644
index 0000000..795fc14
--- /dev/null
+++ b/data/homer/png/codeserver.png
Binary files differ
diff --git a/data/homer/png/codimd.png b/data/homer/png/codimd.png
new file mode 100644
index 0000000..c352d64
--- /dev/null
+++ b/data/homer/png/codimd.png
Binary files differ
diff --git a/data/homer/png/concourse.png b/data/homer/png/concourse.png
new file mode 100644
index 0000000..288a7fe
--- /dev/null
+++ b/data/homer/png/concourse.png
Binary files differ
diff --git a/data/homer/png/couchpotato.png b/data/homer/png/couchpotato.png
new file mode 100644
index 0000000..806d609
--- /dev/null
+++ b/data/homer/png/couchpotato.png
Binary files differ
diff --git a/data/homer/png/cpanel.png b/data/homer/png/cpanel.png
new file mode 100644
index 0000000..b6dc660
--- /dev/null
+++ b/data/homer/png/cpanel.png
Binary files differ
diff --git a/data/homer/png/cryptpad.png b/data/homer/png/cryptpad.png
new file mode 100644
index 0000000..722438d
--- /dev/null
+++ b/data/homer/png/cryptpad.png
Binary files differ
diff --git a/data/homer/png/cyberchef.png b/data/homer/png/cyberchef.png
new file mode 100644
index 0000000..eb18228
--- /dev/null
+++ b/data/homer/png/cyberchef.png
Binary files differ
diff --git a/data/homer/png/deemix.png b/data/homer/png/deemix.png
new file mode 100644
index 0000000..ea3b45f
--- /dev/null
+++ b/data/homer/png/deemix.png
Binary files differ
diff --git a/data/homer/png/deluge.png b/data/homer/png/deluge.png
new file mode 100644
index 0000000..b79fd94
--- /dev/null
+++ b/data/homer/png/deluge.png
Binary files differ
diff --git a/data/homer/png/directus.png b/data/homer/png/directus.png
new file mode 100644
index 0000000..2c9ae11
--- /dev/null
+++ b/data/homer/png/directus.png
Binary files differ
diff --git a/data/homer/png/docker.png b/data/homer/png/docker.png
new file mode 100644
index 0000000..dcce1ac
--- /dev/null
+++ b/data/homer/png/docker.png
Binary files differ
diff --git a/data/homer/png/docspell.png b/data/homer/png/docspell.png
new file mode 100644
index 0000000..ee43a05
--- /dev/null
+++ b/data/homer/png/docspell.png
Binary files differ
diff --git a/data/homer/png/dokuwiki.png b/data/homer/png/dokuwiki.png
new file mode 100644
index 0000000..9691b09
--- /dev/null
+++ b/data/homer/png/dokuwiki.png
Binary files differ
diff --git a/data/homer/png/domoticz.png b/data/homer/png/domoticz.png
new file mode 100644
index 0000000..35090c8
--- /dev/null
+++ b/data/homer/png/domoticz.png
Binary files differ
diff --git a/data/homer/png/dozzle.png b/data/homer/png/dozzle.png
new file mode 100644
index 0000000..7303e86
--- /dev/null
+++ b/data/homer/png/dozzle.png
Binary files differ
diff --git a/data/homer/png/drawio.png b/data/homer/png/drawio.png
new file mode 100644
index 0000000..fc553bc
--- /dev/null
+++ b/data/homer/png/drawio.png
Binary files differ
diff --git a/data/homer/png/drone.png b/data/homer/png/drone.png
new file mode 100644
index 0000000..0340c02
--- /dev/null
+++ b/data/homer/png/drone.png
Binary files differ
diff --git a/data/homer/png/droppy.png b/data/homer/png/droppy.png
new file mode 100644
index 0000000..adbce01
--- /dev/null
+++ b/data/homer/png/droppy.png
Binary files differ
diff --git a/data/homer/png/duplicacy.png b/data/homer/png/duplicacy.png
new file mode 100644
index 0000000..9da4edd
--- /dev/null
+++ b/data/homer/png/duplicacy.png
Binary files differ
diff --git a/data/homer/png/duplicati.png b/data/homer/png/duplicati.png
new file mode 100644
index 0000000..fa5ef33
--- /dev/null
+++ b/data/homer/png/duplicati.png
Binary files differ
diff --git a/data/homer/png/ebay.png b/data/homer/png/ebay.png
new file mode 100644
index 0000000..97a9fdf
--- /dev/null
+++ b/data/homer/png/ebay.png
Binary files differ
diff --git a/data/homer/png/elastic.png b/data/homer/png/elastic.png
new file mode 100644
index 0000000..11d655f
--- /dev/null
+++ b/data/homer/png/elastic.png
Binary files differ
diff --git a/data/homer/png/elasticsearch.png b/data/homer/png/elasticsearch.png
new file mode 100644
index 0000000..faa897c
--- /dev/null
+++ b/data/homer/png/elasticsearch.png
Binary files differ
diff --git a/data/homer/png/element.png b/data/homer/png/element.png
new file mode 100644
index 0000000..d2c8e09
--- /dev/null
+++ b/data/homer/png/element.png
Binary files differ
diff --git a/data/homer/png/emby.png b/data/homer/png/emby.png
new file mode 100644
index 0000000..5c34a84
--- /dev/null
+++ b/data/homer/png/emby.png
Binary files differ
diff --git a/data/homer/png/embystat.png b/data/homer/png/embystat.png
new file mode 100644
index 0000000..5a9a8e7
--- /dev/null
+++ b/data/homer/png/embystat.png
Binary files differ
diff --git a/data/homer/png/emq.png b/data/homer/png/emq.png
new file mode 100644
index 0000000..075cc5a
--- /dev/null
+++ b/data/homer/png/emq.png
Binary files differ
diff --git a/data/homer/png/erste-george.png b/data/homer/png/erste-george.png
new file mode 100644
index 0000000..fc3a16c
--- /dev/null
+++ b/data/homer/png/erste-george.png
Binary files differ
diff --git a/data/homer/png/erste.png b/data/homer/png/erste.png
new file mode 100644
index 0000000..a003ffa
--- /dev/null
+++ b/data/homer/png/erste.png
Binary files differ
diff --git a/data/homer/png/esphome.png b/data/homer/png/esphome.png
new file mode 100644
index 0000000..f71ec0f
--- /dev/null
+++ b/data/homer/png/esphome.png
Binary files differ
diff --git a/data/homer/png/evebox.png b/data/homer/png/evebox.png
new file mode 100644
index 0000000..d14f128
--- /dev/null
+++ b/data/homer/png/evebox.png
Binary files differ
diff --git a/data/homer/png/facebook-messenger.png b/data/homer/png/facebook-messenger.png
new file mode 100644
index 0000000..c41e435
--- /dev/null
+++ b/data/homer/png/facebook-messenger.png
Binary files differ
diff --git a/data/homer/png/facebook.png b/data/homer/png/facebook.png
new file mode 100644
index 0000000..103e5a7
--- /dev/null
+++ b/data/homer/png/facebook.png
Binary files differ
diff --git a/data/homer/png/filebrowser.png b/data/homer/png/filebrowser.png
new file mode 100644
index 0000000..7c50740
--- /dev/null
+++ b/data/homer/png/filebrowser.png
Binary files differ
diff --git a/data/homer/png/filerun.png b/data/homer/png/filerun.png
new file mode 100644
index 0000000..6bab1e3
--- /dev/null
+++ b/data/homer/png/filerun.png
Binary files differ
diff --git a/data/homer/png/firefly.png b/data/homer/png/firefly.png
new file mode 100644
index 0000000..4a95a4f
--- /dev/null
+++ b/data/homer/png/firefly.png
Binary files differ
diff --git a/data/homer/png/firefoxsend.png b/data/homer/png/firefoxsend.png
new file mode 100644
index 0000000..f23d835
--- /dev/null
+++ b/data/homer/png/firefoxsend.png
Binary files differ
diff --git a/data/homer/png/flexget.png b/data/homer/png/flexget.png
new file mode 100644
index 0000000..b3d6654
--- /dev/null
+++ b/data/homer/png/flexget.png
Binary files differ
diff --git a/data/homer/png/flood.png b/data/homer/png/flood.png
new file mode 100644
index 0000000..df96245
--- /dev/null
+++ b/data/homer/png/flood.png
Binary files differ
diff --git a/data/homer/png/foldingathome.png b/data/homer/png/foldingathome.png
new file mode 100644
index 0000000..14c815f
--- /dev/null
+++ b/data/homer/png/foldingathome.png
Binary files differ
diff --git a/data/homer/png/freeipa.png b/data/homer/png/freeipa.png
new file mode 100644
index 0000000..b279d00
--- /dev/null
+++ b/data/homer/png/freeipa.png
Binary files differ
diff --git a/data/homer/png/freenas.png b/data/homer/png/freenas.png
new file mode 100644
index 0000000..d97589b
--- /dev/null
+++ b/data/homer/png/freenas.png
Binary files differ
diff --git a/data/homer/png/freepbx.png b/data/homer/png/freepbx.png
new file mode 100644
index 0000000..0bbbd9c
--- /dev/null
+++ b/data/homer/png/freepbx.png
Binary files differ
diff --git a/data/homer/png/freshrss.png b/data/homer/png/freshrss.png
new file mode 100644
index 0000000..3e54369
--- /dev/null
+++ b/data/homer/png/freshrss.png
Binary files differ
diff --git a/data/homer/png/ghost.png b/data/homer/png/ghost.png
new file mode 100644
index 0000000..d87ffd6
--- /dev/null
+++ b/data/homer/png/ghost.png
Binary files differ
diff --git a/data/homer/png/gitea.png b/data/homer/png/gitea.png
new file mode 100644
index 0000000..dd3e97c
--- /dev/null
+++ b/data/homer/png/gitea.png
Binary files differ
diff --git a/data/homer/png/github.png b/data/homer/png/github.png
new file mode 100644
index 0000000..144ebb6
--- /dev/null
+++ b/data/homer/png/github.png
Binary files differ
diff --git a/data/homer/png/gitlab.png b/data/homer/png/gitlab.png
new file mode 100644
index 0000000..1896ac0
--- /dev/null
+++ b/data/homer/png/gitlab.png
Binary files differ
diff --git a/data/homer/png/glances.png b/data/homer/png/glances.png
new file mode 100644
index 0000000..12f5cbe
--- /dev/null
+++ b/data/homer/png/glances.png
Binary files differ
diff --git a/data/homer/png/gogs.png b/data/homer/png/gogs.png
new file mode 100644
index 0000000..4baf060
--- /dev/null
+++ b/data/homer/png/gogs.png
Binary files differ
diff --git a/data/homer/png/google-calendar.png b/data/homer/png/google-calendar.png
new file mode 100644
index 0000000..8705504
--- /dev/null
+++ b/data/homer/png/google-calendar.png
Binary files differ
diff --git a/data/homer/png/google-keep.png b/data/homer/png/google-keep.png
new file mode 100644
index 0000000..4380656
--- /dev/null
+++ b/data/homer/png/google-keep.png
Binary files differ
diff --git a/data/homer/png/google-mail.png b/data/homer/png/google-mail.png
new file mode 100644
index 0000000..ff1d7ce
--- /dev/null
+++ b/data/homer/png/google-mail.png
Binary files differ
diff --git a/data/homer/png/googlemaps.png b/data/homer/png/googlemaps.png
new file mode 100644
index 0000000..85af7d3
--- /dev/null
+++ b/data/homer/png/googlemaps.png
Binary files differ
diff --git a/data/homer/png/gotify.png b/data/homer/png/gotify.png
new file mode 100644
index 0000000..af45291
--- /dev/null
+++ b/data/homer/png/gotify.png
Binary files differ
diff --git a/data/homer/png/grafana.png b/data/homer/png/grafana.png
new file mode 100644
index 0000000..c9d5215
--- /dev/null
+++ b/data/homer/png/grafana.png
Binary files differ
diff --git a/data/homer/png/grav.png b/data/homer/png/grav.png
new file mode 100644
index 0000000..6cf87cb
--- /dev/null
+++ b/data/homer/png/grav.png
Binary files differ
diff --git a/data/homer/png/graylog.png b/data/homer/png/graylog.png
new file mode 100644
index 0000000..dabd59d
--- /dev/null
+++ b/data/homer/png/graylog.png
Binary files differ
diff --git a/data/homer/png/grocy.png b/data/homer/png/grocy.png
new file mode 100644
index 0000000..1fed6d7
--- /dev/null
+++ b/data/homer/png/grocy.png
Binary files differ
diff --git a/data/homer/png/guacamole.png b/data/homer/png/guacamole.png
new file mode 100644
index 0000000..69dd992
--- /dev/null
+++ b/data/homer/png/guacamole.png
Binary files differ
diff --git a/data/homer/png/handbrake.png b/data/homer/png/handbrake.png
new file mode 100644
index 0000000..ea7f65a
--- /dev/null
+++ b/data/homer/png/handbrake.png
Binary files differ
diff --git a/data/homer/png/haproxy.png b/data/homer/png/haproxy.png
new file mode 100644
index 0000000..1da94bf
--- /dev/null
+++ b/data/homer/png/haproxy.png
Binary files differ
diff --git a/data/homer/png/hasura.png b/data/homer/png/hasura.png
new file mode 100644
index 0000000..ff2e9a3
--- /dev/null
+++ b/data/homer/png/hasura.png
Binary files differ
diff --git a/data/homer/png/hdhomerun.png b/data/homer/png/hdhomerun.png
new file mode 100644
index 0000000..ff18b33
--- /dev/null
+++ b/data/homer/png/hdhomerun.png
Binary files differ
diff --git a/data/homer/png/headphones.png b/data/homer/png/headphones.png
new file mode 100644
index 0000000..f4b48b2
--- /dev/null
+++ b/data/homer/png/headphones.png
Binary files differ
diff --git a/data/homer/png/healthchecks.png b/data/homer/png/healthchecks.png
new file mode 100644
index 0000000..3d7696b
--- /dev/null
+++ b/data/homer/png/healthchecks.png
Binary files differ
diff --git a/data/homer/png/heimdall.png b/data/homer/png/heimdall.png
new file mode 100644
index 0000000..8cc4073
--- /dev/null
+++ b/data/homer/png/heimdall.png
Binary files differ
diff --git a/data/homer/png/home-assistant.png b/data/homer/png/home-assistant.png
new file mode 100644
index 0000000..76d56a2
--- /dev/null
+++ b/data/homer/png/home-assistant.png
Binary files differ
diff --git a/data/homer/png/homebridge.png b/data/homer/png/homebridge.png
new file mode 100644
index 0000000..5f1f334
--- /dev/null
+++ b/data/homer/png/homebridge.png
Binary files differ
diff --git a/data/homer/png/homer.png b/data/homer/png/homer.png
new file mode 100644
index 0000000..279392e
--- /dev/null
+++ b/data/homer/png/homer.png
Binary files differ
diff --git a/data/homer/png/hp.png b/data/homer/png/hp.png
new file mode 100644
index 0000000..f0558ab
--- /dev/null
+++ b/data/homer/png/hp.png
Binary files differ
diff --git a/data/homer/png/hubitat.png b/data/homer/png/hubitat.png
new file mode 100644
index 0000000..6384698
--- /dev/null
+++ b/data/homer/png/hubitat.png
Binary files differ
diff --git a/data/homer/png/huginn.png b/data/homer/png/huginn.png
new file mode 100644
index 0000000..15aca01
--- /dev/null
+++ b/data/homer/png/huginn.png
Binary files differ
diff --git a/data/homer/png/hugo.png b/data/homer/png/hugo.png
new file mode 100644
index 0000000..50e23ce
--- /dev/null
+++ b/data/homer/png/hugo.png
Binary files differ
diff --git a/data/homer/png/hydra.png b/data/homer/png/hydra.png
new file mode 100644
index 0000000..c527d84
--- /dev/null
+++ b/data/homer/png/hydra.png
Binary files differ
diff --git a/data/homer/png/icecast.png b/data/homer/png/icecast.png
new file mode 100644
index 0000000..3dafa79
--- /dev/null
+++ b/data/homer/png/icecast.png
Binary files differ
diff --git a/data/homer/png/icinga.png b/data/homer/png/icinga.png
new file mode 100644
index 0000000..cc29363
--- /dev/null
+++ b/data/homer/png/icinga.png
Binary files differ
diff --git a/data/homer/png/idrac.png b/data/homer/png/idrac.png
new file mode 100644
index 0000000..4b3d446
--- /dev/null
+++ b/data/homer/png/idrac.png
Binary files differ
diff --git a/data/homer/png/ilo.png b/data/homer/png/ilo.png
new file mode 100644
index 0000000..ca329ac
--- /dev/null
+++ b/data/homer/png/ilo.png
Binary files differ
diff --git a/data/homer/png/infoblox.png b/data/homer/png/infoblox.png
new file mode 100644
index 0000000..94c2c7f
--- /dev/null
+++ b/data/homer/png/infoblox.png
Binary files differ
diff --git a/data/homer/png/invidious.png b/data/homer/png/invidious.png
new file mode 100644
index 0000000..f747efa
--- /dev/null
+++ b/data/homer/png/invidious.png
Binary files differ
diff --git a/data/homer/png/invoiceninja.png b/data/homer/png/invoiceninja.png
new file mode 100644
index 0000000..db1a900
--- /dev/null
+++ b/data/homer/png/invoiceninja.png
Binary files differ
diff --git a/data/homer/png/iobroker.png b/data/homer/png/iobroker.png
new file mode 100644
index 0000000..d9c1587
--- /dev/null
+++ b/data/homer/png/iobroker.png
Binary files differ
diff --git a/data/homer/png/irc.png b/data/homer/png/irc.png
new file mode 100644
index 0000000..c2623d4
--- /dev/null
+++ b/data/homer/png/irc.png
Binary files differ
diff --git a/data/homer/png/jackett.png b/data/homer/png/jackett.png
new file mode 100644
index 0000000..6cd3bc3
--- /dev/null
+++ b/data/homer/png/jackett.png
Binary files differ
diff --git a/data/homer/png/jaeger.png b/data/homer/png/jaeger.png
new file mode 100644
index 0000000..d819360
--- /dev/null
+++ b/data/homer/png/jaeger.png
Binary files differ
diff --git a/data/homer/png/jdownloader.png b/data/homer/png/jdownloader.png
new file mode 100644
index 0000000..8a048d2
--- /dev/null
+++ b/data/homer/png/jdownloader.png
Binary files differ
diff --git a/data/homer/png/jeedom.png b/data/homer/png/jeedom.png
new file mode 100644
index 0000000..bc7c648
--- /dev/null
+++ b/data/homer/png/jeedom.png
Binary files differ
diff --git a/data/homer/png/jellyfin.png b/data/homer/png/jellyfin.png
new file mode 100644
index 0000000..a46021f
--- /dev/null
+++ b/data/homer/png/jellyfin.png
Binary files differ
diff --git a/data/homer/png/jenkins.png b/data/homer/png/jenkins.png
new file mode 100644
index 0000000..6afeb65
--- /dev/null
+++ b/data/homer/png/jenkins.png
Binary files differ
diff --git a/data/homer/png/jitsimeet.png b/data/homer/png/jitsimeet.png
new file mode 100644
index 0000000..d48e45f
--- /dev/null
+++ b/data/homer/png/jitsimeet.png
Binary files differ
diff --git a/data/homer/png/joomla.png b/data/homer/png/joomla.png
new file mode 100644
index 0000000..a493240
--- /dev/null
+++ b/data/homer/png/joomla.png
Binary files differ
diff --git a/data/homer/png/joplin.png b/data/homer/png/joplin.png
new file mode 100644
index 0000000..29a4c32
--- /dev/null
+++ b/data/homer/png/joplin.png
Binary files differ
diff --git a/data/homer/png/kanboard.png b/data/homer/png/kanboard.png
new file mode 100644
index 0000000..c4159c8
--- /dev/null
+++ b/data/homer/png/kanboard.png
Binary files differ
diff --git a/data/homer/png/kavita.png b/data/homer/png/kavita.png
new file mode 100644
index 0000000..68c4ac5
--- /dev/null
+++ b/data/homer/png/kavita.png
Binary files differ
diff --git a/data/homer/png/keila.png b/data/homer/png/keila.png
new file mode 100644
index 0000000..196e330
--- /dev/null
+++ b/data/homer/png/keila.png
Binary files differ
diff --git a/data/homer/png/keycloak.png b/data/homer/png/keycloak.png
new file mode 100644
index 0000000..8470893
--- /dev/null
+++ b/data/homer/png/keycloak.png
Binary files differ
diff --git a/data/homer/png/kibana.png b/data/homer/png/kibana.png
new file mode 100644
index 0000000..c38b32b
--- /dev/null
+++ b/data/homer/png/kibana.png
Binary files differ
diff --git a/data/homer/png/kimai.png b/data/homer/png/kimai.png
new file mode 100644
index 0000000..ae0367e
--- /dev/null
+++ b/data/homer/png/kimai.png
Binary files differ
diff --git a/data/homer/png/kitana.png b/data/homer/png/kitana.png
new file mode 100644
index 0000000..45e25c8
--- /dev/null
+++ b/data/homer/png/kitana.png
Binary files differ
diff --git a/data/homer/png/kodi.png b/data/homer/png/kodi.png
new file mode 100644
index 0000000..502d22f
--- /dev/null
+++ b/data/homer/png/kodi.png
Binary files differ
diff --git a/data/homer/png/komga.png b/data/homer/png/komga.png
new file mode 100644
index 0000000..51d2cf6
--- /dev/null
+++ b/data/homer/png/komga.png
Binary files differ
diff --git a/data/homer/png/krusader.png b/data/homer/png/krusader.png
new file mode 100644
index 0000000..c1120dc
--- /dev/null
+++ b/data/homer/png/krusader.png
Binary files differ
diff --git a/data/homer/png/kubernetes-dashboard.png b/data/homer/png/kubernetes-dashboard.png
new file mode 100644
index 0000000..86c5a33
--- /dev/null
+++ b/data/homer/png/kubernetes-dashboard.png
Binary files differ
diff --git a/data/homer/png/kutt.png b/data/homer/png/kutt.png
new file mode 100644
index 0000000..be751ca
--- /dev/null
+++ b/data/homer/png/kutt.png
Binary files differ
diff --git a/data/homer/png/lazylibrarian.png b/data/homer/png/lazylibrarian.png
new file mode 100644
index 0000000..2799680
--- /dev/null
+++ b/data/homer/png/lazylibrarian.png
Binary files differ
diff --git a/data/homer/png/leantime.png b/data/homer/png/leantime.png
new file mode 100644
index 0000000..f52bf4b
--- /dev/null
+++ b/data/homer/png/leantime.png
Binary files differ
diff --git a/data/homer/png/lemonldapng.png b/data/homer/png/lemonldapng.png
new file mode 100644
index 0000000..4beec3f
--- /dev/null
+++ b/data/homer/png/lemonldapng.png
Binary files differ
diff --git a/data/homer/png/letencrypt.png b/data/homer/png/letencrypt.png
new file mode 100644
index 0000000..8d4c411
--- /dev/null
+++ b/data/homer/png/letencrypt.png
Binary files differ
diff --git a/data/homer/png/librenms.png b/data/homer/png/librenms.png
new file mode 100644
index 0000000..94799e9
--- /dev/null
+++ b/data/homer/png/librenms.png
Binary files differ
diff --git a/data/homer/png/librephotos.png b/data/homer/png/librephotos.png
new file mode 100644
index 0000000..6419cfc
--- /dev/null
+++ b/data/homer/png/librephotos.png
Binary files differ
diff --git a/data/homer/png/librespeed.png b/data/homer/png/librespeed.png
new file mode 100644
index 0000000..a5b71d4
--- /dev/null
+++ b/data/homer/png/librespeed.png
Binary files differ
diff --git a/data/homer/png/lidarr.png b/data/homer/png/lidarr.png
new file mode 100644
index 0000000..af06a1c
--- /dev/null
+++ b/data/homer/png/lidarr.png
Binary files differ
diff --git a/data/homer/png/listmonk.png b/data/homer/png/listmonk.png
new file mode 100644
index 0000000..a561a8a
--- /dev/null
+++ b/data/homer/png/listmonk.png
Binary files differ
diff --git a/data/homer/png/logstash.png b/data/homer/png/logstash.png
new file mode 100644
index 0000000..27f15ab
--- /dev/null
+++ b/data/homer/png/logstash.png
Binary files differ
diff --git a/data/homer/png/loki.png b/data/homer/png/loki.png
new file mode 100644
index 0000000..d63dcdb
--- /dev/null
+++ b/data/homer/png/loki.png
Binary files differ
diff --git a/data/homer/png/longhorn.png b/data/homer/png/longhorn.png
new file mode 100644
index 0000000..ccaa161
--- /dev/null
+++ b/data/homer/png/longhorn.png
Binary files differ
diff --git a/data/homer/png/lychee.png b/data/homer/png/lychee.png
new file mode 100644
index 0000000..04b08b1
--- /dev/null
+++ b/data/homer/png/lychee.png
Binary files differ
diff --git a/data/homer/png/mailcow.png b/data/homer/png/mailcow.png
new file mode 100644
index 0000000..a03de2d
--- /dev/null
+++ b/data/homer/png/mailcow.png
Binary files differ
diff --git a/data/homer/png/mailhog.png b/data/homer/png/mailhog.png
new file mode 100644
index 0000000..371a72a
--- /dev/null
+++ b/data/homer/png/mailhog.png
Binary files differ
diff --git a/data/homer/png/mainsail.png b/data/homer/png/mainsail.png
new file mode 100644
index 0000000..1d1d6b5
--- /dev/null
+++ b/data/homer/png/mainsail.png
Binary files differ
diff --git a/data/homer/png/mak.png b/data/homer/png/mak.png
new file mode 100644
index 0000000..4e11d88
--- /dev/null
+++ b/data/homer/png/mak.png
Binary files differ
diff --git a/data/homer/png/mattermost.png b/data/homer/png/mattermost.png
new file mode 100644
index 0000000..ea7ed30
--- /dev/null
+++ b/data/homer/png/mattermost.png
Binary files differ
diff --git a/data/homer/png/mayanedms.png b/data/homer/png/mayanedms.png
new file mode 100644
index 0000000..6e5d4b5
--- /dev/null
+++ b/data/homer/png/mayanedms.png
Binary files differ
diff --git a/data/homer/png/mcmyadmin.png b/data/homer/png/mcmyadmin.png
new file mode 100644
index 0000000..01f45fa
--- /dev/null
+++ b/data/homer/png/mcmyadmin.png
Binary files differ
diff --git a/data/homer/png/mealie.png b/data/homer/png/mealie.png
new file mode 100644
index 0000000..c317e4e
--- /dev/null
+++ b/data/homer/png/mealie.png
Binary files differ
diff --git a/data/homer/png/mediawiki.png b/data/homer/png/mediawiki.png
new file mode 100644
index 0000000..c8c0b3d
--- /dev/null
+++ b/data/homer/png/mediawiki.png
Binary files differ
diff --git a/data/homer/png/medusa.png b/data/homer/png/medusa.png
new file mode 100644
index 0000000..563ee61
--- /dev/null
+++ b/data/homer/png/medusa.png
Binary files differ
diff --git a/data/homer/png/meraki.png b/data/homer/png/meraki.png
new file mode 100644
index 0000000..9b0b967
--- /dev/null
+++ b/data/homer/png/meraki.png
Binary files differ
diff --git a/data/homer/png/microsoft-todo.png b/data/homer/png/microsoft-todo.png
new file mode 100644
index 0000000..ca96cc7
--- /dev/null
+++ b/data/homer/png/microsoft-todo.png
Binary files differ
diff --git a/data/homer/png/mikrotik.png b/data/homer/png/mikrotik.png
new file mode 100644
index 0000000..e3d86a0
--- /dev/null
+++ b/data/homer/png/mikrotik.png
Binary files differ
diff --git a/data/homer/png/mineos.png b/data/homer/png/mineos.png
new file mode 100644
index 0000000..afd0600
--- /dev/null
+++ b/data/homer/png/mineos.png
Binary files differ
diff --git a/data/homer/png/miniflux.png b/data/homer/png/miniflux.png
new file mode 100644
index 0000000..6f714fa
--- /dev/null
+++ b/data/homer/png/miniflux.png
Binary files differ
diff --git a/data/homer/png/minio.png b/data/homer/png/minio.png
new file mode 100644
index 0000000..d1b32be
--- /dev/null
+++ b/data/homer/png/minio.png
Binary files differ
diff --git a/data/homer/png/molecule.png b/data/homer/png/molecule.png
new file mode 100644
index 0000000..8484ab9
--- /dev/null
+++ b/data/homer/png/molecule.png
Binary files differ
diff --git a/data/homer/png/mongodb.png b/data/homer/png/mongodb.png
new file mode 100644
index 0000000..5dae6cf
--- /dev/null
+++ b/data/homer/png/mongodb.png
Binary files differ
diff --git a/data/homer/png/monica.png b/data/homer/png/monica.png
new file mode 100644
index 0000000..afecdf8
--- /dev/null
+++ b/data/homer/png/monica.png
Binary files differ
diff --git a/data/homer/png/monit.png b/data/homer/png/monit.png
new file mode 100644
index 0000000..1aa8c95
--- /dev/null
+++ b/data/homer/png/monit.png
Binary files differ
diff --git a/data/homer/png/motioneye.png b/data/homer/png/motioneye.png
new file mode 100644
index 0000000..c3dd25d
--- /dev/null
+++ b/data/homer/png/motioneye.png
Binary files differ
diff --git a/data/homer/png/mqtt.png b/data/homer/png/mqtt.png
new file mode 100644
index 0000000..28fa991
--- /dev/null
+++ b/data/homer/png/mqtt.png
Binary files differ
diff --git a/data/homer/png/mylar.png b/data/homer/png/mylar.png
new file mode 100644
index 0000000..3a236bb
--- /dev/null
+++ b/data/homer/png/mylar.png
Binary files differ
diff --git a/data/homer/png/n8n.png b/data/homer/png/n8n.png
new file mode 100644
index 0000000..f69eccb
--- /dev/null
+++ b/data/homer/png/n8n.png
Binary files differ
diff --git a/data/homer/png/nagios.png b/data/homer/png/nagios.png
new file mode 100644
index 0000000..5f4dc1d
--- /dev/null
+++ b/data/homer/png/nagios.png
Binary files differ
diff --git a/data/homer/png/navidrome.png b/data/homer/png/navidrome.png
new file mode 100644
index 0000000..8ce76ce
--- /dev/null
+++ b/data/homer/png/navidrome.png
Binary files differ
diff --git a/data/homer/png/ncore.png b/data/homer/png/ncore.png
new file mode 100644
index 0000000..e740ff8
--- /dev/null
+++ b/data/homer/png/ncore.png
Binary files differ
diff --git a/data/homer/png/nessus.png b/data/homer/png/nessus.png
new file mode 100644
index 0000000..9a48986
--- /dev/null
+++ b/data/homer/png/nessus.png
Binary files differ
diff --git a/data/homer/png/netatmo.png b/data/homer/png/netatmo.png
new file mode 100644
index 0000000..d5a3fc5
--- /dev/null
+++ b/data/homer/png/netatmo.png
Binary files differ
diff --git a/data/homer/png/netboot.png b/data/homer/png/netboot.png
new file mode 100644
index 0000000..6d5dcc5
--- /dev/null
+++ b/data/homer/png/netboot.png
Binary files differ
diff --git a/data/homer/png/netbootxyz.png b/data/homer/png/netbootxyz.png
new file mode 100644
index 0000000..a57a899
--- /dev/null
+++ b/data/homer/png/netbootxyz.png
Binary files differ
diff --git a/data/homer/png/netbox.png b/data/homer/png/netbox.png
new file mode 100644
index 0000000..67afcfc
--- /dev/null
+++ b/data/homer/png/netbox.png
Binary files differ
diff --git a/data/homer/png/netdata.png b/data/homer/png/netdata.png
new file mode 100644
index 0000000..4114d23
--- /dev/null
+++ b/data/homer/png/netdata.png
Binary files differ
diff --git a/data/homer/png/nextcloud.png b/data/homer/png/nextcloud.png
new file mode 100644
index 0000000..8ab73f2
--- /dev/null
+++ b/data/homer/png/nextcloud.png
Binary files differ
diff --git a/data/homer/png/nginx.png b/data/homer/png/nginx.png
new file mode 100644
index 0000000..b2b146e
--- /dev/null
+++ b/data/homer/png/nginx.png
Binary files differ
diff --git a/data/homer/png/nginxproxymanager.png b/data/homer/png/nginxproxymanager.png
new file mode 100644
index 0000000..7373317
--- /dev/null
+++ b/data/homer/png/nginxproxymanager.png
Binary files differ
diff --git a/data/homer/png/nodered.png b/data/homer/png/nodered.png
new file mode 100644
index 0000000..6ae1313
--- /dev/null
+++ b/data/homer/png/nodered.png
Binary files differ
diff --git a/data/homer/png/nowshowing.png b/data/homer/png/nowshowing.png
new file mode 100644
index 0000000..de59d71
--- /dev/null
+++ b/data/homer/png/nowshowing.png
Binary files differ
diff --git a/data/homer/png/ntop.png b/data/homer/png/ntop.png
new file mode 100644
index 0000000..1f47d6f
--- /dev/null
+++ b/data/homer/png/ntop.png
Binary files differ
diff --git a/data/homer/png/nxfilter.png b/data/homer/png/nxfilter.png
new file mode 100644
index 0000000..457ed9d
--- /dev/null
+++ b/data/homer/png/nxfilter.png
Binary files differ
diff --git a/data/homer/png/nzbget.png b/data/homer/png/nzbget.png
new file mode 100644
index 0000000..faedd80
--- /dev/null
+++ b/data/homer/png/nzbget.png
Binary files differ
diff --git a/data/homer/png/nzbhydra.png b/data/homer/png/nzbhydra.png
new file mode 100644
index 0000000..dc3064d
--- /dev/null
+++ b/data/homer/png/nzbhydra.png
Binary files differ
diff --git a/data/homer/png/octoprint.png b/data/homer/png/octoprint.png
new file mode 100644
index 0000000..d4b650e
--- /dev/null
+++ b/data/homer/png/octoprint.png
Binary files differ
diff --git a/data/homer/png/omada.png b/data/homer/png/omada.png
new file mode 100644
index 0000000..b4f31bf
--- /dev/null
+++ b/data/homer/png/omada.png
Binary files differ
diff --git a/data/homer/png/ombi.png b/data/homer/png/ombi.png
new file mode 100644
index 0000000..e554665
--- /dev/null
+++ b/data/homer/png/ombi.png
Binary files differ
diff --git a/data/homer/png/omnidb.png b/data/homer/png/omnidb.png
new file mode 100644
index 0000000..ae22b5a
--- /dev/null
+++ b/data/homer/png/omnidb.png
Binary files differ
diff --git a/data/homer/png/onlyoffice.png b/data/homer/png/onlyoffice.png
new file mode 100644
index 0000000..07d6151
--- /dev/null
+++ b/data/homer/png/onlyoffice.png
Binary files differ
diff --git a/data/homer/png/openhab.png b/data/homer/png/openhab.png
new file mode 100644
index 0000000..2ed5c05
--- /dev/null
+++ b/data/homer/png/openhab.png
Binary files differ
diff --git a/data/homer/png/openmaptiler.png b/data/homer/png/openmaptiler.png
new file mode 100644
index 0000000..15e99d8
--- /dev/null
+++ b/data/homer/png/openmaptiler.png
Binary files differ
diff --git a/data/homer/png/openmediavault.png b/data/homer/png/openmediavault.png
new file mode 100644
index 0000000..fe14909
--- /dev/null
+++ b/data/homer/png/openmediavault.png
Binary files differ
diff --git a/data/homer/png/openspeedtest.png b/data/homer/png/openspeedtest.png
new file mode 100644
index 0000000..73047d0
--- /dev/null
+++ b/data/homer/png/openspeedtest.png
Binary files differ
diff --git a/data/homer/png/opensprinkler.png b/data/homer/png/opensprinkler.png
new file mode 100644
index 0000000..5e0feb3
--- /dev/null
+++ b/data/homer/png/opensprinkler.png
Binary files differ
diff --git a/data/homer/png/openvpn.png b/data/homer/png/openvpn.png
new file mode 100644
index 0000000..b5f41d1
--- /dev/null
+++ b/data/homer/png/openvpn.png
Binary files differ
diff --git a/data/homer/png/openwrt.png b/data/homer/png/openwrt.png
new file mode 100644
index 0000000..d40b59c
--- /dev/null
+++ b/data/homer/png/openwrt.png
Binary files differ
diff --git a/data/homer/png/opnsense.png b/data/homer/png/opnsense.png
new file mode 100644
index 0000000..7807ac8
--- /dev/null
+++ b/data/homer/png/opnsense.png
Binary files differ
diff --git a/data/homer/png/osticket.png b/data/homer/png/osticket.png
new file mode 100644
index 0000000..0a115ab
--- /dev/null
+++ b/data/homer/png/osticket.png
Binary files differ
diff --git a/data/homer/png/overseerr.png b/data/homer/png/overseerr.png
new file mode 100644
index 0000000..0892883
--- /dev/null
+++ b/data/homer/png/overseerr.png
Binary files differ
diff --git a/data/homer/png/owncloud.png b/data/homer/png/owncloud.png
new file mode 100644
index 0000000..75b8c6a
--- /dev/null
+++ b/data/homer/png/owncloud.png
Binary files differ
diff --git a/data/homer/png/ownphotos.png b/data/homer/png/ownphotos.png
new file mode 100644
index 0000000..1225ab9
--- /dev/null
+++ b/data/homer/png/ownphotos.png
Binary files differ
diff --git a/data/homer/png/pagerduty.png b/data/homer/png/pagerduty.png
new file mode 100644
index 0000000..1988522
--- /dev/null
+++ b/data/homer/png/pagerduty.png
Binary files differ
diff --git a/data/homer/png/paloaltonetworks.png b/data/homer/png/paloaltonetworks.png
new file mode 100644
index 0000000..6146326
--- /dev/null
+++ b/data/homer/png/paloaltonetworks.png
Binary files differ
diff --git a/data/homer/png/paperless-ng.png b/data/homer/png/paperless-ng.png
new file mode 100644
index 0000000..07dd385
--- /dev/null
+++ b/data/homer/png/paperless-ng.png
Binary files differ
diff --git a/data/homer/png/papermerge.png b/data/homer/png/papermerge.png
new file mode 100644
index 0000000..5a24a06
--- /dev/null
+++ b/data/homer/png/papermerge.png
Binary files differ
diff --git a/data/homer/png/partkeepr.png b/data/homer/png/partkeepr.png
new file mode 100644
index 0000000..c05eeb1
--- /dev/null
+++ b/data/homer/png/partkeepr.png
Binary files differ
diff --git a/data/homer/png/peertube.png b/data/homer/png/peertube.png
new file mode 100644
index 0000000..5df6fbd
--- /dev/null
+++ b/data/homer/png/peertube.png
Binary files differ
diff --git a/data/homer/png/pfsense.png b/data/homer/png/pfsense.png
new file mode 100644
index 0000000..77e22a2
--- /dev/null
+++ b/data/homer/png/pfsense.png
Binary files differ
diff --git a/data/homer/png/pgadmin.png b/data/homer/png/pgadmin.png
new file mode 100644
index 0000000..4a187b3
--- /dev/null
+++ b/data/homer/png/pgadmin.png
Binary files differ
diff --git a/data/homer/png/phantombot.png b/data/homer/png/phantombot.png
new file mode 100644
index 0000000..2fe5d1b
--- /dev/null
+++ b/data/homer/png/phantombot.png
Binary files differ
diff --git a/data/homer/png/photoprism.png b/data/homer/png/photoprism.png
new file mode 100644
index 0000000..1ba4a60
--- /dev/null
+++ b/data/homer/png/photoprism.png
Binary files differ
diff --git a/data/homer/png/photostructure.png b/data/homer/png/photostructure.png
new file mode 100644
index 0000000..e80aabf
--- /dev/null
+++ b/data/homer/png/photostructure.png
Binary files differ
diff --git a/data/homer/png/photoview.png b/data/homer/png/photoview.png
new file mode 100644
index 0000000..791dc01
--- /dev/null
+++ b/data/homer/png/photoview.png
Binary files differ
diff --git a/data/homer/png/phpldapadmin.png b/data/homer/png/phpldapadmin.png
new file mode 100644
index 0000000..a534292
--- /dev/null
+++ b/data/homer/png/phpldapadmin.png
Binary files differ
diff --git a/data/homer/png/phpmyadmin.png b/data/homer/png/phpmyadmin.png
new file mode 100644
index 0000000..f5b5353
--- /dev/null
+++ b/data/homer/png/phpmyadmin.png
Binary files differ
diff --git a/data/homer/png/piaware.png b/data/homer/png/piaware.png
new file mode 100644
index 0000000..bfab88d
--- /dev/null
+++ b/data/homer/png/piaware.png
Binary files differ
diff --git a/data/homer/png/pihole.png b/data/homer/png/pihole.png
new file mode 100644
index 0000000..1bf3fa4
--- /dev/null
+++ b/data/homer/png/pihole.png
Binary files differ
diff --git a/data/homer/png/pingdom.png b/data/homer/png/pingdom.png
new file mode 100644
index 0000000..ec290ed
--- /dev/null
+++ b/data/homer/png/pingdom.png
Binary files differ
diff --git a/data/homer/png/piwigo.png b/data/homer/png/piwigo.png
new file mode 100644
index 0000000..082e988
--- /dev/null
+++ b/data/homer/png/piwigo.png
Binary files differ
diff --git a/data/homer/png/plausible.png b/data/homer/png/plausible.png
new file mode 100644
index 0000000..17913e8
--- /dev/null
+++ b/data/homer/png/plausible.png
Binary files differ
diff --git a/data/homer/png/pleroma.png b/data/homer/png/pleroma.png
new file mode 100644
index 0000000..5c1e686
--- /dev/null
+++ b/data/homer/png/pleroma.png
Binary files differ
diff --git a/data/homer/png/plesk.png b/data/homer/png/plesk.png
new file mode 100644
index 0000000..9cc0927
--- /dev/null
+++ b/data/homer/png/plesk.png
Binary files differ
diff --git a/data/homer/png/plex.png b/data/homer/png/plex.png
new file mode 100644
index 0000000..3d99945
--- /dev/null
+++ b/data/homer/png/plex.png
Binary files differ
diff --git a/data/homer/png/plexdrive.png b/data/homer/png/plexdrive.png
new file mode 100644
index 0000000..636eb05
--- /dev/null
+++ b/data/homer/png/plexdrive.png
Binary files differ
diff --git a/data/homer/png/plexrequests.png b/data/homer/png/plexrequests.png
new file mode 100644
index 0000000..832c9dd
--- /dev/null
+++ b/data/homer/png/plexrequests.png
Binary files differ
diff --git a/data/homer/png/plume.png b/data/homer/png/plume.png
new file mode 100644
index 0000000..4dde1fb
--- /dev/null
+++ b/data/homer/png/plume.png
Binary files differ
diff --git a/data/homer/png/podify.png b/data/homer/png/podify.png
new file mode 100644
index 0000000..799ab22
--- /dev/null
+++ b/data/homer/png/podify.png
Binary files differ
diff --git a/data/homer/png/portainer.png b/data/homer/png/portainer.png
new file mode 100644
index 0000000..b54bdd6
--- /dev/null
+++ b/data/homer/png/portainer.png
Binary files differ
diff --git a/data/homer/png/portus.png b/data/homer/png/portus.png
new file mode 100644
index 0000000..e03643b
--- /dev/null
+++ b/data/homer/png/portus.png
Binary files differ
diff --git a/data/homer/png/postgres.png b/data/homer/png/postgres.png
new file mode 100644
index 0000000..d765318
--- /dev/null
+++ b/data/homer/png/postgres.png
Binary files differ
diff --git a/data/homer/png/printer.png b/data/homer/png/printer.png
new file mode 100644
index 0000000..61fae5c
--- /dev/null
+++ b/data/homer/png/printer.png
Binary files differ
diff --git a/data/homer/png/privatebin.png b/data/homer/png/privatebin.png
new file mode 100644
index 0000000..578d5ac
--- /dev/null
+++ b/data/homer/png/privatebin.png
Binary files differ
diff --git a/data/homer/png/projectsend.png b/data/homer/png/projectsend.png
new file mode 100644
index 0000000..92f70f0
--- /dev/null
+++ b/data/homer/png/projectsend.png
Binary files differ
diff --git a/data/homer/png/prometheus.png b/data/homer/png/prometheus.png
new file mode 100644
index 0000000..f2f6596
--- /dev/null
+++ b/data/homer/png/prometheus.png
Binary files differ
diff --git a/data/homer/png/prowlarr.png b/data/homer/png/prowlarr.png
new file mode 100644
index 0000000..a2a3abb
--- /dev/null
+++ b/data/homer/png/prowlarr.png
Binary files differ
diff --git a/data/homer/png/proxmox.png b/data/homer/png/proxmox.png
new file mode 100644
index 0000000..22b4300
--- /dev/null
+++ b/data/homer/png/proxmox.png
Binary files differ
diff --git a/data/homer/png/prtg.png b/data/homer/png/prtg.png
new file mode 100644
index 0000000..a6b07b1
--- /dev/null
+++ b/data/homer/png/prtg.png
Binary files differ
diff --git a/data/homer/png/psitransfer.png b/data/homer/png/psitransfer.png
new file mode 100644
index 0000000..1d73913
--- /dev/null
+++ b/data/homer/png/psitransfer.png
Binary files differ
diff --git a/data/homer/png/pterodactyl.png b/data/homer/png/pterodactyl.png
new file mode 100644
index 0000000..b2a9264
--- /dev/null
+++ b/data/homer/png/pterodactyl.png
Binary files differ
diff --git a/data/homer/png/pyload.png b/data/homer/png/pyload.png
new file mode 100644
index 0000000..a529138
--- /dev/null
+++ b/data/homer/png/pyload.png
Binary files differ
diff --git a/data/homer/png/qbittorrent.png b/data/homer/png/qbittorrent.png
new file mode 100644
index 0000000..e6f67c0
--- /dev/null
+++ b/data/homer/png/qbittorrent.png
Binary files differ
diff --git a/data/homer/png/qnap.png b/data/homer/png/qnap.png
new file mode 100644
index 0000000..448920e
--- /dev/null
+++ b/data/homer/png/qnap.png
Binary files differ
diff --git a/data/homer/png/rabbitmq.png b/data/homer/png/rabbitmq.png
new file mode 100644
index 0000000..90e2178
--- /dev/null
+++ b/data/homer/png/rabbitmq.png
Binary files differ
diff --git a/data/homer/png/radarr.png b/data/homer/png/radarr.png
new file mode 100644
index 0000000..46a82cd
--- /dev/null
+++ b/data/homer/png/radarr.png
Binary files differ
diff --git a/data/homer/png/radicale.png b/data/homer/png/radicale.png
new file mode 100644
index 0000000..8cdc723
--- /dev/null
+++ b/data/homer/png/radicale.png
Binary files differ
diff --git a/data/homer/png/rainloop.png b/data/homer/png/rainloop.png
new file mode 100644
index 0000000..cfdbd54
--- /dev/null
+++ b/data/homer/png/rainloop.png
Binary files differ
diff --git a/data/homer/png/rancher.png b/data/homer/png/rancher.png
new file mode 100644
index 0000000..30b0a22
--- /dev/null
+++ b/data/homer/png/rancher.png
Binary files differ
diff --git a/data/homer/png/raneto.png b/data/homer/png/raneto.png
new file mode 100644
index 0000000..a176cdb
--- /dev/null
+++ b/data/homer/png/raneto.png
Binary files differ
diff --git a/data/homer/png/rclone.png b/data/homer/png/rclone.png
new file mode 100644
index 0000000..740a58a
--- /dev/null
+++ b/data/homer/png/rclone.png
Binary files differ
diff --git a/data/homer/png/readarr.png b/data/homer/png/readarr.png
new file mode 100644
index 0000000..7d0c3c1
--- /dev/null
+++ b/data/homer/png/readarr.png
Binary files differ
diff --git a/data/homer/png/recalbox.png b/data/homer/png/recalbox.png
new file mode 100644
index 0000000..81f539b
--- /dev/null
+++ b/data/homer/png/recalbox.png
Binary files differ
diff --git a/data/homer/png/redis.png b/data/homer/png/redis.png
new file mode 100644
index 0000000..923d14a
--- /dev/null
+++ b/data/homer/png/redis.png
Binary files differ
diff --git a/data/homer/png/requestrr.png b/data/homer/png/requestrr.png
new file mode 100644
index 0000000..009b9b6
--- /dev/null
+++ b/data/homer/png/requestrr.png
Binary files differ
diff --git a/data/homer/png/resiliosync.png b/data/homer/png/resiliosync.png
new file mode 100644
index 0000000..2456b63
--- /dev/null
+++ b/data/homer/png/resiliosync.png
Binary files differ
diff --git a/data/homer/png/riot.png b/data/homer/png/riot.png
new file mode 100644
index 0000000..4de3069
--- /dev/null
+++ b/data/homer/png/riot.png
Binary files differ
diff --git a/data/homer/png/rocketchat.png b/data/homer/png/rocketchat.png
new file mode 100644
index 0000000..931c09f
--- /dev/null
+++ b/data/homer/png/rocketchat.png
Binary files differ
diff --git a/data/homer/png/rompya.png b/data/homer/png/rompya.png
new file mode 100644
index 0000000..a32bc80
--- /dev/null
+++ b/data/homer/png/rompya.png
Binary files differ
diff --git a/data/homer/png/rook.png b/data/homer/png/rook.png
new file mode 100644
index 0000000..f047593
--- /dev/null
+++ b/data/homer/png/rook.png
Binary files differ
diff --git a/data/homer/png/roundcube.png b/data/homer/png/roundcube.png
new file mode 100644
index 0000000..60fda3c
--- /dev/null
+++ b/data/homer/png/roundcube.png
Binary files differ
diff --git a/data/homer/png/router.png b/data/homer/png/router.png
new file mode 100644
index 0000000..0e45883
--- /dev/null
+++ b/data/homer/png/router.png
Binary files differ
diff --git a/data/homer/png/rspamd.png b/data/homer/png/rspamd.png
new file mode 100644
index 0000000..eab4c37
--- /dev/null
+++ b/data/homer/png/rspamd.png
Binary files differ
diff --git a/data/homer/png/rstudioserver.png b/data/homer/png/rstudioserver.png
new file mode 100644
index 0000000..b1ef922
--- /dev/null
+++ b/data/homer/png/rstudioserver.png
Binary files differ
diff --git a/data/homer/png/rundeck.png b/data/homer/png/rundeck.png
new file mode 100644
index 0000000..fddd7de
--- /dev/null
+++ b/data/homer/png/rundeck.png
Binary files differ
diff --git a/data/homer/png/runeaudio.png b/data/homer/png/runeaudio.png
new file mode 100644
index 0000000..e9e3b3b
--- /dev/null
+++ b/data/homer/png/runeaudio.png
Binary files differ
diff --git a/data/homer/png/rutorrent.png b/data/homer/png/rutorrent.png
new file mode 100644
index 0000000..da1e4fd
--- /dev/null
+++ b/data/homer/png/rutorrent.png
Binary files differ
diff --git a/data/homer/png/sabnzbd.png b/data/homer/png/sabnzbd.png
new file mode 100644
index 0000000..46b4a92
--- /dev/null
+++ b/data/homer/png/sabnzbd.png
Binary files differ
diff --git a/data/homer/png/scrutiny.png b/data/homer/png/scrutiny.png
new file mode 100644
index 0000000..25e1110
--- /dev/null
+++ b/data/homer/png/scrutiny.png
Binary files differ
diff --git a/data/homer/png/seafile.png b/data/homer/png/seafile.png
new file mode 100644
index 0000000..54c4465
--- /dev/null
+++ b/data/homer/png/seafile.png
Binary files differ
diff --git a/data/homer/png/searxmetasearchengine.png b/data/homer/png/searxmetasearchengine.png
new file mode 100644
index 0000000..deb7c40
--- /dev/null
+++ b/data/homer/png/searxmetasearchengine.png
Binary files differ
diff --git a/data/homer/png/serviio.png b/data/homer/png/serviio.png
new file mode 100644
index 0000000..9db5caf
--- /dev/null
+++ b/data/homer/png/serviio.png
Binary files differ
diff --git a/data/homer/png/shaarli.png b/data/homer/png/shaarli.png
new file mode 100644
index 0000000..b975b31
--- /dev/null
+++ b/data/homer/png/shaarli.png
Binary files differ
diff --git a/data/homer/png/shinobi.png b/data/homer/png/shinobi.png
new file mode 100644
index 0000000..506db4f
--- /dev/null
+++ b/data/homer/png/shinobi.png
Binary files differ
diff --git a/data/homer/png/sickbeard.png b/data/homer/png/sickbeard.png
new file mode 100644
index 0000000..4b58d2a
--- /dev/null
+++ b/data/homer/png/sickbeard.png
Binary files differ
diff --git a/data/homer/png/sickchill.png b/data/homer/png/sickchill.png
new file mode 100644
index 0000000..ce4f796
--- /dev/null
+++ b/data/homer/png/sickchill.png
Binary files differ
diff --git a/data/homer/png/sickgear.png b/data/homer/png/sickgear.png
new file mode 100644
index 0000000..8761fba
--- /dev/null
+++ b/data/homer/png/sickgear.png
Binary files differ
diff --git a/data/homer/png/sinusbot.png b/data/homer/png/sinusbot.png
new file mode 100644
index 0000000..823aecc
--- /dev/null
+++ b/data/homer/png/sinusbot.png
Binary files differ
diff --git a/data/homer/png/slack.png b/data/homer/png/slack.png
new file mode 100644
index 0000000..8abbafa
--- /dev/null
+++ b/data/homer/png/slack.png
Binary files differ
diff --git a/data/homer/png/snibox.png b/data/homer/png/snibox.png
new file mode 100644
index 0000000..33f4f4c
--- /dev/null
+++ b/data/homer/png/snibox.png
Binary files differ
diff --git a/data/homer/png/sonarqube.png b/data/homer/png/sonarqube.png
new file mode 100644
index 0000000..a6ee69f
--- /dev/null
+++ b/data/homer/png/sonarqube.png
Binary files differ
diff --git a/data/homer/png/sonarr.png b/data/homer/png/sonarr.png
new file mode 100644
index 0000000..cfc65fe
--- /dev/null
+++ b/data/homer/png/sonarr.png
Binary files differ
diff --git a/data/homer/png/sourcegraph.png b/data/homer/png/sourcegraph.png
new file mode 100644
index 0000000..16cdea5
--- /dev/null
+++ b/data/homer/png/sourcegraph.png
Binary files differ
diff --git a/data/homer/png/splunk.png b/data/homer/png/splunk.png
new file mode 100644
index 0000000..33e09f5
--- /dev/null
+++ b/data/homer/png/splunk.png
Binary files differ
diff --git a/data/homer/png/spotweb.png b/data/homer/png/spotweb.png
new file mode 100644
index 0000000..8a09eb6
--- /dev/null
+++ b/data/homer/png/spotweb.png
Binary files differ
diff --git a/data/homer/png/squidex.png b/data/homer/png/squidex.png
new file mode 100644
index 0000000..64bb943
--- /dev/null
+++ b/data/homer/png/squidex.png
Binary files differ
diff --git a/data/homer/png/statping.png b/data/homer/png/statping.png
new file mode 100644
index 0000000..52f9a4e
--- /dev/null
+++ b/data/homer/png/statping.png
Binary files differ
diff --git a/data/homer/png/strapi.png b/data/homer/png/strapi.png
new file mode 100644
index 0000000..a5529b3
--- /dev/null
+++ b/data/homer/png/strapi.png
Binary files differ
diff --git a/data/homer/png/streama.png b/data/homer/png/streama.png
new file mode 100644
index 0000000..63c4426
--- /dev/null
+++ b/data/homer/png/streama.png
Binary files differ
diff --git a/data/homer/png/sunshine.png b/data/homer/png/sunshine.png
new file mode 100644
index 0000000..9bea4c1
--- /dev/null
+++ b/data/homer/png/sunshine.png
Binary files differ
diff --git a/data/homer/png/synclounge.png b/data/homer/png/synclounge.png
new file mode 100644
index 0000000..049a252
--- /dev/null
+++ b/data/homer/png/synclounge.png
Binary files differ
diff --git a/data/homer/png/syncthing.png b/data/homer/png/syncthing.png
new file mode 100644
index 0000000..ec084cf
--- /dev/null
+++ b/data/homer/png/syncthing.png
Binary files differ
diff --git a/data/homer/png/synology.png b/data/homer/png/synology.png
new file mode 100644
index 0000000..9f7f577
--- /dev/null
+++ b/data/homer/png/synology.png
Binary files differ
diff --git a/data/homer/png/taiga.png b/data/homer/png/taiga.png
new file mode 100644
index 0000000..24ab018
--- /dev/null
+++ b/data/homer/png/taiga.png
Binary files differ
diff --git a/data/homer/png/tandoorrecipes.png b/data/homer/png/tandoorrecipes.png
new file mode 100644
index 0000000..9e12aad
--- /dev/null
+++ b/data/homer/png/tandoorrecipes.png
Binary files differ
diff --git a/data/homer/png/tasmoadmin.png b/data/homer/png/tasmoadmin.png
new file mode 100644
index 0000000..eed9b64
--- /dev/null
+++ b/data/homer/png/tasmoadmin.png
Binary files differ
diff --git a/data/homer/png/tasmota.png b/data/homer/png/tasmota.png
new file mode 100644
index 0000000..ed75ef1
--- /dev/null
+++ b/data/homer/png/tasmota.png
Binary files differ
diff --git a/data/homer/png/tautulli.png b/data/homer/png/tautulli.png
new file mode 100644
index 0000000..3cf7264
--- /dev/null
+++ b/data/homer/png/tautulli.png
Binary files differ
diff --git a/data/homer/png/tdarr.png b/data/homer/png/tdarr.png
new file mode 100644
index 0000000..68ee24c
--- /dev/null
+++ b/data/homer/png/tdarr.png
Binary files differ
diff --git a/data/homer/png/teedy.png b/data/homer/png/teedy.png
new file mode 100644
index 0000000..36f9dcf
--- /dev/null
+++ b/data/homer/png/teedy.png
Binary files differ
diff --git a/data/homer/png/thanos.png b/data/homer/png/thanos.png
new file mode 100644
index 0000000..1ccfc3d
--- /dev/null
+++ b/data/homer/png/thanos.png
Binary files differ
diff --git a/data/homer/png/theia.png b/data/homer/png/theia.png
new file mode 100644
index 0000000..60a874f
--- /dev/null
+++ b/data/homer/png/theia.png
Binary files differ
diff --git a/data/homer/png/thelounge.png b/data/homer/png/thelounge.png
new file mode 100644
index 0000000..c1f5ad9
--- /dev/null
+++ b/data/homer/png/thelounge.png
Binary files differ
diff --git a/data/homer/png/tinytinyrss.png b/data/homer/png/tinytinyrss.png
new file mode 100644
index 0000000..b4ff6bf
--- /dev/null
+++ b/data/homer/png/tinytinyrss.png
Binary files differ
diff --git a/data/homer/png/tplink.png b/data/homer/png/tplink.png
new file mode 100644
index 0000000..cddabef
--- /dev/null
+++ b/data/homer/png/tplink.png
Binary files differ
diff --git a/data/homer/png/traccar.png b/data/homer/png/traccar.png
new file mode 100644
index 0000000..be57141
--- /dev/null
+++ b/data/homer/png/traccar.png
Binary files differ
diff --git a/data/homer/png/traefik.png b/data/homer/png/traefik.png
new file mode 100644
index 0000000..77a490a
--- /dev/null
+++ b/data/homer/png/traefik.png
Binary files differ
diff --git a/data/homer/png/transmission.png b/data/homer/png/transmission.png
new file mode 100644
index 0000000..f24d9e3
--- /dev/null
+++ b/data/homer/png/transmission.png
Binary files differ
diff --git a/data/homer/png/trilium.png b/data/homer/png/trilium.png
new file mode 100644
index 0000000..c5b5795
--- /dev/null
+++ b/data/homer/png/trilium.png
Binary files differ
diff --git a/data/homer/png/truenas.png b/data/homer/png/truenas.png
new file mode 100644
index 0000000..82bf7a2
--- /dev/null
+++ b/data/homer/png/truenas.png
Binary files differ
diff --git a/data/homer/png/tubearchivist.png b/data/homer/png/tubearchivist.png
new file mode 100644
index 0000000..ca4291f
--- /dev/null
+++ b/data/homer/png/tubearchivist.png
Binary files differ
diff --git a/data/homer/png/tubesync.png b/data/homer/png/tubesync.png
new file mode 100644
index 0000000..56537f3
--- /dev/null
+++ b/data/homer/png/tubesync.png
Binary files differ
diff --git a/data/homer/png/tvheadend.png b/data/homer/png/tvheadend.png
new file mode 100644
index 0000000..5cb0a47
--- /dev/null
+++ b/data/homer/png/tvheadend.png
Binary files differ
diff --git a/data/homer/png/ubooquity.png b/data/homer/png/ubooquity.png
new file mode 100644
index 0000000..cf37a87
--- /dev/null
+++ b/data/homer/png/ubooquity.png
Binary files differ
diff --git a/data/homer/png/ultimateguitar.png b/data/homer/png/ultimateguitar.png
new file mode 100644
index 0000000..5e0d5bd
--- /dev/null
+++ b/data/homer/png/ultimateguitar.png
Binary files differ
diff --git a/data/homer/png/unifi.png b/data/homer/png/unifi.png
new file mode 100644
index 0000000..c9c5e37
--- /dev/null
+++ b/data/homer/png/unifi.png
Binary files differ
diff --git a/data/homer/png/unraid.png b/data/homer/png/unraid.png
new file mode 100644
index 0000000..c9f2244
--- /dev/null
+++ b/data/homer/png/unraid.png
Binary files differ
diff --git a/data/homer/png/updog.png b/data/homer/png/updog.png
new file mode 100644
index 0000000..316097b
--- /dev/null
+++ b/data/homer/png/updog.png
Binary files differ
diff --git a/data/homer/png/urbackup.png b/data/homer/png/urbackup.png
new file mode 100644
index 0000000..460bbf6
--- /dev/null
+++ b/data/homer/png/urbackup.png
Binary files differ
diff --git a/data/homer/png/valetudo.png b/data/homer/png/valetudo.png
new file mode 100644
index 0000000..ca97b77
--- /dev/null
+++ b/data/homer/png/valetudo.png
Binary files differ
diff --git a/data/homer/png/vault.png b/data/homer/png/vault.png
new file mode 100644
index 0000000..3f17975
--- /dev/null
+++ b/data/homer/png/vault.png
Binary files differ
diff --git a/data/homer/png/vikunja.png b/data/homer/png/vikunja.png
new file mode 100644
index 0000000..b0d19da
--- /dev/null
+++ b/data/homer/png/vikunja.png
Binary files differ
diff --git a/data/homer/png/virtualradarserver.png b/data/homer/png/virtualradarserver.png
new file mode 100644
index 0000000..e9eb1fe
--- /dev/null
+++ b/data/homer/png/virtualradarserver.png
Binary files differ
diff --git a/data/homer/png/vmware.png b/data/homer/png/vmware.png
new file mode 100644
index 0000000..2140d3d
--- /dev/null
+++ b/data/homer/png/vmware.png
Binary files differ
diff --git a/data/homer/png/vmwarehorizon.png b/data/homer/png/vmwarehorizon.png
new file mode 100644
index 0000000..5f08013
--- /dev/null
+++ b/data/homer/png/vmwarehorizon.png
Binary files differ
diff --git a/data/homer/png/volumio.png b/data/homer/png/volumio.png
new file mode 100644
index 0000000..c325ceb
--- /dev/null
+++ b/data/homer/png/volumio.png
Binary files differ
diff --git a/data/homer/png/wallabag.png b/data/homer/png/wallabag.png
new file mode 100644
index 0000000..6756739
--- /dev/null
+++ b/data/homer/png/wallabag.png
Binary files differ
diff --git a/data/homer/png/wanikani.png b/data/homer/png/wanikani.png
new file mode 100644
index 0000000..46f6c13
--- /dev/null
+++ b/data/homer/png/wanikani.png
Binary files differ
diff --git a/data/homer/png/watcher.png b/data/homer/png/watcher.png
new file mode 100644
index 0000000..f489ad5
--- /dev/null
+++ b/data/homer/png/watcher.png
Binary files differ
diff --git a/data/homer/png/watchtower.png b/data/homer/png/watchtower.png
new file mode 100644
index 0000000..cc65ff4
--- /dev/null
+++ b/data/homer/png/watchtower.png
Binary files differ
diff --git a/data/homer/png/webdav.png b/data/homer/png/webdav.png
new file mode 100644
index 0000000..2faf149
--- /dev/null
+++ b/data/homer/png/webdav.png
Binary files differ
diff --git a/data/homer/png/webmin.png b/data/homer/png/webmin.png
new file mode 100644
index 0000000..9def1df
--- /dev/null
+++ b/data/homer/png/webmin.png
Binary files differ
diff --git a/data/homer/png/webtools.png b/data/homer/png/webtools.png
new file mode 100644
index 0000000..25f6527
--- /dev/null
+++ b/data/homer/png/webtools.png
Binary files differ
diff --git a/data/homer/png/wekan.png b/data/homer/png/wekan.png
new file mode 100644
index 0000000..c6d5d52
--- /dev/null
+++ b/data/homer/png/wekan.png
Binary files differ
diff --git a/data/homer/png/wetty.png b/data/homer/png/wetty.png
new file mode 100644
index 0000000..3f559a4
--- /dev/null
+++ b/data/homer/png/wetty.png
Binary files differ
diff --git a/data/homer/png/wggenweb.png b/data/homer/png/wggenweb.png
new file mode 100644
index 0000000..9191b95
--- /dev/null
+++ b/data/homer/png/wggenweb.png
Binary files differ
diff --git a/data/homer/png/whoami.png b/data/homer/png/whoami.png
new file mode 100644
index 0000000..4b6617c
--- /dev/null
+++ b/data/homer/png/whoami.png
Binary files differ
diff --git a/data/homer/png/wikijs.png b/data/homer/png/wikijs.png
new file mode 100644
index 0000000..6fa1819
--- /dev/null
+++ b/data/homer/png/wikijs.png
Binary files differ
diff --git a/data/homer/png/wireguard.png b/data/homer/png/wireguard.png
new file mode 100644
index 0000000..6ec3509
--- /dev/null
+++ b/data/homer/png/wireguard.png
Binary files differ
diff --git a/data/homer/png/wizarr.png b/data/homer/png/wizarr.png
new file mode 100644
index 0000000..9ad8f93
--- /dev/null
+++ b/data/homer/png/wizarr.png
Binary files differ
diff --git a/data/homer/png/wordpress.png b/data/homer/png/wordpress.png
new file mode 100644
index 0000000..7fd4d90
--- /dev/null
+++ b/data/homer/png/wordpress.png
Binary files differ
diff --git a/data/homer/png/xbrowsersync.png b/data/homer/png/xbrowsersync.png
new file mode 100644
index 0000000..61ba2c5
--- /dev/null
+++ b/data/homer/png/xbrowsersync.png
Binary files differ
diff --git a/data/homer/png/xigmanas.png b/data/homer/png/xigmanas.png
new file mode 100644
index 0000000..7874f03
--- /dev/null
+++ b/data/homer/png/xigmanas.png
Binary files differ
diff --git a/data/homer/png/xteve.png b/data/homer/png/xteve.png
new file mode 100644
index 0000000..d8df8ee
--- /dev/null
+++ b/data/homer/png/xteve.png
Binary files differ
diff --git a/data/homer/png/xwiki.png b/data/homer/png/xwiki.png
new file mode 100644
index 0000000..2a009f3
--- /dev/null
+++ b/data/homer/png/xwiki.png
Binary files differ
diff --git a/data/homer/png/yacht.png b/data/homer/png/yacht.png
new file mode 100644
index 0000000..cfa0ed9
--- /dev/null
+++ b/data/homer/png/yacht.png
Binary files differ
diff --git a/data/homer/png/ynab.png b/data/homer/png/ynab.png
new file mode 100644
index 0000000..509f2d1
--- /dev/null
+++ b/data/homer/png/ynab.png
Binary files differ
diff --git a/data/homer/png/youtube.png b/data/homer/png/youtube.png
new file mode 100644
index 0000000..3186bb4
--- /dev/null
+++ b/data/homer/png/youtube.png
Binary files differ
diff --git a/data/homer/png/youtubedl.png b/data/homer/png/youtubedl.png
new file mode 100644
index 0000000..c7058d3
--- /dev/null
+++ b/data/homer/png/youtubedl.png
Binary files differ
diff --git a/data/homer/png/zabbix.png b/data/homer/png/zabbix.png
new file mode 100644
index 0000000..17a65f2
--- /dev/null
+++ b/data/homer/png/zabbix.png
Binary files differ
diff --git a/data/homer/png/zigbee2mqtt.png b/data/homer/png/zigbee2mqtt.png
new file mode 100644
index 0000000..c941e8e
--- /dev/null
+++ b/data/homer/png/zigbee2mqtt.png
Binary files differ
diff --git a/data/homer/png/znc.png b/data/homer/png/znc.png
new file mode 100644
index 0000000..bbba0e3
--- /dev/null
+++ b/data/homer/png/znc.png
Binary files differ
diff --git a/data/homer/png/zoneminder.png b/data/homer/png/zoneminder.png
new file mode 100644
index 0000000..6255efc
--- /dev/null
+++ b/data/homer/png/zoneminder.png
Binary files differ
diff --git a/data/homer/png/zulip.png b/data/homer/png/zulip.png
new file mode 100644
index 0000000..103ce1b
--- /dev/null
+++ b/data/homer/png/zulip.png
Binary files differ
diff --git a/data/homer/png/zwavejs.png b/data/homer/png/zwavejs.png
new file mode 100644
index 0000000..9c8b8de
--- /dev/null
+++ b/data/homer/png/zwavejs.png
Binary files differ
diff --git a/data/homer/svg/adguardhome.svg b/data/homer/svg/adguardhome.svg
new file mode 100644
index 0000000..354f30f
--- /dev/null
+++ b/data/homer/svg/adguardhome.svg
@@ -0,0 +1,99 @@
1<?xml version="1.0" encoding="UTF-8" standalone="no"?>
2<svg
3 xmlns:dc="http://purl.org/dc/elements/1.1/"
4 xmlns:cc="http://creativecommons.org/ns#"
5 xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
6 xmlns:svg="http://www.w3.org/2000/svg"
7 xmlns="http://www.w3.org/2000/svg"
8 xmlns:xlink="http://www.w3.org/1999/xlink"
9 version="1.1"
10 id="svg2"
11 width="250"
12 height="250"
13 viewBox="0 0 250 250">
14 <metadata
15 id="metadata8">
16 <rdf:RDF>
17 <cc:Work
18 rdf:about="">
19 <dc:format>image/svg+xml</dc:format>
20 <dc:type
21 rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
22 <dc:title></dc:title>
23 </cc:Work>
24 </rdf:RDF>
25 </metadata>
26 <defs
27 id="defs6" />
28 <g
29 id="g10">
30 <image
31 width="250"
32 height="250"
33 preserveAspectRatio="none"
34 xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAPoAAAD6CAMAAAC/MqoPAAAB+1BMVEUAAABnu3BounNnuXNnunNn
35t3ZntnVntnZot3RotXZntXdntXdntXdntHhntHhntHhms3hnsnlou3FovHFovHFovHFovHFovXFo
36vHFou3JovHFovHFovHFovHFnuHNovHJnunNnuXNqvXBpu3RxuHFnuXJouXNntnZntnZntnZntXdn
37s3hovHFovHFovHFovHFou3Jou3JnuXRotXVnt3ZntXZnsnhnvHFovHFktm1lvnRntnZntHdovHFo
38vHFnunFou3JovHFnunRntnZovHFovHFnvHFovHFnunNqu3Jnt3VouXRntXdovXJovHFnt3ZovHFo
39vHFovXFovHJnuHVpvHNounNnvHFnt3Vnu3JovHFntXZnvHFouG9ntHdnt3VovHJntnd2w36d1KO0
403ri64b+a0qB0wnx/x4fT69b8/vz////N6dB8xYSn2Kzv+PBsvnX+/v6V0JtntnZntnar2rDg8eL5
41/Plou3Gi1qjD5cfm9Ohuv3f7/fuEyYtou3CPzpaKy5Hr9u1wwHn1+/dpvXLy+fN5xIHa79zk8ufL
426M5vtoCHwpWazKaj0K6NxZpxt4FstX7D4cro9Ou22r+Av4/I5M/Q59WSyJ/W6tux17pos3rZ7N7V
436trA38fe7+J4u4hotXeUyaGr1bV0uYVqtHu63MJotnVounMTGp3GAAAAqXRSTlMAEzhYdI6juMrU
444OXs9Pf7/v////Tv4djMwbeoopiGXU48KxoDCDVrk7Pv//7n0Z1tUyJdutr+1bMGDa38/N6PZ0QX
450caRb/gyHp8m/niA9fFkvKXsDyiI4j/rxtoL6YBJYv//////////////////////mXr///8u////
46////sf///////////////////////////////////////////83//////7+tZKv3pQAACsJJREFU
47eAHs1gOStEkUheFS27bKtm3bxv4XMfPrtI1Pt94dPHHyRqSImcQSqWxhcWl5ZXVtfWNza2v7Vzs7
48u3t7+we/Ojw6Pjk9O7+4lF1d38gVShH/U6k1Wt3yqn7TsPNcv+jPZjSZTy2XVptazD+y0u7QOl3r
49W+4d9CH6Xfse74XMJvfxAe2/kQVW1oNY+Yt0FAqfRqKxOGfViqvFhB7mb6Wjg2QqeqPiFFopTy+t
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
96"
97 id="image12" />
98 </g>
99</svg>
diff --git a/data/homer/svg/adminer.svg b/data/homer/svg/adminer.svg
new file mode 100644
index 0000000..dab586c
--- /dev/null
+++ b/data/homer/svg/adminer.svg
@@ -0,0 +1,217 @@
1<?xml version="1.0" encoding="UTF-8" standalone="no"?>
2<svg
3 xmlns:dc="http://purl.org/dc/elements/1.1/"
4 xmlns:cc="http://creativecommons.org/ns#"
5 xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
6 xmlns:svg="http://www.w3.org/2000/svg"
7 xmlns="http://www.w3.org/2000/svg"
8 xmlns:xlink="http://www.w3.org/1999/xlink"
9 version="1.1"
10 id="svg2"
11 width="250"
12 height="250"
13 viewBox="0 0 250 250">
14 <metadata
15 id="metadata8">
16 <rdf:RDF>
17 <cc:Work
18 rdf:about="">
19 <dc:format>image/svg+xml</dc:format>
20 <dc:type
21 rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
22 <dc:title></dc:title>
23 </cc:Work>
24 </rdf:RDF>
25 </metadata>
26 <defs
27 id="defs6" />
28 <g
29 id="g10">
30 <image
31 width="250"
32 height="250"
33 preserveAspectRatio="none"
34 xlink:href="data:image/png;base64,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
214"
215 id="image12" />
216 </g>
217</svg>
diff --git a/data/homer/svg/bazarr.svg b/data/homer/svg/bazarr.svg
new file mode 100644
index 0000000..252cb8b
--- /dev/null
+++ b/data/homer/svg/bazarr.svg
@@ -0,0 +1,9 @@
1<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
2<!--Created with Method Draw https://editor.method.ac/-->
3 <title>Bazarr</title>
4
5 <g>
6 <title>Layer 1</title>
7 <image href="data:image/png;base64,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" xlink:href="data:image/png;base64,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" id="svg_1" height="128" width="128" y="0" x="0"/>
8 </g>
9</svg> \ No newline at end of file
diff --git a/data/homer/svg/caddy.svg b/data/homer/svg/caddy.svg
new file mode 100644
index 0000000..d9b8a67
--- /dev/null
+++ b/data/homer/svg/caddy.svg
@@ -0,0 +1 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg width="100%" height="100%" viewBox="0 0 401 401" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;"><circle id="Icon" cx="200.47" cy="200.47" r="200.47" style="fill:#3dd0ff;"/><clipPath id="_clip1"><circle cx="200.47" cy="200.47" r="200.47"/></clipPath><g clip-path="url(#_clip1)"><g><g><g><clipPath id="_clip2"><path d="M76.817,172.777c0,-76.373 48.909,-111.192 121.629,-110.237c73.117,0.954 121.392,34.182 121.392,111.454l-0.122,73.357l-44.611,0l0,-54.15c-1.198,-18.797 4.977,-81.761 -76.147,-81.761c-81.684,0 -76.885,63.192 -76.59,82.134l0,53.777l-45.551,0l0,-74.574Z"/></clipPath><g clip-path="url(#_clip2)"><rect x="-799.837" y="-162.123" width="1239.9" height="2574.71" style="fill:url(#_Linear3);"/><clipPath id="_clip4"><rect x="-799.837" y="-162.123" width="1239.9" height="2574.71"/></clipPath><g clip-path="url(#_clip4)"><use xlink:href="#_Image5" x="77.126" y="62.579" width="243.021px" height="184.83px" transform="matrix(0.995989,0,0,0.999082,0,0)"/></g></g></g><g><clipPath id="_clip6"><path d="M83.937,172.777c0,-35.021 10.701,-61.186 30.936,-78.398c19.514,-16.597 48.002,-24.913 83.483,-24.447c36.093,0.471 64.414,8.995 83.686,25.429c20.139,17.173 30.676,43.192 30.676,78.633l-0.122,73.345l7.12,-7.38l-44.611,0l7.12,7.392l0,-54.15l0,-0.245l-0.016,-0.243c-0.072,-1.132 -0.116,-2.407 -0.177,-4.848c-0.191,-7.671 -0.322,-10.751 -0.788,-15.6c-0.667,-6.939 -1.836,-13.38 -3.703,-19.544c-4.193,-13.843 -11.647,-25.307 -23.181,-33.734c-13.404,-9.792 -31.692,-14.939 -55.402,-14.939c-23.875,0 -42.381,5.169 -56,14.981c-11.755,8.47 -19.412,20.023 -23.673,33.881c-2.07,6.731 -3.275,13.782 -3.823,21.248c-0.393,5.355 -0.428,9.804 -0.278,16.462c0.002,0.114 0.029,1.307 0.037,1.636c0.012,0.578 0.021,1.029 0.028,1.437l-0.001,-0.119l0,53.777l7.12,-7.392l-45.551,0l7.12,7.392l0,-74.574Zm-14.24,0l0,81.967l59.791,0l0,-7.393l0,-53.777l-0.001,-0.119c-0.007,-0.447 -0.016,-0.927 -0.03,-1.536c-0.007,-0.336 -0.034,-1.535 -0.037,-1.643c-0.139,-6.206 -0.108,-10.242 0.241,-14.994c0.469,-6.39 1.484,-12.329 3.19,-17.875c3.352,-10.904 9.2,-19.728 18.226,-26.231c11.05,-7.962 26.737,-12.343 47.881,-12.343c20.953,0 36.393,4.346 47.207,12.247c8.809,6.435 14.488,15.168 17.792,26.077c1.549,5.113 2.541,10.58 3.117,16.577c0.42,4.365 0.541,7.208 0.723,14.513c0.065,2.638 0.114,4.047 0.203,5.442l-0.015,-0.488l0,61.543l58.839,0l0.013,-7.38l0.121,-73.357c0,-39.667 -12.254,-69.927 -35.878,-90.072c-22.079,-18.828 -53.477,-28.277 -92.544,-28.787c-80.349,-1.055 -128.839,40.19 -128.839,117.629Z" clip-rule="nonzero"/></clipPath><g clip-path="url(#_clip6)"><rect x="-799.837" y="-162.123" width="1239.9" height="2574.71" style="fill:url(#_Linear7);"/><clipPath id="_clip8"><rect x="-799.837" y="-162.123" width="1239.9" height="2574.71"/></clipPath><g clip-path="url(#_clip8)"><use xlink:href="#_Image9" x="69.897" y="55.235" width="257.262px" height="199.615px" transform="matrix(0.997138,0,0,0.998077,0,0)"/></g></g></g><rect x="282.232" y="171.29" width="14.354" height="68.654" style="fill:url(#_Linear10);"/><g><clipPath id="_clip11"><path d="M83.608,177.164c2.359,-59.062 16.982,-103.539 114.522,-107.309c-63.158,9.995 -97.512,30.703 -97.512,92.45l-1.576,76.531l-15.435,0l0,-61.672l0.001,0Z"/></clipPath><g clip-path="url(#_clip11)"><rect x="-823.139" y="-161.522" width="1272.61" height="2570.86" style="fill:url(#_Linear12);"/><clipPath id="_clip13"><rect x="-823.139" y="-161.522" width="1272.61" height="2570.86"/></clipPath><g clip-path="url(#_clip13)"><use xlink:href="#_Image14" x="83.956" y="69.863" width="114.523px" height="168.981px" transform="matrix(0.995849,0,0,0.999887,0,0)"/></g></g></g></g><g><path d="M367.745,286.714c0,-27.082 -21.988,-49.07 -49.071,-49.07l-243.602,0c-27.082,0 -49.07,21.988 -49.07,49.07l0,146.934c0,27.083 21.988,49.071 49.07,49.071l243.602,0c27.083,0 49.071,-21.988 49.071,-49.071l0,-146.934Z" style="fill:#007c00;"/><path d="M351.521,287.596c0,-20.676 -16.786,-37.463 -37.462,-37.463l-234.898,0c-20.676,0 -37.462,16.787 -37.462,37.463l0,144.098c0,20.676 16.786,37.462 37.462,37.462l234.898,0c20.676,0 37.462,-16.786 37.462,-37.462l0,-144.098Z" style="fill:url(#_Linear15);"/><path d="M303.52,263.545c18.994,0 34.414,15.421 34.414,34.414l0,123.371c0,18.994 -15.42,34.415 -34.414,34.415l-213.82,0c-18.994,0 -34.414,-15.421 -34.414,-34.415l0,-123.371c0,-18.993 15.42,-34.414 34.414,-34.414l213.82,0Zm-6.086,17.866l-201.648,0c-11.105,0.001 -20.12,9.016 -20.12,20.121l0,118.08c0,11.105 9.015,20.121 20.12,20.121l201.648,0c11.105,0 20.121,-9.016 20.121,-20.121l0,-118.08c0,-11.105 -9.016,-20.12 -20.121,-20.121Z" style="fill:url(#_Linear16);"/></g></g></g><defs><linearGradient id="_Linear3" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(3.88722e-14,659.127,-634.831,4.03599e-14,76.8169,62.5212)"><stop offset="0" style="stop-color:#babdb6;stop-opacity:0"/><stop offset="0.5" style="stop-color:#eeeeec;stop-opacity:0"/><stop offset="1" style="stop-color:#babdb6;stop-opacity:0"/></linearGradient><image id="_Image5" width="244px" height="185px" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAPQAAAC5CAYAAAAf6RJMAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAa3ElEQVR4nO2d+Y7cRpKHgwafwNYtW5bvUz4k38d4DGN3gH3gXWAHuwv4kNSSLFuHJflezRjeMeYhcv8gmRkZGZEXkyxWd/xgtzIZLDLJzq/iYHZVB6pN6cbNqwagA4Dxp/sBXdfZ/YbNqO/ZuuklQT9ojwfDbdEGAC+9+Co6smpr0l/Oirp247KD1d553G8HMgvoDJDxeaDrvIlDbc8/+6LOqx1Jb/wCunr9K4PQ9GFNghy207Z5IHt4zgDZGxFznc88/bzOt4WlN3imrl770gytDhw7EsgC1GhjEcgUanfIMpDpfuTYHqwEatkWXqvUfur8MzoPG0lvZKGuHAwAe1BikJNQoz7aGIc1Zuu832LLfLkW5M7fUYbaXdx0GAAAePLc0zovK6U3LqHLB1+48JnAWg0ytvk7bCJfjoHsxoCveTwae52hDYOMbgXqe3cZnnjivM7TTOmNYnT54IvBC8P4k4U1ZmsHctIDu0NmgTw/X84H2R+7BHJ4P/CbCWc7e/aczltBemNGXb76uQlywFkg48lI+wLUnq0cZBHWnYHMeOrU/aD9RCHwzOnHdQ4jHembcfnq50NBqxjkXA9M+8LkLMmJ3SFDkJn9ZMgLilsRkDt2rBUgZxQC7U/vcof+6VNnj/RcnnTkbsJXVz836YkbTuJqkDdR3KoDeW5xi9qSHjhqk+5BaDt58vSRm9eTjsyF8yDzE8ObRsJEDWz+j00Ut1o+Q+ZsAaw7Btm91O134vipIzPHAY4A0DGQ1y1ukf1mgjw/J6a2uSCHb2yzQKb3yl6na0i/r/BeARw7dvLQz3WAQwy0zY8BZoIsQI02hhMwYhMmqu3ngkz3I8fOBpmmHeRaq8NstLHmXnnYSyB33H7Ma9D1P/bY8UM75wEOIdC40AXAwTr2M9/ti0AuCbPdIUOQE1CLtvGcPKwxW8t8ORfWmE26B64h/b4kkN0pXf/RR48duvl/aC5IBLnDFzkDZGzzd9hEvhwDeZ18ORdW3MfXbC+KuU7XaAGyMcazHSavvfcXkgaZAXRhkGsePVFb23w5fGOTQPbHnpMvrwUyvmZ01Nj9IH0KsusbMABw/BDk2Xt7AfXPkKmNebenNnSgAGrPVg6y/HhpVyDj60QjrwVZjFDo+BIgs/uVg+xDPYAMxvX2vSq+l4OvW9XFbLf9cBK7pjA5hYlK+0mQWc9UAbJ4P8ajd1ybAbnzLPn3Q7BhOGk/buNBDt94ZaijIBuvZ6GebPv6LHuvBp0EmUziapArcmLan58TY9suilvIhnYIYY3ZpHvA2+I5sXfGBiD73hnbjHPZcOrUmb1iZC8Gmw9yCMfSILcvbskgH5XFIGuCjL0zBhnvd/r0/iwr3fxAvT9fBAJo5JffprhF9psJ8vycmNrmghy+sc0Cmd4re52uIcIa3CvvjFUgB6H08A+xGbwL2c+3nTmz/T8E2fQArxx8wX8GVxJk/MtHNnSgcAJGbMJEtf1ckOl+5NjZINNoxRvujDAbbay5Vx6EEsgU1kqQ5Yo1nxOHNgZw2/dBdjaz+T/d3OTgskBOQo36aGMc1pgtAXICatE2npOHNWZrmS/nwhqz4Xvg+jm/r9DmT8v5IDNetxBk9BIAY+Dxx5/cJDubG9SVgy8NmhswC2Rs83fYSL4sg9w6zA72QxvSsOI+D254na6xLMhycasOZL/tH9q3bfGTVDY1oCvXvtxhvuy9iywK8tFZDELHmnE/SD8XZDlflnPiWpDxiM5tDOpNDGYeyHhiIBs6UAC1ZysHWa5KR2ywJMj4OtHIa0EWIxQ6PglkZnvqfpB+yWKQbJDpfsPBq6B2tuHnVj7YcOeDGD4GVwJZgFWYxK4pTE5hotJ+EmTWM1WATItbEZA7dqzhfSu6H4INw0n7cVs5yC0Xg9jtQwNY+KltOFEdyHgIYOD8k7v/OOKdDuDqta+EfJkB3PYFkCtyYtqXbFUg76S4hWxohxDWmC0X5PB3QkEOI6i5IOctBlkPZIPNAGDgqfPP7pSpnZ2cfrvEmiC3L27JIK9T3PJG1wBkBtANgZyzGKR9vhwHGduefuq5nXG1kxMfXP/KcL/8NsUtst+CIB+KxSCzQeahpvdgV4tBsvLlqC0H5HD7rr72Z/WTHly/bAA4kPEvH9ncBgbWmG0eyCLUUAkyzZe94RaATPcT7kHuvfIgJCCzbbupHOSiZ8jDP8TW5hlylnemIJN8WQLZ9Q08+8wLq/O16gkPrl82YnGLei20MZicJWG2O2QIcgJqz0aOLcMas7XMl3NhjdnwPXD9GpD3YTHIPJANMsdBRkOD555dF+rVThZ+laoAMrb5OzQvfNXlyzLIuhgkF+TWi0HmghzPiZ0tF2T09mAMPP/cS6txtsqJrt24IoTZtF8Gcs2jJ2prmy9jGMajCSD7Y8/Jl7cAsnA/SH/ri0HmgYxhRTY6Bi8KAXhhJagXP8m1G1fEMJv2g8m5EshixRqWBBlfJxrdzkBmtqfuB+nvfDFIzMYsBhFBLvHO9NhisQ/gxedfXpy3RU9w/esrfJjt/4iDXBJKu0NaW9WjJ3LeaHGrFmS0X9H9EGwhuHR8kq0c5KJHT8M/rseE2btcDFINciLM9vZG/RdfeGVR5volD+4muABy45wY96tA3klxC9nQDiGsMVsuyMw9ICCHEVQlyKwH9m27XAyyJsj4jez+g7vmpRdfXQzqxQ584+ZV409Ud7ptFLdkkHUxyH6DLHpgAB/QJMj4OpGNjiEDZNp/+aULi7C3iIe+cfPAAKRgBXaiDn1+osZsbYtb1FYHMu+BvdHtCGR8P7wzVoG8z4tBnE3ywLSf451lkMkVNFfzd4kbNw9MHNaYbR7IItRQCTLNl73hFoBM9xPuQdJG75W9btdg23YT753pPVhyMUgMZG+yZxW3iI2C7HHlg5zzDNnZeA8cvx8MyMT26suvN+ev6QG/vnlg8opbMVsC5BLvTI4twxqzJUAWoA5eQ68Z9eu9s+vXgKyLQdqD7F+nbJvaF155oymDbUPujpm4m8iXZZC3uxiE2qR74Bo+rOioTUA+WotBvL3FfLke5Omf23e/Ma+9+mYzqJsd6OY310wOyDWPnqitbb6MYRiPVgEy5mceyLwHDq+Tjl0CWbgfpN8W5MOxGGQpkLnXvH7hYhMWm3jom99eNwCMB0b91iCLFWtoD7IbDwYSjS6wrQUyvmZ01EqQ93oxiLVleGd67MxnyEGYja5VypdTILdWE6Bbg1z16AkiINOcuBZkwTuH1+zOy9oKc+LQxgCeuB+tF4PEQJ5d3NrYYpCawlcWyMj27e2vzRuvXZrtpWcD/c2tG/bytr4YJAZyWU6MbGiHMu+cCzJzDwjILR49ybD6tqWfITtbCOuaIGc9emoAMh3DXM16R5hg1sUgpB21SfeAt8WLW94Z9xpk0QO7l0ZsYTuw0TGsBLIfrURAHu/BxTfensXkrBd/e/tr552FiRqztS1uUVvL4pY3uh2BjO+Hd8bmILuJKhS3YrbhRAKsMVsOyAKsAbi0vwzIuR44B2TcvvTmO9VcVofcE8y7L24RW8kzZLtLhndGG4tAptGLvU7XEL1ucK+8M4og7/ViELc7sjHbbV8AuXFOjPtVIJe8sc1Q9TvBrTs3fe+MjjYrzIZcWGO2BMgC1MFr0LHdbnO9s+tLUMdArl0McuzYie6f//zDTUF2EueG0v5ERS+pB5lAvRWQa3LiGMiid/Z+JwbeuvheFZtVL5pgXjZfrgO5Xb6cC2vMJt0D14h7Z2zr4LHHjle/Ac/V7//3m5kPMgbANULvnAuyBCsZQ8NnyKKNjKEWZDzcty+VQ10cct+6c9PsPl/GMIxHqwAZ8zMPZN4Dh9dJx86DfPzYyZ2BK+nMaf6rVH/77SGaj1JYyQCaBBmDgmzoQCGsMds8kEWooRJkJsz2ahrejcxX8cS5ffcb553REdbJl/NBDp4hox+Bd0YvXB5kN/oTJ05tDtxWevi3Xw03UXNBDrwW2pjrnUNbAuQS70yOXV0IJNdK2+++/UHRHCn20BLI8uOlSpA3uhjEP4fsdSWQDzPEWE+ee8pe5/8+/BnxsnyYHdocrLQ/P1+WQa4JszlbiYom153vvrVnCECOeWfab74YZCbICy8GOSoQ5+iXX38yLKwLg7ybfBm/eY1HywTZXb6B9975KHv+FHnouuJWHcj7vBjk5MnTCrCgp5/yv/vpp59/8Ke8AHIArmdLeGB3yCyQq54he7Z8kPETBnddaHSFTjp74t397pYpAfkoLgZRkOv140/f+4H4ni0GQS+qA1nwztPPD979OGtuZXtoC2HMA+P9hs5KIAveGW0sArlwMYiCPF/TN0z88ON9NJ/ToTTt72wxSARk6Zm/f4k8yKXl7qyJ+N2923HvDBFYN7oYJGbzsJdA7gBOnlCQl9L3P9wb53M5yLtaDDKaWVsAq2DDbxhu89D+8P1PkvMty0Nj7zz8I3nruYtBaH+bi0EU5OX1wvih9A++/87svrhFbXUgI555GzqQoe1MJYG+d/+OmQPyYVoMoiCvr+mD6e89uOPN7nWKW8RW8gzZ7pLhndFGFuSCsDs5Qe8/uOuORkA+Ot/3pI+etqB79++E3rrEO0MurDFbAuSSMBttDEAWoP74w0+j8zCvKLYayFIojWxohyTIDRaDKMjb0fTh9Hfv3TJFIAfeug7kdvkyD2uuLaboZL3//V2z9cUgMVvogen4JO88tBXm7coucmqeL5vpP9tvly/XgIyjkOHnJx99Js7LqIcegNtFcQu10Q5lYXY6lMa2Do1HQd6+LrzyRjf9XUGbfDkfZL/ansiX0QvnguyHErxEoB/8cM/woTREQGYAjcG6MZChAzhxXGHeF02fZ23/Nj8X5JUWg2SDLC5XpeNLE/2IZJhmddd16LHV0J7gGGzT3kN76mNbN4Hd+W3WNp5netPocDtmm07e2aMC2E3jfsg27D4edWwrzPup1y9c7IwxIwwGzPg/ALg+bdt9HSzGTDC7thnfJMzUAbd9tIybRxuA3ddr0z5t23OA7bvx2YGAMQb+5/O/imTLQEsgdz6sUZC7CMjTa3JAtrDKNvT2IIJs2wTkDjo4fnx7f4Osytcbr13qHKwQBdlgUASQHaxpkE0MZEBeWADZwjodiQMZnTP2KaEs0NPyOxbkwWBhjYIMEZCtLRfWOMjWO3edDLL3BuTgV5gPh958/a3OZIBsfSAGNAAZHKwJkGVYYzYOZMgA2csPArET+cefHriXLJYvd+BvRn3R1uF0F8DrOwO24SPRfLkDgGMb/HQQ1Tzd/Oaan5UKOTG14Tw4yInpfmhjkBPHbDRfHg0GNfCRDBrDBPjU/uzTvwRzVwi5J6+Wzpc7Ykvny77XzcuXp9fZszuPLIbZUyAvh9kK8+HUxTff6VwuDZD0yIZ4XZwTU+8MIHtdm3Rn5MuMRw7ayCO7GgF6DaMA6B9//t50yLOlQPZhBQHkGKx8KB0D2Z4/BrI9TAgywPApmLUTRrV9Xbr4bodhjYIMGSBnF7fiIOPilgiy9wbEgWzEPDoAmgMZ58sByLaZAXJJcSsCssuXIyAT72xjja5TmI+IfO+cBpnPl31Y8/Jl+rowJxa9M5MvW6jt2AeY//rf/xFQHQIdLW4xIEM+yJN3zipuWQ/MgIzGkwLZvkWN++7yo3BV6+rtS+91BoGTAtl659kg27eRDJAxrBLI4IHM9ScxOXQ6JxZtEAdZzpc5kCECMg2l0yADgMJ8BPXOW+93/qOnGMgxWPlQOgayyQHZHmYeyJMYDw0ggSzmy9MLS0HuQljDUDoGMvHiAsjeM3XVkdO7b39gPTWbL0MM1nhOnLsYRASZeGcOZPsoLgLyJG+W//LrT8bN+wlUt1tgQxs7oR2z4Z8jo7bBtu0m5jV2OHz70UePKdFHXFeufTnyMmEDE+XOG0/tqM3YnYxriDaXy5P9qA0AqDfO0b9+9m92bntruZ13hjjI2bDGbB1mFAJ4CawdGk9o81ml3/ekobYKAKxnndoAa4OMYfXOWAUyJy/kDkLrjobgfiidly/T13Fhtl/4EnNiHI6DK4rZ8aN+boiiOjp6/92Pu7zilhSCx3JiZxNzYpovAx9az5H/11Yd453H7W4L6Ys23gNbm7cpxyPzoTTt0xui3lmFFXhd7BkFm+e/iUdm23YTH2YPh27jkamsh3748BfjF75iXpf3zr5HBs8j4+KW52k7/OgpUvgCyPbIJUUE1dHSh+/9qeO881qLQVp7ZCoXcksge7DGwuw4yDbMZkH2Q+kYyPbZcwTkQUYXkKh4sWE2ffacH2ZjkCECsjt9W5D/87/+3R7MAp3KifMWg0RARnl4yaou6dFTDGRjjK7TVon66IM/dyHIUACyiYBsVgOZk+ehi8LsjOKWDzINpUOQoQHItHqoUnH6+MNPu60sBmkpWxSzALkNru/ZOlSrsg3RJha38GvIGGgbQC4iTDcY9fSTR1RZkopboY0vbi316GmOegCAv//20GBwARiQKdTjPxhktm03lYNMb0oKZHXMqiIZw4DMwLoHIE96BKAyX/aKW0ybFreCMLsTQ+tYyIJzHi/MxvmQSpWhTz7+rKP5cry4hWwQS//W11QY6wG4MBuHzuPPRJjt+e8gzMY2PxrO88jUA0+5MrUZOHnyjIbbqmwlV3UR7zzsux2PTDUUxRLPkP3Cl+Cd2cJXB9QjT8rzyO5d076LosKXs6G2SlWgT//0L110VRds0yNLGj10jUcWcmJsg3yP7N8c3yPL+bLfPnXqrHpnVbHi+bJcx9mihip3pLg1tmAOyLkV6yTIpHARQq1S1cjsPciThqIYU9zyw+x1FoMExS1S+MLPB70wG+c8KlWhPvvzX7pdLgZpqSHknopYJMyOF7eQDWZ4ZPHRU55HnmxnTj+u4baqWvvqkan6f/zj9+AL3cXFINQGdSDHniE7m8G7kP1Cm0o1V/sKMVb3xx+/G+7RUzRfBhnk2sUgAcixItjYx2H22TNPqIdWzRL+I4d9VT8tGskBue4ZslzckivWQpg99p1t+Pfs2XMKs0oFAH1evrzsYpBakPf+7VSlaqw+ni+vBbKQLw8H9tr+obW2rVJhPRKu6gofN+V9MkjFqi7y6Mn2YWybsM3ZVKoWwp+eua/qpeJW68UgWWH2cPAi73zuifN7/0tQqVppfA6922fI1WG2emeVylOPV3VhbR1kRVmlCtW3ALl2MUgWrKINh/YqlQpgXMsd+3hRubjlbGJxKyh0gf3ztFjhC6ZjcjaYbCT3VqkaaN8LY3328kxct6IeORlKY1s6J455ZIPa588/s9c3X6VqrR4Ag7ziYpAskN1xMchkb5VKNap3z5ChIciVxS0CMh4DBVkL3CpVqB6H1kGYPfwTaaM+QF1xywM+F2SlWaXi1NuwuNY7A2TCGrNhcGlfoVapctV7+XMslG4YZjsbkxNngqwht0oV6pEBZPcYyHpr8iiKtcHYpo+bABK2sT1FB+N5bJ+1hW2Vagnt86Or3nueK+XLbL9+MUg8Xzb4MGQ/9ErlWaUK1POPrIRHTwB1+XIByN52kEBWmlUqTv3ai0HCfLkcZGMMPP/cS3sbFqlUS6lvD3JtcSsPZJVKJavH8GZ53QTIuc+Q6aOnEOSwzfVVKpVTv/ZikKh3pscWQFacVSpe/QRKuXdOgDwrzMaeW72zSpUrV+VedTHIPJA1l1apePVzQC4LswUbPY8YZhs0SJVKxamXQPawSoIsFLcCcGm/HGQFW6WSFSmK5YNc9OgJvSAeZssgGyVapWIVFsXE4hbuV4CcmRMnQVaWVSpRvfeHDizINTkxtpWD7K9eU++sUuWqBzBMKD22YYniFrUVgmwUapVKUm+hTIIsQI02FoFMoXaHTIOsPKtUrFBRrNA7o41xWGO2vHxZvbNKlSf7mWJzw+yhuVC+rN5ZpcpSnx1aV4Cc9eipAGT1zipVXONHEE1QknbQH9vGtQ0Ku/1v0fABnfr+Rxk5m/tGjXE/w7THE+nST9XS2tePIbJruVdbDEI8Mrt97PuflIJtCrRKxamPgrxGcasQZEVZpZLVW1xmglxb3KoBWUNulYpX5MvqQA6zZ4IsgkvGoCCrVGXqQ1hBBnnmYhDRO0MByBp2q1Si3N9Dl4TZdqfW+bL0l1+hTaVSheq3kS/LIKt3VqnyNXw/NAAKsx2ctL+bfNmAPySjDlqlEuSeQ1eALFellwZZiVapOPWzQE6G0pkge1DHQVbvrFLJ6sXweVZOjG3x4pYcSksgax6tUknq1wJZDrPDUDoKsvuhUqmIegvh4sUtaqsDWXlWqWT5RbGhkeedoRJkmi9DKchKs0olKQy5h0YdyEvlywhkLYqpVLJ6Cu+yi0Hq82UtiqlUaaGPIMoDudUzZIPPkxlmG/xClUoVCH1ZXYvCVw3IGFZrEUFWnFUqWX01yJWLQQKQY2E22kG9s0qVVs/nz2sWt/JBxmNQqVSh+hjIqxS3cJiNdoiBrDirVLzGothai0GEfBkdKAmyemeVSpT4EURLLAZhHz1JIAt/p61Qq1Syeuqd18mX07BmeW6VSuWpn+BYJ18ugFW0GSVapRLU1zxDLs+Xa0B2g6Eg61oxlYpXXwLy7MUgrUBWnlUqVr3kdQEiIMfCbLRDWb7MgyzaVCpVoH5ecQvZ0A61OXEcZLeThtwqFS/73VarLAYRQcaw8jaDxqM8q1S8vKJYOidGNoCdgKzeWaWSFXxZXdI7o41FIHu5NA8y27ab6GtUKhWVC7mHf0AEuclikHS+nAZZcVapJPXpCnYurDFbOsz29g7CbGxToFUqSW7pZxOQeQ+MjloIMooKFGSVKqkeQ7QuyBhWdNQEyAq2SiULFcUyQabFrUROHNqEnDgCskKsUuUJfS43VObErh/3zlJO7J1RQVapZqivB5kPpbEtXtzyzqggq1QN5P899AqLQRRklWo5hR66djEIRECm+TIoyCrVEkKfy12fL4vFrQB4H1wFWaVqq34Xi0EUZJVqGfVrLgZRkFWqZdWnHze5hi4GUam2LfRldQBsyBwrbkVAVohVqvXlln6y3lnKiV176CvIKtUWhIpiOcUtZAMFWaXamnpdDKJSHR71Isg0XwYFWaXautDncuszZJVq34VWiinIKtW+qw/zZQVZpdpXoaKYgqxS7bt6BVmlOjz6f6pOcYc3O8+nAAAAAElFTkSuQmCC"/><linearGradient id="_Linear7" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(3.88722e-14,659.127,-634.831,4.03599e-14,76.8169,62.5212)"><stop offset="0" style="stop-color:#333530;stop-opacity:0"/><stop offset="1" style="stop-color:#828282;stop-opacity:0"/></linearGradient><image id="_Image9" width="258px" height="200px" xlink:href="data:image/png;base64,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"/><linearGradient id="_Linear10" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(2.86481e-15,-69.2121,46.7859,4.23802e-15,289.702,241.741)"><stop offset="0" style="stop-color:#fff;stop-opacity:1"/><stop offset="1" style="stop-color:#fff;stop-opacity:0"/></linearGradient><linearGradient id="_Linear12" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(3.98975e-14,658.139,-651.576,4.02994e-14,83.6072,69.8551)"><stop offset="0" style="stop-color:#fff;stop-opacity:0"/><stop offset="1" style="stop-color:#fff;stop-opacity:0"/></linearGradient><image id="_Image14" width="115px" height="169px" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAHMAAACpCAYAAAAY5gKvAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAC0klEQVR4nO3cwVLDMAwA0aT//8/lVKZAWtJEsuTNvjM0sXfkhg6wLmrlfr/fj37vGnkjeu9MqN/Wdf3TzpjBIoM9bIXb/LroC19FRrRl2R9u83sjb4SqY7jN14t8MYrKo/LUNbIvMINZJu/f6428WCezTt/b61defDRiwGdtbiRLdMBO8X5re2NnXCngsylucq+rRnyY6mZfyf6YbBbT3vjVp3DLdAsw4mtTLSQqJCngsykWZcR9Wi/OiJ9puUifTo9pt1Cn8bg2CzbieS0WbsgY5YuPCHn1iA+lm2DIWCUbYcQcwzfEkHmGboohcw3bGEPmG7I5hhwjfYPOhjTifqkbZcix0jbLkOOlbJgha4RvmiHr3KpvQHFCp8CprBW2eYasF7KBhuyh/D3TkHFOx8z6Q1V97tRUeLz2UnbMGjLe4Zger/2UTKZTmeNQTKeyp48nxIeevoYes4bM9VFMj9fehk2mU5lvd0ynsr/yz2YVZ0hMj9gxdsX0iJ1D+mQ6leP8G9OpnEfqZDqVY/k0C/I2pkfsXNIm0yN2PI9ZkJcxPWLnkzKZHrE1PGZBNmN6xM7JyQQxJkh4TB9+6vyJ6fvlvDxmQYwJEhrT98taP2L6fjk3j1kQY4IYE8SYIN8xffiZX9hk+mNJPY9ZEGOCGBPEmCDGBLktiz+WUDiZIMYEMSaIMUGMCWJMEGOCGBPEmCA3P/3hcDJBjAliTJCQmP7+Tw9OJogxQYwJYkwQY4IYE8SYIMYEMSaIMUGMCWJMEGOCGBPEmCDGBDEmiDFBjAliTBBjghgTxJggxgQxJogxQYwJYkwQY4IYE8SYIMYEMSaIMUGMCWJMEGOCGBPEmCDGBDEmiDFBjAliTBBjghgTxJggp2P6v2b7cDJBjAliTBBjghgTxJggxgQxJogxQYwJYkwQY4IYE8SYIMYEMSaIMUGMCWJMEGOCGBPEmCDGBPkCk9jZKJdIbpQAAAAASUVORK5CYII="/><linearGradient id="_Linear15" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(309.823,0,0,219.023,41.6987,359.645)"><stop offset="0" style="stop-color:#02c49a;stop-opacity:1"/><stop offset="0.21" style="stop-color:#009f00;stop-opacity:1"/><stop offset="0.85" style="stop-color:#01af17;stop-opacity:1"/><stop offset="1" style="stop-color:#40c965;stop-opacity:1"/></linearGradient><linearGradient id="_Linear16" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(83.9654,128.268,-128.268,83.9654,42.3484,247.947)"><stop offset="0" style="stop-color:#fff;stop-opacity:1"/><stop offset="0.53" style="stop-color:#fff;stop-opacity:0.77"/><stop offset="1" style="stop-color:#fff;stop-opacity:0"/></linearGradient></defs></svg> \ No newline at end of file
diff --git a/data/homer/svg/calibreweb.svg b/data/homer/svg/calibreweb.svg
new file mode 100644
index 0000000..55b0822
--- /dev/null
+++ b/data/homer/svg/calibreweb.svg
@@ -0,0 +1,9 @@
1<svg width="256" height="256" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
2<!--Created with Method Draw https://editor.method.ac/-->
3 <title>Calibre-Web</title>
4
5 <g>
6 <title>Layer 1</title>
7 <image href="data:image/png;base64,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" xlink:href="data:image/png;base64,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" id="svg_1" height="256" width="256" y="0" x="0"/>
8 </g>
9</svg>
diff --git a/data/homer/svg/changedetection.svg b/data/homer/svg/changedetection.svg
new file mode 100644
index 0000000..0bcbad1
--- /dev/null
+++ b/data/homer/svg/changedetection.svg
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 188 168"><filter id="a" width="188" height="168" x="0" y="0" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feColorMatrix in="SourceAlpha" result="hardAlpha" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/><feOffset dy="15"/><feGaussianBlur stdDeviation="18"/><feColorMatrix values="0 0 0 0 0.00784314 0 0 0 0 0.188235 0 0 0 0 0.482353 0 0 0 0.13 0"/><feBlend in2="BackgroundImageFix" result="effect1_dropShadow_901_7930"/><feBlend in="SourceGraphic" in2="effect1_dropShadow_901_7930" result="shape"/></filter><filter id="b" width="49.6" height="49.6" x="69.2" y="50.2" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feColorMatrix in="SourceAlpha" result="hardAlpha" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/><feOffset dy="2"/><feGaussianBlur stdDeviation="4"/><feColorMatrix values="0 0 0 0 0.00784314 0 0 0 0 0.188235 0 0 0 0 0.482353 0 0 0 0.12 0"/><feBlend in2="BackgroundImageFix" result="effect1_dropShadow_901_7930"/><feBlend in="SourceGraphic" in2="effect1_dropShadow_901_7930" result="shape"/></filter><g filter="url(#a)"><path fill="#f2f4f8" d="M36 31a10 10 0 0 1 10-10h96a10 10 0 0 1 10 10v3H36z"/><path fill="#fff" d="M36 34h116v73a10 10 0 0 1-10 10H46a10 10 0 0 1-10-10z"/><circle cx="46.4" cy="27.4" r="2.4" fill="#f34949"/><circle cx="53.6" cy="27.4" r="2.4" fill="#ec930d"/><circle cx="60.8" cy="27.4" r="2.4" fill="#45b013"/><path fill="#a8cbff" d="M43 39h102v71H43z"/><path stroke="#fff" stroke-dasharray="5 5" stroke-width="2" d="M47 44h94v61H47z"/><g filter="url(#b)"><circle cx="94" cy="73" r="16.8" fill="#fff"/></g><path fill="#1882ff" fill-rule="evenodd" d="M94 64.25h-.07c-.66 0-1.1 0-1.5.07-.68.12-1.33.4-1.9.78-.34.25-.64.55-1.1 1.02l-.06.05-.48.48a3.65 3.65 0 0 0-3.64 3.65v3.53c0 1.1 0 1.96.06 2.66.06.71.18 1.32.46 1.87.45.89 1.18 1.62 2.07 2.07.55.28 1.16.4 1.87.46.7.06 1.56.06 2.66.06h3.26c1.1 0 1.96 0 2.66-.06a4.88 4.88 0 0 0 1.87-.46 4.75 4.75 0 0 0 2.07-2.07c.28-.55.4-1.16.46-1.87.06-.7.06-1.56.06-2.66V70.3a3.65 3.65 0 0 0-3.64-3.65l-.48-.48-.05-.05a8.71 8.71 0 0 0-1.12-1.02c-.56-.39-1.2-.66-1.88-.78a8.71 8.71 0 0 0-1.51-.07zM97.2 73a3.2 3.2 0 1 1-6.4 0 3.2 3.2 0 0 1 6.4 0z" clip-rule="evenodd"/></g></svg> \ No newline at end of file
diff --git a/data/homer/svg/cloudflare.svg b/data/homer/svg/cloudflare.svg
new file mode 100644
index 0000000..a8cbf3b
--- /dev/null
+++ b/data/homer/svg/cloudflare.svg
@@ -0,0 +1,10 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<svg width="256px" height="116px" viewBox="0 0 256 116" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="xMidYMid">
3 <g>
4 <g transform="translate(0.000000, -1.000000)">
5 <path d="M202.3569,50.394 L197.0459,48.27 C172.0849,104.434 72.7859,70.289 66.8109,86.997 C65.8149,98.283 121.0379,89.143 160.5169,91.056 C172.5559,91.639 178.5929,100.727 173.4809,115.54 L183.5499,115.571 C195.1649,79.362 232.2329,97.841 233.7819,85.891 C231.2369,78.034 191.1809,85.891 202.3569,50.394 Z" fill="#FFFFFF"></path>
6 <path d="M176.332,109.3483 C177.925,104.0373 177.394,98.7263 174.739,95.5393 C172.083,92.3523 168.365,90.2283 163.585,89.6973 L71.17,88.6343 C70.639,88.6343 70.108,88.1033 69.577,88.1033 C69.046,87.5723 69.046,87.0413 69.577,86.5103 C70.108,85.4483 70.639,84.9163 71.701,84.9163 L164.647,83.8543 C175.801,83.3233 187.486,74.2943 191.734,63.6723 L197.046,49.8633 C197.046,49.3313 197.577,48.8003 197.046,48.2693 C191.203,21.1823 166.772,0.9993 138.091,0.9993 C111.535,0.9993 88.697,17.9953 80.73,41.8963 C75.419,38.1783 69.046,36.0533 61.61,36.5853 C48.863,37.6473 38.772,48.2693 37.178,61.0163 C36.647,64.2033 37.178,67.3903 37.71,70.5763 C16.996,71.1073 0,88.1033 0,109.3483 C0,111.4723 0,113.0663 0.531,115.1903 C0.531,116.2533 1.593,116.7843 2.125,116.7843 L172.614,116.7843 C173.676,116.7843 174.739,116.2533 174.739,115.1903 L176.332,109.3483 Z" fill="#F4811F"></path>
7 <path d="M205.5436,49.8628 L202.8876,49.8628 C202.3566,49.8628 201.8256,50.3938 201.2946,50.9248 L197.5766,63.6718 C195.9836,68.9828 196.5146,74.2948 199.1706,77.4808 C201.8256,80.6678 205.5436,82.7918 210.3236,83.3238 L229.9756,84.3858 C230.5066,84.3858 231.0376,84.9168 231.5686,84.9168 C232.0996,85.4478 232.0996,85.9788 231.5686,86.5098 C231.0376,87.5728 230.5066,88.1038 229.4436,88.1038 L209.2616,89.1658 C198.1076,89.6968 186.4236,98.7258 182.1746,109.3478 L181.1116,114.1288 C180.5806,114.6598 181.1116,115.7218 182.1746,115.7218 L252.2826,115.7218 C253.3446,115.7218 253.8756,115.1908 253.8756,114.1288 C254.9376,109.8798 255.9996,105.0998 255.9996,100.3188 C255.9996,72.7008 233.1616,49.8628 205.5436,49.8628" fill="#FAAD3F"></path>
8 </g>
9 </g>
10</svg>
diff --git a/data/homer/svg/discord.svg b/data/homer/svg/discord.svg
new file mode 100644
index 0000000..a5f0198
--- /dev/null
+++ b/data/homer/svg/discord.svg
@@ -0,0 +1,6 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<svg width="256px" height="256px" viewBox="0 -28.5 256 256" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="xMidYMid">
3 <g>
4 <path d="M216.856339,16.5966031 C200.285002,8.84328665 182.566144,3.2084988 164.041564,0 C161.766523,4.11318106 159.108624,9.64549908 157.276099,14.0464379 C137.583995,11.0849896 118.072967,11.0849896 98.7430163,14.0464379 C96.9108417,9.64549908 94.1925838,4.11318106 91.8971895,0 C73.3526068,3.2084988 55.6133949,8.86399117 39.0420583,16.6376612 C5.61752293,67.146514 -3.4433191,116.400813 1.08711069,164.955721 C23.2560196,181.510915 44.7403634,191.567697 65.8621325,198.148576 C71.0772151,190.971126 75.7283628,183.341335 79.7352139,175.300261 C72.104019,172.400575 64.7949724,168.822202 57.8887866,164.667963 C59.7209612,163.310589 61.5131304,161.891452 63.2445898,160.431257 C105.36741,180.133187 151.134928,180.133187 192.754523,160.431257 C194.506336,161.891452 196.298154,163.310589 198.110326,164.667963 C191.183787,168.842556 183.854737,172.420929 176.223542,175.320965 C180.230393,183.341335 184.861538,190.991831 190.096624,198.16893 C211.238746,191.588051 232.743023,181.531619 254.911949,164.955721 C260.227747,108.668201 245.831087,59.8662432 216.856339,16.5966031 Z M85.4738752,135.09489 C72.8290281,135.09489 62.4592217,123.290155 62.4592217,108.914901 C62.4592217,94.5396472 72.607595,82.7145587 85.4738752,82.7145587 C98.3405064,82.7145587 108.709962,94.5189427 108.488529,108.914901 C108.508531,123.290155 98.3405064,135.09489 85.4738752,135.09489 Z M170.525237,135.09489 C157.88039,135.09489 147.510584,123.290155 147.510584,108.914901 C147.510584,94.5396472 157.658606,82.7145587 170.525237,82.7145587 C183.391518,82.7145587 193.761324,94.5189427 193.539891,108.914901 C193.539891,123.290155 183.391518,135.09489 170.525237,135.09489 Z" fill="#5865F2" fill-rule="nonzero"></path>
5 </g>
6</svg> \ No newline at end of file
diff --git a/data/homer/svg/filebrowser.svg b/data/homer/svg/filebrowser.svg
new file mode 100644
index 0000000..c8badde
--- /dev/null
+++ b/data/homer/svg/filebrowser.svg
@@ -0,0 +1,147 @@
1<?xml version="1.0" encoding="UTF-8" standalone="no"?>
2<svg
3 xmlns:dc="http://purl.org/dc/elements/1.1/"
4 xmlns:cc="http://creativecommons.org/ns#"
5 xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
6 xmlns:svg="http://www.w3.org/2000/svg"
7 xmlns="http://www.w3.org/2000/svg"
8 xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
9 xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
10 xml:space="preserve"
11 width="560"
12 height="560"
13 version="1.1"
14 style="clip-rule:evenodd;fill-rule:evenodd;image-rendering:optimizeQuality;shape-rendering:geometricPrecision;text-rendering:geometricPrecision"
15 viewBox="0 0 560 560"
16 id="svg44"
17 sodipodi:docname="icon_raw.svg"
18 inkscape:version="0.92.3 (2405546, 2018-03-11)"
19 inkscape:export-filename="/home/umarcor/filebrowser/logo/icon_raw.svg.png"
20 inkscape:export-xdpi="96"
21 inkscape:export-ydpi="96"><metadata
22 id="metadata48"><rdf:RDF><cc:Work
23 rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
24 rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title /></cc:Work></rdf:RDF></metadata><sodipodi:namedview
25 pagecolor="#ffffff"
26 bordercolor="#666666"
27 borderopacity="1"
28 objecttolerance="10"
29 gridtolerance="10"
30 guidetolerance="10"
31 inkscape:pageopacity="0"
32 inkscape:pageshadow="2"
33 inkscape:window-width="1366"
34 inkscape:window-height="711"
35 id="namedview46"
36 showgrid="false"
37 inkscape:zoom="0.33714286"
38 inkscape:cx="-172.33051"
39 inkscape:cy="280"
40 inkscape:window-x="0"
41 inkscape:window-y="20"
42 inkscape:window-maximized="1"
43 inkscape:current-layer="svg44" />
44 <defs
45 id="defs4">
46 <style
47 type="text/css"
48 id="style2">
49 <![CDATA[
50 .fil1 {fill:#FEFEFE}
51 .fil6 {fill:#006498}
52 .fil7 {fill:#0EA5EB}
53 .fil8 {fill:#2979FF}
54 .fil3 {fill:#2BBCFF}
55 .fil0 {fill:#455A64}
56 .fil4 {fill:#53C6FC}
57 .fil5 {fill:#BDEAFF}
58 .fil2 {fill:#332C2B;fill-opacity:0.149020}
59 ]]>
60 </style>
61 </defs>
62 <g
63 id="g85"
64 transform="translate(-70,-70)"><path
65 class="fil1"
66 d="M 350,71 C 504,71 629,196 629,350 629,504 504,629 350,629 196,629 71,504 71,350 71,196 196,71 350,71 Z"
67 id="path9"
68 inkscape:connector-curvature="0"
69 style="fill:#fefefe" /><path
70 class="fil2"
71 d="M 475,236 593,387 C 596,503 444,639 301,585 L 225,486 339,330 c 0,0 138,-95 136,-94 z"
72 id="path11"
73 inkscape:connector-curvature="0"
74 style="fill:#332c2b;fill-opacity:0.14902003" /><path
75 class="fil3"
76 d="m 231,211 h 208 l 38,24 v 246 c 0,5 -3,8 -8,8 H 231 c -5,0 -8,-3 -8,-8 V 219 c 0,-5 3,-8 8,-8 z"
77 id="path13"
78 inkscape:connector-curvature="0"
79 style="fill:#2bbcff" /><path
80 class="fil4"
81 d="m 231,211 h 208 l 38,24 v 2 L 440,214 H 231 c -4,0 -7,3 -7,7 v 263 c -1,-1 -1,-2 -1,-3 V 219 c 0,-5 3,-8 8,-8 z"
82 id="path15"
83 inkscape:connector-curvature="0"
84 style="fill:#53c6fc" /><polygon
85 class="fil5"
86 points="305,212 418,212 418,310 305,310 "
87 id="polygon17"
88 style="fill:#bdeaff" /><path
89 class="fil5"
90 d="m 255,363 h 189 c 3,0 5,2 5,4 V 483 H 250 V 367 c 0,-2 2,-4 5,-4 z"
91 id="path19"
92 inkscape:connector-curvature="0"
93 style="fill:#bdeaff" /><polygon
94 class="fil6"
95 points="250,470 449,470 449,483 250,483 "
96 id="polygon21"
97 style="fill:#006498" /><path
98 class="fil6"
99 d="m 380,226 h 10 c 3,0 6,2 6,5 v 40 c 0,3 -3,6 -6,6 h -10 c -3,0 -6,-3 -6,-6 v -40 c 0,-3 3,-5 6,-5 z"
100 id="path23"
101 inkscape:connector-curvature="0"
102 style="fill:#006498" /><path
103 class="fil1"
104 d="m 254,226 c 10,0 17,7 17,17 0,9 -7,16 -17,16 -9,0 -17,-7 -17,-16 0,-10 8,-17 17,-17 z"
105 id="path25"
106 inkscape:connector-curvature="0"
107 style="fill:#fefefe" /><path
108 class="fil6"
109 d="m 267,448 h 165 c 2,0 3,1 3,3 v 0 c 0,1 -1,3 -3,3 H 267 c -2,0 -3,-2 -3,-3 v 0 c 0,-2 1,-3 3,-3 z"
110 id="path27"
111 inkscape:connector-curvature="0"
112 style="fill:#006498" /><path
113 class="fil6"
114 d="m 267,415 h 165 c 2,0 3,1 3,3 v 0 c 0,1 -1,2 -3,2 H 267 c -2,0 -3,-1 -3,-2 v 0 c 0,-2 1,-3 3,-3 z"
115 id="path29"
116 inkscape:connector-curvature="0"
117 style="fill:#006498" /><path
118 class="fil6"
119 d="m 267,381 h 165 c 2,0 3,2 3,3 v 0 c 0,2 -1,3 -3,3 H 267 c -2,0 -3,-1 -3,-3 v 0 c 0,-1 1,-3 3,-3 z"
120 id="path31"
121 inkscape:connector-curvature="0"
122 style="fill:#006498" /><path
123 class="fil1"
124 d="m 236,472 c 3,0 5,2 5,5 0,2 -2,4 -5,4 -3,0 -5,-2 -5,-4 0,-3 2,-5 5,-5 z"
125 id="path33"
126 inkscape:connector-curvature="0"
127 style="fill:#fefefe" /><path
128 class="fil1"
129 d="m 463,472 c 3,0 5,2 5,5 0,2 -2,4 -5,4 -3,0 -5,-2 -5,-4 0,-3 2,-5 5,-5 z"
130 id="path35"
131 inkscape:connector-curvature="0"
132 style="fill:#fefefe" /><polygon
133 class="fil6"
134 points="305,212 284,212 284,310 305,310 "
135 id="polygon37"
136 style="fill:#006498" /><path
137 class="fil7"
138 d="m 477,479 v 2 c 0,5 -3,8 -8,8 H 231 c -5,0 -8,-3 -8,-8 v -2 c 0,4 3,8 8,8 h 238 c 5,0 8,-4 8,-8 z"
139 id="path39"
140 inkscape:connector-curvature="0"
141 style="fill:#0ea5eb" /><path
142 class="fil8"
143 d="M 350,70 C 505,70 630,195 630,350 630,505 505,630 350,630 195,630 70,505 70,350 70,195 195,70 350,70 Z m 0,46 C 479,116 584,221 584,350 584,479 479,584 350,584 221,584 116,479 116,350 116,221 221,116 350,116 Z"
144 id="path41"
145 inkscape:connector-curvature="0"
146 style="fill:#2979ff" /></g>
147</svg>
diff --git a/data/homer/svg/filerun.svg b/data/homer/svg/filerun.svg
new file mode 100644
index 0000000..36b3552
--- /dev/null
+++ b/data/homer/svg/filerun.svg
@@ -0,0 +1,7 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<svg width="256px" height="256px" viewBox="0 0 256 256" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
3 <title>FileRun icon 256x256 Diap</title>
4 <g id="FileRun-icon-256x256-Diap" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
5 <path d="M151.385542,109.313253 L143.36603,131.353356 L135.858205,131.353356 C127.849928,131.353356 118.995985,137.846676 116.081099,145.857079 L116.081099,145.857079 L102.641368,182.79218 L117.247462,182.79218 L77.2489689,212.124498 L58.5983936,182.79218 L73.2565754,182.79218 L89.4232671,138.363608 C95.2612107,122.319304 112.996671,109.313253 129.036717,109.313253 L129.036717,109.313253 L151.385542,109.313253 Z M175.429665,43 L201.506024,72.3872215 L154.005412,101.773421 L159.360708,87.0808323 L122.491873,87.0808323 C106.281663,87.0808323 88.358005,100.201551 82.4581725,116.387314 L82.4581725,116.387314 L77.0731935,131.160643 L55,131.160643 L63.0636503,109.039998 C73.4006399,80.6819523 104.803634,57.6936108 133.203487,57.6936108 L133.203487,57.6936108 L170.073346,57.6936108 L175.429665,43 Z" id="FileRun-icon" fill="#590099"></path>
6 </g>
7</svg> \ No newline at end of file
diff --git a/data/homer/svg/freshrss.svg b/data/homer/svg/freshrss.svg
new file mode 100644
index 0000000..caa987d
--- /dev/null
+++ b/data/homer/svg/freshrss.svg
@@ -0,0 +1,12 @@
1<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256">
2 <title>Logo FreshRSS</title>
3 <circle fill="#0062BE" cx="128" cy="128" r="33"/>
4 <g fill="none" stroke="#0062BE" stroke-width="24">
5 <g stroke-opacity="0.3">
6 <path d="M12,128 A116,116 0 1,1 128,244"/>
7 <path d="M54,128 A74,74 0 1,1 128,202"/>
8 </g>
9 <path d="M128,12 A116,116 0 0,1 244,128"/>
10 <path d="M128,54 A74,74 0 0,1 202,128"/>
11 </g>
12</svg>
diff --git a/data/homer/svg/grocy.svg b/data/homer/svg/grocy.svg
new file mode 100644
index 0000000..da0d9fa
--- /dev/null
+++ b/data/homer/svg/grocy.svg
@@ -0,0 +1,33 @@
1<?xml version="1.0" standalone="no"?>
2<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
3 "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
4<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
5 width="242.000000pt" height="93.000000pt" viewBox="0 0 242.000000 93.000000"
6 preserveAspectRatio="xMidYMid meet">
7<metadata>
8Created by potrace 1.15, written by Peter Selinger 2001-2017
9</metadata>
10<g transform="translate(0.000000,93.000000) scale(0.100000,-0.100000)"
11fill="#0b024c" stroke="none">
12<path d="M165 905 c-52 -18 -109 -82 -132 -148 -14 -39 -18 -82 -18 -172 0
13-104 3 -127 23 -170 43 -94 114 -144 205 -145 59 0 112 21 156 63 l33 32 -7
14-75 c-10 -96 -17 -116 -57 -140 -43 -26 -130 -26 -233 0 -43 11 -80 20 -82 20
15-2 0 -3 -30 -1 -67 l3 -68 50 -13 c28 -8 100 -14 160 -15 121 -1 183 14 244
1660 36 28 81 112 81 155 0 54 6 58 90 58 l78 0 4 193 c3 180 4 194 25 223 20
1728 99 72 110 61 2 -3 -1 -24 -6 -48 -6 -24 -11 -79 -11 -121 0 -137 53 -233
18158 -285 50 -24 69 -28 147 -28 107 0 158 20 219 83 l40 41 23 -34 c13 -20 46
19-45 80 -62 51 -25 69 -28 153 -28 68 0 108 5 143 18 l47 19 0 74 0 74 -42 -21
20c-58 -30 -154 -37 -197 -15 -85 44 -98 250 -21 328 24 24 36 28 84 28 99 0 92
219 200 -260 l97 -242 -23 -46 c-32 -65 -67 -87 -134 -87 l-54 0 0 -66 0 -67 45
22-6 c108 -17 215 34 269 128 20 33 296 754 296 771 0 3 -40 5 -89 5 l-90 0 -64
23-197 c-35 -109 -69 -214 -75 -233 -10 -34 -10 -34 -11 -7 -1 16 -31 120 -68
24232 l-66 202 -143 8 c-116 5 -154 4 -197 -9 -61 -18 -126 -61 -145 -98 l-14
25-25 -52 53 c-40 39 -67 56 -106 68 -87 26 -181 19 -272 -19 -14 -5 -18 -2 -18
2614 0 19 -6 21 -52 21 -64 0 -129 -30 -164 -76 -15 -19 -30 -34 -34 -34 -4 0
27-13 23 -20 50 l-12 50 -133 0 -133 0 -7 -40 -7 -40 -36 31 c-67 59 -150 75
28-237 44z m206 -141 c45 -23 62 -72 62 -180 1 -112 -18 -152 -79 -172 -125 -42
29-201 80 -163 260 21 96 97 135 180 92z m888 -10 c40 -33 54 -89 49 -184 -7
30-118 -41 -160 -131 -160 -87 0 -121 53 -121 185 0 135 34 185 125 185 36 0 55
31-6 78 -26z"/>
32</g>
33</svg> \ No newline at end of file
diff --git a/data/homer/svg/hedgedoc.svg b/data/homer/svg/hedgedoc.svg
new file mode 100644
index 0000000..e561ed3
--- /dev/null
+++ b/data/homer/svg/hedgedoc.svg
@@ -0,0 +1,20 @@
1<?xml version="1.0" encoding="UTF-8" standalone="no"?>
2<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
3<svg width="100%" height="100%" viewBox="0 0 1772 1772" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
4 <g>
5 <path d="M1553.66,961.083L1628.91,885.825L1553.66,810.579L1610.27,720.483L1520.17,663.867L1555.32,563.421L1454.88,528.279L1466.79,422.533L1361.05,410.621L1349.13,304.871L1243.39,316.783L1208.24,216.338L1107.81,251.483L1051.18,161.383L961.073,218L885.831,142.754L810.589,218.004L720.485,161.392L663.873,251.488L563.431,216.338L528.289,316.779L422.539,304.863L410.627,410.604L304.885,422.517L316.802,528.258L216.348,563.408L251.493,663.846L161.393,720.458L218.014,810.575L142.756,885.825L218.006,961.083L161.393,1051.19L251.493,1107.8L216.348,1208.24L316.798,1243.39L304.885,1349.12L410.627,1361.04L422.539,1466.78L528.289,1454.87L563.431,1555.3L663.868,1520.17L720.485,1610.27L810.581,1553.65L885.831,1628.2L961.081,1553.67L1051.18,1610.28L1107.79,1520.17L1208.24,1555.32L1243.38,1454.88L1349.13,1466.79L1361.04,1361.04L1466.79,1349.13L1454.87,1243.39L1555.32,1208.24L1520.17,1107.79L1610.27,1051.18L1553.66,961.083Z" style="fill:rgb(181,31,8);fill-rule:nonzero;"/>
6 <path d="M1401.3,1004.78C1401.3,859.343 1283.4,741.439 1137.96,741.439C1065.72,741.439 1000.28,770.551 952.708,817.664L952.675,817.63L885.579,884.73L830.717,829.868C782.45,774.801 711.646,739.989 632.675,739.989C487.233,739.989 369.333,857.893 369.333,1003.33C369.333,1079.33 401.563,1147.76 453.054,1195.82L885.833,1628.37L1309.05,1204.88C1361.93,1155.11 1401.3,1084.88 1401.3,1004.78" style="fill:rgb(252,202,140);fill-rule:nonzero;"/>
7 <path d="M885.579,884.73L830.717,829.868C782.45,774.801 711.646,739.989 632.675,739.989C487.233,739.989 369.333,857.893 369.333,1003.33C369.333,1079.33 401.563,1147.76 453.054,1195.82L885.833,1628.37" style="fill:url(#_Linear1);fill-rule:nonzero;"/>
8 </g>
9 <path d="M885.833,1628.37L885.579,884.73" style="fill:none;"/>
10 <g>
11 <path d="M961.011,1553.59C941.732,1534.24 915.094,1522.28 885.686,1522.28C856.269,1522.28 829.64,1534.24 810.357,1553.59C810.665,1594.93 844.265,1628.35 885.682,1628.35C927.098,1628.35 960.702,1594.92 961.011,1553.59" style="fill:rgb(1,0,7);fill-rule:nonzero;"/>
12 <path d="M797.707,1098.22C797.707,1130.02 771.94,1155.79 740.136,1155.79C708.349,1155.79 682.578,1130.02 682.578,1098.22C682.578,1066.42 708.349,1040.65 740.136,1040.65C771.94,1040.65 797.707,1066.42 797.707,1098.22" style="fill:rgb(1,0,7);fill-rule:nonzero;"/>
13 <path d="M777.962,1089.59C777.962,1098.41 770.816,1105.53 762.012,1105.53C753.204,1105.53 746.054,1098.41 746.054,1089.59C746.054,1080.78 753.204,1073.63 762.012,1073.63C770.816,1073.63 777.962,1080.78 777.962,1089.59" style="fill:rgb(255,255,250);fill-rule:nonzero;"/>
14 <path d="M1089.65,1098.22C1089.65,1130.02 1063.88,1155.79 1032.08,1155.79C1000.29,1155.79 974.513,1130.02 974.513,1098.22C974.513,1066.42 1000.29,1040.65 1032.08,1040.65C1063.88,1040.65 1089.65,1066.42 1089.65,1098.22" style="fill:rgb(1,0,7);fill-rule:nonzero;"/>
15 <path d="M1069.9,1089.59C1069.9,1098.41 1062.75,1105.53 1053.95,1105.53C1045.14,1105.53 1037.99,1098.41 1037.99,1089.59C1037.99,1080.78 1045.14,1073.63 1053.95,1073.63C1062.75,1073.63 1069.9,1080.78 1069.9,1089.59" style="fill:rgb(255,255,250);fill-rule:nonzero;"/>
16 </g>
17 <defs>
18 <linearGradient id="_Linear1" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(200,-420,420,200,660,1340)"><stop offset="0" style="stop-color:rgb(252,202,140);stop-opacity:1"/><stop offset="1" style="stop-color:rgb(220,160,85);stop-opacity:1"/></linearGradient>
19 </defs>
20</svg>
diff --git a/data/homer/svg/home-assistant.svg b/data/homer/svg/home-assistant.svg
new file mode 100644
index 0000000..1c6a958
--- /dev/null
+++ b/data/homer/svg/home-assistant.svg
@@ -0,0 +1,54 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 500 500" version="1.1">
3 <title>home-assistant-logo-responsive</title>
4 <style>svg * { } .only-on-small { opacity: 0; } .hide-on-big { opacity: 0; } @media all and (max-width: 130px) { .only-on-big { opacity: 0; } #background-color-rect { fill: #40BDF5; } #house_big_fill { fill: #FFFFFF; } } @media all and (max-width: 55px) { .only-on-small { opacity: 100; } .hide-on-small { opacity: 0; } #house_small_tree { transform: scale(1); transform-origin: 50% 50%; } } @media all and (max-width: 34px) { #house_small_tree { fill: #40BDF5; transform: scale(1.25); } #background { opacity: 0 } }</style>
5 <defs>
6 <path id="house-path" d="M97,412.234971 L97,279 L62.2629241,279 L62.2629241,279 C57.1083081,279 52.9296662,274.821358 52.9296662,269.666742 C52.9296662,267.21304 53.8959237,264.858035 55.6193053,263.111436 L242.720321,73.4897957 L242.720321,73.4897957 C247.245816,68.9033341 254.632515,68.8539102 259.218977,73.3794044 C259.249346,73.4093694 259.27955,73.4395008 259.309588,73.4697971 L357,172 L357,153.777715 L357,153.777715 C357,149.482202 360.482202,146 364.777715,146 L397.222285,146 L397.222285,146 C401.517798,146 405,149.482202 405,153.777715 L405,220.699997 L446.541007,263.137996 L446.541007,263.137996 C450.146734,266.821581 450.083617,272.730736 446.400032,276.336463 C444.655784,278.043843 442.312095,279 439.871286,279 L405,279 L405,412.234971 L405,412.234971 C405,416.530484 401.517798,420.012685 397.222285,420.012685 L104.777715,420.012685 L104.777715,420.012685 C100.482202,420.012685 97,416.530484 97,412.234971 Z" />
7 <path id="large-tree-path" d="M303.534047,250.479763 L317.995902,264.941618 L317.995902,219.159157 C312.16994,217.099975 307.995902,211.54378 307.995902,205.012685 C307.995902,196.728414 314.711631,190.012685 322.995902,190.012685 C331.280173,190.012685 337.995902,196.728414 337.995902,205.012685 C337.995902,211.54378 333.821864,217.099975 327.995902,219.159157 L327.995902,264.941618 L342.457757,250.479763 C341.520663,248.521586 340.995902,246.328418 340.995902,244.012685 C340.995902,235.728414 347.711631,229.012685 355.995902,229.012685 C364.280173,229.012685 370.995902,235.728414 370.995902,244.012685 C370.995902,252.296957 364.280173,259.012685 355.995902,259.012685 C353.680169,259.012685 351.487001,258.487925 349.528825,257.550831 L327.995902,279.083753 L327.995902,304.083753 L298.06697,334.012685 L339.84943,334.012685 C341.908612,328.186724 347.464808,324.012685 353.995902,324.012685 C362.280173,324.012685 368.995902,330.728414 368.995902,339.012685 C368.995902,347.296957 362.280173,354.012685 353.995902,354.012685 C347.464808,354.012685 341.908612,349.838647 339.84943,344.012685 L288.06697,344.012685 L255.995902,376.083753 L255.995902,408.941618 L282.924834,382.012685 L313.84943,382.012685 C315.908612,376.186724 321.464808,372.012685 327.995902,372.012685 C336.280173,372.012685 342.995902,378.728414 342.995902,387.012685 C342.995902,395.296957 336.280173,402.012685 327.995902,402.012685 C321.464808,402.012685 315.908612,397.838647 313.84943,392.012685 L287.06697,392.012685 L258.06697,421.012685 L243.924834,421.012685 L215.924834,393.012685 L188.142373,393.012685 C186.083191,398.838647 180.526996,403.012685 173.995902,403.012685 C165.711631,403.012685 158.995902,396.296957 158.995902,388.012685 C158.995902,379.728414 165.711631,373.012685 173.995902,373.012685 C180.526996,373.012685 186.083191,377.186724 188.142373,383.012685 L205.924834,383.012685 L172.462979,349.550831 C170.504803,350.487925 168.311634,351.012685 165.995902,351.012685 C157.711631,351.012685 150.995902,344.296957 150.995902,336.012685 C150.995902,327.728414 157.711631,321.012685 165.995902,321.012685 C174.280173,321.012685 180.995902,327.728414 180.995902,336.012685 C180.995902,338.328418 180.471141,340.521586 179.534047,342.479763 L212.995902,375.941618 L212.995902,357.159157 C207.16994,355.099975 202.995902,349.54378 202.995902,343.012685 C202.995902,334.728414 209.711631,328.012685 217.995902,328.012685 C226.280173,328.012685 232.995902,334.728414 232.995902,343.012685 C232.995902,349.54378 228.821864,355.099975 222.995902,357.159157 L222.995902,385.941618 L245.995902,408.941618 L245.995902,334.083753 L202.924834,291.012685 L175.142373,291.012685 C173.083191,296.838647 167.526996,301.012685 160.995902,301.012685 C152.711631,301.012685 145.995902,294.296957 145.995902,286.012685 C145.995902,277.728414 152.711631,271.012685 160.995902,271.012685 C167.526996,271.012685 173.083191,275.186724 175.142373,281.012685 L192.924834,281.012685 L161.995902,250.083753 L161.995902,218.159157 C156.16994,216.099975 151.995902,210.54378 151.995902,204.012685 C151.995902,195.728414 158.711631,189.012685 166.995902,189.012685 C175.280173,189.012685 181.995902,195.728414 181.995902,204.012685 C181.995902,210.54378 177.821864,216.099975 171.995902,218.159157 L171.995902,245.941618 L199.995902,273.941618 L199.995902,255.159157 C194.16994,253.099975 189.995902,247.54378 189.995902,241.012685 C189.995902,232.728414 196.711631,226.012685 204.995902,226.012685 C213.280173,226.012685 219.995902,232.728414 219.995902,241.012685 C219.995902,247.54378 215.821864,253.099975 209.995902,255.159157 L209.995902,283.941618 L245.995902,319.941618 L245.995902,215.083753 L225.462979,194.550831 C223.504803,195.487925 221.311634,196.012685 218.995902,196.012685 C210.711631,196.012685 203.995902,189.296957 203.995902,181.012685 C203.995902,172.728414 210.711631,166.012685 218.995902,166.012685 C227.280173,166.012685 233.995902,172.728414 233.995902,181.012685 C233.995902,183.328418 233.471141,185.521586 232.534047,187.479763 L250.995902,205.941618 L269.457757,187.479763 C268.520663,185.521586 267.995902,183.328418 267.995902,181.012685 C267.995902,172.728414 274.711631,166.012685 282.995902,166.012685 C291.280173,166.012685 297.995902,172.728414 297.995902,181.012685 C297.995902,189.296957 291.280173,196.012685 282.995902,196.012685 C280.680169,196.012685 278.487001,195.487925 276.528825,194.550831 L255.995902,215.083753 L255.995902,361.941618 L280.995902,336.941618 L280.995902,307.159157 C275.16994,305.099975 270.995902,299.54378 270.995902,293.012685 C270.995902,284.728414 277.711631,278.012685 285.995902,278.012685 C294.280173,278.012685 300.995902,284.728414 300.995902,293.012685 C300.995902,299.54378 296.821864,305.099975 290.995902,307.159157 L290.995902,326.941618 L317.995902,299.941618 L317.995902,279.083753 L296.462979,257.550831 C294.504803,258.487925 292.311634,259.012685 289.995902,259.012685 C281.711631,259.012685 274.995902,252.296957 274.995902,244.012685 C274.995902,235.728414 281.711631,229.012685 289.995902,229.012685 C298.280173,229.012685 304.995902,235.728414 304.995902,244.012685 C304.995902,246.328418 304.471141,248.521586 303.534047,250.479763 Z M173.995902,394.012685 C177.30961,394.012685 179.995902,391.326394 179.995902,388.012685 C179.995902,384.698977 177.30961,382.012685 173.995902,382.012685 C170.682193,382.012685 167.995902,384.698977 167.995902,388.012685 C167.995902,391.326394 170.682193,394.012685 173.995902,394.012685 Z M217.995902,349.012685 C221.30961,349.012685 223.995902,346.326394 223.995902,343.012685 C223.995902,339.698977 221.30961,337.012685 217.995902,337.012685 C214.682193,337.012685 211.995902,339.698977 211.995902,343.012685 C211.995902,346.326394 214.682193,349.012685 217.995902,349.012685 Z M165.995902,342.012685 C169.30961,342.012685 171.995902,339.326394 171.995902,336.012685 C171.995902,332.698977 169.30961,330.012685 165.995902,330.012685 C162.682193,330.012685 159.995902,332.698977 159.995902,336.012685 C159.995902,339.326394 162.682193,342.012685 165.995902,342.012685 Z M160.995902,292.012685 C164.30961,292.012685 166.995902,289.326394 166.995902,286.012685 C166.995902,282.698977 164.30961,280.012685 160.995902,280.012685 C157.682193,280.012685 154.995902,282.698977 154.995902,286.012685 C154.995902,289.326394 157.682193,292.012685 160.995902,292.012685 Z M285.995902,299.012685 C289.30961,299.012685 291.995902,296.326394 291.995902,293.012685 C291.995902,289.698977 289.30961,287.012685 285.995902,287.012685 C282.682193,287.012685 279.995902,289.698977 279.995902,293.012685 C279.995902,296.326394 282.682193,299.012685 285.995902,299.012685 Z M353.995902,345.012685 C357.30961,345.012685 359.995902,342.326394 359.995902,339.012685 C359.995902,335.698977 357.30961,333.012685 353.995902,333.012685 C350.682193,333.012685 347.995902,335.698977 347.995902,339.012685 C347.995902,342.326394 350.682193,345.012685 353.995902,345.012685 Z M327.995902,393.012685 C331.30961,393.012685 333.995902,390.326394 333.995902,387.012685 C333.995902,383.698977 331.30961,381.012685 327.995902,381.012685 C324.682193,381.012685 321.995902,383.698977 321.995902,387.012685 C321.995902,390.326394 324.682193,393.012685 327.995902,393.012685 Z M355.995902,250.012685 C359.30961,250.012685 361.995902,247.326394 361.995902,244.012685 C361.995902,240.698977 359.30961,238.012685 355.995902,238.012685 C352.682193,238.012685 349.995902,240.698977 349.995902,244.012685 C349.995902,247.326394 352.682193,250.012685 355.995902,250.012685 Z M322.995902,211.012685 C326.30961,211.012685 328.995902,208.326394 328.995902,205.012685 C328.995902,201.698977 326.30961,199.012685 322.995902,199.012685 C319.682193,199.012685 316.995902,201.698977 316.995902,205.012685 C316.995902,208.326394 319.682193,211.012685 322.995902,211.012685 Z M282.995902,187.012685 C286.30961,187.012685 288.995902,184.326394 288.995902,181.012685 C288.995902,177.698977 286.30961,175.012685 282.995902,175.012685 C279.682193,175.012685 276.995902,177.698977 276.995902,181.012685 C276.995902,184.326394 279.682193,187.012685 282.995902,187.012685 Z M218.995902,187.012685 C222.30961,187.012685 224.995902,184.326394 224.995902,181.012685 C224.995902,177.698977 222.30961,175.012685 218.995902,175.012685 C215.682193,175.012685 212.995902,177.698977 212.995902,181.012685 C212.995902,184.326394 215.682193,187.012685 218.995902,187.012685 Z M166.995902,210.012685 C170.30961,210.012685 172.995902,207.326394 172.995902,204.012685 C172.995902,200.698977 170.30961,198.012685 166.995902,198.012685 C163.682193,198.012685 160.995902,200.698977 160.995902,204.012685 C160.995902,207.326394 163.682193,210.012685 166.995902,210.012685 Z M204.995902,247.012685 C208.30961,247.012685 210.995902,244.326394 210.995902,241.012685 C210.995902,237.698977 208.30961,235.012685 204.995902,235.012685 C201.682193,235.012685 198.995902,237.698977 198.995902,241.012685 C198.995902,244.326394 201.682193,247.012685 204.995902,247.012685 Z M289.995902,250.012685 C293.30961,250.012685 295.995902,247.326394 295.995902,244.012685 C295.995902,240.698977 293.30961,238.012685 289.995902,238.012685 C286.682193,238.012685 283.995902,240.698977 283.995902,244.012685 C283.995902,247.326394 286.682193,250.012685 289.995902,250.012685 Z" />
8 <path id="small-tree-path" d="M210.834697,288.621493 L240,317.786797 L240,229.092415 C225.340848,223.158541 215,208.786888 215,192 C215,169.90861 232.90861,152 255,152 C277.09139,152 295,169.90861 295,192 C295,208.786888 284.659152,223.158541 270,229.092415 L270,317.786797 L299.165303,288.621493 C297.127365,283.822167 296,278.542738 296,273 C296,250.90861 313.90861,233 336,233 C358.09139,233 376,250.90861 376,273 C376,295.09139 358.09139,313 336,313 C330.457262,313 325.177833,311.872635 320.378507,309.834697 L270,360.213203 L270,421 L240,421 L240,360.213203 L189.621493,309.834697 C184.822167,311.872635 179.542738,313 174,313 C151.90861,313 134,295.09139 134,273 C134,250.90861 151.90861,233 174,233 C196.09139,233 214,250.90861 214,273 C214,278.542738 212.872635,283.822167 210.834697,288.621493 Z M255,208 C263.836556,208 271,200.836556 271,192 C271,183.163444 263.836556,176 255,176 C246.163444,176 239,183.163444 239,192 C239,200.836556 246.163444,208 255,208 Z M336,289 C344.836556,289 352,281.836556 352,273 C352,264.163444 344.836556,257 336,257 C327.163444,257 320,264.163444 320,273 C320,281.836556 327.163444,289 336,289 Z M174,289 C182.836556,289 190,281.836556 190,273 C190,264.163444 182.836556,257 174,257 C165.163444,257 158,264.163444 158,273 C158,281.836556 165.163444,289 174,289 Z" />
9 <filter id="house-shadow-filter" x="-15.8%" y="-13.0%" width="131.6%" height="135.7%" filterUnits="objectBoundingBox">
10 <feOffset dx="0" dy="17" in="SourceAlpha" result="shadowOffsetOuter1" />
11 <feGaussianBlur stdDeviation="18" in="shadowOffsetOuter1" result="shadowBlurOuter1" />
12 <feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.2 0" type="matrix" in="shadowBlurOuter1" />
13 </filter>
14 <filter id="white-gloss-edge" x="-0.5%" y="-0.6%" width="101.0%" height="101.1%" filterUnits="objectBoundingBox">
15 <feGaussianBlur stdDeviation="1.5" in="SourceAlpha" result="shadowBlurInner1" />
16 <feOffset dx="0" dy="-1" in="shadowBlurInner1" result="shadowOffsetInner1" />
17 <feComposite in="shadowOffsetInner1" in2="SourceAlpha" operator="arithmetic" k2="-1" k3="1" result="shadowInnerInner1" />
18 <feColorMatrix values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0" type="matrix" in="shadowInnerInner1" />
19 </filter>
20 <mask id="small-tree-mask">
21 <rect width="100%" height="100%" fill="white" />
22 <use fill="#000000" fill-rule="evenodd" xlink:href="#small-tree-path" />
23 </mask>
24 <mask id="big-tree-mask">
25 <rect width="100%" height="100%" fill="white" />
26 <use fill="#000000" fill-rule="evenodd" xlink:href="#large-tree-path" />
27 </mask>
28 <linearGradient id="bg-gradient" x1="50%" y1="9.13982781%" x2="50%" y2="93.6862245%">
29 <stop stop-color="#6CCCF7" offset="0%" />
30 <stop stop-color="#41BDF5" offset="100%" />
31 </linearGradient>
32 <linearGradient id="white-house-gradient" x1="50%" y1="41.7510364%" x2="50%" y2="100%">
33 <stop stop-color="#FFFFFF" offset="0%" />
34 <stop stop-color="#EDEDED" offset="100%" />
35 </linearGradient>
36 </defs>
37 <g id="home-assistant-logo-responsive">
38 <g id="background">
39 <rect id="background-color-rect" fill="url(#bg-gradient)" x="0" y="0" width="500" height="500" rx="36" />
40 <g id="depth-borders" class="only-on-big">
41 <path d="M500,252.5 L500,46.1499336 C500,30.1025963 498.329139,24.283444 495.191611,18.4167773 C492.054084,12.5501107 487.449889,7.94591634 481.583223,4.80838867 C475.716556,1.670861 469.897404,-1.08333549e-15 453.850066,1.86451255e-15 L46.1499336,-1.86451255e-15 C30.1025963,1.08333549e-15 24.283444,1.670861 18.4167773,4.80838867 C12.5501107,7.94591634 7.94591634,12.5501107 4.80838867,18.4167773 C1.670861,24.283444 -7.22223659e-16,30.1025963 1.24300837e-15,46.1499336 L-1.52441497e-17,252.5 L1.24300837e-15,51.1499336 C-7.22223659e-16,35.1025963 1.670861,29.283444 4.80838867,23.4167773 C7.94591634,17.5501107 12.5501107,12.9459163 18.4167773,9.80838867 C24.283444,6.670861 30.1025963,5 46.1499336,5 L453.850066,5 C469.897404,5 475.716556,6.670861 481.583223,9.80838867 C487.449889,12.9459163 492.054084,17.5501107 495.191611,23.4167773 C498.329139,29.283444 500,35.1025963 500,51.1499336 L500,252.5 Z" id="top-depth-border" fill-opacity="0.25" fill="#FFFFFF" />
42 <path d="M500,247.5 L500,453.850066 C500,469.897404 498.329139,475.716556 495.191611,481.583223 C492.054084,487.449889 487.449889,492.054084 481.583223,495.191611 C475.716556,498.329139 469.897404,500 453.850066,500 L46.1499336,500 C30.1025963,500 24.283444,498.329139 18.4167773,495.191611 C12.5501107,492.054084 7.94591634,487.449889 4.80838867,481.583223 C1.670861,475.716556 7.22223659e-16,469.897404 -1.24300837e-15,453.850066 L1.52441497e-17,247.5 L-1.24300837e-15,448.850066 C7.22223659e-16,464.897404 1.670861,470.716556 4.80838867,476.583223 C7.94591634,482.449889 12.5501107,487.054084 18.4167773,490.191611 C24.283444,493.329139 30.1025963,495 46.1499336,495 L453.850066,495 C469.897404,495 475.716556,493.329139 481.583223,490.191611 C487.449889,487.054084 492.054084,482.449889 495.191611,476.583223 C498.329139,470.716556 500,464.897404 500,448.850066 L500,247.5 Z" id="bottom-depth-border" fill-opacity="0.149937726" fill="#000000" />
43 </g>
44 </g>
45 <g id="house_shadow" class="only-on-big" filter="url(#house-shadow-filter)">
46 <use fill="#FFFFFF" fill-rule="evenodd" xlink:href="#house-path" />
47 </g>
48 <g id="house_big_tree" class="hide-on-small">
49 <use id="house_big_fill" fill="url(#white-house-gradient)" fill-rule="evenodd" xlink:href="#house-path" mask="url(#big-tree-mask)" />
50 <use class="only-on-big" fill="black" fill-opacity="1" filter="url(#white-gloss-edge)" fill-rule="evenodd" xlink:href="#house-path" mask="url(#big-tree-mask)" />
51 </g>
52 <use id="house_small_tree" class="only-on-small" fill="#FFFFFF" fill-rule="evenodd" xlink:href="#house-path" mask="url(#small-tree-mask)" />
53 </g>
54</svg>
diff --git a/data/homer/svg/kavita.svg b/data/homer/svg/kavita.svg
new file mode 100644
index 0000000..f56f8a7
--- /dev/null
+++ b/data/homer/svg/kavita.svg
@@ -0,0 +1,124 @@
1<?xml version="1.0" encoding="utf-8"?>
2<!-- Generator: Adobe Illustrator 18.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
3<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
4<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
5 viewBox="0 0 64 64" enable-background="new 0 0 64 64" xml:space="preserve">
6<g>
7 <g>
8 <g>
9 <path fill="#4AC694" d="M32,0c17.7,0,32,14.3,32,32S49.7,64,32,64S0,49.7,0,32S14.3,0,32,0z"/>
10 </g>
11 </g>
12 <g>
13 <g>
14 <path fill="#424C72" d="M52,17H12c-0.6,0-1,0.4-1,1v30c0,0.6,0.4,1,1,1h14.3c1,0,1.9,0.4,2.4,1.2c0.7,1.1,1.9,1.8,3.3,1.8
15 s2.6-0.7,3.3-1.8c0.5-0.8,1.5-1.2,2.4-1.2H52c0.6,0,1-0.4,1-1V18C53,17.4,52.6,17,52,17z"/>
16 </g>
17 </g>
18 <g>
19 <g>
20 <path fill="#E4E7EF" d="M14,28v18h16c1.1,0,2,0.9,2,2c0-1.1,0.9-2,2-2h16V28H14z"/>
21 </g>
22 </g>
23 <g>
24 <g>
25 <path fill="#FFFFFF" d="M35,13c-1.7,0-3,1.3-3,3c0-1.7-1.3-3-3-3H14v31h16c1.1,0,2,0.9,2,2c0-1.1,0.9-2,2-2h16V13H35z"/>
26 </g>
27 </g>
28 <g>
29 <g>
30 <rect x="18" y="16" fill="#57D1F7" width="4" height="7"/>
31 </g>
32 </g>
33 <g>
34 <g>
35 <path fill="#E4E7EF" d="M29,26.5H18c-0.3,0-0.5-0.2-0.5-0.5s0.2-0.5,0.5-0.5h11c0.3,0,0.5,0.2,0.5,0.5S29.3,26.5,29,26.5z"/>
36 </g>
37 </g>
38 <g>
39 <g>
40 <path fill="#E4E7EF" d="M29,23.5h-4.4c-0.3,0-0.5-0.2-0.5-0.5s0.2-0.5,0.5-0.5H29c0.3,0,0.5,0.2,0.5,0.5S29.3,23.5,29,23.5z"/>
41 </g>
42 </g>
43 <g>
44 <g>
45 <path fill="#E4E7EF" d="M29,20.5h-4.4c-0.3,0-0.5-0.2-0.5-0.5s0.2-0.5,0.5-0.5H29c0.3,0,0.5,0.2,0.5,0.5S29.3,20.5,29,20.5z"/>
46 </g>
47 </g>
48 <g>
49 <g>
50 <path fill="#E4E7EF" d="M29,17.5h-4.4c-0.3,0-0.5-0.2-0.5-0.5s0.2-0.5,0.5-0.5H29c0.3,0,0.5,0.2,0.5,0.5S29.3,17.5,29,17.5z"/>
51 </g>
52 </g>
53 <g>
54 <g>
55 <path fill="#E4E7EF" d="M29,29.5H18c-0.3,0-0.5-0.2-0.5-0.5s0.2-0.5,0.5-0.5h11c0.3,0,0.5,0.2,0.5,0.5S29.3,29.5,29,29.5z"/>
56 </g>
57 </g>
58 <g>
59 <g>
60 <path fill="#E4E7EF" d="M29,32.5H18c-0.3,0-0.5-0.2-0.5-0.5s0.2-0.5,0.5-0.5h11c0.3,0,0.5,0.2,0.5,0.5S29.3,32.5,29,32.5z"/>
61 </g>
62 </g>
63 <g>
64 <g>
65 <path fill="#E4E7EF" d="M29,35.5H18c-0.3,0-0.5-0.2-0.5-0.5s0.2-0.5,0.5-0.5h11c0.3,0,0.5,0.2,0.5,0.5S29.3,35.5,29,35.5z"/>
66 </g>
67 </g>
68 <g>
69 <g>
70 <path fill="#E4E7EF" d="M29,38.5H18c-0.3,0-0.5-0.2-0.5-0.5s0.2-0.5,0.5-0.5h11c0.3,0,0.5,0.2,0.5,0.5S29.3,38.5,29,38.5z"/>
71 </g>
72 </g>
73 <g>
74 <g>
75 <path fill="#E4E7EF" d="M29,41.5H18c-0.3,0-0.5-0.2-0.5-0.5s0.2-0.5,0.5-0.5h11c0.3,0,0.5,0.2,0.5,0.5S29.3,41.5,29,41.5z"/>
76 </g>
77 </g>
78 <g>
79 <g>
80 <path fill="#E4E7EF" d="M46,26.5H35c-0.3,0-0.5-0.2-0.5-0.5s0.2-0.5,0.5-0.5h11c0.3,0,0.5,0.2,0.5,0.5S46.3,26.5,46,26.5z"/>
81 </g>
82 </g>
83 <g>
84 <g>
85 <path fill="#E4E7EF" d="M46,29.5H35c-0.3,0-0.5-0.2-0.5-0.5s0.2-0.5,0.5-0.5h11c0.3,0,0.5,0.2,0.5,0.5S46.3,29.5,46,29.5z"/>
86 </g>
87 </g>
88 <g>
89 <g>
90 <path fill="#E4E7EF" d="M46,20.5H35c-0.3,0-0.5-0.2-0.5-0.5s0.2-0.5,0.5-0.5h11c0.3,0,0.5,0.2,0.5,0.5S46.3,20.5,46,20.5z"/>
91 </g>
92 </g>
93 <g>
94 <g>
95 <path fill="#E4E7EF" d="M46,17.5H35c-0.3,0-0.5-0.2-0.5-0.5s0.2-0.5,0.5-0.5h11c0.3,0,0.5,0.2,0.5,0.5S46.3,17.5,46,17.5z"/>
96 </g>
97 </g>
98 <g>
99 <g>
100 <path fill="#E4E7EF" d="M46,23.5H35c-0.3,0-0.5-0.2-0.5-0.5s0.2-0.5,0.5-0.5h11c0.3,0,0.5,0.2,0.5,0.5S46.3,23.5,46,23.5z"/>
101 </g>
102 </g>
103 <g>
104 <g>
105 <path fill="#E4E7EF" d="M46,32.5H35c-0.3,0-0.5-0.2-0.5-0.5s0.2-0.5,0.5-0.5h11c0.3,0,0.5,0.2,0.5,0.5S46.3,32.5,46,32.5z"/>
106 </g>
107 </g>
108 <g>
109 <g>
110 <path fill="#E4E7EF" d="M46,35.5H35c-0.3,0-0.5-0.2-0.5-0.5s0.2-0.5,0.5-0.5h11c0.3,0,0.5,0.2,0.5,0.5S46.3,35.5,46,35.5z"/>
111 </g>
112 </g>
113 <g>
114 <g>
115 <path fill="#E4E7EF" d="M46,38.5H35c-0.3,0-0.5-0.2-0.5-0.5s0.2-0.5,0.5-0.5h11c0.3,0,0.5,0.2,0.5,0.5S46.3,38.5,46,38.5z"/>
116 </g>
117 </g>
118 <g>
119 <g>
120 <path fill="#E4E7EF" d="M46,41.5H35c-0.3,0-0.5-0.2-0.5-0.5s0.2-0.5,0.5-0.5h11c0.3,0,0.5,0.2,0.5,0.5S46.3,41.5,46,41.5z"/>
121 </g>
122 </g>
123</g>
124</svg>
diff --git a/data/homer/svg/mailcow.svg b/data/homer/svg/mailcow.svg
new file mode 100644
index 0000000..63a3492
--- /dev/null
+++ b/data/homer/svg/mailcow.svg
@@ -0,0 +1,3 @@
1<?xml version="1.0" encoding="UTF-8" standalone="no"?>
2<!-- Generator: Adobe Illustrator 17.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
3<svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" version="1.1" id="Layer_1" x="0px" y="0px" width="271.27399" height="298.871" viewBox="0 0 271.27398 298.871" enable-background="new 0 0 1600 1200" xml:space="preserve" inkscape:version="0.91 r13725" sodipodi:docname="logo.svg"><metadata id="metadata144"><rdf:RDF><cc:Work rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/><dc:title/></cc:Work></rdf:RDF></metadata><defs id="defs142"/><sodipodi:namedview pagecolor="#ffffff" bordercolor="#666666" borderopacity="1" objecttolerance="10" gridtolerance="10" guidetolerance="10" inkscape:pageopacity="0" inkscape:pageshadow="2" inkscape:window-width="1203" inkscape:window-height="1168" id="namedview140" showgrid="false" inkscape:zoom="1.1125147" inkscape:cx="151.70799" inkscape:cy="136.82484" inkscape:window-x="561" inkscape:window-y="0" inkscape:window-maximized="0" inkscape:current-layer="g5" fit-margin-top="0" fit-margin-left="0" fit-margin-right="0" fit-margin-bottom="0"/><g id="g3" transform="translate(-648.292,-401.988)"><g id="g5"><g id="email" transform="translate(0,-58)"><path style="fill:#5a3620" inkscape:connector-curvature="0" id="path10" d="m 890.306,557.81 29.26,11.373 0,172.027 c 0,9.753 -7.895,17.649 -17.638,17.649 l -235.998,0 c -9.743,0 -17.638,-7.896 -17.638,-17.649 l 0,-172.026 29.259,-8.937"/><path style="fill:#fee70f;fill-opacity:0.89499996" inkscape:connector-curvature="0" id="path12" d="M 758.871,656.221 649.49,747.45 c 2.507,6.648 8.901,11.409 16.44,11.409 l 235.998,0 c 7.536,0 13.933,-4.761 16.444,-11.409 l -107.402,-91.229 -52.099,0 z"/><g id="g14"><path style="fill:#f9e82d;fill-opacity:1" inkscape:connector-curvature="0" id="path16" d="m 810.391,656.686 107.981,90.764 c -0.331,0.881 -0.744,1.726 -1.205,2.536 l 0.028,0.035 c 1.501,-2.596 2.371,-5.594 2.371,-8.81 l 0,-172.004 -109.175,87.479 z"/><path style="fill:#f9e82d;fill-opacity:1" inkscape:connector-curvature="0" id="path18" d="m 649.49,747.45 108.864,-90.764 -110.061,-87.479 0,172.003 c 0,3.216 0.876,6.214 2.367,8.81 l 0.039,-0.035 c -0.466,-0.809 -0.877,-1.654 -1.209,-2.535 z"/></g></g><path style="opacity:0.1;fill:#3d5263" inkscape:connector-curvature="0" id="path26" d="m 783.931,446.247 c -66.396,0 -120.223,53.827 -120.223,120.223 0,66.396 53.827,120.221 120.223,120.221 66.397,0 120.222,-53.825 120.222,-120.221 0,-66.395 -53.825,-120.223 -120.222,-120.223 z m -11.96,215.702 c -53.009,0 -95.982,-43.855 -95.982,-97.953 0,-54.098 42.973,-97.952 95.982,-97.952 53.007,0 95.98,43.855 95.98,97.952 -10e-4,54.098 -42.973,97.953 -95.98,97.953 z"/><g id="g28"><g id="g30"><polyline style="fill:#3d5263" id="polyline32" points="691.144,492.5 673.257,540.276 686.55,605.582 712.496,631.852 "/><g id="g34"><g id="g36"><polyline style="fill:#fef3df" id="polyline38" points="658.248,450.81 673.501,487.076 693.836,496.903 724.04,458.731 "/><g id="g40"><path style="fill:#b58765" inkscape:connector-curvature="0" id="path42" d="m 710.634,473.205 c 0,0 -22.482,-25.556 -49.793,-18.975 0,0 4.667,34.118 46.349,44.019 l 2.61,8.533 c 0,0 -65.612,-9.689 -59.339,-67.593 0,0 49.008,-19.884 72.598,15.106"/><polyline style="fill:#fef3df" id="polyline44" points="909.697,450.81 894.447,487.076 874.114,496.903 843.907,458.731 "/><path style="fill:#b58765" inkscape:connector-curvature="0" id="path46" d="m 857.314,473.205 c 0,0 22.48,-25.556 49.79,-18.975 0,0 -4.664,34.118 -46.347,44.019 l -2.613,8.533 c 0,0 65.611,-9.689 59.339,-67.593 0,0 -49.006,-19.884 -72.6,15.106"/></g></g><path sodipodi:nodetypes="cccscccccc" style="fill:#b58765" inkscape:connector-curvature="0" id="path48" d="m 726.619,647.067 55.945,0 16.40428,-204.81407 c -55.814,0 -112.41728,30.01707 -112.41728,77.85207 0,1.454 0.085,2.787 0.121,4.175 0.127,3.934 0.448,7.585 0.856,11.135 1.689,14.816 5.451,27.177 8.461,43.383 1.452,7.831 5.002,23.374 5.002,23.374 0.056,0.408 0.165,0.804 0.224,1.211 2.535,16.546 11.832,32.027 25.404,43.684 z"/><path style="fill:#b58765" inkscape:connector-curvature="0" id="path50" d="m 781.992,433.489 0,213.577 55.944,0 c 13.572,-11.657 22.867,-27.138 25.406,-43.684 0.058,-0.407 0.163,-0.803 0.221,-1.211 0,0 3.549,-15.543 5.002,-23.374 3.011,-16.206 6.774,-28.567 8.464,-43.381 0.405,-3.552 0.724,-7.203 0.846,-11.137 0.042,-1.388 0.126,-2.721 0.126,-4.175 0,-47.834 -40.191,-86.615 -96.009,-86.615 z"/><g id="g52"><g id="g54"><path style="fill:#fef3df" inkscape:connector-curvature="0" id="path56" d="m 860.944,613.502 c 0,28.321 -35.091,51.289 -78.383,51.289 -43.299,0 -78.388,-22.968 -78.388,-51.289 0,-28.325 35.089,-51.289 78.388,-51.289 43.292,0 78.383,22.964 78.383,51.289 z"/></g></g><g id="g58"><g id="g60"><g id="g62"><path style="fill:#5a3620" inkscape:connector-curvature="0" id="path64" d="m 747.044,605.582 c 0,6.215 -5.04,11.256 -11.261,11.256 -6.21,0 -11.253,-5.041 -11.253,-11.256 0,-6.223 5.043,-11.257 11.253,-11.257 6.22,0 11.261,5.034 11.261,11.257 z"/></g></g><g id="g66"><g id="g68"><path style="fill:#5a3620" inkscape:connector-curvature="0" id="path70" d="m 840.856,605.582 c 0,6.215 -5.037,11.256 -11.257,11.256 -6.218,0 -11.259,-5.041 -11.259,-11.256 0,-6.223 5.041,-11.257 11.259,-11.257 6.22,0 11.257,5.034 11.257,11.257 z"/></g></g></g><g id="g72"><path style="fill:#87654a" inkscape:connector-curvature="0" id="path74" d="m 875.228,525.835 c 0.354,-3.113 0.634,-6.311 0.743,-9.754 0.035,-1.218 0.109,-2.384 0.109,-3.661 0,-40.785 -33.369,-74.043 -80.237,-75.775 l -7.335,0.005 c -0.003,0 -0.003,0 -0.006,0 -0.007,0.018 -28.632,88.422 76.583,140.268 0.946,-4.317 2.078,-9.585 2.73,-13.088 2.64,-14.196 5.934,-25.021 7.413,-37.995 z"/></g><g id="g76"><g id="g78"><g id="g80"><g id="g82"><path style="fill:#5a3620" inkscape:connector-curvature="0" id="path84" d="m 843.907,519.681 c 0,6.964 -5.65,12.611 -12.618,12.611 -6.963,0 -12.614,-5.646 -12.614,-12.611 0,-6.97 5.651,-12.614 12.614,-12.614 6.968,0 12.618,5.644 12.618,12.614 z"/></g></g></g><g id="g86"><g id="g88"><g id="g90"><path style="fill:#5a3620" inkscape:connector-curvature="0" id="path92" d="m 752.028,519.681 c 0,6.964 -5.649,12.611 -12.612,12.611 -6.969,0 -12.612,-5.646 -12.612,-12.611 0,-6.97 5.642,-12.614 12.612,-12.614 6.964,0 12.612,5.644 12.612,12.614 z"/></g></g></g><g id="g94"><g id="g96"><path style="fill:#ffffff" inkscape:connector-curvature="0" id="path98" d="m 748.75,515.894 c 0,2.558 -2.071,4.629 -4.63,4.629 -2.558,0 -4.633,-2.072 -4.633,-4.629 0,-2.552 2.076,-4.626 4.633,-4.626 2.559,0 4.63,2.073 4.63,4.626 z"/></g></g><g id="g100"><g id="g102"><path style="fill:#ffffff" inkscape:connector-curvature="0" id="path104" d="m 839.771,515.894 c 0,2.558 -2.073,4.629 -4.629,4.629 -2.558,0 -4.631,-2.072 -4.631,-4.629 0,-2.552 2.072,-4.626 4.631,-4.626 2.555,0 4.629,2.073 4.629,4.626 z"/></g></g></g></g><path style="fill:#fef3df" inkscape:connector-curvature="0" id="path106" d="m 734.557,443.625 c 0,0 -18.236,-25.199 0,-41.637 0,0 13.125,32.012 40.242,31.502"/><path style="fill:#fef3df" inkscape:connector-curvature="0" id="path108" d="m 834.496,443.625 c 0,0 18.236,-25.199 0,-41.637 0,0 -13.126,32.012 -40.242,31.502"/><path style="fill:#f1f2f2" inkscape:connector-curvature="0" id="path110" d="m 786.264,431.965 c -66.396,0 -120.223,53.827 -120.223,120.223 0,66.396 53.827,120.221 120.223,120.221 66.397,0 120.222,-53.825 120.222,-120.221 10e-4,-66.395 -53.825,-120.223 -120.222,-120.223 z m -11.96,215.702 c -53.009,0 -95.982,-43.855 -95.982,-97.953 0,-54.098 42.973,-97.952 95.982,-97.952 53.007,0 95.979,43.855 95.979,97.952 0,54.098 -42.972,97.953 -95.979,97.953 z"/></g><g id="g112"><path style="fill:#ffffff" inkscape:connector-curvature="0" id="path114" d="m 781.737,436.751 c 66.396,0 120.221,53.827 120.221,120.223 0,30.718 -11.526,58.74 -30.482,79.991 21.636,-21.74 35.01,-51.708 35.01,-84.803 0,-66.395 -53.825,-120.222 -120.222,-120.222 -35.678,0 -67.721,15.549 -89.739,40.233 21.772,-21.879 51.91,-35.422 85.212,-35.422 z"/></g></g><path d="m 648.292,644.7595 0,46.15088 c 0,5.49435 7.88,9.94862 17.6,9.94862 l 236.073,0 c 9.72,0 17.6,-4.45427 17.6,-9.94862 l 0,-14.07618 c 10e-4,0 -175.814,20.0804 -271.273,-32.0747 z" id="path124" inkscape:connector-curvature="0" style="opacity:0.1;fill:#3d5263"/></g><g id="g126"/></g></svg> \ No newline at end of file
diff --git a/data/homer/svg/mealie.svg b/data/homer/svg/mealie.svg
new file mode 100644
index 0000000..6992129
--- /dev/null
+++ b/data/homer/svg/mealie.svg
@@ -0,0 +1,1148 @@
1<?xml version="1.0" encoding="UTF-8" standalone="no"?>
2<!-- Created with Inkscape (http://www.inkscape.org/) -->
3
4<svg
5 version="1.1"
6 id="svg2"
7 width="20.160788"
8 height="19.136997"
9 viewBox="0 0 20.160788 19.136997"
10 xmlns:xlink="http://www.w3.org/1999/xlink"
11 xmlns="http://www.w3.org/2000/svg"
12 xmlns:svg="http://www.w3.org/2000/svg">
13 <defs
14 id="defs6" />
15 <g
16 id="g8">
17 <image
18 width="20.160788"
19 height="19.136997"
20 preserveAspectRatio="none"
21 xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAgAAAAHmCAYAAAAFjTb4AAAgKXpUWHRSYXcgcHJvZmlsZSB0eXBl
22IGV4aWYAAHjarZtndiQ5doX/YxVaArxZDuw52oGWr+8iMmmK7J6ekbq6SFYyMswz1zwgzf6f/z7m
23v/ivulRNTKXmlrPlv9hi850fqn3+6/ers/F+ff/nX69+e93s/PrR8z3wPTy/qPn57t6vv0/0+u46
24P6UvJ6rz9Yvx/Rctvi5f/zjR60JBd6R7W68TtdeJgn9+4V4n6M9j2dxq+foIYz/f1/tB6/PX6Mt8
25X+d18J//joXorcR1gvc7uGD56kN8biDorzOh80PmKy/owBD4OYV2X/evkxGQ3+Jkv9yV+SUr7res
26nPCRs29JCa9kGV74Hsz88f3X1136PfjmhvjLlcP8KIdvr7fp9p+P8/57zqrmnP08XY+ZkObXQ70f
278f7EgYOQh/u2zJ/C38TP5f5p/KmG6p2kfJGqwZ/pmvOk5bjoluvuuH2/Tze5xei3L3z3fpIovVZD
288c3PYA3JjPrjji/kaoVK5ibpDbzqP+7F3eu2e7npKhdejiO942SOd3ijL/8ff/7yROeo5J27wQw3
29Vnz1qixuQ5nTV44iIe686yjdAL///Pmf8hrIYLphrjxgt+M5xUjuVVuqo3ATHTgw8f3pNVfW6wSE
30iGsnbsYFMmCzC8llZ4v3xTniWMlP50SVpvGDFLiU/OIufQwhk5zqdW3eU9w91if/vAxmkYhEYxVS
31QzORqwiwUT8lVmqop5BiSimnkmpqqeeQY04555IFfr2EEksquZRSSyu9hhprqrmWWk1ttTffAuCY
32Wm6l1dZa71y0c+bOuzsH9D78CCOONPIoo442+qR8Zpxp5llmNbPNvvwKC5xYeZVVV1t9u00p7bjT
33zrvsutvuh1I74cSTTj7l1NNO/8iaM09af/z551lz76z5mykdWD6yxltLeZ/CCU6SckbGfHRkvCgD
34FLRXzmx1MXqj1ClntgnTkucuk5KznDJGBuN2Ph33kbvPzH3Lm4nx/5Q3/86cUer+PzJnlLq/yNzP
35vP2StdUvWwRzM6Q2VFBtoP04YPdU/epl9BbmGHBxbO24tobrLYdD3vKkYfjtbIS6pm1iCSOlWEpI
36e5Xt7LF9hm65UuOON+eK1s8EeKayuUYZq/gcAtGfIeZ9Uj7E2CQyaYefMfY1gLTEI63MXxArzenA
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
165mC5SNh4S1uxOO6uSZiDbrT60FRGjS4dRdAkIXdndDcD7kVNtGix9vANkrlpms1yvPY+2tfn4fYta
166IWzmfwHzHzOc0Cj3FAAAAYVpQ0NQSUNDIHByb2ZpbGUAAHicfZE9SMNAHMVf02pFKg5WEXHIUJ0s
167iIqIk1ahCBVCrdCqg8mlH0KThiTFxVFwLTj4sVh1cHHW1cFVEAQ/QNzcnBRdpMT/pYUWMR4c9+Pd
168vcfdO0CoFplmBUYBTbfNZDwmpjMrYvAV7ehFANPok5llzEpSAp7j6x4+vt5FeZb3uT9Hl5q1GOAT
169iWeYYdrE68STm7bBeZ84zAqySnxOPGLSBYkfua7U+Y1z3mWBZ4bNVHKOOEws5ltYaWFWMDXiCeKI
170qumUL6TrrHLe4qwVy6xxT/7CUFZfXuI6zUHEsYBFSBChoIwNFGEjSqtOioUk7cc8/AOuXyKXQq4N
171MHLMowQNsusH/4Pf3Vq58bF6UigGtL04zscQENwFahXH+T52nNoJ4H8GrvSmv1QFpj5JrzS1yBHQ
172vQ1cXDc1ZQ+43AH6nwzZlF3JT1PI5YD3M/qmDNBzC3Su1ntr7OP0AUhRV4kb4OAQGM5T9prHuzta
173e/v3TKO/H4ILcq3jpCkNAAANGmlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJl
174Z2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4KPHg6eG1wbWV0YSB4bWxu
175czp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iWE1QIENvcmUgNC40LjAtRXhpdjIiPgogPHJk
176ZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgt
177bnMjIj4KICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgeG1sbnM6eG1wTU09Imh0
178dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iCiAgICB4bWxuczpzdEV2dD0iaHR0cDovL25z
179LmFkb2JlLmNvbS94YXAvMS4wL3NUeXBlL1Jlc291cmNlRXZlbnQjIgogICAgeG1sbnM6ZGM9Imh0
180dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIgogICAgeG1sbnM6R0lNUD0iaHR0cDovL3d3
181dy5naW1wLm9yZy94bXAvIgogICAgeG1sbnM6dGlmZj0iaHR0cDovL25zLmFkb2JlLmNvbS90aWZm
182LzEuMC8iCiAgICB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iCiAgIHht
183cE1NOkRvY3VtZW50SUQ9ImdpbXA6ZG9jaWQ6Z2ltcDpiNzkzYzJiMC0xMzhhLTQxMTYtYTNlYi1h
184ZTg5MjZiNTRlOGIiCiAgIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6NzU3NjQwNjQtNTMyNi00
185MmRlLWIxOTQtM2Y2OTM2MjMwNDk3IgogICB4bXBNTTpPcmlnaW5hbERvY3VtZW50SUQ9InhtcC5k
186aWQ6NzlkMzhhN2UtZDQ4ZC00NzEzLWFkNGEtMjBjNzlmNGNjYTU2IgogICBkYzpGb3JtYXQ9Imlt
187YWdlL3BuZyIKICAgR0lNUDpBUEk9IjIuMCIKICAgR0lNUDpQbGF0Zm9ybT0iTGludXgiCiAgIEdJ
188TVA6VGltZVN0YW1wPSIxNjI0MjUxNTQzNTkyNTIzIgogICBHSU1QOlZlcnNpb249IjIuMTAuMjQi
189CiAgIHRpZmY6T3JpZW50YXRpb249IjEiCiAgIHhtcDpDcmVhdG9yVG9vbD0iR0lNUCAyLjEwIj4K
190ICAgPHhtcE1NOkhpc3Rvcnk+CiAgICA8cmRmOlNlcT4KICAgICA8cmRmOmxpCiAgICAgIHN0RXZ0
191OmFjdGlvbj0ic2F2ZWQiCiAgICAgIHN0RXZ0OmNoYW5nZWQ9Ii8iCiAgICAgIHN0RXZ0Omluc3Rh
192bmNlSUQ9InhtcC5paWQ6MmZhMzg2OTMtNWYzYi00NGZhLWI2NDUtMmY1NmQ2ZjhkMmNiIgogICAg
193ICBzdEV2dDpzb2Z0d2FyZUFnZW50PSJHaW1wIDIuMTAgKExpbnV4KSIKICAgICAgc3RFdnQ6d2hl
194bj0iMjAyMS0wNi0yMFQyMzo1OTowMy0wNTowMCIvPgogICAgPC9yZGY6U2VxPgogICA8L3htcE1N
195Okhpc3Rvcnk+CiAgPC9yZGY6RGVzY3JpcHRpb24+CiA8L3JkZjpSREY+CjwveDp4bXBtZXRhPgog
196ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
197ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAg
198ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
199ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAg
200ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
201ICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
202ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
203ICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
204ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAg
205ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
206ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAg
207ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
208ICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
209ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
210ICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
211ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAg
212ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
213ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAg
214ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
215ICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
216ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
217ICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
218ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
219CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
220ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAg
221ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
222ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAg
223ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
224ICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
225ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
226ICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
227ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAg
228ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
229ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAg
230ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
231ICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgCjw/eHBh
232Y2tldCBlbmQ9InciPz5IKuIOAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAXcAAAF3AAHmAuEv
233AAAAB3RJTUUH5QYVBDsDwGhkwgAAIABJREFUeNrsvVdwXGfa5/f7n9PdABEZwZwQCGZSTCApKk7c
234GZe3fONrV7nKF77Y2nLtznwzEsUszTff513v2r61q1zlvfXVfqMJmjwa5URJFClKYgDAIIoZROo+
235f1+cA6ARNCNKotANvL8qiGCLJLrfc877hPf/PI8IBAKBwKyl+2j7fKBN0iKba3Z0ruTCjTWHTw6H
2361ZnZRGEJAoFAYHZy9uCqCFgIbAL2AlsRqxQVm88f2VgIKzSzUViCQCAQmG1Rf5uMGoHFgp3AY5La
237bS4BbxmdlHQmjqJLS586NRRWbGaSC0sQCAQCs4thx/mcvFKwB7xX4iFgObAGWB7JLUDO9sClExtu
238KBcPL/nxuw4rFxyAQCAQCFQhFw53REYFwULBFokDoF3glUAzGElLgRpwHyR9TvQRg8k1YDCs4Mwi
239aAACgUBg9lAXyW0RfhR4GNgKrAQ1AZEgAtcBrcAjtr9nJw9hL7pyuDNoAoIDEAgEAoFq4/VDmyPE
240QsFOiW8L7wPa0sifGAAp+48XGe8Evgvss2hz5HmXj6wLTsAMIogAA4FAYAZz8WiHDA2CpUoj/ieE
2419wOrkZpGjX8ZxiPf3BB6Dfgz8Krg/TiJLi0+fDoIA0MGIBAIBAKVzEASx4IVgv1Z5L8LWA00TmX8
24208hwNDZsBDYjvgs8Ydhged7VcBwQMgCBQCAQqEwuHO2IDLVCCyK8T+I7pMZ/FWjuFw0AjS3Uj3kD
243+JXQnzAfJeLqUI6h1p+eCdUBVUqoAggEAoGZyZwI1ghvB/aCt6bGn2YgcnkEaBtJwNjLY9/JeI5Q
244K/CocZ3Fi5bejBOuAuE4IDgAgUAgEJhuLh5br9vFOILhBcIPSTyZGn+tBUYj/3Hp39T4j39ZI78o
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
1144P1rJWz9Jcza+AAAAAElFTkSuQmCC
1145"
1146 id="image10" />
1147 </g>
1148</svg>
diff --git a/data/homer/svg/mkb_bank.svg b/data/homer/svg/mkb_bank.svg
new file mode 100644
index 0000000..652028f
--- /dev/null
+++ b/data/homer/svg/mkb_bank.svg
@@ -0,0 +1,11 @@
1<?xml version="1.0" encoding="UTF-8" standalone="no"?>
2<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
3<!-- Created with Inkscape (http://www.inkscape.org/) by Marsupilami -->
4<svg xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" version="1.1" width="614" height="768" viewBox="-2.14875 -2.14875 75.9225 94.891319" id="svg13920">
5 <defs id="defs13922"/>
6 <path d="m 0,5e-5 0,90.59375 71.625,0 0,-90.59375 z" id="path13514" style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"/>
7 <path d="m 0,74.5 0,16.09375 71.625,0 L 71.625,74.5 0,74.5 z" id="path13516" style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"/>
8 <path d="m 0,0 0,72.6875 30.4375,0 c 1.745,0 2.21875,-0.49 2.21875,-3.15625 l 0,-28.125 c 0,-2.1325 -0.1775,-2.6075 -0.90625,-2.84375 -1.16625,-0.35625 -1.8125,-0.26125 -1.8125,-1.03125 0,-0.35625 0.295,-0.65625 0.78125,-0.65625 0.68,0 2.14625,0.25 3.75,0.25 1.26375,0 2.435,-0.25 3.40625,-0.25 0.58375,0 0.78125,0.245 0.78125,0.71875 0,0.83 -0.4075,0.55375 -1.71875,0.96875 -0.73,0.23625 -1.0625,0.80875 -1.0625,1.875 l 0,11.15625 c 0,1.71875 0.0587,1.875 0.15625,1.875 0.0975,0 0.42,-0.51 0.90625,-1.28125 0.9225,-1.4225 4.3725,-6.46 6.21875,-9.71875 0.72875,-1.30375 1.21875,-2.335 1.21875,-2.75 0,-0.77 -0.4025,-1.10625 -1.375,-1.34375 -0.82625,-0.1775 -1.1875,-0.31125 -1.1875,-0.84375 0,-0.35625 0.27375,-0.65625 0.90625,-0.65625 0.82625,0 2.3025,0.25 3.46875,0.25 1.36125,0 2.605,-0.25 3.1875,-0.25 0.7775,0 1.03125,0.24125 1.03125,0.65625 0,0.71125 -0.43375,0.54375 -1.40625,0.78125 -0.875,0.2375 -2.1125,1.4075 -3.8125,3.71875 -1.6525,2.2525 -3.2475,4.57375 -4.65625,7.0625 -0.68125,1.185 -0.71875,1.42 -0.71875,1.65625 0,0.2375 0.09,0.475 0.625,1.71875 0.7775,1.7775 2.3725,5.5725 5.96875,14.34375 0.55375,1.35125 0.99375,2.37 1.34375,3.15625 1.095,2.4525 2.13875,2.71875 3.09375,2.71875 l 20.78125,0 L 71.625,0 0,0 z m 56.21875,41.65625 c 2.58125,0 4.21625,0.56625 5.40625,1.5 1.46,1.14625 2,2.93375 2,4.84375 0,3.1425 -1.85,4.995 -4.125,5.84375 l 0,0.125 c 0.50875,0.1275 1.055,0.2875 1.53125,0.5 2.07125,0.97625 3.65625,3.1075 3.65625,6.84375 0,2.2925 -0.5675,4.30625 -1.6875,5.75 -1.05375,1.27375 -2.55625,2.03125 -4.59375,2.03125 -1.3925,0 -2.76625,-0.1875 -4.125,-0.1875 -1.25625,0 -2.67,0.1875 -3.28125,0.1875 -0.51,0 -0.78125,-0.19 -0.78125,-0.65625 0,-0.4675 0.5925,-0.54875 1,-0.71875 0.645,-0.2125 0.8125,-0.58625 0.8125,-1.5625 l 0,-20.625 c 0,-1.31625 -0.3875,-1.80125 -1.40625,-1.84375 -0.2725,0 -0.4925,0.002 -0.59375,-0.125 -0.1025,-0.0425 -0.125,-0.11125 -0.125,-0.28125 0,-0.425 0.59375,-0.69375 1.375,-0.90625 1.59625,-0.51 3.27375,-0.71875 4.9375,-0.71875 z m -48.1875,0.5 c 0.72875,0 1.615,0.15625 2.34375,0.15625 0.53,0 1.34875,-0.15625 1.8125,-0.15625 0.3975,0 0.49,0.0512 0.65625,0.6875 1.15875,5.18 2.27875,10.41375 3.4375,15.59375 0.265,1.27375 0.3925,2.125 0.65625,2.125 0.2325,0 0.32875,-0.39375 0.59375,-1.625 1.16,-5.17875 2.3075,-10.3625 3.5,-15.5 0.165,-0.93375 0.35625,-1.28125 0.6875,-1.28125 0.46375,0 1.31,0.15625 1.90625,0.15625 0.6625,0 1.6525,-0.15625 2.28125,-0.15625 0.29875,0 0.59375,0.17 0.59375,0.59375 0,0.4675 -0.415,0.65375 -1.375,0.78125 -0.43125,0.0425 -0.65625,0.63375 -0.65625,2.375 0,3.86375 0.46375,14.7175 0.5625,19.8125 0.0337,1.27375 0.3175,1.8725 0.78125,2 0.92625,0.2975 1.28125,0.345 1.28125,0.8125 0,0.46625 -0.2875,0.5625 -0.75,0.5625 -0.56375,0 -1.31875,-0.1875 -2.875,-0.1875 -1.29125,0 -2.34375,0.1875 -2.90625,0.1875 -0.4975,0 -0.78125,-0.18125 -0.78125,-0.5625 l 0,-0.0312 c 0,-0.4675 0.3575,-0.61125 1.21875,-0.78125 0.9275,-0.1275 1,-0.5775 1,-2.53125 0,-4.4575 -0.305,-13.1775 -0.4375,-17.59375 -0.0337,-0.63625 -0.0562,-0.84375 -0.15625,-0.84375 -0.1325,0 -0.18125,0.2225 -0.28125,0.5625 -0.33125,1.14625 -1.07125,4.74875 -3.6875,16 -1.0925,4.67 -0.9625,5.59375 -1.625,5.59375 -0.33125,0 -0.47875,-0.33625 -0.84375,-2.375 -0.92625,-5.01 -3.5925,-16.69125 -4.15625,-19.28125 -0.0663,-0.34 -0.1475,-0.5 -0.3125,-0.5 -0.16625,0 -0.15375,0.3425 -0.21875,0.9375 -0.2325,3.60875 -0.34375,10.41125 -0.34375,14.0625 0,2.845 -0.15,5.71375 0.84375,5.96875 0.59625,0.17 1.53125,0.18625 1.53125,0.78125 0,0.33875 -0.16125,0.59375 -0.625,0.59375 -0.59625,0 -0.9875,-0.1875 -2.3125,-0.1875 -1.1925,0 -1.72875,0.1875 -2.125,0.1875 -0.29875,0 -0.53125,-0.0425 -0.53125,-0.59375 0,-0.595 0.53125,-0.54875 1.09375,-0.71875 0.795,-0.2975 0.9325,-1.275 1.03125,-3.3125 0.16625,-4.20375 0.5,-14.44375 0.5,-17.96875 0,-2.335 -0.006,-2.7875 -1,-3 -0.59625,-0.1275 -1.15625,-0.22 -1.15625,-0.6875 0,-0.59375 0.47875,-0.65625 0.84375,-0.65625 z m 49.03125,0.6875 c -1.52875,0 -1.90625,0.48125 -1.90625,1.5 l 0,8.6875 c 0,0.4675 0.1675,0.5625 0.8125,0.5625 0.91625,0 1.75,-0.1775 2.53125,-0.6875 1.25625,-0.80625 1.78125,-2.4575 1.78125,-4.75 0,-3.22625 -1.08,-5.3125 -3.21875,-5.3125 z M 37.53125,54.1875 c -0.38875,0 -1.65625,1.73875 -1.65625,2.6875 l 0,12.65625 c 0,2.785 0.785,3.15625 1.90625,3.15625 l 5.53125,0 c 1.155,0 1.6025,-1.2325 1,-2.8125 -0.2025,-0.53125 -0.51875,-1.2675 -0.96875,-2.34375 -1.6025,-3.79375 -3.14625,-7.585 -4.75,-11.4375 C 38.155,55.0275 37.775,54.1875 37.53125,54.1875 z M 57,54.78125 c -1.66375,0 -1.81,0.0975 -1.84375,0.4375 l 0,9.90625 c 0,1.74125 0.49375,2.78125 2.125,2.78125 1.46,0 2.57375,-0.7725 3.21875,-2.34375 0.50875,-1.10375 0.75,-2.60125 0.75,-4.46875 0,-1.61375 -0.3025,-2.97 -0.8125,-4.03125 C 59.725,55.61875 58.52875,54.78125 57,54.78125 z" id="path13514-1" style="fill:#b60439;fill-opacity:1;fill-rule:nonzero;stroke:none"/>
9 <path d="m 57.4125,78.51255 1.4575,0 0,3.70125 0.035,0 c 0.19125,-0.31 0.395,-0.5975 0.585,-0.87125 l 2.11375,-2.83 1.80375,0 -2.77125,3.42625 2.95,4.62125 -1.72,0 -2.26875,-3.69 -0.7275,0.86 0,2.83 -1.4575,0 0,-8.0475 m -16.8525,8.0475 0,-8.0475 1.68375,0 2.07875,3.45 c 0.525,0.89625 1.0025,1.8275 1.3725,2.69875 L 45.7188,84.65 c -0.1075,-1.0625 -0.13125,-2.1025 -0.13125,-3.33125 l 0,-2.80625 1.36125,0 0,8.0475 -1.52875,0 -2.10125,-3.54625 c -0.51375,-0.88375 -1.03875,-1.8625 -1.4325,-2.77 l -0.0475,0.0113 c 0.0587,1.03875 0.0825,2.1025 0.0825,3.4275 l 0,2.8775 -1.36125,0 M 25.9788,84.2788 l -0.6925,2.28125 -1.51625,0 2.57875,-8.0475 1.875,0 2.615,8.0475 -1.57625,0 -0.72875,-2.28125 -2.555,0 z m 2.30375,-1.11 -0.6325,-1.9825 c -0.155,-0.48875 -0.28625,-1.03875 -0.40625,-1.50375 l -0.0237,0 c -0.11875,0.465 -0.23875,1.02625 -0.38125,1.50375 l -0.62125,1.9825 2.065,0 m -19.32,-4.5375 c 0.46625,-0.095 1.31375,-0.17875 2.15,-0.17875 1.07375,0 1.755,0.1425 2.2925,0.51375 0.50125,0.2975 0.83625,0.82375 0.83625,1.51625 0,0.74 -0.46625,1.40875 -1.3375,1.73125 l 0,0.0362 c 0.8475,0.215 1.62375,0.8825 1.62375,1.98125 0,0.71625 -0.31125,1.2775 -0.76375,1.66 -0.57375,0.50125 -1.5175,0.7525 -2.9975,0.7525 -0.8125,0 -1.42125,-0.06 -1.80375,-0.1075 l 0,-7.905 z m 1.4575,3.17625 0.7525,0 c 1.02625,0 1.5875,-0.4775 1.5875,-1.15875 0,-0.76375 -0.57375,-1.11 -1.505,-1.11 -0.43,0 -0.68,0.0238 -0.835,0.06 l 0,2.20875 z m 0,3.69 c 0.19125,0.0362 0.44125,0.0362 0.77625,0.0362 0.9425,0 1.79,-0.34625 1.79,-1.35 0,-0.93125 -0.82375,-1.31375 -1.83875,-1.31375 l -0.7275,0 0,2.6275" id="path13524" style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none"/>
10</svg>
11<!-- version: 20110311, original size: 71.625 90.593819, border: 3% --> \ No newline at end of file
diff --git a/data/homer/svg/ntop.svg b/data/homer/svg/ntop.svg
new file mode 100644
index 0000000..12a3461
--- /dev/null
+++ b/data/homer/svg/ntop.svg
@@ -0,0 +1,26 @@
1<?xml version="1.0" encoding="UTF-8" standalone="no"?>
2<svg
3 xmlns:dc="http://purl.org/dc/elements/1.1/"
4 xmlns:cc="http://creativecommons.org/ns#"
5 xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
6 xmlns:svg="http://www.w3.org/2000/svg"
7 xmlns="http://www.w3.org/2000/svg"
8 viewBox="0 0 270 270"
9 height="270"
10 width="270"
11 xml:space="preserve"
12 id="svg2"
13 version="1.1"><metadata
14 id="metadata8"><rdf:RDF><cc:Work
15 rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
16 rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
17 id="defs6"><clipPath
18 id="clipPath24"
19 clipPathUnits="userSpaceOnUse"><path
20 id="path22"
21 d="m 1736.15,294.105 c -12.94,6.184 -23.73,15.528 -32.07,27.766 -7.78,11.43 -13.61,25.746 -17.34,42.559 -3.5,15.785 -5.27,34.359 -5.27,55.203 v 530.051 c 0,20.695 1.78,39.136 5.28,54.816 3.74,16.71 9.58,30.93 17.38,42.28 8.34,12.15 19.14,21.42 32.08,27.55 12.43,5.89 26.92,8.87 43.08,8.87 19.73,0 37.62,-5.28 53.17,-15.71 9.15,-6.15 17.61,-14.19 25.3,-24.01 14.04,11.86 29.69,21.41 46.73,28.48 24.23,10.05 51.1,15.15 79.87,15.15 36.5,0 70.67,-7.26 101.57,-21.56 30.8,-14.27 58.99,-35.84 83.77,-64.13 24.63,-28.115 43.36,-60.19 55.68,-95.338 12.22,-34.871 18.41,-73.613 18.41,-115.168 0,-41.414 -6.34,-80.344 -18.86,-115.719 -12.58,-35.558 -31.7,-68.394 -56.83,-97.586 -25.3,-29.406 -53.74,-51.843 -84.52,-66.691 -31.01,-14.957 -65.05,-22.547 -101.17,-22.547 -25.97,0 -50.5,4.277 -72.93,12.707 -10.88,4.09 -21.53,9.207 -31.89,15.309 0,-38.77 0,-96.754 0,-96.754 0,-20.856 -1.8,-39.449 -5.36,-55.258 -3.78,-16.82 -9.69,-31.152 -17.57,-42.59 -8.42,-12.211 -19.26,-21.527 -32.22,-27.691 -12.49,-5.942 -27,-8.953 -43.15,-8.953 -16.18,0 -30.69,3.019 -43.14,8.964 m 190.38,600.36 c -9.83,-4.824 -18.85,-12.301 -26.78,-22.227 -8.21,-10.265 -14.47,-22.312 -18.62,-35.793 -4.3,-13.976 -6.48,-29.953 -6.48,-47.484 0,-18.254 2.16,-34.816 6.42,-49.215 4.09,-13.844 10.25,-26.094 18.32,-36.426 7.76,-9.933 16.68,-17.418 26.53,-22.25 9.95,-4.882 20.93,-7.254 33.54,-7.254 12.59,0 23.54,2.391 33.49,7.297 9.84,4.86 18.77,12.399 26.53,22.403 8.09,10.425 14.27,22.718 18.37,36.55 4.26,14.356 6.41,30.809 6.41,48.895 0,17.352 -2.19,33.203 -6.52,47.117 -4.19,13.461 -10.51,25.543 -18.81,35.902 -8.04,10.032 -17.09,17.59 -26.92,22.458 -9.82,4.863 -20.46,7.226 -32.55,7.226 -12.28,0 -23.05,-2.355 -32.93,-7.199 m -629.2,-399.809 c -18.89,3.547 -37.16,8.949 -54.28,16.063 -34.3,14.25 -65.32,35.863 -92.21,64.25 -26.82,28.304 -47.21,60.937 -60.63,97.008 -6.69,17.953 -11.76,37.101 -15.09,56.921 -3.31,19.661 -4.98,40.532 -4.98,62.016 0,21.484 1.67,42.352 4.98,62.008 3.33,19.805 8.4,38.941 15.09,56.875 13.42,36.027 33.83,68.605 60.65,96.843 26.89,28.29 57.92,49.84 92.2,64.04 17.12,7.1 35.39,12.49 54.28,16.02 18.75,3.51 38.63,5.3 59.09,5.3 20.54,0 40.47,-1.78 59.24,-5.27 18.92,-3.53 37.19,-8.91 54.3,-15.98 34.27,-14.17 65.17,-35.67 91.83,-63.92 26.58,-28.146 46.79,-60.717 60.08,-96.795 13.18,-35.765 19.86,-75.844 19.86,-119.121 0,-21.555 -1.66,-42.473 -4.92,-62.18 -3.3,-19.855 -8.32,-39.035 -14.93,-56.996 -13.3,-36.109 -33.5,-68.734 -60.07,-96.961 -26.66,-28.328 -57.55,-49.902 -91.84,-64.117 -17.1,-7.097 -35.37,-12.484 -54.3,-16.027 -18.78,-3.504 -38.71,-5.285 -59.25,-5.285 -20.47,0 -40.35,1.785 -59.1,5.308 m 25.85,403.703 c -9.84,-4.804 -18.81,-12.261 -26.66,-22.156 -8.14,-10.265 -14.35,-22.316 -18.47,-35.808 -4.26,-13.989 -6.42,-29.973 -6.42,-47.528 0,-18.258 2.16,-34.816 6.41,-49.211 4.09,-13.847 10.26,-26.105 18.33,-36.429 7.75,-9.934 16.68,-17.418 26.52,-22.247 9.95,-4.886 20.93,-7.257 33.54,-7.257 12.59,0 23.54,2.386 33.49,7.297 9.85,4.859 18.78,12.398 26.54,22.402 8.08,10.422 14.26,22.719 18.36,36.551 4.26,14.355 6.41,30.808 6.41,48.894 0,17.352 -2.19,33.203 -6.52,47.117 -4.18,13.461 -10.51,25.543 -18.81,35.903 -8.03,10.035 -17.09,17.59 -26.92,22.453 -9.82,4.867 -20.46,7.23 -32.55,7.23 -12.46,0 -23.33,-2.359 -33.25,-7.211 M 855.262,507.574 c -12.965,6.164 -23.809,15.485 -32.219,27.696 v -0.004 c -7.879,11.422 -13.785,25.703 -17.57,42.445 -3.551,15.734 -5.352,34.211 -5.352,54.922 0,0 0,228.074 0,260.695 -10.394,0.738 -20.078,2.356 -28.902,4.84 -13.199,3.707 -24.875,9.437 -34.692,17.035 -10.25,7.93 -18.105,17.508 -23.336,28.465 -5.175,10.828 -7.796,23.047 -7.796,36.309 0,13.57 2.617,25.953 7.781,36.823 5.293,11.13 13.25,20.69 23.652,28.41 9.735,7.24 21.797,12.91 35.84,16.87 8.309,2.34 17.492,4.13 27.453,5.37 0,19.6 0,51.75 0,51.75 0,20.71 1.801,39.19 5.352,54.91 3.781,16.75 9.691,31.03 17.57,42.46 8.414,12.21 19.254,21.52 32.219,27.69 12.48,5.94 27,8.95 43.16,8.95 16.027,0 30.433,-3.02 42.816,-8.99 12.832,-6.18 23.563,-15.51 31.891,-27.74 7.773,-11.43 13.605,-25.69 17.336,-42.42 3.508,-15.71 5.285,-34.17 5.285,-54.86 0,0 0,-30.17 0,-50.24 14.98,-0.33 28.71,-2.07 40.89,-5.17 14.41,-3.66 27.09,-9.33 37.69,-16.87 11.23,-7.98 19.83,-17.75 25.58,-29.05 5.73,-11.23 8.63,-23.979 8.63,-37.893 0,-14.168 -2.79,-26.977 -8.28,-38.075 -5.66,-11.425 -14.16,-21.054 -25.27,-28.613 -10.33,-7.027 -23.06,-12.297 -37.84,-15.668 -12.14,-2.766 -26.04,-4.336 -41.4,-4.68 0,-33.195 0,-260.308 0,-260.308 0,-20.828 -1.75,-39.375 -5.211,-55.11 -3.687,-16.781 -9.453,-31.039 -17.148,-42.382 -8.258,-12.188 -19.004,-21.481 -31.938,-27.633 -12.398,-5.891 -26.875,-8.879 -43.031,-8.879 -16.16,0 -30.68,3.008 -43.16,8.945 m -293.946,0 c -12.964,6.164 -23.808,15.485 -32.218,27.696 v -0.004 c -7.879,11.422 -13.785,25.703 -17.571,42.445 -3.55,15.734 -5.351,34.211 -5.351,54.922 v 178.312 c 0,49.477 -8.852,72.442 -16.278,82.989 -9.445,13.418 -25.632,19.941 -49.48,19.941 -11.477,0 -21.363,-1.859 -29.363,-5.523 -7.512,-3.438 -13.539,-8.481 -18.43,-15.414 -5.34,-7.567 -9.492,-17.821 -12.34,-30.461 -3.094,-13.735 -4.656,-30.418 -4.656,-49.582 V 632.633 c 0,-20.711 -1.801,-39.188 -5.359,-54.922 -3.778,-16.738 -9.688,-31.02 -17.563,-42.445 -8.418,-12.211 -19.258,-21.528 -32.227,-27.692 -12.48,-5.937 -27,-8.945 -43.156,-8.945 -16.172,0 -30.679,3.016 -43.121,8.953 -12.949,6.188 -23.738,15.531 -32.074,27.781 -7.774,11.407 -13.602,25.676 -17.336,42.399 -3.5,15.715 -5.277,34.172 -5.277,54.871 v 317.051 c 0,20.695 1.777,39.136 5.285,54.816 3.734,16.71 9.578,30.93 17.371,42.28 8.348,12.15 19.141,21.42 32.086,27.55 12.43,5.89 26.918,8.87 43.066,8.87 19.746,0 37.637,-5.28 53.184,-15.71 9.152,-6.15 17.613,-14.19 25.308,-24.02 14.661,12.36 30.883,21.98 48.438,28.69 24.238,9.27 53.656,13.97 87.43,13.97 33.238,0 63.179,-4.89 88.992,-14.52 26.48,-9.9 49.246,-25 67.668,-44.88 18.293,-19.75 32.156,-44.171 41.199,-72.566 8.781,-27.578 13.238,-59.656 13.238,-95.348 V 632.633 c 0,-20.711 -1.801,-39.184 -5.359,-54.922 -3.785,-16.738 -9.692,-31.02 -17.567,-42.445 -8.417,-12.211 -19.261,-21.528 -32.226,-27.692 -12.473,-5.937 -26.996,-8.945 -43.152,-8.945 -16.161,0 -30.68,3.008 -43.161,8.945" /></clipPath></defs><g
22 transform="matrix(1.3333333,0,0,-1.3333333,-26.495333,310.5792)"
23 id="g10"><path
24 style="display:inline;fill:#ff7500;fill-opacity:1;stroke-width:0.09995687"
25 d="m 66.384572,109.39272 c -1.301838,-1.88858 -3.287282,-2.83238 -5.954431,-2.83238 -2.668048,0 -4.653492,0.9438 -5.95493,2.83238 -1.301339,1.88749 -1.951858,4.76864 -1.951858,8.64317 v 17.82351 c 0,4.39501 -0.659116,7.52765 -1.976847,9.39985 -1.317831,1.87189 -3.489494,2.80818 -6.515189,2.80818 -2.863964,0 -4.978751,-0.96908 -6.345262,-2.90574 -1.36661,-1.93707 -2.050315,-4.97326 -2.050315,-9.10737 v -18.01843 c 0,-3.87453 -0.650919,-6.75568 -1.951958,-8.64317 -1.301738,-1.88858 -3.286781,-2.83238 -5.95483,-2.83238 -2.668349,0 -4.644496,0.9438 -5.929842,2.83238 -1.285345,1.88749 -1.927668,4.76864 -1.927668,8.64317 v 31.69143 c 0,3.87403 0.642323,6.74668 1.927668,8.61888 1.285346,1.87219 3.261493,2.80778 5.929842,2.80778 1.594212,0 3.009701,-0.41582 4.247067,-1.24546 1.235767,-0.82964 2.325497,-2.0921 3.269589,-3.78437 1.69187,1.8562 3.643428,3.20662 5.856473,4.05326 2.212745,0.84563 4.897087,1.26945 8.053625,1.26945 6.214419,0 10.964769,-1.77424 14.251651,-5.32271 3.285682,-3.54846 4.929473,-8.70833 4.929473,-15.47971 v -22.60855 c 0,-3.87453 -0.651219,-6.75568 -1.952258,-8.64317 z m 39.997038,38.28348 c -1.67528,-1.13931 -4.1722,-1.70866 -7.491564,-1.70866 h -1.268553 v -27.93165 c 0,-3.90611 -0.634926,-6.79586 -1.904278,-8.66776 -1.268553,-1.87219 -3.238103,-2.80779 -5.905252,-2.80779 -2.668049,0 -4.653092,0.9438 -5.95483,2.83238 -1.301439,1.88749 -1.951958,4.76864 -1.951958,8.64317 v 27.93165 h -0.585248 c -2.766406,0 -4.938069,0.61034 -6.515988,1.83091 -1.577819,1.22097 -2.367279,2.87296 -2.367279,4.95686 0,2.14837 0.78946,3.80865 2.367279,4.98015 1.577919,1.17249 3.944398,1.83921 7.101236,2.00214 v 6.934 c 0,3.87433 0.650519,6.75509 1.951958,8.64327 1.301738,1.88719 3.286781,2.83278 5.95483,2.83278 2.635563,0 4.595317,-0.94559 5.881462,-2.83278 1.284546,-1.88818 1.928068,-4.76894 1.928068,-8.64327 v -6.934 h 1.366211 c 3.123746,0 5.555696,-0.61874 7.295946,-1.8552 1.74125,-1.23747 2.61188,-2.94673 2.61188,-5.12709 0,-2.24673 -0.83864,-3.9394 -2.51392,-5.07911 z m 48.34414,-34.10828 c -4.97885,-5.29072 -11.3561,-7.93488 -19.13274,-7.93488 -7.74466,0 -14.12991,2.65236 -19.15674,7.95907 -5.02783,5.30591 -7.54074,12.0612 -7.54074,20.26506 0,8.20386 2.51291,14.94985 7.54074,20.24086 5.02683,5.28972 11.41208,7.93457 19.15674,7.93457 7.77664,0 14.15389,-2.63686 19.13274,-7.90998 4.97785,-5.27382 7.46678,-12.02991 7.46678,-20.26545 0,-8.23665 -2.48893,-14.99943 -7.46678,-20.28925 z m -11.68996,29.98236 c -1.96815,2.45794 -4.44908,3.68671 -7.44278,3.68671 -3.05968,0 -5.55661,-1.22097 -7.49177,-3.66212 -1.93617,-2.44154 -2.90475,-5.61597 -2.90475,-9.52249 0,-4.03696 0.95959,-7.28396 2.87976,-9.74189 1.92017,-2.45794 4.42509,-3.68671 7.51676,-3.68671 3.09066,0 5.59658,1.23696 7.51675,3.71129 1.91917,2.47354 2.87876,5.71324 2.87876,9.71731 0,3.87373 -0.98458,7.03956 -2.95273,9.4979 z m 72.23483,-29.76255 c -4.73395,-5.50233 -10.43749,-8.25234 -17.10661,-8.25234 -2.37598,0 -4.58103,0.38263 -6.61415,1.1472 -2.03412,0.76407 -3.96129,1.91208 -5.7825,3.44222 V 96.74508 c 0,-3.906114 -0.65172,-6.802964 -1.95316,-8.691549 -1.30144,-1.887886 -3.28658,-2.832378 -5.95343,-2.832378 -2.66985,0 -4.646,0.944492 -5.93144,2.832378 -1.28545,1.888585 -1.92717,4.785435 -1.92717,8.691549 v 52.98224 c 0,3.87403 0.64172,6.74668 1.92717,8.61888 1.28544,1.87219 3.26159,2.80778 5.93144,2.80778 1.59331,0 3.0087,-0.41582 4.24616,-1.24546 1.23547,-0.82964 2.325,-2.0921 3.26859,-3.78437 1.6273,1.79023 3.53948,3.14165 5.73553,4.05326 2.19605,0.9106 4.61201,1.36741 7.24787,1.36741 6.76708,0 12.46162,-2.63687 17.08263,-7.91099 4.62001,-5.27392 6.93001,-11.86598 6.93001,-19.77666 0,-7.8782 -2.36698,-14.56872 -7.10094,-20.06944 z m -11.95784,29.37202 c -1.96915,2.45724 -4.45008,3.68671 -7.44278,3.68671 -3.0257,0 -5.51562,-1.22057 -7.46778,-3.66202 -1.95216,-2.44234 -2.92874,-5.61598 -2.92874,-9.52249 0,-4.03736 0.95959,-7.28396 2.87976,-9.7419 1.92017,-2.45834 4.42509,-3.68671 7.51676,-3.68671 3.09066,0 5.59658,1.23657 7.51675,3.7113 1.91917,2.47354 2.87876,5.71314 2.87876,9.71731 0,3.87373 -0.98457,7.03956 -2.95273,9.4978 z"
26 id="path34" /></g></svg> \ No newline at end of file
diff --git a/data/homer/svg/overseerr.svg b/data/homer/svg/overseerr.svg
new file mode 100644
index 0000000..a841857
--- /dev/null
+++ b/data/homer/svg/overseerr.svg
@@ -0,0 +1,90 @@
1<?xml version="1.0" encoding="UTF-8" standalone="no"?>
2<svg
3 width="96"
4 height="96"
5 fill="none"
6 viewBox="0 0 96 96"
7 version="1.1"
8 id="svg20"
9 sodipodi:docname="os_icon.svg"
10 inkscape:export-filename="/home/nx211/Desktop/os_icon.png"
11 inkscape:export-xdpi="512"
12 inkscape:export-ydpi="512"
13 inkscape:version="1.1 (c4e8f9ed74, 2021-05-24)"
14 xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
15 xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
16 xmlns="http://www.w3.org/2000/svg"
17 xmlns:svg="http://www.w3.org/2000/svg">
18 <sodipodi:namedview
19 id="namedview22"
20 pagecolor="#ffffff"
21 bordercolor="#666666"
22 borderopacity="1.0"
23 inkscape:pageshadow="2"
24 inkscape:pageopacity="0.0"
25 inkscape:pagecheckerboard="0"
26 showgrid="false"
27 inkscape:zoom="7.84375"
28 inkscape:cx="19.059761"
29 inkscape:cy="48"
30 inkscape:window-width="1920"
31 inkscape:window-height="1011"
32 inkscape:window-x="0"
33 inkscape:window-y="0"
34 inkscape:window-maximized="1"
35 inkscape:current-layer="svg20" />
36 <path
37 fill="url(#paint0_linear)"
38 fill-rule="evenodd"
39 d="M48 96C74.5097 96 96 74.5097 96 48C96 21.4903 74.5097 0 48 0C21.4903 0 0 21.4903 0 48C0 74.5097 21.4903 96 48 96ZM80.0001 52C80.0001 67.464 67.4641 80 52.0001 80C36.5361 80 24.0001 67.464 24.0001 52C24.0001 49.1303 24.4318 46.3615 25.2338 43.7548C27.4288 48.6165 32.3194 52 38.0001 52C45.7321 52 52.0001 45.732 52.0001 38C52.0001 32.3192 48.6166 27.4287 43.755 25.2337C46.3616 24.4317 49.1304 24 52.0001 24C67.4641 24 80.0001 36.536 80.0001 52Z"
40 clip-rule="evenodd"
41 id="path2" />
42 <path
43 fill="#131928"
44 fill-rule="evenodd"
45 d="M80.0002 52C80.0002 67.464 67.4642 80 52.0002 80C36.864 80 24.5329 67.9897 24.017 52.9791C24.0057 53.318 24 53.6583 24 54C24 70.5685 37.4315 84 54 84C70.5685 84 84 70.5685 84 54C84 37.4315 70.5685 24 54 24C53.6597 24 53.3207 24.0057 52.9831 24.0169C67.9919 24.5347 80.0002 36.865 80.0002 52Z"
46 clip-rule="evenodd"
47 opacity=".2"
48 id="path4" />
49 <path
50 fill="url(#paint1_linear)"
51 fill-rule="evenodd"
52 d="M48 12C28.1177 12 12 28.1177 12 48C12 50.2091 10.2091 52 8 52C5.79086 52 4 50.2091 4 48C4 23.6995 23.6995 4 48 4C50.2091 4 52 5.79086 52 8C52 10.2091 50.2091 12 48 12Z"
53 clip-rule="evenodd"
54 id="path6" />
55 <defs
56 id="defs18">
57 <linearGradient
58 id="paint0_linear"
59 x1="48"
60 x2="117.5"
61 y1="0"
62 y2="69.5"
63 gradientUnits="userSpaceOnUse">
64 <stop
65 stop-color="#C395FC"
66 id="stop8" />
67 <stop
68 offset="1"
69 stop-color="#4F65F5"
70 id="stop10" />
71 </linearGradient>
72 <linearGradient
73 id="paint1_linear"
74 x1="28"
75 x2="28"
76 y1="8"
77 y2="48"
78 gradientUnits="userSpaceOnUse">
79 <stop
80 stop-color="#fff"
81 stop-opacity=".4"
82 id="stop13" />
83 <stop
84 offset="1"
85 stop-color="#fff"
86 stop-opacity="0"
87 id="stop15" />
88 </linearGradient>
89 </defs>
90</svg>
diff --git a/data/homer/svg/pagerduty.svg b/data/homer/svg/pagerduty.svg
new file mode 100644
index 0000000..a99b251
--- /dev/null
+++ b/data/homer/svg/pagerduty.svg
Binary files differ
diff --git a/data/homer/svg/portainer.svg b/data/homer/svg/portainer.svg
new file mode 100644
index 0000000..e8c91b3
--- /dev/null
+++ b/data/homer/svg/portainer.svg
@@ -0,0 +1 @@
<svg height="2500" viewBox=".16 0 571.71 800" width="1788" xmlns="http://www.w3.org/2000/svg"><g fill="#13bef9"><path d="m190.83 175.88h-12.2v63.2h12.2zm52.47 0h-12.2v63.2h12.2zm71.69-120.61-12.5-21.68-208.67 120.61 12.5 21.68z"/><path d="m313.77 55.27 12.51-21.68 208.67 120.61-12.51 21.68z"/><path d="m571.87 176.18v-25.03h-571.71v25.03z"/><path d="m345.5 529.77v-370.99h25.02v389.01c-6.71-7.64-15.26-13.13-25.02-18.02zm-42.71-6.41v-523.36h25.02v526.41c-7.02-3.36-24.1-3.05-25.02-3.05zm-237.04 52.21c-30.51-22.59-50.64-58.62-50.64-99.54 0-21.68 5.79-43.05 16.47-61.68h213.55c10.98 18.63 16.48 40 16.48 61.68 0 18.93-2.44 36.64-10.07 52.52-16.17-15.57-39.97-22.29-64.07-22.29-42.71 0-79.32 26.56-88.77 66.26-3.36-.31-5.49-.61-8.85-.61-8.24.3-16.17 1.53-24.1 3.66z" fill-rule="evenodd"/><path d="m170.69 267.18h-64.67v65.03h64.67zm-72.91 0h-64.67v65.03h64.67zm0 72.36h-64.67v65.04h64.67zm72.91 0h-64.67v65.04h64.67zm72.61 0h-64.67v65.04h64.67zm0-107.17h-64.67v65.03h64.67z"/><path d="m109.37 585.34c8.85-37.55 42.71-65.65 82.98-65.65 25.94 0 49.12 11.61 64.99 29.93 13.72-9.47 30.2-14.96 48.2-14.96 46.98 0 85.11 38.16 85.11 85.19 0 9.77-1.52 18.93-4.57 27.78 10.37 14.05 16.78 31.76 16.78 50.69 0 47.02-38.14 85.19-85.12 85.19-20.75 0-39.66-7.33-54.3-19.54-15.56 21.68-40.88 36.03-69.56 36.03-32.95 0-61.63-18.93-75.96-46.41-5.8 1.22-11.6 1.83-17.7 1.83-46.98 0-85.42-38.17-85.42-85.19s38.14-85.19 85.42-85.19c3.05-.31 6.1-.31 9.15.3z" fill-rule="evenodd"/></g></svg> \ No newline at end of file
diff --git a/data/homer/svg/prowlarr.svg b/data/homer/svg/prowlarr.svg
new file mode 100644
index 0000000..70ac886
--- /dev/null
+++ b/data/homer/svg/prowlarr.svg
@@ -0,0 +1,296 @@
1<?xml version="1.0" encoding="UTF-8" standalone="no"?>
2<svg
3 xmlns:dc="http://purl.org/dc/elements/1.1/"
4 xmlns:cc="http://creativecommons.org/ns#"
5 xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
6 xmlns:svg="http://www.w3.org/2000/svg"
7 xmlns="http://www.w3.org/2000/svg"
8 xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
9 xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
10 version="1.1"
11 id="svg10"
12 xml:space="preserve"
13 width="1024"
14 height="1024"
15 viewBox="0 0 1024 1024"
16 sodipodi:docname="prowlarr (1).svg"
17 inkscape:version="0.92.2 (5c3e80d, 2017-08-06)"
18 inkscape:export-filename="C:\Users\qstic\source\repos\Prowlarr\Logo\16.png"
19 inkscape:export-xdpi="1.5"
20 inkscape:export-ydpi="1.5"><metadata
21 id="metadata16"><rdf:RDF><cc:Work
22 rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
23 rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
24 id="defs14"><clipPath
25 clipPathUnits="userSpaceOnUse"
26 id="clipPath510"><path
27 d="m 1271.06,905.77 c 0,0 0,0 -0.01,0 0.01,0 0.01,0 0.01,0 v 0"
28 id="path508" /></clipPath></defs><sodipodi:namedview
29 pagecolor="#ffffff"
30 bordercolor="#666666"
31 borderopacity="1"
32 objecttolerance="10"
33 gridtolerance="10"
34 guidetolerance="10"
35 inkscape:pageopacity="0"
36 inkscape:pageshadow="2"
37 inkscape:window-width="1680"
38 inkscape:window-height="997"
39 id="namedview12"
40 showgrid="false"
41 fit-margin-top="0"
42 fit-margin-left="0"
43 fit-margin-right="0"
44 fit-margin-bottom="0"
45 inkscape:zoom="0.60971844"
46 inkscape:cx="341.56775"
47 inkscape:cy="474.60369"
48 inkscape:window-x="0"
49 inkscape:window-y="25"
50 inkscape:window-maximized="1"
51 inkscape:current-layer="g18"
52 inkscape:document-rotation="0"
53 inkscape:pagecheckerboard="false" /><g
54 id="g18"
55 inkscape:groupmode="layer"
56 inkscape:label="ink_ext_XXXXXX"
57 transform="matrix(1.3333333,0,0,-1.3333333,198.62766,515.8368)"
58 style="display:inline"><g
59 id="g2248"
60 transform="matrix(4.0960001,0,0,4.0960001,425.09731,-1123.3485)"><circle
61 style="fill:#ffe6d5;fill-opacity:1;fill-rule:evenodd;stroke-width:1.68177998"
62 id="path2188"
63 cx="-46.40332"
64 cy="-274.95755"
65 transform="scale(1,-1)"
66 r="90" /><path
67 d="m -108.10156,258.50681 16.177,19.001 121.483,0.403 -15.767,-19.404 h -121.893"
68 style="fill:#83331b;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.1"
69 id="path568"
70 inkscape:connector-curvature="0" /><path
71 id="path570"
72 style="fill:#f8a37b;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.133333"
73 d="m -144.13477,321.99023 v 68.08399 a 24.036282,10.884353 0 0 1 21.68555,10.82031 H 3.3222656 A 18.14059,11.337869 0 0 1 1.8144531,396.37109 18.14059,11.337869 0 0 1 18.388672,385.07812 v -63.08789 z"
74 transform="matrix(0.75000002,0,0,-0.75000002,0,500.00002)"
75 inkscape:connector-curvature="0" /><path
76 d="m -38.32356,249.89581 c 1.962,1.06 4.991,4.244 3.122,7.428 -0.346,0.589 -0.684,0.968 -1.008,1.183 h 8.145 c -1.06,-2.333 -4.89,-5.155 -4.89,-5.155 0,0 -0.313,-0.606 -2.011,-1.941 -0.961,-0.754 -2.341,-1.237 -3.358,-1.515 m -24.041,0.131 c -1.001,0.465 -2.455,1.262 -3.478,2.348 -1.708,1.813 -2.052,4.191 -2.052,4.191 l -1.208,1.941 h 4.853 c -0.324,-0.215 -0.661,-0.594 -1.007,-1.183 -1.797,-3.062 0.935,-6.124 2.892,-7.297 m 22.287,0.961 c -1.065,0.104 -3.04,0.372 -5.015,1.03 -2.911,0.97 -4.609,3.153 -4.609,3.153 0,0 -3.275,-2.001 -7.641,-2.796 -1.458,-0.265 -2.408,-0.354 -3.028,-0.354 -0.01,0 -0.019,0 -0.029,0 -0.087,1.464 -0.387,3.029 -0.387,3.029 0,0 -0.827,2.653 -2.068,3.457 h 5.168 20.083 c -1.241,-0.804 -2.069,-3.457 -2.069,-3.457 0,0 -0.464,-2.425 -0.405,-4.062"
77 style="fill:#da845d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.1"
78 id="path572"
79 inkscape:connector-curvature="0" /><path
80 d="m -60.78656,255.04981 c 0,0 0.923,-4.821 0,-5.458 -0.922,-0.637 -6.926,3.548 -4.47,7.732 2.456,4.184 4.47,-2.274 4.47,-2.274"
81 style="fill:#dee6e3;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.1"
82 id="path574"
83 inkscape:connector-curvature="0" /><path
84 d="m -39.67256,255.04981 c 0,0 -0.922,-4.821 0,-5.458 0.923,-0.637 6.927,3.548 4.471,7.732 -2.456,4.184 -4.471,-2.274 -4.471,-2.274"
85 style="fill:#dee6e3;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.1"
86 id="path576"
87 inkscape:connector-curvature="0" /><path
88 d="m -75.27356,295.45981 c 1.117,-5.335 -2.302,-10.566 -7.637,-11.683 -5.335,-1.117 -10.565,2.302 -11.682,7.637 -1.117,5.335 2.302,10.566 7.637,11.683 5.335,1.117 10.565,-2.302 11.682,-7.637"
89 style="fill:#d4541e;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.1"
90 id="path578"
91 inkscape:connector-curvature="0" /><path
92 d="m -79.13656,301.41081 c -2.176,1.581 -4.979,2.281 -7.819,1.686 -5.335,-1.117 -8.754,-6.348 -7.637,-11.683 0.243,-1.161 0.69,-2.224 1.282,-3.177 4.349,1.479 10.933,5.007 14.174,13.174"
93 style="fill:#852e1b;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.1"
94 id="path580"
95 inkscape:connector-curvature="0" /><path
96 d="m -79.55556,294.56281 c 0.621,-2.97 -1.282,-5.881 -4.252,-6.503 -2.969,-0.622 -5.881,1.281 -6.503,4.251 -0.622,2.97 1.282,5.881 4.251,6.503 2.97,0.622 5.882,-1.281 6.504,-4.251"
97 style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.1"
98 id="path582"
99 inkscape:connector-curvature="0" /><path
100 d="m -11.97056,297.29481 c -2.292,0 -4.427,1.456 -5.185,3.752 -0.941,2.854 0.602,5.932 3.449,6.887 1.578,0.545 9.258,3.6 6.96,10.765 -1.66,5.18 -4.8,5.934 -14.08,6.919 -1.702,0.18 -3.461,0.368 -5.289,0.629 -6.567,0.938 -11.396,3.654 -14.351,8.072 -4.312,6.445 -2.622,13.751 -2.415,14.562 0.744,2.922 3.717,4.683 6.64,3.943 2.922,-0.745 4.687,-3.718 3.942,-6.64 0.002,0 -0.74,-3.391 0.946,-5.845 1.168,-1.701 3.45,-2.804 6.783,-3.281 1.632,-0.233 3.292,-0.409 4.897,-0.58 8.623,-0.915 19.355,-2.056 23.327,-14.443 4.228,-13.187 -6.004,-21.855 -13.912,-24.464 -0.568,-0.187 -1.145,-0.276 -1.712,-0.276"
101 style="fill:#e2591e;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.1"
102 id="path584"
103 inkscape:connector-curvature="0" /><path
104 d="m -31.35356,340.33881 -0.162,0.262 c -3.6,-2.3 -8.146,-2.791 -10.691,-2.868 0.426,-1.138 0.987,-2.289 1.74,-3.414 0.966,-1.445 2.153,-2.688 3.516,-3.766 4.784,0.793 8.636,4.38 10.757,6.815 -2.455,0.573 -4.194,1.564 -5.16,2.971"
105 style="fill:#852e1b;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.1"
106 id="path588"
107 inkscape:connector-curvature="0" /><path
108 d="m -19.75856,325.50081 c 3.913,-0.433 6.655,-0.862 8.622,-1.688 1.349,2.24 3.232,5.957 3.93,10.147 -2.897,1.167 -6.016,1.739 -9.03,2.126 0.263,-2.716 0.078,-7.398 -3.522,-10.585"
109 style="fill:#852e1b;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.1"
110 id="path590"
111 inkscape:connector-curvature="0" /><path
112 d="m 4.15644,311.99081 c 0.613,2.488 0.673,5.256 -0.025,8.278 -1.075,-0.869 -2.457,-1.657 -4.202,-2.11 -2.954,-0.768 -5.066,-0.721 -6.408,-0.509 0.581,-2.947 -0.577,-5.128 -2.15,-6.674 4.852,-2.119 9.593,-0.653 12.785,1.015"
113 style="fill:#852e1b;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.1"
114 id="path592"
115 inkscape:connector-curvature="0" /><path
116 d="m -6.44656,314.85681 c -0.003,10e-4 -0.006,0.003 -0.009,0.004 0.012,0.064 0.024,0.128 0.034,0.193 l 0.001,0.002 0.028,0.238 c 0.031,0.251 0.052,0.506 0.056,0.77 l 0.001,0.16 c -0.004,0.338 -0.027,0.685 -0.081,1.047 l 10e-4,-10e-4 c -0.066,0.45 -0.167,0.916 -0.322,1.404 0.111,-0.352 0.199,-0.693 0.265,-1.025 -0.002,0 -0.004,0 -0.006,10e-4 0.201,-1.021 0.194,-1.95 0.032,-2.793 m -0.292,3.82 c -0.003,0.007 -0.005,0.015 -0.008,0.022 -0.856,2.671 -2.105,4.165 -4.386,5.121 v 0 c 2.281,-0.956 3.53,-2.45 4.386,-5.121 0.003,-0.007 0.005,-0.015 0.008,-0.022 m -13.018,6.825 c -0.347,0.039 -0.704,0.077 -1.07,0.116 -1.702,0.18 -3.461,0.368 -5.29,0.629 -0.048,0.007 -0.097,0.014 -0.146,0.022 0.049,-0.008 0.098,-0.015 0.147,-0.022 1.828,-0.261 3.587,-0.449 5.289,-0.629 0.366,-0.039 0.723,-0.077 1.07,-0.116 v 0 m -6.54,0.772 c -0.007,10e-4 -0.015,0.002 -0.023,0.003 0.008,-0.001 0.015,-0.002 0.023,-0.003 m -0.075,0.011 c -0.001,0 -0.002,0 -0.003,0 0,0 0.002,0 0.003,0"
117 style="fill:#83bad2;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.1"
118 id="path594"
119 inkscape:connector-curvature="0" /><path
120 d="m 3.32344,309.50681 c -0.608,0.453 -1.229,0.89 -1.865,1.311 0.962,0.334 1.851,0.733 2.646,1.146 0.013,-0.008 0.025,-0.017 0.037,-0.026 -0.211,-0.844 -0.488,-1.653 -0.818,-2.431 m -7.988,8.008 c -0.71,0 -1.312,0.056 -1.808,0.134 -0.066,0.332 -0.154,0.673 -0.265,1.025 -10e-4,10e-4 -10e-4,0.002 -10e-4,0.003 -0.003,0.007 -0.005,0.015 -0.008,0.022 -0.856,2.671 -2.105,4.165 -4.386,5.121 0.494,0.82 1.059,1.839 1.612,3.006 2.749,-1.108 4.625,-2.821 6.016,-5.603 0.566,-1.131 0.757,-2.351 0.695,-3.592 -0.685,-0.082 -1.303,-0.116 -1.855,-0.116 m -15.092,7.987 c -0.347,0.039 -0.704,0.077 -1.07,0.116 -1.702,0.18 -3.461,0.368 -5.289,0.629 -0.049,0.007 -0.098,0.014 -0.147,0.022 -0.011,0.001 -0.023,0.003 -0.034,0.005 -0.008,10e-4 -0.015,0.002 -0.023,0.003 -0.018,0.003 -0.035,0.005 -0.052,0.008 -0.001,0 -0.003,0 -0.003,0 -4.878,0.737 -8.769,2.481 -11.64,5.178 0.341,-0.316 0.696,-0.619 1.064,-0.91 0.568,0.094 1.123,0.228 1.664,0.394 2.752,-0.893 6.221,-1.645 10.437,-1.887 2.795,-0.16 5.246,-0.326 7.403,-0.556 -0.54,-1.068 -1.287,-2.095 -2.31,-3.002"
121 style="fill:#ba4a1f;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.1"
122 id="path596"
123 inkscape:connector-curvature="0" /><path
124 d="m -36.95056,330.55281 c -0.368,0.291 -0.723,0.594 -1.064,0.91 -0.306,0.287 -0.6,0.585 -0.883,0.893 1.024,-0.474 2.227,-0.959 3.611,-1.409 -0.541,-0.166 -1.096,-0.3 -1.664,-0.394"
125 style="fill:#6f2717;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.1"
126 id="path598"
127 inkscape:connector-curvature="0" /><path
128 d="m -11.13256,323.81981 c -1.97,0.825 -4.707,1.249 -8.624,1.682 v 0 c 1.023,0.907 1.77,1.934 2.31,3.002 3.255,-0.346 5.842,-0.838 7.926,-1.678 -0.553,-1.167 -1.118,-2.186 -1.612,-3.006 v 0"
129 style="fill:#6f2717;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.1"
130 id="path600"
131 inkscape:connector-curvature="0" /><path
132 d="m 1.45844,310.81781 c -2.431,1.612 -5.063,2.99 -7.905,4.039 0.162,0.843 0.169,1.772 -0.032,2.793 0.002,-10e-4 0.004,-10e-4 0.006,-10e-4 0.496,-0.078 1.098,-0.134 1.808,-0.134 0.552,0 1.17,0.034 1.855,0.116 -0.027,-0.549 -0.104,-1.101 -0.219,-1.652 2.551,-1.103 4.934,-2.454 7.133,-4.015 -0.795,-0.413 -1.684,-0.812 -2.646,-1.146"
133 style="fill:#6f2717;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.1"
134 id="path602"
135 inkscape:connector-curvature="0" /><path
136 d="m 1.95644,321.13981 c 0,0 -4.428,6.195 -10.044,9.266 0.233,0.715 0.442,1.456 0.617,2.217 0.927,-0.436 1.771,-0.935 2.477,-1.507 4.964,-4.019 6.95,-9.976 6.95,-9.976 m -18.127,12.572 c -3.248,1.173 -5.535,1.865 -5.535,1.865 0,0 2.427,-0.091 5.54,-0.568 0.014,-0.409 0.015,-0.844 -0.005,-1.297"
137 style="fill:#e66733;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.1"
138 id="path604"
139 inkscape:connector-curvature="0" /><path
140 d="m -8.08756,330.40581 c -0.478,0.261 -0.964,0.5 -1.456,0.71 -2.364,1.009 -4.67,1.89 -6.627,2.596 0.02,0.453 0.019,0.888 0.005,1.297 2.763,-0.423 6.066,-1.15 8.695,-2.386 -0.175,-0.761 -0.384,-1.502 -0.617,-2.217"
141 style="fill:#94401d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.1"
142 id="path606"
143 inkscape:connector-curvature="0" /><path
144 d="m -57.68656,303.78381 c 0,0 14.474,17.064 43.825,12.94 29.351,-4.124 40.267,-42.45 40.267,-42.45 l -12.614,-15.767 h -71.478 v 45.277"
145 style="fill:#ef5d22;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.1"
146 id="path608"
147 inkscape:connector-curvature="0" /><path
148 d="m -53.06456,307.88981 c 3.118,0.854 8.074,1.536 15.715,1.011 18.532,-1.273 42.591,-30.99 42.591,-30.99 0,0 -10.668,33.893 -44.619,36.942 -6.085,-1.951 -10.642,-4.693 -13.687,-6.963"
149 style="fill:#852e1b;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.1"
150 id="path610"
151 inkscape:connector-curvature="0" /><path
152 d="m 5.02844,263.29881 h -4.184 c -1.15,3.215 -6.923,18.376 -16.161,26.318 -7.613,6.544 -18.654,14.166 -32.41,14.879 0.704,0.036 1.401,0.054 2.092,0.054 9.919,0 18.501,-3.657 25.49,-8.144 0.021,0.129 0.034,0.258 0.061,0.388 0.293,1.403 0.874,2.67 1.665,3.758 5.534,-3.983 10.713,-8.764 14.808,-12.906 -0.999,-0.954 -2.191,-1.692 -3.497,-2.159 6.883,-8.215 11.163,-19.471 12.136,-22.188 m -6.338,27.706 c -2.837,4.334 -6.709,9.107 -11.819,13.25 0.149,0.042 0.3,0.08 0.451,0.116 l 0.167,0.041 c 0.259,0.056 0.521,0.099 0.786,0.134 l 0.227,0.033 c 0.249,0.027 0.501,0.04 0.754,0.048 0.091,0.003 0.181,0.012 0.273,0.012 0.008,0 0.015,0 0.023,0 0.245,0 0.492,-0.016 0.74,-0.034 0.09,-0.006 0.18,-0.006 0.271,-0.015 0.344,-0.035 0.689,-0.086 1.035,-0.159 5.335,-1.117 8.754,-6.347 7.637,-11.682 -0.117,-0.56 -0.289,-1.092 -0.492,-1.606 l -0.034,-0.091 c -0.006,-0.016 -0.012,-0.031 -0.019,-0.047"
153 style="fill:#c74d1f;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.1"
154 id="path612"
155 inkscape:connector-curvature="0" /><path
156 d="m -3.61056,287.64581 c -4.095,4.142 -9.274,8.923 -14.808,12.906 0.616,0.847 1.361,1.585 2.198,2.193 v 0 c 0.269,0.196 0.548,0.378 0.835,0.545 l 0.017,0.01 c 0.278,0.161 0.564,0.309 0.857,0.443 l 0.053,0.025 c 0.278,0.125 0.562,0.236 0.852,0.335 l 0.105,0.038 c 0.124,0.04 0.248,0.078 0.373,0.114 5.11,-4.143 8.982,-8.916 11.819,-13.25 -0.207,-0.503 -0.456,-0.98 -0.737,-1.433 v -0.001 c -0.442,-0.711 -0.969,-1.357 -1.564,-1.925"
157 style="fill:#6f2717;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.1"
158 id="path614"
159 inkscape:connector-curvature="0" /><path
160 d="m -24.26856,296.79381 c -1.117,-5.335 2.303,-10.565 7.637,-11.682 5.335,-1.117 10.566,2.302 11.683,7.637 1.117,5.335 -2.302,10.565 -7.637,11.682 -5.335,1.117 -10.565,-2.302 -11.683,-7.637"
161 style="fill:#d4541e;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.1"
162 id="path616"
163 inkscape:connector-curvature="0" /><path
164 d="m -20.40556,302.74481 c 2.176,1.581 4.98,2.281 7.82,1.686 5.335,-1.117 8.754,-6.347 7.637,-11.682 -0.243,-1.162 -0.691,-2.225 -1.282,-3.178 -4.35,1.48 -10.934,5.007 -14.175,13.174"
165 style="fill:#852e1b;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.1"
166 id="path618"
167 inkscape:connector-curvature="0" /><path
168 d="m -19.98556,295.89681 c -0.622,-2.969 1.282,-5.881 4.251,-6.503 2.97,-0.622 5.881,1.282 6.503,4.251 0.622,2.97 -1.281,5.882 -4.251,6.504 -2.97,0.621 -5.881,-1.282 -6.503,-4.252"
169 style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.1"
170 id="path620"
171 inkscape:connector-curvature="0" /><path
172 d="m -95.60856,258.50681 -4.852,4.94 c 0,0 15.465,31.449 36.75,38.724 21.286,7.276 38.024,-3.64 48.394,-12.554 10.37,-8.915 16.373,-26.926 16.373,-26.926 l -3.82,-4.184 h -92.845"
173 style="fill:#f46a2f;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.1"
174 id="path622"
175 inkscape:connector-curvature="0" /><path
176 d="m -71.12356,289.57081 c 0,0 17.435,13.737 20.853,13.737 3.419,0 20.078,-12.054 20.078,-12.054 l -2.762,-3.317 c 0,0 -14.548,11.807 -17.316,11.807 -2.768,0 -16.459,-11.807 -16.459,-11.807 l -4.394,1.634"
177 style="fill:#852e1b;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.1"
178 id="path624"
179 inkscape:connector-curvature="0" /><path
180 d="m -62.48456,284.29281 c 0,0 10.016,8.491 12.885,8.491 2.869,0 12.401,-7.399 12.401,-7.399 l -2.597,-5.067 c 0,0 -7.579,8.22 -9.804,8.22 -2.225,0 -10.777,-7.641 -10.777,-7.641 l -2.108,3.396"
181 style="fill:#852e1b;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.1"
182 id="path626"
183 inkscape:connector-curvature="0" /><path
184 d="m -100.46056,263.44681 13.49,4.52 c 0,0 -4.302,14.009 8.251,20.74 12.553,6.731 18.102,-0.182 19.83,-4.184 1.729,-4.003 1.729,-14.555 1.729,-14.555 h 14.954 c 0,0 -0.157,15.707 6.271,20.194 6.428,4.488 16.095,5.094 19.873,-3.274 3.778,-8.369 0,-15.646 0,-15.646 l 17.119,-8.551 -3.82,-4.184 h -92.845 l -4.852,4.94"
185 style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.1"
186 id="path628"
187 inkscape:connector-curvature="0" /><path
188 d="m -60.78656,277.91081 c 0,-3.282 -2.66,-5.943 -5.943,-5.943 -3.282,0 -5.943,2.661 -5.943,5.943 0,3.282 2.661,5.943 5.943,5.943 3.283,0 5.943,-2.661 5.943,-5.943"
189 style="fill:#fddd04;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.1"
190 id="path630"
191 inkscape:connector-curvature="0" /><path
192 d="m -62.48456,277.91081 c 0,-2.344 -1.901,-4.245 -4.245,-4.245 -2.344,0 -4.244,1.901 -4.244,4.245 0,2.344 1.9,4.244 4.244,4.244 2.344,0 4.245,-1.9 4.245,-4.244"
193 style="fill:#391913;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.1"
194 id="path632"
195 inkscape:connector-curvature="0" /><path
196 d="m -67.62156,280.55381 c 0,-0.967 -0.784,-1.751 -1.751,-1.751 -0.967,0 -1.751,0.784 -1.751,1.751 0,0.967 0.784,1.751 1.751,1.751 0.967,0 1.751,-0.784 1.751,-1.751"
197 style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.1"
198 id="path634"
199 inkscape:connector-curvature="0" /><path
200 d="m -27.01156,277.91081 c 0,-3.282 -2.661,-5.943 -5.943,-5.943 -3.282,0 -5.943,2.661 -5.943,5.943 0,3.282 2.661,5.943 5.943,5.943 3.282,0 5.943,-2.661 5.943,-5.943"
201 style="fill:#fddd04;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.1"
202 id="path636"
203 inkscape:connector-curvature="0" /><path
204 d="m -28.70956,277.91081 c 0,-2.344 -1.901,-4.245 -4.245,-4.245 -2.344,0 -4.244,1.901 -4.244,4.245 0,2.344 1.9,4.244 4.244,4.244 2.344,0 4.245,-1.9 4.245,-4.244"
205 style="fill:#391913;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.1"
206 id="path638"
207 inkscape:connector-curvature="0" /><path
208 d="m -33.84656,280.55381 c 0,-0.967 -0.784,-1.751 -1.752,-1.751 -0.967,0 -1.751,0.784 -1.751,1.751 0,0.967 0.784,1.751 1.751,1.751 0.968,0 1.752,-0.784 1.752,-1.751"
209 style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.1"
210 id="path640"
211 inkscape:connector-curvature="0" /><path
212 d="m 13.79144,258.50681 h -12.371 l 3.821,4.184 c 0,0 -0.072,0.214 -0.213,0.608 h 8.763 l 11.536,14.197 c 0.723,-1.989 1.073,-3.206 1.078,-3.222 v 0 0 l -12.614,-15.767"
213 style="fill:#c74d1f;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.1"
214 id="path642"
215 inkscape:connector-curvature="0" /><path
216 d="m 1.42044,258.50681 h -4.184 l 3.82,4.184 c 0,0 -0.071,0.214 -0.212,0.608 h 4.184 c 0.141,-0.394 0.213,-0.608 0.213,-0.608 l -3.821,-4.184"
217 style="fill:#a33f1e;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.1"
218 id="path644"
219 inkscape:connector-curvature="0" /><path
220 d="m 1.05644,262.69081 -1.217,0.608 h 1.005 c 0.141,-0.394 0.212,-0.608 0.212,-0.608 v 0"
221 style="fill:#de581d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.1"
222 id="path646"
223 inkscape:connector-curvature="0" /><path
224 d="m -2.76356,258.50681 h -92.845 l -4.706,4.792 h 100.154 l 1.217,-0.608 -3.82,-4.184"
225 style="fill:#d5d0cd;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.1"
226 id="path648"
227 inkscape:connector-curvature="0" /><path
228 d="m -58.97456,271.48281 c 0,0 -5.68,-1.462 -7.755,-6.855 -2.075,-5.394 0.745,-9.305 5.475,-10.397 4.731,-1.091 11.553,2.456 11.553,2.456 0,0 5.602,-5.458 12.352,-4.093 6.75,1.364 8.934,9.096 5.659,13.736 -3.275,4.639 -11.28,5.153 -11.28,5.153 0,0 -12.179,-3.488 -16.004,0"
229 style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.1"
230 id="path650"
231 inkscape:connector-curvature="0" /><path
232 d="m -49.70156,256.68681 c 0,0 0.282,-0.27 0.773,-0.664 v 8.605 c 0,0.37 -0.301,0.671 -0.671,0.671 -0.369,0 -0.671,-0.301 -0.671,-0.671 v -8.217 c 0.356,0.167 0.569,0.276 0.569,0.276"
233 style="fill:#a6b9b5;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.1"
234 id="path652"
235 inkscape:connector-curvature="0" /><path
236 d="m -42.30556,265.56381 c 0,0 4.394,5.176 1.554,7.403 -2.841,2.228 -5.535,-1.611 -9.101,-1.267 -3.566,0.344 -7.301,3.095 -8.825,0.572 -1.523,-2.522 2.303,-5.562 2.303,-5.562 0,0 0.908,2.659 3.196,1.379 2.289,-1.28 0.361,-3.99 0.361,-3.99 0,0 1.685,-1.104 3.059,-1.331 1.374,-0.228 3.509,1.549 3.509,1.549 0,0 -1.109,3.074 1.246,4.262 2.356,1.189 2.698,-3.015 2.698,-3.015"
237 style="fill:#852e1b;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.1"
238 id="path654"
239 inkscape:connector-curvature="0" /><path
240 d="m -85.90456,281.52781 c 0,0 -3.367,-4.374 -3.094,-10.832 0.273,-6.459 9.56,-12.189 9.56,-12.189 l -3.656,5.688 c -0.913,1.42 -0.603,3.3 0.717,4.353 l 5.659,4.513 c 0,0 -7.641,-2.456 -8.914,-0.637 -1.274,1.82 -0.272,9.104 -0.272,9.104"
241 style="fill:#852e1b;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.1"
242 id="path656"
243 inkscape:connector-curvature="0" /><path
244 d="m -15.45756,285.37981 c 0,0 -0.525,-5.679 -1.92,-8.498 -1.396,-2.82 -6.217,-1.547 -6.217,-1.547 0,0 5.367,-2.729 5.367,-4.457 0,-1.729 -3.639,-7.186 -3.639,-7.186 0,0 11.817,7.446 6.409,21.688"
245 style="fill:#852e1b;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.1"
246 id="path658"
247 inkscape:connector-curvature="0" /><path
248 d="m -86.02656,258.50681 c 0,0 -2.365,1.758 -3.882,4.245 -1.516,2.486 -2.001,4.002 -2.001,4.002 0,0 -0.182,-3.275 0.728,-5.458 0.91,-2.183 1.637,-2.789 1.637,-2.789 h 3.518"
249 style="fill:#852e1b;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.1"
250 id="path660"
251 inkscape:connector-curvature="0" /><path
252 d="m -13.74056,258.50681 c 0,0 2.209,1.81 4.124,5.185 1.915,3.374 2.608,7.791 2.608,7.791 0,0 0.909,-6.366 0.303,-9.095 -0.607,-2.729 -1.577,-3.881 -1.577,-3.881 h -5.458"
253 style="fill:#852e1b;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.1"
254 id="path662"
255 inkscape:connector-curvature="0" /><path
256 d="m -4.78756,314.17381 c -2.809,1.198 -5.822,2.093 -9.074,2.55 -2.707,0.38 -5.285,0.578 -7.739,0.628 4.835,-1.15 14.841,-4.264 22.793,-11.581 10.901,-10.029 15.146,-19.22 15.146,-19.22 0,0 -6.557,18.098 -21.126,27.623"
257 style="fill:#852e1b;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.1"
258 id="path664"
259 inkscape:connector-curvature="0" /><path
260 d="m 1.95644,301.95081 c 0,0 7.105,-5.586 11.35,-16.571 4.245,-10.985 4.124,-13.897 4.124,-13.897 l -18.072,22.682 12.008,-11.037 c 0,0 -0.486,6.443 -9.41,18.823"
261 style="fill:#852e1b;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.1"
262 id="path666"
263 inkscape:connector-curvature="0" /><path
264 id="path668"
265 style="fill:#f6854f;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.133333"
266 d="m 39.412109,296.11914 -21.023437,25.87109 v 59.92579 a 9.2970522,17.233561 0 0 1 5.419922,-3.23047 9.2970522,17.233561 0 0 1 5.197265,2.94336 l 10.40625,-18.88477 z"
267 transform="matrix(0.75000002,0,0,-0.75000002,0,500.00002)"
268 inkscape:connector-curvature="0" /><path
269 d="m -3.30956,240.43481 h 7.034 v 5.97 h 3.188 l -6.705,7.626 -6.705,-7.626 h 3.188 v -5.97"
270 style="fill:#852e1b;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.1"
271 id="path670"
272 inkscape:connector-curvature="0" /><path
273 d="m 0.20744,238.61981 h -6.705 v -2.309 h 13.41 v 2.309 h -6.705 v 0"
274 style="fill:#852e1b;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.1"
275 id="path672"
276 inkscape:connector-curvature="0" /><path
277 d="m -4.28656,310.01981 c -3.321,1.214 -7.933,2.459 -13.091,2.58 -0.665,0.016 -1.339,0.023 -2.015,0.023 -2.736,0 -5.535,-0.12 -8.147,-0.292 -1.365,0.475 -2.782,0.899 -4.25,1.261 3.254,0.749 7.275,1.374 11.627,1.374 1.151,0 2.323,-0.044 3.511,-0.14 1.663,-0.135 3.198,-0.364 4.61,-0.658 2.489,-1.076 5.144,-2.437 7.755,-4.148"
278 style="fill:#f06c34;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.1"
279 id="path674"
280 inkscape:connector-curvature="0" /><path
281 d="m -39.79556,311.08381 c 0,0 3.151,1.392 8.006,2.508 1.468,-0.362 2.885,-0.786 4.25,-1.261 -6.75,-0.447 -12.256,-1.247 -12.256,-1.247"
282 style="fill:#94401d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.1"
283 id="path676"
284 inkscape:connector-curvature="0" /><path
285 d="m 1.05644,307.62781 c 0,0 -2.028,1.18 -5.343,2.392 -2.611,1.711 -5.266,3.072 -7.755,4.148 9.116,-1.899 13.098,-6.54 13.098,-6.54"
286 style="fill:#94401d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.1"
287 id="path678"
288 inkscape:connector-curvature="0" /><path
289 id="path1131"
290 style="display:inline;fill:#e66001;fill-opacity:1;fill-rule:evenodd;stroke-width:0.69136697"
291 d="m -46.403322,368.70754 a 93.750002,93.750002 0 0 1 -93.749998,-93.75001 93.750002,93.750002 0 0 1 93.749998,-93.75 93.750002,93.750002 0 0 1 93.750003,93.75 93.750002,93.750002 0 0 1 -93.750003,93.75001 z m 0,-18.75 a 75,75 0 0 0 75.000002,-75.00001 75,75 0 0 0 -75.000002,-75 75,75 0 0 0 -74.999998,75 75,75 0 0 0 74.999998,75.00001 z"
292 inkscape:connector-curvature="0" /><path
293 d="m -31.35356,340.33881 c -1.686,2.454 -0.944,5.845 -0.946,5.845 0.745,2.922 -1.02,5.895 -3.942,6.64 -2.923,0.74 -5.896,-1.021 -6.64,-3.943 -0.2,-0.781 -1.757,-7.578 1.972,-13.834 2.907,0.545 7.163,1.779 10.204,4.534 -0.24,0.239 -0.463,0.488 -0.648,0.758"
294 style="display:inline;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.1"
295 id="path586"
296 inkscape:connector-curvature="0" /></g></g></svg>
diff --git a/data/homer/svg/pywttr-docker.svg b/data/homer/svg/pywttr-docker.svg
new file mode 100644
index 0000000..1907757
--- /dev/null
+++ b/data/homer/svg/pywttr-docker.svg
@@ -0,0 +1,101 @@
1<?xml version="1.0" encoding="UTF-8" standalone="no"?>
2<svg
3 xmlns:osb="http://www.openswatchbook.org/uri/2009/osb"
4 xmlns:dc="http://purl.org/dc/elements/1.1/"
5 xmlns:cc="http://creativecommons.org/ns#"
6 xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
7 xmlns:svg="http://www.w3.org/2000/svg"
8 xmlns="http://www.w3.org/2000/svg"
9 xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
10 xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
11 version="1.1"
12 id="Layer_1"
13 x="0px"
14 y="0px"
15 viewBox="0 0 1067.1616 1067.1616"
16 xml:space="preserve"
17 sodipodi:docname="pywttr-docker.svg"
18 inkscape:export-filename="/home/sam/pywttr.png"
19 inkscape:export-xdpi="96"
20 inkscape:export-ydpi="96"
21 inkscape:version="1.0.2 (e86c870879, 2021-01-15)"
22 width="1067.1616"
23 height="1067.1616"><metadata
24 id="metadata98"><rdf:RDF><cc:Work
25 rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
26 rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title /></cc:Work></rdf:RDF></metadata><defs
27 id="defs96"><linearGradient
28 id="linearGradient2923"
29 osb:paint="solid"><stop
30 style="stop-color:#88c0d0;stop-opacity:1;"
31 offset="0"
32 id="stop2921" /></linearGradient><linearGradient
33 id="linearGradient2879"
34 osb:paint="solid"><stop
35 style="stop-color:#242930;stop-opacity:1;"
36 offset="0"
37 id="stop2877" /></linearGradient><linearGradient
38 id="linearGradient2873"
39 osb:paint="solid"><stop
40 style="stop-color:#242930;stop-opacity:1;"
41 offset="0"
42 id="stop2871" /></linearGradient></defs><sodipodi:namedview
43 pagecolor="#ffffff"
44 bordercolor="#666666"
45 borderopacity="1"
46 objecttolerance="10"
47 gridtolerance="10"
48 guidetolerance="10"
49 inkscape:pageopacity="0"
50 inkscape:pageshadow="2"
51 inkscape:window-width="1900"
52 inkscape:window-height="1042"
53 id="namedview94"
54 showgrid="false"
55 inkscape:zoom="0.84909543"
56 inkscape:cx="513.73187"
57 inkscape:cy="533.2709"
58 inkscape:window-x="10"
59 inkscape:window-y="28"
60 inkscape:window-maximized="0"
61 inkscape:current-layer="Layer_1"
62 lock-margins="false"
63 showguides="false"
64 inkscape:document-rotation="0" />
65
66<g
67 inkscape:groupmode="layer"
68 id="layer2"
69 inkscape:label="bottom"
70 transform="translate(21.580829,21.580829)" /><g
71 inkscape:groupmode="layer"
72 id="layer1"
73 inkscape:label="top"
74 transform="translate(21.580829,21.580829)" /><path
75 d="m 21.580799,803.51777 c 0,44.01146 8.38095,84.59981 25.14285,121.76504 16.76191,37.16523 39.61905,66.50621 68.571431,88.02289 28.95239,21.5167 60.19048,32.2751 94.09525,32.2751 h 412.57142 c 33.90476,0 65.52381,-10.7584 94.47619,-32.2751 28.95239,-21.51668 52.19048,-50.85766 68.95239,-88.02289 16.7619,-37.16523 25.5238,-77.75358 25.5238,-121.76504 0,-32.27508 -5.33333,-65.03917 -16,-97.80326 28.95238,-44.98949 43.42858,-99.27029 43.42858,-161.37535 0,-34.72015 -5.33334,-67.97326 -15.61905,-99.75932 -10.28572,-31.78605 -24.76191,-58.68195 -42.66667,-81.66571 -17.90476,-22.98376 -38.85714,-41.56637 -63.61905,-54.76982 -24.7619,-13.69245 -50.66667,-20.04966 -77.71428,-20.04966 -56.38095,0 -105.52381,28.36294 -147.80953,85.08883 -29.33333,-21.51672 -63.61904,-32.27508 -102.85714,-32.27508 -53.71428,0 -100.95238,21.51672 -142.09524,64.06113 -41.14286,42.54441 -67.80952,97.31424 -79.23809,163.82043 -42.66667,12.71443 -77.333341,40.58835 -104.380961,84.59982 -27.04762,44.01146 -40.7619,93.89112 -40.7619,150.12799 z m 65.14285,0 c 0,-41.07737 10.66667,-76.28654 32.000001,-106.11653 21.33334,-29.82999 48.00001,-46.94555 80.00001,-51.83572 l 19.04761,-1.46705 c 4.57144,0 7.2381,-2.9341 7.2381,-8.8023 l 2.66667,-26.40687 c 5.33333,-52.81375 23.23809,-97.31423 53.71429,-132.5234 30.47618,-35.69818 66.28571,-53.30277 107.04761,-53.30277 41.90477,0 78.47619,18.0936 109.33333,53.79179 31.2381,35.69818 48.38096,79.70964 52.19048,132.52339 l 2.66667,28.36295 c 0.76196,5.37918 3.42857,8.31327 8,8.31327 h 61.33333 c 33.52381,0 62.47619,15.64853 86.85714,46.94556 24.38096,31.29704 36.57144,67.97326 36.57144,111.00669 0,44.50048 -12.19048,82.15473 -36.19048,113.45176 -24,31.29705 -53.33334,46.94556 -86.85714,46.94556 H 209.39033 c -33.52382,0 -62.09524,-15.64851 -86.4762,-47.43457 C 98.914129,885.18348 86.723649,847.52923 86.723649,803.51777 Z M 341.96175,203.49484 c 0,11.73639 3.04762,22.00573 9.14286,30.80802 l 25.14286,31.29704 c 9.5238,9.29131 17.5238,13.20344 24.38095,12.22541 8,0 14.85714,-4.40115 20.95238,-12.71442 6.09524,-8.31328 9.14286,-18.58262 9.14286,-30.31902 0,-11.73638 -3.42857,-21.5167 -9.90477,-28.85195 l -22.47618,-32.27507 c -6.85715,-7.82426 -14.4762,-11.73639 -23.2381,-11.73639 -9.14286,0 -17.14286,3.91213 -23.61905,12.2254 -6.47619,7.82426 -9.52381,17.60459 -9.52381,29.34098 z m 203.42858,216.63419 c 26.28571,-32.76408 57.5238,-48.90162 93.33333,-48.90162 37.71428,0 69.71428,16.62655 96,50.36867 26.28571,33.74212 39.61905,74.33047 39.61905,122.74308 0,30.319 -6.4762,60.63801 -19.42858,89.97898 C 717.96175,587.37258 673.77127,563.8998 621.96175,563.8998 h -12.19048 c -9.5238,-53.30276 -30.85714,-101.22635 -64.38094,-143.77077 z m 62.09523,-258.20057 c 0,12.71442 3.04762,22.49475 8.76191,30.31901 5.71428,7.82426 13.33333,11.24737 22.47619,11.24737 9.90476,0 17.52381,-3.91213 23.61905,-11.24737 6.09523,-7.82426 8.7619,-17.60459 8.7619,-30.31901 V 61.680129 c 0,-11.73639 -3.04762,-21.0277 -9.14286,-28.85196 -6.09524,-7.82426 -13.71428,-11.24737 -23.23809,-11.24737 -9.14286,0 -16.38095,3.91213 -22.47619,11.24737 -6.09524,7.33525 -8.76191,17.11557 -8.76191,28.85196 z m 210.28571,111.98472 c 0,12.71442 2.66667,22.49474 8.38096,30.319 8,7.82426 16,11.73639 23.61904,11.73639 6.85715,0 14.4762,-3.91213 22.4762,-11.73639 l 54.47619,-69.92932 c 6.09523,-8.80229 9.14285,-19.07163 9.14285,-31.29704 0,-11.73638 -3.04762,-21.51671 -9.14285,-29.34097 -6.09524,-7.82426 -13.71429,-11.73639 -22.47619,-11.73639 -9.14286,0 -16.38096,3.91213 -22.09524,11.73639 l -56,69.92932 c -5.71429,9.29131 -8.38096,19.07163 -8.38096,30.31901 z m 30.09524,578.99522 c 0,11.73639 3.04762,22.00573 9.52382,30.80802 l 24.7619,30.80803 c 5.71428,7.82426 12.95238,11.73639 22.09524,11.73639 9.14286,0 16.7619,-3.91213 22.85714,-12.2254 6.09524,-8.31329 9.14286,-18.09361 9.14286,-30.31902 0,-10.75836 -3.04762,-20.53867 -9.14286,-28.36293 l -24.7619,-31.78606 c -6.09524,-7.82426 -13.33334,-11.73639 -21.71429,-11.73639 -9.14286,0 -16.76191,3.91213 -22.85715,11.73639 -6.47618,7.82426 -9.90476,17.60458 -9.90476,29.34097 z m 56,-308.56924 c 0,11.24737 3.42858,20.53867 9.90476,28.36294 6.09524,7.82426 14.09524,11.73639 23.2381,11.73639 h 77.71433 c 8.7619,0 16,-3.91213 22.0952,-11.24737 6.0952,-7.33525 8.7619,-17.11557 8.7619,-28.85196 0,-11.73639 -3.0476,-21.51672 -8.7619,-29.34098 -5.7143,-7.82426 -13.3333,-12.2254 -22.0952,-12.2254 h -77.71433 c -9.14286,0 -16.7619,3.91213 -23.2381,12.2254 -6.85714,8.31328 -9.90476,18.09361 -9.90476,29.34098 z"
76 id="path12687"
77 style="fill:#81a1c1;fill-opacity:1;stroke:none;stroke-width:43.1616;stroke-opacity:1" /><path
78 style="opacity:0.999;fill:#666666;fill-opacity:1;stroke:#eceff4;stroke-width:1.17701;stroke-opacity:1"
79 d="m 196.65811,941.29764 c -16.75405,-2.82081 -28.67914,-9.48982 -43.98743,-24.5996 -22.32205,-22.03258 -35.45294,-48.07729 -41.24203,-81.8023 -2.09086,-12.18051 -2.33588,-17.28253 -1.78053,-37.07586 0.67302,-23.98793 1.86008,-31.75958 7.46079,-48.84598 8.17008,-24.92489 25.34872,-50.27481 43.12419,-63.63688 15.55156,-11.69031 30.87095,-17.20429 52.02356,-18.72508 13.85632,-0.99623 21.31733,-3.74165 26.63856,-9.80221 5.79496,-6.60009 6.88909,-10.72851 9.84994,-37.16616 3.22035,-28.75472 6.27757,-44.61345 11.9364,-61.91785 15.14062,-46.29914 47.76577,-86.73479 83.04532,-102.92643 28.23901,-12.96037 62.77016,-12.78707 91.12482,0.45733 13.17982,6.15626 22.76145,12.91833 35.02484,24.71815 24.33948,23.41943 40.16916,49.74769 49.5682,82.44281 4.64289,16.15055 6.90841,30.02238 9.41919,57.67356 2.3961,26.38834 3.8197,31.43256 10.94489,38.78078 8.75275,9.02675 11.36321,9.47344 55.48446,9.49409 28.98999,0.0135 38.81517,0.40838 44.70325,1.79629 37.331,8.79948 71.73262,52.6058 80.84025,102.94034 2.77935,15.36043 2.77328,46.99704 -0.0121,62.97011 -8.80946,50.51911 -40.15065,92.28739 -77.84591,103.74494 -6.62987,2.01516 -18.39016,2.13741 -223.5681,2.32396 -121.11194,0.11017 -219.29517,-0.26189 -222.75258,-0.84401 z"
80 id="path12697" /><path
81 style="opacity:0.999;fill:#ffcc00;fill-opacity:1;stroke:#eceff4;stroke-width:1.17701;stroke-opacity:1"
82 d="M 737.20379,584.51156 C 708.9601,560.53498 671.95473,545.01521 637.6289,542.75064 l -10.12201,-0.66778 -2.11115,-8.95503 c -5.00052,-21.21104 -17.92057,-54.5989 -29.20232,-75.46427 -2.62516,-4.85517 -8.7061,-15.07915 -13.51321,-22.71994 l -8.74019,-13.89235 4.53763,-4.04057 c 10.93943,-9.7411 25.62497,-17.7147 39.25947,-21.31616 10.09528,-2.66659 31.79833,-2.68574 42.6431,-0.0376 41.24887,10.07231 76.3477,53.15163 88.21849,108.27684 3.55615,16.514 3.86382,57.77621 0.5611,75.25373 -1.19245,6.31035 -2.51659,11.66349 -2.94253,11.89587 -0.42592,0.23238 -4.482,-2.72493 -9.01349,-6.5718 z"
83 id="path12699" /><path
84 style="opacity:0.999;fill:#ffcc00;fill-opacity:1;stroke-width:1.28169"
85 d="m 389.12237,277.06322 c -2.73792,-1.12521 -7.00894,-3.81543 -9.49114,-5.97827 -4.59887,-4.00717 -20.36707,-23.87982 -29.92577,-37.71545 -14.08387,-20.38554 -11.12764,-50.26556 6.36789,-64.3636 6.9859,-5.62932 11.55743,-7.45202 19.528,-7.78597 14.67891,-0.61502 21.47199,5.07502 39.30886,32.92622 15.68942,24.49807 18.30249,31.12059 17.347,43.96422 -2.00884,27.00292 -23.96554,46.83089 -43.13484,38.95285 z"
86 id="path121" /><path
87 style="opacity:0.999;fill:#d7f4d7;stroke-width:1.22662"
88 d="m 626.16169,199.63282 c -6.80727,-3.44381 -12.78844,-11.23507 -15.59975,-20.32077 -1.93553,-6.25529 -2.15717,-13.21009 -2.15717,-67.6894 0,-70.513451 -0.16671,-69.297449 11.03684,-80.501001 15.35345,-15.353452 36.55513,-10.647113 46.89597,10.409943 l 3.39832,6.92 0.36082,61.239478 c 0.34376,58.34272 0.24455,61.6223 -2.09736,69.33253 -2.91697,9.60347 -8.87673,17.14671 -16.44004,20.80804 -7.33862,3.55257 -18.14966,3.46793 -25.39763,-0.19882 z"
89 id="path123" /><path
90 style="opacity:0.999;fill:#ffcc00;fill-opacity:1;stroke-width:1.31751"
91 d="m 632.93895,205.57929 c -10.85946,-1.99606 -17.41207,-7.52645 -23.5076,-19.84036 -3.11225,-6.28722 -3.11251,-6.29329 -3.11251,-71.77263 0,-48.780309 0.43561,-66.81809 1.70766,-70.711276 2.21715,-6.7857 11.09464,-16.108288 18.53316,-19.462361 16.03437,-7.229999 32.94743,-0.317067 42.41167,17.335059 l 3.55649,6.633372 v 64.024816 c 0,70.84152 -0.13843,72.16562 -8.63083,82.54988 -7.38254,9.02717 -19.35655,13.37595 -30.95804,11.2435 z"
92 id="path125" /><path
93 style="opacity:0.999;fill:#ffcc00;fill-opacity:1;stroke-width:1.28305"
94 d="m 837.93973,314.28029 c -3.61442,-1.67007 -8.6609,-5.10969 -11.21442,-7.64359 -12.3899,-12.29474 -13.36539,-46.38379 -1.79521,-62.73548 1.51221,-2.13713 16.53568,-20.98527 33.38549,-41.88474 33.73357,-41.84105 35.40097,-43.34412 48.08288,-43.34412 8.90894,0 14.72713,2.83399 21.76488,10.60154 7.92077,8.74215 10.39321,16.52736 10.28898,32.39803 -0.12624,19.21828 -1.0701,20.95432 -34.74466,63.90596 -15.98287,20.38601 -30.89165,38.94395 -33.13063,41.23988 -5.30184,5.43669 -15.7084,10.52722 -21.48849,10.51146 -2.51744,-0.006 -7.53441,-1.37891 -11.14882,-3.04894 z"
95 id="path127" /><path
96 style="opacity:0.999;fill:#ffcc00;fill-opacity:1;stroke-width:1.26584"
97 d="m 926.35874,583.75449 c -6.60114,-2.35822 -15.29926,-11.13844 -19.48832,-19.67228 -3.02508,-6.16258 -3.42027,-8.4311 -3.42027,-19.63343 0,-10.42548 0.49877,-13.74752 2.81981,-18.7815 4.0349,-8.75108 9.80932,-15.55164 16.59238,-19.54087 l 5.8896,-3.4638 43.01307,-0.40202 c 28.38429,-0.26531 45.18869,0.0772 49.41019,1.00721 10.7493,2.368 19.1883,11.13654 23.7573,24.6853 3.1265,9.2705 2.8335,26.54684 -0.606,35.74919 -3.3747,9.0285 -12.1897,18.22184 -19.5744,20.41427 -7.8609,2.33377 -91.71084,2.02521 -98.39336,-0.36207 z"
98 id="path129" /><path
99 style="opacity:0.999;fill:#ffcc00;fill-opacity:1;stroke-width:1.27243"
100 d="m 894.99812,925.80676 c -6.15575,-2.81221 -8.74894,-5.40461 -24.24213,-24.2349 -16.95932,-20.61218 -21.11978,-27.6022 -23.26993,-39.09604 -4.73655,-25.31957 11.29699,-51.04984 31.87591,-51.15384 6.34305,-0.0321 13.65947,2.67103 18.03526,6.66319 5.99717,5.47139 33.7231,41.4254 36.47966,47.30554 2.35343,5.02019 2.81037,8.25374 2.81037,19.88765 0,12.32583 -0.37608,14.68269 -3.33445,20.89674 -4.31447,9.06253 -10.7179,16.17179 -17.16547,19.05754 -5.78318,2.58839 -16.2688,2.92199 -21.18922,0.67412 z"
101 id="path131" /></svg>
diff --git a/data/homer/svg/radicale.svg b/data/homer/svg/radicale.svg
new file mode 100644
index 0000000..546d3d1
--- /dev/null
+++ b/data/homer/svg/radicale.svg
@@ -0,0 +1,10 @@
1<?xml version="1.0" encoding="UTF-8" standalone="no"?>
2<svg width="200" height="300" xmlns="http://www.w3.org/2000/svg">
3 <path fill="#a40000" d="M 186,188 C 184,98 34,105 47,192 C 59,279 130,296 130,296 C 130,296 189,277 186,188 z" />
4 <path fill="#ffffff" d="M 73,238 C 119,242 140,241 177,222 C 172,270 131,288 131,288 C 131,288 88,276 74,238 z" />
5 <g fill="none" stroke="#4e9a06" stroke-width="15">
6 <path d="M 103,137 C 77,69 13,62 13,62" />
7 <path d="M 105,136 C 105,86 37,20 37,20" />
8 <path d="M 105,135 C 112,73 83,17 83,17" />
9 </g>
10</svg>
diff --git a/data/homer/svg/searxng.svg b/data/homer/svg/searxng.svg
new file mode 100644
index 0000000..b94fe37
--- /dev/null
+++ b/data/homer/svg/searxng.svg
@@ -0,0 +1,56 @@
1<?xml version="1.0" encoding="UTF-8" standalone="no"?>
2<svg
3 xmlns:dc="http://purl.org/dc/elements/1.1/"
4 xmlns:cc="http://creativecommons.org/ns#"
5 xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
6 xmlns:svg="http://www.w3.org/2000/svg"
7 xmlns="http://www.w3.org/2000/svg"
8 id="svg8"
9 version="1.1"
10 viewBox="0 0 92 92"
11 height="92mm"
12 width="92mm">
13 <defs
14 id="defs2" />
15 <metadata
16 id="metadata5">
17 <rdf:RDF>
18 <cc:Work
19 rdf:about="">
20 <dc:format>image/svg+xml</dc:format>
21 <dc:type
22 rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
23 <dc:title></dc:title>
24 </cc:Work>
25 </rdf:RDF>
26 </metadata>
27 <g
28 transform="translate(-40.921303,-17.416526)"
29 id="layer1">
30 <circle
31 r="0"
32 style="fill:none;stroke:#000000;stroke-width:12;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
33 cy="92"
34 cx="75"
35 id="path3713" />
36 <circle
37 r="30"
38 cy="53.902557"
39 cx="75.921303"
40 id="path834"
41 style="fill:none;fill-opacity:1;stroke:#3050ff;stroke-width:10;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
42 <path
43 d="m 67.514849,37.91524 a 18,18 0 0 1 21.051475,3.312407 18,18 0 0 1 3.137312,21.078282"
44 id="path852"
45 style="fill:none;fill-opacity:1;stroke:#3050ff;stroke-width:5;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
46 <rect
47 transform="rotate(-46.234709)"
48 ry="1.8669105e-13"
49 y="122.08995"
50 x="3.7063529"
51 height="39.963303"
52 width="18.846331"
53 id="rect912"
54 style="opacity:1;fill:#3050ff;fill-opacity:1;stroke:none;stroke-width:8;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
55 </g>
56</svg>
diff --git a/data/homer/svg/sonarr.svg b/data/homer/svg/sonarr.svg
new file mode 100644
index 0000000..563b44f
--- /dev/null
+++ b/data/homer/svg/sonarr.svg
@@ -0,0 +1,9 @@
1<svg height="216.9" viewBox="0 0 216.7 216.9" width="216.7" xmlns="http://www.w3.org/2000/svg">
2 <path clip-rule="evenodd" d="M216.7 108.45c0 29.833-10.533 55.4-31.6 76.7-.7.833-1.483 1.6-2.35 2.3-3.466 3.4-7.133 6.484-11 9.25-18.267 13.467-39.367 20.2-63.3 20.2-23.967 0-45.033-6.733-63.2-20.2-4.8-3.4-9.3-7.25-13.5-11.55-16.367-16.266-26.417-35.167-30.15-56.7-.733-4.2-1.217-8.467-1.45-12.8-.1-2.4-.15-4.8-.15-7.2 0-2.533.05-4.95.15-7.25 0-.233.066-.467.2-.7 1.567-26.6 12.033-49.583 31.4-68.95C53.05 10.517 78.617 0 108.45 0c29.933 0 55.484 10.517 76.65 31.55 21.067 21.433 31.6 47.067 31.6 76.9z" fill="#EEE" fill-rule="evenodd"/>
3 <path clip-rule="evenodd" d="M194.65 42.5l-22.4 22.4C159.152 77.998 158 89.4 158 109.5c0 17.934 2.852 34.352 16.2 47.7 9.746 9.746 19 18.95 19 18.95-2.5 3.067-5.2 6.067-8.1 9-.7.833-1.483 1.6-2.35 2.3-2.533 2.5-5.167 4.817-7.9 6.95l-17.55-17.55c-15.598-15.6-27.996-17.1-48.6-17.1-19.77 0-33.223 1.822-47.7 16.3-8.647 8.647-18.55 18.6-18.55 18.6-3.767-2.867-7.333-6.034-10.7-9.5-2.8-2.8-5.417-5.667-7.85-8.6 0 0 9.798-9.848 19.15-19.2 13.852-13.853 16.1-29.916 16.1-47.85 0-17.5-2.874-33.823-15.6-46.55-8.835-8.836-21.05-21-21.05-21 2.833-3.6 5.917-7.067 9.25-10.4 2.934-2.867 5.934-5.55 9-8.05L61.1 43.85C74.102 56.852 90.767 60.2 108.7 60.2c18.467 0 35.077-3.577 48.6-17.1 8.32-8.32 19.3-19.25 19.3-19.25 2.9 2.367 5.733 4.933 8.5 7.7 3.467 3.533 6.65 7.183 9.55 10.95z" fill="#3A3F51" fill-rule="evenodd"/>
4 <g clip-rule="evenodd">
5 <path d="M78.7 114c-.2-1.167-.332-2.35-.4-3.55-.032-.667-.05-1.333-.05-2 0-.7.018-1.367.05-2 0-.067.018-.133.05-.2.435-7.367 3.334-13.733 8.7-19.1 5.9-5.833 12.984-8.75 21.25-8.75 8.3 0 15.384 2.917 21.25 8.75 5.834 5.934 8.75 13.033 8.75 21.3 0 8.267-2.916 15.35-8.75 21.25-.2.233-.416.45-.65.65-.966.933-1.982 1.783-3.05 2.55-5.065 3.733-10.916 5.6-17.55 5.6s-12.466-1.866-17.5-5.6c-1.332-.934-2.582-2-3.75-3.2-4.532-4.5-7.316-9.734-8.35-15.7z" fill="#0CF" fill-rule="evenodd"/>
6 <path d="M157.8 59.75l-15 14.65M30.785 32.526L71.65 73.25m84.6 84.25l27.808 28.78m1.855-153.894L157.8 59.75m-125.45 126l27.35-27.4" fill="none" stroke="#0CF" stroke-miterlimit="1" stroke-width="2"/>
7 <path d="M157.8 59.75l-16.95 17.2M58.97 60.604l17.2 17.15M59.623 158.43l16.75-17.4m61.928-1.396l18.028 17.945" fill="none" stroke="#0CF" stroke-miterlimit="1" stroke-width="7"/>
8 </g>
9</svg>
diff --git a/data/homer/svg/text-generation-webui.svg b/data/homer/svg/text-generation-webui.svg
new file mode 100644
index 0000000..70cc7a1
--- /dev/null
+++ b/data/homer/svg/text-generation-webui.svg
@@ -0,0 +1,10 @@
1<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
2 <path d="M8.03076 0.0166016L15.9999 4.38717V6.93362L8.03076 2.56305V0.0166016Z" fill="#FF7C00" fill-opacity="0.75"/>
3 <path d="M15.9974 4.38672L7.99902 8.77375V11.3366L15.9974 6.94955V4.38672Z" fill="#FF7C00"/>
4 <path d="M0 4.38672L7.9991 8.77375V11.3366L0 6.94957V4.38672Z" fill="#FF7C00" fill-opacity="0.75"/>
5 <path d="M8.03008 0L0.000976562 4.38697V6.94982L8.03008 2.56286V0Z" fill="#FF7C00"/>
6 <path d="M8.03076 4.43604L15.9999 8.80659V11.353L8.03076 6.98248V4.43604Z" fill="#FF7C00" fill-opacity="0.75"/>
7 <path d="M15.9974 8.80664L7.99902 13.1937V15.7565L15.9974 11.3695V8.80664Z" fill="#FF7C00"/>
8 <path d="M0 8.80664L7.9991 13.1937V15.7565L0 11.3695V8.80664Z" fill="#FF7C00" fill-opacity="0.75"/>
9 <path d="M8.03008 4.41992L0.000976562 8.8069V11.3697L8.03008 6.98279V4.41992Z" fill="#FF7C00"/>
10</svg>
diff --git a/data/homer/svg/thanos.svg b/data/homer/svg/thanos.svg
new file mode 100644
index 0000000..9c2223c
--- /dev/null
+++ b/data/homer/svg/thanos.svg
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" role="img" viewBox="2.41 2.16 355.42 355.17"><path fill="#6d41ff" d="M9.49808 9.14822v341.59786h241.93485a99.66306 99.66306 0 0 0 99.657-99.663V9.14822zm266.263 215.32837h12.75033v12.75018h-12.75035zm-4.00963-54.89h20.71531v20.72131h-20.71533zm-3.186-54.10261h27.09335v27.11729h-27.09343zm-43.739 159.90321h12.7381v12.75018h-12.75017zm-3.98558-55.53932h20.72129V240.587h-20.72129zm-3.186-53.44731h27.12344v27.09337h-27.09339zm3.186-27.00322v-20.71528h20.72129v20.71527zm-97.82954 135.98981h12.75021v12.75018h-12.75021zm-3.98558-54.8901h20.72131v20.69121h-20.72124zm3.98558-34.1748v-12.75019h12.75021v12.75018zm-3.98558-67.64019h20.72131v20.71527h-20.72124zM72.10081 275.38715H84.875v12.75018H72.10081zm0-50.91056H84.875v12.75018H72.10081zm-3.97955-54.89h20.7153v20.72131h-20.7153zm-3.19206-54.10265h27.09939v27.11729H64.9292zM53.01459 52.67679h254.53469v50.91052H205.74622v203.6302h-50.90449v-203.6302H53.01459z"/></svg> \ No newline at end of file
diff --git a/data/homer/svg/xbrowsersync.svg b/data/homer/svg/xbrowsersync.svg
new file mode 100644
index 0000000..f7c2b01
--- /dev/null
+++ b/data/homer/svg/xbrowsersync.svg
@@ -0,0 +1,168 @@
1<?xml version="1.0" encoding="UTF-8" standalone="no"?>
2<svg
3 version="1.1"
4 viewBox="0 0 440.104 552.999"
5 id="svg78"
6 sodipodi:docname="xbrowsersync.svg"
7 inkscape:export-filename="/home/nx211/Documents/xbrowsersync.png"
8 inkscape:export-xdpi="134.15691"
9 inkscape:export-ydpi="134.15691"
10 inkscape:version="1.1.1 (eb90963e84, 2021-10-02)"
11 xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
12 xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
13 xmlns="http://www.w3.org/2000/svg"
14 xmlns:svg="http://www.w3.org/2000/svg">
15 <defs
16 id="defs82" />
17 <sodipodi:namedview
18 id="namedview80"
19 pagecolor="#ffffff"
20 bordercolor="#666666"
21 borderopacity="1.0"
22 inkscape:pageshadow="2"
23 inkscape:pageopacity="0.0"
24 inkscape:pagecheckerboard="0"
25 showgrid="false"
26 inkscape:zoom="1.3001831"
27 inkscape:cx="121.9059"
28 inkscape:cy="281.88337"
29 inkscape:window-width="1920"
30 inkscape:window-height="1016"
31 inkscape:window-x="0"
32 inkscape:window-y="0"
33 inkscape:window-maximized="1"
34 inkscape:current-layer="svg78" />
35 <polygon
36 points="306.04,147.19 204.53,88.193 204.36,29.494 305.88,88.487 "
37 fill="#0a323d"
38 id="polygon24"
39 transform="translate(44,28)" />
40 <path
41 id="polygon26"
42 d="m 249.0293,292.32031 0.16015,58.69922 101.52149,58.99024 -0.17188,-58.69922 z" />
43 <polygon
44 points="255.46,352.81 153.94,293.81 153.78,235.11 255.29,294.11 "
45 fill="#0a323d"
46 id="polygon28"
47 transform="translate(44,28)" />
48 <polygon
49 points="255.12,235.41 153.61,176.41 153.44,117.69 254.96,176.68 "
50 fill="#0a323d"
51 id="polygon30"
52 transform="translate(44,28)" />
53 <polygon
54 points="204.21,323.6 102.69,264.61 102.52,205.91 204.04,264.9 "
55 fill="#0a323d"
56 id="polygon32"
57 transform="translate(44,28)" />
58 <polygon
59 points="153.29,411.79 51.773,352.8 51.606,294.1 153.12,353.09 "
60 fill="#0a323d"
61 id="polygon34"
62 transform="translate(44,28)" />
63 <polygon
64 points="152.96,294.39 51.44,235.4 51.274,176.67 152.79,235.67 "
65 fill="#0a323d"
66 id="polygon36"
67 transform="translate(44,28)" />
68 <polygon
69 points="101.68,265.18 0.166,206.18 0,147.48 101.52,206.48 "
70 fill="#0a323d"
71 id="polygon38"
72 transform="translate(44,28)" />
73 <polygon
74 points="102.35,500 0.832,441.01 0.666,382.31 102.18,441.3 "
75 fill="#0a323d"
76 id="polygon40"
77 transform="translate(44,28)" />
78 <polygon
79 points="305.88,88.487 204.36,29.494 255.44,0 356.96,58.994 "
80 fill="#267d91"
81 id="polygon42"
82 transform="translate(44,28)" />
83 <polygon
84 points="306.54,323.31 205.03,264.32 256.11,234.82 357.62,293.82 "
85 fill="#267d91"
86 id="polygon44"
87 transform="translate(44,28)" />
88 <polygon
89 points="255.29,294.11 153.78,235.11 204.86,205.62 306.37,264.61 "
90 fill="#267d91"
91 id="polygon46"
92 transform="translate(44,28)" />
93 <polygon
94 points="254.96,176.68 153.44,117.69 204.53,88.193 306.04,147.19 "
95 fill="#267d91"
96 id="polygon48"
97 transform="translate(44,28)" />
98 <polygon
99 points="204.04,264.9 102.52,205.91 153.61,176.41 255.12,235.41 "
100 fill="#267d91"
101 id="polygon50"
102 transform="translate(44,28)" />
103 <polygon
104 points="153.12,353.09 51.606,294.1 102.69,264.61 204.21,323.6 "
105 fill="#267d91"
106 id="polygon52"
107 transform="translate(44,28)" />
108 <polygon
109 points="152.79,235.67 51.274,176.67 102.36,147.18 203.87,206.17 "
110 fill="#267d91"
111 id="polygon54"
112 transform="translate(44,28)" />
113 <polygon
114 points="101.52,206.48 0,147.48 51.107,117.97 152.62,176.97 "
115 fill="#267d91"
116 id="polygon56"
117 transform="translate(44,28)" />
118 <polygon
119 points="102.18,441.3 0.666,382.31 51.773,352.8 153.29,411.79 "
120 fill="#267d91"
121 id="polygon58"
122 transform="translate(44,28)" />
123 <polygon
124 points="356.96,58.994 357.12,117.69 306.04,147.19 305.88,88.487 "
125 fill="#71d2e2"
126 id="polygon60"
127 transform="translate(44,28)" />
128 <polygon
129 points="357.62,293.82 357.79,352.52 306.71,382.01 306.54,323.31 "
130 fill="#71d2e2"
131 id="polygon62"
132 transform="translate(44,28)" />
133 <polygon
134 points="306.37,264.61 306.54,323.31 255.46,352.81 255.29,294.11 "
135 fill="#71d2e2"
136 id="polygon64"
137 transform="translate(44,28)" />
138 <polygon
139 points="306.04,147.19 306.21,205.91 255.12,235.41 254.96,176.68 "
140 fill="#71d2e2"
141 id="polygon66"
142 transform="translate(44,28)" />
143 <polygon
144 points="255.12,235.41 255.29,294.11 204.21,323.6 204.04,264.9 "
145 fill="#71d2e2"
146 id="polygon68"
147 transform="translate(44,28)" />
148 <polygon
149 points="204.21,323.6 204.37,382.3 153.29,411.79 153.12,353.09 "
150 fill="#71d2e2"
151 id="polygon70"
152 transform="translate(44,28)" />
153 <polygon
154 points="203.87,206.17 204.04,264.9 152.96,294.39 152.79,235.67 "
155 fill="#71d2e2"
156 id="polygon72"
157 transform="translate(44,28)" />
158 <polygon
159 points="152.62,176.97 152.79,235.67 101.68,265.18 101.52,206.48 "
160 fill="#71d2e2"
161 id="polygon74"
162 transform="translate(44,28)" />
163 <polygon
164 points="153.29,411.79 153.45,470.49 102.35,500 102.18,441.3 "
165 fill="#71d2e2"
166 id="polygon76"
167 transform="translate(44,28)" />
168</svg>
diff --git a/data/influxdb/influxdb.conf b/data/influxdb/influxdb.conf
new file mode 100644
index 0000000..c5822ff
--- /dev/null
+++ b/data/influxdb/influxdb.conf
@@ -0,0 +1,30 @@
1reporting-enabled = false
2[meta]
3 dir = "/var/lib/influxdb/meta"
4[data]
5 dir = "/var/lib/influxdb/data"
6 wal-dir = "/var/lib/influxdb/wal"
7[coordinator]
8[retention]
9[shard-precreation]
10[monitor]
11[http]
12 enabled = true
13 bind-address = ":8086"
14 auth-enabled = true
15[ifql]
16[logging]
17[subscriber]
18[[graphite]]
19[[collectd]]
20[[opentsdb]]
21[[udp]]
22 enabled = true
23 bind-address = "0.0.0.0:8089"
24 database = "proxmox"
25 batch-size = 1000
26 batch-timeout = "1s"
27[continuous_queries]
28[tls]
29 min-version = "tls1.2"
30
diff --git a/data/invidious/invidious.conf.j2 b/data/invidious/invidious.conf.j2
new file mode 100644
index 0000000..816d880
--- /dev/null
+++ b/data/invidious/invidious.conf.j2
@@ -0,0 +1,34 @@
1server {
2 listen 443 ssl;
3 server_name {{ invidious_server_name }};
4
5 ssl_certificate /etc/letsencrypt/live/chudnick.com/fullchain.pem;
6 ssl_certificate_key /etc/letsencrypt/live/chudnick.com/privkey.pem;
7 add_header Strict-Transport-Security "max-age=31536000" always;
8 ssl_stapling on;
9 ssl_stapling_verify on;
10
11 # Security / XSS Mitigation Headers
12 add_header X-Frame-Options "SAMEORIGIN";
13 add_header X-XSS-Protection "1; mode=block";
14 add_header X-Content-Type-Options "nosniff";
15
16 # authelia
17 include /etc/nginx/snippets/authelia-location.conf;
18
19 location / {
20 #authelia
21 include /etc/nginx/snippets/proxy.conf;
22 include /etc/nginx/snippets/authelia-authrequest.conf;
23
24 proxy_pass http://127.0.0.1:{{ invidious_external_port }}/;
25 }
26
27}
28
29server {
30 listen 80;
31 listen [::]:80;
32 server_name {{ invidious_server_name }};
33 return 301 https://$host$request_uri;
34}
diff --git a/data/invidious/invidious.env b/data/invidious/invidious.env
new file mode 100644
index 0000000..3ef540b
--- /dev/null
+++ b/data/invidious/invidious.env
@@ -0,0 +1,11 @@
1INVIDIOUS_CONFIG: |
2db: |
3 dbname: invidious
4 user: kemal
5 password: changeme
6 host: invidious-db
7 port: 5432
8check_tables: true
9https_only: true
10hsts: true
11domain: invidious.chudnick.com
diff --git a/data/jellyfin/jellyfin.conf b/data/jellyfin/jellyfin.conf
new file mode 100644
index 0000000..01f5ea0
--- /dev/null
+++ b/data/jellyfin/jellyfin.conf
@@ -0,0 +1,68 @@
1server {
2 listen 443 ssl;
3 server_name jellyfin.chudnick.com;
4
5 client_max_body_size 20M;
6
7 # use a variable to store the upstream proxy
8 set $jellyfin 127.0.0.1;
9
10 ssl_certificate /etc/letsencrypt/live/chudnick.com/fullchain.pem;
11 ssl_certificate_key /etc/letsencrypt/live/chudnick.com/privkey.pem;
12 add_header Strict-Transport-Security "max-age=31536000" always;
13 ssl_stapling on;
14 ssl_stapling_verify on;
15
16 # Security / XSS Mitigation Headers
17 add_header X-Frame-Options "SAMEORIGIN";
18 add_header X-XSS-Protection "1; mode=block";
19 add_header X-Content-Type-Options "nosniff";
20
21 location = / {
22 return 302 https://$host/web/;
23 }
24
25 location / {
26 # Proxy main Jellyfin traffic
27 proxy_pass http://$jellyfin:8096;
28 proxy_set_header Host $host;
29 proxy_set_header X-Real-IP $remote_addr;
30 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
31 proxy_set_header X-Forwarded-Proto $scheme;
32 proxy_set_header X-Forwarded-Protocol $scheme;
33 proxy_set_header X-Forwarded-Host $http_host;
34 proxy_buffering off;
35 }
36
37 location = /web/ {
38 # Proxy main Jellyfin traffic
39 proxy_pass http://$jellyfin:8096/web/index.html;
40 proxy_set_header Host $host;
41 proxy_set_header X-Real-IP $remote_addr;
42 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
43 proxy_set_header X-Forwarded-Proto $scheme;
44 proxy_set_header X-Forwarded-Protocol $scheme;
45 proxy_set_header X-Forwarded-Host $http_host;
46 }
47
48 location /socket {
49 # Proxy Jellyfin Websockets traffic
50 proxy_pass http://$jellyfin:8096;
51 proxy_http_version 1.1;
52 proxy_set_header Upgrade $http_upgrade;
53 proxy_set_header Connection "upgrade";
54 proxy_set_header Host $host;
55 proxy_set_header X-Real-IP $remote_addr;
56 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
57 proxy_set_header X-Forwarded-Proto $scheme;
58 proxy_set_header X-Forwarded-Protocol $scheme;
59 proxy_set_header X-Forwarded-Host $http_host;
60 }
61}
62
63server {
64 listen 80;
65 listen [::]:80;
66 server_name jellyfin.chudnick.com;
67 return 301 https://$host$request_uri;
68}
diff --git a/data/jenkins/configuration.yml.j2 b/data/jenkins/configuration.yml.j2
new file mode 100644
index 0000000..c5f652d
--- /dev/null
+++ b/data/jenkins/configuration.yml.j2
@@ -0,0 +1,163 @@
1credentials:
2 system:
3 domainCredentials:
4 - credentials:
5 - basicSSHUserPrivateKey:
6 description: "SSH Key for Jenkins User in FreeIPA"
7 id: "jenkins_freeipa_ssh"
8 privateKeySource:
9 directEntry:
10 privateKey: "{{ jenkins_privkey }}"
11 scope: GLOBAL
12 username: "jenkins"
13 - string:
14 description: "Ansible Vault Credentials"
15 id: "ansible_vault"
16 scope: GLOBAL
17 secret: "{{ jenkins_vault }}"
18jenkins:
19 agentProtocols:
20 - "JNLP4-connect"
21 - "Ping"
22 authorizationStrategy:
23 loggedInUsersCanDoAnything:
24 allowAnonymousRead: true
25 crumbIssuer:
26 standard:
27 excludeClientIPFromCrumb: false
28 disableRememberMe: false
29 disabledAdministrativeMonitors:
30 - "jenkins.diagnostics.ControllerExecutorsNoAgents"
31 labelAtoms:
32 - name: "built-in"
33 markupFormatter: "plainText"
34 mode: NORMAL
35 myViewsTabBar: "standard"
36 noUsageStatistics: true
37 numExecutors: 1
38 primaryView:
39 all:
40 name: "all"
41 projectNamingStrategy: "standard"
42 quietPeriod: 5
43 remotingSecurity:
44 enabled: true
45 scmCheckoutRetryCount: 0
46 securityRealm:
47 oic:
48 authorizationServerUrl: "https://auth.chudnick.com/api/oidc/authorization"
49 automanualconfigure: "auto"
50 clientId: "jenkins"
51 clientSecret: "{{ jenkins_oic_secret }}"
52 disableSslVerification: false
53 escapeHatchEnabled: false
54 escapeHatchSecret: "{{ jenkins_oic_escapehatch }}"
55 logoutFromOpenidProvider: false
56 scopes: "openid offline_access profile groups email"
57 tokenAuthMethod: "client_secret_post"
58 tokenServerUrl: "https://auth.chudnick.com/api/oidc/token"
59 userInfoServerUrl: "https://auth.chudnick.com/api/oidc/userinfo"
60 userNameField: "sub"
61 wellKnownOpenIDConfigurationUrl: "https://auth.chudnick.com/.well-known/openid-configuration"
62 slaveAgentPort: -1
63 updateCenter:
64 sites:
65 - id: "default"
66 url: "https://updates.jenkins.io/update-center.json"
67 views:
68 - all:
69 name: "all"
70 viewsTabBar: "standard"
71globalCredentialsConfiguration:
72 configuration:
73 providerFilter: "none"
74 typeFilter: "none"
75security:
76 apiToken:
77 creationOfLegacyTokenEnabled: false
78 tokenGenerationOnCreationEnabled: false
79 usageStatisticsEnabled: true
80 gitHooks:
81 allowedOnAgents: false
82 allowedOnController: false
83 gitHostKeyVerificationConfiguration:
84 sshHostKeyVerificationStrategy: "knownHostsFileVerificationStrategy"
85 sSHD:
86 port: -1
87unclassified:
88 buildDiscarders:
89 configuredBuildDiscarders:
90 - "jobBuildDiscarder"
91 buildStepOperation:
92 enabled: false
93 fingerprints:
94 fingerprintCleanupDisabled: false
95 storage: "file"
96 scmGit:
97 addGitTagAction: false
98 allowSecondFetch: false
99 createAccountBasedOnEmail: false
100 disableGitToolChooser: false
101 hideCredentials: false
102 showEntireCommitSummaryInChanges: false
103 useExistingAccountWithSameEmail: false
104 giteaServers:
105 servers:
106 - displayName: "Local"
107 manageHooks: false
108 serverUrl: "https://gitea.chudnick.com"
109 globalTimeOutConfiguration:
110 operations:
111 - "abortOperation"
112 overwriteable: false
113 junitTestResultStorage:
114 storage: "file"
115 location:
116 adminAddress: "sam@chudnick.com"
117 url: "https://jenkins.chudnick.com/"
118 mailer:
119 charset: "UTF-8"
120 useSsl: false
121 useTls: false
122 metricsAccessKey:
123 accessKeys:
124 - canHealthCheck: true
125 canMetrics: true
126 canPing: true
127 canThreadDump: false
128 key: "{{ jenkins_metrics_key }}"
129 origins: "*"
130 pollSCM:
131 pollingThreadCount: 10
132 prometheusConfiguration:
133 appendParamLabel: false
134 appendStatusLabel: true
135 collectDiskUsage: true
136 collectingMetricsPeriodInSeconds: 120
137 countAbortedBuilds: true
138 countFailedBuilds: true
139 countNotBuiltBuilds: true
140 countSuccessfulBuilds: true
141 countUnstableBuilds: true
142 defaultNamespace: "default"
143 fetchTestResults: true
144 jobAttributeName: "jenkins_job"
145 path: "prometheus"
146 processingDisabledBuilds: false
147 useAuthenticatedEndpoint: false
148 themeManager:
149 disableUserThemes: false
150 theme: "dark"
151tool:
152 ansible:
153 installations:
154 - home: "/usr/bin/"
155 name: "Default"
156 git:
157 installations:
158 - home: "git"
159 name: "Default"
160 mavenGlobalConfig:
161 globalSettingsProvider: "standard"
162 settingsProvider: "standard"
163
diff --git a/data/jenkins/jenkins.conf b/data/jenkins/jenkins.conf
new file mode 100644
index 0000000..0c477a5
--- /dev/null
+++ b/data/jenkins/jenkins.conf
@@ -0,0 +1,85 @@
1upstream jenkins {
2 keepalive 32; # keepalive connections
3 server 127.0.0.1:8080; # jenkins ip and port
4}
5
6# Required for Jenkins websocket agents
7map $http_upgrade $connection_upgrade {
8 default upgrade;
9 '' close;
10}
11
12server {
13 listen 443 ssl;
14 server_name jenkins.chudnick.com;
15
16 ssl_certificate /etc/letsencrypt/live/chudnick.com/fullchain.pem;
17 ssl_certificate_key /etc/letsencrypt/live/chudnick.com/privkey.pem;
18 add_header Strict-Transport-Security "max-age=31536000" always;
19 ssl_stapling on;
20 ssl_stapling_verify on;
21
22 # Security / XSS Mitigation Headers
23 add_header X-Frame-Options "SAMEORIGIN";
24 add_header X-XSS-Protection "1; mode=block";
25 add_header X-Content-Type-Options "nosniff";
26
27 root /var/run/jenkins/war/;
28 access_log /var/log/nginx/jenkins.access.log;
29 error_log /var/log/nginx/jenkins.error.log;
30 ignore_invalid_headers off;
31
32 location ~ "^/static/[0-9a-fA-F]{8}\/(.*)$" {
33 # rewrite all static files into requests to the root
34 # E.g /static/12345678/css/something.css will become /css/something.css
35 rewrite "^/static/[0-9a-fA-F]{8}\/(.*)" /$1 last;
36 }
37
38 location /userContent {
39 # have nginx handle all the static requests to userContent folder
40 # note : This is the $JENKINS_HOME dir
41 root /var/lib/jenkins/;
42 if (!-f $request_filename){
43 # this file does not exist, might be a directory or a /**view** url
44 rewrite (.*) /$1 last;
45 break;
46 }
47 sendfile on;
48 }
49
50 location / {
51 sendfile off;
52 proxy_pass http://jenkins;
53 proxy_redirect default;
54 proxy_http_version 1.1;
55
56 # Required for Jenkins websocket agents
57 proxy_set_header Connection $connection_upgrade;
58 proxy_set_header Upgrade $http_upgrade;
59
60 proxy_set_header Host $host;
61 proxy_set_header X-Real-IP $remote_addr;
62 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
63 proxy_set_header X-Forwarded-Proto $scheme;
64 proxy_max_temp_file_size 0;
65
66 #this is the maximum upload size
67 client_max_body_size 10m;
68 client_body_buffer_size 128k;
69
70 proxy_connect_timeout 90;
71 proxy_send_timeout 90;
72 proxy_read_timeout 90;
73 proxy_buffering off;
74 proxy_request_buffering off; # Required for HTTP CLI commands
75 proxy_set_header Connection ""; # Clear for keepalive
76 }
77
78}
79
80server {
81 listen 80;
82 listen [::]:80;
83 server_name jenkins.chudnick.com;
84 return 301 https://$host$request_uri;
85}
diff --git a/data/kanboard/config.php b/data/kanboard/config.php
new file mode 100644
index 0000000..b8a8d69
--- /dev/null
+++ b/data/kanboard/config.php
@@ -0,0 +1,59 @@
1<?php
2
3// Data folder (must be writeable by the web server user and absolute)
4define('DATA_DIR', '/var/www/app/data');
5
6// Enable/Disable debug
7define('DEBUG', false);
8
9// Available log drivers: syslog, stderr, stdout, system or file
10define('LOG_DRIVER', 'system');
11
12// Plugins directory
13define('PLUGINS_DIR', __DIR__.DIRECTORY_SEPARATOR.'plugins');
14
15// Available cache drivers are "file" and "memory"
16define('CACHE_DRIVER', 'memory');
17
18// Enable/disable the reverse proxy authentication
19define('REVERSE_PROXY_AUTH', true);
20
21// Header name to use for the username
22define('REVERSE_PROXY_USER_HEADER', 'HTTP_REMOTE_USER');
23
24// Username of the admin, by default blank
25define('REVERSE_PROXY_DEFAULT_ADMIN', 'samadmin');
26
27// Header name to use for the user email
28define('REVERSE_PROXY_EMAIL_HEADER', 'HTTP_REMOTE_EMAIL');
29
30// Header name to use for the user full name
31define('REVERSE_PROXY_FULLNAME_HEADER', 'HTTP_REMOTE_NAME');
32
33// Default domain to use for setting the email address
34define('REVERSE_PROXY_DEFAULT_DOMAIN', 'chudnick.com');
35
36// Enable/disable remember me authentication
37define('REMEMBER_ME_AUTH', true);
38
39// Hide login form, useful if all your users use Google/Github/ReverseProxy authentication
40define('HIDE_LOGIN_FORM', true);
41
42// Disabling logout (useful for external SSO authentication)
43define('DISABLE_LOGOUT', true);
44
45// Enable captcha after 3 authentication failure
46define('BRUTEFORCE_CAPTCHA', 3);
47
48// Lock the account after 6 authentication failure
49define('BRUTEFORCE_LOCKDOWN', 6);
50
51// Lock account duration in minute
52define('BRUTEFORCE_LOCKDOWN_DURATION', 15);
53
54// Session duration in second (0 = until the browser is closed)
55// See http://php.net/manual/en/session.configuration.php#ini.session.cookie-lifetime
56define('SESSION_DURATION', 0);
57
58// Session handler: db or php
59define('SESSION_HANDLER', 'db');
diff --git a/data/kanboard/kanboard.conf.j2 b/data/kanboard/kanboard.conf.j2
new file mode 100644
index 0000000..a9746a6
--- /dev/null
+++ b/data/kanboard/kanboard.conf.j2
@@ -0,0 +1,34 @@
1server {
2 listen 443 ssl;
3 server_name {{ kanboard_server_name }};
4
5 ssl_certificate /etc/letsencrypt/live/chudnick.com/fullchain.pem;
6 ssl_certificate_key /etc/letsencrypt/live/chudnick.com/privkey.pem;
7 add_header Strict-Transport-Security "max-age=31536000" always;
8 ssl_stapling on;
9 ssl_stapling_verify on;
10
11 # Security / XSS Mitigation Headers
12 add_header X-Frame-Options "SAMEORIGIN";
13 add_header X-XSS-Protection "1; mode=block";
14 add_header X-Content-Type-Options "nosniff";
15
16 # authelia
17 include /etc/nginx/snippets/authelia-location.conf;
18
19 location / {
20 #authelia
21 include /etc/nginx/snippets/proxy.conf;
22 include /etc/nginx/snippets/authelia-authrequest.conf;
23
24 proxy_pass http://127.0.0.1:{{ kanboard_external_port }}/;
25 }
26
27}
28
29server {
30 listen 80;
31 listen [::]:80;
32 server_name {{ kanboard_server_name }};
33 return 301 https://$host$request_uri;
34}
diff --git a/data/lidarr/lidarr.conf.j2 b/data/lidarr/lidarr.conf.j2
new file mode 100644
index 0000000..8d9aef9
--- /dev/null
+++ b/data/lidarr/lidarr.conf.j2
@@ -0,0 +1,36 @@
1server {
2 listen 443 ssl;
3 server_name {{ lidarr_server_name }};
4
5 ssl_certificate /etc/letsencrypt/live/chudnick.com/fullchain.pem;
6 ssl_certificate_key /etc/letsencrypt/live/chudnick.com/privkey.pem;
7 add_header Strict-Transport-Security "max-age=31536000" always;
8 ssl_stapling on;
9 ssl_stapling_verify on;
10
11 # Security / XSS Mitigation Headers
12 add_header X-Frame-Options "SAMEORIGIN";
13 add_header X-XSS-Protection "1; mode=block";
14 add_header X-Content-Type-Options "nosniff";
15
16 # authelia
17 include /etc/nginx/snippets/authelia-location.conf;
18
19 location / {
20 #authelia
21 include /etc/nginx/snippets/proxy.conf;
22 include /etc/nginx/snippets/authelia-authrequest.conf;
23
24 proxy_set_header Upgrade $http_upgrade;
25 proxy_set_header Connection $http_connection;
26 proxy_pass http://127.0.0.1:{{ lidarr_external_port }}/;
27 }
28
29}
30
31server {
32 listen 80;
33 listen [::]:80;
34 server_name {{ lidarr_server_name }};
35 return 301 https://$host$request_uri;
36}
diff --git a/data/loki/config.yml b/data/loki/config.yml
new file mode 100644
index 0000000..767922f
--- /dev/null
+++ b/data/loki/config.yml
@@ -0,0 +1,54 @@
1auth_enabled: false
2
3server:
4 http_listen_port: 3100
5 grpc_listen_port: 9096
6
7common:
8 path_prefix: /tmp/loki
9 storage:
10 filesystem:
11 chunks_directory: /tmp/loki/chunks
12 rules_directory: /tmp/loki/rules
13 replication_factor: 1
14 ring:
15 instance_addr: 127.0.0.1
16 kvstore:
17 store: inmemory
18
19# https://github.com/grafana/loki/issues/5123
20
21query_range:
22 parallelise_shardable_queries: true
23 results_cache:
24 cache:
25 embedded_cache:
26 enabled: true
27 max_size_mb: 100
28querier:
29 max_concurrent: 2048
30
31query_scheduler:
32 max_outstanding_requests_per_tenant: 4096
33
34frontend:
35 max_outstanding_per_tenant: 4096
36
37limits_config:
38 split_queries_by_interval: 15m
39 max_query_parallelism: 32
40
41schema_config:
42 configs:
43 - from: 2020-10-24
44 store: boltdb-shipper
45 object_store: filesystem
46 schema: v11
47 index:
48 prefix: index_
49 period: 24h
50ruler:
51 alertmanager_url: http://localhost:9093
52
53analytics:
54 reporting_enabled: false
diff --git a/data/loki/loki.conf b/data/loki/loki.conf
new file mode 100644
index 0000000..4925236
--- /dev/null
+++ b/data/loki/loki.conf
@@ -0,0 +1,21 @@
1server {
2 listen 443 ssl;
3 server_name logs.chudnick.com
4
5 ssl_certificate "/etc/nginx/tls/fullchain.pem";
6 ssl_certificate_key "/etc/nginx/tls/privkey.pem";
7
8 set $upstream_authelia https://auth.chudnick.com/api/verify;
9 resolver 192.168.20.34;
10
11 location / {
12 proxy_pass http://127.0.0.1:3100;
13 }
14
15}
16
17server {
18 listen 80;
19 server_name logs.chudnick.com;
20 return 301 https://$host$request_uri;
21}
diff --git a/data/msmtp_mta/msmtprc b/data/msmtp_mta/msmtprc
new file mode 100644
index 0000000..9cceef5
--- /dev/null
+++ b/data/msmtp_mta/msmtprc
@@ -0,0 +1,11 @@
1defaults
2protocol smtp
3tls on
4tls_starttls off
5auth on
6
7account default
8host mail.chudnick.com
9port 465
10user monitoring@chudnick.com
11from monitoring@chudnick.com
diff --git a/data/navidrome/navidrome.conf b/data/navidrome/navidrome.conf
new file mode 100644
index 0000000..9e2b4a8
--- /dev/null
+++ b/data/navidrome/navidrome.conf
@@ -0,0 +1,34 @@
1server {
2 listen 443 ssl;
3 server_name music.chudnick.com navidrome.chudnick.com;
4
5 ssl_certificate /etc/letsencrypt/live/chudnick.com/fullchain.pem;
6 ssl_certificate_key /etc/letsencrypt/live/chudnick.com/privkey.pem;
7 add_header Strict-Transport-Security "max-age=31536000" always;
8 ssl_stapling on;
9 ssl_stapling_verify on;
10
11 # Security / XSS Mitigation Headers
12 add_header X-Frame-Options "SAMEORIGIN";
13 add_header X-XSS-Protection "1; mode=block";
14 add_header X-Content-Type-Options "nosniff";
15
16 # authelia
17 include /etc/nginx/snippets/authelia-location.conf;
18
19 location / {
20 #authelia
21 include /etc/nginx/snippets/proxy.conf;
22 include /etc/nginx/snippets/authelia-authrequest.conf;
23
24 proxy_pass http://127.0.0.1:4533;
25 }
26
27}
28
29server {
30 listen 80;
31 listen [::]:80;
32 server_name music.chudnick.com navidrome.chudnick.com;
33 return 301 https://$host$request_uri;
34}
diff --git a/data/nextcloud/nextcloud.conf b/data/nextcloud/nextcloud.conf
new file mode 100644
index 0000000..7528dbe
--- /dev/null
+++ b/data/nextcloud/nextcloud.conf
@@ -0,0 +1,45 @@
1map $http_upgrade $connection_upgrade {
2 default upgrade;
3 '' close;
4}
5
6server {
7 listen 443 ssl;
8 server_name nextcloud.chudnick.com;
9
10 ssl_certificate /etc/letsencrypt/live/chudnick.com/fullchain.pem;
11 ssl_certificate_key /etc/letsencrypt/live/chudnick.com/privkey.pem;
12 add_header Strict-Transport-Security "max-age=31536000" always;
13 ssl_stapling on;
14 ssl_stapling_verify on;
15
16 # Security / XSS Mitigation Headers
17 add_header X-Frame-Options "SAMEORIGIN";
18 add_header X-XSS-Protection "1; mode=block";
19 add_header X-Content-Type-Options "nosniff";
20
21 location / {
22 proxy_pass http://localhost:8005;
23
24 proxy_set_header Host $host;
25 proxy_set_header X-Real-IP $remote_addr;
26 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
27 proxy_set_header X-Forwarded-Proto $scheme;
28 proxy_set_header X-Forwarded-Protocol $scheme;
29 proxy_set_header X-Forwarded-Host $http_host;
30 client_max_body_size 0;
31
32 # Websocket
33 proxy_http_version 1.1;
34 proxy_set_header Upgrade $http_upgrade;
35 proxy_set_header Connection $connection_upgrade;
36 }
37
38}
39
40server {
41 listen 80;
42 listen [::]:80;
43 server_name nextcloud.chudnick.com;
44 return 301 https://$host$request_uri;
45}
diff --git a/data/photoprism/photoprism.conf b/data/photoprism/photoprism.conf
new file mode 100644
index 0000000..415bc1d
--- /dev/null
+++ b/data/photoprism/photoprism.conf
@@ -0,0 +1,41 @@
1server {
2 listen 443 ssl;
3 server_name photos.chudnick.com;
4
5 ssl_certificate /etc/letsencrypt/live/chudnick.com/fullchain.pem;
6 ssl_certificate_key /etc/letsencrypt/live/chudnick.com/privkey.pem;
7 add_header Strict-Transport-Security "max-age=31536000" always;
8 ssl_stapling on;
9 ssl_stapling_verify on;
10
11 # Security / XSS Mitigation Headers
12 add_header X-Frame-Options "SAMEORIGIN";
13 add_header X-XSS-Protection "1; mode=block";
14 add_header X-Content-Type-Options "nosniff";
15
16 client_max_body_size 500M;
17
18 # authelia
19 include /etc/nginx/snippets/authelia-location.conf;
20
21 location / {
22 #authelia
23 include /etc/nginx/snippets/proxy.conf;
24 include /etc/nginx/snippets/authelia-authrequest.conf;
25
26 proxy_pass http://127.0.0.1:8006/;
27
28 # websockets
29 proxy_buffering off;
30 proxy_set_header Upgrade $http_upgrade;
31 proxy_set_header Connection "upgrade";
32 }
33
34}
35
36server {
37 listen 80;
38 listen [::]:80;
39 server_name photos.chudnick.com;
40 return 301 https://$host$request_uri;
41}
diff --git a/data/pihole-exporter/pihole-exporter.conf b/data/pihole-exporter/pihole-exporter.conf
new file mode 100644
index 0000000..b42c444
--- /dev/null
+++ b/data/pihole-exporter/pihole-exporter.conf
@@ -0,0 +1,27 @@
1server {
2
3 listen 443 ssl;
4 ssl_certificate /etc/letsencrypt/live/chudnick.com/fullchain.pem;
5 ssl_certificate_key /etc/letsencrypt/live/chudnick.com/privkey.pem;
6 ssl_stapling on;
7 ssl_stapling_verify on;
8
9 # Your server name
10 server_name piholemetrics.chudnick.com;
11
12 # Security Headers
13 add_header X-Frame-Options "SAMEORIGIN";
14 add_header X-XSS-Protection "1; mode=block";
15 add_header X-Content-Type-Options "nosniff";
16
17 location / {
18 proxy_pass http://127.0.0.1:9617;
19 }
20}
21
22server {
23 listen 80;
24 listen [::]:80;
25 server_name piholemetrics.chudnick.com;
26 return 301 https://$host$request_uri;
27}
diff --git a/data/pihole/pihole_unbound.conf b/data/pihole/pihole_unbound.conf
new file mode 100644
index 0000000..7f768f1
--- /dev/null
+++ b/data/pihole/pihole_unbound.conf
@@ -0,0 +1,35 @@
1server:
2 verbosity: 0
3
4 interface: 127.0.0.1
5 port: 5335
6 do-ip4: yes
7 do-udp: yes
8 do-tcp: yes
9 do-ip6: no
10 prefer-ip6: no
11
12
13 # Trust glue only if it is within the server's authority
14 harden-glue: yes
15
16 # Require DNSSEC data for trust-anchored zones
17 harden-dnssec-stripped: yes
18
19 use-caps-for-id: no
20
21 edns-buffer-size: 1232
22
23 prefetch: yes
24
25 num-threads: 1
26
27 so-rcvbuf: 1m
28
29 # Ensure privacy of local IP ranges
30 private-address: 192.168.0.0/16
31 private-address: 169.254.0.0/16
32 private-address: 172.16.0.0/12
33 private-address: 10.0.0.0/8
34 private-address: fd00::/8
35 private-address: fe80::/10
diff --git a/data/pihole/setupVars.conf b/data/pihole/setupVars.conf
new file mode 100644
index 0000000..aed21d7
--- /dev/null
+++ b/data/pihole/setupVars.conf
@@ -0,0 +1,10 @@
1QUERY_LOGGING=true
2INSTALL_WEB=true
3PIHOLE_DNS_1=127.0.0.1#5335
4PIHOLE_INTERFACE=eth0
5DNSSEC=true
6DNS_BOGUS_PRIV=true
7DNSMASQ_LISTENING=single
8BLOCKING_ENABLED=true
9WEBUIBOXEDLAYOUT=boxed
10WEBTHEME=default-dark
diff --git a/data/prometheus-blackbox-exporter/blackbox.yml b/data/prometheus-blackbox-exporter/blackbox.yml
new file mode 100644
index 0000000..dd8e615
--- /dev/null
+++ b/data/prometheus-blackbox-exporter/blackbox.yml
@@ -0,0 +1,62 @@
1modules:
2 http_2xx:
3 prober: http
4 http:
5 preferred_ip_protocol: "ip4"
6 http_2xx_noverify:
7 prober: http
8 http:
9 preferred_ip_protocol: "ip4"
10 tls_config:
11 insecure_skip_verify: true
12 http_401_noverify:
13 prober: http
14 http:
15 preferred_ip_protocol: "ip4"
16 valid_status_codes: 401
17 tls_config:
18 insecure_skip_verify: true
19 http_post_2xx:
20 prober: http
21 http:
22 method: POST
23 tcp_connect:
24 prober: tcp
25 pop3s_banner:
26 prober: tcp
27 tcp:
28 query_response:
29 - expect: "^+OK"
30 tls: true
31 tls_config:
32 insecure_skip_verify: false
33 ssh_banner:
34 prober: tcp
35 tcp:
36 query_response:
37 - expect: "^SSH-2.0-"
38 irc_banner:
39 prober: tcp
40 tcp:
41 query_response:
42 - send: "NICK prober"
43 - send: "USER prober prober prober :prober"
44 - expect: "PING :([^ ]+)"
45 send: "PONG ${1}"
46 - expect: "^:[^ ]+ 001"
47 icmp:
48 prober: icmp
49 smtp:
50 prober: tcp
51 timeout: 15s
52 tcp:
53 query_response:
54 - expect: "^220 ([^ ]+) ESMTP (.+)$"
55 - send: "QUIT\r"
56 imaps:
57 prober: tcp
58 timeout: 5s
59 tcp:
60 tls: true
61 query_response:
62 - expect: "OK.*IMAP4rev1"
diff --git a/data/prometheus-nginx-exporter/defaults b/data/prometheus-nginx-exporter/defaults
new file mode 100644
index 0000000..8b48854
--- /dev/null
+++ b/data/prometheus-nginx-exporter/defaults
@@ -0,0 +1 @@
ARGS="-nginx.scrape-uri http://127.0.0.1:10000/stub_status"
diff --git a/data/prometheus-nginx-exporter/metrics.conf b/data/prometheus-nginx-exporter/metrics.conf
new file mode 100644
index 0000000..034f0ba
--- /dev/null
+++ b/data/prometheus-nginx-exporter/metrics.conf
@@ -0,0 +1,14 @@
1server {
2 listen 10000;
3 keepalive_timeout 0;
4
5 access_log off;
6
7 allow 127.0.0.1;
8 deny all;
9
10 location /stub_status {
11 stub_status on;
12 }
13}
14
diff --git a/data/prometheus-server/defaults b/data/prometheus-server/defaults
new file mode 100644
index 0000000..f6b095a
--- /dev/null
+++ b/data/prometheus-server/defaults
@@ -0,0 +1 @@
ARGS="--web.external-url=http://monitoring.chudnick.com/prometheus --web.route-prefix=prometheus"
diff --git a/data/prometheus-server/prometheus.yml b/data/prometheus-server/prometheus.yml
new file mode 100644
index 0000000..31c2a53
--- /dev/null
+++ b/data/prometheus-server/prometheus.yml
@@ -0,0 +1,168 @@
1# Sample config for Prometheus.
2
3global:
4 scrape_interval: 15s
5 evaluation_interval: 15s
6 external_labels:
7 monitor: 'homelab-monitoring'
8
9# Alertmanager configuration
10alerting:
11 alertmanagers:
12 - static_configs:
13 - targets: ['localhost:9093']
14
15# Load rules once and periodically evaluate them according to the global 'evaluation_interval'.
16rule_files:
17 # - "first_rules.yml"
18 # - "second_rules.yml"
19
20# A scrape configuration containing exactly one endpoint to scrape:
21# Here it's Prometheus itself.
22scrape_configs:
23
24 - job_name: servers
25 static_configs:
26 - targets: [
27 'monitoring.home.local:9100',
28 'ipasrv.home.local:9100',
29 'pihole.home.local:9100',
30 'proxmox.home.local:9100',
31 'docker.home.local:9100',
32 'jump.home.local:9100',
33 'jenkins.home.local:9100',
34 'games.home.local:9100',
35 ]
36
37 - job_name: nginx
38 static_configs:
39 - targets: [
40 'docker.home.local:9113',
41 'monitoring.home.local:9113',
42 'jenkins.home.local:9113'
43 ]
44 metrics_path: "/metrics"
45
46 - job_name: blackbox
47 metrics_path: /probe
48 static_configs:
49 - targets: [
50 'https://chudnick.com',
51 'https://git.chudnick.com',
52 'https://jellyfin.chudnick.com',
53 'https://monitoring.chudnick.com/grafana',
54 'https://monitoring.chudnick.com/prometheus',
55 'https://searxng.chudnick.com/searxng/healthz',
56 'https://ipasrv.home.local/ipa/ui',
57 'https://pihole.chudnick.com/admin',
58 'https://auth.chudnick.com',
59 'https://drawio.chudnick.com',
60 'https://music.chudnick.com',
61 'https://rss.chudnick.com/',
62 'https://dashboard.chudnick.com',
63 'https://gitea.chudnick.com',
64 'https://jenkins.chudnick.com/login',
65 'https://cadvisor.chudnick.com',
66 'https://invidious.chudnick.com',
67 'https://qbittorrent.chudnick.com',
68 'https://sonarr.chudnick.com',
69 'https://radarr.chudnick.com',
70 'https://lidarr.chudnick.com',
71 'https://readarr.chudnick.com',
72 'https://prowlarr.chudnick.com',
73 'https://nextcloud.chudnick.com',
74 'https://photos.chudnick.com',
75 'https://logs.chudnick.com/metrics',
76 'https://dashboard.chudnick.com',
77 'https://wiki.chudnick.com',
78 'https://weather.chudnick.com',
79 'https://gpt.chudnick.com',
80 'https://tasks.chudnick.com',
81 'https://finances.chudnick.com',
82 'https://finimporter.chudnick.com',
83 'https://homeassistant.chudnick.com',
84 ]
85 labels:
86 module: 'http_2xx'
87 - targets: [
88 'https://router.chudnick.com',
89 'https://ap.chudnick.com',
90 ]
91 labels:
92 module: 'http_2xx_noverify'
93 - targets: [
94 'https://games.home.local:47990',
95 ]
96 labels:
97 module: 'http_401_noverify'
98 - targets: ['mail.chudnick.com:25']
99 labels:
100 module: smtp
101 - targets: ['mail.chudnick.com:993']
102 labels:
103 module: imaps
104 relabel_configs:
105 - source_labels: [__address__]
106 target_label: __param_target
107 - source_labels: [__param_target]
108 target_label: instance
109 - source_labels: [module]
110 target_label: __param_module
111 - target_label: __address__
112 replacement: 127.0.0.1:9115
113
114
115 - job_name: applications
116 relabel_configs:
117 - source_labels: [path]
118 target_label: __metrics_path__
119 regex: (.+)
120 replacement: $1
121 - source_labels: [scheme]
122 target_label: __scheme__
123 regex: (.+)
124 replacement: $1
125 static_configs:
126 - targets: ['jellyfin.chudnick.com:443']
127 labels:
128 path: "/metrics"
129 scheme: "https"
130 application: jellyfin
131 - targets: ['monitoring.chudnick.com:443']
132 labels:
133 path: "/grafana/metrics"
134 group: grafana
135 scheme: https
136 application: grafana
137 - targets: ['localhost:9090']
138 labels:
139 application: prometheus
140 path: "/prometheus/metrics"
141 - targets: ['localhost:9115']
142 labels:
143 application: blackbox-exporter
144 path: "/metrics"
145 - targets: ['auth.chudnick.com']
146 labels:
147 application: authelia
148 path: "/metrics"
149 - targets: ['piholemetrics.chudnick.com']
150 labels:
151 application: pihole
152 path: "/metrics"
153 - targets: ['music.chudnick.com']
154 labels:
155 application: navidrome
156 path: "/metrics"
157 - targets: ['cadvisor.chudnick.com']
158 labels:
159 application: cadvisor
160 path: "/metrics"
161 - targets: ['gitea.chudnick.com']
162 labels:
163 application: gitea
164 path: "/metrics"
165 - targets: ['jenkins.chudnick.com']
166 labels:
167 application: jenkins
168 path: "/prometheus"
diff --git a/data/promtail/config.yml b/data/promtail/config.yml
new file mode 100644
index 0000000..8cef116
--- /dev/null
+++ b/data/promtail/config.yml
@@ -0,0 +1,30 @@
1server:
2 http_listen_port: 9080
3 grpc_listen_port: 0
4
5positions:
6 filename: /tmp/positions.yaml
7
8clients:
9 - url: http://logs.chudnick.com:3100/loki/api/v1/push
10
11scrape_configs:
12- job_name: system
13 static_configs:
14 - targets:
15 - localhost
16 labels:
17 job: varlogs
18 __path__: /var/log/*log
19 __path_exclude__: /var/log/lastlog
20
21- job_name: journal
22 journal:
23 max_age: 12h
24 labels:
25 job: systemd-journal
26 relabel_configs:
27 - source_labels: ['__journal__systemd_unit']
28 target_label: 'unit'
29 - source_labels: ['__journal__hostname']
30 target_label: 'hostname'
diff --git a/data/promtail/config_standard.yml b/data/promtail/config_standard.yml
new file mode 100644
index 0000000..750b131
--- /dev/null
+++ b/data/promtail/config_standard.yml
@@ -0,0 +1,28 @@
1server:
2 http_listen_port: 9080
3 grpc_listen_port: 0
4
5positions:
6 filename: /tmp/positions.yaml
7
8clients:
9 - url: http://logs.chudnick.com:3100/loki/api/v1/push
10
11scrape_configs:
12- job_name: system
13 static_configs:
14 - targets:
15 - localhost
16 labels:
17 job: varlogs
18 __path__: /var/log/*log
19 __path_exclude__: /var/log/lastlog
20
21- job_name: journal
22 journal:
23 max_age: 12h
24 labels:
25 job: systemd-journal
26 relabel_configs:
27 - source_labels: ['__journal__systemd_unit']
28 target_label: 'unit'
diff --git a/data/prowlarr/prowlarr.conf.j2 b/data/prowlarr/prowlarr.conf.j2
new file mode 100644
index 0000000..07d2bfd
--- /dev/null
+++ b/data/prowlarr/prowlarr.conf.j2
@@ -0,0 +1,36 @@
1server {
2 listen 443 ssl;
3 server_name {{ prowlarr_server_name }};
4
5 ssl_certificate /etc/letsencrypt/live/chudnick.com/fullchain.pem;
6 ssl_certificate_key /etc/letsencrypt/live/chudnick.com/privkey.pem;
7 add_header Strict-Transport-Security "max-age=31536000" always;
8 ssl_stapling on;
9 ssl_stapling_verify on;
10
11 # Security / XSS Mitigation Headers
12 add_header X-Frame-Options "SAMEORIGIN";
13 add_header X-XSS-Protection "1; mode=block";
14 add_header X-Content-Type-Options "nosniff";
15
16 # authelia
17 include /etc/nginx/snippets/authelia-location.conf;
18
19 location / {
20 #authelia
21 include /etc/nginx/snippets/proxy.conf;
22 include /etc/nginx/snippets/authelia-authrequest.conf;
23
24 proxy_set_header Upgrade $http_upgrade;
25 proxy_set_header Connection $http_connection;
26 proxy_pass http://127.0.0.1:{{ prowlarr_external_port }}/;
27 }
28
29}
30
31server {
32 listen 80;
33 listen [::]:80;
34 server_name {{ prowlarr_server_name }};
35 return 301 https://$host$request_uri;
36}
diff --git a/data/pywttr_docker/pywttr_docker.conf.j2 b/data/pywttr_docker/pywttr_docker.conf.j2
new file mode 100644
index 0000000..921a9dd
--- /dev/null
+++ b/data/pywttr_docker/pywttr_docker.conf.j2
@@ -0,0 +1,33 @@
1server {
2 listen 443 ssl;
3 server_name {{ pywttr_docker_server_name }};
4
5 ssl_certificate /etc/letsencrypt/live/chudnick.com/fullchain.pem;
6 ssl_certificate_key /etc/letsencrypt/live/chudnick.com/privkey.pem;
7 add_header Strict-Transport-Security "max-age=31536000" always;
8 ssl_stapling on;
9 ssl_stapling_verify on;
10
11 # Security / XSS Mitigation Headers
12 add_header X-Frame-Options "SAMEORIGIN";
13 add_header X-XSS-Protection "1; mode=block";
14 add_header X-Content-Type-Options "nosniff";
15
16 # authelia
17 include /etc/nginx/snippets/authelia-location.conf;
18
19 location / {
20 #authelia
21 include /etc/nginx/snippets/proxy.conf;
22 include /etc/nginx/snippets/authelia-authrequest.conf;
23 proxy_pass http://127.0.0.1:{{ pywttr_docker_external_port }}/;
24 }
25
26}
27
28server {
29 listen 80;
30 listen [::]:80;
31 server_name {{ pywttr_docker_server_name }};
32 return 301 https://$host$request_uri;
33}
diff --git a/data/qbittorrent/qbittorrent.conf.j2 b/data/qbittorrent/qbittorrent.conf.j2
new file mode 100644
index 0000000..b946048
--- /dev/null
+++ b/data/qbittorrent/qbittorrent.conf.j2
@@ -0,0 +1,34 @@
1server {
2 listen 443 ssl;
3 server_name {{ qbittorrent_server_name }};
4
5 ssl_certificate /etc/letsencrypt/live/chudnick.com/fullchain.pem;
6 ssl_certificate_key /etc/letsencrypt/live/chudnick.com/privkey.pem;
7 add_header Strict-Transport-Security "max-age=31536000" always;
8 ssl_stapling on;
9 ssl_stapling_verify on;
10
11 # Security / XSS Mitigation Headers
12 add_header X-Frame-Options "SAMEORIGIN";
13 add_header X-XSS-Protection "1; mode=block";
14 add_header X-Content-Type-Options "nosniff";
15
16 # authelia
17 include /etc/nginx/snippets/authelia-location.conf;
18
19 location / {
20 #authelia
21 include /etc/nginx/snippets/proxy.conf;
22 include /etc/nginx/snippets/authelia-authrequest.conf;
23
24 proxy_pass http://127.0.0.1:{{ qbittorrent_external_port }}/;
25 }
26
27}
28
29server {
30 listen 80;
31 listen [::]:80;
32 server_name {{ qbittorrent_server_name }};
33 return 301 https://$host$request_uri;
34}
diff --git a/data/radarr/radarr.conf.j2 b/data/radarr/radarr.conf.j2
new file mode 100644
index 0000000..f4b7293
--- /dev/null
+++ b/data/radarr/radarr.conf.j2
@@ -0,0 +1,36 @@
1server {
2 listen 443 ssl;
3 server_name {{ radarr_server_name }};
4
5 ssl_certificate /etc/letsencrypt/live/chudnick.com/fullchain.pem;
6 ssl_certificate_key /etc/letsencrypt/live/chudnick.com/privkey.pem;
7 add_header Strict-Transport-Security "max-age=31536000" always;
8 ssl_stapling on;
9 ssl_stapling_verify on;
10
11 # Security / XSS Mitigation Headers
12 add_header X-Frame-Options "SAMEORIGIN";
13 add_header X-XSS-Protection "1; mode=block";
14 add_header X-Content-Type-Options "nosniff";
15
16 # authelia
17 include /etc/nginx/snippets/authelia-location.conf;
18
19 location / {
20 #authelia
21 include /etc/nginx/snippets/proxy.conf;
22 include /etc/nginx/snippets/authelia-authrequest.conf;
23
24 proxy_set_header Upgrade $http_upgrade;
25 proxy_set_header Connection $http_connection;
26 proxy_pass http://127.0.0.1:{{ radarr_external_port }}/;
27 }
28
29}
30
31server {
32 listen 80;
33 listen [::]:80;
34 server_name {{ radarr_server_name }};
35 return 301 https://$host$request_uri;
36}
diff --git a/data/readarr/readarr.conf.j2 b/data/readarr/readarr.conf.j2
new file mode 100644
index 0000000..f30cdcb
--- /dev/null
+++ b/data/readarr/readarr.conf.j2
@@ -0,0 +1,36 @@
1server {
2 listen 443 ssl;
3 server_name {{ readarr_server_name }};
4
5 ssl_certificate /etc/letsencrypt/live/chudnick.com/fullchain.pem;
6 ssl_certificate_key /etc/letsencrypt/live/chudnick.com/privkey.pem;
7 add_header Strict-Transport-Security "max-age=31536000" always;
8 ssl_stapling on;
9 ssl_stapling_verify on;
10
11 # Security / XSS Mitigation Headers
12 add_header X-Frame-Options "SAMEORIGIN";
13 add_header X-XSS-Protection "1; mode=block";
14 add_header X-Content-Type-Options "nosniff";
15
16 # authelia
17 include /etc/nginx/snippets/authelia-location.conf;
18
19 location / {
20 #authelia
21 include /etc/nginx/snippets/proxy.conf;
22 include /etc/nginx/snippets/authelia-authrequest.conf;
23
24 proxy_set_header Upgrade $http_upgrade;
25 proxy_set_header Connection $http_connection;
26 proxy_pass http://127.0.0.1:{{ readarr_external_port }}/;
27 }
28
29}
30
31server {
32 listen 80;
33 listen [::]:80;
34 server_name {{ readarr_server_name }};
35 return 301 https://$host$request_uri;
36}
diff --git a/data/searxng/searxng.conf b/data/searxng/searxng.conf
new file mode 100644
index 0000000..7102d60
--- /dev/null
+++ b/data/searxng/searxng.conf
@@ -0,0 +1,48 @@
1server {
2
3 listen 443 ssl;
4 ssl_certificate /etc/letsencrypt/live/chudnick.com/fullchain.pem;
5 ssl_certificate_key /etc/letsencrypt/live/chudnick.com/privkey.pem;
6 ssl_stapling on;
7 ssl_stapling_verify on;
8
9 # Your server name
10 server_name searxng.chudnick.com;
11
12 # If you want to log user activity, comment these
13 access_log /dev/null;
14 error_log /dev/null;
15
16 # Security Headers
17 add_header X-Frame-Options "SAMEORIGIN";
18 add_header X-XSS-Protection "1; mode=block";
19 add_header X-Content-Type-Options "nosniff";
20
21 location = / {
22 return 302 https://$host/searxng;
23 }
24
25 # Searx reverse proxy
26 location /searxng {
27 proxy_pass http://127.0.0.1:8080;
28
29 proxy_set_header Host $host;
30 proxy_set_header Connection $http_connection;
31
32 # see flaskfix.py
33 proxy_set_header X-Scheme $scheme;
34 proxy_set_header X-Script-Name /searxng;
35
36 # see limiter.py
37 proxy_set_header X-Real-IP $remote_addr;
38 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
39
40 }
41}
42
43server {
44 listen 80;
45 listen [::]:80;
46 server_name searxng.chudnick.com;
47 return 301 https://$host$request_uri;
48}
diff --git a/data/searxng/settings.yml b/data/searxng/settings.yml
new file mode 100644
index 0000000..6ff425f
--- /dev/null
+++ b/data/searxng/settings.yml
@@ -0,0 +1,74 @@
1# SearXNG settings, before editing this file read:
2#
3# https://docs.searxng.org/admin/engines/settings.html
4
5use_default_settings:
6 engines:
7 remove:
8 - 1337x
9 - btdigg
10 - kickass
11 - nyaa
12 - piratebay
13 - solidtorrents
14 - tokyotoshokan
15 - z-library
16 - google
17
18general:
19 debug: false
20 instance_name: "SearXNG"
21
22search:
23 safe_search: 0
24 autocomplete: ''
25 default_lang: ''
26 formats:
27 - html
28
29server:
30 base_url: searxng.chudnick.com
31 secret_key: "changeme" # change this!
32 image_proxy: true
33 limiter: false
34 http_protocol_version: "1.1"
35 default_http_headers:
36 X-Content-Type-Options: nosniff
37 X-XSS-Protection: 1; mode=block
38 X-Download-Options: noopen
39 X-Robots-Tag: noindex, nofollow
40 Referrer-Policy: no-referrer
41
42redis:
43 url: redis://redis:6379/0
44
45ui:
46 static_use_hash: true
47 results_on_new_tab: false
48 default_theme: simple
49 theme_args:
50 simple_style: dark
51
52enabled_plugins:
53 - 'Hash plugin'
54 - 'Search on category select'
55 - 'Self Informations'
56 - 'Tracker URL remover'
57 - 'Ahmia blacklist'
58
59engines:
60 - name: free software directory
61 disabled: false
62
63 - name: gitlab
64 disabled: false
65
66 - name: wiby
67 disabled: true
68
69 - name: hoogle
70 disabled: true
71
72 - name: mankier
73 disabled: true
74
diff --git a/data/searxng/uwsgi.ini b/data/searxng/uwsgi.ini
new file mode 100644
index 0000000..3aab8dd
--- /dev/null
+++ b/data/searxng/uwsgi.ini
@@ -0,0 +1,50 @@
1[uwsgi]
2# Who will run the code
3uid = searxng
4gid = searxng
5
6# Number of workers (usually CPU count)
7workers = 1
8threads = 1
9
10# The right granted on the created socket
11chmod-socket = 666
12
13# Plugin to use and interpreter config
14single-interpreter = true
15master = true
16plugin = python3
17lazy-apps = true
18enable-threads = true
19
20# Module to import
21module = searx.webapp
22
23# Virtualenv and python path
24pythonpath = /usr/local/searxng/
25chdir = /usr/local/searxng/searx/
26
27# automatically set processes name to something meaningful
28auto-procname = true
29
30# Disable request logging for privacy
31disable-logging = true
32log-5xx = true
33
34# Set the max size of a request (request-body excluded)
35buffer-size = 8192
36
37# No keep alive
38# See https://github.com/searx/searx-docker/issues/24
39add-header = Connection: close
40
41# uwsgi serves the static files
42# expires set to one year since there are hashes
43static-map = /static=/usr/local/searxng/searx/static
44static-expires = /* 31557600
45static-gzip-all = True
46offload-threads = %k
47
48# Cache
49cache2 = name=searxngcache,items=2000,blocks=2000,blocksize=4096,bitmap=1
50
diff --git a/data/sonarr/sonarr.conf.j2 b/data/sonarr/sonarr.conf.j2
new file mode 100644
index 0000000..3d831ba
--- /dev/null
+++ b/data/sonarr/sonarr.conf.j2
@@ -0,0 +1,36 @@
1server {
2 listen 443 ssl;
3 server_name {{ sonarr_server_name }};
4
5 ssl_certificate /etc/letsencrypt/live/chudnick.com/fullchain.pem;
6 ssl_certificate_key /etc/letsencrypt/live/chudnick.com/privkey.pem;
7 add_header Strict-Transport-Security "max-age=31536000" always;
8 ssl_stapling on;
9 ssl_stapling_verify on;
10
11 # Security / XSS Mitigation Headers
12 add_header X-Frame-Options "SAMEORIGIN";
13 add_header X-XSS-Protection "1; mode=block";
14 add_header X-Content-Type-Options "nosniff";
15
16 # authelia
17 include /etc/nginx/snippets/authelia-location.conf;
18
19 location / {
20 #authelia
21 include /etc/nginx/snippets/proxy.conf;
22 include /etc/nginx/snippets/authelia-authrequest.conf;
23
24 proxy_set_header Upgrade $http_upgrade;
25 proxy_set_header Connection $http_connection;
26 proxy_pass http://127.0.0.1:{{ sonarr_external_port }}/;
27 }
28
29}
30
31server {
32 listen 80;
33 listen [::]:80;
34 server_name {{ sonarr_server_name }};
35 return 301 https://$host$request_uri;
36}
diff --git a/data/text_generation/text_generation.conf.j2 b/data/text_generation/text_generation.conf.j2
new file mode 100644
index 0000000..ca78d4a
--- /dev/null
+++ b/data/text_generation/text_generation.conf.j2
@@ -0,0 +1,37 @@
1server {
2 listen 443 ssl;
3 server_name {{ text_generation_server_name }};
4
5 ssl_certificate /etc/letsencrypt/live/chudnick.com/fullchain.pem;
6 ssl_certificate_key /etc/letsencrypt/live/chudnick.com/privkey.pem;
7 add_header Strict-Transport-Security "max-age=31536000" always;
8 ssl_stapling on;
9 ssl_stapling_verify on;
10
11 # Security / XSS Mitigation Headers
12 add_header X-Frame-Options "SAMEORIGIN";
13 add_header X-XSS-Protection "1; mode=block";
14 add_header X-Content-Type-Options "nosniff";
15
16 # authelia
17 include /etc/nginx/snippets/authelia-location.conf;
18
19 location / {
20 #authelia
21 include /etc/nginx/snippets/proxy.conf;
22 include /etc/nginx/snippets/authelia-authrequest.conf;
23
24 proxy_pass http://127.0.0.1:{{ text_generation_external_port }}/;
25 proxy_set_header Upgrade $http_upgrade;
26 proxy_set_header Connection "upgrade";
27
28 }
29
30}
31
32server {
33 listen 80;
34 listen [::]:80;
35 server_name {{ text_generation_server_name }};
36 return 301 https://$host$request_uri;
37}
diff --git a/data/vaultwarden/vaultwarden.conf.j2 b/data/vaultwarden/vaultwarden.conf.j2
new file mode 100644
index 0000000..76fd99c
--- /dev/null
+++ b/data/vaultwarden/vaultwarden.conf.j2
@@ -0,0 +1,39 @@
1server {
2 listen 443 ssl;
3 server_name {{ vaultwarden_server_name }};
4
5 ssl_certificate /etc/letsencrypt/live/chudnick.com/fullchain.pem;
6 ssl_certificate_key /etc/letsencrypt/live/chudnick.com/privkey.pem;
7 add_header Strict-Transport-Security "max-age=31536000" always;
8 ssl_stapling on;
9 ssl_stapling_verify on;
10
11 # Security / XSS Mitigation Headers
12 add_header X-Frame-Options "SAMEORIGIN";
13 add_header X-XSS-Protection "1; mode=block";
14 add_header X-Content-Type-Options "nosniff";
15
16 # authelia
17 include /etc/nginx/snippets/authelia-location.conf;
18
19 location /admin {
20 #authelia
21 include /etc/nginx/snippets/proxy.conf;
22 include /etc/nginx/snippets/authelia-authrequest.conf;
23
24 proxy_pass http://127.0.0.1:{{ vaultwarden_external_port }};
25 }
26
27 location / {
28 proxy_pass http://127.0.0.1:{{ vaultwarden_external_port }}/;
29 }
30
31
32}
33
34server {
35 listen 80;
36 listen [::]:80;
37 server_name {{ vaultwarden_server_name }};
38 return 301 https://$host$request_uri;
39}
diff --git a/group_vars/all/vars.yml b/group_vars/all/vars.yml
new file mode 100644
index 0000000..7e37fc4
--- /dev/null
+++ b/group_vars/all/vars.yml
@@ -0,0 +1,570 @@
1# Homelab IaC Global Variables File
2
3# Variables are all kept here to avoid having them scattered
4# throughout the various roles
5
6#####################
7# #
8# Infrastructure #
9# #
10#####################
11
12timezone: America/New_York
13domain: "home.local"
14
15# proxmox system
16proxmox_api_user: "vmadmin@pam"
17proxmox_username: vmadmin
18ssh_public_key: data/common/id_rsa.pub
19
20# vm deploy
21vm_vlan: 20
22vm_onboot: yes
23vm_agent: yes
24vm_bridge: vmbr0
25vm_full_clone: yes
26template_id: 1000
27memory_size: 512
28cpu_cores: 1
29cpu_sockets: 1
30bios_type: seabios
31
32# cloud init vars
33ci_target_dir: "/home/vmadmin"
34ci_memory_size: 512
35ci_base_id: 1000
36ci_disk_size: "10G"
37ci_storage: "fast-pool"
38ci_user: "admin"
39ci_debian_name: "debiantest"
40ci_bridge: "vmbr0"
41ci_vlan: 20
42ci_sshkey: "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQCxbdj9XqeorxBqfhWaJp8JonsIhD0g6qSYRdBKqIyHARglr+mG0hjdeDg1TPxGUnuJUDmG1vC0gbSoaKyiPrwf3nbCfqVXT2k+aY4VepV4g4TLTTUVuTXiyVdd83sE5uiF7DDOfoYU34oZ0bHBjpJfuBHHYhipnZTdxdXhxUtDSmJ8kIz3F7NEiKA66U/4JYRolI5jYhIhdUGgEpxxWz+pCsrZIo9N+VkJfqvMoVs+GVOyUMLjmJDB+zBC28V7yaVtt3kQVl38hhYxOhDkrSdaV+PyyTatZxNVW1u9WcdVhCnyke1bmEIbjXBvSHC2MH6VKiXCCkA9YEjUNEYGbkYPfe3wiZMh7OQnMlTSs/cV6T5UUGbmcJQnsys1PR/szzDLAvFziK4q6TvvyzDXyBZPKoBT8SXjeRNWdhWIlsJA7Z8+GTxGr2Ow9WjDDHHU2gL8yWkZvMfGO6MUo6K4ItLW/H1HtasAcHBXjvRFiHiK2WAB55GRrMkwNU1TLRmNvmT27blbe/+c52EySs8FLNlGEgW3/80YUnwEkOWZ2hRsbe8BnxRVr28wjwzWSc4RJP7AXMcn73J1hBZ6mvrQK5V0pneLXXpjdebflwwVc1mAVoFXt8w5etGHn+bx5kKINlzXW1h7PPg/7kexqX7bVYRjcyI0Ot5/rsCikKuyAqDVhQ== picard@titan"
43ssh_key_local: data/common/id_rsa.pub
44ssh_key_dest: "/home/{{ci_user}}/ci_sshkey"
45nameserver: 192.168.20.1
46
47# proxmox backup server
48pbs_admin: "backupadmin@pbs"
49pbs_user: "backup@pbs"
50#pbs_host: "proxmox.home.local"
51pbs_host: "192.168.10.11"
52pbs_datastore: "onsite"
53pbs_datastore_path: "/mnt/backup/onsite"
54pbs_keep_last: "3"
55pbs_keep_daily: "13"
56pbs_keep_weekly: "8"
57pbs_keep_monthly: "11"
58pbs_keep_yearly: "4"
59pbs_fingerprint: "90:39:30:90:e9:11:7b:48:3f:88:a6:78:8d:62:c1:e4:c2:7a:ac:29:44:f7:88:5b:1e:25:f4:f7:b4:69:58:ac"
60
61
62# distro base
63base_packages:
64 - openssh-server
65 - ufw
66 - rsync
67 - htop
68 - vim
69 - sudo
70 - qemu-guest-agent
71 - git
72
73#####################
74# #
75# Services #
76# #
77#####################
78
79# common
80nginx_cert: data/common/fullchain.pem
81services_domain: chudnick.com
82oidc_issuer: "https://auth.chudnick.com"
83
84# chronyd
85chrony_config: data/chronyd/chrony.conf
86
87# msmtp_mta
88msmtp_mta_packages:
89 - msmtp
90 - msmtp-mta
91msmtp_mta_config: data/msmtp_mta/msmtprc
92
93# prometheus server
94prometheus_package: prometheus
95management_ip: 192.168.10.254
96grafana_server_ip: 192.168.20.32
97prometheus_port: '9090'
98prometheus_nginx_config: data/grafana/grafana.conf
99prometheus_config: data/prometheus-server/prometheus.yml
100prometheus_defaults: data/prometheus-server/defaults
101
102# prometheus node exporter
103node_exporter_debian_package: prometheus-node-exporter
104node_exporter_fedora_package: golang-github-prometheus-node-exporter
105prometheus_server_ip: 192.168.20.32
106node_exporter_port: '9100'
107
108# prometheus nginx exporter
109nginx_exporter_debian_package: prometheus-nginx-exporter
110nginx_exporter_fedora_package: golang-github-prometheus-nginx-exporter
111nginx_exporter_port: '9113'
112nginx_exporter_config: data/prometheus-nginx-exporter/metrics.conf
113nginx_exporter_defaults: data/prometheus-nginx-exporter/defaults
114
115# grafana
116grafana_package:
117 - grafana
118 - nginx
119grafana_config: data/grafana/grafana.ini.j2
120grafana_nginx_config: data/grafana/grafana.conf
121grafana_url: https://monitoring.chudnick.com/grafana
122grafana_admin: admin
123grafana_email: admin@home.local
124prometheus_url: https://monitoring.chudnick.com/prometheus
125influxdb_url: http://monitoring.home.local:8086
126influx_database: proxmox
127influx_user: readonly
128loki_url: http://monitoring.home.local:3100
129grafana_dashboard_main: data/grafana/main.json
130
131# loki
132loki_nginx_config: data/loki/loki.conf
133loki_config: data/loki/config.yml
134loki_repo: "https://github.com/grafana/loki"
135loki_version: "v2.7.1"
136
137# promtail
138promtail_config: data/promtail/config.yml
139
140# influxdb
141influxdb_packages:
142 - influxdb
143 - influxdb-client
144influx_config: data/influxdb/influxdb.conf
145influx_data: data/influxdb/influx_data/
146
147# pihole
148pihole_packages:
149 - git
150 - unbound
151 - dns-root-data
152 - lighttpd-mod-openssl
153pihole_setupvars: data/pihole/setupVars.conf
154pihole_unboundconf: data/pihole/pihole_unbound.conf
155
156# unattended upgrades
157unattended_upgrades_packages:
158 - unattended-upgrades
159 - powermgmt-base
160 - python3-gi
161uu_mail_to: sam@chudnick.com
162uu_mail_from: Unattended Upgrades <monitoring@chudnick.com>
163
164# ipaserver
165ipa_dns_ip: 192.168.20.34
166
167# ipaclient
168ipaclient_domain: home.local
169ipaclient_realm: HOME.LOCAL
170ipaclient_mkhomedir: yes
171ipaclient_servers: ipasrv.home.local
172ipaclient_ntp_servers: ntp.home.local
173
174# ipabackup
175ipabackup_name: ipa-full-2022-08-27-07-56-01
176ipabackup_from_controller: yes
177
178# game server
179sunshine_repo: https://github.com/LizardByte/Sunshine
180sunshine_version: v0.20.1
181sunshine_packages:
182 - build-essential
183 - cmake
184 - libavdevice-dev
185 - libboost-filesystem-dev
186 - libboost-log-dev
187 - libboost-program-options-dev
188 - libboost-thread-dev
189 - libcap-dev
190 - libcurl4-openssl-dev
191 - libdrm-dev
192 - libevdev-dev
193 - libmfx-dev
194 - libnuma-dev
195 - libopus-dev
196 - libpulse-dev
197 - libssl-dev
198 - libva-dev
199 - libvdpau-dev
200 - libwayland-dev
201 - libx11-dev
202 - libxcb-shm0-dev
203 - libxcb-xfixes0-dev
204 - libxcb1-dev
205 - libxfixes-dev
206 - libxrandr-dev
207 - libxtst-dev
208 - nodejs
209 - npm
210 - nvidia-cuda-dev
211 - nvidia-cuda-toolkit
212 - xz-utils
213
214game_server_packages:
215 - xorg
216 - task-xfce-desktop
217 - firmware-amd-graphics
218 - amd64-microcode
219 - xserver-xorg-video-all
220 - linux-headers-6.0.0-0.deb11.2-amd64
221 - nginx
222 - ssl-cert
223games_user: gamer
224game_server_nginx_config: data/game_server/sunshine_proxy.conf
225steam_packages:
226 - steam
227 - mesa-vulkan-drivers
228 - libglx-mesa0:i386
229 - mesa-vulkan-drivers:i386
230 - libgl1-mesa-dri:i386
231lightdm_config: data/game_server/lightdm.conf
232xfce_xinit: data/game_server/xinitrc
233
234# jenkins
235jenkins_nginx_config: data/jenkins/jenkins.conf
236jenkins_config: data/jenkins/configuration.yml.j2
237jenkins_packages:
238 - openjdk-11-jre-headless
239 - nginx
240 - git
241 - ansible
242 - jenkins
243 - python3-proxmoxer
244jenkins_username: 7238a8bf-8945-47bc-85c3-d0356ad3428e
245jenkins_url: "https://jenkins.chudnick.com"
246
247
248# docker rootless
249docker_packages:
250 - docker-ce
251 - docker-ce-cli
252 - docker-ce-rootless-extras
253 - docker-compose-plugin
254 - uidmap
255 - dbus-user-session
256 - slirp4netns
257 - fuse-overlayfs
258 - acl
259docker_username: docker_rootless
260docker_uid: "2000"
261docker_home: /srv/docker
262docker_registry_url: "gitea.chudnick.com"
263docker_registry_username: "sam"
264docker_config: data/docker/daemon.json
265
266#####################
267# #
268# Docker Containers #
269# #
270#####################
271
272# authelia
273authelia_repo: "https://github.com/authelia/authelia"
274authelia_version: "master"
275authelia_nginx_config: data/authelia/authelia.conf
276authelia_config: data/authelia/configuration.yml
277authelia_network_name: authelia_net
278authelia_subnet: 172.25.0.0/24
279authelia_gateway: 172.25.0.1
280authelia_ipv4: 172.25.0.2
281redis_authelia_ipv4: 172.25.0.3
282authelia_proxy_snippet: data/authelia/proxy.conf
283authelia_location_snippet: data/authelia/authelia-location.conf
284authelia_request_snippet: data/authelia/authelia-authrequest.conf
285
286# searxng
287searxng_repo: "https://github.com/searxng/searxng"
288searxng_config: data/searxng/settings.yml
289searxng_uwsgi_config: data/searxng/uwsgi.ini
290searxng_nginx_config: data/searxng/searxng.conf
291searxng_network_name: searxng_net
292searxng_subnet: 172.25.1.0/24
293searxng_gateway: 172.25.1.1
294searxng_ipv4: 172.25.1.2
295redis_searxng_ipv4: 172.25.1.3
296
297# pihole_exporter
298pihole_exporter_repo: "https://github.com/eko/pihole-exporter/"
299pihole_exporter_version: "v0.3.0"
300pihole_exporter_network_name: pihole_exporter_net
301pihole_exporter_subnet: 172.25.2.0/24
302pihole_exporter_gateway: 172.25.2.1
303pihole_ip: 192.168.20.34
304pihole_api_port: '9617'
305pihole_exporter_nginx_config: data/pihole-exporter/pihole-exporter.conf
306
307# drawio
308drawio_repo: "https://github.com/jgraph/docker-drawio"
309drawio_nginx_config: data/drawio/drawio.conf
310drawio_network_name: drawio_net
311drawio_subnet: 172.25.3.0/24
312drawio_gateway: 172.25.3.1
313drawio_ipv4: 172.25.3.2
314drawio_plantuml_ipv4: 172.25.3.3
315drawio_export_ipv4: 172.25.3.4
316drawio_base_url: drawio.home.local
317
318# jellyfin
319jellyfin_repo: "https://github.com/jellyfin/jellyfin"
320jellyfin_version: "v10.8.8"
321jellyfin_nginx_config: data/jellyfin/jellyfin.conf
322jellyfin_network_name: jellyfin_net
323jellyfin_subnet: 172.25.4.0/24
324jellyfin_gateway: 172.25.4.1
325jellyfin_ipv4: 172.25.4.2
326jellyfin_config: data/jellyfin/config
327jellyfin_web_config: data/jellyfin/web-config.json
328jellyfin_media: data/jellyfin/media
329jellyfin_url: https://jellyfin.chudnick.com
330
331# navidrome
332navidrome_repo: "https://github.com/navidrome/navidrome"
333navidrome_version: "v0.48.0"
334navidrome_nginx_config: data/navidrome/navidrome.conf
335navidrome_network_name: navidrome_net
336navidrome_subnet: 172.25.5.0/24
337navidrome_gateway: 172.25.5.1
338navidrome_ipv4: 172.25.5.2
339
340# radicale
341radicale_repo: "https://github.com/Kozea/Radicale"
342radicale_version: "v3.1.8"
343radicale_config: data/radicale/config
344radicale_users: data/radicale/users
345radicale_nginx_config: data/radicale/radicale.conf
346radicale_network_name: radicale_net
347radicale_subnet: 172.25.6.0/24
348radicale_gateway: 172.25.6.1
349radicale_ipv4: 172.25.6.2
350
351# freshrss
352freshrss_repo: "https://github.com/FreshRSS/FreshRSS"
353freshrss_version: "v1.20.2"
354freshrss_nginx_config: data/freshrss/freshrss.conf
355freshrss_network_name: freshrss_net
356freshrss_subnet: 172.25.7.0/24
357freshrss_gateway: 172.25.7.1
358freshrss_ipv4: 172.25.7.2
359
360# homer
361homer_repo: "https://github.com/bastienwirtz/homer"
362homer_version: "v22.11.2"
363homer_nginx_config: data/homer/homer.conf
364homer_network_name: homer_net
365homer_subnet: 172.25.9.0/24
366homer_gateway: 172.25.9.1
367homer_ipv4: 172.25.9.2
368homer_assets_dir: data/homer/
369
370# invidious
371invidious_repo: "https://github.com/iv-org/invidious"
372invidious_nginx_config: data/invidious/invidious.conf.j2
373invidious_network_name: invidious_net
374invidious_subnet: 172.25.10.0/24
375invidious_gateway: 172.25.10.1
376invidious_ipv4: 172.25.10.2
377invidious_db_ipv4: 172.25.10.3
378invidious_server_name: "invidious.chudnick.com"
379invidious_external_port: 8002
380 #invidious_username: sam
381 #invidious_version: "v0.3.0-remote_user"
382
383# gitea
384gitea_repo: "https://github.com/go-gitea/gitea"
385gitea_git_uid: "1100"
386gitea_version: "v1.17.3"
387gitea_nginx_config: data/gitea/gitea.conf
388gitea_config: data/gitea/app.ini
389gitea_network_name: gitea_net
390gitea_subnet: 172.25.11.0/24
391gitea_gateway: 172.25.11.1
392gitea_ipv4: 172.25.11.2
393gitea_external_port: 8003
394
395# cadvisor
396cadvisor_repo: "https://github.com/google/cadvisor"
397cadvisor_version: "v0.46.0"
398cadvisor_nginx_config: data/cadvisor/cadvisor.conf
399cadvisor_network_name: cadvisor_net
400cadvisor_subnet: 172.25.12.0/24
401cadvisor_gateway: 172.25.12.1
402cadvisor_ipv4: 172.25.12.2
403cadvisor_external_port: 8004
404
405# nextcloud
406nextcloud_version: "25.0.2"
407nextcloud_nginx_config: data/nextcloud/nextcloud.conf
408nextcloud_network_name: nextcloud_net
409nextcloud_subnet: 172.25.13.0/24
410nextcloud_gateway: 172.25.13.1
411nextcloud_ipv4: 172.25.13.2
412nextcloud_redis_ipv4: 172.25.13.3
413nextcloud_postgres_ipv4: 172.25.13.4
414nextcloud_cron_ipv4: 172.25.13.5
415nextcloud_external_port: 8005
416nextcloud_postgres_db: "nextcloud"
417nextcloud_postgres_user: "nextcloud"
418nextcloud_admin: "admin"
419nextcloud_trusted_domains: "nextcloud.chudnick.com"
420
421# renovate
422renovate_network_name: renovate_net
423renovate_subnet: 172.25.14.0/24
424renovate_gateway: 172.25.14.1
425renovate_ipv4: 172.25.14.2
426renovate_endpoint: "https://gitea.chudnick.com/api/v1/"
427renovate_author: "renovate[bot] <renovate@chudnick.com>"
428
429# photoprism
430photoprism_admin_user: "admin"
431photoprism_auth_mode: "password"
432photoprism_site_url: "https://photos.chudnick.com"
433photoprism_nginx_config: data/photoprism/photoprism.conf
434photoprism_network_name: photoprism_net
435photoprism_subnet: 172.25.15.0/24
436photoprism_gateway: 172.25.15.1
437photoprism_ipv4: 172.25.15.2
438photoprism_external_port: 8006
439
440# gluetun
441gluetun_network_name: gluetun_net
442gluetun_subnet: 172.25.16.0/24
443gluetun_gateway: 172.25.16.1
444gluetun_ipv4: 172.25.16.2
445
446# qbittorrent
447qbittorrent_nginx_config: data/qbittorrent/qbittorrent.conf.j2
448qbittorrent_external_port: "8007"
449qbittorrent_server_name: qbittorrent.chudnick.com
450
451# sonarr
452sonarr_nginx_config: data/sonarr/sonarr.conf.j2
453sonarr_external_port: 8008
454sonarr_server_name: sonarr.chudnick.com
455
456# radarr
457radarr_nginx_config: data/radarr/radarr.conf.j2
458radarr_external_port: 8009
459radarr_server_name: radarr.chudnick.com
460
461# lidarr
462lidarr_nginx_config: data/lidarr/lidarr.conf.j2
463lidarr_external_port: 8010
464lidarr_server_name: lidarr.chudnick.com
465
466# readarr
467readarr_nginx_config: data/readarr/readarr.conf.j2
468readarr_external_port: 8011
469readarr_server_name: readarr.chudnick.com
470
471# prowlarr
472prowlarr_nginx_config: data/prowlarr/prowlarr.conf.j2
473prowlarr_external_port: 8012
474prowlarr_server_name: prowlarr.chudnick.com
475
476# bookstack
477bookstack_nginx_config: data/bookstack/bookstack.conf.j2
478bookstack_network_name: bookstack_net
479bookstack_subnet: 172.25.17.0/24
480bookstack_gateway: 172.25.17.1
481bookstack_ipv4: 172.25.17.2
482bookstack_db_ipv4: 172.25.17.3
483bookstack_server_name: "wiki.chudnick.com"
484bookstack_external_port: 8013
485
486# pywttr-docker
487pywttr_docker_nginx_config: data/pywttr_docker/pywttr_docker.conf.j2
488pywttr_docker_network_name: pywttr_docker_net
489pywttr_docker_subnet: 172.25.18.0/24
490pywttr_docker_gateway: 172.25.18.1
491pywttr_docker_ipv4: 172.25.18.2
492pywttr_docker_db_ipv4: 172.25.18.3
493pywttr_docker_server_name: "weather.chudnick.com"
494pywttr_docker_external_port: 8014
495
496# text-generation-webui
497text_generation_nginx_config: data/text_generation/text_generation.conf.j2
498text_generation_network_name: text_generation_net
499text_generation_subnet: 172.25.19.0/24
500text_generation_gateway: 172.25.19.1
501text_generation_ipv4: 172.25.19.2
502text_generation_db_ipv4: 172.25.19.3
503text_generation_server_name: "gpt.chudnick.com"
504text_generation_external_port: 8015
505text_generation_api_port: 5005
506text_generation_api_stream_port: 5000
507
508# kanboard
509kanboard_config: data/kanboard/config.php
510kanboard_nginx_config: data/kanboard/kanboard.conf.j2
511kanboard_network_name: kanboard_net
512kanboard_subnet: 172.25.20.0/24
513kanboard_gateway: 172.25.20.1
514kanboard_ipv4: 172.25.20.2
515kanboard_db_ipv4: 172.25.20.3
516kanboard_server_name: "tasks.chudnick.com"
517kanboard_external_port: 8016
518
519# firefly
520firefly_nginx_config: data/firefly/firefly.conf.j2
521firefly_network_name: firefly_net
522firefly_subnet: 172.25.21.0/24
523firefly_gateway: 172.25.21.1
524firefly_ipv4: 172.25.21.2
525firefly_db_ipv4: 172.25.21.3
526firefly_cron_ipv4: 172.25.21.4
527firefly_server_name: "finances.chudnick.com"
528firefly_external_port: 8017
529firefly_postgres_db: "firefly"
530firefly_postgres_user: "firefly"
531firefly_importer_ipv4: 172.25.21.5
532firefly_importer_server_name: "finimporter.chudnick.com"
533firefly_importer_external_port: 8018
534
535# home_assistant
536home_assistant_config: data/home_assistant/configuration.yaml
537home_assistant_nginx_config: data/home_assistant/home_assistant.conf.j2
538home_assistant_network_name: home_assistant_net
539home_assistant_subnet: 172.25.22.0/24
540home_assistant_gateway: 172.25.22.1
541home_assistant_ipv4: 172.25.22.2
542home_assistant_server_name: "homeassistant.chudnick.com"
543home_assistant_external_port: 8019
544
545# vaultwarden
546vaultwarden_nginx_config: data/vaultwarden/vaultwarden.conf.j2
547vaultwarden_network_name: vaultwarden_net
548vaultwarden_subnet: 172.25.23.0/24
549vaultwarden_gateway: 172.25.23.1
550vaultwarden_ipv4: 172.25.23.2
551vaultwarden_server_name: "vaultwarden.chudnick.com"
552vaultwarden_external_port: 8020
553
554######################
555# #
556# Networking Devices #
557# #
558######################
559
560# router
561router_hostname: charon
562ntp_server_ip: 192.168.20.2
563router_ip: 192.168.10.1
564router_user: data
565local_interface: enp34s0
566network_config_file: /etc/network/interfaces
567
568# ap
569ap_hostname: sol
570ap_ip: 192.168.10.2
diff --git a/renovate.json b/renovate.json
new file mode 100644
index 0000000..c78bf93
--- /dev/null
+++ b/renovate.json
@@ -0,0 +1,11 @@
1{
2 "$schema": "https://docs.renovatebot.com/renovate-schema.json",
3 "packageRules": [
4 {
5 "matchUpdateTypes": ["minor", "patch"],
6 "matchCurrentVersion": "!/^0/",
7 "automerge": true,
8 "ignoreTests": true
9 }
10 ]
11}
diff --git a/requirements.yml b/requirements.yml
new file mode 100644
index 0000000..3dc5df7
--- /dev/null
+++ b/requirements.yml
@@ -0,0 +1,5 @@
1---
2collections:
3 - name: freeipa.ansible_freeipa
4 - name: community.grafana
5 - name: community.docker
diff --git a/roles/linux_base/defaults/main.yml b/roles/linux_base/defaults/main.yml
new file mode 100644
index 0000000..3fb0cb5
--- /dev/null
+++ b/roles/linux_base/defaults/main.yml
@@ -0,0 +1 @@
domain: "home.local"
diff --git a/roles/linux_base/handlers/main.yml b/roles/linux_base/handlers/main.yml
new file mode 100644
index 0000000..0065ae9
--- /dev/null
+++ b/roles/linux_base/handlers/main.yml
@@ -0,0 +1,16 @@
1- name: update and upgrade - debian
2 when: ansible_facts['distribution'] == 'Debian'
3 become: yes
4 apt:
5 name: "*"
6 state: latest
7 update_cache: yes
8 register: apt_upgrade
9 retries: 100
10 until: apt_upgrade is success or ('Failed to lock apt for exclusive operation' not in apt_upgrade.msg and '/var/lib/dpkg/lock' not in apt_upgrade.msg)
11
12- name: update and upgrade - fedora
13 when: ansible_facts['distribution'] == 'Fedora'
14 dnf:
15 name: "*"
16 state: latest
diff --git a/roles/linux_base/tasks/main.yml b/roles/linux_base/tasks/main.yml
new file mode 100644
index 0000000..ef523ef
--- /dev/null
+++ b/roles/linux_base/tasks/main.yml
@@ -0,0 +1,57 @@
1- name: remove cloud config managed /etc/hosts
2 lineinfile:
3 path: /etc/cloud/cloud.cfg
4 regexp: ".*update_etc_hosts.*"
5 state: absent
6
7- name: set fully qualified hostname
8 notify:
9 - update and upgrade - debian
10 - update and upgrade - fedora
11 hostname:
12 name: "{{ ansible_hostname }}.{{ domain }}"
13
14- name: use https repos - debian
15 when: ansible_facts['distribution'] == 'Debian'
16 replace:
17 path: /etc/apt/sources.list
18 regexp: "http://"
19 replace: "https://"
20
21- name: install packages
22 package:
23 name: "{{ base_packages }}"
24 state: latest
25
26- name: allow ssh
27 when: ansible_facts['hostname'] != 'proxmox'
28 ufw:
29 rule: allow
30 name: ssh
31
32- name: reload ufw
33 when: ansible_facts['hostname'] != 'proxmox'
34 ufw:
35 state: reloaded
36
37- name: enable ufw
38 when: ansible_facts['hostname'] != 'proxmox'
39 ufw:
40 state: enabled
41
42- name: default deny incoming
43 when: ansible_facts['hostname'] != 'proxmox'
44 ufw:
45 default: deny
46 direction: incoming
47
48- name: default allow outgoing
49 when: ansible_facts['hostname'] != 'proxmox'
50 ufw:
51 default: allow
52 direction: outgoing
53
54- name: reload ufw
55 when: ansible_facts['hostname'] != 'proxmox'
56 ufw:
57 state: reloaded
diff --git a/roles/proxmox/cloudinit_guest/defaults/main.yml b/roles/proxmox/cloudinit_guest/defaults/main.yml
new file mode 100644
index 0000000..a562ff3
--- /dev/null
+++ b/roles/proxmox/cloudinit_guest/defaults/main.yml
@@ -0,0 +1,7 @@
1vm_onboot: yes
2vm_agent: yes
3vm_bridge: vmbr0
4vm_full_clone: yes
5memory_size: 512
6cpu_cores: 1
7cpu_sockets: 1
diff --git a/roles/proxmox/cloudinit_guest/tasks/main.yml b/roles/proxmox/cloudinit_guest/tasks/main.yml
new file mode 100644
index 0000000..ab958dc
--- /dev/null
+++ b/roles/proxmox/cloudinit_guest/tasks/main.yml
@@ -0,0 +1,80 @@
1- name: check if id already exists
2 stat:
3 path: "/etc/pve/qemu-server/{{ ci_base_id }}.conf"
4 register: stat_result
5
6- meta: end_play
7 when: stat_result.stat.exists
8
9- name: install packages
10 package:
11 name:
12 - python3-pip
13 - python3-requests
14
15- name: ensure latest version of proxmoxer is installed
16 become: yes
17 become_user: "{{ proxmox_username }}"
18 pip:
19 name: proxmoxer==2.0.0
20
21- name: remove any existing api token
22 command: "pveum user token remove vmadmin@pam ansible"
23 register: result
24 changed_when: result.rc == 0
25 failed_when: result.rc not in [0,255]
26
27- name: create api token
28 register: api_token
29 changed_when: result.rc == 0
30 args:
31 executable: /bin/bash
32 shell: |
33 set -eo pipefail
34 pveum user token add vmadmin@pam ansible --privsep 0 --output-format yaml | grep value | cut -d ' ' -f 2
35
36
37- name: clone template and create guest
38 become: yes
39 become_user: "{{ proxmox_username }}"
40 community.general.proxmox_kvm:
41 api_host: proxmox.home.local
42 api_user: "{{ proxmox_api_user }}"
43 api_token_id: "ansible"
44 api_token_secret: "{{ api_token.stdout }}"
45 node: proxmox
46 full: "{{ vm_full_clone }}"
47 clone: arbitrary
48 vmid: "{{ template_id }}"
49 newid: "{{ vm_id }}"
50 name: "{{ vm_name }}"
51 memory: "{{ memory_size }}"
52 sockets: "{{ cpu_sockets }}"
53 cores: "{{ cpu_cores }}"
54 bios: "{{ bios_type }}"
55 ipconfig:
56 ipconfig0: "ip={{ ip_addr }},gw={{ gateway }}"
57 net:
58 net0: "virtio,bridge={{ vm_bridge }},tag={{ vm_vlan }}"
59 nameservers: "{{ nameserver }}"
60 onboot: "{{ vm_onboot }}"
61 agent: "{{ vm_agent }}"
62 state: present
63
64- name: start vmn
65 become: yes
66 become_user: "{{ proxmox_username }}"
67 community.general.proxmox_kvm:
68 api_host: proxmox.home.local
69 api_user: "{{ proxmox_api_user }}"
70 api_token_id: "ansible"
71 api_token_secret: "{{ api_token.stdout }}"
72 node: proxmox
73 vmid: "{{ vm_id }}"
74 state: started
75
76- name: remove api token
77 command: "pveum user token remove vmadmin@pam ansible"
78 register: result
79 changed_when: result.rc == 0
80 failed_when: result.rc not in [0,255]
diff --git a/roles/proxmox/debian_cloudinit/defaults/main.yml b/roles/proxmox/debian_cloudinit/defaults/main.yml
new file mode 100644
index 0000000..dfebf34
--- /dev/null
+++ b/roles/proxmox/debian_cloudinit/defaults/main.yml
@@ -0,0 +1,8 @@
1ci_target_dir: "/home/{{ci_user}}"
2ci_memory_size: 512
3ci_base_id: 1000
4ci_disk_size: "10G"
5ci_storage: "local-lvm"
6ci_user: "initadmin"
7ssh_key_local: /home/sam/.ssh/id_rsa.pub
8ssh_key_dest: /home/vmadmin/ci_sshkey
diff --git a/roles/proxmox/debian_cloudinit/tasks/main.yml b/roles/proxmox/debian_cloudinit/tasks/main.yml
new file mode 100644
index 0000000..8ed7dfd
--- /dev/null
+++ b/roles/proxmox/debian_cloudinit/tasks/main.yml
@@ -0,0 +1,115 @@
1- name: check if id already exists
2 stat:
3 path: "/etc/pve/qemu-server/{{ ci_base_id }}.conf"
4 register: stat_result
5
6- meta: end_play
7 when: stat_result.stat.exists
8
9- name: install packages
10 package:
11 name:
12 - python3-pip
13 - python3-requests
14
15- name: ensure latest version of proxmoxer is installed
16 become: yes
17 become_user: "{{ proxmox_username }}"
18 pip:
19 name: proxmoxer==2.0.0
20
21- name: download the hashes
22 get_url:
23 url: "https://cloud.debian.org/images/cloud/bookworm/latest/SHA512SUMS"
24 dest: "{{ ci_target_dir }}"
25
26- name: get the hash
27 changed_when: false
28 args:
29 executable: /bin/bash
30 shell: |
31 set -eo pipefail
32 grep debian-12-genericcloud-amd64.qcow2 {{ ci_target_dir }}/SHA512SUMS | cut -d ' ' -f 1
33 register: sha512sum
34
35- name: download the cloud image
36 get_url:
37 url: "https://cloud.debian.org/images/cloud/bookworm/latest/debian-12-genericcloud-amd64.qcow2"
38 dest: "{{ ci_target_dir }}"
39 checksum: "sha512:{{ sha512sum.stdout }}"
40
41- name: remove any existing api token
42 command: "pveum user token remove vmadmin@pam ansible"
43 register: result
44 changed_when: result.rc == 0
45 failed_when: result.rc not in [0,255]
46
47- name: create api token
48 register: api_token
49 changed_when: result.rc == 0
50 args:
51 executable: /bin/bash
52 shell: |
53 set -eo pipefail
54 pveum user token add vmadmin@pam ansible --privsep 0 --output-format yaml | grep value | cut -d ' ' -f 2
55
56- name: create vm
57 become: yes
58 become_user: "{{ proxmox_username }}"
59 community.general.proxmox_kvm:
60 api_host: proxmox.home.local
61 api_user: "{{ proxmox_api_user }}"
62 api_token_id: "ansible"
63 api_token_secret: "{{ api_token.stdout }}"
64 node: proxmox
65 # basic settings
66 vmid: "{{ ci_base_id }}"
67 memory: "{{ ci_memory_size }}"
68 sockets: "{{ cpu_sockets }}"
69 cores: "{{ cpu_cores }}"
70 bios: "{{ bios_type }}"
71 agent: "{{ vm_agent }}"
72 state: "present"
73 # display settings
74 serial:
75 "serial0": "socket"
76 vga: "serial0"
77 # disks and boot settings
78 scsihw: "virtio-scsi-pci"
79 ide:
80 ide2: "{{ ci_storage }}:cloudinit"
81 boot: "c"
82 bootdisk: "scsi0"
83 onboot: "{{ vm_onboot }}"
84 # cloud-init
85 citype: "nocloud"
86 ciuser: "{{ ci_user }}"
87 cipassword: "{{ ci_password }}"
88 sshkeys: "{{ ci_sshkey }}"
89 # network
90 net:
91 net0: "virtio,bridge={{ ci_bridge }},tag={{ ci_vlan }}"
92 nameservers: "{{ nameserver }}"
93 template: "yes"
94
95- name: import the cloud image
96 changed_when: false
97 command:
98 cmd: "qm importdisk {{ ci_base_id }} {{ ci_target_dir }}/debian-12-genericcloud-amd64.qcow2 {{ ci_storage }}"
99 creates: "/dev/pve/vm-{{ ci_base_id }}-disk-0"
100
101- name: attach the cloud image as a new disk
102 changed_when: false
103 command:
104 cmd: "qm set {{ ci_base_id }} --scsi0 {{ ci_storage }}:vm-{{ ci_base_id }}-disk-0"
105
106- name: resize disk to standard size
107 changed_when: false
108 command:
109 cmd: "qm resize {{ ci_base_id }} scsi0 {{ ci_disk_size }}"
110
111- name: remove api token
112 command: "pveum user token remove vmadmin@pam ansible"
113 register: result
114 changed_when: result.rc == 0
115 failed_when: result.rc not in [0,255]
diff --git a/roles/proxmox/fedora_cloudinit/defaults/main.yml b/roles/proxmox/fedora_cloudinit/defaults/main.yml
new file mode 100644
index 0000000..fb44657
--- /dev/null
+++ b/roles/proxmox/fedora_cloudinit/defaults/main.yml
@@ -0,0 +1,8 @@
1ci_target_dir: "/home/{{ci_user}}"
2ci_memory_size: 512
3ci_base_id: 1001
4ci_storage: "local-lvm"
5ci_disk_size: "10G"
6ci_user: "initadmin"
7ssh_key_local: files/id_rsa.pub
8ssh_key_dest: /tmp/ci_sshkey
diff --git a/roles/proxmox/fedora_cloudinit/tasks/main.yml b/roles/proxmox/fedora_cloudinit/tasks/main.yml
new file mode 100644
index 0000000..61ed185
--- /dev/null
+++ b/roles/proxmox/fedora_cloudinit/tasks/main.yml
@@ -0,0 +1,122 @@
1- name: download the hashes
2 get_url:
3 url: "https://getfedora.org/static/checksums/36/images/Fedora-Cloud-36-1.5-x86_64-CHECKSUM"
4 dest: "{{ ci_target_dir }}"
5
6- name: install gpg
7 package:
8 name: gnupg
9 state: latest
10
11- name: download the GPG key
12 get_url:
13 url: "https://getfedora.org/static/fedora.gpg"
14 dest: "{{ ci_target_dir }}"
15
16- name: import gpg key
17 changed_when: false
18 args:
19 executable: /bin/bash
20 shell: |
21 set -eo pipefail
22 cat {{ ci_target_dir }}/fedora.gpg | gpg --import
23
24- name: verify checksum file
25 command:
26 cmd: "gpg --verify {{ ci_target_dir }}/Fedora-Cloud-36-1.5-x86_64-CHECKSUM"
27 register: result
28 changed_when: false
29 failed_when: result.rc > 0
30
31- name: fail if unable to gpg verify checksums
32 fail:
33 msg: "failed to verify the checksums"
34 when: result.rc > 0
35
36- name: get the hash
37 shell:
38 cmd: "grep 'qcow2)' {{ ci_target_dir }}/Fedora-Cloud-36-1.5-x86_64-CHECKSUM | cut -d '=' -f 2 | tr -d ' '"
39 changed_when: false
40 register: sha256sum
41
42- name: download the cloud image
43 get_url:
44 url: "https://download.fedoraproject.org/pub/fedora/linux/releases/36/Cloud/x86_64/images/Fedora-Cloud-Base-36-1.5.x86_64.qcow2"
45 dest: "{{ ci_target_dir }}"
46 checksum: "sha256:{{ sha256sum.stdout }}"
47
48- name: remove any existing api token
49 command: "pveum user token remove vmadmin@pam ansible"
50 register: result
51 changed_when: result.rc == 0
52 failed_when: result.rc not in [0,255]
53
54- name: create api token
55 register: api_token
56 changed_when: result.rc == 0
57 args:
58 executable: /bin/bash
59 shell: |
60 set -eo pipefail
61 pveum user token add vmadmin@pam ansible --privsep 0 --output-format yaml | grep value | cut -d ' ' -f 2
62
63- name: create vm
64 become: yes
65 become_user: "{{ proxmox_username }}"
66 community.general.proxmox_kvm:
67 api_host: proxmox.home.local
68 api_user: "{{ proxmox_api_user }}"
69 api_token_id: "ansible"
70 api_token_secret: "{{ api_token.stdout }}"
71 node: proxmox
72 # basic settings
73 vmid: "{{ ci_base_id }}"
74 memory: "{{ ci_memory_size }}"
75 sockets: "{{ cpu_sockets }}"
76 cores: "{{ cpu_cores }}"
77 bios: "{{ bios_type }}"
78 agent: "{{ vm_agent }}"
79 state: "present"
80 # display settings
81 serial:
82 "serial0": "socket"
83 vga: "serial0"
84 # disks and boot settings
85 scsihw: "virtio-scsi-pci"
86 ide:
87 ide2: "{{ ci_storage }}:cloudinit"
88 boot: "c"
89 bootdisk: "scsi0"
90 onboot: "{{ vm_onboot }}"
91 # cloud-init
92 citype: "nocloud"
93 ciuser: "{{ ci_user }}"
94 cipassword: "{{ ci_password }}"
95 sshkeys: "{{ ci_sshkey }}"
96 # network
97 net:
98 net0: "virtio,bridge={{ ci_bridge }},tag={{ ci_vlan }}"
99 nameservers: "{{ nameserver }}"
100 template: "yes"
101
102- name: import the cloud image
103 changed_when: false
104 command:
105 cmd: "qm importdisk {{ ci_base_id }} {{ ci_target_dir }}/Fedora-Cloud-Base-36-1.5.x86_64.qcow2 {{ ci_storage }}"
106 creates: "/dev/pve/vm-{{ ci_base_id }}-disk-0"
107
108- name: attach the cloud image as a new disk
109 changed_when: false
110 command:
111 cmd: "qm set {{ ci_base_id }} --scsi0 {{ ci_storage }}:vm-{{ ci_base_id }}-disk-0"
112
113- name: resize disk to standard size
114 changed_when: false
115 command:
116 cmd: "qm resize {{ ci_base_id }} scsi0 {{ ci_disk_size }}"
117
118- name: remove api token
119 command: "pveum user token remove vmadmin@pam ansible"
120 register: result
121 changed_when: result.rc == 0
122 failed_when: result.rc not in [0,255]
diff --git a/roles/proxmox/proxmox_backup_server/tasks/main.yml b/roles/proxmox/proxmox_backup_server/tasks/main.yml
new file mode 100644
index 0000000..3e91a19
--- /dev/null
+++ b/roles/proxmox/proxmox_backup_server/tasks/main.yml
@@ -0,0 +1,42 @@
1- name: add proxmox backup repo
2 apt_repository:
3 repo: deb http://download.proxmox.com/debian/pbs bullseye pbs-no-subscription
4 state: present
5 update_cache: yes
6
7- name: install proxmox backup server and client
8 package:
9 name:
10 - proxmox-backup-server
11 - proxmox-backup-client
12
13- name: create datastore
14 command:
15 cmd: "proxmox-backup-manager datastore create {{ pbs_datastore }} {{ pbs_datastore_path }} --keep-last {{ pbs_keep_last }} --keep-daily {{ pbs_keep_daily }} --keep-weekly {{ pbs_keep_weekly }} --keep-monthly {{ pbs_keep_monthly }} --keep-yearly {{ pbs_keep_yearly }}"
16 register: result
17 changed_when: false
18 failed_when: result.rc not in [255]
19
20- name: create backup admin
21 command:
22 cmd: "proxmox-backup-manager user create {{ pbs_admin }} --password {{ pbs_admin_password }}"
23 register: result
24 changed_when: false
25 failed_when: result.rc not in [255]
26
27- name: assign permissions for backup admin
28 changed_when: false
29 command:
30 cmd: "proxmox-backup-manager acl update / Admin --auth-id {{ pbs_admin }}"
31
32- name: create backup user
33 command:
34 cmd: "proxmox-backup-manager user create {{ pbs_user }} --password {{ pbs_password }}"
35 register: result
36 failed_when: result.rc not in [255]
37 changed_when: false
38
39- name: assign permissions for backup user
40 changed_when: false
41 command:
42 cmd: "proxmox-backup-manager acl update / DatastoreBackup --auth-id {{ pbs_user }}"
diff --git a/roles/proxmox/pve_backup/tasks/main.yml b/roles/proxmox/pve_backup/tasks/main.yml
new file mode 100644
index 0000000..eba51d9
--- /dev/null
+++ b/roles/proxmox/pve_backup/tasks/main.yml
@@ -0,0 +1,17 @@
1- name: create cron job for root backup of proxmox ve
2 cron:
3 name: "proxmox / backup"
4 cron_file: backup
5 hour: "23"
6 minute: "0"
7 user: root
8 job: "PBS_PASSWORD='{{ pbs_password }}' PBS_FINGERPRINT={{ pbs_fingerprint }} proxmox-backup-client backup root.pxar:/ --repository {{ pbs_user }}@{{ pbs_host }}:{{ pbs_datastore }}"
9
10- name: create cron job for /etc/pve backup of proxmox ve
11 cron:
12 name: "proxmox /etc/pve backup"
13 cron_file: backup
14 hour: "23"
15 minute: "0"
16 user: root
17 job: "PBS_PASSWORD='{{ pbs_password }}' PBS_FINGERPRINT={{ pbs_fingerprint }} proxmox-backup-client backup pve.pxar:/etc/pve --repository {{ pbs_user }}@{{ pbs_host }}:{{ pbs_datastore }}"
diff --git a/roles/proxmox/system/defaults/main.yml b/roles/proxmox/system/defaults/main.yml
new file mode 100644
index 0000000..0091ea1
--- /dev/null
+++ b/roles/proxmox/system/defaults/main.yml
@@ -0,0 +1,8 @@
1username: vmadmin
2ssh_public_key: changme
3oath_key: changeme
4raid_id: "0"
5raid_level: "1"
6raid_devices: "/dev/sda1 /dev/sdb1"
7raid_name: "prometheus:0"
8
diff --git a/roles/proxmox/system/tasks/main.yml b/roles/proxmox/system/tasks/main.yml
new file mode 100644
index 0000000..ac84900
--- /dev/null
+++ b/roles/proxmox/system/tasks/main.yml
@@ -0,0 +1,30 @@
1---
2- name: remove enterprise repo
3 file:
4 path: /etc/apt/sources.list.d/pve-enterprise.list
5 state: absent
6
7- name: add proxmox no subscription repo
8 apt_repository:
9 repo: deb http://download.proxmox.com/debian/pve bookworm pve-no-subscription
10
11- name: create non-root user
12 user:
13 name: "{{ proxmox_username }}"
14 groups:
15 - sudo
16 shell: /bin/bash
17
18- name: give passwordless sudo to sudo group
19 lineinfile:
20 path: /etc/sudoers
21 state: present
22 regexp: '^%sudo'
23 line: '%sudo ALL=(ALL) NOPASSWD: ALL'
24 validate: '/usr/sbin/visudo -cf %s'
25
26- name: deploy ssh public key
27 authorized_key:
28 user: "{{ proxmox_username }}"
29 state: present
30 key: "{{ lookup('file', 'data/common/id_rsa.pub') }}"
diff --git a/roles/proxmox/system/tasks/proxmox_repo.yml b/roles/proxmox/system/tasks/proxmox_repo.yml
new file mode 100644
index 0000000..bf2508d
--- /dev/null
+++ b/roles/proxmox/system/tasks/proxmox_repo.yml
@@ -0,0 +1,8 @@
1- name: remove enterprise repo
2 file:
3 path: /etc/apt/sources.list.d/pve-enterprise.list
4 state: absent
5
6- name: add proxmox no subscription repo
7 apt_repository:
8 repo: deb http://download.proxmox.com/debian/pve bookworm pve-no-subscription
diff --git a/roles/proxmox/system/tasks/user.yml b/roles/proxmox/system/tasks/user.yml
new file mode 100644
index 0000000..2ba337a
--- /dev/null
+++ b/roles/proxmox/system/tasks/user.yml
@@ -0,0 +1,28 @@
1- name: create non-root user
2 user:
3 name: "{{ username }}"
4 password: "{{ password | password_hash('sha512') }}"
5 groups:
6 - sudo
7 shell: /bin/bash
8 update_password: on_create
9 register: newuser
10
11- name: ensure primary user group exists
12 group:
13 name: "{{ username }}"
14 state: present
15
16- name: give passwordless sudo to sudo group
17 lineinfile:
18 path: /etc/sudoers
19 state: present
20 regexp: '^%sudo'
21 line: '%sudo ALL=(ALL) NOPASSWD: ALL'
22 validate: '/usr/sbin/visudo -cf %s'
23
24- name: deploy ssh public key
25 authorized_key:
26 user: "{{ username }}"
27 state: present
28 key: "{{ ssh_public_key }}"
diff --git a/roles/services/chronyd/handlers/main.yml b/roles/services/chronyd/handlers/main.yml
new file mode 100644
index 0000000..7e6f687
--- /dev/null
+++ b/roles/services/chronyd/handlers/main.yml
@@ -0,0 +1,4 @@
1- name: restart chronyd
2 service:
3 name: chronyd
4 state: restarted
diff --git a/roles/services/chronyd/tasks/main.yml b/roles/services/chronyd/tasks/main.yml
new file mode 100644
index 0000000..73fdc28
--- /dev/null
+++ b/roles/services/chronyd/tasks/main.yml
@@ -0,0 +1,30 @@
1- name: install packages
2 package:
3 name: chrony
4 state: latest
5
6- name: deploy chrony configuration
7 when: ansible_facts['distribution'] == 'Debian'
8 notify: restart chronyd
9 copy:
10 src: "{{ chrony_config }}"
11 dest: /etc/chrony/chrony.conf
12 owner: root
13 group: root
14 mode: '0644'
15
16- name: deploy chrony configuration
17 when: ansible_facts['distribution'] == 'Fedora'
18 notify: restart chronyd
19 copy:
20 src: "{{ chrony_config }}"
21 dest: /etc/chrony.conf
22 owner: root
23 group: root
24 mode: '0644'
25
26- name: make sure chronyd is enabled
27 systemd:
28 name: chronyd
29 enabled: yes
30 masked: no
diff --git a/roles/services/containers/arr_stack/handlers/main.yml b/roles/services/containers/arr_stack/handlers/main.yml
new file mode 100644
index 0000000..5463835
--- /dev/null
+++ b/roles/services/containers/arr_stack/handlers/main.yml
@@ -0,0 +1,4 @@
1- name: restart nginx
2 service:
3 name: nginx
4 state: restarted
diff --git a/roles/services/containers/arr_stack/tasks/gluetun.yml b/roles/services/containers/arr_stack/tasks/gluetun.yml
new file mode 100644
index 0000000..e47d55a
--- /dev/null
+++ b/roles/services/containers/arr_stack/tasks/gluetun.yml
@@ -0,0 +1,105 @@
1- name: set image fact
2 set_fact:
3 image: qmcgaw/gluetun:v3.34.3
4
5- name: set other facts
6 vars:
7 array: "{{ image.split('/', 1) }}"
8 set_fact:
9 repo_tag: "{{ array.1 }}"
10 custom_registry: "{{ docker_registry_url + '/' + docker_registry_username }}"
11
12- name: create gluetun directory
13 file:
14 path: "{{ docker_home }}/gluetun"
15 state: directory
16 owner: "{{ docker_username }}"
17 group: "{{ docker_username }}"
18 mode: '0755'
19
20- name: create gluetun data directory
21 file:
22 path: "{{ docker_home }}/gluetun/data"
23 state: directory
24 owner: "{{ docker_username }}"
25 group: "{{ docker_username }}"
26 mode: '0755'
27
28- name: login to docker registry
29 become: yes
30 become_user: "{{ docker_username }}"
31 environment:
32 XDG_RUNTIME_DIR: "/run/user/{{ docker_uid }}"
33 docker_login:
34 docker_host: "unix://run/user/{{ docker_uid }}/docker.sock"
35 registry_url: "{{ docker_registry_url }}"
36 username: "{{ docker_registry_username }}"
37 password: "{{ docker_registry_password }}"
38
39- name: pull and push gluetun image
40 become: yes
41 become_user: "{{ docker_username }}"
42 environment:
43 XDG_RUNTIME_DIR: "/run/user/{{ docker_uid }}"
44 docker_image:
45 name: "{{ image }}"
46 repository: "{{ custom_registry }}/{{ repo_tag }}"
47 push: yes
48 docker_host: "unix://run/user/{{ docker_uid }}/docker.sock"
49 source: pull
50 force_source: yes
51
52- name: create gluetun docker network
53 docker_network:
54 name: "{{ gluetun_network_name }}"
55 docker_host: "unix://run/user/{{ docker_uid }}/docker.sock"
56 driver: bridge
57 ipam_config:
58 - subnet: "{{ gluetun_subnet }}"
59 gateway: "{{ gluetun_gateway }}"
60
61- name: create and deploy gluetun container
62 become: yes
63 become_user: "{{ docker_username }}"
64 environment:
65 XDG_RUNTIME_DIR: "/run/user/{{ docker_uid }}"
66 docker_container:
67 name: "gluetun"
68 hostname: "gluetun"
69 image: "{{ custom_registry }}/{{ repo_tag }}"
70 recreate: yes
71 pull: yes
72 docker_host: "unix://run/user/{{ docker_uid }}/docker.sock"
73 capabilities:
74 - net_admin
75 devices:
76 - "/dev/net/tun:/dev/net/tun"
77 purge_networks: yes
78 networks:
79 - name: "{{ gluetun_network_name }}"
80 ipv4_address: "{{ gluetun_ipv4 }}"
81 ports:
82 - "127.0.0.1:{{ qbittorrent_external_port }}:{{ qbittorrent_external_port }}"
83 - "127.0.0.1:{{ sonarr_external_port }}:8989"
84 - "127.0.0.1:{{ radarr_external_port }}:7878"
85 - "127.0.0.1:{{ lidarr_external_port }}:8686"
86 - "127.0.0.1:{{ readarr_external_port }}:8787"
87 - "127.0.0.1:{{ prowlarr_external_port }}:9696"
88 state: 'started'
89 comparisons:
90 '*': strict
91 restart_policy: unless-stopped
92 env:
93 "TZ": "{{ timezone }}"
94 "VPN_SERVICE_PROVIDER": "mullvad"
95 "VPN_TYPE": "wireguard"
96 "WIREGUARD_PRIVATE_KEY": "{{ wireguard_privkey }}"
97 "WIREGUARD_ADDRESSES": "{{ wireguard_addrs }}"
98 "SERVER_CITIES": "{{ gluetun_cities }}"
99 "DOT_PROVIDERS": "quad9"
100 "BLOCK_MALICIOUS": "on"
101 "BLOCK_SURVEILLANCE": "on"
102 "BLOCK_ADS": "on"
103 "HEALTH_TARGET_ADDRESS": "www.debian.org:443"
104 volumes:
105 - "{{ docker_home }}/gluetun/data:/gluetun"
diff --git a/roles/services/containers/arr_stack/tasks/lidarr.yml b/roles/services/containers/arr_stack/tasks/lidarr.yml
new file mode 100644
index 0000000..1f70437
--- /dev/null
+++ b/roles/services/containers/arr_stack/tasks/lidarr.yml
@@ -0,0 +1,93 @@
1- name: set image fact
2 set_fact:
3 image: linuxserver/lidarr:1.2.6-nightly
4
5- name: set other facts
6 vars:
7 array: "{{ image.split('/', 1) }}"
8 set_fact:
9 repo_tag: "{{ array.1 }}"
10 custom_registry: "{{ docker_registry_url + '/' + docker_registry_username }}"
11
12- name: create lidarr directory
13 file:
14 path: "{{ docker_home }}/lidarr"
15 state: directory
16 owner: "{{ docker_username }}"
17 group: "{{ docker_username }}"
18 mode: '0755'
19
20- name: create lidarr config directory
21 file:
22 path: "{{ docker_home }}/lidarr/config"
23 state: directory
24 owner: "{{ docker_username }}"
25 group: "{{ docker_username }}"
26 mode: '0755'
27
28- name: login to docker registry
29 become: yes
30 become_user: "{{ docker_username }}"
31 environment:
32 xdg_runtime_dir: "/run/user/{{ docker_uid }}"
33 docker_login:
34 docker_host: "unix://run/user/{{ docker_uid }}/docker.sock"
35 registry_url: "{{ docker_registry_url }}"
36 username: "{{ docker_registry_username }}"
37 password: "{{ docker_registry_password }}"
38
39- name: pull and push lidarr image
40 become: yes
41 become_user: "{{ docker_username }}"
42 environment:
43 xdg_runtime_dir: "/run/user/{{ docker_uid }}"
44 docker_image:
45 name: "{{ image }}"
46 repository: "{{ custom_registry }}/{{ repo_tag }}"
47 push: yes
48 docker_host: "unix://run/user/{{ docker_uid }}/docker.sock"
49 source: pull
50 force_source: yes
51
52- name: create and deploy lidarr container
53 become: yes
54 become_user: "{{ docker_username }}"
55 environment:
56 xdg_runtime_dir: "/run/user/{{ docker_uid }}"
57 docker_container:
58 name: "lidarr"
59 image: "{{ custom_registry }}/{{ repo_tag }}"
60 recreate: yes
61 pull: yes
62 docker_host: "unix://run/user/{{ docker_uid }}/docker.sock"
63 purge_networks: yes
64 network_mode: "container:gluetun"
65 state: 'started'
66 comparisons:
67 '*': strict
68 restart_policy: unless-stopped
69 env:
70 "tz": "{{ timezone }}"
71 "PUID": "0"
72 "PGID": "0"
73 volumes:
74 - "{{ docker_home }}/lidarr/config:/config"
75 - "{{ docker_home }}/arr/data:/data"
76
77- name: deploy nginx configuration
78 notify: restart nginx
79 register: nginx_config
80 template:
81 src: "{{ lidarr_nginx_config }}"
82 dest: /etc/nginx/sites-available/lidarr.conf
83 owner: root
84 group: root
85 mode: '0644'
86
87- name: symlink site
88 file:
89 src: /etc/nginx/sites-available/lidarr.conf
90 dest: /etc/nginx/sites-enabled/lidarr.conf
91 owner: root
92 group: root
93 state: link
diff --git a/roles/services/containers/arr_stack/tasks/main.yml b/roles/services/containers/arr_stack/tasks/main.yml
new file mode 100644
index 0000000..ee27384
--- /dev/null
+++ b/roles/services/containers/arr_stack/tasks/main.yml
@@ -0,0 +1,130 @@
1- name: create arr directory structure
2 file:
3 path: "{{ docker_home }}/arr"
4 state: directory
5 owner: "{{ docker_username }}"
6 group: "{{ docker_username }}"
7 mode: '0775'
8- name: create arr directory structure
9 file:
10 path: "{{ docker_home }}/arr/data"
11 state: directory
12 owner: "{{ docker_username }}"
13 group: "{{ docker_username }}"
14 mode: '0775'
15
16- name: create arr/data directory structure
17 file:
18 path: "{{ docker_home }}/arr/data/torrents"
19 state: directory
20 owner: "{{ docker_username }}"
21 group: "{{ docker_username }}"
22 mode: '0775'
23- name: create arr/data directory structure
24 file:
25 path: "{{ docker_home }}/arr/data/torrents/movies"
26 state: directory
27 owner: "{{ docker_username }}"
28 group: "{{ docker_username }}"
29 mode: '0775'
30- name: create arr/data directory structure
31 file:
32 path: "{{ docker_home }}/arr/data/torrents/music"
33 state: directory
34 owner: "{{ docker_username }}"
35 group: "{{ docker_username }}"
36 mode: '0775'
37- name: create arr/data directory structure
38 file:
39 path: "{{ docker_home }}/arr/data/torrents/books"
40 state: directory
41 owner: "{{ docker_username }}"
42 group: "{{ docker_username }}"
43 mode: '0775'
44- name: create arr/data directory structure
45 file:
46 path: "{{ docker_home }}/arr/data/torrents/tv"
47 state: directory
48 owner: "{{ docker_username }}"
49 group: "{{ docker_username }}"
50 mode: '0775'
51
52- name: create arr/data directory structure
53 file:
54 path: "{{ docker_home }}/arr/data/usenet"
55 state: directory
56 owner: "{{ docker_username }}"
57 group: "{{ docker_username }}"
58 mode: '0775'
59- name: create arr/data directory structure
60 file:
61 path: "{{ docker_home }}/arr/data/usenet/movies"
62 state: directory
63 owner: "{{ docker_username }}"
64 group: "{{ docker_username }}"
65 mode: '0775'
66- name: create arr/data directory structure
67 file:
68 path: "{{ docker_home }}/arr/data/usenet/music"
69 state: directory
70 owner: "{{ docker_username }}"
71 group: "{{ docker_username }}"
72 mode: '0775'
73- name: create arr/data directory structure
74 file:
75 path: "{{ docker_home }}/arr/data/usenet/books"
76 state: directory
77 owner: "{{ docker_username }}"
78 group: "{{ docker_username }}"
79 mode: '0775'
80- name: create arr/data directory structure
81 file:
82 path: "{{ docker_home }}/arr/data/usenet/tv"
83 state: directory
84 owner: "{{ docker_username }}"
85 group: "{{ docker_username }}"
86 mode: '0775'
87
88- name: create arr/data directory structure
89 file:
90 path: "{{ docker_home }}/arr/data/media"
91 state: directory
92 owner: "{{ docker_username }}"
93 group: "{{ docker_username }}"
94 mode: '0775'
95- name: create arr/data directory structure
96 file:
97 path: "{{ docker_home }}/arr/data/media/movies"
98 state: directory
99 owner: "{{ docker_username }}"
100 group: "{{ docker_username }}"
101 mode: '0775'
102- name: create arr/data directory structure
103 file:
104 path: "{{ docker_home }}/arr/data/media/music"
105 state: directory
106 owner: "{{ docker_username }}"
107 group: "{{ docker_username }}"
108 mode: '0775'
109- name: create arr/data directory structure
110 file:
111 path: "{{ docker_home }}/arr/data/media/books"
112 state: directory
113 owner: "{{ docker_username }}"
114 group: "{{ docker_username }}"
115 mode: '0775'
116- name: create arr/data directory structure
117 file:
118 path: "{{ docker_home }}/arr/data/media/tv"
119 state: directory
120 owner: "{{ docker_username }}"
121 group: "{{ docker_username }}"
122 mode: '0775'
123
124- include_tasks: gluetun.yml
125- include_tasks: qbittorrent.yml
126- include_tasks: sonarr.yml
127- include_tasks: radarr.yml
128- include_tasks: lidarr.yml
129- include_tasks: readarr.yml
130- include_tasks: prowlarr.yml
diff --git a/roles/services/containers/arr_stack/tasks/prowlarr.yml b/roles/services/containers/arr_stack/tasks/prowlarr.yml
new file mode 100644
index 0000000..53f1a45
--- /dev/null
+++ b/roles/services/containers/arr_stack/tasks/prowlarr.yml
@@ -0,0 +1,92 @@
1- name: set image fact
2 set_fact:
3 image: linuxserver/prowlarr:1.6.2-nightly
4
5- name: set other facts
6 vars:
7 array: "{{ image.split('/', 1) }}"
8 set_fact:
9 repo_tag: "{{ array.1 }}"
10 custom_registry: "{{ docker_registry_url + '/' + docker_registry_username }}"
11
12- name: create prowlarr directory
13 file:
14 path: "{{ docker_home }}/prowlarr"
15 state: directory
16 owner: "{{ docker_username }}"
17 group: "{{ docker_username }}"
18 mode: '0755'
19
20- name: create prowlarr config directory
21 file:
22 path: "{{ docker_home }}/prowlarr/config"
23 state: directory
24 owner: "{{ docker_username }}"
25 group: "{{ docker_username }}"
26 mode: '0755'
27
28- name: login to docker registry
29 become: yes
30 become_user: "{{ docker_username }}"
31 environment:
32 XDG_RUNTIME_DIR: "/run/user/{{ docker_uid }}"
33 docker_login:
34 docker_host: "unix://run/user/{{ docker_uid }}/docker.sock"
35 registry_url: "{{ docker_registry_url }}"
36 username: "{{ docker_registry_username }}"
37 password: "{{ docker_registry_password }}"
38
39- name: pull and push prowlarr image
40 become: yes
41 become_user: "{{ docker_username }}"
42 environment:
43 XDG_RUNTIME_DIR: "/run/user/{{ docker_uid }}"
44 docker_image:
45 name: "{{ image }}"
46 repository: "{{ custom_registry }}/{{ repo_tag }}"
47 push: yes
48 docker_host: "unix://run/user/{{ docker_uid }}/docker.sock"
49 source: pull
50 force_source: yes
51
52- name: create and deploy prowlarr container
53 become: yes
54 become_user: "{{ docker_username }}"
55 environment:
56 XDG_RUNTIME_DIR: "/run/user/{{ docker_uid }}"
57 docker_container:
58 name: "prowlarr"
59 image: "{{ custom_registry }}/{{ repo_tag }}"
60 recreate: yes
61 pull: yes
62 docker_host: "unix://run/user/{{ docker_uid }}/docker.sock"
63 purge_networks: yes
64 network_mode: "container:gluetun"
65 state: 'started'
66 comparisons:
67 '*': strict
68 restart_policy: unless-stopped
69 env:
70 "TZ": "{{ timezone }}"
71 "PUID": "0"
72 "PGID": "0"
73 volumes:
74 - "{{ docker_home }}/prowlarr/config:/config"
75
76- name: deploy nginx configuration
77 notify: restart nginx
78 register: nginx_config
79 template:
80 src: "{{ prowlarr_nginx_config }}"
81 dest: /etc/nginx/sites-available/prowlarr.conf
82 owner: root
83 group: root
84 mode: '0644'
85
86- name: symlink site
87 file:
88 src: /etc/nginx/sites-available/prowlarr.conf
89 dest: /etc/nginx/sites-enabled/prowlarr.conf
90 owner: root
91 group: root
92 state: link
diff --git a/roles/services/containers/arr_stack/tasks/qbittorrent.yml b/roles/services/containers/arr_stack/tasks/qbittorrent.yml
new file mode 100644
index 0000000..25e554f
--- /dev/null
+++ b/roles/services/containers/arr_stack/tasks/qbittorrent.yml
@@ -0,0 +1,94 @@
1- name: set image fact
2 set_fact:
3 image: linuxserver/qbittorrent:4.5.4
4
5- name: set other facts
6 vars:
7 array: "{{ image.split('/', 1) }}"
8 set_fact:
9 repo_tag: "{{ array.1 }}"
10 custom_registry: "{{ docker_registry_url + '/' + docker_registry_username }}"
11
12- name: create qbittorrent directory
13 file:
14 path: "{{ docker_home }}/qbittorrent"
15 state: directory
16 owner: "{{ docker_username }}"
17 group: "{{ docker_username }}"
18 mode: '0755'
19
20- name: create qbittorrent config directory
21 file:
22 path: "{{ docker_home }}/qbittorrent/config"
23 state: directory
24 owner: "{{ docker_username }}"
25 group: "{{ docker_username }}"
26 mode: '0755'
27
28- name: login to docker registry
29 become: yes
30 become_user: "{{ docker_username }}"
31 environment:
32 XDG_RUNTIME_DIR: "/run/user/{{ docker_uid }}"
33 docker_login:
34 docker_host: "unix://run/user/{{ docker_uid }}/docker.sock"
35 registry_url: "{{ docker_registry_url }}"
36 username: "{{ docker_registry_username }}"
37 password: "{{ docker_registry_password }}"
38
39- name: pull and push qbittorrent image
40 become: yes
41 become_user: "{{ docker_username }}"
42 environment:
43 XDG_RUNTIME_DIR: "/run/user/{{ docker_uid }}"
44 docker_image:
45 name: "{{ image }}"
46 repository: "{{ custom_registry }}/{{ repo_tag }}"
47 push: yes
48 docker_host: "unix://run/user/{{ docker_uid }}/docker.sock"
49 source: pull
50 force_source: yes
51
52- name: create and deploy qbittorrent container
53 become: yes
54 become_user: "{{ docker_username }}"
55 environment:
56 XDG_RUNTIME_DIR: "/run/user/{{ docker_uid }}"
57 docker_container:
58 name: "qbittorrent"
59 image: "{{ custom_registry }}/{{ repo_tag }}"
60 recreate: yes
61 pull: yes
62 docker_host: "unix://run/user/{{ docker_uid }}/docker.sock"
63 purge_networks: yes
64 network_mode: "container:gluetun"
65 state: 'started'
66 comparisons:
67 '*': strict
68 restart_policy: unless-stopped
69 env:
70 "TZ": "{{ timezone }}"
71 "WEBUI_PORT": "{{ qbittorrent_external_port }}"
72 "PUID": "0"
73 "PGID": "0"
74 volumes:
75 - "{{ docker_home }}/qbittorrent/config:/config"
76 - "{{ docker_home }}/arr/data:/data"
77
78- name: deploy nginx configuration
79 notify: restart nginx
80 register: nginx_config
81 template:
82 src: "{{ qbittorrent_nginx_config }}"
83 dest: /etc/nginx/sites-available/qbittorrent.conf
84 owner: root
85 group: root
86 mode: '0644'
87
88- name: symlink site
89 file:
90 src: /etc/nginx/sites-available/qbittorrent.conf
91 dest: /etc/nginx/sites-enabled/qbittorrent.conf
92 owner: root
93 group: root
94 state: link
diff --git a/roles/services/containers/arr_stack/tasks/radarr.yml b/roles/services/containers/arr_stack/tasks/radarr.yml
new file mode 100644
index 0000000..2e98c47
--- /dev/null
+++ b/roles/services/containers/arr_stack/tasks/radarr.yml
@@ -0,0 +1,93 @@
1- name: set image fact
2 set_fact:
3 image: linuxserver/radarr:4.6.4-nightly
4
5- name: set other facts
6 vars:
7 array: "{{ image.split('/', 1) }}"
8 set_fact:
9 repo_tag: "{{ array.1 }}"
10 custom_registry: "{{ docker_registry_url + '/' + docker_registry_username }}"
11
12- name: create radarr directory
13 file:
14 path: "{{ docker_home }}/radarr"
15 state: directory
16 owner: "{{ docker_username }}"
17 group: "{{ docker_username }}"
18 mode: '0755'
19
20- name: create radarr config directory
21 file:
22 path: "{{ docker_home }}/radarr/config"
23 state: directory
24 owner: "{{ docker_username }}"
25 group: "{{ docker_username }}"
26 mode: '0755'
27
28- name: login to docker registry
29 become: yes
30 become_user: "{{ docker_username }}"
31 environment:
32 xdg_runtime_dir: "/run/user/{{ docker_uid }}"
33 docker_login:
34 docker_host: "unix://run/user/{{ docker_uid }}/docker.sock"
35 registry_url: "{{ docker_registry_url }}"
36 username: "{{ docker_registry_username }}"
37 password: "{{ docker_registry_password }}"
38
39- name: pull and push radarr image
40 become: yes
41 become_user: "{{ docker_username }}"
42 environment:
43 xdg_runtime_dir: "/run/user/{{ docker_uid }}"
44 docker_image:
45 name: "{{ image }}"
46 repository: "{{ custom_registry }}/{{ repo_tag }}"
47 push: yes
48 docker_host: "unix://run/user/{{ docker_uid }}/docker.sock"
49 source: pull
50 force_source: yes
51
52- name: create and deploy radarr container
53 become: yes
54 become_user: "{{ docker_username }}"
55 environment:
56 xdg_runtime_dir: "/run/user/{{ docker_uid }}"
57 docker_container:
58 name: "radarr"
59 image: "{{ custom_registry }}/{{ repo_tag }}"
60 recreate: yes
61 pull: yes
62 docker_host: "unix://run/user/{{ docker_uid }}/docker.sock"
63 purge_networks: yes
64 network_mode: "container:gluetun"
65 state: 'started'
66 comparisons:
67 '*': strict
68 restart_policy: unless-stopped
69 env:
70 "tz": "{{ timezone }}"
71 "PUID": "0"
72 "PGID": "0"
73 volumes:
74 - "{{ docker_home }}/radarr/config:/config"
75 - "{{ docker_home }}/arr/data:/data"
76
77- name: deploy nginx configuration
78 notify: restart nginx
79 register: nginx_config
80 template:
81 src: "{{ radarr_nginx_config }}"
82 dest: /etc/nginx/sites-available/radarr.conf
83 owner: root
84 group: root
85 mode: '0644'
86
87- name: symlink site
88 file:
89 src: /etc/nginx/sites-available/radarr.conf
90 dest: /etc/nginx/sites-enabled/radarr.conf
91 owner: root
92 group: root
93 state: link
diff --git a/roles/services/containers/arr_stack/tasks/readarr.yml b/roles/services/containers/arr_stack/tasks/readarr.yml
new file mode 100644
index 0000000..bd8b2ec
--- /dev/null
+++ b/roles/services/containers/arr_stack/tasks/readarr.yml
@@ -0,0 +1,93 @@
1- name: set image fact
2 set_fact:
3 image: linuxserver/readarr:0.2.0-nightly
4
5- name: set other facts
6 vars:
7 array: "{{ image.split('/', 1) }}"
8 set_fact:
9 repo_tag: "{{ array.1 }}"
10 custom_registry: "{{ docker_registry_url + '/' + docker_registry_username }}"
11
12- name: create readarr directory
13 file:
14 path: "{{ docker_home }}/readarr"
15 state: directory
16 owner: "{{ docker_username }}"
17 group: "{{ docker_username }}"
18 mode: '0755'
19
20- name: create readarr config directory
21 file:
22 path: "{{ docker_home }}/readarr/config"
23 state: directory
24 owner: "{{ docker_username }}"
25 group: "{{ docker_username }}"
26 mode: '0755'
27
28- name: login to docker registry
29 become: yes
30 become_user: "{{ docker_username }}"
31 environment:
32 XDG_RUNTIME_DIR: "/run/user/{{ docker_uid }}"
33 docker_login:
34 docker_host: "unix://run/user/{{ docker_uid }}/docker.sock"
35 registry_url: "{{ docker_registry_url }}"
36 username: "{{ docker_registry_username }}"
37 password: "{{ docker_registry_password }}"
38
39- name: pull and push readarr image
40 become: yes
41 become_user: "{{ docker_username }}"
42 environment:
43 XDG_RUNTIME_DIR: "/run/user/{{ docker_uid }}"
44 docker_image:
45 name: "{{ image }}"
46 repository: "{{ custom_registry }}/{{ repo_tag }}"
47 push: yes
48 docker_host: "unix://run/user/{{ docker_uid }}/docker.sock"
49 source: pull
50 force_source: yes
51
52- name: create and deploy readarr container
53 become: yes
54 become_user: "{{ docker_username }}"
55 environment:
56 XDG_RUNTIME_DIR: "/run/user/{{ docker_uid }}"
57 docker_container:
58 name: "readarr"
59 image: "{{ custom_registry }}/{{ repo_tag }}"
60 recreate: yes
61 pull: yes
62 docker_host: "unix://run/user/{{ docker_uid }}/docker.sock"
63 purge_networks: yes
64 network_mode: "container:gluetun"
65 state: 'started'
66 comparisons:
67 '*': strict
68 restart_policy: unless-stopped
69 env:
70 "TZ": "{{ timezone }}"
71 "PUID": "0"
72 "PGID": "0"
73 volumes:
74 - "{{ docker_home }}/readarr/config:/config"
75 - "{{ docker_home }}/arr/data:/data"
76
77- name: deploy nginx configuration
78 notify: restart nginx
79 register: nginx_config
80 template:
81 src: "{{ readarr_nginx_config }}"
82 dest: /etc/nginx/sites-available/readarr.conf
83 owner: root
84 group: root
85 mode: '0644'
86
87- name: symlink site
88 file:
89 src: /etc/nginx/sites-available/readarr.conf
90 dest: /etc/nginx/sites-enabled/readarr.conf
91 owner: root
92 group: root
93 state: link
diff --git a/roles/services/containers/arr_stack/tasks/sonarr.yml b/roles/services/containers/arr_stack/tasks/sonarr.yml
new file mode 100644
index 0000000..ac712ba
--- /dev/null
+++ b/roles/services/containers/arr_stack/tasks/sonarr.yml
@@ -0,0 +1,93 @@
1- name: set image fact
2 set_fact:
3 image: linuxserver/sonarr:develop-version-4.0.0.433
4
5- name: set other facts
6 vars:
7 array: "{{ image.split('/', 1) }}"
8 set_fact:
9 repo_tag: "{{ array.1 }}"
10 custom_registry: "{{ docker_registry_url + '/' + docker_registry_username }}"
11
12- name: create sonarr directory
13 file:
14 path: "{{ docker_home }}/sonarr"
15 state: directory
16 owner: "{{ docker_username }}"
17 group: "{{ docker_username }}"
18 mode: '0755'
19
20- name: create sonarr config directory
21 file:
22 path: "{{ docker_home }}/sonarr/config"
23 state: directory
24 owner: "{{ docker_username }}"
25 group: "{{ docker_username }}"
26 mode: '0755'
27
28- name: login to docker registry
29 become: yes
30 become_user: "{{ docker_username }}"
31 environment:
32 XDG_RUNTIME_DIR: "/run/user/{{ docker_uid }}"
33 docker_login:
34 docker_host: "unix://run/user/{{ docker_uid }}/docker.sock"
35 registry_url: "{{ docker_registry_url }}"
36 username: "{{ docker_registry_username }}"
37 password: "{{ docker_registry_password }}"
38
39- name: pull and push sonarr image
40 become: yes
41 become_user: "{{ docker_username }}"
42 environment:
43 XDG_RUNTIME_DIR: "/run/user/{{ docker_uid }}"
44 docker_image:
45 name: "{{ image }}"
46 repository: "{{ custom_registry }}/{{ repo_tag }}"
47 push: yes
48 docker_host: "unix://run/user/{{ docker_uid }}/docker.sock"
49 source: pull
50 force_source: yes
51
52- name: create and deploy sonarr container
53 become: yes
54 become_user: "{{ docker_username }}"
55 environment:
56 XDG_RUNTIME_DIR: "/run/user/{{ docker_uid }}"
57 docker_container:
58 name: "sonarr"
59 image: "{{ custom_registry }}/{{ repo_tag }}"
60 recreate: yes
61 pull: yes
62 docker_host: "unix://run/user/{{ docker_uid }}/docker.sock"
63 purge_networks: yes
64 network_mode: "container:gluetun"
65 state: 'started'
66 comparisons:
67 '*': strict
68 restart_policy: unless-stopped
69 env:
70 "TZ": "{{ timezone }}"
71 "PUID": "0"
72 "PGID": "0"
73 volumes:
74 - "{{ docker_home }}/sonarr/config:/config"
75 - "{{ docker_home }}/arr/data:/data"
76
77- name: deploy nginx configuration
78 notify: restart nginx
79 register: nginx_config
80 template:
81 src: "{{ sonarr_nginx_config }}"
82 dest: /etc/nginx/sites-available/sonarr.conf
83 owner: root
84 group: root
85 mode: '0644'
86
87- name: symlink site
88 file:
89 src: /etc/nginx/sites-available/sonarr.conf
90 dest: /etc/nginx/sites-enabled/sonarr.conf
91 owner: root
92 group: root
93 state: link
diff --git a/roles/services/containers/authelia/handlers/main.yml b/roles/services/containers/authelia/handlers/main.yml
new file mode 100644
index 0000000..5463835
--- /dev/null
+++ b/roles/services/containers/authelia/handlers/main.yml
@@ -0,0 +1,4 @@
1- name: restart nginx
2 service:
3 name: nginx
4 state: restarted
diff --git a/roles/services/containers/authelia/tasks/main.yml b/roles/services/containers/authelia/tasks/main.yml
new file mode 100644
index 0000000..c6bb337
--- /dev/null
+++ b/roles/services/containers/authelia/tasks/main.yml
@@ -0,0 +1,283 @@
1- name: set image fact
2 set_fact:
3 image: authelia/authelia:master
4
5- name: set other facts
6 vars:
7 array: "{{ image.split('/', 1) }}"
8 set_fact:
9 repo_tag: "{{ array.1 }}"
10 custom_registry: "{{ docker_registry_url + '/' + docker_registry_username }}"
11
12- name: create authelia directory
13 file:
14 path: "{{ docker_home }}/authelia"
15 state: directory
16 owner: "{{ docker_username }}"
17 group: "{{ docker_username }}"
18 mode: '0755'
19
20- name: create authelia config directory
21 file:
22 path: "{{ docker_home }}/authelia/config"
23 state: directory
24 owner: "{{ docker_username }}"
25 group: "{{ docker_username }}"
26 mode: '0755'
27
28- name: create authelia secrets directory
29 file:
30 path: "{{ docker_home }}/authelia/secrets"
31 state: directory
32 owner: "{{ docker_username }}"
33 group: "{{ docker_username }}"
34 mode: '0755'
35
36- name: create redis data directory
37 file:
38 path: "{{ docker_home }}/authelia/redis_data"
39 state: directory
40 owner: "{{ docker_username }}"
41 group: "{{ docker_username }}"
42 mode: '0755'
43
44- name: place authelia config in proper location
45 copy:
46 src: "{{ authelia_config }}"
47 dest: "{{ docker_home }}/authelia/config/configuration.yml"
48 owner: root
49 group: docker
50 mode: '0644'
51
52# nginx snippets
53
54- name: copy proxy.conf snippet
55 copy:
56 src: "{{ authelia_proxy_snippet }}"
57 dest: "/etc/nginx/snippets/proxy.conf"
58 owner: root
59 group: root
60 mode: '0644'
61
62- name: copy authelia-location.conf snippet
63 copy:
64 src: "{{ authelia_location_snippet }}"
65 dest: "/etc/nginx/snippets/authelia-location.conf"
66 owner: root
67 group: root
68 mode: '0644'
69
70- name: copy authelia-authrequest.conf snippet
71 copy:
72 src: "{{ authelia_request_snippet }}"
73 dest: "/etc/nginx/snippets/authelia-authrequest.conf"
74 owner: root
75 group: root
76 mode: '0644'
77
78
79# authelia secrets
80
81- name: create jwt_secret file
82 lineinfile:
83 path: "{{ docker_home }}/authelia/secrets/jwt_secret"
84 insertbefore: BOF
85 line: "{{ authelia_jwt_secret }}"
86 owner: root
87 group: root
88 mode: '0644'
89 create: yes
90
91- name: create session_secret file
92 lineinfile:
93 path: "{{ docker_home }}/authelia/secrets/session_secret"
94 insertbefore: BOF
95 line: "{{ authelia_session_secret }}"
96 owner: root
97 group: root
98 mode: '0644'
99 create: yes
100
101- name: create encryption_key file
102 lineinfile:
103 path: "{{ docker_home }}/authelia/secrets/encryption_key"
104 insertbefore: BOF
105 line: "{{ authelia_encryption_key }}"
106 owner: root
107 group: root
108 mode: '0644'
109 create: yes
110
111- name: create oidc_hmac file
112 lineinfile:
113 path: "{{ docker_home }}/authelia/secrets/oidc_hmac"
114 insertbefore: BOF
115 line: "{{ authelia_oidc_hmac }}"
116 owner: root
117 group: root
118 mode: '0644'
119 create: yes
120
121- name: remove existing cert file
122 file:
123 path: "{{ docker_home }}/authelia/secrets/oidc_cert"
124 state: absent
125
126- name: create oidc_cert file
127 lineinfile:
128 path: "{{ docker_home }}/authelia/secrets/oidc_cert"
129 insertbefore: BOF
130 line: "{{ authelia_oidc_cert }}"
131 owner: root
132 group: root
133 mode: '0644'
134 create: yes
135
136- name: remove existing key file
137 file:
138 path: "{{ docker_home }}/authelia/secrets/oidc_key"
139 state: absent
140
141- name: create oidc_key file
142 lineinfile:
143 path: "{{ docker_home }}/authelia/secrets/oidc_key"
144 insertbefore: BOF
145 line: "{{ authelia_oidc_key }}"
146 owner: root
147 group: root
148 mode: '0644'
149 create: yes
150
151- name: create smtp_password file
152 lineinfile:
153 path: "{{ docker_home }}/authelia/secrets/smtp_password"
154 insertbefore: BOF
155 line: "{{ authelia_smtp_password }}"
156 owner: root
157 group: root
158 mode: '0644'
159 create: yes
160
161- name: create ldap_password file
162 lineinfile:
163 path: "{{ docker_home }}/authelia/secrets/ldap_password"
164 insertbefore: BOF
165 line: "{{ authelia_ldap_password }}"
166 owner: root
167 group: root
168 mode: '0644'
169 create: yes
170
171- name: login to docker registry
172 become: yes
173 become_user: "{{ docker_username }}"
174 environment:
175 XDG_RUNTIME_DIR: "/run/user/{{ docker_uid }}"
176 docker_login:
177 docker_host: "unix://run/user/{{ docker_uid }}/docker.sock"
178 registry_url: "{{ docker_registry_url }}"
179 username: "{{ docker_registry_username }}"
180 password: "{{ docker_registry_password }}"
181
182- name: pull and push authelia image
183 become: yes
184 become_user: "{{ docker_username }}"
185 environment:
186 XDG_RUNTIME_DIR: "/run/user/{{ docker_uid }}"
187 docker_image:
188 name: "{{ image }}"
189 repository: "{{ custom_registry }}/{{ repo_tag }}"
190 push: yes
191 docker_host: "unix://run/user/{{ docker_uid }}/docker.sock"
192 source: pull
193 force_source: yes
194
195- name: create authelia docker network
196 docker_network:
197 name: "{{ authelia_network_name }}"
198 docker_host: "unix://run/user/{{ docker_uid }}/docker.sock"
199 driver: bridge
200 ipam_config:
201 - subnet: "{{ authelia_subnet }}"
202 gateway: "{{ authelia_gateway }}"
203
204- name: create and deploy authelia container
205 become: yes
206 become_user: "{{ docker_username }}"
207 environment:
208 XDG_RUNTIME_DIR: "/run/user/{{ docker_uid }}"
209 docker_container:
210 name: "authelia"
211 hostname: "authelia"
212 image: "{{ custom_registry }}/{{ repo_tag }}"
213 recreate: yes
214 pull: yes
215 docker_host: "unix://run/user/{{ docker_uid }}/docker.sock"
216 purge_networks: yes
217 networks:
218 - name: "{{ authelia_network_name }}"
219 ipv4_address: "{{ authelia_ipv4 }}"
220 ports:
221 - "127.0.0.1:9091:9091"
222 - "127.0.0.1:9959:9959"
223 state: 'started'
224 comparisons:
225 '*': strict
226 restart_policy: unless-stopped
227 env:
228 "TZ": "{{ timezone }}"
229 "AUTHELIA_JWT_SECRET_FILE": "/secrets/jwt_secret"
230 "AUTHELIA_SESSION_SECRET_FILE": "/secrets/session_secret"
231 "AUTHELIA_STORAGE_ENCRYPTION_KEY_FILE": "/secrets/encryption_key"
232 "AUTHELIA_IDENTITY_PROVIDERS_OIDC_HMAC_SECRET_FILE": "/secrets/oidc_hmac"
233 "AUTHELIA_IDENTITY_PROVIDERS_OIDC_ISSUER_CERTIFICATE_CHAIN_FILE": "/secrets/oidc_cert"
234 "AUTHELIA_IDENTITY_PROVIDERS_OIDC_ISSUER_PRIVATE_KEY_FILE": "/secrets/oidc_key"
235 "AUTHELIA_NOTIFIER_SMTP_PASSWORD_FILE": "/secrets/smtp_password"
236 "AUTHELIA_AUTHENTICATION_BACKEND_LDAP_PASSWORD_FILE": "/secrets/ldap_password"
237 volumes:
238 - "{{ docker_home }}/authelia/config:/config"
239 - "{{ docker_home }}/authelia/secrets:/secrets"
240
241
242- name: create and deploy redis container
243 become: yes
244 become_user: "{{ docker_username }}"
245 environment:
246 XDG_RUNTIME_DIR: "/run/user/{{ docker_uid }}"
247 docker_container:
248 name: "redis_authelia"
249 hostname: "redis_authelia"
250 image: redis:alpine
251 state: 'started'
252 recreate: yes
253 pull: yes
254 restart_policy: unless-stopped
255 docker_host: "unix://run/user/{{ docker_uid }}/docker.sock"
256 purge_networks: yes
257 networks:
258 - name: "{{ authelia_network_name }}"
259 ipv4_address: "{{ redis_authelia_ipv4 }}"
260 volumes:
261 - "{{ docker_home }}/authelia/redis_data:/data"
262 exposed_ports:
263 - '6379'
264 env:
265 "TZ": "{{ timezone }}"
266
267- name: deploy nginx configuration
268 notify: restart nginx
269 register: nginx_config
270 copy:
271 src: "{{ authelia_nginx_config }}"
272 dest: /etc/nginx/sites-available/authelia.conf
273 owner: root
274 group: root
275 mode: '0644'
276
277- name: symlink site
278 file:
279 src: /etc/nginx/sites-available/authelia.conf
280 dest: /etc/nginx/sites-enabled/authelia.conf
281 owner: root
282 group: root
283 state: link
diff --git a/roles/services/containers/bookstack/handlers/main.yml b/roles/services/containers/bookstack/handlers/main.yml
new file mode 100644
index 0000000..5463835
--- /dev/null
+++ b/roles/services/containers/bookstack/handlers/main.yml
@@ -0,0 +1,4 @@
1- name: restart nginx
2 service:
3 name: nginx
4 state: restarted
diff --git a/roles/services/containers/bookstack/tasks/main.yml b/roles/services/containers/bookstack/tasks/main.yml
new file mode 100644
index 0000000..3965143
--- /dev/null
+++ b/roles/services/containers/bookstack/tasks/main.yml
@@ -0,0 +1,118 @@
1- name: set image fact
2 set_fact:
3 image: linuxserver/bookstack:version-v23.05
4
5- name: set other facts
6 vars:
7 array: "{{ image.split('/', 1) }}"
8 set_fact:
9 repo_tag: "{{ array.1 }}"
10 custom_registry: "{{ docker_registry_url + '/' + docker_registry_username }}"
11
12- name: create bookstack directory
13 file:
14 path: "{{ docker_home }}/bookstack"
15 state: directory
16 owner: "{{ docker_username }}"
17 group: "{{ docker_username }}"
18 mode: '0755'
19
20- name: create data directory
21 file:
22 path: "{{ docker_home }}/bookstack/data"
23 state: directory
24 owner: "{{ docker_username }}"
25 group: "{{ docker_username }}"
26 mode: '0755'
27
28- name: create bookstack docker network
29 become: yes
30 become_user: "{{ docker_username }}"
31 docker_network:
32 name: "{{ bookstack_network_name }}"
33 docker_host: "unix://run/user/{{ docker_uid }}/docker.sock"
34 driver: bridge
35 ipam_config:
36 - subnet: "{{ bookstack_subnet }}"
37 gateway: "{{ bookstack_gateway }}"
38
39- name: create and deploy bookstack db
40 become: yes
41 become_user: "{{ docker_username }}"
42 environment:
43 XDG_RUNTIME_DIR: "/run/user/{{ docker_uid }}"
44 docker_container:
45 name: "bookstack-db"
46 hostname: "bookstack-db"
47 image: linuxserver/mariadb:10.11.4
48 docker_host: "unix://run/user/{{ docker_uid }}/docker.sock"
49 purge_networks: yes
50 networks:
51 - name: "{{ bookstack_network_name }}"
52 ipv4_address: "{{ bookstack_db_ipv4 }}"
53 volumes:
54 - "{{ docker_home }}/bookstack/data:/config"
55 env:
56 "TZ": "{{ timezone }}"
57 "MYSQL_ROOT_PASSWORD": "{{ bookstack_mysql_root_password }}"
58 "MYSQL_DATABASE": "bookstack"
59 "MYSQL_USER": "bookstack"
60 "MYSQL_PASSWORD": "{{ bookstack_mysql_password }}"
61 state: 'started'
62 recreate: yes
63 restart_policy: unless-stopped
64
65- name: create and deploy bookstack container
66 become: yes
67 become_user: "{{ docker_username }}"
68 environment:
69 XDG_RUNTIME_DIR: "/run/user/{{ docker_uid }}"
70 docker_container:
71 name: "bookstack"
72 hostname: "bookstack"
73 image: "{{ image }}"
74 docker_host: "unix://run/user/{{ docker_uid }}/docker.sock"
75 purge_networks: yes
76 networks:
77 - name: "{{ bookstack_network_name }}"
78 ipv4_address: "{{ bookstack_ipv4 }}"
79 ports:
80 - "127.0.0.1:{{ bookstack_external_port }}:80"
81 volumes:
82 - "{{ docker_home }}/bookstack/data:/config"
83 env:
84 "DB_HOST": "bookstack-db"
85 "DB_PORT": "3306"
86 "DB_USER": "bookstack"
87 "DB_PASS": "{{ bookstack_mysql_password }}"
88 "DB_DATABASE": "bookstack"
89 "APP_URL": "https://{{ bookstack_server_name }}"
90 "AUTH_METHOD": "oidc"
91 "OIDC_NAME": "SSO"
92 "OIDC_DISPLAY_NAME_CLAIMS": "name"
93 "OIDC_CLIENT_ID": "bookstack"
94 "OIDC_CLIENT_SECRET": "{{ bookstack_oidc_secret }}"
95 "OIDC_ISSUER": "{{ oidc_issuer }}"
96 "OIDC_ISSUER_DISCOVER": "true"
97 "APP_DEFAULT_DARK_MODE": "true"
98 #"OIDC_DUMP_USER_DETAILS": "true"
99 state: 'started'
100 recreate: yes
101 restart_policy: unless-stopped
102
103- name: deploy nginx configuration
104 notify: restart nginx
105 template:
106 src: "{{ bookstack_nginx_config }}"
107 dest: /etc/nginx/sites-available/bookstack.conf
108 owner: root
109 group: root
110 mode: '0644'
111
112- name: symlink site
113 file:
114 src: /etc/nginx/sites-available/bookstack.conf
115 dest: /etc/nginx/sites-enabled/bookstack.conf
116 owner: root
117 group: root
118 state: link
diff --git a/roles/services/containers/cadvisor/handlers/main.yml b/roles/services/containers/cadvisor/handlers/main.yml
new file mode 100644
index 0000000..5463835
--- /dev/null
+++ b/roles/services/containers/cadvisor/handlers/main.yml
@@ -0,0 +1,4 @@
1- name: restart nginx
2 service:
3 name: nginx
4 state: restarted
diff --git a/roles/services/containers/cadvisor/tasks/main.yml b/roles/services/containers/cadvisor/tasks/main.yml
new file mode 100644
index 0000000..cc30cdb
--- /dev/null
+++ b/roles/services/containers/cadvisor/tasks/main.yml
@@ -0,0 +1,90 @@
1- name: create cadvisor directory
2 file:
3 path: "{{ docker_home }}/cadvisor"
4 state: directory
5 owner: "{{ docker_username }}"
6 group: "{{ docker_username }}"
7 mode: '0755'
8
9- name: login to docker registry
10 become: yes
11 become_user: "{{ docker_username }}"
12 environment:
13 XDG_RUNTIME_DIR: "/run/user/{{ docker_uid }}"
14 docker_login:
15 docker_host: "unix://run/user/{{ docker_uid }}/docker.sock"
16 registry_url: "{{ docker_registry_url }}"
17 username: "{{ docker_registry_username }}"
18 password: "{{ docker_registry_password }}"
19
20- name: build cadvisor image
21 become: yes
22 become_user: "{{ docker_username }}"
23 environment:
24 XDG_RUNTIME_DIR: "/run/user/{{ docker_uid }}"
25 docker_image:
26 name: "{{ docker_registry_url }}/{{ docker_registry_username }}/cadvisor:latest"
27 docker_host: "unix://run/user/{{ docker_uid }}/docker.sock"
28 build:
29 path: /srv/docker/cadvisor/src
30 dockerfile: deploy/Dockerfile
31 source: build
32 push: yes
33
34- name: create cadvisor docker network
35 become: yes
36 become_user: "{{ docker_username }}"
37 environment:
38 XDG_RUNTIME_DIR: "/run/user/{{ docker_uid }}"
39 docker_network:
40 name: "{{ cadvisor_network_name }}"
41 docker_host: "unix://run/user/{{ docker_uid }}/docker.sock"
42 driver: bridge
43 ipam_config:
44 - subnet: "{{ cadvisor_subnet }}"
45 gateway: "{{ cadvisor_gateway }}"
46
47- name: create and deploy cadvisor container
48 become: yes
49 become_user: "{{ docker_username }}"
50 environment:
51 XDG_RUNTIME_DIR: "/run/user/{{ docker_uid }}"
52 docker_container:
53 name: "cadvisor"
54 hostname: "cadvisor"
55 image: "{{ docker_registry_url }}/{{ docker_registry_username }}/cadvisor:latest"
56 docker_host: "unix://run/user/{{ docker_uid }}/docker.sock"
57 purge_networks: yes
58 networks:
59 - name: "{{ cadvisor_network_name }}"
60 ipv4_address: "{{ cadvisor_ipv4 }}"
61 ports:
62 - "127.0.0.1:{{ cadvisor_external_port }}:8080"
63 state: 'started'
64 comparisons:
65 '*': strict
66 restart_policy: unless-stopped
67 volumes:
68 - "/:/rootfs:ro"
69 - "/run/user/{{ docker_uid }}:/var/run:ro"
70 - "/sys:/sys:ro"
71 - "/{{ docker_home }}/.local/share/docker:/var/lib/docker:ro"
72 - "/dev/disk:/dev/disk:ro"
73
74- name: deploy nginx configuration
75 notify: restart nginx
76 register: nginx_config
77 copy:
78 src: "{{ cadvisor_nginx_config }}"
79 dest: /etc/nginx/sites-available/cadvisor.conf
80 owner: root
81 group: root
82 mode: '0644'
83
84- name: symlink site
85 file:
86 src: /etc/nginx/sites-available/cadvisor.conf
87 dest: /etc/nginx/sites-enabled/cadvisor.conf
88 owner: root
89 group: root
90 state: link
diff --git a/roles/services/containers/drawio/handlers/main.yml b/roles/services/containers/drawio/handlers/main.yml
new file mode 100644
index 0000000..5463835
--- /dev/null
+++ b/roles/services/containers/drawio/handlers/main.yml
@@ -0,0 +1,4 @@
1- name: restart nginx
2 service:
3 name: nginx
4 state: restarted
diff --git a/roles/services/containers/drawio/tasks/main.yml b/roles/services/containers/drawio/tasks/main.yml
new file mode 100644
index 0000000..27bbefd
--- /dev/null
+++ b/roles/services/containers/drawio/tasks/main.yml
@@ -0,0 +1,149 @@
1- name: set image fact
2 set_fact:
3 image: jgraph/drawio:21.5.0
4
5- name: set other facts
6 vars:
7 array: "{{ image.split('/', 1) }}"
8 set_fact:
9 repo_tag: "{{ array.1 }}"
10 custom_registry: "{{ docker_registry_url + '/' + docker_registry_username }}"
11
12- name: create drawio directory
13 file:
14 path: "{{ docker_home }}/drawio"
15 state: directory
16 owner: "{{ docker_username }}"
17 group: "{{ docker_username }}"
18 mode: '0755'
19
20- name: create drawio fonts directory
21 file:
22 path: /usr/share/fonts/drawio
23 state: directory
24 owner: "{{ docker_username }}"
25 group: "{{ docker_username }}"
26 mode: '0755'
27
28- name: login to docker registry
29 become: yes
30 become_user: "{{ docker_username }}"
31 environment:
32 XDG_RUNTIME_DIR: "/run/user/{{ docker_uid }}"
33 docker_login:
34 docker_host: "unix://run/user/{{ docker_uid }}/docker.sock"
35 registry_url: "{{ docker_registry_url }}"
36 username: "{{ docker_registry_username }}"
37 password: "{{ docker_registry_password }}"
38
39- name: get drawio image
40 become: yes
41 become_user: "{{ docker_username }}"
42 environment:
43 XDG_RUNTIME_DIR: "/run/user/{{ docker_uid }}"
44 docker_image:
45 name: "{{ image }}"
46 repository: "{{ custom_registry }}/{{ repo_tag }}"
47 docker_host: "unix://run/user/{{ docker_uid }}/docker.sock"
48 source: pull
49 force_source: yes
50 push: yes
51
52- name: get export-server image
53 become: yes
54 become_user: "{{ docker_username }}"
55 environment:
56 XDG_RUNTIME_DIR: "/run/user/{{ docker_uid }}"
57 docker_image:
58 name: "{{ docker_registry_url }}/{{ docker_registry_username }}/image-export:latest"
59 docker_host: "unix://run/user/{{ docker_uid }}/docker.sock"
60 source: pull
61 force_source: yes
62 push: yes
63
64- name: create drawio docker network
65 become: yes
66 become_user: "{{ docker_username }}"
67 docker_network:
68 name: "{{ drawio_network_name }}"
69 docker_host: "unix://run/user/{{ docker_uid }}/docker.sock"
70 driver: bridge
71 ipam_config:
72 - subnet: "{{ drawio_subnet }}"
73 gateway: "{{ drawio_gateway }}"
74
75- name: create and deploy drawio export-server
76 become: yes
77 become_user: "{{ docker_username }}"
78 environment:
79 XDG_RUNTIME_DIR: "/run/user/{{ docker_uid }}"
80 docker_container:
81 name: "image-export"
82 image: "{{ docker_registry_url }}/{{ docker_registry_username }}/image-export:latest"
83 docker_host: "unix://run/user/{{ docker_uid }}/docker.sock"
84 pull: yes
85 exposed_ports:
86 - '8000'
87 purge_networks: yes
88 networks:
89 - name: "{{ drawio_network_name }}"
90 ipv4_address: "{{ drawio_export_ipv4 }}"
91 volumes:
92 - fonts_volume:/usr/share/fonts/drawio
93 env:
94 DRAWIO_BASE_URL: "{{ drawio_base_url }}"
95 cap_drop:
96 - all
97 hostname: "image-export"
98 restart_policy: unless-stopped
99 state: 'started'
100 recreate: yes
101
102- name: create and deploy drawio
103 become: yes
104 become_user: "{{ docker_username }}"
105 environment:
106 XDG_RUNTIME_DIR: "/run/user/{{ docker_uid }}"
107 docker_container:
108 name: "drawio"
109 image: "{{ custom_registry }}/{{ repo_tag }}"
110 docker_host: "unix://run/user/{{ docker_uid }}/docker.sock"
111 pull: yes
112 purge_networks: yes
113 networks:
114 - name: "{{ drawio_network_name }}"
115 ipv4_address: "{{ drawio_ipv4 }}"
116 ports:
117 - "127.0.0.1:8443:8443"
118 - "127.0.0.1:8400:8080"
119 links:
120 - image-export:image-export
121 env:
122 DRAWIO_SELF_CONTAINED: "1"
123 PLANTUML_URL: "http://plantuml-server:8080/"
124 EXPORT_URL: "http://image-export:8000/"
125 DRAWIO_PUSHER_MODE: "2"
126 cap_drop:
127 - all
128 hostname: "drawio"
129 restart_policy: unless-stopped
130 state: 'started'
131 recreate: yes
132
133- name: deploy nginx configuration
134 notify: restart nginx
135 register: nginx_config
136 copy:
137 src: "{{ drawio_nginx_config }}"
138 dest: /etc/nginx/sites-available/drawio.conf
139 owner: root
140 group: root
141 mode: '0644'
142
143- name: symlink site
144 file:
145 src: /etc/nginx/sites-available/drawio.conf
146 dest: /etc/nginx/sites-enabled/drawio.conf
147 owner: root
148 group: root
149 state: link
diff --git a/roles/services/containers/firefly/handlers/main.yml b/roles/services/containers/firefly/handlers/main.yml
new file mode 100644
index 0000000..5463835
--- /dev/null
+++ b/roles/services/containers/firefly/handlers/main.yml
@@ -0,0 +1,4 @@
1- name: restart nginx
2 service:
3 name: nginx
4 state: restarted
diff --git a/roles/services/containers/firefly/tasks/main.yml b/roles/services/containers/firefly/tasks/main.yml
new file mode 100644
index 0000000..ab389e2
--- /dev/null
+++ b/roles/services/containers/firefly/tasks/main.yml
@@ -0,0 +1,172 @@
1- name: set image fact
2 set_fact:
3 image: fireflyiii/core:version-6.0.13
4
5- name: set other facts
6 vars:
7 array: "{{ image.split('/', 1) }}"
8 set_fact:
9 repo_tag: "{{ array.1 }}"
10 custom_registry: "{{ docker_registry_url + '/' + docker_registry_username }}"
11
12- name: create firefly directory
13 file:
14 path: "{{ docker_home }}/firefly"
15 state: directory
16 owner: "{{ docker_username }}"
17 group: "{{ docker_username }}"
18 mode: '0755'
19
20- name: create data directory
21 file:
22 path: "{{ docker_home }}/firefly/data"
23 state: directory
24 owner: "{{ docker_username }}"
25 group: "{{ docker_username }}"
26 mode: '0755'
27
28- name: create db directory
29 file:
30 path: "{{ docker_home }}/firefly/db"
31 state: directory
32 owner: "{{ docker_username }}"
33 group: "{{ docker_username }}"
34 mode: '0755'
35
36- name: create firefly docker network
37 become: yes
38 become_user: "{{ docker_username }}"
39 docker_network:
40 name: "{{ firefly_network_name }}"
41 docker_host: "unix://run/user/{{ docker_uid }}/docker.sock"
42 driver: bridge
43 ipam_config:
44 - subnet: "{{ firefly_subnet }}"
45 gateway: "{{ firefly_gateway }}"
46
47- name: create and deploy firefly db
48 become: yes
49 become_user: "{{ docker_username }}"
50 environment:
51 XDG_RUNTIME_DIR: "/run/user/{{ docker_uid }}"
52 docker_container:
53 name: "firefly-db"
54 hostname: "firefly-db"
55 image: postgres:alpine
56 docker_host: "unix://run/user/{{ docker_uid }}/docker.sock"
57 purge_networks: yes
58 networks:
59 - name: "{{ firefly_network_name }}"
60 ipv4_address: "{{ firefly_db_ipv4 }}"
61 volumes:
62 - "{{ docker_home }}/firefly/data:/var/lib/postgresql/data"
63 env:
64 "POSTGRES_USER": "{{ firefly_postgres_user }}"
65 "POSTGRES_PASSWORD": "{{ firefly_postgres_password }}"
66 "POSTGRES_DB": "{{ firefly_postgres_db }}"
67 state: 'started'
68 recreate: yes
69 restart_policy: unless-stopped
70
71- name: create and deploy firefly container
72 become: yes
73 become_user: "{{ docker_username }}"
74 environment:
75 XDG_RUNTIME_DIR: "/run/user/{{ docker_uid }}"
76 docker_container:
77 name: "firefly"
78 hostname: "firefly"
79 image: "{{ image }}"
80 docker_host: "unix://run/user/{{ docker_uid }}/docker.sock"
81 purge_networks: yes
82 networks:
83 - name: "{{ firefly_network_name }}"
84 ipv4_address: "{{ firefly_ipv4 }}"
85 ports:
86 - "127.0.0.1:{{ firefly_external_port }}:8080"
87 volumes:
88 - "{{ docker_home }}/firefly/upload:/var/www/html/storage/upload"
89 env:
90 "TZ": "{{ timezone }}"
91 "APP_KEY": "{{ firefly_app_key }}"
92 "STATIC_CRON_TOKEN": "{{ firefly_cron_token }}"
93 "DB_HOST": "firefly-db"
94 "DB_PORT": "5432"
95 "DB_CONNECTION": "pgsql"
96 "DB_USERNAME": "{{ firefly_postgres_user }}"
97 "DB_PASSWORD": "{{ firefly_postgres_password }}"
98 "DB_DATABASE": "{{ firefly_postgres_db }}"
99 "AUTHENTICATION_GUARD": "remote_user_guard"
100 "AUTHENTICATION_GUARD_HEADER": "HTTP_REMOTE_USER"
101 "AUTHENTICATION_GUARD_EMAIL": "HTTP_REMOTE_EMAIL"
102 "APP_URL": "https://{{ firefly_server_name }}"
103 "TRUSTED_PROXIES": "*"
104 state: 'started'
105 recreate: yes
106 restart_policy: unless-stopped
107
108- name: create and deploy firefly importer container
109 become: yes
110 become_user: "{{ docker_username }}"
111 environment:
112 XDG_RUNTIME_DIR: "/run/user/{{ docker_uid }}"
113 docker_container:
114 name: "firefly-importer"
115 hostname: "firefly-importer"
116 image: "fireflyiii/data-importer:version-1.3.0"
117 docker_host: "unix://run/user/{{ docker_uid }}/docker.sock"
118 purge_networks: yes
119 networks:
120 - name: "{{ firefly_network_name }}"
121 ipv4_address: "{{ firefly_importer_ipv4 }}"
122 ports:
123 - "127.0.0.1:{{ firefly_importer_external_port }}:8080"
124 env:
125 "TZ": "{{ timezone }}"
126 "FIREFLY_III_URL": "http://firefly:8080"
127 "FIREFLY_III_ACCESS_TOKEN": "{{ firefly_access_token }}"
128 "VANITY_URL": "https://{{ firefly_server_name }}"
129 "TRUSTED_PROXIES": "*"
130 state: 'started'
131 recreate: yes
132 restart_policy: unless-stopped
133
134- name: create and deploy firefly cron container
135 become: yes
136 become_user: "{{ docker_username }}"
137 environment:
138 XDG_RUNTIME_DIR: "/run/user/{{ docker_uid }}"
139 docker_container:
140 name: "firefly-cron"
141 hostname: "firefly-cron"
142 image: alpine
143 docker_host: "unix://run/user/{{ docker_uid }}/docker.sock"
144 purge_networks: yes
145 networks:
146 - name: "{{ firefly_network_name }}"
147 ipv4_address: "{{ firefly_cron_ipv4 }}"
148 env:
149 "POSTGRES_USER": "{{ firefly_postgres_user }}"
150 "POSTGRES_PASSWORD": "{{ firefly_postgres_password }}"
151 "POSTGRES_DB": "{{ firefly_postgres_db }}"
152 command: 'sh -c "echo \"0 3 * * * wget -qO- http://firefly:8080/api/v1/cron/{{ firefly_cron_token }}\" | crontab - && crond -f -L /dev/stdout"'
153 state: 'started'
154 recreate: yes
155 restart_policy: unless-stopped
156
157- name: deploy nginx configuration
158 notify: restart nginx
159 template:
160 src: "{{ firefly_nginx_config }}"
161 dest: /etc/nginx/sites-available/firefly.conf
162 owner: root
163 group: root
164 mode: '0644'
165
166- name: symlink site
167 file:
168 src: /etc/nginx/sites-available/firefly.conf
169 dest: /etc/nginx/sites-enabled/firefly.conf
170 owner: root
171 group: root
172 state: link
diff --git a/roles/services/containers/freshrss/handlers/main.yml b/roles/services/containers/freshrss/handlers/main.yml
new file mode 100644
index 0000000..5463835
--- /dev/null
+++ b/roles/services/containers/freshrss/handlers/main.yml
@@ -0,0 +1,4 @@
1- name: restart nginx
2 service:
3 name: nginx
4 state: restarted
diff --git a/roles/services/containers/freshrss/tasks/main.yml b/roles/services/containers/freshrss/tasks/main.yml
new file mode 100644
index 0000000..26109b3
--- /dev/null
+++ b/roles/services/containers/freshrss/tasks/main.yml
@@ -0,0 +1,101 @@
1- name: set image fact
2 set_fact:
3 image: freshrss/freshrss:1.21.0
4
5- name: set other facts
6 vars:
7 array: "{{ image.split('/', 1) }}"
8 set_fact:
9 repo_tag: "{{ array.1 }}"
10 custom_registry: "{{ docker_registry_url + '/' + docker_registry_username }}"
11
12- name: create freshrss directory
13 file:
14 path: "{{ docker_home }}/freshrss"
15 state: directory
16 owner: "{{ docker_username }}"
17 group: "{{ docker_username }}"
18 mode: '0755'
19
20- name: login to docker registry
21 become: yes
22 become_user: "{{ docker_username }}"
23 environment:
24 XDG_RUNTIME_DIR: "/run/user/{{ docker_uid }}"
25 docker_login:
26 docker_host: "unix://run/user/{{ docker_uid }}/docker.sock"
27 registry_url: "{{ docker_registry_url }}"
28 username: "{{ docker_registry_username }}"
29 password: "{{ docker_registry_password }}"
30
31- name: get freshrss image
32 become: yes
33 become_user: "{{ docker_username }}"
34 environment:
35 XDG_RUNTIME_DIR: "/run/user/{{ docker_uid }}"
36 docker_image:
37 name: "{{ image }}"
38 repository: "{{ custom_registry }}/{{ repo_tag }}"
39 push: yes
40 docker_host: "unix://run/user/{{ docker_uid }}/docker.sock"
41 source: pull
42 force_source: yes
43
44- name: create freshrss data directory
45 file:
46 path: "{{ docker_home }}/freshrss/data"
47 state: directory
48 owner: "{{ docker_username }}"
49 group: "{{ docker_username }}"
50 mode: '0755'
51
52- name: create freshrss docker network
53 docker_network:
54 name: "{{ freshrss_network_name }}"
55 docker_host: "unix://run/user/{{ docker_uid }}/docker.sock"
56 driver: bridge
57 ipam_config:
58 - subnet: "{{ freshrss_subnet }}"
59 gateway: "{{ freshrss_gateway }}"
60
61- name: create and deploy freshrss container
62 become: yes
63 become_user: "{{ docker_username }}"
64 environment:
65 XDG_RUNTIME_DIR: "/run/user/{{ docker_uid }}"
66 docker_container:
67 name: "freshrss"
68 hostname: "freshrss"
69 image: "{{ custom_registry }}/{{ repo_tag }}"
70 docker_host: "unix://run/user/{{ docker_uid }}/docker.sock"
71 purge_networks: yes
72 networks:
73 - name: "{{ freshrss_network_name }}"
74 ipv4_address: "{{ freshrss_ipv4 }}"
75 ports:
76 - "127.0.0.1:8090:80"
77 state: 'started'
78 recreate: yes
79 restart_policy: unless-stopped
80 volumes:
81 - "{{ docker_home }}/freshrss/data:/var/www/FreshRSS/data"
82 env:
83 "CRON_MIN": "0,15,30,45"
84
85- name: deploy nginx configuration
86 notify: restart nginx
87 register: nginx_config
88 copy:
89 src: "{{ freshrss_nginx_config }}"
90 dest: /etc/nginx/sites-available/freshrss.conf
91 owner: root
92 group: root
93 mode: '0644'
94
95- name: symlink site
96 file:
97 src: /etc/nginx/sites-available/freshrss.conf
98 dest: /etc/nginx/sites-enabled/freshrss.conf
99 owner: root
100 group: root
101 state: link
diff --git a/roles/services/containers/gitea/handlers/main.yml b/roles/services/containers/gitea/handlers/main.yml
new file mode 100644
index 0000000..5463835
--- /dev/null
+++ b/roles/services/containers/gitea/handlers/main.yml
@@ -0,0 +1,4 @@
1- name: restart nginx
2 service:
3 name: nginx
4 state: restarted
diff --git a/roles/services/containers/gitea/tasks/main.yml b/roles/services/containers/gitea/tasks/main.yml
new file mode 100644
index 0000000..fecec5e
--- /dev/null
+++ b/roles/services/containers/gitea/tasks/main.yml
@@ -0,0 +1,171 @@
1- name: set image fact
2 set_fact:
3 image: gitea/gitea:1.19.3
4
5- name: set other facts
6 vars:
7 array: "{{ image.split('/', 1) }}"
8 set_fact:
9 repo_tag: "{{ array.1 }}"
10 custom_registry: "{{ docker_registry_url + '/' + docker_registry_username }}"
11
12- name: create gitea directory
13 file:
14 path: "{{ docker_home }}/gitea"
15 state: directory
16 owner: "{{ docker_username }}"
17 group: "{{ docker_username }}"
18 mode: '0755'
19
20- name: login to docker registry
21 become: yes
22 environment:
23 XDG_RUNTIME_DIR: "/run/user/{{ docker_uid }}"
24 docker_login:
25 docker_host: "unix://run/user/{{ docker_uid }}/docker.sock"
26 registry_url: "{{ docker_registry_url }}"
27 username: "{{ docker_registry_username }}"
28 password: "{{ docker_registry_password }}"
29
30- name: get gitea image
31 become: yes
32 docker_image:
33 name: "{{ image }}"
34 repository: "{{ custom_registry }}/{{ repo_tag }}"
35 push: yes
36 source: pull
37 force_source: yes
38
39- name: create git user on host
40 user:
41 name: "git"
42 uid: "{{ gitea_git_uid }}"
43 create_home: yes
44 generate_ssh_key: yes
45 shell: /bin/bash
46
47- name: get git user public key
48 command: cat /home/git/.ssh/id_rsa.pub
49 register: pubkey
50 changed_when: false
51
52- name: add git user public key to git user's authorized_keys file
53 authorized_key:
54 user: git
55 key: "{{ pubkey.stdout }}"
56
57- name: create fake host gitea
58 blockinfile:
59 path: /usr/local/bin/gitea
60 create: yes
61 owner: root
62 group: root
63 mode: '0755'
64 block: |
65 #!/bin/sh
66 ssh -p 2222 -o StrictHostKeyChecking=no git@127.0.0.1 "SSH_ORIGINAL_COMMAND=\"$SSH_ORIGINAL_COMMAND\" $0 $@"
67
68- name: create gitea data directory
69 file:
70 path: "{{ docker_home }}/gitea/data"
71 state: directory
72 owner: "{{ gitea_git_uid }}"
73 group: "{{ gitea_git_uid }}"
74 mode: '0755'
75
76- name: create gitea config directory
77 file:
78 path: "{{ docker_home }}/gitea/config"
79 state: directory
80 owner: "{{ gitea_git_uid }}"
81 group: "{{ gitea_git_uid }}"
82 mode: '0755'
83
84- name: copy gitea config file
85 copy:
86 src: "{{ gitea_config }}"
87 dest: "{{ docker_home }}/gitea/config/app.ini"
88 owner: "{{ gitea_git_uid }}"
89 group: "{{ gitea_git_uid }}"
90 mode: '0644'
91
92- name: change gitea internal token
93 lineinfile:
94 path: "{{ docker_home }}/gitea/config/app.ini"
95 regexp: "^INTERNAL_TOKEN"
96 line: "INTERNAL_TOKEN = {{ gitea_internal_token }}"
97
98- name: change gitea lfs jwt secret
99 lineinfile:
100 path: "{{ docker_home }}/gitea/config/app.ini"
101 regexp: "^LFS_JWT_SECRET"
102 line: "LFS_JWT_SECRET = {{ gitea_lfs_jwt_secret }}"
103
104- name: set permissions on gitea data
105 file:
106 path: "{{ docker_home }}/gitea/data/"
107 owner: "{{ gitea_git_uid }}"
108 group: "{{ gitea_git_uid }}"
109 mode: u=rwX,g=rX,o=rX
110 recurse: yes
111
112- name: set permissions on gitea config
113 file:
114 path: "{{ docker_home }}/gitea/config/"
115 owner: "{{ gitea_git_uid }}"
116 group: "{{ gitea_git_uid }}"
117 mode: u=rwX,g=rX,o=rX
118 recurse: yes
119
120- name: create gitea docker network
121 docker_network:
122 name: "{{ gitea_network_name }}"
123 driver: bridge
124 ipam_config:
125 - subnet: "{{ gitea_subnet }}"
126 gateway: "{{ gitea_gateway }}"
127
128- name: create and deploy gitea container
129 become: yes
130 docker_container:
131 name: "gitea"
132 hostname: "gitea"
133 image: "{{ custom_registry }}/{{ repo_tag }}"
134 purge_networks: yes
135 networks:
136 - name: "{{ gitea_network_name }}"
137 ipv4_address: "{{ gitea_ipv4 }}"
138 ports:
139 - "127.0.0.1:{{ gitea_external_port }}:3000"
140 - "127.0.0.1:2222:22"
141 state: 'started'
142 comparisons:
143 '*': strict
144 restart_policy: unless-stopped
145 env:
146 "USER_UID": "{{ gitea_git_uid }}"
147 "USER_GID": "{{ gitea_git_uid }}"
148 volumes:
149 - "{{ docker_home }}/gitea/data:/data"
150 - "{{ docker_home }}/gitea/config:/data/gitea/conf"
151 - "/home/git/.ssh/:/data/git/.ssh"
152 - "/etc/timezone:/etc/timezone:ro"
153 - "/etc/localtime:/etc/localtime:ro"
154
155- name: deploy nginx configuration
156 notify: restart nginx
157 register: nginx_config
158 copy:
159 src: "{{ gitea_nginx_config }}"
160 dest: /etc/nginx/sites-available/gitea.conf
161 owner: root
162 group: root
163 mode: '0644'
164
165- name: symlink site
166 file:
167 src: /etc/nginx/sites-available/gitea.conf
168 dest: /etc/nginx/sites-enabled/gitea.conf
169 owner: root
170 group: root
171 state: link
diff --git a/roles/services/containers/home_assistant/handlers/main.yml b/roles/services/containers/home_assistant/handlers/main.yml
new file mode 100644
index 0000000..5463835
--- /dev/null
+++ b/roles/services/containers/home_assistant/handlers/main.yml
@@ -0,0 +1,4 @@
1- name: restart nginx
2 service:
3 name: nginx
4 state: restarted
diff --git a/roles/services/containers/home_assistant/tasks/main.yml b/roles/services/containers/home_assistant/tasks/main.yml
new file mode 100644
index 0000000..b44c529
--- /dev/null
+++ b/roles/services/containers/home_assistant/tasks/main.yml
@@ -0,0 +1,86 @@
1- name: set image fact
2 set_fact:
3 image: homeassistant/home-assistant:2023.6.3
4
5- name: set other facts
6 vars:
7 array: "{{ image.split('/', 1) }}"
8 set_fact:
9 repo_tag: "{{ array.1 }}"
10 custom_registry: "{{ docker_registry_url + '/' + docker_registry_username }}"
11
12- name: create home_assistant directory
13 file:
14 path: "{{ docker_home }}/home_assistant"
15 state: directory
16 owner: "{{ docker_username }}"
17 group: "{{ docker_username }}"
18 mode: '0755'
19
20- name: create config directory
21 file:
22 path: "{{ docker_home }}/home_assistant/config"
23 state: directory
24 owner: "{{ docker_username }}"
25 group: "{{ docker_username }}"
26 mode: '0755'
27
28- name: deploy configuration
29 copy:
30 src: "{{ home_assistant_config }}"
31 dest: "{{ docker_home }}/home_assistant/config/configuration.yaml"
32 owner: "{{ docker_username }}"
33 group: "{{ docker_username }}"
34 mode: '0644'
35
36- name: create home_assistant network
37 become: yes
38 become_user: "{{ docker_username }}"
39 docker_network:
40 name: "{{ home_assistant_network_name }}"
41 docker_host: "unix://run/user/{{ docker_uid }}/docker.sock"
42 driver: bridge
43 ipam_config:
44 - subnet: "{{ home_assistant_subnet }}"
45 gateway: "{{ home_assistant_gateway }}"
46
47- name: create and deploy home_assistant container
48 become: yes
49 become_user: "{{ docker_username }}"
50 environment:
51 XDG_RUNTIME_DIR: "/run/user/{{ docker_uid }}"
52 docker_container:
53 name: "home_assistant"
54 hostname: "home_assistant"
55 image: "{{ image }}"
56 docker_host: "unix://run/user/{{ docker_uid }}/docker.sock"
57 purge_networks: yes
58 networks:
59 - name: "{{ home_assistant_network_name }}"
60 ipv4_address: "{{ home_assistant_ipv4 }}"
61 ports:
62 - "127.0.0.1:{{ home_assistant_external_port }}:8123"
63 volumes:
64 - "{{ docker_home }}/home_assistant/config:/config"
65 env:
66 "TZ": "{{ timezone }}"
67 state: 'started'
68 recreate: yes
69 restart_policy: unless-stopped
70
71- name: deploy nginx configuration
72 notify: restart nginx
73 template:
74 src: "{{ home_assistant_nginx_config }}"
75 dest: /etc/nginx/sites-available/home_assistant.conf
76 owner: root
77 group: root
78 mode: '0644'
79
80- name: symlink site
81 file:
82 src: /etc/nginx/sites-available/home_assistant.conf
83 dest: /etc/nginx/sites-enabled/home_assistant.conf
84 owner: root
85 group: root
86 state: link
diff --git a/roles/services/containers/homer/handlers/main.yml b/roles/services/containers/homer/handlers/main.yml
new file mode 100644
index 0000000..5463835
--- /dev/null
+++ b/roles/services/containers/homer/handlers/main.yml
@@ -0,0 +1,4 @@
1- name: restart nginx
2 service:
3 name: nginx
4 state: restarted
diff --git a/roles/services/containers/homer/tasks/main.yml b/roles/services/containers/homer/tasks/main.yml
new file mode 100644
index 0000000..b646d12
--- /dev/null
+++ b/roles/services/containers/homer/tasks/main.yml
@@ -0,0 +1,122 @@
1- name: set image fact
2 set_fact:
3 image: b4bz/homer:v23.05.1
4
5- name: set other facts
6 vars:
7 array: "{{ image.split('/', 1) }}"
8 set_fact:
9 repo_tag: "{{ array.1 }}"
10 custom_registry: "{{ docker_registry_url + '/' + docker_registry_username }}"
11
12- name: create homer directory
13 file:
14 path: "{{ docker_home }}/homer"
15 state: directory
16 owner: "{{ docker_username }}"
17 group: "{{ docker_username }}"
18 mode: '0755'
19
20- name: login to docker registry
21 become: yes
22 become_user: "{{ docker_username }}"
23 environment:
24 XDG_RUNTIME_DIR: "/run/user/{{ docker_uid }}"
25 docker_login:
26 docker_host: "unix://run/user/{{ docker_uid }}/docker.sock"
27 registry_url: "{{ docker_registry_url }}"
28 username: "{{ docker_registry_username }}"
29 password: "{{ docker_registry_password }}"
30
31- name: get homer image
32 become: yes
33 become_user: "{{ docker_username }}"
34 environment:
35 XDG_RUNTIME_DIR: "/run/user/{{ docker_uid }}"
36 docker_image:
37 name: "{{ image }}"
38 repository: "{{ custom_registry }}/{{ repo_tag }}"
39 push: yes
40 docker_host: "unix://run/user/{{ docker_uid }}/docker.sock"
41 source: pull
42 force_source: yes
43
44- name: create homer assets directory
45 file:
46 path: "{{ docker_home }}/homer/assets"
47 state: directory
48 owner: "{{ docker_username }}"
49 group: "{{ docker_username }}"
50 mode: '0755'
51
52- name: synchronize homer assets
53 synchronize:
54 src: "{{ homer_assets_dir }}"
55 dest: "{{ docker_home }}/homer/assets/"
56 delete: yes
57
58- name: set permissions on homer assets
59 file:
60 path: "{{ docker_home }}/homer/assets/"
61 owner: "{{ docker_username }}"
62 group: "{{ docker_username }}"
63 mode: u=rwX,g=rX,o=rX
64 recurse: yes
65
66- name: set permissions on homer assets
67 file:
68 path: "{{ docker_home }}/homer/assets/"
69 state: directory
70 owner: "{{ docker_username }}"
71 group: "{{ docker_username }}"
72 mode: '0755'
73 recurse: no
74
75- name: create homer docker network
76 docker_network:
77 name: "{{ homer_network_name }}"
78 docker_host: "unix://run/user/{{ docker_uid }}/docker.sock"
79 driver: bridge
80 ipam_config:
81 - subnet: "{{ homer_subnet }}"
82 gateway: "{{ homer_gateway }}"
83
84- name: create and deploy homer container
85 become: yes
86 become_user: "{{ docker_username }}"
87 environment:
88 XDG_RUNTIME_DIR: "/run/user/{{ docker_uid }}"
89 docker_container:
90 name: "homer"
91 hostname: "homer"
92 image: "{{ custom_registry }}/{{ repo_tag }}"
93 docker_host: "unix://run/user/{{ docker_uid }}/docker.sock"
94 purge_networks: yes
95 networks:
96 - name: "{{ homer_network_name }}"
97 ipv4_address: "{{ homer_ipv4 }}"
98 ports:
99 - "127.0.0.1:8001:8080"
100 state: 'started'
101 recreate: yes
102 restart_policy: unless-stopped
103 volumes:
104 - "{{ docker_home }}/homer/assets:/www/assets"
105
106- name: deploy nginx configuration
107 notify: restart nginx
108 register: nginx_config
109 copy:
110 src: "{{ homer_nginx_config }}"
111 dest: /etc/nginx/sites-available/homer.conf
112 owner: root
113 group: root
114 mode: '0644'
115
116- name: symlink site
117 file:
118 src: /etc/nginx/sites-available/homer.conf
119 dest: /etc/nginx/sites-enabled/homer.conf
120 owner: root
121 group: root
122 state: link
diff --git a/roles/services/containers/invidious/handlers/main.yml b/roles/services/containers/invidious/handlers/main.yml
new file mode 100644
index 0000000..a3a5d0b
--- /dev/null
+++ b/roles/services/containers/invidious/handlers/main.yml
@@ -0,0 +1,29 @@
1- name: login to docker registry
2 become: yes
3 become_user: "{{ docker_username }}"
4 environment:
5 XDG_RUNTIME_DIR: "/run/user/{{ docker_uid }}"
6 docker_login:
7 docker_host: "unix://run/user/{{ docker_uid }}/docker.sock"
8 registry_url: "{{ docker_registry_url }}"
9 username: "{{ docker_registry_username }}"
10 password: "{{ docker_registry_password }}"
11
12- name: build invidious image
13 become: yes
14 become_user: "{{ docker_username }}"
15 environment:
16 XDG_RUNTIME_DIR: "/run/user/{{ docker_uid }}"
17 docker_image:
18 name: "{{ docker_registry_url }}/{{ docker_registry_username }}/invidious:latest"
19 docker_host: "unix://run/user/{{ docker_uid }}/docker.sock"
20 build:
21 path: /srv/docker/invidious/src
22 dockerfile: docker/Dockerfile
23 source: build
24 push: yes
25
26- name: restart nginx
27 service:
28 name: nginx
29 state: restarted
diff --git a/roles/services/containers/invidious/tasks/main.yml b/roles/services/containers/invidious/tasks/main.yml
new file mode 100644
index 0000000..6bff0e2
--- /dev/null
+++ b/roles/services/containers/invidious/tasks/main.yml
@@ -0,0 +1,124 @@
1- name: set image fact
2 set_fact:
3 image: gitea.chudnick.com/sam/invidious:latest
4
5- name: set other facts
6 vars:
7 array: "{{ image.split('/', 1) }}"
8 set_fact:
9 repo_tag: "{{ array.1 }}"
10 custom_registry: "{{ docker_registry_url + '/' + docker_registry_username }}"
11
12- name: create invidious directory
13 file:
14 path: "{{ docker_home }}/invidious"
15 state: directory
16 owner: "{{ docker_username }}"
17 group: "{{ docker_username }}"
18 mode: '0755'
19
20- name: create postgres data directory
21 file:
22 path: "{{ docker_home }}/invidious/data"
23 state: directory
24 owner: "{{ docker_username }}"
25 group: "{{ docker_username }}"
26 mode: '0755'
27
28- name: clone invidious repo
29 become: yes
30 become_user: "{{ docker_username }}"
31 notify:
32 - login to docker registry
33 - build invidious image
34 git:
35 repo: "{{ invidious_repo }}"
36 dest: "{{ docker_home }}/invidious/src"
37 version: "master"
38
39- meta: flush_handlers
40
41- name: create invidious docker network
42 become: yes
43 become_user: "{{ docker_username }}"
44 docker_network:
45 name: "{{ invidious_network_name }}"
46 docker_host: "unix://run/user/{{ docker_uid }}/docker.sock"
47 driver: bridge
48 ipam_config:
49 - subnet: "{{ invidious_subnet }}"
50 gateway: "{{ invidious_gateway }}"
51
52- name: create and deploy invidious db
53 become: yes
54 become_user: "{{ docker_username }}"
55 environment:
56 XDG_RUNTIME_DIR: "/run/user/{{ docker_uid }}"
57 docker_container:
58 name: "invidious-db"
59 hostname: "invidious-db"
60 image: postgres:13
61 docker_host: "unix://run/user/{{ docker_uid }}/docker.sock"
62 purge_networks: yes
63 networks:
64 - name: "{{ invidious_network_name }}"
65 ipv4_address: "{{ invidious_db_ipv4 }}"
66 volumes:
67 - "{{ docker_home }}/invidious/data:/var/lib/postgresql/data"
68 - "{{ docker_home }}/invidious/src/config/sql:/config/sql"
69 - "{{ docker_home }}/invidious/src/docker/init-invidious-db.sh:/docker-entrypoint-initdb.d/init-invidious-db.sh"
70 env:
71 "POSTGRES_DB": "invidious"
72 "POSTGRES_USER": "invidious"
73 "POSTGRES_PASSWORD": "{{ invidious_postgres_password }}"
74 state: 'started'
75 recreate: yes
76 restart_policy: unless-stopped
77
78- name: create and deploy invidious container
79 become: yes
80 become_user: "{{ docker_username }}"
81 environment:
82 XDG_RUNTIME_DIR: "/run/user/{{ docker_uid }}"
83 docker_container:
84 name: "invidious"
85 hostname: "invidious"
86 image: "{{ image }}"
87 docker_host: "unix://run/user/{{ docker_uid }}/docker.sock"
88 purge_networks: yes
89 env:
90 "dbname": "invidious"
91 "user": "invidious"
92 "password": "{{ invidious_postgres_password }}"
93 "host": "invidious-db"
94 "port": "5432"
95 "check_tables": "true"
96 "https_only": "true"
97 "hsts": "true"
98 "domain": "{{ invidious_server_name }}"
99 "dark_mode": "dark"
100 networks:
101 - name: "{{ invidious_network_name }}"
102 ipv4_address: "{{ invidious_ipv4 }}"
103 ports:
104 - "127.0.0.1:{{ invidious_external_port }}:3000"
105 state: 'started'
106 recreate: yes
107 restart_policy: unless-stopped
108
109- name: deploy nginx configuration
110 notify: restart nginx
111 template:
112 src: "{{ invidious_nginx_config }}"
113 dest: /etc/nginx/sites-available/invidious.conf
114 owner: root
115 group: root
116 mode: '0644'
117
118- name: symlink site
119 file:
120 src: /etc/nginx/sites-available/invidious.conf
121 dest: /etc/nginx/sites-enabled/invidious.conf
122 owner: root
123 group: root
124 state: link
diff --git a/roles/services/containers/jellyfin/handlers/main.yml b/roles/services/containers/jellyfin/handlers/main.yml
new file mode 100644
index 0000000..5463835
--- /dev/null
+++ b/roles/services/containers/jellyfin/handlers/main.yml
@@ -0,0 +1,4 @@
1- name: restart nginx
2 service:
3 name: nginx
4 state: restarted
diff --git a/roles/services/containers/jellyfin/tasks/main.yml b/roles/services/containers/jellyfin/tasks/main.yml
new file mode 100644
index 0000000..c7a424d
--- /dev/null
+++ b/roles/services/containers/jellyfin/tasks/main.yml
@@ -0,0 +1,159 @@
1- name: set image fact
2 set_fact:
3 image: jellyfin/jellyfin:10.8.10
4
5- name: set other facts
6 vars:
7 array: "{{ image.split('/', 1) }}"
8 set_fact:
9 repo_tag: "{{ array.1 }}"
10 custom_registry: "{{ docker_registry_url + '/' + docker_registry_username }}"
11
12- name: create jellyfin directory
13 file:
14 path: "{{ docker_home }}/jellyfin"
15 state: directory
16 owner: "{{ docker_username }}"
17 group: "{{ docker_username }}"
18 mode: '0755'
19
20- name: login to docker registry
21 become: yes
22 become_user: "{{ docker_username }}"
23 environment:
24 XDG_RUNTIME_DIR: "/run/user/{{ docker_uid }}"
25 docker_login:
26 docker_host: "unix://run/user/{{ docker_uid }}/docker.sock"
27 registry_url: "{{ docker_registry_url }}"
28 username: "{{ docker_registry_username }}"
29 password: "{{ docker_registry_password }}"
30
31- name: get jellyfin image
32 become: yes
33 become_user: "{{ docker_username }}"
34 environment:
35 XDG_RUNTIME_DIR: "/run/user/{{ docker_uid }}"
36 docker_image:
37 name: "{{ image }}"
38 repository: "{{ custom_registry }}/{{ repo_tag }}"
39 docker_host: "unix://run/user/{{ docker_uid }}/docker.sock"
40 source: pull
41 force_source: yes
42 push: yes
43
44- name: create jellyfin config directory
45 file:
46 path: "{{ docker_home }}/jellyfin/config"
47 state: directory
48 owner: "{{ docker_username }}"
49 group: "{{ docker_username }}"
50 mode: '0755'
51
52- name: create jellyfin cache directory
53 file:
54 path: "{{ docker_home }}/jellyfin/cache"
55 state: directory
56 owner: "{{ docker_username }}"
57 group: "{{ docker_username }}"
58 mode: '0755'
59
60- name: create jellyfin media directory
61 file:
62 path: "{{ docker_home }}/jellyfin/media"
63 state: directory
64 group: "{{ docker_username }}"
65 mode: '0755'
66
67- name: copy jellyfin config
68 synchronize:
69 src: "{{ jellyfin_config }}"
70 dest: "{{ docker_home }}/jellyfin/config"
71
72- name: copy jellyfin media
73 synchronize:
74 src: "{{ jellyfin_media }}"
75 dest: "{{ docker_home }}/jellyfin/media"
76 ignore_errors: yes
77
78- name: copy jellyfin web config
79 copy:
80 src: "{{ jellyfin_web_config }}"
81 dest: "{{ docker_home }}/jellyfin/web-config.json"
82 owner: "{{ docker_username }}"
83 group: "{{ docker_username }}"
84 mode: '0644'
85
86- name: set config permissions
87 file:
88 path: "{{ docker_home }}/jellyfin/config"
89 owner: "{{ docker_username }}"
90 group: "{{ docker_username }}"
91 mode: '0755'
92 recurse: yes
93
94- name: set media permissions
95 file:
96 path: "{{ docker_home }}/jellyfin/media"
97 owner: "{{ docker_username }}"
98 group: "{{ docker_username }}"
99 mode: '0755'
100 recurse: yes
101
102- name: create jellyfin docker network
103 become: yes
104 become_user: "{{ docker_username }}"
105 docker_network:
106 name: "{{ jellyfin_network_name }}"
107 docker_host: "unix://run/user/{{ docker_uid }}/docker.sock"
108 driver: bridge
109 ipam_config:
110 - subnet: "{{ jellyfin_subnet }}"
111 gateway: "{{ jellyfin_gateway }}"
112
113- name: create and deploy jellyfin container
114 become: yes
115 become_user: "{{ docker_username }}"
116 environment:
117 XDG_RUNTIME_DIR: "/run/user/{{ docker_uid }}"
118 docker_container:
119 name: "jellyfin"
120 image: "{{ custom_registry }}/{{ repo_tag }}"
121 pull: yes
122 docker_host: "unix://run/user/{{ docker_uid }}/docker.sock"
123 purge_networks: yes
124 networks:
125 - name: "{{ jellyfin_network_name }}"
126 ipv4_address: "{{ jellyfin_ipv4 }}"
127 ports:
128 - "127.0.0.1:8096:8096"
129 volumes:
130 - "{{ docker_home }}/jellyfin/config:/config"
131 - "{{ docker_home }}/jellyfin/cache:/cache"
132 - "{{ docker_home }}/arr/data/media:/media:ro"
133 - "{{ docker_home }}/jellyfin/web-config.json:/jellyfin/jellyfin-web/config.json"
134 env:
135 JELLYFIN_PublishedServerUrl: "{{ jellyfin_url }}"
136 cap_drop:
137 - all
138 hostname: "jellyfin"
139 restart_policy: unless-stopped
140 state: 'started'
141 recreate: yes
142
143- name: deploy nginx configuration
144 notify: restart nginx
145 register: nginx_config
146 copy:
147 src: "{{ jellyfin_nginx_config }}"
148 dest: /etc/nginx/sites-available/jellyfin.conf
149 owner: root
150 group: root
151 mode: '0644'
152
153- name: symlink site
154 file:
155 src: /etc/nginx/sites-available/jellyfin.conf
156 dest: /etc/nginx/sites-enabled/jellyfin.conf
157 owner: root
158 group: root
159 state: link
diff --git a/roles/services/containers/kanboard/handlers/main.yml b/roles/services/containers/kanboard/handlers/main.yml
new file mode 100644
index 0000000..de5dcb6
--- /dev/null
+++ b/roles/services/containers/kanboard/handlers/main.yml
@@ -0,0 +1,18 @@
1- name: build pywttr-docker image
2 become: yes
3 become_user: "{{ docker_username }}"
4 environment:
5 XDG_RUNTIME_DIR: "/run/user/{{ docker_uid }}"
6 docker_image:
7 name: "{{ docker_registry_url }}/{{ docker_registry_username }}/pywttr-docker:latest"
8 docker_host: "unix://run/user/{{ docker_uid }}/docker.sock"
9 build:
10 path: /srv/docker/pywttr-docker/src
11 source: build
12 push: yes
13 force_source: yes
14
15- name: restart nginx
16 service:
17 name: nginx
18 state: restarted
diff --git a/roles/services/containers/kanboard/tasks/main.yml b/roles/services/containers/kanboard/tasks/main.yml
new file mode 100644
index 0000000..1efc16e
--- /dev/null
+++ b/roles/services/containers/kanboard/tasks/main.yml
@@ -0,0 +1,93 @@
1- name: set image fact
2 set_fact:
3 image: kanboard/kanboard:v1.2.30
4
5- name: set other facts
6 vars:
7 array: "{{ image.split('/', 1) }}"
8 set_fact:
9 repo_tag: "{{ array.1 }}"
10 custom_registry: "{{ docker_registry_url + '/' + docker_registry_username }}"
11
12- name: create kanboard directory
13 file:
14 path: "{{ docker_home }}/kanboard"
15 state: directory
16 owner: "{{ docker_username }}"
17 group: "{{ docker_username }}"
18 mode: '0755'
19
20- name: create data directory
21 file:
22 path: "{{ docker_home }}/kanboard/data"
23 state: directory
24 owner: "{{ docker_username }}"
25 group: "{{ docker_username }}"
26 mode: '0755'
27
28- name: deploy custom configuration
29 copy:
30 src: "{{ kanboard_config }}"
31 dest: "{{ docker_home }}/kanboard/data/config.php"
32 owner: "{{ docker_username }}"
33 group: "{{ docker_username }}"
34 mode: '0644'
35
36- name: create plugins directory
37 file:
38 path: "{{ docker_home }}/kanboard/plugins"
39 state: directory
40 owner: "{{ docker_username }}"
41 group: "{{ docker_username }}"
42 mode: '0755'
43
44- name: create kanboard network
45 become: yes
46 become_user: "{{ docker_username }}"
47 docker_network:
48 name: "{{ kanboard_network_name }}"
49 docker_host: "unix://run/user/{{ docker_uid }}/docker.sock"
50 driver: bridge
51 ipam_config:
52 - subnet: "{{ kanboard_subnet }}"
53 gateway: "{{ kanboard_gateway }}"
54
55- name: create and deploy kanboard container
56 become: yes
57 become_user: "{{ docker_username }}"
58 environment:
59 XDG_RUNTIME_DIR: "/run/user/{{ docker_uid }}"
60 docker_container:
61 name: "kanboard"
62 hostname: "kanboard"
63 image: "{{ image }}"
64 docker_host: "unix://run/user/{{ docker_uid }}/docker.sock"
65 purge_networks: yes
66 networks:
67 - name: "{{ kanboard_network_name }}"
68 ipv4_address: "{{ kanboard_ipv4 }}"
69 ports:
70 - "127.0.0.1:{{ kanboard_external_port }}:80"
71 volumes:
72 - "{{ docker_home }}/kanboard/data:/var/www/app/data"
73 - "{{ docker_home }}/kanboard/plugins:/var/www/app/plugins"
74 state: 'started'
75 recreate: yes
76 restart_policy: unless-stopped
77
78- name: deploy nginx configuration
79 notify: restart nginx
80 template:
81 src: "{{ kanboard_nginx_config }}"
82 dest: /etc/nginx/sites-available/kanboard.conf
83 owner: root
84 group: root
85 mode: '0644'
86
87- name: symlink site
88 file:
89 src: /etc/nginx/sites-available/kanboard.conf
90 dest: /etc/nginx/sites-enabled/kanboard.conf
91 owner: root
92 group: root
93 state: link
diff --git a/roles/services/containers/navidrome/handlers/main.yml b/roles/services/containers/navidrome/handlers/main.yml
new file mode 100644
index 0000000..5463835
--- /dev/null
+++ b/roles/services/containers/navidrome/handlers/main.yml
@@ -0,0 +1,4 @@
1- name: restart nginx
2 service:
3 name: nginx
4 state: restarted
diff --git a/roles/services/containers/navidrome/tasks/main.yml b/roles/services/containers/navidrome/tasks/main.yml
new file mode 100644
index 0000000..e95e849
--- /dev/null
+++ b/roles/services/containers/navidrome/tasks/main.yml
@@ -0,0 +1,117 @@
1- name: set image fact
2 set_fact:
3 image: deluan/navidrome:0.49.2
4
5- name: set other facts
6 vars:
7 array: "{{ image.split('/', 1) }}"
8 set_fact:
9 repo_tag: "{{ array.1 }}"
10 custom_registry: "{{ docker_registry_url + '/' + docker_registry_username }}"
11
12- name: create navidrome directory
13 file:
14 path: "{{ docker_home }}/navidrome"
15 state: directory
16 owner: "{{ docker_username }}"
17 group: "{{ docker_username }}"
18 mode: '0755'
19
20- name: create navidrome data directory
21 file:
22 path: "{{ docker_home }}/navidrome/data"
23 state: directory
24 owner: "{{ docker_username }}"
25 group: "{{ docker_username }}"
26 mode: '0755'
27
28- name: create navidrome music directory
29 file:
30 path: "{{ docker_home }}/navidrome/music"
31 state: directory
32 owner: "{{ docker_username }}"
33 group: "{{ docker_username }}"
34 mode: '0755'
35
36- name: login to docker registry
37 become: yes
38 become_user: "{{ docker_username }}"
39 environment:
40 XDG_RUNTIME_DIR: "/run/user/{{ docker_uid }}"
41 docker_login:
42 docker_host: "unix://run/user/{{ docker_uid }}/docker.sock"
43 registry_url: "{{ docker_registry_url }}"
44 username: "{{ docker_registry_username }}"
45 password: "{{ docker_registry_password }}"
46
47- name: pull and push navidrome image
48 become: yes
49 become_user: "{{ docker_username }}"
50 environment:
51 XDG_RUNTIME_DIR: "/run/user/{{ docker_uid }}"
52 docker_image:
53 name: "{{ image }}"
54 repository: "{{ custom_registry }}/{{ repo_tag }}"
55 docker_host: "unix://run/user/{{ docker_uid }}/docker.sock"
56 source: pull
57 force_source: yes
58 push: yes
59
60- name: create navidrome docker network
61 docker_network:
62 name: "{{ navidrome_network_name }}"
63 docker_host: "unix://run/user/{{ docker_uid }}/docker.sock"
64 driver: bridge
65 ipam_config:
66 - subnet: "{{ navidrome_subnet }}"
67 gateway: "{{ navidrome_gateway }}"
68
69- name: create and deploy navidrome container
70 become: yes
71 become_user: "{{ docker_username }}"
72 environment:
73 XDG_RUNTIME_DIR: "/run/user/{{ docker_uid }}"
74 docker_container:
75 name: "navidrome"
76 hostname: "navidrome"
77 image: "{{ custom_registry }}/{{ repo_tag }}"
78 pull: yes
79 docker_host: "unix://run/user/{{ docker_uid }}/docker.sock"
80 purge_networks: yes
81 networks:
82 - name: "{{ navidrome_network_name }}"
83 ipv4_address: "{{ navidrome_ipv4 }}"
84 ports:
85 - "127.0.0.1:4533:4533"
86 state: 'started'
87 recreate: yes
88 restart_policy: unless-stopped
89 env:
90 "ND_AUTHREQUEST_LIMIT": "2"
91 "ND_PASSWORDENCRYPTIONKEY": "{{ navidrome_encryptionkey }}"
92 "ND_LASTFM_ENABLED": "false"
93 "ND_PROMETHEUS_ENABLED": "true"
94 "ND_PROMETHEUS_METRICSPATH": "/metrics"
95 "ND_REVERSEPROXYWHITELIST": "172.25.5.0/24"
96 "ND_LOGLEVEL": "debug"
97 volumes:
98 - "{{ docker_home }}/navidrome/data:/data"
99 - "{{ docker_home }}/arr/data/media/music:/music:ro"
100
101- name: deploy nginx configuration
102 notify: restart nginx
103 register: nginx_config
104 copy:
105 src: "{{ navidrome_nginx_config }}"
106 dest: /etc/nginx/sites-available/navidrome.conf
107 owner: root
108 group: root
109 mode: '0644'
110
111- name: symlink site
112 file:
113 src: /etc/nginx/sites-available/navidrome.conf
114 dest: /etc/nginx/sites-enabled/navidrome.conf
115 owner: root
116 group: root
117 state: link
diff --git a/roles/services/containers/nextcloud/handlers/main.yml b/roles/services/containers/nextcloud/handlers/main.yml
new file mode 100644
index 0000000..5463835
--- /dev/null
+++ b/roles/services/containers/nextcloud/handlers/main.yml
@@ -0,0 +1,4 @@
1- name: restart nginx
2 service:
3 name: nginx
4 state: restarted
diff --git a/roles/services/containers/nextcloud/tasks/main.yml b/roles/services/containers/nextcloud/tasks/main.yml
new file mode 100644
index 0000000..fbd4a76
--- /dev/null
+++ b/roles/services/containers/nextcloud/tasks/main.yml
@@ -0,0 +1,184 @@
1- name: set image fact
2 set_fact:
3 image: nextcloud:27.0.0-apache
4
5- name: set other facts
6 set_fact:
7 repo_tag: "{{ image }}"
8 custom_registry: "{{ docker_registry_url + '/' + docker_registry_username }}"
9
10- name: create nextcloud directory
11 file:
12 path: "{{ docker_home }}/nextcloud"
13 state: directory
14 owner: "{{ docker_username }}"
15 group: "{{ docker_username }}"
16 mode: '0755'
17
18- name: create nextcloud app directory
19 file:
20 path: "{{ docker_home }}/nextcloud/app/"
21 state: directory
22 owner: "{{ docker_username }}"
23 group: "{{ docker_username }}"
24 mode: '0755'
25
26- name: create nextcloud data directory
27 file:
28 path: "{{ docker_home }}/nextcloud/data/"
29 state: directory
30 owner: "{{ docker_username }}"
31 group: "{{ docker_username }}"
32 mode: '0755'
33
34- name: login to docker registry
35 become: yes
36 become_user: "{{ docker_username }}"
37 environment:
38 XDG_RUNTIME_DIR: "/run/user/{{ docker_uid }}"
39 docker_login:
40 docker_host: "unix://run/user/{{ docker_uid }}/docker.sock"
41 registry_url: "{{ docker_registry_url }}"
42 username: "{{ docker_registry_username }}"
43 password: "{{ docker_registry_password }}"
44
45- name: pull and push nextcloud image
46 become: yes
47 become_user: "{{ docker_username }}"
48 environment:
49 XDG_RUNTIME_DIR: "/run/user/{{ docker_uid }}"
50 docker_image:
51 name: "{{ image }}"
52 repository: "{{ custom_registry }}/{{ repo_tag }}"
53 docker_host: "unix://run/user/{{ docker_uid }}/docker.sock"
54 source: pull
55 force_source: yes
56 push: yes
57
58- name: create nextcloud docker network
59 docker_network:
60 name: "{{ nextcloud_network_name }}"
61 docker_host: "unix://run/user/{{ docker_uid }}/docker.sock"
62 driver: bridge
63 ipam_config:
64 - subnet: "{{ nextcloud_subnet }}"
65 gateway: "{{ nextcloud_gateway }}"
66
67- name: create and deploy postgres container
68 become: yes
69 become_user: "{{ docker_username }}"
70 environment:
71 XDG_RUNTIME_DIR: "/run/user/{{ docker_uid }}"
72 docker_container:
73 name: "nextcloud-postgres"
74 hostname: "nextcloud-postgres"
75 image: "postgres:alpine"
76 pull: yes
77 docker_host: "unix://run/user/{{ docker_uid }}/docker.sock"
78 purge_networks: yes
79 networks:
80 - name: "{{ nextcloud_network_name }}"
81 ipv4_address: "{{ nextcloud_postgres_ipv4 }}"
82 state: 'started'
83 comparisons:
84 '*': strict
85 restart_policy: unless-stopped
86 env:
87 "POSTGRES_USER": "{{ nextcloud_postgres_user }}"
88 "POSTGRES_PASSWORD": "{{ nextcloud_postgres_password }}"
89 "POSTGRES_DB": "{{ nextcloud_postgres_db }}"
90 volumes:
91 - "{{ docker_home }}/nextcloud/data:/var/lib/postgresql/data"
92
93- name: create and deploy redis container
94 become: yes
95 become_user: "{{ docker_username }}"
96 environment:
97 XDG_RUNTIME_DIR: "/run/user/{{ docker_uid }}"
98 docker_container:
99 name: "nextcloud-redis"
100 hostname: "nextcloud-redis"
101 image: "redis:alpine"
102 pull: yes
103 docker_host: "unix://run/user/{{ docker_uid }}/docker.sock"
104 purge_networks: yes
105 networks:
106 - name: "{{ nextcloud_network_name }}"
107 ipv4_address: "{{ nextcloud_redis_ipv4 }}"
108 state: 'started'
109 comparisons:
110 '*': strict
111 restart_policy: unless-stopped
112
113- name: create and deploy nextcloud container
114 become: yes
115 become_user: "{{ docker_username }}"
116 environment:
117 XDG_RUNTIME_DIR: "/run/user/{{ docker_uid }}"
118 docker_container:
119 name: "nextcloud"
120 hostname: "nextcloud"
121 image: "{{ custom_registry }}/{{ repo_tag }}"
122 pull: yes
123 docker_host: "unix://run/user/{{ docker_uid }}/docker.sock"
124 purge_networks: yes
125 networks:
126 - name: "{{ nextcloud_network_name }}"
127 ipv4_address: "{{ nextcloud_ipv4 }}"
128 ports:
129 - "127.0.0.1:{{ nextcloud_external_port }}:80"
130 state: 'started'
131 comparisons:
132 '*': strict
133 restart_policy: unless-stopped
134 env:
135 "POSTGRES_USER": "{{ nextcloud_postgres_user }}"
136 "POSTGRES_PASSWORD": "{{ nextcloud_postgres_password }}"
137 "POSTGRES_DB": "{{ nextcloud_postgres_db }}"
138 "POSTGRES_HOST": "nextcloud-postgres"
139 "REDIS_HOST": "nextcloud-redis"
140 "NEXTCLOUD_ADMIN_USER": "{{ nextcloud_admin }}"
141 "NEXTCLOUD_ADMIN_PASSWORD": "{{ nextcloud_admin_password }}"
142 "NEXTCLOUD_TRUSTED_DOMAINS": "{{ nextcloud_trusted_domains }}"
143 volumes:
144 - "{{ docker_home }}/nextcloud/app:/var/www/html"
145
146- name: create and deploy nextcloud cron container
147 become: yes
148 become_user: "{{ docker_username }}"
149 environment:
150 XDG_RUNTIME_DIR: "/run/user/{{ docker_uid }}"
151 docker_container:
152 name: "nextcloud-cron"
153 hostname: "nextcloud-cron"
154 image: "{{ custom_registry }}/{{ repo_tag }}"
155 entrypoint: "/cron.sh"
156 pull: yes
157 docker_host: "unix://run/user/{{ docker_uid }}/docker.sock"
158 purge_networks: yes
159 networks:
160 - name: "{{ nextcloud_network_name }}"
161 ipv4_address: "{{ nextcloud_cron_ipv4 }}"
162 state: 'started'
163 recreate: yes
164 restart_policy: unless-stopped
165 volumes:
166 - "{{ docker_home }}/nextcloud/app:/var/www/html"
167
168- name: deploy nginx configuration
169 notify: restart nginx
170 register: nginx_config
171 copy:
172 src: "{{ nextcloud_nginx_config }}"
173 dest: /etc/nginx/sites-available/nextcloud.conf
174 owner: root
175 group: root
176 mode: '0644'
177
178- name: symlink site
179 file:
180 src: /etc/nginx/sites-available/nextcloud.conf
181 dest: /etc/nginx/sites-enabled/nextcloud.conf
182 owner: root
183 group: root
184 state: link
diff --git a/roles/services/containers/photoprism/defaults/main.yml b/roles/services/containers/photoprism/defaults/main.yml
new file mode 100644
index 0000000..ceca8c3
--- /dev/null
+++ b/roles/services/containers/photoprism/defaults/main.yml
@@ -0,0 +1,10 @@
1photoprism_admin_user: "admin"
2photoprism_auth_mode: "password"
3photoprism_site_url: "https://photos.chudnick.com"
4photoprism_external_port: 2342
5photoprism_nginx_config: data/photoprism/photoprism.conf
6photoprism_network_name: photoprism_net
7photoprism_subnet: 172.25.15.0/24
8photoprism_gateway: 172.25.15.1
9photoprism_ipv4: 172.25.15.2
10nextcloud_external_port: 8006
diff --git a/roles/services/containers/photoprism/handlers/main.yml b/roles/services/containers/photoprism/handlers/main.yml
new file mode 100644
index 0000000..5463835
--- /dev/null
+++ b/roles/services/containers/photoprism/handlers/main.yml
@@ -0,0 +1,4 @@
1- name: restart nginx
2 service:
3 name: nginx
4 state: restarted
diff --git a/roles/services/containers/photoprism/tasks/main.yml b/roles/services/containers/photoprism/tasks/main.yml
new file mode 100644
index 0000000..e6ac544
--- /dev/null
+++ b/roles/services/containers/photoprism/tasks/main.yml
@@ -0,0 +1,115 @@
1- name: set image fact
2 set_fact:
3 image: photoprism/photoprism:221118-jammy
4
5- name: set other facts
6 vars:
7 array: "{{ image.split('/', 1) }}"
8 set_fact:
9 repo_tag: "{{ array.1 }}"
10 custom_registry: "{{ docker_registry_url + '/' + docker_registry_username }}"
11
12- name: create photoprism directory
13 file:
14 path: "{{ docker_home }}/photoprism"
15 state: directory
16 owner: "{{ docker_username }}"
17 group: "{{ docker_username }}"
18 mode: '0755'
19
20- name: login to docker registry
21 become: yes
22 become_user: "{{ docker_username }}"
23 environment:
24 XDG_RUNTIME_DIR: "/run/user/{{ docker_uid }}"
25 docker_login:
26 docker_host: "unix://run/user/{{ docker_uid }}/docker.sock"
27 registry_url: "{{ docker_registry_url }}"
28 username: "{{ docker_registry_username }}"
29 password: "{{ docker_registry_password }}"
30
31- name: get photoprism image
32 become: yes
33 become_user: "{{ docker_username }}"
34 environment:
35 XDG_RUNTIME_DIR: "/run/user/{{ docker_uid }}"
36 docker_image:
37 name: "{{ image }}"
38 repository: "{{ custom_registry }}/{{ repo_tag }}"
39 push: yes
40 docker_host: "unix://run/user/{{ docker_uid }}/docker.sock"
41 source: pull
42 force_source: yes
43
44- name: create photoprism data directory
45 file:
46 path: "{{ docker_home }}/photoprism/data"
47 state: directory
48 owner: "{{ docker_username }}"
49 group: "{{ docker_username }}"
50 mode: '0755'
51
52- name: create photoprism photos directory
53 file:
54 path: "{{ docker_home }}/photoprism/photos"
55 state: directory
56 owner: "{{ docker_username }}"
57 group: "{{ docker_username }}"
58 mode: '0755'
59
60- name: create photoprism docker network
61 docker_network:
62 name: "{{ photoprism_network_name }}"
63 docker_host: "unix://run/user/{{ docker_uid }}/docker.sock"
64 driver: bridge
65 ipam_config:
66 - subnet: "{{ photoprism_subnet }}"
67 gateway: "{{ photoprism_gateway }}"
68
69- name: create and deploy photoprism container
70 become: yes
71 become_user: "{{ docker_username }}"
72 environment:
73 XDG_RUNTIME_DIR: "/run/user/{{ docker_uid }}"
74 docker_container:
75 name: "photoprism"
76 hostname: "photoprism"
77 image: "{{ custom_registry }}/{{ repo_tag }}"
78 docker_host: "unix://run/user/{{ docker_uid }}/docker.sock"
79 purge_networks: yes
80 networks:
81 - name: "{{ photoprism_network_name }}"
82 ipv4_address: "{{ photoprism_ipv4 }}"
83 ports:
84 - "127.0.0.1:{{ photoprism_external_port }}:2342"
85 state: 'started'
86 recreate: yes
87 restart_policy: unless-stopped
88 volumes:
89 - "{{ docker_home }}/photoprism/photos:/photoprism/originals"
90 - "{{ docker_home }}/photoprism/data:/photoprism/storage"
91 env:
92 "PHOTOPRISM_ADMIN_USER": "{{ photoprism_admin_user }}"
93 "PHOTOPRISM_ADMIN_PASSWORD": "{{ photoprism_admin_password }}"
94 "PHOTOPRISM_AUTH_MODE": "{{ photoprism_auth_mode }}"
95 "PHOTOPRISM_SITE_URL": "{{ photoprism_site_url }}"
96 "PHOTOPRISM_DATABASE_DRIVER": "sqlite"
97 "PHOTOPRISM_DISABLE_PLACES": "true"
98
99- name: deploy nginx configuration
100 notify: restart nginx
101 register: nginx_config
102 copy:
103 src: "{{ photoprism_nginx_config }}"
104 dest: /etc/nginx/sites-available/photoprism.conf
105 owner: root
106 group: root
107 mode: '0644'
108
109- name: symlink site
110 file:
111 src: /etc/nginx/sites-available/photoprism.conf
112 dest: /etc/nginx/sites-enabled/photoprism.conf
113 owner: root
114 group: root
115 state: link
diff --git a/roles/services/containers/pihole_exporter/tasks/main.yml b/roles/services/containers/pihole_exporter/tasks/main.yml
new file mode 100644
index 0000000..4c52dc7
--- /dev/null
+++ b/roles/services/containers/pihole_exporter/tasks/main.yml
@@ -0,0 +1,97 @@
1- name: set image fact
2 set_fact:
3 image: ekofr/pihole-exporter:v0.4.0
4
5- name: set other facts
6 vars:
7 array: "{{ image.split('/', 1) }}"
8 set_fact:
9 repo_tag: "{{ array.1 }}"
10 custom_registry: "{{ docker_registry_url + '/' + docker_registry_username }}"
11
12- name: create pihole_exporter directory
13 file:
14 path: "{{ docker_home }}/pihole_exporter"
15 state: directory
16 owner: "{{ docker_username }}"
17 group: "{{ docker_username }}"
18 mode: '0755'
19
20- name: login to docker registry
21 become: yes
22 become_user: "{{ docker_username }}"
23 environment:
24 XDG_RUNTIME_DIR: "/run/user/{{ docker_uid }}"
25 docker_login:
26 docker_host: "unix://run/user/{{ docker_uid }}/docker.sock"
27 registry_url: "{{ docker_registry_url }}"
28 username: "{{ docker_registry_username }}"
29 password: "{{ docker_registry_password }}"
30
31- name: get pihole_exporter image
32 become: yes
33 become_user: "{{ docker_username }}"
34 environment:
35 XDG_RUNTIME_DIR: "/run/user/{{ docker_uid }}"
36 docker_image:
37 name: "{{ image }}"
38 repository: "{{ custom_registry }}/{{ repo_tag }}"
39 docker_host: "unix://run/user/{{ docker_uid }}/docker.sock"
40 source: pull
41 force_source: yes
42 push: yes
43
44- name: create pihole_exporter docker network
45 become: yes
46 become_user: "{{ docker_username }}"
47 docker_network:
48 name: "{{ pihole_exporter_network_name }}"
49 docker_host: "unix://run/user/{{ docker_uid }}/docker.sock"
50 driver: bridge
51 ipam_config:
52 - subnet: "{{ pihole_exporter_subnet }}"
53 gateway: "{{ pihole_exporter_gateway }}"
54
55- name: create and deploy pihole_exporter container
56 become: yes
57 become_user: "{{ docker_username }}"
58 environment:
59 XDG_RUNTIME_DIR: "/run/user/{{ docker_uid }}"
60 docker_container:
61 name: "pihole_exporter"
62 hostname: "pihole_exporter"
63 image: "{{ custom_registry }}/{{ repo_tag }}"
64 pull: yes
65 docker_host: "unix://run/user/{{ docker_uid }}/docker.sock"
66 purge_networks: yes
67 networks:
68 - name: "{{ pihole_exporter_network_name }}"
69 ports:
70 - "127.0.0.1:9617:9617"
71 state: 'started'
72 recreate: yes
73 restart_policy: unless-stopped
74 env:
75 "PIHOLE_HOSTNAME": "{{ pihole_ip }}"
76 "PIHOLE_API_TOKEN": "{{ pihole_api_token }}"
77 "PORT": "{{ pihole_api_port }}"
78 cap_drop:
79 - all
80
81- name: deploy nginx configuration
82 notify: restart nginx
83 register: nginx_config
84 copy:
85 src: "{{ pihole_exporter_nginx_config }}"
86 dest: /etc/nginx/sites-available/pihole-exporter.conf
87 owner: root
88 group: root
89 mode: '0644'
90
91- name: symlink site
92 file:
93 src: /etc/nginx/sites-available/pihole-exporter.conf
94 dest: /etc/nginx/sites-enabled/pihole-exporter.conf
95 owner: root
96 group: root
97 state: link
diff --git a/roles/services/containers/pywttr_docker/handlers/main.yml b/roles/services/containers/pywttr_docker/handlers/main.yml
new file mode 100644
index 0000000..de5dcb6
--- /dev/null
+++ b/roles/services/containers/pywttr_docker/handlers/main.yml
@@ -0,0 +1,18 @@
1- name: build pywttr-docker image
2 become: yes
3 become_user: "{{ docker_username }}"
4 environment:
5 XDG_RUNTIME_DIR: "/run/user/{{ docker_uid }}"
6 docker_image:
7 name: "{{ docker_registry_url }}/{{ docker_registry_username }}/pywttr-docker:latest"
8 docker_host: "unix://run/user/{{ docker_uid }}/docker.sock"
9 build:
10 path: /srv/docker/pywttr-docker/src
11 source: build
12 push: yes
13 force_source: yes
14
15- name: restart nginx
16 service:
17 name: nginx
18 state: restarted
diff --git a/roles/services/containers/pywttr_docker/tasks/main.yml b/roles/services/containers/pywttr_docker/tasks/main.yml
new file mode 100644
index 0000000..45f7b2f
--- /dev/null
+++ b/roles/services/containers/pywttr_docker/tasks/main.yml
@@ -0,0 +1,74 @@
1- name: set image fact
2 set_fact:
3 image: gitea.chudnick.com/sam/pywttr-docker:latest
4
5- name: set other facts
6 vars:
7 array: "{{ image.split('/', 1) }}"
8 set_fact:
9 repo_tag: "{{ array.1 }}"
10 custom_registry: "{{ docker_registry_url + '/' + docker_registry_username }}"
11
12- name: create pywttr-docker directory
13 file:
14 path: "{{ docker_home }}/pywttr-docker"
15 state: directory
16 owner: "{{ docker_username }}"
17 group: "{{ docker_username }}"
18 mode: '0755'
19
20- name: clone pywttr-docker repository
21 notify: build pywttr-docker image
22 git:
23 repo: https://gitea.chudnick.com/sam/pywttr-docker
24 dest: "{{ docker_home }}/pywttr-docker/src"
25
26- meta: flush_handlers
27
28- name: create pywttr-docker network
29 become: yes
30 become_user: "{{ docker_username }}"
31 docker_network:
32 name: "{{ pywttr_docker_network_name }}"
33 docker_host: "unix://run/user/{{ docker_uid }}/docker.sock"
34 driver: bridge
35 ipam_config:
36 - subnet: "{{ pywttr_docker_subnet }}"
37 gateway: "{{ pywttr_docker_gateway }}"
38
39- name: create and deploy pywttr-docker container
40 become: yes
41 become_user: "{{ docker_username }}"
42 environment:
43 XDG_RUNTIME_DIR: "/run/user/{{ docker_uid }}"
44 docker_container:
45 name: "pywttr-docker"
46 hostname: "pywttr-docker"
47 image: "{{ image }}"
48 docker_host: "unix://run/user/{{ docker_uid }}/docker.sock"
49 purge_networks: yes
50 networks:
51 - name: "{{ pywttr_docker_network_name }}"
52 ipv4_address: "{{ pywttr_docker_ipv4 }}"
53 ports:
54 - "127.0.0.1:{{ pywttr_docker_external_port }}:8000"
55 state: 'started'
56 recreate: yes
57 restart_policy: unless-stopped
58
59- name: deploy nginx configuration
60 notify: restart nginx
61 template:
62 src: "{{ pywttr_docker_nginx_config }}"
63 dest: /etc/nginx/sites-available/pywttr-docker.conf
64 owner: root
65 group: root
66 mode: '0644'
67
68- name: symlink site
69 file:
70 src: /etc/nginx/sites-available/pywttr-docker.conf
71 dest: /etc/nginx/sites-enabled/pywttr-docker.conf
72 owner: root
73 group: root
74 state: link
diff --git a/roles/services/containers/renovate/tasks/main.yml b/roles/services/containers/renovate/tasks/main.yml
new file mode 100644
index 0000000..bbbfe11
--- /dev/null
+++ b/roles/services/containers/renovate/tasks/main.yml
@@ -0,0 +1,87 @@
1- name: set image fact
2 set_fact:
3 image: renovate/renovate:35.141.3-slim
4
5- name: set other facts
6 vars:
7 array: "{{ image.split('/', 1) }}"
8 set_fact:
9 repo_tag: "{{ array.1 }}"
10 custom_registry: "{{ docker_registry_url + '/' + docker_registry_username }}"
11
12- name: create renovate directory
13 file:
14 path: "{{ docker_home }}/renovate"
15 state: directory
16 owner: "{{ docker_username }}"
17 group: "{{ docker_username }}"
18 mode: '0755'
19
20- name: login to docker registry
21 become: yes
22 become_user: "{{ docker_username }}"
23 environment:
24 XDG_RUNTIME_DIR: "/run/user/{{ docker_uid }}"
25 docker_login:
26 docker_host: "unix://run/user/{{ docker_uid }}/docker.sock"
27 registry_url: "{{ docker_registry_url }}"
28 username: "{{ docker_registry_username }}"
29 password: "{{ docker_registry_password }}"
30
31- name: create renovate docker network
32 become: yes
33 become_user: "{{ docker_username }}"
34 docker_network:
35 name: "{{ renovate_network_name }}"
36 docker_host: "unix://run/user/{{ docker_uid }}/docker.sock"
37 driver: bridge
38 ipam_config:
39 - subnet: "{{ renovate_subnet }}"
40 gateway: "{{ renovate_gateway }}"
41
42- name: pull and push renovate image
43 become: yes
44 become_user: "{{ docker_username }}"
45 environment:
46 XDG_RUNTIME_DIR: "/run/user/{{ docker_uid }}"
47 docker_image:
48 name: "{{ image }}"
49 repository: "{{ custom_registry }}/{{ repo_tag }}"
50 docker_host: "unix://run/user/{{ docker_uid }}/docker.sock"
51 source: pull
52 force_source: yes
53 push: yes
54
55- name: create and deploy renovate container
56 become: yes
57 become_user: "{{ docker_username }}"
58 environment:
59 XDG_RUNTIME_DIR: "/run/user/{{ docker_uid }}"
60 docker_container:
61 name: "renovate"
62 image: "{{ custom_registry }}/{{ repo_tag }}"
63 pull: yes
64 recreate: yes
65 docker_host: "unix://run/user/{{ docker_uid }}/docker.sock"
66 purge_networks: yes
67 networks:
68 - name: "{{ renovate_network_name }}"
69 ipv4_address: "{{ renovate_ipv4 }}"
70 env:
71 "RENOVATE_ENDPOINT": "{{ renovate_endpoint }}"
72 "RENOVATE_PLATFORM": "gitea"
73 "RENOVATE_TOKEN": "{{ renovate_token }}"
74 "RENOVATE_AUTODISCOVER": "true"
75 "LOG_LEVEL": "debug"
76 "RENOVATE_GIT_AUTHOR": "{{ renovate_author }}"
77 restart_policy: "no"
78 state: 'started'
79
80
81- name: create cron job to run renovate container daily
82 cron:
83 name: "run renovate"
84 job: "docker start renovate"
85 user: "{{ docker_username }}"
86 minute: "0"
87 hour: "6"
diff --git a/roles/services/containers/searxng/handlers/main.yml b/roles/services/containers/searxng/handlers/main.yml
new file mode 100644
index 0000000..5463835
--- /dev/null
+++ b/roles/services/containers/searxng/handlers/main.yml
@@ -0,0 +1,4 @@
1- name: restart nginx
2 service:
3 name: nginx
4 state: restarted
diff --git a/roles/services/containers/searxng/tasks/main.yml b/roles/services/containers/searxng/tasks/main.yml
new file mode 100644
index 0000000..fa7609c
--- /dev/null
+++ b/roles/services/containers/searxng/tasks/main.yml
@@ -0,0 +1,170 @@
1- name: set image fact
2 set_fact:
3 image: "searxng/searxng:2023.6.16-71b6ff07"
4
5- name: set other facts
6 vars:
7 array: "{{ image.split('/', 1) }}"
8 set_fact:
9 repo_tag: "{{ array.1 }}"
10 custom_registry: "{{ docker_registry_url + '/' + docker_registry_username }}"
11
12- name: create searxng directory
13 file:
14 path: "{{ docker_home }}/searxng"
15 state: directory
16 owner: "{{ docker_username }}"
17 group: "{{ docker_username }}"
18 mode: '0755'
19
20- name: login to docker registry
21 become: yes
22 become_user: "{{ docker_username }}"
23 environment:
24 XDG_RUNTIME_DIR: "/run/user/{{ docker_uid }}"
25 docker_login:
26 docker_host: "unix://run/user/{{ docker_uid }}/docker.sock"
27 registry_url: "{{ docker_registry_url }}"
28 username: "{{ docker_registry_username }}"
29 password: "{{ docker_registry_password }}"
30
31- name: get searxng image
32 become: yes
33 become_user: "{{ docker_username }}"
34 environment:
35 XDG_RUNTIME_DIR: "/run/user/{{ docker_uid }}"
36 docker_image:
37 source: pull
38 force_source: yes
39 name: "{{ image }}"
40 repository: "{{ custom_registry }}/{{ repo_tag }}"
41 docker_host: "unix://run/user/{{ docker_uid }}/docker.sock"
42 push: yes
43
44- name: create searxng config directory
45 file:
46 path: "{{ docker_home }}/searxng/config"
47 state: directory
48 owner: "{{ docker_username }}"
49 group: "{{ docker_username }}"
50 mode: '0755'
51
52- name: create redis_searxng directory
53 file:
54 path: "{{ docker_home }}/redis_searxng"
55 state: directory
56 owner: "{{ docker_username }}"
57 group: "{{ docker_username }}"
58 mode: '0755'
59
60- name: create redis_searxng data directory
61 file:
62 path: "{{ docker_home }}/redis_searxng/data"
63 state: directory
64 owner: "{{ docker_username }}"
65 group: "{{ docker_username }}"
66 mode: '0755'
67
68- name: place searxng config in proper location
69 copy:
70 src: "{{ searxng_config }}"
71 dest: "{{ docker_home }}/searxng/config/settings.yml"
72 owner: root
73 group: docker
74 mode: '0644'
75
76- name: place uwsgi config
77 copy:
78 src: "{{ searxng_uwsgi_config }}"
79 dest: "{{ docker_home }}/searxng/config/uwsgi.ini"
80 owner: root
81 group: docker
82 mode: '0644'
83
84- name: create searxng docker network
85 docker_network:
86 name: "{{ searxng_network_name }}"
87 docker_host: "unix://run/user/{{ docker_uid }}/docker.sock"
88 driver: bridge
89 ipam_config:
90 - subnet: "{{ searxng_subnet }}"
91 gateway: "{{ searxng_gateway }}"
92
93- name: create and deploy searxng container
94 become: yes
95 become_user: "{{ docker_username }}"
96 environment:
97 XDG_RUNTIME_DIR: "/run/user/{{ docker_uid }}"
98 docker_container:
99 name: "searxng"
100 image: "{{ custom_registry }}/{{ repo_tag }}"
101 pull: yes
102 docker_host: "unix://run/user/{{ docker_uid }}/docker.sock"
103 purge_networks: yes
104 networks:
105 - name: "{{ searxng_network_name }}"
106 ipv4_address: "{{ searxng_ipv4 }}"
107 ports:
108 - "127.0.0.1:8080:8080"
109 volumes:
110 - "{{ docker_home }}/searxng/config:/etc/searxng"
111 env:
112 SEARXNG_BASE_URL: "https://searxng.chudnick.com/"
113 cap_drop:
114 - all
115 capabilities:
116 - CHOWN
117 - SETGID
118 - SETUID
119 - DAC_OVERRIDE
120 hostname: "searxng"
121 restart_policy: unless-stopped
122 state: 'started'
123 recreate: yes
124
125- name: create and deploy redis container
126 become: yes
127 become_user: "{{ docker_username }}"
128 environment:
129 XDG_RUNTIME_DIR: "/run/user/{{ docker_uid }}"
130 docker_container:
131 restart_policy: unless-stopped
132 name: "redis_searxng"
133 image: redis:alpine
134 pull: yes
135 command: redis-server --save "" --appendonly "no"
136 purge_networks: yes
137 networks:
138 - name: "{{ searxng_network_name }}"
139 ipv4_address: "{{ redis_searxng_ipv4 }}"
140 tmpfs:
141 - /var/lib/redis
142 cap_drop:
143 - all
144 capabilities:
145 - SETGID
146 - SETUID
147 - DAC_OVERRIDE
148 hostname: "redis"
149 docker_host: "unix://run/user/{{ docker_uid }}/docker.sock"
150 state: 'started'
151 comparisons:
152 '*': strict
153
154- name: deploy nginx configuration
155 notify: restart nginx
156 register: nginx_config
157 copy:
158 src: "{{ searxng_nginx_config }}"
159 dest: /etc/nginx/sites-available/searxng.conf
160 owner: root
161 group: root
162 mode: '0644'
163
164- name: symlink site
165 file:
166 src: /etc/nginx/sites-available/searxng.conf
167 dest: /etc/nginx/sites-enabled/searxng.conf
168 owner: root
169 group: root
170 state: link
diff --git a/roles/services/containers/text_generation/handlers/main.yml b/roles/services/containers/text_generation/handlers/main.yml
new file mode 100644
index 0000000..7aab823
--- /dev/null
+++ b/roles/services/containers/text_generation/handlers/main.yml
@@ -0,0 +1,29 @@
1- name: login to docker registry
2 become: yes
3 become_user: "{{ docker_username }}"
4 environment:
5 XDG_RUNTIME_DIR: "/run/user/{{ docker_uid }}"
6 docker_login:
7 docker_host: "unix://run/user/{{ docker_uid }}/docker.sock"
8 registry_url: "{{ docker_registry_url }}"
9 username: "{{ docker_registry_username }}"
10 password: "{{ docker_registry_password }}"
11
12- name: build text-generation image
13 become: yes
14 become_user: "{{ docker_username }}"
15 environment:
16 XDG_RUNTIME_DIR: "/run/user/{{ docker_uid }}"
17 docker_image:
18 name: "{{ docker_registry_url }}/{{ docker_registry_username }}/text-generation:latest"
19 docker_host: "unix://run/user/{{ docker_uid }}/docker.sock"
20 build:
21 path: /srv/docker/text-generation/src
22 source: build
23 push: yes
24 force_source: yes
25
26- name: restart nginx
27 service:
28 name: nginx
29 state: restarted
diff --git a/roles/services/containers/text_generation/tasks/main.yml b/roles/services/containers/text_generation/tasks/main.yml
new file mode 100644
index 0000000..80988a6
--- /dev/null
+++ b/roles/services/containers/text_generation/tasks/main.yml
@@ -0,0 +1,89 @@
1- name: set image fact
2 set_fact:
3 image: gitea.chudnick.com/sam/text-generation:latest
4
5- name: set other facts
6 vars:
7 array: "{{ image.split('/', 1) }}"
8 set_fact:
9 repo_tag: "{{ array.1 }}"
10 custom_registry: "{{ docker_registry_url + '/' + docker_registry_username }}"
11
12- name: create text-generation directory
13 file:
14 path: "{{ docker_home }}/text-generation"
15 state: directory
16 owner: "{{ docker_username }}"
17 group: "{{ docker_username }}"
18 mode: '0755'
19
20- name: create models directory
21 file:
22 path: "{{ docker_home }}/text-generation/models"
23 state: directory
24 owner: "{{ docker_username }}"
25 group: "{{ docker_username }}"
26 mode: '0755'
27
28- name: clone text-generation repository
29 notify:
30 - login to docker registry
31 - build text-generation image
32 git:
33 repo: https://gitea.chudnick.com/sam/text-generation-docker
34 dest: "{{ docker_home }}/text-generation/src"
35
36- meta: flush_handlers
37
38- name: create text-generation network
39 become: yes
40 become_user: "{{ docker_username }}"
41 docker_network:
42 name: "{{ text_generation_network_name }}"
43 docker_host: "unix://run/user/{{ docker_uid }}/docker.sock"
44 driver: bridge
45 ipam_config:
46 - subnet: "{{ text_generation_subnet }}"
47 gateway: "{{ text_generation_gateway }}"
48
49- name: create and deploy text-generation container
50 become: yes
51 become_user: "{{ docker_username }}"
52 environment:
53 XDG_RUNTIME_DIR: "/run/user/{{ docker_uid }}"
54 docker_container:
55 name: "text-generation"
56 hostname: "text-generation"
57 image: "{{ image }}"
58 docker_host: "unix://run/user/{{ docker_uid }}/docker.sock"
59 purge_networks: yes
60 networks:
61 - name: "{{ text_generation_network_name }}"
62 ipv4_address: "{{ text_generation_ipv4 }}"
63 volumes:
64 - "{{ docker_home }}/text-generation/models:/models"
65 ports:
66 - "127.0.0.1:{{ text_generation_external_port }}:7860"
67 - "127.0.0.1:{{ text_generation_api_port }}:5005"
68 - "127.0.0.1:{{ text_generation_api_stream_port }}:5000"
69 command: "--cpu --listen --listen-port 7860 --chat --auto-devices --mlock"
70 state: 'started'
71 recreate: yes
72 restart_policy: unless-stopped
73
74- name: deploy nginx configuration
75 notify: restart nginx
76 template:
77 src: "{{ text_generation_nginx_config }}"
78 dest: /etc/nginx/sites-available/text-generation.conf
79 owner: root
80 group: root
81 mode: '0644'
82
83- name: symlink site
84 file:
85 src: /etc/nginx/sites-available/text-generation.conf
86 dest: /etc/nginx/sites-enabled/text-generation.conf
87 owner: root
88 group: root
89 state: link
diff --git a/roles/services/containers/vaultwarden/handlers/main.yml b/roles/services/containers/vaultwarden/handlers/main.yml
new file mode 100644
index 0000000..5463835
--- /dev/null
+++ b/roles/services/containers/vaultwarden/handlers/main.yml
@@ -0,0 +1,4 @@
1- name: restart nginx
2 service:
3 name: nginx
4 state: restarted
diff --git a/roles/services/containers/vaultwarden/tasks/main.yml b/roles/services/containers/vaultwarden/tasks/main.yml
new file mode 100644
index 0000000..fa63b58
--- /dev/null
+++ b/roles/services/containers/vaultwarden/tasks/main.yml
@@ -0,0 +1,79 @@
1- name: set image fact
2 set_fact:
3 image: vaultwarden/server:1.28.1
4
5- name: set other facts
6 vars:
7 array: "{{ image.split('/', 1) }}"
8 set_fact:
9 repo_tag: "{{ array.1 }}"
10 custom_registry: "{{ docker_registry_url + '/' + docker_registry_username }}"
11
12- name: create vaultwarden directory
13 file:
14 path: "{{ docker_home }}/vaultwarden"
15 state: directory
16 owner: "{{ docker_username }}"
17 group: "{{ docker_username }}"
18 mode: '0755'
19
20- name: create data directory
21 file:
22 path: "{{ docker_home }}/vaultwarden/data"
23 state: directory
24 owner: "{{ docker_username }}"
25 group: "{{ docker_username }}"
26 mode: '0755'
27
28- name: create vaultwarden docker network
29 become: yes
30 become_user: "{{ docker_username }}"
31 docker_network:
32 name: "{{ vaultwarden_network_name }}"
33 docker_host: "unix://run/user/{{ docker_uid }}/docker.sock"
34 driver: bridge
35 ipam_config:
36 - subnet: "{{ vaultwarden_subnet }}"
37 gateway: "{{ vaultwarden_gateway }}"
38
39- name: create and deploy vaultwarden container
40 become: yes
41 become_user: "{{ docker_username }}"
42 environment:
43 XDG_RUNTIME_DIR: "/run/user/{{ docker_uid }}"
44 docker_container:
45 name: "vaultwarden"
46 hostname: "vaultwarden"
47 image: "{{ image }}"
48 docker_host: "unix://run/user/{{ docker_uid }}/docker.sock"
49 purge_networks: yes
50 networks:
51 - name: "{{ vaultwarden_network_name }}"
52 ipv4_address: "{{ vaultwarden_ipv4 }}"
53 ports:
54 - "127.0.0.1:{{ vaultwarden_external_port }}:80"
55 volumes:
56 - "{{ docker_home }}/vaultwarden/data:/data"
57 env:
58 "DOMAIN": "https://{{ vaultwarden_server_name }}"
59 "DISABLE_ADMIN_TOKEN": "true"
60 state: 'started'
61 recreate: yes
62 restart_policy: unless-stopped
63
64- name: deploy nginx configuration
65 notify: restart nginx
66 template:
67 src: "{{ vaultwarden_nginx_config }}"
68 dest: /etc/nginx/sites-available/vaultwarden.conf
69 owner: root
70 group: root
71 mode: '0644'
72
73- name: symlink site
74 file:
75 src: /etc/nginx/sites-available/vaultwarden.conf
76 dest: /etc/nginx/sites-enabled/vaultwarden.conf
77 owner: root
78 group: root
79 state: link
diff --git a/roles/services/docker_rootless/defaults/main.yml b/roles/services/docker_rootless/defaults/main.yml
new file mode 100644
index 0000000..064825f
--- /dev/null
+++ b/roles/services/docker_rootless/defaults/main.yml
@@ -0,0 +1,18 @@
1docker_packages:
2 - docker-ce
3 - acl
4 - docker-ce-cli
5 - docker-ce-rootless-extras
6 - docker-compose-plugin
7 - uidmap
8 - dbus-user-session
9 - slirp4netns
10 - fuse-overlayfs
11
12docker_username: docker_rootless
13docker_uid: 2000
14
15docker_home: /srv/docker
16docker_config: /srv/docker/config
17docker_data: /srv/docker/data
18
diff --git a/roles/services/docker_rootless/handlers/main.yml b/roles/services/docker_rootless/handlers/main.yml
new file mode 100644
index 0000000..510db7b
--- /dev/null
+++ b/roles/services/docker_rootless/handlers/main.yml
@@ -0,0 +1,6 @@
1- name: update repos
2 apt:
3 update_cache: yes
4 register: apt_upgrade
5 retries: 100
6 until: apt_upgrade is success or ('Failed to lock apt for exclusive operation' not in apt_upgrade.msg and '/var/lib/dpkg/lock' not in apt_upgrade.msg)
diff --git a/roles/services/docker_rootless/tasks/main.yml b/roles/services/docker_rootless/tasks/main.yml
new file mode 100644
index 0000000..9b2e527
--- /dev/null
+++ b/roles/services/docker_rootless/tasks/main.yml
@@ -0,0 +1,93 @@
1- name: install packages
2 package:
3 name:
4 - extrepo
5 - nginx
6 - python3-docker
7 state: latest
8
9- name: allow http (80/tcp) traffic
10 ufw:
11 rule: allow
12 port: '80'
13 proto: tcp
14
15- name: allow https (443/tcp) traffic
16 ufw:
17 rule: allow
18 port: '443'
19 proto: tcp
20
21- name: enable docker-ce repo
22 register: result
23 changed_when: result.stdout | regex_search("skipped") | bool
24 notify: update repos
25 command:
26 cmd: extrepo enable docker-ce
27 creates: /etc/apt/sources.list.d/extrepo_docker-ce.sources
28
29- meta: flush_handlers
30
31- name: enable docker-ce repo
32 changed_when: false
33 command:
34 cmd: extrepo update docker-ce
35
36- name: create docker user
37 user:
38 name: "{{ docker_username }}"
39 shell: /bin/bash
40 uid: "{{ docker_uid }}"
41 home: "{{ docker_home }}"
42 create_home: yes
43
44- name: add XDG_RUNTIME_DIR to docker user bash profile
45 lineinfile:
46 path: "{{ docker_home }}/.bash_profile"
47 line: "export XDG_RUNTIME_DIR=/run/user/{{ docker_uid }}"
48 insertbefore: EOF
49 owner: "{{ docker_username }}"
50 group: "{{ docker_username }}"
51 mode: "0644"
52 create: yes
53
54- name: install docker packages
55 package:
56 name: "{{ docker_packages }}"
57 state: latest
58
59- name: add docker user to /etc/subuid
60 lineinfile:
61 path: /etc/subuid
62 line: "{{ docker_username }}:100000:65536"
63 insertbefore: EOF
64
65- name: add docker user to /etc/subgid
66 lineinfile:
67 path: /etc/subgid
68 line: "{{ docker_username }}:100000:65536"
69 insertbefore: EOF
70
71- name: enable lingering for docker user
72 command:
73 cmd: loginctl enable-linger "{{ docker_username }}"
74 creates: "/var/lib/systemd/linger/{{ docker_username }}"
75
76- name: run docker rootless setup script
77 become_user: "{{ docker_username }}"
78 register: setup_script
79 command:
80 cmd: /usr/bin/dockerd-rootless-setuptool.sh install --force
81 creates: "{{ docker_home }}/.config/systemd/user/docker.service"
82 environment:
83 XDG_RUNTIME_DIR: "/run/user/{{ docker_uid }}"
84
85- name: enable and start docker service
86 become_user: "{{ docker_username }}"
87 systemd:
88 name: docker
89 enabled: yes
90 state: started
91 scope: user
92 environment:
93 XDG_RUNTIME_DIR: "/run/user/{{ docker_uid }}"
diff --git a/roles/services/freeipa/client/defaults/main.yml b/roles/services/freeipa/client/defaults/main.yml
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/roles/services/freeipa/client/defaults/main.yml
diff --git a/roles/services/freeipa/client/tasks/main.yml b/roles/services/freeipa/client/tasks/main.yml
new file mode 100644
index 0000000..ccb047e
--- /dev/null
+++ b/roles/services/freeipa/client/tasks/main.yml
@@ -0,0 +1,4 @@
1---
2- name: configure freeipa client
3 include_role:
4 name: freeipa.ansible_freeipa.ipaclient
diff --git a/roles/services/freeipa/server/defaults/main.yml b/roles/services/freeipa/server/defaults/main.yml
new file mode 100644
index 0000000..3e91a21
--- /dev/null
+++ b/roles/services/freeipa/server/defaults/main.yml
@@ -0,0 +1 @@
ipabackup_from_controller: yes
diff --git a/roles/services/freeipa/server/tasks/main.yml b/roles/services/freeipa/server/tasks/main.yml
new file mode 100644
index 0000000..32badc2
--- /dev/null
+++ b/roles/services/freeipa/server/tasks/main.yml
@@ -0,0 +1,43 @@
1---
2- name: set fedora dns
3 lineinfile:
4 path: /etc/systemd/resolved.conf
5 regexp: "^#?DNS="
6 line: "DNS={{ ipa_dns_ip }}"
7
8- name: restart systemd-resolved
9 service:
10 name: systemd-resolved
11 state: restarted
12
13- name: set hostname
14 hostname:
15 name: ipasrv.home.local
16
17- name: remove lines from /etc/hosts
18 lineinfile:
19 path: /etc/hosts
20 regexp: "^::1.*ipasrv"
21 state: absent
22
23- name: remove lines from /etc/hosts
24 lineinfile:
25 path: /etc/hosts
26 regexp: "^127.0.0.1.*ipasrv"
27 state: absent
28
29- name: add line to /etc/hosts
30 lineinfile:
31 path: /etc/hosts
32 line: "{{ ansible_default_ipv4.address }} ipasrv.home.local ipasrv"
33 state: present
34
35- name: install freeipa-server
36 package:
37 name: freeipa-server
38 state: latest
39
40#- name: restore ipaserver from backup
41 #include_role:
42 #name: freeipa.ansible_freeipa.ipabackup
43 #state: restored
diff --git a/roles/services/game_server/handlers/main.yml b/roles/services/game_server/handlers/main.yml
new file mode 100644
index 0000000..8e221e1
--- /dev/null
+++ b/roles/services/game_server/handlers/main.yml
@@ -0,0 +1,71 @@
1- name: create sunshine build dir
2 become: yes
3 become_user: "{{ games_user }}"
4 file:
5 path: "/home/{{ games_user }}/sunshine/build"
6 state: directory
7 owner: "{{ games_user }}"
8 group: "{{ games_user }}"
9 mode: "0755"
10
11- name: run npm install
12 become: yes
13 become_user: "{{ games_user }}"
14 command:
15 cmd: "npm install"
16 chdir: "/home/{{ games_user }}/sunshine/build"
17
18- name: build sunshine - cmake
19 become: yes
20 become_user: "{{ games_user }}"
21 command:
22 cmd: "cmake -DCMAKE_C_COMPILER=gcc-10 -DCMAKE_CXX_COMPILER=g++-10 .."
23 chdir: "/home/{{ games_user }}/sunshine/build"
24
25- name: build sunshine - make
26 become: yes
27 become_user: "{{ games_user }}"
28 command:
29 cmd: "make"
30 chdir: "/home/{{ games_user }}/sunshine/build"
31
32- name: build sunshine deb package
33 become: yes
34 become_user: "{{ games_user }}"
35 command:
36 cmd: "cpack -G DEB"
37 chdir: "/home/{{ games_user }}/sunshine/build"
38
39- name: install sunshine from deb
40 apt:
41 deb: "/home/{{ games_user }}/sunshine/build/cpack_artifacts/Sunshine.deb"
42
43- name: restart sunshine
44 become: yes
45 become_user: "{{ games_user }}"
46 systemd:
47 scope: user
48 name: sunshine
49 state: restarted
50
51- name: decompress and extract firmware
52 unarchive:
53 src: "/tmp/linux-firmware-20221109.tar.gz"
54 dest: "/tmp/"
55 remote_src: yes
56
57- name: copy all files from amdgpu to /lib/firmware/amdgpu/
58 copy:
59 src: /tmp/linux-firmware-20221109/amdgpu
60 dest: /lib/firmware
61 remote_src: yes
62 owner: root
63 group: root
64 mode: "0644"
65
66- name: update initramfs
67 command:
68 cmd: "update-initramfs -u"
69
70- name: reboot system
71 reboot:
diff --git a/roles/services/game_server/tasks/main.yml b/roles/services/game_server/tasks/main.yml
new file mode 100644
index 0000000..f2b12bd
--- /dev/null
+++ b/roles/services/game_server/tasks/main.yml
@@ -0,0 +1,223 @@
1- name: enable contrib and non-free repos
2 apt_repository:
3 repo: deb https://deb.debian.org/debian bookworm main contrib non-free
4
5- name: enable contrib and non-free repos
6 apt_repository:
7 repo: deb https://security.debian.org/debian-security bookworm-security main contrib non-free
8
9- name: enable contrib and non-free repos
10 apt_repository:
11 repo: deb https://deb.debian.org/debian bookworm-updates main contrib non-free
12
13- name: enable contrib and non-free repos
14 apt_repository:
15 repo: deb https://deb.debian.org/debian bookworm-backports main contrib non-free
16
17- name: enable contrib and non-free repos
18 apt_repository:
19 repo: deb-src https://deb.debian.org/debian bookworm main contrib non-free
20
21- name: enable contrib and non-free repos
22 apt_repository:
23 repo: deb-src https://security.debian.org/debian-security bookworm-security main contrib non-free
24- name: enable contrib and non-free repos
25 apt_repository:
26 repo: deb-src https://deb.debian.org/debian bookworm-updates main contrib non-free
27
28- name: enable contrib and non-free repos
29 apt_repository:
30 repo: deb-src https://deb.debian.org/debian bookworm-backports main contrib non-free
31
32- name: update repos
33 apt:
34 update_cache: yes
35 register: apt_upgrade
36 retries: 100
37 until: apt_upgrade is success or ('Failed to lock apt for exclusive operation' not in apt_upgrade.msg and '/var/lib/dpkg/lock' not in apt_upgrade.msg)
38
39- name: install packages
40 package:
41 name: "{{ game_server_packages }}"
42 state: latest
43
44- name: create games user
45 user:
46 name: "{{ games_user }}"
47 create_home: yes
48
49- name: add user to sudo group
50 user:
51 name: "{{ games_user }}"
52 groups: sudo
53 append: yes
54
55- name: add user to ssl-cert group
56 user:
57 name: "{{ games_user }}"
58 groups: ssl-cert
59 append: yes
60
61- name: set authorized ssh key
62 authorized_key:
63 user: "{{ games_user }}"
64 state: present
65 key: "{{ lookup('file', 'data/common/id_rsa.pub') }}"
66
67- name: clone sunshine repo
68 become: yes
69 become_user: "{{ games_user }}"
70 git:
71 repo: "{{ sunshine_repo }}"
72 dest: "/home/{{ games_user }}/sunshine"
73 version: "{{ sunshine_version }}"
74 recursive: yes
75 force: yes
76 register: sunshine_repo
77 notify:
78 - create sunshine build dir
79 - run npm install
80 - build sunshine - cmake
81 - build sunshine - make
82 - build sunshine deb package
83 - install sunshine from deb
84 - restart sunshine
85
86- name: install sunshine packages
87 package:
88 name: "{{ sunshine_packages }}"
89 state: latest
90
91- meta: flush_handlers
92
93- name: add user to input group
94 user:
95 name: "{{ games_user }}"
96 groups: input
97 append: yes
98
99- name: set sunshine udev rules
100 lineinfile:
101 path: /etc/udev/rules.d/85-sunshine-input.rules
102 insertbefore: EOF
103 line: KERNEL=="uinput", GROUP="input", MODE="0660", OPTIONS+="static_node=uinput"
104 owner: root
105 group: root
106 mode: "0644"
107 create: yes
108
109- name: install backports kernel
110 apt:
111 name: linux-image-amd64
112 state: latest
113 update_cache: yes
114
115- name: update-pciids
116 changed_when: false
117 command:
118 cmd: "update-pciids"
119
120- name: check if needed firmware has alredy been installed
121 stat: path=/lib/firmware/amdgpu/dimgrey_cavefish_sos.bin
122 register: bin
123
124- name: manually download latest firmware for amdgpu from kernel source tree
125 when: not bin.stat.exists
126 get_url:
127 url: "https://git.kernel.org/pub/scm/linux/kernel/git/firmware/linux-firmware.git/snapshot/linux-firmware-20221109.tar.gz"
128 dest: "/tmp/linux-firmware-20221109.tar.gz"
129 register: firmware
130 notify:
131 - decompress and extract firmware
132 - copy all files from amdgpu to /lib/firmware/amdgpu/
133 - update initramfs
134 - reboot system
135
136- name: allow sunshine ports
137 ufw:
138 rule: allow
139 proto: tcp
140 port: 47984
141
142- name: allow sunshine ports
143 ufw:
144 rule: allow
145 proto: tcp
146 port: 47989
147
148- name: allow sunshine ports
149 ufw:
150 rule: allow
151 proto: tcp
152 port: 47990
153
154- name: allow sunshine ports
155 ufw:
156 rule: allow
157 proto: udp
158 port: 47998
159
160- name: allow sunshine ports
161 ufw:
162 rule: allow
163 proto: udp
164 port: 47999
165
166- name: allow sunshine ports
167 ufw:
168 rule: allow
169 proto: tcp
170
171- name: allow sunshine ports
172 ufw:
173 rule: allow
174 proto: udp
175 port: 48000
176
177- name: allow sunshine ports
178 ufw:
179 rule: allow
180 proto: udp
181 port: 48002
182
183- name: check if i386 architecture is already enabled
184 args:
185 executable: /bin/bash
186 shell: |
187 set -eo pipefail
188 dpkg --print-foreign-architectures | grep i386
189 register: i386_check
190 changed_when: false
191
192- name: add i386 architecture
193 when: i386_check.rc == 1
194 command:
195 cmd: "dpkg --add-architecture i386"
196
197- name: update repos
198 when: i386_check.rc == 1
199 apt:
200 update_cache: yes
201 register: apt_upgrade
202 retries: 100
203 until: apt_upgrade is success or ('Failed to lock apt for exclusive operation' not in apt_upgrade.msg and '/var/lib/dpkg/lock' not in apt_upgrade.msg)
204
205- name: install steam and related packages
206 package:
207 name: "{{ steam_packages }}"
208
209- name: copy lightdm config
210 copy:
211 src: "{{ lightdm_config }}"
212 dest: /etc/lightdm/lightdm.conf
213 owner: root
214 group: root
215 mode: "0644"
216
217- name: copy xfce xinit config
218 copy:
219 src: "{{ xfce_xinit }}"
220 dest: /etc/xdg/xfce4/xinitrc
221 owner: root
222 group: root
223 mode: "0755"
diff --git a/roles/services/jenkins/handlers/main.yml b/roles/services/jenkins/handlers/main.yml
new file mode 100644
index 0000000..92f0084
--- /dev/null
+++ b/roles/services/jenkins/handlers/main.yml
@@ -0,0 +1,13 @@
1- name: update repos
2 apt:
3 update_cache: yes
4
5- name: restart nginx
6 service:
7 name: nginx
8 state: restarted
9
10- name: restart jenkins
11 service:
12 name: jenkins
13 state: restarted
diff --git a/roles/services/jenkins/tasks/main.yml b/roles/services/jenkins/tasks/main.yml
new file mode 100644
index 0000000..29dbb28
--- /dev/null
+++ b/roles/services/jenkins/tasks/main.yml
@@ -0,0 +1,184 @@
1- name: install extrepo
2 package:
3 name: extrepo
4 state: latest
5
6- name: add jenkins repo
7 register: result
8 changed_when: result.stdout | regex_search("skipped") | bool
9 notify: update repos
10 command:
11 cmd: extrepo enable jenkins
12 creates: /etc/apt/sources.list.d/extrepo_jenkins.sources
13
14- meta: flush_handlers
15
16- name: update jenkins repo data
17 changed_when: false
18 command:
19 cmd: extrepo update jenkins
20
21- name: install packages
22 package:
23 name: "{{ jenkins_packages }}"
24
25- name: generate ssh key for jenkins user
26 user:
27 name: jenkins
28 generate_ssh_key: yes
29
30- name: get jenkins user ssh key
31 changed_when: false
32 command: cat /var/lib/jenkins/.ssh/id_rsa.pub
33 register: pubkey
34
35- name: create jenkins user in freeipa
36 freeipa.ansible_freeipa.ipauser:
37 ipaadmin_principal:
38 ipaadmin_password: "{{ ipafulladmin_password }}"
39 name: jenkins
40 passwordexpiration: "2050-01-01"
41 first: jenkins
42 last: ci
43 sshpubkey: "{{ pubkey.stdout }}"
44
45- name: create jenkins_admin group in freeipa
46 freeipa.ansible_freeipa.ipagroup:
47 ipaadmin_password: "{{ ipafulladmin_password }}"
48 name: jenkins_admin
49
50- name: add user jenkins to jenkins_admin group in freeipa
51 freeipa.ansible_freeipa.ipagroup:
52 ipaadmin_password: "{{ ipafulladmin_password }}"
53 name: jenkins_admin
54 action: member
55 user:
56 - jenkins
57
58- name: create sudo rule to allow jenkins to execute on all without password
59 freeipa.ansible_freeipa.ipasudorule:
60 ipaadmin_password: "{{ ipafulladmin_password }}"
61 name: jenkins_rule
62 sudooption: "!authenticate"
63 group: jenkins_admin
64 hostcategory: all
65 cmdcategory: all
66 runasusercategory: all
67 runasgroupcategory: all
68
69- name: deploy nginx configuration
70 copy:
71 src: "{{ jenkins_nginx_config }}"
72 dest: /etc/nginx/sites-available/jenkins.conf
73 owner: root
74 group: root
75 mode: '0644'
76 register: nginx_config
77 notify: restart nginx
78
79- name: create cert/key dir
80 file:
81 state: directory
82 path: "/etc/letsencrypt/live/{{ services_domain }}"
83 owner: root
84 group: root
85 mode: "0755"
86
87- name: remove existing private key file
88 file:
89 path: "/etc/letsencrypt/live/{{ services_domain }}/privkey.pem"
90 state: absent
91
92- name: write private key to file
93 lineinfile:
94 path: "/etc/letsencrypt/live/{{ services_domain }}/privkey.pem"
95 line: "{{ nginx_key }}"
96 insertbefore: EOF
97 create: yes
98
99- name: deploy cert
100 copy:
101 src: "{{ nginx_cert }}"
102 dest: "/etc/letsencrypt/live/{{ services_domain }}/fullchain.pem"
103 owner: root
104 group: root
105 mode: '0644'
106
107- name: symlink site
108 file:
109 src: /etc/nginx/sites-available/jenkins.conf
110 dest: /etc/nginx/sites-enabled/jenkins.conf
111 owner: root
112 group: root
113 state: link
114
115- name: allow http (80/tcp) traffic
116 ufw:
117 rule: allow
118 port: '80'
119 proto: tcp
120
121- name: allow https (443/tcp) traffic
122 ufw:
123 rule: allow
124 port: '443'
125 proto: tcp
126
127- name: install ansible plugin
128 jenkins_plugin:
129 url_username: "{{ jenkins_username }}"
130 url_password: "{{ jenkins_apikey }}"
131 url: "{{ jenkins_url }}"
132 name: ansible
133
134- name: install gitea plugin
135 jenkins_plugin:
136 url_username: "{{ jenkins_username }}"
137 url_password: "{{ jenkins_apikey }}"
138 url: "{{ jenkins_url }}"
139 name: gitea
140
141- name: install openid login plugin
142 jenkins_plugin:
143 url_username: "{{ jenkins_username }}"
144 url_password: "{{ jenkins_apikey }}"
145 url: "{{ jenkins_url }}"
146 name: oic-auth
147
148- name: install prometheus plugin
149 jenkins_plugin:
150 url_username: "{{ jenkins_username }}"
151 url_password: "{{ jenkins_apikey }}"
152 url: "{{ jenkins_url }}"
153 name: prometheus
154
155- name: install casc plugin
156 jenkins_plugin:
157 url_username: "{{ jenkins_username }}"
158 url_password: "{{ jenkins_apikey }}"
159 url: "{{ jenkins_url }}"
160 name: configuration-as-code
161
162- name: install warnings-ng plugin
163 jenkins_plugin:
164 url_username: "{{ jenkins_username }}"
165 url_password: "{{ jenkins_apikey }}"
166 url: "{{ jenkins_url }}"
167 name: warnings-ng
168
169- name: deploy configuration as code file
170 register: casc_file
171 notify: restart jenkins
172 template:
173 src: "{{ jenkins_config }}"
174 dest: "/var/lib/jenkins/jenkins.yaml"
175 owner: jenkins
176 group: jenkins
177 mode: "0644"
178
179- name: enable jenkins
180 systemd:
181 daemon_reload: yes
182 enabled: yes
183 masked: no
184 name: jenkins
diff --git a/roles/services/monitoring/grafana/defaults/main.yml b/roles/services/monitoring/grafana/defaults/main.yml
new file mode 100644
index 0000000..c346e54
--- /dev/null
+++ b/roles/services/monitoring/grafana/defaults/main.yml
@@ -0,0 +1,5 @@
1grafana_package:
2 - grafana
3 - nginx
4grafana_config: files/grafana_config/
5grafana_data: files/grafana.db
diff --git a/roles/services/monitoring/grafana/handlers/main.yml b/roles/services/monitoring/grafana/handlers/main.yml
new file mode 100644
index 0000000..8026c6d
--- /dev/null
+++ b/roles/services/monitoring/grafana/handlers/main.yml
@@ -0,0 +1,13 @@
1- name: update repos
2 apt:
3 update_cache: yes
4
5- name: restart grafana
6 service:
7 name: grafana-server
8 state: restarted
9
10- name: restart nginx
11 service:
12 name: nginx
13 state: restarted
diff --git a/roles/services/monitoring/grafana/tasks/main.yml b/roles/services/monitoring/grafana/tasks/main.yml
new file mode 100644
index 0000000..e9f824e
--- /dev/null
+++ b/roles/services/monitoring/grafana/tasks/main.yml
@@ -0,0 +1,125 @@
1- name: install extrepo
2 package:
3 name: extrepo
4 state: latest
5
6- name: add Grafana repo
7 register: result
8 changed_when: result.stdout | regex_search("skipped") | bool
9 notify: update repos
10 command:
11 cmd: extrepo enable grafana
12 creates: /etc/apt/sources.list.d/extrepo_grafana.sources
13
14- meta: flush_handlers
15
16- name: update Grafana repo
17 changed_when: false
18 command:
19 cmd: extrepo update grafana
20
21- name: install grafana
22 package:
23 name: "{{ grafana_package }}"
24
25- name: deploy grafana config
26 notify: restart grafana
27 template:
28 src: "{{ grafana_config }}"
29 dest: /etc/grafana/grafana.ini
30 owner: root
31 group: grafana
32 mode: '0640'
33
34- name: deploy nginx configuration
35 notify: restart nginx
36 copy:
37 src: "{{ grafana_nginx_config }}"
38 dest: /etc/nginx/sites-available/grafana.conf
39 owner: root
40 group: root
41 mode: '0644'
42
43- name: symlink site
44 notify: restart nginx
45 file:
46 src: /etc/nginx/sites-available/grafana.conf
47 dest: /etc/nginx/sites-enabled/grafana.conf
48 owner: root
49 group: root
50 state: link
51
52- name: allow http (80/tcp) traffic
53 ufw:
54 rule: allow
55 port: '80'
56 proto: tcp
57
58- name: allow https (443/tcp) traffic
59 ufw:
60 rule: allow
61 port: '443'
62 proto: tcp
63
64- name: enable grafana
65 systemd:
66 daemon_reload: yes
67 enabled: yes
68 masked: no
69 name: grafana-server
70
71- meta: flush_handlers
72
73- name: add grafana user
74 ignore_errors: yes
75 community.grafana.grafana_user:
76 name: "{{ grafana_admin }}"
77 email: "{{ grafana_email }}"
78 url: "{{ grafana_url }}"
79 login: "{{ grafana_admin }}"
80 password: "{{ grafana_password }}"
81 is_admin: true
82 state: present
83
84- name: add prometheus datasource
85 community.grafana.grafana_datasource:
86 grafana_url: "{{ grafana_url }}"
87 grafana_user: "{{ grafana_admin }}"
88 grafana_password: "{{ grafana_password }}"
89 name: "Prometheus"
90 ds_type: prometheus
91 ds_url: "{{ prometheus_url }}"
92 access: proxy
93
94- name: add influxdb datasource
95 community.grafana.grafana_datasource:
96 grafana_url: "{{ grafana_url }}"
97 grafana_user: "{{ grafana_admin }}"
98 grafana_password: "{{ grafana_password }}"
99 name: "Proxmox InfluxDB"
100 ds_type: influxdb
101 ds_url: "{{ influxdb_url }}"
102 database: "{{ influx_database }}"
103 user: "{{ influx_user }}"
104 password: "{{ influx_password }}"
105 access: proxy
106
107- name: add loki datasource
108 community.grafana.grafana_datasource:
109 grafana_url: "{{ grafana_url }}"
110 grafana_user: "{{ grafana_admin }}"
111 grafana_password: "{{ grafana_password }}"
112 name: "Loki"
113 ds_type: loki
114 ds_url: "{{ loki_url }}"
115 access: proxy
116
117- name: import main custom dashboard
118 delegate_to: localhost
119 become: no
120 community.grafana.grafana_dashboard:
121 grafana_url: "{{ grafana_url }}"
122 grafana_user: "{{ grafana_admin }}"
123 grafana_password: "{{ grafana_password }}"
124 path: "{{ grafana_dashboard_main }}"
125 overwrite: yes
diff --git a/roles/services/monitoring/influxdb/defaults/main.yml b/roles/services/monitoring/influxdb/defaults/main.yml
new file mode 100644
index 0000000..180ad8e
--- /dev/null
+++ b/roles/services/monitoring/influxdb/defaults/main.yml
@@ -0,0 +1,6 @@
1influxdb_packages:
2 - influxdb
3 - influxdb-client
4
5influx_config: files/influxdb.conf
6influx_data: files/influx_data/
diff --git a/roles/services/monitoring/influxdb/handlers/main.yml b/roles/services/monitoring/influxdb/handlers/main.yml
new file mode 100644
index 0000000..765a040
--- /dev/null
+++ b/roles/services/monitoring/influxdb/handlers/main.yml
@@ -0,0 +1,4 @@
1- name: restart influxdb
2 service:
3 name: influxdb
4 state: restarted
diff --git a/roles/services/monitoring/influxdb/tasks/main.yml b/roles/services/monitoring/influxdb/tasks/main.yml
new file mode 100644
index 0000000..06d6e86
--- /dev/null
+++ b/roles/services/monitoring/influxdb/tasks/main.yml
@@ -0,0 +1,19 @@
1- name: install packages
2 package:
3 name: "{{ influxdb_packages }}"
4 state: latest
5
6- name: copy config
7 notify: restart influxdb
8 copy:
9 src: "{{ influx_config }}"
10 dest: /etc/influxdb/influxdb.conf
11 owner: root
12 group: root
13 mode: '0644'
14
15- name: enable influxdb
16 systemd:
17 name: influxdb
18 enabled: yes
19 masked: no
diff --git a/roles/services/monitoring/loki/handlers/main.yml b/roles/services/monitoring/loki/handlers/main.yml
new file mode 100644
index 0000000..e70412f
--- /dev/null
+++ b/roles/services/monitoring/loki/handlers/main.yml
@@ -0,0 +1,8 @@
1- name: update repos
2 apt:
3 update_cache: yes
4
5- name: restart nginx
6 service:
7 name: nginx
8 state: restarted
diff --git a/roles/services/monitoring/loki/tasks/main.yml b/roles/services/monitoring/loki/tasks/main.yml
new file mode 100644
index 0000000..31a7375
--- /dev/null
+++ b/roles/services/monitoring/loki/tasks/main.yml
@@ -0,0 +1,80 @@
1- name: install extrepo
2 package:
3 name: extrepo
4 state: latest
5
6- name: add Grafana repo
7 register: result
8 changed_when: result.stdout | regex_search("skipped") | bool
9 notify: update repos
10 command:
11 cmd: extrepo enable grafana
12 creates: /etc/apt/sources.list.d/extrepo_grafana.sources
13
14- meta: flush_handlers
15
16- name: add Grafana repo
17 changed_when: false
18 command:
19 cmd: extrepo update grafana
20
21- name: install loki
22 package:
23 name: loki
24 state: latest
25
26- name: deploy loki configuration
27 copy:
28 src: "{{ loki_config }}"
29 dest: /etc/loki/config.yml
30 owner: root
31 group: root
32 mode: '0644'
33
34- name: deploy nginx configuration
35 copy:
36 src: "{{ loki_nginx_config }}"
37 dest: /etc/nginx/sites-available/loki.conf
38 owner: root
39 group: root
40 mode: '0644'
41 register: nginxconfig
42 notify: restart nginx
43
44- name: symlink site
45 file:
46 src: /etc/nginx/sites-available/loki.conf
47 dest: /etc/nginx/sites-enabled/loki.conf
48 owner: root
49 group: root
50 state: link
51
52- name: allow http (80/tcp) traffic
53 ufw:
54 rule: allow
55 port: '80'
56 proto: tcp
57
58- name: allow https (443/tcp) traffic
59 ufw:
60 rule: allow
61 port: '443'
62 proto: tcp
63
64- name: allow loki log (3100/tcp) traffic
65 ufw:
66 rule: allow
67 port: '3100'
68 proto: tcp
69
70- name: enable loki
71 systemd:
72 daemon_reload: yes
73 enabled: yes
74 masked: no
75 name: loki
76
77- name: restart loki
78 systemd:
79 name: loki
80 state: restarted
diff --git a/roles/services/monitoring/prometheus/blackbox-exporter/tasks/main.yml b/roles/services/monitoring/prometheus/blackbox-exporter/tasks/main.yml
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/roles/services/monitoring/prometheus/blackbox-exporter/tasks/main.yml
diff --git a/roles/services/monitoring/prometheus/nginx_exporter/defaults/main.yml b/roles/services/monitoring/prometheus/nginx_exporter/defaults/main.yml
new file mode 100644
index 0000000..9d2b8a5
--- /dev/null
+++ b/roles/services/monitoring/prometheus/nginx_exporter/defaults/main.yml
@@ -0,0 +1,4 @@
1nginx_exporter_debian_package: prometheus-nginx-exporter
2nginx_exporter_fedora_package: golang-github-prometheus-node-exporter
3prometheus_server_ip: 192.168.88.32
4nginx_exporter_port: '9113'
diff --git a/roles/services/monitoring/prometheus/nginx_exporter/handlers/main.yml b/roles/services/monitoring/prometheus/nginx_exporter/handlers/main.yml
new file mode 100644
index 0000000..fe9a90d
--- /dev/null
+++ b/roles/services/monitoring/prometheus/nginx_exporter/handlers/main.yml
@@ -0,0 +1,9 @@
1- name: restart nginx
2 service:
3 name: nginx
4 state: restarted
5
6- name: restart nginx-exporter
7 service:
8 name: prometheus-nginx-exporter
9 state: started
diff --git a/roles/services/monitoring/prometheus/nginx_exporter/tasks/main.yml b/roles/services/monitoring/prometheus/nginx_exporter/tasks/main.yml
new file mode 100644
index 0000000..819f71e
--- /dev/null
+++ b/roles/services/monitoring/prometheus/nginx_exporter/tasks/main.yml
@@ -0,0 +1,44 @@
1- name: install package (Debian)
2 when: ansible_facts['distribution'] == "Debian"
3 package:
4 name: "{{ nginx_exporter_debian_package }}"
5
6- name: allow port
7 ufw:
8 rule: allow
9 direction: in
10 proto: tcp
11 src: "{{ prometheus_server_ip }}"
12 to_port: "{{ nginx_exporter_port }}"
13
14- name: copy defaults file
15 notify: restart nginx-exporter
16 copy:
17 src: "{{ nginx_exporter_defaults }}"
18 dest: /etc/default/prometheus-nginx-exporter
19 owner: root
20 group: root
21 mode: '0644'
22
23- name: deploy nginx configuration
24 notify: restart nginx
25 copy:
26 src: "{{ nginx_exporter_config }}"
27 dest: /etc/nginx/sites-available/metrics.conf
28 owner: root
29 group: root
30 mode: '0644'
31
32- name: symlink site
33 file:
34 src: /etc/nginx/sites-available/metrics.conf
35 dest: /etc/nginx/sites-enabled/metrics.conf
36 owner: root
37 group: root
38 state: link
39
40- name: enable service
41 systemd:
42 name: prometheus-nginx-exporter
43 enabled: yes
44 masked: no
diff --git a/roles/services/monitoring/prometheus/node_exporter/defaults/main.yml b/roles/services/monitoring/prometheus/node_exporter/defaults/main.yml
new file mode 100644
index 0000000..e4ff351
--- /dev/null
+++ b/roles/services/monitoring/prometheus/node_exporter/defaults/main.yml
@@ -0,0 +1,4 @@
1node_exporter_debian_package: prometheus-node-exporter
2node_exporter_fedora_package: golang-github-prometheus-node-exporter
3prometheus_server_ip: 192.168.88.32
4node_exporter_port: '9100'
diff --git a/roles/services/monitoring/prometheus/node_exporter/tasks/main.yml b/roles/services/monitoring/prometheus/node_exporter/tasks/main.yml
new file mode 100644
index 0000000..6bbcc08
--- /dev/null
+++ b/roles/services/monitoring/prometheus/node_exporter/tasks/main.yml
@@ -0,0 +1,28 @@
1- name: install package (Debian)
2 when: ansible_facts['distribution'] == "Debian"
3 package:
4 name: "{{ node_exporter_debian_package }}"
5
6- name: install package (Fedora)
7 when: ansible_facts['distribution'] == "Fedora"
8 package:
9 name: "{{ node_exporter_fedora_package }}"
10
11- name: allow port
12 ufw:
13 rule: allow
14 direction: in
15 proto: tcp
16 src: "{{ prometheus_server_ip }}"
17 to_port: "{{ node_exporter_port }}"
18
19- name: enable service
20 systemd:
21 name: prometheus-node-exporter
22 enabled: yes
23 masked: no
24
25- name: restart service
26 service:
27 name: prometheus-node-exporter
28 state: restarted
diff --git a/roles/services/monitoring/prometheus/server/defaults/main.yml b/roles/services/monitoring/prometheus/server/defaults/main.yml
new file mode 100644
index 0000000..696e7cc
--- /dev/null
+++ b/roles/services/monitoring/prometheus/server/defaults/main.yml
@@ -0,0 +1,6 @@
1prometheus_package: prometheus
2management_ip: 192.168.88.254
3grafana_server_ip: 192.168.88.21
4prometheus_port: '9090'
5prometheus_config: files/prometheus.yml
6prometheus_defaults: files/prometheus
diff --git a/roles/services/monitoring/prometheus/server/tasks/main.yml b/roles/services/monitoring/prometheus/server/tasks/main.yml
new file mode 100644
index 0000000..06ecc10
--- /dev/null
+++ b/roles/services/monitoring/prometheus/server/tasks/main.yml
@@ -0,0 +1,79 @@
1- name: install package
2 package:
3 name: "{{ prometheus_package }}"
4
5- name: allow access to metrics from grafana
6 ufw:
7 rule: allow
8 direction: in
9 proto: tcp
10 src: "{{ grafana_server_ip }}"
11 to_port: "{{ prometheus_port }}"
12
13- name: allow access to metrics from management
14 ufw:
15 rule: allow
16 direction: in
17 proto: tcp
18 src: "{{ management_ip }}"
19 to_port: "{{ prometheus_port }}"
20
21- name: copy config file
22 copy:
23 src: "{{ prometheus_config }}"
24 dest: /etc/prometheus/prometheus.yml
25 owner: root
26 group: root
27 mode: '0644'
28
29- name: copy defaults file
30 copy:
31 src: "{{ prometheus_defaults }}"
32 dest: /etc/default/prometheus
33 owner: root
34 group: root
35 mode: '0644'
36
37- name: enable service
38 systemd:
39 name: prometheus
40 enabled: yes
41 masked: no
42
43- name: restart service
44 service:
45 name: prometheus
46 state: restarted
47
48- name: deploy nginx configuration
49 copy:
50 src: "{{ prometheus_nginx_config }}"
51 dest: /etc/nginx/sites-available/grafana.conf
52 owner: root
53 group: root
54 mode: '0644'
55
56- name: symlink site
57 file:
58 src: /etc/nginx/sites-available/grafana.conf
59 dest: /etc/nginx/sites-enabled/grafana.conf
60 owner: root
61 group: root
62 state: link
63
64- name: allow http (80/tcp) traffic
65 ufw:
66 rule: allow
67 port: '80'
68 proto: tcp
69
70- name: allow https (443/tcp) traffic
71 ufw:
72 rule: allow
73 port: '443'
74 proto: tcp
75
76- name: restart nginx
77 service:
78 name: nginx
79 state: restarted
diff --git a/roles/services/monitoring/promtail/handlers/main.yml b/roles/services/monitoring/promtail/handlers/main.yml
new file mode 100644
index 0000000..97ea7d3
--- /dev/null
+++ b/roles/services/monitoring/promtail/handlers/main.yml
@@ -0,0 +1,39 @@
1- name: update repos - debian
2 apt:
3 update_cache: yes
4
5- name: update repos - fedora
6 dnf:
7 name: "*"
8 state: latest
9
10- name: build loki-docker-driver plugin for private repo
11 become: yes
12 become_user: "{{ docker_username }}"
13 environment:
14 LOKI_DOCKER_DRIVER: "{{ docker_registry_url }}/{{ docker_registry_username }}/loki-docker-driver"
15 community.general.make:
16 chdir: "{{ docker_home }}/plugins/loki"
17 target: docker-driver-push
18
19- name: restart rootless docker
20 become: yes
21 become_user: "{{ docker_username }}"
22 systemd:
23 name: docker
24 enabled: yes
25 state: restarted
26 scope: user
27 environment:
28 XDG_RUNTIME_DIR: "/run/user/{{ docker_uid }}"
29
30- name: restart docker
31 service:
32 name: docker
33 state: restarted
34
35- name: restart promtail
36 when: promtail_config.changed
37 service:
38 name: promtail
39 state: restarted
diff --git a/roles/services/monitoring/promtail/tasks/main.yml b/roles/services/monitoring/promtail/tasks/main.yml
new file mode 100644
index 0000000..f8b28cc
--- /dev/null
+++ b/roles/services/monitoring/promtail/tasks/main.yml
@@ -0,0 +1,151 @@
1- name: install extrepo
2 when: ansible_facts['distribution'] == 'Debian'
3 package:
4 name: extrepo
5 state: latest
6
7- name: add grafana repo | debian
8 when: ansible_facts['distribution'] == 'Debian'
9 register: result
10 changed_when: result.stdout | regex_search("skipped") | bool
11 notify: update repos - debian
12 command:
13 cmd: extrepo enable grafana
14 creates: /etc/apt/sources.list.d/extrepo_grafana.sources
15
16- meta: flush_handlers
17
18- name: update grafana extrepo data | debian
19 when: ansible_facts['distribution'] == 'Debian'
20 changed_when: false
21 command:
22 cmd: extrepo update grafana
23
24- name: add Grafana repo | fedora
25 when: ansible_facts['distribution'] == 'Fedora'
26 notify: update repos - fedora
27 yum_repository:
28 name: grafana
29 file: grafna
30 description: "Grafana OSS Repo"
31 baseurl: "https://rpm.grafana.com"
32 repo_gpgcheck: yes
33 enabled: yes
34 gpgcheck: yes
35 gpgkey: https://rpm.grafana.com/gpg.key
36 sslverify: yes
37 sslcacert: /etc/pki/tls/certs/ca-bundle.crt
38 exclude: "*beta*"
39
40- name: install promtail
41 package:
42 name: promtail
43 state: latest
44
45- name: add promtail to adm group for log access (debian)
46 when: ansible_facts['distribution'] == 'Debian'
47 user:
48 name: promtail
49 groups: adm
50 append: yes
51
52- name: add promtail to systemd-journal group for journal access
53 user:
54 name: promtail
55 groups: systemd-journal
56 append: yes
57
58- name: create docker plugin directory
59 when: "'docker_hosts' in group_names"
60 become: yes
61 become_user: "{{ docker_username }}"
62 file:
63 path: "{{ docker_home }}/plugins"
64 state: directory
65 owner: "{{ docker_username }}"
66 group: "{{ docker_username }}"
67 mode: "0755"
68
69- name: clone loki repo
70 when: "'docker_hosts' in group_names"
71 become: yes
72 become_user: "{{ docker_username }}"
73 git:
74 repo: "{{ loki_repo }}"
75 dest: "{{ docker_home }}/plugins/loki"
76 version: "{{ loki_version }}"
77 register: repo
78 notify: build loki-docker-driver plugin for private repo
79
80- meta: flush_handlers
81
82- name: login to docker registry
83 when: "'docker_hosts' in group_names"
84 become: yes
85 become_user: "{{ docker_username }}"
86 environment:
87 XDG_RUNTIME_DIR: "/run/user/{{ docker_uid }}"
88 docker_login:
89 docker_host: "unix://run/user/{{ docker_uid }}/docker.sock"
90 registry_url: "{{ docker_registry_url }}"
91 username: "{{ docker_registry_username }}"
92 password: "{{ docker_registry_password }}"
93
94# docker driver rootless
95
96- name: enable loki-docker-driver plugin
97 when: "'docker_hosts' in group_names"
98 become: yes
99 become_user: "{{ docker_username }}"
100 notify: restart rootless docker
101 community.docker.docker_plugin:
102 plugin_name: "{{ docker_registry_url }}/{{ docker_registry_username }}/loki-docker-driver:main"
103 state: enable
104 docker_host: "unix://run/user/{{ docker_uid }}/docker.sock"
105 alias: loki
106
107- name: deploy docker config
108 when: "'docker_hosts' in group_names"
109 notify: restart rootless docker
110 copy:
111 src: "{{ docker_config }}"
112 dest: "{{ docker_home }}/.config/docker/daemon.json"
113 owner: "{{ docker_username }}"
114 group: "{{ docker_username }}"
115 mode: '0644'
116
117# docker driver root
118
119- name: enable loki-docker-driver plugin
120 when: "'docker_hosts' in group_names"
121 notify: restart docker
122 community.docker.docker_plugin:
123 plugin_name: "{{ docker_registry_url }}/{{ docker_registry_username }}/loki-docker-driver:main"
124 state: enable
125 alias: loki
126
127- name: deploy docker config
128 when: "'docker_hosts' in group_names"
129 notify: restart docker
130 copy:
131 src: "{{ docker_config }}"
132 dest: /etc/docker/daemon.json
133 owner: root
134 group: root
135 mode: '0644'
136
137- name: deploy promtail configuration
138 notify: restart promtail
139 copy:
140 src: "{{ promtail_config }}"
141 dest: /etc/promtail/config.yml
142 owner: root
143 group: root
144 mode: '0644'
145
146- name: enable promtail
147 systemd:
148 daemon_reload: yes
149 enabled: yes
150 masked: no
151 name: promtail
diff --git a/roles/services/msmtp_mta/tasks/main.yml b/roles/services/msmtp_mta/tasks/main.yml
new file mode 100644
index 0000000..4958acc
--- /dev/null
+++ b/roles/services/msmtp_mta/tasks/main.yml
@@ -0,0 +1,11 @@
1- name: install msmtp packages
2 package:
3 name: "{{ msmtp_mta_packages }}"
4
5- name: copy msmtp config file
6 copy:
7 src: "{{ msmtp_mta_config }}"
8 dest: /etc/msmtprc
9 owner: root
10 group: msmtp
11 mode: '0640'
diff --git a/roles/services/pihole/handlers/main.yml b/roles/services/pihole/handlers/main.yml
new file mode 100644
index 0000000..9c1d311
--- /dev/null
+++ b/roles/services/pihole/handlers/main.yml
@@ -0,0 +1,14 @@
1- name: restart unbound
2 service:
3 name: unbound
4 state: restarted
5
6- name: restart lighttpd
7 service:
8 name: lighttpd
9 state: restarted
10
11- name: restart ftl
12 service:
13 name: pihole-FTL
14 state: restarted
diff --git a/roles/services/pihole/tasks/main.yml b/roles/services/pihole/tasks/main.yml
new file mode 100644
index 0000000..3f3abde
--- /dev/null
+++ b/roles/services/pihole/tasks/main.yml
@@ -0,0 +1,80 @@
1- name: install packages
2 package:
3 name: "{{ pihole_packages }}"
4
5- name: clone pihole repository
6 git:
7 repo: https://github.com/pi-hole/pi-hole.git
8 dest: /tmp/pi-hole
9 version: v5.17.1
10 depth: 1
11
12- name: create configuration directory
13 file:
14 path: /etc/pihole
15 state: directory
16 owner: root
17 group: root
18 mode: '0755'
19
20- name: copy setupVars.conf
21 copy:
22 src: "{{ pihole_setupvars }}"
23 dest: /etc/pihole/setupVars.conf
24 owner: root
25 group: root
26 mode: '0644'
27
28- name: copy pihole unbound configuration
29 notify: restart unbound
30 copy:
31 src: "{{ pihole_unboundconf }}"
32 dest: /etc/unbound/unbound.conf.d/pihole.conf
33 owner: root
34 group: root
35 mode: '0644'
36
37- name: run installation script
38 command:
39 cmd: "/bin/bash '/tmp/pi-hole/automated install/basic-install.sh' --unattended"
40 creates: /etc/pihole/install.log
41 ignore_errors: yes
42 notify:
43 - restart lighttpd
44 - restart ftl
45
46- name: change pihole admin password
47 register: result
48 changed_when: result.rc == 0
49 command:
50 cmd: "pihole -a -p {{ pihole_password }}"
51
52- name: initialize gravity
53 register: result
54 changed_when: result.rc == 0
55 command:
56 cmd: "pihole -g"
57
58- name: allow http (80/tcp) traffic
59 ufw:
60 rule: allow
61 port: '80'
62 proto: tcp
63
64- name: allow https (443/tcp) traffic
65 ufw:
66 rule: allow
67 port: '443'
68 proto: tcp
69
70- name: allow dns (53/udp) traffic
71 ufw:
72 rule: allow
73 port: '53'
74 proto: udp
75
76- name: allow dns tcp (53/tcp) traffic
77 ufw:
78 rule: allow
79 port: '53'
80 proto: tcp
diff --git a/roles/services/ssh/tasks/main.yml b/roles/services/ssh/tasks/main.yml
new file mode 100644
index 0000000..d2cabab
--- /dev/null
+++ b/roles/services/ssh/tasks/main.yml
@@ -0,0 +1,46 @@
1- name: explicitly only allow pubkey auth
2 lineinfile:
3 path: /etc/ssh/sshd_config
4 regexp: "^#?AuthenticationMethods.*"
5 line: "AuthenticationMethods publickey"
6
7- name: disable root ssh login
8 lineinfile:
9 path: /etc/ssh/sshd_config
10 regexp: "^#?PermitRootLogin"
11 line: "PermitRootLogin no"
12
13- name: enable publickey authentication
14 lineinfile:
15 path: /etc/ssh/sshd_config
16 regexp: "^#?PubkeyAuthentication.*"
17 line: "PubkeyAuthentication yes"
18
19- name: disable password authentication
20 lineinfile:
21 path: /etc/ssh/sshd_config
22 regexp: "^#?PasswordAuthentication.*"
23 line: "PasswordAuthentication no"
24
25- name: disable challenge response
26 lineinfile:
27 path: /etc/ssh/sshd_config
28 regexp: "^#?ChallengeResponseAuthentication.*"
29 line: "ChallengeResponseAuthentication no"
30
31- name: disable pam
32 lineinfile:
33 path: /etc/ssh/sshd_config
34 regexp: "^#?UsePAM.*"
35 line: "UsePAM no"
36
37- name: ensure sshd is enabled
38 systemd:
39 name: sshd
40 enabled: yes
41 masked: no
42
43- name: restart sshd
44 service:
45 name: sshd
46 state: restarted
diff --git a/roles/services/unattended_upgrades/tasks/main.yml b/roles/services/unattended_upgrades/tasks/main.yml
new file mode 100644
index 0000000..bad3c02
--- /dev/null
+++ b/roles/services/unattended_upgrades/tasks/main.yml
@@ -0,0 +1,63 @@
1- name: install packages
2 package:
3 name: "{{ unattended_upgrades_packages }}"
4 state: latest
5
6- name: edit apt update timer
7 lineinfile:
8 path: /etc/systemd/system/timers.target.wants/apt-daily.timer
9 regexp: "OnCalendar.*"
10 line: "OnCalendar=*-*-* 0,4,8,12,16,20:00"
11
12- name: edit apt update timer
13 lineinfile:
14 path: /etc/systemd/system/timers.target.wants/apt-daily.timer
15 regexp: "RandomizedDelaySec.*"
16 line: "RandomizedDelaySec=10m"
17
18- name: edit apt upgrade timer
19 lineinfile:
20 path: /etc/systemd/system/timers.target.wants/apt-daily-upgrade.timer
21 regexp: "OnCalendar.*"
22 line: "OnCalendar=*-*-* 0,4,8,12,16,20:30"
23
24- name: edit apt upgrade timer
25 lineinfile:
26 path: /etc/systemd/system/timers.target.wants/apt-daily-upgrade.timer
27 regexp: "RandomizedDelaySec.*"
28 line: "RandomizedDelaySec=5m"
29
30- name: edit APT::Periodic settings
31 lineinfile:
32 path: /etc/apt/apt.conf.d/20auto-upgrades
33 regexp: "APT::Periodic::Update.*"
34 line: 'APT::Periodic::Update-Package-Lists "always";'
35
36- name: edit APT::Periodic settings
37 lineinfile:
38 path: /etc/apt/apt.conf.d/20auto-upgrades
39 regexp: "APT::Periodic::Unattended.*"
40 line: 'APT::Periodic::Unattended-Upgrade "always";'
41
42- name: configure unattended upgrades
43 lineinfile:
44 path: /etc/apt/apt.conf.d/50unattended-upgrades
45 regexp: ".*Unattended-Upgrade::Mail.*"
46 line: 'Unattended-Upgrade::Mail "{{ uu_mail_to }}";'
47
48- name: configure unattended upgrades
49 lineinfile:
50 path: /etc/apt/apt.conf.d/50unattended-upgrades
51 insertafter: 'Unattended-Upgrade::Mail "{{ uu_mail_to }}";'
52 line: 'Unattended-Upgrade::Sender "{{ uu_mail_from }}";'
53
54- name: configure unattended upgrades
55 lineinfile:
56 path: /etc/apt/apt.conf.d/50unattended-upgrades
57 regexp: ".*Unattended-Upgrade::MailReport.*"
58 line: 'Unattended-Upgrade::MailReport "always";'
59
60- name: restart service
61 service:
62 name: unattended-upgrades
63 state: restarted
diff --git a/run.yml b/run.yml
new file mode 100644
index 0000000..43211bf
--- /dev/null
+++ b/run.yml
@@ -0,0 +1,89 @@
1---
2- name: configure proxmox host
3 hosts: hypervisors
4 become: yes
5 roles:
6 - linux_base
7 - proxmox/system
8 #- proxmox/proxmox_backup_server
9 - proxmox/pve_backup
10 - proxmox/debian_cloudinit
11 - proxmox/fedora_cloudinit
12 - services/msmtp_mta
13
14- name: common configuration
15 hosts: virtual_machines,hypervisors
16 roles:
17 - linux_base
18 - services/ssh
19 - services/chronyd
20 - services/monitoring/prometheus/node_exporter
21
22- name: configure freeipa server
23 hosts: ipaservers
24 roles:
25 - services/freeipa/server
26
27- name: enroll freeipa clients
28 hosts: ipaclients
29 roles:
30 - services/freeipa/client
31
32- name: configure nginx exporter
33 hosts: nginx_servers
34 roles:
35 - services/monitoring/prometheus/nginx_exporter
36
37- name: configure promtail
38 hosts: promtail_hosts
39 roles:
40 - services/monitoring/promtail
41
42- name: configure monitoring server
43 hosts: monitoring_server
44 roles:
45 - services/monitoring/prometheus/server
46 - services/monitoring/grafana
47 - services/monitoring/influxdb
48 - services/monitoring/loki
49
50- name: configure pihole
51 hosts: pihole_server
52 roles:
53 - services/pihole
54
55- name: configure jenkins server
56 hosts: jenkins_servers
57 roles:
58 - services/jenkins
59
60- name: configure docker host and containers
61 hosts: docker_server
62 roles:
63 - services/docker_rootless
64 - services/containers/authelia
65 - services/containers/searxng
66 - services/containers/pihole_exporter
67 - services/containers/drawio
68 - services/containers/jellyfin
69 - services/containers/navidrome
70 - services/containers/freshrss
71 - services/containers/homer
72 - services/containers/invidious
73 - services/containers/gitea
74 - services/containers/cadvisor
75 - services/containers/nextcloud
76 - services/containers/renovate
77 - services/containers/photoprism
78 - services/containers/arr_stack
79 - services/containers/bookstack
80 - services/containers/pywttr_docker
81 - services/containers/text_generation
82 - services/containers/kanboard
83 - services/containers/firefly
84 - services/containers/vaultwarden
85
86- name: configure game server
87 hosts: games_server
88 roles:
89 - services/game_server