Repository: asylumexp/Proxmox Branch: main Commit: db0e6dcb9bad Files: 1728 Total size: 4.6 MB Directory structure: gitextract_ip_dad7u/ ├── .editorconfig ├── .gitattributes ├── .github/ │ ├── CODEOWNERS │ ├── DISCUSSION_TEMPLATE/ │ │ └── request-script.yml │ ├── FUNDING.yml │ ├── ISSUE_TEMPLATE/ │ │ ├── bug_report.yml │ │ ├── config.yml │ │ ├── feature_request.yml │ │ ├── frontend_report.yml │ │ └── task.yml │ ├── autolabeler-config.json │ ├── changelog-pr-config.json │ ├── changelogs/ │ │ ├── 2022/ │ │ │ ├── 01.md │ │ │ ├── 02.md │ │ │ ├── 03.md │ │ │ ├── 04.md │ │ │ ├── 05.md │ │ │ ├── 06.md │ │ │ ├── 07.md │ │ │ ├── 08.md │ │ │ ├── 09.md │ │ │ ├── 10.md │ │ │ ├── 11.md │ │ │ └── 12.md │ │ ├── 2023/ │ │ │ ├── 01.md │ │ │ ├── 02.md │ │ │ ├── 03.md │ │ │ ├── 04.md │ │ │ ├── 05.md │ │ │ ├── 06.md │ │ │ ├── 07.md │ │ │ ├── 08.md │ │ │ ├── 09.md │ │ │ ├── 10.md │ │ │ ├── 11.md │ │ │ └── 12.md │ │ ├── 2024/ │ │ │ ├── 01.md │ │ │ ├── 02.md │ │ │ ├── 03.md │ │ │ ├── 04.md │ │ │ ├── 05.md │ │ │ ├── 06.md │ │ │ ├── 07.md │ │ │ ├── 08.md │ │ │ ├── 09.md │ │ │ ├── 10.md │ │ │ ├── 11.md │ │ │ └── 12.md │ │ ├── 2025/ │ │ │ ├── 01.md │ │ │ ├── 02.md │ │ │ ├── 03.md │ │ │ ├── 04.md │ │ │ ├── 05.md │ │ │ ├── 06.md │ │ │ ├── 07.md │ │ │ ├── 08.md │ │ │ ├── 09.md │ │ │ ├── 10.md │ │ │ ├── 11.md │ │ │ └── 12.md │ │ └── 2026/ │ │ ├── 01.md │ │ ├── 02.md │ │ ├── 03.md │ │ ├── 04.md │ │ └── 05.md │ ├── pull_request_template.md │ └── workflows/ │ ├── delete-pocketbase-entry-on-removal.yml │ ├── pocketbase-bot.yml │ ├── push-json-to-pocketbase.yml │ ├── trigger_github_pages_redirect.yml │ └── update-script-timestamp-on-sh-change.yml ├── .gitignore ├── .shellcheckrc ├── .vscode/ │ ├── extensions.json │ └── settings.json ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── SECURITY.md ├── ct/ │ ├── 2fauth.sh │ ├── 5etools.sh │ ├── actualbudget.sh │ ├── adguard.sh │ ├── adventurelog.sh │ ├── agentdvr.sh │ ├── alpine-adguard.sh │ ├── alpine-bitmagnet.sh │ ├── alpine-borgbackup-server.sh │ ├── alpine-caddy.sh │ ├── alpine-docker.sh │ ├── alpine-forgejo.sh │ ├── alpine-garage.sh │ ├── alpine-gatus.sh │ ├── alpine-gitea.sh │ ├── alpine-grafana.sh │ ├── alpine-ironclaw.sh │ ├── alpine-it-tools.sh │ ├── alpine-komodo.sh │ ├── alpine-loki.sh │ ├── alpine-mariadb.sh │ ├── alpine-nextcloud.sh │ ├── alpine-node-red.sh │ ├── alpine-ntfy.sh │ ├── alpine-postgresql.sh │ ├── alpine-prometheus.sh │ ├── alpine-rclone.sh │ ├── alpine-redis.sh │ ├── alpine-redlib.sh │ ├── alpine-rustdeskserver.sh │ ├── alpine-rustypaste.sh │ ├── alpine-syncthing.sh │ ├── alpine-teamspeak-server.sh │ ├── alpine-tinyauth.sh │ ├── alpine-traefik.sh │ ├── alpine-transmission.sh │ ├── alpine-valkey.sh │ ├── alpine-vaultwarden.sh │ ├── alpine-wakapi.sh │ ├── alpine-wireguard.sh │ ├── alpine-zigbee2mqtt.sh │ ├── alpine.sh │ ├── ampache.sh │ ├── anchor.sh │ ├── anytype-server.sh │ ├── apache-cassandra.sh │ ├── apache-couchdb.sh │ ├── apache-guacamole.sh │ ├── apache-tika.sh │ ├── apache-tomcat.sh │ ├── apprise-api.sh │ ├── apt-cacher-ng.sh │ ├── archivebox.sh │ ├── argus.sh │ ├── aria2.sh │ ├── asterisk.sh │ ├── audiobookshelf.sh │ ├── authelia.sh │ ├── autobrr.sh │ ├── autocaliweb.sh │ ├── babybuddy.sh │ ├── backrest.sh │ ├── baikal.sh │ ├── bambuddy.sh │ ├── bar-assistant.sh │ ├── bazarr.sh │ ├── bentopdf.sh │ ├── beszel.sh │ ├── bichon.sh │ ├── birdnet-go.sh │ ├── bitmagnet.sh │ ├── blocky.sh │ ├── bookstack.sh │ ├── bunkerweb.sh │ ├── byparr.sh │ ├── bytestash.sh │ ├── caddy.sh │ ├── calibre-web.sh │ ├── casaos.sh │ ├── changedetection.sh │ ├── channels.sh │ ├── checkmate.sh │ ├── checkmk.sh │ ├── cleanuparr.sh │ ├── cloudflare-ddns.sh │ ├── cloudflared.sh │ ├── cloudreve.sh │ ├── cockpit.sh │ ├── comfyui.sh │ ├── commafeed.sh │ ├── configarr.sh │ ├── convertx.sh │ ├── coolify.sh │ ├── coredns.sh │ ├── cosmos.sh │ ├── crafty-controller.sh │ ├── cronicle.sh │ ├── cross-seed.sh │ ├── cryptpad.sh │ ├── dagu.sh │ ├── dashy.sh │ ├── databasus.sh │ ├── dawarich.sh │ ├── ddclient.sh │ ├── debian.sh │ ├── deconz.sh │ ├── deluge.sh │ ├── discopanel.sh │ ├── dispatcharr.sh │ ├── docker.sh │ ├── dockge.sh │ ├── docmost.sh │ ├── dokploy.sh │ ├── dolibarr.sh │ ├── domain-locker.sh │ ├── domain-monitor.sh │ ├── donetick.sh │ ├── dotnetaspwebapi.sh │ ├── drawdb.sh │ ├── drawio.sh │ ├── duplicati.sh │ ├── ebusd.sh │ ├── elementsynapse.sh │ ├── emby.sh │ ├── emqx.sh │ ├── endurain.sh │ ├── erpnext.sh │ ├── ersatztv.sh │ ├── esphome.sh │ ├── evcc.sh │ ├── excalidraw.sh │ ├── fhem.sh │ ├── fileflows.sh │ ├── firefly.sh │ ├── fireshare.sh │ ├── fladder.sh │ ├── flaresolverr.sh │ ├── flatnotes.sh │ ├── flowiseai.sh │ ├── fluid-calendar.sh │ ├── foldergram.sh │ ├── forgejo.sh │ ├── freepbx.sh │ ├── freshrss.sh │ ├── frigate.sh │ ├── fumadocs.sh │ ├── garage.sh │ ├── gatus.sh │ ├── geopulse.sh │ ├── ghost.sh │ ├── ghostfolio.sh │ ├── gitea-mirror.sh │ ├── gitea.sh │ ├── github-runner.sh │ ├── glance.sh │ ├── globaleaks.sh │ ├── glpi.sh │ ├── gluetun.sh │ ├── go2rtc.sh │ ├── gogs.sh │ ├── gokapi.sh │ ├── gotify.sh │ ├── grafana.sh │ ├── gramps-web.sh │ ├── graylog.sh │ ├── grist.sh │ ├── grocy.sh │ ├── guardian.sh │ ├── gwn-manager.sh │ ├── headers/ │ │ ├── 2fauth │ │ ├── actualbudget │ │ ├── adguard │ │ ├── adventurelog │ │ ├── agentdvr │ │ ├── alpine │ │ ├── alpine-adguard │ │ ├── alpine-bitmagnet │ │ ├── alpine-borgbackup-server │ │ ├── alpine-caddy │ │ ├── alpine-docker │ │ ├── alpine-forgejo │ │ ├── alpine-garage │ │ ├── alpine-gatus │ │ ├── alpine-gitea │ │ ├── alpine-grafana │ │ ├── alpine-ironclaw │ │ ├── alpine-it-tools │ │ ├── alpine-komodo │ │ ├── alpine-loki │ │ ├── alpine-mariadb │ │ ├── alpine-nextcloud │ │ ├── alpine-node-red │ │ ├── alpine-ntfy │ │ ├── alpine-postgresql │ │ ├── alpine-prometheus │ │ ├── alpine-rclone │ │ ├── alpine-redis │ │ ├── alpine-redlib │ │ ├── alpine-rustdeskserver │ │ ├── alpine-rustypaste │ │ ├── alpine-syncthing │ │ ├── alpine-teamspeak-server │ │ ├── alpine-tinyauth │ │ ├── alpine-traefik │ │ ├── alpine-transmission │ │ ├── alpine-valkey │ │ ├── alpine-vaultwarden │ │ ├── alpine-wakapi │ │ ├── alpine-wireguard │ │ ├── alpine-zigbee2mqtt │ │ ├── ampache │ │ ├── anchor │ │ ├── anytype-server │ │ ├── apache-cassandra │ │ ├── apache-couchdb │ │ ├── apache-guacamole │ │ ├── apache-tika │ │ ├── apache-tomcat │ │ ├── apprise-api │ │ ├── apt-cacher-ng │ │ ├── archivebox │ │ ├── argus │ │ ├── aria2 │ │ ├── asterisk │ │ ├── audiobookshelf │ │ ├── authelia │ │ ├── autobrr │ │ ├── autocaliweb │ │ ├── babybuddy │ │ ├── backrest │ │ ├── baikal │ │ ├── bambuddy │ │ ├── bar-assistant │ │ ├── bazarr │ │ ├── bentopdf │ │ ├── beszel │ │ ├── bichon │ │ ├── birdnet-go │ │ ├── bitmagnet │ │ ├── blocky │ │ ├── bookstack │ │ ├── bunkerweb │ │ ├── byparr │ │ ├── bytestash │ │ ├── caddy │ │ ├── calibre-web │ │ ├── casaos │ │ ├── changedetection │ │ ├── channels │ │ ├── checkmate │ │ ├── checkmk │ │ ├── cleanuparr │ │ ├── cloudflare-ddns │ │ ├── cloudflared │ │ ├── cloudreve │ │ ├── cockpit │ │ ├── comfyui │ │ ├── commafeed │ │ ├── configarr │ │ ├── convertx │ │ ├── coolify │ │ ├── coredns │ │ ├── cosmos │ │ ├── crafty-controller │ │ ├── cronicle │ │ ├── cross-seed │ │ ├── cryptpad │ │ ├── dagu │ │ ├── dashy │ │ ├── databasus │ │ ├── dawarich │ │ ├── ddclient │ │ ├── debian │ │ ├── deconz │ │ ├── deluge │ │ ├── discopanel │ │ ├── dispatcharr │ │ ├── docker │ │ ├── dockge │ │ ├── docmost │ │ ├── dokploy │ │ ├── dolibarr │ │ ├── domain-locker │ │ ├── domain-monitor │ │ ├── donetick │ │ ├── dotnetaspwebapi │ │ ├── drawdb │ │ ├── drawio │ │ ├── duplicati │ │ ├── ebusd │ │ ├── elementsynapse │ │ ├── emby │ │ ├── emqx │ │ ├── endurain │ │ ├── erpnext │ │ ├── ersatztv │ │ ├── esphome │ │ ├── evcc │ │ ├── excalidraw │ │ ├── fhem │ │ ├── fileflows │ │ ├── firefly │ │ ├── fireshare │ │ ├── fladder │ │ ├── flaresolverr │ │ ├── flatnotes │ │ ├── flowiseai │ │ ├── fluid-calendar │ │ ├── foldergram │ │ ├── forgejo │ │ ├── freepbx │ │ ├── freshrss │ │ ├── frigate │ │ ├── fumadocs │ │ ├── garage │ │ ├── gatus │ │ ├── geopulse │ │ ├── ghost │ │ ├── ghostfolio │ │ ├── gitea │ │ ├── gitea-mirror │ │ ├── github-runner │ │ ├── glance │ │ ├── globaleaks │ │ ├── glpi │ │ ├── gluetun │ │ ├── go2rtc │ │ ├── gogs │ │ ├── gokapi │ │ ├── gotify │ │ ├── grafana │ │ ├── gramps-web │ │ ├── graylog │ │ ├── grist │ │ ├── grocy │ │ ├── guardian │ │ ├── gwn-manager │ │ ├── headscale │ │ ├── healthchecks │ │ ├── heimdall-dashboard │ │ ├── hev-socks5-server │ │ ├── hivemq │ │ ├── homarr │ │ ├── homeassistant │ │ ├── homebox │ │ ├── homebridge │ │ ├── homelable │ │ ├── homepage │ │ ├── homer │ │ ├── hoodik │ │ ├── hortusfox │ │ ├── hyperhdr │ │ ├── hyperion │ │ ├── igotify │ │ ├── immich │ │ ├── immichframe │ │ ├── infisical │ │ ├── influxdb │ │ ├── inspircd │ │ ├── inventree │ │ ├── investbrain │ │ ├── invoiceninja │ │ ├── iobroker │ │ ├── ironclaw │ │ ├── isponsorblocktv │ │ ├── itsm-ng │ │ ├── jackett │ │ ├── jeedom │ │ ├── jellyfin │ │ ├── jellyseerr │ │ ├── jenkins │ │ ├── jitsi-meet │ │ ├── joplin-server │ │ ├── jotty │ │ ├── jupyternotebook │ │ ├── kapowarr │ │ ├── karakeep │ │ ├── kasm │ │ ├── kavita │ │ ├── keycloak │ │ ├── kima-hub │ │ ├── kimai │ │ ├── kitchenowl │ │ ├── koel │ │ ├── koillection │ │ ├── kometa │ │ ├── komga │ │ ├── komodo │ │ ├── kubo │ │ ├── kutt │ │ ├── languagetool │ │ ├── lazylibrarian │ │ ├── leantime │ │ ├── librechat │ │ ├── librenms │ │ ├── librespeed-rust │ │ ├── libretranslate │ │ ├── lidarr │ │ ├── limesurvey │ │ ├── linkding │ │ ├── linkstack │ │ ├── linkwarden │ │ ├── listmonk │ │ ├── livebook │ │ ├── lldap │ │ ├── loki │ │ ├── lubelogger │ │ ├── lychee │ │ ├── lyrionmusicserver │ │ ├── mafl │ │ ├── magicmirror │ │ ├── mail-archiver │ │ ├── managemydamnlife │ │ ├── manyfold │ │ ├── mariadb │ │ ├── matomo │ │ ├── matter-server │ │ ├── matterbridge │ │ ├── mattermost │ │ ├── mealie │ │ ├── mediamanager │ │ ├── mediamtx │ │ ├── medusa │ │ ├── meilisearch │ │ ├── memos │ │ ├── meshcentral │ │ ├── metabase │ │ ├── metube │ │ ├── minarca │ │ ├── mini-qr │ │ ├── miniflux │ │ ├── minio │ │ ├── minthcm │ │ ├── mongodb │ │ ├── monica │ │ ├── motioneye │ │ ├── mqtt │ │ ├── myip │ │ ├── mylar3 │ │ ├── myspeed │ │ ├── mysql │ │ ├── n8n │ │ ├── nagios │ │ ├── nametag │ │ ├── navidrome │ │ ├── neko │ │ ├── neo4j │ │ ├── netbird │ │ ├── netboot-xyz │ │ ├── netbox │ │ ├── netvisor │ │ ├── nextcloudpi │ │ ├── nextexplorer │ │ ├── nextpvr │ │ ├── nginx-ui │ │ ├── nginxproxymanager │ │ ├── nightscout │ │ ├── nocodb │ │ ├── node-red │ │ ├── nodebb │ │ ├── nodecast-tv │ │ ├── notifiarr │ │ ├── npmplus │ │ ├── ntfy │ │ ├── nxwitness │ │ ├── nzbget │ │ ├── oauth2-proxy │ │ ├── octoprint │ │ ├── odoo │ │ ├── ollama │ │ ├── omada │ │ ├── ombi │ │ ├── omv │ │ ├── onedev │ │ ├── onlyoffice │ │ ├── open-archiver │ │ ├── opencloud │ │ ├── opengist │ │ ├── openhab │ │ ├── openobserve │ │ ├── openproject │ │ ├── openthread-br │ │ ├── openwebui │ │ ├── openziti-controller │ │ ├── openziti-tunnel │ │ ├── ots │ │ ├── outline │ │ ├── overseerr │ │ ├── owncast │ │ ├── ownfoil │ │ ├── pairdrop │ │ ├── pangolin │ │ ├── paperless-ai │ │ ├── paperless-gpt │ │ ├── paperless-ngx │ │ ├── papra │ │ ├── part-db │ │ ├── passbolt │ │ ├── patchmon │ │ ├── paymenter │ │ ├── peanut │ │ ├── pelican-panel │ │ ├── pelican-wings │ │ ├── photoprism │ │ ├── pialert │ │ ├── pihole │ │ ├── planka │ │ ├── plant-it │ │ ├── plex │ │ ├── pocketbase │ │ ├── pocketid │ │ ├── podman │ │ ├── podman-homeassistant │ │ ├── postgresql │ │ ├── powerdns │ │ ├── privatebin │ │ ├── profilarr │ │ ├── projectsend │ │ ├── prometheus │ │ ├── prometheus-alertmanager │ │ ├── prometheus-blackbox-exporter │ │ ├── prometheus-pve-exporter │ │ ├── protonmail-bridge │ │ ├── prowlarr │ │ ├── proxmox-backup-server │ │ ├── proxmox-datacenter-manager │ │ ├── proxmox-mail-gateway │ │ ├── ps5-mqtt │ │ ├── pterodactyl-panel │ │ ├── pterodactyl-wings │ │ ├── pulse │ │ ├── pve-scripts-local │ │ ├── qbittorrent │ │ ├── qdrant │ │ ├── qui │ │ ├── rabbitmq │ │ ├── radarr │ │ ├── radicale │ │ ├── rclone │ │ ├── rdtclient │ │ ├── reactive-resume │ │ ├── readarr │ │ ├── readeck │ │ ├── recyclarr │ │ ├── redis │ │ ├── reitti │ │ ├── resiliosync │ │ ├── revealjs │ │ ├── romm │ │ ├── runtipi │ │ ├── rustdeskserver │ │ ├── rustypaste │ │ ├── rwmarkable │ │ ├── sabnzbd │ │ ├── salt │ │ ├── scanopy │ │ ├── scraparr │ │ ├── searxng │ │ ├── seaweedfs │ │ ├── seelf │ │ ├── seerr │ │ ├── semaphore │ │ ├── sftpgo │ │ ├── shelfmark │ │ ├── shinobi │ │ ├── shlink │ │ ├── signoz │ │ ├── silverbullet │ │ ├── slskd │ │ ├── smokeping │ │ ├── snipeit │ │ ├── snowshare │ │ ├── solidtime │ │ ├── sonarqube │ │ ├── sonarr │ │ ├── sonobarr │ │ ├── soulsync │ │ ├── sparkyfitness │ │ ├── speedtest-tracker │ │ ├── split-pro │ │ ├── splunk-enterprise │ │ ├── spoolman │ │ ├── sportarr │ │ ├── sqlserver2022 │ │ ├── sqlserver2025 │ │ ├── step-ca │ │ ├── stirling-pdf │ │ ├── storybook │ │ ├── storyteller │ │ ├── strapi │ │ ├── streamlink-webui │ │ ├── stylus │ │ ├── sure │ │ ├── swizzin │ │ ├── syncthing │ │ ├── tandoor │ │ ├── tasmoadmin │ │ ├── tasmocompiler │ │ ├── tautulli │ │ ├── tdarr │ │ ├── teable │ │ ├── teamspeak-server │ │ ├── technitiumdns │ │ ├── teddycloud │ │ ├── telegraf │ │ ├── teleport │ │ ├── termix │ │ ├── the-lounge │ │ ├── thingsboard │ │ ├── threadfin │ │ ├── tianji │ │ ├── tinyauth │ │ ├── traccar │ │ ├── tracearr │ │ ├── tracktor │ │ ├── traefik │ │ ├── transmission │ │ ├── transmute │ │ ├── trek │ │ ├── trilium │ │ ├── trip │ │ ├── tubearchivist │ │ ├── tududi │ │ ├── tunarr │ │ ├── twingate-connector │ │ ├── ubuntu │ │ ├── uhf │ │ ├── umami │ │ ├── umlautadaptarr │ │ ├── unbound │ │ ├── unifi-os-server │ │ ├── unmanic │ │ ├── upgopher │ │ ├── upsnap │ │ ├── uptimekuma │ │ ├── urbackupserver │ │ ├── valkey │ │ ├── vaultwarden │ │ ├── versitygw │ │ ├── victoriametrics │ │ ├── vikunja │ │ ├── wallabag │ │ ├── wallos │ │ ├── wanderer │ │ ├── warracker │ │ ├── wastebin │ │ ├── watcharr │ │ ├── watchyourlan │ │ ├── wavelog │ │ ├── wazuh │ │ ├── wealthfolio │ │ ├── web-check │ │ ├── wger │ │ ├── whisparr │ │ ├── whodb │ │ ├── wikijs │ │ ├── wireguard │ │ ├── wishlist │ │ ├── wizarr │ │ ├── wordpress │ │ ├── writefreely │ │ ├── yamtrack │ │ ├── yourls │ │ ├── yt-dlp-webui │ │ ├── yubal │ │ ├── yunohost │ │ ├── zabbix │ │ ├── zammad │ │ ├── zerobyte │ │ ├── zerotier-one │ │ ├── zigbee2mqtt │ │ ├── zipline │ │ ├── zitadel │ │ ├── zoraxy │ │ ├── zot-registry │ │ └── zwave-js-ui │ ├── headscale.sh │ ├── healthchecks.sh │ ├── heimdall-dashboard.sh │ ├── hev-socks5-server.sh │ ├── hivemq.sh │ ├── homarr.sh │ ├── homeassistant.sh │ ├── homebox.sh │ ├── homebridge.sh │ ├── homelable.sh │ ├── homepage.sh │ ├── homer.sh │ ├── hoodik.sh │ ├── hortusfox.sh │ ├── hyperhdr.sh │ ├── hyperion.sh │ ├── igotify.sh │ ├── immich.sh │ ├── immichframe.sh │ ├── infisical.sh │ ├── influxdb.sh │ ├── inspircd.sh │ ├── inventree.sh │ ├── investbrain.sh │ ├── invoiceninja.sh │ ├── iobroker.sh │ ├── ironclaw.sh │ ├── isponsorblocktv.sh │ ├── itsm-ng.sh │ ├── jackett.sh │ ├── jeedom.sh │ ├── jellyfin.sh │ ├── jellyseerr.sh │ ├── jenkins.sh │ ├── jitsi-meet.sh │ ├── joplin-server.sh │ ├── jotty.sh │ ├── jupyternotebook.sh │ ├── kapowarr.sh │ ├── karakeep.sh │ ├── kasm.sh │ ├── kavita.sh │ ├── keycloak.sh │ ├── kima-hub.sh │ ├── kimai.sh │ ├── kitchenowl.sh │ ├── koel.sh │ ├── koillection.sh │ ├── kometa.sh │ ├── komga.sh │ ├── komodo.sh │ ├── kubo.sh │ ├── kutt.sh │ ├── languagetool.sh │ ├── lazylibrarian.sh │ ├── leantime.sh │ ├── librechat.sh │ ├── librenms.sh │ ├── librespeed-rust.sh │ ├── libretranslate.sh │ ├── lidarr.sh │ ├── limesurvey.sh │ ├── linkding.sh │ ├── linkstack.sh │ ├── linkwarden.sh │ ├── listmonk.sh │ ├── livebook.sh │ ├── lldap.sh │ ├── loki.sh │ ├── lubelogger.sh │ ├── lychee.sh │ ├── lyrionmusicserver.sh │ ├── mafl.sh │ ├── magicmirror.sh │ ├── mail-archiver.sh │ ├── managemydamnlife.sh │ ├── manyfold.sh │ ├── mariadb.sh │ ├── matomo.sh │ ├── matter-server.sh │ ├── matterbridge.sh │ ├── mattermost.sh │ ├── mealie.sh │ ├── mediamanager.sh │ ├── mediamtx.sh │ ├── medusa.sh │ ├── meilisearch.sh │ ├── memos.sh │ ├── meshcentral.sh │ ├── metabase.sh │ ├── metube.sh │ ├── minarca.sh │ ├── mini-qr.sh │ ├── miniflux.sh │ ├── minio.sh │ ├── minthcm.sh │ ├── mongodb.sh │ ├── monica.sh │ ├── motioneye.sh │ ├── mqtt.sh │ ├── myip.sh │ ├── mylar3.sh │ ├── myspeed.sh │ ├── mysql.sh │ ├── n8n.sh │ ├── nagios.sh │ ├── nametag.sh │ ├── navidrome.sh │ ├── neko.sh │ ├── neo4j.sh │ ├── netbird.sh │ ├── netboot-xyz.sh │ ├── netbox.sh │ ├── netvisor.sh │ ├── nextcloudpi.sh │ ├── nextexplorer.sh │ ├── nextpvr.sh │ ├── nginx-ui.sh │ ├── nginxproxymanager.sh │ ├── nightscout.sh │ ├── nocodb.sh │ ├── node-red.sh │ ├── nodebb.sh │ ├── nodecast-tv.sh │ ├── notifiarr.sh │ ├── npmplus.sh │ ├── ntfy.sh │ ├── nxwitness.sh │ ├── nzbget.sh │ ├── oauth2-proxy.sh │ ├── octoprint.sh │ ├── odoo.sh │ ├── ollama.sh │ ├── omada.sh │ ├── ombi.sh │ ├── omv.sh │ ├── onedev.sh │ ├── onlyoffice.sh │ ├── open-archiver.sh │ ├── opencloud.sh │ ├── opengist.sh │ ├── openhab.sh │ ├── openobserve.sh │ ├── openproject.sh │ ├── openthread-br.sh │ ├── openwebui.sh │ ├── openziti-controller.sh │ ├── openziti-tunnel.sh │ ├── ots.sh │ ├── outline.sh │ ├── overseerr.sh │ ├── owncast.sh │ ├── ownfoil.sh │ ├── pairdrop.sh │ ├── pangolin.sh │ ├── paperless-ai.sh │ ├── paperless-gpt.sh │ ├── paperless-ngx.sh │ ├── papra.sh │ ├── part-db.sh │ ├── passbolt.sh │ ├── patchmon.sh │ ├── paymenter.sh │ ├── peanut.sh │ ├── pelican-panel.sh │ ├── pelican-wings.sh │ ├── photoprism.sh │ ├── pialert.sh │ ├── pihole.sh │ ├── planka.sh │ ├── plant-it.sh │ ├── plex.sh │ ├── pocketbase.sh │ ├── pocketid.sh │ ├── podman-homeassistant.sh │ ├── podman.sh │ ├── postgresql.sh │ ├── powerdns.sh │ ├── privatebin.sh │ ├── profilarr.sh │ ├── projectsend.sh │ ├── prometheus-alertmanager.sh │ ├── prometheus-blackbox-exporter.sh │ ├── prometheus-pve-exporter.sh │ ├── prometheus.sh │ ├── protonmail-bridge.sh │ ├── prowlarr.sh │ ├── proxmox-backup-server.sh │ ├── proxmox-datacenter-manager.sh │ ├── proxmox-mail-gateway.sh │ ├── ps5-mqtt.sh │ ├── pterodactyl-panel.sh │ ├── pterodactyl-wings.sh │ ├── pulse.sh │ ├── pve-scripts-local.sh │ ├── qbittorrent.sh │ ├── qdrant.sh │ ├── qui.sh │ ├── rabbitmq.sh │ ├── radarr.sh │ ├── radicale.sh │ ├── rclone.sh │ ├── rdtclient.sh │ ├── reactive-resume.sh │ ├── readarr.sh │ ├── readeck.sh │ ├── recyclarr.sh │ ├── redis.sh │ ├── reitti.sh │ ├── resiliosync.sh │ ├── revealjs.sh │ ├── romm.sh │ ├── runtipi.sh │ ├── rustdeskserver.sh │ ├── rustypaste.sh │ ├── rwmarkable.sh │ ├── sabnzbd.sh │ ├── salt.sh │ ├── scanopy.sh │ ├── scraparr.sh │ ├── searxng.sh │ ├── seaweedfs.sh │ ├── seelf.sh │ ├── seerr.sh │ ├── semaphore.sh │ ├── sftpgo.sh │ ├── shelfmark.sh │ ├── shinobi.sh │ ├── shlink.sh │ ├── signoz.sh │ ├── silverbullet.sh │ ├── slskd.sh │ ├── smokeping.sh │ ├── snipeit.sh │ ├── snowshare.sh │ ├── solidtime.sh │ ├── sonarqube.sh │ ├── sonarr.sh │ ├── sonobarr.sh │ ├── soulsync.sh │ ├── sparkyfitness.sh │ ├── speedtest-tracker.sh │ ├── split-pro.sh │ ├── splunk-enterprise.sh │ ├── spoolman.sh │ ├── sportarr.sh │ ├── sqlserver2022.sh │ ├── sqlserver2025.sh │ ├── step-ca.sh │ ├── stirling-pdf.sh │ ├── storybook.sh │ ├── storyteller.sh │ ├── strapi.sh │ ├── streamlink-webui.sh │ ├── stylus.sh │ ├── sure.sh │ ├── swizzin.sh │ ├── syncthing.sh │ ├── tandoor.sh │ ├── tasmoadmin.sh │ ├── tasmocompiler.sh │ ├── tautulli.sh │ ├── tdarr.sh │ ├── teable.sh │ ├── teamspeak-server.sh │ ├── technitiumdns.sh │ ├── teddycloud.sh │ ├── telegraf.sh │ ├── teleport.sh │ ├── termix.sh │ ├── the-lounge.sh │ ├── thingsboard.sh │ ├── threadfin.sh │ ├── tianji.sh │ ├── tinyauth.sh │ ├── traccar.sh │ ├── tracearr.sh │ ├── tracktor.sh │ ├── traefik.sh │ ├── transmission.sh │ ├── transmute.sh │ ├── trek.sh │ ├── trilium.sh │ ├── trip.sh │ ├── tubearchivist.sh │ ├── tududi.sh │ ├── tunarr.sh │ ├── twingate-connector.sh │ ├── ubuntu.sh │ ├── uhf.sh │ ├── umami.sh │ ├── umlautadaptarr.sh │ ├── unbound.sh │ ├── unifi-os-server.sh │ ├── unmanic.sh │ ├── upgopher.sh │ ├── upsnap.sh │ ├── uptimekuma.sh │ ├── urbackupserver.sh │ ├── valkey.sh │ ├── vaultwarden.sh │ ├── versitygw.sh │ ├── victoriametrics.sh │ ├── vikunja.sh │ ├── wallabag.sh │ ├── wallos.sh │ ├── wanderer.sh │ ├── warracker.sh │ ├── wastebin.sh │ ├── watcharr.sh │ ├── watchyourlan.sh │ ├── wavelog.sh │ ├── wazuh.sh │ ├── wealthfolio.sh │ ├── web-check.sh │ ├── wger.sh │ ├── whisparr.sh │ ├── whodb.sh │ ├── wikijs.sh │ ├── wireguard.sh │ ├── wishlist.sh │ ├── wizarr.sh │ ├── wordpress.sh │ ├── writefreely.sh │ ├── yamtrack.sh │ ├── yourls.sh │ ├── yt-dlp-webui.sh │ ├── yubal.sh │ ├── yunohost.sh │ ├── zabbix.sh │ ├── zammad.sh │ ├── zerobyte.sh │ ├── zerotier-one.sh │ ├── zigbee2mqtt.sh │ ├── zipline.sh │ ├── zitadel.sh │ ├── zoraxy.sh │ ├── zot-registry.sh │ └── zwave-js-ui.sh ├── install/ │ ├── 2fauth-install.sh │ ├── actualbudget-install.sh │ ├── adguard-install.sh │ ├── adventurelog-install.sh │ ├── agentdvr-install.sh │ ├── alpine-adguard-install.sh │ ├── alpine-bitmagnet-install.sh │ ├── alpine-borgbackup-server-install.sh │ ├── alpine-caddy-install.sh │ ├── alpine-docker-install.sh │ ├── alpine-forgejo-install.sh │ ├── alpine-garage-install.sh │ ├── alpine-gatus-install.sh │ ├── alpine-gitea-install.sh │ ├── alpine-grafana-install.sh │ ├── alpine-install.sh │ ├── alpine-ironclaw-install.sh │ ├── alpine-it-tools-install.sh │ ├── alpine-loki-install.sh │ ├── alpine-mariadb-install.sh │ ├── alpine-nextcloud-install.sh │ ├── alpine-node-red-install.sh │ ├── alpine-ntfy-install.sh │ ├── alpine-postgresql-install.sh │ ├── alpine-prometheus-install.sh │ ├── alpine-rclone-install.sh │ ├── alpine-redis-install.sh │ ├── alpine-redlib-install.sh │ ├── alpine-rustdeskserver-install.sh │ ├── alpine-rustypaste-install.sh │ ├── alpine-syncthing-install.sh │ ├── alpine-teamspeak-server-install.sh │ ├── alpine-tinyauth-install.sh │ ├── alpine-traefik-install.sh │ ├── alpine-transmission-install.sh │ ├── alpine-valkey-install.sh │ ├── alpine-vaultwarden-install.sh │ ├── alpine-wakapi-install.sh │ ├── alpine-wireguard-install.sh │ ├── alpine-zigbee2mqtt-install.sh │ ├── ampache-install.sh │ ├── anchor-install.sh │ ├── anytype-server-install.sh │ ├── apache-cassandra-install.sh │ ├── apache-couchdb-install.sh │ ├── apache-guacamole-install.sh │ ├── apache-tika-install.sh │ ├── apache-tomcat-install.sh │ ├── apprise-api-install.sh │ ├── apt-cacher-ng-install.sh │ ├── archivebox-install.sh │ ├── argus-install.sh │ ├── aria2-install.sh │ ├── asterisk-install.sh │ ├── audiobookshelf-install.sh │ ├── authelia-install.sh │ ├── autobrr-install.sh │ ├── autocaliweb-install.sh │ ├── babybuddy-install.sh │ ├── backrest-install.sh │ ├── baikal-install.sh │ ├── bambuddy-install.sh │ ├── bar-assistant-install.sh │ ├── bazarr-install.sh │ ├── bentopdf-install.sh │ ├── beszel-install.sh │ ├── bichon-install.sh │ ├── birdnet-go-install.sh │ ├── bitmagnet-install.sh │ ├── blocky-install.sh │ ├── bookstack-install.sh │ ├── bunkerweb-install.sh │ ├── byparr-install.sh │ ├── bytestash-install.sh │ ├── caddy-install.sh │ ├── calibre-web-install.sh │ ├── casaos-install.sh │ ├── changedetection-install.sh │ ├── channels-install.sh │ ├── checkmate-install.sh │ ├── checkmk-install.sh │ ├── cleanuparr-install.sh │ ├── cloudflare-ddns-install.sh │ ├── cloudflared-install.sh │ ├── cloudreve-install.sh │ ├── cockpit-install.sh │ ├── comfyui-install.sh │ ├── commafeed-install.sh │ ├── configarr-install.sh │ ├── convertx-install.sh │ ├── coredns-install.sh │ ├── cosmos-install.sh │ ├── crafty-controller-install.sh │ ├── cronicle-install.sh │ ├── cross-seed-install.sh │ ├── cryptpad-install.sh │ ├── dagu-install.sh │ ├── dashy-install.sh │ ├── databasus-install.sh │ ├── dawarich-install.sh │ ├── ddclient-install.sh │ ├── debian-install.sh │ ├── deconz-install.sh │ ├── deluge-install.sh │ ├── discopanel-install.sh │ ├── dispatcharr-install.sh │ ├── docker-install.sh │ ├── docmost-install.sh │ ├── dolibarr-install.sh │ ├── domain-locker-install.sh │ ├── domain-monitor-install.sh │ ├── donetick-install.sh │ ├── dotnetaspwebapi-install.sh │ ├── drawdb-install.sh │ ├── drawio-install.sh │ ├── duplicati-install.sh │ ├── ebusd-install.sh │ ├── elementsynapse-install.sh │ ├── emby-install.sh │ ├── emqx-install.sh │ ├── endurain-install.sh │ ├── erpnext-install.sh │ ├── ersatztv-install.sh │ ├── esphome-install.sh │ ├── evcc-install.sh │ ├── excalidraw-install.sh │ ├── fhem-install.sh │ ├── fileflows-install.sh │ ├── firefly-install.sh │ ├── fireshare-install.sh │ ├── fladder-install.sh │ ├── flaresolverr-install.sh │ ├── flatnotes-install.sh │ ├── flowiseai-install.sh │ ├── fluid-calendar-install.sh │ ├── foldergram-install.sh │ ├── forgejo-install.sh │ ├── freepbx-install.sh │ ├── freshrss-install.sh │ ├── frigate-install.sh │ ├── fumadocs-install.sh │ ├── garage-install.sh │ ├── gatus-install.sh │ ├── geopulse-install.sh │ ├── ghost-install.sh │ ├── ghostfolio-install.sh │ ├── gitea-install.sh │ ├── gitea-mirror-install.sh │ ├── github-runner-install.sh │ ├── glance-install.sh │ ├── globaleaks-install.sh │ ├── glpi-install.sh │ ├── gluetun-install.sh │ ├── go2rtc-install.sh │ ├── gogs-install.sh │ ├── gokapi-install.sh │ ├── gotify-install.sh │ ├── grafana-install.sh │ ├── gramps-web-install.sh │ ├── graylog-install.sh │ ├── grist-install.sh │ ├── grocy-install.sh │ ├── guardian-install.sh │ ├── gwn-manager-install.sh │ ├── headscale-install.sh │ ├── healthchecks-install.sh │ ├── heimdall-dashboard-install.sh │ ├── hev-socks5-server-install.sh │ ├── hivemq-install.sh │ ├── homarr-install.sh │ ├── homeassistant-install.sh │ ├── homebox-install.sh │ ├── homebridge-install.sh │ ├── homelable-install.sh │ ├── homepage-install.sh │ ├── homer-install.sh │ ├── hoodik-install.sh │ ├── hortusfox-install.sh │ ├── hyperhdr-install.sh │ ├── hyperion-install.sh │ ├── igotify-install.sh │ ├── immich-install.sh │ ├── immichframe-install.sh │ ├── infisical-install.sh │ ├── influxdb-install.sh │ ├── inspircd-install.sh │ ├── inventree-install.sh │ ├── investbrain-install.sh │ ├── invoiceninja-install.sh │ ├── iobroker-install.sh │ ├── ironclaw-install.sh │ ├── isponsorblocktv-install.sh │ ├── itsm-ng-install.sh │ ├── jackett-install.sh │ ├── jeedom-install.sh │ ├── jellyfin-install.sh │ ├── jenkins-install.sh │ ├── jitsi-meet-install.sh │ ├── joplin-server-install.sh │ ├── jotty-install.sh │ ├── jupyternotebook-install.sh │ ├── kapowarr-install.sh │ ├── karakeep-install.sh │ ├── kasm-install.sh │ ├── kavita-install.sh │ ├── keycloak-install.sh │ ├── kima-hub-install.sh │ ├── kimai-install.sh │ ├── kitchenowl-install.sh │ ├── koel-install.sh │ ├── koillection-install.sh │ ├── kometa-install.sh │ ├── komga-install.sh │ ├── kubo-install.sh │ ├── kutt-install.sh │ ├── languagetool-install.sh │ ├── lazylibrarian-install.sh │ ├── leantime-install.sh │ ├── librechat-install.sh │ ├── librenms-install.sh │ ├── librespeed-rust-install.sh │ ├── libretranslate-install.sh │ ├── lidarr-install.sh │ ├── limesurvey-install.sh │ ├── linkding-install.sh │ ├── linkstack-install.sh │ ├── linkwarden-install.sh │ ├── listmonk-install.sh │ ├── livebook-install.sh │ ├── lldap-install.sh │ ├── loki-install.sh │ ├── lubelogger-install.sh │ ├── lychee-install.sh │ ├── lyrionmusicserver-install.sh │ ├── mafl-install.sh │ ├── magicmirror-install.sh │ ├── mail-archiver-install.sh │ ├── managemydamnlife-install.sh │ ├── manyfold-install.sh │ ├── mariadb-install.sh │ ├── matomo-install.sh │ ├── matter-server-install.sh │ ├── matterbridge-install.sh │ ├── mattermost-install.sh │ ├── mealie-install.sh │ ├── mediamanager-install.sh │ ├── mediamtx-install.sh │ ├── medusa-install.sh │ ├── meilisearch-install.sh │ ├── memos-install.sh │ ├── meshcentral-install.sh │ ├── metabase-install.sh │ ├── metube-install.sh │ ├── minarca-install.sh │ ├── mini-qr-install.sh │ ├── miniflux-install.sh │ ├── minio-install.sh │ ├── minthcm-install.sh │ ├── mongodb-install.sh │ ├── monica-install.sh │ ├── motioneye-install.sh │ ├── mqtt-install.sh │ ├── myip-install.sh │ ├── mylar3-install.sh │ ├── myspeed-install.sh │ ├── mysql-install.sh │ ├── n8n-install.sh │ ├── nagios-install.sh │ ├── nametag-install.sh │ ├── navidrome-install.sh │ ├── neko-install.sh │ ├── neo4j-install.sh │ ├── netbird-install.sh │ ├── netboot-xyz-install.sh │ ├── netbox-install.sh │ ├── nextcloudpi-install.sh │ ├── nextexplorer-install.sh │ ├── nextpvr-install.sh │ ├── nginx-ui-install.sh │ ├── nginxproxymanager-install.sh │ ├── nightscout-install.sh │ ├── nocodb-install.sh │ ├── node-red-install.sh │ ├── nodebb-install.sh │ ├── nodecast-tv-install.sh │ ├── notifiarr-install.sh │ ├── npmplus-install.sh │ ├── ntfy-install.sh │ ├── nxwitness-install.sh │ ├── nzbget-install.sh │ ├── oauth2-proxy-install.sh │ ├── octoprint-install.sh │ ├── odoo-install.sh │ ├── ollama-install.sh │ ├── omada-install.sh │ ├── ombi-install.sh │ ├── omv-install.sh │ ├── onedev-install.sh │ ├── onlyoffice-install.sh │ ├── open-archiver-install.sh │ ├── opencloud-install.sh │ ├── opengist-install.sh │ ├── openhab-install.sh │ ├── openobserve-install.sh │ ├── openproject-install.sh │ ├── openthread-br-install.sh │ ├── openwebui-install.sh │ ├── openziti-controller-install.sh │ ├── openziti-tunnel-install.sh │ ├── ots-install.sh │ ├── outline-install.sh │ ├── owncast-install.sh │ ├── ownfoil-install.sh │ ├── pairdrop-install.sh │ ├── pangolin-install.sh │ ├── paperless-ai-install.sh │ ├── paperless-gpt-install.sh │ ├── paperless-ngx-install.sh │ ├── papra-install.sh │ ├── part-db-install.sh │ ├── passbolt-install.sh │ ├── patchmon-install.sh │ ├── paymenter-install.sh │ ├── peanut-install.sh │ ├── pelican-panel-install.sh │ ├── pelican-wings-install.sh │ ├── photoprism-install.sh │ ├── pialert-install.sh │ ├── pihole-install.sh │ ├── planka-install.sh │ ├── plant-it-install.sh │ ├── plex-install.sh │ ├── pocketbase-install.sh │ ├── pocketid-install.sh │ ├── podman-homeassistant-install.sh │ ├── podman-install.sh │ ├── postgresql-install.sh │ ├── powerdns-install.sh │ ├── privatebin-install.sh │ ├── profilarr-install.sh │ ├── projectsend-install.sh │ ├── prometheus-alertmanager-install.sh │ ├── prometheus-blackbox-exporter-install.sh │ ├── prometheus-install.sh │ ├── prometheus-pve-exporter-install.sh │ ├── protonmail-bridge-install.sh │ ├── prowlarr-install.sh │ ├── proxmox-backup-server-install.sh │ ├── proxmox-datacenter-manager-install.sh │ ├── proxmox-mail-gateway-install.sh │ ├── ps5-mqtt-install.sh │ ├── pterodactyl-panel-install.sh │ ├── pterodactyl-wings-install.sh │ ├── pulse-install.sh │ ├── pve-scripts-local-install.sh │ ├── qbittorrent-install.sh │ ├── qdrant-install.sh │ ├── qui-install.sh │ ├── rabbitmq-install.sh │ ├── radarr-install.sh │ ├── radicale-install.sh │ ├── rclone-install.sh │ ├── rdtclient-install.sh │ ├── reactive-resume-install.sh │ ├── readarr-install.sh │ ├── readeck-install.sh │ ├── recyclarr-install.sh │ ├── redis-install.sh │ ├── reitti-install.sh │ ├── resiliosync-install.sh │ ├── revealjs-install.sh │ ├── romm-install.sh │ ├── rustdeskserver-install.sh │ ├── rustypaste-install.sh │ ├── sabnzbd-install.sh │ ├── salt-install.sh │ ├── scanopy-install.sh │ ├── scraparr-install.sh │ ├── scrypted-install.sh │ ├── searxng-install.sh │ ├── seaweedfs-install.sh │ ├── seelf-install.sh │ ├── seerr-install.sh │ ├── semaphore-install.sh │ ├── sftpgo-install.sh │ ├── shelfmark-install.sh │ ├── shinobi-install.sh │ ├── shlink-install.sh │ ├── signoz-install.sh │ ├── silverbullet-install.sh │ ├── slskd-install.sh │ ├── smokeping-install.sh │ ├── snipeit-install.sh │ ├── snowshare-install.sh │ ├── solidtime-install.sh │ ├── sonarqube-install.sh │ ├── sonarr-install.sh │ ├── sonobarr-install.sh │ ├── soulsync-install.sh │ ├── sparkyfitness-install.sh │ ├── speedtest-tracker-install.sh │ ├── split-pro-install.sh │ ├── splunk-enterprise-install.sh │ ├── spoolman-install.sh │ ├── sportarr-install.sh │ ├── sqlserver2022-install.sh │ ├── sqlserver2025-install.sh │ ├── step-ca-install.sh │ ├── stirling-pdf-install.sh │ ├── storybook-install.sh │ ├── storyteller-install.sh │ ├── strapi-install.sh │ ├── streamlink-webui-install.sh │ ├── stylus-install.sh │ ├── sure-install.sh │ ├── swizzin-install.sh │ ├── syncthing-install.sh │ ├── tandoor-install.sh │ ├── tasmoadmin-install.sh │ ├── tasmocompiler-install.sh │ ├── tautulli-install.sh │ ├── tdarr-install.sh │ ├── teable-install.sh │ ├── teamspeak-server-install.sh │ ├── technitiumdns-install.sh │ ├── teddycloud-install.sh │ ├── telegraf-install.sh │ ├── teleport-install.sh │ ├── termix-install.sh │ ├── the-lounge-install.sh │ ├── thingsboard-install.sh │ ├── threadfin-install.sh │ ├── tianji-install.sh │ ├── tinyauth-install.sh │ ├── traccar-install.sh │ ├── tracearr-install.sh │ ├── tracktor-install.sh │ ├── traefik-install.sh │ ├── transmission-install.sh │ ├── transmute-install.sh │ ├── trek-install.sh │ ├── trilium-install.sh │ ├── trip-install.sh │ ├── tubearchivist-install.sh │ ├── tududi-install.sh │ ├── tunarr-install.sh │ ├── twingate-connector-install.sh │ ├── ubuntu-install.sh │ ├── uhf-install.sh │ ├── umami-install.sh │ ├── umlautadaptarr-install.sh │ ├── unbound-install.sh │ ├── unifi-os-server-install.sh │ ├── unmanic-install.sh │ ├── upgopher-install.sh │ ├── upsnap-install.sh │ ├── uptimekuma-install.sh │ ├── urbackupserver-install.sh │ ├── valkey-install.sh │ ├── vaultwarden-install.sh │ ├── verdaccio-install.sh │ ├── versitygw-install.sh │ ├── victoriametrics-install.sh │ ├── vikunja-install.sh │ ├── wallabag-install.sh │ ├── wallos-install.sh │ ├── wanderer-install.sh │ ├── warracker-install.sh │ ├── wastebin-install.sh │ ├── watcharr-install.sh │ ├── watchyourlan-install.sh │ ├── wavelog-install.sh │ ├── wazuh-install.sh │ ├── wealthfolio-install.sh │ ├── web-check-install.sh │ ├── wger-install.sh │ ├── whisparr-install.sh │ ├── whodb-install.sh │ ├── wikijs-install.sh │ ├── wireguard-install.sh │ ├── wishlist-install.sh │ ├── wizarr-install.sh │ ├── wordpress-install.sh │ ├── writefreely-install.sh │ ├── yamtrack-install.sh │ ├── yourls-install.sh │ ├── yt-dlp-webui-install.sh │ ├── yubal-install.sh │ ├── yunohost-install.sh │ ├── zabbix-install.sh │ ├── zammad-install.sh │ ├── zerobyte-install.sh │ ├── zerotier-one-install.sh │ ├── zigbee2mqtt-install.sh │ ├── zipline-install.sh │ ├── zitadel-install.sh │ ├── zoraxy-install.sh │ ├── zot-registry-install.sh │ └── zwave-js-ui-install.sh ├── misc/ │ ├── add-beszel-agent-lxc.sh │ ├── alpine-install.func │ ├── alpine-tools.func │ ├── api.func │ ├── auto-build.func │ ├── build.func │ ├── cloud-init.func │ ├── core.func │ ├── error_handler.func │ ├── install.func │ ├── test-automation.sh │ ├── test-build.func │ ├── tools.func │ └── vm-core.func ├── tools/ │ ├── addon/ │ │ ├── add-netbird-lxc.sh │ │ ├── add-tailscale-lxc.sh │ │ ├── adguardhome-sync.sh │ │ ├── all-templates.sh │ │ ├── arcane.sh │ │ ├── coder-code-server.sh │ │ ├── coolify.sh │ │ ├── copyparty.sh │ │ ├── cronmaster.sh │ │ ├── crowdsec.sh │ │ ├── dockge.sh │ │ ├── dokploy.sh │ │ ├── filebrowser-quantum.sh │ │ ├── filebrowser.sh │ │ ├── glances.sh │ │ ├── homebrew.sh │ │ ├── immich-public-proxy.sh │ │ ├── jellystat.sh │ │ ├── komodo.sh │ │ ├── netdata.sh │ │ ├── nextcloud-exporter.sh │ │ ├── olivetin.sh │ │ ├── phpmyadmin.sh │ │ ├── pihole-exporter.sh │ │ ├── prometheus-paperless-ngx-exporter.sh │ │ ├── pyenv.sh │ │ ├── qbittorrent-exporter.sh │ │ ├── runtipi.sh │ │ ├── sparkyfitness-garmin.sh │ │ └── webmin.sh │ ├── copy-data/ │ │ ├── README.md │ │ ├── home-assistant-container-copy-data-home-assistant-container.sh │ │ ├── home-assistant-container-copy-data-home-assistant-core.sh │ │ ├── home-assistant-container-copy-data-podman-home-assistant.sh │ │ ├── home-assistant-core-copy-data-home-assistant-container.sh │ │ ├── home-assistant-core-copy-data-home-assistant-core.sh │ │ ├── plex-copy-data-plex.sh │ │ ├── podman-home-assistant-copy-data-home-assistant-container.sh │ │ ├── z2m-copy-data-z2m.sh │ │ └── zwavejs2mqtt-copy-data-zwavejsui.sh │ ├── headers/ │ │ ├── add-iptag │ │ ├── adguardhome-sync │ │ ├── arcane │ │ ├── coder-code-server │ │ ├── container-restore-from-backup │ │ ├── coolify │ │ ├── copyparty │ │ ├── core-restore-from-backup │ │ ├── cronmaster │ │ ├── crowdsec │ │ ├── dockge │ │ ├── dokploy │ │ ├── filebrowser │ │ ├── filebrowser-quantum │ │ ├── glances │ │ ├── homebrew │ │ ├── immich-public-proxy │ │ ├── jellystat │ │ ├── komodo │ │ ├── nextcloud-exporter │ │ ├── olivetin │ │ ├── phpmyadmin │ │ ├── pihole-exporter │ │ ├── prometheus-paperless-ngx-exporter │ │ ├── pve-privilege-converter │ │ ├── qbittorrent-exporter │ │ ├── runtipi │ │ └── sparkyfitness-garmin │ ├── maintenance.py │ └── pve/ │ ├── add-iptag.sh │ ├── clean-lxcs.sh │ ├── clean-orphaned-lvm.sh │ ├── container-restore-from-backup.sh │ ├── core-restore-from-backup.sh │ ├── cron-update-lxcs.sh │ ├── execute.sh │ ├── frigate-support.sh │ ├── fstrim.sh │ ├── host-backup.sh │ ├── hw-acceleration.sh │ ├── kernel-clean.sh │ ├── kernel-pin.sh │ ├── lxc-delete.sh │ ├── microcode.sh │ ├── monitor-all.sh │ ├── nic-offloading-fix.sh │ ├── pbs-microcode.sh │ ├── pbs3-upgrade.sh │ ├── pbs4-upgrade.sh │ ├── post-install-hook-examples.sh │ ├── post-pbs-install.sh │ ├── post-pmg-install.sh │ ├── post-pve-install.sh │ ├── pve-privilege-converter.sh │ ├── pve8-upgrade.sh │ ├── scaling-governor.sh │ ├── update-apps.sh │ ├── update-lxcs-cron.sh │ ├── update-lxcs.sh │ ├── update-repo.sh │ └── usb-passthrough.sh ├── turnkey/ │ └── turnkey.sh └── vm/ ├── archlinux-vm.sh ├── debian-13-vm.sh ├── debian-vm.sh ├── docker-vm.sh ├── haos-vm.sh ├── headers/ │ ├── docker-vm │ └── owncloud-vm ├── mikrotik-routeros.sh ├── nextcloud-vm.sh ├── openwrt-vm.sh ├── opnsense-vm.sh ├── owncloud-vm.sh ├── pimox-haos-vm.sh ├── truenas-vm.sh ├── ubuntu2204-vm.sh ├── ubuntu2404-vm.sh ├── ubuntu2504-vm.sh └── umbrel-os-vm.sh ================================================ FILE CONTENTS ================================================ ================================================ FILE: .editorconfig ================================================ ; editorconfig.org root = true [*] charset = utf-8 continuation_indent_size = 2 end_of_line = lf indent_size = 2 indent_style = space insert_final_newline = true max_line_length = 120 tab_width = 2 ; trim_trailing_whitespace = true ; disabled until files are cleaned up [*.md] trim_trailing_whitespace = false ================================================ FILE: .gitattributes ================================================ # --------------------------------------- # Treat Shell files as first-class code # --------------------------------------- *.sh linguist-detectable=true *.bash linguist-language=Shell *.func linguist-language=Shell *.func linguist-detectable=true *.install linguist-language=Shell # --------------------------------------- # Exclude header art from stats # --------------------------------------- ct/headers/* linguist-documentation # --------------------------------------- # Exclude documentation from stats # --------------------------------------- *.md linguist-documentation README.md linguist-documentation CONTRIBUTING.md linguist-documentation SECURITY.md linguist-documentation # --------------------------------------- # Exclude generated/config files # --------------------------------------- .github/** linguist-generated .vscode/** linguist-generated # --------------------------------------- # Standard text handling # --------------------------------------- * text=auto eol=lf ================================================ FILE: .github/CODEOWNERS ================================================ # # CODEOWNERS for ProxmoxVE # # Order is important; the last matching pattern takes the most # precedence. # Codeowners for specific folders and files # Remember ending folders with / # Set default reviewers * @asylumexp ================================================ FILE: .github/DISCUSSION_TEMPLATE/request-script.yml ================================================ title: "[Script request]: " labels: ["enhancement"] body: - type: input attributes: label: Application Name description: Enter the application name. placeholder: "e.g., Home Assistant" validations: required: true - type: input attributes: label: Website description: Official website or github page. placeholder: "e.g., https://www.home-assistant.io/" validations: required: true - type: textarea attributes: label: Description description: Explain what the application does and why it should be added to Proxmox VE Helper-Scripts. placeholder: "e.g., Home Assistant is a popular open-source platform that brings all your smart home devices together in one place. Adding it to Proxmox VE Helper-Scripts would make setup and management on Proxmox easy, letting users quickly get a powerful, self-hosted smart home system up and running." validations: required: true - type: checkboxes attributes: label: Due Diligence options: - label: "I have searched existing [scripts](https://community-scripts.github.io/Proxmox/scripts) and found no duplicates." required: true - label: "I have searched existing [discussions](https://github.com/community-scripts/ProxmoxVE/discussions?discussions_q=) and found no duplicate requests." required: true - label: "The application requested has 600+ stars on Github (if applicable), is older than 6 months, actively maintained and has release tarballs published." required: true - label: "I understand that not all applications will be accepted due to various reasons and criteria by the community-scripts ORG." required: true - type: markdown attributes: value: "Thanks for submitting your request! The team will review it and reach out if we need more information." ================================================ FILE: .github/FUNDING.yml ================================================ ko_fi: community_scripts github: community_scripts ================================================ FILE: .github/ISSUE_TEMPLATE/bug_report.yml ================================================ name: "🐞 Script Issue Report" description: Report a specific issue with a script. For other inquiries, please use the Discussions section. labels: ["bug"] body: - type: markdown attributes: value: | # 🐞 **Script Issue Report** Thank you for taking the time to report an issue! Please provide as much detail as possible to help us address the problem efficiently. ## ⚠️ **IMPORTANT - READ FIRST** - 🔍 **Search first:** Before submitting, check if the issue has already been reported or resolved in [closed issues](https://github.com/community-scripts/ProxmoxVE/issues?q=is%3Aissue+is%3Aclosed). If found, comment on that issue instead of creating a new one. Alternatively, check the **[Discussions](https://github.com/community-scripts/ProxmoxVE/discussions)** under the *"Announcement"* or *"Guide"* categories for relevant information. - 🛠️ **Supported environments only:** Ensure you are using a default Linux distribution. Custom setups may not be supported. - 🔎 If you encounter `[ERROR] in line 23: exit code *: while executing command "$@" > /dev/null 2>&1`, rerun the script with verbose mode before submitting the issue. - 💡 For general questions, feature requests, or suggestions, use the [Discussions section](https://github.com/community-scripts/ProxmoxVE/discussions). - type: input id: guidelines attributes: label: ✅ Have you read and understood the above guidelines? placeholder: "yes" validations: required: true - type: dropdown id: verbose_run attributes: label: 🔎 Did you run the script with verbose mode enabled? description: "Required for debugging any script issue. A verbose log is mandatory." options: - "" - "Yes, verbose mode was enabled and the output is included below" - "No (this issue will likely be closed automatically)" validations: required: true - type: input id: script_name attributes: label: 📜 What is the name of the script you are using? placeholder: "e.g., NextcloudPi, Zigbee2MQTT" validations: required: true - type: input id: script_command attributes: label: 📂 What was the exact command used to execute the script? placeholder: "e.g., bash -c \"$(wget -qLO - https://github.com/asylumexp/Proxmox/raw/main/ct/zigbee2mqtt.sh)\" or \"update\"" validations: required: true - type: textarea id: issue_description attributes: label: 📝 Provide a clear and concise description of the issue. validations: required: true - type: checkboxes validations: required: true attributes: label: ⚙️ What settings are you using? options: - label: Default Settings - label: Advanced Settings - type: markdown attributes: value: "💡 **Tip:** If you are using Advanced Settings, please test with Default Settings before submitting an issue." - type: dropdown id: linux_distribution attributes: label: 🖥️ Which Linux distribution are you using? options: - - Alpine - Debian 11 - Debian 12 - Debian 13 - Ubuntu 22.04 - Ubuntu 24.04 - Ubuntu 24.10 validations: required: true - type: input id: pve_version attributes: label: 📈 Which Proxmox version are you on? placeholder: "run pveversion in your PVE node console" validations: required: true - type: textarea id: steps_to_reproduce attributes: label: 🔄 Steps to reproduce the issue. placeholder: "e.g., Step 1: ..., Step 2: ..." validations: required: true - type: textarea id: error_output attributes: label: ❌ Paste the full error output (if available). placeholder: "Include any relevant logs or error messages." validations: required: true - type: textarea id: additional_context attributes: label: 🖼️ Additional context (optional). placeholder: "Include screenshots, code blocks (use triple backticks ```), or any other relevant information." validations: required: false ================================================ FILE: .github/ISSUE_TEMPLATE/config.yml ================================================ blank_issues_enabled: false contact_links: - name: 🌟 new Script request url: https://github.com/community-scripts/ProxmoxVE/discussions/new?category=request-script about: For feature/script requests, please use the Discussions section. - name: 🤔 Questions and Help url: https://github.com/community-scripts/ProxmoxVE/discussions about: For suggestions or questions, please use the Discussions section. - name: 💻 Discord url: https://discord.gg/UHrpNWGwkH about: Join our Discord server to chat with other users in the Proxmox Helper Scripts community. ================================================ FILE: .github/ISSUE_TEMPLATE/feature_request.yml ================================================ name: "✨ Feature Request" description: "Suggest a new feature or enhancement. (not for script requests)" labels: ["enhancement"] body: - type: markdown attributes: value: | # ✨ **Feature Request** Have an idea for a new feature? Share your thoughts below! - type: input id: feature_summary attributes: label: "🌟 Briefly describe the feature" placeholder: "e.g., Add support for XYZ" validations: required: true - type: textarea id: feature_description attributes: label: "📝 Detailed description" placeholder: "Explain the feature in detail" validations: required: true - type: textarea id: use_case attributes: label: "💡 Why is this useful?" placeholder: "Describe the benefit of this feature" validations: required: true ================================================ FILE: .github/ISSUE_TEMPLATE/frontend_report.yml ================================================ name: "🌐 Website Issue Report" description: Report an issue, an optimization request or an documentation issue specifically related to the website. labels: "website" body: - type: markdown attributes: value: | **IMPORTANT:** Failure to comply with the following guidelines may result in immediate closure. - Prior to submitting, kindly search the closed issues to check if the problem you are reporting has already been addressed and resolved. If you come across a closed issue that pertains to your problem, please leave a comment on that issue instead of creating a new one. - If the problem is related to a bug in the website, kindly check for browser compatibility and ensure the issue occurs in multiple browsers before submitting. - For suggestions, questions, or feature requests, please use the [Discussions section.](https://github.com/community-scripts/ProxmoxVE/discussions) - type: input id: guidelines attributes: label: Please verify that you have read and understood the guidelines. placeholder: 'yes' validations: required: true - type: dropdown id: issue_type validations: required: true attributes: label: What type of issue is this? options: - - Bug - Optimization - Documentation - Other - type: textarea id: bug_description attributes: label: A clear and concise description of the issue. validations: required: true - type: dropdown id: browser validations: required: true attributes: label: Which browser are you using? options: - - Chrome - Firefox - Safari - Edge - Other - type: markdown attributes: value: | **If the issue is browser-related**, please provide information on the version and platform (Windows, MacOS, Linux, etc.). - type: textarea id: screenshot attributes: label: If relevant, including screenshots or a code block can be helpful in clarifying the issue. placeholder: "Code blocks begin and conclude by enclosing the code with three backticks (```) above and below it." validations: required: false - type: textarea id: reproduce attributes: label: Please provide detailed steps to reproduce the issue. placeholder: "First do this, then this ..." validations: required: false ================================================ FILE: .github/ISSUE_TEMPLATE/task.yml ================================================ name: "🛠️ Task / General Request" description: "Request a general task, improvement, or refactor." labels: ["task"] body: - type: markdown attributes: value: | # 🛠️ **Task / General Request** Request a task that isn't a bug or feature request. - type: input id: task_summary attributes: label: "📌 Task summary" placeholder: "e.g., Refactor XYZ" validations: required: true - type: textarea id: task_details attributes: label: "📋 Task details" placeholder: "Explain what needs to be done" validations: required: true ================================================ FILE: .github/autolabeler-config.json ================================================ { "new script": [ { "fileStatus": "added", "includeGlobs": [ "ct/**", "install/**", "turnkey/**", "vm/**" ], "excludeGlobs": [] } ], "update script": [ { "fileStatus": "modified", "includeGlobs": [ "ct/**", "install/**", "turnkey/**", "vm/**" ], "excludeGlobs": [] } ], "delete script": [ { "fileStatus": "removed", "includeGlobs": [ "ct/**", "install/**", "turnkey/**", "vm/**" ], "excludeGlobs": [] } ], "vm": [ { "fileStatus": null, "includeGlobs": [ "vm/**" ], "excludeGlobs": [] } ], "tools": [ { "fileStatus": null, "includeGlobs": [ "tools/**" ], "excludeGlobs": [] } ], "addon": [ { "fileStatus": null, "includeGlobs": [ "tools/addon/**" ], "excludeGlobs": [] } ], "pve-tool": [ { "fileStatus": null, "includeGlobs": [ "tools/pve/**" ], "excludeGlobs": [] } ], "core": [ { "fileStatus": null, "includeGlobs": [ "misc/*.func" ], "excludeGlobs": [ "misc/api.func" ] } ], "documentation": [ { "fileStatus": null, "includeGlobs": [ "docs/**" ], "excludeGlobs": [] } ], "github": [ { "fileStatus": null, "includeGlobs": [ ".github/**", "README.md", "SECURITY.md", "LICENSE", "CHANGELOG.md" ], "excludeGlobs": [] } ], "api": [ { "fileStatus": null, "includeGlobs": [ "api/**", "misc/api.func" ], "excludeGlobs": [] } ], "website": [ { "fileStatus": null, "includeGlobs": [ "frontend/**" ], "excludeGlobs": [ "frontend/public/json/**" ] } ], "json": [ { "fileStatus": "modified", "includeGlobs": [ "frontend/public/json/**" ], "excludeGlobs": [] } ] } ================================================ FILE: .github/changelog-pr-config.json ================================================ [ { "title": "🆕 New Scripts", "labels": [ "new script" ] }, { "title": "🚀 Updated Scripts", "labels": [ "update script" ], "subCategories": [ { "title": "🐞 Bug Fixes", "labels": [ "bugfix" ], "notes": [] }, { "title": "✨ New Features", "labels": [ "feature" ], "notes": [] }, { "title": "💥 Breaking Changes", "labels": [ "breaking change" ], "notes": [] }, { "title": "🔧 Refactor", "labels": [ "refactor" ], "notes": [] } ] }, { "title": "🗑️ Deleted Scripts", "labels": [ "delete script" ] }, { "title": "💾 Core", "labels": [ "core" ], "subCategories": [ { "title": "🐞 Bug Fixes", "labels": [ "bugfix" ], "notes": [] }, { "title": "✨ New Features", "labels": [ "feature" ], "notes": [] }, { "title": "💥 Breaking Changes", "labels": [ "breaking change" ], "notes": [] }, { "title": "🔧 Refactor", "labels": [ "refactor" ], "notes": [] } ] }, { "title": "🧰 Tools", "labels": [ "tools" ], "subCategories": [ { "title": "🐞 Bug Fixes", "labels": [ "bugfix" ], "notes": [] }, { "title": "✨ New Features", "labels": [ "feature" ], "notes": [] }, { "title": "💥 Breaking Changes", "labels": [ "breaking change" ], "notes": [] }, { "title": "🔧 Refactor", "labels": [ "refactor" ], "notes": [] } ] }, { "title": "📚 Documentation", "labels": [ "documentation" ] }, { "title": "📂 Github", "labels": [ "github" ] }, { "title": "📡 API", "labels": [ "api" ], "subCategories": [ { "title": "🐞 Bug Fixes", "labels": [ "bugfix" ], "notes": [] }, { "title": "✨ New Features", "labels": [ "feature" ], "notes": [] }, { "title": "💥 Breaking Changes", "labels": [ "breaking change" ], "notes": [] }, { "title": "🔧 Refactor", "labels": [ "refactor" ], "notes": [] } ] }, { "title": "🌐 Website", "labels": [ "website" ], "subCategories": [ { "title": "🐞 Bug Fixes", "labels": [ "bugfix" ], "notes": [] }, { "title": "✨ New Features", "labels": [ "feature" ], "notes": [] }, { "title": "💥 Breaking Changes", "labels": [ "breaking change" ], "notes": [] }, { "title": "📝 Script Information", "labels": [ "json" ], "notes": [] } ] }, { "title": "❔ Uncategorized", "labels": [ "needs triage" ] } ] ================================================ FILE: .github/changelogs/2022/01.md ================================================ ## 2022-01-30 ### Changed - **Zigbee2MQTT LXC** - Clean up / Improve script - Improve documentation ## 2022-01-29 ### Changed - **Node-Red LXC** - Clean up / Improve script - Improve documentation ## 2022-01-25 ### Changed - **Jellyfin Media Server LXC** - new script ## 2022-01-24 ### Changed - **Plex Media Server LXC** - better Hardware Acceleration Support - `va-driver-all` is preinstalled - now using Ubuntu 21.10 - **misc** - new GUI script to copy data from one Plex Media Server LXC to another Plex Media Server LXC ## Initial Catch up - 2022-01-23 ### Changed - **Plex Media Server LXC** - add Hardware Acceleration Support - add script to install Intel Drivers - **Zwavejs2MQTT LXC** - new script to solve no auto start at boot - **Nginx Proxy Manager LXC** - new script to use Debian 11 - **Ubuntu 21.10 LXC** - new script - **Mariadb LXC** - add MariaDB Package Repository - **MQTT LXC** - add Eclipse Mosquitto Package Repository - **Home Assistant Container LXC** - change if ZFS filesystem is detected, execute automatic installation of static fuse-overlayfs - add script for easy Home Assistant update - **Home Assistant Container LXC (Podman)** - change if ZFS filesystem is detected, execute automatic installation of static fuse-overlayfs - **Home Assistant OS VM** - change disk type from SATA to SCSI to follow Proxmox official recommendations of choosing VirtIO-SCSI with SCSI disk - clean up - **Proxmox VE 7 Post Install** - new *No-Nag* method - **misc** - new GUI script to copy data from one Home Assistant LXC to another Home Assistant LXC - new GUI script to copy data from one Zigbee2MQTT LXC to another Zigbee2MQTT LXC ================================================ FILE: .github/changelogs/2022/02.md ================================================ ## 2022-02-28 ### Changed - **Vaultwarden LXC** - Add Update Script ## 2022-02-24 ### Changed - **Nginx Proxy Manager LXC** - New V2 Install Script ## 2022-02-23 ### Changed - **Adguard Home LXC** - New V2 Install Script - **Zigbee2MQTT LXC** - New V2 Install Script - **Home Assistant Container LXC** - Update Menu usability improvements ## 2022-02-22 ### Changed - **Home Assistant Container LXC** - New V2 Install Script - **Node-Red LXC** - New V2 Install Script - **Mariadb LXC** - New V2 Install Script - **MQTT LXC** - New V2 Install Script - **Debian 11 LXC** - New V2 Install Script - **Ubuntu 21.10 LXC** - New V2 Install Script ## 2022-02-20 ### Changed - **Home Assistant Container LXC** - New Script to migrate to the latest Update Menu ## 2022-02-19 ### Changed - **Nginx Proxy Manager LXC** - Add Update Script - **Vaultwarden LXC** - Make unattended install & Cleanup Script ## 2022-02-18 ### Changed - **Node-Red LXC** - Add Install Themes Script ## 2022-02-16 ### Changed - **Home Assistant Container LXC** - Add Options to Update Menu ## 2022-02-14 ### Changed - **Home Assistant Container LXC** - Add Update Menu ## 2022-02-13 ### Changed - **Mariadb LXC** - Add Adminer (formerly phpMinAdmin), a full-featured database management tool ## 2022-02-12 ### Changed - **Home Assistant Container LXC (Podman)** - Add Yacht web interface for managing Podman containers - new GUI script to copy data from a **Home Assistant LXC** to a **Podman Home Assistant LXC** - Improve documentation for several LXC's ## 2022-02-10 ### Changed - **GamUntu LXC** - New Script - **Jellyfin Media Server LXC** - new script to fix [start issue](https://github.com/tteck/Proxmox/issues/29#issue-1127457380) - **MotionEye NVR LXC** - New script ## 2022-02-09 ### Changed - **Zigbee2MQTT LXC** - added USB passthrough during installation (no extra script) - Improve documentation - **Zwavejs2MQTT LXC** - added USB passthrough during installation (no extra script) - **Jellyfin Media Server LXC** - Moved to testing due to issues. - Changed install method. - **Home Assistant Container LXC (Podman)** - add script for easy Home Assistant update ## 2022-02-06 ### Changed - **Debian 11 LXC** - Add Docker Support - **Ubuntu 21.10 LXC** - Add Docker Support ## 2022-02-05 ### Changed - **Vaultwarden LXC** - New script ## 2022-02-01 ### Changed - **All Scripts** - Fix issue where some networks were slow to assign a IP address to the container causing scripts to fail. ================================================ FILE: .github/changelogs/2022/03.md ================================================ ## 2022-03-28 ### Changed - **Docker LXC** - Add Docker Compose Option (@wovalle) ## 2022-03-27 ### Changed - **Heimdall Dashboard LXC** - New Update Script ## 2022-03-26 ### Changed - **UniFi Network Application LXC** - New Script V2 - **Omada Controller LXC** - New Script V2 ## 2022-03-25 ### Changed - **Proxmox CPU Scaling Governor** - New Script ## 2022-03-24 ### Changed - **Plex Media Server LXC** - Switch to Ubuntu 20.04 to support HDR tone mapping - **Docker LXC** - Add Portainer Option ## 2022-03-23 ### Changed - **Heimdall Dashboard LXC** - New Script V2 ## 2022-03-20 ### Changed - **Scripts** (V2) - ADD choose between Automatic or Manual DHCP ## 2022-03-18 ### Changed - **Technitium DNS LXC** - New Script V2 - **WireGuard LXC** - Add WGDashboard ## 2022-03-17 ### Changed - **Docker LXC** - New Script V2 ## 2022-03-16 ### Changed - **PhotoPrism LXC** - New Update/Branch Script ## 2022-03-15 ### Changed - **Dashy LXC** - New Update Script ## 2022-03-14 ### Changed - **Zwavejs2MQTT LXC** - New Update Script ## 2022-03-12 ### Changed - **PhotoPrism LXC** - New Script V2 ## 2022-03-11 ### Changed - **Vaultwarden LXC** - New V2 Install Script ## 2022-03-08 ### Changed - **Scripts** (V2) - Choose between Privileged or Unprivileged CT and Automatic or Password Login - **ESPHome LXC** - New V2 Install Script - **Zwavejs2MQTT LXC** - New V2 Install Script - **Motioneye LXC** - New V2 Install Script - **Pihole LXC** - New V2 Install Script - **GamUntu LXC** - New V2 Install Script ## 2022-03-06 ### Changed - **Zwavejs2MQTT LXC** - New GUI script to copy data from one Zwavejs2MQTT LXC to another Zwavejs2MQTT LXC ## 2022-03-05 ### Changed - **Homebridge LXC** - New Script V2 ## 2022-03-04 ### Changed - **Proxmox Kernel Clean** - New Script ## 2022-03-03 ### Changed - **WireGuard LXC** - New Script V2 ## 2022-03-02 ### Changed - **Proxmox LXC Updater** - New Script - **Dashy LXC** - New Script V2 - **Grafana LXC** - New Script V2 - **InfluxDB/Telegraf LXC** - New Script V2 ## 2022-03-01 ### Changed - **Daemon Sync Server LXC** - New Script V2 ================================================ FILE: .github/changelogs/2022/04.md ================================================ ## 2022-04-28 ### Changed - **v3 Script** - Remove Internet Check ## 2022-04-27 ### Changed - **Home Assistant OS VM** - ADD Option to set Bridge, VLAN and MAC Address - **v3 Script** - Improve Internet Check (prevent ‼ ERROR 4@57) ## 2022-04-26 ### Changed - **Home Assistant OS VM** - Fixed bad path - ADD Option to create VM using Latest or Stable image - **UniFi Network Application LXC** - ADD Local Controller Option ## 2022-04-25 ### Changed - **v3 Script** - Improve Error Handling ## 2022-04-23 ### Changed - **v3 Script** - ADD Internet Connection Check - **Proxmox VE 7 Post Install** - NEW v3 Script - **Proxmox Kernel Clean** - NEW v3 Script ## 2022-04-22 ### Changed - **Omada Controller LXC** - Update script to install version 5.1.7 - **Uptime Kuma LXC** - ADD Update script ## 2022-04-20 ### Changed - **Ubuntu LXC** - ADD option to install version 20.04 or 21.10 - **v3 Script** - ADD option to set Bridge ## 2022-04-19 ### Changed - **ALL LXC's** - New [V3 Install Script](https://github.com/tteck/Proxmox/issues/162) - **ioBroker LXC** - New Script V3 ## 2022-04-13 ### Changed - **Uptime Kuma LXC** - New Script V2 ## 2022-04-11 ### Changed - **Proxmox LXC Updater** - ADD option to skip stopped containers - **Proxmox VE 7 Post Install** - ADD PVE 7 check ## 2022-04-10 ### Changed - **Debian 11 LXC** - ADD An early look at the v3 install script ## 2022-04-09 ### Changed - **NocoDB LXC** - New Script V2 ## 2022-04-05 ### Changed - **MeshCentral LXC** - New Script V2 ## 2022-04-01 ### Changed - **Scripts** (V2) - FIX Pressing enter without making a selection first would cause an Error ================================================ FILE: .github/changelogs/2022/05.md ================================================ ## 2022-05-29 ### Changed - **Vaultwarden LXC** - Code refactoring - **CrowdSec** - NEW Script ## 2022-05-21 ### Changed - **Home Assistant OS VM** - Code refactoring ## 2022-05-19 ### Changed - **Keycloak LXC** - NEW Script ## 2022-05-18 ### Changed - **File Browser** - NEW Script ## 2022-05-13 ### Changed - **PostgreSQL LXC** - NEW Script ## 2022-05-10 ### Changed - **deCONZ LXC** - NEW Script ## 2022-05-07 ### Changed - **NocoDB LXC** - ADD update script ## 2022-05-06 ### Changed - **PhotoPrism LXC** - ADD GO Dependencies for full functionality ## 2022-05-05 ### Changed - **Ubuntu LXC** - ADD option to define version (18.04 20.04 21.10 22.04) ================================================ FILE: .github/changelogs/2022/06.md ================================================ ## 2022-06-30 ### Changed - **Prometheus LXC** - NEW Script ## 2022-06-06 ### Changed - **Whoogle LXC** - NEW Script ================================================ FILE: .github/changelogs/2022/07.md ================================================ ## 2022-07-26 ### Changed - **Home Assistant OS VM** - Set the real time clock (RTC) to local time. - Disable the USB tablet device (save resources / not needed). ## 2022-07-24 ### Changed - **Home Assistant OS VM** - Present the drive to the guest as a solid-state drive rather than a rotational hard disk. There is no requirement that the underlying storage actually be backed by SSD's. - When the VM’s filesystem marks blocks as unused after deleting files, the SCSI controller will relay this information to the storage, which will then shrink the disk image accordingly. - 👉 [more info](https://github.com/tteck/Proxmox/discussions/378) ## 2022-07-22 ### Changed - **n8n LXC** (thanks to @cyakimov) - NEW Script ## 2022-07-21 ### Changed - **grocy LXC** - NEW Script ## 2022-07-17 ### Changed - **Vaultwarden LXC** - NEW Vaultwarden Update (post 2022-05-29 installs only) Script - NEW Web-vault Update (any) Script ## 2022-07-14 ### Changed - **MagicMirror Server LXC** - NEW Script ## 2022-07-13 ### Changed - **Proxmox Edge Kernel Tool** - NEW Script ## 2022-07-11 ### Changed - **Home Assistant OS VM** - Supports lvmthin, zfspool, nfs, dir and btrfs storage types. ## 2022-07-08 ### Changed - **openHAB LXC** - NEW Script ## 2022-07-03 ### Changed - **Tailscale** - NEW Script ## 2022-07-01 ### Changed - **Home Assistant OS VM** - Allow different storage types (lvmthin, nfs, dir). ================================================ FILE: .github/changelogs/2022/08.md ================================================ ## 2022-08-31 ### Changed - **All LXC's** - Add Internet & DNS Check ## 2022-08-22 ### Changed - **Wiki.js LXC** - NEW Script - **Emby Media Server LXC** - NEW Script ## 2022-08-20 ### Changed - **Mikrotik RouterOS VM** - NEW Script ## 2022-08-19 ### Changed - **PhotoPrism LXC** - Fixed .env bug (Thanks @cklam2) ## 2022-08-13 ### Changed - **Home Assistant OS VM** - Option to create VM using Stable, Beta or Dev Image ## 2022-08-11 ### Changed - **Home Assistant OS VM** - Validate Storage ## 2022-08-04 ### Changed - **VS Code Server** - NEW Script ## 2022-08-02 ### Changed - **All LXC/VM** - v4 Script - Whiptail menu's ================================================ FILE: .github/changelogs/2022/09.md ================================================ ## 2022-09-29 ### Changed - **Home Assistant Container LXC** - If the LXC is created Privileged, the script will automatically set up USB passthrough. - **Home Assistant Core LXC** - NEW Script - **PiMox HAOS VM** - NEW Script ## 2022-09-23 ### Changed - **EMQX LXC** - NEW Script ## 2022-09-22 ### Changed - **NextCloudPi LXC** - NEW Script ## 2022-09-21 ### Changed - **Proxmox Backup Server Post Install** - NEW Script - **Z-wave JS UI LXC** - NEW Script (and all sub scripts 🤞) - **Zwave2MQTT LXC** - Bye Bye Script ## 2022-09-20 ### Changed - **OpenMediaVault LXC** - NEW Script ## 2022-09-16 ### Changed - **Paperless-ngx LXC** - NEW Script (Thanks @Donkeykong307) ## 2022-09-11 ### Changed - **Trilium LXC** - NEW Script ## 2022-09-10 ### Changed - **Syncthing LXC** - NEW Script ## 2022-09-09 ### Changed - **CasaOS LXC** - NEW Script - **Proxmox Kernel Clean** - Now works with Proxmox Backup Server ## 2022-09-08 ### Changed - **Navidrome LXC** - NEW Script - **Homepage LXC** - NEW Script ================================================ FILE: .github/changelogs/2022/10.md ================================================ ## 2022-10-27 ### Changed - **Container & Core Restore from Backup** - [NEW Scripts](https://github.com/tteck/Proxmox/discussions/674) ## 2022-10-07 ### Changed - **Home Assistant OS VM** - Add "Latest" Image ## 2022-10-05 ### Changed - **Umbrel LXC** - NEW Script (Docker) - **Blocky LXC** - NEW Script (Adblocker - DNS) ================================================ FILE: .github/changelogs/2022/11.md ================================================ ## 2022-11-27 ### Changed - **Shinobi LXC** - NEW Script ## 2022-11-24 ### Changed - **Home Assistant OS VM** - Add option to set machine type during VM creation (Advanced) ## 2022-11-23 ### Changed - **All LXC's** - Add option to enable root ssh access during LXC creation (Advanced) ## 2022-11-21 ### Changed - **Proxmox LXC Updater** - Now updates Ubuntu, Debian, Devuan, Alpine Linux, CentOS-Rocky-Alma, Fedora, ArchLinux [(@Uruknara)](https://github.com/community-scripts/ProxmoxVE/commits?author=Uruknara) ## 2022-11-13 ### Changed - **All LXC's** - Add option to continue upon Internet NOT Connected ## 2022-11-11 ### Changed - **HA Bluetooth Integration Preparation** - [NEW Script](https://github.com/tteck/Proxmox/discussions/719) ## 2022-11-04 ### Changed - **Scrypted LXC** - NEW Script ## 2022-11-01 ### Changed - **Alpine LXC** - NEW Script - **Arch LXC** - NEW Script ================================================ FILE: .github/changelogs/2022/12.md ================================================ ## 2022-12-31 ### Changed - **v5 Scripts** (Testing before moving forward https://github.com/tteck/Proxmox/discussions/881) - Adguard Home LXC - Docker LXC - Home Assistant Core LXC - PhotoPrism LXC - Shinobi NVR LXC - Vaultwarden LXC ## 2022-12-27 ### Changed - **Home Assistant Container LXC** - Add an option to use Fuse Overlayfs (ZFS) (Advanced) - **Docker LXC** - Add an option to use Fuse Overlayfs (ZFS) (Advanced) - If the LXC is created Privileged, the script will automatically set up USB passthrough. ## 2022-12-22 ### Changed - **All LXC's** - Add an option to run the script in Verbose Mode (Advanced) ## 2022-12-20 ### Changed - **Hyperion LXC** - NEW Script ## 2022-12-17 ### Changed - **Home Assistant Core LXC** - Linux D-Bus Message Broker - Mariadb & PostgreSQL Ready - Bluetooth Ready - Fix for Inconsistent Dependency Versions (dbus-fast & bleak) ## 2022-12-16 ### Changed - **Home Assistant Core LXC** - Python 3.10.8 ## 2022-12-09 ### Changed - **Change Detection LXC** - NEW Script ## 2022-12-03 ### Changed - **All LXC's** - Add options to set DNS Server IP Address and DNS Search Domain (Advanced) ================================================ FILE: .github/changelogs/2023/01.md ================================================ ## 2023-01-28 ### Changed - **LXC Cleaner** - Code refactoring to give the user the option to choose whether cache or logs will be deleted for each app/service. - Leaves directory structure intact ## 2023-01-27 ### Changed - **LXC Cleaner** - NEW Script ## 2023-01-26 ### Changed - **ALL LXC's** - Add an option to disable IPv6 (Advanced) ## 2023-01-25 ### Changed - **Home Assistant OS VM** - switch to v5 - add an option to set MTU size (Advanced) - add arch check (no ARM64) (issue from community.home-assistant.io) - add check to insure VMID isn't already used before VM creation (Advanced) (issue from forum.proxmox.com) - code refactoring - **PiMox Home Assistant OS VM** - switch to v5 - add an option to set MTU size (Advanced) - add arch check (no AMD64) - add pve check (=>7.2) - add check to insure VMID isn't already used before VM creation (Advanced) - code refactoring - **All LXC's** - add arch check (no ARM64) (issue from forum.proxmox.com) ## 2023-01-24 ### Changed - **Transmission LXC** - NEW Script ## 2023-01-23 ### Changed - **ALL LXC's** - Add [Midnight Commander (mc)](https://www.linuxcommand.org/lc3_adv_mc.php) ## 2023-01-22 ### Changed - **Autobrr LXC** - NEW Script ## 2023-01-21 ### Changed - **Kavita LXC** - NEW Script ## 2023-01-19 ### Changed - **SABnzbd LXC** - NEW Script ## 2023-01-17 ### Changed - **Homer LXC** - NEW Script ## 2023-01-14 ### Changed - **Tdarr LXC** - NEW Script - **Deluge LXC** - NEW Script ## 2023-01-13 ### Changed - **Lidarr LXC** - NEW Script - **Prowlarr LXC** - NEW Script - **Radarr LXC** - NEW Script - **Readarr LXC** - NEW Script - **Sonarr LXC** - NEW Script - **Whisparr LXC** - NEW Script ## 2023-01-12 ### Changed - **ALL LXC's** - Add an option to set MTU size (Advanced) ## 2023-01-11 ### Changed - **Home Assistant Core LXC** - Auto Initialize - **Cronicle Primary/Worker LXC** - NEW Script ## 2023-01-09 ### Changed - **ALL LXC's** - v5 - **k0s Kubernetes LXC** - NEW Script - **Podman LXC** - NEW Script ## 2023-01-04 ### Changed - **YunoHost LXC** - NEW Script ================================================ FILE: .github/changelogs/2023/02.md ================================================ ## 2023-02-24 ### Changed - **qBittorrent LXC** (Thanks @romka777) - NEW Script - **Jackett LXC** (Thanks @romka777) - NEW Script ## 2023-02-23 ### Changed - **Proxmox LXC Updater** - Skip all templates, allowing for the starting, updating, and shutting down of containers to be resumed automatically. - Exclude an additional container by adding the CTID at the end of the shell command ( -s 103). ## 2023-02-16 ### Changed - **RSTPtoWEB LXC** - NEW Script - **go2rtc LXC** - NEW Script ## 2023-02-12 ### Changed - **OliveTin** - NEW Script ## 2023-02-10 ### Changed - **Home Assistant OS VM** - Code Refactoring ## 2023-02-05 ### Changed - **Devuan LXC** - NEW Script ## 2023-02-02 ### Changed - **Audiobookshelf LXC** - NEW Script - **Rocky Linux LXC** - NEW Script ================================================ FILE: .github/changelogs/2023/03.md ================================================ ## 2023-03-31 ### Changed - **Home Assistant OS VM** - Include a choice within the "Advanced" settings to configure the disk cache between none (default) or Write Through. ## 2023-03-27 ### Changed - **Removed Alpine-ESPHome LXC** - Nonoperational - **All Scripts** - Incorporate code that examines whether SSH is being used and, if yes, offers a suggestion against it without restricting or blocking its usage. ## 2023-03-25 ### Changed - **Alpine-ESPHome LXC** - NEW Script - **Alpine-Whoogle LXC** - NEW Script ## 2023-03-22 ### Changed - **The latest iteration of the scripts** - Going forward, versioning will no longer be utilized in order to avoid breaking web-links in blogs and YouTube videos. - The scripts have been made more legible as the repetitive code has been moved to function files, making it simpler to share among the scripts and hopefully easier to maintain. This also makes it simpler to contribute to the project. - When a container is created with privileged mode enabled, the USB passthrough feature is automatically activated. ## 2023-03-18 ### Changed - **Alpine-AdGuard Home LXC** (Thanks @nicedevil007) - NEW Script - **Alpine-Docker LXC** - NEW Script - **Alpine-Zigbee2MQTT LXC** - NEW Script ## 2023-03-15 ### Changed - **Alpine-Grafana LXC** (Thanks @nicedevil007) - NEW Script ## 2023-03-10 ### Changed - **Proxmox LXC Updater** - You can use the command line to exclude multiple containers simultaneously. ## 2023-03-08 ### Changed - **Proxmox CPU Scaling Governor** - Menu options dynamically based on the available scaling governors. ## 2023-03-07 ### Changed - **Alpine-Vaultwarden LXC** - NEW Script - **All LXC Scripts** - Retrieve the time zone from Proxmox and configure the container to use the same time zone ================================================ FILE: .github/changelogs/2023/04.md ================================================ ## 2023-04-30 ### Changed - **Proxmox VE Monitor-All** - NEW Script - Replaces Proxmox VE LXC Monitor ## 2023-04-28 ### Changed - **Proxmox VE LXC Monitor** - NEW Script ## 2023-04-26 ### Changed - **The site can now be accessed through a more memorable URL, which is [helper-scripts.com](http://helper-scripts.com).** ## 2023-04-23 ### Changed - **Non-Alpine LXC's** - Advanced settings provide the option for users to switch between Debian and Ubuntu distributions. However, some applications or services, such as Deconz, grocy or Omada, may not be compatible with the selected distribution due to dependencies. ## 2023-04-16 ### Changed - **Home Assistant Core LXC** - Python 3.11.2 ## 2023-04-15 ### Changed - **InfluxDB LXC** - Choosing InfluxDB v1 will result in Chronograf being installed automatically. - **[User Submitted Guides](https://github.com/community-scripts/ProxmoxVE/blob/main/USER_SUBMITTED_GUIDES.md)** - Informative guides that demonstrate how to install various software packages using Proxmox VE Helper Scripts. ## 2023-04-14 ### Changed - **Cloudflared LXC** - NEW Script ## 2023-04-05 ### Changed - **Jellyfin LXC** - Set Ubuntu 22.04 as default - Use the Deb822 format jellyfin.sources configuration (jellyfin.list configuration has been obsoleted) ## 2023-04-02 ### Changed - **Home Assistant OS VM** - Include a choice within the "Advanced" settings to configure the CPU model between kvm64 (default) or host. ================================================ FILE: .github/changelogs/2023/05.md ================================================ ## 2023-05-27 ### Changed - **Proxmox VE 7 Post Install** - If an Intel N-series processor is detected, ~the script provides options to install both the Proxmox 6.2 kernel and the Intel microcode.~ and using PVE7, recommend using PVE8 ## 2023-05-23 ### Changed - **OpenWrt VM** - NEW Script ## 2023-05-17 ### Changed - **Alpine-AdGuard Home LXC** - Removed, it wasn't installed through the Alpine package manager. - **Alpine-Whoogle LXC** - Removed, it wasn't installed through the Alpine package manager. ## 2023-05-16 ### Changed - **Proxmox VE LXC Updater** - Add information about the boot disk, which provides an easy way to determine if you need to expand the disk. - **Proxmox VE Processor Microcode** - [Intel microcode-20230512 Release](https://github.com/intel/Intel-Linux-Processor-Microcode-Data-Files/releases/tag/microcode-20230512) ## 2023-05-13 ### Changed - **Tautulli LXC** - NEW Script ## 2023-05-12 ### Changed - **Bazarr LXC** - NEW Script ## 2023-05-08 ### Changed - **Proxmox VE Intel Processor Microcode** - Renamed to **Proxmox VE Processor Microcode** - Automatically identifies the processor vendor (Intel/AMD) and installs the appropriate microcode. ## 2023-05-07 ### Changed - **FHEM LXC** - NEW Script ## 2023-05-01 ### Changed - **OctoPrint LXC** - NEW Script - **Proxmox VE Intel Processor Microcode** - NEW Script ================================================ FILE: .github/changelogs/2023/06.md ================================================ ## 2023-06-18 ### Changed - **OpenObserve LXC** - NEW Script ## 2023-06-17 ### Changed - **UniFi Network Application LXC** - Now distribution agnostic. - **Omada Controller LXC** - Now distribution agnostic. ## 2023-06-16 ### Changed - **Proxmox VE Monitor-All** - Skip instances based on onboot and templates. [8c2a3cc](https://github.com/community-scripts/ProxmoxVE/commit/8c2a3cc4d774fa13d17f695d6bdf9a4deedb1372). ## 2023-06-12 ### Changed - **Proxmox VE Edge Kernel** - Removed, with the Proxmox opt-in kernels and the upcoming Proxmox Virtual Environment 8, edge kernels are no longer needed. - **Proxmox VE Kernel Clean** - Now compatible with PVE8. ## 2023-06-11 ### Changed - **Proxmox VE Post Install** - Now compatible with both Proxmox Virtual Environment 7 (PVE7) and Proxmox Virtual Environment 8 (PVE8). ## 2023-06-02 ### Changed - **Proxmox VE 7 Post Install** - In a non-clustered environment, you can choose to disable high availability, which helps save system resources. ================================================ FILE: .github/changelogs/2023/07.md ================================================ ## 2023-07-24 ### Changed - **Ombi LXC** - NEW Script ## 2023-07-23 ### Changed - **Zoraxy LXC** - NEW Script ## 2023-07-18 ### Changed - **Proxmox VE Cron LXC Updater** - NEW Script ## 2023-07-11 ### Changed - **Scrypted LXC** - Add VAAPI hardware transcoding ## 2023-07-07 ### Changed - **Real-Debrid Torrent Client LXC** - NEW Script ## 2023-07-05 ### Changed - There have been more than 110 commits since June 18th, although not all of them are significant, with a majority focused on ensuring compatibility with Proxmox VE 8 and Debian 12. ================================================ FILE: .github/changelogs/2023/08.md ================================================ ## 2023-08-31 ### Changed - **TurnKey ZoneMinder LXC** - NEW Script - **TurnKey OpenVPN LXC** - NEW Script ## 2023-08-30 ### Changed - **TurnKey** - Introducing a **NEW** Category on the Site. - My intention is to maintain the TurnKey scripts in their simplest form, contained within a single file, and with minimal options, if any. - **TurnKey Core LXC** - NEW Script - **TurnKey File Server LXC** - NEW Script - **TurnKey Gitea LXC** - NEW Script - **TurnKey GitLab LXC** - NEW Script - **TurnKey Nextcloud LXC** - NEW Script - **TurnKey Observium LXC** - NEW Script - **TurnKey ownCloud LXC** - NEW Script - **TurnKey Torrent Server LXC** - NEW Script - **TurnKey Wordpress LXC** - NEW Script ## 2023-08-24 ### Changed - **qBittorrent LXC** - Added back to repository with UPnP disabled and password changed. ## 2023-08-24 ### Changed - **qBittorrent LXC** - Removed from this repository for potential malicious hidden code https://github.com/tteck/Proxmox/discussions/1725 ## 2023-08-16 ### Changed - **Homarr LXC** - NEW Script ## 2023-08-10 ### Changed - **Proxmox VE Processor Microcode** - AMD microcode-20230808 Release ## 2023-08-09 ### Changed - **Omada Controller LXC** - Update via script - **Proxmox VE Processor Microcode** - [Intel microcode-20230808 Release](https://github.com/intel/Intel-Linux-Processor-Microcode-Data-Files/releases/tag/microcode-20230808) ## 2023-08-01 ### Changed - **Overseerr LXC** - NEW Script - **Jellyseerr LXC** - NEW Script ================================================ FILE: .github/changelogs/2023/09.md ================================================ ## 2023-09-30 ### Changed - **All Templates** - NEW Script ## 2023-09-28 ### Changed - **Alpine Nextcloud Hub LXC** - NEW Script (Thanks to @nicedevil007) ## 2023-09-14 ### Changed - **Proxmox VE Processor Microcode** - Allow users to select available microcode packages. ## 2023-09-13 ### Changed - **Pi.Alert LXC** - NEW Script - **Proxmox VE Kernel Clean** - Code overhaul with a fresh start. This script offers the flexibility to select specific kernels for removal, unlike the previous version, which was an all-or-nothing approach. ## 2023-09-11 ### Changed - **Paperless-ngx LXC** - Modify the script to incorporate Redis and PostgreSQL, while also introducing an option to include Adminer during installation. ## 2023-09-10 ### Changed - **TurnKey Game Server LXC** - NEW Script ## 2023-09-09 ### Changed - **Proxmox VE Host Backup** - Users are now able to specify both the backup path and the directory in which they wish to work. ## 2023-09-07 ### Changed - **Proxmox VE Host Backup** - NEW Script ## 2023-09-06 ### Changed - **Proxmox VE LXC Cleaner** - Added a new menu that allows you to choose which containers you want to exclude from the clean process. - **Tailscale** - Added a menu that enables you to choose the specific container where you want to install Tailscale. ## 2023-09-05 ### Changed - **Proxmox VE LXC Updater** - Added a new menu that allows you to choose which containers you want to exclude from the update process. ## 2023-09-01 ### Changed - **TurnKey Media Server LXC** - NEW Script ================================================ FILE: .github/changelogs/2023/10.md ================================================ ## 2023-10-31 ### Changed - **Debian 12 VM** - NEW Script ## 2023-10-29 ### Changed - **Unmanic LXC** - NEW Script ## 2023-10-27 ### Changed - **Webmin** - A full code overhaul. ## 2023-10-15 ### Changed - **TasmoAdmin LXC** - NEW Script ## 2023-10-14 ### Changed - **Sonarr LXC** - Include an option to install v4 (experimental) ## 2023-10-11 ### Changed - **Proxmox VE CPU Scaling Governor** - A full code overhaul. - Include an option to configure a crontab for ensuring that the CPU Scaling Governor configuration persists across reboots. ## 2023-10-08 ### Changed - **Proxmox VE LXC Updater** - Now displays which containers require a reboot. - **File Browser** - Uninstall by re-executing the script - Option to use No Authentication ## 2023-10-05 ### Changed - **Pingvin Share LXC** - NEW Script ================================================ FILE: .github/changelogs/2023/11.md ================================================ ## 2023-11-19 ### Changed - **Dockge LXC** - NEW Script ## 2023-11-18 ### Changed - **Ubuntu 22.04 VM** - NEW Script ## 2023-11-14 ### Changed - **TurnKey Nextcloud VM** - NEW Script - **TurnKey ownCloud VM** - NEW Script ## 2023-11-11 ### Changed - **Homarr LXC** - Returns with v0.14.0 (The authentication update). ## 2023-11-9 ### Changed - **AgentDVR LXC** - NEW Script ## 2023-11-8 ### Changed - **Linkwarden LXC** - NEW Script ## 2023-11-2 ### Changed - **PhotoPrism LXC** - Transitioned to PhotoPrism's latest installation package, featuring Linux binaries. ## 2023-11-1 ### Changed - **Owncast LXC** - NEW Script ================================================ FILE: .github/changelogs/2023/12.md ================================================ ## 2023-12-19 ### Changed - **Proxmox VE Netdata** - NEW Script ## 2023-12-10 ### Changed - **Homarr LXC** - Removed, again. ## 2023-12-02 ### Changed - **Runtipi LXC** - NEW Script ## 2023-12-01 ### Changed - **Mikrotik RouterOS VM** - Now Mikrotik RouterOS CHR VM - code refactoring - update to CHR - thanks to @NiccyB - **Channels DVR Server LXC** - NEW Script ================================================ FILE: .github/changelogs/2024/01.md ================================================ ## 2024-01-25 ### Changed - **PairDrop LXC** - NEW Script ## 2024-01-20 ### Changed - **Apache-Cassandra LXC** - NEW Script - **Redis LXC** - NEW Script ## 2024-01-17 ### Changed - **ntfy LXC** - NEW Script - **HyperHDR LXC** - NEW Script ## 2024-01-16 ### Changed - **Website Improvements** - Refine and correct pointers. - Change hover colors to intuitively indicate categories/items. - Implement opening links in new tabs for better navigation. - Enhance the Copy button to better indicate that the command has been successfully copied. - Introduce a Clear Search button. - While not directly related to the website, it's worth mentioning that the logo in newly created LXC notes now serves as a link to the website, conveniently opening in a new tab. ## 2024-01-12 ### Changed - **Apt-Cacher-NG LXC** - NEW Script - **New Feature** - The option to utilize Apt-Cacher-NG (Advanced settings) when creating LXCs. The added functionality is expected to decrease bandwidth usage and expedite package installation and updates. https://github.com/tteck/Proxmox/discussions/2332 ## 2024-01-09 ### Changed - **Verbose mode** - Only entries with `$STD` will be shown ## 2024-01-07 ### Changed - **Stirling-PDF LXC** - NEW Script - **SFTPGo LXC** - NEW Script ## 2024-01-04 ### Changed - **CommaFeed LXC** - NEW Script ## 2024-01-03 ### Changed - **Sonarr LXC** - Breaking Change - Complete recode - https://github.com/tteck/Proxmox/discussions/1738#discussioncomment-8005107 ## 2024-01-01 ### Changed - **Gotify LXC** - NEW Script ================================================ FILE: .github/changelogs/2024/02.md ================================================ ## 2024-02-26 ### Changed - **Mafl LXC** - NEW Script ## 2024-02-23 ### Changed - **Tandoor Recipes LXC** - NEW Script (Thanks @MickLesk) ## 2024-02-21 ### Changed - **All scripts** - As of today, the scripts require the Bash shell specifically. ([more info](https://github.com/tteck/Proxmox/discussions/2536)) ## 2024-02-19 ### Changed - **PairDrop LXC** - Removed from the website ([more info](https://github.com/tteck/Proxmox/discussions/2516)) ## 2024-02-16 ### Changed - **Proxmox VE LXC Filesystem Trim** - NEW Script ([more info](https://github.com/tteck/Proxmox/discussions/2505#discussion-6226037)) ## 2024-02-11 ### Changed - **HiveMQ CE LXC** - NEW Script - **Apache-CouchDB LXC** - NEW Script ## 2024-02-06 ### Changed - **All Scripts** - The scripts will only work with PVE7 Version 7.4-13 or later, or PVE8 Version 8.1.1 or later. ## 2024-02-05 ### Changed - **Gokapi LXC** - NEW Script - **Nginx Proxy Manager LXC** - Option to install v2.10.4 ## 2024-02-04 ### Changed - **Pi-hole LXC** - Option to add Unbound ## 2024-02-02 ### Changed - **Readeck LXC** - NEW Script ================================================ FILE: .github/changelogs/2024/03.md ================================================ ## 2024-03-26 ### Changed - **MediaMTX LXC** - NEW Script ## 2024-03-25 ### Changed - **Proxmox VE Post Install** - ~Requires Proxmox Virtual Environment Version 8.1.1 or later.~ - Requires Proxmox Virtual Environment Version 8.0 or later. - **Proxmox Backup Server LXC** - NEW Script ## 2024-03-24 ### Changed - **SmokePing LXC** - NEW Script ## 2024-03-13 ### Changed - **FlowiseAI LXC** - NEW Script ## 2024-03-11 ### Changed - **Wastebin LXC** - NEW Script ## 2024-03-08 ### Changed - **Proxmox VE Post Install** - Requires Proxmox Virtual Environment Version 8.1.1 or later. ================================================ FILE: .github/changelogs/2024/04.md ================================================ ## 2024-04-30 ### Changed - **Tdarr LXC** - Default settings are now **Unprivileged** - Unprivileged Hardware Acceleration ## 2024-04-29 ### Changed - **ErsatzTV LXC** - NEW Script ## 2024-04-28 ### Changed - **Scrypted LXC** - Unprivileged Hardware Acceleration - **Emby LXC** - Unprivileged Hardware Acceleration ## 2024-04-27 ### Changed - **Frigate LXC** - Unprivileged Hardware Acceleration https://github.com/tteck/Proxmox/discussions/2711#discussioncomment-9244629 - **Ubuntu 24.04 VM** - NEW Script ## 2024-04-26 ### Changed - **Glances** - NEW Script ## 2024-04-25 ### Changed - **Jellyfin LXC** - Default settings are now **Unprivileged** - Unprivileged Hardware Acceleration - Groups are set automatically - Checks for the existence of `/dev/dri/card0` if not found, use `/dev/dri/card1`. Set the GID to `44` - Set the GID for `/dev/dri/renderD128` to `104` - Not tested <8.1.11 - **Plex LXC** - Default settings are now **Unprivileged** - Unprivileged Hardware Acceleration - Groups are set automatically - Checks for the existence of `/dev/dri/card0` if not found, use `/dev/dri/card1`. Set the GID to `44` - Set the GID for `/dev/dri/renderD128` to `104` - Not tested <8.1.11 ## 2024-04-24 ### Changed - **Traccar LXC** - NEW Script - **Calibre-Web LXC** - NEW Script ## 2024-04-21 ### Changed - **Aria2 LXC** - NEW Script ## 2024-04-15 ### Changed - **Homarr LXC** - Add back to website - **Umbrel LXC** - Add back to website - **OpenMediaVault LXC** - Add back to website ## 2024-04-12 ### Changed - **OpenMediaVault LXC** - Removed from website ## 2024-04-09 ### Changed - **PairDrop LXC** - Add back to website ## 2024-04-05 ### Changed - **Medusa LXC** - NEW Script - **WatchYourLAN LXC** - NEW Script ## 2024-04-04 ### Changed - **Actual Budget LXC** - NEW Script ## 2024-04-03 ### Changed - **LazyLibrarian LXC** - NEW Script ## 2024-04-01 ### Changed - **Frigate LXC** - NEW Script ================================================ FILE: .github/changelogs/2024/05.md ================================================ ## 2024-05-31 ### Changed - **Advanced Settings** [(Commit)](https://github.com/community-scripts/ProxmoxVE/commit/fc9dff220b4ea426d3a75178ad8accacae4683ca) - Passwords are now masked ## 2024-05-30 ### Changed - **Forgejo LXC** - NEW Script ## 2024-05-28 ### Changed - **Notifiarr LXC** - NEW Script ## 2024-05-25 ### Changed - **Threadfin LXC** - NEW Script ## 2024-05-23 ### Changed - **BunkerWeb LXC** - NEW Script ## 2024-05-20 ### Changed - **Traefik LXC** - NEW Script ## 2024-05-19 ### Changed - **NetBird** - NEW Script - **Tailscale** - Refactor Code ## 2024-05-18 ### Changed - **MongoDB LXC** - NEW Script ## 2024-05-17 ### Changed - **New Website** - We have officially moved to [Helper-Scripts.com](https://helper-scripts.com) ## 2024-05-16 ### Changed - **iVentoy LXC** - NEW Script ## 2024-05-13 ### Changed - **Headscale LXC** - NEW Script ## 2024-05-11 ### Changed - **Caddy LXC** - NEW Script ## 2024-05-09 ### Changed - **Umami LXC** - NEW Script ## 2024-05-08 ### Changed - **Kernel Pin** - NEW Script - **Home Assistant Core LXC** - Ubuntu 24.04 ONLY ## 2024-05-07 ### Changed - **Pocketbase LXC** - NEW Script ## 2024-05-05 ### Changed - **Fenrus LXC** - NEW Script ## 2024-05-02 ### Changed - **OpenMediaVault LXC** - Set Debian 12 as default - OpenMediaVault 7 (sandworm) ================================================ FILE: .github/changelogs/2024/06.md ================================================ ## 2024-06-30 ### Changed - **All Scripts** [(Commit)](https://github.com/community-scripts/ProxmoxVE/commit/39ea1d4a20b83c07d084ebafdc811eec3548f289) - Requires Proxmox Virtual Environment version 8.1 or later. ## 2024-06-27 ### Changed - **Kubo LXC** [(View Source)](https://github.com/community-scripts/ProxmoxVE/blob/main/install/kubo-install.sh) - NEW Script - **RabbitMQ LXC** [(View Source)](https://github.com/community-scripts/ProxmoxVE/blob/main/install/rabbitmq-install.sh) - NEW Script - **Scrutiny LXC** - Removed from website, broken. ## 2024-06-26 ### Changed - **Scrutiny LXC** - NEW Script ## 2024-06-14 ### Changed - **MySpeed LXC** [(View Source)](https://github.com/community-scripts/ProxmoxVE/blob/main/install/myspeed-install.sh) - NEW Script ## 2024-06-13 ### Changed - **PeaNUT LXC** [(View Source)](https://github.com/community-scripts/ProxmoxVE/blob/main/install/peanut-install.sh) - NEW Script - **Website** - If the Changelog has changed recently, the link on the website will pulse. - **Spoolman LXC** [(View Source)](https://github.com/community-scripts/ProxmoxVE/blob/main/install/spoolman-install.sh) - NEW Script ## 2024-06-12 ### Changed - **MeTube LXC** [(View Source)](https://github.com/community-scripts/ProxmoxVE/blob/main/install/metube-install.sh) - NEW Script - **Matterbridge LXC** [(View Source)](https://github.com/community-scripts/ProxmoxVE/blob/main/install/matterbridge-install.sh) - NEW Script - **Website** - Reopen the gh-pages site (https://tteck.github.io/Proxmox/) ## 2024-06-11 ### Changed - **Zabbix LXC** [(View Source)](https://github.com/community-scripts/ProxmoxVE/blob/main/install/zabbix-install.sh) - NEW Script ## 2024-06-06 ### Changed - **Petio LXC** [(View Source)](https://github.com/community-scripts/ProxmoxVE/blob/main/install/petio-install.sh) - NEW Script - **Website** - Important notices will now be displayed on the landing page. ## 2024-06-04 ### Changed - **FlareSolverr LXC** [(View Source)](https://github.com/community-scripts/ProxmoxVE/blob/main/install/flaresolverr-install.sh) - NEW Script ================================================ FILE: .github/changelogs/2024/07.md ================================================ ## 2024-07-26 ### Changed - **Gitea LXC** [(View Source)](https://github.com/community-scripts/ProxmoxVE/blob/main/install/gitea-install.sh) - NEW Script ================================================ FILE: .github/changelogs/2024/08.md ================================================ ## 2024-08-21 ### Changed - **WireGuard LXC** [(Commit)](https://github.com/community-scripts/ProxmoxVE/commit/723365a79df7cc0fd29b1af8f7ef200a7e0921b1) - Refactor Code - Breaking Change ## 2024-08-19 ### Changed - **CommaFeed LXC** [(Commit)](https://github.com/community-scripts/ProxmoxVE/commit/0a33d1739ec3a49011411929bd46a260e92e99f9) - Refactor Code - Breaking Change ## 2024-08-06 ### Changed - **lldap LXC** [(View Source)](https://github.com/community-scripts/ProxmoxVE/blob/main/install/lldap-install.sh) - NEW Script ================================================ FILE: .github/changelogs/2024/09.md ================================================ ## 2024-09-16 ### Changed - **HomeBox LXC** [(View Source)](https://github.com/community-scripts/ProxmoxVE/blob/main/install/homebox-install.sh) - NEW Script - **Zipline LXC** [(View Source)](https://github.com/community-scripts/ProxmoxVE/blob/main/install/zipline-install.sh) - NEW Script ## 2024-09-13 ### Changed - **Tianji LXC** [(View Source)](https://github.com/community-scripts/ProxmoxVE/blob/main/install/tianji-install.sh) - NEW Script ================================================ FILE: .github/changelogs/2024/10.md ================================================ ## 2024-10-31 ### Changed - **NZBGet LXC** [(View Source)](https://github.com/community-scripts/ProxmoxVE/blob/main/install/nzbget-install.sh) - NEW Script - **Memos LXC** [(View Source)](https://github.com/community-scripts/ProxmoxVE/blob/main/install/memos-install.sh) - NEW Script ## 2024-10-27 ### Changed - **Open WebUI LXC** [(Commit)](https://github.com/community-scripts/ProxmoxVE/commit/8a21f6e7f025a911865395d4c0fa9a001bd0d512) - Refactor Script to add an option to install Ollama. ## 2024-10-26 ### Changed - **AdventureLog LXC** [(View Source)](https://github.com/community-scripts/ProxmoxVE/blob/main/install/adventurelog-install.sh) - NEW Script ## 2024-10-25 ### Changed - **Zoraxy LXC** [(Commit)](https://github.com/community-scripts/ProxmoxVE/commit/468a5d367ded4cf453a1507452e112ac3e234e2a) - Switch built from source to a pre-compiled binary version. - Breaking Change ## 2024-10-23 ### Changed - **Wallos LXC** [(View Source)](https://github.com/community-scripts/ProxmoxVE/blob/main/install/wallos-install.sh) - NEW Script - **Open WebUI LXC** [(View Source)](https://github.com/community-scripts/ProxmoxVE/blob/main/install/openwebui-install.sh) - NEW Script ## 2024-10-19 ### Changed - **Cockpit LXC** [(View Source)](https://github.com/community-scripts/ProxmoxVE/blob/main/install/cockpit-install.sh) - NEW Script - **Neo4j LXC** [(View Source)](https://github.com/community-scripts/ProxmoxVE/blob/main/install/neo4j-install.sh) - NEW Script ## 2024-10-18 ### Changed - **ArchiveBox LXC** [(View Source)](https://github.com/community-scripts/ProxmoxVE/blob/main/install/archivebox-install.sh) - NEW Script ## 2024-10-15 ### Changed - **evcc LXC** [(View Source)](https://github.com/community-scripts/ProxmoxVE/blob/main/install/evcc-install.sh) - NEW Script ## 2024-10-10 ### Changed - **MySQL LXC** [(View Source)](https://github.com/community-scripts/ProxmoxVE/blob/main/install/mysql-install.sh) - NEW Script - **Tianji LXC** [(Commit)](https://github.com/community-scripts/ProxmoxVE/commit/4c83a790ac9b040da1f11ad2cbe13d3fc5f480e9) - Breaking Change - Switch from `pm2` process management to `systemd` ## 2024-10-03 ### Changed - **Home Assistant Core LXC** [(Commit)](https://github.com/community-scripts/ProxmoxVE/commit/f2937febe69b2bad8b3a14eb84aa562a8f14cc6a) [(Commit)](https://github.com/community-scripts/ProxmoxVE/commit/f2966ced7f457fd506f865f7f5b70ea12c4b0049) - Refactor Code - Breaking Change - Home Assistant has transitioned to using `uv` for managing the virtual environment and installing additional modules. ================================================ FILE: .github/changelogs/2024/11.md ================================================ ## 2024-11-30 ### Changed ### 🚀 Updated Scripts - Convert line endings in the-lounge.sh [@jamezpolley](https://github.com/jamezpolley) ([#599](https://github.com/community-scripts/ProxmoxVE/pull/599)) ### 🌐 Website - add some Information for Monitor-All Script [@MickLesk](https://github.com/MickLesk) ([#605](https://github.com/community-scripts/ProxmoxVE/pull/605)) ## 2024-11-29 ### Changed ### ✨ New Scripts - New Script: The Lounge IRC [@quantumryuu](https://github.com/quantumryuu) ([#571](https://github.com/community-scripts/ProxmoxVE/pull/571)) - New Script: LubeLogger [@quantumryuu](https://github.com/quantumryuu) ([#574](https://github.com/community-scripts/ProxmoxVE/pull/574)) - New Script: Inspircd [@quantumryuu](https://github.com/quantumryuu) ([#576](https://github.com/community-scripts/ProxmoxVE/pull/576)) ### 🚀 Updated Scripts - Fix msg_error on zwave-js-ui [@MickLesk](https://github.com/MickLesk) ([#585](https://github.com/community-scripts/ProxmoxVE/pull/585)) - Fix Kimai Apache2 Rights [@MickLesk](https://github.com/MickLesk) ([#577](https://github.com/community-scripts/ProxmoxVE/pull/577)) ## 2024-11-28 ### Changed ### 💥 Breaking Changes - Fix Z-Wave JS UI script [@MickLesk](https://github.com/MickLesk) ([#546](https://github.com/community-scripts/ProxmoxVE/pull/546)) - [Migration guide](https://github.com/community-scripts/ProxmoxVE/discussions/635) ### 🚀 Updated Scripts - Add vitest, add json validation tests, fix broken json files [@havardthom](https://github.com/havardthom) ([#566](https://github.com/community-scripts/ProxmoxVE/pull/566)) - Add update script to Pocketbase [@dsiebel](https://github.com/dsiebel) ([#535](https://github.com/community-scripts/ProxmoxVE/pull/535)) - Fix MongoDB install in Unifi script [@havardthom](https://github.com/havardthom) ([#564](https://github.com/community-scripts/ProxmoxVE/pull/564)) - Remove changing DISK_REF for zfspool mikrotik-routeros.sh [@tjcomserv](https://github.com/tjcomserv) ([#529](https://github.com/community-scripts/ProxmoxVE/pull/529)) ### 🌐 Website - Show Changelog on Mobile Devices [@MickLesk](https://github.com/MickLesk) ([#558](https://github.com/community-scripts/ProxmoxVE/pull/558)) ## 2024-11-27 ### Changed ### 💥 Breaking Changes - Zabbix: Use Agent2 as Default | Update Script added | some other Improvements [@MickLesk](https://github.com/MickLesk) ([#527](https://github.com/community-scripts/ProxmoxVE/pull/527)) ### 🚀 Updated Scripts - Fix: install mosquitto from mosquitto repo [@dsiebel](https://github.com/dsiebel) ([#534](https://github.com/community-scripts/ProxmoxVE/pull/534)) - Patch Netbird Script | Container Boot-Check | Debian/Ubuntu Only [@MickLesk](https://github.com/MickLesk) ([#528](https://github.com/community-scripts/ProxmoxVE/pull/528)) - Install MongoDB 4.2 for non-AVX CPUs in Unifi LXC [@ColinOppenheim](https://github.com/ColinOppenheim) ([#319](https://github.com/community-scripts/ProxmoxVE/pull/319)) ### 🌐 Website - Fix json error in zabbix.json [@havardthom](https://github.com/havardthom) ([#543](https://github.com/community-scripts/ProxmoxVE/pull/543)) - Fix another json error in add-netbird-lxc.json [@havardthom](https://github.com/havardthom) ([#545](https://github.com/community-scripts/ProxmoxVE/pull/545)) ## 2024-11-26 ### Changed ### 🚀 Updated Scripts - Fix Vikunja install script to prevent database deletion upon updating [@vhsdream](https://github.com/vhsdream) ([#524](https://github.com/community-scripts/ProxmoxVE/pull/524)) ## 2024-11-25 ### Changed ### 💥 Breaking Changes - Remove Scrypted script [@MickLesk](https://github.com/MickLesk) ([#511](https://github.com/community-scripts/ProxmoxVE/pull/511)) - Because of request from Scrypted maintainer: [#494](https://github.com/community-scripts/ProxmoxVE/issues/494) - Official Scrypted script can be used instead: https://docs.scrypted.app/installation.html#proxmox-ve ### 🚀 Updated Scripts - Fix bugs in Calibre-Web update [@havardthom](https://github.com/havardthom) ([#517](https://github.com/community-scripts/ProxmoxVE/pull/517)) - Fix upload folder in listmonk LXC [@bvdberg01](https://github.com/bvdberg01) ([#515](https://github.com/community-scripts/ProxmoxVE/pull/515)) ### 🌐 Website - Fix website url in Zoraxy documentation [@miggi92](https://github.com/miggi92) ([#506](https://github.com/community-scripts/ProxmoxVE/pull/506)) ## 2024-11-24 ### Changed ### ✨ New Scripts - New script: listmonk LXC [@bvdberg01](https://github.com/bvdberg01) ([#442](https://github.com/community-scripts/ProxmoxVE/pull/442)) ### 🧰 Maintenance - Add release title to github-release.yml [@havardthom](https://github.com/havardthom) ([#481](https://github.com/community-scripts/ProxmoxVE/pull/481)) ## 2024-11-23 ### Changed ### 🚀 Updated Scripts - Fix Actual Budget install missing build tools [@cour64](https://github.com/cour64) ([#455](https://github.com/community-scripts/ProxmoxVE/pull/455)) - Fix Vikunja Update [@MickLesk](https://github.com/MickLesk) ([#440](https://github.com/community-scripts/ProxmoxVE/pull/440)) - Patch PostInstall-Script to PVE 8.3 | Add PVE 8.3 in Security [@MickLesk](https://github.com/MickLesk) ([#431](https://github.com/community-scripts/ProxmoxVE/pull/431)) ### 🌐 Website - Frontend: fix reported issue with json-editor page and add OS select in installmethod [@BramSuurdje](https://github.com/BramSuurdje) ([#426](https://github.com/community-scripts/ProxmoxVE/pull/426)) - Fixed Typo [@BenBakDev](https://github.com/BenBakDev) ([#441](https://github.com/community-scripts/ProxmoxVE/pull/441)) ### 🧰 Maintenance - Fix newline issue in changelog pr [@havardthom](https://github.com/havardthom) ([#474](https://github.com/community-scripts/ProxmoxVE/pull/474)) - Remove newline in changelog-pr action [@havardthom](https://github.com/havardthom) ([#461](https://github.com/community-scripts/ProxmoxVE/pull/461)) - Add action that creates github release based on CHANGELOG.md [@havardthom](https://github.com/havardthom) ([#462](https://github.com/community-scripts/ProxmoxVE/pull/462)) ## 2024-11-21 ### Changed ### ✨ New Scripts - Add new LXC: NextPVR [@MickLesk](https://github.com/MickLesk) ([#391](https://github.com/community-scripts/ProxmoxVE/pull/391)) - Add new LXC: Kimai [@MickLesk](https://github.com/MickLesk) ([#397](https://github.com/community-scripts/ProxmoxVE/pull/397)) ### 🚀 Updated Scripts - Add .env file support for HomeBox [@404invalid-user](https://github.com/404invalid-user) ([#383](https://github.com/community-scripts/ProxmoxVE/pull/383)) - RDTClient Remove .NET 8.0 | Add .NET 9.0 [@MickLesk](https://github.com/MickLesk) ([#413](https://github.com/community-scripts/ProxmoxVE/pull/413)) - Remove old resource message from vaultwarden [@havardthom](https://github.com/havardthom) ([#402](https://github.com/community-scripts/ProxmoxVE/pull/402)) ### 🌐 Website - Add PostInstall Documentation to zigbee2mqtt.json [@MickLesk](https://github.com/MickLesk) ([#411](https://github.com/community-scripts/ProxmoxVE/pull/411)) - Fix incorrect hdd values in json files [@havardthom](https://github.com/havardthom) ([#403](https://github.com/community-scripts/ProxmoxVE/pull/403)) - Website: Add discord link to navbar [@BramSuurdje](https://github.com/BramSuurdje) ([#405](https://github.com/community-scripts/ProxmoxVE/pull/405)) ### 🧰 Maintenance - Use github app in changelog-pr.yml and add auto approval [@havardthom](https://github.com/havardthom) ([#416](https://github.com/community-scripts/ProxmoxVE/pull/416)) ## 2024-11-20 ### Changed ### 🚀 Updated Scripts - LinkWarden: Moved PATH into service [@newzealandpaul](https://github.com/newzealandpaul) ([#376](https://github.com/community-scripts/ProxmoxVE/pull/376)) ### 🌐 Website - Replace dash "-" with "/" in metadata [@newzealandpaul](https://github.com/newzealandpaul) ([#374](https://github.com/community-scripts/ProxmoxVE/pull/374)) - Proxmox VE Cron LXC Updater: Add tteck's notes. [@newzealandpaul](https://github.com/newzealandpaul) ([#378](https://github.com/community-scripts/ProxmoxVE/pull/378)) ## 2024-11-19 ### Changed ### 🚀 Updated Scripts - Fix Wallos Update [@MickLesk](https://github.com/MickLesk) ([#339](https://github.com/community-scripts/ProxmoxVE/pull/339)) - Linkwarden: Move Secret Key above in install.sh [@MickLesk](https://github.com/MickLesk) ([#356](https://github.com/community-scripts/ProxmoxVE/pull/356)) - Linkwarden: add gnupg to installed dependencies [@erfansamandarian](https://github.com/erfansamandarian) ([#349](https://github.com/community-scripts/ProxmoxVE/pull/349)) ### 🌐 Website - Add *Arr Suite category for Website [@MickLesk](https://github.com/MickLesk) ([#370](https://github.com/community-scripts/ProxmoxVE/pull/370)) - Refactor Buttons component to use a ButtonLink for cleaner code, simplifying the source URL generation and layout [@BramSuurdje](https://github.com/BramSuurdje) ([#371](https://github.com/community-scripts/ProxmoxVE/pull/371)) ### 🧰 Maintenance - [github]: add new Frontend_Report / Issue_Report & optimize config.yml [@MickLesk](https://github.com/MickLesk) ([#226](https://github.com/community-scripts/ProxmoxVE/pull/226)) - [chore] Update FUNDING.yml [@MickLesk](https://github.com/MickLesk) ([#352](https://github.com/community-scripts/ProxmoxVE/pull/352)) ## 2024-11-18 ### Changed ### 💥 Breaking Changes - Massive Update - Remove old storage check, add new storage and resource check to all scripts - Remove downscaling with pct set [@MickLesk](https://github.com/MickLesk) ([#333](https://github.com/community-scripts/ProxmoxVE/pull/333)) ### ✨ New Scripts - new scripts for NetBox [@bvdberg01](https://github.com/bvdberg01) ([#308](https://github.com/community-scripts/ProxmoxVE/pull/308)) ### 🚀 Updated Scripts - Support SSE 4.2 in Frigate script [@anishp55](https://github.com/anishp55) ([#328](https://github.com/community-scripts/ProxmoxVE/pull/328)) - Bugfix: Wallos Patch (Cron Log & Media Backup) [@MickLesk](https://github.com/MickLesk) ([#331](https://github.com/community-scripts/ProxmoxVE/pull/331)) - Linkwarden - Harmonize Script, Add Monolith & Bugfixing [@MickLesk](https://github.com/MickLesk) ([#306](https://github.com/community-scripts/ProxmoxVE/pull/306)) - Fix optional installs in Cockpit LXC [@havardthom](https://github.com/havardthom) ([#317](https://github.com/community-scripts/ProxmoxVE/pull/317)) ### 🌐 Website - Added additional instructions to nginxproxymanager [@newzealandpaul](https://github.com/newzealandpaul) ([#329](https://github.com/community-scripts/ProxmoxVE/pull/329)) ### 🧰 Maintenance - Verify changes before commit in changelog-pr.yml [@havardthom](https://github.com/havardthom) ([#310](https://github.com/community-scripts/ProxmoxVE/pull/310)) ## 2024-11-17 ### Changed ### ✨ New Scripts - Add Komga LXC [@DysfunctionalProgramming](https://github.com/DysfunctionalProgramming) ([#275](https://github.com/community-scripts/ProxmoxVE/pull/275)) ### 🚀 Updated Scripts - Tweak: Patch Prometheus for v.3.0.0 [@MickLesk](https://github.com/MickLesk) ([#300](https://github.com/community-scripts/ProxmoxVE/pull/300)) - Wavelog - Small Adjustment [@HB9HIL](https://github.com/HB9HIL) ([#292](https://github.com/community-scripts/ProxmoxVE/pull/292)) ### 🌐 Website - Add Note for Komga Installation (Website) [@MickLesk](https://github.com/MickLesk) ([#303](https://github.com/community-scripts/ProxmoxVE/pull/303)) - Fix Komga logo [@havardthom](https://github.com/havardthom) ([#298](https://github.com/community-scripts/ProxmoxVE/pull/298)) ### 🧰 Maintenance - Add github workflow for automatic changelog PR [@havardthom](https://github.com/havardthom) ([#299](https://github.com/community-scripts/ProxmoxVE/pull/299)) - Use website label in autolabeler [@havardthom](https://github.com/havardthom) ([#297](https://github.com/community-scripts/ProxmoxVE/pull/297)) ## 2024-11-16 ### Changed - **Recyclarr LXC** [(View Source)](https://github.com/community-scripts/ProxmoxVE/blob/main/install/recyclarr-install.sh) - NEW Script - **Wavelog LXC** [(View Source)](https://github.com/community-scripts/ProxmoxVE/blob/main/install/wavelog-install.sh) - NEW Script - **Vaultwarden LXC:** RAM has now been increased to 6144 MB [(PR)](https://github.com/community-scripts/ProxmoxVE/pull/285) - Breaking Change ## 2024-11-05 ### Changed - **Bookstack LXC** [(View Source)](https://github.com/community-scripts/ProxmoxVE/blob/main/install/bookstack-install.sh) - NEW Script - **Vikunja LXC** [(View Source)](https://github.com/community-scripts/ProxmoxVE/blob/main/install/vikunja-install.sh) - NEW Script ## 2024-11-04 ### Breaking Change - **Automatic Update of Repository:** The update function now uses the new repository `community-scripts/ProxmoxVE` for Debian/Ubuntu LXC containers. ```bash bash -c "$(curl -fsSL https://github.com/community-scripts/ProxmoxVE/raw/main/misc/update-repo.sh)" ================================================ FILE: .github/changelogs/2024/12.md ================================================ ## 2024-12-30 ### Changed ### 🚀 Updated Scripts - [Archivebox] Fix wrong port being printed post install. [@Strana-Mechty](https://github.com/Strana-Mechty) ([#1105](https://github.com/community-scripts/ProxmoxVE/pull/1105)) - fix: add homepage version during build step [@se-bastiaan](https://github.com/se-bastiaan) ([#1107](https://github.com/community-scripts/ProxmoxVE/pull/1107)) ### 🌐 Website - Fix Trilium Website to TriliumNext [@tmkis2](https://github.com/tmkis2) ([#1103](https://github.com/community-scripts/ProxmoxVE/pull/1103)) ## 2024-12-29 ### Changed ### ✨ New Scripts - New Script: Grist [@cfurrow](https://github.com/cfurrow) ([#1076](https://github.com/community-scripts/ProxmoxVE/pull/1076)) - New Script: TeddyCloud Server [@dsiebel](https://github.com/dsiebel) ([#1064](https://github.com/community-scripts/ProxmoxVE/pull/1064)) ### 🚀 Updated Scripts - Fix Install / Update on Grist Script [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#1091](https://github.com/community-scripts/ProxmoxVE/pull/1091)) ### 🌐 Website - fix: Update add-lxc-iptag.json warn to warning [@BramSuurdje](https://github.com/BramSuurdje) ([#1094](https://github.com/community-scripts/ProxmoxVE/pull/1094)) ### 🧰 Maintenance - Introduce editorconfig for more consistent formatting [@dsiebel](https://github.com/dsiebel) ([#1073](https://github.com/community-scripts/ProxmoxVE/pull/1073)) ## 2024-12-28 ### Changed ### 💥 Breaking Changes - Add Figlet into Repo | Creation of local ASCII Header [@MickLesk](https://github.com/MickLesk) ([#1072](https://github.com/community-scripts/ProxmoxVE/pull/1072)) - Add an IP-Update for MOTD if IP Changed [@MickLesk](https://github.com/MickLesk) ([#1067](https://github.com/community-scripts/ProxmoxVE/pull/1067)) ### 🚀 Updated Scripts - Zabbix: Fix SQL Path for 7.2 [@MickLesk](https://github.com/MickLesk) ([#1069](https://github.com/community-scripts/ProxmoxVE/pull/1069)) - Authentik: added missing port to access url [@TheRealVira](https://github.com/TheRealVira) ([#1065](https://github.com/community-scripts/ProxmoxVE/pull/1065)) ## 2024-12-27 ### Changed ### ✨ New Scripts - new scripts for Authentik [@remz1337](https://github.com/remz1337) ([#291](https://github.com/community-scripts/ProxmoxVE/pull/291)) ### 🚀 Updated Scripts - Add 8.0 for MongoDB Installation [@MickLesk](https://github.com/MickLesk) ([#1046](https://github.com/community-scripts/ProxmoxVE/pull/1046)) - Update Zabbix to 7.2. Release [@MickLesk](https://github.com/MickLesk) ([#1048](https://github.com/community-scripts/ProxmoxVE/pull/1048)) - Apache-Guacamole script bug fix [@sannier3](https://github.com/sannier3) ([#1039](https://github.com/community-scripts/ProxmoxVE/pull/1039)) ### 🌐 Website - Updated SAB documentation based on RAM increase [@TheRealVira](https://github.com/TheRealVira) ([#1035](https://github.com/community-scripts/ProxmoxVE/pull/1035)) ### ❔ Unlabelled - Patch Figlet Repo if missing [@MickLesk](https://github.com/MickLesk) ([#1044](https://github.com/community-scripts/ProxmoxVE/pull/1044)) - fix Tags for Advanced Settings [@MickLesk](https://github.com/MickLesk) ([#1042](https://github.com/community-scripts/ProxmoxVE/pull/1042)) ## 2024-12-26 ### Changed ### ✨ New Scripts - New Script: Jenkins [@quantumryuu](https://github.com/quantumryuu) ([#1019](https://github.com/community-scripts/ProxmoxVE/pull/1019)) - New Script: 2FAuth [@jkrgr0](https://github.com/jkrgr0) ([#943](https://github.com/community-scripts/ProxmoxVE/pull/943)) ### 🚀 Updated Scripts - ChangeDetection Update: Update also Browsers [@Niklas04](https://github.com/Niklas04) ([#1027](https://github.com/community-scripts/ProxmoxVE/pull/1027)) - ensure all RFC1918 local Ipv4 addresses are in iptag script [@AskAlice](https://github.com/AskAlice) ([#992](https://github.com/community-scripts/ProxmoxVE/pull/992)) - Fix Proxmox DataCenter: incorrect build.func url [@rbradley0](https://github.com/rbradley0) ([#1013](https://github.com/community-scripts/ProxmoxVE/pull/1013)) ### 🧰 Maintenance - [GitHub Actions] Introduce Shellcheck to check bash code [@andygrunwald](https://github.com/andygrunwald) ([#1018](https://github.com/community-scripts/ProxmoxVE/pull/1018)) ## 2024-12-25 ### Changed ### ✨ New Scripts - add: pve-datacenter-manager [@CrazyWolf13](https://github.com/CrazyWolf13) ([#947](https://github.com/community-scripts/ProxmoxVE/pull/947)) ### 🚀 Updated Scripts - Fix Script: Alpine Nextcloud Upload File Size Limit [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#933](https://github.com/community-scripts/ProxmoxVE/pull/933)) - Doubled RAM for SAB [@TheRealVira](https://github.com/TheRealVira) ([#1007](https://github.com/community-scripts/ProxmoxVE/pull/1007)) ## 2024-12-23 ### Changed ### 🚀 Updated Scripts - Fix Navidrome Update & Install [@MickLesk](https://github.com/MickLesk) ([#991](https://github.com/community-scripts/ProxmoxVE/pull/991)) - Update emby.sh to correct port [@Rageplant](https://github.com/Rageplant) ([#989](https://github.com/community-scripts/ProxmoxVE/pull/989)) ## 2024-12-21 ### Changed ### 🚀 Updated Scripts - update Port in homeassistant-core CT [@fraefel](https://github.com/fraefel) ([#961](https://github.com/community-scripts/ProxmoxVE/pull/961)) ## 2024-12-20 ### Changed ### ✨ New Scripts - New Script: Apache Guacamole [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#657](https://github.com/community-scripts/ProxmoxVE/pull/657)) - New Script: silverbullet [@dsiebel](https://github.com/dsiebel) ([#659](https://github.com/community-scripts/ProxmoxVE/pull/659)) - New Script: Zammad [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#640](https://github.com/community-scripts/ProxmoxVE/pull/640)) - New Script: CheckMk [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#926](https://github.com/community-scripts/ProxmoxVE/pull/926)) ### 🚀 Updated Scripts - Fix: Remove PHP Key generation in Bookstack Update [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#948](https://github.com/community-scripts/ProxmoxVE/pull/948)) ### 🌐 Website - Update checkmk description [@BramSuurdje](https://github.com/BramSuurdje) ([#954](https://github.com/community-scripts/ProxmoxVE/pull/954)) - Add Login Note for Checkmk [@MickLesk](https://github.com/MickLesk) ([#940](https://github.com/community-scripts/ProxmoxVE/pull/940)) ### ❔ Unlabelled - Update build.func to display the Proxmox Hostname [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#894](https://github.com/community-scripts/ProxmoxVE/pull/894)) ## 2024-12-19 ### Changed ### 🚀 Updated Scripts - Fix: Bookstack Update Function [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#844](https://github.com/community-scripts/ProxmoxVE/pull/844)) - mysql not showing ip after install [@snow2k9](https://github.com/snow2k9) ([#924](https://github.com/community-scripts/ProxmoxVE/pull/924)) - Fix Omada - Crawling latest version [@MickLesk](https://github.com/MickLesk) ([#918](https://github.com/community-scripts/ProxmoxVE/pull/918)) ### 🌐 Website - Fix script path formatting in InstallMethod component [@BramSuurdje](https://github.com/BramSuurdje) ([#909](https://github.com/community-scripts/ProxmoxVE/pull/909)) - Fix Part-DB Docu (cred command) [@EvilBlood](https://github.com/EvilBlood) ([#898](https://github.com/community-scripts/ProxmoxVE/pull/898)) - Enhance Tooltip component by adding CircleHelp icon and fix instructions in script component [@BramSuurdje](https://github.com/BramSuurdje) ([#910](https://github.com/community-scripts/ProxmoxVE/pull/910)) ## 2024-12-18 ### Changed ### ✨ New Scripts - New script: Part-DB LXC [@bvdberg01](https://github.com/bvdberg01) ([#591](https://github.com/community-scripts/ProxmoxVE/pull/591)) ### 🚀 Updated Scripts - Fix Kernel-Clean for Proxmox 8.x [@MickLesk](https://github.com/MickLesk) ([#904](https://github.com/community-scripts/ProxmoxVE/pull/904)) - [Frigate] Remove SSE 4.2 from instruction set supporting OpenVino [@remz1337](https://github.com/remz1337) ([#902](https://github.com/community-scripts/ProxmoxVE/pull/902)) ### 🌐 Website - New Metadata Category: "Coding & AI" [@newzealandpaul](https://github.com/newzealandpaul) ([#890](https://github.com/community-scripts/ProxmoxVE/pull/890)) - Moved Webmin to "Server & Networking" [@newzealandpaul](https://github.com/newzealandpaul) ([#891](https://github.com/community-scripts/ProxmoxVE/pull/891)) ## 2024-12-17 ### Changed ### 🚀 Updated Scripts - Fix Alpine-Nextcloud: Bump PHP Version to 8.3 [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#865](https://github.com/community-scripts/ProxmoxVE/pull/865)) - Correction of Jellyfin CT Port [@mneten](https://github.com/mneten) ([#884](https://github.com/community-scripts/ProxmoxVE/pull/884)) - fix spinner on lxc-ip-tag [@MickLesk](https://github.com/MickLesk) ([#876](https://github.com/community-scripts/ProxmoxVE/pull/876)) - Fix Keycloak Installation [@MickLesk](https://github.com/MickLesk) ([#874](https://github.com/community-scripts/ProxmoxVE/pull/874)) - Fix ports ressources [@MickLesk](https://github.com/MickLesk) ([#867](https://github.com/community-scripts/ProxmoxVE/pull/867)) ### 🧰 Maintenance - Small Changes to the PR Template [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#862](https://github.com/community-scripts/ProxmoxVE/pull/862)) ### ❔ Unlabelled - calculate terminal size for header_info [@MickLesk](https://github.com/MickLesk) ([#879](https://github.com/community-scripts/ProxmoxVE/pull/879)) - Fix header creation with figlet for alpine [@MickLesk](https://github.com/MickLesk) ([#869](https://github.com/community-scripts/ProxmoxVE/pull/869)) ## 2024-12-16 ### Changed ### 💥 Breaking Changes - Massive Update: build.func | install.func | create_lxc.sh (Part 1) [@MickLesk](https://github.com/MickLesk) ([#643](https://github.com/community-scripts/ProxmoxVE/pull/643)) - Update ALL CT's to new default (Part 2) [@MickLesk](https://github.com/MickLesk) ([#710](https://github.com/community-scripts/ProxmoxVE/pull/710)) ### ✨ New Scripts - New Script: LXC IP-Tag [@MickLesk](https://github.com/MickLesk) ([#536](https://github.com/community-scripts/ProxmoxVE/pull/536)) ### 🚀 Updated Scripts - Increase Size | Description & Download-URL of Debian VM [@MickLesk](https://github.com/MickLesk) ([#837](https://github.com/community-scripts/ProxmoxVE/pull/837)) - Update Script: Remove Docker Compose Question [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#847](https://github.com/community-scripts/ProxmoxVE/pull/847)) ### 🌐 Website - Bump nanoid from 3.3.7 to 3.3.8 in /frontend [@dependabot[bot]](https://github.com/dependabot[bot]) ([#845](https://github.com/community-scripts/ProxmoxVE/pull/845)) ### ❔ Unlabelled - Fix SSH root access in install.func [@havardthom](https://github.com/havardthom) ([#858](https://github.com/community-scripts/ProxmoxVE/pull/858)) - Fix variable name for CT_TYPE override [@remz1337](https://github.com/remz1337) ([#855](https://github.com/community-scripts/ProxmoxVE/pull/855)) - Keeps the same style after writing the SEARCH icon [@remz1337](https://github.com/remz1337) ([#851](https://github.com/community-scripts/ProxmoxVE/pull/851)) ## 2024-12-13 ### Changed ### 🚀 Updated Scripts - Fix Keycloak Update Function [@MickLesk](https://github.com/MickLesk) ([#762](https://github.com/community-scripts/ProxmoxVE/pull/762)) - Fix config bug in Alpine Vaultwarden [@havardthom](https://github.com/havardthom) ([#775](https://github.com/community-scripts/ProxmoxVE/pull/775)) ### 🌐 Website - Change MISC from red to green [@MickLesk](https://github.com/MickLesk) ([#815](https://github.com/community-scripts/ProxmoxVE/pull/815)) - Update some JSON Files for Website [@MickLesk](https://github.com/MickLesk) ([#812](https://github.com/community-scripts/ProxmoxVE/pull/812)) - Update Notes & Documentation for Proxmox Backup Server [@MickLesk](https://github.com/MickLesk) ([#804](https://github.com/community-scripts/ProxmoxVE/pull/804)) ### 🧰 Maintenance - Github: Optimize Issue Template & PR Template [@MickLesk](https://github.com/MickLesk) ([#802](https://github.com/community-scripts/ProxmoxVE/pull/802)) ## 2024-12-12 ### Changed ### 🚀 Updated Scripts - Update jellyfin.sh / Fix infinite loop [@gerpo](https://github.com/gerpo) ([#792](https://github.com/community-scripts/ProxmoxVE/pull/792)) ### 🌐 Website - Fix port and website in nextcloudpi.json [@PhoenixEmik](https://github.com/PhoenixEmik) ([#790](https://github.com/community-scripts/ProxmoxVE/pull/790)) - Add post-install note to mqtt.json [@havardthom](https://github.com/havardthom) ([#783](https://github.com/community-scripts/ProxmoxVE/pull/783)) ### 🧰 Maintenance - Filter pull requests on main branch in changelog-pr.yml [@havardthom](https://github.com/havardthom) ([#793](https://github.com/community-scripts/ProxmoxVE/pull/793)) - Fix Z-Wave JS UI Breaking Change in CHANGELOG.md [@havardthom](https://github.com/havardthom) ([#781](https://github.com/community-scripts/ProxmoxVE/pull/781)) ## 2024-12-09 ### Changed ### 🚀 Updated Scripts - Fix PostgreSQL password bug in Umami install [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#750](https://github.com/community-scripts/ProxmoxVE/pull/750)) ## 2024-12-08 ### Changed ### 🚀 Updated Scripts - Use MongoDB 4.4 in Unifi for non-AVX users [@havardthom](https://github.com/havardthom) ([#691](https://github.com/community-scripts/ProxmoxVE/pull/691)) ### 🌐 Website - Move homarr to Dashboards section [@CrazyWolf13](https://github.com/CrazyWolf13) ([#740](https://github.com/community-scripts/ProxmoxVE/pull/740)) ## 2024-12-07 ### Changed ### 🚀 Updated Scripts - Zigbee2MQTT: Remove dev branch choice until v2.0.0 release [@havardthom](https://github.com/havardthom) ([#702](https://github.com/community-scripts/ProxmoxVE/pull/702)) - Fix Hoarder build failure by installing Chromium stable [@vhsdream](https://github.com/vhsdream) ([#723](https://github.com/community-scripts/ProxmoxVE/pull/723)) ### 🌐 Website - Bugfix: Include script name in website search [@havardthom](https://github.com/havardthom) ([#731](https://github.com/community-scripts/ProxmoxVE/pull/731)) ### ❔ Unlabelled - Fix broken build.func [@havardthom](https://github.com/havardthom) ([#736](https://github.com/community-scripts/ProxmoxVE/pull/736)) ## 2024-12-06 ### Changed ### 🚀 Updated Scripts - Fix bugs in Komga update [@DysfunctionalProgramming](https://github.com/DysfunctionalProgramming) ([#717](https://github.com/community-scripts/ProxmoxVE/pull/717)) - Bookstack: Fix Update function composer [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#700](https://github.com/community-scripts/ProxmoxVE/pull/700)) ### 🌐 Website - fix: note component in json-editor getting out of focus when typing and revert theme switch animation [@BramSuurdje](https://github.com/BramSuurdje) ([#706](https://github.com/community-scripts/ProxmoxVE/pull/706)) ### 🧰 Maintenance - Update frontend CI/CD workflow [@havardthom](https://github.com/havardthom) ([#703](https://github.com/community-scripts/ProxmoxVE/pull/703)) ## 2024-12-05 ### Changed ### 🚀 Updated Scripts - PostgreSQL: Change authentication method from peer to md5 for UNIX sockets [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#650](https://github.com/community-scripts/ProxmoxVE/pull/650)) - Fix stdout in unifi.sh [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#688](https://github.com/community-scripts/ProxmoxVE/pull/688)) - Fix `rm` bug in Vikunja update [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#692](https://github.com/community-scripts/ProxmoxVE/pull/692)) ## 2024-12-04 ### Changed ### 🚀 Updated Scripts - Update Spelling 'Environment' in nginxproxymanager [@MathijsG](https://github.com/MathijsG) ([#676](https://github.com/community-scripts/ProxmoxVE/pull/676)) ### 🌐 Website - Update homepage.json documentation and website links [@patchmonkey](https://github.com/patchmonkey) ([#668](https://github.com/community-scripts/ProxmoxVE/pull/668)) ## 2024-12-03 ### Changed ### ✨ New Scripts - New Script: Onedev [@quantumryuu](https://github.com/quantumryuu) ([#612](https://github.com/community-scripts/ProxmoxVE/pull/612)) ### 🚀 Updated Scripts - Script Update: SnipeIT [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#641](https://github.com/community-scripts/ProxmoxVE/pull/641)) ## 2024-12-02 ### Changed ### ✨ New Scripts - New Script: Hoarder LXC [@vhsdream](https://github.com/vhsdream) ([#567](https://github.com/community-scripts/ProxmoxVE/pull/567)) - New script: SnipeIT LXC [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#538](https://github.com/community-scripts/ProxmoxVE/pull/538)) - New Script: Glance [@quantumryuu](https://github.com/quantumryuu) ([#595](https://github.com/community-scripts/ProxmoxVE/pull/595)) - New script: Unbound LXC [@wimb0](https://github.com/wimb0) ([#547](https://github.com/community-scripts/ProxmoxVE/pull/547)) - New script: Mylar3 LXC [@davalanche](https://github.com/davalanche) ([#554](https://github.com/community-scripts/ProxmoxVE/pull/554)) ### 🚀 Updated Scripts - Stirling-PDF: replace dependency for v0.35.0 and add check and fix in stirling-pdf.sh [@vhsdream](https://github.com/vhsdream) ([#614](https://github.com/community-scripts/ProxmoxVE/pull/614)) - qbittorrent: do not override the configuration port in systemd [@zdraganov](https://github.com/zdraganov) ([#618](https://github.com/community-scripts/ProxmoxVE/pull/618)) ### 🌐 Website - chore: Update unbound logo to have only the actual logo [@BramSuurdje](https://github.com/BramSuurdje) ([#648](https://github.com/community-scripts/ProxmoxVE/pull/648)) - fix: vaultwarden info mismatch [@BramSuurdje](https://github.com/BramSuurdje) ([#645](https://github.com/community-scripts/ProxmoxVE/pull/645)) - Wallos json fix [@quantumryuu](https://github.com/quantumryuu) ([#630](https://github.com/community-scripts/ProxmoxVE/pull/630)) ================================================ FILE: .github/changelogs/2025/01.md ================================================ ## 2025-01-31 ### Changed ### ✨ New Scripts - New Script: Paymenter [@opastorello](https://github.com/opastorello) ([#1827](https://github.com/community-scripts/ProxmoxVE/pull/1827)) ### 🚀 Updated Scripts - [Fix] Alpine-IT-Tools, add missing ssh package for root ssh access [@CrazyWolf13](https://github.com/CrazyWolf13) ([#1891](https://github.com/community-scripts/ProxmoxVE/pull/1891)) - [Fix] Change Download of Trilium after there change the tag/release logic [@MickLesk](https://github.com/MickLesk) ([#1892](https://github.com/community-scripts/ProxmoxVE/pull/1892)) ### 🌐 Website - [Website] Enhance DataFetcher with better UI components and add reactive data fetching intervals [@BramSuurdje](https://github.com/BramSuurdje) ([#1902](https://github.com/community-scripts/ProxmoxVE/pull/1902)) - [Website] Update /data/page.tsx [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#1900](https://github.com/community-scripts/ProxmoxVE/pull/1900)) ## 2025-01-30 ### Changed ### ✨ New Scripts - New Script: IT-Tools [@nicedevil007](https://github.com/nicedevil007) ([#1862](https://github.com/community-scripts/ProxmoxVE/pull/1862)) - New Script: Mattermost [@Dracentis](https://github.com/Dracentis) ([#1856](https://github.com/community-scripts/ProxmoxVE/pull/1856)) ### 🚀 Updated Scripts - Optimize PVE Manager Version-Check [@MickLesk](https://github.com/MickLesk) ([#1866](https://github.com/community-scripts/ProxmoxVE/pull/1866)) ### 🌐 Website - [API] Update build.func to set the status message correct [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#1878](https://github.com/community-scripts/ProxmoxVE/pull/1878)) - [Website] Update /data/page.tsx [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#1876](https://github.com/community-scripts/ProxmoxVE/pull/1876)) - Fix IT-Tools Website Entry (Default | Alpine) [@MickLesk](https://github.com/MickLesk) ([#1869](https://github.com/community-scripts/ProxmoxVE/pull/1869)) - fix: remove rounded styles from command primitive [@steveiliop56](https://github.com/steveiliop56) ([#1840](https://github.com/community-scripts/ProxmoxVE/pull/1840)) ### 🧰 Maintenance - [API] Update build.func: add function to see if a script failed or not [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#1874](https://github.com/community-scripts/ProxmoxVE/pull/1874)) ## 2025-01-29 ### Changed ### ✨ New Scripts - New Script: Prometheus Proxmox VE Exporter [@andygrunwald](https://github.com/andygrunwald) ([#1805](https://github.com/community-scripts/ProxmoxVE/pull/1805)) - New Script: Clean Orphaned LVM [@MickLesk](https://github.com/MickLesk) ([#1838](https://github.com/community-scripts/ProxmoxVE/pull/1838)) ### 🌐 Website - Patch http Url to https in build.func and /data/page.tsx [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#1849](https://github.com/community-scripts/ProxmoxVE/pull/1849)) - [Frontend] Add /data to show API results [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#1841](https://github.com/community-scripts/ProxmoxVE/pull/1841)) - Update clean-orphaned-lvm.json [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#1843](https://github.com/community-scripts/ProxmoxVE/pull/1843)) ### 🧰 Maintenance - Update build.func [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#1851](https://github.com/community-scripts/ProxmoxVE/pull/1851)) - [Diagnostic] Introduced optional lxc install diagnostics via API call [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#1801](https://github.com/community-scripts/ProxmoxVE/pull/1801)) ## 2025-01-28 ### Changed ### 💥 Breaking Changes - Breaking Change: Homarr v1 (Read Guide) [@MickLesk](https://github.com/MickLesk) ([#1825](https://github.com/community-scripts/ProxmoxVE/pull/1825)) - Update PingVin: Fix problem with update und switch to new method of getting files. [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#1819](https://github.com/community-scripts/ProxmoxVE/pull/1819)) ### ✨ New Scripts - New script: Monica LXC [@bvdberg01](https://github.com/bvdberg01) ([#1813](https://github.com/community-scripts/ProxmoxVE/pull/1813)) - New Script: NodeBB [@MickLesk](https://github.com/MickLesk) ([#1811](https://github.com/community-scripts/ProxmoxVE/pull/1811)) - New Script: Pocket ID [@Snarkenfaugister](https://github.com/Snarkenfaugister) ([#1779](https://github.com/community-scripts/ProxmoxVE/pull/1779)) ### 🚀 Updated Scripts - Update all Alpine LXC's to 3.21 (Docker, Grafana, Nextcloud, Vaultwarden, Zigbee2Mqtt, Alpine) [@MickLesk](https://github.com/MickLesk) ([#1803](https://github.com/community-scripts/ProxmoxVE/pull/1803)) - [Standardization] Fix Spelling for "Setup Python3" [@MickLesk](https://github.com/MickLesk) ([#1810](https://github.com/community-scripts/ProxmoxVE/pull/1810)) ### 🌐 Website - Filter out duplicate scripts in LatestScripts component and sort by creation date [@BramSuurdje](https://github.com/BramSuurdje) ([#1828](https://github.com/community-scripts/ProxmoxVE/pull/1828)) ### 🧰 Maintenance - [core]: Remove Figlet | Get Headers by Repo & Store Local [@MickLesk](https://github.com/MickLesk) ([#1802](https://github.com/community-scripts/ProxmoxVE/pull/1802)) - [docs] Update AppName.md: Make it clear where to change the URLs [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#1809](https://github.com/community-scripts/ProxmoxVE/pull/1809)) ## 2025-01-27 ### Changed ### ✨ New Scripts - New Script: Arch Linux VM [@MickLesk](https://github.com/MickLesk) ([#1780](https://github.com/community-scripts/ProxmoxVE/pull/1780)) ### 🚀 Updated Scripts - Increase alpine-vaultwarden default var_disk size [@nayzm](https://github.com/nayzm) ([#1788](https://github.com/community-scripts/ProxmoxVE/pull/1788)) - Added change of the mobile GUI to disable nag request [@GarryG](https://github.com/GarryG) ([#1785](https://github.com/community-scripts/ProxmoxVE/pull/1785)) ### 🌐 Website - Update frontend alpine-vaultwarden hdd size and OS version [@nayzm](https://github.com/nayzm) ([#1789](https://github.com/community-scripts/ProxmoxVE/pull/1789)) - Website: Add Description for Metadata Categories [@MickLesk](https://github.com/MickLesk) ([#1783](https://github.com/community-scripts/ProxmoxVE/pull/1783)) - [Fix] Double "VM" on website (Arch Linux) [@lasharor](https://github.com/lasharor) ([#1782](https://github.com/community-scripts/ProxmoxVE/pull/1782)) ## 2025-01-26 ### Changed ### 🚀 Updated Scripts - Fix jellyfin update command [@jcisio](https://github.com/jcisio) ([#1771](https://github.com/community-scripts/ProxmoxVE/pull/1771)) - openHAB - Use https and include doc url [@moodyblue](https://github.com/moodyblue) ([#1766](https://github.com/community-scripts/ProxmoxVE/pull/1766)) - Jellyfin: Fix default logging level [@tremor021](https://github.com/tremor021) ([#1768](https://github.com/community-scripts/ProxmoxVE/pull/1768)) - Calibre-Web: added installation of calibre binaries [@tremor021](https://github.com/tremor021) ([#1763](https://github.com/community-scripts/ProxmoxVE/pull/1763)) - Added environment variable to accept EULA for SQLServer2022 [@tremor021](https://github.com/tremor021) ([#1755](https://github.com/community-scripts/ProxmoxVE/pull/1755)) ### 🌐 Website - The Lounge: Fix the command to create new users [@tremor021](https://github.com/tremor021) ([#1762](https://github.com/community-scripts/ProxmoxVE/pull/1762)) ## 2025-01-24 ### Changed ### ✨ New Scripts - New Script: Ubuntu 24.10 VM [@MickLesk](https://github.com/MickLesk) ([#1711](https://github.com/community-scripts/ProxmoxVE/pull/1711)) ### 🚀 Updated Scripts - openHAB - Update to Zulu21 [@moodyblue](https://github.com/moodyblue) ([#1734](https://github.com/community-scripts/ProxmoxVE/pull/1734)) - Feature: Filebrowser Script > Redesign | Update Logic | Remove Logic [@MickLesk](https://github.com/MickLesk) ([#1716](https://github.com/community-scripts/ProxmoxVE/pull/1716)) - Feature: Ubuntu 22.04 VM > Redesign | Optional HDD-Size Prompt [@MickLesk](https://github.com/MickLesk) ([#1712](https://github.com/community-scripts/ProxmoxVE/pull/1712)) - Feature: Ubuntu 24.04 VM > Redesign | Optional HDD-Size Prompt | cifs support [@MickLesk](https://github.com/MickLesk) ([#1714](https://github.com/community-scripts/ProxmoxVE/pull/1714)) ### 🧰 Maintenance - [Core] Better Creation of App Headers for next feature [@MickLesk](https://github.com/MickLesk) ([#1719](https://github.com/community-scripts/ProxmoxVE/pull/1719)) ## 2025-01-23 ### Changed ### 🚀 Updated Scripts - Feature: Add Debian Disk Size / Redesign / Increase Disk [@MickLesk](https://github.com/MickLesk) ([#1695](https://github.com/community-scripts/ProxmoxVE/pull/1695)) - Fix: Paperless Service Timings & Optimization: Ghostscript Installation [@MickLesk](https://github.com/MickLesk) ([#1688](https://github.com/community-scripts/ProxmoxVE/pull/1688)) ### 🌐 Website - Refactor ScriptInfoBlocks and siteConfig to properly show the most populair scripts [@BramSuurdje](https://github.com/BramSuurdje) ([#1697](https://github.com/community-scripts/ProxmoxVE/pull/1697)) ### 🧰 Maintenance - Update build.func: Ubuntu advanced settings version [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#1701](https://github.com/community-scripts/ProxmoxVE/pull/1701)) ## 2025-01-22 ### Changed ### 🚀 Updated Scripts - Tweak: LubeLogger Script Upcoming Changes 1.4.3 [@JcMinarro](https://github.com/JcMinarro) ([#1656](https://github.com/community-scripts/ProxmoxVE/pull/1656)) - Fix: SQL Server 2022 Install [@MickLesk](https://github.com/MickLesk) ([#1669](https://github.com/community-scripts/ProxmoxVE/pull/1669)) ### 🌐 Website - Refactor Sidebar component to display unique scripts count [@BramSuurdje](https://github.com/BramSuurdje) ([#1681](https://github.com/community-scripts/ProxmoxVE/pull/1681)) - Refactor various components and configuration for mobile responsiveness. [@BramSuurdje](https://github.com/BramSuurdje) ([#1679](https://github.com/community-scripts/ProxmoxVE/pull/1679)) - Add Docker-VM to Containers & Docker Category [@thost96](https://github.com/thost96) ([#1667](https://github.com/community-scripts/ProxmoxVE/pull/1667)) - Moving SQL Server 2022 to database category [@CamronBorealis](https://github.com/CamronBorealis) ([#1659](https://github.com/community-scripts/ProxmoxVE/pull/1659)) ## 2025-01-21 ### Changed ### ✨ New Scripts - Add new Script: LXC Delete (Proxmox) [@MickLesk](https://github.com/MickLesk) ([#1636](https://github.com/community-scripts/ProxmoxVE/pull/1636)) - New script: ProjectSend [@bvdberg01](https://github.com/bvdberg01) ([#1616](https://github.com/community-scripts/ProxmoxVE/pull/1616)) - New Script: Beszel [@Sinofage](https://github.com/Sinofage) ([#1619](https://github.com/community-scripts/ProxmoxVE/pull/1619)) - New Script: Docker VM [@thost96](https://github.com/thost96) ([#1608](https://github.com/community-scripts/ProxmoxVE/pull/1608)) - New script: SQL Server 2022 [@kris701](https://github.com/kris701) ([#1482](https://github.com/community-scripts/ProxmoxVE/pull/1482)) ### 🚀 Updated Scripts - Fix: Teddycloud Script (install, clean up & update) [@MickLesk](https://github.com/MickLesk) ([#1652](https://github.com/community-scripts/ProxmoxVE/pull/1652)) - Fix: Docker VM deprecated gpg [@MickLesk](https://github.com/MickLesk) ([#1649](https://github.com/community-scripts/ProxmoxVE/pull/1649)) - ActualBudget: Fix Update-Function, Fix Wget Crawling, Add Versionscheck [@MickLesk](https://github.com/MickLesk) ([#1643](https://github.com/community-scripts/ProxmoxVE/pull/1643)) - Fix Photoprism missing folder & environments [@MickLesk](https://github.com/MickLesk) ([#1639](https://github.com/community-scripts/ProxmoxVE/pull/1639)) - Update MOTD: Add Dynamic IP with profile.d by @JcMinarro [@MickLesk](https://github.com/MickLesk) ([#1633](https://github.com/community-scripts/ProxmoxVE/pull/1633)) - PBS.sh: Fix wrong URL after Setup [@thost96](https://github.com/thost96) ([#1629](https://github.com/community-scripts/ProxmoxVE/pull/1629)) ### 🌐 Website - Bump vite from 6.0.1 to 6.0.11 in /frontend [@dependabot[bot]](https://github.com/dependabot[bot]) ([#1653](https://github.com/community-scripts/ProxmoxVE/pull/1653)) - Update glpi.json [@opastorello](https://github.com/opastorello) ([#1641](https://github.com/community-scripts/ProxmoxVE/pull/1641)) - Fix Docker-VM name on website [@Sinofage](https://github.com/Sinofage) ([#1630](https://github.com/community-scripts/ProxmoxVE/pull/1630)) ## 2025-01-20 ### Changed ### ✨ New Scripts - New Script: UrBackup Server [@kris701](https://github.com/kris701) ([#1569](https://github.com/community-scripts/ProxmoxVE/pull/1569)) - New Script: Proxmox Mail Gateway Post Installer [@thost96](https://github.com/thost96) ([#1559](https://github.com/community-scripts/ProxmoxVE/pull/1559)) ### 🚀 Updated Scripts - Update Kimai Dependency: Use PHP 8.3 [@MickLesk](https://github.com/MickLesk) ([#1609](https://github.com/community-scripts/ProxmoxVE/pull/1609)) - Feature: Add xCaddy for external Modules on Caddy-LXC [@MickLesk](https://github.com/MickLesk) ([#1613](https://github.com/community-scripts/ProxmoxVE/pull/1613)) - Fix Pocketbase URL after install [@MickLesk](https://github.com/MickLesk) ([#1597](https://github.com/community-scripts/ProxmoxVE/pull/1597)) - Unifi.sh fix wrong URL after Install [@thost96](https://github.com/thost96) ([#1601](https://github.com/community-scripts/ProxmoxVE/pull/1601)) ### 🌐 Website - Update Website | Add new Categories [@MickLesk](https://github.com/MickLesk) ([#1606](https://github.com/community-scripts/ProxmoxVE/pull/1606)) - Grafana: Mark container as updateable [@andygrunwald](https://github.com/andygrunwald) ([#1603](https://github.com/community-scripts/ProxmoxVE/pull/1603)) ### 🧰 Maintenance - [core] Update build.func: Add defaults to Advanced mode [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#1548](https://github.com/community-scripts/ProxmoxVE/pull/1548)) - Update build.func: Fix Advanced Tags (Remove all if empty / overwrite if default cleared) [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#1612](https://github.com/community-scripts/ProxmoxVE/pull/1612)) - Add new Check for LXC MaxKeys by @cricalix [@MickLesk](https://github.com/MickLesk) ([#1602](https://github.com/community-scripts/ProxmoxVE/pull/1602)) ## 2025-01-19 ### Changed ### 🚀 Updated Scripts - Update Opengist.sh: Fix broken backup function [@bvdberg01](https://github.com/bvdberg01) ([#1572](https://github.com/community-scripts/ProxmoxVE/pull/1572)) ## 2025-01-18 ### Changed ### 💥 Breaking Changes - **READ GUIDE FIRST** breaking change: Homeassistant-Core upgrade os and python3 [@MickLesk](https://github.com/MickLesk) ([#1550](https://github.com/community-scripts/ProxmoxVE/pull/1550)) - Update Openwrt: Delete lines that do WAN input and forward accept [@chackl1990](https://github.com/chackl1990) ([#1540](https://github.com/community-scripts/ProxmoxVE/pull/1540)) ### 🚀 Updated Scripts - added cifs support in ubuntu2404-vm.sh [@plonxyz](https://github.com/plonxyz) ([#1461](https://github.com/community-scripts/ProxmoxVE/pull/1461)) - Fix linkwarden update [@burgerga](https://github.com/burgerga) ([#1565](https://github.com/community-scripts/ProxmoxVE/pull/1565)) - [jellyseerr] Update nodejs if not up-to-date [@makstech](https://github.com/makstech) ([#1563](https://github.com/community-scripts/ProxmoxVE/pull/1563)) - Update VM Tags [@oOStroudyOo](https://github.com/oOStroudyOo) ([#1562](https://github.com/community-scripts/ProxmoxVE/pull/1562)) - Update apt-cacher-ng.sh: Typo/Missing $ [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#1545](https://github.com/community-scripts/ProxmoxVE/pull/1545)) ## 2025-01-16 ### Changed ### 💥 Breaking Changes - Update jellyseerr-install.sh to use Node 22 as required by latest Jellyseerr version [@pedrovieira](https://github.com/pedrovieira) ([#1535](https://github.com/community-scripts/ProxmoxVE/pull/1535)) ### ✨ New Scripts - New script: Dotnet ASP.NET Web Server [@kris701](https://github.com/kris701) ([#1501](https://github.com/community-scripts/ProxmoxVE/pull/1501)) - New script: phpIPAM [@bvdberg01](https://github.com/bvdberg01) ([#1503](https://github.com/community-scripts/ProxmoxVE/pull/1503)) ### 🌐 Website - Add Mobile check for empty icon-url on website [@MickLesk](https://github.com/MickLesk) ([#1532](https://github.com/community-scripts/ProxmoxVE/pull/1532)) ### 🧰 Maintenance - [Workflow]Update autolabeler-config.json [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#1525](https://github.com/community-scripts/ProxmoxVE/pull/1525)) - [core]Update update_json_date.yml [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#1526](https://github.com/community-scripts/ProxmoxVE/pull/1526)) - [core] Recreate Update JSON Workflow [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#1523](https://github.com/community-scripts/ProxmoxVE/pull/1523)) ## 2025-01-15 ### Changed ### 🚀 Updated Scripts - Fix: Add FFMPEG for OpenWebUI [@MickLesk](https://github.com/MickLesk) ([#1497](https://github.com/community-scripts/ProxmoxVE/pull/1497)) ### 🧰 Maintenance - [core] build.func&install.func: Fix ssh keynot added error [@dsiebel](https://github.com/dsiebel) ([#1502](https://github.com/community-scripts/ProxmoxVE/pull/1502)) ## 2025-01-14 ### Changed ### 💥 Breaking Changes - Update tianji-install.sh: Add OPENAI_API_KEY to .env [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#1480](https://github.com/community-scripts/ProxmoxVE/pull/1480)) ### ✨ New Scripts - New Script: Wordpress [@MickLesk](https://github.com/MickLesk) ([#1485](https://github.com/community-scripts/ProxmoxVE/pull/1485)) - New Script: Opengist [@jd-apprentice](https://github.com/jd-apprentice) ([#1429](https://github.com/community-scripts/ProxmoxVE/pull/1429)) ### 🚀 Updated Scripts - Update lazylibrarian-install.sh: Add pypdf libary [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#1467](https://github.com/community-scripts/ProxmoxVE/pull/1467)) - Update opengist-install.sh: Add git as dependencie [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#1471](https://github.com/community-scripts/ProxmoxVE/pull/1471)) ### 🌐 Website - [website] Update footer text [@rajatdiptabiswas](https://github.com/rajatdiptabiswas) ([#1466](https://github.com/community-scripts/ProxmoxVE/pull/1466)) ### 🧰 Maintenance - Hotfix build.func: Error when tags are empty [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#1492](https://github.com/community-scripts/ProxmoxVE/pull/1492)) - [core] Update build.func: Fix bug with advanced tags [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#1473](https://github.com/community-scripts/ProxmoxVE/pull/1473)) ## 2025-01-13 ### Changed ### 💥 Breaking Changes - Update Hoarder: Improvement .env location (see PR comment for little migration) [@MahrWe](https://github.com/MahrWe) ([#1325](https://github.com/community-scripts/ProxmoxVE/pull/1325)) ### 🚀 Updated Scripts - Fix: tandoor.sh: Call version.py to write current version [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#1454](https://github.com/community-scripts/ProxmoxVE/pull/1454)) - Fix inexistent folder on actualbudget update script [@dosten](https://github.com/dosten) ([#1444](https://github.com/community-scripts/ProxmoxVE/pull/1444)) ### 🌐 Website - Update kavita.json: Add info on how to enable folder adding. [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#1447](https://github.com/community-scripts/ProxmoxVE/pull/1447)) ### 🧰 Maintenance - GitHub Actions: Fix Shellsheck workflow to only run on changes `*.sh` files [@andygrunwald](https://github.com/andygrunwald) ([#1423](https://github.com/community-scripts/ProxmoxVE/pull/1423)) ### ❔ Unlabelled - feat: allow adding SSH authorized key for root (advanced settings) by @dsiebel [@MickLesk](https://github.com/MickLesk) ([#1456](https://github.com/community-scripts/ProxmoxVE/pull/1456)) ## 2025-01-11 ### Changed ### 🚀 Updated Scripts - Prometheus: Fix installation via creating the service file [@andygrunwald](https://github.com/andygrunwald) ([#1416](https://github.com/community-scripts/ProxmoxVE/pull/1416)) - Update prometheus-install.sh: Service creation fix [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#1417](https://github.com/community-scripts/ProxmoxVE/pull/1417)) - Update prometheus-alertmanager-install.sh: Service Creation Fix [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#1418](https://github.com/community-scripts/ProxmoxVE/pull/1418)) - Fix: LubeLogger CT vehicle tag typo [@kkroboth](https://github.com/kkroboth) ([#1413](https://github.com/community-scripts/ProxmoxVE/pull/1413)) ## 2025-01-10 ### Changed ### ✨ New Scripts - New script : Ghost [@fabrice1236](https://github.com/fabrice1236) ([#1361](https://github.com/community-scripts/ProxmoxVE/pull/1361)) ### 🚀 Updated Scripts - Fix user in ghost-cli install command [@fabrice1236](https://github.com/fabrice1236) ([#1408](https://github.com/community-scripts/ProxmoxVE/pull/1408)) - Update Prometheus + Alertmanager: Unify scripts for easier maintenance [@andygrunwald](https://github.com/andygrunwald) ([#1402](https://github.com/community-scripts/ProxmoxVE/pull/1402)) - Update komodo.sh: Fix broken paths in update_script(). [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#1403](https://github.com/community-scripts/ProxmoxVE/pull/1403)) - Fix: ActualBudget Update-Function [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#1376](https://github.com/community-scripts/ProxmoxVE/pull/1376)) - Fix: bookstack.sh - Ignore empty folder [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#1388](https://github.com/community-scripts/ProxmoxVE/pull/1388)) - Fix: checkmk-install.sh: Version crawling. [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#1385](https://github.com/community-scripts/ProxmoxVE/pull/1385)) ### 🌐 Website - Change Website-Category of nzbget [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#1379](https://github.com/community-scripts/ProxmoxVE/pull/1379)) ### 🧰 Maintenance - Update check_and_update_json_date.yml: Change path to /json [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#1399](https://github.com/community-scripts/ProxmoxVE/pull/1399)) - Visual Studio Code: Set Workspace recommended extensions [@andygrunwald](https://github.com/andygrunwald) ([#1398](https://github.com/community-scripts/ProxmoxVE/pull/1398)) - [core]: add support for custom tags [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#1384](https://github.com/community-scripts/ProxmoxVE/pull/1384)) - [core]: check json date of new prs & update it [@MickLesk](https://github.com/MickLesk) ([#1395](https://github.com/community-scripts/ProxmoxVE/pull/1395)) - Add initial PR for Contributing & Coding Standard [@MickLesk](https://github.com/MickLesk) ([#920](https://github.com/community-scripts/ProxmoxVE/pull/920)) - [Core] add Github Action for Generate AppHeaders (figlet remove part 1) [@MickLesk](https://github.com/MickLesk) ([#1382](https://github.com/community-scripts/ProxmoxVE/pull/1382)) ## 2025-01-09 ### Changed ### 💥 Breaking Changes - Removal calibre-server (no Headless Support) [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#1362](https://github.com/community-scripts/ProxmoxVE/pull/1362)) ### ✨ New Scripts - New Script: Prometheus Alertmanager [@andygrunwald](https://github.com/andygrunwald) ([#1272](https://github.com/community-scripts/ProxmoxVE/pull/1272)) - New script: ps5-mqtt [@liecno](https://github.com/liecno) ([#1198](https://github.com/community-scripts/ProxmoxVE/pull/1198)) ### 🚀 Updated Scripts - Fix: AdventureLog: unzip to /opt/ [@JesperDramsch](https://github.com/JesperDramsch) ([#1370](https://github.com/community-scripts/ProxmoxVE/pull/1370)) - Fix: Stirling-PDF > LibreOffice/unoconv Integration Issues [@m6urns](https://github.com/m6urns) ([#1322](https://github.com/community-scripts/ProxmoxVE/pull/1322)) - Fix: AdventureLog - update script bug [@JesperDramsch](https://github.com/JesperDramsch) ([#1334](https://github.com/community-scripts/ProxmoxVE/pull/1334)) - Install/update ActualBudget based on releases, not latest master [@SpyrosRoum](https://github.com/SpyrosRoum) ([#1254](https://github.com/community-scripts/ProxmoxVE/pull/1254)) - Fix Checkmk: Version grep broken [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#1341](https://github.com/community-scripts/ProxmoxVE/pull/1341)) ### 🧰 Maintenance - fix: only validate scripts in validate-scripts workflow [@se-bastiaan](https://github.com/se-bastiaan) ([#1344](https://github.com/community-scripts/ProxmoxVE/pull/1344)) ## 2025-01-08 ### Changed ### 🌐 Website - update postgresql json to add post install password setup [@rdiazlugo](https://github.com/rdiazlugo) ([#1318](https://github.com/community-scripts/ProxmoxVE/pull/1318)) ### 🧰 Maintenance - fix(ci): formatting event & chmod +x [@se-bastiaan](https://github.com/se-bastiaan) ([#1335](https://github.com/community-scripts/ProxmoxVE/pull/1335)) - fix: correctly handle pull_request_target event [@se-bastiaan](https://github.com/se-bastiaan) ([#1327](https://github.com/community-scripts/ProxmoxVE/pull/1327)) ## 2025-01-07 ### Changed ### 🚀 Updated Scripts - Fix: Folder-Check for Updatescript Zammad [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#1309](https://github.com/community-scripts/ProxmoxVE/pull/1309)) ### 🧰 Maintenance - fix: permissions of validate pipelines [@se-bastiaan](https://github.com/se-bastiaan) ([#1316](https://github.com/community-scripts/ProxmoxVE/pull/1316)) - Set Execution Rights for GH-Action: Validate Scripts [@MickLesk](https://github.com/MickLesk) ([#1312](https://github.com/community-scripts/ProxmoxVE/pull/1312)) ## 2025-01-06 ### Changed ### ✨ New Scripts - New Script: Typesense [@tlissak](https://github.com/tlissak) ([#1291](https://github.com/community-scripts/ProxmoxVE/pull/1291)) - New script: GLPI [@opastorello](https://github.com/opastorello) ([#1201](https://github.com/community-scripts/ProxmoxVE/pull/1201)) ### 🚀 Updated Scripts - Fix Tag in HyperHDR Script [@MickLesk](https://github.com/MickLesk) ([#1299](https://github.com/community-scripts/ProxmoxVE/pull/1299)) - [Fix]: Fixed rm Bug in pf2etools [@MickLesk](https://github.com/MickLesk) ([#1292](https://github.com/community-scripts/ProxmoxVE/pull/1292)) - Fix: Homebox Update Script [@MickLesk](https://github.com/MickLesk) ([#1284](https://github.com/community-scripts/ProxmoxVE/pull/1284)) - Add ca-certificates for Install (Frigate) [@MickLesk](https://github.com/MickLesk) ([#1282](https://github.com/community-scripts/ProxmoxVE/pull/1282)) - fix: buffer from base64 in formatting pipeline [@se-bastiaan](https://github.com/se-bastiaan) ([#1285](https://github.com/community-scripts/ProxmoxVE/pull/1285)) ### 🧰 Maintenance - Add reapproval of Changelog-PR [@MickLesk](https://github.com/MickLesk) ([#1279](https://github.com/community-scripts/ProxmoxVE/pull/1279)) - ci: combine header checks into workflow with PR comment [@se-bastiaan](https://github.com/se-bastiaan) ([#1257](https://github.com/community-scripts/ProxmoxVE/pull/1257)) - ci: change filename checks into steps with PR comment [@se-bastiaan](https://github.com/se-bastiaan) ([#1255](https://github.com/community-scripts/ProxmoxVE/pull/1255)) - ci: add pipeline for code formatting checks [@se-bastiaan](https://github.com/se-bastiaan) ([#1239](https://github.com/community-scripts/ProxmoxVE/pull/1239)) ## 2025-01-05 ### Changed ### 💥 Breaking Changes - [Breaking] Update Zigbee2mqtt to v.2.0.0 (Read PR Description) [@MickLesk](https://github.com/MickLesk) ([#1221](https://github.com/community-scripts/ProxmoxVE/pull/1221)) ### ❔ Unlabelled - Add RAM and Disk units [@oOStroudyOo](https://github.com/oOStroudyOo) ([#1261](https://github.com/community-scripts/ProxmoxVE/pull/1261)) ## 2025-01-04 ### Changed ### 🚀 Updated Scripts - Fix gpg key pf2tools & 5etools [@MickLesk](https://github.com/MickLesk) ([#1242](https://github.com/community-scripts/ProxmoxVE/pull/1242)) - Homarr: Fix missing curl dependency [@MickLesk](https://github.com/MickLesk) ([#1238](https://github.com/community-scripts/ProxmoxVE/pull/1238)) - Homeassistan Core: Fix Python3 and add missing dependencies [@MickLesk](https://github.com/MickLesk) ([#1236](https://github.com/community-scripts/ProxmoxVE/pull/1236)) - Fix: Update Python for HomeAssistant [@MickLesk](https://github.com/MickLesk) ([#1227](https://github.com/community-scripts/ProxmoxVE/pull/1227)) - OneDev: Add git-lfs [@MickLesk](https://github.com/MickLesk) ([#1225](https://github.com/community-scripts/ProxmoxVE/pull/1225)) - Pf2eTools & 5eTools: Fixing npm build [@TheRealVira](https://github.com/TheRealVira) ([#1213](https://github.com/community-scripts/ProxmoxVE/pull/1213)) ### 🌐 Website - Bump next from 15.0.2 to 15.1.3 in /frontend [@dependabot[bot]](https://github.com/dependabot[bot]) ([#1212](https://github.com/community-scripts/ProxmoxVE/pull/1212)) ### 🧰 Maintenance - [GitHub Action] Add filename case check [@quantumryuu](https://github.com/quantumryuu) ([#1228](https://github.com/community-scripts/ProxmoxVE/pull/1228)) ## 2025-01-03 ### Changed ### 🚀 Updated Scripts - Improve Homarr Installation [@MickLesk](https://github.com/MickLesk) ([#1208](https://github.com/community-scripts/ProxmoxVE/pull/1208)) - Fix: Zabbix-Update Script [@MickLesk](https://github.com/MickLesk) ([#1205](https://github.com/community-scripts/ProxmoxVE/pull/1205)) - Update Script: Lazylibrarian [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#1190](https://github.com/community-scripts/ProxmoxVE/pull/1190)) - Fix: Memos update function [@MickLesk](https://github.com/MickLesk) ([#1207](https://github.com/community-scripts/ProxmoxVE/pull/1207)) - Keep Lubelogger data after update to a new version [@JcMinarro](https://github.com/JcMinarro) ([#1200](https://github.com/community-scripts/ProxmoxVE/pull/1200)) ### 🌐 Website - Update Nextcloud-LXC JSON [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#1191](https://github.com/community-scripts/ProxmoxVE/pull/1191)) ### 🧰 Maintenance - Github action to check metadata lines in scripts. [@quantumryuu](https://github.com/quantumryuu) ([#1110](https://github.com/community-scripts/ProxmoxVE/pull/1110)) ## 2025-01-02 ### Changed ### ✨ New Scripts - New Script: Pf2eTools [@TheRealVira](https://github.com/TheRealVira) ([#1162](https://github.com/community-scripts/ProxmoxVE/pull/1162)) - New Script: 5etools [@TheRealVira](https://github.com/TheRealVira) ([#1157](https://github.com/community-scripts/ProxmoxVE/pull/1157)) ### 🚀 Updated Scripts - Update config template in blocky-install.sh [@xFichtl1](https://github.com/xFichtl1) ([#1059](https://github.com/community-scripts/ProxmoxVE/pull/1059)) ## 2025-01-01 ### Changed ### ✨ New Scripts - New Script: Komodo [@MickLesk](https://github.com/MickLesk) ([#1167](https://github.com/community-scripts/ProxmoxVE/pull/1167)) - New Script: Firefly [@quantumryuu](https://github.com/quantumryuu) ([#616](https://github.com/community-scripts/ProxmoxVE/pull/616)) - New Script: Semaphore [@quantumryuu](https://github.com/quantumryuu) ([#596](https://github.com/community-scripts/ProxmoxVE/pull/596)) ### 🚀 Updated Scripts - Fix Script Homepage: add version during build step [@se-bastiaan](https://github.com/se-bastiaan) ([#1155](https://github.com/community-scripts/ProxmoxVE/pull/1155)) - Happy new Year! Update Copyright to 2025 [@MickLesk](https://github.com/MickLesk) ([#1150](https://github.com/community-scripts/ProxmoxVE/pull/1150)) - Update Kernel-Clean to new Version & Bugfixing [@MickLesk](https://github.com/MickLesk) ([#1147](https://github.com/community-scripts/ProxmoxVE/pull/1147)) - Fix chromium installation for ArchiveBox [@tkunzfeld](https://github.com/tkunzfeld) ([#1140](https://github.com/community-scripts/ProxmoxVE/pull/1140)) ### 🌐 Website - Fix Category of Semaphore [@MickLesk](https://github.com/MickLesk) ([#1148](https://github.com/community-scripts/ProxmoxVE/pull/1148)) ### 🧰 Maintenance - Correctly check for changed files in Shellcheck workflow [@se-bastiaan](https://github.com/se-bastiaan) ([#1156](https://github.com/community-scripts/ProxmoxVE/pull/1156)) ## 2024-12-31 - Happy new Year! 🎉✨ ### Changed ### 💥 Breaking Changes - Add ExecReload to prometheus.service [@BasixKOR](https://github.com/BasixKOR) ([#1131](https://github.com/community-scripts/ProxmoxVE/pull/1131)) - Fix: Figlet Version & Font Check [@MickLesk](https://github.com/MickLesk) ([#1133](https://github.com/community-scripts/ProxmoxVE/pull/1133)) ### 🚀 Updated Scripts - Fix: Copy issue after update in Bookstack LXC [@MickLesk](https://github.com/MickLesk) ([#1137](https://github.com/community-scripts/ProxmoxVE/pull/1137)) - Omada: Switch Base-URL to prevent issues [@MickLesk](https://github.com/MickLesk) ([#1135](https://github.com/community-scripts/ProxmoxVE/pull/1135)) - fix: guacd service not start during Apache-Guacamole script installation process [@PhoenixEmik](https://github.com/PhoenixEmik) ([#1122](https://github.com/community-scripts/ProxmoxVE/pull/1122)) - Fix Homepage-Script: Installation/Update [@MickLesk](https://github.com/MickLesk) ([#1129](https://github.com/community-scripts/ProxmoxVE/pull/1129)) - Netbox: Updating URL to https [@surajsbmn](https://github.com/surajsbmn) ([#1124](https://github.com/community-scripts/ProxmoxVE/pull/1124)) ================================================ FILE: .github/changelogs/2025/02.md ================================================ ## 2025-02-28 ### 🧰 Maintenance - #### ✨ New Features - Shell Format Workflow [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#2400](https://github.com/community-scripts/ProxmoxVE/pull/2400)) - #### 📂 Github - Update all Action to new selfhosted Runner Cluster [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#2739](https://github.com/community-scripts/ProxmoxVE/pull/2739)) - Update Script Test Workflow [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#2741](https://github.com/community-scripts/ProxmoxVE/pull/2741)) ## 2025-02-27 ### 🆕 New Scripts - web-check [@CrazyWolf13](https://github.com/CrazyWolf13) ([#2662](https://github.com/community-scripts/ProxmoxVE/pull/2662)) - Pelican Panel [@bvdberg01](https://github.com/bvdberg01) ([#2678](https://github.com/community-scripts/ProxmoxVE/pull/2678)) - Pelican Wings [@bvdberg01](https://github.com/bvdberg01) ([#2677](https://github.com/community-scripts/ProxmoxVE/pull/2677)) - ByteStash [@tremor021](https://github.com/tremor021) ([#2680](https://github.com/community-scripts/ProxmoxVE/pull/2680)) ### 🚀 Updated Scripts - ByteStash: Removed sed, app supports Node v22 now [@tremor021](https://github.com/tremor021) ([#2728](https://github.com/community-scripts/ProxmoxVE/pull/2728)) - Keycloak: Update installation script [@tremor021](https://github.com/tremor021) ([#2714](https://github.com/community-scripts/ProxmoxVE/pull/2714)) - ByteStash: Fix Node 22 compatibility (thanks t2lc) [@tremor021](https://github.com/tremor021) ([#2705](https://github.com/community-scripts/ProxmoxVE/pull/2705)) - #### 🐞 Bug Fixes - EOF not detected [@CrazyWolf13](https://github.com/CrazyWolf13) ([#2726](https://github.com/community-scripts/ProxmoxVE/pull/2726)) - Zitadel-install.sh: Remove one version file and update to our standard [@bvdberg01](https://github.com/bvdberg01) ([#2710](https://github.com/community-scripts/ProxmoxVE/pull/2710)) - Outline: Change key to hex32 [@tremor021](https://github.com/tremor021) ([#2709](https://github.com/community-scripts/ProxmoxVE/pull/2709)) - Typo in update scripts [@bvdberg01](https://github.com/bvdberg01) ([#2707](https://github.com/community-scripts/ProxmoxVE/pull/2707)) - SFTPGo Remove unneeded RELEASE variable [@MickLesk](https://github.com/MickLesk) ([#2683](https://github.com/community-scripts/ProxmoxVE/pull/2683)) ### 🧰 Maintenance - #### 🐞 Bug Fixes - Update install.func: Change Line Number for Error message. [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#2690](https://github.com/community-scripts/ProxmoxVE/pull/2690)) - #### 📂 Github - New Workflow to close Script Request Discussions on PR merge [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#2688](https://github.com/community-scripts/ProxmoxVE/pull/2688)) - Improve Script-Test Workflow [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#2712](https://github.com/community-scripts/ProxmoxVE/pull/2712)) - Switch all actions to self-hosted Runners [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#2711](https://github.com/community-scripts/ProxmoxVE/pull/2711)) ### 🌐 Website - #### ✨ New Features - Use HTML button element for copying to clipboard [@scallaway](https://github.com/scallaway) ([#2720](https://github.com/community-scripts/ProxmoxVE/pull/2720)) - Add basic pagination to Data Viewer [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#2715](https://github.com/community-scripts/ProxmoxVE/pull/2715)) - #### 📝 Script Information - wger - Add HTTPS instructions to the website [@tremor021](https://github.com/tremor021) ([#2695](https://github.com/community-scripts/ProxmoxVE/pull/2695)) ## 2025-02-26 ### 🆕 New Scripts - New Script: Outline [@tremor021](https://github.com/tremor021) ([#2653](https://github.com/community-scripts/ProxmoxVE/pull/2653)) ### 🚀 Updated Scripts - Fix: SABnzbd - Removed few artefacts in the code preventing the update [@tremor021](https://github.com/tremor021) ([#2670](https://github.com/community-scripts/ProxmoxVE/pull/2670)) - #### 🐞 Bug Fixes - Fix: Homarr - Manually correct db-migration wrong-folder [@CrazyWolf13](https://github.com/CrazyWolf13) ([#2676](https://github.com/community-scripts/ProxmoxVE/pull/2676)) - Kimai: add local.yaml & fix path permissions [@MickLesk](https://github.com/MickLesk) ([#2646](https://github.com/community-scripts/ProxmoxVE/pull/2646)) - PiHole: Fix Unbound sed for DNS [@MickLesk](https://github.com/MickLesk) ([#2647](https://github.com/community-scripts/ProxmoxVE/pull/2647)) - Alpine IT-Tools fix typo "unexpected EOF while looking for matching `"' [@MickLesk](https://github.com/MickLesk) ([#2644](https://github.com/community-scripts/ProxmoxVE/pull/2644)) ### 🧰 Maintenance - #### 📂 Github - [gh] Furhter Impove Changelog Workflow [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#2655](https://github.com/community-scripts/ProxmoxVE/pull/2655)) ### 🌐 Website - #### 🐞 Bug Fixes - Website: PocketID Change of website and documentation links [@schneider-de-com](https://github.com/schneider-de-com) ([#2643](https://github.com/community-scripts/ProxmoxVE/pull/2643)) - #### 📝 Script Information - Fix: Graylog - Improve application description for website [@tremor021](https://github.com/tremor021) ([#2658](https://github.com/community-scripts/ProxmoxVE/pull/2658)) ## 2025-02-25 ### Changes ### ✨ New Features - Update Tailscale: Add Tag when installation is finished [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#2633](https://github.com/community-scripts/ProxmoxVE/pull/2633)) ### 🚀 Updated Scripts #### 🐞 Bug Fixes - Fix Omada installer [@JcMinarro](https://github.com/JcMinarro) ([#2625](https://github.com/community-scripts/ProxmoxVE/pull/2625)) ### 🌐 Website - Update Tailscale-lxc Json: Add message for Supported OS [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#2629](https://github.com/community-scripts/ProxmoxVE/pull/2629)) ### 🧰 Maintenance - [gh] Updated Changelog Workflow [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#2632](https://github.com/community-scripts/ProxmoxVE/pull/2632)) ## 2025-02-24 ### Changes ### 🆕 New Scripts - New Script: wger [@tremor021](https://github.com/tremor021) ([#2574](https://github.com/community-scripts/ProxmoxVE/pull/2574)) - New Script: VictoriaMetrics [@tremor021](https://github.com/tremor021) ([#2565](https://github.com/community-scripts/ProxmoxVE/pull/2565)) - New Script: Authelia [@thost96](https://github.com/thost96) ([#2060](https://github.com/community-scripts/ProxmoxVE/pull/2060)) - New Script: Jupyter Notebook [@Dave-code-creater](https://github.com/Dave-code-creater) ([#2561](https://github.com/community-scripts/ProxmoxVE/pull/2561)) ### 🐞 Bug Fixes - Fix Docmost: default upload size and saving data when updating [@bvdberg01](https://github.com/bvdberg01) ([#2598](https://github.com/community-scripts/ProxmoxVE/pull/2598)) - Fix: homarr db migration [@CrazyWolf13](https://github.com/CrazyWolf13) ([#2575](https://github.com/community-scripts/ProxmoxVE/pull/2575)) - Fix: Wireguard - Restart wgdashboard automatically after update [@LostALice](https://github.com/LostALice) ([#2587](https://github.com/community-scripts/ProxmoxVE/pull/2587)) - Fix: Authelia Unbound Variable Argon2id [@MickLesk](https://github.com/MickLesk) ([#2604](https://github.com/community-scripts/ProxmoxVE/pull/2604)) - Fix: Omada check for AVX Support and use the correct MongoDB Version [@MickLesk](https://github.com/MickLesk) ([#2600](https://github.com/community-scripts/ProxmoxVE/pull/2600)) - Fix: Update-Script Firefly III based on their docs [@MickLesk](https://github.com/MickLesk) ([#2534](https://github.com/community-scripts/ProxmoxVE/pull/2534)) ### ✨ New Features - Feature: Template-Check, Better Handling of Downloads, Better Network… [@MickLesk](https://github.com/MickLesk) ([#2592](https://github.com/community-scripts/ProxmoxVE/pull/2592)) - Feature: Possibility to perform updates in silent / verbose (+ logging) [@MickLesk](https://github.com/MickLesk) ([#2583](https://github.com/community-scripts/ProxmoxVE/pull/2583)) - Feature: Use Verbose Mode for all Scripts (removed &>/dev/null) [@MickLesk](https://github.com/MickLesk) ([#2596](https://github.com/community-scripts/ProxmoxVE/pull/2596)) ### 🌐 Website - Fix: Authelia - Make user enter their domain manually [@tremor021](https://github.com/tremor021) ([#2618](https://github.com/community-scripts/ProxmoxVE/pull/2618)) - Website: Change Info for PiHole Password [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#2602](https://github.com/community-scripts/ProxmoxVE/pull/2602)) - Fix: Jupyter Json (missing logo & improve name on website) [@MickLesk](https://github.com/MickLesk) ([#2584](https://github.com/community-scripts/ProxmoxVE/pull/2584)) ### 🧰 Maintenance - [gh] Update Script Test Workflow [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#2599](https://github.com/community-scripts/ProxmoxVE/pull/2599)) - [gh] Contributor-Guide: Update AppName.md & AppName.sh [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#2603](https://github.com/community-scripts/ProxmoxVE/pull/2603)) ## 2025-02-23 ### Changes ### 🆕 New Scripts - New Script: Hev socks5 server [@miviro](https://github.com/miviro) ([#2454](https://github.com/community-scripts/ProxmoxVE/pull/2454)) - New Script: bolt.diy [@tremor021](https://github.com/tremor021) ([#2528](https://github.com/community-scripts/ProxmoxVE/pull/2528)) ### 🚀 Updated Scripts - Fix: Wireguard - Remove setting NAT as its already in PostUp/Down [@tremor021](https://github.com/tremor021) ([#2510](https://github.com/community-scripts/ProxmoxVE/pull/2510)) ### 🌐 Website - Fix: Home Assistant Core - fixed wrong text in application description on website [@TMigue](https://github.com/TMigue) ([#2576](https://github.com/community-scripts/ProxmoxVE/pull/2576)) ## 2025-02-22 ### Changes ### 🌐 Website - Fix a few broken icon links [@Snarkenfaugister](https://github.com/Snarkenfaugister) ([#2548](https://github.com/community-scripts/ProxmoxVE/pull/2548)) ### 🧰 Maintenance - Fix: URL's in CONTRIBUTING.md [@bvdberg01](https://github.com/bvdberg01) ([#2552](https://github.com/community-scripts/ProxmoxVE/pull/2552)) ## 2025-02-21 ### Changes ### 🚀 Updated Scripts - Add ZFS to Podman. Now it works on ZFS! [@jaminmc](https://github.com/jaminmc) ([#2526](https://github.com/community-scripts/ProxmoxVE/pull/2526)) - Fix: Tianji - Downgrade Node [@MickLesk](https://github.com/MickLesk) ([#2530](https://github.com/community-scripts/ProxmoxVE/pull/2530)) ### 🧰 Maintenance - [gh] General Cleanup & Moving Files / Folders [@MickLesk](https://github.com/MickLesk) ([#2532](https://github.com/community-scripts/ProxmoxVE/pull/2532)) ## 2025-02-20 ### Changes ### 💥 Breaking Changes - Breaking: Actual Budget Script (HTTPS / DB Migration / New Structure) - Read Description [@MickLesk](https://github.com/MickLesk) ([#2496](https://github.com/community-scripts/ProxmoxVE/pull/2496)) - Pihole & Unbound: Installation for Pihole V6 (read description) [@MickLesk](https://github.com/MickLesk) ([#2505](https://github.com/community-scripts/ProxmoxVE/pull/2505)) ### ✨ New Scripts - New Script: Dolibarr [@tremor021](https://github.com/tremor021) ([#2502](https://github.com/community-scripts/ProxmoxVE/pull/2502)) ### 🚀 Updated Scripts - Fix: Pingvin Share - Update not copying to correct directory [@tremor021](https://github.com/tremor021) ([#2521](https://github.com/community-scripts/ProxmoxVE/pull/2521)) - WikiJS: Prepare for Using PostgreSQL [@MickLesk](https://github.com/MickLesk) ([#2516](https://github.com/community-scripts/ProxmoxVE/pull/2516)) ### 🧰 Maintenance - [gh] better handling of labels [@MickLesk](https://github.com/MickLesk) ([#2517](https://github.com/community-scripts/ProxmoxVE/pull/2517)) ## 2025-02-19 ### Changes ### 🚀 Updated Scripts - Fix: file replacement in Watcharr Update Script [@Clusters](https://github.com/Clusters) ([#2498](https://github.com/community-scripts/ProxmoxVE/pull/2498)) - Fix: Kometa - fixed successful setup message and added info to json [@tremor021](https://github.com/tremor021) ([#2495](https://github.com/community-scripts/ProxmoxVE/pull/2495)) - Fix: Actual Budget, add missing .env when updating [@MickLesk](https://github.com/MickLesk) ([#2494](https://github.com/community-scripts/ProxmoxVE/pull/2494)) ## 2025-02-18 ### Changes ### ✨ New Scripts - New Script: Docmost [@MickLesk](https://github.com/MickLesk) ([#2472](https://github.com/community-scripts/ProxmoxVE/pull/2472)) ### 🚀 Updated Scripts - Fix: SQL Server 2022 | GPG & Install [@MickLesk](https://github.com/MickLesk) ([#2476](https://github.com/community-scripts/ProxmoxVE/pull/2476)) - Feature: PBS Bare Metal Installation - Allow Microcode [@MickLesk](https://github.com/MickLesk) ([#2477](https://github.com/community-scripts/ProxmoxVE/pull/2477)) - Fix: MagicMirror force Node version and fix backups [@tremor021](https://github.com/tremor021) ([#2468](https://github.com/community-scripts/ProxmoxVE/pull/2468)) - Update BunkerWeb scripts to latest NGINX and specs [@TheophileDiot](https://github.com/TheophileDiot) ([#2466](https://github.com/community-scripts/ProxmoxVE/pull/2466)) ## 2025-02-17 ### Changes ### 💥 Breaking Changes - Zipline: Prepare for Version 4.0.0 [@MickLesk](https://github.com/MickLesk) ([#2455](https://github.com/community-scripts/ProxmoxVE/pull/2455)) ### 🚀 Updated Scripts - Fix: Zipline increase SECRET to 42 chars [@V1d1o7](https://github.com/V1d1o7) ([#2444](https://github.com/community-scripts/ProxmoxVE/pull/2444)) ## 2025-02-16 ### Changes ### 🚀 Updated Scripts - Fix: Typo in Ubuntu 24.10 VM Script [@PhoenixEmik](https://github.com/PhoenixEmik) ([#2430](https://github.com/community-scripts/ProxmoxVE/pull/2430)) - Fix: Grist update no longer removes previous user data [@cfurrow](https://github.com/cfurrow) ([#2428](https://github.com/community-scripts/ProxmoxVE/pull/2428)) ### 🌐 Website - Debian icon update [@bannert1337](https://github.com/bannert1337) ([#2433](https://github.com/community-scripts/ProxmoxVE/pull/2433)) - Update Graylog icon [@bannert1337](https://github.com/bannert1337) ([#2434](https://github.com/community-scripts/ProxmoxVE/pull/2434)) ## 2025-02-15 ### Changes ### 🚀 Updated Scripts - Setup cron in install/freshrss-install.sh [@zimmra](https://github.com/zimmra) ([#2412](https://github.com/community-scripts/ProxmoxVE/pull/2412)) - Fix: Homarr update service files [@CrazyWolf13](https://github.com/CrazyWolf13) ([#2416](https://github.com/community-scripts/ProxmoxVE/pull/2416)) - Update MagicMirror install and update scripts [@tremor021](https://github.com/tremor021) ([#2409](https://github.com/community-scripts/ProxmoxVE/pull/2409)) ### 🌐 Website - Fix RustDesk slug in json [@tremor021](https://github.com/tremor021) ([#2411](https://github.com/community-scripts/ProxmoxVE/pull/2411)) ### 🧰 Maintenance - [GH] Update script-test Workflow [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#2415](https://github.com/community-scripts/ProxmoxVE/pull/2415)) ## 2025-02-14 ### Changes ### 🚀 Updated Scripts - Fix homarr [@CrazyWolf13](https://github.com/CrazyWolf13) ([#2369](https://github.com/community-scripts/ProxmoxVE/pull/2369)) ### 🌐 Website - RustDesk Server - Added configuration guide to json [@tremor021](https://github.com/tremor021) ([#2389](https://github.com/community-scripts/ProxmoxVE/pull/2389)) ### 🧰 Maintenance - [gh] Update script-test.yml [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#2399](https://github.com/community-scripts/ProxmoxVE/pull/2399)) - [gh] Introducing new Issue Github Template Feature (Bug, Feature, Task) [@MickLesk](https://github.com/MickLesk) ([#2394](https://github.com/community-scripts/ProxmoxVE/pull/2394)) ### 📡 API - [API]Add more enpoints to API [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#2390](https://github.com/community-scripts/ProxmoxVE/pull/2390)) - [API] Update api.func: Remove unwanted file creation [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#2378](https://github.com/community-scripts/ProxmoxVE/pull/2378)) ## 2025-02-13 ### Changes ### ✨ New Scripts - Re-Add: Pf2eTools [@MickLesk](https://github.com/MickLesk) ([#2336](https://github.com/community-scripts/ProxmoxVE/pull/2336)) - New Script: Nx Witness [@MickLesk](https://github.com/MickLesk) ([#2350](https://github.com/community-scripts/ProxmoxVE/pull/2350)) - New Script: RustDesk Server [@tremor021](https://github.com/tremor021) ([#2326](https://github.com/community-scripts/ProxmoxVE/pull/2326)) - New Script: MinIO [@MickLesk](https://github.com/MickLesk) ([#2333](https://github.com/community-scripts/ProxmoxVE/pull/2333)) ### 🚀 Updated Scripts - Missing ";" in ct/trilium.sh [@Scorpoon](https://github.com/Scorpoon) ([#2380](https://github.com/community-scripts/ProxmoxVE/pull/2380)) - Fix: Element Synapse - Fixed server listening on both localhost and 0.0.0.0 [@tremor021](https://github.com/tremor021) ([#2376](https://github.com/community-scripts/ProxmoxVE/pull/2376)) - [core] cleanup (remove # App Default Values) [@MickLesk](https://github.com/MickLesk) ([#2356](https://github.com/community-scripts/ProxmoxVE/pull/2356)) - Fix: Kometa - Increase RAM and HDD resources [@tremor021](https://github.com/tremor021) ([#2367](https://github.com/community-scripts/ProxmoxVE/pull/2367)) - [core] cleanup (remove base_settings & unneeded comments) [@MickLesk](https://github.com/MickLesk) ([#2351](https://github.com/community-scripts/ProxmoxVE/pull/2351)) - Fix: Authentik Embedded Outpost Upgrade [@vidonnus](https://github.com/vidonnus) ([#2327](https://github.com/community-scripts/ProxmoxVE/pull/2327)) - Fix HomeAsisstant LXC: Use the latest versions of runlike with --use-volume-id [@genehand](https://github.com/genehand) ([#2325](https://github.com/community-scripts/ProxmoxVE/pull/2325)) ### 🌐 Website - Fix: Zoraxy - now shows application as updateable on the website [@tremor021](https://github.com/tremor021) ([#2352](https://github.com/community-scripts/ProxmoxVE/pull/2352)) - Fix script category name text alignment in ScriptAccordion [@BramSuurdje](https://github.com/BramSuurdje) ([#2342](https://github.com/community-scripts/ProxmoxVE/pull/2342)) ### 🧰 Maintenance - [gh] Remove unwanted output from script test workflow [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#2337](https://github.com/community-scripts/ProxmoxVE/pull/2337)) - [gh] Workflow to change date on new json files [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#2319](https://github.com/community-scripts/ProxmoxVE/pull/2319)) ## 2025-02-12 ### Changes ### 💥 Breaking Changes - Frigate: Use Fixed Version 14 [@MickLesk](https://github.com/MickLesk) ([#2288](https://github.com/community-scripts/ProxmoxVE/pull/2288)) ### ✨ New Scripts - New Script: Kometa [@tremor021](https://github.com/tremor021) ([#2281](https://github.com/community-scripts/ProxmoxVE/pull/2281)) - New Script: Excalidraw [@tremor021](https://github.com/tremor021) ([#2285](https://github.com/community-scripts/ProxmoxVE/pull/2285)) - New Script: Graylog [@tremor021](https://github.com/tremor021) ([#2270](https://github.com/community-scripts/ProxmoxVE/pull/2270)) - New Script: TasmoCompiler [@tremor021](https://github.com/tremor021) ([#2235](https://github.com/community-scripts/ProxmoxVE/pull/2235)) - New script: cross-seed [@jmatraszek](https://github.com/jmatraszek) ([#2186](https://github.com/community-scripts/ProxmoxVE/pull/2186)) ### 🚀 Updated Scripts - FIX: Frigate - remove bad variable [@tremor021](https://github.com/tremor021) ([#2323](https://github.com/community-scripts/ProxmoxVE/pull/2323)) - Fix: Kometa - Fix wrong web site address [@tremor021](https://github.com/tremor021) ([#2318](https://github.com/community-scripts/ProxmoxVE/pull/2318)) - Fix: var_tags instead of TAGS in some CT's [@MickLesk](https://github.com/MickLesk) ([#2310](https://github.com/community-scripts/ProxmoxVE/pull/2310)) - Update ubuntu2410-vm.sh: Fix typo in API call. [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#2305](https://github.com/community-scripts/ProxmoxVE/pull/2305)) - Fix: Myspeed Installation (g++) [@MickLesk](https://github.com/MickLesk) ([#2308](https://github.com/community-scripts/ProxmoxVE/pull/2308)) - Fix: Pingvin wrong variable used for version tracking [@alberanid](https://github.com/alberanid) ([#2302](https://github.com/community-scripts/ProxmoxVE/pull/2302)) - Fix: SQL Server 2022 - remove unnecessary sudo [@tremor021](https://github.com/tremor021) ([#2282](https://github.com/community-scripts/ProxmoxVE/pull/2282)) - fix: frigate pin version [@CrazyWolf13](https://github.com/CrazyWolf13) ([#2296](https://github.com/community-scripts/ProxmoxVE/pull/2296)) - Fix changedetection: Correct Browser install [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#2277](https://github.com/community-scripts/ProxmoxVE/pull/2277)) - Paperless-AI: add dependency "make" [@MickLesk](https://github.com/MickLesk) ([#2289](https://github.com/community-scripts/ProxmoxVE/pull/2289)) - Fix: Typo OPNsense VM [@chpego](https://github.com/chpego) ([#2291](https://github.com/community-scripts/ProxmoxVE/pull/2291)) - Fix: CraftyControler fix java default [@CrazyWolf13](https://github.com/CrazyWolf13) ([#2286](https://github.com/community-scripts/ProxmoxVE/pull/2286)) ### 🌐 Website - Fix: some jsons (debian instead Debian in OS) [@MickLesk](https://github.com/MickLesk) ([#2311](https://github.com/community-scripts/ProxmoxVE/pull/2311)) - Website: Add After-Install Note for Ubuntu VM 22.04/24.04/24.10 and Debian VM [@MickLesk](https://github.com/MickLesk) ([#2307](https://github.com/community-scripts/ProxmoxVE/pull/2307)) - Fix: duplicate 'VM' name in opnsense-vm.json [@nayzm](https://github.com/nayzm) ([#2293](https://github.com/community-scripts/ProxmoxVE/pull/2293)) ## 2025-02-11 ### Changes ### ✨ New Scripts - New Script: Opnsense VM [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#2097](https://github.com/community-scripts/ProxmoxVE/pull/2097)) - New Script: Watcharr [@tremor021](https://github.com/tremor021) ([#2243](https://github.com/community-scripts/ProxmoxVE/pull/2243)) - New Script: Suwayomi-Server [@tremor021](https://github.com/tremor021) ([#2139](https://github.com/community-scripts/ProxmoxVE/pull/2139)) ### 🚀 Updated Scripts - Fix Photoprism: Add defaults.yml for CLI Tool [@MickLesk](https://github.com/MickLesk) ([#2261](https://github.com/community-scripts/ProxmoxVE/pull/2261)) - Update Checkmk: include Patch versions in Release grepping [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#2264](https://github.com/community-scripts/ProxmoxVE/pull/2264)) - Fix: Apache Guacamole Version Crawling - only latest Version [@MickLesk](https://github.com/MickLesk) ([#2258](https://github.com/community-scripts/ProxmoxVE/pull/2258)) ### 🌐 Website - Update Komodo icon [@bannert1337](https://github.com/bannert1337) ([#2263](https://github.com/community-scripts/ProxmoxVE/pull/2263)) ### 🧰 Maintenance - Add Workflow to test Scripts [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#2269](https://github.com/community-scripts/ProxmoxVE/pull/2269)) ## 2025-02-10 ### Changes ### 💥 Breaking Changes - [Fix] Filebrowser - Add Static Path for DB [@MickLesk](https://github.com/MickLesk) ([#2207](https://github.com/community-scripts/ProxmoxVE/pull/2207)) ### ✨ New Scripts - New Script: Prometheus Paperless-NGX Exporter [@andygrunwald](https://github.com/andygrunwald) ([#2153](https://github.com/community-scripts/ProxmoxVE/pull/2153)) - New Script: Proxmox Mail Gateway [@thost96](https://github.com/thost96) ([#1906](https://github.com/community-scripts/ProxmoxVE/pull/1906)) - New Script: FreshRSS [@bvdberg01](https://github.com/bvdberg01) ([#2226](https://github.com/community-scripts/ProxmoxVE/pull/2226)) - New Script: Zitadel [@dave-yap](https://github.com/dave-yap) ([#2141](https://github.com/community-scripts/ProxmoxVE/pull/2141)) ### 🚀 Updated Scripts - Feature: Automatic Deletion of choosen LXC's (lxc-delete.sh) [@MickLesk](https://github.com/MickLesk) ([#2228](https://github.com/community-scripts/ProxmoxVE/pull/2228)) - Quickfix: Crafty-Controller remove unnecessary \ [@MickLesk](https://github.com/MickLesk) ([#2233](https://github.com/community-scripts/ProxmoxVE/pull/2233)) - Fix: Crafty-Controller java versions and set default [@CrazyWolf13](https://github.com/CrazyWolf13) ([#2199](https://github.com/community-scripts/ProxmoxVE/pull/2199)) - Feature: Add optional Port for Filebrowser [@MickLesk](https://github.com/MickLesk) ([#2224](https://github.com/community-scripts/ProxmoxVE/pull/2224)) - [core] Prevent double spinner [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#2203](https://github.com/community-scripts/ProxmoxVE/pull/2203)) ### 🌐 Website - Website: Fix Zitadel Logo & Created-Date [@MickLesk](https://github.com/MickLesk) ([#2217](https://github.com/community-scripts/ProxmoxVE/pull/2217)) - Fixed URL typo zerotier-one.json [@Divaksh](https://github.com/Divaksh) ([#2206](https://github.com/community-scripts/ProxmoxVE/pull/2206)) - evcc.json Clarify the config file location [@mvdw](https://github.com/mvdw) ([#2193](https://github.com/community-scripts/ProxmoxVE/pull/2193)) ### 🧰 Maintenance - [gh]: Improve Workflows, Templates, Handling [@MickLesk](https://github.com/MickLesk) ([#2214](https://github.com/community-scripts/ProxmoxVE/pull/2214)) - [core] Fix app-header workflow and add API [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#2204](https://github.com/community-scripts/ProxmoxVE/pull/2204)) - Fix: "read -p" does not support color formatting [@PhoenixEmik](https://github.com/PhoenixEmik) ([#2191](https://github.com/community-scripts/ProxmoxVE/pull/2191)) - [API] Add API to vms [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#2021](https://github.com/community-scripts/ProxmoxVE/pull/2021)) ## 2025-02-09 ### Changed ### ✨ New Scripts - New Script: pbs_microcode.sh [@DonPablo1010](https://github.com/DonPablo1010) ([#2166](https://github.com/community-scripts/ProxmoxVE/pull/2166)) ### 🚀 Updated Scripts - Keep the same hass_config volume for Home Assistant [@genehand](https://github.com/genehand) ([#2160](https://github.com/community-scripts/ProxmoxVE/pull/2160)) ### 🌐 Website - Website: Set new Logo for Paperless-AI [@MickLesk](https://github.com/MickLesk) ([#2194](https://github.com/community-scripts/ProxmoxVE/pull/2194)) - Fix: Barcode Buddy Logo & Title [@MickLesk](https://github.com/MickLesk) ([#2183](https://github.com/community-scripts/ProxmoxVE/pull/2183)) ## 2025-02-08 ### Changed ### ✨ New Scripts - New script: Barcode Buddy [@bvdberg01](https://github.com/bvdberg01) ([#2167](https://github.com/community-scripts/ProxmoxVE/pull/2167)) ### 🚀 Updated Scripts - Fix: Actualbudget - salvage the `.migrate` file when upgrading [@bourquep](https://github.com/bourquep) ([#2173](https://github.com/community-scripts/ProxmoxVE/pull/2173)) ### 🌐 Website - Update cosmos.json description [@BramSuurdje](https://github.com/BramSuurdje) ([#2162](https://github.com/community-scripts/ProxmoxVE/pull/2162)) ### 🧰 Maintenance - fix typos in CONTRIBUTOR_GUIDE [@thomashondema](https://github.com/thomashondema) ([#2174](https://github.com/community-scripts/ProxmoxVE/pull/2174)) ## 2025-02-07 - 10.000 ⭐ ### Changed ### 💥 Breaking Changes - [core]: Enhance LXC template handling and improve error recovery [@MickLesk](https://github.com/MickLesk) ([#2128](https://github.com/community-scripts/ProxmoxVE/pull/2128)) ### ✨ New Scripts - New Script: Cosmos [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#2120](https://github.com/community-scripts/ProxmoxVE/pull/2120)) - New Script: SearXNG [@MickLesk](https://github.com/MickLesk) ([#2123](https://github.com/community-scripts/ProxmoxVE/pull/2123)) ### 🚀 Updated Scripts - Fix: Trillium Update Function & Harmonize Installation [@MickLesk](https://github.com/MickLesk) ([#2148](https://github.com/community-scripts/ProxmoxVE/pull/2148)) - Fix: Zerotier-One fixed missing dependency [@tremor021](https://github.com/tremor021) ([#2147](https://github.com/community-scripts/ProxmoxVE/pull/2147)) - Fix: Openwrt Version checking [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#2137](https://github.com/community-scripts/ProxmoxVE/pull/2137)) - Fix: PeaNUT Increase HDD & RAM Size [@MickLesk](https://github.com/MickLesk) ([#2127](https://github.com/community-scripts/ProxmoxVE/pull/2127)) ### 🌐 Website - Fix: Zerotier json had a bad script path [@tremor021](https://github.com/tremor021) ([#2144](https://github.com/community-scripts/ProxmoxVE/pull/2144)) - Fix: Cosmos logo doesnt display on website [@MickLesk](https://github.com/MickLesk) ([#2132](https://github.com/community-scripts/ProxmoxVE/pull/2132)) - Fix JSON-Editor [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#2121](https://github.com/community-scripts/ProxmoxVE/pull/2121)) ### 🧰 Maintenance - [gh]: Following the trend - add star-history in readme [@MickLesk](https://github.com/MickLesk) ([#2135](https://github.com/community-scripts/ProxmoxVE/pull/2135)) ## 2025-02-06 ### Changed ### ✨ New Scripts - New Script: Duplicati [@tremor021](https://github.com/tremor021) ([#2052](https://github.com/community-scripts/ProxmoxVE/pull/2052)) - New Script: Paperless-AI [@MickLesk](https://github.com/MickLesk) ([#2093](https://github.com/community-scripts/ProxmoxVE/pull/2093)) - New Script: Apache Tika [@andygrunwald](https://github.com/andygrunwald) ([#2079](https://github.com/community-scripts/ProxmoxVE/pull/2079)) ### 🚀 Updated Scripts - Fix: Alpine IT-Tools Update [@MickLesk](https://github.com/MickLesk) ([#2067](https://github.com/community-scripts/ProxmoxVE/pull/2067)) - Fix: Pocket-ID Change link to GH Repo [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#2082](https://github.com/community-scripts/ProxmoxVE/pull/2082)) ### 🌐 Website - Refactor JSON generator buttons layout for better alignment and consistency [@BramSuurdje](https://github.com/BramSuurdje) ([#2106](https://github.com/community-scripts/ProxmoxVE/pull/2106)) - Website: Refactor Footer for improved layout and styling consistency [@BramSuurdje](https://github.com/BramSuurdje) ([#2107](https://github.com/community-scripts/ProxmoxVE/pull/2107)) - Website: Update Footer for Json-Editor & Api [@MickLesk](https://github.com/MickLesk) ([#2100](https://github.com/community-scripts/ProxmoxVE/pull/2100)) - Website: Add Download for json-editor [@MickLesk](https://github.com/MickLesk) ([#2099](https://github.com/community-scripts/ProxmoxVE/pull/2099)) - Radicale: Provide additional information about configuration [@tremor021](https://github.com/tremor021) ([#2072](https://github.com/community-scripts/ProxmoxVE/pull/2072)) ## 2025-02-05 ### Changed ### ✨ New Scripts - New Script: Zerotier Controller [@tremor021](https://github.com/tremor021) ([#1928](https://github.com/community-scripts/ProxmoxVE/pull/1928)) - New Script: Radicale [@tremor021](https://github.com/tremor021) ([#1941](https://github.com/community-scripts/ProxmoxVE/pull/1941)) - New Script: seelf [@tremor021](https://github.com/tremor021) ([#2023](https://github.com/community-scripts/ProxmoxVE/pull/2023)) - New Script: Crafty-Controller [@CrazyWolf13](https://github.com/CrazyWolf13) ([#1926](https://github.com/community-scripts/ProxmoxVE/pull/1926)) - New script: Koillection [@bvdberg01](https://github.com/bvdberg01) ([#2031](https://github.com/community-scripts/ProxmoxVE/pull/2031)) ### 🚀 Updated Scripts - Bugfix: Jellyseerr pnpm Version [@vidonnus](https://github.com/vidonnus) ([#2033](https://github.com/community-scripts/ProxmoxVE/pull/2033)) - Radicale: Fixed missing htpasswd flag [@tremor021](https://github.com/tremor021) ([#2065](https://github.com/community-scripts/ProxmoxVE/pull/2065)) - [API] Update build.func / Improve error messages #2 [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#2050](https://github.com/community-scripts/ProxmoxVE/pull/2050)) - [API] Update create-lxc.sh / Improve error messages #1 [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#2049](https://github.com/community-scripts/ProxmoxVE/pull/2049)) - Feature: Element Synapse: add option to enter server name during LXC installation [@tremor021](https://github.com/tremor021) ([#2038](https://github.com/community-scripts/ProxmoxVE/pull/2038)) ### 🌐 Website - Paperless NGX: Mark it as updateable [@andygrunwald](https://github.com/andygrunwald) ([#2070](https://github.com/community-scripts/ProxmoxVE/pull/2070)) - Bump vitest from 2.1.6 to 2.1.9 in /frontend [@dependabot[bot]](https://github.com/dependabot[bot]) ([#2042](https://github.com/community-scripts/ProxmoxVE/pull/2042)) ### 🧰 Maintenance - [API] Add API backend code [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#2040](https://github.com/community-scripts/ProxmoxVE/pull/2040)) - Update auto-update-app-headers.yml: Enable auto approval [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#2057](https://github.com/community-scripts/ProxmoxVE/pull/2057)) ## 2025-02-04 ### Changed ### 💥 Breaking Changes - Rename & Optimize: Proxmox Backup Server (Renaming & Update fix) [@thost96](https://github.com/thost96) ([#2012](https://github.com/community-scripts/ProxmoxVE/pull/2012)) ### 🚀 Updated Scripts - Fix: Authentik - Remove deprecated GO-Remove in Footer [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#2020](https://github.com/community-scripts/ProxmoxVE/pull/2020)) - Fix: Authentik Fix wrong HDD Size [@thost96](https://github.com/thost96) ([#2001](https://github.com/community-scripts/ProxmoxVE/pull/2001)) - Fix: Tandoor - node Version [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#2010](https://github.com/community-scripts/ProxmoxVE/pull/2010)) - Fix actual update - missing hidden files, downloaded release cleanup [@maciejmatczak](https://github.com/maciejmatczak) ([#2027](https://github.com/community-scripts/ProxmoxVE/pull/2027)) - Fix Script: post-pmg-install.sh [@thost96](https://github.com/thost96) ([#2022](https://github.com/community-scripts/ProxmoxVE/pull/2022)) - Fix Tianji: Add heap-space value for nodejs [@MickLesk](https://github.com/MickLesk) ([#2011](https://github.com/community-scripts/ProxmoxVE/pull/2011)) - Fix: Ghost LXC - Use Node20 [@MickLesk](https://github.com/MickLesk) ([#2006](https://github.com/community-scripts/ProxmoxVE/pull/2006)) ### 🌐 Website - [API] Massive update to api (remove many, optimize website for users) [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#1990](https://github.com/community-scripts/ProxmoxVE/pull/1990)) ### 🧰 Maintenance - Fix header comments on contributor templates [@tremor021](https://github.com/tremor021) ([#2029](https://github.com/community-scripts/ProxmoxVE/pull/2029)) - [Fix]: Headername of Proxmox-Datacenter-Manager not in CamelCase [@MickLesk](https://github.com/MickLesk) ([#2017](https://github.com/community-scripts/ProxmoxVE/pull/2017)) - [Fix] Header breaks at long title - add width for figlet github action [@MickLesk](https://github.com/MickLesk) ([#2015](https://github.com/community-scripts/ProxmoxVE/pull/2015)) ## 2025-02-03 ### Changed ### ✨ New Scripts - New Script: Element Synapse [@tremor021](https://github.com/tremor021) ([#1955](https://github.com/community-scripts/ProxmoxVE/pull/1955)) - New Script: Privatebin [@opastorello](https://github.com/opastorello) ([#1925](https://github.com/community-scripts/ProxmoxVE/pull/1925)) ### 🚀 Updated Scripts - Fix: Monica Install with nodejs [@MickLesk](https://github.com/MickLesk) ([#1996](https://github.com/community-scripts/ProxmoxVE/pull/1996)) - Element Synapse sed fix [@tremor021](https://github.com/tremor021) ([#1994](https://github.com/community-scripts/ProxmoxVE/pull/1994)) - Fix Hoarder corepack install/update error [@vhsdream](https://github.com/vhsdream) ([#1957](https://github.com/community-scripts/ProxmoxVE/pull/1957)) - [Security & Maintenance] Update NodeJS Repo to 22 for new Installs [@MickLesk](https://github.com/MickLesk) ([#1984](https://github.com/community-scripts/ProxmoxVE/pull/1984)) - [Standardization]: Same Setup for GoLang on all LXC's & Clear Tarball [@MickLesk](https://github.com/MickLesk) ([#1977](https://github.com/community-scripts/ProxmoxVE/pull/1977)) - Feature: urbackupserver Include fuse&nesting features during install [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#1968](https://github.com/community-scripts/ProxmoxVE/pull/1968)) - Fix: MSSQL-Server: Better gpg handling [@MickLesk](https://github.com/MickLesk) ([#1962](https://github.com/community-scripts/ProxmoxVE/pull/1962)) - Fix: Grist ran into a heap space during the update [@MickLesk](https://github.com/MickLesk) ([#1964](https://github.com/community-scripts/ProxmoxVE/pull/1964)) - Fix: FS-Trim Cancel / Error-Button [@MickLesk](https://github.com/MickLesk) ([#1965](https://github.com/community-scripts/ProxmoxVE/pull/1965)) - Fix: Increase HDD Space for Hoarder [@MickLesk](https://github.com/MickLesk) ([#1970](https://github.com/community-scripts/ProxmoxVE/pull/1970)) - Feature: Clean Orphan LVM without CEPH [@MickLesk](https://github.com/MickLesk) ([#1974](https://github.com/community-scripts/ProxmoxVE/pull/1974)) - [Standardization] Fix Spelling for "Setup Python3" [@MickLesk](https://github.com/MickLesk) ([#1975](https://github.com/community-scripts/ProxmoxVE/pull/1975)) ### 🌐 Website - [Website] update data/page.tsx [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#1969](https://github.com/community-scripts/ProxmoxVE/pull/1969)) - Prometheus Proxmox VE Exporter: Set correct website slug [@andygrunwald](https://github.com/andygrunwald) ([#1961](https://github.com/community-scripts/ProxmoxVE/pull/1961)) ### 🧰 Maintenance - [API] Remove Hostname, Verbose, SSH and TAGS [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#1967](https://github.com/community-scripts/ProxmoxVE/pull/1967)) ## 2025-02-02 ### Changed ### 🚀 Updated Scripts - Prometheus PVE Exporter: Add `--default-timeout=300` to pip install commands [@andygrunwald](https://github.com/andygrunwald) ([#1950](https://github.com/community-scripts/ProxmoxVE/pull/1950)) - fix z2m update function to 2.1.0 [@MickLesk](https://github.com/MickLesk) ([#1938](https://github.com/community-scripts/ProxmoxVE/pull/1938)) ### 🧰 Maintenance - VSCode: Add Shellscript Syntax highlighting for *.func files [@andygrunwald](https://github.com/andygrunwald) ([#1948](https://github.com/community-scripts/ProxmoxVE/pull/1948)) ## 2025-02-01 ### Changed ### 💥 Breaking Changes - [DCMA] Delete scripts 5etools and pf2etools - Copyright abuse [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#1922](https://github.com/community-scripts/ProxmoxVE/pull/1922)) ### ✨ New Scripts - New script: Baïkal [@bvdberg01](https://github.com/bvdberg01) ([#1913](https://github.com/community-scripts/ProxmoxVE/pull/1913)) ### 🚀 Updated Scripts - Bug fix: Paymenter [@opastorello](https://github.com/opastorello) ([#1917](https://github.com/community-scripts/ProxmoxVE/pull/1917)) ================================================ FILE: .github/changelogs/2025/03.md ================================================ ## 2025-03-31 ### 🆕 New Scripts - slskd [@vhsdream](https://github.com/vhsdream) ([#3516](https://github.com/community-scripts/ProxmoxVE/pull/3516)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - The Lounge: Fix sqlite3 failing to build [@tremor021](https://github.com/tremor021) ([#3542](https://github.com/community-scripts/ProxmoxVE/pull/3542)) - 2FAuth: Update PHP to 8.3 [@BrockHumblet](https://github.com/BrockHumblet) ([#3510](https://github.com/community-scripts/ProxmoxVE/pull/3510)) - GoMFT: Update Curl Path [@MickLesk](https://github.com/MickLesk) ([#3537](https://github.com/community-scripts/ProxmoxVE/pull/3537)) - slskd: fix broken curl for soularr [@MickLesk](https://github.com/MickLesk) ([#3533](https://github.com/community-scripts/ProxmoxVE/pull/3533)) - Docmost: Bump NodeJS to 22 & fixed pnpm [@MickLesk](https://github.com/MickLesk) ([#3521](https://github.com/community-scripts/ProxmoxVE/pull/3521)) - Tianji: Bump NodeJS to V22 [@MickLesk](https://github.com/MickLesk) ([#3519](https://github.com/community-scripts/ProxmoxVE/pull/3519)) - #### ✨ New Features - NPMPlus: update function & better create handling (user/password) [@MickLesk](https://github.com/MickLesk) ([#3520](https://github.com/community-scripts/ProxmoxVE/pull/3520)) - #### 🔧 Refactor - Remove old `.jar` versions of Stirling-PDF [@JcMinarro](https://github.com/JcMinarro) ([#3512](https://github.com/community-scripts/ProxmoxVE/pull/3512)) ### 🧰 Maintenance - #### 💾 Core - core: fix empty header if header in repo exist [@MickLesk](https://github.com/MickLesk) ([#3536](https://github.com/community-scripts/ProxmoxVE/pull/3536)) ### 🌐 Website - #### ✨ New Features - Change Frontend Version Info [@MickLesk](https://github.com/MickLesk) ([#3527](https://github.com/community-scripts/ProxmoxVE/pull/3527)) - #### 📝 Script Information - HomeAssistant (Container): Better Portainer explanation [@MickLesk](https://github.com/MickLesk) ([#3518](https://github.com/community-scripts/ProxmoxVE/pull/3518)) ## 2025-03-30 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Open WebUI: Fix Ollama update logic [@tremor021](https://github.com/tremor021) ([#3506](https://github.com/community-scripts/ProxmoxVE/pull/3506)) - GoMFT: Add frontend build procedure [@tremor021](https://github.com/tremor021) ([#3499](https://github.com/community-scripts/ProxmoxVE/pull/3499)) - #### ✨ New Features - Open WebUI: Add Ollama update check [@tremor021](https://github.com/tremor021) ([#3478](https://github.com/community-scripts/ProxmoxVE/pull/3478)) ## 2025-03-29 ### 🆕 New Scripts - Alpine MariaDB [@MickLesk](https://github.com/MickLesk) ([#3456](https://github.com/community-scripts/ProxmoxVE/pull/3456)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Komodo: Fix wrong sed text [@tremor021](https://github.com/tremor021) ([#3491](https://github.com/community-scripts/ProxmoxVE/pull/3491)) - GoMFT: Fix release archive naming [@tremor021](https://github.com/tremor021) ([#3483](https://github.com/community-scripts/ProxmoxVE/pull/3483)) - Homepage: Fix release parsing [@tremor021](https://github.com/tremor021) ([#3484](https://github.com/community-scripts/ProxmoxVE/pull/3484)) - Netdata: Fix debian-keyring dependency missing [@tremor021](https://github.com/tremor021) ([#3477](https://github.com/community-scripts/ProxmoxVE/pull/3477)) - ErsatzTV: Fix temp file reference [@tremor021](https://github.com/tremor021) ([#3476](https://github.com/community-scripts/ProxmoxVE/pull/3476)) - Komodo: Fix compose.env [@tremor021](https://github.com/tremor021) ([#3466](https://github.com/community-scripts/ProxmoxVE/pull/3466)) ## 2025-03-28 ### 🆕 New Scripts - Alpine Node-RED [@MickLesk](https://github.com/MickLesk) ([#3457](https://github.com/community-scripts/ProxmoxVE/pull/3457)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - GoMFT: Fix release grep [@tremor021](https://github.com/tremor021) ([#3462](https://github.com/community-scripts/ProxmoxVE/pull/3462)) - ErsatzTV: Fix path in update function [@tremor021](https://github.com/tremor021) ([#3463](https://github.com/community-scripts/ProxmoxVE/pull/3463)) ## 2025-03-27 ### 🚀 Updated Scripts - [core]: add functions for Alpine (update / core deps) [@MickLesk](https://github.com/MickLesk) ([#3437](https://github.com/community-scripts/ProxmoxVE/pull/3437)) ### 🧰 Maintenance - #### 💾 Core - [core]: Refactor Spinner/MSG Function (support now alpine / performance / handling) [@MickLesk](https://github.com/MickLesk) ([#3436](https://github.com/community-scripts/ProxmoxVE/pull/3436)) ## 2025-03-26 ### 🆕 New Scripts - Alpine: Gitea [@MickLesk](https://github.com/MickLesk) ([#3424](https://github.com/community-scripts/ProxmoxVE/pull/3424)) ### 🚀 Updated Scripts - FlowiseAI: Fix dependencies [@tremor021](https://github.com/tremor021) ([#3427](https://github.com/community-scripts/ProxmoxVE/pull/3427)) - #### 🐞 Bug Fixes - fluid-calendar: Fix failed build during updates [@vhsdream](https://github.com/vhsdream) ([#3417](https://github.com/community-scripts/ProxmoxVE/pull/3417)) ### 🧰 Maintenance - #### 📂 Github - Remove coredeps in CONTRIBUTOR_AND_GUIDES [@bvdberg01](https://github.com/bvdberg01) ([#3420](https://github.com/community-scripts/ProxmoxVE/pull/3420)) ## 2025-03-25 ### 🧰 Maintenance - #### 📂 Github - Discord invite link updated [@MickLesk](https://github.com/MickLesk) ([#3412](https://github.com/community-scripts/ProxmoxVE/pull/3412)) ## 2025-03-24 ### 🆕 New Scripts - fileflows [@kkroboth](https://github.com/kkroboth) ([#3392](https://github.com/community-scripts/ProxmoxVE/pull/3392)) - wazuh [@omiinaya](https://github.com/omiinaya) ([#3381](https://github.com/community-scripts/ProxmoxVE/pull/3381)) - yt-dlp-webui [@CrazyWolf13](https://github.com/CrazyWolf13) ([#3364](https://github.com/community-scripts/ProxmoxVE/pull/3364)) - Extension/New Script: Redis Alpine Installation [@MickLesk](https://github.com/MickLesk) ([#3367](https://github.com/community-scripts/ProxmoxVE/pull/3367)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Extend HOME Env for Kubo [@MickLesk](https://github.com/MickLesk) ([#3397](https://github.com/community-scripts/ProxmoxVE/pull/3397)) - #### ✨ New Features - [core] Rebase Scripts (formatting, highlighting & remove old deps) [@MickLesk](https://github.com/MickLesk) ([#3378](https://github.com/community-scripts/ProxmoxVE/pull/3378)) - #### 🔧 Refactor - qBittorrent: Switch to static builds for faster updating/upgrading [@tremor021](https://github.com/tremor021) ([#3405](https://github.com/community-scripts/ProxmoxVE/pull/3405)) - Refactor: ErsatzTV Script [@MickLesk](https://github.com/MickLesk) ([#3365](https://github.com/community-scripts/ProxmoxVE/pull/3365)) ### 🧰 Maintenance - #### ✨ New Features - [core] install core deps (debian / ubuntu) [@MickLesk](https://github.com/MickLesk) ([#3366](https://github.com/community-scripts/ProxmoxVE/pull/3366)) - #### 💾 Core - [core] extend hardware transcoding for fileflows #3392 [@MickLesk](https://github.com/MickLesk) ([#3396](https://github.com/community-scripts/ProxmoxVE/pull/3396)) - #### 📂 Github - Refactor Changelog Workflow [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#3371](https://github.com/community-scripts/ProxmoxVE/pull/3371)) ### 🌐 Website - Update siteConfig.tsx to use new analytics code [@BramSuurdje](https://github.com/BramSuurdje) ([#3389](https://github.com/community-scripts/ProxmoxVE/pull/3389)) - #### 🐞 Bug Fixes - Better Text for Version Date [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#3388](https://github.com/community-scripts/ProxmoxVE/pull/3388)) ## 2025-03-23 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - GoMFT: Check if build-essential is present before updating, if not then install it [@tremor021](https://github.com/tremor021) ([#3358](https://github.com/community-scripts/ProxmoxVE/pull/3358)) ## 2025-03-22 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - revealjs: Fix update process [@tremor021](https://github.com/tremor021) ([#3341](https://github.com/community-scripts/ProxmoxVE/pull/3341)) - Cronicle: add missing gnupg package [@MickLesk](https://github.com/MickLesk) ([#3323](https://github.com/community-scripts/ProxmoxVE/pull/3323)) - #### ✨ New Features - Update nextcloud-vm.sh to 18.1 ISO [@0xN0BADC0FF33](https://github.com/0xN0BADC0FF33) ([#3333](https://github.com/community-scripts/ProxmoxVE/pull/3333)) ## 2025-03-21 ### 🚀 Updated Scripts - Omada jdk to jre [@bvdberg01](https://github.com/bvdberg01) ([#3319](https://github.com/community-scripts/ProxmoxVE/pull/3319)) - #### 🐞 Bug Fixes - Omada zulu 8 to 21 [@bvdberg01](https://github.com/bvdberg01) ([#3318](https://github.com/community-scripts/ProxmoxVE/pull/3318)) - MySQL: Correctly add repo to mysql.list [@tremor021](https://github.com/tremor021) ([#3315](https://github.com/community-scripts/ProxmoxVE/pull/3315)) - GoMFT: Fix build dependencies [@tremor021](https://github.com/tremor021) ([#3313](https://github.com/community-scripts/ProxmoxVE/pull/3313)) - GoMFT: Don't rely on binaries from github [@tremor021](https://github.com/tremor021) ([#3303](https://github.com/community-scripts/ProxmoxVE/pull/3303)) ### 🧰 Maintenance - #### 💾 Core - Clarify MTU in advanced Settings. [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#3296](https://github.com/community-scripts/ProxmoxVE/pull/3296)) ### 🌐 Website - Bump next from 15.1.3 to 15.2.3 in /frontend [@dependabot[bot]](https://github.com/dependabot[bot]) ([#3316](https://github.com/community-scripts/ProxmoxVE/pull/3316)) - #### 📝 Script Information - Proxmox, rather than Promox [@gringocl](https://github.com/gringocl) ([#3293](https://github.com/community-scripts/ProxmoxVE/pull/3293)) - Audiobookshelf: Fix category on website [@jaykup26](https://github.com/jaykup26) ([#3304](https://github.com/community-scripts/ProxmoxVE/pull/3304)) - Threadfin: add port for website [@MickLesk](https://github.com/MickLesk) ([#3295](https://github.com/community-scripts/ProxmoxVE/pull/3295)) ## 2025-03-20 ### 🚀 Updated Scripts - #### ✨ New Features - Netdata: Update to newer deb File [@MickLesk](https://github.com/MickLesk) ([#3276](https://github.com/community-scripts/ProxmoxVE/pull/3276)) ### 🧰 Maintenance - #### ✨ New Features - [core] add gitignore to prevent big pulls [@MickLesk](https://github.com/MickLesk) ([#3278](https://github.com/community-scripts/ProxmoxVE/pull/3278)) ## 2025-03-19 ### 🚀 Updated Scripts - License url VED to VE [@bvdberg01](https://github.com/bvdberg01) ([#3258](https://github.com/community-scripts/ProxmoxVE/pull/3258)) - #### 🐞 Bug Fixes - Snipe-IT: Remove composer update & add no interaction for install [@MickLesk](https://github.com/MickLesk) ([#3256](https://github.com/community-scripts/ProxmoxVE/pull/3256)) - Fluid-Calendar: Remove unneeded $STD in update [@MickLesk](https://github.com/MickLesk) ([#3250](https://github.com/community-scripts/ProxmoxVE/pull/3250)) - #### 💥 Breaking Changes - FluidCalendar: Switch to safer DB operations [@vhsdream](https://github.com/vhsdream) ([#3270](https://github.com/community-scripts/ProxmoxVE/pull/3270)) ### 🌐 Website - #### 🐞 Bug Fixes - JSON editor note fix [@bvdberg01](https://github.com/bvdberg01) ([#3260](https://github.com/community-scripts/ProxmoxVE/pull/3260)) ## 2025-03-18 ### 🆕 New Scripts - CryptPad [@MickLesk](https://github.com/MickLesk) ([#3205](https://github.com/community-scripts/ProxmoxVE/pull/3205)) - GoMFT [@tremor021](https://github.com/tremor021) ([#3157](https://github.com/community-scripts/ProxmoxVE/pull/3157)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Update omada download url [@bvdberg01](https://github.com/bvdberg01) ([#3245](https://github.com/community-scripts/ProxmoxVE/pull/3245)) - Wikijs: Remove Dev Message & Performance-Boost [@bvdberg01](https://github.com/bvdberg01) ([#3232](https://github.com/community-scripts/ProxmoxVE/pull/3232)) - Fix openwebui update script when backup directory already exists [@chrisdoc](https://github.com/chrisdoc) ([#3213](https://github.com/community-scripts/ProxmoxVE/pull/3213)) - #### 💥 Breaking Changes - Tandoor: Extend needed dependencies (Read for Update-Functionality) [@MickLesk](https://github.com/MickLesk) ([#3207](https://github.com/community-scripts/ProxmoxVE/pull/3207)) ### 🧰 Maintenance - #### 📂 Github - [core] cleanup - remove old backups of workflow files [@MickLesk](https://github.com/MickLesk) ([#3247](https://github.com/community-scripts/ProxmoxVE/pull/3247)) - Add worflow to crawl APP verisons [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#3192](https://github.com/community-scripts/ProxmoxVE/pull/3192)) - Update pr template and WF [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#3200](https://github.com/community-scripts/ProxmoxVE/pull/3200)) - Update Workflow Context [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#3171](https://github.com/community-scripts/ProxmoxVE/pull/3171)) - Change json path CONTRIBUTING.md [@bvdberg01](https://github.com/bvdberg01) ([#3187](https://github.com/community-scripts/ProxmoxVE/pull/3187)) - Relocate the Json Files [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#3184](https://github.com/community-scripts/ProxmoxVE/pull/3184)) - Update Workflow to Close Discussion [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#3185](https://github.com/community-scripts/ProxmoxVE/pull/3185)) ### 🌐 Website - #### 🐞 Bug Fixes - Move cryptpad files to right folders [@bvdberg01](https://github.com/bvdberg01) ([#3242](https://github.com/community-scripts/ProxmoxVE/pull/3242)) - Update Frontend Version Logic [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#3223](https://github.com/community-scripts/ProxmoxVE/pull/3223)) - #### ✨ New Features - Add Latest Change Date to Frontend [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#3231](https://github.com/community-scripts/ProxmoxVE/pull/3231)) - Show Version Information on Frontend [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#3216](https://github.com/community-scripts/ProxmoxVE/pull/3216)) - #### 📝 Script Information - CrowdSec: Add debian only warning to website [@tremor021](https://github.com/tremor021) ([#3210](https://github.com/community-scripts/ProxmoxVE/pull/3210)) - Debian VM: Update webpage with login info [@tremor021](https://github.com/tremor021) ([#3215](https://github.com/community-scripts/ProxmoxVE/pull/3215)) - Heimdall Dashboard: Fix missing logo on website [@tremor021](https://github.com/tremor021) ([#3227](https://github.com/community-scripts/ProxmoxVE/pull/3227)) - Seafile: lowercase slug for Install/Update-Source [@MickLesk](https://github.com/MickLesk) ([#3209](https://github.com/community-scripts/ProxmoxVE/pull/3209)) - Website: Lowercase Zitadel-Slug [@MickLesk](https://github.com/MickLesk) ([#3222](https://github.com/community-scripts/ProxmoxVE/pull/3222)) - VictoriaMetrics: Fix Wrong Slug [@MickLesk](https://github.com/MickLesk) ([#3225](https://github.com/community-scripts/ProxmoxVE/pull/3225)) - Update Pimox Logo [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#3233](https://github.com/community-scripts/ProxmoxVE/pull/3233)) - [AUTOMATIC PR]Update versions.json [@community-scripts-pr-app[bot]](https://github.com/community-scripts-pr-app[bot]) ([#3201](https://github.com/community-scripts/ProxmoxVE/pull/3201)) - GoMFT: Update Logo [@MickLesk](https://github.com/MickLesk) ([#3188](https://github.com/community-scripts/ProxmoxVE/pull/3188)) ## 2025-03-17 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - TriliumNotes: Fix release handling [@tremor021](https://github.com/tremor021) ([#3160](https://github.com/community-scripts/ProxmoxVE/pull/3160)) - TriliumNext: Fix release file name/path, preventing install and update [@tremor021](https://github.com/tremor021) ([#3152](https://github.com/community-scripts/ProxmoxVE/pull/3152)) - qBittorrent: Accept legal notice in config file [@tremor021](https://github.com/tremor021) ([#3150](https://github.com/community-scripts/ProxmoxVE/pull/3150)) - Tandoor: Switch Repo to new Link [@MickLesk](https://github.com/MickLesk) ([#3140](https://github.com/community-scripts/ProxmoxVE/pull/3140)) - Fixed wrong PHP values to match default part-db size (100M) [@dMopp](https://github.com/dMopp) ([#3143](https://github.com/community-scripts/ProxmoxVE/pull/3143)) - Kimai: Fix Permission Issue on new Timerecords [@MickLesk](https://github.com/MickLesk) ([#3136](https://github.com/community-scripts/ProxmoxVE/pull/3136)) - #### ✨ New Features - InfluxDB: Add Ports as Info / Script-End [@MickLesk](https://github.com/MickLesk) ([#3141](https://github.com/community-scripts/ProxmoxVE/pull/3141)) - ByteStash: Add option for multiple accounts and generate JWT secret [@tremor021](https://github.com/tremor021) ([#3132](https://github.com/community-scripts/ProxmoxVE/pull/3132)) ### 🌐 Website - #### 📝 Script Information - Paperless-ngx: Fix example on website [@tremor021](https://github.com/tremor021) ([#3155](https://github.com/community-scripts/ProxmoxVE/pull/3155)) ## 2025-03-16 ### 🚀 Updated Scripts - Typo Enviroment > Environment [@MathijsG](https://github.com/MathijsG) ([#3115](https://github.com/community-scripts/ProxmoxVE/pull/3115)) - Paperless-ngx: Add additional information to website on how to install OCR languages [@tremor021](https://github.com/tremor021) ([#3111](https://github.com/community-scripts/ProxmoxVE/pull/3111)) - Prometheus PVE Exporter: Rightsizing RAM and Disk [@andygrunwald](https://github.com/andygrunwald) ([#3098](https://github.com/community-scripts/ProxmoxVE/pull/3098)) - #### 🐞 Bug Fixes - Jellyseerr: Fix dependencies [@tremor021](https://github.com/tremor021) ([#3125](https://github.com/community-scripts/ProxmoxVE/pull/3125)) - wger: Fix build.func path [@tremor021](https://github.com/tremor021) ([#3121](https://github.com/community-scripts/ProxmoxVE/pull/3121)) - Filebrowser: Fix hardcoded port in Debian service file [@Xerovoxx98](https://github.com/Xerovoxx98) ([#3105](https://github.com/community-scripts/ProxmoxVE/pull/3105)) ### 🌐 Website - #### 📝 Script Information - Website: Fix alpine-it-tools "undefined" Link [@CrazyWolf13](https://github.com/CrazyWolf13) ([#3110](https://github.com/community-scripts/ProxmoxVE/pull/3110)) ## 2025-03-15 ### 🚀 Updated Scripts - #### 💥 Breaking Changes - Homepage: Bugfix for v1.0.0 [@vhsdream](https://github.com/vhsdream) ([#3092](https://github.com/community-scripts/ProxmoxVE/pull/3092)) ## 2025-03-14 ### 🚀 Updated Scripts - Memos: Increase RAM Usage and max space [@MickLesk](https://github.com/MickLesk) ([#3072](https://github.com/community-scripts/ProxmoxVE/pull/3072)) - Seafile - Minor bug fix: domain.sh script fix [@dave-yap](https://github.com/dave-yap) ([#3046](https://github.com/community-scripts/ProxmoxVE/pull/3046)) - #### 🐞 Bug Fixes - openwrt: fix typo netmask [@qzydustin](https://github.com/qzydustin) ([#3084](https://github.com/community-scripts/ProxmoxVE/pull/3084)) ### 🌐 Website - #### 📝 Script Information - NPMplus: Add info about docker use. [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#3085](https://github.com/community-scripts/ProxmoxVE/pull/3085)) ## 2025-03-13 ### 🆕 New Scripts - NPMplus [@MickLesk](https://github.com/MickLesk) ([#3051](https://github.com/community-scripts/ProxmoxVE/pull/3051)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - OpenWebUI check if there are stashed changes before poping [@tremor021](https://github.com/tremor021) ([#3064](https://github.com/community-scripts/ProxmoxVE/pull/3064)) - Update Fluid Calendar for v1.2.0 [@vhsdream](https://github.com/vhsdream) ([#3053](https://github.com/community-scripts/ProxmoxVE/pull/3053)) ### 🧰 Maintenance - #### 💾 Core - alpine-Install (core) add timezone (tz) check [@MickLesk](https://github.com/MickLesk) ([#3057](https://github.com/community-scripts/ProxmoxVE/pull/3057)) - #### 📂 Github - New Workflow: Close Issues in DEV Repo when new Script is merged [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#3042](https://github.com/community-scripts/ProxmoxVE/pull/3042)) ### 🌐 Website - Bump @babel/runtime from 7.26.0 to 7.26.10 in /frontend [@dependabot[bot]](https://github.com/dependabot[bot]) ([#3044](https://github.com/community-scripts/ProxmoxVE/pull/3044)) - #### 📝 Script Information - Update Vaultwarden Source [@MickLesk](https://github.com/MickLesk) ([#3036](https://github.com/community-scripts/ProxmoxVE/pull/3036)) - Website: Fix Alpine "undefined" Link [@MickLesk](https://github.com/MickLesk) ([#3048](https://github.com/community-scripts/ProxmoxVE/pull/3048)) ## 2025-03-12 ### 🆕 New Scripts - Fluid Calendar [@vhsdream](https://github.com/vhsdream) ([#2869](https://github.com/community-scripts/ProxmoxVE/pull/2869)) ### 🚀 Updated Scripts - #### ✨ New Features - Feature: Filebrowser: support now alpine [@MickLesk](https://github.com/MickLesk) ([#2997](https://github.com/community-scripts/ProxmoxVE/pull/2997)) ## 2025-03-11 ### 🆕 New Scripts - Plant-it [@MickLesk](https://github.com/MickLesk) ([#3000](https://github.com/community-scripts/ProxmoxVE/pull/3000)) - Seafile [@dave-yap](https://github.com/dave-yap) ([#2987](https://github.com/community-scripts/ProxmoxVE/pull/2987)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Headscale: Re-enable Service after Update [@Cerothen](https://github.com/Cerothen) ([#3012](https://github.com/community-scripts/ProxmoxVE/pull/3012)) - SnipeIT: Harmonize composer install to Project-Dockerfile [@MickLesk](https://github.com/MickLesk) ([#3009](https://github.com/community-scripts/ProxmoxVE/pull/3009)) - Teddycloud: fix update function [@tremor021](https://github.com/tremor021) ([#2996](https://github.com/community-scripts/ProxmoxVE/pull/2996)) ### 🧰 Maintenance - #### 📂 Github - Cleanup Old Project Files (figlet, app-header, images) [@MickLesk](https://github.com/MickLesk) ([#3004](https://github.com/community-scripts/ProxmoxVE/pull/3004)) - Additions and amends to the CONTIRBUTOR docs [@tremor021](https://github.com/tremor021) ([#2983](https://github.com/community-scripts/ProxmoxVE/pull/2983)) ### 🌐 Website - #### 📝 Script Information - Jellyseer not labeled as updateable even though update function exists [@tremor021](https://github.com/tremor021) ([#2991](https://github.com/community-scripts/ProxmoxVE/pull/2991)) - Fix Website - Show correct wget path for alpine [@MickLesk](https://github.com/MickLesk) ([#2998](https://github.com/community-scripts/ProxmoxVE/pull/2998)) ## 2025-03-10 ### 🆕 New Scripts - Paperless-GPT [@MickLesk](https://github.com/MickLesk) ([#2965](https://github.com/community-scripts/ProxmoxVE/pull/2965)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Rework SnipeIT: Tarball & Tempfile [@MickLesk](https://github.com/MickLesk) ([#2963](https://github.com/community-scripts/ProxmoxVE/pull/2963)) - pihole: fix path when accessing pihole using `pct enter` [@CrazyWolf13](https://github.com/CrazyWolf13) ([#2964](https://github.com/community-scripts/ProxmoxVE/pull/2964)) - Hoarder: v0.23.0 dependency update [@vhsdream](https://github.com/vhsdream) ([#2958](https://github.com/community-scripts/ProxmoxVE/pull/2958)) ### 🧰 Maintenance - #### 📂 Github - Update autolabeler.yml: Set Labels correctly [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#2968](https://github.com/community-scripts/ProxmoxVE/pull/2968)) ### 🌐 Website - Add warnings about externaly sourced scripts [@tremor021](https://github.com/tremor021) ([#2975](https://github.com/community-scripts/ProxmoxVE/pull/2975)) ## 2025-03-09 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Fix wikijs update issue while backing up data [@AdelRefaat](https://github.com/AdelRefaat) ([#2950](https://github.com/community-scripts/ProxmoxVE/pull/2950)) ### 🧰 Maintenance - #### 🐞 Bug Fixes - Improve Release-Action (awk function) [@MickLesk](https://github.com/MickLesk) ([#2934](https://github.com/community-scripts/ProxmoxVE/pull/2934)) ### 🌐 Website - #### 🐞 Bug Fixes - Pi-hole interface port in documentation [@la7eralus](https://github.com/la7eralus) ([#2953](https://github.com/community-scripts/ProxmoxVE/pull/2953)) ## 2025-03-08 ### 🌐 Website - #### 🐞 Bug Fixes - Update slug to lowercase in pf2etools.json [@PhoenixEmik](https://github.com/PhoenixEmik) ([#2942](https://github.com/community-scripts/ProxmoxVE/pull/2942)) ## 2025-03-07 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - JupyterNotebook: Fix APP Variable [@MickLesk](https://github.com/MickLesk) ([#2924](https://github.com/community-scripts/ProxmoxVE/pull/2924)) - #### ✨ New Features - Beszel: restarting service after update [@C0pywriting](https://github.com/C0pywriting) ([#2915](https://github.com/community-scripts/ProxmoxVE/pull/2915)) - #### 💥 Breaking Changes - ActualBudget: Update Script with new Repo [@MickLesk](https://github.com/MickLesk) ([#2907](https://github.com/community-scripts/ProxmoxVE/pull/2907)) ### 🌐 Website - #### 📝 Script Information - Improve Nextcloud(pi) docu and Name to NextcloudPi [@MickLesk](https://github.com/MickLesk) ([#2930](https://github.com/community-scripts/ProxmoxVE/pull/2930)) - fix jupyternotebook slug [@MickLesk](https://github.com/MickLesk) ([#2922](https://github.com/community-scripts/ProxmoxVE/pull/2922)) - Improve Trilium Description and Name to TriliumNext [@MickLesk](https://github.com/MickLesk) ([#2929](https://github.com/community-scripts/ProxmoxVE/pull/2929)) - Prowlarr icon [@bannert1337](https://github.com/bannert1337) ([#2906](https://github.com/community-scripts/ProxmoxVE/pull/2906)) - Update Apache Tika icon to SVG [@bannert1337](https://github.com/bannert1337) ([#2904](https://github.com/community-scripts/ProxmoxVE/pull/2904)) - Update Prometheus Alertmanager Icon [@bannert1337](https://github.com/bannert1337) ([#2905](https://github.com/community-scripts/ProxmoxVE/pull/2905)) ## 2025-03-06 ### 🆕 New Scripts - InvenTree [@tremor021](https://github.com/tremor021) ([#2890](https://github.com/community-scripts/ProxmoxVE/pull/2890)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Homarr: Optional Reboot after update [@CrazyWolf13](https://github.com/CrazyWolf13) ([#2876](https://github.com/community-scripts/ProxmoxVE/pull/2876)) - Fix Tag "community-scripts" for ArchLinux / OPNSense [@MickLesk](https://github.com/MickLesk) ([#2875](https://github.com/community-scripts/ProxmoxVE/pull/2875)) - #### ✨ New Features - Wastebin: Update Script for Version 3.0.0 [@MickLesk](https://github.com/MickLesk) ([#2885](https://github.com/community-scripts/ProxmoxVE/pull/2885)) ## 2025-03-05 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Kimai: Better Handling of Updates (backup var / env / yaml) [@MickLesk](https://github.com/MickLesk) ([#2862](https://github.com/community-scripts/ProxmoxVE/pull/2862)) - Fix NextcloudPi-Installation [@MickLesk](https://github.com/MickLesk) ([#2853](https://github.com/community-scripts/ProxmoxVE/pull/2853)) ## 2025-03-04 ### 🆕 New Scripts - Reveal.js [@tremor021](https://github.com/tremor021) ([#2806](https://github.com/community-scripts/ProxmoxVE/pull/2806)) - Apache Tomcat [@MickLesk](https://github.com/MickLesk) ([#2797](https://github.com/community-scripts/ProxmoxVE/pull/2797)) - Pterodactyl Wings [@bvdberg01](https://github.com/bvdberg01) ([#2800](https://github.com/community-scripts/ProxmoxVE/pull/2800)) - Pterodactyl Panel [@bvdberg01](https://github.com/bvdberg01) ([#2801](https://github.com/community-scripts/ProxmoxVE/pull/2801)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - reveal.js: Update function now backs up index and config [@tremor021](https://github.com/tremor021) ([#2845](https://github.com/community-scripts/ProxmoxVE/pull/2845)) - Changedetection: Increase RAM & Disk-Space [@MickLesk](https://github.com/MickLesk) ([#2838](https://github.com/community-scripts/ProxmoxVE/pull/2838)) - Linkwarden: Optimze RUST Installation [@MickLesk](https://github.com/MickLesk) ([#2817](https://github.com/community-scripts/ProxmoxVE/pull/2817)) - Nginx: Fix $STD for tar [@MickLesk](https://github.com/MickLesk) ([#2813](https://github.com/community-scripts/ProxmoxVE/pull/2813)) - #### ✨ New Features - Add source to install scripts and make license one line [@bvdberg01](https://github.com/bvdberg01) ([#2842](https://github.com/community-scripts/ProxmoxVE/pull/2842)) ### 🧰 Maintenance - #### 🐞 Bug Fixes - Better handling of create release [@MickLesk](https://github.com/MickLesk) ([#2818](https://github.com/community-scripts/ProxmoxVE/pull/2818)) ### 🌐 Website - #### 🐞 Bug Fixes - Json file update [@bvdberg01](https://github.com/bvdberg01) ([#2824](https://github.com/community-scripts/ProxmoxVE/pull/2824)) - Prometheus-paperless-ngx-exporter: Fix wrong Interface Port [@schneider-de-com](https://github.com/schneider-de-com) ([#2812](https://github.com/community-scripts/ProxmoxVE/pull/2812)) - #### ✨ New Features - Feature: Update Icons (selfhst repo) [@bannert1337](https://github.com/bannert1337) ([#2834](https://github.com/community-scripts/ProxmoxVE/pull/2834)) - Website: Add Mikrotik to Network too, OPNSense & OpenWRT to OS [@MickLesk](https://github.com/MickLesk) ([#2823](https://github.com/community-scripts/ProxmoxVE/pull/2823)) ## 2025-03-03 ### 🆕 New Scripts - Habitica [@tremor021](https://github.com/tremor021) ([#2779](https://github.com/community-scripts/ProxmoxVE/pull/2779)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Zigbee2Mqtt Use fixed pnpm Version 10.4.1 [@MickLesk](https://github.com/MickLesk) ([#2805](https://github.com/community-scripts/ProxmoxVE/pull/2805)) - Linkwarden: Fix & Update Monolith-Installation [@MickLesk](https://github.com/MickLesk) ([#2787](https://github.com/community-scripts/ProxmoxVE/pull/2787)) - #### ✨ New Features - Feature: MinIO use now static port 9001 [@MickLesk](https://github.com/MickLesk) ([#2786](https://github.com/community-scripts/ProxmoxVE/pull/2786)) - Feature Template Path for Mountings [@MickLesk](https://github.com/MickLesk) ([#2785](https://github.com/community-scripts/ProxmoxVE/pull/2785)) ### 🌐 Website - #### ✨ New Features - Feature: Website - show default OS [@MickLesk](https://github.com/MickLesk) ([#2790](https://github.com/community-scripts/ProxmoxVE/pull/2790)) - #### 📝 Script Information - Update zigbee2mqtt.json - make sure link is clickable [@gurtjun](https://github.com/gurtjun) ([#2802](https://github.com/community-scripts/ProxmoxVE/pull/2802)) ## 2025-03-02 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Fix gpg Repo for nzbget [@flatlinebb](https://github.com/flatlinebb) ([#2774](https://github.com/community-scripts/ProxmoxVE/pull/2774)) ## 2025-03-01 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Firefly III: FIx Ownership for OAuth Key [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#2759](https://github.com/community-scripts/ProxmoxVE/pull/2759)) - homarr: double restart to fix homarr migration [@CrazyWolf13](https://github.com/CrazyWolf13) ([#2757](https://github.com/community-scripts/ProxmoxVE/pull/2757)) - #### ✨ New Features - ActualBudget: New Installation Script with new Repo [@MickLesk](https://github.com/MickLesk) ([#2770](https://github.com/community-scripts/ProxmoxVE/pull/2770)) - #### 💥 Breaking Changes - Breaking: Remove Update Function for Actual Budget until it fixed [@MickLesk](https://github.com/MickLesk) ([#2768](https://github.com/community-scripts/ProxmoxVE/pull/2768)) ### 🧰 Maintenance - #### 🐞 Bug Fixes - Remove Note on Changelog [@MickLesk](https://github.com/MickLesk) ([#2758](https://github.com/community-scripts/ProxmoxVE/pull/2758)) - Fix Release Creation if Changelog.md to long [@MickLesk](https://github.com/MickLesk) ([#2752](https://github.com/community-scripts/ProxmoxVE/pull/2752)) ================================================ FILE: .github/changelogs/2025/04.md ================================================ ## 2025-04-30 ### 🚀 Updated Scripts - Refactor: Matterbridge [@MickLesk](https://github.com/MickLesk) ([#4148](https://github.com/community-scripts/ProxmoxVE/pull/4148)) - Refactor: Ollama & Adding to Website [@MickLesk](https://github.com/MickLesk) ([#4147](https://github.com/community-scripts/ProxmoxVE/pull/4147)) ### 🌐 Website - #### 📝 Script Information - mark Caddy as updateable [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#4154](https://github.com/community-scripts/ProxmoxVE/pull/4154)) - Website: Add missing docs and config paths [@tremor021](https://github.com/tremor021) ([#4131](https://github.com/community-scripts/ProxmoxVE/pull/4131)) ## 2025-04-29 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Umlautadaptarr Service File [@MickLesk](https://github.com/MickLesk) ([#4124](https://github.com/community-scripts/ProxmoxVE/pull/4124)) - CheckMK added filter to not install beta versions [@briodan](https://github.com/briodan) ([#4118](https://github.com/community-scripts/ProxmoxVE/pull/4118)) - #### 🔧 Refactor - Refactor: sabnzbd [@MickLesk](https://github.com/MickLesk) ([#4127](https://github.com/community-scripts/ProxmoxVE/pull/4127)) - Refactor: Navidrome [@MickLesk](https://github.com/MickLesk) ([#4120](https://github.com/community-scripts/ProxmoxVE/pull/4120)) ### 🧰 Maintenance - #### ✨ New Features - core: add setup_uv() function to automate installation and updating of uv [@MickLesk](https://github.com/MickLesk) ([#4129](https://github.com/community-scripts/ProxmoxVE/pull/4129)) - core: persist /usr/local/bin via profile.d helper [@MickLesk](https://github.com/MickLesk) ([#4133](https://github.com/community-scripts/ProxmoxVE/pull/4133)) - #### 📝 Documentation - SECURITY.md: add pve 8.4 as supported [@MickLesk](https://github.com/MickLesk) ([#4123](https://github.com/community-scripts/ProxmoxVE/pull/4123)) ### 🌐 Website - #### ✨ New Features - Feat: Random Script picker for Website [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#4090](https://github.com/community-scripts/ProxmoxVE/pull/4090)) ## 2025-04-28 ### 🆕 New Scripts - umlautadaptarr ([#4093](https://github.com/community-scripts/ProxmoxVE/pull/4093)) - documenso ([#4080](https://github.com/community-scripts/ProxmoxVE/pull/4080)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Install rsync in ct/commafeed.sh [@mzhaodev](https://github.com/mzhaodev) ([#4086](https://github.com/community-scripts/ProxmoxVE/pull/4086)) - fstrim: cancel/no whiptail support [@PonyXplosion](https://github.com/PonyXplosion) ([#4101](https://github.com/community-scripts/ProxmoxVE/pull/4101)) - clean-lxc.sh: cancel/no whiptail support [@PonyXplosion](https://github.com/PonyXplosion) ([#4102](https://github.com/community-scripts/ProxmoxVE/pull/4102)) - #### ✨ New Features - karakeep: add cli and mcp build commands [@vhsdream](https://github.com/vhsdream) ([#4112](https://github.com/community-scripts/ProxmoxVE/pull/4112)) - Make apt-cacher-ng a client of its own server [@pgcudahy](https://github.com/pgcudahy) ([#4092](https://github.com/community-scripts/ProxmoxVE/pull/4092)) ### 🧰 Maintenance - #### ✨ New Features - Add: tools.func [@MickLesk](https://github.com/MickLesk) ([#4100](https://github.com/community-scripts/ProxmoxVE/pull/4100)) - #### 💾 Core - core: remove unneeded logging [@MickLesk](https://github.com/MickLesk) ([#4103](https://github.com/community-scripts/ProxmoxVE/pull/4103)) ### 🌐 Website - Website: Fix frontend path in footer [@tremor021](https://github.com/tremor021) ([#4108](https://github.com/community-scripts/ProxmoxVE/pull/4108)) - #### 📝 Script Information - GoMFT: Move configuration info into config_path [@tremor021](https://github.com/tremor021) ([#4106](https://github.com/community-scripts/ProxmoxVE/pull/4106)) ## 2025-04-27 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Re-Add DeamonSync Package [@MickLesk](https://github.com/MickLesk) ([#4079](https://github.com/community-scripts/ProxmoxVE/pull/4079)) ### 🌐 Website - #### 📝 Script Information - add default configuration file location for Caddy LXC [@aly-yvette](https://github.com/aly-yvette) ([#4074](https://github.com/community-scripts/ProxmoxVE/pull/4074)) ## 2025-04-26 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Element Synapse: Fix install script cd command error [@thegeorgeliu](https://github.com/thegeorgeliu) ([#4066](https://github.com/community-scripts/ProxmoxVE/pull/4066)) ## 2025-04-25 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Element Synapse: Fix update for older versions [@tremor021](https://github.com/tremor021) ([#4050](https://github.com/community-scripts/ProxmoxVE/pull/4050)) ### 🌐 Website - #### 🐞 Bug Fixes - Only show update source when app is marked updateable [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#4059](https://github.com/community-scripts/ProxmoxVE/pull/4059)) - #### 📝 Script Information - Filebrowser: Add Category Files & Donwloads [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#4055](https://github.com/community-scripts/ProxmoxVE/pull/4055)) ### 💥 Breaking Changes - Removal of Seafile due to recurring problems [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#4058](https://github.com/community-scripts/ProxmoxVE/pull/4058)) ## 2025-04-24 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Element Synapse: Fix Admin UI install and update procedure [@tremor021](https://github.com/tremor021) ([#4041](https://github.com/community-scripts/ProxmoxVE/pull/4041)) - SLSKD: always check for soularr update [@vhsdream](https://github.com/vhsdream) ([#4012](https://github.com/community-scripts/ProxmoxVE/pull/4012)) - #### ✨ New Features - Element Synapse: Add Synapse-Admin web UI to manage Matrix [@tremor021](https://github.com/tremor021) ([#4010](https://github.com/community-scripts/ProxmoxVE/pull/4010)) - Pi-Hole: Fix Unbound update [@CrazyWolf13](https://github.com/CrazyWolf13) ([#4026](https://github.com/community-scripts/ProxmoxVE/pull/4026)) ### 🌐 Website - #### ✨ New Features - Feat: Add config path to website [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#4005](https://github.com/community-scripts/ProxmoxVE/pull/4005)) - #### 📝 Script Information - Jellyfin: Mark as updateable [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#4029](https://github.com/community-scripts/ProxmoxVE/pull/4029)) - Prepare JSON files for new website feature [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#4004](https://github.com/community-scripts/ProxmoxVE/pull/4004)) ## 2025-04-23 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Zipline: Add new ENV Variable and Change Update [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#3997](https://github.com/community-scripts/ProxmoxVE/pull/3997)) - karakeep: use nightly channel for yt-dlp [@vhsdream](https://github.com/vhsdream) ([#3992](https://github.com/community-scripts/ProxmoxVE/pull/3992)) ### 🧰 Maintenance - #### 📂 Github - Fix Workflow to close discussions [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#3999](https://github.com/community-scripts/ProxmoxVE/pull/3999)) ## 2025-04-22 ### 🆕 New Scripts - reactive-resume ([#3980](https://github.com/community-scripts/ProxmoxVE/pull/3980)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - wger: Fix a bug in update procedure and general code maintenance [@tremor021](https://github.com/tremor021) ([#3974](https://github.com/community-scripts/ProxmoxVE/pull/3974)) ### 🧰 Maintenance - #### 📂 Github - Add workflow to close ttek Repo relatate issues [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#3981](https://github.com/community-scripts/ProxmoxVE/pull/3981)) ### 🌐 Website - #### 🐞 Bug Fixes - Fix Turnkey Source Link in Button Component [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#3978](https://github.com/community-scripts/ProxmoxVE/pull/3978)) - #### 📝 Script Information - qBittorrent: Update web page [@tremor021](https://github.com/tremor021) ([#3969](https://github.com/community-scripts/ProxmoxVE/pull/3969)) ## 2025-04-19 ### 🆕 New Scripts - LXC Iptag [@DesertGamer](https://github.com/DesertGamer) ([#3531](https://github.com/community-scripts/ProxmoxVE/pull/3531)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - seelf: Add missing gpg dependency [@tremor021](https://github.com/tremor021) ([#3953](https://github.com/community-scripts/ProxmoxVE/pull/3953)) ### 🌐 Website - #### 📝 Script Information - Tailscale: Clarify tailscale script instruction on website [@tremor021](https://github.com/tremor021) ([#3952](https://github.com/community-scripts/ProxmoxVE/pull/3952)) ## 2025-04-18 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Changedetection: Increase connection timeout for older systems [@tremor021](https://github.com/tremor021) ([#3935](https://github.com/community-scripts/ProxmoxVE/pull/3935)) ### 🌐 Website - #### 📝 Script Information - VaultWarden: Update json with additonal information [@uSlackr](https://github.com/uSlackr) ([#3929](https://github.com/community-scripts/ProxmoxVE/pull/3929)) ## 2025-04-17 ### 🚀 Updated Scripts - fix minor grammatical error in several scripts [@jordanpatton](https://github.com/jordanpatton) ([#3921](https://github.com/community-scripts/ProxmoxVE/pull/3921)) - #### 🐞 Bug Fixes - PeaNUT: Fix tar command [@tremor021](https://github.com/tremor021) ([#3925](https://github.com/community-scripts/ProxmoxVE/pull/3925)) - GoMFT: Fix install and update process (final time) [@tremor021](https://github.com/tremor021) ([#3922](https://github.com/community-scripts/ProxmoxVE/pull/3922)) ## 2025-04-15 ### 🚀 Updated Scripts - [core] remove unneeded vars from shellcheck [@MickLesk](https://github.com/MickLesk) ([#3899](https://github.com/community-scripts/ProxmoxVE/pull/3899)) - #### 🐞 Bug Fixes - Outline: Installation and update fixes [@tremor021](https://github.com/tremor021) ([#3895](https://github.com/community-scripts/ProxmoxVE/pull/3895)) - SABnzbd: Fix update error caused by externaly managed message [@tremor021](https://github.com/tremor021) ([#3892](https://github.com/community-scripts/ProxmoxVE/pull/3892)) ### 🧰 Maintenance - #### 📡 API - Bump golang.org/x/crypto from 0.32.0 to 0.35.0 in /api [@dependabot[bot]](https://github.com/dependabot[bot]) ([#3887](https://github.com/community-scripts/ProxmoxVE/pull/3887)) - #### 📂 Github - shrink & minimalize report templates [@MickLesk](https://github.com/MickLesk) ([#3902](https://github.com/community-scripts/ProxmoxVE/pull/3902)) ## 2025-04-14 ### 🆕 New Scripts - openziti-controller ([#3880](https://github.com/community-scripts/ProxmoxVE/pull/3880)) - Alpine-AdGuardHome [@MickLesk](https://github.com/MickLesk) ([#3875](https://github.com/community-scripts/ProxmoxVE/pull/3875)) ### 🚀 Updated Scripts - Paymenter: bump php to 8.3 [@opastorello](https://github.com/opastorello) ([#3825](https://github.com/community-scripts/ProxmoxVE/pull/3825)) - #### 🐞 Bug Fixes - Neo4j: Add Java dependency [@tremor021](https://github.com/tremor021) ([#3871](https://github.com/community-scripts/ProxmoxVE/pull/3871)) - #### 🔧 Refactor - Refactor Cockpit update_script part [@MickLesk](https://github.com/MickLesk) ([#3878](https://github.com/community-scripts/ProxmoxVE/pull/3878)) ## 2025-04-12 ### 🌐 Website - #### ✨ New Features - Add "Not Updateable" tooltip to scripts [@BramSuurdje](https://github.com/BramSuurdje) ([#3852](https://github.com/community-scripts/ProxmoxVE/pull/3852)) ## 2025-04-11 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - slskd: fix missing -o for curl [@MickLesk](https://github.com/MickLesk) ([#3828](https://github.com/community-scripts/ProxmoxVE/pull/3828)) - 2FAuth: Fix php dependencies [@tremor021](https://github.com/tremor021) ([#3820](https://github.com/community-scripts/ProxmoxVE/pull/3820)) - Komodo: Update Repository link [@sendyputra](https://github.com/sendyputra) ([#3823](https://github.com/community-scripts/ProxmoxVE/pull/3823)) ### 🧰 Maintenance - #### 💾 Core - Enlarge the size of the menu in build.func [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#3833](https://github.com/community-scripts/ProxmoxVE/pull/3833)) ### 🌐 Website - Bump vite from 6.2.5 to 6.2.6 in /frontend [@dependabot[bot]](https://github.com/dependabot[bot]) ([#3842](https://github.com/community-scripts/ProxmoxVE/pull/3842)) - #### 📝 Script Information - SQLServer: fix some typos in notes [@stiny861](https://github.com/stiny861) ([#3838](https://github.com/community-scripts/ProxmoxVE/pull/3838)) - Radicale: move to misc category [@tremor021](https://github.com/tremor021) ([#3830](https://github.com/community-scripts/ProxmoxVE/pull/3830)) ## 2025-04-10 ### 🆕 New Scripts - openproject ([#3637](https://github.com/community-scripts/ProxmoxVE/pull/3637)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Fix: NodeJS Check (Tianji/Docmost) [@MickLesk](https://github.com/MickLesk) ([#3813](https://github.com/community-scripts/ProxmoxVE/pull/3813)) - #### ✨ New Features - change var in ct files to new standard [@MickLesk](https://github.com/MickLesk) ([#3804](https://github.com/community-scripts/ProxmoxVE/pull/3804)) ### 🧰 Maintenance - #### 💾 Core - New Feature: Config File [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#3808](https://github.com/community-scripts/ProxmoxVE/pull/3808)) ### 💥 Breaking Changes - Remove Actualbudget [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#3801](https://github.com/community-scripts/ProxmoxVE/pull/3801)) ## 2025-04-09 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Paperless-NGX: Extend Granian Service Env [@MickLesk](https://github.com/MickLesk) ([#3790](https://github.com/community-scripts/ProxmoxVE/pull/3790)) - Paperless-NGX: remove gunicorn, use python3 for webserver [@MickLesk](https://github.com/MickLesk) ([#3785](https://github.com/community-scripts/ProxmoxVE/pull/3785)) - HomeAssistantOS: allow Proxmox version 8.4 [@quentinvnk](https://github.com/quentinvnk) ([#3773](https://github.com/community-scripts/ProxmoxVE/pull/3773)) - Tandoor: Add xmlsec as dependency [@tremor021](https://github.com/tremor021) ([#3762](https://github.com/community-scripts/ProxmoxVE/pull/3762)) - #### 🔧 Refactor - harmonize pve versions check & vm vars [@MickLesk](https://github.com/MickLesk) ([#3779](https://github.com/community-scripts/ProxmoxVE/pull/3779)) ### 🧰 Maintenance - #### 💥 Breaking Changes - core: Removal of OS/Version Selection from Advanced Settings [@MickLesk](https://github.com/MickLesk) ([#3771](https://github.com/community-scripts/ProxmoxVE/pull/3771)) - core: move misc scripts to structured addon/pve paths | Refactor JSON Editor & Script Mapping [@MickLesk](https://github.com/MickLesk) ([#3765](https://github.com/community-scripts/ProxmoxVE/pull/3765)) ## 2025-04-08 ### 🆕 New Scripts - Alpine-PostgreSQL [@MickLesk](https://github.com/MickLesk) ([#3751](https://github.com/community-scripts/ProxmoxVE/pull/3751)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Alpine-Wireguard: Fix for sysctl and ip_forward [@juronja](https://github.com/juronja) ([#3744](https://github.com/community-scripts/ProxmoxVE/pull/3744)) - TriliumNext: fix dump-db [@MickLesk](https://github.com/MickLesk) ([#3741](https://github.com/community-scripts/ProxmoxVE/pull/3741)) - Actual: Reduce RAM to 4GB and old space to 3072MB [@dannyellis](https://github.com/dannyellis) ([#3730](https://github.com/community-scripts/ProxmoxVE/pull/3730)) - #### ✨ New Features - Alpine-MariaDB: better handling of adminer installation [@MickLesk](https://github.com/MickLesk) ([#3739](https://github.com/community-scripts/ProxmoxVE/pull/3739)) - Paperless-GPT: Add logging to service file [@tremor021](https://github.com/tremor021) ([#3738](https://github.com/community-scripts/ProxmoxVE/pull/3738)) ### 🌐 Website - #### 📝 Script Information - Meilisearch: Fix Typo [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#3749](https://github.com/community-scripts/ProxmoxVE/pull/3749)) ## 2025-04-07 ### 🆕 New Scripts - Breaking: Hoarder > Karakeep [@MickLesk](https://github.com/MickLesk) ([#3699](https://github.com/community-scripts/ProxmoxVE/pull/3699)) ### 🚀 Updated Scripts - Actual: Increase RAM and add heap-space var for nodejs [@MickLesk](https://github.com/MickLesk) ([#3713](https://github.com/community-scripts/ProxmoxVE/pull/3713)) - #### 🐞 Bug Fixes - Alpine-MariaDB: Fix Install Service startup [@MickLesk](https://github.com/MickLesk) ([#3701](https://github.com/community-scripts/ProxmoxVE/pull/3701)) - Zitadel: Fix release tarball crawling [@tremor021](https://github.com/tremor021) ([#3716](https://github.com/community-scripts/ProxmoxVE/pull/3716)) - #### ✨ New Features - Kimai: bump php to 8.4 [@MickLesk](https://github.com/MickLesk) ([#3724](https://github.com/community-scripts/ProxmoxVE/pull/3724)) - #### 🔧 Refactor - Refactor: Zabbix, get always latest version [@MickLesk](https://github.com/MickLesk) ([#3720](https://github.com/community-scripts/ProxmoxVE/pull/3720)) ### 🌐 Website - #### 📝 Script Information - Changed the category of Channels DVR and NextPVR [@johnsturgeon](https://github.com/johnsturgeon) ([#3729](https://github.com/community-scripts/ProxmoxVE/pull/3729)) ## 2025-04-06 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Actual: Add git dependency & change yarn commands [@MickLesk](https://github.com/MickLesk) ([#3703](https://github.com/community-scripts/ProxmoxVE/pull/3703)) - Pelican-Panel: Fix PHP 8.4 Repository [@MickLesk](https://github.com/MickLesk) ([#3700](https://github.com/community-scripts/ProxmoxVE/pull/3700)) ### 🌐 Website - #### 🐞 Bug Fixes - Implement FAQ component and integrate it into the main page [@BramSuurdje](https://github.com/BramSuurdje) ([#3709](https://github.com/community-scripts/ProxmoxVE/pull/3709)) ## 2025-04-05 ### 🌐 Website - Bump vite from 6.2.4 to 6.2.5 in /frontend [@dependabot[bot]](https://github.com/dependabot[bot]) ([#3668](https://github.com/community-scripts/ProxmoxVE/pull/3668)) ## 2025-04-04 ### 🆕 New Scripts - meilisearch [@MickLesk](https://github.com/MickLesk) ([#3638](https://github.com/community-scripts/ProxmoxVE/pull/3638)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Pelican Panel: Bump php to 8.4 [@bvdberg01](https://github.com/bvdberg01) ([#3669](https://github.com/community-scripts/ProxmoxVE/pull/3669)) - Pterodactyl: Bump php to 8.4 [@MickLesk](https://github.com/MickLesk) ([#3655](https://github.com/community-scripts/ProxmoxVE/pull/3655)) - #### ✨ New Features - Caddy: add git for xcaddy [@MickLesk](https://github.com/MickLesk) ([#3657](https://github.com/community-scripts/ProxmoxVE/pull/3657)) ### 🧰 Maintenance - #### 💾 Core - core: fix raw path [@MickLesk](https://github.com/MickLesk) ([#3656](https://github.com/community-scripts/ProxmoxVE/pull/3656)) ## 2025-04-03 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Prowlarr: Fix Typo in URL (update_function) [@ribera96](https://github.com/ribera96) ([#3640](https://github.com/community-scripts/ProxmoxVE/pull/3640)) - Prowlarr: Fix typo in release URL [@tremor021](https://github.com/tremor021) ([#3636](https://github.com/community-scripts/ProxmoxVE/pull/3636)) - GoMFT: Fix the node_modules deletion command [@tremor021](https://github.com/tremor021) ([#3624](https://github.com/community-scripts/ProxmoxVE/pull/3624)) - BookStack: Fix path to downloaded release file [@tremor021](https://github.com/tremor021) ([#3627](https://github.com/community-scripts/ProxmoxVE/pull/3627)) - #### ✨ New Features - VM: show progress bar while downloading [@MickLesk](https://github.com/MickLesk) ([#3634](https://github.com/community-scripts/ProxmoxVE/pull/3634)) - *Arr: Move Arr apps to github release crawling and provide update functionality [@tremor021](https://github.com/tremor021) ([#3625](https://github.com/community-scripts/ProxmoxVE/pull/3625)) ### 🧰 Maintenance - #### 📂 Github - Correct URL in contributing docs [@verbumfeit](https://github.com/verbumfeit) ([#3648](https://github.com/community-scripts/ProxmoxVE/pull/3648)) ### 🌐 Website - Bump next from 15.2.3 to 15.2.4 in /frontend [@dependabot[bot]](https://github.com/dependabot[bot]) ([#3628](https://github.com/community-scripts/ProxmoxVE/pull/3628)) - #### 📝 Script Information - slskd: fix typo for config note [@MickLesk](https://github.com/MickLesk) ([#3633](https://github.com/community-scripts/ProxmoxVE/pull/3633)) ## 2025-04-02 ### 🆕 New Scripts - openziti-tunnel [@emoscardini](https://github.com/emoscardini) ([#3610](https://github.com/community-scripts/ProxmoxVE/pull/3610)) - Alpine-Wireguard [@MickLesk](https://github.com/MickLesk) ([#3611](https://github.com/community-scripts/ProxmoxVE/pull/3611)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Authelia: fix incorrect rights for email.txt [@MickLesk](https://github.com/MickLesk) ([#3612](https://github.com/community-scripts/ProxmoxVE/pull/3612)) - Photoprism: harmonize curl [@MickLesk](https://github.com/MickLesk) ([#3601](https://github.com/community-scripts/ProxmoxVE/pull/3601)) - Fix link in clean-lxcs.sh [@thalatamsainath](https://github.com/thalatamsainath) ([#3593](https://github.com/community-scripts/ProxmoxVE/pull/3593)) - Fileflows: Add ImageMagick dependecy [@tremor021](https://github.com/tremor021) ([#3589](https://github.com/community-scripts/ProxmoxVE/pull/3589)) - General fixes for several scripts [@tremor021](https://github.com/tremor021) ([#3587](https://github.com/community-scripts/ProxmoxVE/pull/3587)) ### 🧰 Maintenance - #### 💾 Core - UI-Fix: verbose without useless space in header [@MickLesk](https://github.com/MickLesk) ([#3598](https://github.com/community-scripts/ProxmoxVE/pull/3598)) ## 2025-04-01 ### 🆕 New Scripts - Alpine Prometheus [@MickLesk](https://github.com/MickLesk) ([#3547](https://github.com/community-scripts/ProxmoxVE/pull/3547)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Flaresolverr: Fix curl command [@tremor021](https://github.com/tremor021) ([#3583](https://github.com/community-scripts/ProxmoxVE/pull/3583)) - Authentik - Fix YQ_LATEST regex [@ceres-c](https://github.com/ceres-c) ([#3565](https://github.com/community-scripts/ProxmoxVE/pull/3565)) - Fileflows: Fix update dependencies [@tremor021](https://github.com/tremor021) ([#3577](https://github.com/community-scripts/ProxmoxVE/pull/3577)) - CheckMK: Increase Disk size [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#3559](https://github.com/community-scripts/ProxmoxVE/pull/3559)) - switch arr lxc's (lidarr,-prowlarr,-radarr,-readarr,-whisparr) to curl -fsSL [@MickLesk](https://github.com/MickLesk) ([#3554](https://github.com/community-scripts/ProxmoxVE/pull/3554)) - #### 💥 Breaking Changes - Replace wget with curl -fsSL, normalize downloads, and prep for IPv6 [@MickLesk](https://github.com/MickLesk) ([#3455](https://github.com/community-scripts/ProxmoxVE/pull/3455)) - #### 🔧 Refactor - Fixes and standard enforcement [@tremor021](https://github.com/tremor021) ([#3564](https://github.com/community-scripts/ProxmoxVE/pull/3564)) ### 🌐 Website - Update metadata inside layout.tsx for better SEO [@BramSuurdje](https://github.com/BramSuurdje) ([#3570](https://github.com/community-scripts/ProxmoxVE/pull/3570)) - Bump vite from 5.4.14 to 5.4.16 in /frontend [@dependabot[bot]](https://github.com/dependabot[bot]) ([#3549](https://github.com/community-scripts/ProxmoxVE/pull/3549)) - #### ✨ New Features - Refactor ScriptItem and Buttons components to enhance layout and integrate dropdown for links. Update InterFaces component for improved styling and structure. [@BramSuurdje](https://github.com/BramSuurdje) ([#3567](https://github.com/community-scripts/ProxmoxVE/pull/3567)) ================================================ FILE: .github/changelogs/2025/05.md ================================================ ## 2025-05-31 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Silverbullet: Fix Installation (wrong path) [@MickLesk](https://github.com/MickLesk) ([#4873](https://github.com/community-scripts/ProxmoxVE/pull/4873)) - ActualBudget: fix update check (file instead of folder check) [@MickLesk](https://github.com/MickLesk) ([#4874](https://github.com/community-scripts/ProxmoxVE/pull/4874)) - Omada Controller: Fix libssl url [@tremor021](https://github.com/tremor021) ([#4868](https://github.com/community-scripts/ProxmoxVE/pull/4868)) ### 🌐 Website - #### 🐞 Bug Fixes - Revert "Update package dependencies in package.json and package-lock.json (#4845) [@BramSuurdje](https://github.com/BramSuurdje) ([#4869](https://github.com/community-scripts/ProxmoxVE/pull/4869)) ### 💥 Breaking Changes - Remove Authentik script [@tremor021](https://github.com/tremor021) ([#4867](https://github.com/community-scripts/ProxmoxVE/pull/4867)) ## 2025-05-30 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - slskd: add space in sed command [@vhsdream](https://github.com/vhsdream) ([#4853](https://github.com/community-scripts/ProxmoxVE/pull/4853)) - Alpine Traefik: Fix working directory and plugins [@tremor021](https://github.com/tremor021) ([#4838](https://github.com/community-scripts/ProxmoxVE/pull/4838)) ### 🌐 Website - #### 🐞 Bug Fixes - Update package dependencies in package.json and package-lock.json [@enough-jainil](https://github.com/enough-jainil) ([#4845](https://github.com/community-scripts/ProxmoxVE/pull/4845)) ## 2025-05-29 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - SearXNG fix limiter [@CrazyWolf13](https://github.com/CrazyWolf13) ([#4834](https://github.com/community-scripts/ProxmoxVE/pull/4834)) - Docmost: add jq before nodejs install [@MickLesk](https://github.com/MickLesk) ([#4831](https://github.com/community-scripts/ProxmoxVE/pull/4831)) - Alpine Traefik: Fix Dashboard not beign accessible [@tremor021](https://github.com/tremor021) ([#4828](https://github.com/community-scripts/ProxmoxVE/pull/4828)) - MySQL: Fix Wrong Command [@MickLesk](https://github.com/MickLesk) ([#4820](https://github.com/community-scripts/ProxmoxVE/pull/4820)) - docs: fix casing of OpenWrt [@GoetzGoerisch](https://github.com/GoetzGoerisch) ([#4805](https://github.com/community-scripts/ProxmoxVE/pull/4805)) - #### ✨ New Features - Docker-VM: set individual Hostname / Disk-Space formatting [@MickLesk](https://github.com/MickLesk) ([#4821](https://github.com/community-scripts/ProxmoxVE/pull/4821)) ## 2025-05-28 ### 🆕 New Scripts - Umbrel-OS [@MickLesk](https://github.com/MickLesk) ([#4788](https://github.com/community-scripts/ProxmoxVE/pull/4788)) - oauth2-proxy ([#4784](https://github.com/community-scripts/ProxmoxVE/pull/4784)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Habitica: Use Node20 [@MickLesk](https://github.com/MickLesk) ([#4796](https://github.com/community-scripts/ProxmoxVE/pull/4796)) - Alpine-Node-RED: add service to rc [@MickLesk](https://github.com/MickLesk) ([#4783](https://github.com/community-scripts/ProxmoxVE/pull/4783)) - #### ✨ New Features - Pulse: use prebuild tarball file / remove unneeded npm actions [@MickLesk](https://github.com/MickLesk) ([#4776](https://github.com/community-scripts/ProxmoxVE/pull/4776)) - #### 💥 Breaking Changes - Refactor: Linkwarden + OS Upgrade [@MickLesk](https://github.com/MickLesk) ([#4756](https://github.com/community-scripts/ProxmoxVE/pull/4756)) - #### 🔧 Refactor - refactor: use binary and randomize credentials in tinyauth [@steveiliop56](https://github.com/steveiliop56) ([#4632](https://github.com/community-scripts/ProxmoxVE/pull/4632)) - MariaDB CLI Update, Go Install Helper & Minor Cleanup [@MickLesk](https://github.com/MickLesk) ([#4793](https://github.com/community-scripts/ProxmoxVE/pull/4793)) - Refactor: Remove redundant dependencies & unify unzip usage [@MickLesk](https://github.com/MickLesk) ([#4780](https://github.com/community-scripts/ProxmoxVE/pull/4780)) - Refactor: Remove gpg / gnupg from script base [@MickLesk](https://github.com/MickLesk) ([#4775](https://github.com/community-scripts/ProxmoxVE/pull/4775)) ### 🌐 Website - #### 📝 Script Information - pulse: correct url in note [@xb00tt](https://github.com/xb00tt) ([#4809](https://github.com/community-scripts/ProxmoxVE/pull/4809)) ## 2025-05-27 ### 🆕 New Scripts - Backrest ([#4766](https://github.com/community-scripts/ProxmoxVE/pull/4766)) - Pulse ([#4728](https://github.com/community-scripts/ProxmoxVE/pull/4728)) ### 🚀 Updated Scripts - Alpine-Vaultwarden: Increase min disk requirements to 1GB [@neyzm](https://github.com/neyzm) ([#4764](https://github.com/community-scripts/ProxmoxVE/pull/4764)) - #### 🐞 Bug Fixes - lldap: fix update-check [@MickLesk](https://github.com/MickLesk) ([#4742](https://github.com/community-scripts/ProxmoxVE/pull/4742)) - #### ✨ New Features - Big NodeJS Update: Use Helper Function on all Install-Scripts [@MickLesk](https://github.com/MickLesk) ([#4744](https://github.com/community-scripts/ProxmoxVE/pull/4744)) - #### 🔧 Refactor - merge MariaDB to tools.func Installer [@MickLesk](https://github.com/MickLesk) ([#4753](https://github.com/community-scripts/ProxmoxVE/pull/4753)) - merge PostgreSQL to tools.func Installer [@MickLesk](https://github.com/MickLesk) ([#4752](https://github.com/community-scripts/ProxmoxVE/pull/4752)) ### 🌐 Website - #### 📝 Script Information - Increase default RAM allocation for BunkerWeb to 8192MB [@TheophileDiot](https://github.com/TheophileDiot) ([#4762](https://github.com/community-scripts/ProxmoxVE/pull/4762)) ## 2025-05-26 ### 🆕 New Scripts - Argus ([#4717](https://github.com/community-scripts/ProxmoxVE/pull/4717)) - Kasm ([#4716](https://github.com/community-scripts/ProxmoxVE/pull/4716)) ### 🚀 Updated Scripts - Excalidraw: increase HDD to 10GB [@MickLesk](https://github.com/MickLesk) ([#4718](https://github.com/community-scripts/ProxmoxVE/pull/4718)) - #### 🐞 Bug Fixes - BREAKING CHANGE: Fix PocketID for v1.0.0 [@vhsdream](https://github.com/vhsdream) ([#4711](https://github.com/community-scripts/ProxmoxVE/pull/4711)) - InspIRCd: Fix release name in release url [@tremor021](https://github.com/tremor021) ([#4720](https://github.com/community-scripts/ProxmoxVE/pull/4720)) ## 2025-05-25 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Pelican-panel: back-up database if using sqlite [@bvdberg01](https://github.com/bvdberg01) ([#4700](https://github.com/community-scripts/ProxmoxVE/pull/4700)) - #### 🔧 Refactor - Pterodactyl panel read typo [@bvdberg01](https://github.com/bvdberg01) ([#4701](https://github.com/community-scripts/ProxmoxVE/pull/4701)) ## 2025-05-24 ## 2025-05-23 ### 🚀 Updated Scripts - #### 🔧 Refactor - TYPO: Fix nexcloud to nextcloud (VM) [@Stoufiler](https://github.com/Stoufiler) ([#4670](https://github.com/community-scripts/ProxmoxVE/pull/4670)) ### 🌐 Website - #### 📝 Script Information - Update Icons to selfhst/icons (FreePBX & Configarr) [@MickLesk](https://github.com/MickLesk) ([#4680](https://github.com/community-scripts/ProxmoxVE/pull/4680)) ### 💥 Breaking Changes - Remove rtsptoweb (deprecated) [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#4686](https://github.com/community-scripts/ProxmoxVE/pull/4686)) ## 2025-05-22 ### 🆕 New Scripts - FreePBX ([#4648](https://github.com/community-scripts/ProxmoxVE/pull/4648)) - cloudflare-ddns ([#4647](https://github.com/community-scripts/ProxmoxVE/pull/4647)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - slskd: fix #4649 [@vhsdream](https://github.com/vhsdream) ([#4651](https://github.com/community-scripts/ProxmoxVE/pull/4651)) - #### ✨ New Features - Paperless-AI: Add RAG chat [@tremor021](https://github.com/tremor021) ([#4635](https://github.com/community-scripts/ProxmoxVE/pull/4635)) ### 🧰 Maintenance - #### 📂 Github - [gh]: Feature: Header-Generation for vm | tools | addon [@MickLesk](https://github.com/MickLesk) ([#4643](https://github.com/community-scripts/ProxmoxVE/pull/4643)) ### 🌐 Website - #### 📝 Script Information - Commafeed: move to Documents category [@diemade](https://github.com/diemade) ([#4665](https://github.com/community-scripts/ProxmoxVE/pull/4665)) ## 2025-05-21 ### 🆕 New Scripts - configarr ([#4620](https://github.com/community-scripts/ProxmoxVE/pull/4620)) - babybuddy ([#4619](https://github.com/community-scripts/ProxmoxVE/pull/4619)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Alpine-Node-RED: Update Service File [@MickLesk](https://github.com/MickLesk) ([#4628](https://github.com/community-scripts/ProxmoxVE/pull/4628)) - RustDesk Server: Fix update for older installs [@tremor021](https://github.com/tremor021) ([#4612](https://github.com/community-scripts/ProxmoxVE/pull/4612)) - #### ✨ New Features - Tandoor Recipes: Capture version information when installing [@jbolla](https://github.com/jbolla) ([#4633](https://github.com/community-scripts/ProxmoxVE/pull/4633)) ## 2025-05-20 ### 🚀 Updated Scripts - [tools.func]: Update fetch_and_deploy_gh_release function [@tremor021](https://github.com/tremor021) ([#4605](https://github.com/community-scripts/ProxmoxVE/pull/4605)) - [core] New Features for Config File function [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#4601](https://github.com/community-scripts/ProxmoxVE/pull/4601)) ### 🌐 Website - #### 📝 Script Information - Website: harmonize all Logos | use jsDelivr CDN links for icons from selfhst/icons repo [@MickLesk](https://github.com/MickLesk) ([#4603](https://github.com/community-scripts/ProxmoxVE/pull/4603)) ## 2025-05-19 ### 🆕 New Scripts - rclone ([#4579](https://github.com/community-scripts/ProxmoxVE/pull/4579)) ### 🚀 Updated Scripts - increase ressources of Homarr (3 vCPU / 6GB RAM) [@MickLesk](https://github.com/MickLesk) ([#4583](https://github.com/community-scripts/ProxmoxVE/pull/4583)) - #### 🐞 Bug Fixes - Various unrelated fixes to kimai update script [@jamezpolley](https://github.com/jamezpolley) ([#4549](https://github.com/community-scripts/ProxmoxVE/pull/4549)) - #### ✨ New Features - RustDesk Server: Add WebUI [@tremor021](https://github.com/tremor021) ([#4590](https://github.com/community-scripts/ProxmoxVE/pull/4590)) ## 2025-05-18 ### 🚀 Updated Scripts - tools.func - Add function to create self-signed certificates [@tremor021](https://github.com/tremor021) ([#4562](https://github.com/community-scripts/ProxmoxVE/pull/4562)) - #### 🐞 Bug Fixes - Homarr: fix the build [@CrazyWolf13](https://github.com/CrazyWolf13) ([#4569](https://github.com/community-scripts/ProxmoxVE/pull/4569)) ### 🌐 Website - #### 📝 Script Information - Fix Dashy Config Path on Frontend [@CrazyWolf13](https://github.com/CrazyWolf13) ([#4570](https://github.com/community-scripts/ProxmoxVE/pull/4570)) ## 2025-05-17 ## 2025-05-16 ### 🧰 Maintenance - #### 💾 Core - [core] Refactor Config File function [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#4528](https://github.com/community-scripts/ProxmoxVE/pull/4528)) - [core] Fix Bridge detection in Advanced Mode [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#4522](https://github.com/community-scripts/ProxmoxVE/pull/4522)) - [core] Enable SSH_KEY and SSH without password [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#4523](https://github.com/community-scripts/ProxmoxVE/pull/4523)) - #### 📂 Github - Updates to contributor docs/guide [@tremor021](https://github.com/tremor021) ([#4518](https://github.com/community-scripts/ProxmoxVE/pull/4518)) ### 🌐 Website - Remove bolt.diy script [@tremor021](https://github.com/tremor021) ([#4541](https://github.com/community-scripts/ProxmoxVE/pull/4541)) ## 2025-05-15 ### 🆕 New Scripts - bitmagnet ([#4493](https://github.com/community-scripts/ProxmoxVE/pull/4493)) ### 🚀 Updated Scripts - core: Add TAB3 formatting var to core [@tremor021](https://github.com/tremor021) ([#4496](https://github.com/community-scripts/ProxmoxVE/pull/4496)) - Update scripts that use "read -p" to properly indent text [@tremor021](https://github.com/tremor021) ([#4498](https://github.com/community-scripts/ProxmoxVE/pull/4498)) - #### ✨ New Features - tools.func: fix some things & add ruby default function [@MickLesk](https://github.com/MickLesk) ([#4507](https://github.com/community-scripts/ProxmoxVE/pull/4507)) ### 🧰 Maintenance - #### 💾 Core - core: fix bridge detection for OVS [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#4495](https://github.com/community-scripts/ProxmoxVE/pull/4495)) ## 2025-05-14 ### 🆕 New Scripts - odoo ([#4477](https://github.com/community-scripts/ProxmoxVE/pull/4477)) - asterisk ([#4468](https://github.com/community-scripts/ProxmoxVE/pull/4468)) ### 🚀 Updated Scripts - fix: fetch_release_and_deploy function [@CrazyWolf13](https://github.com/CrazyWolf13) ([#4478](https://github.com/community-scripts/ProxmoxVE/pull/4478)) - Website: re-add documenso & some little bugfixes [@MickLesk](https://github.com/MickLesk) ([#4456](https://github.com/community-scripts/ProxmoxVE/pull/4456)) - #### 🐞 Bug Fixes - Add make installation dependency to Actual Budget script [@maciejmatczak](https://github.com/maciejmatczak) ([#4485](https://github.com/community-scripts/ProxmoxVE/pull/4485)) - Bookstack: fix copy of themes/uploads/storage [@MickLesk](https://github.com/MickLesk) ([#4457](https://github.com/community-scripts/ProxmoxVE/pull/4457)) - Alpine-Rclone: Fix location of passwords file [@tremor021](https://github.com/tremor021) ([#4465](https://github.com/community-scripts/ProxmoxVE/pull/4465)) - #### ✨ New Features - monitor-all: improvements - tag based filtering [@grizmin](https://github.com/grizmin) ([#4437](https://github.com/community-scripts/ProxmoxVE/pull/4437)) ### 🧰 Maintenance - #### 📂 Github - Add Github app for auto PR merge [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#4461](https://github.com/community-scripts/ProxmoxVE/pull/4461)) ## 2025-05-13 ### 🆕 New Scripts - gatus ([#4443](https://github.com/community-scripts/ProxmoxVE/pull/4443)) - alpine-gatus ([#4442](https://github.com/community-scripts/ProxmoxVE/pull/4442)) ### 🚀 Updated Scripts - update some improvements from dev (tools.func) [@MickLesk](https://github.com/MickLesk) ([#4430](https://github.com/community-scripts/ProxmoxVE/pull/4430)) - #### 🐞 Bug Fixes - openhab: use zulu17-jdk [@moodyblue](https://github.com/moodyblue) ([#4438](https://github.com/community-scripts/ProxmoxVE/pull/4438)) - #### 🔧 Refactor - openhab. correct some typos [@moodyblue](https://github.com/moodyblue) ([#4448](https://github.com/community-scripts/ProxmoxVE/pull/4448)) ### 🧰 Maintenance - #### 💾 Core - fix: improve bridge detection in all network interface configuration files [@filippolauria](https://github.com/filippolauria) ([#4413](https://github.com/community-scripts/ProxmoxVE/pull/4413)) ### 🌐 Website - #### 📝 Script Information - Jellyfin Media Server: Update configuration path [@tremor021](https://github.com/tremor021) ([#4434](https://github.com/community-scripts/ProxmoxVE/pull/4434)) - Pingvin Share: Added explanation on how to add/edit environment variables [@tremor021](https://github.com/tremor021) ([#4432](https://github.com/community-scripts/ProxmoxVE/pull/4432)) - pingvin.json: fix typo [@warmbo](https://github.com/warmbo) ([#4426](https://github.com/community-scripts/ProxmoxVE/pull/4426)) ## 2025-05-12 ### 🆕 New Scripts - Alpine-Traefik [@MickLesk](https://github.com/MickLesk) ([#4412](https://github.com/community-scripts/ProxmoxVE/pull/4412)) ### 🚀 Updated Scripts - Alpine: Use onliner for updates [@tremor021](https://github.com/tremor021) ([#4414](https://github.com/community-scripts/ProxmoxVE/pull/4414)) - #### 🐞 Bug Fixes - homarr: fetch versions dynamically from source repo [@CrazyWolf13](https://github.com/CrazyWolf13) ([#4409](https://github.com/community-scripts/ProxmoxVE/pull/4409)) - #### ✨ New Features - Feature: LXC-Delete (pve helper): add "all items" [@MickLesk](https://github.com/MickLesk) ([#4296](https://github.com/community-scripts/ProxmoxVE/pull/4296)) ### 🧰 Maintenance - #### 💾 Core - Config file Function in build.func [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#4411](https://github.com/community-scripts/ProxmoxVE/pull/4411)) ### 🌐 Website - #### 📝 Script Information - Navidrome - Fix config path (use /etc/ instead of /var/lib) [@quake1508](https://github.com/quake1508) ([#4406](https://github.com/community-scripts/ProxmoxVE/pull/4406)) ## 2025-05-11 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Zammad: Enable ElasticSearch service [@tremor021](https://github.com/tremor021) ([#4391](https://github.com/community-scripts/ProxmoxVE/pull/4391)) ## 2025-05-10 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - (fix) Documenso: fix build failures [@vhsdream](https://github.com/vhsdream) ([#4382](https://github.com/community-scripts/ProxmoxVE/pull/4382)) - Jellyseerr: better handling of node and pnpm [@MickLesk](https://github.com/MickLesk) ([#4365](https://github.com/community-scripts/ProxmoxVE/pull/4365)) ## 2025-05-09 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Authentik: change install to UV & increase resources to 10GB RAM [@MickLesk](https://github.com/MickLesk) ([#4364](https://github.com/community-scripts/ProxmoxVE/pull/4364)) - #### ✨ New Features - HomeAssistant-Core: update script for 2025.5+ [@MickLesk](https://github.com/MickLesk) ([#4363](https://github.com/community-scripts/ProxmoxVE/pull/4363)) - Feature: autologin for Alpine [@MickLesk](https://github.com/MickLesk) ([#4344](https://github.com/community-scripts/ProxmoxVE/pull/4344)) ### 🧰 Maintenance - #### 💾 Core - fix: detect all bridge types, not just vmbr prefix [@filippolauria](https://github.com/filippolauria) ([#4351](https://github.com/community-scripts/ProxmoxVE/pull/4351)) - #### 📂 Github - Add a Repo check to all Workflows [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#4339](https://github.com/community-scripts/ProxmoxVE/pull/4339)) - Auto-Merge Automatic PR [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#4343](https://github.com/community-scripts/ProxmoxVE/pull/4343)) ## 2025-05-08 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - SearXNG: fix to resolve yaml dependency error [@Biendeo](https://github.com/Biendeo) ([#4322](https://github.com/community-scripts/ProxmoxVE/pull/4322)) - Bugfix: Mikrotik & Pimox HAOS VM (NEXTID) [@MickLesk](https://github.com/MickLesk) ([#4313](https://github.com/community-scripts/ProxmoxVE/pull/4313)) ### 🧰 Maintenance - #### 💾 Core - build.func Change the menu for Bridge Selection [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#4326](https://github.com/community-scripts/ProxmoxVE/pull/4326)) ### 🌐 Website - FAQ: Explanation "updatable" [@tremor021](https://github.com/tremor021) ([#4300](https://github.com/community-scripts/ProxmoxVE/pull/4300)) ## 2025-05-07 ### 🚀 Updated Scripts - Alpine scripts: Set minimum disk space to 0.5GB [@tremor021](https://github.com/tremor021) ([#4288](https://github.com/community-scripts/ProxmoxVE/pull/4288)) - #### 🐞 Bug Fixes - SuwayomiServer: Bump Java to v21, code formating [@tremor021](https://github.com/tremor021) ([#3987](https://github.com/community-scripts/ProxmoxVE/pull/3987)) - #### ✨ New Features - Feature: get correct next VMID [@MickLesk](https://github.com/MickLesk) ([#4292](https://github.com/community-scripts/ProxmoxVE/pull/4292)) ### 🌐 Website - #### 📝 Script Information - OpenWebUI: Update docs link [@tremor021](https://github.com/tremor021) ([#4298](https://github.com/community-scripts/ProxmoxVE/pull/4298)) ## 2025-05-06 ### 🆕 New Scripts - alpine-transmission ([#4277](https://github.com/community-scripts/ProxmoxVE/pull/4277)) - streamlink-webui ([#4262](https://github.com/community-scripts/ProxmoxVE/pull/4262)) - Fumadocs ([#4263](https://github.com/community-scripts/ProxmoxVE/pull/4263)) - alpine-rclone ([#4265](https://github.com/community-scripts/ProxmoxVE/pull/4265)) - alpine-tinyauth ([#4264](https://github.com/community-scripts/ProxmoxVE/pull/4264)) - Re-Add: ActualBudget [@MickLesk](https://github.com/MickLesk) ([#4228](https://github.com/community-scripts/ProxmoxVE/pull/4228)) ### 🧰 Maintenance - #### 🐞 Bug Fixes - whiptail menu - cancel button now exists the advanced menu [@MickLesk](https://github.com/MickLesk) ([#4259](https://github.com/community-scripts/ProxmoxVE/pull/4259)) ## 2025-05-05 ### 🆕 New Scripts - Alpine-Komodo [@MickLesk](https://github.com/MickLesk) ([#4234](https://github.com/community-scripts/ProxmoxVE/pull/4234)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Docker VM: Fix variable doublequoting [@tremor021](https://github.com/tremor021) ([#4245](https://github.com/community-scripts/ProxmoxVE/pull/4245)) - Alpine-Vaultwarden: Fix sed and better cert generation [@tremor021](https://github.com/tremor021) ([#4232](https://github.com/community-scripts/ProxmoxVE/pull/4232)) - Apache Guacamole: Fix Version Grepping [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#4229](https://github.com/community-scripts/ProxmoxVE/pull/4229)) - #### ✨ New Features - Docker-VM: Add Disk Size choice [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#4241](https://github.com/community-scripts/ProxmoxVE/pull/4241)) - #### 🔧 Refactor - Refactor: Komodo update logic [@MickLesk](https://github.com/MickLesk) ([#4231](https://github.com/community-scripts/ProxmoxVE/pull/4231)) ### 🧰 Maintenance - #### 💾 Core - tools.func: better function handling + gs as new helper [@MickLesk](https://github.com/MickLesk) ([#4238](https://github.com/community-scripts/ProxmoxVE/pull/4238)) ## 2025-05-04 ### 🌐 Website - Code Server: Update misleading name, description and icon. [@ArmainAP](https://github.com/ArmainAP) ([#4211](https://github.com/community-scripts/ProxmoxVE/pull/4211)) ## 2025-05-03 ### 🚀 Updated Scripts - Vaultwarden: Enable HTTPS by default [@tremor021](https://github.com/tremor021) ([#4197](https://github.com/community-scripts/ProxmoxVE/pull/4197)) - #### 🐞 Bug Fixes - Vaultwarden: Fix access URL [@tremor021](https://github.com/tremor021) ([#4199](https://github.com/community-scripts/ProxmoxVE/pull/4199)) ### 🌐 Website - #### 📝 Script Information - SFTPGo: Switch updatable to true on website [@tremor021](https://github.com/tremor021) ([#4186](https://github.com/community-scripts/ProxmoxVE/pull/4186)) ## 2025-05-02 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - NetBox: Fix typo in sed command, preventing install [@tremor021](https://github.com/tremor021) ([#4179](https://github.com/community-scripts/ProxmoxVE/pull/4179)) ### 🌐 Website - #### 🐞 Bug Fixes - Changed the random script button to be the same as all the other buttons [@BramSuurdje](https://github.com/BramSuurdje) ([#4183](https://github.com/community-scripts/ProxmoxVE/pull/4183)) - #### 📝 Script Information - Habitica: correct config path [@DrDonoso](https://github.com/DrDonoso) ([#4181](https://github.com/community-scripts/ProxmoxVE/pull/4181)) ## 2025-05-01 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Readeck: Fix release crawling [@tremor021](https://github.com/tremor021) ([#4172](https://github.com/community-scripts/ProxmoxVE/pull/4172)) - #### ✨ New Features - homepage: Add build time var [@burgerga](https://github.com/burgerga) ([#4167](https://github.com/community-scripts/ProxmoxVE/pull/4167)) ### 🌐 Website - Bump vite from 6.2.6 to 6.3.4 in /frontend [@dependabot[bot]](https://github.com/dependabot[bot]) ([#4159](https://github.com/community-scripts/ProxmoxVE/pull/4159)) - #### 📝 Script Information - Grafana: add config path & documentation [@JamborJan](https://github.com/JamborJan) ([#4162](https://github.com/community-scripts/ProxmoxVE/pull/4162)) ================================================ FILE: .github/changelogs/2025/06.md ================================================ ## 2025-06-30 ### 🆕 New Scripts - Alpine Syncthing [@MickLesk](https://github.com/MickLesk) ([#5586](https://github.com/community-scripts/ProxmoxVE/pull/5586)) - Kapowarr ([#5584](https://github.com/community-scripts/ProxmoxVE/pull/5584)) ### 🚀 Updated Scripts - Fixing Cloudflare DDNS - lack of resources [@meszolym](https://github.com/meszolym) ([#5600](https://github.com/community-scripts/ProxmoxVE/pull/5600)) - #### 🐞 Bug Fixes - Immich: make changes to automatically enable QuickSync [@vhsdream](https://github.com/vhsdream) ([#5560](https://github.com/community-scripts/ProxmoxVE/pull/5560)) - Apache Guacamole: Install auth-jdbc component that matches release version [@tremor021](https://github.com/tremor021) ([#5563](https://github.com/community-scripts/ProxmoxVE/pull/5563)) - #### ✨ New Features - tools.func: optimize binary installs with fetch_and_deploy helper [@MickLesk](https://github.com/MickLesk) ([#5588](https://github.com/community-scripts/ProxmoxVE/pull/5588)) - [core]: add ipv6 configuration support [@MickLesk](https://github.com/MickLesk) ([#5575](https://github.com/community-scripts/ProxmoxVE/pull/5575)) ## 2025-06-29 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Linkwarden: Add backing up of data folder to the update function [@tremor021](https://github.com/tremor021) ([#5548](https://github.com/community-scripts/ProxmoxVE/pull/5548)) - #### ✨ New Features - Add cron-job api-key env variable to homarr script [@Meierschlumpf](https://github.com/Meierschlumpf) ([#5204](https://github.com/community-scripts/ProxmoxVE/pull/5204)) ### 🧰 Maintenance - #### 📝 Documentation - update readme with valid discord link. other one expired [@BramSuurdje](https://github.com/BramSuurdje) ([#5567](https://github.com/community-scripts/ProxmoxVE/pull/5567)) ### 🌐 Website - Update script-item.tsx [@ape364](https://github.com/ape364) ([#5549](https://github.com/community-scripts/ProxmoxVE/pull/5549)) - #### 🐞 Bug Fixes - fix bug in tooltip that would always render 'updateable' [@BramSuurdje](https://github.com/BramSuurdje) ([#5552](https://github.com/community-scripts/ProxmoxVE/pull/5552)) ## 2025-06-28 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Ollama: Clean up old Ollama files before running update [@tremor021](https://github.com/tremor021) ([#5534](https://github.com/community-scripts/ProxmoxVE/pull/5534)) - ONLYOFFICE: Update install script to manually create RabbitMQ user [@tremor021](https://github.com/tremor021) ([#5535](https://github.com/community-scripts/ProxmoxVE/pull/5535)) ### 🌐 Website - #### 📝 Script Information - Booklore: Correct documentation and website [@pieman3000](https://github.com/pieman3000) ([#5528](https://github.com/community-scripts/ProxmoxVE/pull/5528)) ## 2025-06-27 ### 🆕 New Scripts - BookLore ([#5524](https://github.com/community-scripts/ProxmoxVE/pull/5524)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - wizarr: remove unneeded tmp file [@MickLesk](https://github.com/MickLesk) ([#5517](https://github.com/community-scripts/ProxmoxVE/pull/5517)) ### 🧰 Maintenance - #### 🐞 Bug Fixes - Remove npm legacy errors, created single source of truth for ESlint. updated analytics url. updated script background [@BramSuurdje](https://github.com/BramSuurdje) ([#5498](https://github.com/community-scripts/ProxmoxVE/pull/5498)) - #### 📂 Github - New workflow to push to gitea and change links to gitea [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#5510](https://github.com/community-scripts/ProxmoxVE/pull/5510)) ### 🌐 Website - #### 📝 Script Information - Wireguard, Update Link to Documentation. [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#5514](https://github.com/community-scripts/ProxmoxVE/pull/5514)) ## 2025-06-26 ### 🆕 New Scripts - ConvertX ([#5484](https://github.com/community-scripts/ProxmoxVE/pull/5484)) ### 🚀 Updated Scripts - [tools] Update setup_nodejs function [@tremor021](https://github.com/tremor021) ([#5488](https://github.com/community-scripts/ProxmoxVE/pull/5488)) - [tools] Fix setup_mongodb function [@tremor021](https://github.com/tremor021) ([#5486](https://github.com/community-scripts/ProxmoxVE/pull/5486)) ## 2025-06-25 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Docmost: Increase resources [@tremor021](https://github.com/tremor021) ([#5458](https://github.com/community-scripts/ProxmoxVE/pull/5458)) - #### ✨ New Features - tools.func: new helper for imagemagick [@MickLesk](https://github.com/MickLesk) ([#5452](https://github.com/community-scripts/ProxmoxVE/pull/5452)) - YunoHost: add Update-Function [@MickLesk](https://github.com/MickLesk) ([#5450](https://github.com/community-scripts/ProxmoxVE/pull/5450)) - #### 🔧 Refactor - Refactor: Tailscale [@MickLesk](https://github.com/MickLesk) ([#5454](https://github.com/community-scripts/ProxmoxVE/pull/5454)) ### 🌐 Website - #### 🐞 Bug Fixes - Update Tooltips component to conditionally display updateable status based on item type [@BramSuurdje](https://github.com/BramSuurdje) ([#5461](https://github.com/community-scripts/ProxmoxVE/pull/5461)) - Refactor CommandMenu to prevent duplicate scripts across categories [@BramSuurdje](https://github.com/BramSuurdje) ([#5463](https://github.com/community-scripts/ProxmoxVE/pull/5463)) - #### ✨ New Features - Enhance InstallCommand component to support Gitea as an alternative source for installation scripts. [@BramSuurdje](https://github.com/BramSuurdje) ([#5464](https://github.com/community-scripts/ProxmoxVE/pull/5464)) - #### 📝 Script Information - Website: mark VM's and "OS"-LXC's as updatable [@MickLesk](https://github.com/MickLesk) ([#5453](https://github.com/community-scripts/ProxmoxVE/pull/5453)) ## 2025-06-24 ### 🆕 New Scripts - ONLYOFFICE Docs ([#5420](https://github.com/community-scripts/ProxmoxVE/pull/5420)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - GoMFT: tmpl bugfix to work with current version until a new release pushed [@MickLesk](https://github.com/MickLesk) ([#5435](https://github.com/community-scripts/ProxmoxVE/pull/5435)) - Update all Alpine Scripts to atleast 1GB HDD [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#5418](https://github.com/community-scripts/ProxmoxVE/pull/5418)) - #### ✨ New Features - general: update all alpine scripts to version 3.22 [@MickLesk](https://github.com/MickLesk) ([#5428](https://github.com/community-scripts/ProxmoxVE/pull/5428)) - Minio: use latest version or latest feature rich version [@MickLesk](https://github.com/MickLesk) ([#5423](https://github.com/community-scripts/ProxmoxVE/pull/5423)) - [core]: Improve GitHub release fetch robustness with split timeouts and retry logic [@MickLesk](https://github.com/MickLesk) ([#5422](https://github.com/community-scripts/ProxmoxVE/pull/5422)) - #### 💥 Breaking Changes - bump scripts (Installer) from Ubuntu 22.04 to Ubuntu 24.04 (agentdvr, emby, jellyfin, plex, shinobi) [@MickLesk](https://github.com/MickLesk) ([#5434](https://github.com/community-scripts/ProxmoxVE/pull/5434)) - #### 🔧 Refactor - Refactor: MeTube to uv based install [@MickLesk](https://github.com/MickLesk) ([#5411](https://github.com/community-scripts/ProxmoxVE/pull/5411)) - Refactor: Prometheus PVE Exporter to uv based install [@MickLesk](https://github.com/MickLesk) ([#5412](https://github.com/community-scripts/ProxmoxVE/pull/5412)) - Refactor: ESPHome to uv based install [@MickLesk](https://github.com/MickLesk) ([#5413](https://github.com/community-scripts/ProxmoxVE/pull/5413)) ## 2025-06-23 ### 🆕 New Scripts - Alpine-Forgejo by @Johann3s-H [@MickLesk](https://github.com/MickLesk) ([#5396](https://github.com/community-scripts/ProxmoxVE/pull/5396)) ### 🚀 Updated Scripts - [core]: tools.func -> autoupdate npm to newest version on install [@MickLesk](https://github.com/MickLesk) ([#5397](https://github.com/community-scripts/ProxmoxVE/pull/5397)) - #### 🐞 Bug Fixes - PLANKA: Fix the update procedure [@tremor021](https://github.com/tremor021) ([#5391](https://github.com/community-scripts/ProxmoxVE/pull/5391)) - changed trilium github repo [@miggi92](https://github.com/miggi92) ([#5390](https://github.com/community-scripts/ProxmoxVE/pull/5390)) - changedetection: fix: hermetic msedge [@CrazyWolf13](https://github.com/CrazyWolf13) ([#5388](https://github.com/community-scripts/ProxmoxVE/pull/5388)) ### 🌐 Website - #### 📝 Script Information - MariaDB: Add information about Adminer on website [@tremor021](https://github.com/tremor021) ([#5400](https://github.com/community-scripts/ProxmoxVE/pull/5400)) ## 2025-06-22 ### 🚀 Updated Scripts - [core]: fix timing issues while template update & timezone setup at create new LXC [@MickLesk](https://github.com/MickLesk) ([#5358](https://github.com/community-scripts/ProxmoxVE/pull/5358)) - alpine: increase hdd to 1gb [@MickLesk](https://github.com/MickLesk) ([#5377](https://github.com/community-scripts/ProxmoxVE/pull/5377)) - #### 🐞 Bug Fixes - fix: casing and naming error after #5254 [@GoetzGoerisch](https://github.com/GoetzGoerisch) ([#5380](https://github.com/community-scripts/ProxmoxVE/pull/5380)) - fix: install_adminer > setup_adminer [@MickLesk](https://github.com/MickLesk) ([#5356](https://github.com/community-scripts/ProxmoxVE/pull/5356)) - gitea: Update gitea.sh to stop update failures [@tystuyfzand](https://github.com/tystuyfzand) ([#5361](https://github.com/community-scripts/ProxmoxVE/pull/5361)) - #### 🔧 Refactor - Immich: unpin release; use fetch & deploy function for update [@vhsdream](https://github.com/vhsdream) ([#5355](https://github.com/community-scripts/ProxmoxVE/pull/5355)) ## 2025-06-21 ## 2025-06-20 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Immich: remove unneeded tmp_file [@MickLesk](https://github.com/MickLesk) ([#5332](https://github.com/community-scripts/ProxmoxVE/pull/5332)) - Huntarr: Fix duplicate update status messages [@tremor021](https://github.com/tremor021) ([#5336](https://github.com/community-scripts/ProxmoxVE/pull/5336)) - fix planka Tags [@CrazyWolf13](https://github.com/CrazyWolf13) ([#5311](https://github.com/community-scripts/ProxmoxVE/pull/5311)) - PLANKA: Better DB password generate [@tremor021](https://github.com/tremor021) ([#5313](https://github.com/community-scripts/ProxmoxVE/pull/5313)) - Immich: Hotfix for #5299 [@vhsdream](https://github.com/vhsdream) ([#5300](https://github.com/community-scripts/ProxmoxVE/pull/5300)) - changedetection: add msedge as Browser dependency [@Niklas04](https://github.com/Niklas04) ([#5301](https://github.com/community-scripts/ProxmoxVE/pull/5301)) - #### ✨ New Features - (turnkey) Add OpenLDAP as a TurnKey option [@mhaligowski](https://github.com/mhaligowski) ([#5305](https://github.com/community-scripts/ProxmoxVE/pull/5305)) - #### 🔧 Refactor - [core]: unify misc/*.func scripts with centralized logic from core.func [@MickLesk](https://github.com/MickLesk) ([#5316](https://github.com/community-scripts/ProxmoxVE/pull/5316)) - Refactor: migrate AdventureLog update to uv and GitHub release logic [@MickLesk](https://github.com/MickLesk) ([#5318](https://github.com/community-scripts/ProxmoxVE/pull/5318)) - Refactor: migrate Jupyter Notebook to uv-based installation with update support [@MickLesk](https://github.com/MickLesk) ([#5320](https://github.com/community-scripts/ProxmoxVE/pull/5320)) ### 🌐 Website - #### 📝 Script Information - Argus: fix wrong port on website [@MickLesk](https://github.com/MickLesk) ([#5322](https://github.com/community-scripts/ProxmoxVE/pull/5322)) ## 2025-06-19 ### 🆕 New Scripts - Ubuntu 25.04 VM [@MickLesk](https://github.com/MickLesk) ([#5284](https://github.com/community-scripts/ProxmoxVE/pull/5284)) - PLANKA ([#5277](https://github.com/community-scripts/ProxmoxVE/pull/5277)) - Wizarr ([#5273](https://github.com/community-scripts/ProxmoxVE/pull/5273)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - immich-install.sh: restore pgvector module install [@vhsdream](https://github.com/vhsdream) ([#5286](https://github.com/community-scripts/ProxmoxVE/pull/5286)) - Immich: prepare for v1.135.0 [@vhsdream](https://github.com/vhsdream) ([#5025](https://github.com/community-scripts/ProxmoxVE/pull/5025)) ### 🧰 Maintenance - #### ✨ New Features - [core]: Feature - Check Quorum Status in create_lxc to prevent issues [@MickLesk](https://github.com/MickLesk) ([#5278](https://github.com/community-scripts/ProxmoxVE/pull/5278)) - [core]: add validation and replace recursion for invalid inputs in adv. settings [@MickLesk](https://github.com/MickLesk) ([#5291](https://github.com/community-scripts/ProxmoxVE/pull/5291)) ### 🌐 Website - #### 📝 Script Information - cloudflare-ddns: fix typo in info-text [@LukaZagar](https://github.com/LukaZagar) ([#5263](https://github.com/community-scripts/ProxmoxVE/pull/5263)) ## 2025-06-18 ### 🆕 New Scripts - FileBrowser Quantum [@MickLesk](https://github.com/MickLesk) ([#5248](https://github.com/community-scripts/ProxmoxVE/pull/5248)) - Huntarr ([#5249](https://github.com/community-scripts/ProxmoxVE/pull/5249)) ### 🚀 Updated Scripts - tools.func: Standardized and Renamed Setup Functions [@MickLesk](https://github.com/MickLesk) ([#5241](https://github.com/community-scripts/ProxmoxVE/pull/5241)) - #### 🐞 Bug Fixes - Immich: fix prompt clobber issue [@vhsdream](https://github.com/vhsdream) ([#5231](https://github.com/community-scripts/ProxmoxVE/pull/5231)) - #### 🔧 Refactor - Refactor all VM's to same logic & functions [@MickLesk](https://github.com/MickLesk) ([#5254](https://github.com/community-scripts/ProxmoxVE/pull/5254)) - upgrade old Scriptcalls to new tools.func calls [@MickLesk](https://github.com/MickLesk) ([#5242](https://github.com/community-scripts/ProxmoxVE/pull/5242)) ## 2025-06-17 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - gitea-mirror: increase build ressources [@CrazyWolf13](https://github.com/CrazyWolf13) ([#5235](https://github.com/community-scripts/ProxmoxVE/pull/5235)) - Immich: ensure in proper working dir when updating [@vhsdream](https://github.com/vhsdream) ([#5227](https://github.com/community-scripts/ProxmoxVE/pull/5227)) - Update IP-Tag [@DesertGamer](https://github.com/DesertGamer) ([#5226](https://github.com/community-scripts/ProxmoxVE/pull/5226)) - trilium: fix update function after db changes folder [@tjcomserv](https://github.com/tjcomserv) ([#5207](https://github.com/community-scripts/ProxmoxVE/pull/5207)) - #### ✨ New Features - LibreTranslate: Add .env for easier configuration [@tremor021](https://github.com/tremor021) ([#5216](https://github.com/community-scripts/ProxmoxVE/pull/5216)) ### 🌐 Website - #### 📝 Script Information - IPTag: Better explanation [@MickLesk](https://github.com/MickLesk) ([#5213](https://github.com/community-scripts/ProxmoxVE/pull/5213)) ## 2025-06-16 ### 🆕 New Scripts - Intel NIC offload Fix by @rcastley [@MickLesk](https://github.com/MickLesk) ([#5155](https://github.com/community-scripts/ProxmoxVE/pull/5155)) ### 🚀 Updated Scripts - [core] Move install_php() from VED to main [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#5182](https://github.com/community-scripts/ProxmoxVE/pull/5182)) - Firefly: Add Data Importer to LXC [@tremor021](https://github.com/tremor021) ([#5159](https://github.com/community-scripts/ProxmoxVE/pull/5159)) - #### 🐞 Bug Fixes - Wastebin: Fix missing dependencies [@tremor021](https://github.com/tremor021) ([#5185](https://github.com/community-scripts/ProxmoxVE/pull/5185)) - Kasm: Storing Creds Fix [@omiinaya](https://github.com/omiinaya) ([#5162](https://github.com/community-scripts/ProxmoxVE/pull/5162)) - #### ✨ New Features - add optional Cloud-init support to Debian VM script [@koendiender](https://github.com/koendiender) ([#5137](https://github.com/community-scripts/ProxmoxVE/pull/5137)) - #### 🔧 Refactor - Refactor: 2FAuth [@tremor021](https://github.com/tremor021) ([#5184](https://github.com/community-scripts/ProxmoxVE/pull/5184)) ### 🌐 Website - Refactor layout and component styles for improved responsiveness [@BramSuurdje](https://github.com/BramSuurdje) ([#5195](https://github.com/community-scripts/ProxmoxVE/pull/5195)) - #### 🐞 Bug Fixes - Refactor ScriptItem and ConfigFile components to conditionally render config file location. Update ConfigFile to accept configPath prop instead of item. [@BramSuurdje](https://github.com/BramSuurdje) ([#5197](https://github.com/community-scripts/ProxmoxVE/pull/5197)) - Update default image asset in the public directory and update api route to only search for files that end with .json [@BramSuurdje](https://github.com/BramSuurdje) ([#5179](https://github.com/community-scripts/ProxmoxVE/pull/5179)) - #### ✨ New Features - Update default image asset in the public directory [@BramSuurdje](https://github.com/BramSuurdje) ([#5189](https://github.com/community-scripts/ProxmoxVE/pull/5189)) ## 2025-06-15 ### 🆕 New Scripts - LibreTranslate ([#5154](https://github.com/community-scripts/ProxmoxVE/pull/5154)) ## 2025-06-14 ### 🚀 Updated Scripts - [core] Update install_mariadb func [@tremor021](https://github.com/tremor021) ([#5138](https://github.com/community-scripts/ProxmoxVE/pull/5138)) - #### 🐞 Bug Fixes - flowiseai: set NodeJS to Version 20 [@MickLesk](https://github.com/MickLesk) ([#5130](https://github.com/community-scripts/ProxmoxVE/pull/5130)) - Update dolibarr-install.sh - Get largest version number [@tjcomserv](https://github.com/tjcomserv) ([#5127](https://github.com/community-scripts/ProxmoxVE/pull/5127)) ## 2025-06-13 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Zigbee2MQTT: Fix missing directory [@tremor021](https://github.com/tremor021) ([#5120](https://github.com/community-scripts/ProxmoxVE/pull/5120)) ### 🌐 Website - #### 📝 Script Information - Umbrel OS: Fix bad disk size shown on website [@tremor021](https://github.com/tremor021) ([#5125](https://github.com/community-scripts/ProxmoxVE/pull/5125)) ## 2025-06-12 ### 🆕 New Scripts - Manage my Damn Life ([#5100](https://github.com/community-scripts/ProxmoxVE/pull/5100)) ### 🚀 Updated Scripts - Kasm: Increase Ressources & Hint for Fuse / Swap [@MickLesk](https://github.com/MickLesk) ([#5112](https://github.com/community-scripts/ProxmoxVE/pull/5112)) ## 2025-06-11 ## 2025-06-10 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Element Synapse: switched from development to production mode and fixed update [@Frankmaaan](https://github.com/Frankmaaan) ([#5066](https://github.com/community-scripts/ProxmoxVE/pull/5066)) - Tinyauth: Fix creation of service file [@tremor021](https://github.com/tremor021) ([#5090](https://github.com/community-scripts/ProxmoxVE/pull/5090)) - Dolibarr: Fix typo in SQL command [@tremor021](https://github.com/tremor021) ([#5091](https://github.com/community-scripts/ProxmoxVE/pull/5091)) ### 🧰 Maintenance - #### 📡 API - [core] Prevent API form sending Data when disabled [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#5080](https://github.com/community-scripts/ProxmoxVE/pull/5080)) ### 🌐 Website - #### 📝 Script Information - Immich: Update JSON [@vhsdream](https://github.com/vhsdream) ([#5085](https://github.com/community-scripts/ProxmoxVE/pull/5085)) ## 2025-06-09 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Authelia: Fix the URL of the container [@tremor021](https://github.com/tremor021) ([#5064](https://github.com/community-scripts/ProxmoxVE/pull/5064)) ### 🌐 Website - GoMFT: Remove from website temporarily [@tremor021](https://github.com/tremor021) ([#5065](https://github.com/community-scripts/ProxmoxVE/pull/5065)) ## 2025-06-08 ### 🆕 New Scripts - Minarca ([#5058](https://github.com/community-scripts/ProxmoxVE/pull/5058)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - zot: fix missing var (Dev -> Main) [@MickLesk](https://github.com/MickLesk) ([#5056](https://github.com/community-scripts/ProxmoxVE/pull/5056)) - #### ✨ New Features - karakeep: Add more configuration defaults [@vhsdream](https://github.com/vhsdream) ([#5054](https://github.com/community-scripts/ProxmoxVE/pull/5054)) ## 2025-06-07 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - alpine-it-tools fix update [@CrazyWolf13](https://github.com/CrazyWolf13) ([#5039](https://github.com/community-scripts/ProxmoxVE/pull/5039)) ### 🧰 Maintenance - #### 💾 Core - Fix typo in build.func [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#5041](https://github.com/community-scripts/ProxmoxVE/pull/5041)) ## 2025-06-06 ### 🆕 New Scripts - Zot-Registry ([#5016](https://github.com/community-scripts/ProxmoxVE/pull/5016)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - zipline: fix old upload copy from v3 to v4 [@MickLesk](https://github.com/MickLesk) ([#5015](https://github.com/community-scripts/ProxmoxVE/pull/5015)) ## 2025-06-05 ### 🆕 New Scripts - Lyrion Music Server ([#4992](https://github.com/community-scripts/ProxmoxVE/pull/4992)) - gitea-mirror ([#4967](https://github.com/community-scripts/ProxmoxVE/pull/4967)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Zipline: Fix PostgreSQL install [@tremor021](https://github.com/tremor021) ([#4989](https://github.com/community-scripts/ProxmoxVE/pull/4989)) - Homarr: add nodejs upgrade [@CrazyWolf13](https://github.com/CrazyWolf13) ([#4974](https://github.com/community-scripts/ProxmoxVE/pull/4974)) - add FUSE to rclone [@Frankmaaan](https://github.com/Frankmaaan) ([#4972](https://github.com/community-scripts/ProxmoxVE/pull/4972)) - #### ✨ New Features - Zitadel: Upgrade Install to PSQL 17 [@MickLesk](https://github.com/MickLesk) ([#5000](https://github.com/community-scripts/ProxmoxVE/pull/5000)) ### 🌐 Website - #### 📝 Script Information - Fix clean-lxcs.sh type categorization [@bitspill](https://github.com/bitspill) ([#4980](https://github.com/community-scripts/ProxmoxVE/pull/4980)) ## 2025-06-04 ### 🚀 Updated Scripts - Pulse: add polkit for sudoless web updates [@rcourtman](https://github.com/rcourtman) ([#4970](https://github.com/community-scripts/ProxmoxVE/pull/4970)) - Pulse: add correct Port for URL output [@rcourtman](https://github.com/rcourtman) ([#4951](https://github.com/community-scripts/ProxmoxVE/pull/4951)) - #### 🐞 Bug Fixes - [refactor] Seelf [@tremor021](https://github.com/tremor021) ([#4954](https://github.com/community-scripts/ProxmoxVE/pull/4954)) ## 2025-06-03 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Kasm: Swap fix [@omiinaya](https://github.com/omiinaya) ([#4937](https://github.com/community-scripts/ProxmoxVE/pull/4937)) ### 🌐 Website - #### 📝 Script Information - netbox: correct website URL [@theincrediblenoone](https://github.com/theincrediblenoone) ([#4952](https://github.com/community-scripts/ProxmoxVE/pull/4952)) ## 2025-06-02 ### 🆕 New Scripts - PVE-Privilege-Converter [@MickLesk](https://github.com/MickLesk) ([#4906](https://github.com/community-scripts/ProxmoxVE/pull/4906)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - fix(wastebin): use tar asset [@dbeg](https://github.com/dbeg) ([#4934](https://github.com/community-scripts/ProxmoxVE/pull/4934)) - MySQL/MariaDB: fix create user with password [@MickLesk](https://github.com/MickLesk) ([#4918](https://github.com/community-scripts/ProxmoxVE/pull/4918)) - Fix alpine-tinyauth env configuration parsing logic [@gokussjx](https://github.com/gokussjx) ([#4901](https://github.com/community-scripts/ProxmoxVE/pull/4901)) - #### 💥 Breaking Changes - make Pulse installation non-interactive [@rcourtman](https://github.com/rcourtman) ([#4848](https://github.com/community-scripts/ProxmoxVE/pull/4848)) ### 🧰 Maintenance - #### 💾 Core - [core] add hw-accelerated for immich, openwebui / remove scrypted [@MickLesk](https://github.com/MickLesk) ([#4927](https://github.com/community-scripts/ProxmoxVE/pull/4927)) - [core] tools.func: Bugfix old gpg key for mysql & little improvements [@MickLesk](https://github.com/MickLesk) ([#4916](https://github.com/community-scripts/ProxmoxVE/pull/4916)) - [core] Varius fixes to Config file feature [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#4924](https://github.com/community-scripts/ProxmoxVE/pull/4924)) ### 🌐 Website - #### 🐞 Bug Fixes - Display default password even if there isn't a default username [@0risc](https://github.com/0risc) ([#4900](https://github.com/community-scripts/ProxmoxVE/pull/4900)) ## 2025-06-01 ### 🆕 New Scripts - immich ([#4886](https://github.com/community-scripts/ProxmoxVE/pull/4886)) ### 🌐 Website - #### 📝 Script Information - AdventureLog: add login credentials info [@tremor021](https://github.com/tremor021) ([#4887](https://github.com/community-scripts/ProxmoxVE/pull/4887)) ================================================ FILE: .github/changelogs/2025/07.md ================================================ ## 2025-07-31 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - OpenObserve: Fix release fetching [@tremor021](https://github.com/tremor021) ([#6409](https://github.com/community-scripts/ProxmoxVE/pull/6409)) - #### 💥 Breaking Changes - Remove temp. Tandoor during Install & Update Issues [@MickLesk](https://github.com/MickLesk) ([#6438](https://github.com/community-scripts/ProxmoxVE/pull/6438)) - #### 🔧 Refactor - Refactor: listmonk [@tremor021](https://github.com/tremor021) ([#6399](https://github.com/community-scripts/ProxmoxVE/pull/6399)) - Refactor: Neo4j [@tremor021](https://github.com/tremor021) ([#6418](https://github.com/community-scripts/ProxmoxVE/pull/6418)) - Refactor: Monica [@tremor021](https://github.com/tremor021) ([#6416](https://github.com/community-scripts/ProxmoxVE/pull/6416)) - Refactor: Memos [@tremor021](https://github.com/tremor021) ([#6415](https://github.com/community-scripts/ProxmoxVE/pull/6415)) - Refactor: Opengist [@tremor021](https://github.com/tremor021) ([#6423](https://github.com/community-scripts/ProxmoxVE/pull/6423)) - Refactor: Ombi [@tremor021](https://github.com/tremor021) ([#6422](https://github.com/community-scripts/ProxmoxVE/pull/6422)) - Refactor: MySpeed [@tremor021](https://github.com/tremor021) ([#6417](https://github.com/community-scripts/ProxmoxVE/pull/6417)) ## 2025-07-30 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Strip SD and NS prefixes before writing to config file [@mattv8](https://github.com/mattv8) ([#6356](https://github.com/community-scripts/ProxmoxVE/pull/6356)) - [core] fix: expand $CACHER_IP in apt-proxy-detect.sh (revert quoted heredoc) [@MickLesk](https://github.com/MickLesk) ([#6385](https://github.com/community-scripts/ProxmoxVE/pull/6385)) - PiAlert: bugfix dependencies [@leiweibau](https://github.com/leiweibau) ([#6396](https://github.com/community-scripts/ProxmoxVE/pull/6396)) - Librespeed-Rust: Fix service name and RELEASE var fetching [@tremor021](https://github.com/tremor021) ([#6378](https://github.com/community-scripts/ProxmoxVE/pull/6378)) - n8n: add build-essential as dependency [@MickLesk](https://github.com/MickLesk) ([#6392](https://github.com/community-scripts/ProxmoxVE/pull/6392)) - Habitica: Fix trusted domains [@tremor021](https://github.com/tremor021) ([#6380](https://github.com/community-scripts/ProxmoxVE/pull/6380)) - Mafl: Fix undeclared var [@tremor021](https://github.com/tremor021) ([#6371](https://github.com/community-scripts/ProxmoxVE/pull/6371)) - #### 🔧 Refactor - Refactor: Lidarr [@tremor021](https://github.com/tremor021) ([#6379](https://github.com/community-scripts/ProxmoxVE/pull/6379)) - Refactor: Kubo [@tremor021](https://github.com/tremor021) ([#6376](https://github.com/community-scripts/ProxmoxVE/pull/6376)) - Refactor: Komga [@tremor021](https://github.com/tremor021) ([#6374](https://github.com/community-scripts/ProxmoxVE/pull/6374)) - Refactor: Koillection [@tremor021](https://github.com/tremor021) ([#6373](https://github.com/community-scripts/ProxmoxVE/pull/6373)) - Refactor: Karakeep [@tremor021](https://github.com/tremor021) ([#6372](https://github.com/community-scripts/ProxmoxVE/pull/6372)) ## 2025-07-29 ### 🆕 New Scripts - Jeedom ([#6336](https://github.com/community-scripts/ProxmoxVE/pull/6336)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - PiAlert: add new dependencies [@leiweibau](https://github.com/leiweibau) ([#6337](https://github.com/community-scripts/ProxmoxVE/pull/6337)) - Element Synapse CT RAM increased from 1024 to 2048 [@tjcomserv](https://github.com/tjcomserv) ([#6330](https://github.com/community-scripts/ProxmoxVE/pull/6330)) - #### 🔧 Refactor - Refactor: HomeBox [@tremor021](https://github.com/tremor021) ([#6338](https://github.com/community-scripts/ProxmoxVE/pull/6338)) - Refactor: InspIRCd [@tremor021](https://github.com/tremor021) ([#6343](https://github.com/community-scripts/ProxmoxVE/pull/6343)) - Refactor: Jackett [@tremor021](https://github.com/tremor021) ([#6344](https://github.com/community-scripts/ProxmoxVE/pull/6344)) - Update keycloak script to support configuration of latest release (v26) [@remz1337](https://github.com/remz1337) ([#6322](https://github.com/community-scripts/ProxmoxVE/pull/6322)) - Refactor: Photoprism [@MickLesk](https://github.com/MickLesk) ([#6335](https://github.com/community-scripts/ProxmoxVE/pull/6335)) - Refactor: Autobrr [@tremor021](https://github.com/tremor021) ([#6302](https://github.com/community-scripts/ProxmoxVE/pull/6302)) ## 2025-07-28 ### 🚀 Updated Scripts - Refactor: Cronicle [@tremor021](https://github.com/tremor021) ([#6314](https://github.com/community-scripts/ProxmoxVE/pull/6314)) - Refactor: HiveMQ [@tremor021](https://github.com/tremor021) ([#6313](https://github.com/community-scripts/ProxmoxVE/pull/6313)) - #### 🐞 Bug Fixes - fix: SSH authorized keys not added in Alpine install script [@enihsyou](https://github.com/enihsyou) ([#6316](https://github.com/community-scripts/ProxmoxVE/pull/6316)) - fix: removing ",gw=" from GATE var when reading/writing from/to config. [@teohz](https://github.com/teohz) ([#6177](https://github.com/community-scripts/ProxmoxVE/pull/6177)) - add 'g++' to actualbudget-install.sh [@saivishnu725](https://github.com/saivishnu725) ([#6293](https://github.com/community-scripts/ProxmoxVE/pull/6293)) - #### ✨ New Features - [core]: create_lxc: better handling, fix lock handling, improve template validation & storage selection UX [@MickLesk](https://github.com/MickLesk) ([#6296](https://github.com/community-scripts/ProxmoxVE/pull/6296)) - ProxmoxVE 9.0 Beta: add BETA Version as test in pve_check [@MickLesk](https://github.com/MickLesk) ([#6295](https://github.com/community-scripts/ProxmoxVE/pull/6295)) - karakeep: Run workers in prod without tsx [@vhsdream](https://github.com/vhsdream) ([#6285](https://github.com/community-scripts/ProxmoxVE/pull/6285)) - #### 🔧 Refactor - Refactor: grocy [@tremor021](https://github.com/tremor021) ([#6307](https://github.com/community-scripts/ProxmoxVE/pull/6307)) - Refactor: Navidrome [@tremor021](https://github.com/tremor021) ([#6300](https://github.com/community-scripts/ProxmoxVE/pull/6300)) - Refactor: Gotify [@tremor021](https://github.com/tremor021) ([#6301](https://github.com/community-scripts/ProxmoxVE/pull/6301)) - Refactor: Grafana [@tremor021](https://github.com/tremor021) ([#6306](https://github.com/community-scripts/ProxmoxVE/pull/6306)) - Refactor: Argus [@tremor021](https://github.com/tremor021) ([#6305](https://github.com/community-scripts/ProxmoxVE/pull/6305)) - n8n: refactor environmentfile [@CrazyWolf13](https://github.com/CrazyWolf13) ([#6297](https://github.com/community-scripts/ProxmoxVE/pull/6297)) ### 🌐 Website - #### 🐞 Bug Fixes - temp change the analytics url to one that works untill community one is fixed [@BramSuurdje](https://github.com/BramSuurdje) ([#6319](https://github.com/community-scripts/ProxmoxVE/pull/6319)) ## 2025-07-27 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - karakeep: export DATA_DIR from user config in update [@vhsdream](https://github.com/vhsdream) ([#6283](https://github.com/community-scripts/ProxmoxVE/pull/6283)) - go2rtc: Fix release download handling [@tremor021](https://github.com/tremor021) ([#6280](https://github.com/community-scripts/ProxmoxVE/pull/6280)) ## 2025-07-26 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - PiAlert: Update dependencies [@leiweibau](https://github.com/leiweibau) ([#6251](https://github.com/community-scripts/ProxmoxVE/pull/6251)) ## 2025-07-25 ### 🆕 New Scripts - Cleanuparr ([#6238](https://github.com/community-scripts/ProxmoxVE/pull/6238)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Immich: fix #6236 [@vhsdream](https://github.com/vhsdream) ([#6243](https://github.com/community-scripts/ProxmoxVE/pull/6243)) - Wizarr: use absolute path to uv [@vhsdream](https://github.com/vhsdream) ([#6221](https://github.com/community-scripts/ProxmoxVE/pull/6221)) - Immich v1.136.0 [@vhsdream](https://github.com/vhsdream) ([#6219](https://github.com/community-scripts/ProxmoxVE/pull/6219)) ## 2025-07-24 ### 🆕 New Scripts - Alpine TeamSpeak Server [@tremor021](https://github.com/tremor021) ([#6201](https://github.com/community-scripts/ProxmoxVE/pull/6201)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Immich: Pin Version to v1.135.3 [@MickLesk](https://github.com/MickLesk) ([#6212](https://github.com/community-scripts/ProxmoxVE/pull/6212)) - Habitica: force npm to 10 [@MickLesk](https://github.com/MickLesk) ([#6192](https://github.com/community-scripts/ProxmoxVE/pull/6192)) - sabnzbd: add uv setup in update [@MickLesk](https://github.com/MickLesk) ([#6191](https://github.com/community-scripts/ProxmoxVE/pull/6191)) - #### ✨ New Features - SnipeIT - Update dependencies [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#6217](https://github.com/community-scripts/ProxmoxVE/pull/6217)) - Refactor: VictoriaMetrics [@tremor021](https://github.com/tremor021) ([#6210](https://github.com/community-scripts/ProxmoxVE/pull/6210)) - Headscale: Add headscale-admin UI as option [@tremor021](https://github.com/tremor021) ([#6205](https://github.com/community-scripts/ProxmoxVE/pull/6205)) - #### 🔧 Refactor - Refactor: Gokapi [@tremor021](https://github.com/tremor021) ([#6197](https://github.com/community-scripts/ProxmoxVE/pull/6197)) - Refactor: duplicati [@tremor021](https://github.com/tremor021) ([#6202](https://github.com/community-scripts/ProxmoxVE/pull/6202)) - Refactor: go2rtc [@tremor021](https://github.com/tremor021) ([#6198](https://github.com/community-scripts/ProxmoxVE/pull/6198)) - Refactor: Headscale [@tremor021](https://github.com/tremor021) ([#6180](https://github.com/community-scripts/ProxmoxVE/pull/6180)) ## 2025-07-23 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - documenso: remove customerId by creating initial user [@MickLesk](https://github.com/MickLesk) ([#6171](https://github.com/community-scripts/ProxmoxVE/pull/6171)) ## 2025-07-22 ### 🆕 New Scripts - Salt ([#6116](https://github.com/community-scripts/ProxmoxVE/pull/6116)) - LinkStack ([#6137](https://github.com/community-scripts/ProxmoxVE/pull/6137)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - tools.func - fix typo for target_file [@tjcomserv](https://github.com/tjcomserv) ([#6156](https://github.com/community-scripts/ProxmoxVE/pull/6156)) - fix(nginxproxymanager.sh): Set the version number before build. [@JMarcosHP](https://github.com/JMarcosHP) ([#6139](https://github.com/community-scripts/ProxmoxVE/pull/6139)) - #### ✨ New Features - Fixed the previous fix of the anti-nag hook and propagated fixes everywhere [@imcrazytwkr](https://github.com/imcrazytwkr) ([#6162](https://github.com/community-scripts/ProxmoxVE/pull/6162)) - [core]: Improved LXC Container Startup Handling [@MickLesk](https://github.com/MickLesk) ([#6142](https://github.com/community-scripts/ProxmoxVE/pull/6142)) - wallos: add cron in installer for yearly cost [@CrazyWolf13](https://github.com/CrazyWolf13) ([#6133](https://github.com/community-scripts/ProxmoxVE/pull/6133)) - #### 💥 Breaking Changes - gitea-mirror: add: migration to 3.0 [@CrazyWolf13](https://github.com/CrazyWolf13) ([#6138](https://github.com/community-scripts/ProxmoxVE/pull/6138)) - #### 🔧 Refactor - [core]: tools.func: increase setup_php function [@MickLesk](https://github.com/MickLesk) ([#6141](https://github.com/community-scripts/ProxmoxVE/pull/6141)) ### 🌐 Website - Bump form-data from 4.0.3 to 4.0.4 in /frontend [@dependabot[bot]](https://github.com/dependabot[bot]) ([#6150](https://github.com/community-scripts/ProxmoxVE/pull/6150)) ## 2025-07-21 ### 🆕 New Scripts - Teamspeak-Server ([#6121](https://github.com/community-scripts/ProxmoxVE/pull/6121)) ### 🚀 Updated Scripts - pve-post-installer: remove Nag-File if already exist [@luckman212](https://github.com/luckman212) ([#6098](https://github.com/community-scripts/ProxmoxVE/pull/6098)) - #### 🐞 Bug Fixes - firefly: fix permissions at update [@MickLesk](https://github.com/MickLesk) ([#6119](https://github.com/community-scripts/ProxmoxVE/pull/6119)) - nginxproxymanager: remove injected footer link (tteck) [@MickLesk](https://github.com/MickLesk) ([#6117](https://github.com/community-scripts/ProxmoxVE/pull/6117)) ## 2025-07-20 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Fix OpenWebUI install/update scripts [@karamanliev](https://github.com/karamanliev) ([#6093](https://github.com/community-scripts/ProxmoxVE/pull/6093)) - #### ✨ New Features - karakeep: add DB_WAL_MODE; suppress test output [@vhsdream](https://github.com/vhsdream) ([#6101](https://github.com/community-scripts/ProxmoxVE/pull/6101)) ## 2025-07-19 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Fixed nag script on ProxMox 8.4.5 [@imcrazytwkr](https://github.com/imcrazytwkr) ([#6084](https://github.com/community-scripts/ProxmoxVE/pull/6084)) ## 2025-07-18 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - AdventureLog: add backup folder before update [@MickLesk](https://github.com/MickLesk) ([#6066](https://github.com/community-scripts/ProxmoxVE/pull/6066)) - #### ✨ New Features - Bar-Assistant: add Cocktail database [@MickLesk](https://github.com/MickLesk) ([#6068](https://github.com/community-scripts/ProxmoxVE/pull/6068)) - ErsatzTV: use project prebuild ffmpeg version [@MickLesk](https://github.com/MickLesk) ([#6067](https://github.com/community-scripts/ProxmoxVE/pull/6067)) ## 2025-07-17 ### 🆕 New Scripts - Cloudreve ([#6044](https://github.com/community-scripts/ProxmoxVE/pull/6044)) ### 🚀 Updated Scripts - config-file: set GATE [@ahmaddxb](https://github.com/ahmaddxb) ([#6042](https://github.com/community-scripts/ProxmoxVE/pull/6042)) - #### 🐞 Bug Fixes - add "setup_composer" in update_script (baikal, bar-assistant, firefly) [@MickLesk](https://github.com/MickLesk) ([#6047](https://github.com/community-scripts/ProxmoxVE/pull/6047)) - PLANKA: Fix update procedure [@tremor021](https://github.com/tremor021) ([#6031](https://github.com/community-scripts/ProxmoxVE/pull/6031)) - #### ✨ New Features - Reactive Resume: switch source to community-maintained fork [@vhsdream](https://github.com/vhsdream) ([#6051](https://github.com/community-scripts/ProxmoxVE/pull/6051)) ## 2025-07-16 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - homepage.sh: resolves #6028 [@vhsdream](https://github.com/vhsdream) ([#6032](https://github.com/community-scripts/ProxmoxVE/pull/6032)) - karakeep-install: Disable Playwright browser download, remove MCP build [@vhsdream](https://github.com/vhsdream) ([#5833](https://github.com/community-scripts/ProxmoxVE/pull/5833)) - #### 🔧 Refactor - chore: reorganize nginxproxymanager update script [@Kirbo](https://github.com/Kirbo) ([#5971](https://github.com/community-scripts/ProxmoxVE/pull/5971)) ## 2025-07-15 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - checkmk: change password crawling based on there docs [@MickLesk](https://github.com/MickLesk) ([#6001](https://github.com/community-scripts/ProxmoxVE/pull/6001)) - Whiptail: Improve Dialogue to work with ESC [@MickLesk](https://github.com/MickLesk) ([#6003](https://github.com/community-scripts/ProxmoxVE/pull/6003)) - 2FAuth: Improve Update-Check [@MickLesk](https://github.com/MickLesk) ([#5998](https://github.com/community-scripts/ProxmoxVE/pull/5998)) - #### 💥 Breaking Changes - EMQX: Purge Old Install (remove acl.conf too!) [@MickLesk](https://github.com/MickLesk) ([#5999](https://github.com/community-scripts/ProxmoxVE/pull/5999)) - #### 🔧 Refactor - Refactor: PeaNUT [@MickLesk](https://github.com/MickLesk) ([#6002](https://github.com/community-scripts/ProxmoxVE/pull/6002)) ## 2025-07-14 ### 🆕 New Scripts - Bar Assistant ([#5977](https://github.com/community-scripts/ProxmoxVE/pull/5977)) - Mealie ([#5968](https://github.com/community-scripts/ProxmoxVE/pull/5968)) ### 🚀 Updated Scripts - Config-File: Some Addons, Bugfixes... [@MickLesk](https://github.com/MickLesk) ([#5978](https://github.com/community-scripts/ProxmoxVE/pull/5978)) - #### 🐞 Bug Fixes - add --break-system-packages certbot-dns-cloudflare to the nginxproxym… [@tug-benson](https://github.com/tug-benson) ([#5957](https://github.com/community-scripts/ProxmoxVE/pull/5957)) - Dashy: remove unbound variable (RELEASE) [@MickLesk](https://github.com/MickLesk) ([#5974](https://github.com/community-scripts/ProxmoxVE/pull/5974)) ### 🌐 Website - #### 📝 Script Information - Update nic-offloading-fix: add Intel as search Text [@calvin-li-developer](https://github.com/calvin-li-developer) ([#5954](https://github.com/community-scripts/ProxmoxVE/pull/5954)) ## 2025-07-12 ## 2025-07-11 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - immich: hotfix #5921 [@vhsdream](https://github.com/vhsdream) ([#5938](https://github.com/community-scripts/ProxmoxVE/pull/5938)) - bookstack: add setup_composer in update [@MickLesk](https://github.com/MickLesk) ([#5935](https://github.com/community-scripts/ProxmoxVE/pull/5935)) - Quickfix: Immich: revert install sequence [@vhsdream](https://github.com/vhsdream) ([#5932](https://github.com/community-scripts/ProxmoxVE/pull/5932)) - #### ✨ New Features - Refactor & Function Bump: Docker [@MickLesk](https://github.com/MickLesk) ([#5889](https://github.com/community-scripts/ProxmoxVE/pull/5889)) - #### 🔧 Refactor - Immich: handle custom library dependency updates; other fixes [@vhsdream](https://github.com/vhsdream) ([#5896](https://github.com/community-scripts/ProxmoxVE/pull/5896)) ## 2025-07-10 ### 🚀 Updated Scripts - Refactor: Habitica [@MickLesk](https://github.com/MickLesk) ([#5911](https://github.com/community-scripts/ProxmoxVE/pull/5911)) - #### 🐞 Bug Fixes - core: fix breaking re-download of lxc containers [@MickLesk](https://github.com/MickLesk) ([#5906](https://github.com/community-scripts/ProxmoxVE/pull/5906)) - PLANKA: Fix paths to application directory [@tremor021](https://github.com/tremor021) ([#5900](https://github.com/community-scripts/ProxmoxVE/pull/5900)) - #### 🔧 Refactor - Refactor: EMQX + Update-Function + Improved NodeJS Crawling [@MickLesk](https://github.com/MickLesk) ([#5907](https://github.com/community-scripts/ProxmoxVE/pull/5907)) ## 2025-07-09 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Omada Update: add missing exit [@MickLesk](https://github.com/MickLesk) ([#5894](https://github.com/community-scripts/ProxmoxVE/pull/5894)) - FreshRSS: fix needed php modules [@MickLesk](https://github.com/MickLesk) ([#5886](https://github.com/community-scripts/ProxmoxVE/pull/5886)) - core: Fix VAAPI passthrough for unprivileged LXC containers via devX [@MickLesk](https://github.com/MickLesk) ([#5875](https://github.com/community-scripts/ProxmoxVE/pull/5875)) - tools.func: fix an bug while php libapache2-mod breaks [@MickLesk](https://github.com/MickLesk) ([#5857](https://github.com/community-scripts/ProxmoxVE/pull/5857)) - BabyBuddy: fix path issues for update [@MickLesk](https://github.com/MickLesk) ([#5856](https://github.com/community-scripts/ProxmoxVE/pull/5856)) - #### ✨ New Features - tools.func: strip leading folders for prebuild assets [@MickLesk](https://github.com/MickLesk) ([#5865](https://github.com/community-scripts/ProxmoxVE/pull/5865)) - #### 💥 Breaking Changes - Refactor: Stirling-PDF [@MickLesk](https://github.com/MickLesk) ([#5872](https://github.com/community-scripts/ProxmoxVE/pull/5872)) - #### 🔧 Refactor - Refactor: EMQX [@tremor021](https://github.com/tremor021) ([#5840](https://github.com/community-scripts/ProxmoxVE/pull/5840)) - Refactor: Excalidraw [@tremor021](https://github.com/tremor021) ([#5841](https://github.com/community-scripts/ProxmoxVE/pull/5841)) - Refactor: Firefly [@tremor021](https://github.com/tremor021) ([#5844](https://github.com/community-scripts/ProxmoxVE/pull/5844)) - Refactor: gatus [@tremor021](https://github.com/tremor021) ([#5849](https://github.com/community-scripts/ProxmoxVE/pull/5849)) - Refactor: FreshRSS [@tremor021](https://github.com/tremor021) ([#5847](https://github.com/community-scripts/ProxmoxVE/pull/5847)) - Refactor: Fluid-Calendar [@tremor021](https://github.com/tremor021) ([#5846](https://github.com/community-scripts/ProxmoxVE/pull/5846)) - Refactor: Commafeed [@tremor021](https://github.com/tremor021) ([#5802](https://github.com/community-scripts/ProxmoxVE/pull/5802)) - Refactor: FlareSolverr [@tremor021](https://github.com/tremor021) ([#5845](https://github.com/community-scripts/ProxmoxVE/pull/5845)) - Refactor: Glance [@tremor021](https://github.com/tremor021) ([#5874](https://github.com/community-scripts/ProxmoxVE/pull/5874)) - Refactor: Gitea [@tremor021](https://github.com/tremor021) ([#5876](https://github.com/community-scripts/ProxmoxVE/pull/5876)) - Refactor: Ghost (use now MySQL) [@MickLesk](https://github.com/MickLesk) ([#5871](https://github.com/community-scripts/ProxmoxVE/pull/5871)) ### 🧰 Maintenance - #### 📂 Github - Github: AutoLabler | ChangeLog (Refactor) [@MickLesk](https://github.com/MickLesk) ([#5868](https://github.com/community-scripts/ProxmoxVE/pull/5868)) ## 2025-07-08 ### 🚀 Updated Scripts - Refactor: Emby [@tremor021](https://github.com/tremor021) ([#5839](https://github.com/community-scripts/ProxmoxVE/pull/5839)) - #### 🐞 Bug Fixes - Ollama: fix update script [@lucacome](https://github.com/lucacome) ([#5819](https://github.com/community-scripts/ProxmoxVE/pull/5819)) - #### ✨ New Features - tools.func: add ffmpeg + minor improvement [@MickLesk](https://github.com/MickLesk) ([#5834](https://github.com/community-scripts/ProxmoxVE/pull/5834)) - #### 🔧 Refactor - Refactor: ErsatzTV [@MickLesk](https://github.com/MickLesk) ([#5835](https://github.com/community-scripts/ProxmoxVE/pull/5835)) ## 2025-07-07 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Fix/stirling pdf script [@JcMinarro](https://github.com/JcMinarro) ([#5803](https://github.com/community-scripts/ProxmoxVE/pull/5803)) - gitea-mirror: update repo-url [@CrazyWolf13](https://github.com/CrazyWolf13) ([#5794](https://github.com/community-scripts/ProxmoxVE/pull/5794)) - Fix unbound var in pulse.sh [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#5807](https://github.com/community-scripts/ProxmoxVE/pull/5807)) - Bookstack: Fix PHP Issue & Bump to PHP 8.3 [@MickLesk](https://github.com/MickLesk) ([#5779](https://github.com/community-scripts/ProxmoxVE/pull/5779)) - #### ✨ New Features - Refactor: Threadfin (+ updatable) [@MickLesk](https://github.com/MickLesk) ([#5783](https://github.com/community-scripts/ProxmoxVE/pull/5783)) - tools.func: better handling when unpacking tarfiles in prebuild mode [@MickLesk](https://github.com/MickLesk) ([#5781](https://github.com/community-scripts/ProxmoxVE/pull/5781)) - tools.func: add AVX check for MongoDB [@MickLesk](https://github.com/MickLesk) ([#5780](https://github.com/community-scripts/ProxmoxVE/pull/5780)) - #### 🔧 Refactor - Refactor: Docmost [@tremor021](https://github.com/tremor021) ([#5806](https://github.com/community-scripts/ProxmoxVE/pull/5806)) - Refactor: Baby Buddy [@tremor021](https://github.com/tremor021) ([#5769](https://github.com/community-scripts/ProxmoxVE/pull/5769)) - Refactor: Changed the way we install BunkerWeb by leveraging the brand new install-bunkerweb.sh [@TheophileDiot](https://github.com/TheophileDiot) ([#5707](https://github.com/community-scripts/ProxmoxVE/pull/5707)) ### 🌐 Website - #### 📝 Script Information - PBS: add hint for advanced installs [@MickLesk](https://github.com/MickLesk) ([#5788](https://github.com/community-scripts/ProxmoxVE/pull/5788)) - EMQX: Add warning to website [@tremor021](https://github.com/tremor021) ([#5770](https://github.com/community-scripts/ProxmoxVE/pull/5770)) ## 2025-07-06 ### 🚀 Updated Scripts - Refactor: Barcodebuddy [@tremor021](https://github.com/tremor021) ([#5735](https://github.com/community-scripts/ProxmoxVE/pull/5735)) - #### 🐞 Bug Fixes - Fix update script for Mafl: ensure directory is removed recursively [@jonalbr](https://github.com/jonalbr) ([#5759](https://github.com/community-scripts/ProxmoxVE/pull/5759)) - BookStack: Typo fix [@tremor021](https://github.com/tremor021) ([#5746](https://github.com/community-scripts/ProxmoxVE/pull/5746)) - Resolves incorrect URL at end of Pocket ID script [@johnsturgeon](https://github.com/johnsturgeon) ([#5743](https://github.com/community-scripts/ProxmoxVE/pull/5743)) - #### ✨ New Features - [Feature] Add option to expose Docker via TCP port (alpine docker) [@oformaniuk](https://github.com/oformaniuk) ([#5716](https://github.com/community-scripts/ProxmoxVE/pull/5716)) - #### 🔧 Refactor - Refactor: Bitmagnet [@tremor021](https://github.com/tremor021) ([#5733](https://github.com/community-scripts/ProxmoxVE/pull/5733)) - Refactor: Baikal [@tremor021](https://github.com/tremor021) ([#5736](https://github.com/community-scripts/ProxmoxVE/pull/5736)) ## 2025-07-05 ### 🚀 Updated Scripts - #### 🔧 Refactor - Refactor: BookStack [@tremor021](https://github.com/tremor021) ([#5732](https://github.com/community-scripts/ProxmoxVE/pull/5732)) - Refactor: Authelia [@tremor021](https://github.com/tremor021) ([#5722](https://github.com/community-scripts/ProxmoxVE/pull/5722)) - Refactor: Dashy [@tremor021](https://github.com/tremor021) ([#5723](https://github.com/community-scripts/ProxmoxVE/pull/5723)) - Refactor: CryptPad [@tremor021](https://github.com/tremor021) ([#5724](https://github.com/community-scripts/ProxmoxVE/pull/5724)) - Refactor: ByteStash [@tremor021](https://github.com/tremor021) ([#5725](https://github.com/community-scripts/ProxmoxVE/pull/5725)) - Refactor: AgentDVR [@tremor021](https://github.com/tremor021) ([#5726](https://github.com/community-scripts/ProxmoxVE/pull/5726)) ## 2025-07-04 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Refactor: Mafl [@tremor021](https://github.com/tremor021) ([#5702](https://github.com/community-scripts/ProxmoxVE/pull/5702)) - Outline: Fix sed command for v0.85.0 [@tremor021](https://github.com/tremor021) ([#5688](https://github.com/community-scripts/ProxmoxVE/pull/5688)) - Komodo: Update Script to use FerretDB / remove psql & sqlite options [@MickLesk](https://github.com/MickLesk) ([#5690](https://github.com/community-scripts/ProxmoxVE/pull/5690)) - ESPHome: Fix Linking issue to prevent version mismatch [@MickLesk](https://github.com/MickLesk) ([#5685](https://github.com/community-scripts/ProxmoxVE/pull/5685)) - Cloudflare-DDNS: fix unvisible read command at install [@MickLesk](https://github.com/MickLesk) ([#5682](https://github.com/community-scripts/ProxmoxVE/pull/5682)) - #### ✨ New Features - Core layer refactor: centralized error traps and msg_* consistency [@MickLesk](https://github.com/MickLesk) ([#5705](https://github.com/community-scripts/ProxmoxVE/pull/5705)) - #### 💥 Breaking Changes - Update Iptag [@DesertGamer](https://github.com/DesertGamer) ([#5677](https://github.com/community-scripts/ProxmoxVE/pull/5677)) ### 🌐 Website - #### 📝 Script Information - MySQL phpMyAdmin Access Information [@austinpilz](https://github.com/austinpilz) ([#5679](https://github.com/community-scripts/ProxmoxVE/pull/5679)) ## 2025-07-03 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Zipline: Fix typo in uploads directory path [@tremor021](https://github.com/tremor021) ([#5662](https://github.com/community-scripts/ProxmoxVE/pull/5662)) - #### ✨ New Features - Improve asset matching in fetch_and_deploy_gh_release for prebuild and singlefile modes [@MickLesk](https://github.com/MickLesk) ([#5669](https://github.com/community-scripts/ProxmoxVE/pull/5669)) - #### 🔧 Refactor - Refactor: Trilium [@MickLesk](https://github.com/MickLesk) ([#5665](https://github.com/community-scripts/ProxmoxVE/pull/5665)) ### 🌐 Website - #### 📝 Script Information - Bump Icons to selfhst repo | switch svg to webp [@MickLesk](https://github.com/MickLesk) ([#5659](https://github.com/community-scripts/ProxmoxVE/pull/5659)) ## 2025-07-02 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Changedetection: Base64 encode the launch options [@tremor021](https://github.com/tremor021) ([#5640](https://github.com/community-scripts/ProxmoxVE/pull/5640)) - #### 🔧 Refactor - Refactor & Bump to Node24: Zigbee2MQTT [@MickLesk](https://github.com/MickLesk) ([#5638](https://github.com/community-scripts/ProxmoxVE/pull/5638)) ### 🌐 Website - #### 💥 Breaking Changes - Remove: Pingvin-Share [@MickLesk](https://github.com/MickLesk) ([#5635](https://github.com/community-scripts/ProxmoxVE/pull/5635)) - Remove: Readarr [@MickLesk](https://github.com/MickLesk) ([#5636](https://github.com/community-scripts/ProxmoxVE/pull/5636)) ## 2025-07-01 ### 🆕 New Scripts - Librespeed Rust ([#5614](https://github.com/community-scripts/ProxmoxVE/pull/5614)) - ITSM-NG ([#5615](https://github.com/community-scripts/ProxmoxVE/pull/5615)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Open WebUI: Fix Ollama update procedure [@tremor021](https://github.com/tremor021) ([#5601](https://github.com/community-scripts/ProxmoxVE/pull/5601)) - #### ✨ New Features - [tools]: increase fetch_and_deploy with dns pre check [@MickLesk](https://github.com/MickLesk) ([#5608](https://github.com/community-scripts/ProxmoxVE/pull/5608)) ### 🌐 Website - #### 📝 Script Information - Jellyfin GPU Passthrough NVIDIA Note [@austinpilz](https://github.com/austinpilz) ([#5625](https://github.com/community-scripts/ProxmoxVE/pull/5625)) ================================================ FILE: .github/changelogs/2025/08.md ================================================ ## 2025-08-31 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - n8n: Increase disk size [@tremor021](https://github.com/tremor021) ([#7320](https://github.com/community-scripts/ProxmoxVE/pull/7320)) ## 2025-08-30 ### 🚀 Updated Scripts - Immich: bump version to 1.140.0 [@vhsdream](https://github.com/vhsdream) ([#7275](https://github.com/community-scripts/ProxmoxVE/pull/7275)) - #### 🔧 Refactor - Refactor gitea-mirror env-file [@CrazyWolf13](https://github.com/CrazyWolf13) ([#7240](https://github.com/community-scripts/ProxmoxVE/pull/7240)) ## 2025-08-29 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - fix version check for pocket-id migration [@MickLesk](https://github.com/MickLesk) ([#7298](https://github.com/community-scripts/ProxmoxVE/pull/7298)) - fix: remove file creation at release fetching and version checking logic [@MickLesk](https://github.com/MickLesk) ([#7299](https://github.com/community-scripts/ProxmoxVE/pull/7299)) - Zitadel: Fix initial setup [@tremor021](https://github.com/tremor021) ([#7284](https://github.com/community-scripts/ProxmoxVE/pull/7284)) - post-pbs: increase enterprise recognition [@MickLesk](https://github.com/MickLesk) ([#7280](https://github.com/community-scripts/ProxmoxVE/pull/7280)) - Fix typo where install mode was changed instead of pinned version [@Brandsma](https://github.com/Brandsma) ([#7277](https://github.com/community-scripts/ProxmoxVE/pull/7277)) - #### ✨ New Features - [core]: feature - check_for_gh_release - version pinning [@MickLesk](https://github.com/MickLesk) ([#7279](https://github.com/community-scripts/ProxmoxVE/pull/7279)) - [feat]: migrate all update_scripts to new version helper (gh) [@MickLesk](https://github.com/MickLesk) ([#7262](https://github.com/community-scripts/ProxmoxVE/pull/7262)) ## 2025-08-28 ### 🆕 New Scripts - MediaManager ([#7238](https://github.com/community-scripts/ProxmoxVE/pull/7238)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - MMDL: add build-essential as dep [@vhsdream](https://github.com/vhsdream) ([#7266](https://github.com/community-scripts/ProxmoxVE/pull/7266)) - #### ✨ New Features - add support for multiple ip addresses in monitor-all.sh [@moshekv](https://github.com/moshekv) ([#7244](https://github.com/community-scripts/ProxmoxVE/pull/7244)) - [core]: feature - check_for_gh_release as update-handler [@MickLesk](https://github.com/MickLesk) ([#7254](https://github.com/community-scripts/ProxmoxVE/pull/7254)) - #### 💥 Breaking Changes - Flaresolverr: Pin to 3.3.25 (Python Issue) [@MickLesk](https://github.com/MickLesk) ([#7248](https://github.com/community-scripts/ProxmoxVE/pull/7248)) ### 🌐 Website - #### 📝 Script Information - Keycloak: Update website [@tremor021](https://github.com/tremor021) ([#7256](https://github.com/community-scripts/ProxmoxVE/pull/7256)) ## 2025-08-27 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - searxng: improve installation [@MickLesk](https://github.com/MickLesk) ([#7233](https://github.com/community-scripts/ProxmoxVE/pull/7233)) - Homebox: Fix Update Script [@MickLesk](https://github.com/MickLesk) ([#7232](https://github.com/community-scripts/ProxmoxVE/pull/7232)) ## 2025-08-26 ### 🆕 New Scripts - tracktor ([#7190](https://github.com/community-scripts/ProxmoxVE/pull/7190)) - PBS: Upgrade Script for v4 [@MickLesk](https://github.com/MickLesk) ([#7214](https://github.com/community-scripts/ProxmoxVE/pull/7214)) ### 🚀 Updated Scripts - #### ✨ New Features - Refactor: Post-PBS-Script [@MickLesk](https://github.com/MickLesk) ([#7213](https://github.com/community-scripts/ProxmoxVE/pull/7213)) - Refactor: Post-PMG-Script [@MickLesk](https://github.com/MickLesk) ([#7212](https://github.com/community-scripts/ProxmoxVE/pull/7212)) ### 🌐 Website - #### 📝 Script Information - [website] Update documentation URLs [@tremor021](https://github.com/tremor021) ([#7201](https://github.com/community-scripts/ProxmoxVE/pull/7201)) ## 2025-08-25 ### 🆕 New Scripts - Alpine-RustDesk Server [@tremor021](https://github.com/tremor021) ([#7191](https://github.com/community-scripts/ProxmoxVE/pull/7191)) - Alpine-Redlib ([#7178](https://github.com/community-scripts/ProxmoxVE/pull/7178)) - healthchecks ([#7177](https://github.com/community-scripts/ProxmoxVE/pull/7177)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - FileBrowser Quantum: safer update (tmp download + atomic replace + arch autodetect) [@CommanderPaladin](https://github.com/CommanderPaladin) ([#7174](https://github.com/community-scripts/ProxmoxVE/pull/7174)) - Immich: bump to v1.139.4 [@vhsdream](https://github.com/vhsdream) ([#7138](https://github.com/community-scripts/ProxmoxVE/pull/7138)) - Komodo: Fix compose.env path [@tremor021](https://github.com/tremor021) ([#7202](https://github.com/community-scripts/ProxmoxVE/pull/7202)) - Komodo: Fix update procedure and missing env var [@tremor021](https://github.com/tremor021) ([#7198](https://github.com/community-scripts/ProxmoxVE/pull/7198)) - SnipeIT: Update nginx config to v8.3 [@tremor021](https://github.com/tremor021) ([#7171](https://github.com/community-scripts/ProxmoxVE/pull/7171)) - Lidarr: Fix RELEASE variable fetching [@tremor021](https://github.com/tremor021) ([#7162](https://github.com/community-scripts/ProxmoxVE/pull/7162)) - #### ✨ New Features - Komodo: Generate admin users password [@tremor021](https://github.com/tremor021) ([#7193](https://github.com/community-scripts/ProxmoxVE/pull/7193)) - [core]: uv uses now "update-shell" command [@MickLesk](https://github.com/MickLesk) ([#7172](https://github.com/community-scripts/ProxmoxVE/pull/7172)) - [core]: tools.func - better verbose for postgresql [@MickLesk](https://github.com/MickLesk) ([#7173](https://github.com/community-scripts/ProxmoxVE/pull/7173)) - n8n: Force update to NodeJS v22 [@tremor021](https://github.com/tremor021) ([#7176](https://github.com/community-scripts/ProxmoxVE/pull/7176)) ### 🌐 Website - #### 📝 Script Information - 2FAuth: Fix website and docs URLs [@tremor021](https://github.com/tremor021) ([#7199](https://github.com/community-scripts/ProxmoxVE/pull/7199)) ## 2025-08-24 ### 🚀 Updated Scripts - Kasm: Fix install log parsing [@tremor021](https://github.com/tremor021) ([#7140](https://github.com/community-scripts/ProxmoxVE/pull/7140)) - #### 🐞 Bug Fixes - BookLore: Fix Nginx config [@tremor021](https://github.com/tremor021) ([#7155](https://github.com/community-scripts/ProxmoxVE/pull/7155)) - #### ✨ New Features - Syncthing: Switch to v2 stable repository [@tremor021](https://github.com/tremor021) ([#7150](https://github.com/community-scripts/ProxmoxVE/pull/7150)) ## 2025-08-23 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - qBittorrent: Fix file names [@tremor021](https://github.com/tremor021) ([#7136](https://github.com/community-scripts/ProxmoxVE/pull/7136)) - Tandoor: Fix env path [@tremor021](https://github.com/tremor021) ([#7130](https://github.com/community-scripts/ProxmoxVE/pull/7130)) - #### 💥 Breaking Changes - Immich: v1.139.2 [@vhsdream](https://github.com/vhsdream) ([#7116](https://github.com/community-scripts/ProxmoxVE/pull/7116)) - #### 🔧 Refactor - Refactor: Pf2eTools [@tremor021](https://github.com/tremor021) ([#7096](https://github.com/community-scripts/ProxmoxVE/pull/7096)) - Refactor: Prowlarr [@tremor021](https://github.com/tremor021) ([#7091](https://github.com/community-scripts/ProxmoxVE/pull/7091)) - Refactor: Radarr [@tremor021](https://github.com/tremor021) ([#7088](https://github.com/community-scripts/ProxmoxVE/pull/7088)) - Refactor: Snipe-IT [@tremor021](https://github.com/tremor021) ([#7081](https://github.com/community-scripts/ProxmoxVE/pull/7081)) ## 2025-08-22 ### 🚀 Updated Scripts - Refactor: Prometheus [@tremor021](https://github.com/tremor021) ([#7093](https://github.com/community-scripts/ProxmoxVE/pull/7093)) - #### 🐞 Bug Fixes - paperless: nltk fix [@MickLesk](https://github.com/MickLesk) ([#7098](https://github.com/community-scripts/ProxmoxVE/pull/7098)) - Tududi Fix: use correct tag parsing for release during update check [@vhsdream](https://github.com/vhsdream) ([#7072](https://github.com/community-scripts/ProxmoxVE/pull/7072)) - #### 🔧 Refactor - Refactor: phpIPAM [@tremor021](https://github.com/tremor021) ([#7095](https://github.com/community-scripts/ProxmoxVE/pull/7095)) - Refactor: Prometheus Paperless NGX Exporter [@tremor021](https://github.com/tremor021) ([#7092](https://github.com/community-scripts/ProxmoxVE/pull/7092)) - Refactor: PS5-MQTT [@tremor021](https://github.com/tremor021) ([#7090](https://github.com/community-scripts/ProxmoxVE/pull/7090)) - Refactor: qBittorrent [@tremor021](https://github.com/tremor021) ([#7089](https://github.com/community-scripts/ProxmoxVE/pull/7089)) - Refactor: RDTClient [@tremor021](https://github.com/tremor021) ([#7086](https://github.com/community-scripts/ProxmoxVE/pull/7086)) - Refactor: Recyclarr [@tremor021](https://github.com/tremor021) ([#7085](https://github.com/community-scripts/ProxmoxVE/pull/7085)) - Refactor: RevealJS [@tremor021](https://github.com/tremor021) ([#7084](https://github.com/community-scripts/ProxmoxVE/pull/7084)) - Refactor: Rclone [@tremor021](https://github.com/tremor021) ([#7087](https://github.com/community-scripts/ProxmoxVE/pull/7087)) - Refactor: Semaphore [@tremor021](https://github.com/tremor021) ([#7083](https://github.com/community-scripts/ProxmoxVE/pull/7083)) - Refactor: Silverbullet [@tremor021](https://github.com/tremor021) ([#7082](https://github.com/community-scripts/ProxmoxVE/pull/7082)) - Refactor: Plant-it [@tremor021](https://github.com/tremor021) ([#7094](https://github.com/community-scripts/ProxmoxVE/pull/7094)) - Refactor: TasmoAdmin [@tremor021](https://github.com/tremor021) ([#7080](https://github.com/community-scripts/ProxmoxVE/pull/7080)) ## 2025-08-21 ### 🆕 New Scripts - LiteLLM ([#7052](https://github.com/community-scripts/ProxmoxVE/pull/7052)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - tianji: add uv deps [@MickLesk](https://github.com/MickLesk) ([#7066](https://github.com/community-scripts/ProxmoxVE/pull/7066)) - Zitadel: installer for v4 [@MickLesk](https://github.com/MickLesk) ([#7058](https://github.com/community-scripts/ProxmoxVE/pull/7058)) - Paperless-NGX: create direction for nltk [@MickLesk](https://github.com/MickLesk) ([#7064](https://github.com/community-scripts/ProxmoxVE/pull/7064)) - Immich: hotfix - revert 7035 [@vhsdream](https://github.com/vhsdream) ([#7054](https://github.com/community-scripts/ProxmoxVE/pull/7054)) - duplicati: fix release pattern [@MickLesk](https://github.com/MickLesk) ([#7049](https://github.com/community-scripts/ProxmoxVE/pull/7049)) - technitiumdns: fix unbound variable [@MickLesk](https://github.com/MickLesk) ([#7047](https://github.com/community-scripts/ProxmoxVE/pull/7047)) - [core]: improve binary globbing for gh releases [@MickLesk](https://github.com/MickLesk) ([#7044](https://github.com/community-scripts/ProxmoxVE/pull/7044)) ## 2025-08-20 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Post-Install, change pve-test for trixie [@MickLesk](https://github.com/MickLesk) ([#7031](https://github.com/community-scripts/ProxmoxVE/pull/7031)) - Immich: fix small issue with immich-admin "start" script [@vhsdream](https://github.com/vhsdream) ([#7035](https://github.com/community-scripts/ProxmoxVE/pull/7035)) - WasteBin: Small fixes [@tremor021](https://github.com/tremor021) ([#7018](https://github.com/community-scripts/ProxmoxVE/pull/7018)) - Komga: Fix update [@tremor021](https://github.com/tremor021) ([#7027](https://github.com/community-scripts/ProxmoxVE/pull/7027)) - Barcode Buddy: Fix missing dependency [@tremor021](https://github.com/tremor021) ([#7020](https://github.com/community-scripts/ProxmoxVE/pull/7020)) - PBS: ifupdown2 reload [@MickLesk](https://github.com/MickLesk) ([#7013](https://github.com/community-scripts/ProxmoxVE/pull/7013)) - #### ✨ New Features - Feature: Netdata support PVE9 (Debian 13 Trixie) [@MickLesk](https://github.com/MickLesk) ([#7012](https://github.com/community-scripts/ProxmoxVE/pull/7012)) - #### 🔧 Refactor - Refactor: RustDesk Server [@tremor021](https://github.com/tremor021) ([#7008](https://github.com/community-scripts/ProxmoxVE/pull/7008)) - ghost: fix: verbose [@CrazyWolf13](https://github.com/CrazyWolf13) ([#7023](https://github.com/community-scripts/ProxmoxVE/pull/7023)) - Refactor: Paperless-ngx [@MickLesk](https://github.com/MickLesk) ([#6938](https://github.com/community-scripts/ProxmoxVE/pull/6938)) ## 2025-08-19 ### 🆕 New Scripts - Debian 13 VM [@MickLesk](https://github.com/MickLesk) ([#6970](https://github.com/community-scripts/ProxmoxVE/pull/6970)) - Swizzin ([#6962](https://github.com/community-scripts/ProxmoxVE/pull/6962)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - [core]: create_lxc - fix offline issue with alpine packages [@MickLesk](https://github.com/MickLesk) ([#6994](https://github.com/community-scripts/ProxmoxVE/pull/6994)) - OpenObserve: Fix release fetching [@tremor021](https://github.com/tremor021) ([#6961](https://github.com/community-scripts/ProxmoxVE/pull/6961)) - Update hev-socks5-server-install.sh [@iAzamat2](https://github.com/iAzamat2) ([#6953](https://github.com/community-scripts/ProxmoxVE/pull/6953)) - #### ✨ New Features - Refactor: Glances (+ Feature Bump) [@MickLesk](https://github.com/MickLesk) ([#6976](https://github.com/community-scripts/ProxmoxVE/pull/6976)) - [core]: add new features to create_lxc [@MickLesk](https://github.com/MickLesk) ([#6979](https://github.com/community-scripts/ProxmoxVE/pull/6979)) - [core]: extend setup_uv to work with alpine [@MickLesk](https://github.com/MickLesk) ([#6978](https://github.com/community-scripts/ProxmoxVE/pull/6978)) - Immich: Bump version to 1.138.1 [@vhsdream](https://github.com/vhsdream) ([#6984](https://github.com/community-scripts/ProxmoxVE/pull/6984)) - #### 🔧 Refactor - Refactor: Tdarr [@MickLesk](https://github.com/MickLesk) ([#6969](https://github.com/community-scripts/ProxmoxVE/pull/6969)) - Refactor: The Lounge [@tremor021](https://github.com/tremor021) ([#6958](https://github.com/community-scripts/ProxmoxVE/pull/6958)) - Refactor: TeddyCloud [@tremor021](https://github.com/tremor021) ([#6963](https://github.com/community-scripts/ProxmoxVE/pull/6963)) - Refactor: Technitium DNS [@tremor021](https://github.com/tremor021) ([#6968](https://github.com/community-scripts/ProxmoxVE/pull/6968)) ### 🌐 Website - #### 📝 Script Information - [web]: update logos from reactive-resume & slskd [@MickLesk](https://github.com/MickLesk) ([#6990](https://github.com/community-scripts/ProxmoxVE/pull/6990)) ## 2025-08-18 ### 🆕 New Scripts - CopyParty [@MickLesk](https://github.com/MickLesk) ([#6929](https://github.com/community-scripts/ProxmoxVE/pull/6929)) - Twingate-Connector ([#6921](https://github.com/community-scripts/ProxmoxVE/pull/6921)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Keycloak: fix update function [@MickLesk](https://github.com/MickLesk) ([#6943](https://github.com/community-scripts/ProxmoxVE/pull/6943)) - Immich: add message to indicate image-processing library update check [@vhsdream](https://github.com/vhsdream) ([#6935](https://github.com/community-scripts/ProxmoxVE/pull/6935)) - fix(uptimekuma): unbound env variable [@vidonnus](https://github.com/vidonnus) ([#6922](https://github.com/community-scripts/ProxmoxVE/pull/6922)) - #### 🔧 Refactor - Refactor: Traefik [@tremor021](https://github.com/tremor021) ([#6940](https://github.com/community-scripts/ProxmoxVE/pull/6940)) - Refactor: Traccar [@tremor021](https://github.com/tremor021) ([#6942](https://github.com/community-scripts/ProxmoxVE/pull/6942)) - Refactor: Umami [@tremor021](https://github.com/tremor021) ([#6939](https://github.com/community-scripts/ProxmoxVE/pull/6939)) - Refactor: GoMFT [@tremor021](https://github.com/tremor021) ([#6916](https://github.com/community-scripts/ProxmoxVE/pull/6916)) ### 🌐 Website - #### 📝 Script Information - OpenWRT: add info for VLAN-aware in frontend [@MickLesk](https://github.com/MickLesk) ([#6944](https://github.com/community-scripts/ProxmoxVE/pull/6944)) ## 2025-08-17 ## 2025-08-16 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Wireguard: Fix WGDashboard not updating [@tremor021](https://github.com/tremor021) ([#6898](https://github.com/community-scripts/ProxmoxVE/pull/6898)) - Tandoor Images Fix [@WarLord185](https://github.com/WarLord185) ([#6892](https://github.com/community-scripts/ProxmoxVE/pull/6892)) - #### 🔧 Refactor - Refactor: Uptime Kuma [@tremor021](https://github.com/tremor021) ([#6902](https://github.com/community-scripts/ProxmoxVE/pull/6902)) - Refactor: Wallos [@tremor021](https://github.com/tremor021) ([#6900](https://github.com/community-scripts/ProxmoxVE/pull/6900)) ## 2025-08-15 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Immich: pin Vectorchord release; adjust extension update commands [@vhsdream](https://github.com/vhsdream) ([#6878](https://github.com/community-scripts/ProxmoxVE/pull/6878)) - #### ✨ New Features - Bump Immich to v1.138.0 [@vhsdream](https://github.com/vhsdream) ([#6813](https://github.com/community-scripts/ProxmoxVE/pull/6813)) - #### 🔧 Refactor - Refactor: Wavelog [@tremor021](https://github.com/tremor021) ([#6869](https://github.com/community-scripts/ProxmoxVE/pull/6869)) - Refactor: WatchYourLAN [@tremor021](https://github.com/tremor021) ([#6871](https://github.com/community-scripts/ProxmoxVE/pull/6871)) - Refactor: Watcharr [@tremor021](https://github.com/tremor021) ([#6872](https://github.com/community-scripts/ProxmoxVE/pull/6872)) ### 🌐 Website - #### 📝 Script Information - Add missing default user & pass for RabbitMQ [@hbenyoussef](https://github.com/hbenyoussef) ([#6883](https://github.com/community-scripts/ProxmoxVE/pull/6883)) ## 2025-08-14 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Bugfix Searxng Redis replaced with Valkey in installscript [@elvito](https://github.com/elvito) ([#6831](https://github.com/community-scripts/ProxmoxVE/pull/6831)) - Spoolman: Use environment variables to control host and port [@tremor021](https://github.com/tremor021) ([#6825](https://github.com/community-scripts/ProxmoxVE/pull/6825)) - Pulse: v4.3.2+ [@vhsdream](https://github.com/vhsdream) ([#6859](https://github.com/community-scripts/ProxmoxVE/pull/6859)) - rustdeskserver: fix API version file [@steadfasterX](https://github.com/steadfasterX) ([#6847](https://github.com/community-scripts/ProxmoxVE/pull/6847)) - Immich: quickfix #6836 [@vhsdream](https://github.com/vhsdream) ([#6848](https://github.com/community-scripts/ProxmoxVE/pull/6848)) - #### 🔧 Refactor - Refactor: WikiJS [@tremor021](https://github.com/tremor021) ([#6840](https://github.com/community-scripts/ProxmoxVE/pull/6840)) - Refactor: Zoraxy [@tremor021](https://github.com/tremor021) ([#6823](https://github.com/community-scripts/ProxmoxVE/pull/6823)) - Refactor: Zitadel [@tremor021](https://github.com/tremor021) ([#6826](https://github.com/community-scripts/ProxmoxVE/pull/6826)) - Refactor: WordPress [@tremor021](https://github.com/tremor021) ([#6837](https://github.com/community-scripts/ProxmoxVE/pull/6837)) - Refactor: WireGuard [@tremor021](https://github.com/tremor021) ([#6839](https://github.com/community-scripts/ProxmoxVE/pull/6839)) - Refactor: yt-dlp-webui [@tremor021](https://github.com/tremor021) ([#6832](https://github.com/community-scripts/ProxmoxVE/pull/6832)) - Refactor: Zipline [@tremor021](https://github.com/tremor021) ([#6829](https://github.com/community-scripts/ProxmoxVE/pull/6829)) - Refactor: Zot-Registry [@tremor021](https://github.com/tremor021) ([#6822](https://github.com/community-scripts/ProxmoxVE/pull/6822)) - Refactor: Zwave-JS-UI [@tremor021](https://github.com/tremor021) ([#6820](https://github.com/community-scripts/ProxmoxVE/pull/6820)) ### 🧰 Maintenance - #### 📂 Github - ProxmoxVE svg logo [@LuisPalacios](https://github.com/LuisPalacios) ([#6846](https://github.com/community-scripts/ProxmoxVE/pull/6846)) ## 2025-08-13 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - emby: fix update output [@MickLesk](https://github.com/MickLesk) ([#6791](https://github.com/community-scripts/ProxmoxVE/pull/6791)) - archivebox: fix wrong formatted uv command [@MickLesk](https://github.com/MickLesk) ([#6794](https://github.com/community-scripts/ProxmoxVE/pull/6794)) - Outline: Fixes for install and update procedures [@tremor021](https://github.com/tremor021) ([#6806](https://github.com/community-scripts/ProxmoxVE/pull/6806)) - Palmr: fix release version parsing // increase RAM [@vhsdream](https://github.com/vhsdream) ([#6800](https://github.com/community-scripts/ProxmoxVE/pull/6800)) - myspeed: fix update process if no data exist [@MickLesk](https://github.com/MickLesk) ([#6795](https://github.com/community-scripts/ProxmoxVE/pull/6795)) - crafty-controller: fix update output [@MickLesk](https://github.com/MickLesk) ([#6793](https://github.com/community-scripts/ProxmoxVE/pull/6793)) - GLPI: Fix timezone command [@tremor021](https://github.com/tremor021) ([#6783](https://github.com/community-scripts/ProxmoxVE/pull/6783)) - #### ✨ New Features - Docker LXC: Add Portainer info [@tremor021](https://github.com/tremor021) ([#6803](https://github.com/community-scripts/ProxmoxVE/pull/6803)) - AgentDVR: Added update function [@tremor021](https://github.com/tremor021) ([#6804](https://github.com/community-scripts/ProxmoxVE/pull/6804)) ## 2025-08-12 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Pulse: binary path changed AGAIN; other fixes [@vhsdream](https://github.com/vhsdream) ([#6770](https://github.com/community-scripts/ProxmoxVE/pull/6770)) - fix alpine syncthing config not being created [@GamerHun1238](https://github.com/GamerHun1238) ([#6773](https://github.com/community-scripts/ProxmoxVE/pull/6773)) - change owner of hortusfox directory [@snow2k9](https://github.com/snow2k9) ([#6763](https://github.com/community-scripts/ProxmoxVE/pull/6763)) ## 2025-08-11 ### 🚀 Updated Scripts - Reactive Resume: use new release parsing; other fixes [@vhsdream](https://github.com/vhsdream) ([#6744](https://github.com/community-scripts/ProxmoxVE/pull/6744)) ## 2025-08-10 ### 🚀 Updated Scripts - Fix/thinpool detection as it allows to delete active thinpool with different name than "data" [@onethree7](https://github.com/onethree7) ([#6730](https://github.com/community-scripts/ProxmoxVE/pull/6730)) - #### 🐞 Bug Fixes - Pulse: fix binary path [@vhsdream](https://github.com/vhsdream) ([#6740](https://github.com/community-scripts/ProxmoxVE/pull/6740)) - Karakeep: chromium fix [@vhsdream](https://github.com/vhsdream) ([#6729](https://github.com/community-scripts/ProxmoxVE/pull/6729)) ## 2025-08-09 ### 🚀 Updated Scripts - Paperless-AI: increase HDD Space to 20G [@MickLesk](https://github.com/MickLesk) ([#6716](https://github.com/community-scripts/ProxmoxVE/pull/6716)) - #### 🐞 Bug Fixes - vaultwarden: increase disk space [@CrazyWolf13](https://github.com/CrazyWolf13) ([#6712](https://github.com/community-scripts/ProxmoxVE/pull/6712)) - Fix: Bazarr requirements.txt file not parse-able by UV [@Xerovoxx98](https://github.com/Xerovoxx98) ([#6701](https://github.com/community-scripts/ProxmoxVE/pull/6701)) - Improve backup of adventurelog folder [@ThomasDetemmerman](https://github.com/ThomasDetemmerman) ([#6653](https://github.com/community-scripts/ProxmoxVE/pull/6653)) - HomeBox: Fixes for update procedure [@tremor021](https://github.com/tremor021) ([#6702](https://github.com/community-scripts/ProxmoxVE/pull/6702)) - #### 🔧 Refactor - Refactor: Tianji [@MickLesk](https://github.com/MickLesk) ([#6662](https://github.com/community-scripts/ProxmoxVE/pull/6662)) ## 2025-08-08 ### 🆕 New Scripts - Palmr ([#6642](https://github.com/community-scripts/ProxmoxVE/pull/6642)) - HortusFox ([#6641](https://github.com/community-scripts/ProxmoxVE/pull/6641)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Unifi: Update libssl dependency [@tremor021](https://github.com/tremor021) ([#6680](https://github.com/community-scripts/ProxmoxVE/pull/6680)) - HomeBox: Fix checking for existing install [@tremor021](https://github.com/tremor021) ([#6677](https://github.com/community-scripts/ProxmoxVE/pull/6677)) - Immich: unpin libvips revision [@vhsdream](https://github.com/vhsdream) ([#6669](https://github.com/community-scripts/ProxmoxVE/pull/6669)) - Meilisearch: fix wrong path switch [@MickLesk](https://github.com/MickLesk) ([#6668](https://github.com/community-scripts/ProxmoxVE/pull/6668)) - MariaDB: fix "feedback" (statistical informations) whiptail [@MickLesk](https://github.com/MickLesk) ([#6657](https://github.com/community-scripts/ProxmoxVE/pull/6657)) - Karakeep: workaround/fix for #6593 [@vhsdream](https://github.com/vhsdream) ([#6648](https://github.com/community-scripts/ProxmoxVE/pull/6648)) - #### ✨ New Features - Feature: FSTrim (Filesystem Trim) - Log / LVM Check / ZFS [@MickLesk](https://github.com/MickLesk) ([#6660](https://github.com/community-scripts/ProxmoxVE/pull/6660)) - IP Tag: Allow installation on PVE 9.x [@webhdx](https://github.com/webhdx) ([#6679](https://github.com/community-scripts/ProxmoxVE/pull/6679)) - #### 🔧 Refactor - Refactor: Alpine IT-Tools [@tremor021](https://github.com/tremor021) ([#6579](https://github.com/community-scripts/ProxmoxVE/pull/6579)) - Refactor: ArchiveBox [@MickLesk](https://github.com/MickLesk) ([#6670](https://github.com/community-scripts/ProxmoxVE/pull/6670)) - Refactor: Bazarr [@MickLesk](https://github.com/MickLesk) ([#6663](https://github.com/community-scripts/ProxmoxVE/pull/6663)) - Refactor: Kometa [@MickLesk](https://github.com/MickLesk) ([#6673](https://github.com/community-scripts/ProxmoxVE/pull/6673)) ## 2025-08-07 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Commafeed: Fix Backup Handling while Update [@MickLesk](https://github.com/MickLesk) ([#6629](https://github.com/community-scripts/ProxmoxVE/pull/6629)) - VictoriaMetrics: Fix release fetching [@tremor021](https://github.com/tremor021) ([#6632](https://github.com/community-scripts/ProxmoxVE/pull/6632)) - #### ✨ New Features - Feature: Post-PVE-Script (PVE9 Support + some Features) [@MickLesk](https://github.com/MickLesk) ([#6626](https://github.com/community-scripts/ProxmoxVE/pull/6626)) - Feature: Clean-LXC now supports Alpine based containers [@MickLesk](https://github.com/MickLesk) ([#6628](https://github.com/community-scripts/ProxmoxVE/pull/6628)) - #### 🔧 Refactor - Refactor: Tandoor v2 [@MickLesk](https://github.com/MickLesk) ([#6635](https://github.com/community-scripts/ProxmoxVE/pull/6635)) - Refactor: Paymenter [@tremor021](https://github.com/tremor021) ([#6589](https://github.com/community-scripts/ProxmoxVE/pull/6589)) ## 2025-08-06 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - [core] better y/N handling for ressource check [@MickLesk](https://github.com/MickLesk) ([#6608](https://github.com/community-scripts/ProxmoxVE/pull/6608)) - fix: update Pulse scripts for v4 Go rewrite support [@rcourtman](https://github.com/rcourtman) ([#6574](https://github.com/community-scripts/ProxmoxVE/pull/6574)) - OpenProject: Fix missing apt update [@tremor021](https://github.com/tremor021) ([#6598](https://github.com/community-scripts/ProxmoxVE/pull/6598)) - #### ✨ New Features - PVE9: Remove Beta Whiptail / add correct version check [@MickLesk](https://github.com/MickLesk) ([#6599](https://github.com/community-scripts/ProxmoxVE/pull/6599)) ## 2025-08-05 ### 🚀 Updated Scripts - #### ✨ New Features - NIC offloading: e1000 support [@rcastley](https://github.com/rcastley) ([#6575](https://github.com/community-scripts/ProxmoxVE/pull/6575)) - #### 💥 Breaking Changes - Temporary Remove: SearXNG [@MickLesk](https://github.com/MickLesk) ([#6578](https://github.com/community-scripts/ProxmoxVE/pull/6578)) - #### 🔧 Refactor - Refactor: Prometheus Alertmanager [@tremor021](https://github.com/tremor021) ([#6577](https://github.com/community-scripts/ProxmoxVE/pull/6577)) ## 2025-08-04 ### 🆕 New Scripts - Tududi ([#6534](https://github.com/community-scripts/ProxmoxVE/pull/6534)) - ots ([#6532](https://github.com/community-scripts/ProxmoxVE/pull/6532)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - MySpeed: fix update and copy old tests back [@MickLesk](https://github.com/MickLesk) ([#6550](https://github.com/community-scripts/ProxmoxVE/pull/6550)) - Composer: PATH Issues when updating [@MickLesk](https://github.com/MickLesk) ([#6543](https://github.com/community-scripts/ProxmoxVE/pull/6543)) - #### ✨ New Features - Feat: enable tun for VPN services (wireguard) [@MickLesk](https://github.com/MickLesk) ([#6562](https://github.com/community-scripts/ProxmoxVE/pull/6562)) - turnkey: add hostname & Fix TUN access [@masterofrpm](https://github.com/masterofrpm) ([#6512](https://github.com/community-scripts/ProxmoxVE/pull/6512)) - Increase: Core Network check (pre-LXC Creation) [@MickLesk](https://github.com/MickLesk) ([#6546](https://github.com/community-scripts/ProxmoxVE/pull/6546)) - #### 🔧 Refactor - Refactor: PrivateBin [@tremor021](https://github.com/tremor021) ([#6559](https://github.com/community-scripts/ProxmoxVE/pull/6559)) - Refactor: PocketID [@tremor021](https://github.com/tremor021) ([#6556](https://github.com/community-scripts/ProxmoxVE/pull/6556)) - Refactor: Pocketbase [@tremor021](https://github.com/tremor021) ([#6554](https://github.com/community-scripts/ProxmoxVE/pull/6554)) - Refactor: NocoDB [@tremor021](https://github.com/tremor021) ([#6548](https://github.com/community-scripts/ProxmoxVE/pull/6548)) - Refactor: PairDrop [@tremor021](https://github.com/tremor021) ([#6528](https://github.com/community-scripts/ProxmoxVE/pull/6528)) ## 2025-08-03 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - docmost: remove build step due new version [@MickLesk](https://github.com/MickLesk) ([#6513](https://github.com/community-scripts/ProxmoxVE/pull/6513)) - Fix: Komga uses .komga as storage / so it fails after install [@MickLesk](https://github.com/MickLesk) ([#6517](https://github.com/community-scripts/ProxmoxVE/pull/6517)) - #### 💥 Breaking Changes - Remove: Ubuntu 24.10-VM [@MickLesk](https://github.com/MickLesk) ([#6515](https://github.com/community-scripts/ProxmoxVE/pull/6515)) - #### 🔧 Refactor - Refactor: openHAB [@tremor021](https://github.com/tremor021) ([#6524](https://github.com/community-scripts/ProxmoxVE/pull/6524)) - Refactor: OpenProject [@tremor021](https://github.com/tremor021) ([#6525](https://github.com/community-scripts/ProxmoxVE/pull/6525)) ## 2025-08-02 ### 🚀 Updated Scripts - Alternative connectivity checks for LXC [@mariano-dagostino](https://github.com/mariano-dagostino) ([#6472](https://github.com/community-scripts/ProxmoxVE/pull/6472)) - #### 🐞 Bug Fixes - Immich: fix copy error during install [@vhsdream](https://github.com/vhsdream) ([#6497](https://github.com/community-scripts/ProxmoxVE/pull/6497)) - MagicMirror: Fix install process [@tremor021](https://github.com/tremor021) ([#6492](https://github.com/community-scripts/ProxmoxVE/pull/6492)) - chore: BookLore repo change [@vhsdream](https://github.com/vhsdream) ([#6493](https://github.com/community-scripts/ProxmoxVE/pull/6493)) - #### ✨ New Features - VictoriaMetrics: Make VictoriaLogs optional add-on [@tremor021](https://github.com/tremor021) ([#6489](https://github.com/community-scripts/ProxmoxVE/pull/6489)) ## 2025-08-01 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Fumadocs: add git as dependency [@MickLesk](https://github.com/MickLesk) ([#6459](https://github.com/community-scripts/ProxmoxVE/pull/6459)) - Immich: Fix immich-admin script; other fixes | pin to v.137.3 [@vhsdream](https://github.com/vhsdream) ([#6443](https://github.com/community-scripts/ProxmoxVE/pull/6443)) - #### ✨ New Features - Re-Add: Suwayomi-Server [@MickLesk](https://github.com/MickLesk) ([#6458](https://github.com/community-scripts/ProxmoxVE/pull/6458)) - #### 🔧 Refactor - Update homepage.sh to use setup_nodejs [@burgerga](https://github.com/burgerga) ([#6462](https://github.com/community-scripts/ProxmoxVE/pull/6462)) - Refactor: Owncast [@tremor021](https://github.com/tremor021) ([#6434](https://github.com/community-scripts/ProxmoxVE/pull/6434)) - Refactor: MediaMTX [@tremor021](https://github.com/tremor021) ([#6406](https://github.com/community-scripts/ProxmoxVE/pull/6406)) - Refactor: LubeLogger [@tremor021](https://github.com/tremor021) ([#6400](https://github.com/community-scripts/ProxmoxVE/pull/6400)) - Refactor: MagicMirror [@tremor021](https://github.com/tremor021) ([#6402](https://github.com/community-scripts/ProxmoxVE/pull/6402)) - Refactor: Manage My Damn Life [@tremor021](https://github.com/tremor021) ([#6403](https://github.com/community-scripts/ProxmoxVE/pull/6403)) - Refactor: Meilisearch [@tremor021](https://github.com/tremor021) ([#6407](https://github.com/community-scripts/ProxmoxVE/pull/6407)) - Refactor: NodeBB [@tremor021](https://github.com/tremor021) ([#6419](https://github.com/community-scripts/ProxmoxVE/pull/6419)) - Refactor: oauth2-proxy [@tremor021](https://github.com/tremor021) ([#6421](https://github.com/community-scripts/ProxmoxVE/pull/6421)) - Refactor: Outline [@tremor021](https://github.com/tremor021) ([#6424](https://github.com/community-scripts/ProxmoxVE/pull/6424)) - Refactor: Overseerr [@tremor021](https://github.com/tremor021) ([#6425](https://github.com/community-scripts/ProxmoxVE/pull/6425)) ================================================ FILE: .github/changelogs/2025/09.md ================================================ ## 2025-09-30 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - caddy: fix typo for setup_go [@MickLesk](https://github.com/MickLesk) ([#8017](https://github.com/community-scripts/ProxmoxVE/pull/8017)) - Changedetection: Fix Browserless installation and update process [@h-stoyanov](https://github.com/h-stoyanov) ([#8011](https://github.com/community-scripts/ProxmoxVE/pull/8011)) - n8n: Update procedure workaround [@tremor021](https://github.com/tremor021) ([#8004](https://github.com/community-scripts/ProxmoxVE/pull/8004)) - Changedetection: Bump nodejs to 24 [@MickLesk](https://github.com/MickLesk) ([#8002](https://github.com/community-scripts/ProxmoxVE/pull/8002)) - #### ✨ New Features - Bump Guacamole to Debian 13 [@burgerga](https://github.com/burgerga) ([#8010](https://github.com/community-scripts/ProxmoxVE/pull/8010)) ## 2025-09-29 ### 🆕 New Scripts - Ghostfolio ([#7982](https://github.com/community-scripts/ProxmoxVE/pull/7982)) - Warracker ([#7977](https://github.com/community-scripts/ProxmoxVE/pull/7977)) - MyIP ([#7974](https://github.com/community-scripts/ProxmoxVE/pull/7974)) - Verdaccio ([#7967](https://github.com/community-scripts/ProxmoxVE/pull/7967)) ### 🌐 Website - #### 🐞 Bug Fixes - fix sidebar loading issues and navbar on mobile [@BramSuurdje](https://github.com/BramSuurdje) ([#7991](https://github.com/community-scripts/ProxmoxVE/pull/7991)) - #### ✨ New Features - Improve mobile ui: added a hamburger navigation to the mobile view. [@BramSuurdje](https://github.com/BramSuurdje) ([#7987](https://github.com/community-scripts/ProxmoxVE/pull/7987)) - #### 📝 Script Information - Remove Frigate from Website [@MickLesk](https://github.com/MickLesk) ([#7972](https://github.com/community-scripts/ProxmoxVE/pull/7972)) ## 2025-09-28 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Metube: remove uv flags [@vhsdream](https://github.com/vhsdream) ([#7962](https://github.com/community-scripts/ProxmoxVE/pull/7962)) - freshrss: fix for broken permissions after update [@CrazyWolf13](https://github.com/CrazyWolf13) ([#7953](https://github.com/community-scripts/ProxmoxVE/pull/7953)) ## 2025-09-27 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - GoAway: Make admin password aquisition more reliable [@tremor021](https://github.com/tremor021) ([#7946](https://github.com/community-scripts/ProxmoxVE/pull/7946)) - MeTube: Various fixes [@vhsdream](https://github.com/vhsdream) ([#7936](https://github.com/community-scripts/ProxmoxVE/pull/7936)) - Oddo: Fix typo in update procedure [@tremor021](https://github.com/tremor021) ([#7941](https://github.com/community-scripts/ProxmoxVE/pull/7941)) ## 2025-09-26 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Odoo: Fix missing dependencies [@tremor021](https://github.com/tremor021) ([#7931](https://github.com/community-scripts/ProxmoxVE/pull/7931)) - OpenWebUI: Update NODE_OPTIONS to increase memory limit [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#7919](https://github.com/community-scripts/ProxmoxVE/pull/7919)) ### 🌐 Website - #### 📝 Script Information - Clarify descriptions of update scripts [@tremor021](https://github.com/tremor021) ([#7929](https://github.com/community-scripts/ProxmoxVE/pull/7929)) ## 2025-09-25 ### 🆕 New Scripts - GoAway ([#7900](https://github.com/community-scripts/ProxmoxVE/pull/7900)) ### 🚀 Updated Scripts - #### ✨ New Features - ntfy: bump to debian 13 [@CrazyWolf13](https://github.com/CrazyWolf13) ([#7895](https://github.com/community-scripts/ProxmoxVE/pull/7895)) ### 🌐 Website - #### ✨ New Features - feat: add menu icons to website [@BramSuurdje](https://github.com/BramSuurdje) ([#7894](https://github.com/community-scripts/ProxmoxVE/pull/7894)) ## 2025-09-24 ### 🆕 New Scripts - Add Script: Joplin Server ([#7879](https://github.com/community-scripts/ProxmoxVE/pull/7879)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Monica: Fix dependencies [@tremor021](https://github.com/tremor021) ([#7877](https://github.com/community-scripts/ProxmoxVE/pull/7877)) ### 🌐 Website - #### 📝 Script Information - Update name in lxc-delete.json to 'PVE LXC Deletion' [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#7872](https://github.com/community-scripts/ProxmoxVE/pull/7872)) ## 2025-09-23 ### 🆕 New Scripts - UpSnap ([#7825](https://github.com/community-scripts/ProxmoxVE/pull/7825)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - tools.func: Check for /usr/local/bin in PATH during yq setup [@vhsdream](https://github.com/vhsdream) ([#7856](https://github.com/community-scripts/ProxmoxVE/pull/7856)) - BookLore: increase RAM [@vhsdream](https://github.com/vhsdream) ([#7855](https://github.com/community-scripts/ProxmoxVE/pull/7855)) - Bump Immich to v1.143.1 [@vhsdream](https://github.com/vhsdream) ([#7864](https://github.com/community-scripts/ProxmoxVE/pull/7864)) - zabbix: Remove not exist admin credentials from output [@MickLesk](https://github.com/MickLesk) ([#7849](https://github.com/community-scripts/ProxmoxVE/pull/7849)) - Suppress wrong errors from uv shell integration in setup_uv [@MickLesk](https://github.com/MickLesk) ([#7822](https://github.com/community-scripts/ProxmoxVE/pull/7822)) - Refactor Caddyfile configuration for headscale-admin [@MickLesk](https://github.com/MickLesk) ([#7821](https://github.com/community-scripts/ProxmoxVE/pull/7821)) - Improve subscription element removal (mobile) in post-pve script [@MickLesk](https://github.com/MickLesk) ([#7814](https://github.com/community-scripts/ProxmoxVE/pull/7814)) - Blocky: Fix release fetching [@tremor021](https://github.com/tremor021) ([#7807](https://github.com/community-scripts/ProxmoxVE/pull/7807)) - #### ✨ New Features - Improve globaleaks install ensuring install can proceed without user … [@evilaliv3](https://github.com/evilaliv3) ([#7860](https://github.com/community-scripts/ProxmoxVE/pull/7860)) - Manage My Damn Life: use NodeJS 22 [@vhsdream](https://github.com/vhsdream) ([#7861](https://github.com/community-scripts/ProxmoxVE/pull/7861)) - VM: Increase pv & xz functions (HA OS / Umbrel OS) [@MickLesk](https://github.com/MickLesk) ([#7838](https://github.com/community-scripts/ProxmoxVE/pull/7838)) - Tandoor: update for newer dependencies (psql) + bump nodejs to 22 [@MickLesk](https://github.com/MickLesk) ([#7826](https://github.com/community-scripts/ProxmoxVE/pull/7826)) - Update Monica and Outline to use Node.js 22 [@MickLesk](https://github.com/MickLesk) ([#7833](https://github.com/community-scripts/ProxmoxVE/pull/7833)) - Update Zabbix install for Debian 13 and agent selection [@MickLesk](https://github.com/MickLesk) ([#7819](https://github.com/community-scripts/ProxmoxVE/pull/7819)) - tracktor: bump to debian 13 | feature bump [@CrazyWolf13](https://github.com/CrazyWolf13) ([#7818](https://github.com/community-scripts/ProxmoxVE/pull/7818)) - LiteLLM: Bump to Debian 13 & add deps [@MickLesk](https://github.com/MickLesk) ([#7815](https://github.com/community-scripts/ProxmoxVE/pull/7815)) - Immich: bump to v1.143.0 [@vhsdream](https://github.com/vhsdream) ([#7801](https://github.com/community-scripts/ProxmoxVE/pull/7801)) ### 🧰 Maintenance - #### 📂 Github - gh: remove ai autolabel test [@MickLesk](https://github.com/MickLesk) ([#7817](https://github.com/community-scripts/ProxmoxVE/pull/7817)) ### 🌐 Website - #### 📝 Script Information - OpenWebUI: Add information about Ollama [@tremor021](https://github.com/tremor021) ([#7843](https://github.com/community-scripts/ProxmoxVE/pull/7843)) - cosmos: add info note for configuration file [@MickLesk](https://github.com/MickLesk) ([#7824](https://github.com/community-scripts/ProxmoxVE/pull/7824)) - ElementSynapse: add note for Bridge Install Methods [@MickLesk](https://github.com/MickLesk) ([#7820](https://github.com/community-scripts/ProxmoxVE/pull/7820)) ## 2025-09-22 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - [core]: Update detection of current running subshell [@tremor021](https://github.com/tremor021) ([#7796](https://github.com/community-scripts/ProxmoxVE/pull/7796)) - Paymenter: Installation and update fixes [@tremor021](https://github.com/tremor021) ([#7792](https://github.com/community-scripts/ProxmoxVE/pull/7792)) ## 2025-09-21 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - fix openwebui update and installer [@HeedfulCrayon](https://github.com/HeedfulCrayon) ([#7788](https://github.com/community-scripts/ProxmoxVE/pull/7788)) - tracktor: add: cleanup before upgrade [@CrazyWolf13](https://github.com/CrazyWolf13) ([#7782](https://github.com/community-scripts/ProxmoxVE/pull/7782)) - Fix regex to extract MySQL version correctly [@MickLesk](https://github.com/MickLesk) ([#7774](https://github.com/community-scripts/ProxmoxVE/pull/7774)) - Update Ollama Installer in OpenWebUI to resume downloads if interrupted [@HeedfulCrayon](https://github.com/HeedfulCrayon) ([#7779](https://github.com/community-scripts/ProxmoxVE/pull/7779)) - #### ✨ New Features - Implement clean install option in tools.func (fetch_and_deploy_gh_release) [@MickLesk](https://github.com/MickLesk) ([#7785](https://github.com/community-scripts/ProxmoxVE/pull/7785)) - caddy: modify disk size and implement xCaddy update [@MickLesk](https://github.com/MickLesk) ([#7775](https://github.com/community-scripts/ProxmoxVE/pull/7775)) ### 🌐 Website - #### 📝 Script Information - Harmonize and shorten JSON Names for PVE/PBS/PMG [@MickLesk](https://github.com/MickLesk) ([#7773](https://github.com/community-scripts/ProxmoxVE/pull/7773)) ## 2025-09-20 ### 🚀 Updated Scripts - checkmk.sh Update: Revert old Pr [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#7765](https://github.com/community-scripts/ProxmoxVE/pull/7765)) - #### 🐞 Bug Fixes - Wazuh: Increase HDD size [@tremor021](https://github.com/tremor021) ([#7759](https://github.com/community-scripts/ProxmoxVE/pull/7759)) ### 🧰 Maintenance - #### 📂 Github - Add Debian 13 in bug report template [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#7757](https://github.com/community-scripts/ProxmoxVE/pull/7757)) ## 2025-09-19 ### 🆕 New Scripts - Tunarr ([#7735](https://github.com/community-scripts/ProxmoxVE/pull/7735)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - SigNoz: Fix wrong URL for Zookeeper [@tremor021](https://github.com/tremor021) ([#7742](https://github.com/community-scripts/ProxmoxVE/pull/7742)) ## 2025-09-18 ### 🆕 New Scripts - Alpine-Caddy [@tremor021](https://github.com/tremor021) ([#7711](https://github.com/community-scripts/ProxmoxVE/pull/7711)) - pve-tool: execute.sh by @jeroenzwart [@MickLesk](https://github.com/MickLesk) ([#7708](https://github.com/community-scripts/ProxmoxVE/pull/7708)) - GlobaLeaks ([#7707](https://github.com/community-scripts/ProxmoxVE/pull/7707)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Delay chmod after updating beszel [@CrazyWolf13](https://github.com/CrazyWolf13) ([#7725](https://github.com/community-scripts/ProxmoxVE/pull/7725)) - Remove redundant globaleaks configuration [@evilaliv3](https://github.com/evilaliv3) ([#7723](https://github.com/community-scripts/ProxmoxVE/pull/7723)) - Gatus: check for GO path before update [@vhsdream](https://github.com/vhsdream) ([#7705](https://github.com/community-scripts/ProxmoxVE/pull/7705)) - #### ✨ New Features - Cloudflared: Bump to Debian 13 [@MickLesk](https://github.com/MickLesk) ([#7719](https://github.com/community-scripts/ProxmoxVE/pull/7719)) - AdGuard Home: Bump to Debian 13 [@MickLesk](https://github.com/MickLesk) ([#7720](https://github.com/community-scripts/ProxmoxVE/pull/7720)) - #### 🔧 Refactor - Immich: Debian Trixie [@vhsdream](https://github.com/vhsdream) ([#7728](https://github.com/community-scripts/ProxmoxVE/pull/7728)) ### 🌐 Website - #### 📝 Script Information - Add Warning for Containerized Home Assistant [@ZaxLofful](https://github.com/ZaxLofful) ([#7704](https://github.com/community-scripts/ProxmoxVE/pull/7704)) ## 2025-09-17 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - beszel: fix: binary permission after upgrade [@CrazyWolf13](https://github.com/CrazyWolf13) ([#7697](https://github.com/community-scripts/ProxmoxVE/pull/7697)) - RabbitMQ: Update repositories [@tremor021](https://github.com/tremor021) ([#7689](https://github.com/community-scripts/ProxmoxVE/pull/7689)) - Komodo: Add docker compose pull for actually updating docker container [@hanneshier](https://github.com/hanneshier) ([#7682](https://github.com/community-scripts/ProxmoxVE/pull/7682)) - #### ✨ New Features - Debian-LXC: Bump to Debian 13 Trixie [@MickLesk](https://github.com/MickLesk) ([#7683](https://github.com/community-scripts/ProxmoxVE/pull/7683)) - Bump Immich to v1.142.1 [@vhsdream](https://github.com/vhsdream) ([#7675](https://github.com/community-scripts/ProxmoxVE/pull/7675)) - #### 🔧 Refactor - Refactor: Grist [@tremor021](https://github.com/tremor021) ([#7681](https://github.com/community-scripts/ProxmoxVE/pull/7681)) ### 🧰 Maintenance - #### 📂 Github - Improve: SECURITY.md for clarity and detail + Adding PVE9 as supported [@MickLesk](https://github.com/MickLesk) ([#7690](https://github.com/community-scripts/ProxmoxVE/pull/7690)) ## 2025-09-16 ### 🚀 Updated Scripts - Improve OpenWrt VM boot and readiness check [@MickLesk](https://github.com/MickLesk) ([#7669](https://github.com/community-scripts/ProxmoxVE/pull/7669)) - #### 🐞 Bug Fixes - hortusfox: fix update check [@MickLesk](https://github.com/MickLesk) ([#7667](https://github.com/community-scripts/ProxmoxVE/pull/7667)) ## 2025-09-15 ### 🆕 New Scripts - SigNoz ([#7648](https://github.com/community-scripts/ProxmoxVE/pull/7648)) - Scraparr ([#7644](https://github.com/community-scripts/ProxmoxVE/pull/7644)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - vm: move pv installation into ensure_pv function [@MickLesk](https://github.com/MickLesk) ([#7642](https://github.com/community-scripts/ProxmoxVE/pull/7642)) - Cloudflare-DDNS: Fix the IP6_PROVIDER variable [@hugodantas](https://github.com/hugodantas) ([#7660](https://github.com/community-scripts/ProxmoxVE/pull/7660)) - Wikijs: Bump Node.js version to 22 [@MickLesk](https://github.com/MickLesk) ([#7643](https://github.com/community-scripts/ProxmoxVE/pull/7643)) ## 2025-09-14 ## 2025-09-13 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Palmr: hotfix #7622 [@vhsdream](https://github.com/vhsdream) ([#7625](https://github.com/community-scripts/ProxmoxVE/pull/7625)) - ollama: fix: ccurl continue on interrupts [@CrazyWolf13](https://github.com/CrazyWolf13) ([#7620](https://github.com/community-scripts/ProxmoxVE/pull/7620)) - #### 🔧 Refactor - pdm: refactor for beta version [@CrazyWolf13](https://github.com/CrazyWolf13) ([#7619](https://github.com/community-scripts/ProxmoxVE/pull/7619)) - Immich: bump to v1.142.0 [@vhsdream](https://github.com/vhsdream) ([#7594](https://github.com/community-scripts/ProxmoxVE/pull/7594)) ### 🌐 Website - fix: tagline grammar [@jonathanwuki](https://github.com/jonathanwuki) ([#7621](https://github.com/community-scripts/ProxmoxVE/pull/7621)) - fix: grammar/capitalization for links and taglines [@jonathanwuki](https://github.com/jonathanwuki) ([#7609](https://github.com/community-scripts/ProxmoxVE/pull/7609)) ## 2025-09-12 ### 🆕 New Scripts - Stylus ([#7588](https://github.com/community-scripts/ProxmoxVE/pull/7588)) - UHF ([#7589](https://github.com/community-scripts/ProxmoxVE/pull/7589)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Tweak: post-pve-install: create folder if Not exist [@JVKeller](https://github.com/JVKeller) ([#7598](https://github.com/community-scripts/ProxmoxVE/pull/7598)) - Update openwebui.sh [@webmogul1](https://github.com/webmogul1) ([#7582](https://github.com/community-scripts/ProxmoxVE/pull/7582)) - #### ✨ New Features - [core]: add fallback if mariadb upstream unreachable [@MickLesk](https://github.com/MickLesk) ([#7599](https://github.com/community-scripts/ProxmoxVE/pull/7599)) - ESPHome: Increase default disk size [@tremor021](https://github.com/tremor021) ([#7600](https://github.com/community-scripts/ProxmoxVE/pull/7600)) ## 2025-09-11 ### 🆕 New Scripts - telegraf ([#7576](https://github.com/community-scripts/ProxmoxVE/pull/7576)) ### 🚀 Updated Scripts - [core] Sort tools.func functions alphabeticaly [@tremor021](https://github.com/tremor021) ([#7569](https://github.com/community-scripts/ProxmoxVE/pull/7569)) - mobile subscription nag fix [@dvino](https://github.com/dvino) ([#7567](https://github.com/community-scripts/ProxmoxVE/pull/7567)) - #### 🐞 Bug Fixes - alpine-install: switch to using GitHub to fetch tools when using GitHub [@burritosoftware](https://github.com/burritosoftware) ([#7566](https://github.com/community-scripts/ProxmoxVE/pull/7566)) ### 🌐 Website - #### 🐞 Bug Fixes - Add margin-bottom to Most Viewed Scripts header to unifi UI [@BramSuurdje](https://github.com/BramSuurdje) ([#7572](https://github.com/community-scripts/ProxmoxVE/pull/7572)) - #### 📝 Script Information - Fix frontend url [@r1cebank](https://github.com/r1cebank) ([#7578](https://github.com/community-scripts/ProxmoxVE/pull/7578)) ## 2025-09-10 ### 🆕 New Scripts - Autocaliweb ([#7515](https://github.com/community-scripts/ProxmoxVE/pull/7515)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Palmr: fix #7556 [@vhsdream](https://github.com/vhsdream) ([#7558](https://github.com/community-scripts/ProxmoxVE/pull/7558)) - Wizarr: Fix DB migrations [@vhsdream](https://github.com/vhsdream) ([#7552](https://github.com/community-scripts/ProxmoxVE/pull/7552)) - fix: pmg - split no-nag script into separate config files [@MickLesk](https://github.com/MickLesk) ([#7540](https://github.com/community-scripts/ProxmoxVE/pull/7540)) - #### ✨ New Features - Update Palmr to Support new v3.2.1 [@vhsdream](https://github.com/vhsdream) ([#7526](https://github.com/community-scripts/ProxmoxVE/pull/7526)) - add external installer warnings and user confirmation in several LXC's [@MickLesk](https://github.com/MickLesk) ([#7539](https://github.com/community-scripts/ProxmoxVE/pull/7539)) - Booklore: Add Bookdrop location to .env [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#7533](https://github.com/community-scripts/ProxmoxVE/pull/7533)) - #### 🔧 Refactor - Refactor: audiobookshelf [@MickLesk](https://github.com/MickLesk) ([#7538](https://github.com/community-scripts/ProxmoxVE/pull/7538)) - Refactor: Blocky [@MickLesk](https://github.com/MickLesk) ([#7537](https://github.com/community-scripts/ProxmoxVE/pull/7537)) - Improve npmplus credential retrieval and messaging [@MickLesk](https://github.com/MickLesk) ([#7532](https://github.com/community-scripts/ProxmoxVE/pull/7532)) ### 🌐 Website - #### 💥 Breaking Changes - Remove Pingvin Share [@CrazyWolf13](https://github.com/CrazyWolf13) ([#7553](https://github.com/community-scripts/ProxmoxVE/pull/7553)) - #### 📝 Script Information - set updateable to true for several lxc JSON-configs [@MickLesk](https://github.com/MickLesk) ([#7534](https://github.com/community-scripts/ProxmoxVE/pull/7534)) ## 2025-09-09 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Tududi: v0.81 [@vhsdream](https://github.com/vhsdream) ([#7517](https://github.com/community-scripts/ProxmoxVE/pull/7517)) - WGDashboard: Revert back to old update method [@tremor021](https://github.com/tremor021) ([#7500](https://github.com/community-scripts/ProxmoxVE/pull/7500)) - AdventureLog: remove folder during update process [@MickLesk](https://github.com/MickLesk) ([#7507](https://github.com/community-scripts/ProxmoxVE/pull/7507)) - PLANKA: Fix backup and restore commands [@tremor021](https://github.com/tremor021) ([#7505](https://github.com/community-scripts/ProxmoxVE/pull/7505)) - Recyclarr: Suppress config creation output [@tremor021](https://github.com/tremor021) ([#7502](https://github.com/community-scripts/ProxmoxVE/pull/7502)) - #### 🔧 Refactor - Pulse: standardise install/update with Pulse repo script [@vhsdream](https://github.com/vhsdream) ([#7519](https://github.com/community-scripts/ProxmoxVE/pull/7519)) ### 🌐 Website - #### 🐞 Bug Fixes - Refactor GitHubStarsButton to wrap in Link component for external navigation [@BramSuurdje](https://github.com/BramSuurdje) ([#7492](https://github.com/community-scripts/ProxmoxVE/pull/7492)) - #### ✨ New Features - Bump vite from 7.0.0 to 7.1.5 in /frontend [@dependabot[bot]](https://github.com/dependabot[bot]) ([#7522](https://github.com/community-scripts/ProxmoxVE/pull/7522)) - #### 📝 Script Information - swizzin: Change category from nvr to media [@MickLesk](https://github.com/MickLesk) ([#7511](https://github.com/community-scripts/ProxmoxVE/pull/7511)) ## 2025-09-08 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - CT's: fix missing variable declaration (actualBudget, openziti, umlautadaptarr) [@MickLesk](https://github.com/MickLesk) ([#7483](https://github.com/community-scripts/ProxmoxVE/pull/7483)) - karakeep: fix service file [@CrazyWolf13](https://github.com/CrazyWolf13) ([#7482](https://github.com/community-scripts/ProxmoxVE/pull/7482)) - Update searxng-install.sh [@sebguy](https://github.com/sebguy) ([#7469](https://github.com/community-scripts/ProxmoxVE/pull/7469)) - #### ✨ New Features - Immich: bump to version 1.141.1 [@vhsdream](https://github.com/vhsdream) ([#7418](https://github.com/community-scripts/ProxmoxVE/pull/7418)) - [core]: switch all base_settings to variables [@MickLesk](https://github.com/MickLesk) ([#7479](https://github.com/community-scripts/ProxmoxVE/pull/7479)) - #### 💥 Breaking Changes - RustDesk Server: Update the credentials info [@tremor021](https://github.com/tremor021) ([#7473](https://github.com/community-scripts/ProxmoxVE/pull/7473)) ### 🌐 Website - #### 🐞 Bug Fixes - Format numerical values in DataFetcher component for better readability [@BramSuurdje](https://github.com/BramSuurdje) ([#7477](https://github.com/community-scripts/ProxmoxVE/pull/7477)) - #### ✨ New Features - feat: enhance github stars button to be better looking and more compact [@BramSuurdje](https://github.com/BramSuurdje) ([#7464](https://github.com/community-scripts/ProxmoxVE/pull/7464)) ## 2025-09-07 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Update ExecStart path for karakeep service [@CrazyWolf13](https://github.com/CrazyWolf13) ([#7460](https://github.com/community-scripts/ProxmoxVE/pull/7460)) ## 2025-09-06 ### 🆕 New Scripts - Resilio Sync ([#7442](https://github.com/community-scripts/ProxmoxVE/pull/7442)) - leantime ([#7414](https://github.com/community-scripts/ProxmoxVE/pull/7414)) ### 🚀 Updated Scripts - use debian source for direct installation of MQTT [@EtlamGit](https://github.com/EtlamGit) ([#7423](https://github.com/community-scripts/ProxmoxVE/pull/7423)) - #### ✨ New Features - feat: added mobile ui subscription nag removal [@ivan-penchev](https://github.com/ivan-penchev) ([#7164](https://github.com/community-scripts/ProxmoxVE/pull/7164)) ### 🌐 Website - #### 📝 Script Information - MediaManager Configuration Path [@austinpilz](https://github.com/austinpilz) ([#7408](https://github.com/community-scripts/ProxmoxVE/pull/7408)) - Paperless-NGX: Remove default credentials from json [@tremor021](https://github.com/tremor021) ([#7403](https://github.com/community-scripts/ProxmoxVE/pull/7403)) ## 2025-09-05 ### 🚀 Updated Scripts - Tududi: Pin version to 0.80 [@vhsdream](https://github.com/vhsdream) ([#7420](https://github.com/community-scripts/ProxmoxVE/pull/7420)) - #### 🐞 Bug Fixes - AdventureLog: Update dependencies [@tremor021](https://github.com/tremor021) ([#7404](https://github.com/community-scripts/ProxmoxVE/pull/7404)) ### 🌐 Website - refactor: Enhance ScriptAccordion and Sidebar components to support selectedCategory state [@BramSuurdje](https://github.com/BramSuurdje) ([#7405](https://github.com/community-scripts/ProxmoxVE/pull/7405)) ## 2025-09-04 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - fix: Syntax error in Immich scripts [@henworth](https://github.com/henworth) ([#7398](https://github.com/community-scripts/ProxmoxVE/pull/7398)) - Netdata: Fix pve_check for 8 [@MickLesk](https://github.com/MickLesk) ([#7392](https://github.com/community-scripts/ProxmoxVE/pull/7392)) - #### 🔧 Refactor - Immich: pin compiled photo library revisions [@vhsdream](https://github.com/vhsdream) ([#7395](https://github.com/community-scripts/ProxmoxVE/pull/7395)) ## 2025-09-03 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Element-Synapse: Increase HDD size [@tremor021](https://github.com/tremor021) ([#7384](https://github.com/community-scripts/ProxmoxVE/pull/7384)) - Wizarr: fix uv lock issue; use correct output suppression [@vhsdream](https://github.com/vhsdream) ([#7378](https://github.com/community-scripts/ProxmoxVE/pull/7378)) - Netbox: Fix missing directory [@tremor021](https://github.com/tremor021) ([#7374](https://github.com/community-scripts/ProxmoxVE/pull/7374)) - #### 🔧 Refactor - Enhanced IP-Tag installation script with interactive configuration, improved VM IP detection, and better visual indicators [@DesertGamer](https://github.com/DesertGamer) ([#7366](https://github.com/community-scripts/ProxmoxVE/pull/7366)) ### 🌐 Website - #### 🐞 Bug Fixes - Fix navigation [@BramSuurdje](https://github.com/BramSuurdje) ([#7376](https://github.com/community-scripts/ProxmoxVE/pull/7376)) ## 2025-09-02 ### 🚀 Updated Scripts - Increase default disk size for Apt-Cacher-NG [@MickLesk](https://github.com/MickLesk) ([#7352](https://github.com/community-scripts/ProxmoxVE/pull/7352)) - #### 🐞 Bug Fixes - Snipe-IT: Fix Nginx configuration [@tremor021](https://github.com/tremor021) ([#7358](https://github.com/community-scripts/ProxmoxVE/pull/7358)) - booklore: remove folder before update [@MickLesk](https://github.com/MickLesk) ([#7351](https://github.com/community-scripts/ProxmoxVE/pull/7351)) - #### ✨ New Features - Immich: bump version to 1.140.1 [@vhsdream](https://github.com/vhsdream) ([#7349](https://github.com/community-scripts/ProxmoxVE/pull/7349)) ### 🌐 Website - #### 📝 Script Information - pbs: increase note on website for ipv6 [@MickLesk](https://github.com/MickLesk) ([#7339](https://github.com/community-scripts/ProxmoxVE/pull/7339)) ## 2025-09-01 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Update configarr.sh to mv backep up .env correctly [@finkerle](https://github.com/finkerle) ([#7323](https://github.com/community-scripts/ProxmoxVE/pull/7323)) - #### ✨ New Features - Refactor + Feature Bump: HomeAssistant OS [@MickLesk](https://github.com/MickLesk) ([#7336](https://github.com/community-scripts/ProxmoxVE/pull/7336)) - UmbrelOS: Refactor / use q35 / better import [@MickLesk](https://github.com/MickLesk) ([#7329](https://github.com/community-scripts/ProxmoxVE/pull/7329)) - Harmonize GH Release Check (excl. Pre-Releases & Migrate old "_version.txt" [@MickLesk](https://github.com/MickLesk) ([#7328](https://github.com/community-scripts/ProxmoxVE/pull/7328)) ### 🌐 Website - Bump next from 15.2.4 to 15.5.2 in /frontend [@dependabot[bot]](https://github.com/dependabot[bot]) ([#7309](https://github.com/community-scripts/ProxmoxVE/pull/7309)) - #### 📝 Script Information - booklore: add note for start-up in frontend [@MickLesk](https://github.com/MickLesk) ([#7331](https://github.com/community-scripts/ProxmoxVE/pull/7331)) ================================================ FILE: .github/changelogs/2025/10.md ================================================ ## 2025-10-31 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Reitti: Fix missing data directory [@tremor021](https://github.com/tremor021) ([#8787](https://github.com/community-scripts/ProxmoxVE/pull/8787)) - omada: fix update script with mongodb 8 [@MickLesk](https://github.com/MickLesk) ([#8724](https://github.com/community-scripts/ProxmoxVE/pull/8724)) - Booklore: Fix port configuration for Nginx [@tremor021](https://github.com/tremor021) ([#8780](https://github.com/community-scripts/ProxmoxVE/pull/8780)) - Fix paths in grist.sh [@mrinaldi](https://github.com/mrinaldi) ([#8777](https://github.com/community-scripts/ProxmoxVE/pull/8777)) ### 🌐 Website - #### 📝 Script Information - Removed errant ` from wireguard.json [@AndrewDragonCh](https://github.com/AndrewDragonCh) ([#8791](https://github.com/community-scripts/ProxmoxVE/pull/8791)) ## 2025-10-30 ### 🆕 New Scripts - Livebook ([#8739](https://github.com/community-scripts/ProxmoxVE/pull/8739)) - Reitti ([#8736](https://github.com/community-scripts/ProxmoxVE/pull/8736)) - BentoPDF ([#8735](https://github.com/community-scripts/ProxmoxVE/pull/8735)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Open Archiver: Fix missing daemon-reload [@tremor021](https://github.com/tremor021) ([#8768](https://github.com/community-scripts/ProxmoxVE/pull/8768)) - Open Archiver: Fix missing command in update procedure [@tremor021](https://github.com/tremor021) ([#8765](https://github.com/community-scripts/ProxmoxVE/pull/8765)) - Kimai: Fix database connection string [@tremor021](https://github.com/tremor021) ([#8758](https://github.com/community-scripts/ProxmoxVE/pull/8758)) - Add explicit exit calls to update_script functions [@MickLesk](https://github.com/MickLesk) ([#8752](https://github.com/community-scripts/ProxmoxVE/pull/8752)) - kimai: Set global SQL mode to empty in install script [@MickLesk](https://github.com/MickLesk) ([#8747](https://github.com/community-scripts/ProxmoxVE/pull/8747)) - #### ✨ New Features - Immich: Updates for v2.2.0 [@vhsdream](https://github.com/vhsdream) ([#8770](https://github.com/community-scripts/ProxmoxVE/pull/8770)) - Standardize update success messages in scripts [@MickLesk](https://github.com/MickLesk) ([#8757](https://github.com/community-scripts/ProxmoxVE/pull/8757)) - core: add function cleanup_lxc [@MickLesk](https://github.com/MickLesk) ([#8749](https://github.com/community-scripts/ProxmoxVE/pull/8749)) - Asterisk: add interactive version selection to installer [@MickLesk](https://github.com/MickLesk) ([#8726](https://github.com/community-scripts/ProxmoxVE/pull/8726)) ### 🌐 Website - #### 📝 Script Information - Cronicle: Update default credentials [@tremor021](https://github.com/tremor021) ([#8720](https://github.com/community-scripts/ProxmoxVE/pull/8720)) ## 2025-10-29 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Docker-VM: add workaround for libguestfs issue on Proxmox VE 9+ [@MickLesk](https://github.com/MickLesk) ([#8722](https://github.com/community-scripts/ProxmoxVE/pull/8722)) - Dispatcharr: add folders in installer / add more build ressources [@MickLesk](https://github.com/MickLesk) ([#8708](https://github.com/community-scripts/ProxmoxVE/pull/8708)) - LibreTranslate: bump torch version [@MickLesk](https://github.com/MickLesk) ([#8710](https://github.com/community-scripts/ProxmoxVE/pull/8710)) - #### ✨ New Features - Archivebox: add Chromium and Node modules [@MickLesk](https://github.com/MickLesk) ([#8725](https://github.com/community-scripts/ProxmoxVE/pull/8725)) - #### 🔧 Refactor - tracktor: refactor envfile [@CrazyWolf13](https://github.com/CrazyWolf13) ([#8711](https://github.com/community-scripts/ProxmoxVE/pull/8711)) - Kimai / Ghost / ManageMyDamnLife: Switch to MariaDB [@MickLesk](https://github.com/MickLesk) ([#8712](https://github.com/community-scripts/ProxmoxVE/pull/8712)) ## 2025-10-28 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Update alpine-komodo.sh fixing missing pull images command [@glopes](https://github.com/glopes) ([#8689](https://github.com/community-scripts/ProxmoxVE/pull/8689)) - #### ✨ New Features - Update SABnzbd. Include par2cmdline-turbo [@burgerga](https://github.com/burgerga) ([#8648](https://github.com/community-scripts/ProxmoxVE/pull/8648)) - jotty: Add more ENV VARS (disabled) [@vhsdream](https://github.com/vhsdream) ([#8688](https://github.com/community-scripts/ProxmoxVE/pull/8688)) - Bump bazarr to Debian 13 [@burgerga](https://github.com/burgerga) ([#8677](https://github.com/community-scripts/ProxmoxVE/pull/8677)) - Update flaresolverr to Debian 13 [@burgerga](https://github.com/burgerga) ([#8672](https://github.com/community-scripts/ProxmoxVE/pull/8672)) ## 2025-10-27 ### 🆕 New Scripts - Dispatcharr ([#8658](https://github.com/community-scripts/ProxmoxVE/pull/8658)) - Garage | Alpine-Garage ([#8656](https://github.com/community-scripts/ProxmoxVE/pull/8656)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Add typescript and esbuild to browserless setup [@MickLesk](https://github.com/MickLesk) ([#8666](https://github.com/community-scripts/ProxmoxVE/pull/8666)) - jellyfin: fix: intel deps [@CrazyWolf13](https://github.com/CrazyWolf13) ([#8657](https://github.com/community-scripts/ProxmoxVE/pull/8657)) ## 2025-10-26 ### 🆕 New Scripts - ComfyUI ([#8633](https://github.com/community-scripts/ProxmoxVE/pull/8633)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - PiHole: Bump to Debian 12 [@MickLesk](https://github.com/MickLesk) ([#8649](https://github.com/community-scripts/ProxmoxVE/pull/8649)) - #### 🔧 Refactor - Refactor: Mylar3 [@tremor021](https://github.com/tremor021) ([#8642](https://github.com/community-scripts/ProxmoxVE/pull/8642)) ## 2025-10-25 ### 🆕 New Scripts - PatchMon ([#8632](https://github.com/community-scripts/ProxmoxVE/pull/8632)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - UrBackup Server: Fix install going interactive [@tremor021](https://github.com/tremor021) ([#8622](https://github.com/community-scripts/ProxmoxVE/pull/8622)) ## 2025-10-24 ### 🌐 Website - #### 📝 Script Information - Fix config path for BunkerWeb [@Nonolanlan1007](https://github.com/Nonolanlan1007) ([#8618](https://github.com/community-scripts/ProxmoxVE/pull/8618)) - Update logo URL in guardian.json [@HydroshieldMKII](https://github.com/HydroshieldMKII) ([#8615](https://github.com/community-scripts/ProxmoxVE/pull/8615)) ## 2025-10-23 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Radicale: Update dependencies [@ilofX](https://github.com/ilofX) ([#8603](https://github.com/community-scripts/ProxmoxVE/pull/8603)) - Various Downgrades to Debian 12 (MySQL / OMW / Technitium) [@MickLesk](https://github.com/MickLesk) ([#8595](https://github.com/community-scripts/ProxmoxVE/pull/8595)) - MeTube: Fix inserting path into .bashrc [@tremor021](https://github.com/tremor021) ([#8589](https://github.com/community-scripts/ProxmoxVE/pull/8589)) - #### 🔧 Refactor - Refactor: Kavita + Updated tools.func (no-same-owner) [@MickLesk](https://github.com/MickLesk) ([#8594](https://github.com/community-scripts/ProxmoxVE/pull/8594)) - tools.func: update update_check messages for clarity [@MickLesk](https://github.com/MickLesk) ([#8588](https://github.com/community-scripts/ProxmoxVE/pull/8588)) ## 2025-10-22 ### 🚀 Updated Scripts - Refactor: Full Change & Feature-Bump of tools.func [@MickLesk](https://github.com/MickLesk) ([#8409](https://github.com/community-scripts/ProxmoxVE/pull/8409)) - #### 🐞 Bug Fixes - part-db: use helper-script php function [@MickLesk](https://github.com/MickLesk) ([#8575](https://github.com/community-scripts/ProxmoxVE/pull/8575)) - omada: remove static mongodb install [@MickLesk](https://github.com/MickLesk) ([#8577](https://github.com/community-scripts/ProxmoxVE/pull/8577)) ## 2025-10-21 ### 🆕 New Scripts - rwMarkable: migrate from rwMarkable => jotty [@vhsdream](https://github.com/vhsdream) ([#8554](https://github.com/community-scripts/ProxmoxVE/pull/8554)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Guardian: Added validation before copying file and fix build command error [@HydroshieldMKII](https://github.com/HydroshieldMKII) ([#8553](https://github.com/community-scripts/ProxmoxVE/pull/8553)) - Unifi: Bump libssl debian version to new update [@fastiuk](https://github.com/fastiuk) ([#8547](https://github.com/community-scripts/ProxmoxVE/pull/8547)) - Alpine-TeamSpeak-Server: Fix release version fetching [@tremor021](https://github.com/tremor021) ([#8537](https://github.com/community-scripts/ProxmoxVE/pull/8537)) - jellyfin: fix opencl dep for ubuntu [@MickLesk](https://github.com/MickLesk) ([#8535](https://github.com/community-scripts/ProxmoxVE/pull/8535)) - #### ✨ New Features - Refactor: ProjectSend [@tremor021](https://github.com/tremor021) ([#8552](https://github.com/community-scripts/ProxmoxVE/pull/8552)) ### 🌐 Website - #### 📝 Script Information - Open Archiver: Fix application icon [@tremor021](https://github.com/tremor021) ([#8542](https://github.com/community-scripts/ProxmoxVE/pull/8542)) ## 2025-10-20 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - jellyfin: fix: version conflict [@CrazyWolf13](https://github.com/CrazyWolf13) ([#8520](https://github.com/community-scripts/ProxmoxVE/pull/8520)) - Paperless-AI: Increase CPU and RAM [@MickLesk](https://github.com/MickLesk) ([#8507](https://github.com/community-scripts/ProxmoxVE/pull/8507)) - #### ✨ New Features - Enhance error message for container creation failure [@MickLesk](https://github.com/MickLesk) ([#8511](https://github.com/community-scripts/ProxmoxVE/pull/8511)) - Filebrowser-Quantum: change initial config to newer default [@MickLesk](https://github.com/MickLesk) ([#8497](https://github.com/community-scripts/ProxmoxVE/pull/8497)) - #### 💥 Breaking Changes - Remove: GoMFT [@MickLesk](https://github.com/MickLesk) ([#8499](https://github.com/community-scripts/ProxmoxVE/pull/8499)) - #### 🔧 Refactor - palmr: update node to v24 [@CrazyWolf13](https://github.com/CrazyWolf13) ([#8521](https://github.com/community-scripts/ProxmoxVE/pull/8521)) - jellyfin: add: intel dependencies [@CrazyWolf13](https://github.com/CrazyWolf13) ([#8508](https://github.com/community-scripts/ProxmoxVE/pull/8508)) - Jellyfin: ensure libjemalloc is used / increase hdd space [@MickLesk](https://github.com/MickLesk) ([#8494](https://github.com/community-scripts/ProxmoxVE/pull/8494)) ## 2025-10-19 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - rwMarkable: Increase RAM [@vhsdream](https://github.com/vhsdream) ([#8482](https://github.com/community-scripts/ProxmoxVE/pull/8482)) - changedetection: fix: update [@CrazyWolf13](https://github.com/CrazyWolf13) ([#8480](https://github.com/community-scripts/ProxmoxVE/pull/8480)) ## 2025-10-18 ### 🆕 New Scripts - Open-Archiver ([#8452](https://github.com/community-scripts/ProxmoxVE/pull/8452)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Cronicle: Dont copy init.d service file [@tremor021](https://github.com/tremor021) ([#8451](https://github.com/community-scripts/ProxmoxVE/pull/8451)) - #### 🔧 Refactor - Refactor: Nginx Proxy Manager [@MickLesk](https://github.com/MickLesk) ([#8453](https://github.com/community-scripts/ProxmoxVE/pull/8453)) ## 2025-10-17 ### 🚀 Updated Scripts - Revert back to debian 12 template for various apps [@tremor021](https://github.com/tremor021) ([#8431](https://github.com/community-scripts/ProxmoxVE/pull/8431)) - #### 🐞 Bug Fixes - [FIX]Pulse: replace policykit-1 with polkitd [@vhsdream](https://github.com/vhsdream) ([#8439](https://github.com/community-scripts/ProxmoxVE/pull/8439)) - MySpeed: Fix build step [@tremor021](https://github.com/tremor021) ([#8427](https://github.com/community-scripts/ProxmoxVE/pull/8427)) - #### ✨ New Features - GLPI: Bump to Debian 13 base [@tremor021](https://github.com/tremor021) ([#8443](https://github.com/community-scripts/ProxmoxVE/pull/8443)) - #### 🔧 Refactor - refactor: fix pve-scripts local install script [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#8418](https://github.com/community-scripts/ProxmoxVE/pull/8418)) ### 🌐 Website - #### 📝 Script Information - PLANKA: Fix config path [@tremor021](https://github.com/tremor021) ([#8422](https://github.com/community-scripts/ProxmoxVE/pull/8422)) ## 2025-10-16 ### 🚀 Updated Scripts - post-pve/post-pbs: Disable 'pve-enterprise' and 'ceph enterprise' repositories [@MickLesk](https://github.com/MickLesk) ([#8399](https://github.com/community-scripts/ProxmoxVE/pull/8399)) - #### 🐞 Bug Fixes - fix: changedetection: fix for tsc and esbuild not found [@CrazyWolf13](https://github.com/CrazyWolf13) ([#8407](https://github.com/community-scripts/ProxmoxVE/pull/8407)) - paperless-ngx: remove unneeded deps, use static ghostscript [@MickLesk](https://github.com/MickLesk) ([#8397](https://github.com/community-scripts/ProxmoxVE/pull/8397)) - UmlautAdaptarr: Revert back to bookworm repo [@tremor021](https://github.com/tremor021) ([#8392](https://github.com/community-scripts/ProxmoxVE/pull/8392)) - #### 🔧 Refactor - Enhance nginx proxy manager install script [@MickLesk](https://github.com/MickLesk) ([#8400](https://github.com/community-scripts/ProxmoxVE/pull/8400)) ## 2025-10-15 ### 🆕 New Scripts - LimeSurvey ([#8364](https://github.com/community-scripts/ProxmoxVE/pull/8364)) - Guardian ([#8365](https://github.com/community-scripts/ProxmoxVE/pull/8365)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Update omada-install.sh to use correct libssl version [@punctualwesley](https://github.com/punctualwesley) ([#8380](https://github.com/community-scripts/ProxmoxVE/pull/8380)) - zigbee2mqtt: Use hardlinks for PNPM packages [@mikeage](https://github.com/mikeage) ([#8357](https://github.com/community-scripts/ProxmoxVE/pull/8357)) - #### ✨ New Features - Bump Q to S-Scripts to Debian 13 (Trixie) [@MickLesk](https://github.com/MickLesk) ([#8366](https://github.com/community-scripts/ProxmoxVE/pull/8366)) - Bump O to P-Scripts to Debian 13 (Trixie) [@MickLesk](https://github.com/MickLesk) ([#8367](https://github.com/community-scripts/ProxmoxVE/pull/8367)) - Bump L to N-Scripts to Debian 13 (Trixie) [@MickLesk](https://github.com/MickLesk) ([#8368](https://github.com/community-scripts/ProxmoxVE/pull/8368)) - Immich: v2.1.0 - VectorChord 0.5+ support [@vhsdream](https://github.com/vhsdream) ([#8348](https://github.com/community-scripts/ProxmoxVE/pull/8348)) ## 2025-10-14 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - MediaManager: Use managed Python 3.13 [@vhsdream](https://github.com/vhsdream) ([#8343](https://github.com/community-scripts/ProxmoxVE/pull/8343)) - #### 🔧 Refactor - Update cockpit installation/update [@burgerga](https://github.com/burgerga) ([#8346](https://github.com/community-scripts/ProxmoxVE/pull/8346)) ## 2025-10-13 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - GLPI: fix version 11 [@opastorello](https://github.com/opastorello) ([#8238](https://github.com/community-scripts/ProxmoxVE/pull/8238)) - Keycloak: Fix typo in update function [@tremor021](https://github.com/tremor021) ([#8316](https://github.com/community-scripts/ProxmoxVE/pull/8316)) - #### 🔧 Refactor - fix: adjust configarr to use binaries [@BlackDark](https://github.com/BlackDark) ([#8254](https://github.com/community-scripts/ProxmoxVE/pull/8254)) ## 2025-10-12 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Immich: add Debian Testing repo [@vhsdream](https://github.com/vhsdream) ([#8310](https://github.com/community-scripts/ProxmoxVE/pull/8310)) - Tinyauth: Fix install issues for v4 [@tremor021](https://github.com/tremor021) ([#8309](https://github.com/community-scripts/ProxmoxVE/pull/8309)) ## 2025-10-11 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Zabbix: various bugfixes agent1/agent2 [@MickLesk](https://github.com/MickLesk) ([#8294](https://github.com/community-scripts/ProxmoxVE/pull/8294)) - wger: fix python and pip install [@MickLesk](https://github.com/MickLesk) ([#8295](https://github.com/community-scripts/ProxmoxVE/pull/8295)) - searxng: add msgspec as dependency [@MickLesk](https://github.com/MickLesk) ([#8293](https://github.com/community-scripts/ProxmoxVE/pull/8293)) - keycloak: fix update check [@MickLesk](https://github.com/MickLesk) ([#8275](https://github.com/community-scripts/ProxmoxVE/pull/8275)) - komga: fix update check [@MickLesk](https://github.com/MickLesk) ([#8285](https://github.com/community-scripts/ProxmoxVE/pull/8285)) - #### ✨ New Features - host-backup.sh: Added "ALL" option and include timestamp in backup filename [@stumpyofpain](https://github.com/stumpyofpain) ([#8276](https://github.com/community-scripts/ProxmoxVE/pull/8276)) - Komga: Update dependencies and enable RAR5 support [@tremor021](https://github.com/tremor021) ([#8257](https://github.com/community-scripts/ProxmoxVE/pull/8257)) ### 🌐 Website - Update script count in metadata and page content from 300+ to 400+ [@BramSuurdje](https://github.com/BramSuurdje) ([#8279](https://github.com/community-scripts/ProxmoxVE/pull/8279)) - Refactor CI workflow to use Bun instead of Node.js. [@BramSuurdje](https://github.com/BramSuurdje) ([#8277](https://github.com/community-scripts/ProxmoxVE/pull/8277)) ## 2025-10-10 ### 🆕 New Scripts - Prometheus-Blackbox-Exporter ([#8255](https://github.com/community-scripts/ProxmoxVE/pull/8255)) - SonarQube ([#8256](https://github.com/community-scripts/ProxmoxVE/pull/8256)) ### 🚀 Updated Scripts - Unifi installation script fix [@knightfall](https://github.com/knightfall) ([#8242](https://github.com/community-scripts/ProxmoxVE/pull/8242)) - #### 🐞 Bug Fixes - Docmost: Fix env variables [@tremor021](https://github.com/tremor021) ([#8244](https://github.com/community-scripts/ProxmoxVE/pull/8244)) - #### 🔧 Refactor - Harmonize Service MSG-Blocks [@MickLesk](https://github.com/MickLesk) ([#8233](https://github.com/community-scripts/ProxmoxVE/pull/8233)) ## 2025-10-09 ### 🆕 New Scripts - New Script: rwMarkable ([#8215](https://github.com/community-scripts/ProxmoxVE/pull/8215)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Alpine-Tinyauth: Fixes for v4 release [@tremor021](https://github.com/tremor021) ([#8225](https://github.com/community-scripts/ProxmoxVE/pull/8225)) - #### ✨ New Features - Bump U-T Scripts to Debian 13 [@MickLesk](https://github.com/MickLesk) ([#8227](https://github.com/community-scripts/ProxmoxVE/pull/8227)) ## 2025-10-08 ### 🚀 Updated Scripts - MyIP: Increase resources [@tremor021](https://github.com/tremor021) ([#8199](https://github.com/community-scripts/ProxmoxVE/pull/8199)) - #### 🐞 Bug Fixes - Wireguard: Fix sysctl for Trixie [@tremor021](https://github.com/tremor021) ([#8209](https://github.com/community-scripts/ProxmoxVE/pull/8209)) - Update prompt for Stirling-PDF login option [@EarMaster](https://github.com/EarMaster) ([#8196](https://github.com/community-scripts/ProxmoxVE/pull/8196)) - #### 🔧 Refactor - Refactor: Fixed incorrect tag variables in several scripts [@tremor021](https://github.com/tremor021) ([#8182](https://github.com/community-scripts/ProxmoxVE/pull/8182)) - ZeroTier One: Fix install output [@tremor021](https://github.com/tremor021) ([#8179](https://github.com/community-scripts/ProxmoxVE/pull/8179)) ## 2025-10-07 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Alpine-Caddy: remove functions [@MickLesk](https://github.com/MickLesk) ([#8177](https://github.com/community-scripts/ProxmoxVE/pull/8177)) - Palmr: Fix NodeJS setup [@tremor021](https://github.com/tremor021) ([#8173](https://github.com/community-scripts/ProxmoxVE/pull/8173)) - GLPI: Fix UNBOUND variable [@tremor021](https://github.com/tremor021) ([#8167](https://github.com/community-scripts/ProxmoxVE/pull/8167)) - BookLore: upgrade to Java 25/Gradle 9 [@vhsdream](https://github.com/vhsdream) ([#8165](https://github.com/community-scripts/ProxmoxVE/pull/8165)) - Alpine-Wireguard: Fix for update failing in normal mode [@tremor021](https://github.com/tremor021) ([#8160](https://github.com/community-scripts/ProxmoxVE/pull/8160)) - #### ✨ New Features - Bump W-V Scripts to Debian 13 [@MickLesk](https://github.com/MickLesk) ([#8176](https://github.com/community-scripts/ProxmoxVE/pull/8176)) - Bump Z-Y Scripts to Debian 13 [@MickLesk](https://github.com/MickLesk) ([#8174](https://github.com/community-scripts/ProxmoxVE/pull/8174)) - Docmost: Fixes and updates [@tremor021](https://github.com/tremor021) ([#8158](https://github.com/community-scripts/ProxmoxVE/pull/8158)) ## 2025-10-06 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - GLPI: Revert fix for v11 [@tremor021](https://github.com/tremor021) ([#8148](https://github.com/community-scripts/ProxmoxVE/pull/8148)) - #### ✨ New Features - Node-Red: bump to Debian 13 [@MickLesk](https://github.com/MickLesk) ([#8141](https://github.com/community-scripts/ProxmoxVE/pull/8141)) - NocoDB: bump to Debian 13 [@MickLesk](https://github.com/MickLesk) ([#8140](https://github.com/community-scripts/ProxmoxVE/pull/8140)) - Navidrome: bump to Debian 13 [@MickLesk](https://github.com/MickLesk) ([#8139](https://github.com/community-scripts/ProxmoxVE/pull/8139)) - pve-scripts-local: add update function [@MickLesk](https://github.com/MickLesk) ([#8138](https://github.com/community-scripts/ProxmoxVE/pull/8138)) ### 🌐 Website - #### 📝 Script Information - Update config_path for Zigbee2MQTT configuration [@MickLesk](https://github.com/MickLesk) ([#8153](https://github.com/community-scripts/ProxmoxVE/pull/8153)) ## 2025-10-05 ### 🚀 Updated Scripts - #### ✨ New Features - ActualBudget: bump to debian 13 [@MickLesk](https://github.com/MickLesk) ([#8124](https://github.com/community-scripts/ProxmoxVE/pull/8124)) - 2fauth: bump to debian 13 [@MickLesk](https://github.com/MickLesk) ([#8123](https://github.com/community-scripts/ProxmoxVE/pull/8123)) - AdventureLog: bump to debian 13 [@MickLesk](https://github.com/MickLesk) ([#8125](https://github.com/community-scripts/ProxmoxVE/pull/8125)) - Update cockpit to Debian 13 [@burgerga](https://github.com/burgerga) ([#8119](https://github.com/community-scripts/ProxmoxVE/pull/8119)) ## 2025-10-04 ### 🚀 Updated Scripts - immich: guard /dev/dri permissions so CPU-only installs don’t fail [@mlongwell](https://github.com/mlongwell) ([#8094](https://github.com/community-scripts/ProxmoxVE/pull/8094)) - #### ✨ New Features - PosgreSQL: Add version choice [@tremor021](https://github.com/tremor021) ([#8103](https://github.com/community-scripts/ProxmoxVE/pull/8103)) ## 2025-10-03 ### 🆕 New Scripts - pve-scripts-local ([#8083](https://github.com/community-scripts/ProxmoxVE/pull/8083)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - GLPI: Pin version to v10.0.20 [@tremor021](https://github.com/tremor021) ([#8092](https://github.com/community-scripts/ProxmoxVE/pull/8092)) - GLPI: Fix database setup [@tremor021](https://github.com/tremor021) ([#8074](https://github.com/community-scripts/ProxmoxVE/pull/8074)) - Overseerr: Increase resources [@tremor021](https://github.com/tremor021) ([#8086](https://github.com/community-scripts/ProxmoxVE/pull/8086)) - FIX: post-pve-install.sh just quitting [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#8070](https://github.com/community-scripts/ProxmoxVE/pull/8070)) - fix: ensure /etc/pulse exists before chown in update script [@rcourtman](https://github.com/rcourtman) ([#8068](https://github.com/community-scripts/ProxmoxVE/pull/8068)) - grist: remove unneeded var [@MickLesk](https://github.com/MickLesk) ([#8060](https://github.com/community-scripts/ProxmoxVE/pull/8060)) - #### 🔧 Refactor - Immich: bump version to 2.0.1 [@vhsdream](https://github.com/vhsdream) ([#8090](https://github.com/community-scripts/ProxmoxVE/pull/8090)) ### 🌐 Website - #### 🐞 Bug Fixes - Adjust navbar layout for large screen [@BramSuurdje](https://github.com/BramSuurdje) ([#8087](https://github.com/community-scripts/ProxmoxVE/pull/8087)) ## 2025-10-02 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - EMQX: removal logic in emqx update [@MickLesk](https://github.com/MickLesk) ([#8050](https://github.com/community-scripts/ProxmoxVE/pull/8050)) - fix FlareSolverr version check to v3.3.25 [@MickLesk](https://github.com/MickLesk) ([#8051](https://github.com/community-scripts/ProxmoxVE/pull/8051)) ## 2025-10-01 ### 🆕 New Scripts - New Script: PhpMyAdmin (Addon) [@MickLesk](https://github.com/MickLesk) ([#8030](https://github.com/community-scripts/ProxmoxVE/pull/8030)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - openwrt: Add conditional logic for EFI disk allocation [@MickLesk](https://github.com/MickLesk) ([#8024](https://github.com/community-scripts/ProxmoxVE/pull/8024)) - Plant-IT: Pin version to v0.10.0 [@tremor021](https://github.com/tremor021) ([#8023](https://github.com/community-scripts/ProxmoxVE/pull/8023)) - #### ✨ New Features - Immich: bump version to 2.0.0 stable [@vhsdream](https://github.com/vhsdream) ([#8041](https://github.com/community-scripts/ProxmoxVE/pull/8041)) - #### 🔧 Refactor - Immich: bump version to 1.144.1 [@vhsdream](https://github.com/vhsdream) ([#7994](https://github.com/community-scripts/ProxmoxVE/pull/7994)) ================================================ FILE: .github/changelogs/2025/11.md ================================================ ## 2025-11-30 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - fix(recyclarr): remove update script systemctl commands [@vidonnus](https://github.com/vidonnus) ([#9522](https://github.com/community-scripts/ProxmoxVE/pull/9522)) - #### 🔧 Refactor - Refactor: Actual Budget [@tremor021](https://github.com/tremor021) ([#9518](https://github.com/community-scripts/ProxmoxVE/pull/9518)) ## 2025-11-29 ### 🆕 New Scripts - Valkey ([#9510](https://github.com/community-scripts/ProxmoxVE/pull/9510)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Fix duplicate ORIGIN in .env for OpenArchiver install script [@Copilot](https://github.com/Copilot) ([#9503](https://github.com/community-scripts/ProxmoxVE/pull/9503)) - #### 💥 Breaking Changes - Remove: Documenso [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#9507](https://github.com/community-scripts/ProxmoxVE/pull/9507)) ### 🌐 Website - Update Discord link on website [@tremor021](https://github.com/tremor021) ([#9499](https://github.com/community-scripts/ProxmoxVE/pull/9499)) ## 2025-11-28 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Apache-guacamole: fixed to early rm [@mtorazzi](https://github.com/mtorazzi) ([#9492](https://github.com/community-scripts/ProxmoxVE/pull/9492)) - #### 💥 Breaking Changes - Remove: Habitica [@MickLesk](https://github.com/MickLesk) ([#9489](https://github.com/community-scripts/ProxmoxVE/pull/9489)) ## 2025-11-27 ### 🆕 New Scripts - Qdrant ([#9465](https://github.com/community-scripts/ProxmoxVE/pull/9465)) ### 🚀 Updated Scripts - #### 💥 Breaking Changes - Upgrade pve-scripts-local to node 24 [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#9457](https://github.com/community-scripts/ProxmoxVE/pull/9457)) ### 🌐 Website - #### 🐞 Bug Fixes - PBS: fix typo [@joshuaharmsen845](https://github.com/joshuaharmsen845) ([#9482](https://github.com/community-scripts/ProxmoxVE/pull/9482)) ## 2025-11-26 ### 🚀 Updated Scripts - Joplin Server: Increase RAM for LXC [@tremor021](https://github.com/tremor021) ([#9460](https://github.com/community-scripts/ProxmoxVE/pull/9460)) - #### 🐞 Bug Fixes - Fix Open WebUI update logic (swap upgrade/install) [@camcop](https://github.com/camcop) ([#9461](https://github.com/community-scripts/ProxmoxVE/pull/9461)) ## 2025-11-25 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Open WebUI: Change install command to upgrade for Open-WebUI [@tremor021](https://github.com/tremor021) ([#9448](https://github.com/community-scripts/ProxmoxVE/pull/9448)) - core: set default LANG in locale configuration [@MickLesk](https://github.com/MickLesk) ([#9440](https://github.com/community-scripts/ProxmoxVE/pull/9440)) - documenso: switch to npm peer-.deps to get build running [@MickLesk](https://github.com/MickLesk) ([#9441](https://github.com/community-scripts/ProxmoxVE/pull/9441)) - Refactor Asterisk installation process [@MickLesk](https://github.com/MickLesk) ([#9429](https://github.com/community-scripts/ProxmoxVE/pull/9429)) - Fix the mikrotik VM installer after they reformatted their downloads page [@paul-ridgway](https://github.com/paul-ridgway) ([#9434](https://github.com/community-scripts/ProxmoxVE/pull/9434)) - paperless: patch consume to uv [@MickLesk](https://github.com/MickLesk) ([#9425](https://github.com/community-scripts/ProxmoxVE/pull/9425)) - #### ✨ New Features - add Zabbix version selection to install and update scripts [@MickLesk](https://github.com/MickLesk) ([#9430](https://github.com/community-scripts/ProxmoxVE/pull/9430)) ### 🧰 Maintenance - #### 📂 Github - gh: update supported PVE Version [@MickLesk](https://github.com/MickLesk) ([#9422](https://github.com/community-scripts/ProxmoxVE/pull/9422)) ## 2025-11-24 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - core: remove uv cache clean command [@MickLesk](https://github.com/MickLesk) ([#9413](https://github.com/community-scripts/ProxmoxVE/pull/9413)) - Joplin-Server: Bump Node.js version from 22 to 24 [@tremor021](https://github.com/tremor021) ([#9405](https://github.com/community-scripts/ProxmoxVE/pull/9405)) - #### 🔧 Refactor - [Fix]: Wizarr DB error during install [@vhsdream](https://github.com/vhsdream) ([#9415](https://github.com/community-scripts/ProxmoxVE/pull/9415)) ### 🌐 Website - #### 📝 Script Information - Gitea: Update website [@tremor021](https://github.com/tremor021) ([#9406](https://github.com/community-scripts/ProxmoxVE/pull/9406)) - huntarr: disable on website during install issues [@MickLesk](https://github.com/MickLesk) ([#9403](https://github.com/community-scripts/ProxmoxVE/pull/9403)) ## 2025-11-23 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - core: remove journal log rotation [@MickLesk](https://github.com/MickLesk) ([#9392](https://github.com/community-scripts/ProxmoxVE/pull/9392)) - [LibreNMS] Correcting mariadb sed string for Debian 13 default in install/librenms-install.sh, website config for Debian 13 #9369 [@htmlspinnr](https://github.com/htmlspinnr) ([#9370](https://github.com/community-scripts/ProxmoxVE/pull/9370)) - fix: Snipe-IT update check failure [@ruanmed](https://github.com/ruanmed) ([#9371](https://github.com/community-scripts/ProxmoxVE/pull/9371)) - #### ✨ New Features - PVE Kernel Clean: Add info about currently running kernel [@tremor021](https://github.com/tremor021) ([#9388](https://github.com/community-scripts/ProxmoxVE/pull/9388)) - #### 🔧 Refactor - Update glpi-install.sh to remove install.php [@CrazyWolf13](https://github.com/CrazyWolf13) ([#9378](https://github.com/community-scripts/ProxmoxVE/pull/9378)) ### 🌐 Website - #### 🐞 Bug Fixes - fix: enhance back navigation in NotFoundPage component and remove unused deps [@BramSuurdje](https://github.com/BramSuurdje) ([#9341](https://github.com/community-scripts/ProxmoxVE/pull/9341)) - #### ✨ New Features - feat(frontend): add script disable functionality with visual indicators [@AlphaLawless](https://github.com/AlphaLawless) ([#9374](https://github.com/community-scripts/ProxmoxVE/pull/9374)) ## 2025-11-22 ### 🆕 New Scripts - Upgopher ([#9360](https://github.com/community-scripts/ProxmoxVE/pull/9360)) ### 🚀 Updated Scripts - Expand support to Proxmox VE 9.1 in VM scripts [@MickLesk](https://github.com/MickLesk) ([#9351](https://github.com/community-scripts/ProxmoxVE/pull/9351)) - #### 🐞 Bug Fixes - fix: Snipe-IT install and update failure due to new repository url [@ruanmed](https://github.com/ruanmed) ([#9362](https://github.com/community-scripts/ProxmoxVE/pull/9362)) - glpi - allow migration of existing databases [@moodyblue](https://github.com/moodyblue) ([#9353](https://github.com/community-scripts/ProxmoxVE/pull/9353)) - #### ✨ New Features - Refactor cleanup steps to use cleanup_lxc function (install/ Folder) [@MickLesk](https://github.com/MickLesk) ([#9354](https://github.com/community-scripts/ProxmoxVE/pull/9354)) - Remove redundant cleanup steps from update scripts (ct/ Folder) [@MickLesk](https://github.com/MickLesk) ([#9359](https://github.com/community-scripts/ProxmoxVE/pull/9359)) ### 🌐 Website - #### ✨ New Features - Refactor /data page [@BramSuurdje](https://github.com/BramSuurdje) ([#9343](https://github.com/community-scripts/ProxmoxVE/pull/9343)) ## 2025-11-21 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - plex: prevent [] syntax issue [@MickLesk](https://github.com/MickLesk) ([#9318](https://github.com/community-scripts/ProxmoxVE/pull/9318)) - fix: karakeep strip "v" from release version [@CrazyWolf13](https://github.com/CrazyWolf13) ([#9324](https://github.com/community-scripts/ProxmoxVE/pull/9324)) - NetVisor: fix grep in update [@vhsdream](https://github.com/vhsdream) ([#9334](https://github.com/community-scripts/ProxmoxVE/pull/9334)) - Immich: pin correct version [@vhsdream](https://github.com/vhsdream) ([#9332](https://github.com/community-scripts/ProxmoxVE/pull/9332)) - #### 🔧 Refactor - Refactor IPv6 disable logic and add 'disable' option [@MickLesk](https://github.com/MickLesk) ([#9326](https://github.com/community-scripts/ProxmoxVE/pull/9326)) ## 2025-11-20 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - core: change 'uv cache clear' to 'uv cache clean' [@MickLesk](https://github.com/MickLesk) ([#9299](https://github.com/community-scripts/ProxmoxVE/pull/9299)) - #### ✨ New Features - Immich v2.3.1: OpenVINO tuning, OCR fixes, Maintenance mode, workflows/plugin framework [@vhsdream](https://github.com/vhsdream) ([#9310](https://github.com/community-scripts/ProxmoxVE/pull/9310)) - kasm: add: update [@CrazyWolf13](https://github.com/CrazyWolf13) ([#9253](https://github.com/community-scripts/ProxmoxVE/pull/9253)) - tools/pve: expand PVE support to 9.0–9.1 (post-install & netdata) [@MickLesk](https://github.com/MickLesk) ([#9298](https://github.com/community-scripts/ProxmoxVE/pull/9298)) - #### 💥 Breaking Changes - Omada - AVX-only support [@MickLesk](https://github.com/MickLesk) ([#9295](https://github.com/community-scripts/ProxmoxVE/pull/9295)) ## 2025-11-19 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - HotFix: Fix NetVisor env var [@vhsdream](https://github.com/vhsdream) ([#9286](https://github.com/community-scripts/ProxmoxVE/pull/9286)) - Jotty: reduce RAM requirement [@vhsdream](https://github.com/vhsdream) ([#9272](https://github.com/community-scripts/ProxmoxVE/pull/9272)) - Nginx Proxy Manager: Pin version to v2.13.4 [@tremor021](https://github.com/tremor021) ([#9259](https://github.com/community-scripts/ProxmoxVE/pull/9259)) - #### ✨ New Features - PVE 9.1 version support [@MickLesk](https://github.com/MickLesk) ([#9280](https://github.com/community-scripts/ProxmoxVE/pull/9280)) - force disable IPv6 if IPV6_METHOD = none [@MickLesk](https://github.com/MickLesk) ([#9277](https://github.com/community-scripts/ProxmoxVE/pull/9277)) - #### 💥 Breaking Changes - NetVisor: v0.10.0 fixes [@vhsdream](https://github.com/vhsdream) ([#9255](https://github.com/community-scripts/ProxmoxVE/pull/9255)) ## 2025-11-18 ### 🚀 Updated Scripts - librenms: Fix password to short [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#9236](https://github.com/community-scripts/ProxmoxVE/pull/9236)) - #### 🐞 Bug Fixes - Huntarr: Downgrade Python to 3.12 [@MickLesk](https://github.com/MickLesk) ([#9246](https://github.com/community-scripts/ProxmoxVE/pull/9246)) - kasm: fix release fetching [@MickLesk](https://github.com/MickLesk) ([#9244](https://github.com/community-scripts/ProxmoxVE/pull/9244)) ## 2025-11-17 ### 🆕 New Scripts - Passbolt ([#9226](https://github.com/community-scripts/ProxmoxVE/pull/9226)) - Domain-Locker ([#9214](https://github.com/community-scripts/ProxmoxVE/pull/9214)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Domain Monitor: Fix encryption key length in install script [@tremor021](https://github.com/tremor021) ([#9239](https://github.com/community-scripts/ProxmoxVE/pull/9239)) - NetVisor: add build deps, increase RAM [@vhsdream](https://github.com/vhsdream) ([#9205](https://github.com/community-scripts/ProxmoxVE/pull/9205)) - fix: restart apache2 after installing zabbix config [@AlphaLawless](https://github.com/AlphaLawless) ([#9206](https://github.com/community-scripts/ProxmoxVE/pull/9206)) - #### ✨ New Features - [core]: harmonize app_name for creds [@MickLesk](https://github.com/MickLesk) ([#9224](https://github.com/community-scripts/ProxmoxVE/pull/9224)) - #### 💥 Breaking Changes - Refactor: paperless-ngx (Breaking Change Inside) [@MickLesk](https://github.com/MickLesk) ([#9223](https://github.com/community-scripts/ProxmoxVE/pull/9223)) ### 🧰 Maintenance - #### 📂 Github - github: add verbose mode check to bug report template [@MickLesk](https://github.com/MickLesk) ([#9234](https://github.com/community-scripts/ProxmoxVE/pull/9234)) ## 2025-11-16 ### 🆕 New Scripts - Metabase ([#9190](https://github.com/community-scripts/ProxmoxVE/pull/9190)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Change backup directory to /opt for paperless-ngx [@ProfDrYoMan](https://github.com/ProfDrYoMan) ([#9195](https://github.com/community-scripts/ProxmoxVE/pull/9195)) - Kimai: remove deprecated admin_lte section [@MickLesk](https://github.com/MickLesk) ([#9182](https://github.com/community-scripts/ProxmoxVE/pull/9182)) - healthchecks: bump python to 3.13 [@MickLesk](https://github.com/MickLesk) ([#9175](https://github.com/community-scripts/ProxmoxVE/pull/9175)) ### 🌐 Website - #### 📝 Script Information - fixed config_path for donetick [@TazztheMonster](https://github.com/TazztheMonster) ([#9203](https://github.com/community-scripts/ProxmoxVE/pull/9203)) ## 2025-11-15 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - privatebin: fix: syntax error in chmod command [@CrazyWolf13](https://github.com/CrazyWolf13) ([#9169](https://github.com/community-scripts/ProxmoxVE/pull/9169)) - phpIPHAM: patch db and add fping [@MickLesk](https://github.com/MickLesk) ([#9177](https://github.com/community-scripts/ProxmoxVE/pull/9177)) - changedetection: fix: increase ressources [@CrazyWolf13](https://github.com/CrazyWolf13) ([#9171](https://github.com/community-scripts/ProxmoxVE/pull/9171)) - 2fauth: update composer command [@CrazyWolf13](https://github.com/CrazyWolf13) ([#9168](https://github.com/community-scripts/ProxmoxVE/pull/9168)) - #### 🔧 Refactor - firefly: refactor update_script and add dataimporter update [@MickLesk](https://github.com/MickLesk) ([#9178](https://github.com/community-scripts/ProxmoxVE/pull/9178)) ## 2025-11-14 ### 🆕 New Scripts - LibreNMS ([#9148](https://github.com/community-scripts/ProxmoxVE/pull/9148)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - karakeep: clean install after every update [@MickLesk](https://github.com/MickLesk) ([#9144](https://github.com/community-scripts/ProxmoxVE/pull/9144)) - #### ✨ New Features - bump grafana to debian 13 [@mschabhuettl](https://github.com/mschabhuettl) ([#9141](https://github.com/community-scripts/ProxmoxVE/pull/9141)) ## 2025-11-13 ### 🆕 New Scripts - Netvisor ([#9133](https://github.com/community-scripts/ProxmoxVE/pull/9133)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Domain Monitor: Add domain checking cron [@tremor021](https://github.com/tremor021) ([#9129](https://github.com/community-scripts/ProxmoxVE/pull/9129)) - Kimai: Fix for MariaDB connection URL [@tremor021](https://github.com/tremor021) ([#9124](https://github.com/community-scripts/ProxmoxVE/pull/9124)) - Fix: filebrowser-quantum update [@MickLesk](https://github.com/MickLesk) ([#9115](https://github.com/community-scripts/ProxmoxVE/pull/9115)) - tools.func: fix wrong output for setup_java (error token is "0") [@snow2k9](https://github.com/snow2k9) ([#9110](https://github.com/community-scripts/ProxmoxVE/pull/9110)) - #### ✨ New Features - tools.func: improve Rust setup and crate installation logic [@MickLesk](https://github.com/MickLesk) ([#9120](https://github.com/community-scripts/ProxmoxVE/pull/9120)) - #### 💥 Breaking Changes - Remove Barcodebuddy [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#9135](https://github.com/community-scripts/ProxmoxVE/pull/9135)) - Downgrade Swizzin to Debian 12 Bookworm [@MickLesk](https://github.com/MickLesk) ([#9116](https://github.com/community-scripts/ProxmoxVE/pull/9116)) ## 2025-11-12 ### 🆕 New Scripts - Miniflux ([#9091](https://github.com/community-scripts/ProxmoxVE/pull/9091)) - Splunk Enterprise ([#9090](https://github.com/community-scripts/ProxmoxVE/pull/9090)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - evcc: add missing fi in update [@MichaelVetter1979](https://github.com/MichaelVetter1979) ([#9107](https://github.com/community-scripts/ProxmoxVE/pull/9107)) - PeaNUT: use clean install flag during update [@vhsdream](https://github.com/vhsdream) ([#9100](https://github.com/community-scripts/ProxmoxVE/pull/9100)) - Tududi: Create new env file from example; fix installation & update [@vhsdream](https://github.com/vhsdream) ([#9097](https://github.com/community-scripts/ProxmoxVE/pull/9097)) - openwebui: Python version usage | core: zsh completion install [@MickLesk](https://github.com/MickLesk) ([#9079](https://github.com/community-scripts/ProxmoxVE/pull/9079)) - Refactor: evcc [@CrazyWolf13](https://github.com/CrazyWolf13) ([#9057](https://github.com/community-scripts/ProxmoxVE/pull/9057)) - #### ✨ New Features - Bump K to H-Scripts to Debian 13 (Trixie) [@MickLesk](https://github.com/MickLesk) ([#8597](https://github.com/community-scripts/ProxmoxVE/pull/8597)) - #### 🔧 Refactor - Refactor: web-check [@CrazyWolf13](https://github.com/CrazyWolf13) ([#9055](https://github.com/community-scripts/ProxmoxVE/pull/9055)) ### 🌐 Website - Refactor web analytics to use Rybbit instead of Umami [@BramSuurdje](https://github.com/BramSuurdje) ([#9072](https://github.com/community-scripts/ProxmoxVE/pull/9072)) ## 2025-11-11 ### 🆕 New Scripts - Domain-Monitor ([#9029](https://github.com/community-scripts/ProxmoxVE/pull/9029)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - tools.func: fix JDK count variable initialization in setup_java [@MickLesk](https://github.com/MickLesk) ([#9058](https://github.com/community-scripts/ProxmoxVE/pull/9058)) - flaresolverr: unpin - use latest version [@CrazyWolf13](https://github.com/CrazyWolf13) ([#9046](https://github.com/community-scripts/ProxmoxVE/pull/9046)) - Part-DB: Increase amount of RAM [@tremor021](https://github.com/tremor021) ([#9039](https://github.com/community-scripts/ProxmoxVE/pull/9039)) - #### 🔧 Refactor - Refactor: openHAB [@MickLesk](https://github.com/MickLesk) ([#9060](https://github.com/community-scripts/ProxmoxVE/pull/9060)) ### 🧰 Maintenance - #### 📂 Github - [docs / gh]: modernize README | Change Version Support in SECURITY.md | Shoutout to selfhst\icons [@MickLesk](https://github.com/MickLesk) ([#9049](https://github.com/community-scripts/ProxmoxVE/pull/9049)) ## 2025-11-10 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Plex: extend checking for deb822 source [@Matt17000](https://github.com/Matt17000) ([#9036](https://github.com/community-scripts/ProxmoxVE/pull/9036)) - #### ✨ New Features - tools.func: add helper functions for MariaDB and PostgreSQL setup [@MickLesk](https://github.com/MickLesk) ([#9026](https://github.com/community-scripts/ProxmoxVE/pull/9026)) - core: update message for no available updates scenario (if pinned) [@MickLesk](https://github.com/MickLesk) ([#9021](https://github.com/community-scripts/ProxmoxVE/pull/9021)) - Migrate Open WebUI to uv-based installation [@MickLesk](https://github.com/MickLesk) ([#9019](https://github.com/community-scripts/ProxmoxVE/pull/9019)) - #### 🔧 Refactor - Refactor: phpIPAM [@MickLesk](https://github.com/MickLesk) ([#9027](https://github.com/community-scripts/ProxmoxVE/pull/9027)) ## 2025-11-09 ### 🚀 Updated Scripts - core: improve log cleaning [@MickLesk](https://github.com/MickLesk) ([#8999](https://github.com/community-scripts/ProxmoxVE/pull/8999)) - #### 🐞 Bug Fixes - Add wkhtmltopdf to Odoo installation dependencies [@akileos](https://github.com/akileos) ([#9010](https://github.com/community-scripts/ProxmoxVE/pull/9010)) - fix(jotty): Comments removed from variables, as they are interpreted. [@schneider-de-com](https://github.com/schneider-de-com) ([#9002](https://github.com/community-scripts/ProxmoxVE/pull/9002)) - fix(n8n): Add python3-setuptools dependency for Debian 13 [@chrikodo](https://github.com/chrikodo) ([#9007](https://github.com/community-scripts/ProxmoxVE/pull/9007)) - Paperless-ngx: hotfix config path [@vhsdream](https://github.com/vhsdream) ([#9003](https://github.com/community-scripts/ProxmoxVE/pull/9003)) - Paperless-NGX: Move config backup outside of app folder [@vhsdream](https://github.com/vhsdream) ([#8996](https://github.com/community-scripts/ProxmoxVE/pull/8996)) ## 2025-11-08 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Technitium DNS: Fix update [@tremor021](https://github.com/tremor021) ([#8980](https://github.com/community-scripts/ProxmoxVE/pull/8980)) - MediaManager: add LOG_FILE to start.sh script; fix BASE_PATH and PUBLIC_API_URL [@vhsdream](https://github.com/vhsdream) ([#8981](https://github.com/community-scripts/ProxmoxVE/pull/8981)) - Firefly: Fix missing command in update script [@tremor021](https://github.com/tremor021) ([#8972](https://github.com/community-scripts/ProxmoxVE/pull/8972)) - MongoDB: Remove unused message [@tremor021](https://github.com/tremor021) ([#8969](https://github.com/community-scripts/ProxmoxVE/pull/8969)) - Set TZ=Etc/UTC in Ghostfolio installation script [@LuloDev](https://github.com/LuloDev) ([#8961](https://github.com/community-scripts/ProxmoxVE/pull/8961)) - #### 🔧 Refactor - paperless: refactor - remove backup after update and enable clean install [@MickLesk](https://github.com/MickLesk) ([#8988](https://github.com/community-scripts/ProxmoxVE/pull/8988)) - Refactor setup_deb822_repo for optional architectures [@MickLesk](https://github.com/MickLesk) ([#8983](https://github.com/community-scripts/ProxmoxVE/pull/8983)) ## 2025-11-07 ### 🆕 New Scripts - infisical ([#8926](https://github.com/community-scripts/ProxmoxVE/pull/8926)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Update script URLs to ProxmoxVE repository [@MickLesk](https://github.com/MickLesk) ([#8946](https://github.com/community-scripts/ProxmoxVE/pull/8946)) - tools.func: fix amd64 arm64 mismatch [@MickLesk](https://github.com/MickLesk) ([#8943](https://github.com/community-scripts/ProxmoxVE/pull/8943)) - ghostfolio: refactor CoinGecko key prompts in installer [@MickLesk](https://github.com/MickLesk) ([#8935](https://github.com/community-scripts/ProxmoxVE/pull/8935)) - flaresolverr: pin release to 3.4.3 [@CrazyWolf13](https://github.com/CrazyWolf13) ([#8937](https://github.com/community-scripts/ProxmoxVE/pull/8937)) - #### ✨ New Features - Pangolin: Add Traefik proxy [@tremor021](https://github.com/tremor021) ([#8952](https://github.com/community-scripts/ProxmoxVE/pull/8952)) ## 2025-11-06 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - OpenProject: Remove duplicate server_path_prefix configuration [@tremor021](https://github.com/tremor021) ([#8919](https://github.com/community-scripts/ProxmoxVE/pull/8919)) - Grist: Fix change directory to /opt/grist before build steps [@tremor021](https://github.com/tremor021) ([#8913](https://github.com/community-scripts/ProxmoxVE/pull/8913)) - Jotty hotfix: SSO_FALLBACK_LOCAL value [@vhsdream](https://github.com/vhsdream) ([#8907](https://github.com/community-scripts/ProxmoxVE/pull/8907)) - npm: add Debian version check to update script [@MickLesk](https://github.com/MickLesk) ([#8901](https://github.com/community-scripts/ProxmoxVE/pull/8901)) - #### ✨ New Features - MongoDB: install script now use setup_mongodb [@MickLesk](https://github.com/MickLesk) ([#8897](https://github.com/community-scripts/ProxmoxVE/pull/8897)) - #### 🔧 Refactor - Refactor: Graylog [@tremor021](https://github.com/tremor021) ([#8912](https://github.com/community-scripts/ProxmoxVE/pull/8912)) ## 2025-11-05 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Immich: Pin version to 2.2.3 [@vhsdream](https://github.com/vhsdream) ([#8861](https://github.com/community-scripts/ProxmoxVE/pull/8861)) - Jotty: increase RAM to 4GB [@vhsdream](https://github.com/vhsdream) ([#8887](https://github.com/community-scripts/ProxmoxVE/pull/8887)) - Zabbix: fix agent service recognition in update [@MickLesk](https://github.com/MickLesk) ([#8881](https://github.com/community-scripts/ProxmoxVE/pull/8881)) - #### 💥 Breaking Changes - fix: npm: refactor for v2.13.x [@CrazyWolf13](https://github.com/CrazyWolf13) ([#8870](https://github.com/community-scripts/ProxmoxVE/pull/8870)) - #### 🔧 Refactor - Refactor: Open WebUI [@tremor021](https://github.com/tremor021) ([#8874](https://github.com/community-scripts/ProxmoxVE/pull/8874)) - Refactor(tools.func): Add Retry Logic, OS-Upgrade Safety, Smart Version Detection + 10 Critical Bugfixes [@MickLesk](https://github.com/MickLesk) ([#8871](https://github.com/community-scripts/ProxmoxVE/pull/8871)) ### 🌐 Website - #### 📝 Script Information - npm: Increase RAM and HDD, update Certbot notes [@MickLesk](https://github.com/MickLesk) ([#8882](https://github.com/community-scripts/ProxmoxVE/pull/8882)) - Update config_path in donetick.json [@fyxtro](https://github.com/fyxtro) ([#8872](https://github.com/community-scripts/ProxmoxVE/pull/8872)) ## 2025-11-04 ### 🚀 Updated Scripts - #### ✨ New Features - stirling-pdf: add native jbig2 dep to installation script [@MickLesk](https://github.com/MickLesk) ([#8858](https://github.com/community-scripts/ProxmoxVE/pull/8858)) ## 2025-11-03 ### 🆕 New Scripts - Donetick ([#8835](https://github.com/community-scripts/ProxmoxVE/pull/8835)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Immich: Pin version to 2.2.2 [@vhsdream](https://github.com/vhsdream) ([#8848](https://github.com/community-scripts/ProxmoxVE/pull/8848)) - Asterisk: handle errors in version retrieval commands [@MickLesk](https://github.com/MickLesk) ([#8844](https://github.com/community-scripts/ProxmoxVE/pull/8844)) - linkstack: fix wrong directory installation [@omertahaoztop](https://github.com/omertahaoztop) ([#8814](https://github.com/community-scripts/ProxmoxVE/pull/8814)) - Remove BOM from shebang lines in ct scripts [@MickLesk](https://github.com/MickLesk) ([#8833](https://github.com/community-scripts/ProxmoxVE/pull/8833)) - #### 💥 Breaking Changes - Removed: MeTube [@MickLesk](https://github.com/MickLesk) ([#8830](https://github.com/community-scripts/ProxmoxVE/pull/8830)) ## 2025-11-02 ### 🚀 Updated Scripts - Zigbee2MQTT: fix: pnpm workspace in update [@fkroeger](https://github.com/fkroeger) ([#8825](https://github.com/community-scripts/ProxmoxVE/pull/8825)) - #### 🐞 Bug Fixes - Pangolin: Fix install and database migration [@tremor021](https://github.com/tremor021) ([#8828](https://github.com/community-scripts/ProxmoxVE/pull/8828)) - MediaManager: fix BASE_PATH error preventing main page load [@vhsdream](https://github.com/vhsdream) ([#8821](https://github.com/community-scripts/ProxmoxVE/pull/8821)) ## 2025-11-01 ### 🆕 New Scripts - Pangolin ([#8809](https://github.com/community-scripts/ProxmoxVE/pull/8809)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - VictoriaMetrics: Fix release fetching for Victori Logs add-on [@tremor021](https://github.com/tremor021) ([#8807](https://github.com/community-scripts/ProxmoxVE/pull/8807)) - Immich: Pin version to 2.2.1 [@vhsdream](https://github.com/vhsdream) ([#8800](https://github.com/community-scripts/ProxmoxVE/pull/8800)) - jellyfin: fix: initial update [@CrazyWolf13](https://github.com/CrazyWolf13) ([#8784](https://github.com/community-scripts/ProxmoxVE/pull/8784)) ### 🌐 Website - frontend: chore: bump debian OS [@CrazyWolf13](https://github.com/CrazyWolf13) ([#8798](https://github.com/community-scripts/ProxmoxVE/pull/8798)) ================================================ FILE: .github/changelogs/2025/12.md ================================================ ## 2025-12-31 ### 🚀 Updated Scripts - fix(wazuh): add LXC rootcheck exclusion to prevent false positives [@brettlyons](https://github.com/brettlyons) ([#10436](https://github.com/community-scripts/ProxmoxVE/pull/10436)) - #### 🐞 Bug Fixes - Increase BentoPDF RAM requirement from 2GB to 4GB [@Copilot](https://github.com/Copilot) ([#10449](https://github.com/community-scripts/ProxmoxVE/pull/10449)) - fix(swizzin): Use HTTPS and add curl error handling [@fmcglinn](https://github.com/fmcglinn) ([#10440](https://github.com/community-scripts/ProxmoxVE/pull/10440)) ## 2025-12-30 ### 🚀 Updated Scripts - #### ✨ New Features - Unlink default nginx config [@iLikeToCode](https://github.com/iLikeToCode) ([#10432](https://github.com/community-scripts/ProxmoxVE/pull/10432)) - #### 🔧 Refactor - Refactor: Firefly [@tremor021](https://github.com/tremor021) ([#10421](https://github.com/community-scripts/ProxmoxVE/pull/10421)) ### 🗑️ Deleted Scripts - Remove: GoAway [@MickLesk](https://github.com/MickLesk) ([#10429](https://github.com/community-scripts/ProxmoxVE/pull/10429)) ## 2025-12-29 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - syncthing: check for deb822 source [@MickLesk](https://github.com/MickLesk) ([#10414](https://github.com/community-scripts/ProxmoxVE/pull/10414)) - speedtest-tracker: add external IP URL and internet check hostname in .env [@MickLesk](https://github.com/MickLesk) ([#10078](https://github.com/community-scripts/ProxmoxVE/pull/10078)) - Pelican-panel: prevent composer superuser prompt [@MickLesk](https://github.com/MickLesk) ([#10418](https://github.com/community-scripts/ProxmoxVE/pull/10418)) ### 💾 Core - #### 🐞 Bug Fixes - add libmfx-gen1.2 for intel gpu hwaccel [@jcnix](https://github.com/jcnix) ([#10400](https://github.com/community-scripts/ProxmoxVE/pull/10400)) ## 2025-12-28 ### 🆕 New Scripts - Mail-Archiver ([#10393](https://github.com/community-scripts/ProxmoxVE/pull/10393)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Fix mongodb update logic [@durzo](https://github.com/durzo) ([#10388](https://github.com/community-scripts/ProxmoxVE/pull/10388)) - fix pulse downloading incorrect tarball [@durzo](https://github.com/durzo) ([#10383](https://github.com/community-scripts/ProxmoxVE/pull/10383)) - #### 🔧 Refactor - Linkwarden: enable Corepack and prepare Yarn v4 before running yarn [@MickLesk](https://github.com/MickLesk) ([#10390](https://github.com/community-scripts/ProxmoxVE/pull/10390)) - metube: use pnpm + corepack for frontend build [@MickLesk](https://github.com/MickLesk) ([#10392](https://github.com/community-scripts/ProxmoxVE/pull/10392)) ### 💾 Core - #### 🐞 Bug Fixes - Set default LANG in locale configuration [@jamezpolley](https://github.com/jamezpolley) ([#10378](https://github.com/community-scripts/ProxmoxVE/pull/10378)) ### ❔ Uncategorized - Updated Frontend Debian and Ubuntu VM notes so links can be copied quickly. [@mzb2xeo](https://github.com/mzb2xeo) ([#10379](https://github.com/community-scripts/ProxmoxVE/pull/10379)) ## 2025-12-27 ### 🆕 New Scripts - nextcloud-exporter ([#10314](https://github.com/community-scripts/ProxmoxVE/pull/10314)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Dotnet ASP Web API: Fix need for verbose [@tremor021](https://github.com/tremor021) ([#10368](https://github.com/community-scripts/ProxmoxVE/pull/10368)) - Npm: fix build for 2.13.5 [@durzo](https://github.com/durzo) ([#10340](https://github.com/community-scripts/ProxmoxVE/pull/10340)) - Outline: Fix for database connection string [@tremor021](https://github.com/tremor021) ([#10359](https://github.com/community-scripts/ProxmoxVE/pull/10359)) ## 2025-12-26 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - phpipam: use PHP 8.4 with correct mysql module for PDO support [@MickLesk](https://github.com/MickLesk) ([#10348](https://github.com/community-scripts/ProxmoxVE/pull/10348)) - hyperion: increase disk to 4GB and tools.func: fix /root/. path error [@MickLesk](https://github.com/MickLesk) ([#10349](https://github.com/community-scripts/ProxmoxVE/pull/10349)) ### ❔ Uncategorized - fix: zoraxy: category [@CrazyWolf13](https://github.com/CrazyWolf13) ([#10344](https://github.com/community-scripts/ProxmoxVE/pull/10344)) - categorize valkey as database [@pshankinclarke](https://github.com/pshankinclarke) ([#10331](https://github.com/community-scripts/ProxmoxVE/pull/10331)) ## 2025-12-25 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - InfluxDB: Fixes [@tremor021](https://github.com/tremor021) ([#10308](https://github.com/community-scripts/ProxmoxVE/pull/10308)) - Increase Zot Default Memory, Recategorize [@chrismuzyn](https://github.com/chrismuzyn) ([#10311](https://github.com/community-scripts/ProxmoxVE/pull/10311)) - #### 🔧 Refactor - Refactor: OpenObserve [@tremor021](https://github.com/tremor021) ([#10279](https://github.com/community-scripts/ProxmoxVE/pull/10279)) - Refactor: NZBGet [@tremor021](https://github.com/tremor021) ([#10302](https://github.com/community-scripts/ProxmoxVE/pull/10302)) - Refactor: ntfy [@tremor021](https://github.com/tremor021) ([#10303](https://github.com/community-scripts/ProxmoxVE/pull/10303)) - Refactor: Notifiarr [@tremor021](https://github.com/tremor021) ([#10304](https://github.com/community-scripts/ProxmoxVE/pull/10304)) ### 🌐 Website - Fix horizontal scroll on website [@mateossh](https://github.com/mateossh) ([#10317](https://github.com/community-scripts/ProxmoxVE/pull/10317)) ## 2025-12-24 ### 🚀 Updated Scripts - recyclarr: increase cron path [@Uncloak2](https://github.com/Uncloak2) ([#10272](https://github.com/community-scripts/ProxmoxVE/pull/10272)) - #### 🐞 Bug Fixes - fix: technitium: service migration [@CrazyWolf13](https://github.com/CrazyWolf13) ([#10300](https://github.com/community-scripts/ProxmoxVE/pull/10300)) - #### 🔧 Refactor - Overseerr: Update dependencies [@tremor021](https://github.com/tremor021) ([#10275](https://github.com/community-scripts/ProxmoxVE/pull/10275)) - Refactor: Paperless-GPT [@tremor021](https://github.com/tremor021) ([#10274](https://github.com/community-scripts/ProxmoxVE/pull/10274)) - Refactor: Outline [@tremor021](https://github.com/tremor021) ([#10276](https://github.com/community-scripts/ProxmoxVE/pull/10276)) - Refactor: OTS [@tremor021](https://github.com/tremor021) ([#10277](https://github.com/community-scripts/ProxmoxVE/pull/10277)) - Refactor: OpenProject [@tremor021](https://github.com/tremor021) ([#10278](https://github.com/community-scripts/ProxmoxVE/pull/10278)) - Refactor: Open Archiver [@tremor021](https://github.com/tremor021) ([#10280](https://github.com/community-scripts/ProxmoxVE/pull/10280)) - Refactor: Tautulli [@tremor021](https://github.com/tremor021) ([#10241](https://github.com/community-scripts/ProxmoxVE/pull/10241)) - Refactor: PrivateBin [@tremor021](https://github.com/tremor021) ([#10256](https://github.com/community-scripts/ProxmoxVE/pull/10256)) - Refactor: Podman-Home Assistant [@tremor021](https://github.com/tremor021) ([#10258](https://github.com/community-scripts/ProxmoxVE/pull/10258)) - Refactor: Plant-it [@tremor021](https://github.com/tremor021) ([#10259](https://github.com/community-scripts/ProxmoxVE/pull/10259)) - Refactor: PatchMon [@tremor021](https://github.com/tremor021) ([#10260](https://github.com/community-scripts/ProxmoxVE/pull/10260)) - Refactor: Part-DB [@tremor021](https://github.com/tremor021) ([#10262](https://github.com/community-scripts/ProxmoxVE/pull/10262)) ### 💾 Core - #### 🐞 Bug Fixes - core: correct local template discovery regex pattern [@MickLesk](https://github.com/MickLesk) ([#10282](https://github.com/community-scripts/ProxmoxVE/pull/10282)) ### 🧰 Tools - #### 🐞 Bug Fixes - pihole-exporter fix: unbound var [@CrazyWolf13](https://github.com/CrazyWolf13) ([#10307](https://github.com/community-scripts/ProxmoxVE/pull/10307)) ### ❔ Uncategorized - Pocketbase: Add note for superuser account creation [@tremor021](https://github.com/tremor021) ([#10245](https://github.com/community-scripts/ProxmoxVE/pull/10245)) ## 2025-12-23 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Technitium DNS: Migrate service [@tremor021](https://github.com/tremor021) ([#10240](https://github.com/community-scripts/ProxmoxVE/pull/10240)) - Update forgejo to debian13 and fix env var [@burgerga](https://github.com/burgerga) ([#10242](https://github.com/community-scripts/ProxmoxVE/pull/10242)) - #### 🔧 Refactor - Passbolt: Small fixes [@tremor021](https://github.com/tremor021) ([#10261](https://github.com/community-scripts/ProxmoxVE/pull/10261)) - Refactor: ProjectSend [@tremor021](https://github.com/tremor021) ([#10255](https://github.com/community-scripts/ProxmoxVE/pull/10255)) - Prometheus Paperless NGX Exporter: Small fix [@tremor021](https://github.com/tremor021) ([#10254](https://github.com/community-scripts/ProxmoxVE/pull/10254)) - Podman: Fixes [@tremor021](https://github.com/tremor021) ([#10257](https://github.com/community-scripts/ProxmoxVE/pull/10257)) - Refactor: Beszel [@tremor021](https://github.com/tremor021) ([#10195](https://github.com/community-scripts/ProxmoxVE/pull/10195)) ### 🧰 Tools - #### 🐞 Bug Fixes - fix: pihole-exporter: unknown function [@CrazyWolf13](https://github.com/CrazyWolf13) ([#10249](https://github.com/community-scripts/ProxmoxVE/pull/10249)) ### ❔ Uncategorized - Fix Recyclarr page TypeError: schema mismatch in notes field [@Copilot](https://github.com/Copilot) ([#10253](https://github.com/community-scripts/ProxmoxVE/pull/10253)) ## 2025-12-22 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - InvoiceNinja: add chromium dependencies for PDF generation [@MickLesk](https://github.com/MickLesk) ([#10230](https://github.com/community-scripts/ProxmoxVE/pull/10230)) - MediaManager) use npm install [@MickLesk](https://github.com/MickLesk) ([#10228](https://github.com/community-scripts/ProxmoxVE/pull/10228)) - Kometa: Fix update procedure [@tremor021](https://github.com/tremor021) ([#10217](https://github.com/community-scripts/ProxmoxVE/pull/10217)) - #### 💥 Breaking Changes - refactor: reitti: v3.0.0 [@CrazyWolf13](https://github.com/CrazyWolf13) ([#10196](https://github.com/community-scripts/ProxmoxVE/pull/10196)) ### 💾 Core - #### ✨ New Features - tools.func - hwaccel: skip setup without GPU passthrough and fix Ubuntu AMD firmware [@MickLesk](https://github.com/MickLesk) ([#10225](https://github.com/community-scripts/ProxmoxVE/pull/10225)) ### 📚 Documentation - contribution docs: update templates with modern patterns [@MickLesk](https://github.com/MickLesk) ([#10227](https://github.com/community-scripts/ProxmoxVE/pull/10227)) ### ❔ Uncategorized - InvoiceNinja: switch category [@DragoQC](https://github.com/DragoQC) ([#10223](https://github.com/community-scripts/ProxmoxVE/pull/10223)) ## 2025-12-21 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Typo fix in Heimdall install script [@Turcid-uwu](https://github.com/Turcid-uwu) ([#10187](https://github.com/community-scripts/ProxmoxVE/pull/10187)) - #### ✨ New Features - recyclarr: add default daily cron job for recyclarr sync [@MickLesk](https://github.com/MickLesk) ([#10208](https://github.com/community-scripts/ProxmoxVE/pull/10208)) - #### 🔧 Refactor - Optimize Jotty installation with standalone mode [@MickLesk](https://github.com/MickLesk) ([#10207](https://github.com/community-scripts/ProxmoxVE/pull/10207)) - unifi: remove mongodb 4.4 support | bump to java 21 [@MickLesk](https://github.com/MickLesk) ([#10206](https://github.com/community-scripts/ProxmoxVE/pull/10206)) - Refactor: Backrest [@tremor021](https://github.com/tremor021) ([#10193](https://github.com/community-scripts/ProxmoxVE/pull/10193)) ### 💾 Core - #### ✨ New Features - Fix AMD GPU firmware installation by adding non-free repositories [@MickLesk](https://github.com/MickLesk) ([#10205](https://github.com/community-scripts/ProxmoxVE/pull/10205)) ### 🧰 Tools - pihole-exporter ([#10091](https://github.com/community-scripts/ProxmoxVE/pull/10091)) ## 2025-12-20 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Update Technitium DNS and Restart Service [@DrEVILish](https://github.com/DrEVILish) ([#10181](https://github.com/community-scripts/ProxmoxVE/pull/10181)) - bump: ersatztv: deb13 [@CrazyWolf13](https://github.com/CrazyWolf13) ([#10174](https://github.com/community-scripts/ProxmoxVE/pull/10174)) ## 2025-12-19 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Update Reitti to Java 25 for 3.0.0 compatibility [@Copilot](https://github.com/Copilot) ([#10164](https://github.com/community-scripts/ProxmoxVE/pull/10164)) - Bump Bar-Assistant to php 8.4 [@MickLesk](https://github.com/MickLesk) ([#10138](https://github.com/community-scripts/ProxmoxVE/pull/10138)) - Zabbix: Add version-specific SQL script path for 7.0 LTS [@MickLesk](https://github.com/MickLesk) ([#10142](https://github.com/community-scripts/ProxmoxVE/pull/10142)) - InfluxDB: Fix update function [@Liganic](https://github.com/Liganic) ([#10151](https://github.com/community-scripts/ProxmoxVE/pull/10151)) - #### ✨ New Features - Bump Immich to v2.4.1 [@vhsdream](https://github.com/vhsdream) ([#10154](https://github.com/community-scripts/ProxmoxVE/pull/10154)) - #### 🔧 Refactor - Refactor: Cosmos: + Upgrade to Debian 13 [@MickLesk](https://github.com/MickLesk) ([#10147](https://github.com/community-scripts/ProxmoxVE/pull/10147)) - Refactor: Proxmox-Mail-Gateway [@tremor021](https://github.com/tremor021) ([#10070](https://github.com/community-scripts/ProxmoxVE/pull/10070)) ### 💾 Core - #### ✨ New Features - core: Auto-cleanup after all update_script executions [@MickLesk](https://github.com/MickLesk) ([#10141](https://github.com/community-scripts/ProxmoxVE/pull/10141)) ### 🧰 Tools - #### 🔧 Refactor - fix: removed verbose option to avoid unnecessary output [@wolle604](https://github.com/wolle604) ([#10144](https://github.com/community-scripts/ProxmoxVE/pull/10144)) ### ❔ Uncategorized - Update paymenter.json(#10133) [@DragoQC](https://github.com/DragoQC) ([#10134](https://github.com/community-scripts/ProxmoxVE/pull/10134)) ## 2025-12-18 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - [HOTFIX] Fix Scanopy release check [@vhsdream](https://github.com/vhsdream) ([#10097](https://github.com/community-scripts/ProxmoxVE/pull/10097)) - Fix cleanup issues in npm cache and rustup toolchain [@MickLesk](https://github.com/MickLesk) ([#10107](https://github.com/community-scripts/ProxmoxVE/pull/10107)) - Fix Zabbix 7.0 repository URL structure [@MickLesk](https://github.com/MickLesk) ([#10106](https://github.com/community-scripts/ProxmoxVE/pull/10106)) - #### ✨ New Features - bump pihole to debian 13 [@mschabhuettl](https://github.com/mschabhuettl) ([#10118](https://github.com/community-scripts/ProxmoxVE/pull/10118)) - Immich: v2.4.0 [@vhsdream](https://github.com/vhsdream) ([#10095](https://github.com/community-scripts/ProxmoxVE/pull/10095)) ### 💾 Core - #### 🔧 Refactor - tools.func: hardening/Improve error handling and cleanup in shell functions [@MickLesk](https://github.com/MickLesk) ([#10116](https://github.com/community-scripts/ProxmoxVE/pull/10116)) ### 🧰 Tools - qbittorrent-exporter ([#10090](https://github.com/community-scripts/ProxmoxVE/pull/10090)) - #### 🐞 Bug Fixes - Improved error handling when a command does not exist [@wolle604](https://github.com/wolle604) ([#10089](https://github.com/community-scripts/ProxmoxVE/pull/10089)) ## 2025-12-17 ### 🚀 Updated Scripts - Tracktor: updated environment variables for latest release [@javedh-dev](https://github.com/javedh-dev) ([#10067](https://github.com/community-scripts/ProxmoxVE/pull/10067)) - #### 🐞 Bug Fixes - Semaphore: Fix release binary package fetching [@tremor021](https://github.com/tremor021) ([#10055](https://github.com/community-scripts/ProxmoxVE/pull/10055)) - update github repo for endurain [@johanngrobe](https://github.com/johanngrobe) ([#10074](https://github.com/community-scripts/ProxmoxVE/pull/10074)) - #### ✨ New Features - use setup_hwaccel for robust hardware acceleration [@MickLesk](https://github.com/MickLesk) ([#10054](https://github.com/community-scripts/ProxmoxVE/pull/10054)) - add hardware acceleration support for 17 additional apps [@MickLesk](https://github.com/MickLesk) ([#10061](https://github.com/community-scripts/ProxmoxVE/pull/10061)) - #### 🔧 Refactor - Telegraf: Small refactor [@tremor021](https://github.com/tremor021) ([#10056](https://github.com/community-scripts/ProxmoxVE/pull/10056)) - Refactor: Salt [@tremor021](https://github.com/tremor021) ([#10057](https://github.com/community-scripts/ProxmoxVE/pull/10057)) - Refactor: Resilio Sync [@tremor021](https://github.com/tremor021) ([#10058](https://github.com/community-scripts/ProxmoxVE/pull/10058)) - Refactor: Reitti [@tremor021](https://github.com/tremor021) ([#10059](https://github.com/community-scripts/ProxmoxVE/pull/10059)) - Refactor: Redis [@tremor021](https://github.com/tremor021) ([#10060](https://github.com/community-scripts/ProxmoxVE/pull/10060)) - Refactor: Reactive-Resume [@tremor021](https://github.com/tremor021) ([#10062](https://github.com/community-scripts/ProxmoxVE/pull/10062)) - Refactor: RDTClient [@tremor021](https://github.com/tremor021) ([#10064](https://github.com/community-scripts/ProxmoxVE/pull/10064)) - Refactor: RabbitMQ [@tremor021](https://github.com/tremor021) ([#10065](https://github.com/community-scripts/ProxmoxVE/pull/10065)) - Qdrant: Code cleanup [@tremor021](https://github.com/tremor021) ([#10066](https://github.com/community-scripts/ProxmoxVE/pull/10066)) - Refactor: Pterodactyl Wings [@tremor021](https://github.com/tremor021) ([#10069](https://github.com/community-scripts/ProxmoxVE/pull/10069)) ## 2025-12-16 ### 🆕 New Scripts - [REFACTOR]: NetVisor => Scanopy [@vhsdream](https://github.com/vhsdream) ([#10011](https://github.com/community-scripts/ProxmoxVE/pull/10011)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - zabbix: fix repo url after change [@MickLesk](https://github.com/MickLesk) ([#10042](https://github.com/community-scripts/ProxmoxVE/pull/10042)) - Fix: mariadb repo in update_scripts [@MickLesk](https://github.com/MickLesk) ([#10034](https://github.com/community-scripts/ProxmoxVE/pull/10034)) - 2fauth: update PHP version from 8.3 to 8.4 in update_script [@MickLesk](https://github.com/MickLesk) ([#10035](https://github.com/community-scripts/ProxmoxVE/pull/10035)) - pdm: add rsyslog to fix /dev/log Connection refused errors [@MickLesk](https://github.com/MickLesk) ([#10018](https://github.com/community-scripts/ProxmoxVE/pull/10018)) - 2fauth: bump to php8.4 [@MickLesk](https://github.com/MickLesk) ([#10019](https://github.com/community-scripts/ProxmoxVE/pull/10019)) - Miniflux: use correct systemctl to check service instead of file path [@MickLesk](https://github.com/MickLesk) ([#10024](https://github.com/community-scripts/ProxmoxVE/pull/10024)) - PhotoPrism: export env variables for CLI tools [@MickLesk](https://github.com/MickLesk) ([#10023](https://github.com/community-scripts/ProxmoxVE/pull/10023)) ### 💾 Core - #### ✨ New Features - core: IP-Range-Scan Support (app.vars / default.vars) [@MickLesk](https://github.com/MickLesk) ([#10038](https://github.com/community-scripts/ProxmoxVE/pull/10038)) - tools.func: add optional enabled parameter to setup_deb822_repo [@MickLesk](https://github.com/MickLesk) ([#10017](https://github.com/community-scripts/ProxmoxVE/pull/10017)) - core: map Etc/* timezones to 'host' for pct compatibility [@MickLesk](https://github.com/MickLesk) ([#10020](https://github.com/community-scripts/ProxmoxVE/pull/10020)) ### 🌐 Website - website: bump deps & prevent security issues [@MickLesk](https://github.com/MickLesk) ([#10045](https://github.com/community-scripts/ProxmoxVE/pull/10045)) ## 2025-12-15 ### 🆕 New Scripts - Koel ([#9972](https://github.com/community-scripts/ProxmoxVE/pull/9972)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Fix DiscoPanel build [@PouletteMC](https://github.com/PouletteMC) ([#10009](https://github.com/community-scripts/ProxmoxVE/pull/10009)) - fix:ct/openwebui.sh adding progressbar and minimize service downtime [@jobben-2025](https://github.com/jobben-2025) ([#9894](https://github.com/community-scripts/ProxmoxVE/pull/9894)) - homarr: add: temp note aboute deb13 requirement [@CrazyWolf13](https://github.com/CrazyWolf13) ([#9992](https://github.com/community-scripts/ProxmoxVE/pull/9992)) - paperless-ai: backup data and recreate venv during update [@MickLesk](https://github.com/MickLesk) ([#9987](https://github.com/community-scripts/ProxmoxVE/pull/9987)) - fix(booklore): add setup_yq to update script [@MickLesk](https://github.com/MickLesk) ([#9989](https://github.com/community-scripts/ProxmoxVE/pull/9989)) - fix(pangolin-install): add network-online dependency [@worried-networking](https://github.com/worried-networking) ([#9984](https://github.com/community-scripts/ProxmoxVE/pull/9984)) - #### ✨ New Features - OPNsense: dynamic crawl latest stable FreeBSD [@austindsmith](https://github.com/austindsmith) ([#9831](https://github.com/community-scripts/ProxmoxVE/pull/9831)) - #### 🔧 Refactor - Refactor: Heimdall Dashboard [@tremor021](https://github.com/tremor021) ([#9959](https://github.com/community-scripts/ProxmoxVE/pull/9959)) ### 💾 Core - #### 🐞 Bug Fixes - tools: prevent awk errors in setup_rust on restricted containers [@MickLesk](https://github.com/MickLesk) ([#9985](https://github.com/community-scripts/ProxmoxVE/pull/9985)) - core: App Defaults force mode and prevent unbound variables [@MickLesk](https://github.com/MickLesk) ([#9971](https://github.com/community-scripts/ProxmoxVE/pull/9971)) - core: load app defaults before applying base_settings / fix composer cleanup after install/update [@MickLesk](https://github.com/MickLesk) ([#9965](https://github.com/community-scripts/ProxmoxVE/pull/9965)) - #### ✨ New Features - tools: handle flat repositories in setup_deb822_repo [@MickLesk](https://github.com/MickLesk) ([#9994](https://github.com/community-scripts/ProxmoxVE/pull/9994)) ### 📚 Documentation - (github) remove old files and assets [@MickLesk](https://github.com/MickLesk) ([#9991](https://github.com/community-scripts/ProxmoxVE/pull/9991)) - README; add project statistics / formatting [@MickLesk](https://github.com/MickLesk) ([#9967](https://github.com/community-scripts/ProxmoxVE/pull/9967)) ## 2025-12-14 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - SonarQube: Fix database variables [@tremor021](https://github.com/tremor021) ([#9946](https://github.com/community-scripts/ProxmoxVE/pull/9946)) - #### 💥 Breaking Changes - refactor: homarr [@CrazyWolf13](https://github.com/CrazyWolf13) ([#9948](https://github.com/community-scripts/ProxmoxVE/pull/9948)) ### 🌐 Website - Update dependencies and remove unused files [@BramSuurdje](https://github.com/BramSuurdje) ([#9945](https://github.com/community-scripts/ProxmoxVE/pull/9945)) ## 2025-12-13 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Umami: Use `pnpm` [@tremor021](https://github.com/tremor021) ([#9937](https://github.com/community-scripts/ProxmoxVE/pull/9937)) - Tunarr: Switch to prebuild archive [@tremor021](https://github.com/tremor021) ([#9920](https://github.com/community-scripts/ProxmoxVE/pull/9920)) - [HOTFIX] NetVisor: backup OIDC config before update [@vhsdream](https://github.com/vhsdream) ([#9895](https://github.com/community-scripts/ProxmoxVE/pull/9895)) - Update OPNsense download URL to version 14.3 [@jaredcarling42-design](https://github.com/jaredcarling42-design) ([#9899](https://github.com/community-scripts/ProxmoxVE/pull/9899)) - #### ✨ New Features - Add optional TLS setup to Valkey installer [@pshankinclarke](https://github.com/pshankinclarke) ([#9789](https://github.com/community-scripts/ProxmoxVE/pull/9789)) - #### 🔧 Refactor - Refactor: Spoolman [@tremor021](https://github.com/tremor021) ([#9873](https://github.com/community-scripts/ProxmoxVE/pull/9873)) ### 🧰 Tools - AdGuardHome-Sync ([#9783](https://github.com/community-scripts/ProxmoxVE/pull/9783)) ### ❔ Uncategorized - Update category value in glance.json and adguard-home.json [@Bensonheimer992](https://github.com/Bensonheimer992) ([#9932](https://github.com/community-scripts/ProxmoxVE/pull/9932)) - Change category ID from 6 to 3 in coolify.json and dokploy.json [@Bensonheimer992](https://github.com/Bensonheimer992) ([#9930](https://github.com/community-scripts/ProxmoxVE/pull/9930)) ## 2025-12-12 ### 🆕 New Scripts - Wallabag ([#9904](https://github.com/community-scripts/ProxmoxVE/pull/9904)) - InvoiceNinja ([#9905](https://github.com/community-scripts/ProxmoxVE/pull/9905)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Pangolin: URL fixes [@tremor021](https://github.com/tremor021) ([#9902](https://github.com/community-scripts/ProxmoxVE/pull/9902)) ## 2025-12-11 ### 🆕 New Scripts - Speedtest-Tracker ([#9802](https://github.com/community-scripts/ProxmoxVE/pull/9802)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - dokploy: require unprivileged LXC environment [@MickLesk](https://github.com/MickLesk) ([#9891](https://github.com/community-scripts/ProxmoxVE/pull/9891)) - Update NetVisor repo information [@vhsdream](https://github.com/vhsdream) ([#9864](https://github.com/community-scripts/ProxmoxVE/pull/9864)) - #### 🔧 Refactor - Syncthing: Various fixes [@tremor021](https://github.com/tremor021) ([#9872](https://github.com/community-scripts/ProxmoxVE/pull/9872)) - Sonarr: Fix standard [@tremor021](https://github.com/tremor021) ([#9874](https://github.com/community-scripts/ProxmoxVE/pull/9874)) - Refactor: Snipe-IT [@tremor021](https://github.com/tremor021) ([#9876](https://github.com/community-scripts/ProxmoxVE/pull/9876)) - Technitium DNS: Various fixes [@tremor021](https://github.com/tremor021) ([#9863](https://github.com/community-scripts/ProxmoxVE/pull/9863)) - SonarQube: Fixes [@tremor021](https://github.com/tremor021) ([#9875](https://github.com/community-scripts/ProxmoxVE/pull/9875)) - endurain: remove unneeded deps [@johanngrobe](https://github.com/johanngrobe) ([#9855](https://github.com/community-scripts/ProxmoxVE/pull/9855)) ### 💾 Core - #### 🐞 Bug Fixes - core: skip -features flag when empty [@MickLesk](https://github.com/MickLesk) ([#9871](https://github.com/community-scripts/ProxmoxVE/pull/9871)) ### 🌐 Website - #### 📝 Script Information - paperless: add note on website (uv usage) [@MickLesk](https://github.com/MickLesk) ([#9833](https://github.com/community-scripts/ProxmoxVE/pull/9833)) ## 2025-12-10 ### 🆕 New Scripts - DiscoPanel ([#9847](https://github.com/community-scripts/ProxmoxVE/pull/9847)) ### 🚀 Updated Scripts - #### 🔧 Refactor - Refactor: UmlautAdaptarr [@tremor021](https://github.com/tremor021) ([#9839](https://github.com/community-scripts/ProxmoxVE/pull/9839)) - Verdaccio: Small fixes [@tremor021](https://github.com/tremor021) ([#9836](https://github.com/community-scripts/ProxmoxVE/pull/9836)) - Refactor: WaveLog [@tremor021](https://github.com/tremor021) ([#9835](https://github.com/community-scripts/ProxmoxVE/pull/9835)) - Refactor: Unifi Network Server [@tremor021](https://github.com/tremor021) ([#9838](https://github.com/community-scripts/ProxmoxVE/pull/9838)) - Refactor: Umami [@tremor021](https://github.com/tremor021) ([#9840](https://github.com/community-scripts/ProxmoxVE/pull/9840)) - Refactor: UrBackup Server [@tremor021](https://github.com/tremor021) ([#9837](https://github.com/community-scripts/ProxmoxVE/pull/9837)) - Refactor: Tianji [@tremor021](https://github.com/tremor021) ([#9842](https://github.com/community-scripts/ProxmoxVE/pull/9842)) - Tracktor: Remove unused variable [@tremor021](https://github.com/tremor021) ([#9841](https://github.com/community-scripts/ProxmoxVE/pull/9841)) ### ❔ Uncategorized - Update icon URLs from master to main branch [@MickLesk](https://github.com/MickLesk) ([#9834](https://github.com/community-scripts/ProxmoxVE/pull/9834)) ## 2025-12-09 ### 🆕 New Scripts - Dokploy ([#9793](https://github.com/community-scripts/ProxmoxVE/pull/9793)) - Coolify ([#9792](https://github.com/community-scripts/ProxmoxVE/pull/9792)) ### 🚀 Updated Scripts - #### ✨ New Features - Refactor: Zerotier-One [@tremor021](https://github.com/tremor021) ([#9804](https://github.com/community-scripts/ProxmoxVE/pull/9804)) - Refactor: Zabbix [@tremor021](https://github.com/tremor021) ([#9807](https://github.com/community-scripts/ProxmoxVE/pull/9807)) - #### 🔧 Refactor - Refactor: Zigbee2MQTT [@tremor021](https://github.com/tremor021) ([#9803](https://github.com/community-scripts/ProxmoxVE/pull/9803)) - Refactor: Wordpress [@tremor021](https://github.com/tremor021) ([#9808](https://github.com/community-scripts/ProxmoxVE/pull/9808)) - Wizarr: Various fixes [@tremor021](https://github.com/tremor021) ([#9809](https://github.com/community-scripts/ProxmoxVE/pull/9809)) - Refactor: Wiki.js [@tremor021](https://github.com/tremor021) ([#9810](https://github.com/community-scripts/ProxmoxVE/pull/9810)) - Zammad: Various fixes [@tremor021](https://github.com/tremor021) ([#9805](https://github.com/community-scripts/ProxmoxVE/pull/9805)) - Refactor: Zipline [@tremor021](https://github.com/tremor021) ([#9801](https://github.com/community-scripts/ProxmoxVE/pull/9801)) ### 💾 Core - #### 🐞 Bug Fixes - fix(tools): handle repos with 30+ pre-releases in check_for_gh_release [@vidonnus](https://github.com/vidonnus) ([#9786](https://github.com/community-scripts/ProxmoxVE/pull/9786)) - #### ✨ New Features - Feature: extend advanced settings with more options & inherit app defaults [@MickLesk](https://github.com/MickLesk) ([#9776](https://github.com/community-scripts/ProxmoxVE/pull/9776)) ### 📚 Documentation - website: fix/check updateable flags [@MickLesk](https://github.com/MickLesk) ([#9777](https://github.com/community-scripts/ProxmoxVE/pull/9777)) - fixed grammar on alert that pops up when you copy the curl command [@Sarthak-Sidhant](https://github.com/Sarthak-Sidhant) ([#9799](https://github.com/community-scripts/ProxmoxVE/pull/9799)) ### ❔ Uncategorized - Website: Remove Palmr script [@tremor021](https://github.com/tremor021) ([#9824](https://github.com/community-scripts/ProxmoxVE/pull/9824)) ## 2025-12-08 ### 🚀 Updated Scripts - typo: tandoor instead of trandoor [@Neonize](https://github.com/Neonize) ([#9771](https://github.com/community-scripts/ProxmoxVE/pull/9771)) - #### 🐞 Bug Fixes - Tandoor: Remove postgres17-contrib package [@tremor021](https://github.com/tremor021) ([#9781](https://github.com/community-scripts/ProxmoxVE/pull/9781)) - #### ✨ New Features - feat: Add var_gpu flag for GPU passthrough configuration [@MickLesk](https://github.com/MickLesk) ([#9764](https://github.com/community-scripts/ProxmoxVE/pull/9764)) ### 💾 Core - #### 🐞 Bug Fixes - fix: always show SSH access dialog in advanced settings [@MickLesk](https://github.com/MickLesk) ([#9765](https://github.com/community-scripts/ProxmoxVE/pull/9765)) ## 2025-12-07 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - wanderer: add meilisearch dumpless upgrade for database migration [@MickLesk](https://github.com/MickLesk) ([#9749](https://github.com/community-scripts/ProxmoxVE/pull/9749)) - #### 💥 Breaking Changes - Refactor: Inventree (uses now ubuntu 24.04) [@MickLesk](https://github.com/MickLesk) ([#9752](https://github.com/community-scripts/ProxmoxVE/pull/9752)) - Revert Zammad: use Debian 12 and dynamic APT source version [@MickLesk](https://github.com/MickLesk) ([#9750](https://github.com/community-scripts/ProxmoxVE/pull/9750)) ### 💾 Core - #### 🐞 Bug Fixes - tools.func: handle empty grep results in stop_all_services [@MickLesk](https://github.com/MickLesk) ([#9748](https://github.com/community-scripts/ProxmoxVE/pull/9748)) - Remove Debian from GPU passthrough [@MickLesk](https://github.com/MickLesk) ([#9754](https://github.com/community-scripts/ProxmoxVE/pull/9754)) - #### ✨ New Features - core: motd - dynamically read OS version on each login [@MickLesk](https://github.com/MickLesk) ([#9751](https://github.com/community-scripts/ProxmoxVE/pull/9751)) ### 🌐 Website - FAQ update [@tremor021](https://github.com/tremor021) ([#9742](https://github.com/community-scripts/ProxmoxVE/pull/9742)) ## 2025-12-06 ### 🚀 Updated Scripts - Update domain-locker-install.sh to enable auto-start after reboot [@alexindigo](https://github.com/alexindigo) ([#9715](https://github.com/community-scripts/ProxmoxVE/pull/9715)) - #### 🐞 Bug Fixes - InfluxDB: Remove InfluxData source list post-installation [@tremor021](https://github.com/tremor021) ([#9723](https://github.com/community-scripts/ProxmoxVE/pull/9723)) - InfluxDB: Update InfluxDB repository key URL [@tremor021](https://github.com/tremor021) ([#9720](https://github.com/community-scripts/ProxmoxVE/pull/9720)) - #### ✨ New Features - pin Portainer Update to CE Version only [@sgaert](https://github.com/sgaert) ([#9710](https://github.com/community-scripts/ProxmoxVE/pull/9710)) ## 2025-12-05 ### 🆕 New Scripts - Endurain ([#9681](https://github.com/community-scripts/ProxmoxVE/pull/9681)) - MeTube ([#9671](https://github.com/community-scripts/ProxmoxVE/pull/9671)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - libretranslate: pin uv python to 3.12 (pytorch fix) [@MickLesk](https://github.com/MickLesk) ([#9699](https://github.com/community-scripts/ProxmoxVE/pull/9699)) - alpine: (mariadb/postgresql): correct php-cgi path for php83 (adminer) [@MickLesk](https://github.com/MickLesk) ([#9698](https://github.com/community-scripts/ProxmoxVE/pull/9698)) - fix(librespeed-rs): use correct service name [@jniles](https://github.com/jniles) ([#9683](https://github.com/community-scripts/ProxmoxVE/pull/9683)) - NetVisor: fix daemon auto-config [@vhsdream](https://github.com/vhsdream) ([#9682](https://github.com/community-scripts/ProxmoxVE/pull/9682)) - Improve NVIDIA device detection for container passthrough [@MickLesk](https://github.com/MickLesk) ([#9670](https://github.com/community-scripts/ProxmoxVE/pull/9670)) - Fix AdventureLog installation failure: missing postgis extension permissions [@Copilot](https://github.com/Copilot) ([#9674](https://github.com/community-scripts/ProxmoxVE/pull/9674)) - paperless: ASGI interface typo [@MickLesk](https://github.com/MickLesk) ([#9668](https://github.com/community-scripts/ProxmoxVE/pull/9668)) - var. core fixes (bash to sh in fix_gpu_gids ...) [@MickLesk](https://github.com/MickLesk) ([#9666](https://github.com/community-scripts/ProxmoxVE/pull/9666)) - #### ✨ New Features - tools.func: handle GitHub 300 Multiple Choices in tarball mode [@MickLesk](https://github.com/MickLesk) ([#9697](https://github.com/community-scripts/ProxmoxVE/pull/9697)) - #### 🔧 Refactor - Refactor: OneDev [@MickLesk](https://github.com/MickLesk) ([#9597](https://github.com/community-scripts/ProxmoxVE/pull/9597)) ### 📂 Github - chore(github): improve PR template and cleanup obsolete references | move contribution guide [@MickLesk](https://github.com/MickLesk) ([#9700](https://github.com/community-scripts/ProxmoxVE/pull/9700)) ## 2025-12-04 ### 🛠️ Core Overhaul - Major refactor of the entire `/misc` subsystem introducing a secure, modular and fully extensible foundation for all future scripts. Includes the new three-tier defaults architecture (ENV → App → User), strict variable whitelisting, safe `.vars` parsing without `source/eval`, centralized `error_handler.func`, structured logging, an improved 19-step advanced wizard, unified container creation, dedicated storage selector, updated sysctl handling, IPv6 disable mode, cloud-init library, SSH key auto-discovery, and a complete cleanup of legacy components. Documentation added under `/docs/guides`. [@MickLesk](https://github.com/MickLesk) ([#9540](https://github.com/community-scripts/ProxmoxVE/pull/9540)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Fix kimai.sh update script path typo for local.yaml [@Copilot](https://github.com/Copilot) ([#9645](https://github.com/community-scripts/ProxmoxVE/pull/9645)) - #### ✨ New Features - core: extend storage type support (rbd, nfs, cifs) and validation (iscidirect, isci, zfs, cephfs, pbs) [@MickLesk](https://github.com/MickLesk) ([#9646](https://github.com/community-scripts/ProxmoxVE/pull/9646)) - #### 🔧 Refactor - update pdm repo to stable [@CrazyWolf13](https://github.com/CrazyWolf13) ([#9648](https://github.com/community-scripts/ProxmoxVE/pull/9648)) ## 2025-12-03 ### 🚀 Updated Scripts - fix(opnsense-vm): improve script and add single-interface mode [@AlphaLawless](https://github.com/AlphaLawless) ([#9614](https://github.com/community-scripts/ProxmoxVE/pull/9614)) - #### 🐞 Bug Fixes - Fix Homebridge update detection for Debian 13 DEB822 format [@Copilot](https://github.com/Copilot) ([#9629](https://github.com/community-scripts/ProxmoxVE/pull/9629)) - go2rtc: Add WorkingDirectory to go2rtc service configuration [@tremor021](https://github.com/tremor021) ([#9618](https://github.com/community-scripts/ProxmoxVE/pull/9618)) - #### 🔧 Refactor - explicit node versions [@CrazyWolf13](https://github.com/CrazyWolf13) ([#9594](https://github.com/community-scripts/ProxmoxVE/pull/9594)) ### 🌐 Website - Bump next from 15.5.2 to 15.5.7 in /frontend in the npm_and_yarn group across 1 directory [@dependabot[bot]](https://github.com/dependabot[bot]) ([#9632](https://github.com/community-scripts/ProxmoxVE/pull/9632)) - #### 📝 Script Information - Update logo URL in swizzin.json [@MickLesk](https://github.com/MickLesk) ([#9627](https://github.com/community-scripts/ProxmoxVE/pull/9627)) ## 2025-12-02 ### 🆕 New Scripts - Snowshare ([#9578](https://github.com/community-scripts/ProxmoxVE/pull/9578)) ### 🚀 Updated Scripts - NetVisor: patch systemd file to fix new OIDC config [@vhsdream](https://github.com/vhsdream) ([#9562](https://github.com/community-scripts/ProxmoxVE/pull/9562)) - Refactor: BookStack [@tremor021](https://github.com/tremor021) ([#9567](https://github.com/community-scripts/ProxmoxVE/pull/9567)) - #### 🐞 Bug Fixes - Matterbridge: Fix ExecStart command in service install script to allow childbridge mode [@jonalbr](https://github.com/jonalbr) ([#9603](https://github.com/community-scripts/ProxmoxVE/pull/9603)) - Open-webui add .env backup and restore functionality from older versions [@DrDonoso](https://github.com/DrDonoso) ([#9592](https://github.com/community-scripts/ProxmoxVE/pull/9592)) - Booklore: Downgrad Java from 25 to 21 [@Pr0mises](https://github.com/Pr0mises) ([#9566](https://github.com/community-scripts/ProxmoxVE/pull/9566)) - #### ✨ New Features - Set Valkey memory and eviction defaults [@pshankinclarke](https://github.com/pshankinclarke) ([#9602](https://github.com/community-scripts/ProxmoxVE/pull/9602)) - Add auth via requirepass to Valkey [@pshankinclarke](https://github.com/pshankinclarke) ([#9570](https://github.com/community-scripts/ProxmoxVE/pull/9570)) - #### 🔧 Refactor - Refactor: 2FAuth [@tremor021](https://github.com/tremor021) ([#9582](https://github.com/community-scripts/ProxmoxVE/pull/9582)) - Refactor: Paperless-AI [@MickLesk](https://github.com/MickLesk) ([#9588](https://github.com/community-scripts/ProxmoxVE/pull/9588)) - Refactor: AdventureLog [@tremor021](https://github.com/tremor021) ([#9583](https://github.com/community-scripts/ProxmoxVE/pull/9583)) - CommaFeed: Bump Java and service file [@tremor021](https://github.com/tremor021) ([#9564](https://github.com/community-scripts/ProxmoxVE/pull/9564)) - Refactor: Docmost [@tremor021](https://github.com/tremor021) ([#9563](https://github.com/community-scripts/ProxmoxVE/pull/9563)) - Cloudflared: Add repo via helper function [@tremor021](https://github.com/tremor021) ([#9565](https://github.com/community-scripts/ProxmoxVE/pull/9565)) ### 🧰 Maintenance - #### 📝 Documentation - add configuration and deployment guides to docs [@MickLesk](https://github.com/MickLesk) ([#9591](https://github.com/community-scripts/ProxmoxVE/pull/9591)) ### 🌐 Website - #### 📝 Script Information - Update category for "Wanderer" [@Lorondos](https://github.com/Lorondos) ([#9607](https://github.com/community-scripts/ProxmoxVE/pull/9607)) ## 2025-12-01 ### 🆕 New Scripts - Wanderer ([#9556](https://github.com/community-scripts/ProxmoxVE/pull/9556)) - core: add cloud-init.func library for VM configuration [@MickLesk](https://github.com/MickLesk) ([#9538](https://github.com/community-scripts/ProxmoxVE/pull/9538)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - core: sanitize appname for certificate generation [@tremor021](https://github.com/tremor021) ([#9552](https://github.com/community-scripts/ProxmoxVE/pull/9552)) - Fix Django superuser creation failing with ImproperlyConfigured error [@Copilot](https://github.com/Copilot) ([#9554](https://github.com/community-scripts/ProxmoxVE/pull/9554)) - #### ✨ New Features - Bump Baikal to deb13 [@MickLesk](https://github.com/MickLesk) ([#9544](https://github.com/community-scripts/ProxmoxVE/pull/9544)) - Enhance MariaDB version fallback logic [@MickLesk](https://github.com/MickLesk) ([#9545](https://github.com/community-scripts/ProxmoxVE/pull/9545)) - #### 💥 Breaking Changes - Refactor: Healthchecks [@MickLesk](https://github.com/MickLesk) ([#9188](https://github.com/community-scripts/ProxmoxVE/pull/9188)) - #### 🔧 Refactor - Refactor: Mealie [@MickLesk](https://github.com/MickLesk) ([#9308](https://github.com/community-scripts/ProxmoxVE/pull/9308)) ### 🧰 Maintenance - #### 📂 Github - add comprehensive documentation (core, develop, functions, technical guide, contributor guide) [@MickLesk](https://github.com/MickLesk) ([#9537](https://github.com/community-scripts/ProxmoxVE/pull/9537)) ### 🌐 Website - #### 📝 Script Information - update selfhst icon-URLs to use @master path [@MickLesk](https://github.com/MickLesk) ([#9543](https://github.com/community-scripts/ProxmoxVE/pull/9543)) ## 2025-12-31 ### 🚀 Updated Scripts - fix(wazuh): add LXC rootcheck exclusion to prevent false positives [@brettlyons](https://github.com/brettlyons) ([#10436](https://github.com/community-scripts/ProxmoxVE/pull/10436)) - #### 🐞 Bug Fixes - Increase BentoPDF RAM requirement from 2GB to 4GB [@Copilot](https://github.com/Copilot) ([#10449](https://github.com/community-scripts/ProxmoxVE/pull/10449)) - fix(swizzin): Use HTTPS and add curl error handling [@fmcglinn](https://github.com/fmcglinn) ([#10440](https://github.com/community-scripts/ProxmoxVE/pull/10440)) ================================================ FILE: .github/changelogs/2026/01.md ================================================ ## 2026-01-31 ### 🆕 New Scripts - shelfmark ([#11371](https://github.com/community-scripts/ProxmoxVE/pull/11371)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - fix: yubal: add git [@CrazyWolf13](https://github.com/CrazyWolf13) ([#11394](https://github.com/community-scripts/ProxmoxVE/pull/11394)) ## 2026-01-30 ### 🆕 New Scripts - languagetool ([#11370](https://github.com/community-scripts/ProxmoxVE/pull/11370)) - Ampache ([#11369](https://github.com/community-scripts/ProxmoxVE/pull/11369)) ### 🚀 Updated Scripts - #### 🔧 Refactor - Refactor: remove redundant PHP_MODULE entries in several scripts [@MickLesk](https://github.com/MickLesk) ([#11362](https://github.com/community-scripts/ProxmoxVE/pull/11362)) - Refactor: Koillection [@MickLesk](https://github.com/MickLesk) ([#11361](https://github.com/community-scripts/ProxmoxVE/pull/11361)) ### 💾 Core - #### 🐞 Bug Fixes - core: meilisearch - add data migration for version upgrades [@MickLesk](https://github.com/MickLesk) ([#11356](https://github.com/community-scripts/ProxmoxVE/pull/11356)) - #### ✨ New Features - [tools] Add `fetch_and_deploy_from_url()` [@tremor021](https://github.com/tremor021) ([#11376](https://github.com/community-scripts/ProxmoxVE/pull/11376)) - core: php - improve module handling and prevent installation failures [@MickLesk](https://github.com/MickLesk) ([#11358](https://github.com/community-scripts/ProxmoxVE/pull/11358)) ## 2026-01-29 ### 🆕 New Scripts - Alpine-Valkey [@MickLesk](https://github.com/MickLesk) ([#11320](https://github.com/community-scripts/ProxmoxVE/pull/11320)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Immich: Pin version to 2.5.2 [@vhsdream](https://github.com/vhsdream) ([#11335](https://github.com/community-scripts/ProxmoxVE/pull/11335)) - Kollection: Update to php 8.5 [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#11315](https://github.com/community-scripts/ProxmoxVE/pull/11315)) - Notifiarr: change installation check from apt to systemd service [@MickLesk](https://github.com/MickLesk) ([#11319](https://github.com/community-scripts/ProxmoxVE/pull/11319)) - #### ✨ New Features - [FEAT] Immich: Enable Maintenance Mode before update [@vhsdream](https://github.com/vhsdream) ([#11342](https://github.com/community-scripts/ProxmoxVE/pull/11342)) - jellyfin: add logrotate instead of reducing log level [@MickLesk](https://github.com/MickLesk) ([#11326](https://github.com/community-scripts/ProxmoxVE/pull/11326)) - core: Add config file handling options | Fix Vikunja update with interactive overwrite [@MickLesk](https://github.com/MickLesk) ([#11317](https://github.com/community-scripts/ProxmoxVE/pull/11317)) - Immich: v2.5.0 [@vhsdream](https://github.com/vhsdream) ([#11240](https://github.com/community-scripts/ProxmoxVE/pull/11240)) - #### 💥 Breaking Changes - fix: vikunja v1 [@CrazyWolf13](https://github.com/CrazyWolf13) ([#11308](https://github.com/community-scripts/ProxmoxVE/pull/11308)) - #### 🔧 Refactor - Refactor: Byparr [@vhsdream](https://github.com/vhsdream) ([#11338](https://github.com/community-scripts/ProxmoxVE/pull/11338)) - cloudflare: Remove deprecated DNS-over-HTTPS proxy option [@MickLesk](https://github.com/MickLesk) ([#11068](https://github.com/community-scripts/ProxmoxVE/pull/11068)) ### 💾 Core - #### 🐞 Bug Fixes - build.func: Replace storage variable with searchdomain variable [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#11322](https://github.com/community-scripts/ProxmoxVE/pull/11322)) ### 📂 Github - Add workflow to lock closed issues [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#11316](https://github.com/community-scripts/ProxmoxVE/pull/11316)) ## 2026-01-28 ### 🆕 New Scripts - nodecast-tv ([#11287](https://github.com/community-scripts/ProxmoxVE/pull/11287)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Ubuntu 25.04 VM - Change default start from yes to no [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#11292](https://github.com/community-scripts/ProxmoxVE/pull/11292)) - #### ✨ New Features - various scripts: use setup_meilisearch function [@MickLesk](https://github.com/MickLesk) ([#11259](https://github.com/community-scripts/ProxmoxVE/pull/11259)) - #### 🔧 Refactor - Refactor: NPMPlus / Default Login [@MickLesk](https://github.com/MickLesk) ([#11262](https://github.com/community-scripts/ProxmoxVE/pull/11262)) ### 💾 Core - #### 🐞 Bug Fixes - core: sed patch for ram [@lavacano](https://github.com/lavacano) ([#11285](https://github.com/community-scripts/ProxmoxVE/pull/11285)) - Fix installer loop caused by invalid whiptail menu separator [@Mesteriis](https://github.com/Mesteriis) ([#11237](https://github.com/community-scripts/ProxmoxVE/pull/11237)) - core: fix Debian 13 LXC template root ownership bug [@MickLesk](https://github.com/MickLesk) ([#11277](https://github.com/community-scripts/ProxmoxVE/pull/11277)) - tools.func: prevent systemd-tmpfiles failure in unprivileged LXC during deb install [@MickLesk](https://github.com/MickLesk) ([#11271](https://github.com/community-scripts/ProxmoxVE/pull/11271)) - tools.func: fix php "wait_for" hint [@MickLesk](https://github.com/MickLesk) ([#11254](https://github.com/community-scripts/ProxmoxVE/pull/11254)) - #### ✨ New Features - core: update dynamic values in LXC profile on update_motd_ip [@MickLesk](https://github.com/MickLesk) ([#11268](https://github.com/community-scripts/ProxmoxVE/pull/11268)) - tools.func: add new function - setup_meilisearch [@MickLesk](https://github.com/MickLesk) ([#11258](https://github.com/community-scripts/ProxmoxVE/pull/11258)) ### 📂 Github - github: add GitHub-based versions.json updater [@MickLesk](https://github.com/MickLesk) ([#10021](https://github.com/community-scripts/ProxmoxVE/pull/10021)) ### 🌐 Website - #### ✨ New Features - Frontend: use github-versions.json for version display [@MickLesk](https://github.com/MickLesk) ([#11281](https://github.com/community-scripts/ProxmoxVE/pull/11281)) - #### 📝 Script Information - fix: homarr: conf location [@CrazyWolf13](https://github.com/CrazyWolf13) ([#11253](https://github.com/community-scripts/ProxmoxVE/pull/11253)) ## 2026-01-27 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - [FIX] Jotty: backup and restore custom config [@vhsdream](https://github.com/vhsdream) ([#11212](https://github.com/community-scripts/ProxmoxVE/pull/11212)) - Immich: update libraw [@vhsdream](https://github.com/vhsdream) ([#11233](https://github.com/community-scripts/ProxmoxVE/pull/11233)) - #### ✨ New Features - grist: enable optional enterprise features toggle [@MickLesk](https://github.com/MickLesk) ([#11239](https://github.com/community-scripts/ProxmoxVE/pull/11239)) - #### 🔧 Refactor - Termix: use nginx.conf from upstream repo [@MickLesk](https://github.com/MickLesk) ([#11228](https://github.com/community-scripts/ProxmoxVE/pull/11228)) ### 💾 Core - #### ✨ New Features - feat: add NVIDIA driver install prompt for GPU-enabled containers [@devdecrux](https://github.com/devdecrux) ([#11184](https://github.com/community-scripts/ProxmoxVE/pull/11184)) ### 📚 Documentation - doc setup_deb822_repo arg order [@chrnie](https://github.com/chrnie) ([#11215](https://github.com/community-scripts/ProxmoxVE/pull/11215)) - changelog: archive old entries to year/month files [@MickLesk](https://github.com/MickLesk) ([#11225](https://github.com/community-scripts/ProxmoxVE/pull/11225)) ## 2026-01-26 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Ghost: Fix missing dependency [@tremor021](https://github.com/tremor021) ([#11196](https://github.com/community-scripts/ProxmoxVE/pull/11196)) - tracearr: fix install check and update node to version 24 [@durzo](https://github.com/durzo) ([#11188](https://github.com/community-scripts/ProxmoxVE/pull/11188)) - #### ✨ New Features - jotty: full refactor / prebuild package [@MickLesk](https://github.com/MickLesk) ([#11059](https://github.com/community-scripts/ProxmoxVE/pull/11059)) - #### 💥 Breaking Changes - Termix: Fixing Nginx configuration for 1.11.0 installs (read description for fix!) [@8b1th3r0](https://github.com/8b1th3r0) ([#11207](https://github.com/community-scripts/ProxmoxVE/pull/11207)) ### 💾 Core - #### 🐞 Bug Fixes - core: refine cleanup_lxc to safely clear caches [@MickLesk](https://github.com/MickLesk) ([#11197](https://github.com/community-scripts/ProxmoxVE/pull/11197)) - #### ✨ New Features - core: add nesting warning for systemd-based distributions [@MickLesk](https://github.com/MickLesk) ([#11208](https://github.com/community-scripts/ProxmoxVE/pull/11208)) ### 🧰 Tools - #### 🐞 Bug Fixes - jellystat: correct WorkingDirectory to /backend [@MickLesk](https://github.com/MickLesk) ([#11201](https://github.com/community-scripts/ProxmoxVE/pull/11201)) ## 2026-01-25 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - [FIX] Tautulli: ensure virtualenv is recreated during update; backup tautulli.db [@vhsdream](https://github.com/vhsdream) ([#11182](https://github.com/community-scripts/ProxmoxVE/pull/11182)) - [Fix] Pangolin: ensure additional JSON files are in place [@vhsdream](https://github.com/vhsdream) ([#11183](https://github.com/community-scripts/ProxmoxVE/pull/11183)) - Manyfold: fix permissions error [@vhsdream](https://github.com/vhsdream) ([#11165](https://github.com/community-scripts/ProxmoxVE/pull/11165)) - Termix: recreate nginx dirs and backup uploads on update [@MickLesk](https://github.com/MickLesk) ([#11169](https://github.com/community-scripts/ProxmoxVE/pull/11169)) - Deluge: correct service paths to /usr/local/bin [@MickLesk](https://github.com/MickLesk) ([#11170](https://github.com/community-scripts/ProxmoxVE/pull/11170)) - #### ✨ New Features - Karakeep: Add the FFmpeg option to the installation script [@vonhyou](https://github.com/vonhyou) ([#11157](https://github.com/community-scripts/ProxmoxVE/pull/11157)) - apt-cacher-ng: add avahi-daemon for mDNS service discovery [@MickLesk](https://github.com/MickLesk) ([#11140](https://github.com/community-scripts/ProxmoxVE/pull/11140)) ## 2026-01-24 ### 🆕 New Scripts - Manyfold ([#11143](https://github.com/community-scripts/ProxmoxVE/pull/11143)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - elementsynapse: correct parameter order in fetch_and_deploy_gh_release [@MickLesk](https://github.com/MickLesk) ([#11145](https://github.com/community-scripts/ProxmoxVE/pull/11145)) - leantime: fix backup file naming [@MickLesk](https://github.com/MickLesk) ([#11137](https://github.com/community-scripts/ProxmoxVE/pull/11137)) - [Hotfix] Element Synapse [@vhsdream](https://github.com/vhsdream) ([#11135](https://github.com/community-scripts/ProxmoxVE/pull/11135)) - authelia: use POSIX-safe arithmetic to avoid exit code 1 with set -e in subshells [@MickLesk](https://github.com/MickLesk) ([#11125](https://github.com/community-scripts/ProxmoxVE/pull/11125)) - Bitmagnet: PostgreSQL and environment variable fixes [@tremor021](https://github.com/tremor021) ([#11119](https://github.com/community-scripts/ProxmoxVE/pull/11119)) - Spoolman: move to uv [@vhsdream](https://github.com/vhsdream) ([#11121](https://github.com/community-scripts/ProxmoxVE/pull/11121)) - #### 🔧 Refactor - bump crafty-controller to debian 13 [@CrazyWolf13](https://github.com/CrazyWolf13) ([#11094](https://github.com/community-scripts/ProxmoxVE/pull/11094)) - Netbox: Refactor [@vhsdream](https://github.com/vhsdream) ([#11126](https://github.com/community-scripts/ProxmoxVE/pull/11126)) - Flatnotes: Standard enforcing [@tremor021](https://github.com/tremor021) ([#11109](https://github.com/community-scripts/ProxmoxVE/pull/11109)) ### 💾 Core - #### 🐞 Bug Fixes - nvidia: use versioned nvidia-utils package for Ubuntu fallback [@MickLesk](https://github.com/MickLesk) ([#11139](https://github.com/community-scripts/ProxmoxVE/pull/11139)) ### 🌐 Website - #### 📝 Script Information - Byparr: Add config file path to website [@CrazyWolf13](https://github.com/CrazyWolf13) ([#11120](https://github.com/community-scripts/ProxmoxVE/pull/11120)) ## 2026-01-23 ### 🆕 New Scripts - Tracearr ([#11079](https://github.com/community-scripts/ProxmoxVE/pull/11079)) - Dawarich ([#11075](https://github.com/community-scripts/ProxmoxVE/pull/11075)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - fix: homarr: more ram [@CrazyWolf13](https://github.com/CrazyWolf13) ([#11102](https://github.com/community-scripts/ProxmoxVE/pull/11102)) - plant-it: re-add JWT_SECRET [@MickLesk](https://github.com/MickLesk) ([#11098](https://github.com/community-scripts/ProxmoxVE/pull/11098)) - Tautulli: fix config backup and restore logic [@MickLesk](https://github.com/MickLesk) ([#11099](https://github.com/community-scripts/ProxmoxVE/pull/11099)) - Scanopy: remove integrated daemon script [@vhsdream](https://github.com/vhsdream) ([#11100](https://github.com/community-scripts/ProxmoxVE/pull/11100)) - fix: reitti start nginx [@CrazyWolf13](https://github.com/CrazyWolf13) ([#11095](https://github.com/community-scripts/ProxmoxVE/pull/11095)) - add: uptime-kuma: chromium [@CrazyWolf13](https://github.com/CrazyWolf13) ([#11081](https://github.com/community-scripts/ProxmoxVE/pull/11081)) - fix(install): Add typing_extensions to SearXNG Python dependencies [@ZarenOFF](https://github.com/ZarenOFF) ([#11074](https://github.com/community-scripts/ProxmoxVE/pull/11074)) - #### ✨ New Features - Bump various scripts to Debian 13 (Trixie) [@MickLesk](https://github.com/MickLesk) ([#11093](https://github.com/community-scripts/ProxmoxVE/pull/11093)) - several scripts: bump default Alpine version to 3.23 [@MickLesk](https://github.com/MickLesk) ([#11082](https://github.com/community-scripts/ProxmoxVE/pull/11082)) - PDM: avoid installing useless package [@LongQT-sea](https://github.com/LongQT-sea) ([#10833](https://github.com/community-scripts/ProxmoxVE/pull/10833)) - #### 🔧 Refactor - FHEM: Bump to Debian 13 [@tremor021](https://github.com/tremor021) ([#11061](https://github.com/community-scripts/ProxmoxVE/pull/11061)) - Duplicati: Bump to Debian 13 [@tremor021](https://github.com/tremor021) ([#11060](https://github.com/community-scripts/ProxmoxVE/pull/11060)) ### 💾 Core - #### ✨ New Features - core: add IPv6 fallback support to get_current_ip functions | add check for SSH_KEYS_FILE in user_defaults [@MickLesk](https://github.com/MickLesk) ([#11067](https://github.com/community-scripts/ProxmoxVE/pull/11067)) ## 2026-01-22 ### 🆕 New Scripts - Loki | Alpine-Loki ([#11048](https://github.com/community-scripts/ProxmoxVE/pull/11048)) ### 🚀 Updated Scripts - Immich: Increase RAM to 6GB [@vhsdream](https://github.com/vhsdream) ([#10965](https://github.com/community-scripts/ProxmoxVE/pull/10965)) - #### 🐞 Bug Fixes - Jotty: Increase default disk size from 6 to 8 [@tremor021](https://github.com/tremor021) ([#11056](https://github.com/community-scripts/ProxmoxVE/pull/11056)) - Fix tags in several scripts [@s4dmach1ne](https://github.com/s4dmach1ne) ([#11050](https://github.com/community-scripts/ProxmoxVE/pull/11050)) ### 💾 Core - #### ✨ New Features - tools: use distro packages for MariaDB by default [@MickLesk](https://github.com/MickLesk) ([#11049](https://github.com/community-scripts/ProxmoxVE/pull/11049)) ## 2026-01-21 ### 🆕 New Scripts - Byparr ([#11039](https://github.com/community-scripts/ProxmoxVE/pull/11039)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - fix: Snipe-IT update missing all user uploads (#11032) [@ruanmed](https://github.com/ruanmed) ([#11033](https://github.com/community-scripts/ProxmoxVE/pull/11033)) - yubal: fix for v0.2 [@CrazyWolf13](https://github.com/CrazyWolf13) ([#11006](https://github.com/community-scripts/ProxmoxVE/pull/11006)) - Joplin-Server: use yarn workspaces focus for faster builds [@MickLesk](https://github.com/MickLesk) ([#11027](https://github.com/community-scripts/ProxmoxVE/pull/11027)) ### 💾 Core - #### ✨ New Features - tools: add ubuntu PHP repository setup [@MickLesk](https://github.com/MickLesk) ([#11034](https://github.com/community-scripts/ProxmoxVE/pull/11034)) - #### 🔧 Refactor - core: allow empty tags & improve template search [@MickLesk](https://github.com/MickLesk) ([#11020](https://github.com/community-scripts/ProxmoxVE/pull/11020)) ### 🌐 Website - #### 📝 Script Information - Joplin Server: Set disable flag to true in joplin-server.json [@tremor021](https://github.com/tremor021) ([#11008](https://github.com/community-scripts/ProxmoxVE/pull/11008)) ## 2026-01-20 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - dolibarr: switch mirror [@MickLesk](https://github.com/MickLesk) ([#11004](https://github.com/community-scripts/ProxmoxVE/pull/11004)) - checkmk: reordner base function [@MickLesk](https://github.com/MickLesk) ([#10990](https://github.com/community-scripts/ProxmoxVE/pull/10990)) - Homepage: preserve config directory during updates [@MickLesk](https://github.com/MickLesk) ([#10993](https://github.com/community-scripts/ProxmoxVE/pull/10993)) - DiscoPanel: add go for update build process [@miausalvaje](https://github.com/miausalvaje) ([#10991](https://github.com/community-scripts/ProxmoxVE/pull/10991)) ### 💾 Core - #### ✨ New Features - core: add retry logic for template lock in LXC container creation [@MickLesk](https://github.com/MickLesk) ([#11002](https://github.com/community-scripts/ProxmoxVE/pull/11002)) - core: implement ensure_profile_loaded function [@MickLesk](https://github.com/MickLesk) ([#10999](https://github.com/community-scripts/ProxmoxVE/pull/10999)) - core: add input validations for several functions [@MickLesk](https://github.com/MickLesk) ([#10995](https://github.com/community-scripts/ProxmoxVE/pull/10995)) ## 2026-01-19 ### 🆕 New Scripts - yubal ([#10955](https://github.com/community-scripts/ProxmoxVE/pull/10955)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Apache-Guacamole: move jdbc cleanup after schema upgrade [@MickLesk](https://github.com/MickLesk) ([#10974](https://github.com/community-scripts/ProxmoxVE/pull/10974)) - Outline: prevent corepack interactive prompt blocking installation [@MickLesk](https://github.com/MickLesk) ([#10973](https://github.com/community-scripts/ProxmoxVE/pull/10973)) - firefly: prevent nested storage directories during update (#10967) [@MickLesk](https://github.com/MickLesk) ([#10972](https://github.com/community-scripts/ProxmoxVE/pull/10972)) - PeaNUT: change default port [@vhsdream](https://github.com/vhsdream) ([#10962](https://github.com/community-scripts/ProxmoxVE/pull/10962)) - Update/splunk enterprise [@rcastley](https://github.com/rcastley) ([#10949](https://github.com/community-scripts/ProxmoxVE/pull/10949)) - #### ✨ New Features - Pangolin: use dynamic badger plugin version [@MickLesk](https://github.com/MickLesk) ([#10975](https://github.com/community-scripts/ProxmoxVE/pull/10975)) - Tautulli: add version detection and add proper update script [@MickLesk](https://github.com/MickLesk) ([#10976](https://github.com/community-scripts/ProxmoxVE/pull/10976)) - #### 🔧 Refactor - Refactor: Remove custom IP fetching in scripts [@tremor021](https://github.com/tremor021) ([#10954](https://github.com/community-scripts/ProxmoxVE/pull/10954)) - Refactor: Homepage [@tremor021](https://github.com/tremor021) ([#10950](https://github.com/community-scripts/ProxmoxVE/pull/10950)) - Refactor: hev-socks5-server [@tremor021](https://github.com/tremor021) ([#10945](https://github.com/community-scripts/ProxmoxVE/pull/10945)) ### 🗑️ Deleted Scripts - Remove: phpIPAM [@MickLesk](https://github.com/MickLesk) ([#10939](https://github.com/community-scripts/ProxmoxVE/pull/10939)) ### 💾 Core - #### ✨ New Features - core: add RFC 1123/952 compliant hostname/FQDN validation [@MickLesk](https://github.com/MickLesk) ([#10977](https://github.com/community-scripts/ProxmoxVE/pull/10977)) - [core]: Make LXC IP a global variable [@tremor021](https://github.com/tremor021) ([#10951](https://github.com/community-scripts/ProxmoxVE/pull/10951)) ### 🧰 Tools - #### 🔧 Refactor - Refactor: copyparty [@MickLesk](https://github.com/MickLesk) ([#10941](https://github.com/community-scripts/ProxmoxVE/pull/10941)) ## 2026-01-18 ### 🆕 New Scripts - Termix ([#10887](https://github.com/community-scripts/ProxmoxVE/pull/10887)) - ThingsBoard ([#10904](https://github.com/community-scripts/ProxmoxVE/pull/10904)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Fix Patchmon install script (escaping) [@christiaangoossens](https://github.com/christiaangoossens) ([#10920](https://github.com/community-scripts/ProxmoxVE/pull/10920)) - refactor: peanut entrypoint [@CrazyWolf13](https://github.com/CrazyWolf13) ([#10902](https://github.com/community-scripts/ProxmoxVE/pull/10902)) - #### 💥 Breaking Changes - Update Patchmon default Nginx config (IPv6 and correct scheme) [@christiaangoossens](https://github.com/christiaangoossens) ([#10917](https://github.com/community-scripts/ProxmoxVE/pull/10917)) - #### 🔧 Refactor - Refactor: FluidCalendar [@tremor021](https://github.com/tremor021) ([#10928](https://github.com/community-scripts/ProxmoxVE/pull/10928)) ### 🗑️ Deleted Scripts - Remove iVentoy script [@tremor021](https://github.com/tremor021) ([#10924](https://github.com/community-scripts/ProxmoxVE/pull/10924)) ### 💾 Core - #### ✨ New Features - core: improve password handling and validation logic [@MickLesk](https://github.com/MickLesk) ([#10925](https://github.com/community-scripts/ProxmoxVE/pull/10925)) - #### 🔧 Refactor - hwaccel: improve NVIDIA version matching and GPU selection UI [@MickLesk](https://github.com/MickLesk) ([#10901](https://github.com/community-scripts/ProxmoxVE/pull/10901)) ### 📂 Github - Fix typo in the New Script request template [@tremor021](https://github.com/tremor021) ([#10891](https://github.com/community-scripts/ProxmoxVE/pull/10891)) ### 🌐 Website - #### 🐞 Bug Fixes - fix: preserve newest scripts pagination [@jgrubiox](https://github.com/jgrubiox) ([#10882](https://github.com/community-scripts/ProxmoxVE/pull/10882)) ### ❔ Uncategorized - Update qui.json [@GalaxyCatD3v](https://github.com/GalaxyCatD3v) ([#10896](https://github.com/community-scripts/ProxmoxVE/pull/10896)) ## 2026-01-17 ### 🆕 New Scripts - TRIP ([#10864](https://github.com/community-scripts/ProxmoxVE/pull/10864)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - fix sonarqube update version info (#10870) [@Karlito83](https://github.com/Karlito83) ([#10871](https://github.com/community-scripts/ProxmoxVE/pull/10871)) - WGDashboard: Update repo URL [@tremor021](https://github.com/tremor021) ([#10872](https://github.com/community-scripts/ProxmoxVE/pull/10872)) ### 🌐 Website - #### 📝 Script Information - Disable Palmer [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#10889](https://github.com/community-scripts/ProxmoxVE/pull/10889)) ## 2026-01-16 ### 🆕 New Scripts - Flatnotes ([#10857](https://github.com/community-scripts/ProxmoxVE/pull/10857)) - Unifi OS Server ([#10856](https://github.com/community-scripts/ProxmoxVE/pull/10856)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Jotty: increase RAM; cap heap size at 3GB during build [@vhsdream](https://github.com/vhsdream) ([#10868](https://github.com/community-scripts/ProxmoxVE/pull/10868)) - SnowShare: Increase default resources [@TuroYT](https://github.com/TuroYT) ([#10865](https://github.com/community-scripts/ProxmoxVE/pull/10865)) - postgresql: name of sources file fixed (update check) [@JamborJan](https://github.com/JamborJan) ([#10854](https://github.com/community-scripts/ProxmoxVE/pull/10854)) - immich: use dpkg-query to get intel-opencl-icd version [@MickLesk](https://github.com/MickLesk) ([#10848](https://github.com/community-scripts/ProxmoxVE/pull/10848)) - domain-monitor: fix: cron user [@CrazyWolf13](https://github.com/CrazyWolf13) ([#10846](https://github.com/community-scripts/ProxmoxVE/pull/10846)) - pihole/unbound: create unbound config before apt install to prevent port conflicts [@MickLesk](https://github.com/MickLesk) ([#10839](https://github.com/community-scripts/ProxmoxVE/pull/10839)) - zammad: use ln -sf to avoid failure when symlink exists [@MickLesk](https://github.com/MickLesk) ([#10840](https://github.com/community-scripts/ProxmoxVE/pull/10840)) ### ❔ Uncategorized - qui: fix: category [@CrazyWolf13](https://github.com/CrazyWolf13) ([#10847](https://github.com/community-scripts/ProxmoxVE/pull/10847)) ## 2026-01-15 ### 🆕 New Scripts - Qui ([#10829](https://github.com/community-scripts/ProxmoxVE/pull/10829)) ### 🚀 Updated Scripts - #### ✨ New Features - Refactor: FreshRSS + Bump to Debian 13 [@MickLesk](https://github.com/MickLesk) ([#10824](https://github.com/community-scripts/ProxmoxVE/pull/10824)) ## 2026-01-14 ### 🆕 New Scripts - Kutt ([#10812](https://github.com/community-scripts/ProxmoxVE/pull/10812)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Switch Ollama install to .tar.zst and add zstd dependency [@MickLesk](https://github.com/MickLesk) ([#10814](https://github.com/community-scripts/ProxmoxVE/pull/10814)) - Immich: Install libde265-dev from Debian Testing [@vhsdream](https://github.com/vhsdream) ([#10810](https://github.com/community-scripts/ProxmoxVE/pull/10810)) - nginxproxymanager: allow updates now the build is fixed [@durzo](https://github.com/durzo) ([#10796](https://github.com/community-scripts/ProxmoxVE/pull/10796)) - Fixed Apache Guacamole installer [@horvatbenjamin](https://github.com/horvatbenjamin) ([#10798](https://github.com/community-scripts/ProxmoxVE/pull/10798)) ### 💾 Core - #### ✨ New Features - core: Improve NVIDIA GPU setup (5000x Series) [@MickLesk](https://github.com/MickLesk) ([#10807](https://github.com/community-scripts/ProxmoxVE/pull/10807)) ### 🧰 Tools - Fix whiptail dialog hanging in Proxmox web console [@comk22](https://github.com/comk22) ([#10794](https://github.com/community-scripts/ProxmoxVE/pull/10794)) ### 🌐 Website - #### 🐞 Bug Fixes - Add search filtering to CommandDialog for improved script search functionality [@BramSuurdje](https://github.com/BramSuurdje) ([#10800](https://github.com/community-scripts/ProxmoxVE/pull/10800)) ## 2026-01-13 ### 🆕 New Scripts - Investbrain ([#10774](https://github.com/community-scripts/ProxmoxVE/pull/10774)) - Fladder ([#10768](https://github.com/community-scripts/ProxmoxVE/pull/10768)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Immich: Fix Intel version check; install legacy Intel packages during new install [@vhsdream](https://github.com/vhsdream) ([#10787](https://github.com/community-scripts/ProxmoxVE/pull/10787)) - Openwrt: Remove default VLAN for LAN [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#10782](https://github.com/community-scripts/ProxmoxVE/pull/10782)) - Refactor: Joplin Server [@tremor021](https://github.com/tremor021) ([#10769](https://github.com/community-scripts/ProxmoxVE/pull/10769)) - Fix Zammad nginx configuration causing installation failure [@Copilot](https://github.com/Copilot) ([#10757](https://github.com/community-scripts/ProxmoxVE/pull/10757)) - #### 🔧 Refactor - Backrest: Bump to Trixie [@tremor021](https://github.com/tremor021) ([#10758](https://github.com/community-scripts/ProxmoxVE/pull/10758)) - Refactor: Caddy [@tremor021](https://github.com/tremor021) ([#10759](https://github.com/community-scripts/ProxmoxVE/pull/10759)) - Refactor: Leantime [@tremor021](https://github.com/tremor021) ([#10760](https://github.com/community-scripts/ProxmoxVE/pull/10760)) ### 🧰 Tools - #### 🐞 Bug Fixes - update_lxcs.sh: Add the option to skip stopped LXC [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#10783](https://github.com/community-scripts/ProxmoxVE/pull/10783)) ## 2026-01-12 ### 🆕 New Scripts - Jellystat ([#10628](https://github.com/community-scripts/ProxmoxVE/pull/10628)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - InfluxSB: fix If / fi [@chrnie](https://github.com/chrnie) ([#10753](https://github.com/community-scripts/ProxmoxVE/pull/10753)) - Cockpit: Downgrade to Debian 12 Bookworm (45Drives Issue) [@MickLesk](https://github.com/MickLesk) ([#10717](https://github.com/community-scripts/ProxmoxVE/pull/10717)) - #### ✨ New Features - InfluxDB: add setup for influxdb v3 [@victorlap](https://github.com/victorlap) ([#10736](https://github.com/community-scripts/ProxmoxVE/pull/10736)) - Apache Guacamole: add schema upgrades and extension updates [@MickLesk](https://github.com/MickLesk) ([#10746](https://github.com/community-scripts/ProxmoxVE/pull/10746)) - Apache Tomcat: update support and refactor install script + debian 13 [@MickLesk](https://github.com/MickLesk) ([#10739](https://github.com/community-scripts/ProxmoxVE/pull/10739)) - Apache Guacamole: Function Bump + update_script [@MickLesk](https://github.com/MickLesk) ([#10728](https://github.com/community-scripts/ProxmoxVE/pull/10728)) - Apache CouchDB: bump to debian 13 and add update support [@MickLesk](https://github.com/MickLesk) ([#10721](https://github.com/community-scripts/ProxmoxVE/pull/10721)) - Apache Cassandra: bump to debian 13 and add update support [@MickLesk](https://github.com/MickLesk) ([#10720](https://github.com/community-scripts/ProxmoxVE/pull/10720)) - #### 🔧 Refactor - Refactor: Booklore [@MickLesk](https://github.com/MickLesk) ([#10742](https://github.com/community-scripts/ProxmoxVE/pull/10742)) - Bump Argus to Debian 13 [@MickLesk](https://github.com/MickLesk) ([#10718](https://github.com/community-scripts/ProxmoxVE/pull/10718)) - Refactor Docker/Dockge & Bump to Debian 13 [@MickLesk](https://github.com/MickLesk) ([#10719](https://github.com/community-scripts/ProxmoxVE/pull/10719)) ### 💾 Core - #### 🐞 Bug Fixes - core: remove duplicated pve_version in advanced installs [@MickLesk](https://github.com/MickLesk) ([#10743](https://github.com/community-scripts/ProxmoxVE/pull/10743)) - #### ✨ New Features - core: add storage validation & fix GB/MB display [@MickLesk](https://github.com/MickLesk) ([#10745](https://github.com/community-scripts/ProxmoxVE/pull/10745)) - core: validate container ID before pct create to prevent failures [@MickLesk](https://github.com/MickLesk) ([#10729](https://github.com/community-scripts/ProxmoxVE/pull/10729)) - #### 🔧 Refactor - Enforce non-interactive apt mode in DB setup scripts [@MickLesk](https://github.com/MickLesk) ([#10714](https://github.com/community-scripts/ProxmoxVE/pull/10714)) ## 2026-01-11 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Fix Invoice Ninja Error 500 by restoring file ownership after artisan commands [@Copilot](https://github.com/Copilot) ([#10709](https://github.com/community-scripts/ProxmoxVE/pull/10709)) - #### 🔧 Refactor - Refactor: Infisical [@tremor021](https://github.com/tremor021) ([#10693](https://github.com/community-scripts/ProxmoxVE/pull/10693)) - Refactor: HortusFox [@tremor021](https://github.com/tremor021) ([#10697](https://github.com/community-scripts/ProxmoxVE/pull/10697)) - Refactor: Homer [@tremor021](https://github.com/tremor021) ([#10698](https://github.com/community-scripts/ProxmoxVE/pull/10698)) ## 2026-01-10 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - [Endurain] Increase default RAM from 2048 to 4096 [@FutureCow](https://github.com/FutureCow) ([#10690](https://github.com/community-scripts/ProxmoxVE/pull/10690)) ### 💾 Core - #### 🐞 Bug Fixes - tools.func: hwaccel - make beignet-opencl-icd optional for legacy Intel GPUs [@MickLesk](https://github.com/MickLesk) ([#10677](https://github.com/community-scripts/ProxmoxVE/pull/10677)) ## 2026-01-09 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Jenkins: Fix application repository setup [@tremor021](https://github.com/tremor021) ([#10671](https://github.com/community-scripts/ProxmoxVE/pull/10671)) - deCONZ: Fix sources check in update script [@tremor021](https://github.com/tremor021) ([#10664](https://github.com/community-scripts/ProxmoxVE/pull/10664)) - Remove '--cpu' option from ExecStart command [@sethgregory](https://github.com/sethgregory) ([#10659](https://github.com/community-scripts/ProxmoxVE/pull/10659)) ### 💾 Core - #### 💥 Breaking Changes - fix: setup_mariadb hangs on [@CrazyWolf13](https://github.com/CrazyWolf13) ([#10672](https://github.com/community-scripts/ProxmoxVE/pull/10672)) ## 2026-01-08 ### 🆕 New Scripts - GWN-Manager ([#10642](https://github.com/community-scripts/ProxmoxVE/pull/10642)) ### 🚀 Updated Scripts - Fix line continuation for vlc-bin installation [@chinedu40](https://github.com/chinedu40) ([#10654](https://github.com/community-scripts/ProxmoxVE/pull/10654)) - #### 🐞 Bug Fixes - outline: use corepack yarn module [@MickLesk](https://github.com/MickLesk) ([#10652](https://github.com/community-scripts/ProxmoxVE/pull/10652)) - Remove unnecessary quotes from variable expansions in VM scripts [@MickLesk](https://github.com/MickLesk) ([#10649](https://github.com/community-scripts/ProxmoxVE/pull/10649)) - Monica: Fix database variable names [@tremor021](https://github.com/tremor021) ([#10634](https://github.com/community-scripts/ProxmoxVE/pull/10634)) - Tianji: Fix PostrgreSQL vars [@tremor021](https://github.com/tremor021) ([#10633](https://github.com/community-scripts/ProxmoxVE/pull/10633)) - #### 🔧 Refactor - deCONZ: Bump to Trixie base [@tremor021](https://github.com/tremor021) ([#10643](https://github.com/community-scripts/ProxmoxVE/pull/10643)) ## 2026-01-07 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - pve-scripts-local: fix missing exit in update [@MickLesk](https://github.com/MickLesk) ([#10630](https://github.com/community-scripts/ProxmoxVE/pull/10630)) - #### ✨ New Features - Upgrade ESPHome LXC to Debian 13 [@heinemannj](https://github.com/heinemannj) ([#10624](https://github.com/community-scripts/ProxmoxVE/pull/10624)) - #### 🔧 Refactor - Explicitly state installation method [@tremor021](https://github.com/tremor021) ([#10608](https://github.com/community-scripts/ProxmoxVE/pull/10608)) ### 🧰 Tools - Modify Debian sources list for trixie updates (as 4.1.0-1 config) [@maiux](https://github.com/maiux) ([#10505](https://github.com/community-scripts/ProxmoxVE/pull/10505)) ## 2026-01-06 ### 🆕 New Scripts - Sportarr ([#10600](https://github.com/community-scripts/ProxmoxVE/pull/10600)) ### 🚀 Updated Scripts - chore: fix update msg [@CrazyWolf13](https://github.com/CrazyWolf13) ([#10593](https://github.com/community-scripts/ProxmoxVE/pull/10593)) - #### 🐞 Bug Fixes - InspIRCd: Fix release fetching [@tremor021](https://github.com/tremor021) ([#10578](https://github.com/community-scripts/ProxmoxVE/pull/10578)) - #### 🔧 Refactor - Refactor: Sonarr [@tremor021](https://github.com/tremor021) ([#10573](https://github.com/community-scripts/ProxmoxVE/pull/10573)) - Refactor: Dispatcharr [@tremor021](https://github.com/tremor021) ([#10599](https://github.com/community-scripts/ProxmoxVE/pull/10599)) ### 💾 Core - #### ✨ New Features - hwaccel: rewrite of GPU hardware acceleration support [@MickLesk](https://github.com/MickLesk) ([#10597](https://github.com/community-scripts/ProxmoxVE/pull/10597)) ### 🧰 Tools - #### 🐞 Bug Fixes - iptag: fix syntax error in VM config file parsing [@MickLesk](https://github.com/MickLesk) ([#10598](https://github.com/community-scripts/ProxmoxVE/pull/10598)) - #### ✨ New Features - Update clean-lxcs.sh to support Red Hat compatible distros [@jabofh](https://github.com/jabofh) ([#10583](https://github.com/community-scripts/ProxmoxVE/pull/10583)) ### 📚 Documentation - [gh] New Script template update [@tremor021](https://github.com/tremor021) ([#10607](https://github.com/community-scripts/ProxmoxVE/pull/10607)) - chore: bump copyright to 2026 - happy new year [@CrazyWolf13](https://github.com/CrazyWolf13) ([#10585](https://github.com/community-scripts/ProxmoxVE/pull/10585)) ### 📂 Github - re-add shellcheck exclusions [@CrazyWolf13](https://github.com/CrazyWolf13) ([#10586](https://github.com/community-scripts/ProxmoxVE/pull/10586)) ## 2026-01-05 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - reitti: add postgis extension to PostgreSQL DB setup [@MickLesk](https://github.com/MickLesk) ([#10555](https://github.com/community-scripts/ProxmoxVE/pull/10555)) - openWRT: separate disk attachment and resizing in VM setup [@MickLesk](https://github.com/MickLesk) ([#10557](https://github.com/community-scripts/ProxmoxVE/pull/10557)) - paperless-ai: Set TMPDIR for pip to use disk during install [@MickLesk](https://github.com/MickLesk) ([#10559](https://github.com/community-scripts/ProxmoxVE/pull/10559)) - #### 🔧 Refactor - Refactor: Monica [@tremor021](https://github.com/tremor021) ([#10552](https://github.com/community-scripts/ProxmoxVE/pull/10552)) - Upgrade Wazuh LXC Container to Debian 13 [@heinemannj](https://github.com/heinemannj) ([#10551](https://github.com/community-scripts/ProxmoxVE/pull/10551)) - Upgrade evcc LXC to Debian 13 [@heinemannj](https://github.com/heinemannj) ([#10548](https://github.com/community-scripts/ProxmoxVE/pull/10548)) ### 💾 Core - #### 🔧 Refactor - Harden setup_hwaccel for old Intel GPUs [@MickLesk](https://github.com/MickLesk) ([#10556](https://github.com/community-scripts/ProxmoxVE/pull/10556)) ### 🧰 Tools - #### 🔧 Refactor - Refactor: IP-Tag (Multiple IP / Performance / Execution Time) [@MickLesk](https://github.com/MickLesk) ([#10558](https://github.com/community-scripts/ProxmoxVE/pull/10558)) ## 2026-01-04 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - PocketID: Update PocketID for 2.x [@tremor021](https://github.com/tremor021) ([#10506](https://github.com/community-scripts/ProxmoxVE/pull/10506)) - fix: reitti: nginx [@CrazyWolf13](https://github.com/CrazyWolf13) ([#10511](https://github.com/community-scripts/ProxmoxVE/pull/10511)) - MagicMirror: bump to nodejs 24 [@MickLesk](https://github.com/MickLesk) ([#10534](https://github.com/community-scripts/ProxmoxVE/pull/10534)) - #### 🔧 Refactor - Refactor: SFTPGo [@tremor021](https://github.com/tremor021) ([#10518](https://github.com/community-scripts/ProxmoxVE/pull/10518)) - Refactor: Pelican Wings [@tremor021](https://github.com/tremor021) ([#10517](https://github.com/community-scripts/ProxmoxVE/pull/10517)) - Refactor: Pelican Panel [@tremor021](https://github.com/tremor021) ([#10516](https://github.com/community-scripts/ProxmoxVE/pull/10516)) - Refactor: Audiobookshelf [@tremor021](https://github.com/tremor021) ([#10519](https://github.com/community-scripts/ProxmoxVE/pull/10519)) ### 💾 Core - #### 🐞 Bug Fixes - Export IPV6_METHOD to trigger verb_ip6() function [@remz1337](https://github.com/remz1337) ([#10538](https://github.com/community-scripts/ProxmoxVE/pull/10538)) ### 🌐 Website - #### 📝 Script Information - Prowlarr: Update config_path [@tremor021](https://github.com/tremor021) ([#10504](https://github.com/community-scripts/ProxmoxVE/pull/10504)) ## 2026-01-03 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Fix ownership and permissions for InvoiceNinja setup [@twinzdragonz](https://github.com/twinzdragonz) ([#10298](https://github.com/community-scripts/ProxmoxVE/pull/10298)) - Fix headscale Caddyfile to pass non-API URLs [@IlyaSemenov](https://github.com/IlyaSemenov) ([#10493](https://github.com/community-scripts/ProxmoxVE/pull/10493)) ### 💾 Core - #### 🔧 Refactor - [core]: Preserve log files [@tremor021](https://github.com/tremor021) ([#10509](https://github.com/community-scripts/ProxmoxVE/pull/10509)) ### ❔ Uncategorized - Wireguard: Update WGDashboard notes URL to the new link [@tremor021](https://github.com/tremor021) ([#10496](https://github.com/community-scripts/ProxmoxVE/pull/10496)) - InvoiceNinja: Update database credentias information [@tremor021](https://github.com/tremor021) ([#10497](https://github.com/community-scripts/ProxmoxVE/pull/10497)) ## 2026-01-02 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Fix Intel Level Zero package conflict on Debian 13 [@Copilot](https://github.com/Copilot) ([#10467](https://github.com/community-scripts/ProxmoxVE/pull/10467)) ### ❔ Uncategorized - Extend guidance for changing the immich upload location for #10447 [@jshprentz](https://github.com/jshprentz) ([#10475](https://github.com/community-scripts/ProxmoxVE/pull/10475)) ## 2026-01-01 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - fix(sabnzbd): update script now migrates old service files to use venv Python [@vidonnus](https://github.com/vidonnus) ([#10466](https://github.com/community-scripts/ProxmoxVE/pull/10466)) - fix(bazarr): update script now migrates old service files to use venv Python [@vidonnus](https://github.com/vidonnus) ([#10459](https://github.com/community-scripts/ProxmoxVE/pull/10459)) - fix #10453 broken sonarqube update [@Karlito83](https://github.com/Karlito83) ([#10456](https://github.com/community-scripts/ProxmoxVE/pull/10456)) ### 💾 Core - #### 🐞 Bug Fixes - Fix MariaDB runtime directory persistence on container reboot [@Copilot](https://github.com/Copilot) ([#10468](https://github.com/community-scripts/ProxmoxVE/pull/10468)) ================================================ FILE: .github/changelogs/2026/02.md ================================================ ## 2026-02-28 ### 🚀 Updated Scripts - Update Reactive Resume install script with useful .env information for reverse proxy setup [@Mazianni](https://github.com/Mazianni) ([#12401](https://github.com/community-scripts/ProxmoxVE/pull/12401)) - #### 🐞 Bug Fixes - gramps-web: install addons (FilterRules) for relationship diagram [@MickLesk](https://github.com/MickLesk) ([#12387](https://github.com/community-scripts/ProxmoxVE/pull/12387)) - [Fix] Immich: Change `sed` command to fully replace line in postgresql.conf [@vhsdream](https://github.com/vhsdream) ([#12429](https://github.com/community-scripts/ProxmoxVE/pull/12429)) - [FIX] Immich: fix Openvino memory leak during OCR; improve HW-accelerated ML performance [@vhsdream](https://github.com/vhsdream) ([#12426](https://github.com/community-scripts/ProxmoxVE/pull/12426)) - Fix default tag for ioBroker LXC install [@josefglatz](https://github.com/josefglatz) ([#12423](https://github.com/community-scripts/ProxmoxVE/pull/12423)) - Ombi: Add database.json [@hraphael](https://github.com/hraphael) ([#12412](https://github.com/community-scripts/ProxmoxVE/pull/12412)) - Dawarich: add missing build deps and handle seed failure [@MickLesk](https://github.com/MickLesk) ([#12410](https://github.com/community-scripts/ProxmoxVE/pull/12410)) - pangolin: increase hdd to 10G [@MickLesk](https://github.com/MickLesk) ([#12409](https://github.com/community-scripts/ProxmoxVE/pull/12409)) - #### ✨ New Features - BookLore: add additional JVM flags [@vhsdream](https://github.com/vhsdream) ([#12421](https://github.com/community-scripts/ProxmoxVE/pull/12421)) ### 🗑️ Deleted Scripts - Delete Palmr [@vhsdream](https://github.com/vhsdream) ([#12399](https://github.com/community-scripts/ProxmoxVE/pull/12399)) ### 💾 Core - #### 🐞 Bug Fixes - core: read from /dev/tty in all interactive prompts | fix empty or cropped logs due build process [@MickLesk](https://github.com/MickLesk) ([#12406](https://github.com/community-scripts/ProxmoxVE/pull/12406)) ## 2026-02-27 ### 🆕 New Scripts - Strapi ([#12320](https://github.com/community-scripts/ProxmoxVE/pull/12320)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - TrueNAS VM: filter out new nightlies with MASTER [@juronja](https://github.com/juronja) ([#12355](https://github.com/community-scripts/ProxmoxVE/pull/12355)) ### 💾 Core - #### ✨ New Features - core: graceful fallback for apt-get update failures [@MickLesk](https://github.com/MickLesk) ([#12386](https://github.com/community-scripts/ProxmoxVE/pull/12386)) - core: Improve error outputs across core functions [@MickLesk](https://github.com/MickLesk) ([#12378](https://github.com/community-scripts/ProxmoxVE/pull/12378)) ## 2026-02-26 ### 🆕 New Scripts - Kima-Hub ([#12319](https://github.com/community-scripts/ProxmoxVE/pull/12319)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - tools.func: update glx alternatives / nvidia alternative if nvidia glx are missing [@MickLesk](https://github.com/MickLesk) ([#12372](https://github.com/community-scripts/ProxmoxVE/pull/12372)) - hotfix: overseer version [@CrazyWolf13](https://github.com/CrazyWolf13) ([#12366](https://github.com/community-scripts/ProxmoxVE/pull/12366)) - #### ✨ New Features - Add ffmpeg for booklore (ffprobe) [@MickLesk](https://github.com/MickLesk) ([#12371](https://github.com/community-scripts/ProxmoxVE/pull/12371)) - [QOL] Immich: add warning regarding library compilation time [@vhsdream](https://github.com/vhsdream) ([#12345](https://github.com/community-scripts/ProxmoxVE/pull/12345)) ### 🧰 Tools - #### 🐞 Bug Fixes - Improves adguardhome-sync addon when running on alpine LXCs [@Darkangeel-hd](https://github.com/Darkangeel-hd) ([#12362](https://github.com/community-scripts/ProxmoxVE/pull/12362)) - #### ✨ New Features - Add Alpine support and improve Tailscale install [@MickLesk](https://github.com/MickLesk) ([#12370](https://github.com/community-scripts/ProxmoxVE/pull/12370)) ### 📚 Documentation - fix wrong link on contributions README.md [@Darkangeel-hd](https://github.com/Darkangeel-hd) ([#12363](https://github.com/community-scripts/ProxmoxVE/pull/12363)) ### 📂 Github - github: add workflow to autom. close unauthorized new-script PRs [@MickLesk](https://github.com/MickLesk) ([#12356](https://github.com/community-scripts/ProxmoxVE/pull/12356)) ## 2026-02-25 ### 🆕 New Scripts - Zerobyte ([#12321](https://github.com/community-scripts/ProxmoxVE/pull/12321)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - fix: overseer migration [@CrazyWolf13](https://github.com/CrazyWolf13) ([#12340](https://github.com/community-scripts/ProxmoxVE/pull/12340)) - add: vikunja: daemon reload [@CrazyWolf13](https://github.com/CrazyWolf13) ([#12323](https://github.com/community-scripts/ProxmoxVE/pull/12323)) - opnsense-VM: Use ip link to verify bridge existence [@MickLesk](https://github.com/MickLesk) ([#12329](https://github.com/community-scripts/ProxmoxVE/pull/12329)) - wger: Use $http_host for proxy Host header [@MickLesk](https://github.com/MickLesk) ([#12327](https://github.com/community-scripts/ProxmoxVE/pull/12327)) - Passbolt: Update Nginx config `client_max_body_size` [@tremor021](https://github.com/tremor021) ([#12313](https://github.com/community-scripts/ProxmoxVE/pull/12313)) - Zammad: configure Elasticsearch before zammad start [@MickLesk](https://github.com/MickLesk) ([#12308](https://github.com/community-scripts/ProxmoxVE/pull/12308)) - #### 🔧 Refactor - OpenProject: Various fixes [@tremor021](https://github.com/tremor021) ([#12246](https://github.com/community-scripts/ProxmoxVE/pull/12246)) ### 💾 Core - #### 🐞 Bug Fixes - Fix detection of ssh keys [@1-tempest](https://github.com/1-tempest) ([#12230](https://github.com/community-scripts/ProxmoxVE/pull/12230)) - #### ✨ New Features - tools.func: Improve GitHub/Codeberg API error handling and error output [@MickLesk](https://github.com/MickLesk) ([#12330](https://github.com/community-scripts/ProxmoxVE/pull/12330)) - #### 🔧 Refactor - core: remove duplicate traps, consolidate error handling and harden signal traps [@MickLesk](https://github.com/MickLesk) ([#12316](https://github.com/community-scripts/ProxmoxVE/pull/12316)) ### 📂 Github - github: improvements for node drift wf [@MickLesk](https://github.com/MickLesk) ([#12309](https://github.com/community-scripts/ProxmoxVE/pull/12309)) ## 2026-02-24 ### 🚀 Updated Scripts - several scripts: add additional github link in source [@MickLesk](https://github.com/MickLesk) ([#12282](https://github.com/community-scripts/ProxmoxVE/pull/12282)) - adds further documentation during the installation script. [@d12rio](https://github.com/d12rio) ([#12248](https://github.com/community-scripts/ProxmoxVE/pull/12248)) - #### 🐞 Bug Fixes - [Fix] PatchMon: remove VITE_API_URL from frontend env [@vhsdream](https://github.com/vhsdream) ([#12294](https://github.com/community-scripts/ProxmoxVE/pull/12294)) - fix(searxng): remove orphaned fi causing syntax error [@mark-jeffrey](https://github.com/mark-jeffrey) ([#12283](https://github.com/community-scripts/ProxmoxVE/pull/12283)) - Refactor n8n [@MickLesk](https://github.com/MickLesk) ([#12264](https://github.com/community-scripts/ProxmoxVE/pull/12264)) - Firefly: PHP bump [@tremor021](https://github.com/tremor021) ([#12247](https://github.com/community-scripts/ProxmoxVE/pull/12247)) - #### ✨ New Features - Databasus: add mariadb path for mysql/mariadb backups | add mongodb database tools [@MickLesk](https://github.com/MickLesk) ([#12259](https://github.com/community-scripts/ProxmoxVE/pull/12259)) - make searxng updateable [@shtefko](https://github.com/shtefko) ([#12207](https://github.com/community-scripts/ProxmoxVE/pull/12207)) - #### 💥 Breaking Changes - fix: wealthfolio for v3 [@CrazyWolf13](https://github.com/CrazyWolf13) ([#11765](https://github.com/community-scripts/ProxmoxVE/pull/11765)) - #### 🔧 Refactor - bump various scripts from Node 22 to 24 [@MickLesk](https://github.com/MickLesk) ([#12265](https://github.com/community-scripts/ProxmoxVE/pull/12265)) ### 💾 Core - #### 🐞 Bug Fixes - core: fix broken "command not found" after err_trap [@MickLesk](https://github.com/MickLesk) ([#12280](https://github.com/community-scripts/ProxmoxVE/pull/12280)) - #### ✨ New Features - tools.func: add get_latest_gh_tag helper function [@MickLesk](https://github.com/MickLesk) ([#12261](https://github.com/community-scripts/ProxmoxVE/pull/12261)) ### 🧰 Tools - Arcane ([#12263](https://github.com/community-scripts/ProxmoxVE/pull/12263)) ### 📂 Github - github: add weekly Node.js version drift check workflow [@MickLesk](https://github.com/MickLesk) ([#12267](https://github.com/community-scripts/ProxmoxVE/pull/12267)) - add: workflow to close stale PRs [@CrazyWolf13](https://github.com/CrazyWolf13) ([#12243](https://github.com/community-scripts/ProxmoxVE/pull/12243)) ## 2026-02-23 ### 🆕 New Scripts - SeaweedFS ([#12220](https://github.com/community-scripts/ProxmoxVE/pull/12220)) - Sonobarr ([#12221](https://github.com/community-scripts/ProxmoxVE/pull/12221)) - SparkyFitness ([#12185](https://github.com/community-scripts/ProxmoxVE/pull/12185)) - Frigate v16.4 [@MickLesk](https://github.com/MickLesk) ([#11887](https://github.com/community-scripts/ProxmoxVE/pull/11887)) ### 🚀 Updated Scripts - #### ✨ New Features - memos: unpin version due new release artifacts [@MickLesk](https://github.com/MickLesk) ([#12224](https://github.com/community-scripts/ProxmoxVE/pull/12224)) - core: Enhance signal handling, reported "status" and logs [@MickLesk](https://github.com/MickLesk) ([#12216](https://github.com/community-scripts/ProxmoxVE/pull/12216)) - #### 🔧 Refactor - booklore v2: embed frontend, bump Java to 25, remove nginx [@MickLesk](https://github.com/MickLesk) ([#12223](https://github.com/community-scripts/ProxmoxVE/pull/12223)) ### 🗑️ Deleted Scripts - Remove: Huntarr (deprecated & Security) [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#12226](https://github.com/community-scripts/ProxmoxVE/pull/12226)) ### 💾 Core - #### 🔧 Refactor - core: Improve error handling and logging for LXC builds [@MickLesk](https://github.com/MickLesk) ([#12208](https://github.com/community-scripts/ProxmoxVE/pull/12208)) ### 🌐 Website - #### 🐞 Bug Fixes - calibre-web: update default credentials [@LaevaertK](https://github.com/LaevaertK) ([#12201](https://github.com/community-scripts/ProxmoxVE/pull/12201)) - #### 📝 Script Information - chore: update Frigate documentation and website URLs [@JohnICB](https://github.com/JohnICB) ([#12218](https://github.com/community-scripts/ProxmoxVE/pull/12218)) ## 2026-02-22 ### 🆕 New Scripts - Gramps-Web ([#12157](https://github.com/community-scripts/ProxmoxVE/pull/12157)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - fix: Apache Guacamole - bump to Temurin JDK 17 to resolve Debian 13 (Trixie) install failure [@Copilot](https://github.com/Copilot) ([#12161](https://github.com/community-scripts/ProxmoxVE/pull/12161)) - Docker-VM: add error handling for virt-customize finalization [@MickLesk](https://github.com/MickLesk) ([#12127](https://github.com/community-scripts/ProxmoxVE/pull/12127)) - [Fix] Sure: add Sidekiq service [@vhsdream](https://github.com/vhsdream) ([#12186](https://github.com/community-scripts/ProxmoxVE/pull/12186)) - #### ✨ New Features - Refactor & Bump to v2: Plex [@MickLesk](https://github.com/MickLesk) ([#12179](https://github.com/community-scripts/ProxmoxVE/pull/12179)) - #### 🔧 Refactor - karakeep: bump to node 24 [@CrazyWolf13](https://github.com/CrazyWolf13) ([#12183](https://github.com/community-scripts/ProxmoxVE/pull/12183)) ### 💾 Core - #### ✨ New Features - tools.func: add GitHub API rate-limit detection and GITHUB_TOKEN support [@MickLesk](https://github.com/MickLesk) ([#12176](https://github.com/community-scripts/ProxmoxVE/pull/12176)) ### 🧰 Tools - CR*NMASTER ([#12065](https://github.com/community-scripts/ProxmoxVE/pull/12065)) - #### 🔧 Refactor - Update package management commands in clean-lxcs.sh [@heinemannj](https://github.com/heinemannj) ([#12166](https://github.com/community-scripts/ProxmoxVE/pull/12166)) ### ❔ Uncategorized - calibre-web: Update logo URL [@MickLesk](https://github.com/MickLesk) ([#12178](https://github.com/community-scripts/ProxmoxVE/pull/12178)) ## 2026-02-21 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Pangolin: restore config before db migration, use drizzle-kit push [@MickLesk](https://github.com/MickLesk) ([#12130](https://github.com/community-scripts/ProxmoxVE/pull/12130)) - PLANKA: fix msg's [@danielalanbates](https://github.com/danielalanbates) ([#12143](https://github.com/community-scripts/ProxmoxVE/pull/12143)) ### 🌐 Website - #### 📝 Script Information - MediaManager: Update documentation URL [@tremor021](https://github.com/tremor021) ([#12154](https://github.com/community-scripts/ProxmoxVE/pull/12154)) ## 2026-02-20 ### 🆕 New Scripts - Sure ([#12114](https://github.com/community-scripts/ProxmoxVE/pull/12114)) - Calibre-Web ([#12115](https://github.com/community-scripts/ProxmoxVE/pull/12115)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Zammad: fix Elasticsearch JVM config and add daemon-reload [@MickLesk](https://github.com/MickLesk) ([#12125](https://github.com/community-scripts/ProxmoxVE/pull/12125)) - Huntarr: add build-essential for native pip dependencies [@MickLesk](https://github.com/MickLesk) ([#12126](https://github.com/community-scripts/ProxmoxVE/pull/12126)) - Dokploy: fix update function [@vhsdream](https://github.com/vhsdream) ([#12116](https://github.com/community-scripts/ProxmoxVE/pull/12116)) - #### 💥 Breaking Changes - recyclarr: adjust paths for v8.0 breaking changes [@MickLesk](https://github.com/MickLesk) ([#12129](https://github.com/community-scripts/ProxmoxVE/pull/12129)) - #### 🔧 Refactor - Planka: migrate data paths to new v2 directory structure [@MickLesk](https://github.com/MickLesk) ([#12128](https://github.com/community-scripts/ProxmoxVE/pull/12128)) ### 🌐 Website - #### 📝 Script Information - fixen broken link to dawarich documentation [@RiX012](https://github.com/RiX012) ([#12103](https://github.com/community-scripts/ProxmoxVE/pull/12103)) ## 2026-02-19 ### 🆕 New Scripts - TrueNAS-VM ([#12059](https://github.com/community-scripts/ProxmoxVE/pull/12059)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - add: patchmon breaking change msg [@CrazyWolf13](https://github.com/CrazyWolf13) ([#12075](https://github.com/community-scripts/ProxmoxVE/pull/12075)) - LibreNMS: Various fixes [@tremor021](https://github.com/tremor021) ([#12089](https://github.com/community-scripts/ProxmoxVE/pull/12089)) ### 🌐 Website - #### 📝 Script Information - truenas-vm: slug fix for source code link [@juronja](https://github.com/juronja) ([#12088](https://github.com/community-scripts/ProxmoxVE/pull/12088)) ## 2026-02-18 ### 🚀 Updated Scripts - #### 💥 Breaking Changes - [Fix] PatchMon: use `SERVER_PORT` in Nginx config if set in env [@vhsdream](https://github.com/vhsdream) ([#12053](https://github.com/community-scripts/ProxmoxVE/pull/12053)) ### 💾 Core - #### ✨ New Features - core: Execution ID & Telemetry Improvements [@MickLesk](https://github.com/MickLesk) ([#12041](https://github.com/community-scripts/ProxmoxVE/pull/12041)) ## 2026-02-17 ### 🆕 New Scripts - Databasus ([#12018](https://github.com/community-scripts/ProxmoxVE/pull/12018)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - [Hotfix] Cleanuparr: backup config before update [@vhsdream](https://github.com/vhsdream) ([#12039](https://github.com/community-scripts/ProxmoxVE/pull/12039)) - fix: pterodactyl-panel add symlink [@CrazyWolf13](https://github.com/CrazyWolf13) ([#11997](https://github.com/community-scripts/ProxmoxVE/pull/11997)) ### 💾 Core - #### 🐞 Bug Fixes - core: call get_lxc_ip in start() before updates [@MickLesk](https://github.com/MickLesk) ([#12015](https://github.com/community-scripts/ProxmoxVE/pull/12015)) - #### ✨ New Features - tools/pve: add data analytics / formatting / linting [@MickLesk](https://github.com/MickLesk) ([#12034](https://github.com/community-scripts/ProxmoxVE/pull/12034)) - core: smart recovery for failed installs | extend exit_codes [@MickLesk](https://github.com/MickLesk) ([#11221](https://github.com/community-scripts/ProxmoxVE/pull/11221)) - #### 🔧 Refactor - core: error-handler improvements | better exit_code handling | better tools.func source check [@MickLesk](https://github.com/MickLesk) ([#12019](https://github.com/community-scripts/ProxmoxVE/pull/12019)) ### 🧰 Tools - #### 🔧 Refactor - Immich Public Proxy: centralize and fix systemd service creation [@MickLesk](https://github.com/MickLesk) ([#12025](https://github.com/community-scripts/ProxmoxVE/pull/12025)) ### 📚 Documentation - fix contribution/setup-fork [@andreasabeck](https://github.com/andreasabeck) ([#12047](https://github.com/community-scripts/ProxmoxVE/pull/12047)) ## 2026-02-16 ### 🆕 New Scripts - RomM ([#11987](https://github.com/community-scripts/ProxmoxVE/pull/11987)) - LinkDing ([#11976](https://github.com/community-scripts/ProxmoxVE/pull/11976)) ### 🚀 Updated Scripts - Opencloud: Pin version to 5.1.0 [@vhsdream](https://github.com/vhsdream) ([#12004](https://github.com/community-scripts/ProxmoxVE/pull/12004)) - #### 🐞 Bug Fixes - Tududi: Fix sed command for DB_FILE configuration [@tremor021](https://github.com/tremor021) ([#11988](https://github.com/community-scripts/ProxmoxVE/pull/11988)) - slskd: fix exit position [@MickLesk](https://github.com/MickLesk) ([#11963](https://github.com/community-scripts/ProxmoxVE/pull/11963)) - cryptpad: restore config earlier and run onlyoffice upgrade [@MickLesk](https://github.com/MickLesk) ([#11964](https://github.com/community-scripts/ProxmoxVE/pull/11964)) - jellyseerr/overseerr: Migrate update script to Seerr; prompt rerun [@MickLesk](https://github.com/MickLesk) ([#11965](https://github.com/community-scripts/ProxmoxVE/pull/11965)) - #### 🔧 Refactor - core/vm's: ensure script state is sent on script exit [@MickLesk](https://github.com/MickLesk) ([#11991](https://github.com/community-scripts/ProxmoxVE/pull/11991)) - Vaultwarden: export VW_VERSION as version number [@MickLesk](https://github.com/MickLesk) ([#11966](https://github.com/community-scripts/ProxmoxVE/pull/11966)) - Zabbix: Improve zabbix-agent service detection [@MickLesk](https://github.com/MickLesk) ([#11968](https://github.com/community-scripts/ProxmoxVE/pull/11968)) ### 💾 Core - #### ✨ New Features - tools.func: ensure /usr/local/bin PATH persists for pct enter sessions [@MickLesk](https://github.com/MickLesk) ([#11970](https://github.com/community-scripts/ProxmoxVE/pull/11970)) - #### 🔧 Refactor - core: remove duplicate error handler from alpine-install.func [@MickLesk](https://github.com/MickLesk) ([#11971](https://github.com/community-scripts/ProxmoxVE/pull/11971)) ### 📂 Github - github: add "website" label if "json" changed [@MickLesk](https://github.com/MickLesk) ([#11975](https://github.com/community-scripts/ProxmoxVE/pull/11975)) ### 🌐 Website - #### 📝 Script Information - Update Wishlist LXC webpage to include reverse proxy info [@summoningpixels](https://github.com/summoningpixels) ([#11973](https://github.com/community-scripts/ProxmoxVE/pull/11973)) - Update OpenCloud LXC webpage to include services ports [@summoningpixels](https://github.com/summoningpixels) ([#11969](https://github.com/community-scripts/ProxmoxVE/pull/11969)) ## 2026-02-15 ### 🆕 New Scripts - ebusd ([#11942](https://github.com/community-scripts/ProxmoxVE/pull/11942)) - add: seer script and migrations [@CrazyWolf13](https://github.com/CrazyWolf13) ([#11930](https://github.com/community-scripts/ProxmoxVE/pull/11930)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Fix seerr URL in jellyseerr script [@lucacome](https://github.com/lucacome) ([#11951](https://github.com/community-scripts/ProxmoxVE/pull/11951)) - Fix jellyseer and overseer script replacement [@lucacome](https://github.com/lucacome) ([#11949](https://github.com/community-scripts/ProxmoxVE/pull/11949)) - Tautulli: Add setuptools < 81 [@tremor021](https://github.com/tremor021) ([#11943](https://github.com/community-scripts/ProxmoxVE/pull/11943)) - #### 💥 Breaking Changes - Refactor: Patchmon [@vhsdream](https://github.com/vhsdream) ([#11888](https://github.com/community-scripts/ProxmoxVE/pull/11888)) ## 2026-02-14 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Increase disk allocation for OpenWebUI and Ollama to prevent installation failures [@Copilot](https://github.com/Copilot) ([#11920](https://github.com/community-scripts/ProxmoxVE/pull/11920)) ### 💾 Core - #### 🐞 Bug Fixes - core: handle missing RAM speed in nested VMs [@MickLesk](https://github.com/MickLesk) ([#11913](https://github.com/community-scripts/ProxmoxVE/pull/11913)) - #### ✨ New Features - core: overwriteable app version [@CrazyWolf13](https://github.com/CrazyWolf13) ([#11753](https://github.com/community-scripts/ProxmoxVE/pull/11753)) - core: validate container IDs cluster-wide across all nodes [@MickLesk](https://github.com/MickLesk) ([#11906](https://github.com/community-scripts/ProxmoxVE/pull/11906)) - core: improve error reporting with structured error strings and better categorization + output formatting [@MickLesk](https://github.com/MickLesk) ([#11907](https://github.com/community-scripts/ProxmoxVE/pull/11907)) - core: unified logging system with combined logs [@MickLesk](https://github.com/MickLesk) ([#11761](https://github.com/community-scripts/ProxmoxVE/pull/11761)) ### 🧰 Tools - lxc-updater: add patchmon aware [@failure101](https://github.com/failure101) ([#11905](https://github.com/community-scripts/ProxmoxVE/pull/11905)) ### 🌐 Website - #### 📝 Script Information - Disable UniFi script - APT packages no longer available [@Copilot](https://github.com/Copilot) ([#11898](https://github.com/community-scripts/ProxmoxVE/pull/11898)) ## 2026-02-13 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - OpenWebUI: pin numba constraint [@MickLesk](https://github.com/MickLesk) ([#11874](https://github.com/community-scripts/ProxmoxVE/pull/11874)) - Planka: add migrate step to update function [@ZimmermannLeon](https://github.com/ZimmermannLeon) ([#11877](https://github.com/community-scripts/ProxmoxVE/pull/11877)) - Pangolin: switch sqlite-specific back to generic [@MickLesk](https://github.com/MickLesk) ([#11868](https://github.com/community-scripts/ProxmoxVE/pull/11868)) - [Hotfix] Jotty: Copy contents of config backup into /opt/jotty/config [@vhsdream](https://github.com/vhsdream) ([#11864](https://github.com/community-scripts/ProxmoxVE/pull/11864)) - #### 🔧 Refactor - Refactor: Radicale [@vhsdream](https://github.com/vhsdream) ([#11850](https://github.com/community-scripts/ProxmoxVE/pull/11850)) - chore(donetick): add config entry for v0.1.73 [@tomfrenzel](https://github.com/tomfrenzel) ([#11872](https://github.com/community-scripts/ProxmoxVE/pull/11872)) ### 💾 Core - #### 🔧 Refactor - core: retry reporting with fallback payloads [@MickLesk](https://github.com/MickLesk) ([#11885](https://github.com/community-scripts/ProxmoxVE/pull/11885)) ### 📡 API - #### ✨ New Features - error-handler: Implement json_escape and enhance error handling [@MickLesk](https://github.com/MickLesk) ([#11875](https://github.com/community-scripts/ProxmoxVE/pull/11875)) ### 🌐 Website - #### 📝 Script Information - SQLServer-2025: add PVE9/Kernel 6.x incompatibility warning [@MickLesk](https://github.com/MickLesk) ([#11829](https://github.com/community-scripts/ProxmoxVE/pull/11829)) ## 2026-02-12 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - EMQX: increase disk to 6GB and add optional MQ disable prompt [@MickLesk](https://github.com/MickLesk) ([#11844](https://github.com/community-scripts/ProxmoxVE/pull/11844)) - Increased the Grafana container default disk size. [@shtefko](https://github.com/shtefko) ([#11840](https://github.com/community-scripts/ProxmoxVE/pull/11840)) - Pangolin: Update database generation command in install script [@tremor021](https://github.com/tremor021) ([#11825](https://github.com/community-scripts/ProxmoxVE/pull/11825)) - Deluge: add python3-setuptools as dep [@MickLesk](https://github.com/MickLesk) ([#11833](https://github.com/community-scripts/ProxmoxVE/pull/11833)) - Dispatcharr: migrate to uv sync [@MickLesk](https://github.com/MickLesk) ([#11831](https://github.com/community-scripts/ProxmoxVE/pull/11831)) - #### ✨ New Features - Archlinux-VM: fix LVM/LVM-thin storage and improve error reporting | VM's add correct exit_code for analytics [@MickLesk](https://github.com/MickLesk) ([#11842](https://github.com/community-scripts/ProxmoxVE/pull/11842)) - Debian13-VM: Optimize First Boot & add noCloud/Cloud Selection [@MickLesk](https://github.com/MickLesk) ([#11810](https://github.com/community-scripts/ProxmoxVE/pull/11810)) ### 💾 Core - #### ✨ New Features - tools.func: auto-detect binary vs armored GPG keys in setup_deb822_repo [@MickLesk](https://github.com/MickLesk) ([#11841](https://github.com/community-scripts/ProxmoxVE/pull/11841)) - core: remove old Go API and extend misc/api.func with new backend [@MickLesk](https://github.com/MickLesk) ([#11822](https://github.com/community-scripts/ProxmoxVE/pull/11822)) - #### 🔧 Refactor - error_handler: prevent stuck 'installing' status [@MickLesk](https://github.com/MickLesk) ([#11845](https://github.com/community-scripts/ProxmoxVE/pull/11845)) ### 🧰 Tools - #### 🐞 Bug Fixes - Tailscale: fix DNS check and keyrings directory issues [@MickLesk](https://github.com/MickLesk) ([#11837](https://github.com/community-scripts/ProxmoxVE/pull/11837)) ## 2026-02-11 ### 🆕 New Scripts - Draw.io ([#11788](https://github.com/community-scripts/ProxmoxVE/pull/11788)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - dispatcharr: include port 9191 in success-message [@MickLesk](https://github.com/MickLesk) ([#11808](https://github.com/community-scripts/ProxmoxVE/pull/11808)) - fix: make donetick 0.1.71 compatible [@tomfrenzel](https://github.com/tomfrenzel) ([#11804](https://github.com/community-scripts/ProxmoxVE/pull/11804)) - Kasm: Support new version URL format without hash suffix [@MickLesk](https://github.com/MickLesk) ([#11787](https://github.com/community-scripts/ProxmoxVE/pull/11787)) - LibreTranslate: Remove Torch [@tremor021](https://github.com/tremor021) ([#11783](https://github.com/community-scripts/ProxmoxVE/pull/11783)) - Snowshare: fix update script [@TuroYT](https://github.com/TuroYT) ([#11726](https://github.com/community-scripts/ProxmoxVE/pull/11726)) - #### ✨ New Features - [Feature] OpenCloud: support PosixFS Collaborative Mode [@vhsdream](https://github.com/vhsdream) ([#11806](https://github.com/community-scripts/ProxmoxVE/pull/11806)) ### 💾 Core - #### 🔧 Refactor - core: respect EDITOR variable for config editing [@ls-root](https://github.com/ls-root) ([#11693](https://github.com/community-scripts/ProxmoxVE/pull/11693)) ### 📚 Documentation - Fix formatting in kutt.json notes section [@tiagodenoronha](https://github.com/tiagodenoronha) ([#11774](https://github.com/community-scripts/ProxmoxVE/pull/11774)) ## 2026-02-10 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Immich: Pin version to 2.5.6 [@vhsdream](https://github.com/vhsdream) ([#11775](https://github.com/community-scripts/ProxmoxVE/pull/11775)) - Libretranslate: Fix setuptools [@tremor021](https://github.com/tremor021) ([#11772](https://github.com/community-scripts/ProxmoxVE/pull/11772)) - Element Synapse: prevent systemd invoke failure during apt install [@MickLesk](https://github.com/MickLesk) ([#11758](https://github.com/community-scripts/ProxmoxVE/pull/11758)) - #### ✨ New Features - Refactor: Slskd & Soularr [@vhsdream](https://github.com/vhsdream) ([#11674](https://github.com/community-scripts/ProxmoxVE/pull/11674)) ### 🗑️ Deleted Scripts - move paperless-exporter from LXC to addon ([#11737](https://github.com/community-scripts/ProxmoxVE/pull/11737)) ### 🧰 Tools - #### 🐞 Bug Fixes - feat: improve storage parsing & add guestname [@carlosmaroot](https://github.com/carlosmaroot) ([#11752](https://github.com/community-scripts/ProxmoxVE/pull/11752)) ### 📂 Github - Github-Version Workflow: include addon scripts in extraction [@MickLesk](https://github.com/MickLesk) ([#11757](https://github.com/community-scripts/ProxmoxVE/pull/11757)) ### 🌐 Website - #### 📝 Script Information - Snowshare: fix typo in config file path on website [@BirdMakingStuff](https://github.com/BirdMakingStuff) ([#11754](https://github.com/community-scripts/ProxmoxVE/pull/11754)) ## 2026-02-09 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - several scripts: add --clear to uv venv calls for uv 0.10 compatibility [@MickLesk](https://github.com/MickLesk) ([#11723](https://github.com/community-scripts/ProxmoxVE/pull/11723)) - Koillection: ensure setup_composer is in update script [@MickLesk](https://github.com/MickLesk) ([#11734](https://github.com/community-scripts/ProxmoxVE/pull/11734)) - PeaNUT: symlink server.js after update [@vhsdream](https://github.com/vhsdream) ([#11696](https://github.com/community-scripts/ProxmoxVE/pull/11696)) - Umlautadaptarr: use release appsettings.json instead of hardcoded copy [@MickLesk](https://github.com/MickLesk) ([#11725](https://github.com/community-scripts/ProxmoxVE/pull/11725)) - tracearr: prepare for next stable release [@durzo](https://github.com/durzo) ([#11673](https://github.com/community-scripts/ProxmoxVE/pull/11673)) - #### ✨ New Features - remove whiptail from update scripts for unattended update support [@MickLesk](https://github.com/MickLesk) ([#11712](https://github.com/community-scripts/ProxmoxVE/pull/11712)) - #### 🔧 Refactor - Refactor: FileFlows [@tremor021](https://github.com/tremor021) ([#11108](https://github.com/community-scripts/ProxmoxVE/pull/11108)) - Refactor: wger [@MickLesk](https://github.com/MickLesk) ([#11722](https://github.com/community-scripts/ProxmoxVE/pull/11722)) - Nginx-UI: better User Handling | ACME [@MickLesk](https://github.com/MickLesk) ([#11715](https://github.com/community-scripts/ProxmoxVE/pull/11715)) - NginxProxymanager: use better-sqlite3 [@MickLesk](https://github.com/MickLesk) ([#11708](https://github.com/community-scripts/ProxmoxVE/pull/11708)) ### 💾 Core - #### 🔧 Refactor - hwaccel: add libmfx-gen1.2 to Intel Arc setup for QSV support [@MickLesk](https://github.com/MickLesk) ([#11707](https://github.com/community-scripts/ProxmoxVE/pull/11707)) ### 🧰 Tools - #### 🐞 Bug Fixes - addons: ensure curl is installed before use [@MickLesk](https://github.com/MickLesk) ([#11718](https://github.com/community-scripts/ProxmoxVE/pull/11718)) - Netbird (addon): add systemd ordering to start after Docker [@MickLesk](https://github.com/MickLesk) ([#11716](https://github.com/community-scripts/ProxmoxVE/pull/11716)) ### ❔ Uncategorized - Bichon: Update website [@tremor021](https://github.com/tremor021) ([#11711](https://github.com/community-scripts/ProxmoxVE/pull/11711)) ## 2026-02-08 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - feat(healthchecks): add sendalerts service [@Mika56](https://github.com/Mika56) ([#11694](https://github.com/community-scripts/ProxmoxVE/pull/11694)) - ComfyUI: Dynamic Fetch PyTorch Versions [@MickLesk](https://github.com/MickLesk) ([#11657](https://github.com/community-scripts/ProxmoxVE/pull/11657)) - #### 💥 Breaking Changes - Semaphore: switch from Debian to Ubuntu 24.04 [@MickLesk](https://github.com/MickLesk) ([#11670](https://github.com/community-scripts/ProxmoxVE/pull/11670)) ## 2026-02-07 ### 🆕 New Scripts - Checkmate ([#11672](https://github.com/community-scripts/ProxmoxVE/pull/11672)) - Bichon ([#11671](https://github.com/community-scripts/ProxmoxVE/pull/11671)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - NocoDB: pin to v0.301.1 [@MickLesk](https://github.com/MickLesk) ([#11655](https://github.com/community-scripts/ProxmoxVE/pull/11655)) - Pin Memos to v0.25.3 - last version with release binaries [@MickLesk](https://github.com/MickLesk) ([#11658](https://github.com/community-scripts/ProxmoxVE/pull/11658)) - Downgrade: OpenProject | NginxProxyManager | Semaphore to Debian 12 due to persistent SHA1 issues [@MickLesk](https://github.com/MickLesk) ([#11654](https://github.com/community-scripts/ProxmoxVE/pull/11654)) ### 💾 Core - #### ✨ New Features - tools: fallback to previous release when asset is missing [@MickLesk](https://github.com/MickLesk) ([#11660](https://github.com/community-scripts/ProxmoxVE/pull/11660)) ### 📚 Documentation - fix(setup): correctly auto-detect username when using --full [@ls-root](https://github.com/ls-root) ([#11650](https://github.com/community-scripts/ProxmoxVE/pull/11650)) ### 🌐 Website - #### ✨ New Features - feat(frontend): add JSON script import functionality [@ls-root](https://github.com/ls-root) ([#11563](https://github.com/community-scripts/ProxmoxVE/pull/11563)) ## 2026-02-06 ### 🆕 New Scripts - Nightscout ([#11621](https://github.com/community-scripts/ProxmoxVE/pull/11621)) - PVE LXC Apps Updater [@MickLesk](https://github.com/MickLesk) ([#11533](https://github.com/community-scripts/ProxmoxVE/pull/11533)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Immich: supress startup messages for immich-admin [@vhsdream](https://github.com/vhsdream) ([#11635](https://github.com/community-scripts/ProxmoxVE/pull/11635)) - Semaphore: Change Ubuntu release from 'jammy' to 'noble' [@MickLesk](https://github.com/MickLesk) ([#11625](https://github.com/community-scripts/ProxmoxVE/pull/11625)) - Pangolin: replace build:sqlite with db:generate + build [@MickLesk](https://github.com/MickLesk) ([#11616](https://github.com/community-scripts/ProxmoxVE/pull/11616)) - [FIX] OpenCloud: path issues [@vhsdream](https://github.com/vhsdream) ([#11593](https://github.com/community-scripts/ProxmoxVE/pull/11593)) - [FIX] Homepage: preserve public/images & public/icons if they exist [@vhsdream](https://github.com/vhsdream) ([#11594](https://github.com/community-scripts/ProxmoxVE/pull/11594)) - #### ✨ New Features - Shelfmark: remove Chromedriver dep, add URL_BASE env [@vhsdream](https://github.com/vhsdream) ([#11619](https://github.com/community-scripts/ProxmoxVE/pull/11619)) - Immich: pin to v2.5.5 [@vhsdream](https://github.com/vhsdream) ([#11598](https://github.com/community-scripts/ProxmoxVE/pull/11598)) - #### 🔧 Refactor - refactor: homepage [@CrazyWolf13](https://github.com/CrazyWolf13) ([#11605](https://github.com/community-scripts/ProxmoxVE/pull/11605)) ### 💾 Core - #### 🐞 Bug Fixes - fix(core): spinner misalignment [@ls-root](https://github.com/ls-root) ([#11627](https://github.com/community-scripts/ProxmoxVE/pull/11627)) - #### 🔧 Refactor - [Fix] build.func: QOL grammar adjustment for Creating LXC message [@vhsdream](https://github.com/vhsdream) ([#11633](https://github.com/community-scripts/ProxmoxVE/pull/11633)) ### 📚 Documentation - [gh] Update to the New Script request template [@tremor021](https://github.com/tremor021) ([#11612](https://github.com/community-scripts/ProxmoxVE/pull/11612)) ### 🌐 Website - #### 📝 Script Information - Update LXC App Updater JSON to reflect tag override option [@vhsdream](https://github.com/vhsdream) ([#11626](https://github.com/community-scripts/ProxmoxVE/pull/11626)) ### ❔ Uncategorized - Opencloud: fix JSON [@vhsdream](https://github.com/vhsdream) ([#11617](https://github.com/community-scripts/ProxmoxVE/pull/11617)) ## 2026-02-05 ### 🆕 New Scripts - OpenCloud ([#11538](https://github.com/community-scripts/ProxmoxVE/pull/11538)) - Nginx-UI ([#11573](https://github.com/community-scripts/ProxmoxVE/pull/11573)) - New: SQL-Server 2025 | Refactor SQL-Server 2022 [@MickLesk](https://github.com/MickLesk) ([#11546](https://github.com/community-scripts/ProxmoxVE/pull/11546)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - OpenCloud: pin version to 5.0.2; Collabora CSP fix [@vhsdream](https://github.com/vhsdream) ([#11585](https://github.com/community-scripts/ProxmoxVE/pull/11585)) - Wanderer: Fix repo [@tremor021](https://github.com/tremor021) ([#11567](https://github.com/community-scripts/ProxmoxVE/pull/11567)) - #### ✨ New Features - Refactor: Docker-VM (Multi-OS / Cloud-Init / Stabilization) [@MickLesk](https://github.com/MickLesk) ([#9047](https://github.com/community-scripts/ProxmoxVE/pull/9047)) ### 💾 Core - #### ✨ New Features - cloud-init: add interactive SSH key discovery and selection [@MickLesk](https://github.com/MickLesk) ([#11547](https://github.com/community-scripts/ProxmoxVE/pull/11547)) ### 📚 Documentation - github: extend docs / contribution / templates [@MickLesk](https://github.com/MickLesk) ([#10921](https://github.com/community-scripts/ProxmoxVE/pull/10921)) ### 🌐 Website - #### 🐞 Bug Fixes - fix(frontend): theme respective syntax highlighting [@ls-root](https://github.com/ls-root) ([#11565](https://github.com/community-scripts/ProxmoxVE/pull/11565)) ## 2026-02-04 ### 🆕 New Scripts - Wishlist ([#11527](https://github.com/community-scripts/ProxmoxVE/pull/11527)) - WriteFreely ([#11524](https://github.com/community-scripts/ProxmoxVE/pull/11524)) ### 💾 Core - #### ✨ New Features - core: create vm-core.func from dev [@MickLesk](https://github.com/MickLesk) ([#11528](https://github.com/community-scripts/ProxmoxVE/pull/11528)) ### 🌐 Website - #### 🐞 Bug Fixes - fix(frontend): implement weighted search scoring for command menu [@ls-root](https://github.com/ls-root) ([#11534](https://github.com/community-scripts/ProxmoxVE/pull/11534)) ## 2026-02-03 ### 🆕 New Scripts - Wealthfolio ([#11511](https://github.com/community-scripts/ProxmoxVE/pull/11511)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - [FIX] Shelfmark: unpin Chromium version [@vhsdream](https://github.com/vhsdream) ([#11505](https://github.com/community-scripts/ProxmoxVE/pull/11505)) - #### ✨ New Features - [FEAT] Scanopy: automatically update integrated daemon [@vhsdream](https://github.com/vhsdream) ([#11506](https://github.com/community-scripts/ProxmoxVE/pull/11506)) ### 💾 Core - #### 🐞 Bug Fixes - [FIX] tools.func: trim spaces in app_lc when checking for gh release [@vhsdream](https://github.com/vhsdream) ([#11512](https://github.com/community-scripts/ProxmoxVE/pull/11512)) ### 🌐 Website - #### 🐞 Bug Fixes - fix(frontend): decouple table pagination from summary fetching [@ls-root](https://github.com/ls-root) ([#11495](https://github.com/community-scripts/ProxmoxVE/pull/11495)) ## 2026-02-02 ### 🆕 New Scripts - rustypaste | Alpine-rustypaste ([#11457](https://github.com/community-scripts/ProxmoxVE/pull/11457)) - KitchenOwl ([#11453](https://github.com/community-scripts/ProxmoxVE/pull/11453)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Grist: Update dependencies [@tremor021](https://github.com/tremor021) ([#11489](https://github.com/community-scripts/ProxmoxVE/pull/11489)) - Allow "downgrade" of libigdgmm12 [@vhsdream](https://github.com/vhsdream) ([#11478](https://github.com/community-scripts/ProxmoxVE/pull/11478)) - Disable NPM install and update due to OpenResty SHA-1 signature issues [@MickLesk](https://github.com/MickLesk) ([#11471](https://github.com/community-scripts/ProxmoxVE/pull/11471)) - #### ✨ New Features - Refactor: Forgejo & readeck - migrate to codeberg functions [@MickLesk](https://github.com/MickLesk) ([#11460](https://github.com/community-scripts/ProxmoxVE/pull/11460)) - #### 💥 Breaking Changes - [FIX] Scanopy: remove daemon build [@vhsdream](https://github.com/vhsdream) ([#11444](https://github.com/community-scripts/ProxmoxVE/pull/11444)) - #### 🔧 Refactor - Refactor: Vaultwarden [@MickLesk](https://github.com/MickLesk) ([#11445](https://github.com/community-scripts/ProxmoxVE/pull/11445)) - various scripts: use ensure_dependencies instead of apt [@MickLesk](https://github.com/MickLesk) ([#11463](https://github.com/community-scripts/ProxmoxVE/pull/11463)) ### 🌐 Website - cleanup(frontend): remove unused /category-view route [@ls-root](https://github.com/ls-root) ([#11461](https://github.com/community-scripts/ProxmoxVE/pull/11461)) - #### ✨ New Features - feat(frontend): preview tab [@ls-root](https://github.com/ls-root) ([#11475](https://github.com/community-scripts/ProxmoxVE/pull/11475)) ## 2026-02-01 ### 🚀 Updated Scripts - fix headers [@CrazyWolf13](https://github.com/CrazyWolf13) ([#11422](https://github.com/community-scripts/ProxmoxVE/pull/11422)) - #### 🐞 Bug Fixes - 2fauth: export PHP_VERSION for nginx config [@MickLesk](https://github.com/MickLesk) ([#11441](https://github.com/community-scripts/ProxmoxVE/pull/11441)) - Prometheus Paperless NGX Exporter: Set correct binary path in systemd unit file [@andygrunwald](https://github.com/andygrunwald) ([#11438](https://github.com/community-scripts/ProxmoxVE/pull/11438)) - tracearr: install/update new prestart script from upstream [@durzo](https://github.com/durzo) ([#11433](https://github.com/community-scripts/ProxmoxVE/pull/11433)) - n8n: Fix dependencies [@tremor021](https://github.com/tremor021) ([#11429](https://github.com/community-scripts/ProxmoxVE/pull/11429)) - [Hotfix] Bunkerweb update [@vhsdream](https://github.com/vhsdream) ([#11402](https://github.com/community-scripts/ProxmoxVE/pull/11402)) - [Hotfix] Immich: revert healthcheck feature [@vhsdream](https://github.com/vhsdream) ([#11427](https://github.com/community-scripts/ProxmoxVE/pull/11427)) - #### ✨ New Features - tools.func: add codeberg functions & autocaliweb: migrate from GitHub to Codeberg [@MickLesk](https://github.com/MickLesk) ([#11440](https://github.com/community-scripts/ProxmoxVE/pull/11440)) - Immich Refactor #2 [@vhsdream](https://github.com/vhsdream) ([#11375](https://github.com/community-scripts/ProxmoxVE/pull/11375)) - #### 🔧 Refactor - WordPress: Refactor [@tremor021](https://github.com/tremor021) ([#11408](https://github.com/community-scripts/ProxmoxVE/pull/11408)) - Refactor: Whisparr [@tremor021](https://github.com/tremor021) ([#11411](https://github.com/community-scripts/ProxmoxVE/pull/11411)) ### 💾 Core - #### ✨ New Features - [tools]: Update `fetch_and_deply_from_url()` [@tremor021](https://github.com/tremor021) ([#11410](https://github.com/community-scripts/ProxmoxVE/pull/11410)) ### 🌐 Website - feat(frontend): implement UX refinements and syntax highlighting [@ls-root](https://github.com/ls-root) ([#11423](https://github.com/community-scripts/ProxmoxVE/pull/11423)) - #### ✨ New Features - feat(frontend): add contribution CTA to empty search state [@ls-root](https://github.com/ls-root) ([#11412](https://github.com/community-scripts/ProxmoxVE/pull/11412)) ================================================ FILE: .github/changelogs/2026/03.md ================================================ ## 2026-03-31 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Graylog: set vm.max_map_count on host for OpenSearch [@MickLesk](https://github.com/MickLesk) ([#13441](https://github.com/community-scripts/ProxmoxVE/pull/13441)) - Koillection: ensure newline before appending to .env.local [@MickLesk](https://github.com/MickLesk) ([#13440](https://github.com/community-scripts/ProxmoxVE/pull/13440)) ### 💾 Core - #### 🔧 Refactor - core: skip empty gateway value in network config [@MickLesk](https://github.com/MickLesk) ([#13442](https://github.com/community-scripts/ProxmoxVE/pull/13442)) ## 2026-03-30 ### 🆕 New Scripts - Bambuddy ([#13411](https://github.com/community-scripts/ProxmoxVE/pull/13411)) ### 🚀 Updated Scripts - #### 💥 Breaking Changes - Rename: BirdNET > BirdNET-Go [@MickLesk](https://github.com/MickLesk) ([#13410](https://github.com/community-scripts/ProxmoxVE/pull/13410)) ## 2026-03-29 ### 🆕 New Scripts - YOURLS ([#13379](https://github.com/community-scripts/ProxmoxVE/pull/13379)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - fix(victoriametrics): use jq to filter releases [@Joery-M](https://github.com/Joery-M) ([#13393](https://github.com/community-scripts/ProxmoxVE/pull/13393)) - Ollama: add error handling for Intel GPG key imports [@MickLesk](https://github.com/MickLesk) ([#13397](https://github.com/community-scripts/ProxmoxVE/pull/13397)) - Immich: ignore Redis connection error on maintenance mode disable [@MickLesk](https://github.com/MickLesk) ([#13398](https://github.com/community-scripts/ProxmoxVE/pull/13398)) - NPM: unmask openresty after migration from package [@MickLesk](https://github.com/MickLesk) ([#13399](https://github.com/community-scripts/ProxmoxVE/pull/13399)) ## 2026-03-28 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Fix: Update gokapi binary name for v2.2.4+ and add migration step [@krazos](https://github.com/krazos) ([#13377](https://github.com/community-scripts/ProxmoxVE/pull/13377)) - Fix: update gokapi asset matching for v2.2.4+ naming convention [@krazos](https://github.com/krazos) ([#13369](https://github.com/community-scripts/ProxmoxVE/pull/13369)) - Tandoor Recipes: Add missing env variable [@tremor021](https://github.com/tremor021) ([#13365](https://github.com/community-scripts/ProxmoxVE/pull/13365)) - #### ✨ New Features - FileFlows: add option to install Node [@tremor021](https://github.com/tremor021) ([#13368](https://github.com/community-scripts/ProxmoxVE/pull/13368)) ## 2026-03-27 ### 🆕 New Scripts - Matter-Server ([#13355](https://github.com/community-scripts/ProxmoxVE/pull/13355)) - GeoPulse ([#13320](https://github.com/community-scripts/ProxmoxVE/pull/13320)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - RevealJS: Switch from gulp to vite [@tremor021](https://github.com/tremor021) ([#13336](https://github.com/community-scripts/ProxmoxVE/pull/13336)) - #### ✨ New Features - Dispatcharr add custom Postgres port support for upgrade [@MickLesk](https://github.com/MickLesk) ([#13347](https://github.com/community-scripts/ProxmoxVE/pull/13347)) - Immich: bump to v2.6.3 [@MickLesk](https://github.com/MickLesk) ([#13324](https://github.com/community-scripts/ProxmoxVE/pull/13324)) ### 🧰 Tools - #### ✨ New Features - Refactor/Feature-Bump/Security: Update-Cron-LXCs (Now Local Mode!) [@MickLesk](https://github.com/MickLesk) ([#13339](https://github.com/community-scripts/ProxmoxVE/pull/13339)) ## 2026-03-26 ### 🆕 New Scripts - BirdNET ([#13313](https://github.com/community-scripts/ProxmoxVE/pull/13313)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Immich: Bump to 2.6.2 | use start.sh in service, ensure DB_HOSTNAME in .env | Fix Rights Issue with ZFS Shares [@MickLesk](https://github.com/MickLesk) ([#13199](https://github.com/community-scripts/ProxmoxVE/pull/13199)) - #### ✨ New Features - SparkyFitness: add garmin microservice as addon [@tomfrenzel](https://github.com/tomfrenzel) ([#12642](https://github.com/community-scripts/ProxmoxVE/pull/12642)) - Frigate: bump to v0.17.1 & change build order [@MickLesk](https://github.com/MickLesk) ([#13304](https://github.com/community-scripts/ProxmoxVE/pull/13304)) ### 💾 Core - #### 🐞 Bug Fixes - tools.func: pin npm to 11.11.0 to work around Node.js 22.22.2 regression [@MickLesk](https://github.com/MickLesk) ([#13296](https://github.com/community-scripts/ProxmoxVE/pull/13296)) - #### ✨ New Features - core: APT/APK Mirror Fallback for CDN Failures [@MickLesk](https://github.com/MickLesk) ([#13316](https://github.com/community-scripts/ProxmoxVE/pull/13316)) - core/tools: replace generic return 1 exit_codes with more specific exit_codes [@MickLesk](https://github.com/MickLesk) ([#13311](https://github.com/community-scripts/ProxmoxVE/pull/13311)) - #### 🔧 Refactor - core: use /usr/bin/install to prevent function shadowing [@MickLesk](https://github.com/MickLesk) ([#13299](https://github.com/community-scripts/ProxmoxVE/pull/13299)) ### 🧰 Tools - #### 🐞 Bug Fixes - SparkyFitness-Garmin: fix app name [@tomfrenzel](https://github.com/tomfrenzel) ([#13325](https://github.com/community-scripts/ProxmoxVE/pull/13325)) ## 2026-03-25 ### 🚀 Updated Scripts - #### ✨ New Features - Komodo v2: migrate env vars to v2 and update source [@MickLesk](https://github.com/MickLesk) ([#13262](https://github.com/community-scripts/ProxmoxVE/pull/13262)) ### 💾 Core - #### 🔧 Refactor - core: make shell command substitutions safe with || true [@MickLesk](https://github.com/MickLesk) ([#13279](https://github.com/community-scripts/ProxmoxVE/pull/13279)) ## 2026-03-24 ### 🆕 New Scripts - Homebrew (Addon) ([#13249](https://github.com/community-scripts/ProxmoxVE/pull/13249)) - NextExplorer ([#13252](https://github.com/community-scripts/ProxmoxVE/pull/13252)) ### 🚀 Updated Scripts - #### ✨ New Features - Turnkey: modernize turnkey.sh with shared libraries [@MickLesk](https://github.com/MickLesk) ([#13242](https://github.com/community-scripts/ProxmoxVE/pull/13242)) - #### 🔧 Refactor - chore: replace helper-scripts.com with community-scripts.com [@MickLesk](https://github.com/MickLesk) ([#13244](https://github.com/community-scripts/ProxmoxVE/pull/13244)) ### 🗑️ Deleted Scripts - Remove: Booklore [@MickLesk](https://github.com/MickLesk) ([#13265](https://github.com/community-scripts/ProxmoxVE/pull/13265)) ## 2026-03-23 ### 🚀 Updated Scripts - #### 🔧 Refactor - core: harden shell scripts against injection and insecure permissions [@MickLesk](https://github.com/MickLesk) ([#13239](https://github.com/community-scripts/ProxmoxVE/pull/13239)) ## 2026-03-22 ### 🆕 New Scripts - versitygw ([#13180](https://github.com/community-scripts/ProxmoxVE/pull/13180)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Adventurelog: pin DRF <3.15 to fix coreapi module removal [@MickLesk](https://github.com/MickLesk) ([#13194](https://github.com/community-scripts/ProxmoxVE/pull/13194)) - #### ✨ New Features - ConvertX: add libreoffice-writer for ODT/document conversions [@MickLesk](https://github.com/MickLesk) ([#13196](https://github.com/community-scripts/ProxmoxVE/pull/13196)) - #### 🔧 Refactor - iSponsorblockTV: add AVX CPU check before installation [@MickLesk](https://github.com/MickLesk) ([#13197](https://github.com/community-scripts/ProxmoxVE/pull/13197)) ### 💾 Core - #### 🐞 Bug Fixes - core: guard against empty IPv6 address in static mode [@MickLesk](https://github.com/MickLesk) ([#13195](https://github.com/community-scripts/ProxmoxVE/pull/13195)) ## 2026-03-21 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Anytype-server: wait for MongoDB readiness before rs.initiate() [@MickLesk](https://github.com/MickLesk) ([#13165](https://github.com/community-scripts/ProxmoxVE/pull/13165)) - Frigate: use correct CPU model fallback path [@MickLesk](https://github.com/MickLesk) ([#13164](https://github.com/community-scripts/ProxmoxVE/pull/13164)) - iSponsorBlockTV: Fix release fetching [@tremor021](https://github.com/tremor021) ([#13157](https://github.com/community-scripts/ProxmoxVE/pull/13157)) - Isponsorblocktv: use quoted heredoc to prevent unbound variable error during CLI wrapper creation [@Copilot](https://github.com/Copilot) ([#13146](https://github.com/community-scripts/ProxmoxVE/pull/13146)) - #### ✨ New Features - Headscale: Enable TUN [@tremor021](https://github.com/tremor021) ([#13158](https://github.com/community-scripts/ProxmoxVE/pull/13158)) ### 💾 Core - #### 🐞 Bug Fixes - core: add missing -searchdomain/-nameserver prefix in base_settings [@MickLesk](https://github.com/MickLesk) ([#13166](https://github.com/community-scripts/ProxmoxVE/pull/13166)) ## 2026-03-20 ### 🆕 New Scripts - iSponsorBlockTV ([#13123](https://github.com/community-scripts/ProxmoxVE/pull/13123)) - Alpine-Wakapi ([#13119](https://github.com/community-scripts/ProxmoxVE/pull/13119)) - teleport ([#13086](https://github.com/community-scripts/ProxmoxVE/pull/13086)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Reactive-Resume: add git dependency for v5.0.13+ [@MickLesk](https://github.com/MickLesk) ([#13133](https://github.com/community-scripts/ProxmoxVE/pull/13133)) - Scanopy: increase default CPU, RAM, and HDD to prevent OOM during Rust build [@Copilot](https://github.com/Copilot) ([#13130](https://github.com/community-scripts/ProxmoxVE/pull/13130)) - #### ✨ New Features - Immich: v2.6.1 [@vhsdream](https://github.com/vhsdream) ([#13111](https://github.com/community-scripts/ProxmoxVE/pull/13111)) - VM's: add input validation and hostname sanitization to all VM scripts [@MickLesk](https://github.com/MickLesk) ([#12973](https://github.com/community-scripts/ProxmoxVE/pull/12973)) ### 🧰 Tools - #### 🔧 Refactor - Harden code-server addon install script [@MickLesk](https://github.com/MickLesk) ([#13116](https://github.com/community-scripts/ProxmoxVE/pull/13116)) ## 2026-03-19 ### 🚀 Updated Scripts - Owncast: increase default disk size from 2GB to 10GB [@Copilot](https://github.com/Copilot) ([#13079](https://github.com/community-scripts/ProxmoxVE/pull/13079)) - #### 🐞 Bug Fixes - fix: remove extra backslash to match single quoted here-doc [@Zelnes](https://github.com/Zelnes) ([#13108](https://github.com/community-scripts/ProxmoxVE/pull/13108)) - Reactive-Resume: Upgrade Node to 24 and enable Corepack [@MickLesk](https://github.com/MickLesk) ([#13093](https://github.com/community-scripts/ProxmoxVE/pull/13093)) - Increase Tracearr RAM; derive APP_VERSION [@MickLesk](https://github.com/MickLesk) ([#13087](https://github.com/community-scripts/ProxmoxVE/pull/13087)) - ProjectSend: Update application access URL [@tremor021](https://github.com/tremor021) ([#13078](https://github.com/community-scripts/ProxmoxVE/pull/13078)) - Dispatcharr: use npm install --no-audit --progress=false [@MickLesk](https://github.com/MickLesk) ([#13074](https://github.com/community-scripts/ProxmoxVE/pull/13074)) - core: reorder hwaccel setup and adjust GPU group usermod [@MickLesk](https://github.com/MickLesk) ([#13072](https://github.com/community-scripts/ProxmoxVE/pull/13072)) - #### ✨ New Features - tools.func: display pin reason in release-check messages [@MickLesk](https://github.com/MickLesk) ([#13095](https://github.com/community-scripts/ProxmoxVE/pull/13095)) - NocoDB: Unpin Version to latest [@MickLesk](https://github.com/MickLesk) ([#13094](https://github.com/community-scripts/ProxmoxVE/pull/13094)) ### 💾 Core - #### 🐞 Bug Fixes - tools.func: use dpkg-query for reliable JDK version detection [@MickLesk](https://github.com/MickLesk) ([#13101](https://github.com/community-scripts/ProxmoxVE/pull/13101)) ### 📚 Documentation - Update link from helper-scripts.com to community-scripts.org [@adnanvaldes](https://github.com/adnanvaldes) ([#13098](https://github.com/community-scripts/ProxmoxVE/pull/13098)) - github: add PocketBase bot workflow [@MickLesk](https://github.com/MickLesk) ([#13075](https://github.com/community-scripts/ProxmoxVE/pull/13075)) ## 2026-03-18 ### 🆕 New Scripts - Alpine-Ntfy [@MickLesk](https://github.com/MickLesk) ([#13048](https://github.com/community-scripts/ProxmoxVE/pull/13048)) - Split-Pro ([#12975](https://github.com/community-scripts/ProxmoxVE/pull/12975)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Tdarr: use curl_with_retry and correct exit code [@MickLesk](https://github.com/MickLesk) ([#13060](https://github.com/community-scripts/ProxmoxVE/pull/13060)) - reitti: fix: v4 [@CrazyWolf13](https://github.com/CrazyWolf13) ([#13039](https://github.com/community-scripts/ProxmoxVE/pull/13039)) - Paperless-NGX: increase default RAM to 3GB [@MickLesk](https://github.com/MickLesk) ([#13018](https://github.com/community-scripts/ProxmoxVE/pull/13018)) - Plex: restart service after update to apply new version [@MickLesk](https://github.com/MickLesk) ([#13017](https://github.com/community-scripts/ProxmoxVE/pull/13017)) - #### ✨ New Features - tools: centralize GPU group setup via setup_hwaccel [@MickLesk](https://github.com/MickLesk) ([#13044](https://github.com/community-scripts/ProxmoxVE/pull/13044)) - Termix: add guacd build and systemd integration [@MickLesk](https://github.com/MickLesk) ([#12999](https://github.com/community-scripts/ProxmoxVE/pull/12999)) - #### 🔧 Refactor - Podman: replace deprecated commands with Quadlets [@MickLesk](https://github.com/MickLesk) ([#13052](https://github.com/community-scripts/ProxmoxVE/pull/13052)) - Refactor: Jellyfin repo, ffmpeg package and symlinks [@MickLesk](https://github.com/MickLesk) ([#13045](https://github.com/community-scripts/ProxmoxVE/pull/13045)) - pve-scripts-local: Increase default disk size from 4GB to 10GB [@MickLesk](https://github.com/MickLesk) ([#13009](https://github.com/community-scripts/ProxmoxVE/pull/13009)) ### 💾 Core - #### ✨ New Features - tools.func Implement pg_cron setup for setup_postgresql [@MickLesk](https://github.com/MickLesk) ([#13053](https://github.com/community-scripts/ProxmoxVE/pull/13053)) - tools.func: Implement check_for_gh_tag function [@MickLesk](https://github.com/MickLesk) ([#12998](https://github.com/community-scripts/ProxmoxVE/pull/12998)) - tools.func: Implement fetch_and_deploy_gh_tag function [@MickLesk](https://github.com/MickLesk) ([#13000](https://github.com/community-scripts/ProxmoxVE/pull/13000)) ## 2026-03-17 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Gluetun: add OpenVPN process user and cleanup stale config [@MickLesk](https://github.com/MickLesk) ([#13016](https://github.com/community-scripts/ProxmoxVE/pull/13016)) - Frigate: check OpenVino model files exist before configuring detector and use curl_with_retry instead of default wget [@MickLesk](https://github.com/MickLesk) ([#13019](https://github.com/community-scripts/ProxmoxVE/pull/13019)) ### 💾 Core - #### 🔧 Refactor - tools.func: Update `create_self_signed_cert()` [@tremor021](https://github.com/tremor021) ([#13008](https://github.com/community-scripts/ProxmoxVE/pull/13008)) ## 2026-03-16 ### 🆕 New Scripts - Gluetun ([#12976](https://github.com/community-scripts/ProxmoxVE/pull/12976)) - Anytype-Server ([#12974](https://github.com/community-scripts/ProxmoxVE/pull/12974)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Immich: use gcc-13 for compilation & add uv python pre-install with retry logic [@MickLesk](https://github.com/MickLesk) ([#12935](https://github.com/community-scripts/ProxmoxVE/pull/12935)) - Tautulli: add setuptools<81 constraint to update script [@MickLesk](https://github.com/MickLesk) ([#12959](https://github.com/community-scripts/ProxmoxVE/pull/12959)) - Seerr: add missing build deps [@MickLesk](https://github.com/MickLesk) ([#12960](https://github.com/community-scripts/ProxmoxVE/pull/12960)) - fix: yubal update [@CrazyWolf13](https://github.com/CrazyWolf13) ([#12961](https://github.com/community-scripts/ProxmoxVE/pull/12961)) ### 💾 Core - #### 🐞 Bug Fixes - hwaccel: remove ROCm install from AMD APU setup [@MickLesk](https://github.com/MickLesk) ([#12958](https://github.com/community-scripts/ProxmoxVE/pull/12958)) ## 2026-03-15 ### 🆕 New Scripts - Yamtrack ([#12936](https://github.com/community-scripts/ProxmoxVE/pull/12936)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Wishlist: use --frozen-lockfile for pnpm install [@MickLesk](https://github.com/MickLesk) ([#12892](https://github.com/community-scripts/ProxmoxVE/pull/12892)) - SparkyFitness: use --legacy-peer-deps for npm install [@MickLesk](https://github.com/MickLesk) ([#12888](https://github.com/community-scripts/ProxmoxVE/pull/12888)) - Frigate: add fallback for OpenVino labelmap file [@MickLesk](https://github.com/MickLesk) ([#12889](https://github.com/community-scripts/ProxmoxVE/pull/12889)) - #### 🔧 Refactor - Refactor: ITSM-NG [@MickLesk](https://github.com/MickLesk) ([#12918](https://github.com/community-scripts/ProxmoxVE/pull/12918)) - core: unify RELEASE variable for check_for_gh_release and fetch_and_deploy [@MickLesk](https://github.com/MickLesk) ([#12917](https://github.com/community-scripts/ProxmoxVE/pull/12917)) - Standardize NSAPP names across VM scripts [@MickLesk](https://github.com/MickLesk) ([#12924](https://github.com/community-scripts/ProxmoxVE/pull/12924)) ### 💾 Core - #### ✨ New Features - core: retry downloads with exponential backoff [@MickLesk](https://github.com/MickLesk) ([#12896](https://github.com/community-scripts/ProxmoxVE/pull/12896)) ### ❔ Uncategorized - [go2rtc] Add ffmpeg dependency to install script [@Copilot](https://github.com/Copilot) ([#12944](https://github.com/community-scripts/ProxmoxVE/pull/12944)) ## 2026-03-14 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Patchmon: remove v prefix from pinned version [@MickLesk](https://github.com/MickLesk) ([#12891](https://github.com/community-scripts/ProxmoxVE/pull/12891)) ### 💾 Core - #### 🐞 Bug Fixes - tools.func: don't abort on AMD repo apt update failure [@MickLesk](https://github.com/MickLesk) ([#12890](https://github.com/community-scripts/ProxmoxVE/pull/12890)) ## 2026-03-13 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Hotfix: Removed clean install usage from original script. [@nickheyer](https://github.com/nickheyer) ([#12870](https://github.com/community-scripts/ProxmoxVE/pull/12870)) - #### 🔧 Refactor - Discopanel: V2 Support + Script rewrite [@nickheyer](https://github.com/nickheyer) ([#12763](https://github.com/community-scripts/ProxmoxVE/pull/12763)) ### 🧰 Tools - update-apps: fix restore path, add PBS support and improve restore messages [@omertahaoztop](https://github.com/omertahaoztop) ([#12528](https://github.com/community-scripts/ProxmoxVE/pull/12528)) - #### 🐞 Bug Fixes - fix(pve-privilege-converter): handle already stopped container in manage_states [@liuqitoday](https://github.com/liuqitoday) ([#12765](https://github.com/community-scripts/ProxmoxVE/pull/12765)) ### 📚 Documentation - Update: Docs/website metadata workflow [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#12858](https://github.com/community-scripts/ProxmoxVE/pull/12858)) ## 2026-03-12 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - manyfold: fix incorrect port in upstream requests by forwarding original host [@anlopo](https://github.com/anlopo) ([#12812](https://github.com/community-scripts/ProxmoxVE/pull/12812)) - SparkyFitness: install pnpm dependencies from workspace root [@MickLesk](https://github.com/MickLesk) ([#12792](https://github.com/community-scripts/ProxmoxVE/pull/12792)) - n8n: add build-essential to update dependencies [@MickLesk](https://github.com/MickLesk) ([#12795](https://github.com/community-scripts/ProxmoxVE/pull/12795)) - Frigate openvino labelmap patch [@semtex1987](https://github.com/semtex1987) ([#12751](https://github.com/community-scripts/ProxmoxVE/pull/12751)) - #### 🔧 Refactor - Pin Patchmon to 1.4.2 [@vhsdream](https://github.com/vhsdream) ([#12789](https://github.com/community-scripts/ProxmoxVE/pull/12789)) ### 💾 Core - #### 🐞 Bug Fixes - tools.func: correct PATH escaping in ROCm profile script [@MickLesk](https://github.com/MickLesk) ([#12793](https://github.com/community-scripts/ProxmoxVE/pull/12793)) - #### ✨ New Features - core: add mode=generated for unattended frontend installs [@MickLesk](https://github.com/MickLesk) ([#12807](https://github.com/community-scripts/ProxmoxVE/pull/12807)) - core: validate storage availability when loading defaults [@MickLesk](https://github.com/MickLesk) ([#12794](https://github.com/community-scripts/ProxmoxVE/pull/12794)) - #### 🔧 Refactor - tools.func: support older NVIDIA driver versions with 2 segments (xxx.xxx) [@MickLesk](https://github.com/MickLesk) ([#12796](https://github.com/community-scripts/ProxmoxVE/pull/12796)) ### 🧰 Tools - #### 🐞 Bug Fixes - Fix PBS microcode naming [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#12834](https://github.com/community-scripts/ProxmoxVE/pull/12834)) ### 📂 Github - Cleanup: remove old workflow files [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#12818](https://github.com/community-scripts/ProxmoxVE/pull/12818)) - Cleanup: remove frontend, move JSONs to json/ top-level [@MickLesk](https://github.com/MickLesk) ([#12813](https://github.com/community-scripts/ProxmoxVE/pull/12813)) ### ❔ Uncategorized - Remove json files [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#12830](https://github.com/community-scripts/ProxmoxVE/pull/12830)) ## 2026-03-11 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - fix: Init telemetry in addon scripts [@MickLesk](https://github.com/MickLesk) ([#12777](https://github.com/community-scripts/ProxmoxVE/pull/12777)) - Tracearr: Increase default disk variable from 5 to 10 [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#12762](https://github.com/community-scripts/ProxmoxVE/pull/12762)) - Fix Wireguard Dashboard update [@odin568](https://github.com/odin568) ([#12767](https://github.com/community-scripts/ProxmoxVE/pull/12767)) ### 🧰 Tools - #### ✨ New Features - Coder-Code-Server: Check if config file exists [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#12758](https://github.com/community-scripts/ProxmoxVE/pull/12758)) ## 2026-03-10 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - [Fix] Immich: Pin libvips to 8.17.3 [@vhsdream](https://github.com/vhsdream) ([#12744](https://github.com/community-scripts/ProxmoxVE/pull/12744)) ## 2026-03-09 ### 🚀 Updated Scripts - Pin Opencloud to 5.2.0 [@vhsdream](https://github.com/vhsdream) ([#12721](https://github.com/community-scripts/ProxmoxVE/pull/12721)) - #### 🐞 Bug Fixes - [Hotfix] qBittorrent: Disable UPnP port forwarding by default [@vhsdream](https://github.com/vhsdream) ([#12728](https://github.com/community-scripts/ProxmoxVE/pull/12728)) - [Quickfix] Opencloud: ensure correct case for binary [@vhsdream](https://github.com/vhsdream) ([#12729](https://github.com/community-scripts/ProxmoxVE/pull/12729)) - Omada: Bump libssl [@MickLesk](https://github.com/MickLesk) ([#12724](https://github.com/community-scripts/ProxmoxVE/pull/12724)) - openwebui: Ensure required dependencies [@MickLesk](https://github.com/MickLesk) ([#12717](https://github.com/community-scripts/ProxmoxVE/pull/12717)) - Frigate: try an OpenVino model build fallback [@MickLesk](https://github.com/MickLesk) ([#12704](https://github.com/community-scripts/ProxmoxVE/pull/12704)) - Change cronjob setup to use www-data user [@opastorello](https://github.com/opastorello) ([#12695](https://github.com/community-scripts/ProxmoxVE/pull/12695)) - RustDesk Server: Fix check_for_gh_release function call [@tremor021](https://github.com/tremor021) ([#12694](https://github.com/community-scripts/ProxmoxVE/pull/12694)) - #### ✨ New Features - feat: improve zigbee2mqtt backup handler [@MickLesk](https://github.com/MickLesk) ([#12714](https://github.com/community-scripts/ProxmoxVE/pull/12714)) - #### 💥 Breaking Changes - Reactive Resume: rewrite for v5 using original repo amruthpilla/reactive-resume [@MickLesk](https://github.com/MickLesk) ([#12705](https://github.com/community-scripts/ProxmoxVE/pull/12705)) ### 💾 Core - #### ✨ New Features - tools: add Alpine (apk) support to ensure_dependencies and is_package_installed [@MickLesk](https://github.com/MickLesk) ([#12703](https://github.com/community-scripts/ProxmoxVE/pull/12703)) - tools.func: extend hwaccel with ROCm [@MickLesk](https://github.com/MickLesk) ([#12707](https://github.com/community-scripts/ProxmoxVE/pull/12707)) ### 🌐 Website - #### ✨ New Features - feat: add CopycatWarningToast component for user warnings [@BramSuurdje](https://github.com/BramSuurdje) ([#12733](https://github.com/community-scripts/ProxmoxVE/pull/12733)) ## 2026-03-08 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - [Fix] Immich: chown install dir before machine-learning update [@vhsdream](https://github.com/vhsdream) ([#12684](https://github.com/community-scripts/ProxmoxVE/pull/12684)) - [Fix] Scanopy: Build generate-fixtures [@vhsdream](https://github.com/vhsdream) ([#12686](https://github.com/community-scripts/ProxmoxVE/pull/12686)) - fix: rustdeskserver: use correct repo string [@CrazyWolf13](https://github.com/CrazyWolf13) ([#12682](https://github.com/community-scripts/ProxmoxVE/pull/12682)) - NZBGet: Fixes for RAR5 handling [@tremor021](https://github.com/tremor021) ([#12675](https://github.com/community-scripts/ProxmoxVE/pull/12675)) ### 🌐 Website - #### 🐞 Bug Fixes - LXC-Execute: Fix slug [@tremor021](https://github.com/tremor021) ([#12681](https://github.com/community-scripts/ProxmoxVE/pull/12681)) ## 2026-03-07 ### 🆕 New Scripts - ImmichFrame ([#12653](https://github.com/community-scripts/ProxmoxVE/pull/12653)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Grocy: bump PHP version from 8.3 to 8.5 [@MickLesk](https://github.com/MickLesk) ([#12651](https://github.com/community-scripts/ProxmoxVE/pull/12651)) - Check for influxdb3 installation in update_script [@odin568](https://github.com/odin568) ([#12648](https://github.com/community-scripts/ProxmoxVE/pull/12648)) - Update Rdtclient to dotnet 10.0 [@asylumexp](https://github.com/asylumexp) ([#12638](https://github.com/community-scripts/ProxmoxVE/pull/12638)) - fix(immich): fix update script failing to add Debian testing repo when preferences file already exists [@Copilot](https://github.com/Copilot) ([#12631](https://github.com/community-scripts/ProxmoxVE/pull/12631)) ### 💾 Core - #### ✨ New Features - tools: add interactive GitHub PAT prompt on rate limit / auth failure [@MickLesk](https://github.com/MickLesk) ([#12652](https://github.com/community-scripts/ProxmoxVE/pull/12652)) ### 🌐 Website - #### 📝 Script Information - Papra: update repository URL to papra-hq/papra [@MickLesk](https://github.com/MickLesk) ([#12650](https://github.com/community-scripts/ProxmoxVE/pull/12650)) ## 2026-03-06 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - RustDesk Server: Fix update script [@tremor021](https://github.com/tremor021) ([#12625](https://github.com/community-scripts/ProxmoxVE/pull/12625)) - [Node-RED] Restart service after update [@Aurelien30000](https://github.com/Aurelien30000) ([#12621](https://github.com/community-scripts/ProxmoxVE/pull/12621)) - wealthfolio: update cors [@CrazyWolf13](https://github.com/CrazyWolf13) ([#12617](https://github.com/community-scripts/ProxmoxVE/pull/12617)) - CryptPad: Better update handling [@tremor021](https://github.com/tremor021) ([#12611](https://github.com/community-scripts/ProxmoxVE/pull/12611)) - #### ✨ New Features - RustDesk Server: Switch to updated repository [@tremor021](https://github.com/tremor021) ([#12083](https://github.com/community-scripts/ProxmoxVE/pull/12083)) - #### 💥 Breaking Changes - Semaphore: Move from BoltDB to SQLite [@tremor021](https://github.com/tremor021) ([#12624](https://github.com/community-scripts/ProxmoxVE/pull/12624)) ## 2026-03-05 ### 🆕 New Scripts - ddclient ([#12587](https://github.com/community-scripts/ProxmoxVE/pull/12587)) - Netbird ([#12585](https://github.com/community-scripts/ProxmoxVE/pull/12585)) - Papra ([#12577](https://github.com/community-scripts/ProxmoxVE/pull/12577)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - fluid-calendar: add build-essential to install and update dependencies [@Copilot](https://github.com/Copilot) ([#12602](https://github.com/community-scripts/ProxmoxVE/pull/12602)) - Refactor: BentoPDF [@vhsdream](https://github.com/vhsdream) ([#12597](https://github.com/community-scripts/ProxmoxVE/pull/12597)) - Tianji: Fix the bug introduced by the refactor [@tremor021](https://github.com/tremor021) ([#12564](https://github.com/community-scripts/ProxmoxVE/pull/12564)) - PowerDNS: use 'launch=' instead of 'launch+=' for gsqlite3 backend [@MickLesk](https://github.com/MickLesk) ([#12579](https://github.com/community-scripts/ProxmoxVE/pull/12579)) ### 🗑️ Deleted Scripts - Suwayomi-Server: remove due to inactivity and very low usage [@MickLesk](https://github.com/MickLesk) ([#12596](https://github.com/community-scripts/ProxmoxVE/pull/12596)) ### 💾 Core - #### 🔧 Refactor - core: add var_os / var_version to whitelist for app.vars [@MickLesk](https://github.com/MickLesk) ([#12576](https://github.com/community-scripts/ProxmoxVE/pull/12576)) ## 2026-03-04 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - fix: gitea-mirror [@CrazyWolf13](https://github.com/CrazyWolf13) ([#12549](https://github.com/community-scripts/ProxmoxVE/pull/12549)) - fix(immich): correct LibRaw clone URL to official upstream [@DenislavDenev](https://github.com/DenislavDenev) ([#12526](https://github.com/community-scripts/ProxmoxVE/pull/12526)) - update: stirling-pdf: java 25 [@CrazyWolf13](https://github.com/CrazyWolf13) ([#12552](https://github.com/community-scripts/ProxmoxVE/pull/12552)) - Docmost: register NoopAuditService globally when EE submodule is missing [@MickLesk](https://github.com/MickLesk) ([#12551](https://github.com/community-scripts/ProxmoxVE/pull/12551)) - jellyseer/overseer migration corrupting /usr/bin/update [@MickLesk](https://github.com/MickLesk) ([#12539](https://github.com/community-scripts/ProxmoxVE/pull/12539)) - PowerDNS: use gsqlite3 backend instead of BIND [@MickLesk](https://github.com/MickLesk) ([#12538](https://github.com/community-scripts/ProxmoxVE/pull/12538)) - addon migrations: /usr/bin/update replacement to prevent syntax error [@MickLesk](https://github.com/MickLesk) ([#12540](https://github.com/community-scripts/ProxmoxVE/pull/12540)) - #### 🔧 Refactor - Fluid-Calendar: NodeJS bump [@tremor021](https://github.com/tremor021) ([#12558](https://github.com/community-scripts/ProxmoxVE/pull/12558)) - Refactor: LiteLLM [@tremor021](https://github.com/tremor021) ([#12550](https://github.com/community-scripts/ProxmoxVE/pull/12550)) ### 💾 Core - #### 🐞 Bug Fixes - tools: fall back to distro packages for psql [@MickLesk](https://github.com/MickLesk) ([#12542](https://github.com/community-scripts/ProxmoxVE/pull/12542)) - fix: whitelist var_searchdomain and fix the handling of var_ns and va… [@tommoyer](https://github.com/tommoyer) ([#12521](https://github.com/community-scripts/ProxmoxVE/pull/12521)) ## 2026-03-03 ### 🆕 New Scripts - Tinyauth: v5 Support & add Debian Version [@MickLesk](https://github.com/MickLesk) ([#12501](https://github.com/community-scripts/ProxmoxVE/pull/12501)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - cross-seed: install build-essential to resolve missing `make` error [@Copilot](https://github.com/Copilot) ([#12522](https://github.com/community-scripts/ProxmoxVE/pull/12522)) - meshcentral: increased disk space to 4GB [@MickLesk](https://github.com/MickLesk) ([#12509](https://github.com/community-scripts/ProxmoxVE/pull/12509)) - #### 🔧 Refactor - opnsense-vm: harden temp dir, bridge detection and network selection [@MickLesk](https://github.com/MickLesk) ([#12513](https://github.com/community-scripts/ProxmoxVE/pull/12513)) ### 🗑️ Deleted Scripts - Remove Unifi Network Server scripts (dead APT repo) [@Copilot](https://github.com/Copilot) ([#12500](https://github.com/community-scripts/ProxmoxVE/pull/12500)) ### 💾 Core - #### ✨ New Features - core: recovery - add ENOSPC disk-full detection with auto-retry using * 2 hdd [@MickLesk](https://github.com/MickLesk) ([#12511](https://github.com/community-scripts/ProxmoxVE/pull/12511)) ### 📚 Documentation - Fix config_path casing in reactive-resume.json [@ScubyG](https://github.com/ScubyG) ([#12525](https://github.com/community-scripts/ProxmoxVE/pull/12525)) ### 🌐 Website - #### 🐞 Bug Fixes - Revert #11534 PR that messed up search [@BramSuurdje](https://github.com/BramSuurdje) ([#12492](https://github.com/community-scripts/ProxmoxVE/pull/12492)) ## 2026-03-02 ### 🆕 New Scripts - PowerDNS ([#12481](https://github.com/community-scripts/ProxmoxVE/pull/12481)) - Profilarr ([#12441](https://github.com/community-scripts/ProxmoxVE/pull/12441)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Tracearr: prepare for imminent v1.4.19 release [@durzo](https://github.com/durzo) ([#12413](https://github.com/community-scripts/ProxmoxVE/pull/12413)) - #### ✨ New Features - Frigate: Bump to v0.17 [@MickLesk](https://github.com/MickLesk) ([#12474](https://github.com/community-scripts/ProxmoxVE/pull/12474)) - #### 💥 Breaking Changes - Migrate: DokPloy, Komodo, Coolify, Dockge, Runtipi to Addons [@MickLesk](https://github.com/MickLesk) ([#12275](https://github.com/community-scripts/ProxmoxVE/pull/12275)) - #### 🔧 Refactor - ref: replace generic exit 1 with specific exit codes in ct & install [@MickLesk](https://github.com/MickLesk) ([#12475](https://github.com/community-scripts/ProxmoxVE/pull/12475)) ### 💾 Core - #### ✨ New Features - tools.func: Improve stability with retry logic, caching, and debug mode [@MickLesk](https://github.com/MickLesk) ([#10351](https://github.com/community-scripts/ProxmoxVE/pull/10351)) - #### 🔧 Refactor - core: standardize exit codes and add mappings [@MickLesk](https://github.com/MickLesk) ([#12467](https://github.com/community-scripts/ProxmoxVE/pull/12467)) ### 🌐 Website - frontend: improve detail view badges, addon texts, and HTML title [@MickLesk](https://github.com/MickLesk) ([#12461](https://github.com/community-scripts/ProxmoxVE/pull/12461)) ## 2026-03-01 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Sparkyfitness: use pnpm [@tomfrenzel](https://github.com/tomfrenzel) ([#12445](https://github.com/community-scripts/ProxmoxVE/pull/12445)) - OpenArchiver: Fix installation [@tremor021](https://github.com/tremor021) ([#12447](https://github.com/community-scripts/ProxmoxVE/pull/12447)) ================================================ FILE: .github/changelogs/2026/04.md ================================================ ## 2026-04-30 ### 🆕 New Scripts - Nagios ([#14126](https://github.com/community-scripts/ProxmoxVE/pull/14126)) - Neko ([#14121](https://github.com/community-scripts/ProxmoxVE/pull/14121)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - alpine-docker: install openssl as core dependency | alpine-komodo: check & install openssl if missing [@MickLesk](https://github.com/MickLesk) ([#14134](https://github.com/community-scripts/ProxmoxVE/pull/14134)) - endurain: update source references to Codeberg [@MickLesk](https://github.com/MickLesk) ([#14128](https://github.com/community-scripts/ProxmoxVE/pull/14128)) ### 💾 Core - #### 🔧 Refactor - tools.func: Manage minor versions for MongoDB 8.x [@tremor021](https://github.com/tremor021) ([#14131](https://github.com/community-scripts/ProxmoxVE/pull/14131)) ## 2026-04-29 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - GrayLog: MongoDB update to 8.2.x [@tremor021](https://github.com/tremor021) ([#14114](https://github.com/community-scripts/ProxmoxVE/pull/14114)) - Graylog: Better information in the log file [@tremor021](https://github.com/tremor021) ([#14110](https://github.com/community-scripts/ProxmoxVE/pull/14110)) - #### 🔧 Refactor - Refactor: checkMK [@MickLesk](https://github.com/MickLesk) ([#14105](https://github.com/community-scripts/ProxmoxVE/pull/14105)) - PatchMon: Unpin release [@tremor021](https://github.com/tremor021) ([#14097](https://github.com/community-scripts/ProxmoxVE/pull/14097)) ### 💾 Core - #### 🔧 Refactor - core: add guidance when storage lacks rootdir support [@MickLesk](https://github.com/MickLesk) ([#14108](https://github.com/community-scripts/ProxmoxVE/pull/14108)) ## 2026-04-28 ### 🆕 New Scripts - StoryBook ([#14081](https://github.com/community-scripts/ProxmoxVE/pull/14081)) - CoreDNS ([#14082](https://github.com/community-scripts/ProxmoxVE/pull/14082)) ### 🚀 Updated Scripts - Fix Dawarich Install/Update [@Jerry1098](https://github.com/Jerry1098) ([#14078](https://github.com/community-scripts/ProxmoxVE/pull/14078)) - #### ✨ New Features - PatchMon Version 2.0.2 Script update [@9technologygroup](https://github.com/9technologygroup) ([#14095](https://github.com/community-scripts/ProxmoxVE/pull/14095)) ## 2026-04-27 ### 🚀 Updated Scripts - Add pamUsername column to userOrgs table [@JVKeller](https://github.com/JVKeller) ([#14075](https://github.com/community-scripts/ProxmoxVE/pull/14075)) - #### 🐞 Bug Fixes - Dawarich: run db:migrate before assets:precompile [@MickLesk](https://github.com/MickLesk) ([#14051](https://github.com/community-scripts/ProxmoxVE/pull/14051)) - TechnitiumDNS: always install .NET 10 if not already present [@MickLesk](https://github.com/MickLesk) ([#14049](https://github.com/community-scripts/ProxmoxVE/pull/14049)) - #### 💥 Breaking Changes - PatchMon: v2.0.0 migration [@vhsdream](https://github.com/vhsdream) ([#14015](https://github.com/community-scripts/ProxmoxVE/pull/14015)) ### 💾 Core - #### 🔧 Refactor - Update build.func - fixed spelling mistake [@m1ckywill](https://github.com/m1ckywill) ([#14047](https://github.com/community-scripts/ProxmoxVE/pull/14047)) ### 🧰 Tools - #### 🐞 Bug Fixes - update-lxcs/apps: avoid pct exec on containers mid-shutdown [@MickLesk](https://github.com/MickLesk) ([#14050](https://github.com/community-scripts/ProxmoxVE/pull/14050)) - #### ✨ New Features - Add patchmon-agent report execution in update script [@heinemannj](https://github.com/heinemannj) ([#14054](https://github.com/community-scripts/ProxmoxVE/pull/14054)) ## 2026-04-26 ### 🆕 New Scripts - TREK ([#14017](https://github.com/community-scripts/ProxmoxVE/pull/14017)) ### 🚀 Updated Scripts - fix(2fauth): handle stale backup directory on update [@omertahaoztop](https://github.com/omertahaoztop) ([#14018](https://github.com/community-scripts/ProxmoxVE/pull/14018)) - #### 🐞 Bug Fixes - Increase Frigate default CPU cores from 4 to 8 [@MickLesk](https://github.com/MickLesk) ([#14039](https://github.com/community-scripts/ProxmoxVE/pull/14039)) - Technitium DNS: Ensure directories exist before running service [@tremor021](https://github.com/tremor021) ([#14030](https://github.com/community-scripts/ProxmoxVE/pull/14030)) ### 💾 Core - #### 🐞 Bug Fixes - core: Correct deb822 repository flat path detection [@MickLesk](https://github.com/MickLesk) ([#14037](https://github.com/community-scripts/ProxmoxVE/pull/14037)) ## 2026-04-25 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - VictoriaMetrics: Stop vmagent/vmalert before update [@irishpadres](https://github.com/irishpadres) ([#14016](https://github.com/community-scripts/ProxmoxVE/pull/14016)) - Domain-Monitor: start apache2 after stop instead of reload [@omertahaoztop](https://github.com/omertahaoztop) ([#14019](https://github.com/community-scripts/ProxmoxVE/pull/14019)) - Transmute: Fix ffmpeg detection [@tremor021](https://github.com/tremor021) ([#14008](https://github.com/community-scripts/ProxmoxVE/pull/14008)) - #### 🔧 Refactor - Refactor: Technitium DNS [@tremor021](https://github.com/tremor021) ([#14013](https://github.com/community-scripts/ProxmoxVE/pull/14013)) ## 2026-04-24 ### 🆕 New Scripts - Apprise-API ([#13934](https://github.com/community-scripts/ProxmoxVE/pull/13934)) - fireshare ([#13995](https://github.com/community-scripts/ProxmoxVE/pull/13995)) - Transmute ([#13935](https://github.com/community-scripts/ProxmoxVE/pull/13935)) - Jitsi-Meet ([#13897](https://github.com/community-scripts/ProxmoxVE/pull/13897)) ### 🚀 Updated Scripts - Update wger.sh [@Soppster1029](https://github.com/Soppster1029) ([#13977](https://github.com/community-scripts/ProxmoxVE/pull/13977)) - #### 🔧 Refactor - Refactor: Ghostfolio [@MickLesk](https://github.com/MickLesk) ([#13990](https://github.com/community-scripts/ProxmoxVE/pull/13990)) ## 2026-04-23 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - mealie: start.sh missing after failed update [@MickLesk](https://github.com/MickLesk) ([#13958](https://github.com/community-scripts/ProxmoxVE/pull/13958)) - twingate-connector: perform real apt upgrade during update flow [@MickLesk](https://github.com/MickLesk) ([#13959](https://github.com/community-scripts/ProxmoxVE/pull/13959)) - #### ✨ New Features - core: auto-size NODE_OPTIONS heap [@MickLesk](https://github.com/MickLesk) ([#13960](https://github.com/community-scripts/ProxmoxVE/pull/13960)) - #### 🔧 Refactor - Update scripts to match standard [@tremor021](https://github.com/tremor021) ([#13956](https://github.com/community-scripts/ProxmoxVE/pull/13956)) ### 💾 Core - #### 🐞 Bug Fixes - tools.func: upgrade Node.js minor/patch on same major version [@MickLesk](https://github.com/MickLesk) ([#13957](https://github.com/community-scripts/ProxmoxVE/pull/13957)) - core: hotfix - prefer silent mode on PHS env conflict [@MickLesk](https://github.com/MickLesk) ([#13951](https://github.com/community-scripts/ProxmoxVE/pull/13951)) - #### 🔧 Refactor - core: improve system update information / lxc stack upgrade [@MickLesk](https://github.com/MickLesk) ([#13970](https://github.com/community-scripts/ProxmoxVE/pull/13970)) ## 2026-04-22 ### 🆕 New Scripts - Dashy ([#13817](https://github.com/community-scripts/ProxmoxVE/pull/13817)) - Mini-QR ([#13902](https://github.com/community-scripts/ProxmoxVE/pull/13902)) - ownfoil ([#13904](https://github.com/community-scripts/ProxmoxVE/pull/13904)) - ERPNext ([#13921](https://github.com/community-scripts/ProxmoxVE/pull/13921)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - add --clear to uv venv in update_script() to prevent interactive prompt [@MickLesk](https://github.com/MickLesk) ([#13926](https://github.com/community-scripts/ProxmoxVE/pull/13926)) ### 💾 Core - #### ✨ New Features - core: Add PHS_VERBOSE env var to skip verbose mode prompts [@gormanity](https://github.com/gormanity) ([#13797](https://github.com/community-scripts/ProxmoxVE/pull/13797)) ## 2026-04-21 ### 🆕 New Scripts - gogs ([#13896](https://github.com/community-scripts/ProxmoxVE/pull/13896)) - anchor ([#13895](https://github.com/community-scripts/ProxmoxVE/pull/13895)) - minthcm ([#13903](https://github.com/community-scripts/ProxmoxVE/pull/13903)) - foldergram ([#13900](https://github.com/community-scripts/ProxmoxVE/pull/13900)) ### 🚀 Updated Scripts - OpenCloud: Pin version to 6.1.0 [@vhsdream](https://github.com/vhsdream) ([#13890](https://github.com/community-scripts/ProxmoxVE/pull/13890)) - #### 🐞 Bug Fixes - Domain-Locker: Update dependencies [@tremor021](https://github.com/tremor021) ([#13901](https://github.com/community-scripts/ProxmoxVE/pull/13901)) - homelable: fix install failure by correcting password-reset chmod target [@Copilot](https://github.com/Copilot) ([#13894](https://github.com/community-scripts/ProxmoxVE/pull/13894)) - #### ✨ New Features - FileFlows: Update dependencies [@tremor021](https://github.com/tremor021) ([#13917](https://github.com/community-scripts/ProxmoxVE/pull/13917)) ## 2026-04-20 ### 🆕 New Scripts - WhoDB ([#13880](https://github.com/community-scripts/ProxmoxVE/pull/13880)) ### 🚀 Updated Scripts - pangolin: create migration tables before data transfer to prevent role loss [@MickLesk](https://github.com/MickLesk) ([#13874](https://github.com/community-scripts/ProxmoxVE/pull/13874)) - #### 🐞 Bug Fixes - Pangolin: pre-apply schema migrations to prevent data loss [@MickLesk](https://github.com/MickLesk) ([#13861](https://github.com/community-scripts/ProxmoxVE/pull/13861)) - ActualBudget: change migration messages to warnings [@MickLesk](https://github.com/MickLesk) ([#13860](https://github.com/community-scripts/ProxmoxVE/pull/13860)) - slskd: migrate config keys for 0.25.0 breaking change [@MickLesk](https://github.com/MickLesk) ([#13862](https://github.com/community-scripts/ProxmoxVE/pull/13862)) - #### ✨ New Features - Wanderer: add pocketbase CLI wrapper with env [@MickLesk](https://github.com/MickLesk) ([#13863](https://github.com/community-scripts/ProxmoxVE/pull/13863)) - feat(homelable): add password reset utility script [@davidsoncabista](https://github.com/davidsoncabista) ([#13798](https://github.com/community-scripts/ProxmoxVE/pull/13798)) - #### 🔧 Refactor - Several Scripts: Bump NodeJS to align Node.js versions with upstream for 5 scripts [@MickLesk](https://github.com/MickLesk) ([#13875](https://github.com/community-scripts/ProxmoxVE/pull/13875)) - Refactor: PMG Post Install [@MickLesk](https://github.com/MickLesk) ([#13693](https://github.com/community-scripts/ProxmoxVE/pull/13693)) ### 💾 Core - #### 🐞 Bug Fixes - core: detect Perl breakage after LXC stack upgrade and improve storage validation [@MickLesk](https://github.com/MickLesk) ([#13879](https://github.com/community-scripts/ProxmoxVE/pull/13879)) ## 2026-04-19 ### 🆕 New Scripts - nametag ([#13849](https://github.com/community-scripts/ProxmoxVE/pull/13849)) ## 2026-04-18 ### 🆕 New Scripts - Dagu ([#13830](https://github.com/community-scripts/ProxmoxVE/pull/13830)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - BabyBuddy: set DJANGO_SETTINGS_MODULE before migrate in update [@MickLesk](https://github.com/MickLesk) ([#13836](https://github.com/community-scripts/ProxmoxVE/pull/13836)) - litellm: add prisma generate and use venv binary directly [@MickLesk](https://github.com/MickLesk) ([#13835](https://github.com/community-scripts/ProxmoxVE/pull/13835)) - yamtrack: add missing nginx.conf sed edits to update script [@MickLesk](https://github.com/MickLesk) ([#13834](https://github.com/community-scripts/ProxmoxVE/pull/13834)) ### 🧰 Tools - #### 🐞 Bug Fixes - SparkyFitness Garmin Microservice: fix update function [@tomfrenzel](https://github.com/tomfrenzel) ([#13824](https://github.com/community-scripts/ProxmoxVE/pull/13824)) - #### 🔧 Refactor - Clean-Orphan-LVM: check all cluster nodes for VM/CT configs [@MickLesk](https://github.com/MickLesk) ([#13837](https://github.com/community-scripts/ProxmoxVE/pull/13837)) ## 2026-04-17 ### 🆕 New Scripts - step-ca ([#13775](https://github.com/community-scripts/ProxmoxVE/pull/13775)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - core: pin IGC version to compute-runtime compatible tag (Intel GPU) [@MickLesk](https://github.com/MickLesk) ([#13814](https://github.com/community-scripts/ProxmoxVE/pull/13814)) - Fix for bambuddy community script update [@abbasegbeyemi](https://github.com/abbasegbeyemi) ([#13816](https://github.com/community-scripts/ProxmoxVE/pull/13816)) - Umami: Fix update procedure [@tremor021](https://github.com/tremor021) ([#13807](https://github.com/community-scripts/ProxmoxVE/pull/13807)) ### 💾 Core - #### 🐞 Bug Fixes - core: sanitize mount_fs input — strip spaces and trailing commas [@MickLesk](https://github.com/MickLesk) ([#13806](https://github.com/community-scripts/ProxmoxVE/pull/13806)) - #### 🔧 Refactor - core: fix some pct create issues (telemetry) + cleanup [@MickLesk](https://github.com/MickLesk) ([#13810](https://github.com/community-scripts/ProxmoxVE/pull/13810)) ## 2026-04-16 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Add pnpm as a dependency to ghost-cli install [@YourFavoriteKyle](https://github.com/YourFavoriteKyle) ([#13789](https://github.com/community-scripts/ProxmoxVE/pull/13789)) ### 💾 Core - #### ✨ New Features - core: wire ENABLE_MKNOD and ALLOW_MOUNT_FS into LXC features [@MickLesk](https://github.com/MickLesk) ([#13796](https://github.com/community-scripts/ProxmoxVE/pull/13796)) ## 2026-04-15 ### 🆕 New Scripts - iGotify ([#13773](https://github.com/community-scripts/ProxmoxVE/pull/13773)) - GitHub-Runner ([#13709](https://github.com/community-scripts/ProxmoxVE/pull/13709)) - Revert "Remove low-install-count CT scripts and installers (#13570)" [@CrazyWolf13](https://github.com/CrazyWolf13) ([#13752](https://github.com/community-scripts/ProxmoxVE/pull/13752)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - [alpine-nextcloud] Update Nginx MIME types to support .mjs files [@GuiltyFox](https://github.com/GuiltyFox) ([#13771](https://github.com/community-scripts/ProxmoxVE/pull/13771)) - Domain Monitor: Fix file ownership after update [@tremor021](https://github.com/tremor021) ([#13759](https://github.com/community-scripts/ProxmoxVE/pull/13759)) - #### 💥 Breaking Changes - Reitti: refactor scripts for v4 - remove RabbitMQ and Photon [@MickLesk](https://github.com/MickLesk) ([#13728](https://github.com/community-scripts/ProxmoxVE/pull/13728)) - #### 🔧 Refactor - Semaphore: add BoltDB to SQLite migration [@tremor021](https://github.com/tremor021) ([#13779](https://github.com/community-scripts/ProxmoxVE/pull/13779)) ### 📚 Documentation - cleanup: remove docs/, update README & CONTRIBUTING, fix repo config [@MickLesk](https://github.com/MickLesk) ([#13770](https://github.com/community-scripts/ProxmoxVE/pull/13770)) ## 2026-04-14 ### 🚀 Updated Scripts - Immich: Pin photo-processing library revisions [@vhsdream](https://github.com/vhsdream) ([#13748](https://github.com/community-scripts/ProxmoxVE/pull/13748)) - #### 🐞 Bug Fixes - BentoPDF: Nginx fixes [@tremor021](https://github.com/tremor021) ([#13741](https://github.com/community-scripts/ProxmoxVE/pull/13741)) - Zerobyte: add git to dependencies to fix bun install failure [@Copilot](https://github.com/Copilot) ([#13721](https://github.com/community-scripts/ProxmoxVE/pull/13721)) - alpine-nextcloud-install: do not use deprecated nginx config [@AlexanderStein](https://github.com/AlexanderStein) ([#13726](https://github.com/community-scripts/ProxmoxVE/pull/13726)) - #### ✨ New Features - Mealie: support v3.15+ Nuxt 4 migration [@MickLesk](https://github.com/MickLesk) ([#13731](https://github.com/community-scripts/ProxmoxVE/pull/13731)) - #### 🔧 Refactor - Lyrion: correct service name and version file in update script [@MickLesk](https://github.com/MickLesk) ([#13734](https://github.com/community-scripts/ProxmoxVE/pull/13734)) - Changedetection: move env vars from service file to .env [@tremor021](https://github.com/tremor021) ([#13732](https://github.com/community-scripts/ProxmoxVE/pull/13732)) ## 2026-04-13 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Slskd: Remove stale Soularr lock file on startup and redirect logs to stderr [@MickLesk](https://github.com/MickLesk) ([#13669](https://github.com/community-scripts/ProxmoxVE/pull/13669)) - Bambuddy: preserve database and archive on update [@Copilot](https://github.com/Copilot) ([#13706](https://github.com/community-scripts/ProxmoxVE/pull/13706)) - #### ✨ New Features - Immich: Pin version to 2.7.5 [@vhsdream](https://github.com/vhsdream) ([#13715](https://github.com/community-scripts/ProxmoxVE/pull/13715)) - Bytestash: auto backup/restore data on update [@MickLesk](https://github.com/MickLesk) ([#13707](https://github.com/community-scripts/ProxmoxVE/pull/13707)) - OpenCloud: pin version to 6.0.0 [@vhsdream](https://github.com/vhsdream) ([#13691](https://github.com/community-scripts/ProxmoxVE/pull/13691)) - #### 💥 Breaking Changes - Mealie: pin version to v3.14.0 in install and update scripts [@Copilot](https://github.com/Copilot) ([#13724](https://github.com/community-scripts/ProxmoxVE/pull/13724)) - #### 🔧 Refactor - core: remove unused TEMP_DIR mktemp leak in build_container / clean sonarqube [@MickLesk](https://github.com/MickLesk) ([#13708](https://github.com/community-scripts/ProxmoxVE/pull/13708)) ## 2026-04-12 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Alpine-Wakapi: Remove container checks in update_script function [@MickLesk](https://github.com/MickLesk) ([#13694](https://github.com/community-scripts/ProxmoxVE/pull/13694)) - #### 🔧 Refactor - IronClaw: Install keychain dependencies and launch in a DBus session [@MickLesk](https://github.com/MickLesk) ([#13692](https://github.com/community-scripts/ProxmoxVE/pull/13692)) - MeTube: Allow pnpm build scripts to fix ERR_PNPM_IGNORED_BUILDS [@MickLesk](https://github.com/MickLesk) ([#13668](https://github.com/community-scripts/ProxmoxVE/pull/13668)) ## 2026-04-11 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Immich: Ensure newline before appending IMMICH_HELMET_FILE to .env [@MickLesk](https://github.com/MickLesk) ([#13667](https://github.com/community-scripts/ProxmoxVE/pull/13667)) - #### ✨ New Features - BentoPDF: replace http-server with nginx to fix WASM initialization timeout [@MickLesk](https://github.com/MickLesk) ([#13625](https://github.com/community-scripts/ProxmoxVE/pull/13625)) - Element Synapse: Add MatrixRTC configuration for Element Call support [@MickLesk](https://github.com/MickLesk) ([#13665](https://github.com/community-scripts/ProxmoxVE/pull/13665)) - RomM: Use ROMM_BASE_PATH from .env for symlinks and nginx config [@MickLesk](https://github.com/MickLesk) ([#13666](https://github.com/community-scripts/ProxmoxVE/pull/13666)) - Immich: Pin version to 2.7.4 [@vhsdream](https://github.com/vhsdream) ([#13661](https://github.com/community-scripts/ProxmoxVE/pull/13661)) - #### 🔧 Refactor - Crafty Controller: Wait for credentials file instead of fixed sleep [@MickLesk](https://github.com/MickLesk) ([#13670](https://github.com/community-scripts/ProxmoxVE/pull/13670)) - Refactor: Alpine-Wakapi [@tremor021](https://github.com/tremor021) ([#13656](https://github.com/community-scripts/ProxmoxVE/pull/13656)) ## 2026-04-10 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - fix: ensure trailing newline in redis.conf before appending bind directive [@Copilot](https://github.com/Copilot) ([#13647](https://github.com/community-scripts/ProxmoxVE/pull/13647)) - #### ✨ New Features - Immich: Pin version to 2.7.3 [@vhsdream](https://github.com/vhsdream) ([#13631](https://github.com/community-scripts/ProxmoxVE/pull/13631)) - Homarr: bind Redis to localhost only [@MickLesk](https://github.com/MickLesk) ([#13552](https://github.com/community-scripts/ProxmoxVE/pull/13552)) ### 💾 Core - #### 🐞 Bug Fixes - tools.func: prevent script crash when entering GitHub token after rate limit [@MickLesk](https://github.com/MickLesk) ([#13638](https://github.com/community-scripts/ProxmoxVE/pull/13638)) ### 🧰 Tools - #### 🔧 Refactor - addons: Filebrowser & Filebrowser-Quantum get warning if host install [@MickLesk](https://github.com/MickLesk) ([#13639](https://github.com/community-scripts/ProxmoxVE/pull/13639)) ## 2026-04-09 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - boostack: add: git [@CrazyWolf13](https://github.com/CrazyWolf13) ([#13620](https://github.com/community-scripts/ProxmoxVE/pull/13620)) - #### ✨ New Features - Update OPNsense version from 25.7 to 26.1 [@tdn131](https://github.com/tdn131) ([#13626](https://github.com/community-scripts/ProxmoxVE/pull/13626)) - CheckMK: Bump Default OS to 13 (trixie) + dynamic codename + fix RELEASE-Tag Fetching [@MickLesk](https://github.com/MickLesk) ([#13610](https://github.com/community-scripts/ProxmoxVE/pull/13610)) ## 2026-04-08 ### 🆕 New Scripts - IronClaw | Alpine-IronClaw ([#13591](https://github.com/community-scripts/ProxmoxVE/pull/13591)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - immich: disable upgrade-insecure-requests CSP directive [@MickLesk](https://github.com/MickLesk) ([#13600](https://github.com/community-scripts/ProxmoxVE/pull/13600)) - Immich: v2.7.2 [@vhsdream](https://github.com/vhsdream) ([#13579](https://github.com/community-scripts/ProxmoxVE/pull/13579)) - Update flaresolverr-install.sh [@maztheman](https://github.com/maztheman) ([#13584](https://github.com/community-scripts/ProxmoxVE/pull/13584)) - #### ✨ New Features - bambuddy: add mkdir before data restore & add ffmpeg dependency [@MickLesk](https://github.com/MickLesk) ([#13601](https://github.com/community-scripts/ProxmoxVE/pull/13601)) - #### 🔧 Refactor - feat: update UHF Server script to use setup_ffmpeg [@zackwithak13](https://github.com/zackwithak13) ([#13564](https://github.com/community-scripts/ProxmoxVE/pull/13564)) ### 💾 Core - #### ✨ New Features - core: add script page badges to descriptions | change donate URL [@MickLesk](https://github.com/MickLesk) ([#13596](https://github.com/community-scripts/ProxmoxVE/pull/13596)) ## 2026-04-07 ### 🗑️ Deleted Scripts - Remove low-install-count CT scripts and installers [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#13570](https://github.com/community-scripts/ProxmoxVE/pull/13570)) ### 💾 Core - #### ✨ New Features - core: improve resilience for top Proxmox error codes (209, 215, 118, 206) [@MickLesk](https://github.com/MickLesk) ([#13575](https://github.com/community-scripts/ProxmoxVE/pull/13575)) ## 2026-04-06 ### 🆕 New Scripts - OpenThread Border Router ([#13536](https://github.com/community-scripts/ProxmoxVE/pull/13536)) - Homelable ([#13539](https://github.com/community-scripts/ProxmoxVE/pull/13539)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Papra: check env before copy [@MickLesk](https://github.com/MickLesk) ([#13553](https://github.com/community-scripts/ProxmoxVE/pull/13553)) - changedetection: fix: typing_extensions error [@CrazyWolf13](https://github.com/CrazyWolf13) ([#13548](https://github.com/community-scripts/ProxmoxVE/pull/13548)) - kasm: fix: fetch latest version [@CrazyWolf13](https://github.com/CrazyWolf13) ([#13547](https://github.com/community-scripts/ProxmoxVE/pull/13547)) ## 2026-04-05 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Grist: remove install:ee step (private repo, not needed for grist-core) [@MickLesk](https://github.com/MickLesk) ([#13526](https://github.com/community-scripts/ProxmoxVE/pull/13526)) - Nginx Proxy Manager: ensure /tmp/nginx/body exists via openresty service [@MickLesk](https://github.com/MickLesk) ([#13528](https://github.com/community-scripts/ProxmoxVE/pull/13528)) - MotionEye: run as root to enable SMB share support [@MickLesk](https://github.com/MickLesk) ([#13527](https://github.com/community-scripts/ProxmoxVE/pull/13527)) ### 💾 Core - #### 🔧 Refactor - core: silent() function - use return instead of exit to allow || true error handling [@MickLesk](https://github.com/MickLesk) ([#13529](https://github.com/community-scripts/ProxmoxVE/pull/13529)) ## 2026-04-04 ### 🧰 Tools - #### 🐞 Bug Fixes - komodo: set `PERIPHERY_CORE_PUBLIC_KEYS` to default value if absent [@4ndv](https://github.com/4ndv) ([#13519](https://github.com/community-scripts/ProxmoxVE/pull/13519)) ## 2026-04-03 ### 🆕 New Scripts - netboot.xyz ([#13480](https://github.com/community-scripts/ProxmoxVE/pull/13480)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - OpenWRT-VM: use poweroff instead of halt to properly stop VM [@MickLesk](https://github.com/MickLesk) ([#13504](https://github.com/community-scripts/ProxmoxVE/pull/13504)) - NginxProxyManager: fix openresty restart by setting user root before reload [@MickLesk](https://github.com/MickLesk) ([#13500](https://github.com/community-scripts/ProxmoxVE/pull/13500)) - #### ✨ New Features - Crafty Controller: add Java 25 for Minecraft 1.26.1+ [@MickLesk](https://github.com/MickLesk) ([#13502](https://github.com/community-scripts/ProxmoxVE/pull/13502)) - Wealthfolio: update to v3.2.1 and Node.js 24 [@afadil](https://github.com/afadil) ([#13486](https://github.com/community-scripts/ProxmoxVE/pull/13486)) ### 💾 Core - #### 🐞 Bug Fixes - core.func: prevent profile.d scripts from aborting on non-zero exit [@MickLesk](https://github.com/MickLesk) ([#13503](https://github.com/community-scripts/ProxmoxVE/pull/13503)) - #### ✨ New Features - APT Proxy: Support full URLs (http/https with custom ports) [@MickLesk](https://github.com/MickLesk) ([#13474](https://github.com/community-scripts/ProxmoxVE/pull/13474)) ### 🧰 Tools - #### 🐞 Bug Fixes - PVE LXC-Updater: pipe apt list through cat to prevent pager hang [@MickLesk](https://github.com/MickLesk) ([#13501](https://github.com/community-scripts/ProxmoxVE/pull/13501)) ## 2026-04-02 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Grist: Guard backup restore for empty docs/db files [@MickLesk](https://github.com/MickLesk) ([#13472](https://github.com/community-scripts/ProxmoxVE/pull/13472)) - fix(zigbee2mqtt): suppress grep error when pnpm-workspace.yaml is absent on update [@Copilot](https://github.com/Copilot) ([#13476](https://github.com/community-scripts/ProxmoxVE/pull/13476)) ### 🧰 Tools - #### 🐞 Bug Fixes - Cron LXC Updater: Add full PATH for cron environment [@MickLesk](https://github.com/MickLesk) ([#13473](https://github.com/community-scripts/ProxmoxVE/pull/13473)) ## 2026-04-01 ### 🆕 New Scripts - DrawDB ([#13454](https://github.com/community-scripts/ProxmoxVE/pull/13454)) ### 🧰 Tools - #### 🐞 Bug Fixes - Filebrowser: make noauth setup use correct database [@MickLesk](https://github.com/MickLesk) ([#13457](https://github.com/community-scripts/ProxmoxVE/pull/13457)) ================================================ FILE: .github/changelogs/2026/05.md ================================================ ## 2026-05-09 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - FlowiseAI: Migrate to pnpm [@MickLesk](https://github.com/MickLesk) ([#14344](https://github.com/community-scripts/ProxmoxVE/pull/14344)) - Purge openresty [@lucacome](https://github.com/lucacome) ([#14353](https://github.com/community-scripts/ProxmoxVE/pull/14353)) - Check for release for Sonarr [@lucacome](https://github.com/lucacome) ([#14354](https://github.com/community-scripts/ProxmoxVE/pull/14354)) - fix(termix-install.sh): add tmpfiles.d persistence and systemd PIDFile path [@runnylogan](https://github.com/runnylogan) ([#14350](https://github.com/community-scripts/ProxmoxVE/pull/14350)) - ERPNext: start bench Redis services before bench new-site [@MickLesk](https://github.com/MickLesk) ([#14343](https://github.com/community-scripts/ProxmoxVE/pull/14343)) - [Hotfix]Jotty: use absolute path when creating data dir [@vhsdream](https://github.com/vhsdream) ([#14355](https://github.com/community-scripts/ProxmoxVE/pull/14355)) ## 2026-05-08 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - wishlist: pin pnpm to v10 to match engine requirements [@MickLesk](https://github.com/MickLesk) ([#14342](https://github.com/community-scripts/ProxmoxVE/pull/14342)) - [pelican] fix env copy regression [@LetterN](https://github.com/LetterN) ([#14328](https://github.com/community-scripts/ProxmoxVE/pull/14328)) - fix(homepage): fix ERR_PNPM_IGNORED_BUILDS error [@Sergih28](https://github.com/Sergih28) ([#14315](https://github.com/community-scripts/ProxmoxVE/pull/14315)) - #### ✨ New Features - tools.func: add setup_nltk as new function [@MickLesk](https://github.com/MickLesk) ([#14314](https://github.com/community-scripts/ProxmoxVE/pull/14314)) ### 💾 Core - #### 🐞 Bug Fixes - tools.func: fix meilisearch import-dump background process handling [@MickLesk](https://github.com/MickLesk) ([#14341](https://github.com/community-scripts/ProxmoxVE/pull/14341)) ## 2026-05-07 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - termix: create /tmp/nginx before nginx -t [@MickLesk](https://github.com/MickLesk) ([#14312](https://github.com/community-scripts/ProxmoxVE/pull/14312)) - The Lounge: Fix service not starting automaticaly [@tremor021](https://github.com/tremor021) ([#14311](https://github.com/community-scripts/ProxmoxVE/pull/14311)) - netbird-lxc: fix installation check [@MickLesk](https://github.com/MickLesk) ([#14309](https://github.com/community-scripts/ProxmoxVE/pull/14309)) - databasus: Backup and secure configuration file [@MickLesk](https://github.com/MickLesk) ([#14308](https://github.com/community-scripts/ProxmoxVE/pull/14308)) - vm: update disk image URL for Ubuntu 25.04 [@MickLesk](https://github.com/MickLesk) ([#14290](https://github.com/community-scripts/ProxmoxVE/pull/14290)) - #### ✨ New Features - pangolin: bump version to 1.18.3 [@MickLesk](https://github.com/MickLesk) ([#14297](https://github.com/community-scripts/ProxmoxVE/pull/14297)) ### 🗑️ Deleted Scripts - Remove: LiteLLM [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#14294](https://github.com/community-scripts/ProxmoxVE/pull/14294)) ### 💾 Core - #### ✨ New Features - update-apps: some improvements [@MickLesk](https://github.com/MickLesk) ([#14275](https://github.com/community-scripts/ProxmoxVE/pull/14275)) ## 2026-05-06 ### 🆕 New Scripts - Hoodik ([#14279](https://github.com/community-scripts/ProxmoxVE/pull/14279)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Pelican-Panel: create backup subdirectory before copying storage [@MickLesk](https://github.com/MickLesk) ([#14274](https://github.com/community-scripts/ProxmoxVE/pull/14274)) - Rustdeskserver: remove redundant else with undefined RELEASE var [@MickLesk](https://github.com/MickLesk) ([#14272](https://github.com/community-scripts/ProxmoxVE/pull/14272)) ### 🧰 Tools - #### 🔧 Refactor - AdguardHome-Sync replace ifconfig with hostname -I for IP detection [@MickLesk](https://github.com/MickLesk) ([#14273](https://github.com/community-scripts/ProxmoxVE/pull/14273)) ## 2026-05-05 ### 🆕 New Scripts - LibreChat ([#14247](https://github.com/community-scripts/ProxmoxVE/pull/14247)) - Matomo ([#14248](https://github.com/community-scripts/ProxmoxVE/pull/14248)) - Storyteller ([#14122](https://github.com/community-scripts/ProxmoxVE/pull/14122)) ### 🧰 Tools - Fix container count message in update-apps.sh [@Quotacious](https://github.com/Quotacious) ([#14265](https://github.com/community-scripts/ProxmoxVE/pull/14265)) ## 2026-05-04 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Databasus: move .env to filesystem root so service starts correctly [@Copilot](https://github.com/Copilot) ([#14252](https://github.com/community-scripts/ProxmoxVE/pull/14252)) - Databasus: update mongo-tools fallback to 100.16.1 and use now pnpm instead of npm ci [@MickLesk](https://github.com/MickLesk) ([#14240](https://github.com/community-scripts/ProxmoxVE/pull/14240)) ### 💾 Core - #### ✨ New Features - tools.func get_latest_gh_tag - add pagination to find prefixed tags beyond first 50 [@MickLesk](https://github.com/MickLesk) ([#14241](https://github.com/community-scripts/ProxmoxVE/pull/14241)) - tools.func: add GitLab release check/fetch/deploy helpers [@MickLesk](https://github.com/MickLesk) ([#14242](https://github.com/community-scripts/ProxmoxVE/pull/14242)) ## 2026-05-03 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Hortusfox: fix update issues [@tomfrenzel](https://github.com/tomfrenzel) ([#14214](https://github.com/community-scripts/ProxmoxVE/pull/14214)) - #### ✨ New Features - Refactor: PeaNUT for v6 [@MickLesk](https://github.com/MickLesk) ([#14224](https://github.com/community-scripts/ProxmoxVE/pull/14224)) - pangolin: pin version, drop manual SQL, use upstream migrator [@MickLesk](https://github.com/MickLesk) ([#14223](https://github.com/community-scripts/ProxmoxVE/pull/14223)) ### 💾 Core - #### 🐞 Bug Fixes - core: fix validate_bridge function [@MichaelOultram](https://github.com/MichaelOultram) ([#14206](https://github.com/community-scripts/ProxmoxVE/pull/14206)) ### 🧰 Tools - #### 🐞 Bug Fixes - pve/pbs scripts: guard sed against missing /etc/apt/sources.list [@MickLesk](https://github.com/MickLesk) ([#14222](https://github.com/community-scripts/ProxmoxVE/pull/14222)) ## 2026-05-02 ### 🆕 New Scripts - protonmail-bridge ([#14136](https://github.com/community-scripts/ProxmoxVE/pull/14136)) - Tube Archivist ([#14123](https://github.com/community-scripts/ProxmoxVE/pull/14123)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Nagios: Ping fix [@tremor021](https://github.com/tremor021) ([#14186](https://github.com/community-scripts/ProxmoxVE/pull/14186)) - opnsense-vm: retry pvesm alloc on transient zfs 'got timeout' errors [@MickLesk](https://github.com/MickLesk) ([#14157](https://github.com/community-scripts/ProxmoxVE/pull/14157)) - ImmichFrame: fix update by reinstalling dotnet-sdk before publish [@MickLesk](https://github.com/MickLesk) ([#14158](https://github.com/community-scripts/ProxmoxVE/pull/14158)) - [FIX]ShelfMark: Use UV sync for shelfmark backend build; update to Python 3.14 [@vhsdream](https://github.com/vhsdream) ([#14170](https://github.com/community-scripts/ProxmoxVE/pull/14170)) - alpine: remove deb/ubuntu-only resource & storage checks from update-script [@MickLesk](https://github.com/MickLesk) ([#14166](https://github.com/community-scripts/ProxmoxVE/pull/14166)) - Threadfin: use 'threadfin-app' as app name to avoid version-file clash [@MickLesk](https://github.com/MickLesk) ([#14159](https://github.com/community-scripts/ProxmoxVE/pull/14159)) ### 💾 Core - #### ✨ New Features - core: prompt to also run installed addon update scripts (…/bin/update_*) after update_script [@MickLesk](https://github.com/MickLesk) ([#14162](https://github.com/community-scripts/ProxmoxVE/pull/14162)) ## 2026-05-01 ### 🆕 New Scripts - SoulSync ([#14124](https://github.com/community-scripts/ProxmoxVE/pull/14124)) - Teable ([#14125](https://github.com/community-scripts/ProxmoxVE/pull/14125)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Step ca update [@heinemannj](https://github.com/heinemannj) ([#14058](https://github.com/community-scripts/ProxmoxVE/pull/14058)) - paperless-ngx: refresh NLTK data on update [@kurtislanderson](https://github.com/kurtislanderson) ([#14144](https://github.com/community-scripts/ProxmoxVE/pull/14144)) - [Pelican Panel] stop deleting the public storage [@LetterN](https://github.com/LetterN) ([#14145](https://github.com/community-scripts/ProxmoxVE/pull/14145)) - #### 🔧 Refactor - Mail-Archiver: update dependencies [@tremor021](https://github.com/tremor021) ([#14152](https://github.com/community-scripts/ProxmoxVE/pull/14152)) ================================================ FILE: .github/pull_request_template.md ================================================ ## ✍️ Description ## 🔗 Related PR / Discussion / Issue Link: # ## ✅ Prerequisites Before this PR can be reviewed, the following must be completed: - [] **Self-review performed** – Code follows established patterns and conventions. - [] **Testing performed** – Changes have been thoroughly tested and verified. ## 📋 Additional Information (optional) - [ ] **Self-review completed** – Code follows project standards. - [ ] **Tested thoroughly** – Changes work as expected. - [ ] **No security risks** – No hardcoded secrets, unnecessary privilege escalations, or permission issues. --- ## 🛠️ Type of Change (**X** in brackets) - [ ] 🐞 **Bug fix** – Resolves an issue without breaking functionality. - [ ] ✨ **New feature** – Adds new, non-breaking functionality. - [ ] 💥 **Breaking change** – Alters existing functionality in a way that may require updates. - [ ] 🆕 **New script** – A fully functional and tested script or script set. - [ ] 🌍 **Website update** – Changes to script metadata (PocketBase/website data). - [ ] 🔧 **Refactoring / Code Cleanup** – Improves readability or maintainability without changing functionality. - [ ] 📝 **Documentation update** – Changes to `README`, `AppName.md`, `CONTRIBUTING.md`, or other docs. ================================================ FILE: .github/workflows/delete-pocketbase-entry-on-removal.yml ================================================ name: Set state to is_deleted in pocketbase on: push: branches: - main paths: - "json/**" - "vm/**" - "tools/**" - "turnkey/**" - "ct/**" - "install/**" jobs: delete-pocketbase-entry: runs-on: self-hosted steps: - name: Checkout Repository uses: actions/checkout@v4 with: fetch-depth: 0 - name: Get slugs from deleted JSON and script files id: slugs run: | BEFORE="${{ github.event.before }}" AFTER="${{ github.event.after }}" slugs="" # Deleted JSON files: get slug from previous commit deleted_json=$(git diff --name-only --diff-filter=D "$BEFORE" "$AFTER" -- json/ | grep '\.json$' || true) for f in $deleted_json; do [[ -z "$f" ]] && continue s=$(git show "$BEFORE:$f" 2>/dev/null | jq -r '.slug // empty' 2>/dev/null || true) [[ -n "$s" ]] && slugs="$slugs $s" done # Deleted script files: derive slug from path deleted_sh=$(git diff --name-only --diff-filter=D "$BEFORE" "$AFTER" -- ct/ install/ tools/ turnkey/ vm/ | grep '\.sh$' || true) for f in $deleted_sh; do [[ -z "$f" ]] && continue base="${f##*/}" base="${base%.sh}" if [[ "$f" == install/* && "$base" == *-install ]]; then s="${base%-install}" else s="$base" fi [[ -n "$s" ]] && slugs="$slugs $s" done slugs=$(echo $slugs | xargs -n1 | sort -u | tr '\n' ' ') if [[ -z "$slugs" ]]; then echo "No deleted JSON or script files to mark as deleted in PocketBase." echo "count=0" >> "$GITHUB_OUTPUT" exit 0 fi echo "$slugs" > slugs_to_delete.txt echo "count=$(echo $slugs | wc -w)" >> "$GITHUB_OUTPUT" echo "Slugs to mark as deleted: $slugs" - name: Mark as deleted in PocketBase if: steps.slugs.outputs.count != '0' env: POCKETBASE_URL: ${{ secrets.POCKETBASE_URL }} POCKETBASE_COLLECTION: ${{ secrets.POCKETBASE_COLLECTION }} POCKETBASE_ADMIN_EMAIL: ${{ secrets.POCKETBASE_ADMIN_EMAIL }} POCKETBASE_ADMIN_PASSWORD: ${{ secrets.POCKETBASE_ADMIN_PASSWORD }} run: | node << 'ENDSCRIPT' (async function() { const fs = require('fs'); const https = require('https'); const http = require('http'); const url = require('url'); function request(fullUrl, opts, redirectCount) { redirectCount = redirectCount || 0; return new Promise(function(resolve, reject) { const u = url.parse(fullUrl); const isHttps = u.protocol === 'https:'; const body = opts.body; const options = { hostname: u.hostname, port: u.port || (isHttps ? 443 : 80), path: u.path, method: opts.method || 'GET', headers: opts.headers || {} }; if (body) options.headers['Content-Length'] = Buffer.byteLength(body); const lib = isHttps ? https : http; const req = lib.request(options, function(res) { if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) { if (redirectCount >= 5) return reject(new Error('Too many redirects from ' + fullUrl)); const redirectUrl = url.resolve(fullUrl, res.headers.location); res.resume(); resolve(request(redirectUrl, opts, redirectCount + 1)); return; } let data = ''; res.on('data', function(chunk) { data += chunk; }); res.on('end', function() { resolve({ ok: res.statusCode >= 200 && res.statusCode < 300, statusCode: res.statusCode, body: data }); }); }); req.on('error', reject); if (body) req.write(body); req.end(); }); } const raw = process.env.POCKETBASE_URL.replace(/\/$/, ''); const apiBase = /\/api$/i.test(raw) ? raw : raw + '/api'; const coll = process.env.POCKETBASE_COLLECTION; const slugs = fs.readFileSync('slugs_to_delete.txt', 'utf8').trim().split(/\s+/).filter(Boolean); const authUrl = apiBase + '/collections/users/auth-with-password'; const authBody = JSON.stringify({ identity: process.env.POCKETBASE_ADMIN_EMAIL, password: process.env.POCKETBASE_ADMIN_PASSWORD }); const authRes = await request(authUrl, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: authBody }); if (!authRes.ok) { throw new Error('Auth failed. Response: ' + authRes.body); } const token = JSON.parse(authRes.body).token; const recordsUrl = apiBase + '/collections/' + encodeURIComponent(coll) + '/records'; const patchBody = JSON.stringify({ is_deleted: true }); for (const slug of slugs) { const filter = "(slug='" + slug + "')"; const listRes = await request(recordsUrl + '?filter=' + encodeURIComponent(filter) + '&perPage=1', { headers: { 'Authorization': token } }); const list = JSON.parse(listRes.body); const existingId = list.items && list.items[0] && list.items[0].id; if (!existingId) { console.log('No PocketBase record for slug "' + slug + '", skipping.'); continue; } const patchRes = await request(recordsUrl + '/' + existingId, { method: 'PATCH', headers: { 'Authorization': token, 'Content-Type': 'application/json' }, body: patchBody }); if (patchRes.ok) { console.log('Set is_deleted=true for slug "' + slug + '" (id=' + existingId + ').'); } else { console.warn('PATCH failed for slug "' + slug + '": ' + patchRes.statusCode + ' ' + patchRes.body); } } console.log('Done.'); })().catch(e => { console.error(e); process.exit(1); }); ENDSCRIPT shell: bash ================================================ FILE: .github/workflows/pocketbase-bot.yml ================================================ name: PocketBase Bot on: issue_comment: types: [created] permissions: issues: write pull-requests: write contents: write jobs: pocketbase-bot: runs-on: self-hosted # Only act on /pocketbase commands if: startsWith(github.event.comment.body, '/pocketbase') steps: - name: Execute PocketBase bot command env: POCKETBASE_URL: ${{ secrets.POCKETBASE_URL }} POCKETBASE_COLLECTION: ${{ secrets.POCKETBASE_COLLECTION }} POCKETBASE_ADMIN_EMAIL: ${{ secrets.POCKETBASE_ADMIN_EMAIL }} POCKETBASE_ADMIN_PASSWORD: ${{ secrets.POCKETBASE_ADMIN_PASSWORD }} COMMENT_BODY: ${{ github.event.comment.body }} COMMENT_ID: ${{ github.event.comment.id }} ISSUE_NUMBER: ${{ github.event.issue.number }} REPO_OWNER: ${{ github.repository_owner }} REPO_NAME: ${{ github.event.repository.name }} ACTOR: ${{ github.event.comment.user.login }} ACTOR_ASSOCIATION: ${{ github.event.comment.author_association }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} FRONTEND_URL: ${{ secrets.FRONTEND_URL }} REVALIDATE_SECRET: ${{ secrets.REVALIDATE_SECRET }} run: | node << 'ENDSCRIPT' (async function () { const https = require('https'); const http = require('http'); const url = require('url'); // ── HTTP helper with redirect following ──────────────────────────── function request(fullUrl, opts, redirectCount) { redirectCount = redirectCount || 0; return new Promise(function (resolve, reject) { const u = url.parse(fullUrl); const isHttps = u.protocol === 'https:'; const body = opts.body; const options = { hostname: u.hostname, port: u.port || (isHttps ? 443 : 80), path: u.path, method: opts.method || 'GET', headers: opts.headers || {} }; if (body) options.headers['Content-Length'] = Buffer.byteLength(body); const lib = isHttps ? https : http; const req = lib.request(options, function (res) { if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) { if (redirectCount >= 5) return reject(new Error('Too many redirects from ' + fullUrl)); const redirectUrl = url.resolve(fullUrl, res.headers.location); res.resume(); resolve(request(redirectUrl, opts, redirectCount + 1)); return; } let data = ''; res.on('data', function (chunk) { data += chunk; }); res.on('end', function () { resolve({ ok: res.statusCode >= 200 && res.statusCode < 300, statusCode: res.statusCode, body: data }); }); }); req.on('error', reject); if (body) req.write(body); req.end(); }); } // ── GitHub API helpers ───────────────────────────────────────────── const owner = process.env.REPO_OWNER; const repo = process.env.REPO_NAME; const issueNumber = parseInt(process.env.ISSUE_NUMBER, 10); const commentId = parseInt(process.env.COMMENT_ID, 10); const actor = process.env.ACTOR; function ghRequest(path, method, body) { const headers = { 'Authorization': 'Bearer ' + process.env.GITHUB_TOKEN, 'Accept': 'application/vnd.github+json', 'X-GitHub-Api-Version': '2022-11-28', 'User-Agent': 'PocketBase-Bot' }; const bodyStr = body ? JSON.stringify(body) : undefined; if (bodyStr) headers['Content-Type'] = 'application/json'; return request('https://api.github.com' + path, { method: method || 'GET', headers, body: bodyStr }); } function encodeContentPath(filePath) { return filePath.split('/').map(encodeURIComponent).join('/'); } function decodeGitHubContent(content) { return Buffer.from((content || '').replace(/\n/g, ''), 'base64').toString('utf8'); } function sanitizeBranchPart(value) { return (value || '') .toLowerCase() .replace(/[^a-z0-9._/-]+/g, '-') .replace(/\/+/g, '/') .replace(/^-+|-+$/g, ''); } function applyCtDefaultChanges(scriptText, varChanges) { let nextText = scriptText; const updatedVars = []; const unchangedVars = []; for (const [varName, rawValue] of Object.entries(varChanges)) { const newValue = String(rawValue); const pattern = new RegExp('(^\\s*' + varName + '="\\$\\{' + varName + ':-)([^"}]*)(\\}"\\s*$)', 'm'); const match = nextText.match(pattern); if (!match) continue; if (match[2] === newValue) { unchangedVars.push(varName); continue; } nextText = nextText.replace(pattern, '$1' + newValue + '$3'); updatedVars.push(varName); } return { nextText, updatedVars, unchangedVars }; } async function ensureBranch(defaultBranch, branchName) { const branchRefRes = await ghRequest('/repos/' + owner + '/' + repo + '/git/ref/heads/' + encodeURIComponent(branchName)); if (branchRefRes.ok) return; const defaultRefRes = await ghRequest('/repos/' + owner + '/' + repo + '/git/ref/heads/' + encodeURIComponent(defaultBranch)); if (!defaultRefRes.ok) { throw new Error('Could not read default branch ref: ' + defaultRefRes.body); } const defaultRef = JSON.parse(defaultRefRes.body); const createBranchRes = await ghRequest('/repos/' + owner + '/' + repo + '/git/refs', 'POST', { ref: 'refs/heads/' + branchName, sha: defaultRef.object.sha }); if (!createBranchRes.ok) { throw new Error('Could not create branch: ' + createBranchRes.body); } } async function upsertCtDefaultsPr(slugValue, varChanges) { const wantedEntries = Object.entries(varChanges || {}).filter(function ([, v]) { return v !== undefined && v !== null && String(v) !== ''; }); if (wantedEntries.length === 0) { return { status: 'skipped', reason: 'No mapped CT defaults changed.' }; } const repoRes = await ghRequest('/repos/' + owner + '/' + repo); if (!repoRes.ok) { throw new Error('Could not read repository metadata: ' + repoRes.body); } const repoInfo = JSON.parse(repoRes.body); const defaultBranch = repoInfo.default_branch; const ctPath = 'ct/' + slugValue + '.sh'; const encodedCtPath = encodeContentPath(ctPath); const defaultFileRes = await ghRequest('/repos/' + owner + '/' + repo + '/contents/' + encodedCtPath + '?ref=' + encodeURIComponent(defaultBranch)); if (defaultFileRes.statusCode === 404) { return { status: 'skipped', reason: 'No matching CT file found at `' + ctPath + '`.' }; } if (!defaultFileRes.ok) { throw new Error('Could not read CT file from default branch: ' + defaultFileRes.body); } const branchName = 'pocketbase-sync/' + sanitizeBranchPart(slugValue || 'unknown'); await ensureBranch(defaultBranch, branchName); const branchFileRes = await ghRequest('/repos/' + owner + '/' + repo + '/contents/' + encodedCtPath + '?ref=' + encodeURIComponent(branchName)); if (!branchFileRes.ok) { throw new Error('Could not read CT file from sync branch: ' + branchFileRes.body); } const branchFile = JSON.parse(branchFileRes.body); const currentBranchText = decodeGitHubContent(branchFile.content); const updateResult = applyCtDefaultChanges(currentBranchText, Object.fromEntries(wantedEntries)); if (updateResult.updatedVars.length === 0) { return { status: 'skipped', reason: 'CT defaults already up to date.', unchangedVars: updateResult.unchangedVars }; } const commitMessage = 'chore(ct): sync ' + slugValue + ' defaults from PocketBase'; const putRes = await ghRequest('/repos/' + owner + '/' + repo + '/contents/' + encodedCtPath, 'PUT', { message: commitMessage, content: Buffer.from(updateResult.nextText, 'utf8').toString('base64'), sha: branchFile.sha, branch: branchName }); if (!putRes.ok) { throw new Error('Could not update CT file: ' + putRes.body); } const openPrRes = await ghRequest( '/repos/' + owner + '/' + repo + '/pulls?state=open&head=' + encodeURIComponent(owner + ':' + branchName) + '&base=' + encodeURIComponent(defaultBranch) ); if (!openPrRes.ok) { throw new Error('Could not query existing PRs: ' + openPrRes.body); } const openPrs = JSON.parse(openPrRes.body); if (openPrs.length > 0) { return { status: 'updated', prUrl: openPrs[0].html_url, updatedVars: updateResult.updatedVars }; } const prTitle = 'chore(ct): sync ' + slugValue + ' defaults with PocketBase'; const prBody = '## Summary\n' + '- Sync default CT variables for `' + slugValue + '` after `/pocketbase` update.\n' + '- Updated vars: `' + updateResult.updatedVars.join('`, `') + '`.\n\n' + '## Source\n' + '- Triggered by @' + actor + ' via PocketBase bot.\n'; const createPrRes = await ghRequest('/repos/' + owner + '/' + repo + '/pulls', 'POST', { title: prTitle, body: prBody, head: branchName, base: defaultBranch }); if (!createPrRes.ok) { throw new Error('Could not create PR: ' + createPrRes.body); } const pr = JSON.parse(createPrRes.body); return { status: 'created', prUrl: pr.html_url, updatedVars: updateResult.updatedVars }; } function formatCtSyncResult(syncResult) { if (!syncResult) return ''; if (syncResult.status === 'created') return '\n\n**CT sync PR:** ' + syncResult.prUrl; if (syncResult.status === 'updated') return '\n\n**CT sync PR updated:** ' + syncResult.prUrl; if (syncResult.status === 'skipped') return '\n\n**CT sync skipped:** ' + syncResult.reason; return ''; } async function addReaction(content) { try { await ghRequest( '/repos/' + owner + '/' + repo + '/issues/comments/' + commentId + '/reactions', 'POST', { content } ); } catch (e) { console.warn('Could not add reaction:', e.message); } } async function postComment(text) { const res = await ghRequest( '/repos/' + owner + '/' + repo + '/issues/' + issueNumber + '/comments', 'POST', { body: text } ); if (!res.ok) console.warn('Could not post comment:', res.body); } // ── Permission check ─────────────────────────────────────────────── const association = process.env.ACTOR_ASSOCIATION; if (association !== 'OWNER' && association !== 'MEMBER') { await addReaction('-1'); await postComment( '❌ **PocketBase Bot**: @' + actor + ' is not authorized to use this command.\n' + 'Only org members (Contributors team) can use `/pocketbase`.' ); process.exit(0); } // ── Acknowledge ──────────────────────────────────────────────────── await addReaction('eyes'); // ── Parse command ────────────────────────────────────────────────── const commentBody = process.env.COMMENT_BODY || ''; const lines = commentBody.trim().split('\n'); const firstLine = lines[0].trim(); const withoutCmd = firstLine.replace(/^\/pocketbase\s+/, '').trim(); function extractCodeBlock(body) { const m = body.match(/```[^\n]*\n([\s\S]*?)```/); return m ? m[1].trim() : null; } const codeBlockValue = extractCodeBlock(commentBody); const HELP_TEXT = '**Show current state:**\n' + '```\n/pocketbase info\n```\n\n' + '**Field update (simple):** `/pocketbase field=value [field=value ...]`\n\n' + '**Field update (HTML/multiline) — value from code block:**\n' + '````\n' + '/pocketbase set description\n' + '```html\n' + '

Your HTML or multi-line content here

\n' + '```\n' + '````\n\n' + '**Note management:**\n' + '```\n' + '/pocketbase note list\n' + '/pocketbase note add ""\n' + '/pocketbase note edit "" ""\n' + '/pocketbase note remove ""\n' + '```\n\n' + '**Install method management:**\n' + '```\n' + '/pocketbase method list\n' + '/pocketbase method cpu=4 ram=2048 hdd=20\n' + '/pocketbase method config_path="/opt/app/.env"\n' + '/pocketbase method os=debian version=13\n' + '/pocketbase method add cpu=2 ram=2048 hdd=8 os=debian version=13\n' + '/pocketbase method remove \n' + '```\n' + 'Method fields: `cpu` `ram` `hdd` `os` `version` `config_path` `script`\n\n' + '**Editable fields:** `name` `description` `logo` `documentation` `website` `project_url` `github` ' + '`config_path` `port` `default_user` `default_passwd` ' + '`updateable` `privileged` `has_arm` `is_dev` ' + '`is_disabled` `disable_message` `is_deleted` `deleted_message`'; if (!withoutCmd) { await addReaction('-1'); await postComment('❌ **PocketBase Bot**: No slug or command specified.\n\n' + HELP_TEXT); process.exit(0); } const spaceIdx = withoutCmd.indexOf(' '); const slug = (spaceIdx === -1 ? withoutCmd : withoutCmd.substring(0, spaceIdx)).trim(); const rest = spaceIdx === -1 ? '' : withoutCmd.substring(spaceIdx + 1).trim(); if (!rest) { await addReaction('-1'); await postComment('❌ **PocketBase Bot**: No command specified for slug `' + slug + '`.\n\n' + HELP_TEXT); process.exit(0); } // ── PocketBase: authenticate ─────────────────────────────────────── const raw = process.env.POCKETBASE_URL.replace(/\/$/, ''); const apiBase = /\/api$/i.test(raw) ? raw : raw + '/api'; const coll = process.env.POCKETBASE_COLLECTION; const authRes = await request(apiBase + '/collections/users/auth-with-password', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ identity: process.env.POCKETBASE_ADMIN_EMAIL, password: process.env.POCKETBASE_ADMIN_PASSWORD }) }); if (!authRes.ok) { await addReaction('-1'); await postComment('❌ **PocketBase Bot**: PocketBase authentication failed. CC @' + owner + '/maintainers'); process.exit(1); } const token = JSON.parse(authRes.body).token; // ── PocketBase: find record by slug ──────────────────────────────── const recordsUrl = apiBase + '/collections/' + encodeURIComponent(coll) + '/records'; const filter = "(slug='" + slug.replace(/'/g, "''") + "')"; const listRes = await request(recordsUrl + '?filter=' + encodeURIComponent(filter) + '&perPage=1', { headers: { 'Authorization': token } }); const list = JSON.parse(listRes.body); const record = list.items && list.items[0]; if (!record) { await addReaction('-1'); await postComment( '❌ **PocketBase Bot**: No record found for slug `' + slug + '`.\n\n' + 'Make sure the script was already pushed to PocketBase (JSON must exist and have been synced).' ); process.exit(0); } // ── Shared helpers ───────────────────────────────────────────────── // Key=value parser: handles unquoted and "quoted" values function parseKVPairs(str) { const fields = {}; let pos = 0; while (pos < str.length) { while (pos < str.length && /\s/.test(str[pos])) pos++; if (pos >= str.length) break; let keyStart = pos; while (pos < str.length && str[pos] !== '=' && !/\s/.test(str[pos])) pos++; const key = str.substring(keyStart, pos).trim(); if (!key || pos >= str.length || str[pos] !== '=') { pos++; continue; } pos++; let value; if (pos < str.length && str[pos] === '"') { pos++; let valStart = pos; while (pos < str.length && str[pos] !== '"') { if (str[pos] === '\\') pos++; pos++; } value = str.substring(valStart, pos).replace(/\\"/g, '"'); if (pos < str.length) pos++; } else { let valStart = pos; while (pos < str.length && !/\s/.test(str[pos])) pos++; value = str.substring(valStart, pos); } fields[key] = value; } return fields; } // Token parser for note commands: unquoted-word OR "quoted string" function parseTokens(str) { const tokens = []; let pos = 0; while (pos < str.length) { while (pos < str.length && /\s/.test(str[pos])) pos++; if (pos >= str.length) break; if (str[pos] === '"') { pos++; let start = pos; while (pos < str.length && str[pos] !== '"') { if (str[pos] === '\\') pos++; pos++; } tokens.push(str.substring(start, pos).replace(/\\"/g, '"')); if (pos < str.length) pos++; } else { let start = pos; while (pos < str.length && !/\s/.test(str[pos])) pos++; tokens.push(str.substring(start, pos)); } } return tokens; } // Read JSON blob from record (handles parsed objects and strings) function readJsonBlob(val) { if (Array.isArray(val)) return val; try { return JSON.parse(val || '[]'); } catch (e) { return []; } } // Frontend cache revalidation (silent, best-effort) async function revalidate(s) { const frontendUrl = process.env.FRONTEND_URL; const secret = process.env.REVALIDATE_SECRET; if (!frontendUrl || !secret) return; try { await request(frontendUrl.replace(/\/$/, '') + '/api/revalidate', { method: 'POST', headers: { 'Authorization': 'Bearer ' + secret, 'Content-Type': 'application/json' }, body: JSON.stringify({ tags: ['scripts', 'script-' + s] }) }); } catch (e) { console.warn('Revalidation skipped:', e.message); } } // Format notes list for display function formatNotesList(arr) { if (arr.length === 0) return '*None*'; return arr.map(function (n, i) { return (i + 1) + '. **`' + (n.type || '?') + '`**: ' + (n.text || ''); }).join('\n'); } // Format install methods list for display function formatMethodsList(arr) { if (arr.length === 0) return '*None*'; return arr.map(function (im, i) { const r = im.resources || {}; const parts = [ (r.os || '?') + ' ' + (r.version || '?'), (r.cpu != null ? r.cpu : '?') + 'C / ' + (r.ram != null ? r.ram : '?') + ' MB / ' + (r.hdd != null ? r.hdd : '?') + ' GB' ]; if (im.config_path) parts.push('config: `' + im.config_path + '`'); if (im.script) parts.push('script: `' + im.script + '`'); return (i + 1) + '. **`' + (im.type || '?') + '`** — ' + parts.join(', '); }).join('\n'); } // ── Route: dispatch to subcommand handler ────────────────────────── const infoMatch = rest.match(/^info$/i); const noteMatch = rest.match(/^note\s+(list|add|edit|remove)\b/i); const methodMatch = rest.match(/^method\b/i); const setMatch = rest.match(/^set\s+(\S+)/i); if (infoMatch) { // ── INFO SUBCOMMAND ────────────────────────────────────────────── const notesArr = readJsonBlob(record.notes); const methodsArr = readJsonBlob(record.install_methods); const out = []; out.push('ℹ️ **PocketBase Bot**: Info for **`' + slug + '`**\n'); out.push('**Basic info:**'); out.push('- **Name:** ' + (record.name || '—')); out.push('- **Slug:** `' + slug + '`'); out.push('- **Port:** ' + (record.port != null ? '`' + record.port + '`' : '—')); out.push('- **Updateable:** ' + (record.updateable ? 'Yes' : 'No')); out.push('- **Privileged:** ' + (record.privileged ? 'Yes' : 'No')); out.push('- **ARM:** ' + (record.has_arm ? 'Yes' : 'No')); if (record.is_dev) out.push('- **Dev:** Yes'); if (record.is_disabled) out.push('- **Disabled:** Yes' + (record.disable_message ? ' — ' + record.disable_message : '')); if (record.is_deleted) out.push('- **Deleted:** Yes' + (record.deleted_message ? ' — ' + record.deleted_message : '')); out.push(''); out.push('**Links:**'); out.push('- **Website:** ' + (record.website || '—')); out.push('- **Docs:** ' + (record.documentation || '—')); out.push('- **Logo:** ' + (record.logo ? '[link](' + record.logo + ')' : '—')); out.push('- **GitHub:** ' + (record.github || '—')); if (record.config_path) out.push('- **Config:** `' + record.config_path + '`'); out.push(''); out.push('**Credentials:**'); out.push('- **User:** ' + (record.default_user || '—')); out.push('- **Password:** ' + (record.default_passwd ? '*(set)*' : '—')); out.push(''); out.push('**Install methods** (' + methodsArr.length + '):'); out.push(formatMethodsList(methodsArr)); out.push(''); out.push('**Notes** (' + notesArr.length + '):'); out.push(formatNotesList(notesArr)); await addReaction('+1'); await postComment(out.join('\n')); } else if (noteMatch) { // ── NOTE SUBCOMMAND ────────────────────────────────────────────── const noteAction = noteMatch[1].toLowerCase(); const noteArgsStr = rest.substring(noteMatch[0].length).trim(); let notesArr = readJsonBlob(record.notes); async function patchNotes(arr) { const res = await request(recordsUrl + '/' + record.id, { method: 'PATCH', headers: { 'Authorization': token, 'Content-Type': 'application/json' }, body: JSON.stringify({ notes: arr }) }); if (!res.ok) { await addReaction('-1'); await postComment('❌ **PocketBase Bot**: Failed to update notes:\n```\n' + res.body + '\n```'); process.exit(1); } } if (noteAction === 'list') { await addReaction('+1'); await postComment( 'ℹ️ **PocketBase Bot**: Notes for **`' + slug + '`** (' + notesArr.length + ' total)\n\n' + formatNotesList(notesArr) ); } else if (noteAction === 'add') { const tokens = parseTokens(noteArgsStr); if (tokens.length < 2) { await addReaction('-1'); await postComment( '❌ **PocketBase Bot**: `note add` requires `` and `""`.\n\n' + '**Usage:** `/pocketbase ' + slug + ' note add ""`' ); process.exit(0); } const noteType = tokens[0].toLowerCase(); const noteText = tokens.slice(1).join(' '); notesArr.push({ type: noteType, text: noteText }); await patchNotes(notesArr); await revalidate(slug); await addReaction('+1'); await postComment( '✅ **PocketBase Bot**: Added note to **`' + slug + '`**\n\n' + '- **Type:** `' + noteType + '`\n' + '- **Text:** ' + noteText + '\n\n' + '*Executed by @' + actor + '*' ); } else if (noteAction === 'edit') { const tokens = parseTokens(noteArgsStr); if (tokens.length < 3) { await addReaction('-1'); await postComment( '❌ **PocketBase Bot**: `note edit` requires ``, `""`, and `""`.\n\n' + '**Usage:** `/pocketbase ' + slug + ' note edit "" ""`\n\n' + 'Use `/pocketbase ' + slug + ' note list` to see current notes.' ); process.exit(0); } const noteType = tokens[0].toLowerCase(); const oldText = tokens[1]; const newText = tokens[2]; const idx = notesArr.findIndex(function (n) { return n.type.toLowerCase() === noteType && n.text === oldText; }); if (idx === -1) { await addReaction('-1'); await postComment( '❌ **PocketBase Bot**: No `' + noteType + '` note found with that exact text.\n\n' + '**Current notes for `' + slug + '`:**\n' + formatNotesList(notesArr) ); process.exit(0); } notesArr[idx].text = newText; await patchNotes(notesArr); await revalidate(slug); await addReaction('+1'); await postComment( '✅ **PocketBase Bot**: Edited note in **`' + slug + '`**\n\n' + '- **Type:** `' + noteType + '`\n' + '- **Old:** ' + oldText + '\n' + '- **New:** ' + newText + '\n\n' + '*Executed by @' + actor + '*' ); } else if (noteAction === 'remove') { const tokens = parseTokens(noteArgsStr); if (tokens.length < 2) { await addReaction('-1'); await postComment( '❌ **PocketBase Bot**: `note remove` requires `` and `""`.\n\n' + '**Usage:** `/pocketbase ' + slug + ' note remove ""`\n\n' + 'Use `/pocketbase ' + slug + ' note list` to see current notes.' ); process.exit(0); } const noteType = tokens[0].toLowerCase(); const noteText = tokens[1]; const before = notesArr.length; notesArr = notesArr.filter(function (n) { return !(n.type.toLowerCase() === noteType && n.text === noteText); }); if (notesArr.length === before) { await addReaction('-1'); await postComment( '❌ **PocketBase Bot**: No `' + noteType + '` note found with that exact text.\n\n' + '**Current notes for `' + slug + '`:**\n' + formatNotesList(notesArr) ); process.exit(0); } await patchNotes(notesArr); await revalidate(slug); await addReaction('+1'); await postComment( '✅ **PocketBase Bot**: Removed note from **`' + slug + '`**\n\n' + '- **Type:** `' + noteType + '`\n' + '- **Text:** ' + noteText + '\n\n' + '*Executed by @' + actor + '*' ); } } else if (methodMatch) { // ── METHOD SUBCOMMAND ──────────────────────────────────────────── const methodArgs = rest.replace(/^method\s*/i, '').trim(); const methodListMode = !methodArgs || methodArgs.toLowerCase() === 'list'; let methodsArr = readJsonBlob(record.install_methods); // Method field classification const RESOURCE_KEYS = { cpu: 'number', ram: 'number', hdd: 'number', os: 'string', version: 'string' }; const METHOD_KEYS = { config_path: 'string', script: 'string' }; const ALL_METHOD_KEYS = Object.assign({}, RESOURCE_KEYS, METHOD_KEYS); const RESOURCE_TO_CT_VAR = { cpu: 'var_cpu', ram: 'var_ram', hdd: 'var_disk', os: 'var_os', version: 'var_version' }; function applyMethodChanges(method, parsed) { if (!method.resources) method.resources = {}; for (const [k, v] of Object.entries(parsed)) { if (RESOURCE_KEYS[k]) { method.resources[k] = RESOURCE_KEYS[k] === 'number' ? parseInt(v, 10) : v; } else if (METHOD_KEYS[k]) { method[k] = v === '' ? null : v; } } } async function patchMethods(arr) { const res = await request(recordsUrl + '/' + record.id, { method: 'PATCH', headers: { 'Authorization': token, 'Content-Type': 'application/json' }, body: JSON.stringify({ install_methods: arr }) }); if (!res.ok) { await addReaction('-1'); await postComment('❌ **PocketBase Bot**: Failed to update install methods:\n```\n' + res.body + '\n```'); process.exit(1); } } if (methodListMode) { await addReaction('+1'); await postComment( 'ℹ️ **PocketBase Bot**: Install methods for **`' + slug + '`** (' + methodsArr.length + ' total)\n\n' + formatMethodsList(methodsArr) ); } else { // Check for add / remove sub-actions const addMatch = methodArgs.match(/^add\s+(\S+)(?:\s+(.+))?$/i); const removeMatch = methodArgs.match(/^remove\s+(\S+)$/i); if (addMatch) { // ── METHOD ADD ─────────────────────────────────────────────── const newType = addMatch[1]; const parsed = addMatch[2] ? parseKVPairs(addMatch[2]) : {}; if (methodsArr.some(function (im) { return (im.type || '').toLowerCase() === newType.toLowerCase(); })) { await addReaction('-1'); await postComment('❌ **PocketBase Bot**: Install method `' + newType + '` already exists for `' + slug + '`.\n\nUse `/pocketbase ' + slug + ' method list` to see all methods.'); process.exit(0); } const newMethod = { type: newType, resources: { cpu: 1, ram: 512, hdd: 4, os: 'debian', version: '13' } }; if (addMatch[2]) { const unknown = Object.keys(parsed).filter(function (k) { return !ALL_METHOD_KEYS[k]; }); if (unknown.length > 0) { await addReaction('-1'); await postComment('❌ **PocketBase Bot**: Unknown method field(s): `' + unknown.join('`, `') + '`\n\n**Allowed:** `' + Object.keys(ALL_METHOD_KEYS).join('`, `') + '`'); process.exit(0); } applyMethodChanges(newMethod, parsed); } methodsArr.push(newMethod); await patchMethods(methodsArr); await revalidate(slug); const addCtChanges = {}; for (const [k, v] of Object.entries(parsed)) { if (RESOURCE_TO_CT_VAR[k]) addCtChanges[RESOURCE_TO_CT_VAR[k]] = v; } let addCtSync = null; try { addCtSync = await upsertCtDefaultsPr(slug, addCtChanges); } catch (e) { addCtSync = { status: 'skipped', reason: 'CT sync failed: ' + e.message }; } await addReaction('+1'); await postComment( '✅ **PocketBase Bot**: Added install method **`' + newType + '`** to **`' + slug + '`**\n\n' + formatMethodsList([newMethod]) + '\n\n' + formatCtSyncResult(addCtSync) + '\n\n' + '*Executed by @' + actor + '*' ); } else if (removeMatch) { // ── METHOD REMOVE ──────────────────────────────────────────── const removeType = removeMatch[1].toLowerCase(); const removed = methodsArr.filter(function (im) { return (im.type || '').toLowerCase() === removeType; }); if (removed.length === 0) { await addReaction('-1'); const available = methodsArr.map(function (im) { return im.type || '?'; }); await postComment('❌ **PocketBase Bot**: No install method `' + removeType + '` found.\n\n**Available:** `' + (available.length ? available.join('`, `') : '(none)') + '`'); process.exit(0); } methodsArr = methodsArr.filter(function (im) { return (im.type || '').toLowerCase() !== removeType; }); await patchMethods(methodsArr); await revalidate(slug); await addReaction('+1'); await postComment( '✅ **PocketBase Bot**: Removed install method **`' + removed[0].type + '`** from **`' + slug + '`**\n\n' + '*Executed by @' + actor + '*' ); } else { // ── METHOD EDIT ────────────────────────────────────────────── const editParts = methodArgs.match(/^(\S+)\s+(.+)$/); if (!editParts) { await addReaction('-1'); await postComment( '❌ **PocketBase Bot**: Invalid `method` syntax.\n\n' + '**Usage:**\n```\n/pocketbase ' + slug + ' method list\n' + '/pocketbase ' + slug + ' method cpu=4 ram=2048 hdd=20\n' + '/pocketbase ' + slug + ' method config_path="/opt/app/.env"\n' + '/pocketbase ' + slug + ' method add cpu=2 ram=2048 hdd=8\n' + '/pocketbase ' + slug + ' method remove \n```' ); process.exit(0); } const targetType = editParts[1].toLowerCase(); const parsed = parseKVPairs(editParts[2]); const unknown = Object.keys(parsed).filter(function (k) { return !ALL_METHOD_KEYS[k]; }); if (unknown.length > 0) { await addReaction('-1'); await postComment('❌ **PocketBase Bot**: Unknown method field(s): `' + unknown.join('`, `') + '`\n\n**Allowed:** `' + Object.keys(ALL_METHOD_KEYS).join('`, `') + '`'); process.exit(0); } if (Object.keys(parsed).length === 0) { await addReaction('-1'); await postComment('❌ **PocketBase Bot**: No valid `key=value` pairs found.\n\n**Allowed:** `' + Object.keys(ALL_METHOD_KEYS).join('`, `') + '`'); process.exit(0); } const idx = methodsArr.findIndex(function (im) { return (im.type || '').toLowerCase() === targetType; }); if (idx === -1) { await addReaction('-1'); const available = methodsArr.map(function (im) { return im.type || '?'; }); await postComment( '❌ **PocketBase Bot**: No install method `' + targetType + '` found for `' + slug + '`.\n\n' + '**Available:** `' + (available.length ? available.join('`, `') : '(none)') + '`\n\n' + 'Use `/pocketbase ' + slug + ' method list` to see all methods.' ); process.exit(0); } applyMethodChanges(methodsArr[idx], parsed); await patchMethods(methodsArr); await revalidate(slug); const editCtChanges = {}; for (const [k, v] of Object.entries(parsed)) { if (RESOURCE_TO_CT_VAR[k]) editCtChanges[RESOURCE_TO_CT_VAR[k]] = v; } let editCtSync = null; try { editCtSync = await upsertCtDefaultsPr(slug, editCtChanges); } catch (e) { editCtSync = { status: 'skipped', reason: 'CT sync failed: ' + e.message }; } const changesLines = Object.entries(parsed) .map(function ([k, v]) { const unit = k === 'ram' ? ' MB' : k === 'hdd' ? ' GB' : ''; return '- `' + k + '` → `' + v + unit + '`'; }).join('\n'); await addReaction('+1'); await postComment( '✅ **PocketBase Bot**: Updated install method **`' + methodsArr[idx].type + '`** for **`' + slug + '`**\n\n' + '**Changes applied:**\n' + changesLines + '\n\n' + formatCtSyncResult(editCtSync) + '\n\n' + '*Executed by @' + actor + '*' ); } } } else if (setMatch) { // ── SET SUBCOMMAND (value from code block) ─────────────────────── const fieldName = setMatch[1].toLowerCase(); const SET_ALLOWED = { name: 'string', description: 'string', logo: 'string', documentation: 'string', website: 'string', project_url: 'string', github: 'string', config_path: 'string', disable_message: 'string', deleted_message: 'string' }; if (!SET_ALLOWED[fieldName]) { await addReaction('-1'); await postComment( '❌ **PocketBase Bot**: `set` only supports text fields.\n\n' + '**Allowed:** `' + Object.keys(SET_ALLOWED).join('`, `') + '`\n\n' + 'For boolean/number fields use `field=value` syntax instead.' ); process.exit(0); } if (!codeBlockValue) { await addReaction('-1'); await postComment( '❌ **PocketBase Bot**: `set` requires a code block with the value.\n\n' + '**Usage:**\n````\n/pocketbase ' + slug + ' set ' + fieldName + '\n```\nYour content here (HTML, multiline, special chars all fine)\n```\n````' ); process.exit(0); } const setPayload = {}; setPayload[fieldName] = codeBlockValue; const setPatchRes = await request(recordsUrl + '/' + record.id, { method: 'PATCH', headers: { 'Authorization': token, 'Content-Type': 'application/json' }, body: JSON.stringify(setPayload) }); if (!setPatchRes.ok) { await addReaction('-1'); await postComment('❌ **PocketBase Bot**: PATCH failed for `' + slug + '`:\n```\n' + setPatchRes.body + '\n```'); process.exit(1); } await revalidate(slug); const preview = codeBlockValue.length > 300 ? codeBlockValue.substring(0, 300) + '…' : codeBlockValue; await addReaction('+1'); await postComment( '✅ **PocketBase Bot**: Set `' + fieldName + '` for **`' + slug + '`**\n\n' + '**Value set:**\n```\n' + preview + '\n```\n\n' + '*Executed by @' + actor + '*' ); } else { // ── FIELD=VALUE PATH ───────────────────────────────────────────── const ALLOWED_FIELDS = { name: 'string', description: 'string', logo: 'string', documentation: 'string', website: 'string', project_url: 'string', github: 'string', config_path: 'string', tags: 'string', port: 'number', default_user: 'nullable_string', default_passwd: 'nullable_string', unprivileged: 'number', updateable: 'boolean', privileged: 'boolean', has_arm: 'boolean', is_dev: 'boolean', is_disabled: 'boolean', disable_message: 'string', is_deleted: 'boolean', deleted_message: 'string', }; const parsedFields = parseKVPairs(rest); const unknownFields = Object.keys(parsedFields).filter(function (f) { return !ALLOWED_FIELDS[f]; }); if (unknownFields.length > 0) { await addReaction('-1'); await postComment( '❌ **PocketBase Bot**: Unknown field(s): `' + unknownFields.join('`, `') + '`\n\n' + '**Allowed fields:** `' + Object.keys(ALLOWED_FIELDS).join('`, `') + '`' ); process.exit(0); } if (Object.keys(parsedFields).length === 0) { await addReaction('-1'); await postComment('❌ **PocketBase Bot**: Could not parse any valid `field=value` pairs.\n\n' + HELP_TEXT); process.exit(0); } // Cast values to correct types const payload = {}; for (const [key, rawVal] of Object.entries(parsedFields)) { const type = ALLOWED_FIELDS[key]; if (type === 'boolean') { if (rawVal === 'true') payload[key] = true; else if (rawVal === 'false') payload[key] = false; else { await addReaction('-1'); await postComment('❌ **PocketBase Bot**: `' + key + '` must be `true` or `false`, got: `' + rawVal + '`'); process.exit(0); } } else if (type === 'number') { const n = parseInt(rawVal, 10); if (isNaN(n)) { await addReaction('-1'); await postComment('❌ **PocketBase Bot**: `' + key + '` must be a number, got: `' + rawVal + '`'); process.exit(0); } payload[key] = n; } else if (type === 'nullable_string') { payload[key] = rawVal === '' ? null : rawVal; } else { payload[key] = rawVal; } } const patchRes = await request(recordsUrl + '/' + record.id, { method: 'PATCH', headers: { 'Authorization': token, 'Content-Type': 'application/json' }, body: JSON.stringify(payload) }); if (!patchRes.ok) { await addReaction('-1'); await postComment('❌ **PocketBase Bot**: PATCH failed for `' + slug + '`:\n```\n' + patchRes.body + '\n```'); process.exit(1); } await revalidate(slug); const FIELD_TO_CT_VAR = { tags: 'var_tags', unprivileged: 'var_unprivileged' }; const fieldCtChanges = {}; for (const [k, v] of Object.entries(payload)) { if (FIELD_TO_CT_VAR[k]) fieldCtChanges[FIELD_TO_CT_VAR[k]] = v; } let fieldCtSync = null; try { fieldCtSync = await upsertCtDefaultsPr(slug, fieldCtChanges); } catch (e) { fieldCtSync = { status: 'skipped', reason: 'CT sync failed: ' + e.message }; } await addReaction('+1'); const changesLines = Object.entries(payload) .map(function ([k, v]) { return '- `' + k + '` → `' + JSON.stringify(v) + '`'; }) .join('\n'); await postComment( '✅ **PocketBase Bot**: Updated **`' + slug + '`** successfully!\n\n' + '**Changes applied:**\n' + changesLines + '\n\n' + formatCtSyncResult(fieldCtSync) + '\n\n' + '*Executed by @' + actor + '*' ); } console.log('Done.'); })().catch(function (e) { console.error('Fatal error:', e.message || e); process.exit(1); }); ENDSCRIPT shell: bash ================================================ FILE: .github/workflows/push-json-to-pocketbase.yml ================================================ name: Push JSON changes to PocketBase on: push: branches: - main paths: - "json/**" jobs: push-json: runs-on: self-hosted steps: - name: Checkout Repository uses: actions/checkout@v4 with: fetch-depth: 0 - name: Get changed JSON files with slug id: changed run: | changed=$(git diff --name-only "${{ github.event.before }}" "${{ github.event.after }}" -- json/ | grep '\.json$' || true) with_slug="" for f in $changed; do [[ -f "$f" ]] || continue jq -e '.slug' "$f" >/dev/null 2>&1 && with_slug="$with_slug $f" done with_slug=$(echo $with_slug | xargs -n1) if [[ -z "$with_slug" ]]; then echo "No app JSON files changed (or no files with slug)." echo "count=0" >> "$GITHUB_OUTPUT" exit 0 fi echo "$with_slug" > changed_app_jsons.txt echo "count=$(echo "$with_slug" | wc -w)" >> "$GITHUB_OUTPUT" - name: Push to PocketBase if: steps.changed.outputs.count != '0' env: POCKETBASE_URL: ${{ secrets.POCKETBASE_URL }} POCKETBASE_COLLECTION: ${{ secrets.POCKETBASE_COLLECTION }} POCKETBASE_ADMIN_EMAIL: ${{ secrets.POCKETBASE_ADMIN_EMAIL }} POCKETBASE_ADMIN_PASSWORD: ${{ secrets.POCKETBASE_ADMIN_PASSWORD }} run: | node << 'ENDSCRIPT' (async function() { const fs = require('fs'); const https = require('https'); const http = require('http'); const url = require('url'); function request(fullUrl, opts, redirectCount) { redirectCount = redirectCount || 0; return new Promise(function(resolve, reject) { const u = url.parse(fullUrl); const isHttps = u.protocol === 'https:'; const body = opts.body; const options = { hostname: u.hostname, port: u.port || (isHttps ? 443 : 80), path: u.path, method: opts.method || 'GET', headers: opts.headers || {} }; if (body) options.headers['Content-Length'] = Buffer.byteLength(body); const lib = isHttps ? https : http; const req = lib.request(options, function(res) { if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) { if (redirectCount >= 5) return reject(new Error('Too many redirects from ' + fullUrl)); const redirectUrl = url.resolve(fullUrl, res.headers.location); res.resume(); resolve(request(redirectUrl, opts, redirectCount + 1)); return; } let data = ''; res.on('data', function(chunk) { data += chunk; }); res.on('end', function() { resolve({ ok: res.statusCode >= 200 && res.statusCode < 300, statusCode: res.statusCode, body: data }); }); }); req.on('error', reject); if (body) req.write(body); req.end(); }); } const raw = process.env.POCKETBASE_URL.replace(/\/$/, ''); const apiBase = /\/api$/i.test(raw) ? raw : raw + '/api'; const coll = process.env.POCKETBASE_COLLECTION; const files = fs.readFileSync('changed_app_jsons.txt', 'utf8').trim().split(/\s+/).filter(Boolean); const authUrl = apiBase + '/collections/users/auth-with-password'; console.log('Auth URL: ' + authUrl); const authBody = JSON.stringify({ identity: process.env.POCKETBASE_ADMIN_EMAIL, password: process.env.POCKETBASE_ADMIN_PASSWORD }); const authRes = await request(authUrl, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: authBody }); if (!authRes.ok) { throw new Error('Auth failed. Tried: ' + authUrl + ' - Verify POST to that URL with body {"identity":"...","password":"..."} works. Response: ' + authRes.body); } const token = JSON.parse(authRes.body).token; const recordsUrl = apiBase + '/collections/' + encodeURIComponent(coll) + '/records'; let categoryIdToName = {}; try { const metadata = JSON.parse(fs.readFileSync('json/metadata.json', 'utf8')); (metadata.categories || []).forEach(function(cat) { categoryIdToName[cat.id] = cat.name; }); } catch (e) { console.warn('Could not load metadata.json:', e.message); } let typeValueToId = {}; let categoryNameToPbId = {}; try { const typesRes = await request(apiBase + '/collections/z_ref_script_types/records?perPage=500', { headers: { 'Authorization': token } }); if (typesRes.ok) { const typesData = JSON.parse(typesRes.body); (typesData.items || []).forEach(function(item) { if (item.type != null) typeValueToId[item.type] = item.id; if (item.name != null) typeValueToId[item.name] = item.id; if (item.value != null) typeValueToId[item.value] = item.id; }); } } catch (e) { console.warn('Could not fetch z_ref_script_types:', e.message); } try { const catRes = await request(apiBase + '/collections/script_categories/records?perPage=500', { headers: { 'Authorization': token } }); if (catRes.ok) { const catData = JSON.parse(catRes.body); (catData.items || []).forEach(function(item) { if (item.name) categoryNameToPbId[item.name] = item.id; }); } } catch (e) { console.warn('Could not fetch script_categories:', e.message); } var noteTypeToId = {}; var installMethodTypeToId = {}; var osToId = {}; var osVersionToId = {}; try { const res = await request(apiBase + '/collections/z_ref_note_types/records?perPage=500', { headers: { 'Authorization': token } }); if (res.ok) JSON.parse(res.body).items?.forEach(function(item) { if (item.type != null) { noteTypeToId[item.type] = item.id; noteTypeToId[item.type.toLowerCase()] = item.id; } }); } catch (e) { console.warn('z_ref_note_types:', e.message); } try { const res = await request(apiBase + '/collections/z_ref_install_method_types/records?perPage=500', { headers: { 'Authorization': token } }); if (res.ok) JSON.parse(res.body).items?.forEach(function(item) { if (item.type != null) { installMethodTypeToId[item.type] = item.id; installMethodTypeToId[item.type.toLowerCase()] = item.id; } }); } catch (e) { console.warn('z_ref_install_method_types:', e.message); } try { const res = await request(apiBase + '/collections/z_ref_os/records?perPage=500', { headers: { 'Authorization': token } }); if (res.ok) JSON.parse(res.body).items?.forEach(function(item) { if (item.os != null) { osToId[item.os] = item.id; osToId[item.os.toLowerCase()] = item.id; } }); } catch (e) { console.warn('z_ref_os:', e.message); } try { const res = await request(apiBase + '/collections/z_ref_os_version/records?perPage=500&expand=os', { headers: { 'Authorization': token } }); if (res.ok) { (JSON.parse(res.body).items || []).forEach(function(item) { var osName = item.expand && item.expand.os && item.expand.os.os != null ? item.expand.os.os : null; if (osName != null && item.version != null) osVersionToId[osName + '|' + item.version] = item.id; }); } } catch (e) { console.warn('z_ref_os_version:', e.message); } var notesCollUrl = apiBase + '/collections/script_notes/records'; var installMethodsCollUrl = apiBase + '/collections/script_install_methods/records'; for (const file of files) { if (!fs.existsSync(file)) continue; const data = JSON.parse(fs.readFileSync(file, 'utf8')); if (!data.slug) { console.log('Skipping', file, '(no slug)'); continue; } var payload = { name: data.name, slug: data.slug, script_created: data.date_created || data.script_created, script_updated: new Date().toISOString().split('T')[0], updateable: data.updateable, privileged: data.privileged, port: data.interface_port != null ? data.interface_port : data.port, documentation: data.documentation, website: data.website, logo: data.logo, description: data.description, default_user: (data.default_credentials && data.default_credentials.username) || data.default_user || null, default_passwd: (data.default_credentials && data.default_credentials.password) || data.default_passwd || null, is_dev: false }; var resolvedType = typeValueToId[data.type]; if (resolvedType == null && data.type === 'ct') resolvedType = typeValueToId['lxc']; if (resolvedType) payload.type = resolvedType; var resolvedCats = (data.categories || []).map(function(n) { return categoryNameToPbId[categoryIdToName[n]]; }).filter(Boolean); if (resolvedCats.length) payload.categories = resolvedCats; if (data.version !== undefined) payload.version = data.version; if (data.changelog !== undefined) payload.changelog = data.changelog; if (data.screenshots !== undefined) payload.screenshots = data.screenshots; const filter = "(slug='" + data.slug + "')"; const listRes = await request(recordsUrl + '?filter=' + encodeURIComponent(filter) + '&perPage=1', { headers: { 'Authorization': token } }); const list = JSON.parse(listRes.body); const existingId = list.items && list.items[0] && list.items[0].id; async function resolveNotesAndInstallMethods(scriptId) { var noteIds = []; for (var i = 0; i < (data.notes || []).length; i++) { var note = data.notes[i]; var typeId = noteTypeToId[note.type]; if (typeId == null) continue; var postRes = await request(notesCollUrl, { method: 'POST', headers: { 'Authorization': token, 'Content-Type': 'application/json' }, body: JSON.stringify({ text: note.text || '', type: typeId, script: scriptId }) }); if (postRes.ok) noteIds.push(JSON.parse(postRes.body).id); } var installMethodIds = []; for (var j = 0; j < (data.install_methods || []).length; j++) { var im = data.install_methods[j]; var typeId = installMethodTypeToId[im.type]; var res = im.resources || {}; var osId = osToId[res.os]; var osVersionKey = (res.os != null && res.version != null) ? res.os + '|' + res.version : null; var osVersionId = osVersionKey ? osVersionToId[osVersionKey] : null; var imBody = { script: scriptId, resources_cpu: res.cpu != null ? res.cpu : 0, resources_ram: res.ram != null ? res.ram : 0, resources_hdd: res.hdd != null ? res.hdd : 0 }; if (typeId) imBody.type = typeId; if (osId) imBody.os = osId; if (osVersionId) imBody.os_version = osVersionId; var imPostRes = await request(installMethodsCollUrl, { method: 'POST', headers: { 'Authorization': token, 'Content-Type': 'application/json' }, body: JSON.stringify(imBody) }); if (imPostRes.ok) installMethodIds.push(JSON.parse(imPostRes.body).id); } return { noteIds: noteIds, installMethodIds: installMethodIds }; } if (existingId) { var resolved = await resolveNotesAndInstallMethods(existingId); payload.notes = resolved.noteIds; payload.install_methods = resolved.installMethodIds; console.log('Updating', file, '(slug=' + data.slug + ')'); const r = await request(recordsUrl + '/' + existingId, { method: 'PATCH', headers: { 'Authorization': token, 'Content-Type': 'application/json' }, body: JSON.stringify(payload) }); if (!r.ok) throw new Error('PATCH failed: ' + r.body); } else { console.log('Creating', file, '(slug=' + data.slug + ')'); const r = await request(recordsUrl, { method: 'POST', headers: { 'Authorization': token, 'Content-Type': 'application/json' }, body: JSON.stringify(payload) }); if (!r.ok) throw new Error('POST failed: ' + r.body); var scriptId = JSON.parse(r.body).id; var resolved = await resolveNotesAndInstallMethods(scriptId); var patchRes = await request(recordsUrl + '/' + scriptId, { method: 'PATCH', headers: { 'Authorization': token, 'Content-Type': 'application/json' }, body: JSON.stringify({ install_methods: resolved.installMethodIds, notes: resolved.noteIds }) }); if (!patchRes.ok) throw new Error('PATCH relations failed: ' + patchRes.body); } } console.log('Done.'); })().catch(e => { console.error(e); process.exit(1); }); ENDSCRIPT shell: bash ================================================ FILE: .github/workflows/trigger_github_pages_redirect.yml ================================================ name: Pages Redirect on: workflow_dispatch: permissions: pages: write id-token: write contents: read jobs: deploy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Create redirect page run: | mkdir site cat < site/index.html Redirecting... Redirecting... EOF - uses: actions/upload-pages-artifact@v3 with: path: site - name: Deploy uses: actions/deploy-pages@v4 ================================================ FILE: .github/workflows/update-script-timestamp-on-sh-change.yml ================================================ name: Update script timestamp on .sh changes on: push: branches: - main paths: - "ct/**/*.sh" - "install/**/*.sh" - "tools/**/*.sh" - "turnkey/**/*.sh" - "vm/**/*.sh" jobs: update-script-timestamp: runs-on: self-hosted steps: - name: Checkout Repository uses: actions/checkout@v4 with: fetch-depth: 0 - name: Get changed .sh files and derive slugs id: slugs run: | changed=$(git diff --name-only "${{ github.event.before }}" "${{ github.event.after }}" -- ct/ install/ tools/ turnkey/ vm/ | grep '\.sh$' || true) if [[ -z "$changed" ]]; then echo "No .sh files changed in ct/, install/, tools/, turnkey/, or vm/." echo "count=0" >> "$GITHUB_OUTPUT" exit 0 fi declare -A seen slugs="" for f in $changed; do [[ -f "$f" ]] || continue base="${f##*/}" base="${base%.sh}" if [[ "$f" == install/* && "$base" == *-install ]]; then slug="${base%-install}" else slug="$base" fi if [[ -z "${seen[$slug]:-}" ]]; then seen[$slug]=1 slugs="$slugs $slug" fi done slugs=$(echo $slugs | xargs -n1 | sort -u) if [[ -z "$slugs" ]]; then echo "No slugs to update." echo "count=0" >> "$GITHUB_OUTPUT" exit 0 fi echo "$slugs" > changed_slugs.txt echo "count=$(echo "$slugs" | wc -w)" >> "$GITHUB_OUTPUT" - name: Parse PR number from merge commit id: pr run: | re='#([0-9]+)' if [[ "$COMMIT_MSG" =~ $re ]]; then echo "number=${BASH_REMATCH[1]}" >> "$GITHUB_OUTPUT" else echo "number=" >> "$GITHUB_OUTPUT" fi env: COMMIT_MSG: ${{ github.event.head_commit.message }} - name: Update script timestamps in PocketBase if: steps.slugs.outputs.count != '0' env: POCKETBASE_URL: ${{ secrets.POCKETBASE_URL }} POCKETBASE_COLLECTION: ${{ secrets.POCKETBASE_COLLECTION }} POCKETBASE_ADMIN_EMAIL: ${{ secrets.POCKETBASE_ADMIN_EMAIL }} POCKETBASE_ADMIN_PASSWORD: ${{ secrets.POCKETBASE_ADMIN_PASSWORD }} COMMIT_URL: ${{ github.server_url }}/${{ github.repository }}/commit/${{ github.sha }} PR_URL: ${{ steps.pr.outputs.number != '' && format('{0}/{1}/pull/{2}', github.server_url, github.repository, steps.pr.outputs.number) || '' }} run: | node << 'ENDSCRIPT' (async function() { const fs = require('fs'); const https = require('https'); const http = require('http'); const url = require('url'); function request(fullUrl, opts, redirectCount) { redirectCount = redirectCount || 0; return new Promise(function(resolve, reject) { const u = url.parse(fullUrl); const isHttps = u.protocol === 'https:'; const body = opts.body; const options = { hostname: u.hostname, port: u.port || (isHttps ? 443 : 80), path: u.path, method: opts.method || 'GET', headers: opts.headers || {} }; if (body) options.headers['Content-Length'] = Buffer.byteLength(body); const lib = isHttps ? https : http; const req = lib.request(options, function(res) { if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) { if (redirectCount >= 5) return reject(new Error('Too many redirects from ' + fullUrl)); const redirectUrl = url.resolve(fullUrl, res.headers.location); res.resume(); resolve(request(redirectUrl, opts, redirectCount + 1)); return; } let data = ''; res.on('data', function(chunk) { data += chunk; }); res.on('end', function() { resolve({ ok: res.statusCode >= 200 && res.statusCode < 300, statusCode: res.statusCode, body: data }); }); }); req.on('error', reject); if (body) req.write(body); req.end(); }); } const raw = process.env.POCKETBASE_URL.replace(/\/$/, ''); const apiBase = /\/api$/i.test(raw) ? raw : raw + '/api'; const coll = process.env.POCKETBASE_COLLECTION; const slugsText = fs.readFileSync('changed_slugs.txt', 'utf8').trim(); const slugs = slugsText ? slugsText.split(/\s+/).filter(Boolean) : []; if (slugs.length === 0) { console.log('No slugs to update.'); return; } const authUrl = apiBase + '/collections/users/auth-with-password'; const authBody = JSON.stringify({ identity: process.env.POCKETBASE_ADMIN_EMAIL, password: process.env.POCKETBASE_ADMIN_PASSWORD }); const authRes = await request(authUrl, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: authBody }); if (!authRes.ok) { throw new Error('Auth failed: ' + authRes.body); } const token = JSON.parse(authRes.body).token; const recordsUrl = apiBase + '/collections/' + encodeURIComponent(coll) + '/records'; for (const slug of slugs) { const filter = "(slug='" + slug.replace(/'/g, "''") + "')"; const listRes = await request(recordsUrl + '?filter=' + encodeURIComponent(filter) + '&perPage=1', { headers: { 'Authorization': token } }); const list = JSON.parse(listRes.body); const record = list.items && list.items[0]; if (!record) { console.log('Slug not in DB, skipping: ' + slug); continue; } const today = new Date().toISOString().split('T')[0]; const patchBody = { script_updated: today, last_update_commit: process.env.PR_URL || process.env.COMMIT_URL || '' }; // When a dev script is merged into main, promote it to production if (record.is_dev === true) { patchBody.is_dev = false; patchBody.script_created = today; console.log('Promoting dev script to production: ' + slug); } const patchRes = await request(recordsUrl + '/' + record.id, { method: 'PATCH', headers: { 'Authorization': token, 'Content-Type': 'application/json' }, body: JSON.stringify(patchBody) }); if (!patchRes.ok) { console.warn('PATCH failed for slug ' + slug + ': ' + patchRes.body); continue; } console.log('Updated timestamp for slug: ' + slug); } console.log('Done.'); })().catch(e => { console.error(e); process.exit(1); }); ENDSCRIPT shell: bash ================================================ FILE: .gitignore ================================================ script*.py # General OS files .DS_Store Thumbs.db test-app/ # Editor & IDE files !.vscode/ .vscode/*.workspace .vscode/*.tmp # Log files logs/ *.log # Install scripts and temporary files install/tmp/ install/*.bak # VM and Container-specific exclusions vm/tmp/ vm/*.qcow2 vm/*.img vm/*.vmdk vm/*.iso vm/*.bak # Miscellaneous temporary files *.bak *.swp *.swo *.swn *.tmp *.backup # JSON temporary files json/ json/*.bak json/*.tmp ================================================ FILE: .shellcheckrc ================================================ disable=SC2034,SC1091,SC2155,SC2086,SC2317,SC2181,SC2164 ================================================ FILE: .vscode/extensions.json ================================================ { "recommendations": [ "bmalehorn.shell-syntax", "timonwong.shellcheck", "foxundermoon.shell-format", "editorconfig.editorconfig" ], "unwantedRecommendations": [] } ================================================ FILE: .vscode/settings.json ================================================ { "files.associations": { "*.func": "shellscript" }, "files.eol": "\n", "files.insertFinalNewline": true, "editor.formatOnSave": true, "editor.codeActionsOnSave": { "source.fixAll": "explicit" } } ================================================ FILE: CHANGELOG.md ================================================ 

Changelog

All notable changes to this project will be documented in this file.

> [!CAUTION] Exercise vigilance regarding copycat or coat-tailing sites that seek to exploit the project's popularity for potentially malicious purposes.

📜 History

2026

May (9 entries)

[View May 2026 Changelog](.github/changelogs/2026/05.md)

April (30 entries)

[View April 2026 Changelog](.github/changelogs/2026/04.md)

March (31 entries)

[View March 2026 Changelog](.github/changelogs/2026/03.md)

February (28 entries)

[View February 2026 Changelog](.github/changelogs/2026/02.md)

January (31 entries)

[View January 2026 Changelog](.github/changelogs/2026/01.md)

2025

December (31 entries)

[View December 2025 Changelog](.github/changelogs/2025/12.md)

November (29 entries)

[View November 2025 Changelog](.github/changelogs/2025/11.md)

October (30 entries)

[View October 2025 Changelog](.github/changelogs/2025/10.md)

September (29 entries)

[View September 2025 Changelog](.github/changelogs/2025/09.md)

August (30 entries)

[View August 2025 Changelog](.github/changelogs/2025/08.md)

July (29 entries)

[View July 2025 Changelog](.github/changelogs/2025/07.md)

June (29 entries)

[View June 2025 Changelog](.github/changelogs/2025/06.md)

May (30 entries)

[View May 2025 Changelog](.github/changelogs/2025/05.md)

April (25 entries)

[View April 2025 Changelog](.github/changelogs/2025/04.md)

March (30 entries)

[View March 2025 Changelog](.github/changelogs/2025/03.md)

February (26 entries)

[View February 2025 Changelog](.github/changelogs/2025/02.md)

January (27 entries)

[View January 2025 Changelog](.github/changelogs/2025/01.md)

2024

December (22 entries)

[View December 2024 Changelog](.github/changelogs/2024/12.md)

November (15 entries)

[View November 2024 Changelog](.github/changelogs/2024/11.md)

October (9 entries)

[View October 2024 Changelog](.github/changelogs/2024/10.md)

September (1 entries)

[View September 2024 Changelog](.github/changelogs/2024/09.md)

August (2 entries)

[View August 2024 Changelog](.github/changelogs/2024/08.md)

July (0 entries)

[View July 2024 Changelog](.github/changelogs/2024/07.md)

June (8 entries)

[View June 2024 Changelog](.github/changelogs/2024/06.md)

May (16 entries)

[View May 2024 Changelog](.github/changelogs/2024/05.md)

April (14 entries)

[View April 2024 Changelog](.github/changelogs/2024/04.md)

March (5 entries)

[View March 2024 Changelog](.github/changelogs/2024/03.md)

February (9 entries)

[View February 2024 Changelog](.github/changelogs/2024/02.md)

January (9 entries)

[View January 2024 Changelog](.github/changelogs/2024/01.md)

2023

December (3 entries)

[View December 2023 Changelog](.github/changelogs/2023/12.md)

November (3 entries)

[View November 2023 Changelog](.github/changelogs/2023/11.md)

October (7 entries)

[View October 2023 Changelog](.github/changelogs/2023/10.md)

September (10 entries)

[View September 2023 Changelog](.github/changelogs/2023/09.md)

August (7 entries)

[View August 2023 Changelog](.github/changelogs/2023/08.md)

July (5 entries)

[View July 2023 Changelog](.github/changelogs/2023/07.md)

June (5 entries)

[View June 2023 Changelog](.github/changelogs/2023/06.md)

May (8 entries)

[View May 2023 Changelog](.github/changelogs/2023/05.md)

April (8 entries)

[View April 2023 Changelog](.github/changelogs/2023/04.md)

March (8 entries)

[View March 2023 Changelog](.github/changelogs/2023/03.md)

February (6 entries)

[View February 2023 Changelog](.github/changelogs/2023/02.md)

January (15 entries)

[View January 2023 Changelog](.github/changelogs/2023/01.md)

2022

December (7 entries)

[View December 2022 Changelog](.github/changelogs/2022/12.md)

November (7 entries)

[View November 2022 Changelog](.github/changelogs/2022/11.md)

October (2 entries)

[View October 2022 Changelog](.github/changelogs/2022/10.md)

September (9 entries)

[View September 2022 Changelog](.github/changelogs/2022/09.md)

August (7 entries)

[View August 2022 Changelog](.github/changelogs/2022/08.md)

July (10 entries)

[View July 2022 Changelog](.github/changelogs/2022/07.md)

June (1 entries)

[View June 2022 Changelog](.github/changelogs/2022/06.md)

May (8 entries)

[View May 2022 Changelog](.github/changelogs/2022/05.md)

April (13 entries)

[View April 2022 Changelog](.github/changelogs/2022/04.md)

March (20 entries)

[View March 2022 Changelog](.github/changelogs/2022/03.md)

February (15 entries)

[View February 2022 Changelog](.github/changelogs/2022/02.md)

January (3 entries)

[View January 2022 Changelog](.github/changelogs/2022/01.md)
## 2026-05-12 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Save Omada version [@lucacome](https://github.com/lucacome) ([#14433](https://github.com/community-scripts/ProxmoxVE/pull/14433)) ## 2026-05-11 ### 🆕 New Scripts - Lychee ([#14424](https://github.com/community-scripts/ProxmoxVE/pull/14424)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Termix: fix nginx pid path and log paths on update (#) [@MickLesk](https://github.com/MickLesk) ([#14419](https://github.com/community-scripts/ProxmoxVE/pull/14419)) - Nginxproxymanager: restore NPM nginx.conf after OpenResty rebuid [@MickLesk](https://github.com/MickLesk) ([#14421](https://github.com/community-scripts/ProxmoxVE/pull/14421)) - #### 🔧 Refactor - InvestBrain: add commented reverse proxy config hints to .env [@MickLesk](https://github.com/MickLesk) ([#14422](https://github.com/community-scripts/ProxmoxVE/pull/14422)) ### 🧰 Tools - #### 🐞 Bug Fixes - Cronmaster: fix unexpected EOF in update_cronmaster script [@MickLesk](https://github.com/MickLesk) ([#14420](https://github.com/community-scripts/ProxmoxVE/pull/14420)) ## 2026-05-10 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Save Beszel version [@lucacome](https://github.com/lucacome) ([#14389](https://github.com/community-scripts/ProxmoxVE/pull/14389)) - karakeep: Fix SERVER_VERSION update [@MickLesk](https://github.com/MickLesk) ([#14378](https://github.com/community-scripts/ProxmoxVE/pull/14378)) - inspIRCd: Fix service not autostarting [@tremor021](https://github.com/tremor021) ([#14368](https://github.com/community-scripts/ProxmoxVE/pull/14368)) - #### 🔧 Refactor - refactor: webcheck [@CrazyWolf13](https://github.com/CrazyWolf13) ([#14391](https://github.com/community-scripts/ProxmoxVE/pull/14391)) ### 💾 Core - #### 🐞 Bug Fixes - [tools.func]: Pin `pnpm` version [@tremor021](https://github.com/tremor021) ([#14386](https://github.com/community-scripts/ProxmoxVE/pull/14386)) ## 2026-05-09 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - FlowiseAI: Migrate to pnpm [@MickLesk](https://github.com/MickLesk) ([#14344](https://github.com/community-scripts/ProxmoxVE/pull/14344)) - Purge openresty [@lucacome](https://github.com/lucacome) ([#14353](https://github.com/community-scripts/ProxmoxVE/pull/14353)) - Check for release for Sonarr [@lucacome](https://github.com/lucacome) ([#14354](https://github.com/community-scripts/ProxmoxVE/pull/14354)) - fix(termix-install.sh): add tmpfiles.d persistence and systemd PIDFile path [@runnylogan](https://github.com/runnylogan) ([#14350](https://github.com/community-scripts/ProxmoxVE/pull/14350)) - ERPNext: start bench Redis services before bench new-site [@MickLesk](https://github.com/MickLesk) ([#14343](https://github.com/community-scripts/ProxmoxVE/pull/14343)) - [Hotfix]Jotty: use absolute path when creating data dir [@vhsdream](https://github.com/vhsdream) ([#14355](https://github.com/community-scripts/ProxmoxVE/pull/14355)) ## 2026-05-08 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - wishlist: pin pnpm to v10 to match engine requirements [@MickLesk](https://github.com/MickLesk) ([#14342](https://github.com/community-scripts/ProxmoxVE/pull/14342)) - [pelican] fix env copy regression [@LetterN](https://github.com/LetterN) ([#14328](https://github.com/community-scripts/ProxmoxVE/pull/14328)) - fix(homepage): fix ERR_PNPM_IGNORED_BUILDS error [@Sergih28](https://github.com/Sergih28) ([#14315](https://github.com/community-scripts/ProxmoxVE/pull/14315)) - #### ✨ New Features - tools.func: add setup_nltk as new function [@MickLesk](https://github.com/MickLesk) ([#14314](https://github.com/community-scripts/ProxmoxVE/pull/14314)) ### 💾 Core - #### 🐞 Bug Fixes - tools.func: fix meilisearch import-dump background process handling [@MickLesk](https://github.com/MickLesk) ([#14341](https://github.com/community-scripts/ProxmoxVE/pull/14341)) ## 2026-05-07 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - termix: create /tmp/nginx before nginx -t [@MickLesk](https://github.com/MickLesk) ([#14312](https://github.com/community-scripts/ProxmoxVE/pull/14312)) - The Lounge: Fix service not starting automaticaly [@tremor021](https://github.com/tremor021) ([#14311](https://github.com/community-scripts/ProxmoxVE/pull/14311)) - netbird-lxc: fix installation check [@MickLesk](https://github.com/MickLesk) ([#14309](https://github.com/community-scripts/ProxmoxVE/pull/14309)) - databasus: Backup and secure configuration file [@MickLesk](https://github.com/MickLesk) ([#14308](https://github.com/community-scripts/ProxmoxVE/pull/14308)) - vm: update disk image URL for Ubuntu 25.04 [@MickLesk](https://github.com/MickLesk) ([#14290](https://github.com/community-scripts/ProxmoxVE/pull/14290)) - #### ✨ New Features - pangolin: bump version to 1.18.3 [@MickLesk](https://github.com/MickLesk) ([#14297](https://github.com/community-scripts/ProxmoxVE/pull/14297)) ### 🗑️ Deleted Scripts - Remove: LiteLLM [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#14294](https://github.com/community-scripts/ProxmoxVE/pull/14294)) ### 💾 Core - #### ✨ New Features - update-apps: some improvements [@MickLesk](https://github.com/MickLesk) ([#14275](https://github.com/community-scripts/ProxmoxVE/pull/14275)) ## 2026-05-06 ### 🆕 New Scripts - Hoodik ([#14279](https://github.com/community-scripts/ProxmoxVE/pull/14279)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Pelican-Panel: create backup subdirectory before copying storage [@MickLesk](https://github.com/MickLesk) ([#14274](https://github.com/community-scripts/ProxmoxVE/pull/14274)) - Rustdeskserver: remove redundant else with undefined RELEASE var [@MickLesk](https://github.com/MickLesk) ([#14272](https://github.com/community-scripts/ProxmoxVE/pull/14272)) ### 🧰 Tools - #### 🔧 Refactor - AdguardHome-Sync replace ifconfig with hostname -I for IP detection [@MickLesk](https://github.com/MickLesk) ([#14273](https://github.com/community-scripts/ProxmoxVE/pull/14273)) ## 2026-05-05 ### 🆕 New Scripts - LibreChat ([#14247](https://github.com/community-scripts/ProxmoxVE/pull/14247)) - Matomo ([#14248](https://github.com/community-scripts/ProxmoxVE/pull/14248)) - Storyteller ([#14122](https://github.com/community-scripts/ProxmoxVE/pull/14122)) ### 🧰 Tools - Fix container count message in update-apps.sh [@Quotacious](https://github.com/Quotacious) ([#14265](https://github.com/community-scripts/ProxmoxVE/pull/14265)) ## 2026-05-04 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Databasus: move .env to filesystem root so service starts correctly [@Copilot](https://github.com/Copilot) ([#14252](https://github.com/community-scripts/ProxmoxVE/pull/14252)) - Databasus: update mongo-tools fallback to 100.16.1 and use now pnpm instead of npm ci [@MickLesk](https://github.com/MickLesk) ([#14240](https://github.com/community-scripts/ProxmoxVE/pull/14240)) ### 💾 Core - #### ✨ New Features - tools.func get_latest_gh_tag - add pagination to find prefixed tags beyond first 50 [@MickLesk](https://github.com/MickLesk) ([#14241](https://github.com/community-scripts/ProxmoxVE/pull/14241)) - tools.func: add GitLab release check/fetch/deploy helpers [@MickLesk](https://github.com/MickLesk) ([#14242](https://github.com/community-scripts/ProxmoxVE/pull/14242)) ## 2026-05-03 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Hortusfox: fix update issues [@tomfrenzel](https://github.com/tomfrenzel) ([#14214](https://github.com/community-scripts/ProxmoxVE/pull/14214)) - #### ✨ New Features - Refactor: PeaNUT for v6 [@MickLesk](https://github.com/MickLesk) ([#14224](https://github.com/community-scripts/ProxmoxVE/pull/14224)) - pangolin: pin version, drop manual SQL, use upstream migrator [@MickLesk](https://github.com/MickLesk) ([#14223](https://github.com/community-scripts/ProxmoxVE/pull/14223)) ### 💾 Core - #### 🐞 Bug Fixes - core: fix validate_bridge function [@MichaelOultram](https://github.com/MichaelOultram) ([#14206](https://github.com/community-scripts/ProxmoxVE/pull/14206)) ### 🧰 Tools - #### 🐞 Bug Fixes - pve/pbs scripts: guard sed against missing /etc/apt/sources.list [@MickLesk](https://github.com/MickLesk) ([#14222](https://github.com/community-scripts/ProxmoxVE/pull/14222)) ## 2026-05-02 ### 🆕 New Scripts - protonmail-bridge ([#14136](https://github.com/community-scripts/ProxmoxVE/pull/14136)) - Tube Archivist ([#14123](https://github.com/community-scripts/ProxmoxVE/pull/14123)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Nagios: Ping fix [@tremor021](https://github.com/tremor021) ([#14186](https://github.com/community-scripts/ProxmoxVE/pull/14186)) - opnsense-vm: retry pvesm alloc on transient zfs 'got timeout' errors [@MickLesk](https://github.com/MickLesk) ([#14157](https://github.com/community-scripts/ProxmoxVE/pull/14157)) - ImmichFrame: fix update by reinstalling dotnet-sdk before publish [@MickLesk](https://github.com/MickLesk) ([#14158](https://github.com/community-scripts/ProxmoxVE/pull/14158)) - [FIX]ShelfMark: Use UV sync for shelfmark backend build; update to Python 3.14 [@vhsdream](https://github.com/vhsdream) ([#14170](https://github.com/community-scripts/ProxmoxVE/pull/14170)) - alpine: remove deb/ubuntu-only resource & storage checks from update-script [@MickLesk](https://github.com/MickLesk) ([#14166](https://github.com/community-scripts/ProxmoxVE/pull/14166)) - Threadfin: use 'threadfin-app' as app name to avoid version-file clash [@MickLesk](https://github.com/MickLesk) ([#14159](https://github.com/community-scripts/ProxmoxVE/pull/14159)) ### 💾 Core - #### ✨ New Features - core: prompt to also run installed addon update scripts (…/bin/update_*) after update_script [@MickLesk](https://github.com/MickLesk) ([#14162](https://github.com/community-scripts/ProxmoxVE/pull/14162)) ## 2026-05-01 ### 🆕 New Scripts - SoulSync ([#14124](https://github.com/community-scripts/ProxmoxVE/pull/14124)) - Teable ([#14125](https://github.com/community-scripts/ProxmoxVE/pull/14125)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Step ca update [@heinemannj](https://github.com/heinemannj) ([#14058](https://github.com/community-scripts/ProxmoxVE/pull/14058)) - paperless-ngx: refresh NLTK data on update [@kurtislanderson](https://github.com/kurtislanderson) ([#14144](https://github.com/community-scripts/ProxmoxVE/pull/14144)) - [Pelican Panel] stop deleting the public storage [@LetterN](https://github.com/LetterN) ([#14145](https://github.com/community-scripts/ProxmoxVE/pull/14145)) - #### 🔧 Refactor - Mail-Archiver: update dependencies [@tremor021](https://github.com/tremor021) ([#14152](https://github.com/community-scripts/ProxmoxVE/pull/14152)) ## 2026-04-30 ### 🆕 New Scripts - Nagios ([#14126](https://github.com/community-scripts/ProxmoxVE/pull/14126)) - Neko ([#14121](https://github.com/community-scripts/ProxmoxVE/pull/14121)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - alpine-docker: install openssl as core dependency | alpine-komodo: check & install openssl if missing [@MickLesk](https://github.com/MickLesk) ([#14134](https://github.com/community-scripts/ProxmoxVE/pull/14134)) - endurain: update source references to Codeberg [@MickLesk](https://github.com/MickLesk) ([#14128](https://github.com/community-scripts/ProxmoxVE/pull/14128)) ### 💾 Core - #### 🔧 Refactor - tools.func: Manage minor versions for MongoDB 8.x [@tremor021](https://github.com/tremor021) ([#14131](https://github.com/community-scripts/ProxmoxVE/pull/14131)) ## 2026-04-29 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - GrayLog: MongoDB update to 8.2.x [@tremor021](https://github.com/tremor021) ([#14114](https://github.com/community-scripts/ProxmoxVE/pull/14114)) - Graylog: Better information in the log file [@tremor021](https://github.com/tremor021) ([#14110](https://github.com/community-scripts/ProxmoxVE/pull/14110)) - #### 🔧 Refactor - Refactor: checkMK [@MickLesk](https://github.com/MickLesk) ([#14105](https://github.com/community-scripts/ProxmoxVE/pull/14105)) - PatchMon: Unpin release [@tremor021](https://github.com/tremor021) ([#14097](https://github.com/community-scripts/ProxmoxVE/pull/14097)) ### 💾 Core - #### 🔧 Refactor - core: add guidance when storage lacks rootdir support [@MickLesk](https://github.com/MickLesk) ([#14108](https://github.com/community-scripts/ProxmoxVE/pull/14108)) ## 2026-04-28 ### 🆕 New Scripts - StoryBook ([#14081](https://github.com/community-scripts/ProxmoxVE/pull/14081)) - CoreDNS ([#14082](https://github.com/community-scripts/ProxmoxVE/pull/14082)) ### 🚀 Updated Scripts - Fix Dawarich Install/Update [@Jerry1098](https://github.com/Jerry1098) ([#14078](https://github.com/community-scripts/ProxmoxVE/pull/14078)) - #### ✨ New Features - PatchMon Version 2.0.2 Script update [@9technologygroup](https://github.com/9technologygroup) ([#14095](https://github.com/community-scripts/ProxmoxVE/pull/14095)) ## 2026-04-27 ### 🚀 Updated Scripts - Add pamUsername column to userOrgs table [@JVKeller](https://github.com/JVKeller) ([#14075](https://github.com/community-scripts/ProxmoxVE/pull/14075)) - #### 🐞 Bug Fixes - Dawarich: run db:migrate before assets:precompile [@MickLesk](https://github.com/MickLesk) ([#14051](https://github.com/community-scripts/ProxmoxVE/pull/14051)) - TechnitiumDNS: always install .NET 10 if not already present [@MickLesk](https://github.com/MickLesk) ([#14049](https://github.com/community-scripts/ProxmoxVE/pull/14049)) - #### 💥 Breaking Changes - PatchMon: v2.0.0 migration [@vhsdream](https://github.com/vhsdream) ([#14015](https://github.com/community-scripts/ProxmoxVE/pull/14015)) ### 💾 Core - #### 🔧 Refactor - Update build.func - fixed spelling mistake [@m1ckywill](https://github.com/m1ckywill) ([#14047](https://github.com/community-scripts/ProxmoxVE/pull/14047)) ### 🧰 Tools - #### 🐞 Bug Fixes - update-lxcs/apps: avoid pct exec on containers mid-shutdown [@MickLesk](https://github.com/MickLesk) ([#14050](https://github.com/community-scripts/ProxmoxVE/pull/14050)) - #### ✨ New Features - Add patchmon-agent report execution in update script [@heinemannj](https://github.com/heinemannj) ([#14054](https://github.com/community-scripts/ProxmoxVE/pull/14054)) ## 2026-04-26 ### 🆕 New Scripts - TREK ([#14017](https://github.com/community-scripts/ProxmoxVE/pull/14017)) ### 🚀 Updated Scripts - fix(2fauth): handle stale backup directory on update [@omertahaoztop](https://github.com/omertahaoztop) ([#14018](https://github.com/community-scripts/ProxmoxVE/pull/14018)) - #### 🐞 Bug Fixes - Increase Frigate default CPU cores from 4 to 8 [@MickLesk](https://github.com/MickLesk) ([#14039](https://github.com/community-scripts/ProxmoxVE/pull/14039)) - Technitium DNS: Ensure directories exist before running service [@tremor021](https://github.com/tremor021) ([#14030](https://github.com/community-scripts/ProxmoxVE/pull/14030)) ### 💾 Core - #### 🐞 Bug Fixes - core: Correct deb822 repository flat path detection [@MickLesk](https://github.com/MickLesk) ([#14037](https://github.com/community-scripts/ProxmoxVE/pull/14037)) ## 2026-04-25 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - VictoriaMetrics: Stop vmagent/vmalert before update [@irishpadres](https://github.com/irishpadres) ([#14016](https://github.com/community-scripts/ProxmoxVE/pull/14016)) - Domain-Monitor: start apache2 after stop instead of reload [@omertahaoztop](https://github.com/omertahaoztop) ([#14019](https://github.com/community-scripts/ProxmoxVE/pull/14019)) - Transmute: Fix ffmpeg detection [@tremor021](https://github.com/tremor021) ([#14008](https://github.com/community-scripts/ProxmoxVE/pull/14008)) - #### 🔧 Refactor - Refactor: Technitium DNS [@tremor021](https://github.com/tremor021) ([#14013](https://github.com/community-scripts/ProxmoxVE/pull/14013)) ## 2026-04-24 ### 🆕 New Scripts - Apprise-API ([#13934](https://github.com/community-scripts/ProxmoxVE/pull/13934)) - fireshare ([#13995](https://github.com/community-scripts/ProxmoxVE/pull/13995)) - Transmute ([#13935](https://github.com/community-scripts/ProxmoxVE/pull/13935)) - Jitsi-Meet ([#13897](https://github.com/community-scripts/ProxmoxVE/pull/13897)) ### 🚀 Updated Scripts - Update wger.sh [@Soppster1029](https://github.com/Soppster1029) ([#13977](https://github.com/community-scripts/ProxmoxVE/pull/13977)) - #### 🔧 Refactor - Refactor: Ghostfolio [@MickLesk](https://github.com/MickLesk) ([#13990](https://github.com/community-scripts/ProxmoxVE/pull/13990)) ## 2026-04-23 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - mealie: start.sh missing after failed update [@MickLesk](https://github.com/MickLesk) ([#13958](https://github.com/community-scripts/ProxmoxVE/pull/13958)) - twingate-connector: perform real apt upgrade during update flow [@MickLesk](https://github.com/MickLesk) ([#13959](https://github.com/community-scripts/ProxmoxVE/pull/13959)) - #### ✨ New Features - core: auto-size NODE_OPTIONS heap [@MickLesk](https://github.com/MickLesk) ([#13960](https://github.com/community-scripts/ProxmoxVE/pull/13960)) - #### 🔧 Refactor - Update scripts to match standard [@tremor021](https://github.com/tremor021) ([#13956](https://github.com/community-scripts/ProxmoxVE/pull/13956)) ### 💾 Core - #### 🐞 Bug Fixes - tools.func: upgrade Node.js minor/patch on same major version [@MickLesk](https://github.com/MickLesk) ([#13957](https://github.com/community-scripts/ProxmoxVE/pull/13957)) - core: hotfix - prefer silent mode on PHS env conflict [@MickLesk](https://github.com/MickLesk) ([#13951](https://github.com/community-scripts/ProxmoxVE/pull/13951)) - #### 🔧 Refactor - core: improve system update information / lxc stack upgrade [@MickLesk](https://github.com/MickLesk) ([#13970](https://github.com/community-scripts/ProxmoxVE/pull/13970)) ## 2026-04-22 ### 🆕 New Scripts - Dashy ([#13817](https://github.com/community-scripts/ProxmoxVE/pull/13817)) - Mini-QR ([#13902](https://github.com/community-scripts/ProxmoxVE/pull/13902)) - ownfoil ([#13904](https://github.com/community-scripts/ProxmoxVE/pull/13904)) - ERPNext ([#13921](https://github.com/community-scripts/ProxmoxVE/pull/13921)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - add --clear to uv venv in update_script() to prevent interactive prompt [@MickLesk](https://github.com/MickLesk) ([#13926](https://github.com/community-scripts/ProxmoxVE/pull/13926)) ### 💾 Core - #### ✨ New Features - core: Add PHS_VERBOSE env var to skip verbose mode prompts [@gormanity](https://github.com/gormanity) ([#13797](https://github.com/community-scripts/ProxmoxVE/pull/13797)) ## 2026-04-21 ### 🆕 New Scripts - gogs ([#13896](https://github.com/community-scripts/ProxmoxVE/pull/13896)) - anchor ([#13895](https://github.com/community-scripts/ProxmoxVE/pull/13895)) - minthcm ([#13903](https://github.com/community-scripts/ProxmoxVE/pull/13903)) - foldergram ([#13900](https://github.com/community-scripts/ProxmoxVE/pull/13900)) ### 🚀 Updated Scripts - OpenCloud: Pin version to 6.1.0 [@vhsdream](https://github.com/vhsdream) ([#13890](https://github.com/community-scripts/ProxmoxVE/pull/13890)) - #### 🐞 Bug Fixes - Domain-Locker: Update dependencies [@tremor021](https://github.com/tremor021) ([#13901](https://github.com/community-scripts/ProxmoxVE/pull/13901)) - homelable: fix install failure by correcting password-reset chmod target [@Copilot](https://github.com/Copilot) ([#13894](https://github.com/community-scripts/ProxmoxVE/pull/13894)) - #### ✨ New Features - FileFlows: Update dependencies [@tremor021](https://github.com/tremor021) ([#13917](https://github.com/community-scripts/ProxmoxVE/pull/13917)) ## 2026-04-20 ### 🆕 New Scripts - WhoDB ([#13880](https://github.com/community-scripts/ProxmoxVE/pull/13880)) ### 🚀 Updated Scripts - pangolin: create migration tables before data transfer to prevent role loss [@MickLesk](https://github.com/MickLesk) ([#13874](https://github.com/community-scripts/ProxmoxVE/pull/13874)) - #### 🐞 Bug Fixes - Pangolin: pre-apply schema migrations to prevent data loss [@MickLesk](https://github.com/MickLesk) ([#13861](https://github.com/community-scripts/ProxmoxVE/pull/13861)) - ActualBudget: change migration messages to warnings [@MickLesk](https://github.com/MickLesk) ([#13860](https://github.com/community-scripts/ProxmoxVE/pull/13860)) - slskd: migrate config keys for 0.25.0 breaking change [@MickLesk](https://github.com/MickLesk) ([#13862](https://github.com/community-scripts/ProxmoxVE/pull/13862)) - #### ✨ New Features - Wanderer: add pocketbase CLI wrapper with env [@MickLesk](https://github.com/MickLesk) ([#13863](https://github.com/community-scripts/ProxmoxVE/pull/13863)) - feat(homelable): add password reset utility script [@davidsoncabista](https://github.com/davidsoncabista) ([#13798](https://github.com/community-scripts/ProxmoxVE/pull/13798)) - #### 🔧 Refactor - Several Scripts: Bump NodeJS to align Node.js versions with upstream for 5 scripts [@MickLesk](https://github.com/MickLesk) ([#13875](https://github.com/community-scripts/ProxmoxVE/pull/13875)) - Refactor: PMG Post Install [@MickLesk](https://github.com/MickLesk) ([#13693](https://github.com/community-scripts/ProxmoxVE/pull/13693)) ### 💾 Core - #### 🐞 Bug Fixes - core: detect Perl breakage after LXC stack upgrade and improve storage validation [@MickLesk](https://github.com/MickLesk) ([#13879](https://github.com/community-scripts/ProxmoxVE/pull/13879)) ## 2026-04-19 ### 🆕 New Scripts - nametag ([#13849](https://github.com/community-scripts/ProxmoxVE/pull/13849)) ## 2026-04-18 ### 🆕 New Scripts - Dagu ([#13830](https://github.com/community-scripts/ProxmoxVE/pull/13830)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - BabyBuddy: set DJANGO_SETTINGS_MODULE before migrate in update [@MickLesk](https://github.com/MickLesk) ([#13836](https://github.com/community-scripts/ProxmoxVE/pull/13836)) - litellm: add prisma generate and use venv binary directly [@MickLesk](https://github.com/MickLesk) ([#13835](https://github.com/community-scripts/ProxmoxVE/pull/13835)) - yamtrack: add missing nginx.conf sed edits to update script [@MickLesk](https://github.com/MickLesk) ([#13834](https://github.com/community-scripts/ProxmoxVE/pull/13834)) ### 🧰 Tools - #### 🐞 Bug Fixes - SparkyFitness Garmin Microservice: fix update function [@tomfrenzel](https://github.com/tomfrenzel) ([#13824](https://github.com/community-scripts/ProxmoxVE/pull/13824)) - #### 🔧 Refactor - Clean-Orphan-LVM: check all cluster nodes for VM/CT configs [@MickLesk](https://github.com/MickLesk) ([#13837](https://github.com/community-scripts/ProxmoxVE/pull/13837)) ## 2026-04-17 ### 🆕 New Scripts - step-ca ([#13775](https://github.com/community-scripts/ProxmoxVE/pull/13775)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - core: pin IGC version to compute-runtime compatible tag (Intel GPU) [@MickLesk](https://github.com/MickLesk) ([#13814](https://github.com/community-scripts/ProxmoxVE/pull/13814)) - Fix for bambuddy community script update [@abbasegbeyemi](https://github.com/abbasegbeyemi) ([#13816](https://github.com/community-scripts/ProxmoxVE/pull/13816)) - Umami: Fix update procedure [@tremor021](https://github.com/tremor021) ([#13807](https://github.com/community-scripts/ProxmoxVE/pull/13807)) ### 💾 Core - #### 🐞 Bug Fixes - core: sanitize mount_fs input — strip spaces and trailing commas [@MickLesk](https://github.com/MickLesk) ([#13806](https://github.com/community-scripts/ProxmoxVE/pull/13806)) - #### 🔧 Refactor - core: fix some pct create issues (telemetry) + cleanup [@MickLesk](https://github.com/MickLesk) ([#13810](https://github.com/community-scripts/ProxmoxVE/pull/13810)) ## 2026-04-16 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Add pnpm as a dependency to ghost-cli install [@YourFavoriteKyle](https://github.com/YourFavoriteKyle) ([#13789](https://github.com/community-scripts/ProxmoxVE/pull/13789)) ### 💾 Core - #### ✨ New Features - core: wire ENABLE_MKNOD and ALLOW_MOUNT_FS into LXC features [@MickLesk](https://github.com/MickLesk) ([#13796](https://github.com/community-scripts/ProxmoxVE/pull/13796)) ## 2026-04-15 ### 🆕 New Scripts - iGotify ([#13773](https://github.com/community-scripts/ProxmoxVE/pull/13773)) - GitHub-Runner ([#13709](https://github.com/community-scripts/ProxmoxVE/pull/13709)) - Revert "Remove low-install-count CT scripts and installers (#13570)" [@CrazyWolf13](https://github.com/CrazyWolf13) ([#13752](https://github.com/community-scripts/ProxmoxVE/pull/13752)) ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - [alpine-nextcloud] Update Nginx MIME types to support .mjs files [@GuiltyFox](https://github.com/GuiltyFox) ([#13771](https://github.com/community-scripts/ProxmoxVE/pull/13771)) - Domain Monitor: Fix file ownership after update [@tremor021](https://github.com/tremor021) ([#13759](https://github.com/community-scripts/ProxmoxVE/pull/13759)) - #### 💥 Breaking Changes - Reitti: refactor scripts for v4 - remove RabbitMQ and Photon [@MickLesk](https://github.com/MickLesk) ([#13728](https://github.com/community-scripts/ProxmoxVE/pull/13728)) - #### 🔧 Refactor - Semaphore: add BoltDB to SQLite migration [@tremor021](https://github.com/tremor021) ([#13779](https://github.com/community-scripts/ProxmoxVE/pull/13779)) ### 📚 Documentation - cleanup: remove docs/, update README & CONTRIBUTING, fix repo config [@MickLesk](https://github.com/MickLesk) ([#13770](https://github.com/community-scripts/ProxmoxVE/pull/13770)) ## 2026-04-14 ### 🚀 Updated Scripts - Immich: Pin photo-processing library revisions [@vhsdream](https://github.com/vhsdream) ([#13748](https://github.com/community-scripts/ProxmoxVE/pull/13748)) - #### 🐞 Bug Fixes - BentoPDF: Nginx fixes [@tremor021](https://github.com/tremor021) ([#13741](https://github.com/community-scripts/ProxmoxVE/pull/13741)) - Zerobyte: add git to dependencies to fix bun install failure [@Copilot](https://github.com/Copilot) ([#13721](https://github.com/community-scripts/ProxmoxVE/pull/13721)) - alpine-nextcloud-install: do not use deprecated nginx config [@AlexanderStein](https://github.com/AlexanderStein) ([#13726](https://github.com/community-scripts/ProxmoxVE/pull/13726)) - #### ✨ New Features - Mealie: support v3.15+ Nuxt 4 migration [@MickLesk](https://github.com/MickLesk) ([#13731](https://github.com/community-scripts/ProxmoxVE/pull/13731)) - #### 🔧 Refactor - Lyrion: correct service name and version file in update script [@MickLesk](https://github.com/MickLesk) ([#13734](https://github.com/community-scripts/ProxmoxVE/pull/13734)) - Changedetection: move env vars from service file to .env [@tremor021](https://github.com/tremor021) ([#13732](https://github.com/community-scripts/ProxmoxVE/pull/13732)) ## 2026-04-13 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Slskd: Remove stale Soularr lock file on startup and redirect logs to stderr [@MickLesk](https://github.com/MickLesk) ([#13669](https://github.com/community-scripts/ProxmoxVE/pull/13669)) - Bambuddy: preserve database and archive on update [@Copilot](https://github.com/Copilot) ([#13706](https://github.com/community-scripts/ProxmoxVE/pull/13706)) - #### ✨ New Features - Immich: Pin version to 2.7.5 [@vhsdream](https://github.com/vhsdream) ([#13715](https://github.com/community-scripts/ProxmoxVE/pull/13715)) - Bytestash: auto backup/restore data on update [@MickLesk](https://github.com/MickLesk) ([#13707](https://github.com/community-scripts/ProxmoxVE/pull/13707)) - OpenCloud: pin version to 6.0.0 [@vhsdream](https://github.com/vhsdream) ([#13691](https://github.com/community-scripts/ProxmoxVE/pull/13691)) - #### 💥 Breaking Changes - Mealie: pin version to v3.14.0 in install and update scripts [@Copilot](https://github.com/Copilot) ([#13724](https://github.com/community-scripts/ProxmoxVE/pull/13724)) - #### 🔧 Refactor - core: remove unused TEMP_DIR mktemp leak in build_container / clean sonarqube [@MickLesk](https://github.com/MickLesk) ([#13708](https://github.com/community-scripts/ProxmoxVE/pull/13708)) ## 2026-04-12 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Alpine-Wakapi: Remove container checks in update_script function [@MickLesk](https://github.com/MickLesk) ([#13694](https://github.com/community-scripts/ProxmoxVE/pull/13694)) - #### 🔧 Refactor - IronClaw: Install keychain dependencies and launch in a DBus session [@MickLesk](https://github.com/MickLesk) ([#13692](https://github.com/community-scripts/ProxmoxVE/pull/13692)) - MeTube: Allow pnpm build scripts to fix ERR_PNPM_IGNORED_BUILDS [@MickLesk](https://github.com/MickLesk) ([#13668](https://github.com/community-scripts/ProxmoxVE/pull/13668)) ## 2026-04-11 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - Immich: Ensure newline before appending IMMICH_HELMET_FILE to .env [@MickLesk](https://github.com/MickLesk) ([#13667](https://github.com/community-scripts/ProxmoxVE/pull/13667)) - #### ✨ New Features - BentoPDF: replace http-server with nginx to fix WASM initialization timeout [@MickLesk](https://github.com/MickLesk) ([#13625](https://github.com/community-scripts/ProxmoxVE/pull/13625)) - Element Synapse: Add MatrixRTC configuration for Element Call support [@MickLesk](https://github.com/MickLesk) ([#13665](https://github.com/community-scripts/ProxmoxVE/pull/13665)) - RomM: Use ROMM_BASE_PATH from .env for symlinks and nginx config [@MickLesk](https://github.com/MickLesk) ([#13666](https://github.com/community-scripts/ProxmoxVE/pull/13666)) - Immich: Pin version to 2.7.4 [@vhsdream](https://github.com/vhsdream) ([#13661](https://github.com/community-scripts/ProxmoxVE/pull/13661)) - #### 🔧 Refactor - Crafty Controller: Wait for credentials file instead of fixed sleep [@MickLesk](https://github.com/MickLesk) ([#13670](https://github.com/community-scripts/ProxmoxVE/pull/13670)) - Refactor: Alpine-Wakapi [@tremor021](https://github.com/tremor021) ([#13656](https://github.com/community-scripts/ProxmoxVE/pull/13656)) ## 2026-04-10 ### 🚀 Updated Scripts - #### 🐞 Bug Fixes - fix: ensure trailing newline in redis.conf before appending bind directive [@Copilot](https://github.com/Copilot) ([#13647](https://github.com/community-scripts/ProxmoxVE/pull/13647)) - #### ✨ New Features - Immich: Pin version to 2.7.3 [@vhsdream](https://github.com/vhsdream) ([#13631](https://github.com/community-scripts/ProxmoxVE/pull/13631)) - Homarr: bind Redis to localhost only [@MickLesk](https://github.com/MickLesk) ([#13552](https://github.com/community-scripts/ProxmoxVE/pull/13552)) ### 💾 Core - #### 🐞 Bug Fixes - tools.func: prevent script crash when entering GitHub token after rate limit [@MickLesk](https://github.com/MickLesk) ([#13638](https://github.com/community-scripts/ProxmoxVE/pull/13638)) ### 🧰 Tools - #### 🔧 Refactor - addons: Filebrowser & Filebrowser-Quantum get warning if host install [@MickLesk](https://github.com/MickLesk) ([#13639](https://github.com/community-scripts/ProxmoxVE/pull/13639)) ================================================ FILE: CONTRIBUTING.md ================================================ # Contributing to Proxmox VE Helper-Scripts Welcome! We're glad you want to contribute. This guide covers everything you need to add new scripts, improve existing ones, or help in other ways. For detailed coding standards and full documentation, visit **[community-scripts.org/docs](https://community-scripts.org/docs)**. --- ## How Can I Help? > [!IMPORTANT] > **New scripts** must always be submitted to [ProxmoxVED](https://github.com/community-scripts/ProxmoxVED) first — not to this repository. > PRs with new scripts opened directly against ProxmoxVE **will be closed without review**. > **Bug fixes, improvements, and features for existing scripts** go here (ProxmoxVE). | I want to… | Where to go | | :------------------------------------------ | :------------------------------------------------------------------------------------------- | | **Add a brand-new script** | [ProxmoxVED](https://github.com/community-scripts/ProxmoxVED) — testing repo for new scripts | | **Fix a bug or improve an existing script** | This repo (ProxmoxVE) — open a PR here | | **Add a feature to an existing script** | This repo (ProxmoxVE) — open a PR here | | Report a bug or broken script | [Open an Issue](https://github.com/community-scripts/ProxmoxVE/issues) | | Request a new script or feature | [Start a Discussion](https://github.com/community-scripts/ProxmoxVE/discussions) | | Report a security vulnerability | [Security Policy](SECURITY.md) | | Chat with contributors | [Discord](https://discord.gg/3AnUqsXnmK) | --- ## Prerequisites Before writing scripts, we recommend setting up: - **Visual Studio Code** with these extensions: - [Shell Syntax](https://marketplace.visualstudio.com/items?itemName=bmalehorn.shell-syntax) - [ShellCheck](https://marketplace.visualstudio.com/items?itemName=timonwong.shellcheck) - [Shell Format](https://marketplace.visualstudio.com/items?itemName=foxundermoon.shell-format) --- ## Script Structure Every script consists of two files: | File | Purpose | | :--------------------------- | :------------------------------------------------------ | | `ct/AppName.sh` | Container creation, variable setup, and update handling | | `install/AppName-install.sh` | Application installation logic | Use existing scripts in [`ct/`](ct/) and [`install/`](install/) as reference. Full coding standards and annotated templates are at **[community-scripts.org/docs/contribution](https://community-scripts.org/docs/contribution)**. --- ## Contribution Process ### Adding a new script New scripts are **not accepted directly in this repository**. The workflow is: 1. Fork [ProxmoxVED](https://github.com/community-scripts/ProxmoxVED) and clone it 2. Create a branch: `git switch -c feat/myapp` 3. Write your two script files: - `ct/myapp.sh` - `install/myapp-install.sh` 4. Test thoroughly in ProxmoxVED — run the script against a real Proxmox instance 5. Open a PR in **ProxmoxVED** for review and testing 6. Once accepted and verified there, the script will be promoted to ProxmoxVE by maintainers Follow the coding standards at [community-scripts.org/docs/contribution](https://community-scripts.org/docs/contribution). --- ### Fixing a bug or improving an existing script Changes to scripts that already exist in ProxmoxVE go directly here: 1. Fork **this repository** (ProxmoxVE) and clone it: ```bash git clone https://github.com/YOUR_USERNAME/ProxmoxVE cd ProxmoxVE ``` 2. Create a branch: ```bash git switch -c fix/myapp-description ``` 3. Make your changes to the relevant files in `ct/` and/or `install/` 4. Open a PR from your fork to `community-scripts/ProxmoxVE/main` Your PR should only contain the files you changed. Do not include unrelated modifications. --- ## Code Standards Key rules at a glance: - One script per service — keep them focused - Naming convention: lowercase, hyphen-separated (`my-app.sh`) - Shebang: `#!/usr/bin/env bash` - Quote all variables: `"$VAR"` not `$VAR` - Use lowercase variable names - Do not hardcode credentials or sensitive values Full standards and examples: **[community-scripts.org/docs/contribution](https://community-scripts.org/docs/contribution)** --- ## Developer Mode & Debugging Set the `dev_mode` variable to enable debugging features when testing. Flags can be combined (comma-separated): ```bash dev_mode="trace,keep" bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/myapp.sh)" ``` | Flag | Description | | :----------- | :----------------------------------------------------------- | | `trace` | Enables `set -x` for maximum verbosity during execution | | `keep` | Prevents the container from being deleted if the build fails | | `pause` | Pauses execution at key points before customization | | `breakpoint` | Drops to a shell at hardcoded `breakpoint` calls in scripts | | `logs` | Saves detailed build logs to `/var/log/community-scripts/` | | `dryrun` | Bypasses actual container creation (limited support) | | `motd` | Forces an update of the Message of the Day | --- ## Notes - **Website metadata** (name, description, logo, tags) is managed via the website — use the "Report Issue" link on any script page to request changes. Do not submit metadata changes via repo files. - **JSON files** in `json/` define script properties used by the website. See existing files for structure reference. - Keep PRs small and focused. One fix or feature per PR is ideal. - PRs with **new scripts** opened against ProxmoxVE will be closed — submit them to [ProxmoxVED](https://github.com/community-scripts/ProxmoxVED) instead. - PRs that fail CI checks will not be merged. ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2021-2026 asylumexp Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================

Proxmox arm64 Install Scripts

a port of the Promox VE Helper-Scripts to arm64!

Website | Contribute | Guides | Changelog | Support (tteck)

GitHub Repo stars ##

### About Ports the [Proxmox VE Helper Scripts](https://github.com/community-scripts/proxmoxve) project to ARM64. ## Support the Project This project is maintained by volunteers. All infrastructure costs come out of pocket, and the work is done in people's spare time. **30% of all donations are forwarded directly to cancer research and hospice care** — a cause that was important to tteck.
Support on Ko-fi   Donate via community-scripts.org
--- ## License This project is licensed under the [MIT License](LICENSE) — free to use, modify, and redistribute for personal and commercial purposes. See the full license text in [LICENSE](LICENSE). Find the scripts in the website linked above. Any issues with the scripts, please put an issue within this repository rather than upstream, as it is likely caused by my modifications. ### Support If you would like to offer support, I would appreciate a star on the repository, or for you to support the creator of the Proxmox scripts [tteck on Ko-Fi](https://ko-fi.com/D1D7EP4GF)! ## Compatibility Guide [View Compatibility Guide here](https://pimox-scripts.com)
Built on the foundation of tteck's original work · Original Repository
Maintained and expanded by the community · In memory of tteck
Proxmox® is a registered trademark of Proxmox Server Solutions GmbH
================================================ FILE: SECURITY.md ================================================ # Security Policy ## Supported Versions This project currently supports the following versions of Proxmox VE (PVE): | Version | Supported | | ------- | ------------------ | | 9.1.x | :white_check_mark: | | 9.0.x | :white_check_mark: | | 8.4.x | :white_check_mark: | | 8.3.x | Limited support* ❕ | | 8.2.x | Limited support* ❕ | | 8.1.x | Limited support* ❕ | | 8.0.x | Limited support* ❕ | | < 8.0 | :x: | *Version 8.0.x - 8.3.x has limited support. Security updates may not be provided for all issues affecting this version. *Debian 13 Containers may fail to install. You can write var_version=12 before the bash call. --- ## Reporting a Vulnerability Security vulnerabilities must not be reported publicly to avoid potential exploitation. Instead, please report them privately via one of the following channels: - **Discord**: Join our [Discord server](https://discord.gg/jsYVk5JBxq) and send a direct message to a maintainer. - **Email**: Write to us at **contact@community-scripts.org** with the subject line: `Vulnerability Report - `. When reporting a vulnerability, please provide: - A clear description of the issue - Steps to reproduce the vulnerability - Affected versions or environments - (Optional) Suggested fixes or workarounds --- ## Response Process 1. **Acknowledgment** - We will review and acknowledge your report within **7 business days**. 2. **Assessment** - The maintainers will verify the issue and classify its severity. - Depending on impact, a patch may be released immediately or scheduled for the next update. 3. **Resolution** - Critical security fixes will be prioritized. - Non-critical issues may be deferred or declined with an explanation. --- ## Disclaimer Not all reported issues will be treated as vulnerabilities. Reports may be declined if they are deemed: - Low-risk - Out of project scope - Conflicting with intended design or architecture --- If you have any questions or concerns about this security policy, please reach out to the maintainers through the contact options above. ================================================ FILE: ct/2fauth.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: jkrgr0 # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://docs.2fauth.app/ APP="2FAuth" var_tags="${var_tags:-2fa;authenticator}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-512}" var_disk="${var_disk:-2}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/2fauth ]]; then msg_error "No ${APP} Installation Found!" exit fi setup_mariadb if check_for_gh_release "2fauth" "Bubka/2FAuth"; then $STD apt update $STD apt -y upgrade msg_info "Creating Backup" rm -rf /opt/2fauth-backup mv /opt/2fauth /opt/2fauth-backup if ! dpkg -l | grep -q 'php8.4'; then cp /etc/nginx/conf.d/2fauth.conf /etc/nginx/conf.d/2fauth.conf.bak fi msg_ok "Backup Created" if ! dpkg -l | grep -q 'php8.4'; then PHP_VERSION="8.4" PHP_FPM="YES" setup_php sed -i 's/php8\.[0-9]/php8.4/g' /etc/nginx/conf.d/2fauth.conf fi fetch_and_deploy_gh_release "2fauth" "Bubka/2FAuth" "tarball" setup_composer cp /opt/2fauth-backup/.env /opt/2fauth/.env cp -r /opt/2fauth-backup/storage /opt/2fauth/storage cd /opt/2fauth || return export COMPOSER_ALLOW_SUPERUSER=1 $STD composer install --no-dev --prefer-dist php artisan 2fauth:install chown -R www-data: /opt/2fauth chmod -R 755 /opt/2fauth $STD systemctl restart php8.4-fpm $STD systemctl restart nginx rm -rf /opt/2fauth-backup msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:80${CL}" ================================================ FILE: ct/5etools.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2025 community-scripts ORG # Author: TheRealVira # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://5e.tools/ # App Default Values APP="5etools" var_tags="wiki" var_cpu="1" var_ram="512" var_disk="13" var_os="debian" var_version="12" var_unprivileged="1" # App Output & Base Settings header_info "$APP" base_settings # Core variables color catch_errors function update_script() { header_info check_container_storage check_container_resources # Check if installation is present | -f for file, -d for folder if [[ ! -d "/opt/${APP}" ]]; then msg_error "No ${APP} Installation Found!" exit fi RELEASE=$(curl -s https://api.github.com/repos/5etools-mirror-3/5etools-src/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }') if [[ "${RELEASE}" != "$(cat /opt/${APP}_version.txt)" ]] || [[ ! -f "/opt/${APP}_version.txt" ]]; then # Crawling the new version and checking whether an update is required msg_info "Updating System" apt-get update &>/dev/null apt-get -y upgrade &>/dev/null msg_ok "Updated System" # Execute Update msg_info "Updating base 5etools" cd /opt wget -q "https://github.com/5etools-mirror-3/5etools-src/archive/refs/tags/${RELEASE}.zip" unzip -q "${RELEASE}.zip" mv "/opt/${APP}/img" "/opt/img-backup" rm -rf "/opt/${APP}" mv "${APP}-src-${RELEASE:1}" "/opt/${APP}" mv "/opt/img-backup" "/opt/${APP}/img" cd /opt/5etools $STD npm install $STD npm run build cd ~ echo "${RELEASE}" >"/opt/${APP}_version.txt" chown -R www-data: "/opt/${APP}" chmod -R 755 "/opt/${APP}" msg_ok "Updated base 5etools" # Cleaning up msg_info "Cleaning Up" rm -rf /opt/${RELEASE}.zip $STD apt-get -y autoremove $STD apt-get -y autoclean msg_ok "Cleanup Completed" else msg_ok "No update required. Base ${APP} is already at ${RELEASE}" fi IMG_RELEASE=$(curl -s https://api.github.com/repos/5etools-mirror-2/5etools-img/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }') if [[ "${IMG_RELEASE}" != "$(cat /opt/${APP}_IMG_version.txt)" ]] || [[ ! -f "/opt/${APP}_IMG_version.txt" ]]; then # Crawling the new version and checking whether an update is required msg_info "Updating System" apt-get update &>/dev/null apt-get -y upgrade &>/dev/null msg_ok "Updated System" # Execute Update msg_info "Updating 5etools images" curl -sSL "https://github.com/5etools-mirror-2/5etools-img/archive/refs/tags/${IMG_RELEASE}.zip" > "${IMG_RELEASE}.zip" unzip -q "${IMG_RELEASE}.zip" rm -rf "/opt/${APP}/img" mv "${APP}-img-${IMG_RELEASE:1}" "/opt/${APP}/img" echo "${IMG_RELEASE}" >"/opt/${APP}_IMG_version.txt" chown -R www-data: "/opt/${APP}" chmod -R 755 "/opt/${APP}" msg_ok "Updating 5etools images" # Cleaning up msg_info "Cleaning Up" rm -rf /opt/${RELEASE}.zip rm -rf ${IMG_RELEASE}.zip $STD apt-get -y autoremove $STD apt-get -y autoclean msg_ok "Cleanup Completed" else msg_ok "No update required. ${APP} images are already at ${IMG_RELEASE}" fi } start build_container description msg_ok "Completed Successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}${CL}" ================================================ FILE: ct/actualbudget.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://actualbudget.org/ APP="Actual Budget" var_tags="${var_tags:-finance}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -f ~/.actualbudget && ! -f /opt/actualbudget_version.txt ]]; then msg_error "No ${APP} Installation Found!" exit fi NODE_VERSION="22" setup_nodejs RELEASE=$(get_latest_github_release "actualbudget/actual") if [[ -f /opt/actualbudget-data/config.json ]]; then if check_for_gh_release "actualbudget" "actualbudget/actual"; then msg_info "Stopping Service" systemctl stop actualbudget msg_ok "Stopped Service" msg_info "Updating Actual Budget to ${RELEASE}" $STD npm update -g @actual-app/sync-server echo "${RELEASE}" >~/.actualbudget msg_ok "Updated Actual Budget to ${RELEASE}" msg_info "Starting Service" systemctl start actualbudget msg_ok "Started Service" msg_ok "Updated successfully!" fi else msg_warn "Old Installation Found, you need to migrate your data and recreate to a new container" msg_warn "Please follow the instructions on the Actual Budget website to migrate your data" msg_warn "https://actualbudget.org/docs/backup-restore/backup" exit fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}https://${IP}:5006${CL}" ================================================ FILE: ct/adguard.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://adguard.com/ APP="Adguard" var_tags="${var_tags:-adblock}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-512}" var_disk="${var_disk:-2}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/AdGuardHome ]]; then msg_error "No ${APP} Installation Found!" exit fi msg_error "Adguard Home can only be updated via the user interface." exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:3000${CL}" ================================================ FILE: ct/adventurelog.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: MickLesk (Canbiz) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://adventurelog.app/ APP="AdventureLog" var_tags="${var_tags:-traveling}" var_disk="${var_disk:-7}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/adventurelog ]]; then msg_error "No ${APP} Installation Found!" exit fi ensure_dependencies memcached libmemcached-tools if check_for_gh_release "adventurelog" "seanmorley15/adventurelog"; then msg_info "Stopping Services" systemctl stop adventurelog-backend systemctl stop adventurelog-frontend msg_ok "Services Stopped" msg_info "Backup Old Installation" cp -r /opt/adventurelog /opt/adventurelog-backup rm -rf /opt/adventurelog msg_ok "Backup done" fetch_and_deploy_gh_release "adventurelog" "seanmorley15/adventurelog" "tarball" PYTHON_VERSION="3.13" setup_uv msg_info "Ensuring PostgreSQL Extensions" $STD sudo -u postgres psql -d adventurelog_db -c "CREATE EXTENSION IF NOT EXISTS postgis;" msg_ok "PostgreSQL Extensions Ready" msg_info "Updating ${APP}" cp /opt/adventurelog-backup/backend/server/.env /opt/adventurelog/backend/server/.env cp -r /opt/adventurelog-backup/backend/server/media /opt/adventurelog/backend/server/media cd /opt/adventurelog/backend/server if [[ ! -x .venv/bin/python ]]; then $STD uv venv --clear .venv $STD .venv/bin/python -m ensurepip --upgrade fi $STD .venv/bin/python -m pip install --upgrade pip $STD .venv/bin/python -m pip install -r requirements.txt $STD .venv/bin/python -m pip install 'djangorestframework<3.15' $STD .venv/bin/python -m manage collectstatic --noinput $STD .venv/bin/python -m manage migrate cp /opt/adventurelog-backup/frontend/.env /opt/adventurelog/frontend/.env cd /opt/adventurelog/frontend $STD pnpm i $STD pnpm build rm -rf /opt/adventurelog-backup msg_ok "Updated ${APP}" msg_info "Starting Services" systemctl daemon-reexec systemctl start adventurelog-backend systemctl start adventurelog-frontend msg_ok "Services Started" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:3000${CL}" ================================================ FILE: ct/agentdvr.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.ispyconnect.com/ APP="AgentDVR" var_tags="${var_tags:-dvr}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-8}" var_os="${var_os:-ubuntu}" var_version="${var_version:-24.04}" var_unprivileged="${var_unprivileged:-0}" var_gpu="${var_gpu:-yes}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/agentdvr ]]; then msg_error "No ${APP} Installation Found!" exit fi RELEASE=$(curl -fsSL "https://www.ispyconnect.com/api/Agent/DownloadLocation4?platform=LinuxARM64&fromVersion=0" | tr -d '\r' | grep -Eo 'https://[^"[:space:]]+\.zip' | head -n1) if [[ "${RELEASE}" != "$(cat ~/.agentdvr 2>/dev/null)" ]] || [[ ! -f ~/.agentdvr ]]; then msg_info "Stopping service" systemctl stop AgentDVR msg_ok "Service stopped" msg_info "Updating AgentDVR" cd /opt/agentdvr/agent curl -fsSL "$RELEASE" -o $(basename "$RELEASE") $STD unzip -o Agent_Linux64*.zip chmod +x ./Agent echo $RELEASE >~/.agentdvr rm -rf Agent_Linux64*.zip msg_ok "Updated AgentDVR" msg_info "Starting service" systemctl start AgentDVR msg_ok "Service started" msg_ok "Updated successfully!" else msg_ok "No update required. ${APP} is already at ${RELEASE}" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8090${CL}" ================================================ FILE: ct/alpine-adguard.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://adguardhome.com/ APP="Alpine-AdGuard" var_tags="${var_tags:-alpine;adblock}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-256}" var_disk="${var_disk:-1}" var_os="${var_os:-alpine}" var_version="${var_version:-3.23}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info msg_info "Updating Alpine Packages" $STD apk -U upgrade msg_ok "Updated Alpine Packages" msg_info "Updating AdGuard Home" $STD /opt/AdGuardHome/AdGuardHome --update msg_ok "Updated AdGuard Home" msg_info "Restarting AdGuard Home" $STD rc-service adguardhome restart msg_ok "Restarted AdGuard Home" msg_ok "Updated successfully!" exit 0 } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:3000${CL}" ================================================ FILE: ct/alpine-bitmagnet.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Slaviša Arežina (tremor021) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/bitmagnet-io/bitmagnet APP="Alpine-bitmagnet" var_tags="${var_tags:-alpine;torrent}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-1024}" var_disk="${var_disk:-3}" var_os="${var_os:-alpine}" var_version="${var_version:-3.23}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info if [[ ! -d /opt/bitmagnet ]]; then msg_error "No ${APP} Installation Found!" exit fi RELEASE=$(curl -fsSL https://api.github.com/repos/bitmagnet-io/bitmagnet/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4) }') if [ "${RELEASE}" != "$(cat /opt/bitmagnet_version.txt)" ] || [ ! -f /opt/bitmagnet_version.txt ]; then msg_info "Backing up database" rm -f /tmp/backup.sql $STD sudo -u postgres pg_dump \ --column-inserts \ --data-only \ --on-conflict-do-nothing \ --rows-per-insert=1000 \ --table=metadata_sources \ --table=content \ --table=content_attributes \ --table=content_collections \ --table=content_collections_content \ --table=torrent_sources \ --table=torrents \ --table=torrent_files \ --table=torrent_hints \ --table=torrent_contents \ --table=torrent_tags \ --table=torrents_torrent_sources \ --table=key_values \ bitmagnet \ >/tmp/backup.sql mv /tmp/backup.sql /opt/ msg_ok "Database backed up" msg_info "Updating ${APP} from $(cat /opt/bitmagnet_version.txt) to ${RELEASE}" $STD apk -U upgrade $STD service bitmagnet stop [ -f /opt/bitmagnet/.env ] && cp /opt/bitmagnet/.env /opt/ [ -f /opt/bitmagnet/config.yml ] && cp /opt/bitmagnet/config.yml /opt/ rm -rf /opt/bitmagnet/* temp_file=$(mktemp) curl -fsSL "https://github.com/bitmagnet-io/bitmagnet/archive/refs/tags/v${RELEASE}.tar.gz" -o "$temp_file" tar zxf "$temp_file" --strip-components=1 -C /opt/bitmagnet cd /opt/bitmagnet VREL=v$RELEASE $STD go build -ldflags "-s -w -X github.com/bitmagnet-io/bitmagnet/internal/version.GitTag=$VREL" chmod +x bitmagnet [ -f "/opt/.env" ] && cp "/opt/.env" /opt/bitmagnet/ [ -f "/opt/config.yml" ] && cp "/opt/config.yml" /opt/bitmagnet/ rm -f "$temp_file" echo "${RELEASE}" >/opt/bitmagnet_version.txt $STD service bitmagnet start msg_ok "Updated successfully!" else msg_ok "No update required. ${APP} is already at ${RELEASE}" fi exit 0 } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following IP:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:3333${CL}" ================================================ FILE: ct/alpine-borgbackup-server.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Sander Koenders (sanderkoenders) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://www.borgbackup.org/ APP="Alpine-BorgBackup-Server" var_tags="${var_tags:-alpine;backup}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-1024}" var_disk="${var_disk:-20}" var_os="${var_os:-alpine}" var_version="${var_version:-3.23}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info if [[ ! -f /usr/bin/borg ]]; then msg_error "No ${APP} Installation Found!" exit fi CHOICE=$(msg_menu "BorgBackup Server Update Options" \ "1" "Update BorgBackup Server" \ "2" "Reset SSH Access" \ "3" "Enable password authentication for backup user (not recommended, use SSH key instead)" \ "4" "Disable password authentication for backup user (recommended for security, use SSH key)") case $CHOICE in 1) msg_info "Updating $APP LXC" $STD apk -U upgrade msg_ok "Updated $APP LXC successfully!" ;; 2) if [[ "${PHS_SILENT:-0}" == "1" ]]; then msg_warn "Reset SSH Public key requires interactive mode, skipping." exit fi msg_info "Setting up SSH Public Key for backup user" msg_info "Please paste your SSH public key (e.g., ssh-rsa AAAAB3... user@host): \n" read -p "Key: " SSH_PUBLIC_KEY echo if [[ -z "$SSH_PUBLIC_KEY" ]]; then msg_error "No SSH public key provided!" exit 1 fi if [[ ! "$SSH_PUBLIC_KEY" =~ ^(ssh-rsa|ssh-dss|ssh-ed25519|ecdsa-sha2-) ]]; then msg_error "Invalid SSH public key format!" exit 1 fi msg_info "Setting up SSH access" mkdir -p /home/backup/.ssh echo "$SSH_PUBLIC_KEY" >/home/backup/.ssh/authorized_keys chown -R backup:backup /home/backup/.ssh chmod 700 /home/backup/.ssh chmod 600 /home/backup/.ssh/authorized_keys msg_ok "SSH access configured for backup user" ;; 3) if [[ "${PHS_SILENT:-0}" == "1" ]]; then msg_warn "Enabling password authentication requires interactive mode, skipping." exit fi msg_info "Enabling password authentication for backup user" msg_warn "Password authentication is less secure than using SSH keys. Consider using SSH keys instead." passwd backup sed -i 's/^#*\s*PasswordAuthentication\s\+\(yes\|no\)/PasswordAuthentication yes/' /etc/ssh/sshd_config rc-service sshd restart msg_ok "Password authentication enabled for backup user" ;; 4) msg_info "Disabling password authentication for backup user" sed -i 's/^#*\s*PasswordAuthentication\s\+\(yes\|no\)/PasswordAuthentication no/' /etc/ssh/sshd_config rc-service sshd restart msg_ok "Password authentication disabled for backup user" ;; esac exit 0 } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW}Connection information:${CL}" echo -e "${TAB}${GATEWAY}${BGN}ssh backup@${IP}${CL}" echo -e "${TAB}${VERIFYPW}${YW}To set SSH key, run this script with the 'update' option and select option 2${CL}" ================================================ FILE: ct/alpine-caddy.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: cobalt (cobaltgit) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://caddyserver.com/ APP="Alpine-Caddy" var_tags="${var_tags:-webserver}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-256}" var_disk="${var_disk:-3}" var_os="${var_os:-alpine}" var_version="${var_version:-3.23}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info if [[ ! -d /etc/caddy ]]; then msg_error "No ${APP} Installation Found!" exit fi msg_info "Updating $APP LXC" $STD apk -U upgrade msg_ok "Updated $APP LXC" msg_info "Restarting Caddy" rc-service caddy restart msg_ok "Restarted Caddy" msg_ok "Updated successfully!" exit 0 } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:80${CL}" ================================================ FILE: ct/alpine-docker.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.docker.com/ APP="Alpine-Docker" var_tags="${var_tags:-docker;alpine}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-1024}" var_disk="${var_disk:-2}" var_os="${var_os:-alpine}" var_version="${var_version:-3.23}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info $STD apk -U upgrade msg_ok "Updated successfully!" exit 0 } start build_container description msg_ok "Completed successfully!\n" ================================================ FILE: ct/alpine-forgejo.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Johann3s-H (An!ma) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://forgejo.org/ APP="Alpine-Forgejo" var_tags="${var_tags:-alpine;git}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-256}" var_disk="${var_disk:-1}" var_os="${var_os:-alpine}" var_version="${var_version:-3.23}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { msg_info "Updating Alpine Packages" $STD apk -U upgrade msg_ok "Updated Alpine Packages" msg_info "Updating Forgejo" $STD apk upgrade forgejo msg_ok "Updated Forgejo" msg_info "Restarting Forgejo" $STD rc-service forgejo restart msg_ok "Restarted Forgejo" msg_ok "Updated successfully!" exit 0 } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:3000${CL}" ================================================ FILE: ct/alpine-garage.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://garagehq.deuxfleurs.fr/ APP="Alpine-Garage" var_tags="${var_tags:-alpine;object-storage}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-512}" var_disk="${var_disk:-5}" var_os="${var_os:-alpine}" var_version="${var_version:-3.23}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info if [[ ! -f /usr/local/bin/garage ]]; then msg_error "No ${APP} Installation Found!" exit fi GITEA_RELEASE=$(curl -fsSL https://api.github.com/repos/deuxfleurs-org/garage/tags | jq -r '.[0].name') if [[ "${GITEA_RELEASE}" != "$(cat ~/.garage 2>/dev/null)" ]] || [[ ! -f ~/.garage ]]; then msg_info "Stopping Service" rc-service garage stop || true msg_ok "Stopped Service" msg_info "Backing Up Data" cp /usr/local/bin/garage /usr/local/bin/garage.old 2>/dev/null || true cp /etc/garage.toml /etc/garage.toml.bak 2>/dev/null || true msg_ok "Backed Up Data" msg_info "Updating Garage" curl -fsSL "https://garagehq.deuxfleurs.fr/_releases/${GITEA_RELEASE}/aarch64-unknown-linux-musl/garage" -o /usr/local/bin/garage chmod +x /usr/local/bin/garage echo "${GITEA_RELEASE}" >~/.garage msg_ok "Updated Garage" msg_info "Starting Service" rc-service garage start || rc-service garage restart msg_ok "Started Service" msg_ok "Updated successfully!" else msg_ok "No update required. Garage is already at ${GITEA_RELEASE}" fi exit 0 } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}${CL}" ================================================ FILE: ct/alpine-gatus.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Slaviša Arežina (tremor021) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/TwiN/gatus APP="Alpine-gatus" var_tags="${var_tags:-alpine;monitoring}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-256}" var_disk="${var_disk:-3}" var_os="${var_os:-alpine}" var_version="${var_version:-3.23}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info if [[ ! -d /opt/gatus ]]; then msg_error "No ${APP} Installation Found!" exit fi RELEASE=$(curl -s https://api.github.com/repos/TwiN/gatus/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4) }') if [ "${RELEASE}" != "$(cat /opt/gatus_version.txt)" ] || [ ! -f /opt/gatus_version.txt ]; then msg_info "Updating ${APP} LXC" $STD apk -U upgrade $STD service gatus stop mv /opt/gatus/config/config.yaml /opt rm -rf /opt/gatus/* temp_file=$(mktemp) curl -fsSL "https://github.com/TwiN/gatus/archive/refs/tags/v${RELEASE}.tar.gz" -o "$temp_file" tar zxf "$temp_file" --strip-components=1 -C /opt/gatus cd /opt/gatus $STD go mod tidy CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o gatus . setcap CAP_NET_RAW+ep gatus mv /opt/config.yaml config rm -f "$temp_file" echo "${RELEASE}" >/opt/gatus_version.txt $STD service gatus start msg_ok "Updated successfully!" else msg_ok "No update required. ${APP} is already at ${RELEASE}" fi exit 0 } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following IP:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8080${CL}" ================================================ FILE: ct/alpine-gitea.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://gitea.io APP="Alpine-Gitea" var_tags="${var_tags:-alpine;git}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-256}" var_disk="${var_disk:-1}" var_os="${var_os:-alpine}" var_version="${var_version:-3.23}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info msg_info "Updating Alpine Packages" $STD apk -U upgrade msg_ok "Updated Alpine Packages" msg_info "Updating Gitea" apk upgrade gitea msg_ok "Updated Gitea" msg_info "Restarting Gitea" rc-service gitea restart msg_ok "Restarted Gitea" msg_ok "Updated successfully!" exit 0 } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:3000${CL}" ================================================ FILE: ct/alpine-grafana.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://grafana.com/ APP="Alpine-Grafana" var_tags="${var_tags:-alpine;monitoring}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-256}" var_disk="${var_disk:-2}" var_os="${var_os:-alpine}" var_version="${var_version:-3.23}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { LXCIP=$(ip a s dev eth0 | awk '/inet / {print $2}' | cut -d/ -f1) CHOICE=$(msg_menu "Grafana Update Options" \ "1" "Check for Grafana Updates" \ "2" "Allow 0.0.0.0 for listening" \ "3" "Allow only ${LXCIP} for listening") case $CHOICE in 1) $STD apk -U upgrade msg_ok "Updated successfully!" exit ;; 2) sed -i -e "s/cfg:server.http_addr=.*/cfg:server.http_addr=0.0.0.0/g" /etc/conf.d/grafana service grafana restart msg_ok "Allowed listening on all interfaces!" exit ;; 3) sed -i -e "s/cfg:server.http_addr=.*/cfg:server.http_addr=$LXCIP/g" /etc/conf.d/grafana service grafana restart msg_ok "Allowed listening only on ${LXCIP}!" exit ;; esac exit 0 } start build_container description msg_ok "Completed successfully!\n" echo -e "${APP} should be reachable by going to the following URL. ${BL}http://${IP}:3000${CL} \n" ================================================ FILE: ct/alpine-ironclaw.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/nearai/ironclaw APP="Alpine-IronClaw" var_tags="${var_tags:-ai;agent;alpine}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-1024}" var_disk="${var_disk:-8}" var_os="${var_os:-alpine}" var_version="${var_version:-3.23}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info if [[ ! -f /usr/local/bin/ironclaw ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "ironclaw-bin" "nearai/ironclaw"; then msg_info "Stopping Service" rc-service ironclaw stop 2>/dev/null || true msg_ok "Stopped Service" msg_info "Backing up Configuration" cp /root/.ironclaw/.env /root/ironclaw.env.bak msg_ok "Backed up Configuration" fetch_and_deploy_gh_release "ironclaw-bin" "nearai/ironclaw" "prebuild" "latest" "/usr/local/bin" \ "ironclaw-$(uname -m)-unknown-linux-musl.tar.gz" chmod +x /usr/local/bin/ironclaw msg_info "Restoring Configuration" cp /root/ironclaw.env.bak /root/.ironclaw/.env rm -f /root/ironclaw.env.bak msg_ok "Restored Configuration" msg_info "Starting Service" rc-service ironclaw start msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed Successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Complete setup by running:${CL}" echo -e "${TAB}${BGN}ironclaw onboard${CL}" echo -e "${INFO}${YW} Then start the service:${CL}" echo -e "${TAB}${BGN}rc-service ironclaw start${CL}" echo -e "${INFO}${YW} Access the Web UI at:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:3000${CL}" echo -e "${INFO}${YW} Auth token and database credentials:${CL}" echo -e "${TAB}${BGN}cat /root/.ironclaw/.env${CL}" ================================================ FILE: ct/alpine-it-tools.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: nicedevil007 (NiceDevil) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://it-tools.tech/ APP="Alpine-IT-Tools" var_tags="${var_tags:-alpine;development}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-256}" var_disk="${var_disk:-1}" var_os="${var_os:-alpine}" var_version="${var_version:-3.23}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info if [ ! -d /usr/share/nginx/html ]; then msg_error "No ${APP} Installation Found!" exit fi RELEASE=$(curl -fsSL https://api.github.com/repos/sharevb/it-tools/releases/latest | grep '"tag_name":' | cut -d '"' -f4) if [ "${RELEASE}" != "$(cat /opt/${APP}_version.txt)" ] || [ ! -f /opt/${APP}_version.txt ]; then msg_info "Updating ${APP} LXC" curl -fsSL "https://github.com/sharevb/it-tools/releases/download/${RELEASE}/it-tools-${RELEASE#v}.zip" -o it-tools.zip mkdir -p /usr/share/nginx/html rm -rf /usr/share/nginx/html/* $STD unzip it-tools.zip -d /tmp cp -r /tmp/dist/* /usr/share/nginx/html rm -rf /tmp/dist rm -f it-tools.zip msg_ok "Updated successfully!" else msg_ok "No update required. ${APP} is already at ${RELEASE}" fi exit 0 } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following IP:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}${CL}" ================================================ FILE: ct/alpine-komodo.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://komo.do APP="Alpine-Komodo" var_tags="${var_tags:-docker;alpine}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-1024}" var_disk="${var_disk:-10}" var_os="${var_os:-alpine}" var_version="${var_version:-3.23}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors ADDON_SCRIPT="https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/tools/addon/komodo.sh" function update_script() { if [[ ! -d /opt/komodo ]]; then msg_error "No ${APP} Installation Found!" exit fi msg_warn "⚠️ ${APP} has been migrated to an addon script." echo "" msg_info "This is a one-time migration. After this, you can update ${APP} anytime with:" echo -e "${TAB}${TAB}${GN}update_komodo${CL} or ${GN}bash <(curl -fsSL ${ADDON_SCRIPT})${CL}" echo "" read -r -p "${TAB}Migrate update function now? [y/N]: " CONFIRM if [[ ! "${CONFIRM,,}" =~ ^(y|yes)$ ]]; then msg_warn "Migration skipped. The old update will continue to work for now." msg_warn "⚠️ Komodo v2 uses :2 image tags. The :latest tag is deprecated and will not receive v2 updates." msg_warn "Please migrate to the addon script to receive Komodo v2." msg_info "Updating ${APP} (legacy)" COMPOSE_FILE=$(find /opt/komodo -maxdepth 1 -type f -name '*.compose.yaml' ! -name 'compose.env' | head -n1) if [[ -z "$COMPOSE_FILE" ]]; then msg_error "No valid compose file found in /opt/komodo!" exit 252 fi $STD docker compose -p komodo -f "$COMPOSE_FILE" --env-file /opt/komodo/compose.env pull $STD docker compose -p komodo -f "$COMPOSE_FILE" --env-file /opt/komodo/compose.env up -d msg_ok "Updated ${APP}" exit fi msg_info "Migrating update function" TMP_UPDATE=$(mktemp) cat <<'MIGRATION_EOF' >"$TMP_UPDATE" bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/tools/addon/komodo.sh)" MIGRATION_EOF mv "$TMP_UPDATE" /usr/bin/update chmod +x /usr/bin/update ln -sf /usr/bin/update /usr/bin/update_komodo 2>/dev/null || true msg_ok "Migration complete" msg_info "Running addon update" type=update bash <(curl -fsSL "${ADDON_SCRIPT}") exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:9120${CL}" ================================================ FILE: ct/alpine-loki.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: hoholms # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/grafana/loki APP="Alpine-Loki" var_tags="${var_tags:-alpine;monitoring}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-256}" var_disk="${var_disk:-1}" var_os="${var_os:-alpine}" var_version="${var_version:-3.23}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { LXCIP=$(ip a s dev eth0 | awk '/inet / {print $2}' | cut -d/ -f1) CHOICE=$(msg_menu "Loki Update Options" \ "1" "Check for Loki Updates" \ "2" "Allow 0.0.0.0 for listening" \ "3" "Allow only ${LXCIP} for listening") case $CHOICE in 1) $STD apk -U upgrade msg_ok "Updated successfully!" exit ;; 2) sed -i -e "s/cfg:server.http_addr=.*/cfg:server.http_addr=0.0.0.0/g" /etc/conf.d/loki service loki restart msg_ok "Allowed listening on all interfaces!" exit ;; 3) sed -i -e "s/cfg:server.http_addr=.*/cfg:server.http_addr=$LXCIP/g" /etc/conf.d/loki service loki restart msg_ok "Allowed listening only on ${LXCIP}!" exit ;; esac exit 0 } start build_container description msg_ok "Completed Successfully!\n" echo -e "${APP} should be reachable by going to the following URL. ${BL}http://${IP}:3100${CL} \n" echo -e "Promtail should be reachable by going to the following URL. ${BL}http://${IP}:9080${CL} \n" ================================================ FILE: ct/alpine-mariadb.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://mariadb.org APP="Alpine-MariaDB" var_tags="${var_tags:-alpine;database}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-256}" var_disk="${var_disk:-1}" var_os="${var_os:-alpine}" var_version="${var_version:-3.23}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { msg_info "Updating Alpine Packages" $STD apk -U upgrade msg_ok "Updated Alpine Packages" msg_info "Updating MariaDB" $STD apk upgrade mariadb mariadb-client msg_ok "Updated MariaDB" msg_info "Restarting MariaDB" $STD rc-service mariadb restart msg_ok "Restarted MariaDB" msg_ok "Updated successfully!" exit 0 } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following IP:${CL}" echo -e "${TAB}${GATEWAY}${BGN}${IP}:3306${CL}" ================================================ FILE: ct/alpine-nextcloud.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://nextcloud.com/ APP="Alpine-Nextcloud" var_tags="${var_tags:-alpine;cloud}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-1024}" var_disk="${var_disk:-2}" var_os="${var_os:-alpine}" var_version="${var_version:-3.23}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { if [[ ! -d /usr/share/webapps/nextcloud ]]; then msg_error "No ${APP} Installation Found!" exit fi CHOICE=$(msg_menu "Nextcloud Options" \ "1" "Update Alpine Packages" \ "2" "Nextcloud Login Credentials" \ "3" "Renew Self-signed Certificate") case $CHOICE in 1) msg_info "Updating Alpine Packages" $STD apk -U upgrade msg_ok "Updated Alpine Packages" msg_ok "Updated successfully!" exit ;; 2) cat nextcloud.creds exit ;; 3) openssl req -x509 -nodes -days 365 -newkey rsa:4096 -keyout /etc/ssl/private/nextcloud-selfsigned.key -out /etc/ssl/certs/nextcloud-selfsigned.crt -subj "/C=US/O=Nextcloud/OU=Domain Control Validated/CN=nextcloud.local" >/dev/null 2>&1 rc-service nginx restart msg_ok "Renewed self-signed certificate" exit ;; esac } start build_container description msg_ok "Completed successfully!\n" echo -e "${APP} should be reachable by going to the following URL. ${BL}https://${IP}${CL} \n" ================================================ FILE: ct/alpine-node-red.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://nodered.org APP="Alpine-Node-RED" var_tags="${var_tags:-alpine;automation}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-256}" var_disk="${var_disk:-1}" var_os="${var_os:-alpine}" var_version="${var_version:-3.23}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { msg_info "Updating Alpine Packages" $STD apk -U upgrade msg_ok "Updated Alpine Packages" msg_info "Updating Node.js and npm" $STD apk upgrade nodejs npm msg_ok "Updated Node.js and npm" msg_info "Updating Node-RED" $STD npm install -g --unsafe-perm node-red msg_ok "Updated Node-RED" msg_info "Restarting Node-RED" $STD rc-service nodered restart msg_ok "Restarted Node-RED" msg_ok "Updated successfully!" exit 0 } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:1880${CL}" ================================================ FILE: ct/alpine-ntfy.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: cobalt (cobaltgit) # License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE # Source: https://ntfy.sh/ APP="Alpine-ntfy" var_tags="${var_tags:-notification}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-256}" var_disk="${var_disk:-2}" var_os="${var_os:-alpine}" var_version="${var_version:-3.23}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info if [[ ! -d /etc/ntfy ]]; then msg_error "No ${APP} Installation Found!" exit fi msg_info "Updating ntfy LXC" $STD apk -U upgrade setcap 'cap_net_bind_service=+ep' /usr/bin/ntfy msg_ok "Updated ntfy LXC" msg_info "Restarting ntfy" rc-service ntfy restart msg_ok "Restarted ntfy" msg_ok "Updated successfully!" exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}${CL}" ================================================ FILE: ct/alpine-postgresql.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://postgresql.org/ APP="Alpine-PostgreSQL" var_tags="${var_tags:-alpine;database}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-256}" var_disk="${var_disk:-1}" var_os="${var_os:-alpine}" var_version="${var_version:-3.23}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { msg_info "Updating Alpine Packages" $STD apk -U upgrade msg_ok "Updated Alpine Packages" msg_info "Updating PostgreSQL" $STD apk upgrade postgresql postgresql-contrib msg_ok "Updated PostgreSQL" msg_info "Restarting PostgreSQL" $STD rc-service postgresql restart msg_ok "Restarted PostgreSQL" msg_ok "Updated successfully!" exit 0 } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following IP:${CL}" echo -e "${TAB}${GATEWAY}${BGN}${IP}:5432${CL}" ================================================ FILE: ct/alpine-prometheus.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://prometheus.io/ APP="Alpine-Prometheus" var_tags="${var_tags:-alpine;monitoring}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-256}" var_disk="${var_disk:-1}" var_os="${var_os:-alpine}" var_version="${var_version:-3.23}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { msg_info "Updating Alpine Packages" $STD apk -U upgrade msg_ok "Updated Alpine Packages" msg_info "Updating Prometheus" $STD apk upgrade prometheus msg_ok "Updated Prometheus" msg_info "Restarting Prometheus" $STD rc-service prometheus restart msg_ok "Restarted Prometheus" msg_ok "Updated successfully!" exit 0 } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:9090${CL}" ================================================ FILE: ct/alpine-rclone.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Slaviša Arežina (tremor021) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/rclone/rclone APP="Alpine-rclone" var_tags="${var_tags:-alpine;backup}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-256}" var_disk="${var_disk:-1}" var_os="${var_os:-alpine}" var_version="${var_version:-3.23}" var_unprivileged="${var_unprivileged:-1}" var_fuse="${var_fuse:-yes}" header_info "$APP" variables color catch_errors function update_script() { header_info if [ ! -d /opt/rclone ]; then msg_error "No ${APP} Installation Found!" exit fi RELEASE=$(curl -s https://api.github.com/repos/rclone/rclone/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4) }') if [ "${RELEASE}" != "$(cat /opt/rclone_version.txt)" ] || [ ! -f /opt/rclone_version.txt ]; then msg_info "Updating ${APP} LXC" temp_file=$(mktemp) curl -fsSL "https://github.com/rclone/rclone/releases/download/v${RELEASE}/rclone-v${RELEASE}-linux-arm64.zip" -o "$temp_file" $STD unzip -o "$temp_file" '*/**' -d /opt/rclone rm -f "$temp_file" echo "${RELEASE}" >/opt/rclone_version.txt msg_ok "Updated successfully!" else msg_ok "No update required. ${APP} is already at ${RELEASE}" fi exit 0 } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following IP:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:3000${CL}" ================================================ FILE: ct/alpine-redis.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://redis.io/ APP="Alpine-Redis" var_tags="${var_tags:-alpine;database}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-256}" var_disk="${var_disk:-1}" var_os="${var_os:-alpine}" var_version="${var_version:-3.23}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { LXCIP=$(ip a s dev eth0 | awk '/inet / {print $2}' | cut -d/ -f1) CHOICE=$(msg_menu "Redis Management" \ "1" "Update Redis" \ "2" "Allow 0.0.0.0 for listening" \ "3" "Allow only ${LXCIP} for listening") case $CHOICE in 1) msg_info "Updating Redis" apk update && apk upgrade redis rc-service redis restart msg_ok "Updated successfully!" exit ;; 2) msg_info "Setting Redis to listen on all interfaces" sed -i 's/^bind .*/bind 0.0.0.0/' /etc/redis.conf rc-service redis restart msg_ok "Redis now listens on all interfaces!" exit ;; 3) msg_info "Setting Redis to listen only on ${LXCIP}" sed -i "s/^bind .*/bind ${LXCIP}/" /etc/redis.conf rc-service redis restart msg_ok "Redis now listens only on ${LXCIP}!" exit ;; esac } start build_container description msg_ok "Completed successfully!\n" echo -e "${APP} should be reachable on port 6379. ${BL}redis-cli -h ${IP} -p 6379${CL} \n" ================================================ FILE: ct/alpine-redlib.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: andrej-kocijan (Andrej Kocijan) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/redlib-org/redlib APP="Alpine-Redlib" var_tags="${var_tags:-alpine;frontend}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-512}" var_disk="${var_disk:-1}" var_os="${var_os:-alpine}" var_version="${var_version:-3.23}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info if [[ ! -d /opt/redlib ]]; then msg_error "No ${APP} Installation Found!" exit fi msg_info "Updating Alpine Packages" $STD apk -U upgrade msg_ok "Updated Alpine Packages" msg_info "Stopping Service" $STD rc-service redlib stop msg_ok "Stopped Service" fetch_and_deploy_gh_release "redlib" "redlib-org/redlib" "prebuild" "latest" "/opt/redlib" "redlib-aarch64-unknown-linux-musl.tar.gz" msg_info "Starting Service" $STD rc-service redlib start msg_ok "Started Service" msg_ok "Updated successfully!" exit 0 } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:5252${CL}" ================================================ FILE: ct/alpine-rustdeskserver.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Slaviša Arežina (tremor021) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/rustdesk/rustdesk-server APP="Alpine-RustDeskServer" var_tags="${var_tags:-alpine;monitoring}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-512}" var_disk="${var_disk:-3}" var_os="${var_os:-alpine}" var_version="${var_version:-3.23}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info if [[ ! -d /opt/rustdesk-server ]]; then msg_error "No ${APP} Installation Found!" exit fi APIRELEASE=$(curl -s https://api.github.com/repos/lejianwen/rustdesk-api/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4) }') RELEASE=$(curl -s https://api.github.com/repos/lejianwen/rustdesk-server/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }') if [ "${RELEASE}" != "$(cat ~/.rustdesk-server 2>/dev/null)" ] || [ ! -f ~/.rustdesk-server ]; then msg_info "Updating RustDesk Server to v${RELEASE}" $STD apk -U upgrade $STD service rustdesk-server-hbbs stop $STD service rustdesk-server-hbbr stop temp_file1=$(mktemp) curl -fsSL "https://github.com/rustdesk/rustdesk-server/releases/download/${RELEASE}/rustdesk-server-linux-arm64.zip" -o "$temp_file1" $STD unzip "$temp_file1" cp -r arm64/* /opt/rustdesk-server/ echo "${RELEASE}" >~/.rustdesk-server $STD service rustdesk-server-hbbs start $STD service rustdesk-server-hbbr start rm -rf arm64 rm -f $temp_file1 msg_ok "Updated RustDesk Server" else msg_ok "No update required. ${APP} is already at v${RELEASE}" fi if [ "${APIRELEASE}" != "$(cat ~/.rustdesk-api)" ] || [ ! -f ~/.rustdesk-api ]; then msg_info "Updating RustDesk API to v${APIRELEASE}" $STD service rustdesk-api stop temp_file2=$(mktemp) curl -fsSL "https://github.com/lejianwen/rustdesk-api/releases/download/v${APIRELEASE}/linux-arm64.tar.gz" -o "$temp_file2" $STD tar zxvf "$temp_file2" cp -r release/* /opt/rustdesk-api echo "${APIRELEASE}" >~/.rustdesk-api $STD service rustdesk-api start rm -rf release rm -f "$temp_file2" msg_ok "Updated RustDesk API" else msg_ok "No update required. RustDesk API is already at v${APIRELEASE}" fi msg_ok "Updated successfully!" exit 0 } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following IP:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:21114${CL}" ================================================ FILE: ct/alpine-rustypaste.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/orhun/rustypaste APP="Alpine-RustyPaste" var_tags="${var_tags:-alpine;pastebin;storage}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-256}" var_disk="${var_disk:-4}" var_os="${var_os:-alpine}" var_version="${var_version:-3.23}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info if ! apk info -e rustypaste >/dev/null 2>&1; then msg_error "No ${APP} Installation Found!" exit fi msg_info "Updating RustyPaste" $STD apk update $STD apk upgrade rustypaste --repository=https://dl-cdn.alpinelinux.org/alpine/edge/community msg_ok "Updated RustyPaste" msg_info "Restarting Services" $STD rc-service rustypaste restart msg_ok "Restarted Services" msg_ok "Updated successfully!" exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8000${CL}" ================================================ FILE: ct/alpine-syncthing.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://syncthing.net/ APP="Alpine-Syncthing" var_tags="${var_tags:-alpine;networking}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-256}" var_disk="${var_disk:-1}" var_os="${var_os:-alpine}" var_version="${var_version:-3.23}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { msg_info "Updating Alpine Packages" $STD apk -U upgrade msg_ok "Updated Alpine Packages" msg_info "Updating Syncthing" $STD apk upgrade syncthing msg_ok "Updated Syncthing" msg_info "Restarting Syncthing" $STD rc-service syncthing restart msg_ok "Restarted Syncthing" msg_ok "Updated successfully!" exit 0 } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8384${CL}" ================================================ FILE: ct/alpine-teamspeak-server.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: tremor021 (Slaviša Arežina) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://teamspeak.com/en/ APP="Alpine-TeamSpeak-Server" var_tags="${var_tags:-alpine;communication}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-256}" var_disk="${var_disk:-2}" var_os="${var_os:-alpine}" var_version="${var_version:-3.23}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info if [[ ! -d /opt/teamspeak-server ]]; then msg_error "No ${APP} Installation Found!" exit fi RELEASE=$(curl -fsSL https://teamspeak.com/en/downloads/#server | sed -n 's/.*teamspeak3-server_linux_amd64-\([0-9.]*[0-9]\).*/\1/p' | awk 'NR==1') if [ "${RELEASE}" != "$(cat ~/.teamspeak-server)" ] || [ ! -f ~/.teamspeak-server ]; then msg_info "Updating ${APP} LXC" $STD apk -U upgrade $STD service teamspeak stop curl -fsSL "https://files.teamspeak-services.com/releases/server/${RELEASE}/teamspeak3-server_linux_arm64-${RELEASE}.tar.bz2" -o ts3server.tar.bz2 tar -xf ./ts3server.tar.bz2 cp -ru teamspeak3-server_linux_arm64/* /opt/teamspeak-server/ rm -f ~/ts3server.tar.bz* rm -rf teamspeak3-server_linux_arm64 echo "${RELEASE}" >~/.teamspeak-server $STD service teamspeak start msg_ok "Updated successfully!" else msg_ok "No update required. ${APP} is already at ${RELEASE}" fi exit 0 } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following IP:${CL}" echo -e "${TAB}${GATEWAY}${BGN}${IP}:9987${CL}" ================================================ FILE: ct/alpine-tinyauth.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Slaviša Arežina (tremor021) | Co-Author: Stavros (steveiliop56) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/steveiliop56/tinyauth APP="Alpine-Tinyauth" var_tags="${var_tags:-alpine;auth}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-256}" var_disk="${var_disk:-2}" var_os="${var_os:-alpine}" var_version="${var_version:-3.23}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { if [[ ! -d /opt/tinyauth ]]; then msg_error "No ${APP} Installation Found!" exit fi msg_info "Updating packages" $STD apk -U upgrade msg_ok "Updated packages" RELEASE=$(curl -s https://api.github.com/repos/steveiliop56/tinyauth/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4) }') if [ "${RELEASE}" != "$(cat ~/.tinyauth 2>/dev/null)" ] || [ ! -f ~/.tinyauth ]; then msg_info "Stopping Service" $STD service tinyauth stop msg_ok "Service Stopped" if [[ -f /opt/tinyauth/.env ]] && ! grep -q "^TINYAUTH_" /opt/tinyauth/.env; then msg_info "Migrating .env to v5 format" sed -i \ -e 's/^DATABASE_PATH=/TINYAUTH_DATABASE_PATH=/' \ -e 's/^USERS=/TINYAUTH_AUTH_USERS=/' \ -e "s/^USERS='/TINYAUTH_AUTH_USERS='/" \ -e 's/^APP_URL=/TINYAUTH_APPURL=/' \ -e 's/^SECRET=/TINYAUTH_AUTH_SECRET=/' \ -e 's/^PORT=/TINYAUTH_SERVER_PORT=/' \ -e 's/^ADDRESS=/TINYAUTH_SERVER_ADDRESS=/' \ /opt/tinyauth/.env msg_ok "Migrated .env to v5 format" fi msg_info "Updating Tinyauth" rm -f /opt/tinyauth/tinyauth curl -fsSL "https://github.com/steveiliop56/tinyauth/releases/download/v${RELEASE}/tinyauth-arm64" -o /opt/tinyauth/tinyauth chmod +x /opt/tinyauth/tinyauth echo "${RELEASE}" >~/.tinyauth msg_ok "Updated Tinyauth" msg_info "Restarting Tinyauth" $STD service tinyauth start msg_ok "Restarted Tinyauth" msg_ok "Updated successfully!" else msg_ok "No update required. ${APP} is already at ${RELEASE}" fi exit 0 } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:3000${CL}" ================================================ FILE: ct/alpine-traefik.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://alpinelinux.org/ APP="Alpine-Traefik" var_tags="${var_tags:-os;alpine}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-512}" var_disk="${var_disk:-1}" var_os="${var_os:-alpine}" var_version="${var_version:-3.23}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info msg_info "Updating Alpine Packages" $STD apk -U upgrade msg_ok "Updated Alpine Packages" msg_info "Updating traefik from edge" $STD apk add traefik --repository=https://dl-cdn.alpinelinux.org/alpine/edge/community msg_ok "Updated traefik" msg_ok "Updated successfully!" exit 0 } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} WebUI Access (if configured) - using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8080/dashboard${CL}" ================================================ FILE: ct/alpine-transmission.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Slaviša Arežina (tremor021) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://transmissionbt.com/ APP="Alpine-Transmission" var_tags="${var_tags:-alpine;torrent}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-256}" var_disk="${var_disk:-1}" var_os="${var_os:-alpine}" var_version="${var_version:-3.23}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { msg_info "Updating Alpine Packages" $STD apk -U upgrade msg_ok "Updated Alpine Packages" msg_info "Updating Transmission" $STD apk upgrade transmission-daemon msg_ok "Updated Transmission" msg_info "Restarting Transmission" $STD rc-service transmission-daemon restart msg_ok "Restarted Transmission" msg_ok "Updated successfully!" exit 0 } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:9091${CL}" ================================================ FILE: ct/alpine-valkey.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: pshankinclarke (lazarillo) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://valkey.io/ APP="Alpine-Valkey" var_tags="${var_tags:-alpine;database}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-256}" var_disk="${var_disk:-1}" var_os="${var_os:-alpine}" var_version="${var_version:-3.23}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { LXCIP=$(ip a s dev eth0 | awk '/inet / {print $2}' | cut -d/ -f1) CHOICE=$(msg_menu "Valkey Management" \ "1" "Update Valkey" \ "2" "Allow 0.0.0.0 for listening" \ "3" "Allow only ${LXCIP} for listening") case $CHOICE in 1) msg_info "Updating Valkey" apk update && apk upgrade valkey rc-service valkey restart msg_ok "Updated Valkey" msg_ok "Updated successfully!" exit ;; 2) msg_info "Setting Valkey to listen on all interfaces" sed -i 's/^bind .*/bind 0.0.0.0/' /etc/valkey/valkey.conf rc-service valkey restart msg_ok "Valkey now listens on all interfaces!" exit ;; 3) msg_info "Setting Valkey to listen only on ${LXCIP}" sed -i "s/^bind .*/bind ${LXCIP}/" /etc/valkey/valkey.conf rc-service valkey restart msg_ok "Valkey now listens only on ${LXCIP}!" exit ;; esac } start build_container description msg_ok "Completed successfully!\n" echo -e "${APP} should be reachable on port 6379. ${BL}valkey-cli -h ${IP} -p 6379${CL} \n" ================================================ FILE: ct/alpine-vaultwarden.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/dani-garcia/vaultwarden APP="Alpine-Vaultwarden" var_tags="${var_tags:-alpine;vault}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-256}" var_disk="${var_disk:-1}" var_os="${var_os:-alpine}" var_version="${var_version:-3.23}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { CHOICE=$(msg_menu "Vaultwarden Update Options" \ "1" "Update Vaultwarden" \ "2" "Reset ADMIN_TOKEN") case $CHOICE in 1) $STD apk -U upgrade rc-service vaultwarden restart -q msg_ok "Updated successfully!" exit ;; 2) if [[ "${PHS_SILENT:-0}" == "1" ]]; then msg_warn "Reset ADMIN_TOKEN requires interactive mode, skipping." exit fi read -r -s -p "Setup your ADMIN_TOKEN (make it strong): " NEWTOKEN echo "" if [[ -n "$NEWTOKEN" ]]; then if ! command -v argon2 >/dev/null 2>&1; then apk add argon2 &>/dev/null; fi TOKEN=$(echo -n "${NEWTOKEN}" | argon2 "$(openssl rand -base64 32)" -e -id -k 19456 -t 2 -p 1) if [[ ! -f /var/lib/vaultwarden/config.json ]]; then sed -i "s|export ADMIN_TOKEN=.*|export ADMIN_TOKEN='${TOKEN}'|" /etc/conf.d/vaultwarden else sed -i "s|\"admin_token\": .*|\"admin_token\": \"${TOKEN}\",|" /var/lib/vaultwarden/config.json fi rc-service vaultwarden restart -q msg_ok "Admin token updated" fi exit ;; esac } start build_container description msg_ok "Completed successfully!\n" echo -e "${APP} should be reachable by going to the following URL. ${BL}https://${IP}:8000${CL} \n" ================================================ FILE: ct/alpine-wakapi.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Slaviša Arežina (tremor021) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://wakapi.dev/ | https://github.com/muety/wakapi APP="Alpine-Wakapi" var_tags="${var_tags:-code;time-tracking}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-512}" var_disk="${var_disk:-4}" var_os="${var_os:-alpine}" var_version="${var_version:-3.23}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info if [[ ! -d /opt/wakapi ]]; then msg_error "No ${APP} Installation Found!" exit fi RELEASE=$(curl -s https://api.github.com/repos/muety/wakapi/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }') if [ "${RELEASE}" != "$(cat ~/.wakapi 2>/dev/null)" ] || [ ! -f ~/.wakapi ]; then msg_info "Stopping Wakapi Service" $STD rc-service wakapi stop msg_ok "Stopped Wakapi Service" msg_info "Updating Wakapi LXC" $STD apk -U upgrade msg_ok "Updated Wakapi LXC" msg_info "Creating backup" mkdir -p /opt/wakapi-backup cp /opt/wakapi/config.yml /opt/wakapi/wakapi_db.db /opt/wakapi-backup/ msg_ok "Created backup" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "wakapi" "muety/wakapi" "prebuild" "latest" "/opt/wakapi" "wakapi_linux_amd64.zip" msg_info "Configuring Wakapi" cd /opt/wakapi cp /opt/wakapi-backup/config.yml /opt/wakapi/ cp /opt/wakapi-backup/wakapi_db.db /opt/wakapi/ rm -rf /opt/wakapi-backup msg_ok "Configured Wakapi" msg_info "Starting Service" $STD rc-service wakapi start msg_ok "Started Service" msg_ok "Updated successfully" else msg_ok "No update required. ${APP} is already at ${RELEASE}" fi exit 0 } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:3000${CL}" ================================================ FILE: ct/alpine-wireguard.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.wireguard.com/ APP="Alpine-Wireguard" var_tags="${var_tags:-alpine;vpn}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-256}" var_disk="${var_disk:-1}" var_os="${var_os:-alpine}" var_version="${var_version:-3.23}" var_unprivileged="${var_unprivileged:-1}" var_tun="${var_tun:-1}" header_info "$APP" variables color catch_errors function update_script() { msg_info "Updating Alpine Packages" $STD apk -U upgrade msg_ok "Updated Alpine Packages" msg_info "update wireguard-tools" $STD apk add --no-cache --upgrade wireguard-tools msg_ok "wireguard-tools updated" if [[ -d /etc/wgdashboard/src ]]; then msg_info "update WGDashboard" cd /etc/wgdashboard/src echo "y" | ./wgd.sh update >/dev/null 2>&1 $STD ./wgd.sh start msg_ok "WGDashboard updated" fi msg_ok "Updated successfully!" exit 0 } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} WGDashboard Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:10086${CL}" ================================================ FILE: ct/alpine-zigbee2mqtt.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.zigbee2mqtt.io/ APP="Alpine-Zigbee2MQTT" var_tags="${var_tags:-alpine;zigbee;mqtt;smarthome}" var_disk="${var_disk:-1}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-256}" var_os="${var_os:-alpine}" var_version="${var_version:-3.23}" var_unprivileged="${var_unprivileged:-0}" header_info "$APP" variables color catch_errors function update_script() { header_info $STD apk -U upgrade msg_ok "Updated successfully!" exit 0 } start build_container description msg_ok "Completed successfully!\n" ================================================ FILE: ct/alpine.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://alpinelinux.org/ APP="Alpine" var_tags="${var_tags:-os;alpine}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-512}" var_disk="${var_disk:-1}" var_os="${var_os:-alpine}" var_version="${var_version:-3.23}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info $STD apk -U upgrade msg_ok "Updated successfully!" exit 0 } start build_container description msg_ok "Completed successfully!\n" ================================================ FILE: ct/ampache.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (Canbiz) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/ampache/ampache APP="Ampache" var_tags="${var_tags:-music}" var_disk="${var_disk:-5}" var_cpu="${var_cpu:-4}" var_ram="${var_ram:-2048}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/ampache ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "Ampache" "ampache/ampache"; then msg_info "Stopping Service" systemctl stop apache2 msg_ok "Stopped Service" msg_info "Creating Backup" cp /opt/ampache/config/ampache.cfg.php /tmp/ampache.cfg.php.backup cp /opt/ampache/public/rest/.htaccess /tmp/ampache_rest.htaccess.backup cp /opt/ampache/public/play/.htaccess /tmp/ampache_play.htaccess.backup rm -rf /opt/ampache_backup mv /opt/ampache /opt/ampache_backup msg_ok "Created Backup" fetch_and_deploy_gh_release "Ampache" "ampache/ampache" "prebuild" "latest" "/opt/ampache" "ampache-*_all_php8.4.zip" msg_info "Restoring Backup" cp /tmp/ampache.cfg.php.backup /opt/ampache/config/ampache.cfg.php cp /tmp/ampache_rest.htaccess.backup /opt/ampache/public/rest/.htaccess cp /tmp/ampache_play.htaccess.backup /opt/ampache/public/play/.htaccess chmod 664 /opt/ampache/public/rest/.htaccess /opt/ampache/public/play/.htaccess chown -R www-data:www-data /opt/ampache rm -f /tmp/ampache*.backup msg_ok "Restored Configuration" msg_info "Starting Service" systemctl start apache2 msg_ok "Started Service" msg_ok "Updated successfully!" msg_custom "⚠️" "${YW}" "Complete database update by visiting: http://${LOCAL_IP}/update.php" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}/install.php${CL}" ================================================ FILE: ct/anchor.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/ZhFahim/anchor APP="Anchor" var_tags="${var_tags:-notes;productivity;sync}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-10}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -f ~/.anchor ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "anchor" "ZhFahim/anchor"; then msg_info "Stopping Services" systemctl stop anchor-web anchor-server msg_ok "Stopped Services" msg_info "Backing up Configuration" cp /opt/anchor/.env /opt/anchor.env.bak msg_ok "Backed up Configuration" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "anchor" "ZhFahim/anchor" "tarball" msg_info "Building Server" cd /opt/anchor/server $STD pnpm install --frozen-lockfile $STD pnpm prisma generate $STD pnpm build [[ -d src/generated ]] && mkdir -p dist/src && cp -R src/generated dist/src/ msg_ok "Built Server" msg_info "Building Web Interface" cd /opt/anchor/web $STD pnpm install --frozen-lockfile SERVER_URL=http://127.0.0.1:3001 $STD pnpm build cp -r .next/static .next/standalone/.next/static cp -r public .next/standalone/public msg_ok "Built Web Interface" cp /opt/anchor.env.bak /opt/anchor/.env rm -f /opt/anchor.env.bak msg_info "Running Database Migrations" cd /opt/anchor/server set -a && source /opt/anchor/.env && set +a $STD pnpm prisma migrate deploy msg_ok "Ran Database Migrations" msg_info "Starting Services" systemctl start anchor-server anchor-web msg_ok "Started Services" msg_ok "Updated ${APP}" fi exit } start build_container description msg_ok "Completed Successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:3000${CL}" ================================================ FILE: ct/anytype-server.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://anytype.io APP="Anytype-Server" var_tags="${var_tags:-notes;productivity;sync}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-4096}" var_disk="${var_disk:-16}" var_os="${var_os:-ubuntu}" var_version="${var_version:-24.04}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -f /opt/anytype/any-sync-bundle ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "anytype" "grishy/any-sync-bundle"; then msg_info "Stopping Service" systemctl stop anytype msg_ok "Stopped Service" msg_info "Backing up Data" cp -r /opt/anytype/data /opt/anytype_data_backup msg_ok "Backed up Data" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "anytype" "grishy/any-sync-bundle" "prebuild" "latest" "/opt/anytype" "any-sync-bundle_*_linux_amd64.tar.gz" chmod +x /opt/anytype/any-sync-bundle msg_info "Restoring Data" cp -r /opt/anytype_data_backup/. /opt/anytype/data rm -rf /opt/anytype_data_backup msg_ok "Restored Data" msg_info "Starting Service" systemctl start anytype msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed Successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:33010${CL}" echo -e "${INFO}${YW} Client config file:${CL}" echo -e "${TAB}${GATEWAY}${BGN}/opt/anytype/data/client-config.yml${CL}" ================================================ FILE: ct/apache-cassandra.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://cassandra.apache.org/_/index.html APP="Apache-Cassandra" var_tags="${var_tags:-database;NoSQL}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -f /etc/init.d/cassandra ]]; then msg_error "No ${APP} Installation Found!" exit fi msg_info "Updating Apache Cassandra" $STD apt update $STD apt install -y --only-upgrade cassandra cassandra-tools msg_ok "Updated Apache Cassandra" msg_ok "Updated successfully!" exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" ================================================ FILE: ct/apache-couchdb.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://couchdb.apache.org/ APP="Apache-CouchDB" var_tags="${var_tags:-database}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-4096}" var_disk="${var_disk:-10}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -f /usr/lib/systemd/system/couchdb.service ]]; then msg_error "No ${APP} Installation Found!" exit fi msg_info "Updating Apache CouchDB" $STD apt-get update $STD apt-get install -y --only-upgrade couchdb msg_ok "Updated Apache CouchDB" msg_ok "Updated successfully!" exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:5984/_utils/${CL}" ================================================ FILE: ct/apache-guacamole.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Michel Roegl-Brunner (michelroegl-brunner) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://guacamole.apache.org/ APP="Apache-Guacamole" var_tags="${var_tags:-webserver;remote}" var_disk="${var_disk:-4}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-2048}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/apache-guacamole ]]; then msg_error "No ${APP} Installation Found!" exit fi # Fetch latest versions LATEST_TOMCAT=$(curl -fsSL https://dlcdn.apache.org/tomcat/tomcat-9/ | grep -oP '(?<=href=")v[^"/]+(?=/")' | sed 's/^v//' | sort -V | tail -n1) LATEST_SERVER=$(curl -fsSL https://api.github.com/repos/apache/guacamole-server/tags | jq -r '.[].name' | grep -v -- '-RC' | head -n 1) LATEST_CLIENT=$(curl -fsSL https://api.github.com/repos/apache/guacamole-client/tags | jq -r '.[].name' | grep -v -- '-RC' | head -n 1) LATEST_MYSQL_CONNECTOR=$(curl -fsSL "https://repo1.maven.org/maven2/com/mysql/mysql-connector-j/maven-metadata.xml" | grep -oP '\K[^<]+') # Read current versions from ~/.guacamole_* CURRENT_TOMCAT=$(cat ~/.guacamole_tomcat 2>/dev/null || echo "unknown") CURRENT_SERVER=$(cat ~/.guacamole_server 2>/dev/null || echo "unknown") CURRENT_CLIENT=$(cat ~/.guacamole_client 2>/dev/null || echo "unknown") CURRENT_MYSQL_CONNECTOR=$(cat ~/.guacamole_mysql_connector 2>/dev/null || echo "unknown") UPDATE_NEEDED=false [[ "$CURRENT_TOMCAT" != "$LATEST_TOMCAT" ]] && UPDATE_NEEDED=true [[ "$CURRENT_SERVER" != "$LATEST_SERVER" ]] && UPDATE_NEEDED=true [[ "$CURRENT_CLIENT" != "$LATEST_CLIENT" ]] && UPDATE_NEEDED=true [[ "$CURRENT_MYSQL_CONNECTOR" != "$LATEST_MYSQL_CONNECTOR" ]] && UPDATE_NEEDED=true if [[ "$UPDATE_NEEDED" == "false" ]]; then msg_ok "All components are up to date" exit fi JAVA_VERSION="17" setup_java msg_info "Stopping Services" systemctl stop guacd tomcat msg_ok "Stopped Services" # Update Tomcat if [[ "$CURRENT_TOMCAT" != "$LATEST_TOMCAT" ]]; then msg_info "Updating Tomcat (${CURRENT_TOMCAT} → ${LATEST_TOMCAT})" cp -a /opt/apache-guacamole/tomcat9/conf /tmp/tomcat-conf-backup curl -fsSL "https://dlcdn.apache.org/tomcat/tomcat-9/v${LATEST_TOMCAT}/bin/apache-tomcat-${LATEST_TOMCAT}.tar.gz" | tar -xz -C /opt/apache-guacamole/tomcat9 --strip-components=1 --exclude='conf/*' cp -a /tmp/tomcat-conf-backup/* /opt/apache-guacamole/tomcat9/conf/ rm -rf /tmp/tomcat-conf-backup chown -R tomcat: /opt/apache-guacamole/tomcat9 echo "${LATEST_TOMCAT}" >~/.guacamole_tomcat msg_ok "Updated Tomcat" else msg_ok "Tomcat already up to date (${CURRENT_TOMCAT})" fi # Update Guacamole Server if [[ "$CURRENT_SERVER" != "$LATEST_SERVER" ]]; then msg_info "Updating Guacamole Server (${CURRENT_SERVER} → ${LATEST_SERVER})" rm -rf /opt/apache-guacamole/server/* curl -fsSL "https://api.github.com/repos/apache/guacamole-server/tarball/refs/tags/${LATEST_SERVER}" | tar -xz --strip-components=1 -C /opt/apache-guacamole/server cd /opt/apache-guacamole/server export CPPFLAGS="-Wno-error=deprecated-declarations" $STD autoreconf -fi $STD ./configure --with-init-dir=/etc/init.d --enable-allow-freerdp-snapshots $STD make $STD make install $STD ldconfig echo "${LATEST_SERVER}" >~/.guacamole_server msg_ok "Updated Guacamole Server" # Auth JDBC follows server version msg_info "Updating Guacamole Auth JDBC" rm -f /etc/guacamole/extensions/guacamole-auth-jdbc-mysql-*.jar curl -fsSL "https://downloads.apache.org/guacamole/${LATEST_SERVER}/binary/guacamole-auth-jdbc-${LATEST_SERVER}.tar.gz" -o "/tmp/guacamole-auth-jdbc.tar.gz" $STD tar -xf /tmp/guacamole-auth-jdbc.tar.gz -C /tmp mv /tmp/guacamole-auth-jdbc-"${LATEST_SERVER}"/mysql/guacamole-auth-jdbc-mysql-"${LATEST_SERVER}".jar /etc/guacamole/extensions/ echo "${LATEST_SERVER}" >~/.guacamole_auth_jdbc msg_ok "Updated Guacamole Auth JDBC" else msg_ok "Guacamole Server already up to date (${CURRENT_SERVER})" fi # Update Guacamole Client if [[ "$CURRENT_CLIENT" != "$LATEST_CLIENT" ]]; then msg_info "Updating Guacamole Client (${CURRENT_CLIENT} → ${LATEST_CLIENT})" curl -fsSL "https://downloads.apache.org/guacamole/${LATEST_CLIENT}/binary/guacamole-${LATEST_CLIENT}.war" -o "/opt/apache-guacamole/tomcat9/webapps/guacamole.war" echo "${LATEST_CLIENT}" >~/.guacamole_client msg_ok "Updated Guacamole Client" else msg_ok "Guacamole Client already up to date (${CURRENT_CLIENT})" fi # Update MySQL Connector if [[ "$CURRENT_MYSQL_CONNECTOR" != "$LATEST_MYSQL_CONNECTOR" ]]; then msg_info "Updating MySQL Connector (${CURRENT_MYSQL_CONNECTOR} → ${LATEST_MYSQL_CONNECTOR})" curl -fsSL "https://repo1.maven.org/maven2/com/mysql/mysql-connector-j/${LATEST_MYSQL_CONNECTOR}/mysql-connector-j-${LATEST_MYSQL_CONNECTOR}.jar" -o "/etc/guacamole/lib/mysql-connector-j.jar" echo "${LATEST_MYSQL_CONNECTOR}" >~/.guacamole_mysql_connector msg_ok "Updated MySQL Connector" else msg_ok "MySQL Connector already up to date (${CURRENT_MYSQL_CONNECTOR})" fi # Apply SQL Schema Upgrades (CRITICAL!) if [[ "$CURRENT_SERVER" != "$LATEST_SERVER" ]]; then msg_info "Applying MySQL Schema Upgrades" cd /tmp/guacamole-auth-jdbc-"${LATEST_SERVER}"/mysql/schema/upgrade/ UPGRADE_FILES=($(ls -1 upgrade-pre-*.sql 2>/dev/null | sort -V)) if [[ ${#UPGRADE_FILES[@]} -gt 0 ]]; then for SQL_FILE in "${UPGRADE_FILES[@]}"; do FILE_VERSION=$(echo ${SQL_FILE} | grep -oP 'upgrade-pre-\K[0-9\.]+(?=\.)') # Apply upgrade if file version is newer than current but older/equal to target if [[ $(echo -e "${FILE_VERSION}\n${CURRENT_SERVER}" | sort -V | head -n1) == "${CURRENT_SERVER}" && "${FILE_VERSION}" != "${CURRENT_SERVER}" ]]; then msg_info "Applying schema patch: ${SQL_FILE}" mysql -u root guacamole_db <"${SQL_FILE}" 2>/dev/null if [[ $? -eq 0 ]]; then msg_ok "Applied ${SQL_FILE}" else msg_warn "Failed to apply ${SQL_FILE} (may already be applied)" fi fi done fi rm -rf /tmp/guacamole-auth-jdbc* msg_ok "MySQL Schema updated" fi # Check and upgrade optional extensions # TOTP Extension if [[ -f /etc/guacamole/extensions/guacamole-auth-totp-*.jar ]]; then msg_info "Updating TOTP Extension" rm -f /etc/guacamole/extensions/guacamole-auth-totp-*.jar curl -fsSL "https://downloads.apache.org/guacamole/${LATEST_SERVER}/binary/guacamole-auth-totp-${LATEST_SERVER}.tar.gz" -o "/tmp/guacamole-auth-totp.tar.gz" $STD tar -xf /tmp/guacamole-auth-totp.tar.gz -C /tmp mv /tmp/guacamole-auth-totp-"${LATEST_SERVER}"/guacamole-auth-totp-"${LATEST_SERVER}".jar /etc/guacamole/extensions/ chmod 664 /etc/guacamole/extensions/guacamole-auth-totp-"${LATEST_SERVER}".jar rm -rf /tmp/guacamole-auth-totp* msg_ok "Updated TOTP Extension" fi # DUO Extension if [[ -f /etc/guacamole/extensions/guacamole-auth-duo-*.jar ]]; then msg_info "Updating DUO Extension" rm -f /etc/guacamole/extensions/guacamole-auth-duo-*.jar curl -fsSL "https://downloads.apache.org/guacamole/${LATEST_SERVER}/binary/guacamole-auth-duo-${LATEST_SERVER}.tar.gz" -o "/tmp/guacamole-auth-duo.tar.gz" $STD tar -xf /tmp/guacamole-auth-duo.tar.gz -C /tmp mv /tmp/guacamole-auth-duo-"${LATEST_SERVER}"/guacamole-auth-duo-"${LATEST_SERVER}".jar /etc/guacamole/extensions/ chmod 664 /etc/guacamole/extensions/guacamole-auth-duo-"${LATEST_SERVER}".jar rm -rf /tmp/guacamole-auth-duo* msg_ok "Updated DUO Extension" fi # LDAP Extension if [[ -f /etc/guacamole/extensions/guacamole-auth-ldap-*.jar ]]; then msg_info "Updating LDAP Extension" rm -f /etc/guacamole/extensions/guacamole-auth-ldap-*.jar curl -fsSL "https://downloads.apache.org/guacamole/${LATEST_SERVER}/binary/guacamole-auth-ldap-${LATEST_SERVER}.tar.gz" -o "/tmp/guacamole-auth-ldap.tar.gz" $STD tar -xf /tmp/guacamole-auth-ldap.tar.gz -C /tmp mv /tmp/guacamole-auth-ldap-"${LATEST_SERVER}"/guacamole-auth-ldap-"${LATEST_SERVER}".jar /etc/guacamole/extensions/ chmod 664 /etc/guacamole/extensions/guacamole-auth-ldap-"${LATEST_SERVER}".jar rm -rf /tmp/guacamole-auth-ldap* msg_ok "Updated LDAP Extension" fi # Quick Connect Extension if [[ -f /etc/guacamole/extensions/guacamole-auth-quickconnect-*.jar ]]; then msg_info "Updating Quick Connect Extension" rm -f /etc/guacamole/extensions/guacamole-auth-quickconnect-*.jar curl -fsSL "https://downloads.apache.org/guacamole/${LATEST_SERVER}/binary/guacamole-auth-quickconnect-${LATEST_SERVER}.tar.gz" -o "/tmp/guacamole-auth-quickconnect.tar.gz" $STD tar -xf /tmp/guacamole-auth-quickconnect.tar.gz -C /tmp mv /tmp/guacamole-auth-quickconnect-"${LATEST_SERVER}"/guacamole-auth-quickconnect-"${LATEST_SERVER}".jar /etc/guacamole/extensions/ chmod 664 /etc/guacamole/extensions/guacamole-auth-quickconnect-"${LATEST_SERVER}".jar rm -rf /tmp/guacamole-auth-quickconnect* msg_ok "Updated Quick Connect Extension" fi # History Recording Storage Extension if [[ -f /etc/guacamole/extensions/guacamole-history-recording-storage-*.jar ]]; then msg_info "Updating History Recording Storage Extension" rm -f /etc/guacamole/extensions/guacamole-history-recording-storage-*.jar curl -fsSL "https://downloads.apache.org/guacamole/${LATEST_SERVER}/binary/guacamole-history-recording-storage-${LATEST_SERVER}.tar.gz" -o "/tmp/guacamole-history-recording-storage.tar.gz" $STD tar -xf /tmp/guacamole-history-recording-storage.tar.gz -C /tmp mv /tmp/guacamole-history-recording-storage-"${LATEST_SERVER}"/guacamole-history-recording-storage-"${LATEST_SERVER}".jar /etc/guacamole/extensions/ chmod 664 /etc/guacamole/extensions/guacamole-history-recording-storage-"${LATEST_SERVER}".jar rm -rf /tmp/guacamole-history-recording-storage* msg_ok "Updated History Recording Storage Extension" fi # Reset permissions and prepare for service start msg_info "Resetting permissions" mkdir -p /var/guacamole chown daemon:daemon /var/guacamole mkdir -p /home/daemon/.config/freerdp chown daemon:daemon /home/daemon/.config/freerdp msg_ok "Permissions reset" msg_info "Starting Services" systemctl daemon-reload systemctl start tomcat guacd msg_ok "Started Services" msg_ok "Updated successfully!" exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8080/guacamole${CL}" ================================================ FILE: ct/apache-tika.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Andy Grunwald (andygrunwald) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/apache/tika/ APP="Apache-Tika" var_tags="${var_tags:-document}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-10}" var_os="${var_os:-debian}" var_version="${var_version:-12}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -f /etc/systemd/system/apache-tika.service ]]; then msg_error "No ${APP} Installation Found!" exit fi RELEASE="$(curl -fsSL https://dlcdn.apache.org/tika/ | grep -oP '(?<=href=")[0-9]+\.[0-9]+\.[0-9]+(?=/")' | sort -V | tail -n1)" if [[ ! -f /opt/${APP}_version.txt ]] || [[ "${RELEASE}" != "$(cat /opt/${APP}_version.txt)" ]]; then msg_info "Stopping Service" systemctl stop apache-tika msg_ok "Stopped Service" msg_info "Updating ${APP} to v${RELEASE}" cd /opt/apache-tika curl -fsSL -o tika-server-standard-${RELEASE}.jar "https://dlcdn.apache.org/tika/${RELEASE}/tika-server-standard-${RELEASE}.jar" mv --force tika-server-standard.jar tika-server-standard-prev-version.jar mv tika-server-standard-${RELEASE}.jar tika-server-standard.jar rm -rf /opt/apache-tika/tika-server-standard-prev-version.jar echo "${RELEASE}" >/opt/${APP}_version.txt msg_ok "Updated ${APP} to v${RELEASE}" msg_info "Starting Service" systemctl start apache-tika msg_ok "Started Service" msg_ok "Updated successfully!" else msg_ok "No update required. ${APP} is already at v${RELEASE}" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:9998${CL}" ================================================ FILE: ct/apache-tomcat.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (Canbiz) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://tomcat.apache.org/ APP="Apache-Tomcat" var_tags="${var_tags:-webserver}" var_disk="${var_disk:-5}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-1024}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources TOMCAT_DIR=$(ls -d /opt/tomcat-* 2>/dev/null | head -n1) if [[ -z "$TOMCAT_DIR" || ! -d "$TOMCAT_DIR" ]]; then msg_error "No ${APP} Installation Found!" exit fi # Detect major version and current version from install path (e.g., /opt/tomcat-11 -> 11) TOMCAT_MAJOR=$(basename "$TOMCAT_DIR" | grep -oP 'tomcat-\K[0-9]+') if [[ -z "$TOMCAT_MAJOR" ]]; then msg_error "Cannot determine Tomcat major version from path: $TOMCAT_DIR" exit fi CURRENT_VERSION=$(grep -oP 'Apache Tomcat Version \K[0-9.]+' "$TOMCAT_DIR/RELEASE-NOTES" 2>/dev/null || echo "unknown") LATEST_VERSION=$(curl -fsSL "https://dlcdn.apache.org/tomcat/tomcat-${TOMCAT_MAJOR}/" | grep -oP 'v[0-9]+\.[0-9]+\.[0-9]+(-M[0-9]+)?/' | sort -V | tail -n1 | sed 's/\/$//; s/v//') if [[ -z "$LATEST_VERSION" ]]; then msg_error "Failed to fetch latest version for Tomcat ${TOMCAT_MAJOR}" exit fi if [[ "$CURRENT_VERSION" == "$LATEST_VERSION" ]]; then msg_ok "${APP} ${CURRENT_VERSION} is already up to date" exit fi msg_info "Stopping Tomcat service" systemctl stop tomcat msg_ok "Stopped Tomcat service" msg_info "Backing up configuration and applications" BACKUP_DIR="/tmp/tomcat-backup-$$" mkdir -p "$BACKUP_DIR" cp -a "$TOMCAT_DIR/conf" "$BACKUP_DIR/conf" cp -a "$TOMCAT_DIR/webapps" "$BACKUP_DIR/webapps" [[ -d "$TOMCAT_DIR/lib" ]] && cp -a "$TOMCAT_DIR/lib" "$BACKUP_DIR/lib" msg_ok "Backed up configuration and applications" msg_info "Downloading Tomcat ${LATEST_VERSION}" TOMCAT_URL="https://dlcdn.apache.org/tomcat/tomcat-${TOMCAT_MAJOR}/v${LATEST_VERSION}/bin/apache-tomcat-${LATEST_VERSION}.tar.gz" curl -fsSL "$TOMCAT_URL" -o /tmp/tomcat-update.tar.gz msg_ok "Downloaded Tomcat ${LATEST_VERSION}" msg_info "Installing update" rm -rf "${TOMCAT_DIR:?}"/* tar --strip-components=1 -xzf /tmp/tomcat-update.tar.gz -C "$TOMCAT_DIR" rm -f /tmp/tomcat-update.tar.gz msg_ok "Installed update" msg_info "Restoring configuration and applications" cp -a "$BACKUP_DIR/conf"/* "$TOMCAT_DIR/conf/" cp -a "$BACKUP_DIR/webapps"/* "$TOMCAT_DIR/webapps/" 2>/dev/null || true if [[ -d "$BACKUP_DIR/lib" ]]; then for jar in "$BACKUP_DIR/lib"/*.jar; do [[ -f "$jar" ]] || continue jar_name=$(basename "$jar") if [[ ! -f "$TOMCAT_DIR/lib/$jar_name" ]]; then cp "$jar" "$TOMCAT_DIR/lib/" fi done fi rm -rf "$BACKUP_DIR" chown -R root:root "$TOMCAT_DIR" msg_ok "Restored configuration and applications" msg_info "Starting Tomcat service" systemctl start tomcat msg_ok "Started Tomcat service" msg_ok "Updated successfully!" exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8080${CL}" ================================================ FILE: ct/apprise-api.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: SystemIdleProcess # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/caronc/apprise-api APP="Apprise-API" var_tags="${var_tags:-notification}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-512}" var_disk="${var_disk:-2}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d "/opt/apprise" ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "apprise" "caronc/apprise-api"; then msg_info "Stopping Service" systemctl stop apprise-api msg_ok "Stopped Service" PYTHON_VERSION="3.12" setup_uv CLEAN_INSTALL=1 fetch_and_deploy_gh_release "apprise" "caronc/apprise-api" "tarball" msg_info "Updating Apprise-API" cd /opt/apprise cp ./requirements.txt /etc/requirements.txt $STD apt install -y nginx git $STD uv pip install -r requirements.txt gunicorn supervisor --system cp -fr apprise_api/static /usr/share/nginx/html/s/ mv apprise_api/ webapp touch /etc/nginx/server-override.conf touch /etc/nginx/location-override.conf mkdir -p /config/store /attach /plugin /tmp/apprise /opt/apprise/logs chmod 1777 /tmp/apprise && chmod 777 /config /config/store /attach /plugin /opt/apprise/logs sed -i \ -e '/[[]program:nginx]/,/^[[]/ s|stdout_logfile=/dev/stdout|stdout_logfile=/opt/apprise/logs/nginx.log|' \ -e '/[[]program:nginx]/,/^[[]/ s|stderr_logfile=/dev/stderr|stderr_logfile=/opt/apprise/logs/nginx_error.log|' \ -e '/[[]program:gunicorn]/,/^[[]/ s|stdout_logfile=/dev/stdout|stdout_logfile=/opt/apprise/logs/gunicorn.log|' \ -e '/[[]program:gunicorn]/,/^[[]/ s|stderr_logfile=/dev/stderr|stderr_logfile=/opt/apprise/logs/gunicorn_error.log|' \ -e '/[[]supervisord]/,/^[[]/ s|logfile=/dev/null|logfile=/opt/apprise/logs/supervisor.log|' \ -e 's|_maxbytes=0|_maxbytes=10485760|g' \ /opt/apprise/webapp/etc/supervisord.conf msg_ok "Updated Apprise-API" msg_info "Starting Service" systemctl start apprise-api msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8000${CL}" ================================================ FILE: ct/apt-cacher-ng.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://wiki.debian.org/AptCacherNg APP="Apt-Cacher-NG" var_tags="${var_tags:-caching}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-512}" var_disk="${var_disk:-10}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /var ]]; then msg_error "No ${APP} Installation Found!" exit fi msg_info "Updating Apt-Cacher-NG" $STD apt update $STD apt -y upgrade msg_ok "Updated Apt-Cacher-NG" msg_ok "Updated successfully!" exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:3142/acng-report.html${CL}" ================================================ FILE: ct/archivebox.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://archivebox.io/ APP="ArchiveBox" var_tags="${var_tags:-archive;bookmark}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-1024}" var_disk="${var_disk:-8}" var_os="${var_os:-debian}" var_version="${var_version:-12}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/archivebox ]]; then msg_error "No ${APP} Installation Found!" exit fi NODE_VERSION="22" NODE_MODULE="@postlight/parser@latest,single-file-cli@latest" setup_nodejs PYTHON_VERSION="3.13" setup_uv ensure_dependencies chromium msg_info "Stopping Service" systemctl stop archivebox msg_ok "Stopped Service" msg_info "Upgrading Playwright" $STD uv pip install playwright --system $STD playwright install-deps chromium msg_ok "Upgraded Playwright" msg_info "Updating ArchiveBox" cd /opt/archivebox/data $STD uv pip install --system --upgrade --no-reinstall archivebox sudo -u archivebox archivebox init msg_ok "Updated ArchiveBox" msg_info "Starting Service" systemctl start archivebox msg_ok "Started Service" msg_ok "Updated successfully!" exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8000/admin/login${CL}" ================================================ FILE: ct/argus.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://release-argus.io/ APP="Argus" var_tags="${var_tags:-watcher}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-512}" var_disk="${var_disk:-3}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/argus ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "argus" "release-argus/Argus"; then msg_info "Stopping service" systemctl stop argus msg_ok "Service stopped" fetch_and_deploy_gh_release "Argus" "release-argus/Argus" "singlefile" "latest" "/opt/argus" "Argus*linux-arm64" msg_info "Starting service" systemctl start argus msg_ok "Service started" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8080${CL}" ================================================ FILE: ct/aria2.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://aria2.github.io/ APP="Aria2" var_tags="${var_tags:-download-utility}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-1024}" var_disk="${var_disk:-8}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /var ]]; then msg_error "No ${APP} Installation Found!" exit fi msg_info "Updating Aria2" $STD apt update $STD apt -y upgrade msg_ok "Updated Aria2" msg_ok "Updated successfully!" exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:6880${CL}" ================================================ FILE: ct/asterisk.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: michelroegl-brunner # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://asterisk.org/ APP="Asterisk" var_tags="${var_tags:-telephone;pbx}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources msg_error "No Update function provided for ${APP} LXC" exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" ================================================ FILE: ct/audiobookshelf.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.audiobookshelf.org/ APP="audiobookshelf" var_tags="${var_tags:-podcast;audiobook}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-5}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -f /etc/default/audiobookshelf ]]; then msg_error "No ${APP} Installation Found!" exit fi msg_info "Updating AudiobookShelf" $STD apt update $STD apt upgrade -y msg_ok "Updated AudiobookShelf" msg_ok "Updated successfully!" exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:13378${CL}" ================================================ FILE: ct/authelia.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: thost96 (thost96) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.authelia.com/ APP="Authelia" var_tags="${var_tags:-authenticator}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-512}" var_disk="${var_disk:-2}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" base_settings variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /etc/authelia/ ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "authelia" "authelia/authelia"; then $STD apt update $STD apt -y upgrade fetch_and_deploy_gh_release "authelia" "authelia/authelia" "binary" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:9091 or https://auth.YOURDOMAIN ${CL}" ================================================ FILE: ct/autobrr.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://autobrr.com/ APP="Autobrr" var_tags="${var_tags:-arr;}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-8}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -f /root/.config/autobrr/config.toml ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "autobrr" "autobrr/autobrr"; then msg_info "Stopping Service" systemctl stop autobrr msg_ok "Stopped Service" fetch_and_deploy_gh_release "autobrr" "autobrr/autobrr" "prebuild" "latest" "/usr/local/bin" "autobrr_*_linux_arm64.tar.gz" msg_info "Starting Service" systemctl start autobrr msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:7474${CL}" ================================================ FILE: ct/autocaliweb.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: vhsdream # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://codeberg.org/gelbphoenix/autocaliweb APP="Autocaliweb" var_tags="${var_tags:-ebooks}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-6}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/autocaliweb ]]; then msg_error "No ${APP} Installation Found!" exit fi setup_uv RELEASE=$(get_latest_codeberg_release "gelbphoenix/autocaliweb") if check_for_codeberg_release "autocaliweb" "gelbphoenix/autocaliweb"; then msg_info "Stopping Services" systemctl stop autocaliweb metadata-change-detector acw-ingest-service acw-auto-zipper msg_ok "Stopped Services" INSTALL_DIR="/opt/autocaliweb" export VIRTUAL_ENV="${INSTALL_DIR}/venv" $STD tar -cf ~/autocaliweb_bkp.tar "$INSTALL_DIR"/{metadata_change_logs,dirs.json,.env,scripts/ingest_watcher.sh,scripts/auto_zipper_wrapper.sh,scripts/metadata_change_detector_wrapper.sh} fetch_and_deploy_codeberg_release "autocaliweb" "gelbphoenix/autocaliweb" "tarball" "latest" "/opt/autocaliweb" msg_info "Updating Autocaliweb" cd "$INSTALL_DIR" if [[ ! -d "$VIRTUAL_ENV" ]]; then $STD uv venv --clear "$VIRTUAL_ENV" fi $STD uv sync --all-extras --active cd "$INSTALL_DIR"/koreader/plugins PLUGIN_DIGEST="$(find acwsync.koplugin -type f -name "*.lua" -o -name "*.json" | sort | xargs sha256sum | sha256sum | cut -d' ' -f1)" echo "Plugin files digest: $PLUGIN_DIGEST" >acwsync.koplugin/"${PLUGIN_DIGEST}".digest echo "Build date: $(date)" >>acwsync.koplugin/"${PLUGIN_DIGEST}".digest echo "Files included:" >>acwsync.koplugin/"${PLUGIN_DIGEST}".digest $STD zip -r koplugin.zip acwsync.koplugin/ cp -r koplugin.zip "$INSTALL_DIR"/cps/static mkdir -p "$INSTALL_DIR"/metadata_temp $STD tar -xf ~/autocaliweb_bkp.tar --directory / KEPUB_VERSION="$(/usr/bin/kepubify --version)" CALIBRE_RELEASE="$(curl -s https://api.github.com/repos/kovidgoyal/calibre/releases/latest | grep -o '"tag_name": "[^"]*' | cut -d'"' -f4)" echo "${KEPUB_VERSION#v}" >"$INSTALL_DIR"/KEPUBIFY_RELEASE echo "${CALIBRE_RELEASE#v}" >/"$INSTALL_DIR"/CALIBRE_RELEASE sed 's/^/v/' ~/.autocaliweb >"$INSTALL_DIR"/ACW_RELEASE chown -R acw:acw "$INSTALL_DIR" rm ~/autocaliweb_bkp.tar msg_ok "Updated Autocaliweb" msg_info "Starting Services" systemctl start autocaliweb metadata-change-detector acw-ingest-service acw-auto-zipper msg_ok "Started Services" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8083${CL}" ================================================ FILE: ct/babybuddy.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/babybuddy/babybuddy APP="Baby Buddy" var_tags="${var_tags:-baby}" var_disk="${var_disk:-5}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_os="${var_os:-debian}" var_version="${var_version:-12}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/babybuddy ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "babybuddy" "babybuddy/babybuddy"; then setup_uv msg_info "Stopping Services" systemctl stop nginx systemctl stop uwsgi msg_ok "Services Stopped" msg_info "Cleaning old files" cp /opt/babybuddy/babybuddy/settings/production.py /tmp/production.py.bak find . -mindepth 1 -maxdepth 1 ! -name '.venv' -exec rm -rf {} + msg_ok "Cleaned old files" fetch_and_deploy_gh_release "babybuddy" "babybuddy/babybuddy" "tarball" msg_info "Updating ${APP}" cd /opt/babybuddy mv /tmp/production.py.bak /opt/babybuddy/babybuddy/settings/production.py source .venv/bin/activate $STD uv pip install -r requirements.txt export DJANGO_SETTINGS_MODULE=babybuddy.settings.production $STD python manage.py migrate msg_ok "Updated ${APP}" msg_info "Fixing permissions" chown -R www-data:www-data /opt/data chmod 640 /opt/data/db.sqlite3 chmod 750 /opt/data msg_ok "Permissions fixed" msg_info "Starting Services" systemctl start uwsgi systemctl start nginx msg_ok "Services Started" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}${CL}" ================================================ FILE: ct/backrest.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: ksad (enirys31) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://garethgeorge.github.io/backrest/ APP="Backrest" var_tags="${var_tags:-backup}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-512}" var_disk="${var_disk:-8}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/backrest ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "backrest" "garethgeorge/backrest"; then msg_info "Stopping Service" systemctl stop backrest msg_ok "Stopped Service" fetch_and_deploy_gh_release "backrest" "garethgeorge/backrest" "prebuild" "latest" "/opt/backrest/bin" "backrest_Linux_arm64.tar.gz" msg_info "Starting Service" systemctl start backrest msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:9898${CL}" ================================================ FILE: ct/baikal.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: bvdberg01 # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://sabre.io/baikal/ APP="Baikal" var_tags="${var_tags:-Dav}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-512}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/baikal ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "baikal" "sabre-io/Baikal"; then msg_info "Stopping Service" systemctl stop apache2 msg_ok "Stopped Service" msg_info "Backing up data" mv /opt/baikal /opt/baikal-backup msg_ok "Backed up data" PHP_APACHE="YES" PHP_VERSION="8.3" setup_php setup_composer fetch_and_deploy_gh_release "baikal" "sabre-io/Baikal" "tarball" msg_info "Configuring Baikal" cp -r /opt/baikal-backup/config/baikal.yaml /opt/baikal/config/ cp -r /opt/baikal-backup/Specific/ /opt/baikal/ chown -R www-data:www-data /opt/baikal/ chmod -R 755 /opt/baikal/ cd /opt/baikal $STD composer install rm -rf /opt/baikal-backup msg_ok "Configured Baikal" msg_info "Starting Service" systemctl start apache2 msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}${CL}" ================================================ FILE: ct/bambuddy.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Adrian-RDA # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/maziggy/bambuddy APP="Bambuddy" var_tags="${var_tags:-media;3d-printing}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-10}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/bambuddy ]]; then msg_error "No ${APP} Installation Found!" exit fi ensure_dependencies ffmpeg if check_for_gh_release "bambuddy" "maziggy/bambuddy"; then msg_info "Stopping Service" systemctl stop bambuddy msg_ok "Stopped Service" msg_info "Backing up Configuration and Data" cp /opt/bambuddy/.env /opt/bambuddy.env.bak cp -r /opt/bambuddy/data /opt/bambuddy_data_bak [[ -f /opt/bambuddy/bambuddy.db ]] && cp /opt/bambuddy/bambuddy.db /opt/bambuddy.db.bak [[ -f /opt/bambuddy/bambutrack.db ]] && cp /opt/bambuddy/bambutrack.db /opt/bambutrack.db.bak [[ -d /opt/bambuddy/archive ]] && cp -r /opt/bambuddy/archive /opt/bambuddy_archive_bak msg_ok "Backed up Configuration and Data" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "bambuddy" "maziggy/bambuddy" "tarball" "latest" "/opt/bambuddy" msg_info "Updating Python Dependencies" cd /opt/bambuddy $STD uv venv --clear $STD uv pip install -r requirements.txt msg_ok "Updated Python Dependencies" msg_info "Rebuilding Frontend" cd /opt/bambuddy/frontend $STD npm install $STD npm run build msg_ok "Rebuilt Frontend" msg_info "Restoring Configuration and Data" mkdir -p /opt/bambuddy/data cp /opt/bambuddy.env.bak /opt/bambuddy/.env cp -r /opt/bambuddy_data_bak/. /opt/bambuddy/data/ [[ -f /opt/bambuddy.db.bak ]] && cp /opt/bambuddy.db.bak /opt/bambuddy/bambuddy.db [[ -f /opt/bambutrack.db.bak ]] && cp /opt/bambutrack.db.bak /opt/bambuddy/bambutrack.db if [[ -d /opt/bambuddy_archive_bak ]]; then mkdir -p /opt/bambuddy/archive cp -r /opt/bambuddy_archive_bak/. /opt/bambuddy/archive/ fi rm -f /opt/bambuddy.env.bak /opt/bambuddy.db.bak /opt/bambutrack.db.bak rm -rf /opt/bambuddy_data_bak /opt/bambuddy_archive_bak msg_ok "Restored Configuration and Data" msg_info "Starting Service" systemctl start bambuddy msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed Successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8000${CL}" ================================================ FILE: ct/bar-assistant.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: bvdberg01 | CanbiZ # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/karlomikus/bar-assistant # Source: https://github.com/karlomikus/vue-salt-rim # Source: https://www.meilisearch.com/ APP="Bar-Assistant" var_tags="${var_tags:-cocktails;drinks}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/bar-assistant ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "bar-assistant" "karlomikus/bar-assistant"; then msg_info "Stopping nginx" systemctl stop nginx msg_ok "Stopped nginx" PHP_VERSION="8.4" PHP_FPM="YES" PHP_MODULE="pdo-sqlite" setup_php msg_info "Backing up Bar Assistant" mv /opt/bar-assistant /opt/bar-assistant-backup msg_ok "Backed up Bar Assistant" fetch_and_deploy_gh_release "bar-assistant" "karlomikus/bar-assistant" "tarball" "latest" "/opt/bar-assistant" setup_composer msg_info "Updating Bar-Assistant" cp -r /opt/bar-assistant-backup/.env /opt/bar-assistant/.env cp -r /opt/bar-assistant-backup/storage/bar-assistant /opt/bar-assistant/storage/bar-assistant cd /opt/bar-assistant $STD composer install --no-interaction $STD php artisan migrate --force $STD php artisan storage:link $STD php artisan bar:setup-meilisearch $STD php artisan scout:sync-index-settings $STD php artisan config:cache $STD php artisan route:cache $STD php artisan event:cache chown -R www-data:www-data /opt/bar-assistant rm -rf /opt/bar-assistant-backup msg_ok "Updated Bar-Assistant" msg_info "Starting nginx" systemctl start nginx msg_ok "Started nginx" fi if check_for_gh_release "vue-salt-rim" "karlomikus/vue-salt-rim"; then msg_info "Backing up Vue Salt Rim" mv /opt/vue-salt-rim /opt/vue-salt-rim-backup msg_ok "Backed up Vue Salt Rim" msg_info "Stopping nginx" systemctl stop nginx msg_ok "Stopped nginx" fetch_and_deploy_gh_release "vue-salt-rim" "karlomikus/vue-salt-rim" "tarball" "latest" "/opt/vue-salt-rim" msg_info "Updating Vue Salt Rim" cp /opt/vue-salt-rim-backup/public/config.js /opt/vue-salt-rim/public/config.js cd /opt/vue-salt-rim $STD npm install $STD npm run build rm -rf /opt/vue-salt-rim-backup msg_ok "Updated Vue Salt Rim" msg_info "Starting nginx" systemctl start nginx msg_ok "Started nginx" fi setup_meilisearch exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}${CL}" ================================================ FILE: ct/bazarr.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.bazarr.media/ APP="Bazarr" var_tags="${var_tags:-arr}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-1024}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /var/lib/bazarr/ ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "bazarr" "morpheus65535/bazarr"; then apt-get install -y libicu76 &>/dev/null msg_info "Stopping Service" systemctl stop bazarr msg_ok "Stopped Service" PYTHON_VERSION="3.12" setup_uv fetch_and_deploy_gh_release "bazarr" "morpheus65535/bazarr" "prebuild" "latest" "/opt/bazarr" "bazarr.zip" msg_info "Setup Bazarr" mkdir -p /var/lib/bazarr/ chmod 775 /opt/bazarr /var/lib/bazarr/ # Always ensure venv exists if [[ ! -d /opt/bazarr/venv/ ]]; then $STD uv venv --clear /opt/bazarr/venv --python 3.12 fi # Always check and fix service file if needed if [[ -f /etc/systemd/system/bazarr.service ]] && grep -q "ExecStart=/usr/bin/python3" /etc/systemd/system/bazarr.service; then sed -i "s|ExecStart=/usr/bin/python3 /opt/bazarr/bazarr.py|ExecStart=/opt/bazarr/venv/bin/python3 /opt/bazarr/bazarr.py|g" /etc/systemd/system/bazarr.service systemctl daemon-reload fi sed -i.bak 's/--only-binary=Pillow//g' /opt/bazarr/requirements.txt $STD uv pip install -r /opt/bazarr/requirements.txt --python /opt/bazarr/venv/bin/python3 msg_ok "Setup Bazarr" msg_info "Starting Service" systemctl start bazarr msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:6767${CL}" ================================================ FILE: ct/bentopdf.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: vhsdream # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/alam00000/bentopdf APP="BentoPDF" var_tags="${var_tags:-pdf-editor}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-4096}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/bentopdf ]]; then msg_error "No ${APP} Installation Found!" exit fi NODE_VERSION="24" setup_nodejs if check_for_gh_release "bentopdf" "alam00000/bentopdf"; then msg_info "Stopping Service" systemctl stop bentopdf msg_ok "Stopped Service" [[ -f /opt/bentopdf/.env.production ]] && cp /opt/bentopdf/.env.production /opt/production.env CLEAN_INSTALL=1 fetch_and_deploy_gh_release "bentopdf" "alam00000/bentopdf" "tarball" "latest" "/opt/bentopdf" msg_info "Updating BentoPDF" cd /opt/bentopdf $STD npm ci --no-audit --no-fund if [[ -f /opt/production.env ]]; then mv /opt/production.env ./.env.production else cp ./.env.example ./.env.production fi export NODE_OPTIONS="--max-old-space-size=3072" export SIMPLE_MODE=true export VITE_USE_CDN=true $STD npm run build:all if [[ ! -f /opt/bentopdf/dist/config.json ]]; then cat <<'EOF' >/opt/bentopdf/dist/config.json {} EOF fi msg_ok "Updated BentoPDF" msg_info "Starting Service" ensure_dependencies nginx openssl if [[ ! -f /etc/ssl/private/bentopdf-selfsigned.key || ! -f /etc/ssl/certs/bentopdf-selfsigned.crt ]]; then CERT_CN="$(hostname -I | awk '{print $1}')" $STD openssl req -x509 -nodes -newkey rsa:2048 -days 3650 \ -keyout /etc/ssl/private/bentopdf-selfsigned.key \ -out /etc/ssl/certs/bentopdf-selfsigned.crt \ -subj "/CN=${CERT_CN}" fi cat <<'EOF' >/etc/nginx/sites-available/bentopdf server { listen 8080; server_name _; return 301 https://$host:8443$request_uri; } server { listen 8443 ssl; server_name _; ssl_certificate /etc/ssl/certs/bentopdf-selfsigned.crt; ssl_certificate_key /etc/ssl/private/bentopdf-selfsigned.key; root /opt/bentopdf/dist; index index.html; # Required for LibreOffice WASM (Word/Excel/PowerPoint to PDF via SharedArrayBuffer) add_header Cross-Origin-Opener-Policy "same-origin" always; add_header Cross-Origin-Embedder-Policy "require-corp" always; add_header Cross-Origin-Resource-Policy "cross-origin" always; add_header X-Content-Type-Options "nosniff" always; add_header X-Frame-Options "SAMEORIGIN" always; gzip_static on; location ~* /libreoffice-wasm/soffice\.wasm\.gz$ { gzip off; types {} default_type application/wasm; add_header Content-Encoding gzip; add_header Vary "Accept-Encoding"; add_header Cache-Control "public, immutable"; } location ~* /libreoffice-wasm/soffice\.data\.gz$ { gzip off; types {} default_type application/octet-stream; add_header Content-Encoding gzip; add_header Vary "Accept-Encoding"; add_header Cache-Control "public, immutable"; } location ~* \.wasm$ { types {} default_type application/wasm; expires 1y; add_header Cache-Control "public, immutable"; } location ~* \.(wasm\.gz|data\.gz|data)$ { expires 1y; add_header Cache-Control "public, immutable"; } location / { try_files $uri $uri/ $uri.html =404; } error_page 404 /404.html; } EOF rm -f /etc/nginx/sites-enabled/default ln -sf /etc/nginx/sites-available/bentopdf /etc/nginx/sites-enabled/bentopdf cat <<'EOF' >/etc/systemd/system/bentopdf.service [Unit] Description=BentoPDF Service After=network.target [Service] Type=simple ExecStart=/usr/sbin/nginx -g "daemon off;" ExecReload=/bin/kill -HUP $MAINPID Restart=always [Install] WantedBy=multi-user.target EOF systemctl daemon-reload systemctl start bentopdf msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}https://${IP}:8443${CL}" ================================================ FILE: ct/beszel.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Michelle Zitzerman (Sinofage) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://beszel.dev/ APP="Beszel" var_tags="${var_tags:-monitoring}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-512}" var_disk="${var_disk:-5}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/beszel ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "beszel" "henrygd/beszel"; then msg_info "Stopping Service" systemctl stop beszel-hub msg_info "Stopped Service" msg_info "Updating Beszel" $STD /opt/beszel/beszel update sleep 2 && chmod +x /opt/beszel/beszel VERSION=$(/opt/beszel/beszel -v | awk '{print $3}') echo "${VERSION}" >$HOME/.beszel msg_ok "Updated Beszel to ${VERSION}" msg_info "Starting Service" systemctl start beszel-hub msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following IP:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8090${CL}" ================================================ FILE: ct/bichon.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Slaviša Arežina (tremor021) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/rustmailer/bichon APP="Bichon" var_tags="${var_tags:-email;archive}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-1024}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/bichon ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "bichon" "rustmailer/bichon"; then msg_info "Stopping service" systemctl stop bichon msg_ok "Stopped service" cp /opt/bichon/bichon.env /tmp/bichon.env.backup CLEAN_INSTALL=1 fetch_and_deploy_gh_release "bichon" "rustmailer/bichon" "prebuild" "latest" "/opt/bichon" "bichon-*-aarch64-unknown-linux-gnu.tar.gz" cp /tmp/bichon.env.backup /opt/bichon/bichon.env msg_info "Starting service" systemctl start bichon msg_ok "Service started" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:15630${CL}" ================================================ FILE: ct/birdnet-go.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/tphakala/birdnet-go APP="BirdNET-Go" var_tags="${var_tags:-monitoring;ai;nature}" var_cpu="${var_cpu:-4}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-12}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" var_gpu="${var_gpu:-no}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -f /usr/local/bin/birdnet-go ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "birdnet" "tphakala/birdnet-go"; then msg_info "Stopping Service" systemctl stop birdnet msg_ok "Stopped Service" fetch_and_deploy_gh_release "birdnet" "tphakala/birdnet-go" "prebuild" "latest" "/opt/birdnet" "birdnet-go-linux-amd64.tar.gz" msg_info "Deploying Binary" cp /opt/birdnet/birdnet-go /usr/local/bin/birdnet-go chmod +x /usr/local/bin/birdnet-go cp -r /opt/birdnet/libtensorflowlite_c.so /usr/local/lib/ || true ldconfig msg_ok "Deployed Binary" msg_info "Starting Service" systemctl start birdnet msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed Successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8080${CL}" ================================================ FILE: ct/bitmagnet.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Slaviša Arežina (tremor021) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/bitmagnet/bitmagnet APP="Bitmagnet" var_tags="${var_tags:-os}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-1024}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/bitmagnet ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "bitmagnet" "bitmagnet-io/bitmagnet"; then msg_info "Stopping Service" systemctl stop bitmagnet-web msg_ok "Stopped Service" msg_info "Backing up data" rm -f /tmp/backup.sql $STD sudo -u postgres pg_dump \ --column-inserts \ --data-only \ --on-conflict-do-nothing \ --rows-per-insert=1000 \ --table=metadata_sources \ --table=content \ --table=content_attributes \ --table=content_collections \ --table=content_collections_content \ --table=torrent_sources \ --table=torrents \ --table=torrent_files \ --table=torrent_hints \ --table=torrent_contents \ --table=torrent_tags \ --table=torrents_torrent_sources \ --table=key_values \ bitmagnet \ >/tmp/backup.sql mv /tmp/backup.sql /opt/ [ -f /opt/bitmagnet/.env ] && cp /opt/bitmagnet/.env /opt/ [ -f /opt/bitmagnet/config.yml ] && cp /opt/bitmagnet/config.yml /opt/ msg_ok "Data backed up" rm -rf /opt/bitmagnet fetch_and_deploy_gh_release "bitmagnet" "bitmagnet-io/bitmagnet" "tarball" msg_info "Updating Bitmagnet" cd /opt/bitmagnet VREL=v$(curl -fsSL https://api.github.com/repos/bitmagnet-io/bitmagnet/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4) }') $STD go build -ldflags "-s -w -X github.com/bitmagnet-io/bitmagnet/internal/version.GitTag=$VREL" chmod +x bitmagnet [ -f "/opt/.env" ] && cp "/opt/.env" /opt/bitmagnet/ [ -f "/opt/config.yml" ] && cp "/opt/config.yml" /opt/bitmagnet/ msg_ok "Updated Bitmagnet" msg_info "Starting Service" systemctl start bitmagnet-web msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:3333${CL}" ================================================ FILE: ct/blocky.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://0xerr0r.github.io/blocky APP="Blocky" var_tags="${var_tags:-adblock}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-512}" var_disk="${var_disk:-2}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/blocky ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "blocky" "0xERR0R/blocky"; then msg_info "Stopping Service" systemctl stop blocky msg_ok "Stopped Service" msg_info "Backup Config" mv /opt/blocky/config.yml /opt/config.yml msg_ok "Backed Up Config" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "blocky" "0xERR0R/blocky" "prebuild" "latest" "/opt/blocky" "blocky_*_Linux_aarch64.tar.gz" msg_info "Restore Config" mv /opt/config.yml /opt/blocky/config.yml msg_ok "Restored Config" msg_info "Starting Service" systemctl start blocky msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}${CL}" ================================================ FILE: ct/bookstack.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (Canbiz) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/BookStackApp/BookStack APP="Bookstack" var_tags="${var_tags:-organizer}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-1024}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/bookstack ]]; then msg_error "No ${APP} Installation Found!" exit fi setup_mariadb ensure_dependencies git if check_for_gh_release "bookstack" "BookStackApp/BookStack"; then msg_info "Stopping Apache2" systemctl stop apache2 msg_ok "Services Stopped" msg_info "Backing up data" mv /opt/bookstack /opt/bookstack-backup msg_ok "Backup finished" fetch_and_deploy_gh_release "bookstack" "BookStackApp/BookStack" "tarball" PHP_VERSION="8.3" PHP_APACHE="YES" PHP_FPM="YES" PHP_MODULE="ldap,tidy,mysqli" setup_php setup_composer msg_info "Restoring backup" cp /opt/bookstack-backup/.env /opt/bookstack/.env [[ -d /opt/bookstack-backup/public/uploads ]] && cp -a /opt/bookstack-backup/public/uploads/. /opt/bookstack/public/uploads/ [[ -d /opt/bookstack-backup/storage/uploads ]] && cp -a /opt/bookstack-backup/storage/uploads/. /opt/bookstack/storage/uploads/ [[ -d /opt/bookstack-backup/themes ]] && cp -a /opt/bookstack-backup/themes/. /opt/bookstack/themes/ msg_ok "Backup restored" msg_info "Configuring BookStack" cd /opt/bookstack export COMPOSER_ALLOW_SUPERUSER=1 $STD /usr/local/bin/composer install --no-dev $STD php artisan migrate --force chown www-data:www-data -R /opt/bookstack /opt/bookstack/bootstrap/cache /opt/bookstack/public/uploads /opt/bookstack/storage chmod -R 755 /opt/bookstack /opt/bookstack/bootstrap/cache /opt/bookstack/public/uploads /opt/bookstack/storage chmod -R 775 /opt/bookstack/storage /opt/bookstack/bootstrap/cache /opt/bookstack/public/uploads chmod -R 640 /opt/bookstack/.env rm -rf /opt/bookstack-backup msg_ok "Configured BookStack" msg_info "Starting Apache2" systemctl start apache2 msg_ok "Started Apache2" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}${CL}" ================================================ FILE: ct/bunkerweb.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.bunkerweb.io/ APP="BunkerWeb" var_tags="${var_tags:-webserver}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-8192}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /etc/bunkerweb ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "bunkerweb" "bunkerity/bunkerweb"; then msg_info "Updating BunkerWeb" RELEASE=$(get_latest_github_release "bunkerity/bunkerweb") cat </etc/apt/preferences.d/bunkerweb Package: bunkerweb Pin: version ${RELEASE} Pin-Priority: 1001 EOF $STD apt update $STD apt-mark unhold bunkerweb nginx $STD apt install -y --allow-downgrades bunkerweb="${RELEASE}" msg_ok "Updated BunkerWeb" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}/setup${CL}" ================================================ FILE: ct/byparr.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: luismco # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/ThePhaseless/Byparr APP="Byparr" var_tags="${var_tags:-proxy}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/Byparr ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "Byparr" "ThePhaseless/Byparr"; then msg_info "Stopping Service" systemctl stop byparr msg_ok "Stopped Service" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "Byparr" "ThePhaseless/Byparr" "tarball" "latest" if ! dpkg -l | grep -q ffmpeg; then msg_info "Installing dependencies" $STD apt install -y --no-install-recommends \ ffmpeg \ libatk1.0-0 \ libcairo-gobject2 \ libcairo2 \ libdbus-glib-1-2 \ libfontconfig1 \ libfreetype6 \ libgdk-pixbuf-xlib-2.0-0 \ libglib2.0-0 \ libgtk-3-0 \ libpango-1.0-0 \ libpangocairo-1.0-0 \ libpangoft2-1.0-0 \ libx11-6 \ libx11-xcb1 \ libxcb-shm0 \ libxcb1 \ libxcomposite1 \ libxcursor1 \ libxdamage1 \ libxext6 \ libxfixes3 \ libxi6 \ libxrender1 \ libxt6 \ libxtst6 \ xvfb \ fonts-noto-color-emoji \ fonts-unifont \ xfonts-cyrillic \ xfonts-scalable \ fonts-liberation \ fonts-ipafont-gothic \ fonts-wqy-zenhei \ fonts-tlwg-loma-otf $STD apt autoremove -y chromium msg_ok "Installed dependencies" fi msg_info "Configuring Byparr" cd /opt/Byparr $STD uv sync --link-mode copy $STD uv run camoufox fetch msg_ok "Configured Byparr" msg_info "Starting Service" systemctl start byparr msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8191${CL}" ================================================ FILE: ct/bytestash.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Slaviša Arežina (tremor021) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/jordan-dalby/ByteStash APP="ByteStash" var_tags="${var_tags:-code}" var_disk="${var_disk:-4}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-1024}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/bytestash ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "bytestash" "jordan-dalby/ByteStash"; then msg_info "Stopping Services" systemctl stop bytestash-backend bytestash-frontend msg_ok "Services Stopped" msg_info "Backing up data" tmp_dir="/opt/bytestash-data-backup" mkdir -p "$tmp_dir" if [[ -d /opt/bytestash/data ]]; then cp -r /opt/bytestash/data "$tmp_dir"/data elif [[ -d /opt/data ]]; then cp -r /opt/data "$tmp_dir"/data fi msg_ok "Data backed up" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "bytestash" "jordan-dalby/ByteStash" "tarball" msg_info "Restoring data" if [[ -d "$tmp_dir"/data ]]; then mkdir -p /opt/bytestash/data cp -r "$tmp_dir"/data/* /opt/bytestash/data/ rm -rf "$tmp_dir" fi msg_ok "Data restored" msg_info "Configuring ByteStash" cd /opt/bytestash/server $STD npm install cd /opt/bytestash/client $STD npm install msg_ok "Updated ByteStash" msg_info "Starting Services" systemctl start bytestash-backend bytestash-frontend msg_ok "Started Services" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:3000${CL}" ================================================ FILE: ct/caddy.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://caddyserver.com/ APP="Caddy" var_tags="${var_tags:-webserver}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-512}" var_disk="${var_disk:-6}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /etc/caddy ]]; then msg_error "No ${APP} Installation Found!" exit fi msg_info "Updating Caddy LXC" $STD apt update $STD apt upgrade -y msg_ok "Updated Caddy LXC" if command -v xcaddy >/dev/null 2>&1; then if check_for_gh_release "xcaddy" "caddyserver/xcaddy"; then setup_go fetch_and_deploy_gh_release "xcaddy" "caddyserver/xcaddy" "binary" msg_info "Updating xCaddy" $STD xcaddy build msg_ok "Updated xCaddy" fi fi msg_ok "Updated successfully!" exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:80${CL}" ================================================ FILE: ct/calibre-web.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: mikolaj92 # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/janeczku/calibre-web APP="calibre-web" var_tags="${var_tags:-media;books}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-8}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/calibre-web ]]; then msg_error "No Calibre-Web Installation Found!" exit fi if check_for_gh_release "Calibre-Web" "janeczku/calibre-web"; then msg_info "Stopping Service" systemctl stop calibre-web msg_ok "Stopped Service" msg_info "Backing up Data" cp -r /opt/calibre-web/app.db /opt/app.db_backup cp -r /opt/calibre-web/data /opt/data_backup msg_ok "Backed up Data" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "Calibre-Web" "janeczku/calibre-web" "prebuild" "latest" "/opt/calibre-web" "calibre-web*.tar.gz" setup_uv msg_info "Installing Dependencies" cd /opt/calibre-web $STD uv venv --clear /opt/calibre-web/.venv $STD uv pip install --python /opt/calibre-web/.venv/bin/python --no-cache-dir --upgrade pip setuptools wheel $STD uv pip install --python /opt/calibre-web/.venv/bin/python --no-cache-dir -r requirements.txt msg_ok "Installed Dependencies" msg_info "Restoring Data" cp /opt/app.db_backup /opt/calibre-web/app.db 2>/dev/null cp -r /opt/data_backup /opt/calibre-web/data 2>/dev/null rm -rf /opt/app.db_backup /opt/data_backup msg_ok "Restored Data" msg_info "Starting Service" systemctl start calibre-web msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8083${CL}" ================================================ FILE: ct/casaos.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://casaos.io/ APP="CasaOS" var_tags="${var_tags:-cloud}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-8}" var_os="${var_os:-debian}" var_version="${var_version:-12}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /var ]]; then msg_error "No ${APP} Installation Found!" exit fi msg_info "Updating ${APP} LXC" $STD apt-get update $STD apt-get -y upgrade msg_ok "Updated ${APP} LXC" msg_ok "Updated successfully!" exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}${CL}" ================================================ FILE: ct/changedetection.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://changedetection.io/ APP="Change Detection" var_tags="${var_tags:-monitoring;crawler}" var_cpu="${var_cpu:-4}" var_ram="${var_ram:-4096}" var_disk="${var_disk:-10}" var_os="${var_os:-debian}" var_version="${var_version:-12}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -f /etc/systemd/system/changedetection.service ]]; then msg_error "No ${APP} Installation Found!" exit fi ensure_dependencies libjpeg-dev NODE_VERSION="24" setup_nodejs msg_info "Updating ${APP}" $STD pip3 install changedetection.io --upgrade --break-system-packages --ignore-installed typing_extensions msg_ok "Updated ${APP}" msg_info "Updating Playwright" $STD pip3 install playwright --upgrade --break-system-packages msg_ok "Updated Playwright" if [[ -f /etc/systemd/system/browserless.service ]]; then msg_info "Updating Browserless (Patience)" $STD git -C /opt/browserless/ fetch --all $STD git -C /opt/browserless/ reset --hard origin/main $STD npm update --prefix /opt/browserless $STD npm ci --include=optional --include=dev --prefix /opt/browserless $STD /opt/browserless/node_modules/playwright-core/cli.js install --with-deps # Update Chrome separately, as it has to be done with the force option. Otherwise the installation of other browsers will not be done if Chrome is already installed. $STD /opt/browserless/node_modules/playwright-core/cli.js install --force chrome $STD /opt/browserless/node_modules/playwright-core/cli.js install --force msedge $STD /opt/browserless/node_modules/playwright-core/cli.js install chromium firefox webkit $STD npm install --prefix /opt/browserless esbuild typescript ts-node @types/node --save-dev $STD npm run build --prefix /opt/browserless $STD npm run build:function --prefix /opt/browserless $STD npm prune production --prefix /opt/browserless systemctl restart browserless msg_ok "Updated Browserless" else msg_error "No Browserless Installation Found!" fi systemctl restart changedetection msg_ok "Updated successfully!" exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:5000${CL}" ================================================ FILE: ct/channels.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://getchannels.com/dvr-server/ APP="Channels" var_tags="${var_tags:-dvr}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-1024}" var_disk="${var_disk:-8}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-0}" var_gpu="${var_gpu:-yes}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/channels-dvr ]]; then msg_error "No ${APP} Installation Found!" exit fi msg_error "Currently we don't provide an update function for this ${APP}." exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8089${CL}" ================================================ FILE: ct/checkmate.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/bluewave-labs/Checkmate APP="Checkmate" var_tags="${var_tags:-monitoring;uptime}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-4096}" var_disk="${var_disk:-10}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/checkmate ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "checkmate" "bluewave-labs/Checkmate"; then msg_info "Stopping Services" systemctl stop checkmate-server checkmate-client nginx msg_ok "Stopped Services" msg_info "Backing up Data" cp /opt/checkmate/server/.env /opt/checkmate_server.env.bak [ -f /opt/checkmate/client/.env.local ] && cp /opt/checkmate/client/.env.local /opt/checkmate_client.env.local.bak msg_ok "Backed up Data" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "checkmate" "bluewave-labs/Checkmate" "tarball" msg_info "Updating Checkmate Server" cd /opt/checkmate/server $STD npm install if [ -f package.json ]; then grep -q '"build"' package.json && $STD npm run build || true fi msg_ok "Updated Checkmate Server" msg_info "Updating Checkmate Client" cd /opt/checkmate/client $STD npm install VITE_APP_API_BASE_URL="/api/v1" UPTIME_APP_API_BASE_URL="/api/v1" VITE_APP_LOG_LEVEL="warn" $STD npm run build msg_ok "Updated Checkmate Client" msg_info "Restoring Data" mv /opt/checkmate_server.env.bak /opt/checkmate/server/.env [ -f /opt/checkmate_client.env.local.bak ] && mv /opt/checkmate_client.env.local.bak /opt/checkmate/client/.env.local msg_ok "Restored Data" msg_info "Starting Services" systemctl start checkmate-server checkmate-client nginx msg_ok "Started Services" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}${CL}" ================================================ FILE: ct/checkmk.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Michel Roegl-Brunner (michelroegl-brunner) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://checkmk.com/ APP="checkmk" var_tags="${var_tags:-monitoring}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-6}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if ! command -v omd &>/dev/null; then msg_error "No ${APP} Installation Found!" exit fi RELEASE=$(curl_with_retry "https://api.github.com/repos/checkmk/checkmk/tags" "-" | grep "name" | awk '{print substr($2, 3, length($2)-4) }' | tr ' ' '\n' | grep -Ev 'rc|b' | sort -V | tail -n 1) RELEASE="${RELEASE%%+*}" msg_info "Updating checkmk" $STD omd stop monitoring $STD omd cp monitoring monitoringbackup curl -fsSL "https://download.checkmk.com/checkmk/${RELEASE}/check-mk-raw-${RELEASE}_0.bookworm_arm64.deb" -o "/opt/checkmk.deb" $STD apt-get install -y /opt/checkmk.deb $STD omd --force -V ${RELEASE}.cre update --conflict=install monitoring $STD omd start monitoring $STD omd -f rm monitoringbackup $STD omd cleanup rm -rf /opt/checkmk.deb msg_ok "Updated checkmk" msg_ok "Updated successfully!" exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}/monitoring${CL}" ================================================ FILE: ct/cleanuparr.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Lucas Zampieri (zampierilucas) | MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/Cleanuparr/Cleanuparr APP="Cleanuparr" var_tags="${var_tags:-arr}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-1024}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -f /opt/cleanuparr/Cleanuparr ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "cleanuparr" "Cleanuparr/Cleanuparr"; then msg_info "Stopping Service" systemctl stop cleanuparr msg_ok "Stopped Service" msg_info "Backing up config" cp -r /opt/cleanuparr/config /opt/cleanuparr_config_backup msg_ok "Backed up config" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "Cleanuparr" "Cleanuparr/Cleanuparr" "prebuild" "latest" "/opt/cleanuparr" "*linux-arm64.zip" msg_info "Restoring config" [[ -d /opt/cleanuparr/config ]] && rm -rf /opt/cleanuparr/config mv /opt/cleanuparr_config_backup /opt/cleanuparr/config msg_ok "Restored config" msg_info "Starting Service" systemctl start cleanuparr msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:11011${CL}" ================================================ FILE: ct/cloudflare-ddns.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: edoardop13 # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/favonia/cloudflare-ddns APP="Cloudflare-DDNS" var_tags="${var_tags:-network}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-1024}" var_disk="${var_disk:-3}" var_os="${var_os:-debian}" var_version="${var_version:-12}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -f /etc/systemd/system/cloudflare-ddns.service ]]; then msg_error "No ${APP} Installation Found!" exit fi msg_error "There is no update function for ${APP}." exit } start build_container description msg_ok "Completed successfully!\n" ================================================ FILE: ct/cloudflared.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.cloudflare.com/ APP="Cloudflared" var_tags="${var_tags:-network;cloudflare}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-512}" var_disk="${var_disk:-2}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -f /etc/systemd/system/cloudflared.service ]]; then msg_error "No ${APP} Installation Found!" exit fi msg_info "Updating $APP LXC" $STD apt update $STD apt -y upgrade msg_ok "Updated $APP LXC" msg_ok "Updated successfully!" exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" ================================================ FILE: ct/cloudreve.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://cloudreve.org/ APP="Cloudreve" var_tags="${var_tags:-cloud}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-1024}" var_disk="${var_disk:-10}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/cloudreve ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "cloudreve" "cloudreve/cloudreve"; then msg_info "Stopping Service" systemctl stop cloudreve msg_info "Stopped Service" fetch_and_deploy_gh_release "cloudreve" "cloudreve/cloudreve" "prebuild" "latest" "/opt/cloudreve" "*linux_arm64.tar.gz" msg_info "Starting Service" systemctl start cloudreve msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:5212${CL}" ================================================ FILE: ct/cockpit.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck | Co-Author: havardthom # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://cockpit-project.org/ APP="Cockpit" var_tags="${var_tags:-monitoring;network}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-1024}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-12}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /etc/cockpit ]]; then msg_error "No ${APP} Installation Found!" exit fi msg_info "Updating ${APP} LXC" $STD apt update $STD apt -y upgrade msg_ok "Updated ${APP} LXC" msg_ok "Updated successfully!" exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:9090${CL}" ================================================ FILE: ct/comfyui.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: jdacode # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/comfyanonymous/ComfyUI APP="ComfyUI" var_tags="${var_tags:-ai}" var_cpu="${var_cpu:-4}" var_ram="${var_ram:-8192}" var_disk="${var_disk:-25}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" var_gpu="${var_gpu:-yes}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/ComfyUI ]]; then msg_error "No ${APP} Installation Found!" exit fi msg_error "To update use the ComfyUI Manager." exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8188${CL}" ================================================ FILE: ct/commafeed.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.commafeed.com/#/welcome APP="CommaFeed" var_tags="${var_tags:-rss-reader}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/commafeed ]]; then msg_error "No ${APP} Installation Found!" exit fi JAVA_VERSION="25" setup_java if check_for_gh_release "commafeed" "Athou/commafeed"; then msg_info "Stopping Service" systemctl stop commafeed msg_ok "Stopped Service" ensure_dependencies rsync if [ -d /opt/commafeed/data ] && [ "$(ls -A /opt/commafeed/data)" ]; then msg_info "Backing up existing data" mv /opt/commafeed/data /opt/data.bak msg_ok "Backed up existing data" fi CLEAN_INSTALL=1 fetch_and_deploy_gh_release "commafeed" "Athou/commafeed" "prebuild" "latest" "/opt/commafeed" "commafeed-*-h2-jvm.zip" if [ -d /opt/data.bak ] && [ "$(ls -A /opt/data.bak)" ]; then msg_info "Restoring data" mv /opt/data.bak /opt/commafeed/data msg_ok "Restored data" fi msg_info "Starting Service" systemctl start commafeed msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8082${CL}" ================================================ FILE: ct/configarr.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: finkerle # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/raydak-labs/configarr APP="Configarr" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-512}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/configarr ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "configarr" "raydak-labs/configarr"; then apt-get install -y libicu76 &>/dev/null msg_info "Stopping Service" systemctl stop configarr-task.timer msg_ok "Stopped Service" mkdir -p /opt/backup/ mv /opt/configarr/{config.yml,secrets.yml,.env} /opt/backup/ CLEAN_INSTALL=1 fetch_and_deploy_gh_release "configarr" "raydak-labs/configarr" "prebuild" "latest" "/opt/configarr" "configarr-linux-arm64.tar.xz" mv /opt/backup/{config.yml,secrets.yml,.env} /opt/configarr/ rm -rf /opt/backup msg_info "Starting Service" systemctl start configarr-task.timer msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL (no web-ui):${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8989${CL}" ================================================ FILE: ct/convertx.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Omar Minaya | MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/C4illin/ConvertX APP="ConvertX" var_tags="${var_tags:-converter}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-4096}" var_disk="${var_disk:-20}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" var_gpu="${var_gpu:-yes}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/convertx ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "ConvertX" "C4illin/ConvertX"; then msg_info "Stopping Service" systemctl stop convertx msg_info "Stopped Service" ensure_dependencies libreoffice-writer msg_info "Move data-Folder" if [[ -d /opt/convertx/data ]]; then mv /opt/convertx/data /opt/data fi msg_ok "Moved data-Folder" fetch_and_deploy_gh_release "ConvertX" "C4illin/ConvertX" "tarball" "latest" "/opt/convertx" msg_info "Updating ConvertX" if [[ -d /opt/data ]]; then mv /opt/data /opt/convertx/data fi cd /opt/convertx $STD bun install msg_ok "Updated ConvertX" msg_info "Starting Service" systemctl start convertx msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:3000${CL}" ================================================ FILE: ct/coolify.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://coolify.io/ APP="Coolify" var_tags="${var_tags:-docker;paas}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-4096}" var_disk="${var_disk:-30}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors ADDON_SCRIPT="https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/tools/addon/coolify.sh" function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /data/coolify ]]; then msg_error "No ${APP} Installation Found!" exit fi msg_warn "⚠️ ${APP} has been migrated to an addon script." echo "" msg_info "This is a one-time migration. After this, you can update ${APP} anytime with:" echo -e "${TAB}${TAB}${GN}update_coolify${CL} or ${GN}bash <(curl -fsSL ${ADDON_SCRIPT})${CL}" echo "" read -r -p "${TAB}Migrate update function now? [y/N]: " CONFIRM if [[ ! "${CONFIRM,,}" =~ ^(y|yes)$ ]]; then msg_warn "Migration skipped. The old update will continue to work for now." msg_info "Updating ${APP} (legacy)" $STD bash <(curl -fsSL https://cdn.coollabs.io/coolify/install.sh) msg_ok "Updated ${APP}" exit fi msg_info "Migrating update function" TMP_UPDATE=$(mktemp) cat <<'MIGRATION_EOF' >"$TMP_UPDATE" bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/tools/addon/coolify.sh)" MIGRATION_EOF mv "$TMP_UPDATE" /usr/bin/update chmod +x /usr/bin/update ln -sf /usr/bin/update /usr/bin/update_coolify 2>/dev/null || true msg_ok "Migration complete" msg_info "Running addon update" type=update bash <(curl -fsSL "${ADDON_SCRIPT}") exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8000${CL}" ================================================ FILE: ct/coredns.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/coredns/coredns APP="CoreDNS" var_tags="${var_tags:-dns;network}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-256}" var_disk="${var_disk:-1}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -f /usr/local/bin/coredns ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "coredns" "coredns/coredns"; then msg_info "Stopping Service" systemctl stop coredns msg_ok "Stopped Service" fetch_and_deploy_gh_release "coredns" "coredns/coredns" "prebuild" "latest" "/usr/local/bin" \ "coredns_*_linux_$(dpkg --print-architecture).tgz" chmod +x /usr/local/bin/coredns msg_info "Starting Service" systemctl start coredns msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed Successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} CoreDNS is listening on port 53 (DNS)${CL}" echo -e "${TAB}${GATEWAY}${BGN}dns://${IP}${CL}" ================================================ FILE: ct/cosmos.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Michel Roegl-Brunner (michelroegl-brunner) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://https://cosmos-cloud.io/ APP="Cosmos" var_tags="${var_tags:-cloud;docker}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-8}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" var_fuse="${var_fuse:-yes}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/cosmos ]]; then msg_error "No ${APP} Installation Found!" exit fi msg_ok "${APP} updates itself automatically!" exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}${CL}" ================================================ FILE: ct/crafty-controller.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: CrazyWolf13 # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://gitlab.com/crafty-controller/crafty-4 APP="Crafty-Controller" var_tags="${var_tags:-gaming}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-4096}" var_disk="${var_disk:-16}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/crafty-controller ]]; then msg_error "No ${APP} Installation Found!" exit fi RELEASE=$(curl -fsSL "https://gitlab.com/api/v4/projects/20430749/releases" | grep -o '"tag_name":"v[^"]*"' | head -n 1 | sed 's/"tag_name":"v//;s/"//') if [[ ! -f /opt/crafty-controller_version.txt ]] || [[ "${RELEASE}" != "$(cat /opt/crafty-controller_version.txt)" ]]; then msg_info "Stopping Crafty-Controller" systemctl stop crafty-controller msg_ok "Stopped Crafty-Controller" msg_info "Creating Backup of config" cp -a /opt/crafty-controller/crafty/crafty-4/app/config/. /opt/crafty-controller/backup rm /opt/crafty-controller/backup/version.json rm /opt/crafty-controller/backup/credits.json rm /opt/crafty-controller/backup/logging.json rm /opt/crafty-controller/backup/default.json.example rm /opt/crafty-controller/backup/motd_format.json msg_ok "Backup Created" msg_info "Updating Crafty-Controller to v${RELEASE}" curl -fsSL "https://gitlab.com/crafty-controller/crafty-4/-/archive/v${RELEASE}/crafty-4-v${RELEASE}.zip" -o $(basename "https://gitlab.com/crafty-controller/crafty-4/-/archive/v${RELEASE}/crafty-4-v${RELEASE}.zip") $STD unzip crafty-4-v"${RELEASE}".zip cp -a crafty-4-v"${RELEASE}"/. /opt/crafty-controller/crafty/crafty-4/ rm -rf crafty-4-v"${RELEASE}" cd /opt/crafty-controller/crafty/crafty-4 sudo -u crafty bash -c ' source /opt/crafty-controller/crafty/.venv/bin/activate pip3 install --no-cache-dir -r requirements.txt ' &>/dev/null echo "${RELEASE}" >"/opt/crafty-controller_version.txt" msg_ok "Updated Crafty-Controller to v${RELEASE}" msg_info "Restoring Backup of config" cp -a /opt/crafty-controller/backup/. /opt/crafty-controller/crafty/crafty-4/app/config rm -rf /opt/crafty-controller/backup chown -R crafty:crafty /opt/crafty-controller/ msg_ok "Backup Restored" msg_info "Starting Crafty-Controller" systemctl start crafty-controller msg_ok "Started Crafty-Controller" msg_ok "Updated successfully!" else msg_ok "No update required. ${APP} is already at v${RELEASE}" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}https://${IP}:8443${CL}" ================================================ FILE: ct/cronicle.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://cronicle.net/ APP="Cronicle" var_tags="${var_tags:-task-scheduler}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-512}" var_disk="${var_disk:-2}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources UPD=$(msg_menu "Cronicle Update Options" \ "1" "Update ${APP}" \ "2" "Install ${APP} Worker") if [ "$UPD" == "1" ]; then if [[ ! -d /opt/cronicle ]]; then msg_error "No ${APP} Installation Found!" exit fi NODE_VERSION="22" setup_nodejs msg_info "Updating Cronicle" $STD /opt/cronicle/bin/control.sh upgrade msg_ok "Updated Cronicle" exit fi if [ "$UPD" == "2" ]; then NODE_VERSION="22" setup_nodejs if check_for_gh_release "cronicle" "jhuckaby/Cronicle"; then msg_info "Installing Dependencies" ensure_dependencies git build-essential ca-certificates msg_ok "Installed Dependencies" NODE_VERSION="22" setup_nodejs fetch_and_deploy_gh_release "cronicle" "jhuckaby/Cronicle" "tarball" msg_info "Configuring Cronicle Worker" cd /opt/cronicle $STD npm install $STD node bin/build.js dist sed -i "s/localhost:3012/${LOCAL_IP}:3012/g" /opt/cronicle/conf/config.json $STD /opt/cronicle/bin/control.sh start msg_ok "Installed Cronicle Worker" echo -e "\n Add Masters secret key to /opt/cronicle/conf/config.json \n" msg_ok "Updated successfully!" exit fi fi } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:3012${CL}" ================================================ FILE: ct/cross-seed.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Jakub Matraszek (jmatraszek) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.cross-seed.org APP="cross-seed" var_tags="${var_tags:-arr}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-1024}" var_disk="${var_disk:-2}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources NODE_VERSION="24" setup_nodejs ensure_dependencies build-essential if command -v cross-seed &>/dev/null; then current_version=$(cross-seed --version) latest_version=$(npm show cross-seed version) if [ "$current_version" != "$latest_version" ]; then msg_info "Updating cross-seed from version v${current_version} to v${latest_version}" $STD npm install -g cross-seed@latest systemctl restart cross-seed msg_ok "Updated successfully!" else msg_ok "cross-seed is already at v${current_version}" fi else msg_error "No cross-seed Installation Found!" exit fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access cross-seed API using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:2468${CL}" ================================================ FILE: ct/cryptpad.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Slaviša Arežina (tremor021) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/cryptpad/cryptpad APP="CryptPad" var_tags="${var_tags:-docs;office}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-1024}" var_disk="${var_disk:-8}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d "/opt/cryptpad" ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "cryptpad" "cryptpad/cryptpad"; then msg_info "Stopping Service" systemctl stop cryptpad msg_info "Stopped Service" msg_info "Creating backup" [ -f /opt/cryptpad/config/config.js ] && mv /opt/cryptpad/config/config.js /opt/ for dir in blob block customize data datastore www/common/onlyoffice/dist onlyoffice-conf; do [ -d "/opt/cryptpad/${dir}" ] && mv "/opt/cryptpad/${dir}" "/tmp/cryptpad_${dir//\//_}" done msg_ok "Created backup" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "cryptpad" "cryptpad/cryptpad" "tarball" msg_info "Restoring backup" mv /opt/config.js /opt/cryptpad/config/ for dir in blob block customize data datastore www/common/onlyoffice/dist onlyoffice-conf; do [ -d "/tmp/cryptpad_${dir//\//_}" ] && mv "/tmp/cryptpad_${dir//\//_}" "/opt/cryptpad/${dir}" done msg_ok "Restored backup" msg_info "Updating CryptPad" cd /opt/cryptpad $STD npm ci $STD npm run install:components if [ -f "/opt/cryptpad/install-onlyoffice.sh" ]; then $STD bash /opt/cryptpad/install-onlyoffice.sh --accept-license fi $STD npm run build msg_ok "Updated CryptaPad" msg_info "Starting Service" systemctl start cryptpad msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:3000${CL}" ================================================ FILE: ct/dagu.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://dagu.sh/ APP="Dagu" var_tags="${var_tags:-automation;workflow;scheduler}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-512}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -f /opt/dagu/dagu ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "dagu" "dagucloud/dagu"; then msg_info "Stopping Service" systemctl stop dagu msg_ok "Stopped Service" msg_info "Backing up Data" cp -r /opt/dagu/data /opt/dagu_data_backup msg_ok "Backed up Data" fetch_and_deploy_gh_release "dagu" "dagucloud/dagu" "prebuild" "latest" "/opt/dagu" "dagu_*_linux_amd64.tar.gz" msg_info "Restoring Data" mkdir -p /opt/dagu/data cp -r /opt/dagu_data_backup/. /opt/dagu/data rm -rf /opt/dagu_data_backup msg_ok "Restored Data" msg_info "Starting Service" systemctl start dagu msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed Successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8080${CL}" ================================================ FILE: ct/dashy.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: tteck (tteckster) | Co-Author: CrazyWolf13 # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://dashy.to/ APP="Dashy" var_tags="${var_tags:-dashboard}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-6}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/dashy/public/ ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "dashy" "Lissy93/dashy"; then msg_info "Stopping Service" systemctl stop dashy msg_ok "Stopped Service" msg_info "Backing up conf.yml" if [[ -f /opt/dashy/public/conf.yml ]]; then cp -R /opt/dashy/public/conf.yml /opt/dashy_conf_backup.yml else cp -R /opt/dashy/user-data/conf.yml /opt/dashy_conf_backup.yml fi msg_ok "Backed up conf.yml" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "dashy" "Lissy93/dashy" "prebuild" "latest" "/opt/dashy" "dashy-*.tar.gz" msg_info "Updating Dashy" cd /opt/dashy $STD yarn install --ignore-engines --network-timeout 300000 msg_ok "Updated Dashy" msg_info "Restoring conf.yml" cp -R /opt/dashy_conf_backup.yml /opt/dashy/user-data msg_ok "Restored conf.yml" msg_info "Cleaning" rm -rf /opt/dashy_conf_backup.yml /opt/dashy/public/conf.yml msg_ok "Cleaned" msg_info "Starting Dashy" systemctl start dashy msg_ok "Started Dashy" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed Successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:4000${CL}" ================================================ FILE: ct/databasus.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/databasus/databasus APP="Databasus" var_tags="${var_tags:-backup;postgresql;database}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-8}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -f /opt/databasus/databasus ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "databasus" "databasus/databasus"; then msg_info "Stopping Databasus" $STD systemctl stop databasus msg_ok "Stopped Databasus" msg_info "Backing up Configuration" [[ ! -f /.env && -f /opt/databasus/.env ]] && cp /opt/databasus/.env /.env chmod 600 /.env cp /.env /opt/databasus.env.bak chmod 600 /opt/databasus.env.bak msg_ok "Backed up Configuration" msg_info "Ensuring Database Clients" # Create PostgreSQL version symlinks for compatibility for v in 12 13 14 15 16 18; do ln -sf /usr/lib/postgresql/17 /usr/lib/postgresql/$v done # Install MongoDB Database Tools via direct .deb (no APT repo for Debian 13) if ! command -v mongodump &>/dev/null; then [[ "$(get_os_info id)" == "ubuntu" ]] && MONGO_DIST="ubuntu2204" || MONGO_DIST="debian12" fetch_and_deploy_from_url "https://fastdl.mongodb.org/tools/db/mongodb-database-tools-${MONGO_DIST}-x86_64-100.16.1.deb" fi [[ -f /usr/bin/mongodump ]] && ln -sf /usr/bin/mongodump /usr/local/mongodb-database-tools/bin/mongodump [[ -f /usr/bin/mongorestore ]] && ln -sf /usr/bin/mongorestore /usr/local/mongodb-database-tools/bin/mongorestore # Create MariaDB and MySQL client symlinks for compatibility ensure_dependencies mariadb-client mkdir -p /usr/local/mariadb-{10.6,12.1}/bin /usr/local/mysql-{5.7,8.0,8.4,9}/bin /usr/local/mongodb-database-tools/bin for dir in /usr/local/mariadb-{10.6,12.1}/bin; do ln -sf /usr/bin/mariadb-dump "$dir/mariadb-dump" ln -sf /usr/bin/mariadb "$dir/mariadb" done for dir in /usr/local/mysql-{5.7,8.0,8.4,9}/bin; do ln -sf /usr/bin/mariadb-dump "$dir/mysqldump" ln -sf /usr/bin/mariadb "$dir/mysql" done msg_ok "Ensured Database Clients" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "databasus" "databasus/databasus" "tarball" "latest" "/opt/databasus" msg_info "Updating Databasus" export COREPACK_ENABLE_DOWNLOAD_PROMPT=0 cd /opt/databasus/frontend $STD corepack enable $STD corepack prepare pnpm@latest --activate $STD pnpm install --frozen-lockfile $STD pnpm run build cd /opt/databasus/backend $STD go mod download $STD /root/go/bin/swag init -g cmd/main.go -o swagger $STD env CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o databasus ./cmd/main.go mv /opt/databasus/backend/databasus /opt/databasus/databasus mkdir -p /opt/databasus/ui/build cp -r /opt/databasus/frontend/dist/* /opt/databasus/ui/build/ cp -r /opt/databasus/backend/migrations /opt/databasus/ chown -R postgres:postgres /opt/databasus msg_ok "Updated Databasus" msg_info "Restoring Configuration" cp /opt/databasus.env.bak /.env rm -f /opt/databasus.env.bak chmod 600 /.env msg_ok "Restored Configuration" if ! grep -q "EnvironmentFile=/.env" /etc/systemd/system/databasus.service; then msg_info "Updating Service" sed -i 's|EnvironmentFile=.*|EnvironmentFile=/.env|' /etc/systemd/system/databasus.service $STD systemctl daemon-reload msg_ok "Updated Service" fi msg_info "Starting Databasus" $STD systemctl start databasus msg_ok "Started Databasus" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}${CL}" ================================================ FILE: ct/dawarich.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/Freika/dawarich APP="Dawarich" var_tags="${var_tags:-location;tracking;gps}" var_cpu="${var_cpu:-4}" var_ram="${var_ram:-4096}" var_disk="${var_disk:-15}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/dawarich ]]; then msg_error "No ${APP} Installation Found!" exit fi ensure_dependencies libgeos++-dev libxml2-dev libxslt-dev libjemalloc-dev if check_for_gh_release "dawarich" "Freika/dawarich"; then msg_info "Stopping Services" systemctl stop dawarich-web dawarich-worker msg_ok "Stopped Services" msg_info "Backing up Data" cp -r /opt/dawarich/app/storage /opt/dawarich_storage_backup 2>/dev/null || true cp /opt/dawarich/app/config/master.key /opt/dawarich_master.key 2>/dev/null || true cp /opt/dawarich/app/config/credentials.yml.enc /opt/dawarich_credentials.yml.enc 2>/dev/null || true msg_ok "Backed up Data" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "dawarich" "Freika/dawarich" "tarball" "latest" "/opt/dawarich/app" RUBY_VERSION=$(cat /opt/dawarich/app/.ruby-version 2>/dev/null || echo "3.4.6") RUBY_VERSION=${RUBY_VERSION} RUBY_INSTALL_RAILS="false" setup_ruby msg_info "Running Migrations" cd /opt/dawarich/app source /root/.profile export PATH="/root/.rbenv/shims:/root/.rbenv/bin:$PATH" eval "$(/root/.rbenv/bin/rbenv init - bash)" if ! grep -q "OTP_ENCRYPTION_PRIMARY_KEY" /opt/dawarich/.env; then echo "OTP_ENCRYPTION_PRIMARY_KEY=$(openssl rand -hex 64)" >>/opt/dawarich/.env fi if ! grep -q "OTP_ENCRYPTION_DETERMINISTIC_KEY" /opt/dawarich/.env; then echo "OTP_ENCRYPTION_DETERMINISTIC_KEY=$(openssl rand -hex 64)" >>/opt/dawarich/.env fi if ! grep -q "OTP_ENCRYPTION_KEY_DERIVATION_SALT" /opt/dawarich/.env; then echo "OTP_ENCRYPTION_KEY_DERIVATION_SALT=$(openssl rand -hex 64)" >>/opt/dawarich/.env fi set -a && source /opt/dawarich/.env && set +a $STD bundle config set --local deployment 'true' $STD bundle config set --local without 'development test' $STD bundle install if [[ -f /opt/dawarich/package.json ]]; then cd /opt/dawarich $STD npm install cd /opt/dawarich/app elif [[ -f /opt/dawarich/app/package.json ]]; then $STD npm install fi $STD bundle exec rails db:migrate $STD bundle exec rake assets:precompile $STD bundle exec rake data:migrate msg_ok "Ran Migrations" msg_info "Restoring Data" cp -r /opt/dawarich_storage_backup/. /opt/dawarich/app/storage/ 2>/dev/null || true cp /opt/dawarich_master.key /opt/dawarich/app/config/master.key 2>/dev/null || true cp /opt/dawarich_credentials.yml.enc /opt/dawarich/app/config/credentials.yml.enc 2>/dev/null || true rm -rf /opt/dawarich_storage_backup /opt/dawarich_master.key /opt/dawarich_credentials.yml.enc msg_ok "Restored Data" msg_info "Starting Services" systemctl start dawarich-web dawarich-worker msg_ok "Started Services" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed Successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:3000${CL}" ================================================ FILE: ct/ddclient.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: mitchscobell # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://ddclient.net/ APP="ddclient" var_tags="${var_tags:-network}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-512}" var_disk="${var_disk:-2}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -f /etc/ddclient.conf ]]; then msg_error "No ${APP} Installation Found!" exit fi msg_info "Updating ddclient" $STD apt update $STD apt install --only-upgrade -y ddclient $STD systemctl restart ddclient msg_ok "Updated ddclient" msg_ok "Updated successfully!" exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" ================================================ FILE: ct/debian.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.debian.org/ APP="Debian" var_tags="${var_tags:-os}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-512}" var_disk="${var_disk:-2}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /var ]]; then msg_error "No ${APP} Installation Found!" exit fi msg_info "Updating $APP LXC" $STD apt update $STD apt -y upgrade msg_ok "Updated $APP LXC" msg_ok "Updated successfully!" exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" ================================================ FILE: ct/deconz.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.phoscon.de/en/conbee2/software#deconz APP="deCONZ" var_tags="${var_tags:-zigbee}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-1024}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-0}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -f /etc/apt/sources.list.d/deconz.list && ! -f /etc/apt/sources.list.d/deconz.sources ]]; then msg_error "No ${APP} Installation Found!" exit fi msg_info "Updating deCONZ" $STD apt update $STD apt upgrade -y msg_ok "Updated deCONZ" msg_ok "Updated successfully!" exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}${CL}" ================================================ FILE: ct/deluge.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.deluge-torrent.org/ APP="Deluge" var_tags="${var_tags:-torrent}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -f /etc/systemd/system/deluged.service ]]; then msg_error "No ${APP} Installation Found!" exit fi msg_info "Updating Deluge" ensure_dependencies python3-setuptools $STD apt update $STD pip3 install deluge[all] --upgrade msg_ok "Updated Deluge" msg_ok "Updated successfully!" exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8112${CL}" ================================================ FILE: ct/discopanel.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: DragoQC # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://discopanel.app/ APP="DiscoPanel" var_tags="${var_tags:-gaming}" var_cpu="${var_cpu:-4}" var_ram="${var_ram:-4096}" var_disk="${var_disk:-15}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d "/opt/discopanel" ]]; then msg_error "No ${APP} Installation Found!" exit fi setup_docker if check_for_gh_release "discopanel" "nickheyer/discopanel"; then msg_info "Stopping Service" systemctl stop discopanel msg_ok "Stopped Service" msg_info "Creating Backup" mkdir -p /opt/discopanel_backup_temp cp /opt/discopanel/data/discopanel.db /opt/discopanel_backup_temp/discopanel.db msg_ok "Created Backup" fetch_and_deploy_gh_release "discopanel" "nickheyer/discopanel" "prebuild" "latest" "/opt/discopanel" "discopanel-linux-amd64.tar.gz" ln -sf /opt/discopanel/discopanel-linux-amd64 /opt/discopanel/discopanel msg_info "Restoring Data" mkdir -p /opt/discopanel/data mv /opt/discopanel_backup_temp/discopanel.db /opt/discopanel/data/discopanel.db rm -rf /opt/discopanel_backup_temp msg_ok "Restored Data" msg_info "Starting Service" systemctl start discopanel msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8080${CL}" ================================================ FILE: ct/dispatcharr.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: ekke85 | MickLesk # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/Dispatcharr/Dispatcharr APP="Dispatcharr" var_tags="${var_tags:-media;arr}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-8}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" var_gpu="${var_gpu:-yes}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d "/opt/dispatcharr" ]]; then msg_error "No ${APP} Installation Found!" exit fi setup_uv NODE_VERSION="24" setup_nodejs # Fix for nginx not allowing large files if ! grep -q "client_max_body_size 100M;" /etc/nginx/sites-available/dispatcharr.conf; then sed -i '/server_name _;/a \ client_max_body_size 100M;' /etc/nginx/sites-available/dispatcharr.conf systemctl reload nginx fi ensure_dependencies vlc-bin vlc-plugin-base if check_for_gh_release "Dispatcharr" "Dispatcharr/Dispatcharr"; then msg_info "Stopping Services" systemctl stop dispatcharr-celery systemctl stop dispatcharr-celerybeat systemctl stop dispatcharr-daphne systemctl stop dispatcharr msg_ok "Stopped Services" msg_info "Creating Backup" BACKUP_FILE="/opt/dispatcharr_backup_$(date +%F_%H-%M-%S).tar.gz" if [[ -f /opt/dispatcharr/.env ]]; then cp /opt/dispatcharr/.env /tmp/dispatcharr.env.backup fi if [[ -f /opt/dispatcharr/start-gunicorn.sh ]]; then cp /opt/dispatcharr/start-gunicorn.sh /tmp/start-gunicorn.sh.backup fi if [[ -f /opt/dispatcharr/start-celery.sh ]]; then cp /opt/dispatcharr/start-celery.sh /tmp/start-celery.sh.backup fi if [[ -f /opt/dispatcharr/start-celerybeat.sh ]]; then cp /opt/dispatcharr/start-celerybeat.sh /tmp/start-celerybeat.sh.backup fi if [[ -f /opt/dispatcharr/start-daphne.sh ]]; then cp /opt/dispatcharr/start-daphne.sh /tmp/start-daphne.sh.backup fi if [[ -f /opt/dispatcharr/.env ]]; then set -o allexport source /opt/dispatcharr/.env set +o allexport if [[ -n "$POSTGRES_DB" ]] && [[ -n "$POSTGRES_USER" ]] && [[ -n "$POSTGRES_PASSWORD" ]]; then PGPASSWORD=$POSTGRES_PASSWORD pg_dump -U "$POSTGRES_USER" -h "${POSTGRES_HOST:-localhost}" -p "${POSTGRES_PORT:-5432}" "$POSTGRES_DB" >/tmp/dispatcharr_db_$(date +%F).sql msg_info "Database backup created" fi fi $STD tar -czf "$BACKUP_FILE" -C /opt dispatcharr /tmp/dispatcharr_db_*.sql msg_ok "Backup created: $BACKUP_FILE" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "dispatcharr" "Dispatcharr/Dispatcharr" "tarball" msg_info "Updating Dispatcharr Backend" if [[ -f /tmp/dispatcharr.env.backup ]]; then mv /tmp/dispatcharr.env.backup /opt/dispatcharr/.env fi if [[ -f /tmp/start-gunicorn.sh.backup ]]; then mv /tmp/start-gunicorn.sh.backup /opt/dispatcharr/start-gunicorn.sh fi if [[ -f /tmp/start-celery.sh.backup ]]; then mv /tmp/start-celery.sh.backup /opt/dispatcharr/start-celery.sh fi if [[ -f /tmp/start-celerybeat.sh.backup ]]; then mv /tmp/start-celerybeat.sh.backup /opt/dispatcharr/start-celerybeat.sh fi if [[ -f /tmp/start-daphne.sh.backup ]]; then mv /tmp/start-daphne.sh.backup /opt/dispatcharr/start-daphne.sh fi if ! grep -q "DJANGO_SECRET_KEY" /opt/dispatcharr/.env; then DJANGO_SECRET=$(openssl rand -base64 48 | tr -dc 'a-zA-Z0-9' | cut -c1-50) echo "DJANGO_SECRET_KEY=$DJANGO_SECRET" >>/opt/dispatcharr/.env fi cd /opt/dispatcharr rm -rf .venv $STD uv venv --clear $STD uv sync $STD uv pip install gunicorn gevent celery redis daphne msg_ok "Updated Dispatcharr Backend" msg_info "Building Frontend" cd /opt/dispatcharr/frontend node -e "const p=require('./package.json');p.overrides=p.overrides||{};p.overrides['webworkify-webpack']='2.1.3';require('fs').writeFileSync('package.json',JSON.stringify(p,null,2));" rm -f package-lock.json $STD npm install --no-audit --progress=false $STD npm run build msg_ok "Built Frontend" msg_info "Running Django Migrations" cd /opt/dispatcharr if [[ -f .env ]]; then set -o allexport source .env set +o allexport fi $STD uv run python manage.py migrate --noinput $STD uv run python manage.py collectstatic --noinput rm -f /tmp/dispatcharr_db_*.sql msg_ok "Migrations Complete" msg_info "Starting Services" systemctl start dispatcharr systemctl start dispatcharr-celery systemctl start dispatcharr-celerybeat systemctl start dispatcharr-daphne msg_ok "Started Services" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:9191${CL}" ================================================ FILE: ct/docker.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.docker.com/ APP="Docker" var_tags="${var_tags:-docker}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources msg_info "Updating base system" $STD apt update $STD apt upgrade -y msg_ok "Base system updated" msg_info "Updating Docker Engine" $STD apt install --only-upgrade -y docker-ce docker-ce-cli containerd.io docker-compose-plugin docker-buildx-plugin msg_ok "Docker Engine updated" if docker ps -a --format '{{.Image}}' | grep -q '^portainer/portainer-ce:latest$'; then msg_info "Updating Portainer" $STD docker pull portainer/portainer-ce:latest $STD docker stop portainer $STD docker rm portainer $STD docker volume create portainer_data >/dev/null 2>&1 $STD docker run -d \ -p 8000:8000 \ -p 9443:9443 \ --name=portainer \ --restart=always \ -v /var/run/docker.sock:/var/run/docker.sock \ -v portainer_data:/data \ portainer/portainer-ce:latest msg_ok "Updated Portainer" fi if docker ps -a --format '{{.Names}}' | grep -q '^portainer_agent$'; then msg_info "Updating Portainer Agent" $STD docker pull portainer/agent:latest $STD docker stop portainer_agent $STD docker rm portainer_agent $STD docker run -d \ -p 9001:9001 \ --name=portainer_agent \ --restart=always \ -v /var/run/docker.sock:/var/run/docker.sock \ -v /var/lib/docker/volumes:/var/lib/docker/volumes \ portainer/agent msg_ok "Updated Portainer Agent" fi msg_ok "Updated successfully!" exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} If you installed Portainer, access it at the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}https://${IP}:9443${CL}" ================================================ FILE: ct/dockge.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://dockge.kuma.pet/ APP="Dockge" var_tags="${var_tags:-docker}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-18}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors ADDON_SCRIPT="https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/tools/addon/dockge.sh" function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/dockge ]]; then msg_error "No ${APP} Installation Found!" exit fi msg_warn "⚠️ ${APP} has been migrated to an addon script." echo "" msg_info "This is a one-time migration. After this, you can update ${APP} anytime with:" echo -e "${TAB}${TAB}${GN}update_dockge${CL} or ${GN}bash <(curl -fsSL ${ADDON_SCRIPT})${CL}" echo "" read -r -p "${TAB}Migrate update function now? [y/N]: " CONFIRM if [[ ! "${CONFIRM,,}" =~ ^(y|yes)$ ]]; then msg_warn "Migration skipped. The old update will continue to work for now." msg_info "Updating ${APP} (legacy)" cd /opt/dockge $STD docker compose pull $STD docker compose up -d msg_ok "Updated ${APP}" exit fi msg_info "Migrating update function" TMP_UPDATE=$(mktemp) cat <<'MIGRATION_EOF' >"$TMP_UPDATE" bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/tools/addon/dockge.sh)" MIGRATION_EOF mv "$TMP_UPDATE" /usr/bin/update chmod +x /usr/bin/update ln -sf /usr/bin/update /usr/bin/update_dockge 2>/dev/null || true msg_ok "Migration complete" msg_info "Running addon update" type=update bash <(curl -fsSL "${ADDON_SCRIPT}") exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:5001${CL}" ================================================ FILE: ct/docmost.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://docmost.com/ APP="Docmost" var_tags="${var_tags:-documents}" var_cpu="${var_cpu:-3}" var_ram="${var_ram:-4096}" var_disk="${var_disk:-8}" var_os="${var_os:-debian}" var_version="${var_version:-13}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/docmost ]]; then msg_error "No ${APP} Installation Found!" exit fi if ! command -v node >/dev/null || [[ "$(/usr/bin/env node -v | grep -oP '^v\K[0-9]+')" != "22" ]]; then NODE_VERSION="22" NODE_MODULE="pnpm@$(curl -s https://raw.githubusercontent.com/docmost/docmost/main/package.json | jq -r '.packageManager | split("@")[1]')" setup_nodejs fi export NODE_OPTIONS="--max_old_space_size=4096" if check_for_gh_release "docmost" "docmost/docmost"; then msg_info "Stopping Service" systemctl stop docmost msg_ok "Stopped Service" msg_info "Backing up data" cp /opt/docmost/.env /opt/ cp -r /opt/docmost/data /opt/ rm -rf /opt/docmost msg_ok "Data backed up" fetch_and_deploy_gh_release "docmost" "docmost/docmost" "tarball" msg_info "Updating ${APP}" cd /opt/docmost mv /opt/.env /opt/docmost/.env mv /opt/data /opt/docmost/data # Fix: Docmost EE (audit logs etc.) lives in a git submodule that is NOT # included in GitHub tarballs. The community NoopAuditService exists but # is only exported by CoreModule – child modules such as UserModule cannot # resolve it. Making CoreModule @Global() exposes the token app-wide. if [[ ! -f /opt/docmost/apps/server/src/ee/ee.module.ts ]] \ && ! grep -q '@Global()' /opt/docmost/apps/server/src/core/core.module.ts 2>/dev/null; then sed -i '/^ Module,$/a\ Global,' /opt/docmost/apps/server/src/core/core.module.ts sed -i '/^@Module({$/i @Global()' /opt/docmost/apps/server/src/core/core.module.ts fi $STD pnpm install --force $STD pnpm build msg_ok "Updated ${APP}" msg_info "Starting Service" systemctl start docmost msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:3000${CL}" ================================================ FILE: ct/dokploy.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://dokploy.com/ APP="Dokploy" var_tags="${var_tags:-docker;paas}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-10}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-0}" header_info "$APP" variables color catch_errors ADDON_SCRIPT="https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/tools/addon/dokploy.sh" function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /etc/dokploy ]]; then msg_error "No ${APP} Installation Found!" exit fi msg_warn "⚠️ ${APP} has been migrated to an addon script." echo "" msg_info "This is a one-time migration. After this, you can update ${APP} anytime with:" echo -e "${TAB}${TAB}${GN}update_dokploy${CL} or ${GN}bash <(curl -fsSL ${ADDON_SCRIPT})${CL}" echo "" read -r -p "${TAB}Migrate update function now? [y/N]: " CONFIRM if [[ ! "${CONFIRM,,}" =~ ^(y|yes)$ ]]; then msg_warn "Migration skipped. The old update will continue to work for now." msg_info "Updating ${APP} (legacy)" curl -sSL https://dokploy.com/install.sh | $STD bash -s update msg_ok "Updated ${APP}" exit fi msg_info "Migrating update function" TMP_UPDATE=$(mktemp) cat <<'MIGRATION_EOF' >"$TMP_UPDATE" bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/tools/addon/dokploy.sh)" MIGRATION_EOF mv "$TMP_UPDATE" /usr/bin/update chmod +x /usr/bin/update ln -sf /usr/bin/update /usr/bin/update_dokploy 2>/dev/null || true msg_ok "Migration complete" msg_info "Running addon update" type=update bash <(curl -fsSL "${ADDON_SCRIPT}") exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:3000${CL}" ================================================ FILE: ct/dolibarr.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Slaviša Arežina (tremor021) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/Dolibarr/dolibarr/ APP="Dolibarr" var_tags="${var_tags:-erp;accounting}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-6}" var_os="${var_os:-debian}" var_version="${var_version:-12}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /usr/share/dolibarr ]]; then msg_error "No ${APP} Installation Found!" exit fi setup_mariadb msg_error "To update ${APP}, use the applications web interface." exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}/dolibarr/install${CL}" ================================================ FILE: ct/domain-locker.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: CrazyWolf13 # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/Lissy93/domain-locker APP="Domain-Locker" var_tags="${var_tags:-Monitoring}" var_cpu="${var_cpu:-4}" var_ram="${var_ram:-10240}" var_disk="${var_disk:-8}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/domain-locker ]]; then msg_error "No ${APP} Installation Found!" exit fi ensure_dependencies whois if check_for_gh_release "domain-locker" "Lissy93/domain-locker"; then msg_info "Stopping Service" systemctl stop domain-locker msg_info "Service stopped" PG_VERSION="17" setup_postgresql NODE_VERSION="22" setup_nodejs CLEAN_INSTALL=1 fetch_and_deploy_gh_release "domain-locker" "Lissy93/domain-locker" "tarball" msg_info "Installing Modules (patience)" cd /opt/domain-locker $STD npm install msg_ok "Installed Modules" msg_info "Building Domain-Locker (a lot of patience)" set -a source /opt/domain-locker.env set +a $STD npm run build msg_info "Built Domain-Locker" msg_info "Restarting Services" systemctl start domain-locker msg_ok "Restarted Services" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:3000${CL}" ================================================ FILE: ct/domain-monitor.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Slaviša Arežina (tremor021) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/Hosteroid/domain-monitor APP="Domain-Monitor" var_tags="${var_tags:-proxy}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-512}" var_disk="${var_disk:-2}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/domain-monitor ]]; then msg_error "No ${APP} Installation Found!" exit fi setup_mariadb if grep -Fq "root /usr/bin/php /opt/domain-monitor/cron/check_domains.php" /etc/crontab; then sed -i 's|root /usr/bin/php /opt/domain-monitor/cron/check_domains.php|www-data /usr/bin/php /opt/domain-monitor/cron/check_domains.php|' /etc/crontab fi if ! grep -Fq "www-data /usr/bin/php /opt/domain-monitor/cron/check_domains.php" /etc/crontab; then echo "0 0 * * * www-data /usr/bin/php /opt/domain-monitor/cron/check_domains.php" >>/etc/crontab fi if check_for_gh_release "domain-monitor" "Hosteroid/domain-monitor"; then msg_info "Stopping Service" systemctl stop apache2 msg_info "Service stopped" msg_info "Creating backup" mv /opt/domain-monitor/.env /opt msg_ok "Created backup" setup_composer CLEAN_INSTALL=1 fetch_and_deploy_gh_release "domain-monitor" "Hosteroid/domain-monitor" "prebuild" "latest" "/opt/domain-monitor" "domain-monitor-v*.zip" msg_info "Updating Domain Monitor" cd /opt/domain-monitor $STD composer install chown -R www-data:www-data /opt/domain-monitor msg_ok "Updated Domain Monitor" msg_info "Restoring backup" mv /opt/.env /opt/domain-monitor msg_ok "Restored backup" msg_info "Restarting Services" systemctl start apache2 msg_ok "Restarted Services" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}${CL}" ================================================ FILE: ct/donetick.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: fstof # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/donetick/donetick APP="Donetick" var_tags="${var_tags:-productivity;tasks}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-512}" var_disk="${var_disk:-2}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/donetick ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "donetick" "donetick/donetick"; then msg_info "Stopping Service" systemctl stop donetick msg_ok "Stopped Service" msg_info "Backing Up Configurations" mv /opt/donetick/config/selfhosted.yaml /opt/donetick/donetick.db /opt msg_ok "Backed Up Configurations" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "donetick" "donetick/donetick" "prebuild" "latest" "/opt/donetick" "donetick_Linux_arm64.tar.gz" msg_info "Restoring Configurations" mv /opt/selfhosted.yaml /opt/donetick/config grep -q 'http://localhost"$' /opt/donetick/config/selfhosted.yaml || sed -i '/https:\/\/localhost"$/a\ - "http://localhost"' /opt/donetick/config/selfhosted.yaml grep -q 'capacitor://localhost' /opt/donetick/config/selfhosted.yaml || sed -i '/http:\/\/localhost"$/a\ - "capacitor://localhost"' /opt/donetick/config/selfhosted.yaml mv /opt/donetick.db /opt/donetick msg_ok "Restored Configurations" msg_info "Starting Service" systemctl start donetick msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:2021${CL}" ================================================ FILE: ct/dotnetaspwebapi.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Kristian Skov # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://learn.microsoft.com/en-us/aspnet/core/host-and-deploy/linux-nginx?view=aspnetcore-9.0&tabs=linux-ubuntu APP="Dotnet ASP Web API" var_tags="${var_tags:-web}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-1024}" var_disk="${var_disk:-8}" var_os="${var_os:-ubuntu}" var_version="${var_version:-24.04}" var_unprivileged="${var_unprivileged:-0}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /var/www ]]; then msg_error "No ${APP} Installation Found!" exit fi msg_info "Updating ${APP} LXC" $STD apt-get update $STD apt-get -y upgrade msg_ok "Updated successfully!" exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following IP:${CL}" echo -e "${TAB}${GATEWAY}${BGN}${IP}:80${CL}" ================================================ FILE: ct/drawdb.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/drawdb-io/drawdb APP="DrawDB" var_tags="${var_tags:-database;dev-tools}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-6144}" var_disk="${var_disk:-5}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/drawdb ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_tag "drawdb" "drawdb-io/drawdb"; then CLEAN_INSTALL=1 fetch_and_deploy_gh_tag "drawdb" "drawdb-io/drawdb" "latest" "/opt/drawdb" msg_info "Rebuilding Frontend" cd /opt/drawdb $STD npm ci NODE_OPTIONS="--max-old-space-size=4096" $STD npm run build sed -i '//a ' /opt/drawdb/dist/index.html msg_ok "Rebuilt Frontend" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed Successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:3000${CL}" ================================================ FILE: ct/drawio.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Slaviša Arežina (tremor021) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.drawio.com/ APP="DrawIO" var_tags="${var_tags:-diagrams}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -f /var/lib/tomcat11/webapps/draw.war ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "drawio" "jgraph/drawio"; then msg_info "Stopping service" systemctl stop tomcat11 msg_ok "Service stopped" msg_info "Updating Debian LXC" $STD apt update $STD apt upgrade -y msg_ok "Updated Debian LXC" USE_ORIGINAL_FILENAME=true fetch_and_deploy_gh_release "drawio" "jgraph/drawio" "singlefile" "latest" "/var/lib/tomcat11/webapps" "draw.war" msg_info "Starting service" systemctl start tomcat11 msg_ok "Service started" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed Successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8080/draw${CL}" ================================================ FILE: ct/duplicati.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: tremor021 # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/duplicati/duplicati/ APP="Duplicati" var_tags="${var_tags:-backup}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-1024}" var_disk="${var_disk:-10}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -f /usr/bin/duplicati-server ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "duplicati" "duplicati/duplicati"; then msg_info "Stopping Service" systemctl stop duplicati msg_info "Stopped Service" fetch_and_deploy_gh_release "duplicati" "duplicati/duplicati" "binary" "latest" "/opt/duplicati" "duplicati-*-linux-x64-gui.deb" msg_info "Starting Service" systemctl start duplicati msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8200${CL}" ================================================ FILE: ct/ebusd.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Joerg Heinemann (heinemannj) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/john30/ebusd APP="ebusd" var_tags="${var_tags:-automation}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-512}" var_disk="${var_disk:-2}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -f /etc/default/ebusd ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "ebusd" "john30/ebusd"; then msg_info "Stopping Services" systemctl stop ebusd msg_ok "Stopped Services" fetch_and_deploy_gh_release "ebusd" "john30/ebusd" "binary" "latest" "/opt/ebusd" "ebusd-*_arm64-trixie_mqtt1.deb" msg_info "Starting Services" systemctl start ebusd msg_ok "Started Services" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" ================================================ FILE: ct/elementsynapse.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: tremor021 # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/element-hq/synapse APP="Element Synapse" var_tags="${var_tags:-server}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-8}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /etc/matrix-synapse ]]; then msg_error "No ${APP} Installation Found!" exit fi NODE_VERSION="22" NODE_MODULE="yarn" setup_nodejs msg_info "Updating LXC" $STD apt update $STD apt -y upgrade msg_ok "Updated LXC" if check_for_gh_release "synapse-admin" "etkecc/synapse-admin"; then msg_info "Stopping Service" systemctl stop synapse-admin msg_ok "Stopped Service" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "synapse-admin" "etkecc/synapse-admin" "tarball" "latest" "/opt/synapse-admin" msg_info "Building Synapse-Admin" cd /opt/synapse-admin $STD yarn global add serve $STD yarn install --ignore-engines $STD yarn build mv ./dist ../ && rm -rf * && mv ../dist ./ msg_ok "Built Synapse-Admin" msg_info "Starting Service" systemctl start synapse-admin msg_ok "Started Service" msg_ok "Updated Synapse-Admin to ${CHECK_UPDATE_RELEASE}" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8008${CL}" ================================================ FILE: ct/emby.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://emby.media/ APP="Emby" var_tags="${var_tags:-media}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-8}" var_os="${var_os:-ubuntu}" var_version="${var_version:-24.04}" var_unprivileged="${var_unprivileged:-1}" var_gpu="${var_gpu:-yes}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/emby-server ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "emby" "MediaBrowser/Emby.Releases"; then msg_info "Stopping Service" systemctl stop emby-server msg_ok "Stopped Service" fetch_and_deploy_gh_release "emby" "MediaBrowser/Emby.Releases" "binary" msg_info "Starting Service" systemctl start emby-server msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8096${CL}" ================================================ FILE: ct/emqx.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.emqx.com/en APP="EMQX" var_tags="${var_tags:-mqtt}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-1024}" var_disk="${var_disk:-6}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources RELEASE=$(curl -fsSL https://www.emqx.com/en/downloads/enterprise | grep -oP '/en/downloads/enterprise/v\K[0-9]+\.[0-9]+\.[0-9]+' | sort -V | tail -n1) if [[ "$RELEASE" != "$(cat ~/.emqx 2>/dev/null)" ]] || [[ ! -f ~/.emqx ]]; then msg_info "Stopping EMQX" systemctl stop emqx msg_ok "Stopped EMQX" msg_info "Removing old EMQX" if dpkg -l | grep -q "^ii\s\+emqx\s"; then $STD apt remove --purge -y emqx elif dpkg -l | grep -q "^ii\s\+emqx-enterprise\s"; then $STD apt remove --purge -y emqx-enterprise else msg_ok "No old EMQX package found" fi msg_ok "Removed old EMQX" msg_info "Downloading EMQX v${RELEASE}" DEB_FILE="/tmp/emqx-enterprise-${RELEASE}-debian12-arm64.deb" curl -fsSL -o "$DEB_FILE" "https://www.emqx.com/en/downloads/enterprise/v${RELEASE}/emqx-enterprise-${RELEASE}-debian12-arm64.deb" msg_ok "Downloaded EMQX" msg_info "Installing EMQX" $STD apt install -y "$DEB_FILE" rm -f "$DEB_FILE" echo "$RELEASE" >~/.emqx msg_ok "Installed EMQX v${RELEASE}" msg_info "Starting EMQX" systemctl start emqx msg_ok "Started EMQX" msg_ok "Updated successfully!" else msg_ok "No update required. EMQX is already at v${RELEASE}" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:18083${CL}" ================================================ FILE: ct/endurain.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: johanngrobe # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/joaovitoriasilva/endurain APP="Endurain" var_tags="${var_tags:-sport;social-media}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-4096}" var_disk="${var_disk:-5}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/endurain ]]; then msg_error "No ${APP} installation found!" exit 233 fi if check_for_codeberg_release "endurain" "endurain-project/endurain"; then msg_info "Stopping Service" systemctl stop endurain msg_ok "Stopped Service" msg_info "Creating Backup" cp /opt/endurain/.env /opt/endurain.env cp /opt/endurain/frontend/app/dist/env.js /opt/endurain.env.js msg_ok "Created Backup" CLEAN_INSTALL=1 fetch_and_deploy_codeberg_release "endurain" "endurain-project/endurain" "tarball" "latest" "/opt/endurain" msg_info "Preparing Update" cd /opt/endurain rm -rf \ /opt/endurain/{docs,example.env,screenshot_01.png} \ /opt/endurain/docker* \ /opt/endurain/*.yml cp /opt/endurain.env /opt/endurain/.env rm /opt/endurain.env msg_ok "Prepared Update" msg_info "Updating Frontend" cd /opt/endurain/frontend/app $STD npm ci $STD npm run build cp /opt/endurain.env.js /opt/endurain/frontend/app/dist/env.js rm /opt/endurain.env.js msg_ok "Updated Frontend" msg_info "Updating Backend" cd /opt/endurain/backend $STD poetry export -f requirements.txt --output requirements.txt --without-hashes $STD uv venv --clear $STD uv pip install -r requirements.txt msg_ok "Backend Updated" msg_info "Starting Service" systemctl start endurain msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8080${CL}" ================================================ FILE: ct/erpnext.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/frappe/erpnext APP="ERPNext" var_tags="${var_tags:-erp;business;accounting}" var_cpu="${var_cpu:-4}" var_ram="${var_ram:-4096}" var_disk="${var_disk:-20}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/frappe-bench ]]; then msg_error "No ${APP} Installation Found!" exit fi msg_info "Updating ERPNext" $STD sudo -u frappe bash -c 'export PATH="$HOME/.local/bin:$PATH"; cd /opt/frappe-bench && bench update --reset' msg_ok "Updated ERPNext" exit } start build_container description msg_ok "Completed Successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}${CL}" echo -e "${INFO}${YW} Credentials:${CL}" echo -e "${TAB}${BGN}Username: Administrator${CL}" echo -e "${TAB}${BGN}Password: see ~/erpnext.creds${CL}" ================================================ FILE: ct/ersatztv.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: MickLesk (Canbiz) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://ersatztv.org/ APP="ErsatzTV" var_tags="${var_tags:-iptv}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-1024}" var_disk="${var_disk:-5}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" var_gpu="${var_gpu:-yes}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/ErsatzTV ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "ersatztv" "ErsatzTV/ErsatzTV"; then msg_info "Stopping ErsatzTV" systemctl stop ersatzTV msg_ok "Stopped ErsatzTV" fetch_and_deploy_gh_release "ersatztv" "ErsatzTV/ErsatzTV" "prebuild" "latest" "/opt/ErsatzTV" "*linux-arm64.tar.gz" msg_info "Starting ErsatzTV" systemctl start ersatzTV msg_ok "Started ErsatzTV" msg_ok "Updated successfully!" fi if check_for_gh_release "ersatztv-ffmpeg" "ErsatzTV/ErsatzTV-ffmpeg"; then msg_info "Stopping ErsatzTV" systemctl stop ersatzTV msg_ok "Stopped ErsatzTV" fetch_and_deploy_gh_release "ersatztv-ffmpeg" "ErsatzTV/ErsatzTV-ffmpeg" "prebuild" "latest" "/opt/ErsatzTV-ffmpeg" "*-linux64-gpl-7.1.tar.xz" msg_info "Set ErsatzTV-ffmpeg links" chmod +x /opt/ErsatzTV-ffmpeg/bin/* ln -sf /opt/ErsatzTV-ffmpeg/bin/ffmpeg /usr/local/bin/ffmpeg ln -sf /opt/ErsatzTV-ffmpeg/bin/ffplay /usr/local/bin/ffplay ln -sf /opt/ErsatzTV-ffmpeg/bin/ffprobe /usr/local/bin/ffprobe msg_ok "ffmpeg links set" msg_info "Starting ErsatzTV" systemctl start ersatzTV msg_ok "Started ErsatzTV" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8409${CL}" ================================================ FILE: ct/esphome.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://esphome.io/ APP="ESPHome" var_tags="${var_tags:-automation}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-1024}" var_disk="${var_disk:-10}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -f /etc/systemd/system/esphomeDashboard.service ]]; then msg_error "No ${APP} Installation Found!" exit fi msg_info "Stopping Service" systemctl stop esphomeDashboard msg_ok "Stopped Service" VENV_PATH="/opt/esphome/.venv" ESPHOME_BIN="${VENV_PATH}/bin/esphome" export PYTHON_VERSION="3.12" if [[ ! -d "$VENV_PATH" || ! -x "$ESPHOME_BIN" ]]; then PYTHON_VERSION="3.12" setup_uv msg_info "Migrating to uv/venv" rm -rf "$VENV_PATH" mkdir -p /opt/esphome cd /opt/esphome $STD uv venv --clear "$VENV_PATH" $STD "$VENV_PATH/bin/python" -m ensurepip --upgrade $STD "$VENV_PATH/bin/python" -m pip install --upgrade pip $STD "$VENV_PATH/bin/python" -m pip install esphome tornado esptool msg_ok "Migrated to uv/venv" else msg_info "Updating ESPHome" PYTHON_VERSION="3.12" setup_uv $STD "$VENV_PATH/bin/python" -m pip install --upgrade esphome tornado esptool msg_ok "Updated ESPHome" fi SERVICE_FILE="/etc/systemd/system/esphomeDashboard.service" if ! grep -q "${VENV_PATH}/bin/esphome" "$SERVICE_FILE"; then msg_info "Updating systemd service" cat <"$SERVICE_FILE" [Unit] Description=ESPHome Dashboard After=network.target [Service] ExecStart=${VENV_PATH}/bin/esphome dashboard /root/config/ Restart=always User=root [Install] WantedBy=multi-user.target EOF $STD systemctl daemon-reload msg_ok "Updated systemd service" fi msg_info "Linking esphome to /usr/local/bin" rm -f /usr/local/bin/esphome ln -s /opt/esphome/.venv/bin/esphome /usr/local/bin/esphome msg_ok "Linked esphome binary" msg_info "Starting Service" systemctl start esphomeDashboard msg_ok "Started Service" msg_ok "Updated successfully!" exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:6052${CL}" ================================================ FILE: ct/evcc.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: MickLesk (Canbiz) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://evcc.io/en/ APP="evcc" var_tags="${var_tags:-solar;ev;automation}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-1024}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if ! command -v evcc >/dev/null 2>&1; then msg_error "No ${APP} Installation Found!" exit 233 fi if [[ -f /etc/apt/sources.list.d/evcc-stable.list ]]; then setup_deb822_repo \ "evcc-stable" \ "https://dl.evcc.io/public/evcc/stable/gpg.EAD5D0E07B0EC0FD.key" \ "https://dl.evcc.io/public/evcc/stable/deb/debian/" \ "$(get_os_info codename)" \ "main" fi msg_info "Updating evcc LXC" $STD apt update $STD apt --only-upgrade install -y evcc msg_ok "Updated successfully!" exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:7070${CL}" ================================================ FILE: ct/excalidraw.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Slaviša Arežina (tremor021) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/excalidraw/excalidraw APP="Excalidraw" var_tags="${var_tags:-diagrams}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-3072}" var_disk="${var_disk:-10}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/excalidraw ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "excalidraw" "excalidraw/excalidraw"; then msg_info "Stopping Service" systemctl stop excalidraw msg_info "Stopped Service" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "excalidraw" "excalidraw/excalidraw" "tarball" msg_info "Updating Excalidraw" cd /opt/excalidraw $STD yarn msg_ok "Updated Excalidraw" msg_info "Starting Service" systemctl start excalidraw msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:3000${CL}" ================================================ FILE: ct/fhem.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://fhem.de/ APP="FHEM" var_tags="${var_tags:-automation}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-8}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -f /etc/systemd/system/fhem.service ]]; then msg_error "No ${APP} Installation Found!" exit fi msg_info "Updating FHEM" $STD apt update $STD apt upgrade -y msg_ok "Updated FHEM" msg_ok "Updated successfully!" exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8083${CL}" ================================================ FILE: ct/fileflows.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: kkroboth # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://fileflows.com/ APP="FileFlows" var_tags="${var_tags:-media;automation}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-8}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" var_gpu="${var_gpu:-yes}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/fileflows ]]; then msg_error "No ${APP} Installation Found!" exit fi update_available=$(curl -fsSL -X 'GET' "http://localhost:19200/api/status/update-available" -H 'accept: application/json' | jq .UpdateAvailable) if [[ "${update_available}" == "true" ]]; then msg_info "Stopping Service" systemctl stop fileflows* msg_info "Stopped Service" msg_info "Creating Backup" ls /opt/*.tar.gz &>/dev/null && rm -f /opt/*.tar.gz backup_filename="/opt/${APP}_backup_$(date +%F).tar.gz" tar -czf "$backup_filename" -C /opt/fileflows Data msg_ok "Backup Created" fetch_and_deploy_from_url "https://fileflows.com/downloads/zip" "/opt/fileflows" msg_info "Starting Service" systemctl start fileflows* msg_ok "Started Service" msg_ok "Updated successfully!" else msg_ok "No update required. ${APP} is already at latest version" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:19200${CL}" ================================================ FILE: ct/firefly.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: quantumryuu | Co-Author: Slaviša Arežina (tremor021) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://firefly-iii.org/ APP="Firefly" var_tags="${var_tags:-finance}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-1024}" var_disk="${var_disk:-2}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/firefly ]]; then msg_error "No ${APP} Installation Found!" exit fi setup_mariadb PHP_VERSION="8.5" PHP_APACHE="YES" setup_php if check_for_gh_release "firefly" "firefly-iii/firefly-iii"; then systemctl stop apache2 cp /opt/firefly/.env /opt/.env rm -rf /opt/storage cp -r /opt/firefly/storage /opt/storage if [[ -d /opt/firefly/dataimporter ]]; then cp /opt/firefly/dataimporter/.env /opt/dataimporter.env IMPORTER_INSTALLED=1 fi fetch_and_deploy_gh_release "firefly" "firefly-iii/firefly-iii" "prebuild" "latest" "/opt/firefly" "FireflyIII-*.zip" setup_composer msg_info "Updating Firefly" rm -rf /opt/firefly/storage cp -r /opt/storage /opt/firefly/storage cp /opt/.env /opt/firefly/.env chown -R www-data:www-data /opt/firefly chmod -R 775 /opt/firefly/storage mkdir -p /opt/firefly/storage/framework/cache/data mkdir -p /opt/firefly/storage/framework/sessions mkdir -p /opt/firefly/storage/framework/views mkdir -p /opt/firefly/storage/logs mkdir -p /opt/firefly/bootstrap/cache chown -R www-data:www-data /opt/firefly/{storage,bootstrap/cache} cd /opt/firefly $STD runuser -u www-data -- composer install --no-dev --optimize-autoloader $STD runuser -u www-data -- composer dump-autoload -o $STD runuser -u www-data -- php artisan cache:clear $STD runuser -u www-data -- php artisan config:clear $STD runuser -u www-data -- php artisan route:clear $STD runuser -u www-data -- php artisan view:clear $STD runuser -u www-data -- php artisan migrate --seed --force $STD runuser -u www-data -- php artisan firefly-iii:upgrade-database $STD runuser -u www-data -- php artisan firefly-iii:laravel-passport-keys $STD runuser -u www-data -- php artisan storage:link || true $STD runuser -u www-data -- php artisan optimize msg_ok "Updated Firefly" if [[ "${IMPORTER_INSTALLED:-0}" -eq 1 ]]; then CLEAN_INSTALL=1 fetch_and_deploy_gh_release "dataimporter" "firefly-iii/data-importer" "prebuild" "latest" "/opt/firefly/dataimporter" "DataImporter-v*.tar.gz" msg_info "Updating Firefly Importer" if [[ -f /opt/dataimporter.env ]]; then cp /opt/dataimporter.env /opt/firefly/dataimporter/.env fi chown -R www-data:www-data /opt/firefly/dataimporter msg_ok "Updated Firefly Importer" fi rm -rf /opt/storage /opt/.env /opt/dataimporter.env systemctl start apache2 msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}${CL}" ================================================ FILE: ct/fireshare.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Slaviša Arežina (tremor021) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/ShaneIsrael/fireshare APP="Fireshare" var_tags="${var_tags:-sharing;video}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-10}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/fireshare ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "fireshare" "ShaneIsrael/fireshare"; then msg_info "Stopping Service" systemctl stop fireshare msg_ok "Stopped Service" mv /opt/fireshare/fireshare.env /opt CLEAN_INSTALL=1 fetch_and_deploy_gh_release "fireshare" "ShaneIsrael/fireshare" "tarball" mv /opt/fireshare.env /opt/fireshare rm -f /usr/local/bin/fireshare msg_info "Updating Fireshare" cd /opt/fireshare $STD uv venv --clear $STD .venv/bin/python -m ensurepip --upgrade $STD .venv/bin/python -m pip install --upgrade --break-system-packages pip $STD .venv/bin/python -m pip install --no-cache-dir --break-system-packages --ignore-installed app/server cp .venv/bin/fireshare /usr/local/bin/fireshare export FLASK_APP="/opt/fireshare/app/server/fireshare:create_app()" export DATA_DIRECTORY=/opt/fireshare-data export IMAGE_DIRECTORY=/opt/fireshare-images export VIDEO_DIRECTORY=/opt/fireshare-videos export PROCESSED_DIRECTORY=/opt/fireshare-processed $STD uv run flask db upgrade msg_ok "Updated Fireshare" msg_info "Starting Service" systemctl start fireshare msg_ok "Started Service" msg_ok "Updated successfully!" fi cleanup_lxc exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}${CL}" ================================================ FILE: ct/fladder.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: wendyliga # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/DonutWare/Fladder APP="Fladder" var_tags="${var_tags:-media}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/fladder ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "Fladder" "DonutWare/Fladder"; then msg_info "Stopping Service" systemctl stop nginx msg_ok "Stopped Service" if [[ -f /opt/fladder/assets/config/config.json ]]; then msg_info "Backing up configuration" cp /opt/fladder/assets/config/config.json /tmp/fladder_config.json.bak msg_ok "Configuration backed up" fi CLEAN_INSTALL=1 fetch_and_deploy_gh_release "Fladder" "DonutWare/Fladder" "prebuild" "latest" "/opt/fladder" "Fladder-Web-*.zip" if [[ -f /tmp/fladder_config.json.bak ]]; then msg_info "Restoring configuration" mkdir -p /opt/fladder/assets/config cp /tmp/fladder_config.json.bak /opt/fladder/assets/config/config.json rm -f /tmp/fladder_config.json.bak msg_ok "Configuration restored" fi msg_info "Starting Service" systemctl start nginx msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed Successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following IP:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}${CL}" ================================================ FILE: ct/flaresolverr.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) | Co-Author: remz1337 # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/FlareSolverr/FlareSolverr APP="FlareSolverr" var_tags="${var_tags:-proxy}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -f /etc/systemd/system/flaresolverr.service ]]; then msg_error "No ${APP} Installation Found!" exit fi if [[ $(grep -E '^VERSION_ID=' /etc/os-release) == *"12"* ]]; then msg_error "Wrong Debian version detected!" msg_error "You must upgrade your LXC to Debian Trixie before updating." exit fi if check_for_gh_release "flaresolverr" "FlareSolverr/FlareSolverr"; then msg_info "Stopping service" systemctl stop flaresolverr msg_ok "Stopped service" rm -rf /opt/flaresolverr fetch_and_deploy_gh_release "flaresolverr" "FlareSolverr/FlareSolverr" "prebuild" "latest" "/opt/flaresolverr" "flaresolverr_linux_x64.tar.gz" msg_info "Starting service" systemctl start flaresolverr msg_ok "Started service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8191${CL}" ================================================ FILE: ct/flatnotes.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: luismco # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/dullage/flatnotes APP="Flatnotes" var_tags="${var_tags:-notes}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-1024}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/flatnotes ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "flatnotes" "dullage/flatnotes"; then msg_info "Stopping Service" systemctl stop flatnotes msg_ok "Stopped Service" msg_info "Backing up Configuration and Data" cp /opt/flatnotes/.env /opt/flatnotes.env cp -r /opt/flatnotes/data /opt/flatnotes_data_backup msg_ok "Backed up Configuration and Data" fetch_and_deploy_gh_release "flatnotes" "dullage/flatnotes" "tarball" msg_info "Updating Flatnotes" cd /opt/flatnotes/client $STD npm install $STD npm run build cd /opt/flatnotes rm -f uv.lock $STD /usr/local/bin/uvx migrate-to-uv $STD /usr/local/bin/uv sync msg_ok "Updated Flatnotes" msg_info "Restoring Configuration and Data" cp /opt/flatnotes.env /opt/flatnotes/.env cp -r /opt/flatnotes_data_backup/. /opt/flatnotes/data rm -f /opt/flatnotes.env rm -r /opt/flatnotes_data_backup msg_ok "Restored Configuration and Data" msg_info "Starting Service" systemctl start flatnotes msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8080${CL}" ================================================ FILE: ct/flowiseai.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://flowiseai.com/ APP="FlowiseAI" var_tags="${var_tags:-low-code}" var_disk="${var_disk:-10}" var_cpu="${var_cpu:-4}" var_ram="${var_ram:-4096}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -f /etc/systemd/system/flowise.service ]]; then msg_error "No ${APP} Installation Found!" exit fi NODE_VERSION="20" NODE_MODULE="pnpm" setup_nodejs msg_info "Updating FlowiseAI (this may take some time)" systemctl stop flowise $STD pnpm add -g flowise if grep -q 'ExecStart=npx flowise start' /etc/systemd/system/flowise.service; then sed -i 's|ExecStart=npx flowise start|ExecStart=flowise start|' /etc/systemd/system/flowise.service systemctl daemon-reload fi systemctl start flowise msg_ok "Updated FlowiseAI" msg_ok "Updated successfully!" exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:3000${CL}" ================================================ FILE: ct/fluid-calendar.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: vhsdream # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://fluidcalendar.com APP="fluid-calendar" var_tags="${var_tags:-calendar;tasks}" var_cpu="${var_cpu:-3}" var_ram="${var_ram:-4096}" var_disk="${var_disk:-7}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/fluid-calendar ]]; then msg_error "No ${APP} Installation Found!" exit fi ensure_dependencies build-essential NODE_VERSION="24" setup_nodejs if check_for_gh_release "fluid-calendar" "dotnetfactory/fluid-calendar"; then msg_info "Stopping Service" systemctl stop fluid-calendar msg_info "Stopped Service" cp /opt/fluid-calendar/.env /opt/fluid.env CLEAN_INSTALL=1 fetch_and_deploy_gh_release "fluid-calendar" "dotnetfactory/fluid-calendar" "tarball" mv /opt/fluid.env /opt/fluid-calendar/.env msg_info "Updating Fluid Calendar" cd /opt/fluid-calendar export NEXT_TELEMETRY_DISABLED=1 $STD npm install --legacy-peer-deps $STD npm run prisma:generate $STD npx prisma migrate deploy $STD npm run build:os msg_ok "Updated Fluid Calendar" msg_info "Starting Service" systemctl start fluid-calendar msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:3000${CL}" ================================================ FILE: ct/foldergram.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Slaviša Arežina (tremor021) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/foldergram/foldergram APP="Foldergram" var_tags="${var_tags:-photos}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/foldergram ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "foldergram" "foldergram/foldergram"; then msg_info "Stopping Service" systemctl stop foldergram msg_ok "Stopped Service" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "foldergram" "foldergram/foldergram" "tarball" msg_info "Installing Foldergram" cd /opt/foldergram $STD pnpm install --frozen-lockfile $STD pnpm run build msg_ok "Installed Foldergram" msg_info "Starting Service" systemctl start foldergram msg_ok "Started Service" msg_ok "Updated successfully!" fi cleanup_lxc exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:4141${CL}" ================================================ FILE: ct/forgejo.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://forgejo.org/ APP="Forgejo" var_tags="${var_tags:-git}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-10}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/forgejo ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_codeberg_release "forgejo" "forgejo/forgejo"; then msg_info "Stopping Service" systemctl stop forgejo msg_ok "Stopped Service" fetch_and_deploy_codeberg_release "forgejo" "forgejo/forgejo" "singlefile" "latest" "/opt/forgejo" "forgejo-*-linux-arm64" ln -sf /opt/forgejo/forgejo /usr/local/bin/forgejo if grep -q "GITEA_WORK_DIR" /etc/systemd/system/forgejo.service; then msg_info "Updating Service File" sed -i "s/GITEA_WORK_DIR/FORGEJO_WORK_DIR/g" /etc/systemd/system/forgejo.service systemctl daemon-reload msg_ok "Updated Service File" fi msg_info "Starting Service" systemctl start forgejo msg_ok "Started Service" msg_ok "Updated successfully!" else msg_ok "No update required. ${APP} is already at the latest version." fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:3000${CL}" ================================================ FILE: ct/freepbx.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Arian Nasr (arian-nasr) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.freepbx.org/ APP="FreePBX" var_tags="pbx;voip;telephony" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-10}" var_os="${var_os:-debian}" var_version="${var_version:-12}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -f /lib/systemd/system/freepbx.service ]]; then msg_error "No ${APP} Installation Found!" exit fi msg_error "Currently we don't provide an update function for this ${APP}." exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}${CL}" ================================================ FILE: ct/freshrss.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: bvdberg01 # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/FreshRSS/FreshRSS APP="FreshRSS" var_tags="${var_tags:-RSS}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-1024}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/freshrss ]]; then msg_error "No ${APP} Installation Found!" exit fi if [ ! -x /opt/freshrss/cli/sensitive-log.sh ]; then msg_info "Fixing wrong permissions" chmod +x /opt/freshrss/cli/sensitive-log.sh systemctl restart apache2 msg_ok "Fixed wrong permissions" fi if check_for_gh_release "freshrss" "FreshRSS/FreshRSS"; then msg_info "Stopping Apache2" systemctl stop apache2 msg_ok "Stopped Apache2" msg_info "Backing up FreshRSS" mv /opt/freshrss /opt/freshrss-backup msg_ok "Backup Created" fetch_and_deploy_gh_release "freshrss" "FreshRSS/FreshRSS" "tarball" msg_info "Restoring data and configuration" if [[ -d /opt/freshrss-backup/data ]]; then cp -a /opt/freshrss-backup/data/. /opt/freshrss/data/ fi if [[ -d /opt/freshrss-backup/extensions ]]; then cp -a /opt/freshrss-backup/extensions/. /opt/freshrss/extensions/ fi msg_ok "Data Restored" msg_info "Setting permissions" chown -R www-data:www-data /opt/freshrss chmod -R g+rX /opt/freshrss chmod -R g+w /opt/freshrss/data/ msg_ok "Permissions Set" msg_info "Starting Apache2" systemctl start apache2 msg_ok "Started Apache2" msg_info "Cleaning up backup" rm -rf /opt/freshrss-backup msg_ok "Cleaned up backup" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}${CL}" ================================================ FILE: ct/frigate.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Authors: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://frigate.video/ APP="Frigate" var_tags="${var_tags:-nvr}" var_cpu="${var_cpu:-8}" var_ram="${var_ram:-4096}" var_disk="${var_disk:-20}" var_os="${var_os:-debian}" var_version="${var_version:-12}" var_unprivileged="${var_unprivileged:-0}" var_gpu="${var_gpu:-yes}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -f /etc/systemd/system/frigate.service ]]; then msg_error "No ${APP} Installation Found!" exit fi msg_error "To update Frigate, create a new container and transfer your configuration." exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:5000${CL}" ================================================ FILE: ct/fumadocs.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/fuma-nama/fumadoc APP="Fumadocs" var_tags="${var_tags:-documentation}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-5}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/fumadocs ]]; then msg_error "No installation found in /opt/fumadocs!" exit fi if [[ ! -f /opt/fumadocs/.projectname ]]; then msg_error "Project name file not found: /opt/fumadocs/.projectname!" exit fi NODE_VERSION="22" NODE_MODULE="pnpm@latest" setup_nodejs PROJECT_NAME=$(/dev/null || true cp /etc/garage.toml /etc/garage.toml.bak 2>/dev/null || true msg_ok "Backed Up Data" msg_info "Updating Garage" curl -fsSL "https://garagehq.deuxfleurs.fr/_releases/${GITEA_RELEASE}/aarch64-unknown-linux-musl/garage" -o /usr/local/bin/garage chmod +x /usr/local/bin/garage echo "${GITEA_RELEASE}" >~/.garage msg_ok "Updated Garage" msg_info "Starting Service" systemctl start garage msg_ok "Started Service" msg_ok "Updated successfully!" else msg_ok "No update required. Garage is already at ${GITEA_RELEASE}" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}${CL}" ================================================ FILE: ct/gatus.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Slaviša Arežina (tremor021) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/TwiN/gatus APP="gatus" var_tags="${var_tags:-monitoring}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-1024}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/gatus ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "gatus" "TwiN/gatus"; then msg_info "Stopping Service" systemctl stop gatus msg_ok "Stopped Service" mv /opt/gatus/config/config.yaml /opt CLEAN_INSTALL=1 fetch_and_deploy_gh_release "gatus" "TwiN/gatus" "tarball" msg_info "Updating Gatus" cd /opt/gatus $STD go mod tidy CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o gatus . setcap CAP_NET_RAW+ep gatus mv /opt/config.yaml config msg_ok "Updated Gatus" msg_info "Starting Service" systemctl start gatus msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8080${CL}" ================================================ FILE: ct/geopulse.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: CrazyWolf13 # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/tess1o/geopulse APP="GeoPulse" var_tags="${var_tags:-location;tracking;gps}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-1024}" var_disk="${var_disk:-8}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -f /opt/geopulse/backend/geopulse-backend ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "geopulse-backend" "tess1o/geopulse"; then msg_info "Stopping Service" systemctl stop geopulse-backend msg_ok "Stopped Service" if [[ "$(uname -m)" == "aarch64" ]]; then if grep -qi "raspberry\|bcm" /proc/cpuinfo 2>/dev/null; then BINARY_PATTERN="geopulse-backend-native-arm64-compat-*" else BINARY_PATTERN="geopulse-backend-native-arm64-[!c]*" fi else if grep -q avx2 /proc/cpuinfo && grep -q bmi2 /proc/cpuinfo && grep -q fma /proc/cpuinfo; then BINARY_PATTERN="geopulse-backend-native-amd64-[!c]*" else BINARY_PATTERN="geopulse-backend-native-amd64-compat-*" fi fi fetch_and_deploy_gh_release "geopulse-backend" "tess1o/geopulse" "singlefile" "latest" "/opt/geopulse/backend" "${BINARY_PATTERN}" fetch_and_deploy_gh_release "geopulse-frontend" "tess1o/geopulse" "prebuild" "latest" "/var/www/geopulse" "geopulse-frontend-*.tar.gz" msg_info "Starting Service" systemctl start geopulse-backend msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed Successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}${CL}" echo -e "${INFO}${YW} To create an admin account, run:${CL}" echo -e "${TAB}${BGN}/usr/local/bin/create-geopulse-admin${CL}" ================================================ FILE: ct/ghost.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: fabrice1236 # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://ghost.org/ APP="Ghost" var_tags="${var_tags:-cms;blog}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-1024}" var_disk="${var_disk:-5}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources setup_mariadb NODE_VERSION="22" NODE_MODULE="pnpm" setup_nodejs ensure_dependencies git msg_info "Updating Ghost" if command -v ghost &>/dev/null; then current_version=$(ghost version | grep 'Ghost-CLI version' | awk '{print $3}') latest_version=$(npm show ghost-cli version) if [ "$current_version" != "$latest_version" ]; then msg_info "Updating ${APP} from version v${current_version} to v${latest_version}" $STD npm install -g ghost-cli@latest msg_ok "Updated successfully!" else msg_ok "${APP} is already at v${current_version}" fi else msg_error "No ${APP} Installation Found!" exit fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:2368${CL}" ================================================ FILE: ct/ghostfolio.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: lucasfell # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://ghostfol.io/ APP="Ghostfolio" var_tags="${var_tags:-finance;investment}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-4096}" var_disk="${var_disk:-8}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -f /opt/ghostfolio/dist/apps/api/main.js ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "ghostfolio" "ghostfolio/ghostfolio"; then msg_info "Stopping Service" systemctl stop ghostfolio msg_ok "Stopped Service" msg_info "Creating Backup" tar -czf "/opt/ghostfolio_backup_$(date +%F).tar.gz" \ -C /opt \ --exclude="ghostfolio/node_modules" \ --exclude="ghostfolio/dist" \ ghostfolio mv /opt/ghostfolio/.env /opt/env.backup msg_ok "Backup Created" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "ghostfolio" "ghostfolio/ghostfolio" "tarball" "latest" "/opt/ghostfolio" msg_info "Updating Ghostfolio" mv /opt/env.backup /opt/ghostfolio/.env sed -i -E '/^DATABASE_URL=/ s/[?&]sslmode=prefer//g' /opt/ghostfolio/.env cd /opt/ghostfolio $STD npm ci $STD npm run build:production $STD npx prisma migrate deploy msg_ok "Updated Ghostfolio" msg_info "Starting Service" systemctl start ghostfolio msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:3333${CL}" ================================================ FILE: ct/gitea-mirror.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: CrazyWolf13 # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/RayLabsHQ/gitea-mirror APP="gitea-mirror" var_tags="${var_tags:-mirror;gitea}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-6}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/gitea-mirror ]]; then msg_error "No ${APP} Installation Found!" exit fi APP_VERSION=$(grep -o '"version": *"[^"]*"' /opt/gitea-mirror/package.json | cut -d'"' -f4) if [[ $APP_VERSION =~ ^2\. ]]; then if [[ "${PHS_SILENT:-0}" == "1" ]]; then msg_warn "Version $APP_VERSION detected. Major version upgrade requires interactive confirmation, skipping." exit 75 fi msg_warn "WARNING: Version $APP_VERSION detected!" msg_warn "Updating from version 2.x will CLEAR ALL CONFIGURATION." msg_warn "This includes: API tokens, User settings, Repository configurations, All custom settings" echo "" read -r -p "Do you want to continue? (y/N): " CONFIRM if [[ ! "$CONFIRM" =~ ^[Yy]$ ]]; then exit 0 fi msg_warn "FINAL WARNING: This update WILL clear all configuration!" msg_warn "Please ensure you have backed up API tokens, custom configurations, and repository settings." echo "" read -r -p "Final confirmation - proceed? (y/N): " CONFIRM2 if [[ ! "$CONFIRM2" =~ ^[Yy]$ ]]; then msg_info "Update cancelled. Please backup your configuration before proceeding." exit 0 fi msg_info "Proceeding with version $APP_VERSION update. All configuration will be cleared as warned." rm -rf /opt/gitea-mirror fi if [[ ! -f /opt/gitea-mirror.env ]]; then msg_info "Detected old Enviroment, updating files" APP_SECRET=$(openssl rand -base64 32) cat </opt/gitea-mirror.env # See here for config options: https://github.com/RayLabsHQ/gitea-mirror/blob/main/docs/ENVIRONMENT_VARIABLES.md NODE_ENV=production HOST=0.0.0.0 PORT=4321 DATABASE_URL=sqlite://data/gitea-mirror.db BETTER_AUTH_URL=http://${LOCAL_IP}:4321 BETTER_AUTH_SECRET=${APP_SECRET} npm_package_version=${APP_VERSION} EOF rm /etc/systemd/system/gitea-mirror.service cat </etc/systemd/system/gitea-mirror.service [Unit] Description=Gitea Mirror After=network.target [Service] Type=simple WorkingDirectory=/opt/gitea-mirror ExecStart=/usr/local/bin/bun dist/server/entry.mjs Restart=on-failure RestartSec=10 EnvironmentFile=/opt/gitea-mirror.env [Install] WantedBy=multi-user.target EOF systemctl daemon-reload msg_ok "Old Enviroment fixed" fi ensure_dependencies git if check_for_gh_release "gitea-mirror" "RayLabsHQ/gitea-mirror"; then msg_info "Stopping Services" systemctl stop gitea-mirror msg_ok "Services Stopped" msg_info "Backup Data" mkdir -p /opt/gitea-mirror-backup/data cp -r /opt/gitea-mirror/data/* /opt/gitea-mirror-backup/data/ msg_ok "Backup Data" msg_info "Installing Bun" export BUN_INSTALL=/opt/bun curl -fsSL https://bun.sh/install | $STD bash ln -sf /opt/bun/bin/bun /usr/local/bin/bun ln -sf /opt/bun/bin/bun /usr/local/bin/bunx msg_ok "Installed Bun" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "gitea-mirror" "RayLabsHQ/gitea-mirror" "tarball" msg_info "Updating and rebuilding ${APP}" cd /opt/gitea-mirror $STD bun run setup $STD bun run build APP_VERSION=$(grep -o '"version": *"[^"]*"' package.json | cut -d'"' -f4) sed -i.bak "s|^npm_package_version=.*|npm_package_version=${APP_VERSION}|" /opt/gitea-mirror.env msg_ok "Updated and rebuilt ${APP}" msg_info "Restoring Data" cp -r /opt/gitea-mirror-backup/data/* /opt/gitea-mirror/data msg_ok "Restored Data" msg_info "Starting Service" systemctl start gitea-mirror msg_ok "Service Started" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:4321${CL}" ================================================ FILE: ct/gitea.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) | Co-Author: Rogue-King # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://about.gitea.com/ APP="Gitea" var_tags="${var_tags:-git}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-1024}" var_disk="${var_disk:-8}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -f /usr/local/bin/gitea ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "gitea" "go-gitea/gitea"; then msg_info "Stopping service" systemctl stop gitea msg_ok "Service stopped" rm -rf /usr/local/bin/gitea fetch_and_deploy_gh_release "gitea" "go-gitea/gitea" "singlefile" "latest" "/usr/local/bin" "gitea-*-linux-arm64" chmod +x /usr/local/bin/gitea msg_info "Starting service" systemctl start gitea msg_ok "Started service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:3000${CL}" ================================================ FILE: ct/github-runner.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/actions/runner APP="GitHub-Runner" var_tags="${var_tags:-ci}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-8}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" var_nesting="${var_nesting:-1}" var_keyctl="${var_keyctl:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -f /opt/actions-runner/run.sh ]]; then msg_error "No ${APP} Installation Found!" exit 1 fi if check_for_gh_release "actions-runner" "actions/runner"; then msg_info "Stopping Service" systemctl stop actions-runner msg_ok "Stopped Service" msg_info "Backing up runner configuration" BACKUP_DIR="/opt/actions-runner.backup" mkdir -p "$BACKUP_DIR" for f in .runner .credentials .credentials_rsaparams .env .path; do [[ -f /opt/actions-runner/$f ]] && cp -a /opt/actions-runner/$f "$BACKUP_DIR/" done msg_ok "Backed up configuration" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "actions-runner" "actions/runner" "prebuild" "latest" "/opt/actions-runner" "actions-runner-linux-x64-*.tar.gz" msg_info "Restoring runner configuration" for f in .runner .credentials .credentials_rsaparams .env .path; do [[ -f "$BACKUP_DIR/$f" ]] && cp -a "$BACKUP_DIR/$f" /opt/actions-runner/ done rm -rf "$BACKUP_DIR" msg_ok "Restored configuration" msg_info "Starting Service" systemctl start actions-runner msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} After first boot, run config.sh with your token and start the service.${CL}" ================================================ FILE: ct/glance.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: kristocopani # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/glanceapp/glance APP="Glance" var_tags="${var_tags:-dashboard}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-512}" var_disk="${var_disk:-2}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -f /etc/systemd/system/glance.service ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "glance" "glanceapp/glance"; then msg_info "Stopping Service" systemctl stop glance msg_ok "Stopped Service" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "glance" "glanceapp/glance" "prebuild" "latest" "/opt/glance" "glance-linux-arm64.tar.gz" msg_info "Starting Service" systemctl start glance msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8080${CL}" ================================================ FILE: ct/globaleaks.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Giovanni Pellerano (evilaliv3) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/globaleaks/globaleaks-whistleblowing-software APP="GlobaLeaks" var_tags="${var_tags:-whistleblowing-software}" var_disk="${var_disk:-4}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_os="${var_os:-debian}" var_version="${var_version:-13}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -f /usr/sbin/globaleaks ]]; then msg_error "No ${APP} installation found!" exit fi msg_info "Updating $APP LXC" $STD apt update $STD apt -y upgrade msg_ok "Updated $APP LXC" msg_ok "Updated successfully!" } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN} ${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}https://${IP}${CL}" ================================================ FILE: ct/glpi.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Nícolas Pastorello (opastorello) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.glpi-project.org/ APP="GLPI" var_tags="${var_tags:-asset-management;foss}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-10}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/glpi ]]; then msg_error "No ${APP} Installation Found!" exit fi setup_mariadb RELEASE=$(curl -fsSL https://api.github.com/repos/glpi-project/glpi/releases/latest | grep '"tag_name"' | sed -E 's/.*"tag_name": "([^"]+)".*/\1/') if [[ ! -f /opt/${APP}_version.txt ]] || [[ "${RELEASE}" != "$(cat /opt/${APP}_version.txt)" ]]; then msg_error "Currently we don't provide an update function for this ${APP}." else msg_ok "No update required. ${APP} is already at v${RELEASE}." fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:80${CL}" ================================================ FILE: ct/gluetun.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/qdm12/gluetun APP="Gluetun" var_tags="${var_tags:-vpn;wireguard;openvpn}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-8}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" var_tun="${var_tun:-yes}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -f /usr/local/bin/gluetun ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "gluetun" "qdm12/gluetun"; then msg_info "Stopping Service" systemctl stop gluetun msg_ok "Stopped Service" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "gluetun" "qdm12/gluetun" "tarball" msg_info "Building Gluetun" cd /opt/gluetun $STD go mod download CGO_ENABLED=0 $STD go build -trimpath -ldflags="-s -w" -o /usr/local/bin/gluetun ./cmd/gluetun/ msg_ok "Built Gluetun" msg_info "Starting Service" systemctl start gluetun msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed Successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8000${CL}" ================================================ FILE: ct/go2rtc.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/AlexxIT/go2rtc APP="go2rtc" var_tags="${var_tags:-streaming;video}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" var_gpu="${var_gpu:-yes}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/go2rtc ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "go2rtc" "AlexxIT/go2rtc"; then msg_info "Stopping service" systemctl stop go2rtc msg_ok "Stopped service" fetch_and_deploy_gh_release "go2rtc" "AlexxIT/go2rtc" "singlefile" "latest" "/opt/go2rtc" "go2rtc_linux_arm64" msg_info "Starting service" systemctl start go2rtc msg_ok "Started service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:1984${CL}" ================================================ FILE: ct/gogs.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://gogs.io/ APP="Gogs" var_tags="${var_tags:-git;code;devops}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-1024}" var_disk="${var_disk:-8}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -f /opt/gogs/gogs ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "gogs" "gogs/gogs"; then msg_info "Stopping Service" systemctl stop gogs msg_ok "Stopped Service" msg_info "Backing up Data" cp -r /opt/gogs/custom /opt/gogs_custom_backup cp -r /opt/gogs/data /opt/gogs_data_backup msg_ok "Backed up Data" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "gogs" "gogs/gogs" "prebuild" "latest" "/opt/gogs" "gogs_*_linux_amd64.tar.gz" msg_info "Restoring Data" cp -r /opt/gogs_custom_backup/. /opt/gogs/custom cp -r /opt/gogs_data_backup/. /opt/gogs/data rm -rf /opt/gogs_custom_backup /opt/gogs_data_backup msg_ok "Restored Data" msg_info "Starting Service" systemctl start gogs msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed Successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:3000${CL}" ================================================ FILE: ct/gokapi.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/Forceu/Gokapi APP="Gokapi" var_tags="${var_tags:-file;sharing}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-512}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/gokapi ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "gokapi" "Forceu/Gokapi"; then msg_info "Stopping Service" systemctl stop gokapi msg_ok "Stopped Service" fetch_and_deploy_gh_release "gokapi" "Forceu/Gokapi" "prebuild" "latest" "/opt/gokapi" "gokapi-linux_arm64.zip" msg_info "Starting Service" systemctl start gokapi msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:53842/setup${CL}" ================================================ FILE: ct/gotify.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://gotify.net/ APP="Gotify" var_tags="${var_tags:-notification}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-512}" var_disk="${var_disk:-2}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/gotify ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "gotify" "gotify/server"; then msg_info "Stopping Service" systemctl stop gotify msg_ok "Stopped Service" fetch_and_deploy_gh_release "gotify" "gotify/server" "prebuild" "latest" "/opt/gotify" "gotify-linux-arm64.zip" chmod +x /opt/gotify/gotify-linux-arm64 msg_info "Starting Service" systemctl start gotify msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}${CL}" ================================================ FILE: ct/grafana.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://grafana.com/ APP="Grafana" var_tags="${var_tags:-monitoring;visualization}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-512}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if ! dpkg -s grafana >/dev/null 2>&1; then msg_error "No ${APP} Installation Found!" exit 233 fi if [[ -f /etc/apt/sources.list.d/grafana.list ]] || [[ ! -f /etc/apt/sources.list.d/grafana.sources ]]; then setup_deb822_repo \ "grafana" \ "https://apt.grafana.com/gpg.key" \ "https://apt.grafana.com" \ "stable" \ "main" fi msg_info "Updating Grafana LXC" $STD apt update $STD apt --only-upgrade install -y grafana msg_ok "Updated successfully!" exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:3000${CL}" ================================================ FILE: ct/gramps-web.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.grampsweb.org/ | Github: https://github.com/gramps-project/gramps-web APP="gramps-web" var_tags="${var_tags:-genealogy;family;collaboration}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-4096}" var_disk="${var_disk:-20}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/gramps-web-api ]] || [[ ! -d /opt/gramps-web/frontend ]]; then msg_error "No ${APP} Installation Found!" exit fi PYTHON_VERSION="3.12" setup_uv NODE_VERSION="22" setup_nodejs if check_for_gh_release "gramps-web-api" "gramps-project/gramps-web-api"; then msg_info "Stopping Service" systemctl stop gramps-web msg_ok "Stopped Service" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "gramps-web-api" "gramps-project/gramps-web-api" "tarball" "latest" "/opt/gramps-web-api" msg_info "Updating Gramps Web API" $STD uv venv -c -p python3.12 /opt/gramps-web/venv source /opt/gramps-web/venv/bin/activate $STD uv pip install --no-cache-dir --upgrade pip setuptools wheel $STD uv pip install --no-cache-dir gunicorn $STD uv pip install --no-cache-dir /opt/gramps-web-api msg_ok "Updated Gramps Web API" msg_info "Applying Database Migration" cd /opt/gramps-web-api GRAMPS_API_CONFIG=/opt/gramps-web/config/config.cfg \ ALEMBIC_CONFIG=/opt/gramps-web-api/alembic.ini \ GRAMPSHOME=/opt/gramps-web/data \ GRAMPS_DATABASE_PATH=/opt/gramps-web/data/gramps/grampsdb \ $STD /opt/gramps-web/venv/bin/python3 -m gramps_webapi user migrate msg_ok "Applied Database Migration" msg_info "Updating Gramps Addons" GRAMPS_VERSION=$(/opt/gramps-web/venv/bin/python3 -c "import gramps.version; print('%s%s' % (gramps.version.VERSION_TUPLE[0], gramps.version.VERSION_TUPLE[1]))" 2>/dev/null || echo "60") GRAMPS_PLUGINS_DIR="/opt/gramps-web/data/gramps/gramps${GRAMPS_VERSION}/plugins" mkdir -p "$GRAMPS_PLUGINS_DIR" $STD wget -q https://github.com/gramps-project/addons/archive/refs/heads/master.zip -O /tmp/gramps-addons.zip for addon in FilterRules JSON; do unzip -p /tmp/gramps-addons.zip "addons-master/gramps${GRAMPS_VERSION}/download/${addon}.addon.tgz" | tar -xz -C "$GRAMPS_PLUGINS_DIR" done rm -f /tmp/gramps-addons.zip msg_ok "Updated Gramps Addons" msg_info "Starting Service" systemctl start gramps-web msg_ok "Started Service" fi if check_for_gh_release "gramps-web" "gramps-project/gramps-web"; then msg_info "Stopping Service" systemctl stop gramps-web msg_ok "Stopped Service" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "gramps-web" "gramps-project/gramps-web" "tarball" "latest" "/opt/gramps-web/frontend" msg_info "Updating Gramps Web Frontend" cd /opt/gramps-web/frontend export COREPACK_ENABLE_DOWNLOAD_PROMPT=0 $STD corepack enable $STD npm install $STD npm run build msg_ok "Updated Gramps Web Frontend" msg_info "Starting Service" systemctl start gramps-web msg_ok "Started Service" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:5000${CL}" ================================================ FILE: ct/graylog.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Slaviša Arežina (tremor021) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://graylog.org/ APP="Graylog" var_tags="${var_tags:-logging}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-8192}" var_disk="${var_disk:-30}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /etc/graylog ]]; then msg_error "No ${APP} Installation Found!" exit fi msg_info "Stopping Service" systemctl stop graylog-datanode systemctl stop graylog-server msg_info "Stopped Service" CURRENT_VERSION=$(apt list --installed 2>/dev/null | grep graylog-server | grep -oP '\d+\.\d+\.\d+') if dpkg --compare-versions "$CURRENT_VERSION" lt "6.3"; then MONGO_VERSION="8.2" setup_mongodb msg_info "Updating Graylog" $STD apt update $STD apt upgrade -y curl -fsSL "https://packages.graylog2.org/repo/packages/graylog-7.0-repository_latest.deb" -o "graylog-7.0-repository_latest.deb" $STD dpkg -i graylog-7.0-repository_latest.deb $STD apt update ensure_dependencies graylog-server graylog-datanode rm -f graylog-7.0-repository_latest.deb msg_ok "Updated Graylog" elif dpkg --compare-versions "$CURRENT_VERSION" ge "7.0"; then msg_info "Updating Graylog" $STD apt update $STD apt upgrade -y msg_ok "Updated Graylog" fi msg_info "Starting Service" systemctl start graylog-datanode systemctl start graylog-server msg_ok "Started Service" msg_ok "Updated successfully!" exit } start if [[ $(sysctl -n vm.max_map_count 2>/dev/null) -lt 262144 ]]; then sysctl -w vm.max_map_count=262144 >/dev/null 2>&1 echo "vm.max_map_count=262144" >/etc/sysctl.d/graylog.conf fi build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:9000${CL}" ================================================ FILE: ct/grist.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: cfurrow | Co-Author: Slaviša Arežina (tremor021) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE APP="Grist" var_tags="${var_tags:-database;spreadsheet}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-3072}" var_disk="${var_disk:-6}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/grist ]]; then msg_error "No ${APP} Installation Found!" exit fi ensure_dependencies git if check_for_gh_release "grist" "gristlabs/grist-core"; then msg_info "Stopping Service" systemctl stop grist msg_ok "Stopped Service" msg_info "Creating backup" rm -rf /opt/grist_bak mv /opt/grist /opt/grist_bak msg_ok "Backup created" fetch_and_deploy_gh_release "grist" "gristlabs/grist-core" "tarball" msg_info "Updating Grist" mkdir -p /opt/grist/docs cp -n /opt/grist_bak/.env /opt/grist/.env if ls /opt/grist_bak/docs/* &>/dev/null; then cp -r /opt/grist_bak/docs/* /opt/grist/docs/ fi [[ -f /opt/grist_bak/grist-sessions.db ]] && cp /opt/grist_bak/grist-sessions.db /opt/grist/grist-sessions.db [[ -f /opt/grist_bak/landing.db ]] && cp /opt/grist_bak/landing.db /opt/grist/landing.db cd /opt/grist $STD yarn install $STD yarn run build:prod $STD yarn run install:python msg_ok "Updated Grist" msg_info "Starting Service" systemctl start grist msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}Grist: http://${IP}:8484${CL}" ================================================ FILE: ct/grocy.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://grocy.info/ APP="grocy" var_tags="${var_tags:-grocery;household}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-512}" var_disk="${var_disk:-2}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -f /etc/apache2/sites-available/grocy.conf ]]; then msg_error "No ${APP} Installation Found!" exit fi php_ver=$(php -v | head -n 1 | awk '{print $2}') if [[ ! $php_ver == "8.5"* ]]; then PHP_VERSION="8.5" PHP_APACHE="YES" setup_php fi if check_for_gh_release "grocy" "grocy/grocy"; then msg_info "Updating grocy" bash /var/www/html/update.sh msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}${CL}" ================================================ FILE: ct/guardian.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: HydroshieldMKII # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/HydroshieldMKII/Guardian APP="Guardian" var_tags="${var_tags:-media;monitoring}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-6}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d "/opt/guardian" ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "guardian" "HydroshieldMKII/Guardian"; then msg_info "Stopping Services" systemctl stop guardian-backend guardian-frontend msg_ok "Stopped Services" if [[ -f "/opt/guardian/backend/plex-guard.db" ]]; then msg_info "Backing up Database" cp "/opt/guardian/backend/plex-guard.db" "/tmp/plex-guard.db.backup" msg_ok "Backed up Database" fi [[ -f "/opt/guardian/.env" ]] && cp "/opt/guardian/.env" "/opt" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "guardian" "HydroshieldMKII/Guardian" "tarball" "latest" "/opt/guardian" [[ -f "/opt/.env" ]] && mv "/opt/.env" "/opt/guardian" if [[ -f "/tmp/plex-guard.db.backup" ]]; then msg_info "Restoring Database" cp "/tmp/plex-guard.db.backup" "/opt/guardian/backend/plex-guard.db" rm "/tmp/plex-guard.db.backup" msg_ok "Restored Database" fi msg_info "Updating Guardian" cd /opt/guardian/backend $STD npm ci $STD npm run build cd /opt/guardian/frontend $STD npm ci export DEPLOYMENT_MODE=standalone $STD npm run build msg_ok "Updated Guardian" msg_info "Starting Services" systemctl start guardian-backend guardian-frontend msg_ok "Started Services" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:3000${CL}" ================================================ FILE: ct/gwn-manager.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Slaviša Arežina (tremor021) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.grandstream.com/products/networking-solutions/wi-fi-management/product/gwn-manager APP="GWN-Manager" var_tags="${var_tags:-network;management}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-6144}" var_disk="${var_disk:-8}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /gwn ]]; then msg_error "No ${APP} Installation Found!" exit fi msg_custom "🚀" "${GN}" "The app offers a built-in updater. Please use it." exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}https://${IP}:8443${CL}" ================================================ FILE: ct/headers/2fauth ================================================ ___ _________ __ __ |__ \ / ____/ | __ __/ /_/ /_ __/ // /_ / /| |/ / / / __/ __ \ / __// __/ / ___ / /_/ / /_/ / / / /____/_/ /_/ |_\__,_/\__/_/ /_/ ================================================ FILE: ct/headers/actualbudget ================================================ ___ __ __ ____ __ __ / | _____/ /___ ______ _/ / / __ )__ ______/ /___ ____ / /_ / /| |/ ___/ __/ / / / __ `/ / / __ / / / / __ / __ `/ _ \/ __/ / ___ / /__/ /_/ /_/ / /_/ / / / /_/ / /_/ / /_/ / /_/ / __/ /_ /_/ |_\___/\__/\__,_/\__,_/_/ /_____/\__,_/\__,_/\__, /\___/\__/ /____/ ================================================ FILE: ct/headers/adguard ================================================ ___ __ __ / | ____/ /___ ___ ______ __________/ / / /| |/ __ / __ `/ / / / __ `/ ___/ __ / / ___ / /_/ / /_/ / /_/ / /_/ / / / /_/ / /_/ |_\__,_/\__, /\__,_/\__,_/_/ \__,_/ /____/ ================================================ FILE: ct/headers/adventurelog ================================================ ___ __ __ __ / | ____/ / _____ ____ / /___ __________ / / ____ ____ _ / /| |/ __ / | / / _ \/ __ \/ __/ / / / ___/ _ \/ / / __ \/ __ `/ / ___ / /_/ /| |/ / __/ / / / /_/ /_/ / / / __/ /___/ /_/ / /_/ / /_/ |_\__,_/ |___/\___/_/ /_/\__/\__,_/_/ \___/_____/\____/\__, / /____/ ================================================ FILE: ct/headers/agentdvr ================================================ ___ __ ____ _ ______ / | ____ ____ ____ / /_/ __ \ | / / __ \ / /| |/ __ `/ _ \/ __ \/ __/ / / / | / / /_/ / / ___ / /_/ / __/ / / / /_/ /_/ /| |/ / _, _/ /_/ |_\__, /\___/_/ /_/\__/_____/ |___/_/ |_| /____/ ================================================ FILE: ct/headers/alpine ================================================ ___ __ _ / | / /___ (_)___ ___ / /| | / / __ \/ / __ \/ _ \ / ___ |/ / /_/ / / / / / __/ /_/ |_/_/ .___/_/_/ /_/\___/ /_/ ================================================ FILE: ct/headers/alpine-adguard ================================================ ___ __ _ ___ ________ __ / | / /___ (_)___ ___ / | ____/ / ____/_ ______ __________/ / / /| | / / __ \/ / __ \/ _ \______/ /| |/ __ / / __/ / / / __ `/ ___/ __ / / ___ |/ / /_/ / / / / / __/_____/ ___ / /_/ / /_/ / /_/ / /_/ / / / /_/ / /_/ |_/_/ .___/_/_/ /_/\___/ /_/ |_\__,_/\____/\__,_/\__,_/_/ \__,_/ /_/ ================================================ FILE: ct/headers/alpine-bitmagnet ================================================ ___ __ _ __ _ __ __ / | / /___ (_)___ ___ / /_ (_) /_____ ___ ____ _____ _____ ___ / /_ / /| | / / __ \/ / __ \/ _ \______/ __ \/ / __/ __ `__ \/ __ `/ __ `/ __ \/ _ \/ __/ / ___ |/ / /_/ / / / / / __/_____/ /_/ / / /_/ / / / / / /_/ / /_/ / / / / __/ /_ /_/ |_/_/ .___/_/_/ /_/\___/ /_.___/_/\__/_/ /_/ /_/\__,_/\__, /_/ /_/\___/\__/ /_/ /____/ ================================================ FILE: ct/headers/alpine-borgbackup-server ================================================ ___ __ _ ____ ____ __ _____ / | / /___ (_)___ ___ / __ )____ _________ _/ __ )____ ______/ /____ ______ / ___/___ ______ _____ _____ / /| | / / __ \/ / __ \/ _ \______/ __ / __ \/ ___/ __ `/ __ / __ `/ ___/ //_/ / / / __ \______\__ \/ _ \/ ___/ | / / _ \/ ___/ / ___ |/ / /_/ / / / / / __/_____/ /_/ / /_/ / / / /_/ / /_/ / /_/ / /__/ ,< / /_/ / /_/ /_____/__/ / __/ / | |/ / __/ / /_/ |_/_/ .___/_/_/ /_/\___/ /_____/\____/_/ \__, /_____/\__,_/\___/_/|_|\__,_/ .___/ /____/\___/_/ |___/\___/_/ /_/ /____/ /_/ ================================================ FILE: ct/headers/alpine-caddy ================================================ ___ __ _ ______ __ __ / | / /___ (_)___ ___ / ____/___ _____/ /___/ /_ __ / /| | / / __ \/ / __ \/ _ \______/ / / __ `/ __ / __ / / / / / ___ |/ / /_/ / / / / / __/_____/ /___/ /_/ / /_/ / /_/ / /_/ / /_/ |_/_/ .___/_/_/ /_/\___/ \____/\__,_/\__,_/\__,_/\__, / /_/ /____/ ================================================ FILE: ct/headers/alpine-docker ================================================ ___ __ _ ____ __ / | / /___ (_)___ ___ / __ \____ _____/ /_____ _____ / /| | / / __ \/ / __ \/ _ \______/ / / / __ \/ ___/ //_/ _ \/ ___/ / ___ |/ / /_/ / / / / / __/_____/ /_/ / /_/ / /__/ ,< / __/ / /_/ |_/_/ .___/_/_/ /_/\___/ /_____/\____/\___/_/|_|\___/_/ /_/ ================================================ FILE: ct/headers/alpine-forgejo ================================================ ___ __ _ ______ _ / | / /___ (_)___ ___ / ____/___ _________ ____ (_)___ / /| | / / __ \/ / __ \/ _ \______/ /_ / __ \/ ___/ __ `/ _ \ / / __ \ / ___ |/ / /_/ / / / / / __/_____/ __/ / /_/ / / / /_/ / __/ / / /_/ / /_/ |_/_/ .___/_/_/ /_/\___/ /_/ \____/_/ \__, /\___/_/ /\____/ /_/ /____/ /___/ ================================================ FILE: ct/headers/alpine-garage ================================================ ___ __ _ ______ / | / /___ (_)___ ___ / ____/___ __________ _____ ____ / /| | / / __ \/ / __ \/ _ \______/ / __/ __ `/ ___/ __ `/ __ `/ _ \ / ___ |/ / /_/ / / / / / __/_____/ /_/ / /_/ / / / /_/ / /_/ / __/ /_/ |_/_/ .___/_/_/ /_/\___/ \____/\__,_/_/ \__,_/\__, /\___/ /_/ /____/ ================================================ FILE: ct/headers/alpine-gatus ================================================ ___ __ _ __ / | / /___ (_)___ ___ ____ _____ _/ /___ _______ / /| | / / __ \/ / __ \/ _ \______/ __ `/ __ `/ __/ / / / ___/ / ___ |/ / /_/ / / / / / __/_____/ /_/ / /_/ / /_/ /_/ (__ ) /_/ |_/_/ .___/_/_/ /_/\___/ \__, /\__,_/\__/\__,_/____/ /_/ /____/ ================================================ FILE: ct/headers/alpine-gitea ================================================ ___ __ _ _______ __ / | / /___ (_)___ ___ / ____(_) /____ ____ _ / /| | / / __ \/ / __ \/ _ \______/ / __/ / __/ _ \/ __ `/ / ___ |/ / /_/ / / / / / __/_____/ /_/ / / /_/ __/ /_/ / /_/ |_/_/ .___/_/_/ /_/\___/ \____/_/\__/\___/\__,_/ /_/ ================================================ FILE: ct/headers/alpine-grafana ================================================ ___ __ _ ______ ____ / | / /___ (_)___ ___ / ____/________ _/ __/___ _____ ____ _ / /| | / / __ \/ / __ \/ _ \______/ / __/ ___/ __ `/ /_/ __ `/ __ \/ __ `/ / ___ |/ / /_/ / / / / / __/_____/ /_/ / / / /_/ / __/ /_/ / / / / /_/ / /_/ |_/_/ .___/_/_/ /_/\___/ \____/_/ \__,_/_/ \__,_/_/ /_/\__,_/ /_/ ================================================ FILE: ct/headers/alpine-ironclaw ================================================ ___ __ _ ____ ________ / | / /___ (_)___ ___ / _/________ ____ / ____/ /___ __ __ / /| | / / __ \/ / __ \/ _ \______ / // ___/ __ \/ __ \/ / / / __ `/ | /| / / / ___ |/ / /_/ / / / / / __/_____// // / / /_/ / / / / /___/ / /_/ /| |/ |/ / /_/ |_/_/ .___/_/_/ /_/\___/ /___/_/ \____/_/ /_/\____/_/\__,_/ |__/|__/ /_/ ================================================ FILE: ct/headers/alpine-it-tools ================================================ ___ __ _ __________ ______ __ / | / /___ (_)___ ___ / _/_ __/ /_ __/___ ____ / /____ / /| | / / __ \/ / __ \/ _ \______ / / / /_____/ / / __ \/ __ \/ / ___/ / ___ |/ / /_/ / / / / / __/_____// / / /_____/ / / /_/ / /_/ / (__ ) /_/ |_/_/ .___/_/_/ /_/\___/ /___/ /_/ /_/ \____/\____/_/____/ /_/ ================================================ FILE: ct/headers/alpine-komodo ================================================ ___ __ _ __ __ __ / | / /___ (_)___ ___ / //_/___ ____ ___ ____ ____/ /___ / /| | / / __ \/ / __ \/ _ \______/ ,< / __ \/ __ `__ \/ __ \/ __ / __ \ / ___ |/ / /_/ / / / / / __/_____/ /| / /_/ / / / / / / /_/ / /_/ / /_/ / /_/ |_/_/ .___/_/_/ /_/\___/ /_/ |_\____/_/ /_/ /_/\____/\__,_/\____/ /_/ ================================================ FILE: ct/headers/alpine-loki ================================================ ___ __ _ __ __ _ / | / /___ (_)___ ___ / / ____ / /__(_) / /| | / / __ \/ / __ \/ _ \______/ / / __ \/ //_/ / / ___ |/ / /_/ / / / / / __/_____/ /___/ /_/ / ,< / / /_/ |_/_/ .___/_/_/ /_/\___/ /_____/\____/_/|_/_/ /_/ ================================================ FILE: ct/headers/alpine-mariadb ================================================ ___ __ _ __ ___ _ ____ ____ / | / /___ (_)___ ___ / |/ /___ ______(_)___ _/ __ \/ __ ) / /| | / / __ \/ / __ \/ _ \______/ /|_/ / __ `/ ___/ / __ `/ / / / __ | / ___ |/ / /_/ / / / / / __/_____/ / / / /_/ / / / / /_/ / /_/ / /_/ / /_/ |_/_/ .___/_/_/ /_/\___/ /_/ /_/\__,_/_/ /_/\__,_/_____/_____/ /_/ ================================================ FILE: ct/headers/alpine-nextcloud ================================================ ___ __ _ _ __ __ __ __ / | / /___ (_)___ ___ / | / /__ _ __/ /______/ /___ __ ______/ / / /| | / / __ \/ / __ \/ _ \______/ |/ / _ \| |/_/ __/ ___/ / __ \/ / / / __ / / ___ |/ / /_/ / / / / / __/_____/ /| / __/> < /_/ |_/_/ \___/_/ /_/_/ |___/\___/_____/\____/_/|_| ================================================ FILE: ct/headers/argus ================================================ ___ / | _________ ___ _______ / /| | / ___/ __ `/ / / / ___/ / ___ |/ / / /_/ / /_/ (__ ) /_/ |_/_/ \__, /\__,_/____/ /____/ ================================================ FILE: ct/headers/aria2 ================================================ ___ _ ___ / | _____(_)___ |__ \ / /| | / ___/ / __ `/_/ / / ___ |/ / / / /_/ / __/ /_/ |_/_/ /_/\__,_/____/ ================================================ FILE: ct/headers/asterisk ================================================ ___ __ _ __ / | _____/ /____ _____(_)____/ /__ / /| | / ___/ __/ _ \/ ___/ / ___/ //_/ / ___ |(__ ) /_/ __/ / / (__ ) ,< /_/ |_/____/\__/\___/_/ /_/____/_/|_| ================================================ FILE: ct/headers/audiobookshelf ================================================ ___ __ __ __ ______ ____ ___ ______/ (_)___ / /_ ____ ____ / /_______/ /_ ___ / / __/ / __ `/ / / / __ / / __ \/ __ \/ __ \/ __ \/ //_/ ___/ __ \/ _ \/ / /_ / /_/ / /_/ / /_/ / / /_/ / /_/ / /_/ / /_/ / ,< (__ ) / / / __/ / __/ \__,_/\__,_/\__,_/_/\____/_.___/\____/\____/_/|_/____/_/ /_/\___/_/_/ ================================================ FILE: ct/headers/authelia ================================================ ___ __ __ ___ / | __ __/ /_/ /_ ___ / (_)___ _ / /| |/ / / / __/ __ \/ _ \/ / / __ `/ / ___ / /_/ / /_/ / / / __/ / / /_/ / /_/ |_\__,_/\__/_/ /_/\___/_/_/\__,_/ ================================================ FILE: ct/headers/autobrr ================================================ ___ __ __ / | __ __/ /_____ / /_ __________ / /| |/ / / / __/ __ \/ __ \/ ___/ ___/ / ___ / /_/ / /_/ /_/ / /_/ / / / / /_/ |_\__,_/\__/\____/_.___/_/ /_/ ================================================ FILE: ct/headers/autocaliweb ================================================ ___ __ ___ __ / | __ __/ /_____ _________ _/ (_) _____ / /_ / /| |/ / / / __/ __ \/ ___/ __ `/ / / | /| / / _ \/ __ \ / ___ / /_/ / /_/ /_/ / /__/ /_/ / / /| |/ |/ / __/ /_/ / /_/ |_\__,_/\__/\____/\___/\__,_/_/_/ |__/|__/\___/_.___/ ================================================ FILE: ct/headers/babybuddy ================================================ ____ __ ____ __ __ / __ )____ _/ /_ __ __ / __ )__ ______/ /___/ /_ __ / __ / __ `/ __ \/ / / / / __ / / / / __ / __ / / / / / /_/ / /_/ / /_/ / /_/ / / /_/ / /_/ / /_/ / /_/ / /_/ / /_____/\__,_/_.___/\__, / /_____/\__,_/\__,_/\__,_/\__, / /____/ /____/ ================================================ FILE: ct/headers/backrest ================================================ ____ __ __ / __ )____ ______/ /__________ _____/ /_ / __ / __ `/ ___/ //_/ ___/ _ \/ ___/ __/ / /_/ / /_/ / /__/ ,< / / / __(__ ) /_ /_____/\__,_/\___/_/|_/_/ \___/____/\__/ ================================================ FILE: ct/headers/baikal ================================================ ____ _ __ __ / __ )____ _(_) /______ _/ / / __ / __ `/ / //_/ __ `/ / / /_/ / /_/ / / ,< / /_/ / / /_____/\__,_/_/_/|_|\__,_/_/ ================================================ FILE: ct/headers/bambuddy ================================================ ____ __ __ __ / __ )____ _____ ___ / /_ __ ______/ /___/ /_ __ / __ / __ `/ __ `__ \/ __ \/ / / / __ / __ / / / / / /_/ / /_/ / / / / / / /_/ / /_/ / /_/ / /_/ / /_/ / /_____/\__,_/_/ /_/ /_/_.___/\__,_/\__,_/\__,_/\__, / /____/ ================================================ FILE: ct/headers/bar-assistant ================================================ ____ ___ _ __ __ / __ )____ ______ / | __________(_)____/ /_____ _____ / /_ / __ / __ `/ ___/_____/ /| | / ___/ ___/ / ___/ __/ __ `/ __ \/ __/ / /_/ / /_/ / / /_____/ ___ |(__ |__ ) (__ ) /_/ /_/ / / / / /_ /_____/\__,_/_/ /_/ |_/____/____/_/____/\__/\__,_/_/ /_/\__/ ================================================ FILE: ct/headers/bazarr ================================================ ____ / __ )____ _____ ____ ___________ / __ / __ `/_ / / __ `/ ___/ ___/ / /_/ / /_/ / / /_/ /_/ / / / / /_____/\__,_/ /___/\__,_/_/ /_/ ================================================ FILE: ct/headers/bentopdf ================================================ ____ __ ____ ____ ______ / __ )___ ____ / /_____ / __ \/ __ \/ ____/ / __ / _ \/ __ \/ __/ __ \/ /_/ / / / / /_ / /_/ / __/ / / / /_/ /_/ / ____/ /_/ / __/ /_____/\___/_/ /_/\__/\____/_/ /_____/_/ ================================================ FILE: ct/headers/beszel ================================================ ____ __ / __ )___ _________ ___ / / / __ / _ \/ ___/_ / / _ \/ / / /_/ / __(__ ) / /_/ __/ / /_____/\___/____/ /___/\___/_/ ================================================ FILE: ct/headers/bichon ================================================ ____ _ __ / __ )(_)____/ /_ ____ ____ / __ / / ___/ __ \/ __ \/ __ \ / /_/ / / /__/ / / / /_/ / / / / /_____/_/\___/_/ /_/\____/_/ /_/ ================================================ FILE: ct/headers/birdnet-go ================================================ ____ _ ___ ______________ ______ / __ )(_)________/ / | / / ____/_ __/ / ____/___ / __ / / ___/ __ / |/ / __/ / /_____/ / __/ __ \ / /_/ / / / / /_/ / /| / /___ / /_____/ /_/ / /_/ / /_____/_/_/ \__,_/_/ |_/_____/ /_/ \____/\____/ ================================================ FILE: ct/headers/bitmagnet ================================================ ____ _ __ __ / __ )(_) /_____ ___ ____ _____ _____ ___ / /_ / __ / / __/ __ `__ \/ __ `/ __ `/ __ \/ _ \/ __/ / /_/ / / /_/ / / / / / /_/ / /_/ / / / / __/ /_ /_____/_/\__/_/ /_/ /_/\__,_/\__, /_/ /_/\___/\__/ /____/ ================================================ FILE: ct/headers/blocky ================================================ ____ __ __ / __ )/ /___ _____/ /____ __ / __ / / __ \/ ___/ //_/ / / / / /_/ / / /_/ / /__/ ,< / /_/ / /_____/_/\____/\___/_/|_|\__, / /____/ ================================================ FILE: ct/headers/bookstack ================================================ ____ __ __ __ / __ )____ ____ / /_______/ /_____ ______/ /__ / __ / __ \/ __ \/ //_/ ___/ __/ __ `/ ___/ //_/ / /_/ / /_/ / /_/ / ,< (__ ) /_/ /_/ / /__/ ,< /_____/\____/\____/_/|_/____/\__/\__,_/\___/_/|_| ================================================ FILE: ct/headers/bunkerweb ================================================ ____ __ _ __ __ / __ )__ ______ / /_____ ____| | / /__ / /_ / __ / / / / __ \/ //_/ _ \/ ___/ | /| / / _ \/ __ \ / /_/ / /_/ / / / / ,< / __/ / | |/ |/ / __/ /_/ / /_____/\__,_/_/ /_/_/|_|\___/_/ |__/|__/\___/_.___/ ================================================ FILE: ct/headers/byparr ================================================ ____ / __ )__ ______ ____ ___________ / __ / / / / __ \/ __ `/ ___/ ___/ / /_/ / /_/ / /_/ / /_/ / / / / /_____/\__, / .___/\__,_/_/ /_/ /____/_/ ================================================ FILE: ct/headers/bytestash ================================================ ____ __ _____ __ __ / __ )__ __/ /____ / ___// /_____ ______/ /_ / __ / / / / __/ _ \\__ \/ __/ __ `/ ___/ __ \ / /_/ / /_/ / /_/ __/__/ / /_/ /_/ (__ ) / / / /_____/\__, /\__/\___/____/\__/\__,_/____/_/ /_/ /____/ ================================================ FILE: ct/headers/caddy ================================================ ______ __ __ / ____/___ _____/ /___/ /_ __ / / / __ `/ __ / __ / / / / / /___/ /_/ / /_/ / /_/ / /_/ / \____/\__,_/\__,_/\__,_/\__, / /____/ ================================================ FILE: ct/headers/calibre-web ================================================ ___ __ __ _________ _/ (_) /_ ________ _ _____ / /_ / ___/ __ `/ / / __ \/ ___/ _ \_____| | /| / / _ \/ __ \ / /__/ /_/ / / / /_/ / / / __/_____/ |/ |/ / __/ /_/ / \___/\__,_/_/_/_.___/_/ \___/ |__/|__/\___/_.___/ ================================================ FILE: ct/headers/casaos ================================================ ______ ____ _____ / ____/___ __________ _/ __ \/ ___/ / / / __ `/ ___/ __ `/ / / /\__ \ / /___/ /_/ (__ ) /_/ / /_/ /___/ / \____/\__,_/____/\__,_/\____//____/ ================================================ FILE: ct/headers/changedetection ================================================ ________ ____ __ __ _ / ____/ /_ ____ _____ ____ ____ / __ \___ / /____ _____/ /_(_)___ ____ / / / __ \/ __ `/ __ \/ __ `/ _ \ / / / / _ \/ __/ _ \/ ___/ __/ / __ \/ __ \ / /___/ / / / /_/ / / / / /_/ / __/ / /_/ / __/ /_/ __/ /__/ /_/ / /_/ / / / / \____/_/ /_/\__,_/_/ /_/\__, /\___/ /_____/\___/\__/\___/\___/\__/_/\____/_/ /_/ /____/ ================================================ FILE: ct/headers/channels ================================================ ________ __ / ____/ /_ ____ _____ ____ ___ / /____ / / / __ \/ __ `/ __ \/ __ \/ _ \/ / ___/ / /___/ / / / /_/ / / / / / / / __/ (__ ) \____/_/ /_/\__,_/_/ /_/_/ /_/\___/_/____/ ================================================ FILE: ct/headers/checkmate ================================================ ________ __ __ / ____/ /_ ___ _____/ /______ ___ ____ _/ /____ / / / __ \/ _ \/ ___/ //_/ __ `__ \/ __ `/ __/ _ \ / /___/ / / / __/ /__/ ,< / / / / / / /_/ / /_/ __/ \____/_/ /_/\___/\___/_/|_/_/ /_/ /_/\__,_/\__/\___/ ================================================ FILE: ct/headers/checkmk ================================================ __ __ __ _____/ /_ ___ _____/ /______ ___ / /__ / ___/ __ \/ _ \/ ___/ //_/ __ `__ \/ //_/ / /__/ / / / __/ /__/ ,< / / / / / / ,< \___/_/ /_/\___/\___/_/|_/_/ /_/ /_/_/|_| ================================================ FILE: ct/headers/cleanuparr ================================================ ________ / ____/ /__ ____ _____ __ ______ ____ ___________ / / / / _ \/ __ `/ __ \/ / / / __ \/ __ `/ ___/ ___/ / /___/ / __/ /_/ / / / / /_/ / /_/ / /_/ / / / / \____/_/\___/\__,_/_/ /_/\__,_/ .___/\__,_/_/ /_/ /_/ ================================================ FILE: ct/headers/cloudflare-ddns ================================================ ________ ________ ____ ____ _ _______ / ____/ /___ __ ______/ / __/ /___ _________ / __ \/ __ \/ | / / ___/ / / / / __ \/ / / / __ / /_/ / __ `/ ___/ _ \______/ / / / / / / |/ /\__ \ / /___/ / /_/ / /_/ / /_/ / __/ / /_/ / / / __/_____/ /_/ / /_/ / /| /___/ / \____/_/\____/\__,_/\__,_/_/ /_/\__,_/_/ \___/ /_____/_____/_/ |_//____/ ================================================ FILE: ct/headers/cloudflared ================================================ ________ ________ __ / ____/ /___ __ ______/ / __/ /___ _________ ____/ / / / / / __ \/ / / / __ / /_/ / __ `/ ___/ _ \/ __ / / /___/ / /_/ / /_/ / /_/ / __/ / /_/ / / / __/ /_/ / \____/_/\____/\__,_/\__,_/_/ /_/\__,_/_/ \___/\__,_/ ================================================ FILE: ct/headers/cloudreve ================================================ ________ __ / ____/ /___ __ ______/ /_______ _ _____ / / / / __ \/ / / / __ / ___/ _ \ | / / _ \ / /___/ / /_/ / /_/ / /_/ / / / __/ |/ / __/ \____/_/\____/\__,_/\__,_/_/ \___/|___/\___/ ================================================ FILE: ct/headers/cockpit ================================================ ______ __ _ __ / ____/___ _____/ /______ (_) /_ / / / __ \/ ___/ //_/ __ \/ / __/ / /___/ /_/ / /__/ ,< / /_/ / / /_ \____/\____/\___/_/|_/ .___/_/\__/ /_/ ================================================ FILE: ct/headers/comfyui ================================================ ______ ____ __ ______ / ____/___ ____ ___ / __/_ __/ / / / _/ / / / __ \/ __ `__ \/ /_/ / / / / / // / / /___/ /_/ / / / / / / __/ /_/ / /_/ // / \____/\____/_/ /_/ /_/_/ \__, /\____/___/ /____/ ================================================ FILE: ct/headers/commafeed ================================================ ______ ______ __ / ____/___ ____ ___ ____ ___ ____ _/ ____/__ ___ ____/ / / / / __ \/ __ `__ \/ __ `__ \/ __ `/ /_ / _ \/ _ \/ __ / / /___/ /_/ / / / / / / / / / / / /_/ / __/ / __/ __/ /_/ / \____/\____/_/ /_/ /_/_/ /_/ /_/\__,_/_/ \___/\___/\__,_/ ================================================ FILE: ct/headers/configarr ================================================ ______ _____ / ____/___ ____ / __(_)___ _____ ___________ / / / __ \/ __ \/ /_/ / __ `/ __ `/ ___/ ___/ / /___/ /_/ / / / / __/ / /_/ / /_/ / / / / \____/\____/_/ /_/_/ /_/\__, /\__,_/_/ /_/ /____/ ================================================ FILE: ct/headers/convertx ================================================ ______ __ _ __ / ____/___ ____ _ _____ _____/ /| |/ / / / / __ \/ __ \ | / / _ \/ ___/ __/ / / /___/ /_/ / / / / |/ / __/ / / /_/ | \____/\____/_/ /_/|___/\___/_/ \__/_/|_| ================================================ FILE: ct/headers/coolify ================================================ ______ ___ ____ / ____/___ ____ / (_) __/_ __ / / / __ \/ __ \/ / / /_/ / / / / /___/ /_/ / /_/ / / / __/ /_/ / \____/\____/\____/_/_/_/ \__, / /____/ ================================================ FILE: ct/headers/coredns ================================================ ______ ____ _ _______ / ____/___ ________ / __ \/ | / / ___/ / / / __ \/ ___/ _ \/ / / / |/ /\__ \ / /___/ /_/ / / / __/ /_/ / /| /___/ / \____/\____/_/ \___/_____/_/ |_//____/ ================================================ FILE: ct/headers/cosmos ================================================ ______ / ____/___ _________ ___ ____ _____ / / / __ \/ ___/ __ `__ \/ __ \/ ___/ / /___/ /_/ (__ ) / / / / / /_/ (__ ) \____/\____/____/_/ /_/ /_/\____/____/ ================================================ FILE: ct/headers/crafty-controller ================================================ ______ ______ ______ __ ____ / ____/________ _/ __/ /___ __ / ____/___ ____ / /__________ / / /__ _____ / / / ___/ __ `/ /_/ __/ / / /_____/ / / __ \/ __ \/ __/ ___/ __ \/ / / _ \/ ___/ / /___/ / / /_/ / __/ /_/ /_/ /_____/ /___/ /_/ / / / / /_/ / / /_/ / / / __/ / \____/_/ \__,_/_/ \__/\__, / \____/\____/_/ /_/\__/_/ \____/_/_/\___/_/ /____/ ================================================ FILE: ct/headers/cronicle ================================================ ______ _ __ / ____/________ ____ (_)____/ /__ / / / ___/ __ \/ __ \/ / ___/ / _ \ / /___/ / / /_/ / / / / / /__/ / __/ \____/_/ \____/_/ /_/_/\___/_/\___/ ================================================ FILE: ct/headers/cross-seed ================================================ __ ______________ __________ ________ ___ ____/ / / ___/ ___/ __ \/ ___/ ___/_____/ ___/ _ \/ _ \/ __ / / /__/ / / /_/ (__ |__ )_____(__ ) __/ __/ /_/ / \___/_/ \____/____/____/ /____/\___/\___/\__,_/ ================================================ FILE: ct/headers/cryptpad ================================================ ______ __ ____ __ / ____/______ ______ / /_/ __ \____ _____/ / / / / ___/ / / / __ \/ __/ /_/ / __ `/ __ / / /___/ / / /_/ / /_/ / /_/ ____/ /_/ / /_/ / \____/_/ \__, / .___/\__/_/ \__,_/\__,_/ /____/_/ ================================================ FILE: ct/headers/dagu ================================================ ____ / __ \____ _____ ___ __ / / / / __ `/ __ `/ / / / / /_/ / /_/ / /_/ / /_/ / /_____/\__,_/\__, /\__,_/ /____/ ================================================ FILE: ct/headers/dashy ================================================ ____ __ / __ \____ ______/ /_ __ __ / / / / __ `/ ___/ __ \/ / / / / /_/ / /_/ (__ ) / / / /_/ / /_____/\__,_/____/_/ /_/\__, / /____/ ================================================ FILE: ct/headers/databasus ================================================ ____ __ __ / __ \____ _/ /_____ _/ /_ ____ ________ _______ / / / / __ `/ __/ __ `/ __ \/ __ `/ ___/ / / / ___/ / /_/ / /_/ / /_/ /_/ / /_/ / /_/ (__ ) /_/ (__ ) /_____/\__,_/\__/\__,_/_.___/\__,_/____/\__,_/____/ ================================================ FILE: ct/headers/dawarich ================================================ ____ _ __ / __ \____ __ ______ ______(_)____/ /_ / / / / __ `/ | /| / / __ `/ ___/ / ___/ __ \ / /_/ / /_/ /| |/ |/ / /_/ / / / / /__/ / / / /_____/\__,_/ |__/|__/\__,_/_/ /_/\___/_/ /_/ ================================================ FILE: ct/headers/ddclient ================================================ __ __ ___ __ ____/ /___/ /____/ (_)__ ____ / /_ / __ / __ / ___/ / / _ \/ __ \/ __/ / /_/ / /_/ / /__/ / / __/ / / / /_ \__,_/\__,_/\___/_/_/\___/_/ /_/\__/ ================================================ FILE: ct/headers/debian ================================================ ____ __ _ / __ \___ / /_ (_)___ _____ / / / / _ \/ __ \/ / __ `/ __ \ / /_/ / __/ /_/ / / /_/ / / / / /_____/\___/_.___/_/\__,_/_/ /_/ ================================================ FILE: ct/headers/deconz ================================================ __ __________ _ _______ ____/ /__ / ____/ __ \/ | / /__ / / __ / _ \/ / / / / / |/ / / / / /_/ / __/ /___/ /_/ / /| / / /__ \__,_/\___/\____/\____/_/ |_/ /____/ ================================================ FILE: ct/headers/deluge ================================================ ____ __ / __ \___ / /_ ______ ____ / / / / _ \/ / / / / __ `/ _ \ / /_/ / __/ / /_/ / /_/ / __/ /_____/\___/_/\__,_/\__, /\___/ /____/ ================================================ FILE: ct/headers/discopanel ================================================ ____ _ ____ __ / __ \(_)_____________ / __ \____ _____ ___ / / / / / / / ___/ ___/ __ \/ /_/ / __ `/ __ \/ _ \/ / / /_/ / (__ ) /__/ /_/ / ____/ /_/ / / / / __/ / /_____/_/____/\___/\____/_/ \__,_/_/ /_/\___/_/ ================================================ FILE: ct/headers/dispatcharr ================================================ ____ _ __ __ / __ \(_)________ ____ _/ /______/ /_ ____ ___________ / / / / / ___/ __ \/ __ `/ __/ ___/ __ \/ __ `/ ___/ ___/ / /_/ / (__ ) /_/ / /_/ / /_/ /__/ / / / /_/ / / / / /_____/_/____/ .___/\__,_/\__/\___/_/ /_/\__,_/_/ /_/ /_/ ================================================ FILE: ct/headers/docker ================================================ ____ __ / __ \____ _____/ /_____ _____ / / / / __ \/ ___/ //_/ _ \/ ___/ / /_/ / /_/ / /__/ ,< / __/ / /_____/\____/\___/_/|_|\___/_/ ================================================ FILE: ct/headers/dockge ================================================ ____ __ / __ \____ _____/ /______ ____ / / / / __ \/ ___/ //_/ __ `/ _ \ / /_/ / /_/ / /__/ ,< / /_/ / __/ /_____/\____/\___/_/|_|\__, /\___/ /____/ ================================================ FILE: ct/headers/docmost ================================================ ____ __ / __ \____ _________ ___ ____ _____/ /_ / / / / __ \/ ___/ __ `__ \/ __ \/ ___/ __/ / /_/ / /_/ / /__/ / / / / / /_/ (__ ) /_ /_____/\____/\___/_/ /_/ /_/\____/____/\__/ ================================================ FILE: ct/headers/dokploy ================================================ ____ __ __ / __ \____ / /______ / /___ __ __ / / / / __ \/ //_/ __ \/ / __ \/ / / / / /_/ / /_/ / ,< / /_/ / / /_/ / /_/ / /_____/\____/_/|_/ .___/_/\____/\__, / /_/ /____/ ================================================ FILE: ct/headers/dolibarr ================================================ ____ ___ __ / __ \____ / (_) /_ ____ ___________ / / / / __ \/ / / __ \/ __ `/ ___/ ___/ / /_/ / /_/ / / / /_/ / /_/ / / / / /_____/\____/_/_/_.___/\__,_/_/ /_/ ================================================ FILE: ct/headers/domain-locker ================================================ ____ _ __ __ / __ \____ ____ ___ ____ _(_)___ / / ____ _____/ /_____ _____ / / / / __ \/ __ `__ \/ __ `/ / __ \______/ / / __ \/ ___/ //_/ _ \/ ___/ / /_/ / /_/ / / / / / / /_/ / / / / /_____/ /___/ /_/ / /__/ ,< / __/ / /_____/\____/_/ /_/ /_/\__,_/_/_/ /_/ /_____/\____/\___/_/|_|\___/_/ ================================================ FILE: ct/headers/domain-monitor ================================================ ____ _ __ ___ _ __ / __ \____ ____ ___ ____ _(_)___ / |/ /___ ____ (_) /_____ _____ / / / / __ \/ __ `__ \/ __ `/ / __ \______/ /|_/ / __ \/ __ \/ / __/ __ \/ ___/ / /_/ / /_/ / / / / / / /_/ / / / / /_____/ / / / /_/ / / / / / /_/ /_/ / / /_____/\____/_/ /_/ /_/\__,_/_/_/ /_/ /_/ /_/\____/_/ /_/_/\__/\____/_/ ================================================ FILE: ct/headers/donetick ================================================ ____ __ _ __ / __ \____ ____ ___ / /_(_)____/ /__ / / / / __ \/ __ \/ _ \/ __/ / ___/ //_/ / /_/ / /_/ / / / / __/ /_/ / /__/ ,< /_____/\____/_/ /_/\___/\__/_/\___/_/|_| ================================================ FILE: ct/headers/dotnetaspwebapi ================================================ ____ __ __ ___ _____ ____ _ __ __ ___ ____ ____ / __ \____ / /_____ ___ / /_ / | / ___// __ \ | | / /__ / /_ / | / __ \/ _/ / / / / __ \/ __/ __ \/ _ \/ __/ / /| | \__ \/ /_/ / | | /| / / _ \/ __ \ / /| | / /_/ // / / /_/ / /_/ / /_/ / / / __/ /_ / ___ |___/ / ____/ | |/ |/ / __/ /_/ / / ___ |/ ____// / /_____/\____/\__/_/ /_/\___/\__/ /_/ |_/____/_/ |__/|__/\___/_.___/ /_/ |_/_/ /___/ ================================================ FILE: ct/headers/drawdb ================================================ ____ ____ ____ / __ \_________ __ __/ __ \/ __ ) / / / / ___/ __ `/ | /| / / / / / __ | / /_/ / / / /_/ /| |/ |/ / /_/ / /_/ / /_____/_/ \__,_/ |__/|__/_____/_____/ ================================================ FILE: ct/headers/drawio ================================================ ____ ________ / __ \_________ __ __/ _/ __ \ / / / / ___/ __ `/ | /| / // // / / / / /_/ / / / /_/ /| |/ |/ // // /_/ / /_____/_/ \__,_/ |__/|__/___/\____/ ================================================ FILE: ct/headers/duplicati ================================================ ____ ___ __ _ / __ \__ ______ / (_)________ _/ /_(_) / / / / / / / __ \/ / / ___/ __ `/ __/ / / /_/ / /_/ / /_/ / / / /__/ /_/ / /_/ / /_____/\__,_/ .___/_/_/\___/\__,_/\__/_/ /_/ ================================================ FILE: ct/headers/ebusd ================================================ __ __ ___ / /_ __ ___________/ / / _ \/ __ \/ / / / ___/ __ / / __/ /_/ / /_/ (__ ) /_/ / \___/_.___/\__,_/____/\__,_/ ================================================ FILE: ct/headers/elementsynapse ================================================ ________ __ _____ / ____/ /__ ____ ___ ___ ____ / /_ / ___/__ ______ ____ _____ ________ / __/ / / _ \/ __ `__ \/ _ \/ __ \/ __/ \__ \/ / / / __ \/ __ `/ __ \/ ___/ _ \ / /___/ / __/ / / / / / __/ / / / /_ ___/ / /_/ / / / / /_/ / /_/ (__ ) __/ /_____/_/\___/_/ /_/ /_/\___/_/ /_/\__/ /____/\__, /_/ /_/\__,_/ .___/____/\___/ /____/ /_/ ================================================ FILE: ct/headers/emby ================================================ ______ __ / ____/___ ___ / /_ __ __ / __/ / __ `__ \/ __ \/ / / / / /___/ / / / / / /_/ / /_/ / /_____/_/ /_/ /_/_.___/\__, / /____/ ================================================ FILE: ct/headers/emqx ================================================ ________ _______ _ __ / ____/ |/ / __ \ | |/ / / __/ / /|_/ / / / / | / / /___/ / / / /_/ / / | /_____/_/ /_/\___\_\/_/|_| ================================================ FILE: ct/headers/endurain ================================================ ______ __ _ / ____/___ ____/ /_ ___________ _(_)___ / __/ / __ \/ __ / / / / ___/ __ `/ / __ \ / /___/ / / / /_/ / /_/ / / / /_/ / / / / / /_____/_/ /_/\__,_/\__,_/_/ \__,_/_/_/ /_/ ================================================ FILE: ct/headers/erpnext ================================================ __________ ____ _ __ __ / ____/ __ \/ __ \/ | / /__ _ __/ /_ / __/ / /_/ / /_/ / |/ / _ \| |/_/ __/ / /___/ _, _/ ____/ /| / __/> < /_/ /_/\____/_/ /_/ /_/\___/_____/\____/_/|_| ================================================ FILE: ct/headers/homebridge ================================================ __ __ __ _ __ / / / /___ ____ ___ ___ / /_ _____(_)___/ /___ ____ / /_/ / __ \/ __ `__ \/ _ \/ __ \/ ___/ / __ / __ `/ _ \ / __ / /_/ / / / / / / __/ /_/ / / / / /_/ / /_/ / __/ /_/ /_/\____/_/ /_/ /_/\___/_.___/_/ /_/\__,_/\__, /\___/ /____/ ================================================ FILE: ct/headers/homelable ================================================ __ __ __ __ __ / / / /___ ____ ___ ___ / /___ _/ /_ / /__ / /_/ / __ \/ __ `__ \/ _ \/ / __ `/ __ \/ / _ \ / __ / /_/ / / / / / / __/ / /_/ / /_/ / / __/ /_/ /_/\____/_/ /_/ /_/\___/_/\__,_/_.___/_/\___/ ================================================ FILE: ct/headers/homepage ================================================ __ __ / / / /___ ____ ___ ___ ____ ____ _____ ____ / /_/ / __ \/ __ `__ \/ _ \/ __ \/ __ `/ __ `/ _ \ / __ / /_/ / / / / / / __/ /_/ / /_/ / /_/ / __/ /_/ /_/\____/_/ /_/ /_/\___/ .___/\__,_/\__, /\___/ /_/ /____/ ================================================ FILE: ct/headers/homer ================================================ __ __ / / / /___ ____ ___ ___ _____ / /_/ / __ \/ __ `__ \/ _ \/ ___/ / __ / /_/ / / / / / / __/ / /_/ /_/\____/_/ /_/ /_/\___/_/ ================================================ FILE: ct/headers/hoodik ================================================ __ __ ___ __ / / / /___ ____ ____/ (_) /__ / /_/ / __ \/ __ \/ __ / / //_/ / __ / /_/ / /_/ / /_/ / / ,< /_/ /_/\____/\____/\__,_/_/_/|_| ================================================ FILE: ct/headers/hortusfox ================================================ __ __ __ ______ / / / /___ _____/ /___ _______/ ____/___ _ __ / /_/ / __ \/ ___/ __/ / / / ___/ /_ / __ \| |/_/ / __ / /_/ / / / /_/ /_/ (__ ) __/ / /_/ /> < /_/ /_/\____/_/ \__/\__,_/____/_/ \____/_/|_| ================================================ FILE: ct/headers/hyperhdr ================================================ __ __ __ ______ ____ / / / /_ ______ ___ _____/ / / / __ \/ __ \ / /_/ / / / / __ \/ _ \/ ___/ /_/ / / / / /_/ / / __ / /_/ / /_/ / __/ / / __ / /_/ / _, _/ /_/ /_/\__, / .___/\___/_/ /_/ /_/_____/_/ |_| /____/_/ ================================================ FILE: ct/headers/hyperion ================================================ __ __ _ / / / /_ ______ ___ _____(_)___ ____ / /_/ / / / / __ \/ _ \/ ___/ / __ \/ __ \ / __ / /_/ / /_/ / __/ / / / /_/ / / / / /_/ /_/\__, / .___/\___/_/ /_/\____/_/ /_/ /____/_/ ================================================ FILE: ct/headers/igotify ================================================ _ ______ __ _ ____ (_) ____/___ / /_(_) __/_ __ / / / __/ __ \/ __/ / /_/ / / / / / /_/ / /_/ / /_/ / __/ /_/ / /_/\____/\____/\__/_/_/ \__, / /____/ ================================================ FILE: ct/headers/immich ================================================ _ _ __ (_)___ ___ ____ ___ (_)____/ /_ / / __ `__ \/ __ `__ \/ / ___/ __ \ / / / / / / / / / / / / / /__/ / / / /_/_/ /_/ /_/_/ /_/ /_/_/\___/_/ /_/ ================================================ FILE: ct/headers/immichframe ================================================ ____ _ __ ______ / _/___ ___ ____ ___ (_)____/ /_ / ____/________ _____ ___ ___ / // __ `__ \/ __ `__ \/ / ___/ __ \/ /_ / ___/ __ `/ __ `__ \/ _ \ _/ // / / / / / / / / / / / /__/ / / / __/ / / / /_/ / / / / / / __/ /___/_/ /_/ /_/_/ /_/ /_/_/\___/_/ /_/_/ /_/ \__,_/_/ /_/ /_/\___/ ================================================ FILE: ct/headers/infisical ================================================ ____ _____ _ __ / _/___ / __(_)____(_)________ _/ / / // __ \/ /_/ / ___/ / ___/ __ `/ / _/ // / / / __/ (__ ) / /__/ /_/ / / /___/_/ /_/_/ /_/____/_/\___/\__,_/_/ ================================================ FILE: ct/headers/influxdb ================================================ ____ ______ ____ ____ / _/___ / __/ /_ ___ __/ __ \/ __ ) / // __ \/ /_/ / / / / |/_/ / / / __ | _/ // / / / __/ / /_/ /> < /_/ /_/_/_/ /_/_/_/ /_/\__,_/_/|_| ================================================ FILE: ct/headers/minio ================================================ __ ____ ________ / |/ (_)___ / _/ __ \ / /|_/ / / __ \ / // / / / / / / / / / / // // /_/ / /_/ /_/_/_/ /_/___/\____/ ================================================ FILE: ct/headers/minthcm ================================================ __ ____ __ __ __________ ___ / |/ (_)___ / /_/ / / / ____/ |/ / / /|_/ / / __ \/ __/ /_/ / / / /|_/ / / / / / / / / / /_/ __ / /___/ / / / /_/ /_/_/_/ /_/\__/_/ /_/\____/_/ /_/ ================================================ FILE: ct/headers/mongodb ================================================ __ ___ ____ ____ / |/ /___ ____ ____ _____ / __ \/ __ ) / /|_/ / __ \/ __ \/ __ `/ __ \/ / / / __ | / / / / /_/ / / / / /_/ / /_/ / /_/ / /_/ / /_/ /_/\____/_/ /_/\__, /\____/_____/_____/ /____/ ================================================ FILE: ct/headers/monica ================================================ __ ___ _ / |/ /___ ____ (_)________ _ / /|_/ / __ \/ __ \/ / ___/ __ `/ / / / / /_/ / / / / / /__/ /_/ / /_/ /_/\____/_/ /_/_/\___/\__,_/ ================================================ FILE: ct/headers/motioneye ================================================ __ ___ __ _ / |/ /___ / /_(_)___ ____ ___ __ _____ / /|_/ / __ \/ __/ / __ \/ __ \/ _ \/ / / / _ \ / / / / /_/ / /_/ / /_/ / / / / __/ /_/ / __/ /_/ /_/\____/\__/_/\____/_/ /_/\___/\__, /\___/ /____/ ================================================ FILE: ct/headers/mqtt ================================================ __ _______ ____________ / |/ / __ \/_ __/_ __/ / /|_/ / / / / / / / / / / / / /_/ / / / / / /_/ /_/\___\_\/_/ /_/ ================================================ FILE: ct/headers/myip ================================================ __ ___ ________ / |/ /_ __/ _/ __ \ / /|_/ / / / // // /_/ / / / / / /_/ // // ____/ /_/ /_/\__, /___/_/ /____/ ================================================ FILE: ct/headers/mylar3 ================================================ __ ___ __ _____ / |/ /_ __/ /___ _____|__ / / /|_/ / / / / / __ `/ ___//_ < / / / / /_/ / / /_/ / / ___/ / /_/ /_/\__, /_/\__,_/_/ /____/ /____/ ================================================ FILE: ct/headers/myspeed ================================================ __ ___ _____ __ / |/ /_ __/ ___/____ ___ ___ ____/ / / /|_/ / / / /\__ \/ __ \/ _ \/ _ \/ __ / / / / / /_/ /___/ / /_/ / __/ __/ /_/ / /_/ /_/\__, //____/ .___/\___/\___/\__,_/ /____/ /_/ ================================================ FILE: ct/headers/mysql ================================================ __ ___ _____ ____ __ / |/ /_ __/ ___// __ \ / / / /|_/ / / / /\__ \/ / / / / / / / / / /_/ /___/ / /_/ / / /___ /_/ /_/\__, //____/\___\_\/_____/ /____/ ================================================ FILE: ct/headers/n8n ================================================ ____ ____ ( __ )____ / __ \/ __ / __ \ / / / / /_/ / / / / /_/ /_/\____/_/ /_/ ================================================ FILE: ct/headers/nagios ================================================ _ __ _ / | / /___ _____ _(_)___ _____ / |/ / __ `/ __ `/ / __ \/ ___/ / /| / /_/ / /_/ / / /_/ (__ ) /_/ |_/\__,_/\__, /_/\____/____/ /____/ ================================================ FILE: ct/headers/nametag ================================================ _ __ __ / | / /___ _____ ___ ___ / /_____ _____ _ / |/ / __ `/ __ `__ \/ _ \/ __/ __ `/ __ `/ / /| / /_/ / / / / / / __/ /_/ /_/ / /_/ / /_/ |_/\__,_/_/ /_/ /_/\___/\__/\__,_/\__, / /____/ ================================================ FILE: ct/headers/navidrome ================================================ _ __ _ __ / | / /___ __ __(_)___/ /________ ____ ___ ___ / |/ / __ `/ | / / / __ / ___/ __ \/ __ `__ \/ _ \ / /| / /_/ /| |/ / / /_/ / / / /_/ / / / / / / __/ /_/ |_/\__,_/ |___/_/\__,_/_/ \____/_/ /_/ /_/\___/ ================================================ FILE: ct/headers/neko ================================================ _ __ __ / | / /__ / /______ / |/ / _ \/ //_/ __ \ / /| / __/ ,< / /_/ / /_/ |_/\___/_/|_|\____/ ================================================ FILE: ct/headers/neo4j ================================================ _ __ __ __ _ / | / /__ ____ / // / (_) / |/ / _ \/ __ \/ // /_/ / / /| / __/ /_/ /__ __/ / /_/ |_/\___/\____/ /_/_/ / /___/ ================================================ FILE: ct/headers/netbird ================================================ _ __ __ ____ _ __ / | / /__ / /_/ __ )(_)________/ / / |/ / _ \/ __/ __ / / ___/ __ / / /| / __/ /_/ /_/ / / / / /_/ / /_/ |_/\___/\__/_____/_/_/ \__,_/ ================================================ FILE: ct/headers/netboot-xyz ================================================ __ __ __ ____ ___ / /_/ /_ ____ ____ / /_ _ ____ ______ / __ \/ _ \/ __/ __ \/ __ \/ __ \/ __/ | |/_/ / / /_ / / / / / __/ /_/ /_/ / /_/ / /_/ / /__ _> < /_/ |_/\___/\__/_____/\____/_/|_| ================================================ FILE: ct/headers/netvisor ================================================ _____ / ___/_________ _____ ____ ____ __ __ \__ \/ ___/ __ `/ __ \/ __ \/ __ \/ / / / ___/ / /__/ /_/ / / / / /_/ / /_/ / /_/ / /____/\___/\__,_/_/ /_/\____/ .___/\__, / /_/ /____/ ================================================ FILE: ct/headers/nextcloudpi ================================================ _ __ __ ________ ______ _ / | / /__ _ __/ /_/ ____/ /___ __ ______/ / __ \(_) / |/ / _ \| |/_/ __/ / / / __ \/ / / / __ / /_/ / / / /| / __/> < / ____/ / / /_/ /> < | |/ |/ / / /_/ / / / __(__ |__ ) /_/ |_/_/|_| |__/|__/_/\__/_/ /_/\___/____/____/ ================================================ FILE: ct/headers/nzbget ================================================ _ _______ ____ ______ __ / | / /__ / / __ )/ ____/__ / /_ / |/ / / / / __ / / __/ _ \/ __/ / /| / / /__/ /_/ / /_/ / __/ /_ /_/ |_/ /____/_____/\____/\___/\__/ ================================================ FILE: ct/headers/oauth2-proxy ================================================ __ __ ___ ____ ____ ___ __/ /_/ /_ |__ \ ____ _________ _ ____ __ / __ \/ __ `/ / / / __/ __ \__/ /_____/ __ \/ ___/ __ \| |/_/ / / / / /_/ / /_/ / /_/ / /_/ / / / __/_____/ /_/ / / / /_/ /> < /_/ \__,_/ .___/\___/_/ /_/\___/____/____/ /_/ /_/\__, /_/|_| /_/ /____/ ================================================ FILE: ct/headers/papra ================================================ ____ / __ \____ _____ _________ _ / /_/ / __ `/ __ \/ ___/ __ `/ / ____/ /_/ / /_/ / / / /_/ / /_/ \__,_/ .___/_/ \__,_/ /_/ ================================================ FILE: ct/headers/part-db ================================================ ____ __ ____ ____ / __ \____ ______/ /_ / __ \/ __ ) / /_/ / __ `/ ___/ __/_____/ / / / __ | / ____/ /_/ / / / /_/_____/ /_/ / /_/ / /_/ \__,_/_/ \__/ /_____/_____/ ================================================ FILE: ct/headers/passbolt ================================================ ____ __ ____ / __ \____ ___________/ /_ ____ / / /_ / /_/ / __ `/ ___/ ___/ __ \/ __ \/ / __/ / ____/ /_/ (__ |__ ) /_/ / /_/ / / /_ /_/ \__,_/____/____/_.___/\____/_/\__/ ================================================ FILE: ct/headers/patchmon ================================================ ____ __ __ __ ___ / __ \____ _/ /______/ /_ / |/ /___ ____ / /_/ / __ `/ __/ ___/ __ \/ /|_/ / __ \/ __ \ / ____/ /_/ / /_/ /__/ / / / / / / /_/ / / / / /_/ \__,_/\__/\___/_/ /_/_/ /_/\____/_/ /_/ ================================================ FILE: ct/headers/paymenter ================================================ ____ __ / __ \____ ___ ______ ___ ___ ____ / /____ _____ / /_/ / __ `/ / / / __ `__ \/ _ \/ __ \/ __/ _ \/ ___/ / ____/ /_/ / /_/ / / / / / / __/ / / / /_/ __/ / /_/ \__,_/\__, /_/ /_/ /_/\___/_/ /_/\__/\___/_/ /____/ ================================================ FILE: ct/headers/peanut ================================================ ____ _ ____ ________ / __ \___ ____ _/ | / / / / /_ __/ / /_/ / _ \/ __ `/ |/ / / / / / / / ____/ __/ /_/ / /| / /_/ / / / /_/ \___/\__,_/_/ |_/\____/ /_/ ================================================ FILE: ct/headers/pelican-panel ================================================ ____ ___ ____ __ / __ \___ / (_)________ _____ / __ \____ _____ ___ / / / /_/ / _ \/ / / ___/ __ `/ __ \______/ /_/ / __ `/ __ \/ _ \/ / / ____/ __/ / / /__/ /_/ / / / /_____/ ____/ /_/ / / / / __/ / /_/ \___/_/_/\___/\__,_/_/ /_/ /_/ \__,_/_/ /_/\___/_/ ================================================ FILE: ct/headers/pelican-wings ================================================ ____ ___ _ ___ / __ \___ / (_)________ _____ | | / (_)___ ____ ______ / /_/ / _ \/ / / ___/ __ `/ __ \_____| | /| / / / __ \/ __ `/ ___/ / ____/ __/ / / /__/ /_/ / / / /_____/ |/ |/ / / / / / /_/ (__ ) /_/ \___/_/_/\___/\__,_/_/ /_/ |__/|__/_/_/ /_/\__, /____/ /____/ ================================================ FILE: ct/headers/photoprism ================================================ ____ __ __ ____ _ / __ \/ /_ ____ / /_____ / __ \_____(_)________ ___ / /_/ / __ \/ __ \/ __/ __ \/ /_/ / ___/ / ___/ __ `__ \ / ____/ / / / /_/ / /_/ /_/ / ____/ / / (__ ) / / / / / /_/ /_/ /_/\____/\__/\____/_/ /_/ /_/____/_/ /_/ /_/ ================================================ FILE: ct/headers/pialert ================================================ ____ _ ___ __ __ / __ \(_) | / /__ _____/ /_ / /_/ / / /| | / / _ \/ ___/ __/ / ____/ / ___ |/ / __/ / / /_ /_/ /_/_/ |_/_/\___/_/ \__/ ================================================ FILE: ct/headers/pihole ================================================ ____ _ __ __ / __ \(_) /_ ____ / /__ / /_/ / / __ \/ __ \/ / _ \ / ____/ / / / / /_/ / / __/ /_/ /_/_/ /_/\____/_/\___/ ================================================ FILE: ct/headers/planka ================================================ ____ __ ___ _ ____ __ ___ / __ \/ / / | / | / / //_// | / /_/ / / / /| | / |/ / ,< / /| | / ____/ /___/ ___ |/ /| / /| |/ ___ | /_/ /_____/_/ |_/_/ |_/_/ |_/_/ |_| ================================================ FILE: ct/headers/plant-it ================================================ ____ __ __ _ __ / __ \/ /___ _____ / /_ (_) /_ / /_/ / / __ `/ __ \/ __/_____/ / __/ / ____/ / /_/ / / / / /_/_____/ / /_ /_/ /_/\__,_/_/ /_/\__/ /_/\__/ ================================================ FILE: ct/headers/plex ================================================ ____ __ / __ \/ /__ _ __ / /_/ / / _ \| |/_/ / ____/ / __/> < /_/ /_/\___/_/|_| ================================================ FILE: ct/headers/pocketbase ================================================ ____ __ __ __ / __ \____ _____/ /_____ / /_/ /_ ____ _________ / /_/ / __ \/ ___/ //_/ _ \/ __/ __ \/ __ `/ ___/ _ \ / ____/ /_/ / /__/ ,< / __/ /_/ /_/ / /_/ (__ ) __/ /_/ \____/\___/_/|_|\___/\__/_.___/\__,_/____/\___/ ================================================ FILE: ct/headers/pocketid ================================================ ____ __ __ ________ / __ \____ _____/ /_____ / /_/ _/ __ \ / /_/ / __ \/ ___/ //_/ _ \/ __// // / / / / ____/ /_/ / /__/ ,< / __/ /__/ // /_/ / /_/ \____/\___/_/|_|\___/\__/___/_____/ ================================================ FILE: ct/headers/podman ================================================ ____ __ / __ \____ ____/ /___ ___ ____ _____ / /_/ / __ \/ __ / __ `__ \/ __ `/ __ \ / ____/ /_/ / /_/ / / / / / / /_/ / / / / /_/ \____/\__,_/_/ /_/ /_/\__,_/_/ /_/ ================================================ FILE: ct/headers/podman-homeassistant ================================================ ____ __ __ __ ___ _ __ __ / __ \____ ____/ /___ ___ ____ _____ / / / /___ ____ ___ ___ / | __________(_)____/ /_____ _____ / /_ / /_/ / __ \/ __ / __ `__ \/ __ `/ __ \______/ /_/ / __ \/ __ `__ \/ _ \ / /| | / ___/ ___/ / ___/ __/ __ `/ __ \/ __/ / ____/ /_/ / /_/ / / / / / / /_/ / / / /_____/ __ / /_/ / / / / / / __/ / ___ |(__ |__ ) (__ ) /_/ /_/ / / / / /_ /_/ \____/\__,_/_/ /_/ /_/\__,_/_/ /_/ /_/ /_/\____/_/ /_/ /_/\___/ /_/ |_/____/____/_/____/\__/\__,_/_/ /_/\__/ ================================================ FILE: ct/headers/postgresql ================================================ ____ __ _____ ____ __ / __ \____ _____/ /_____ _________ / ___// __ \ / / / /_/ / __ \/ ___/ __/ __ `/ ___/ _ \\__ \/ / / / / / / ____/ /_/ (__ ) /_/ /_/ / / / __/__/ / /_/ / / /___ /_/ \____/____/\__/\__, /_/ \___/____/\___\_\/_____/ /____/ ================================================ FILE: ct/headers/powerdns ================================================ ____ ____ _ _______ / __ \____ _ _____ _____/ __ \/ | / / ___/ / /_/ / __ \ | /| / / _ \/ ___/ / / / |/ /\__ \ / ____/ /_/ / |/ |/ / __/ / / /_/ / /| /___/ / /_/ \____/|__/|__/\___/_/ /_____/_/ |_//____/ ================================================ FILE: ct/headers/privatebin ================================================ ____ _ __ ____ _ / __ \_____(_) ______ _/ /____ / __ )(_)___ / /_/ / ___/ / | / / __ `/ __/ _ \/ __ / / __ \ / ____/ / / /| |/ / /_/ / /_/ __/ /_/ / / / / / /_/ /_/ /_/ |___/\__,_/\__/\___/_____/_/_/ /_/ ================================================ FILE: ct/headers/profilarr ================================================ ____ _____ __ / __ \_________ / __(_) /___ ___________ / /_/ / ___/ __ \/ /_/ / / __ `/ ___/ ___/ / ____/ / / /_/ / __/ / / /_/ / / / / /_/ /_/ \____/_/ /_/_/\__,_/_/ /_/ ================================================ FILE: ct/headers/projectsend ================================================ ____ _ __ _____ __ / __ \_________ (_)__ _____/ /_/ ___/___ ____ ____/ / / /_/ / ___/ __ \ / / _ \/ ___/ __/\__ \/ _ \/ __ \/ __ / / ____/ / / /_/ / / / __/ /__/ /_ ___/ / __/ / / / /_/ / /_/ /_/ \____/_/ /\___/\___/\__//____/\___/_/ /_/\__,_/ /___/ ================================================ FILE: ct/headers/prometheus ================================================ ____ __ __ / __ \_________ ____ ___ ___ / /_/ /_ ___ __ _______ / /_/ / ___/ __ \/ __ `__ \/ _ \/ __/ __ \/ _ \/ / / / ___/ / ____/ / / /_/ / / / / / / __/ /_/ / / / __/ /_/ (__ ) /_/ /_/ \____/_/ /_/ /_/\___/\__/_/ /_/\___/\__,_/____/ ================================================ FILE: ct/headers/prometheus-alertmanager ================================================ ____ __ __ ___ __ __ / __ \_________ ____ ___ ___ / /_/ /_ ___ __ _______ / | / /__ _____/ /_____ ___ ____ _____ ____ _____ ____ _____ / /_/ / ___/ __ \/ __ `__ \/ _ \/ __/ __ \/ _ \/ / / / ___/_____/ /| | / / _ \/ ___/ __/ __ `__ \/ __ `/ __ \/ __ `/ __ `/ _ \/ ___/ / ____/ / / /_/ / / / / / / __/ /_/ / / / __/ /_/ (__ )_____/ ___ |/ / __/ / / /_/ / / / / / /_/ / / / / /_/ / /_/ / __/ / /_/ /_/ \____/_/ /_/ /_/\___/\__/_/ /_/\___/\__,_/____/ /_/ |_/_/\___/_/ \__/_/ /_/ /_/\__,_/_/ /_/\__,_/\__, /\___/_/ /____/ ================================================ FILE: ct/headers/prometheus-blackbox-exporter ================================================ ____ __ __ ____ __ __ __ ______ __ / __ \_________ ____ ___ ___ / /_/ /_ ___ __ _______ / __ )/ /___ ______/ /__/ /_ ____ _ __ / ____/ ______ ____ _____/ /____ _____ / /_/ / ___/ __ \/ __ `__ \/ _ \/ __/ __ \/ _ \/ / / / ___/_____/ __ / / __ `/ ___/ //_/ __ \/ __ \| |/_/_____/ __/ | |/_/ __ \/ __ \/ ___/ __/ _ \/ ___/ / ____/ / / /_/ / / / / / / __/ /_/ / / / __/ /_/ (__ )_____/ /_/ / / /_/ / /__/ ,< / /_/ / /_/ /> < /_/ \___/_/ /_/ /_/ /_/_/_/|_| ================================================ FILE: ct/headers/the-lounge ================================================ ________ __ /_ __/ /_ ___ / / ____ __ ______ ____ ____ / / / __ \/ _ \______/ / / __ \/ / / / __ \/ __ `/ _ \ / / / / / / __/_____/ /___/ /_/ / /_/ / / / / /_/ / __/ /_/ /_/ /_/\___/ /_____/\____/\__,_/_/ /_/\__, /\___/ /____/ ================================================ FILE: ct/headers/thingsboard ================================================ ________ _ ____ __ /_ __/ /_ (_)___ ____ ______/ __ )____ ____ __________/ / / / / __ \/ / __ \/ __ `/ ___/ __ / __ \/ __ `/ ___/ __ / / / / / / / / / / / /_/ (__ ) /_/ / /_/ / /_/ / / / /_/ / /_/ /_/ /_/_/_/ /_/\__, /____/_____/\____/\__,_/_/ \__,_/ /____/ ================================================ FILE: ct/headers/threadfin ================================================ ________ _______ /_ __/ /_ ________ ____ _____/ / __(_)___ / / / __ \/ ___/ _ \/ __ `/ __ / /_/ / __ \ / / / / / / / / __/ /_/ / /_/ / __/ / / / / /_/ /_/ /_/_/ \___/\__,_/\__,_/_/ /_/_/ /_/ ================================================ FILE: ct/headers/tianji ================================================ _______ _ _ /_ __(_)___ _____ (_|_) / / / / __ `/ __ \ / / / / / / / /_/ / / / / / / / /_/ /_/\__,_/_/ /_/_/ /_/ /___/ ================================================ FILE: ct/headers/tinyauth ================================================ _______ __ __ /_ __(_)___ __ ______ ___ __/ /_/ /_ / / / / __ \/ / / / __ `/ / / / __/ __ \ / / / / / / / /_/ / /_/ / /_/ / /_/ / / / /_/ /_/_/ /_/\__, /\__,_/\__,_/\__/_/ /_/ /____/ ================================================ FILE: ct/headers/traccar ================================================ ______ /_ __/________ _______________ ______ / / / ___/ __ `/ ___/ ___/ __ `/ ___/ / / / / / /_/ / /__/ /__/ /_/ / / /_/ /_/ \__,_/\___/\___/\__,_/_/ ================================================ FILE: ct/headers/tracearr ================================================ ______ /_ __/________ _________ ____ ___________ / / / ___/ __ `/ ___/ _ \/ __ `/ ___/ ___/ / / / / / /_/ / /__/ __/ /_/ / / / / /_/ /_/ \__,_/\___/\___/\__,_/_/ /_/ ================================================ FILE: ct/headers/tracktor ================================================ __ __ __ / /__________ ______/ /__/ /_____ _____ / __/ ___/ __ `/ ___/ //_/ __/ __ \/ ___/ / /_/ / / /_/ / /__/ ,< / /_/ /_/ / / \__/_/ \__,_/\___/_/|_|\__/\____/_/ ================================================ FILE: ct/headers/traefik ================================================ ______ _____ __ /_ __/________ ____ / __(_) /__ / / / ___/ __ `/ _ \/ /_/ / //_/ / / / / / /_/ / __/ __/ / ,< /_/ /_/ \__,_/\___/_/ /_/_/|_| ================================================ FILE: ct/headers/transmission ================================================ ______ _ _ /_ __/________ _____ _________ ___ (_)_________(_)___ ____ / / / ___/ __ `/ __ \/ ___/ __ `__ \/ / ___/ ___/ / __ \/ __ \ / / / / / /_/ / / / (__ ) / / / / / (__ |__ ) / /_/ / / / / /_/ /_/ \__,_/_/ /_/____/_/ /_/ /_/_/____/____/_/\____/_/ /_/ ================================================ FILE: ct/headers/transmute ================================================ ______ __ /_ __/________ _____ _________ ___ __ __/ /____ / / / ___/ __ `/ __ \/ ___/ __ `__ \/ / / / __/ _ \ / / / / / /_/ / / / (__ ) / / / / / /_/ / /_/ __/ /_/ /_/ \__,_/_/ /_/____/_/ /_/ /_/\__,_/\__/\___/ ================================================ FILE: ct/headers/trek ================================================ __________ ________ __ /_ __/ __ \/ ____/ //_/ / / / /_/ / __/ / ,< / / / _, _/ /___/ /| | /_/ /_/ |_/_____/_/ |_| ================================================ FILE: ct/headers/trilium ================================================ ______ _ ___ /_ __/____(_) (_)_ ______ ___ / / / ___/ / / / / / / __ `__ \ / / / / / / / / /_/ / / / / / / /_/ /_/ /_/_/_/\__,_/_/ /_/ /_/ ================================================ FILE: ct/headers/trip ================================================ __________ ________ /_ __/ __ \/ _/ __ \ / / / /_/ // // /_/ / / / / _, _// // ____/ /_/ /_/ |_/___/_/ ================================================ FILE: ct/headers/tubearchivist ================================================ ______ __ ___ __ _ _ __ /_ __/_ __/ /_ ___ / | __________/ /_ (_) __(_)____/ /_ / / / / / / __ \/ _ \ / /| | / ___/ ___/ __ \/ / | / / / ___/ __/ / / / /_/ / /_/ / __/ / ___ |/ / / /__/ / / / /| |/ / (__ ) /_ /_/ \__,_/_.___/\___/ /_/ |_/_/ \___/_/ /_/_/ |___/_/____/\__/ ================================================ FILE: ct/headers/tududi ================================================ ______ __ ___ /_ __/_ ______/ /_ ______/ (_) / / / / / / __ / / / / __ / / / / / /_/ / /_/ / /_/ / /_/ / / /_/ \__,_/\__,_/\__,_/\__,_/_/ ================================================ FILE: ct/headers/tunarr ================================================ ______ /_ __/_ ______ ____ ___________ / / / / / / __ \/ __ `/ ___/ ___/ / / / /_/ / / / / /_/ / / / / /_/ \__,_/_/ /_/\__,_/_/ /_/ ================================================ FILE: ct/headers/twingate-connector ================================================ ______ _ __ ______ __ /_ __/ __(_)___ ____ _____ _/ /____ / ____/___ ____ ____ ___ _____/ /_____ _____ / / | | /| / / / __ \/ __ `/ __ `/ __/ _ \______/ / / __ \/ __ \/ __ \/ _ \/ ___/ __/ __ \/ ___/ / / | |/ |/ / / / / / /_/ / /_/ / /_/ __/_____/ /___/ /_/ / / / / / / / __/ /__/ /_/ /_/ / / /_/ |__/|__/_/_/ /_/\__, /\__,_/\__/\___/ \____/\____/_/ /_/_/ /_/\___/\___/\__/\____/_/ /____/ ================================================ FILE: ct/headers/ubuntu ================================================ __ ____ __ / / / / /_ __ ______ / /___ __ / / / / __ \/ / / / __ \/ __/ / / / / /_/ / /_/ / /_/ / / / / /_/ /_/ / \____/_.___/\__,_/_/ /_/\__/\__,_/ ================================================ FILE: ct/headers/uhf ================================================ __ ____ ________ / / / / / / / ____/ / / / / /_/ / /_ / /_/ / __ / __/ \____/_/ /_/_/ ================================================ FILE: ct/headers/umami ================================================ __ __ _ / / / /___ ___ ____ _____ ___ (_) / / / / __ `__ \/ __ `/ __ `__ \/ / / /_/ / / / / / / /_/ / / / / / / / \____/_/ /_/ /_/\__,_/_/ /_/ /_/_/ ================================================ FILE: ct/headers/umlautadaptarr ================================================ __ __ __ __ ___ __ __ / / / /___ ___ / /___ ___ __/ /_/ | ____/ /___ _____ / /_____ ___________ / / / / __ `__ \/ / __ `/ / / / __/ /| |/ __ / __ `/ __ \/ __/ __ `/ ___/ ___/ / /_/ / / / / / / / /_/ / /_/ / /_/ ___ / /_/ / /_/ / /_/ / /_/ /_/ / / / / \____/_/ /_/ /_/_/\__,_/\__,_/\__/_/ |_\__,_/\__,_/ .___/\__/\__,_/_/ /_/ /_/ ================================================ FILE: ct/headers/unbound ================================================ __ __ __ __ / / / /___ / /_ ____ __ ______ ____/ / / / / / __ \/ __ \/ __ \/ / / / __ \/ __ / / /_/ / / / / /_/ / /_/ / /_/ / / / / /_/ / \____/_/ /_/_.___/\____/\__,_/_/ /_/\__,_/ ================================================ FILE: ct/headers/unifi-os-server ================================================ __ __ _ _______ ____ _____ _____ / / / /___ (_) ____(_) / __ \/ ___/ / ___/___ ______ _____ _____ / / / / __ \/ / /_ / /_____/ / / /\__ \______\__ \/ _ \/ ___/ | / / _ \/ ___/ / /_/ / / / / / __/ / /_____/ /_/ /___/ /_____/__/ / __/ / | |/ / __/ / \____/_/ /_/_/_/ /_/ \____//____/ /____/\___/_/ |___/\___/_/ ================================================ FILE: ct/headers/unmanic ================================================ __ __ _ / / / /___ ____ ___ ____ _____ (_)____ / / / / __ \/ __ `__ \/ __ `/ __ \/ / ___/ / /_/ / / / / / / / / / /_/ / / / / / /__ \____/_/ /_/_/ /_/ /_/\__,_/_/ /_/_/\___/ ================================================ FILE: ct/headers/upgopher ================================================ __ __ __ / / / /___ ____ _____ ____ / /_ ___ _____ / / / / __ \/ __ `/ __ \/ __ \/ __ \/ _ \/ ___/ / /_/ / /_/ / /_/ / /_/ / /_/ / / / / __/ / \____/ .___/\__, /\____/ .___/_/ /_/\___/_/ /_/ /____/ /_/ ================================================ FILE: ct/headers/upsnap ================================================ __ __ _____ / / / /___ / ___/____ ____ _____ / / / / __ \\__ \/ __ \/ __ `/ __ \ / /_/ / /_/ /__/ / / / / /_/ / /_/ / \____/ .___/____/_/ /_/\__,_/ .___/ /_/ /_/ ================================================ FILE: ct/headers/uptimekuma ================================================ __ __ __ _ __ __ / / / /___ / /_(_)___ ___ ___ / //_/_ ______ ___ ____ _ / / / / __ \/ __/ / __ `__ \/ _ \ / ,< / / / / __ `__ \/ __ `/ / /_/ / /_/ / /_/ / / / / / / __/ / /| / /_/ / / / / / / /_/ / \____/ .___/\__/_/_/ /_/ /_/\___/ /_/ |_\__,_/_/ /_/ /_/\__,_/ /_/ ================================================ FILE: ct/headers/urbackupserver ================================================ __ __ ____ __ _____ / / / /____/ __ )____ ______/ /____ ______ / ___/___ ______ _____ _____ / / / / ___/ __ / __ `/ ___/ //_/ / / / __ \ \__ \/ _ \/ ___/ | / / _ \/ ___/ / /_/ / / / /_/ / /_/ / /__/ ,< / /_/ / /_/ / ___/ / __/ / | |/ / __/ / \____/_/ /_____/\__,_/\___/_/|_|\__,_/ .___/ /____/\___/_/ |___/\___/_/ /_/ ================================================ FILE: ct/headers/valkey ================================================ _ __ ____ | | / /___ _/ / /_____ __ __ | | / / __ `/ / //_/ _ \/ / / / | |/ / /_/ / / ,< / __/ /_/ / |___/\__,_/_/_/|_|\___/\__, / /____/ ================================================ FILE: ct/headers/vaultwarden ================================================ _ __ ____ __ | | / /___ ___ __/ / /__ ______ __________/ /__ ____ | | / / __ `/ / / / / __/ | /| / / __ `/ ___/ __ / _ \/ __ \ | |/ / /_/ / /_/ / / /_ | |/ |/ / /_/ / / / /_/ / __/ / / / |___/\__,_/\__,_/_/\__/ |__/|__/\__,_/_/ \__,_/\___/_/ /_/ ================================================ FILE: ct/headers/versitygw ================================================ _ __ _ __ _______ __ | | / /__ __________(_) /___ __/ ____/ | / / | | / / _ \/ ___/ ___/ / __/ / / / / __ | | /| / / | |/ / __/ / (__ ) / /_/ /_/ / /_/ / | |/ |/ / |___/\___/_/ /____/_/\__/\__, /\____/ |__/|__/ /____/ ================================================ FILE: ct/headers/victoriametrics ================================================ _ ___ __ _ __ ___ __ _ | | / (_)____/ /_____ _____(_)___ _/ |/ /__ / /______(_)_________ | | / / / ___/ __/ __ \/ ___/ / __ `/ /|_/ / _ \/ __/ ___/ / ___/ ___/ | |/ / / /__/ /_/ /_/ / / / / /_/ / / / / __/ /_/ / / / /__(__ ) |___/_/\___/\__/\____/_/ /_/\__,_/_/ /_/\___/\__/_/ /_/\___/____/ ================================================ FILE: ct/headers/vikunja ================================================ _ ___ __ _ | | / (_) /____ ______ (_)___ _ | | / / / //_/ / / / __ \ / / __ `/ | |/ / / ,< / /_/ / / / / / / /_/ / |___/_/_/|_|\__,_/_/ /_/_/ /\__,_/ /___/ ================================================ FILE: ct/headers/wallabag ================================================ _ __ ____ __ | | / /___ _/ / /___ _/ /_ ____ _____ _ | | /| / / __ `/ / / __ `/ __ \/ __ `/ __ `/ | |/ |/ / /_/ / / / /_/ / /_/ / /_/ / /_/ / |__/|__/\__,_/_/_/\__,_/_.___/\__,_/\__, / /____/ ================================================ FILE: ct/headers/wallos ================================================ _ __ ____ | | / /___ _/ / /___ _____ | | /| / / __ `/ / / __ \/ ___/ | |/ |/ / /_/ / / / /_/ (__ ) |__/|__/\__,_/_/_/\____/____/ ================================================ FILE: ct/headers/wanderer ================================================ _ __ __ | | / /___ _____ ____/ /__ ________ _____ | | /| / / __ `/ __ \/ __ / _ \/ ___/ _ \/ ___/ | |/ |/ / /_/ / / / / /_/ / __/ / / __/ / |__/|__/\__,_/_/ /_/\__,_/\___/_/ \___/_/ ================================================ FILE: ct/headers/warracker ================================================ _ __ __ | | / /___ _______________ ______/ /_____ _____ | | /| / / __ `/ ___/ ___/ __ `/ ___/ //_/ _ \/ ___/ | |/ |/ / /_/ / / / / / /_/ / /__/ ,< / __/ / |__/|__/\__,_/_/ /_/ \__,_/\___/_/|_|\___/_/ ================================================ FILE: ct/headers/wastebin ================================================ _ __ __ __ _ | | / /___ ______/ /____ / /_ (_)___ | | /| / / __ `/ ___/ __/ _ \/ __ \/ / __ \ | |/ |/ / /_/ (__ ) /_/ __/ /_/ / / / / / |__/|__/\__,_/____/\__/\___/_.___/_/_/ /_/ ================================================ FILE: ct/headers/watcharr ================================================ _ __ __ __ | | / /___ _/ /______/ /_ ____ ___________ | | /| / / __ `/ __/ ___/ __ \/ __ `/ ___/ ___/ | |/ |/ / /_/ / /_/ /__/ / / / /_/ / / / / |__/|__/\__,_/\__/\___/_/ /_/\__,_/_/ /_/ ================================================ FILE: ct/headers/watchyourlan ================================================ _ __ __ ____ __ __ ___ _ __ | | / /___ _/ /______/ /\ \/ /___ __ _______/ / / | / | / / | | /| / / __ `/ __/ ___/ __ \ / __ \/ / / / ___/ / / /| | / |/ / | |/ |/ / /_/ / /_/ /__/ / / / / /_/ / /_/ / / / /___/ ___ |/ /| / |__/|__/\__,_/\__/\___/_/ /_/_/\____/\__,_/_/ /_____/_/ |_/_/ |_/ ================================================ FILE: ct/headers/wavelog ================================================ _ __ __ | | / /___ __ _____ / /___ ____ _ | | /| / / __ `/ | / / _ \/ / __ \/ __ `/ | |/ |/ / /_/ /| |/ / __/ / /_/ / /_/ / |__/|__/\__,_/ |___/\___/_/\____/\__, / /____/ ================================================ FILE: ct/headers/wazuh ================================================ _ __ __ | | / /___ _____ __ __/ /_ | | /| / / __ `/_ / / / / / __ \ | |/ |/ / /_/ / / /_/ /_/ / / / / |__/|__/\__,_/ /___/\__,_/_/ /_/ ================================================ FILE: ct/headers/wealthfolio ================================================ _ __ ____ __ ____ ___ | | / /__ ____ _/ / /_/ /_ / __/___ / (_)___ | | /| / / _ \/ __ `/ / __/ __ \/ /_/ __ \/ / / __ \ | |/ |/ / __/ /_/ / / /_/ / / / __/ /_/ / / / /_/ / |__/|__/\___/\__,_/_/\__/_/ /_/_/ \____/_/_/\____/ ================================================ FILE: ct/headers/web-check ================================================ __ __ __ _ _____ / /_ _____/ /_ ___ _____/ /__ | | /| / / _ \/ __ \______/ ___/ __ \/ _ \/ ___/ //_/ | |/ |/ / __/ /_/ /_____/ /__/ / / / __/ /__/ ,< |__/|__/\___/_.___/ \___/_/ /_/\___/\___/_/|_| ================================================ FILE: ct/headers/wger ================================================ _ ______ ____ _____ | | /| / / __ `/ _ \/ ___/ | |/ |/ / /_/ / __/ / |__/|__/\__, /\___/_/ /____/ ================================================ FILE: ct/headers/whisparr ================================================ _ ____ _ | | / / /_ (_)________ ____ ___________ | | /| / / __ \/ / ___/ __ \/ __ `/ ___/ ___/ | |/ |/ / / / / (__ ) /_/ / /_/ / / / / |__/|__/_/ /_/_/____/ .___/\__,_/_/ /_/ /_/ ================================================ FILE: ct/headers/whodb ================================================ _ ____ ____ ____ | | / / /_ ____ / __ \/ __ ) | | /| / / __ \/ __ \/ / / / __ | | |/ |/ / / / / /_/ / /_/ / /_/ / |__/|__/_/ /_/\____/_____/_____/ ================================================ FILE: ct/headers/wikijs ================================================ _ ___ __ _ _ | | / (_) /__(_) (_)____ | | /| / / / //_/ / / / ___/ | |/ |/ / / ,< / / / (__ ) |__/|__/_/_/|_/_/_/ /____/ /___/ ================================================ FILE: ct/headers/wireguard ================================================ _ ___ __ | | / (_)_______ ____ ___ ______ __________/ / | | /| / / / ___/ _ \/ __ `/ / / / __ `/ ___/ __ / | |/ |/ / / / / __/ /_/ / /_/ / /_/ / / / /_/ / |__/|__/_/_/ \___/\__, /\__,_/\__,_/_/ \__,_/ /____/ ================================================ FILE: ct/headers/wishlist ================================================ _ ___ __ ___ __ | | / (_)____/ /_ / (_)____/ /_ | | /| / / / ___/ __ \/ / / ___/ __/ | |/ |/ / (__ ) / / / / (__ ) /_ |__/|__/_/____/_/ /_/_/_/____/\__/ ================================================ FILE: ct/headers/wizarr ================================================ _ ___ | | / (_)___ ____ ___________ | | /| / / /_ / / __ `/ ___/ ___/ | |/ |/ / / / /_/ /_/ / / / / |__/|__/_/ /___/\__,_/_/ /_/ ================================================ FILE: ct/headers/wordpress ================================================ _ __ __ | | / /___ _________/ /___ ________ __________ | | /| / / __ \/ ___/ __ / __ \/ ___/ _ \/ ___/ ___/ | |/ |/ / /_/ / / / /_/ / /_/ / / / __(__ |__ ) |__/|__/\____/_/ \__,_/ .___/_/ \___/____/____/ /_/ ================================================ FILE: ct/headers/writefreely ================================================ _ __ _ __ ______ __ | | / /____(_) /____ / ____/_______ ___ / /_ __ | | /| / / ___/ / __/ _ \/ /_ / ___/ _ \/ _ \/ / / / / | |/ |/ / / / / /_/ __/ __/ / / / __/ __/ / /_/ / |__/|__/_/ /_/\__/\___/_/ /_/ \___/\___/_/\__, / /____/ ================================================ FILE: ct/headers/yamtrack ================================================ __ __ __ __ \ \/ /___ _____ ___ / /__________ ______/ /__ \ / __ `/ __ `__ \/ __/ ___/ __ `/ ___/ //_/ / / /_/ / / / / / / /_/ / / /_/ / /__/ ,< /_/\__,_/_/ /_/ /_/\__/_/ \__,_/\___/_/|_| ================================================ FILE: ct/headers/yourls ================================================ __ ______ __ ______ __ _____ \ \/ / __ \/ / / / __ \/ / / ___/ \ / / / / / / / /_/ / / \__ \ / / /_/ / /_/ / _, _/ /______/ / /_/\____/\____/_/ |_/_____/____/ ================================================ FILE: ct/headers/yt-dlp-webui ================================================ __ ____ __ _ __ __/ /_ ____/ / /___ _ _____ / /_ __ __(_) / / / / __/_____/ __ / / __ \_____| | /| / / _ \/ __ \/ / / / / / /_/ / /_/_____/ /_/ / / /_/ /_____/ |/ |/ / __/ /_/ / /_/ / / \__, /\__/ \__,_/_/ .___/ |__/|__/\___/_.___/\__,_/_/ /____/ /_/ ================================================ FILE: ct/headers/yubal ================================================ __ __ __ __ \ \/ /_ __/ /_ ____ _/ / \ / / / / __ \/ __ `/ / / / /_/ / /_/ / /_/ / / /_/\__,_/_.___/\__,_/_/ ================================================ FILE: ct/headers/yunohost ================================================ __ __ __ __ __ \ \/ /_ ______ ____ / / / /___ _____/ /_ \ / / / / __ \/ __ \/ /_/ / __ \/ ___/ __/ / / /_/ / / / / /_/ / __ / /_/ (__ ) /_ /_/\__,_/_/ /_/\____/_/ /_/\____/____/\__/ ================================================ FILE: ct/headers/zabbix ================================================ _____ __ __ _ /__ / ____ _/ /_ / /_ (_) __ / / / __ `/ __ \/ __ \/ / |/_/ / /__/ /_/ / /_/ / /_/ / /> < /____/\__,_/_.___/_.___/_/_/|_| ================================================ FILE: ct/headers/zammad ================================================ _____ __ /__ / ____ _____ ___ ____ ___ ____ _____/ / / / / __ `/ __ `__ \/ __ `__ \/ __ `/ __ / / /__/ /_/ / / / / / / / / / / / /_/ / /_/ / /____/\__,_/_/ /_/ /_/_/ /_/ /_/\__,_/\__,_/ ================================================ FILE: ct/headers/zerobyte ================================================ _____ __ __ /__ / ___ _________ / /_ __ __/ /____ / / / _ \/ ___/ __ \/ __ \/ / / / __/ _ \ / /__/ __/ / / /_/ / /_/ / /_/ / /_/ __/ /____/\___/_/ \____/_.___/\__, /\__/\___/ /____/ ================================================ FILE: ct/headers/zerotier-one ================================================ _____ __ _ ____ /__ / ___ _________ / /_(_)__ _____ / __ \____ ___ / / / _ \/ ___/ __ \/ __/ / _ \/ ___/_____/ / / / __ \/ _ \ / /__/ __/ / / /_/ / /_/ / __/ / /_____/ /_/ / / / / __/ /____/\___/_/ \____/\__/_/\___/_/ \____/_/ /_/\___/ ================================================ FILE: ct/headers/zigbee2mqtt ================================================ _____ _ __ ___ __ _______ ____________ /__ / (_)___ _/ /_ ___ ___ |__ \ / |/ / __ \/_ __/_ __/ / / / / __ `/ __ \/ _ \/ _ \__/ // /|_/ / / / / / / / / / /__/ / /_/ / /_/ / __/ __/ __// / / / /_/ / / / / / /____/_/\__, /_.___/\___/\___/____/_/ /_/\___\_\/_/ /_/ /____/ ================================================ FILE: ct/headers/zipline ================================================ _____ _ ___ /__ / (_)___ / (_)___ ___ / / / / __ \/ / / __ \/ _ \ / /__/ / /_/ / / / / / / __/ /____/_/ .___/_/_/_/ /_/\___/ /_/ ================================================ FILE: ct/headers/zitadel ================================================ _____ _ __ __ __ /__ / (_) /_____ _____/ /__ / / / / / / __/ __ `/ __ / _ \/ / / /__/ / /_/ /_/ / /_/ / __/ / /____/_/\__/\__,_/\__,_/\___/_/ ================================================ FILE: ct/headers/zoraxy ================================================ _____ /__ / ____ _________ __ ____ __ / / / __ \/ ___/ __ `/ |/_/ / / / / /__/ /_/ / / / /_/ /> /dev/null || grep -q '^REDIS_IS_EXTERNAL=' /opt/homarr.env 2>/dev/null; }; then msg_info "Fixing old structure" systemctl disable -q --now nginx cp /opt/homarr/.env /opt/homarr.env echo "REDIS_IS_EXTERNAL='true'" >> /opt/homarr.env sed -i '/^\[Unit\]/a Requires=redis-server.service\nAfter=redis-server.service' /etc/systemd/system/homarr.service sed -i 's|^ExecStart=.*|ExecStart=/opt/homarr/run.sh|' /etc/systemd/system/homarr.service sed -i 's|^EnvironmentFile=.*|EnvironmentFile=-/opt/homarr.env|' /etc/systemd/system/homarr.service chown -R redis:redis /appdata/redis chmod 744 /appdata/redis mkdir -p /etc/systemd/system/redis-server.service.d/ cat </etc/systemd/system/redis-server.service.d/override.conf [Service] ReadWritePaths=-/appdata/redis -/var/lib/redis -/var/log/redis -/var/run/redis -/etc/redis EOF systemctl daemon-reload rm /opt/run_homarr.sh msg_ok "Fixed old structure" fi msg_info "Updating Nodejs" $STD apt update $STD apt upgrade nodejs -y msg_ok "Updated Nodejs" NODE_VERSION=$(curl -s https://raw.githubusercontent.com/homarr-labs/homarr/dev/package.json | jq -r '.engines.node | split(">=")[1] | split(".")[0]') setup_nodejs CLEAN_INSTALL=1 fetch_and_deploy_gh_release "homarr" "homarr-labs/homarr" "prebuild" "latest" "/opt/homarr" "build-debian-arm64.tar.gz" msg_info "Updating Homarr" cp /opt/homarr/redis.conf /etc/redis/redis.conf sed -i -e '$a\' /etc/redis/redis.conf grep -q '^bind 127.0.0.1 -::1$' /etc/redis/redis.conf || echo "bind 127.0.0.1 -::1" >> /etc/redis/redis.conf rm /etc/nginx/nginx.conf cp /opt/homarr/nginx.conf /etc/nginx/templates/nginx.conf msg_ok "Updated Homarr" msg_info "Starting Services" chmod +x /opt/homarr/run.sh systemctl start homarr systemctl start redis-server msg_ok "Started Services" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:7575${CL}" ================================================ FILE: ct/homeassistant.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.home-assistant.io/ APP="Home Assistant" var_tags="${var_tags:-automation;smarthome}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-16}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /var/lib/docker/volumes/hass_config/_data ]]; then msg_error "No ${APP} Installation Found!" exit fi UPD=$(msg_menu "Home Assistant Update Options" \ "1" "Update ALL Containers" \ "2" "Remove ALL Unused Images" \ "3" "Install HACS" \ "4" "Install FileBrowser") if [ "$UPD" == "1" ]; then msg_info "Updating All Containers" CONTAINER_LIST="${1:-$(docker ps -q)}" for container in ${CONTAINER_LIST}; do CONTAINER_IMAGE="$(docker inspect --format "{{.Config.Image}}" --type container "${container}")" RUNNING_IMAGE="$(docker inspect --format "{{.Image}}" --type container "${container}")" docker pull "${CONTAINER_IMAGE}" LATEST_IMAGE="$(docker inspect --format "{{.Id}}" --type image "${CONTAINER_IMAGE}")" if [[ "${RUNNING_IMAGE}" != "${LATEST_IMAGE}" ]]; then pip install -U runlike echo "Updating ${container} image ${CONTAINER_IMAGE}" DOCKER_COMMAND="$(runlike --use-volume-id "${container}")" docker rm --force "${container}" eval "${DOCKER_COMMAND}" fi done msg_ok "Updated All Containers" exit fi if [ "$UPD" == "2" ]; then msg_info "Removing ALL Unused Images" docker image prune -af msg_ok "Removed ALL Unused Images" exit fi if [ "$UPD" == "3" ]; then msg_info "Installing Home Assistant Community Store (HACS)" $STD apt update cd /var/lib/docker/volumes/hass_config/_data $STD bash <(curl -fsSL https://get.hacs.xyz) msg_ok "Installed Home Assistant Community Store (HACS)" echo -e "\n Reboot Home Assistant and clear browser cache then Add HACS integration.\n" exit fi if [ "$UPD" == "4" ]; then msg_info "Installing FileBrowser" RELEASE=$(curl -fsSL https://api.github.com/repos/filebrowser/filebrowser/releases/latest | grep -o '"tag_name": ".*"' | sed 's/"//g' | sed 's/tag_name: //g') $STD curl -fsSL https://github.com/filebrowser/filebrowser/releases/download/v2.23.0/linux-arm64-filebrowser.tar.gz | tar -xzv -C /usr/local/bin $STD filebrowser config init -a '0.0.0.0' $STD filebrowser config set -a '0.0.0.0' $STD filebrowser users add admin community-scripts.org --perm.admin msg_ok "Installed FileBrowser" msg_info "Creating Service" service_path="/etc/systemd/system/filebrowser.service" echo "[Unit] Description=Filebrowser After=network-online.target [Service] User=root WorkingDirectory=/root/ ExecStart=/usr/local/bin/filebrowser -r / [Install] WantedBy=default.target" >$service_path $STD systemctl enable --now filebrowser msg_ok "Created Service" msg_ok "Completed successfully!\n" echo -e "FileBrowser should be reachable by going to the following URL. ${BL}http://$LOCAL_IP:8080${CL} admin|community-scripts.org\n" exit fi } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}HA: http://${IP}:8123${CL}" echo -e "${TAB}${GATEWAY}${BGN}Portainer: https://${IP}:9443${CL}" ================================================ FILE: ct/homebox.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck | Co-Author: MickLesk (Canbiz) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://homebox.software/en/ APP="HomeBox" var_tags="${var_tags:-inventory;household}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-1024}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -f /etc/systemd/system/homebox.service ]]; then msg_error "No ${APP} Installation Found!" exit fi if [[ -x /opt/homebox ]]; then sed -i 's|WorkingDirectory=/opt$|WorkingDirectory=/opt/homebox|' /etc/systemd/system/homebox.service sed -i 's|ExecStart=/opt/homebox$|ExecStart=/opt/homebox/homebox|' /etc/systemd/system/homebox.service sed -i 's|EnvironmentFile=/opt/.env$|EnvironmentFile=/opt/homebox/.env|' /etc/systemd/system/homebox.service systemctl daemon-reload fi if check_for_gh_release "homebox" "sysadminsmedia/homebox"; then msg_info "Stopping Service" systemctl stop homebox msg_ok "Stopped Service" if [ -f /opt/homebox ] && [ -x /opt/homebox ]; then rm -f /opt/homebox fi fetch_and_deploy_gh_release "homebox" "sysadminsmedia/homebox" "prebuild" "latest" "/opt/homebox" "homebox_Linux_x86_64.tar.gz" chmod +x /opt/homebox/homebox [ -f /opt/.env ] && mv /opt/.env /opt/homebox/.env [ -d /opt/.data ] && mv /opt/.data /opt/homebox/.data msg_info "Starting Service" systemctl start homebox msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:7745${CL}" ================================================ FILE: ct/homebridge.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://homebridge.io/ APP="Homebridge" var_tags="${var_tags:-smarthome;homekit}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-1024}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if ! dpkg -s homebridge >/dev/null 2>&1; then msg_error "No ${APP} Installation Found!" exit fi msg_info "Updating ${APP} LXC" $STD apt update $STD apt install -y homebridge msg_ok "Updated successfully!" exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8581${CL}" ================================================ FILE: ct/homelable.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/Pouzor/homelable APP="Homelable" var_tags="${var_tags:-monitoring;network;visualization}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-8}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/homelable ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "homelable" "Pouzor/homelable"; then msg_info "Stopping Service" systemctl stop homelable msg_ok "Stopped Service" msg_info "Backing up Configuration and Data" cp /opt/homelable/backend/.env /opt/homelable.env.bak cp -r /opt/homelable/data /opt/homelable_data_bak msg_ok "Backed up Configuration and Data" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "homelable" "Pouzor/homelable" "tarball" "latest" "/opt/homelable" msg_info "Updating Python Dependencies" cd /opt/homelable/backend $STD uv venv --clear /opt/homelable/backend/.venv $STD uv pip install --python /opt/homelable/backend/.venv/bin/python -r requirements.txt msg_ok "Updated Python Dependencies" msg_info "Rebuilding Frontend" cd /opt/homelable/frontend $STD npm ci $STD npm run build msg_ok "Rebuilt Frontend" msg_info "Restoring Configuration and Data" cp /opt/homelable.env.bak /opt/homelable/backend/.env cp -r /opt/homelable_data_bak/. /opt/homelable/data/ rm -f /opt/homelable.env.bak rm -rf /opt/homelable_data_bak msg_ok "Restored Configuration and Data" msg_info "Starting Service" systemctl start homelable msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed Successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:3000${CL}" ================================================ FILE: ct/homepage.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://gethomepage.dev/ APP="Homepage" var_tags="${var_tags:-dashboard}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-4096}" var_disk="${var_disk:-6}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/homepage ]]; then msg_error "No ${APP} Installation Found!" exit fi NODE_VERSION="22" NODE_MODULE="pnpm@latest" setup_nodejs ensure_dependencies jq if check_for_gh_release "homepage" "gethomepage/homepage"; then msg_info "Stopping service" systemctl stop homepage msg_ok "Stopped service" msg_info "Creating Backup" cp /opt/homepage/.env /opt/homepage.env cp -r /opt/homepage/config /opt/homepage_config_backup [[ -d /opt/homepage/public/images ]] && cp -r /opt/homepage/public/images /opt/homepage_images_backup [[ -d /opt/homepage/public/icons ]] && cp -r /opt/homepage/public/icons /opt/homepage_icons_backup msg_ok "Created Backup" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "homepage" "gethomepage/homepage" "tarball" msg_info "Restoring Backup" mv /opt/homepage.env /opt/homepage rm -rf /opt/homepage/config mv /opt/homepage_config_backup /opt/homepage/config msg_ok "Restored Backup" msg_info "Updating Homepage (Patience)" RELEASE=$(get_latest_github_release "gethomepage/homepage") cd /opt/homepage echo 'onlyBuiltDependencies=*' >> .npmrc $STD pnpm install $STD pnpm update --no-save caniuse-lite export NEXT_PUBLIC_VERSION="v$RELEASE" export NEXT_PUBLIC_REVISION="source" export NEXT_PUBLIC_BUILDTIME=$(curl -fsSL https://api.github.com/repos/gethomepage/homepage/releases/latest | jq -r '.published_at') export NEXT_TELEMETRY_DISABLED=1 $STD pnpm build [[ -d /opt/homepage_images_backup ]] && mv /opt/homepage_images_backup /opt/homepage/public/images [[ -d /opt/homepage_icons_backup ]] && mv /opt/homepage_icons_backup /opt/homepage/public/icons msg_ok "Updated Homepage" msg_info "Starting service" systemctl start homepage msg_ok "Started service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:3000${CL}" ================================================ FILE: ct/homer.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/bastienwirtz/homer APP="Homer" var_tags="${var_tags:-dashboard}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-512}" var_disk="${var_disk:-2}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/homer ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "homer" "bastienwirtz/homer"; then msg_info "Stopping Service" systemctl stop homer msg_ok "Stopped Service" msg_info "Backing up assets directory" cd ~ mkdir -p assets-backup cp -R /opt/homer/assets/. assets-backup msg_ok "Backed up assets directory" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "homer" "bastienwirtz/homer" "prebuild" "latest" "/opt/homer" "homer.zip" msg_info "Restoring assets directory" cd ~ cp -Rf assets-backup/. /opt/homer/assets/ rm -rf assets-backup msg_ok "Restored assets directory" msg_info "Starting Service" systemctl start homer msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8010${CL}" ================================================ FILE: ct/hoodik.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/hudikhq/hoodik APP="Hoodik" var_tags="${var_tags:-cloud;storage}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-1024}" var_disk="${var_disk:-5}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -f /opt/hoodik/hoodik ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "hoodik" "hudikhq/hoodik"; then msg_info "Stopping Service" systemctl stop hoodik msg_ok "Stopped Service" msg_info "Backing up Configuration" cp /opt/hoodik/.env /opt/hoodik.env.bak msg_ok "Backed up Configuration" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "hoodik" "hudikhq/hoodik" "prebuild" "latest" "/opt/hoodik" "*x86_64.tar.gz" msg_info "Restoring Configuration" cp /opt/hoodik.env.bak /opt/hoodik/.env rm -f /opt/hoodik.env.bak msg_ok "Restored Configuration" msg_info "Starting Service" systemctl start hoodik msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:5443/auth/register${CL}" ================================================ FILE: ct/hortusfox.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/danielbrendel/hortusfox-web APP="HortusFox" var_tags="${var_tags:-plants}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-5}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/hortusfox ]]; then msg_error "No ${APP} Installation Found!" exit fi setup_mariadb if check_for_gh_release "hortusfox" "danielbrendel/hortusfox-web"; then msg_info "Stopping Service" systemctl stop apache2 msg_ok "Stopped Service" msg_info "Backing up current HortusFox installation" cd /opt mv /opt/hortusfox/ /opt/hortusfox-backup msg_ok "Backed up current HortusFox installation" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "hortusfox" "danielbrendel/hortusfox-web" "tarball" msg_info "Updating HortusFox" cd /opt/hortusfox cp /opt/hortusfox-backup/.env /opt/hortusfox/.env cp -a /opt/hortusfox-backup/public/img/. /opt/hortusfox/public/img/ export COMPOSER_ALLOW_SUPERUSER=1 $STD composer install --no-dev --optimize-autoloader $STD php asatru migrate:upgrade $STD php asatru plants:attributes $STD php asatru calendar:classes chown -R www-data:www-data /opt/hortusfox rm -r /opt/hortusfox-backup msg_ok "Updated HortusFox" msg_info "Starting Service" systemctl start apache2 msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}${CL}" ================================================ FILE: ct/hyperhdr.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.hyperhdr.eu/ APP="HyperHDR" var_tags="${var_tags:-ambient-lightning}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-0}" var_gpu="${var_gpu:-yes}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /var ]]; then msg_error "No ${APP} Installation Found!" exit fi msg_info "Updating $APP LXC" $STD apt update $STD apt -y upgrade msg_ok "Updated $APP LXC" msg_ok "Updated successfully!" exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8090${CL}" ================================================ FILE: ct/hyperion.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://hyperion-project.org/forum/ APP="Hyperion" var_tags="${var_tags:-ambient-lightning}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-512}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" var_gpu="${var_gpu:-yes}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -f /etc/apt/sources.list.d/hyperion.list ]]; then msg_error "No ${APP} Installation Found!" exit fi msg_info "Updating ${APP} LXC" $STD apt update $STD apt install -y hyperion msg_ok "Updated successfully!" exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8090${CL}" ================================================ FILE: ct/igotify.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: pfassina # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/androidseb25/iGotify-Notification-Assistent APP="iGotify" var_tags="${var_tags:-notifications;gotify}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/igotify ]]; then msg_error "No iGotify Installation Found!" exit fi if check_for_gh_release "igotify" "androidseb25/iGotify-Notification-Assistent"; then msg_info "Stopping Service" systemctl stop igotify msg_ok "Stopped Service" msg_info "Backing up Configuration" cp /opt/igotify/.env /opt/igotify.env.bak msg_ok "Backed up Configuration" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "igotify" "androidseb25/iGotify-Notification-Assistent" "prebuild" "latest" "/opt/igotify" "iGotify-Notification-Service-amd64-v*.zip" msg_info "Restoring Configuration" cp /opt/igotify.env.bak /opt/igotify/.env rm -f /opt/igotify.env.bak msg_ok "Restored Configuration" msg_info "Starting Service" systemctl start igotify msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed Successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}${CL}" ================================================ FILE: ct/immich.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: vhsdream # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://immich.app APP="immich" var_tags="${var_tags:-photos}" var_disk="${var_disk:-20}" var_cpu="${var_cpu:-4}" var_ram="${var_ram:-6144}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" var_gpu="${var_gpu:-yes}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/immich ]]; then msg_error "No ${APP} Installation Found!" exit fi if [[ -f /etc/apt/sources.list.d/immich.list ]]; then msg_error "Wrong Debian version detected!" msg_error "You must upgrade your LXC to Debian Trixie before updating." msg_error "Please visit https://github.com/community-scripts/ProxmoxVE/discussions/7726 for details." echo "${TAB3} If you have upgraded your LXC to Trixie and you still see this message, please open an Issue in the Community-Scripts repo." exit fi if ! grep -qE '(^|[[:space:]])testing([[:space:]]|$)' /etc/apt/sources.list.d/debian.sources 2>/dev/null; then msg_info "Adding Debian Testing repo" if grep -q "trixie-updates" /etc/apt/sources.list.d/debian.sources 2>/dev/null; then sed -i 's/ trixie-updates/ trixie-updates testing/g' /etc/apt/sources.list.d/debian.sources else sed -i '/^[[:space:]]*Suites:.*trixie/ s/$/ testing/' /etc/apt/sources.list.d/debian.sources fi cat </etc/apt/preferences.d/preferences Package: * Pin: release a=unstable Pin-Priority: 450 Package: * Pin:release a=testing Pin-Priority: 450 EOF [[ -f /etc/apt/preferences.d/immich ]] && rm /etc/apt/preferences.d/immich $STD apt update msg_ok "Added Debian Testing repo" fi if ! dpkg -l "libmimalloc3" | grep -q '3.1' || ! dpkg -l "libde265-dev" | grep -q '1.0.16'; then msg_info "Installing/upgrading Testing repo packages" $STD apt install -t testing libmimalloc3 libde265-dev -y msg_ok "Installed/upgraded Testing repo packages" fi if [[ ! -f /etc/apt/sources.list.d/mise.list ]]; then msg_info "Installing Mise" curl -fSs https://mise.jdx.dev/gpg-key.pub | tee /etc/apt/keyrings/mise-archive-keyring.pub 1>/dev/null echo "deb [signed-by=/etc/apt/keyrings/mise-archive-keyring.pub arch=arm64] https://mise.jdx.dev/deb stable main" >/etc/apt/sources.list.d/mise.list ensure_dependencies mise msg_ok "Installed Mise" fi STAGING_DIR=/opt/staging BASE_DIR=${STAGING_DIR}/base-images SOURCE_DIR=${STAGING_DIR}/image-source cd /tmp if [[ -f ~/.intel_version ]]; then curl_with_retry "https://raw.githubusercontent.com/immich-app/immich/refs/heads/main/machine-learning/Dockerfile" "Dockerfile" readarray -t INTEL_URLS < <( sed -n "/intel-[igc|opencl]/p" ./Dockerfile | awk '{print $3}' sed -n "/libigdgmm12/p" ./Dockerfile | awk '{print $3}' ) INTEL_RELEASE="$(grep "intel-opencl-icd_" ./Dockerfile | awk -F '_' '{print $2}')" if [[ "$INTEL_RELEASE" != "$(cat ~/.intel_version)" ]]; then msg_info "Updating Intel OpenVINO dependencies" for url in "${INTEL_URLS[@]}"; do curl_with_retry "$url" "$(basename "$url")" done $STD apt-mark unhold libigdgmm12 $STD apt install -y --allow-downgrades ./libigdgmm12*.deb rm ./libigdgmm12*.deb $STD apt install -y ./*.deb rm ./*.deb $STD apt-mark hold libigdgmm12 dpkg-query -W -f='${Version}\n' intel-opencl-icd >~/.intel_version rm -f ./Dockerfile msg_ok "Updated Intel OpenVINO dependencies" fi fi if [[ -f ~/.immich_library_revisions ]]; then libraries=("libjxl" "libheif" "libraw" "imagemagick" "libvips") cd "$BASE_DIR" msg_warn "Checking for updates to custom image-processing libraries (recompile time: 2-15min per library)" $STD git pull for library in "${libraries[@]}"; do compile_"$library" done msg_ok "Image-processing libraries up to date" fi RELEASE="v2.7.5" if check_for_gh_release "Immich" "immich-app/immich" "${RELEASE}" "each release is tested individually before the version is updated. Please do not open issues for this"; then if [[ $(cat ~/.immich) > "2.5.1" ]]; then msg_info "Enabling Maintenance Mode" cd /opt/immich/app/bin $STD ./immich-admin enable-maintenance-mode export MAINT_MODE=1 $STD cd - msg_ok "Enabled Maintenance Mode" fi msg_info "Stopping Services" systemctl stop immich-web systemctl stop immich-ml msg_ok "Stopped Services" VCHORD_RELEASE="0.5.3" if [[ ! -f ~/.vchord_version ]] || [[ "$VCHORD_RELEASE" != "$(cat ~/.vchord_version)" ]]; then msg_info "Upgrading VectorChord" curl -fsSL "https://github.com/tensorchord/vectorchord/releases/download/${VCHORD_RELEASE}/postgresql-16-vchord_${VCHORD_RELEASE}-1_arm64.deb" -o vchord.deb $STD apt install -y ./vchord.deb systemctl restart postgresql $STD sudo -u postgres psql -d immich -c "ALTER EXTENSION vector UPDATE;" $STD sudo -u postgres psql -d immich -c "ALTER EXTENSION vchord UPDATE;" $STD sudo -u postgres psql -d immich -c "REINDEX INDEX face_index;" $STD sudo -u postgres psql -d immich -c "REINDEX INDEX clip_index;" fi ensure_dependencies ccache gcc-13 g++-13 INSTALL_DIR="/opt/${APP}" UPLOAD_DIR="$(sed -n '/^IMMICH_MEDIA_LOCATION/s/[^=]*=//p' /opt/immich/.env)" SRC_DIR="${INSTALL_DIR}/source" APP_DIR="${INSTALL_DIR}/app" PLUGIN_DIR="${APP_DIR}/corePlugin" ML_DIR="${APP_DIR}/machine-learning" GEO_DIR="${INSTALL_DIR}/geodata" [[ -f "$ML_DIR"/ml_start.sh ]] && cp "$ML_DIR"/ml_start.sh "$INSTALL_DIR" if grep -qs "set -a" "$APP_DIR"/bin/start.sh && grep -qs "warnings" "$APP_DIR"/bin/start.sh; then cp "$APP_DIR"/bin/start.sh "$INSTALL_DIR" else cat <"$INSTALL_DIR"/start.sh #!/usr/bin/env bash set -a . ${INSTALL_DIR}/.env set +a /usr/bin/node --no-warnings ${APP_DIR}/dist/main.js "\$@" EOF chmod +x "$INSTALL_DIR"/start.sh fi ( shopt -s dotglob rm -rf "${APP_DIR:?}"/* ) setup_uv CLEAN_INSTALL=1 fetch_and_deploy_gh_release "Immich" "immich-app/immich" "tarball" "${RELEASE}" "$SRC_DIR" PNPM_VERSION="$(jq -r '.packageManager | split("@")[1] | split("+")[0]' ${SRC_DIR}/package.json)" NODE_VERSION="24" NODE_MODULE="pnpm@${PNPM_VERSION}" setup_nodejs msg_info "Updating Immich web and microservices" cd "$SRC_DIR"/server export COREPACK_ENABLE_DOWNLOAD_PROMPT=0 export CI=1 corepack enable # server build export SHARP_IGNORE_GLOBAL_LIBVIPS=true $STD pnpm --filter immich --frozen-lockfile build unset SHARP_IGNORE_GLOBAL_LIBVIPS export SHARP_FORCE_GLOBAL_LIBVIPS=true $STD pnpm --filter immich --frozen-lockfile --prod --no-optional deploy "$APP_DIR" # Patch helmet.json: disable upgrade-insecure-requests for HTTP access if [[ -f "$APP_DIR/helmet.json" ]]; then jq '.contentSecurityPolicy.directives["upgrade-insecure-requests"] = null' "$APP_DIR/helmet.json" >"$APP_DIR/helmet.json.tmp" && mv "$APP_DIR/helmet.json.tmp" "$APP_DIR/helmet.json" fi cp "$APP_DIR"/package.json "$APP_DIR"/bin sed -i "s|^start|${APP_DIR}/bin/start|" "$APP_DIR"/bin/immich-admin # openapi & web build cd "$SRC_DIR" echo "packageImportMethod: hardlink" >>./pnpm-workspace.yaml $STD pnpm --filter @immich/sdk --filter immich-web --frozen-lockfile --force install unset SHARP_FORCE_GLOBAL_LIBVIPS export SHARP_IGNORE_GLOBAL_LIBVIPS=true $STD pnpm --filter @immich/sdk --filter immich-web build cp -a web/build "$APP_DIR"/www cp LICENSE "$APP_DIR" # cli build $STD pnpm --filter @immich/sdk --filter @immich/cli --frozen-lockfile install $STD pnpm --filter @immich/sdk --filter @immich/cli build $STD pnpm --filter @immich/cli --prod --no-optional deploy "$APP_DIR"/cli [[ -f "$INSTALL_DIR"/start.sh ]] && mv "$INSTALL_DIR"/start.sh "$APP_DIR"/bin # plugins cd "$SRC_DIR" $STD mise trust --ignore ./mise.toml $STD mise trust ./plugins/mise.toml cd plugins $STD mise install $STD mise run build mkdir -p "$PLUGIN_DIR" cp -r ./dist "$PLUGIN_DIR"/dist cp ./manifest.json "$PLUGIN_DIR" msg_ok "Updated Immich server, web, cli and plugins" cd "$SRC_DIR"/machine-learning mkdir -p "$ML_DIR" # chown excluding upload dir contents (may be a mount with restricted permissions) chown immich:immich "$INSTALL_DIR" find "$INSTALL_DIR" -maxdepth 1 -mindepth 1 ! -name upload -exec chown -R immich:immich {} + chown immich:immich "${UPLOAD_DIR:-$INSTALL_DIR/upload}" 2>/dev/null || true chown immich:immich ./uv.lock export VIRTUAL_ENV="${ML_DIR}"/ml-venv export UV_HTTP_TIMEOUT=300 if [[ -f ~/.openvino ]]; then msg_info "Updating HW-accelerated machine-learning" $STD uv add --no-sync --optional openvino onnxruntime-openvino==1.24.1 --active -n -p python3.13 --managed-python $STD sudo --preserve-env=VIRTUAL_ENV -nu immich uv sync --extra openvino --no-dev --active --link-mode copy -n -p python3.13 --managed-python local sofile if [[ "$(uname -m)" == "aarch64" || "$(uname -m)" == "arm64" ]]; then sofile="${VIRTUAL_ENV}/lib/python3.13/site-packages/onnxruntime/capi/onnxruntime_pybind11_state.cpython-313-aarch64-linux-gnu.so" else sofile="${VIRTUAL_ENV}/lib/python3.13/site-packages/onnxruntime/capi/onnxruntime_pybind11_state.cpython-313-x86_64-linux-gnu.so" fi patchelf --clear-execstack "$sofile" msg_ok "Updated HW-accelerated machine-learning" else ML_PYTHON="python3.11" msg_info "Pre-installing Python ${ML_PYTHON} for machine-learning" for attempt in $(seq 1 3); do $STD sudo --preserve-env=VIRTUAL_ENV -nu immich uv python install "${ML_PYTHON}" && break [[ $attempt -lt 3 ]] && msg_warn "Python download attempt $attempt failed, retrying..." && sleep 5 done msg_ok "Pre-installed Python ${ML_PYTHON}" msg_info "Updating machine-learning" for attempt in $(seq 1 3); do $STD sudo --preserve-env=VIRTUAL_ENV,UV_HTTP_TIMEOUT -nu immich uv sync --extra cpu --no-dev --active --link-mode copy -n -p "${ML_PYTHON}" --managed-python && break [[ $attempt -lt 3 ]] && msg_warn "uv sync attempt $attempt failed, retrying..." && sleep 10 done msg_ok "Updated machine-learning" fi cd "$SRC_DIR" cp -a machine-learning/{ann,immich_ml} "$ML_DIR" [[ -f "$INSTALL_DIR"/ml_start.sh ]] && mv "$INSTALL_DIR"/ml_start.sh "$ML_DIR" [[ -f ~/.openvino ]] && sed -i "/intra_op/s/int = 0/int = os.cpu_count() or 0/" "$ML_DIR"/immich_ml/config.py ln -sf "$APP_DIR"/resources "$INSTALL_DIR" cd "$APP_DIR" grep -rl /usr/src | xargs -n1 sed -i "s|\/usr/src|$INSTALL_DIR|g" grep -rlE "'/build'" | xargs -n1 sed -i "s|'/build'|'$APP_DIR'|g" sed -i "s@\"/cache\"@\"$INSTALL_DIR/cache\"@g" "$ML_DIR"/immich_ml/config.py ln -s "${UPLOAD_DIR:-/opt/immich/upload}" "$APP_DIR"/upload ln -s "${UPLOAD_DIR:-/opt/immich/upload}" "$ML_DIR"/upload ln -s "$GEO_DIR" "$APP_DIR" [[ ! -f /usr/bin/immich ]] && ln -sf "$APP_DIR"/cli/bin/immich /usr/bin/immich [[ ! -f /usr/bin/immich-admin ]] && ln -sf "$APP_DIR"/bin/immich-admin /usr/bin/immich-admin if ! grep -q '^DB_HOSTNAME=' "$INSTALL_DIR"/.env; then sed -i '/^DB_DATABASE_NAME/a DB_HOSTNAME=127.0.0.1' "$INSTALL_DIR"/.env fi if ! grep -q 'HELMET_FILE' "$INSTALL_DIR"/.env; then sed -i -e '$a\' "$INSTALL_DIR"/.env echo "IMMICH_HELMET_FILE=true" >>"$INSTALL_DIR"/.env fi if grep -q 'ExecStart=/usr/bin/node' /etc/systemd/system/immich-web.service; then sed -i '/^EnvironmentFile=/d' /etc/systemd/system/immich-web.service sed -i "s|^ExecStart=.*|ExecStart=${APP_DIR}/bin/start.sh|" /etc/systemd/system/immich-web.service systemctl daemon-reload fi # chown excluding upload dir contents (may be a mount with restricted permissions) chown immich:immich "$INSTALL_DIR" find "$INSTALL_DIR" -maxdepth 1 -mindepth 1 ! -name upload -exec chown -R immich:immich {} + chown immich:immich "${UPLOAD_DIR:-$INSTALL_DIR/upload}" 2>/dev/null || true if [[ "${MAINT_MODE:-0}" == 1 ]]; then msg_info "Disabling Maintenance Mode" cd /opt/immich/app/bin $STD ./immich-admin disable-maintenance-mode || true unset MAINT_MODE $STD cd - msg_ok "Disabled Maintenance Mode" fi systemctl restart immich-ml immich-web [[ -f /etc/systemd/system/immich-proxy.service ]] && systemctl restart immich-proxy msg_ok "Updated successfully!" fi exit } function compile_libjxl() { SOURCE=${SOURCE_DIR}/libjxl JPEGLI_LIBJPEG_LIBRARY_SOVERSION="62" JPEGLI_LIBJPEG_LIBRARY_VERSION="62.3.0" LIBJXL_REVISION="794a5dcf0d54f9f0b20d288a12e87afb91d20dfc" # : "${LIBJXL_REVISION:=$(jq -cr '.revision' "$BASE_DIR"/server/sources/libjxl.json)}" if [[ "$LIBJXL_REVISION" != "$(grep 'libjxl' ~/.immich_library_revisions | awk '{print $2}')" ]]; then msg_info "Recompiling libjxl" [[ -d "$SOURCE" ]] && rm -rf "$SOURCE" $STD git clone https://github.com/libjxl/libjxl.git "$SOURCE" cd "$SOURCE" $STD git reset --hard "$LIBJXL_REVISION" $STD git submodule update --init --recursive --depth 1 --recommend-shallow $STD git apply "$BASE_DIR"/server/sources/libjxl-patches/jpegli-empty-dht-marker.patch $STD git apply "$BASE_DIR"/server/sources/libjxl-patches/jpegli-icc-warning.patch mkdir build cd build $STD cmake \ -DCMAKE_BUILD_TYPE=Release \ -DBUILD_TESTING=OFF \ -DJPEGXL_ENABLE_DOXYGEN=OFF \ -DJPEGXL_ENABLE_MANPAGES=OFF \ -DJPEGXL_ENABLE_PLUGIN_GIMP210=OFF \ -DJPEGXL_ENABLE_BENCHMARK=OFF \ -DJPEGXL_ENABLE_EXAMPLES=OFF \ -DJPEGXL_FORCE_SYSTEM_BROTLI=ON \ -DJPEGXL_FORCE_SYSTEM_HWY=ON \ -DJPEGXL_ENABLE_JPEGLI=ON \ -DJPEGXL_ENABLE_JPEGLI_LIBJPEG=ON \ -DJPEGXL_INSTALL_JPEGLI_LIBJPEG=ON \ -DJPEGXL_ENABLE_PLUGINS=ON \ -DJPEGLI_LIBJPEG_LIBRARY_SOVERSION="$JPEGLI_LIBJPEG_LIBRARY_SOVERSION" \ -DJPEGLI_LIBJPEG_LIBRARY_VERSION="$JPEGLI_LIBJPEG_LIBRARY_VERSION" \ -DLIBJPEG_TURBO_VERSION_NUMBER=2001005 \ .. $STD cmake --build . -- -j"$(nproc)" $STD cmake --install . ldconfig /usr/local/lib $STD make clean cd "$STAGING_DIR" rm -rf "$SOURCE"/{build,third_party} sed -i "s/libjxl: .*$/libjxl: $LIBJXL_REVISION/" ~/.immich_library_revisions msg_ok "Recompiled libjxl" fi } function compile_libheif() { SOURCE=${SOURCE_DIR}/libheif ensure_dependencies libaom-dev LIBHEIF_REVISION="35dad50a9145332a7bfdf1ff6aef6801fb613d68" # : "${LIBHEIF_REVISION:=$(jq -cr '.revision' "$BASE_DIR"/server/sources/libheif.json)}" if [[ "${update:-}" ]] || [[ "$LIBHEIF_REVISION" != "$(grep 'libheif' ~/.immich_library_revisions | awk '{print $2}')" ]]; then msg_info "Recompiling libheif" [[ -d "$SOURCE" ]] && rm -rf "$SOURCE" $STD git clone https://github.com/strukturag/libheif.git "$SOURCE" cd "$SOURCE" $STD git reset --hard "$LIBHEIF_REVISION" mkdir build cd build $STD cmake --preset=release-noplugins \ -DWITH_DAV1D=ON \ -DENABLE_PARALLEL_TILE_DECODING=ON \ -DWITH_LIBSHARPYUV=ON \ -DWITH_LIBDE265=ON \ -DWITH_AOM_DECODER=OFF \ -DWITH_AOM_ENCODER=ON \ -DWITH_X265=OFF \ -DWITH_EXAMPLES=OFF \ .. $STD make install -j"$(nproc)" ldconfig /usr/local/lib $STD make clean cd "$STAGING_DIR" rm -rf "$SOURCE"/build sed -i "s/libheif: .*$/libheif: $LIBHEIF_REVISION/" ~/.immich_library_revisions msg_ok "Recompiled libheif" fi } function compile_libraw() { SOURCE=${SOURCE_DIR}/libraw LIBRAW_REVISION="0b56545a4f828743f28a4345cdfdd4c49f9f9a2a" # : "${LIBRAW_REVISION:=$(jq -cr '.revision' "$BASE_DIR"/server/sources/libraw.json)}" if [[ "$LIBRAW_REVISION" != "$(grep 'libraw' ~/.immich_library_revisions | awk '{print $2}')" ]]; then msg_info "Recompiling libraw" [[ -d "$SOURCE" ]] && rm -rf "$SOURCE" $STD git clone https://github.com/LibRaw/LibRaw.git "$SOURCE" cd "$SOURCE" $STD git reset --hard "$LIBRAW_REVISION" $STD autoreconf --install $STD ./configure --disable-examples $STD make -j"$(nproc)" $STD make install ldconfig /usr/local/lib $STD make clean cd "$STAGING_DIR" sed -i "s/libraw: .*$/libraw: $LIBRAW_REVISION/" ~/.immich_library_revisions msg_ok "Recompiled libraw" fi } function compile_imagemagick() { SOURCE=$SOURCE_DIR/imagemagick : "${IMAGEMAGICK_REVISION:=$(jq -cr '.revision' "$BASE_DIR"/server/sources/imagemagick.json)}" if [[ "$IMAGEMAGICK_REVISION" != "$(grep 'imagemagick' ~/.immich_library_revisions | awk '{print $2}')" ]] || ! grep -q 'DMAGICK_LIBRAW' /usr/local/lib/ImageMagick-7*/config-Q16HDRI/configure.xml; then msg_info "Recompiling ImageMagick" [[ -d "$SOURCE" ]] && rm -rf "$SOURCE" $STD git clone https://github.com/ImageMagick/ImageMagick.git "$SOURCE" cd "$SOURCE" $STD git reset --hard "$IMAGEMAGICK_REVISION" $STD ./configure --with-modules CPPFLAGS="-DMAGICK_LIBRAW_VERSION_TAIL=202502" $STD make -j"$(nproc)" $STD make install ldconfig /usr/local/lib $STD make clean cd "$STAGING_DIR" sed -i "s/imagemagick: .*$/imagemagick: $IMAGEMAGICK_REVISION/" ~/.immich_library_revisions msg_ok "Recompiled ImageMagick" fi } function compile_libvips() { SOURCE=$SOURCE_DIR/libvips LIBVIPS_REVISION="0c9151a4f416d2f8ae20a755db218f6637050eec" if [[ "$LIBVIPS_REVISION" != "$(grep 'libvips' ~/.immich_library_revisions | awk '{print $2}')" ]]; then msg_info "Recompiling libvips" [[ -d "$SOURCE" ]] && rm -rf "$SOURCE" $STD git clone https://github.com/libvips/libvips.git "$SOURCE" cd "$SOURCE" $STD git reset --hard "$LIBVIPS_REVISION" $STD meson setup build --buildtype=release --libdir=lib -Dintrospection=disabled -Dtiff=disabled cd build $STD ninja install ldconfig /usr/local/lib cd "$STAGING_DIR" rm -rf "$SOURCE"/build sed -i "s/libvips: .*$/libvips: $LIBVIPS_REVISION/" ~/.immich_library_revisions msg_ok "Recompiled libvips" fi } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:2283${CL}" ================================================ FILE: ct/immichframe.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Thiago Canozzo Lahr (tclahr) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/immichFrame/ImmichFrame APP="ImmichFrame" var_tags="${var_tags:-photos;slideshow}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-1024}" var_disk="${var_disk:-8}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/immichframe ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "immichframe" "immichFrame/ImmichFrame"; then msg_info "Stopping Service" systemctl stop immichframe msg_ok "Stopped Service" msg_info "Backing up Configuration" cp -r /opt/immichframe/Config /tmp/immichframe_config.bak msg_ok "Backed up Configuration" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "immichframe" "immichFrame/ImmichFrame" "tarball" "latest" "/tmp/immichframe" msg_info "Setting up ImmichFrame" cd /tmp/immichframe $STD dotnet publish ImmichFrame.WebApi/ImmichFrame.WebApi.csproj \ --configuration Release \ --runtime linux-x64 \ --self-contained false \ --output /opt/immichframe cd /tmp/immichframe/immichFrame.Web $STD npm ci --silent $STD npm run build rm -rf /opt/immichframe/wwwroot/* cp -r build/* /opt/immichframe/wwwroot rm -rf /tmp/immichframe msg_ok "Setup ImmichFrame" msg_info "Restoring Configuration" cp -r /tmp/immichframe_config.bak/* /opt/immichframe/Config/ rm -rf /tmp/immichframe_config.bak chown -R immichframe:immichframe /opt/immichframe msg_ok "Restored Configuration" msg_info "Starting Service" systemctl start immichframe msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8080${CL}" ================================================ FILE: ct/infisical.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Slaviša Arežina (tremor021) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://infisical.com/ APP="Infisical" var_tags="${var_tags:-auth}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-6}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /etc/infisical ]]; then msg_error "No ${APP} Installation Found!" exit fi msg_info "Stopping service" $STD infisical-ctl stop msg_ok "Service stopped" msg_info "Creating backup" [[ -f /opt/infisical_backup.sql ]] && rm -f /opt/infisical_backup.sql DB_PASS=$(grep -Po '(?<=^Database Password:\s).*' ~/infisical.creds | head -n1) PGPASSWORD=$DB_PASS pg_dump -U infisical -h localhost -d infisical_db > /opt/infisical_backup.sql msg_ok "Created backup" msg_info "Updating Infisical" $STD apt update $STD apt install -y infisical-core $STD infisical-ctl reconfigure msg_ok "Updated Infisical" msg_info "Starting service" infisical-ctl start msg_ok "Started service" msg_ok "Updated successfully!" exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8080${CL}" ================================================ FILE: ct/influxdb.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.influxdata.com/ APP="InfluxDB" var_tags="${var_tags:-database}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-8}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -f /usr/bin/influxd && ! -f /usr/bin/influxdb3 ]]; then msg_error "No ${APP} Installation Found!" exit fi msg_info "Updating InfluxDB" $STD apt update $STD apt upgrade -y msg_ok "Updated InfluxDB" msg_ok "Updated successfully!" exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP} and Port 8888 for v1, Port 8086 for v2 or Port 8181 for v3${CL}" ================================================ FILE: ct/inspircd.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: kristocopani # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.inspircd.org/ APP="InspIRCd" var_tags="${var_tags:-IRC}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-512}" var_disk="${var_disk:-2}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -f /lib/systemd/system/inspircd.service ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "inspircd" "inspircd/inspircd"; then msg_info "Stopping Service" systemctl stop inspircd msg_ok "Stopped Service" fetch_and_deploy_gh_release "inspircd" "inspircd/inspircd" "binary" "latest" "/opt/inspircd" "inspircd_*.deb13u1_amd64.deb" msg_info "Starting Service" systemctl start inspircd msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Server-Acces it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}${IP}:6667${CL}" ================================================ FILE: ct/inventree.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Slaviša Arežina (tremor021) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/inventree/InvenTree APP="InvenTree" var_tags="${var_tags:-inventory}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-6}" var_os="${var_os:-ubuntu}" var_version="${var_version:-24.04}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d "/opt/inventree" ]]; then msg_error "No ${APP} Installation Found!" exit fi if ! grep -qE "^ID=(ubuntu)$" /etc/os-release; then msg_error "Unsupported OS. InvenTree requires Ubuntu (20.04/22.04/24.04)." exit fi msg_info "Updating InvenTree" $STD apt update $STD apt install --only-upgrade inventree -y msg_ok "Updated InvenTree" msg_ok "Updated successfully!" exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}${CL}" ================================================ FILE: ct/investbrain.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Benito Rodríguez (b3ni) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/investbrainapp/investbrain APP="Investbrain" var_tags="${var_tags:-finance;portfolio;investing}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/investbrain ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "Investbrain" "investbrainapp/investbrain"; then PHP_VERSION="8.4" msg_info "Stopping Services" systemctl stop nginx php${PHP_VERSION}-fpm $STD supervisorctl stop all msg_ok "Services Stopped" setup_composer NODE_VERSION="22" setup_nodejs PG_VERSION="17" setup_postgresql msg_info "Creating Backup" rm -f /opt/.env.backup rm -rf /opt/investbrain_backup cp /opt/investbrain/.env /opt/.env.backup cp -r /opt/investbrain/storage /opt/investbrain_backup msg_ok "Created Backup" fetch_and_deploy_gh_release "Investbrain" "investbrainapp/investbrain" "tarball" "latest" "/opt/investbrain" msg_info "Updating Investbrain" cd /opt/investbrain rm -rf /opt/investbrain/storage cp /opt/.env.backup /opt/investbrain/.env cp -r /opt/investbrain_backup/ /opt/investbrain/storage export COMPOSER_ALLOW_SUPERUSER=1 $STD /usr/local/bin/composer install --no-interaction --no-dev --optimize-autoloader $STD npm install $STD npm run build $STD php artisan storage:link $STD php artisan migrate --force $STD php artisan cache:clear $STD php artisan view:clear $STD php artisan route:clear $STD php artisan event:clear $STD php artisan route:cache $STD php artisan event:cache chown -R www-data:www-data /opt/investbrain chmod -R 775 /opt/investbrain/storage /opt/investbrain/bootstrap/cache rm -rf /opt/.env.backup /opt/investbrain_backup msg_ok "Updated Investbrain" msg_info "Starting Services" systemctl start php${PHP_VERSION}-fpm nginx $STD supervisorctl start all msg_ok "Services Started" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8000${CL}" ================================================ FILE: ct/invoiceninja.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://invoiceninja.com/ APP="InvoiceNinja" var_tags="${var_tags:-invoicing;business}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-8}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/invoiceninja ]]; then msg_error "No ${APP} Installation Found!" exit fi setup_mariadb if check_for_gh_release "invoiceninja" "invoiceninja/invoiceninja"; then msg_info "Stopping Services" systemctl stop supervisor nginx php8.4-fpm msg_ok "Stopped Services" msg_info "Creating Backup" mkdir -p /tmp/invoiceninja_backup cp /opt/invoiceninja/.env /tmp/invoiceninja_backup/ cp -r /opt/invoiceninja/storage /tmp/invoiceninja_backup/ 2>/dev/null || true cp -r /opt/invoiceninja/public/storage /tmp/invoiceninja_backup/public_storage 2>/dev/null || true msg_ok "Created Backup" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "invoiceninja" "invoiceninja/invoiceninja" "prebuild" "latest" "/opt/invoiceninja" "invoiceninja.tar.gz" msg_info "Restoring Data" cp /tmp/invoiceninja_backup/.env /opt/invoiceninja/ cp -r /tmp/invoiceninja_backup/storage/* /opt/invoiceninja/storage/ 2>/dev/null || true cp -r /tmp/invoiceninja_backup/public_storage/* /opt/invoiceninja/public/storage/ 2>/dev/null || true rm -rf /tmp/invoiceninja_backup msg_ok "Restored Data" msg_info "Running Migrations" cd /opt/invoiceninja $STD php artisan migrate --force $STD php artisan config:clear $STD php artisan cache:clear $STD php artisan optimize chown -R www-data:www-data /opt/invoiceninja chmod -R 755 /opt/invoiceninja/storage msg_ok "Ran Migrations" msg_info "Starting Services" systemctl start php8.4-fpm nginx supervisor msg_ok "Started Services" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8080/setup${CL}" ================================================ FILE: ct/iobroker.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.iobroker.net/#en/intro APP="ioBroker" var_tags="${var_tags:-automation}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-8}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/iobroker ]]; then msg_error "No ${APP} Installation Found!" exit fi NODE_VERSION="24" setup_nodejs msg_info "Updating ${APP} LXC" $STD apt update $STD apt -y upgrade msg_ok "Updated successfully!" exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8081${CL}" ================================================ FILE: ct/ironclaw.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/nearai/ironclaw APP="IronClaw" var_tags="${var_tags:-ai;agent;security}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-8}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -f /usr/local/bin/ironclaw ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "ironclaw-bin" "nearai/ironclaw"; then msg_info "Stopping Service" systemctl stop ironclaw msg_ok "Stopped Service" msg_info "Backing up Configuration" cp /root/.ironclaw/.env /root/ironclaw.env.bak msg_ok "Backed up Configuration" fetch_and_deploy_gh_release "ironclaw-bin" "nearai/ironclaw" "prebuild" "latest" "/usr/local/bin" \ "ironclaw-$(uname -m)-unknown-linux-$([[ -f /etc/alpine-release ]] && echo "musl" || echo "gnu").tar.gz" chmod +x /usr/local/bin/ironclaw msg_info "Restoring Configuration" cp /root/ironclaw.env.bak /root/.ironclaw/.env rm -f /root/ironclaw.env.bak msg_ok "Restored Configuration" msg_info "Starting Service" systemctl start ironclaw msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed Successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Complete setup by running:${CL}" echo -e "${TAB}${BGN}ironclaw onboard${CL}" echo -e "${INFO}${YW} Then start the service:${CL}" echo -e "${TAB}${BGN}systemctl start ironclaw${CL}" echo -e "${INFO}${YW} Access the Web UI at:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:3000${CL}" echo -e "${INFO}${YW} Auth token and database credentials:${CL}" echo -e "${TAB}${BGN}cat /root/.ironclaw/.env${CL}" ================================================ FILE: ct/isponsorblocktv.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Matthew Stern (sternma) | MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/dmunozv04/iSponsorBlockTV APP="iSponsorBlockTV" var_tags="${var_tags:-media;automation}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-1024}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/isponsorblocktv ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "isponsorblocktv" "dmunozv04/iSponsorBlockTV"; then msg_info "Stopping Service" systemctl stop isponsorblocktv msg_ok "Stopped Service" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "isponsorblocktv" "dmunozv04/iSponsorBlockTV" "singlefile" "latest" "/opt/isponsorblocktv" "iSponsorBlockTV-x86_64-linux" msg_info "Starting Service" systemctl start isponsorblocktv msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Run the setup wizard inside the container with:${CL}" echo -e "${TAB}${GATEWAY}${BGN}iSponsorBlockTV setup${CL}" ================================================ FILE: ct/itsm-ng.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Florianb63 # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://itsm-ng.com/ APP="ITSM-NG" var_tags="${var_tags:-asset-management;foss}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-10}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -f /etc/itsm-ng/config_db.php ]]; then msg_error "No ${APP} Installation Found!" exit 233 fi setup_mariadb msg_info "Updating ITSM-NG" $STD apt update $STD apt -y upgrade chown -R www-data:www-data /var/lib/itsm-ng mkdir -p /usr/share/itsm-ng/css/palettes chown -R www-data:www-data /usr/share/itsm-ng/css chown -R www-data:www-data /usr/share/itsm-ng/css_compiled chown www-data:www-data /etc/itsm-ng/config_db.php msg_ok "Updated successfully!" exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}${CL}" ================================================ FILE: ct/jackett.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/Jackett/Jackett APP="Jackett" var_tags="${var_tags:-torrent}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-512}" var_disk="${var_disk:-2}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -f /etc/systemd/system/jackett.service ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "Jackett" "Jackett/Jackett"; then if [ ! -f /opt/.env ]; then sed -i 's|^Environment="DisableRootWarning=true"$|EnvironmentFile="/opt/.env"|' /etc/systemd/system/jackett.service cat </opt/.env DisableRootWarning=true EOF fi msg_info "Stopping Service" systemctl stop jackett msg_ok "Stopped Service" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "jackett" "Jackett/Jackett" "prebuild" "latest" "/opt/Jackett" "Jackett.Binaries.LinuxARM64.tar.gz" msg_info "Starting Service" systemctl start jackett msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:9117${CL}" ================================================ FILE: ct/jeedom.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Mips2648 # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://jeedom.com/ APP="Jeedom" var_tags="${var_tags:-automation;smarthome}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-16}" var_os="${var_os:-debian}" var_version="${var_version:-12}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -f /var/www/html/core/config/version ]]; then msg_error "No ${APP} Installation Found!" exit fi msg_info "Updating OS" $STD apt update $STD apt -y upgrade msg_ok "OS updated, you can now update Jeedom from the Web UI." exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}${CL}" ================================================ FILE: ct/jellyfin.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://jellyfin.org/ APP="Jellyfin" var_tags="${var_tags:-media}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-16}" var_os="${var_os:-ubuntu}" var_version="${var_version:-24.04}" var_unprivileged="${var_unprivileged:-0}" var_gpu="${var_gpu:-yes}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /usr/lib/jellyfin ]]; then msg_error "No ${APP} Installation Found!" exit fi if ! grep -qEi 'ubuntu' /etc/os-release; then msg_info "Updating Intel Dependencies" rm -f ~/.intel-* || true fetch_and_deploy_gh_release "intel-libgdgmm12" "intel/compute-runtime" "binary" "latest" "" "libigdgmm12_*_amd64.deb" fetch_and_deploy_gh_release "intel-opencl-icd" "intel/compute-runtime" "binary" "latest" "" "intel-opencl-icd_*_amd64.deb" local igc_tag _resolve_igc_tag igc_tag fetch_and_deploy_gh_release "intel-igc-core-2" "intel/intel-graphics-compiler" "binary" "$igc_tag" "" "intel-igc-core-2_*_amd64.deb" fetch_and_deploy_gh_release "intel-igc-opencl-2" "intel/intel-graphics-compiler" "binary" "$igc_tag" "" "intel-igc-opencl-2_*_amd64.deb" msg_ok "Updated Intel Dependencies" fi msg_info "Setting up Jellyfin Repository" setup_deb822_repo \ "jellyfin" \ "https://repo.jellyfin.org/jellyfin_team.gpg.key" \ "https://repo.jellyfin.org/$(get_os_info id)" \ "$(get_os_info codename)" msg_ok "Set up Jellyfin Repository" msg_info "Updating Jellyfin" ensure_dependencies libjemalloc2 if [[ ! -f /usr/lib/libjemalloc.so ]]; then ln -sf /usr/lib/aarch64-linux-gnu/libjemalloc.so.2 /usr/lib/libjemalloc.so fi $STD apt -y upgrade $STD apt -y --with-new-pkgs upgrade jellyfin jellyfin-server jellyfin-ffmpeg7 ln -sf /usr/lib/jellyfin-ffmpeg/ffmpeg /usr/bin/ffmpeg ln -sf /usr/lib/jellyfin-ffmpeg/ffprobe /usr/bin/ffprobe msg_ok "Updated Jellyfin" msg_ok "Updated successfully!" exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8096${CL}" ================================================ FILE: ct/jellyseerr.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://docs.jellyseerr.dev/ APP="Jellyseerr" var_tags="${var_tags:-media}" var_cpu="${var_cpu:-4}" var_ram="${var_ram:-4096}" var_disk="${var_disk:-8}" var_os="${var_os:-debian}" var_version="${var_version:-12}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/jellyseerr ]]; then msg_error "No ${APP} Installation Found!" exit fi if [[ -f "/opt/jellyseerr/package.json" ]] && [[ "$(grep -m1 '"version"' /opt/jellyseerr/package.json | awk -F'"' '{print $4}')" == "2.7.3" ]]; then echo echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "Jellyseerr v2.7.3 detected." echo echo "Seerr is the new unified Jellyseerr and Overseerr." echo "More info: https://docs.seerr.dev/blog/seerr-release" echo read -rp "Do you want to migrate to Seerr now? (y/N): " MIGRATE echo if [[ ! "$MIGRATE" =~ ^[Yy]$ ]]; then msg_info "Migration cancelled. Exiting." exit 0 fi msg_info "Switching update script to Seerr" TMP_UPDATE=$(mktemp) cat <<'EOF' >"$TMP_UPDATE" bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/seerr.sh)" EOF mv "$TMP_UPDATE" /usr/bin/update chmod +x /usr/bin/update msg_ok "Switched update script to Seerr" msg_warn "Please type 'update' again to complete the migration" exit 0 fi msg_info "Updating Jellyseerr" cd /opt/jellyseerr systemctl stop jellyseerr output=$(git pull --no-rebase) pnpm_desired=$(grep -Po '"pnpm":\s*"\K[^"]+' /opt/jellyseerr/package.json) NODE_VERSION="22" NODE_MODULE="pnpm@$pnpm_desired" setup_nodejs if echo "$output" | grep -q "Already up to date."; then msg_ok "$APP is already up to date." exit fi rm -rf dist .next node_modules export CYPRESS_INSTALL_BINARY=0 cd /opt/jellyseerr $STD pnpm install --frozen-lockfile export NODE_OPTIONS="--max-old-space-size=3072" $STD pnpm build cat </etc/systemd/system/jellyseerr.service [Unit] Description=jellyseerr Service After=network.target [Service] EnvironmentFile=/etc/jellyseerr/jellyseerr.conf Environment=NODE_ENV=production Type=exec WorkingDirectory=/opt/jellyseerr ExecStart=/usr/bin/node dist/index.js [Install] WantedBy=multi-user.target EOF systemctl daemon-reload systemctl start jellyseerr msg_ok "Updated Jellyseerr" msg_ok "Updated successfully!" exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:5055${CL}" ================================================ FILE: ct/jenkins.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: kristocopani # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.jenkins.io/ APP="Jenkins" var_tags="${var_tags:-automation}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-1024}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /var/lib/jenkins ]]; then msg_error "No ${APP} Installation Found!" exit fi JAVA_VERSION="21" setup_java msg_info "Updating Jenkins" $STD apt update $STD apt upgrade -y msg_ok "Updated Jenkins" msg_ok "Updated successfully!" exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8080${CL}" ================================================ FILE: ct/jitsi-meet.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://jitsi.org/ APP="Jitsi-Meet" var_tags="${var_tags:-video;conference;communication}" var_cpu="${var_cpu:-4}" var_ram="${var_ram:-4096}" var_disk="${var_disk:-12}" var_os="${var_os:-debian}" var_version="${var_version:-12}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /etc/jitsi ]]; then msg_error "No ${APP} Installation Found!" exit fi msg_info "Updating Jitsi Meet" $STD apt update $STD apt install -y --only-upgrade \ jitsi-meet \ jicofo \ jitsi-videobridge2 \ prosody msg_ok "Updated Jitsi Meet" exit } start build_container description msg_ok "Completed Successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}https://${IP}${CL}" ================================================ FILE: ct/joplin-server.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Slaviša Arežina (tremor021) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://joplinapp.org/ APP="Joplin-Server" var_tags="${var_tags:-notes}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-6144}" var_disk="${var_disk:-20}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/joplin-server ]]; then msg_error "No ${APP} Installation Found!" exit fi NODE_VERSION="24" NODE_MODULE="yarn,npm,pm2" setup_nodejs if check_for_gh_release "joplin-server" "laurent22/joplin"; then msg_info "Stopping Services" systemctl stop joplin-server msg_ok "Stopped Services" cp /opt/joplin-server/.env /opt CLEAN_INSTALL=1 fetch_and_deploy_gh_release "joplin-server" "laurent22/joplin" "tarball" mv /opt/.env /opt/joplin-server msg_info "Updating Joplin-Server" cd /opt/joplin-server sed -i "/onenote-converter/d" packages/lib/package.json $STD yarn config set --home enableTelemetry 0 export BUILD_SEQUENCIAL=1 $STD yarn workspaces focus @joplin/server $STD yarn workspaces foreach -R --topological-dev --from @joplin/server run build $STD yarn workspaces foreach -R --topological-dev --from @joplin/server run tsc msg_ok "Updated Joplin-Server" msg_info "Starting Services" systemctl start joplin-server msg_ok "Started Services" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:22300${CL}" ================================================ FILE: ct/jotty.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: vhsdream # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/fccview/jotty APP="jotty" var_tags="${var_tags:-tasks;notes}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-1024}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/jotty ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "jotty" "fccview/jotty"; then msg_info "Stopping Service" systemctl stop jotty msg_ok "Stopped Service" msg_info "Backing up configuration & data" cp /opt/jotty/.env /opt/app.env [[ -d /opt/jotty/data ]] && mv /opt/jotty/data /opt/data [[ -d /opt/jotty/config ]] && mv /opt/jotty/config /opt/config msg_ok "Backed up configuration & data" NODE_VERSION="22" NODE_MODULE="yarn" setup_nodejs CLEAN_INSTALL=1 fetch_and_deploy_gh_release "jotty" "fccview/jotty" "prebuild" "latest" "/opt/jotty" "jotty_*_prebuild.tar.gz" msg_info "Restoring configuration & data" mv /opt/app.env /opt/jotty/.env [[ -d /opt/data ]] && mv /opt/data /opt/jotty/data [[ -d /opt/jotty/config ]] && cp -a /opt/config/* /opt/jotty/config && rm -rf /opt/config msg_ok "Restored configuration & data" msg_info "Starting Service" systemctl start jotty msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:3000${CL}" ================================================ FILE: ct/jupyternotebook.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Dave-code-creater (Tan Dat, Ta) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://jupyter.org/ APP="JupyterNotebook" var_tags="${var_tags:-ai;dev-tools}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-4}" var_os="${var_os:-ubuntu}" var_version="${var_version:-24.04}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources INSTALL_DIR="/opt/jupyter" VENV_PYTHON="${INSTALL_DIR}/.venv/bin/python" VENV_JUPYTER="${INSTALL_DIR}/.venv/bin/jupyter" SERVICE_FILE="/etc/systemd/system/jupyternotebook.service" if [[ ! -x "$VENV_JUPYTER" ]]; then msg_info "Migrating to uv venv" PYTHON_VERSION="3.12" setup_uv mkdir -p "$INSTALL_DIR" cd "$INSTALL_DIR" $STD uv venv --clear .venv $STD "$VENV_PYTHON" -m ensurepip --upgrade $STD "$VENV_PYTHON" -m pip install --upgrade pip $STD "$VENV_PYTHON" -m pip install jupyter msg_ok "Migrated to uv and installed Jupyter" else msg_info "Updating Jupyter" $STD "$VENV_PYTHON" -m pip install --upgrade pip $STD "$VENV_PYTHON" -m pip install --upgrade jupyter msg_ok "Jupyter updated" fi if [[ -f "$SERVICE_FILE" && "$(grep ExecStart "$SERVICE_FILE")" != *".venv/bin/jupyter"* ]]; then msg_info "Updating systemd service to use .venv" cat <"$SERVICE_FILE" [Unit] Description=Jupyter Notebook Server After=network.target [Service] Type=simple WorkingDirectory=${INSTALL_DIR} ExecStart=${VENV_JUPYTER} notebook --ip=0.0.0.0 --port=8888 --allow-root Restart=always RestartSec=10 [Install] WantedBy=multi-user.target EOF systemctl daemon-reexec systemctl restart jupyternotebook msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8888${CL}" ================================================ FILE: ct/kapowarr.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Slaviša Arežina (tremor021) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/Casvt/Kapowarr APP="Kapowarr" var_tags="${var_tags:-Arr}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-256}" var_disk="${var_disk:-2}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -f /etc/systemd/system/kapowarr.service ]]; then msg_error "No ${APP} Installation Found!" exit fi setup_uv if check_for_gh_release "kapowarr" "Casvt/Kapowarr"; then msg_info "Stopping Service" systemctl stop kapowarr msg_ok "Stopped Service" msg_info "Creating Backup" mv /opt/kapowarr/db /opt/ msg_ok "Backup Created" fetch_and_deploy_gh_release "kapowarr" "Casvt/Kapowarr" "tarball" msg_info "Updating Kapowarr" mv /opt/db /opt/kapowarr msg_ok "Updated Kapowarr" msg_info "Starting Service" systemctl start kapowarr msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:5656${CL}" ================================================ FILE: ct/karakeep.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: MickLesk (Canbiz) & vhsdream # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://karakeep.app/ APP="karakeep" var_tags="${var_tags:-bookmark}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-4096}" var_disk="${var_disk:-10}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/karakeep ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "karakeep" "karakeep-app/karakeep"; then msg_info "Stopping Services" systemctl stop karakeep-web karakeep-workers karakeep-browser msg_ok "Stopped Services" msg_info "Updating yt-dlp" $STD yt-dlp --update-to nightly msg_ok "Updated yt-dlp" msg_info "Prepare update" ensure_dependencies graphicsmagick ghostscript if [[ -f /opt/karakeep/.env ]] && [[ ! -f /etc/karakeep/karakeep.env ]]; then mkdir -p /etc/karakeep mv /opt/karakeep/.env /etc/karakeep/karakeep.env fi msg_ok "Update prepared" if grep -q "start:prod" /etc/systemd/system/karakeep-workers.service; then sed -i 's|^ExecStart=.*$|ExecStart=/usr/bin/node dist/index.mjs|' /etc/systemd/system/karakeep-workers.service systemctl daemon-reload fi if grep -q '^ExecStart=/usr/bin/node\s\+dist/index\.mjs$' /etc/systemd/system/karakeep-workers.service; then sed -i -E 's#^(ExecStart=/usr/bin/node\s+dist/)index\.mjs$#\1index.js#' /etc/systemd/system/karakeep-workers.service systemctl daemon-reload fi CLEAN_INSTALL=1 fetch_and_deploy_gh_release "karakeep" "karakeep-app/karakeep" "tarball" if command -v corepack >/dev/null; then $STD corepack disable fi sed -i "s/^SERVER_VERSION=.*$/SERVER_VERSION=${CHECK_UPDATE_RELEASE#v}/" /etc/karakeep/karakeep.env MODULE_VERSION="$(jq -r '.packageManager | split("@")[1]' /opt/karakeep/package.json)" NODE_VERSION="24" NODE_MODULE="pnpm@${MODULE_VERSION}" setup_nodejs setup_meilisearch msg_info "Updating Karakeep" corepack enable export PUPPETEER_SKIP_DOWNLOAD="true" export PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD="true" export NEXT_TELEMETRY_DISABLED=1 export CI="true" cd /opt/karakeep/apps/web $STD pnpm install --frozen-lockfile $STD pnpm build cd /opt/karakeep/apps/workers $STD pnpm install --frozen-lockfile $STD pnpm build cd /opt/karakeep/apps/cli $STD pnpm install --frozen-lockfile $STD pnpm build DATA_DIR="$(sed -n '/^DATA_DIR/p' /etc/karakeep/karakeep.env | awk -F= '{print $2}' | tr -d '="=')" export DATA_DIR="${DATA_DIR:-/opt/karakeep_data}" cd /opt/karakeep/packages/db $STD pnpm migrate $STD pnpm store prune msg_ok "Updated Karakeep" msg_info "Starting Services" systemctl start karakeep-browser karakeep-workers karakeep-web msg_ok "Started Services" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:3000${CL}" ================================================ FILE: ct/kasm.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Omar Minaya # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.kasmweb.com/docs/latest/index.html APP="Kasm" var_tags="${var_tags:-os}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-8192}" var_disk="${var_disk:-30}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-0}" var_fuse="${var_fuse:-yes}" var_tun="${var_tun:-yes}" var_kasm_version="${var_kasm_version:-}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/kasm/current ]]; then msg_error "No ${APP} Installation Found!" exit fi msg_info "Checking for new version" CURRENT_VERSION=$(readlink -f /opt/kasm/current | awk -F'/' '{print $4}') KASM_VERSION=$(curl -s https://kasm.com/downloads | grep -oP ']*>.*?' | sed -E 's/<\/?h1[^>]*>//g' | grep -oP '\d+\.\d+\.\d+') KASM_URL="https://kasm-static-content.s3.amazonaws.com/kasm_release_${KASM_VERSION:-var_kasm_version}.tar.gz" # KASM_URL=$(curl -fsSL "https://www.kasm.com/downloads" | tr '\n' ' ' | grep -oE 'https://kasm-static-content[^"]*kasm_release_[0-9]+\.[0-9]+\.[0-9]+\.[a-z0-9]+\.tar\.gz' | head -n 1) # if [[ -z "$KASM_URL" ]]; then # SERVICE_IMAGE_URL=$(curl -fsSL "https://www.kasm.com/downloads" | tr '\n' ' ' | grep -oE 'https://kasm-static-content[^"]*kasm_release_service_images_amd64_[0-9]+\.[0-9]+\.[0-9]+\.tar\.gz' | head -n 1) # if [[ -n "$SERVICE_IMAGE_URL" ]]; then # KASM_VERSION=$(echo "$SERVICE_IMAGE_URL" | sed -E 's/.*kasm_release_service_images_amd64_([0-9]+\.[0-9]+\.[0-9]+).*/\1/') # KASM_URL="https://kasm-static-content.s3.amazonaws.com/kasm_release_${KASM_VERSION}.tar.gz" # fi # else # KASM_VERSION=$(echo "$KASM_URL" | sed -E 's/.*kasm_release_([0-9]+\.[0-9]+\.[0-9]+).*/\1/') # fi if [[ -z "$KASM_VERSION" ]] || [[ -z "$KASM_URL" ]]; then msg_error "Unable to detect latest Kasm release URL." exit 250 fi msg_info "Checked for new version" msg_info "Removing outdated docker-compose plugin" [ -f ~/.docker/cli-plugins/docker-compose ] && rm -rf ~/.docker/cli-plugins/docker-compose msg_ok "Removed outdated docker-compose plugin" if [[ -z "$CURRENT_VERSION" ]] || [[ "$KASM_VERSION" != "$CURRENT_VERSION" ]]; then msg_info "Updating Kasm" cd /tmp msg_warn "WARNING: This script will run an external installer from a third-party source (https://www.kasmweb.com/)." msg_warn "The following code is NOT maintained or audited by our repository." msg_warn "If you have any doubts or concerns, please review the installer code before proceeding:" msg_custom "${TAB3}${GATEWAY}${BGN}${CL}" "\e[1;34m" "→ upgrade.sh inside tar.gz $KASM_URL" echo read -r -p "${TAB3}Do you want to continue? [y/N]: " CONFIRM if [[ ! "$CONFIRM" =~ ^([yY][eE][sS]|[yY])$ ]]; then msg_error "Aborted by user. No changes have been made." exit 10 fi curl -fsSL -o "/tmp/kasm_release_${KASM_VERSION}.tar.gz" "$KASM_URL" tar -xf "kasm_release_${KASM_VERSION}.tar.gz" chmod +x /tmp/kasm_release/install.sh rm -f /tmp/kasm_release_"${KASM_VERSION}".tar.gz bash /tmp/kasm_release/upgrade.sh --proxy-port 443 rm -rf /tmp/kasm_release msg_ok "Updated successfully!" else msg_ok "No update required. Kasm is already at v${KASM_VERSION}" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}https://${IP}${CL}" ================================================ FILE: ct/kavita.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.kavitareader.com/ APP="Kavita" var_tags="${var_tags:-reader}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-8}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/Kavita ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "kavita" "Kareadita/Kavita"; then msg_info "Stopping Service" systemctl stop kavita msg_ok "Service Stopped" fetch_and_deploy_gh_release "kavita" "Kareadita/Kavita" "prebuild" "latest" "/opt/Kavita" "kavita-linux-x64.tar.gz" chmod +x /opt/Kavita/Kavita && chown root:root /opt/Kavita/Kavita msg_info "Starting Service" systemctl start kavita msg_ok "Service Started" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:5000${CL}" ================================================ FILE: ct/keycloak.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) | Co-Author: remz1337 # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.keycloak.org/ APP="Keycloak" var_tags="${var_tags:-access-management}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/keycloak ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "keycloak_app" "keycloak/keycloak"; then msg_info "Stopping Service" systemctl stop keycloak msg_ok "Stopped Service" msg_info "Updating packages" $STD apt-get update $STD apt-get -y upgrade msg_ok "Updated packages" msg_info "Backup old Keycloak" cd /opt mv keycloak keycloak.old msg_ok "Backup done" fetch_and_deploy_gh_release "keycloak_app" "keycloak/keycloak" "prebuild" "latest" "/opt/keycloak" "keycloak-*.tar.gz" msg_info "Updating Keycloak" cd /opt cp -a keycloak.old/conf/. keycloak/conf/ cp -a keycloak.old/providers/. keycloak/providers/ 2>/dev/null || true cp -a keycloak.old/themes/. keycloak/themes/ 2>/dev/null || true rm -rf keycloak.old msg_ok "Updated Keycloak" msg_info "Restarting Service" systemctl restart keycloak msg_ok "Restarted Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8080/admin${CL}" ================================================ FILE: ct/kima-hub.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/Chevron7Locked/kima-hub APP="Kima-Hub" var_tags="${var_tags:-music;streaming;media}" var_cpu="${var_cpu:-4}" var_ram="${var_ram:-8192}" var_disk="${var_disk:-20}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/kima-hub ]]; then msg_error "No ${APP} Installation Found!" exit fi NODE_VERSION="22" setup_nodejs if check_for_gh_release "kima-hub" "Chevron7Locked/kima-hub"; then msg_info "Stopping Services" systemctl stop kima-frontend kima-backend kima-analyzer kima-analyzer-clap msg_ok "Stopped Services" msg_info "Backing up Data" cp /opt/kima-hub/backend/.env /opt/kima-hub-backend-env.bak cp /opt/kima-hub/frontend/.env /opt/kima-hub-frontend-env.bak msg_ok "Backed up Data" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "kima-hub" "Chevron7Locked/kima-hub" "tarball" msg_info "Restoring Data" cp /opt/kima-hub-backend-env.bak /opt/kima-hub/backend/.env cp /opt/kima-hub-frontend-env.bak /opt/kima-hub/frontend/.env rm -f /opt/kima-hub-backend-env.bak /opt/kima-hub-frontend-env.bak msg_ok "Restored Data" msg_info "Rebuilding Backend" cd /opt/kima-hub/backend $STD npm install $STD npm run build $STD npx prisma generate $STD npx prisma migrate deploy msg_ok "Rebuilt Backend" msg_info "Rebuilding Frontend" cd /opt/kima-hub/frontend $STD npm install $STD npm run build msg_ok "Rebuilt Frontend" msg_info "Starting Services" systemctl start kima-backend kima-frontend kima-analyzer kima-analyzer-clap msg_ok "Started Services" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:3030${CL}" ================================================ FILE: ct/kimai.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (Canbiz) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.kimai.org/ APP="Kimai" var_tags="${var_tags:-time-tracking}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-7}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources ensure_dependencies lsb-release if [[ ! -d /opt/kimai ]]; then msg_error "No ${APP} Installation Found!" exit fi setup_mariadb PHP_VERSION="8.4" PHP_APACHE="YES" setup_php setup_composer if check_for_gh_release "kimai" "kimai/kimai"; then BACKUP_DIR="/opt/kimai_backup" msg_info "Stopping Apache2" systemctl stop apache2 msg_ok "Stopped Apache2" msg_info "Backing up Kimai configuration and var directory" mkdir -p "$BACKUP_DIR" [ -d /opt/kimai/var ] && cp -r /opt/kimai/var "$BACKUP_DIR/" [ -f /opt/kimai/.env ] && cp /opt/kimai/.env "$BACKUP_DIR/" [ -f /opt/kimai/config/packages/local.yaml ] && cp /opt/kimai/config/packages/local.yaml "$BACKUP_DIR/" msg_ok "Backup completed" fetch_and_deploy_gh_release "kimai" "kimai/kimai" "tarball" msg_info "Updating Kimai" [ -d "$BACKUP_DIR/var" ] && cp -r "$BACKUP_DIR/var" /opt/kimai/ [ -f "$BACKUP_DIR/.env" ] && cp "$BACKUP_DIR/.env" /opt/kimai/ [ -f "$BACKUP_DIR/local.yaml" ] && cp "$BACKUP_DIR/local.yaml" /opt/kimai/config/packages/ rm -rf "$BACKUP_DIR" cd /opt/kimai sed -i '/^admin_lte:/,/^[^[:space:]]/d' config/packages/local.yaml $STD composer install --no-dev --optimize-autoloader $STD bin/console kimai:update msg_ok "Updated Kimai" msg_info "Starting Apache2" systemctl start apache2 msg_ok "Started Apache2" msg_info "Setup Permissions" chown -R :www-data /opt/* chmod -R g+r /opt/* chmod -R g+rw /opt/* chown -R www-data:www-data /opt/* chmod -R 777 /opt/* rm -rf "$BACKUP_DIR" msg_ok "Setup Permissions" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}${CL}" ================================================ FILE: ct/kitchenowl.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: snazzybean # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/TomBursch/kitchenowl APP="KitchenOwl" var_tags="${var_tags:-food;recipes}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-6}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/kitchenowl ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "kitchenowl" "TomBursch/kitchenowl"; then msg_info "Stopping Service" systemctl stop kitchenowl msg_ok "Stopped Service" msg_info "Creating Backup" mkdir -p /opt/kitchenowl_backup cp -r /opt/kitchenowl/data /opt/kitchenowl_backup/ cp -f /opt/kitchenowl/kitchenowl.env /opt/kitchenowl_backup/ msg_ok "Created Backup" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "kitchenowl" "TomBursch/kitchenowl" "tarball" "latest" "/opt/kitchenowl" rm -rf /opt/kitchenowl/web CLEAN_INSTALL=1 fetch_and_deploy_gh_release "kitchenowl-web" "TomBursch/kitchenowl" "prebuild" "latest" "/opt/kitchenowl/web" "kitchenowl_Web.tar.gz" msg_info "Restoring data" sed -i 's/default=True/default=False/' /opt/kitchenowl/backend/wsgi.py cp -r /opt/kitchenowl_backup/data /opt/kitchenowl/ cp -f /opt/kitchenowl_backup/kitchenowl.env /opt/kitchenowl/ rm -rf /opt/kitchenowl_backup msg_ok "Restored data" msg_info "Updating KitchenOwl" cd /opt/kitchenowl/backend $STD uv sync --frozen cd /opt/kitchenowl/backend set -a source /opt/kitchenowl/kitchenowl.env set +a $STD uv run flask db upgrade msg_ok "Updated KitchenOwl" msg_info "Starting Service" systemctl start kitchenowl msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:80${CL}" ================================================ FILE: ct/koel.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://koel.dev/ APP="Koel" var_tags="${var_tags:-music;streaming}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-8}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/koel ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "koel" "koel/koel"; then msg_info "Stopping Services" systemctl stop nginx php8.4-fpm msg_ok "Stopped Services" msg_info "Creating Backup" mkdir -p /tmp/koel_backup cp /opt/koel/.env /tmp/koel_backup/ cp -r /opt/koel/storage /tmp/koel_backup/ 2>/dev/null || true cp -r /opt/koel/public/img /tmp/koel_backup/ 2>/dev/null || true msg_ok "Created Backup" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "koel" "koel/koel" "prebuild" "latest" "/opt/koel" "koel-*.tar.gz" msg_info "Restoring Data" cp /tmp/koel_backup/.env /opt/koel/ cp -r /tmp/koel_backup/storage/* /opt/koel/storage/ 2>/dev/null || true cp -r /tmp/koel_backup/img/* /opt/koel/public/img/ 2>/dev/null || true rm -rf /tmp/koel_backup msg_ok "Restored Data" msg_info "Running Migrations" cd /opt/koel export COMPOSER_ALLOW_SUPERUSER=1 $STD composer install --no-interaction --no-dev --optimize-autoloader $STD php artisan migrate --force $STD php artisan config:clear $STD php artisan cache:clear $STD php artisan view:clear $STD php artisan koel:init --no-assets --no-interaction chown -R www-data:www-data /opt/koel chmod -R 775 /opt/koel/storage msg_ok "Ran Migrations" msg_info "Starting Services" systemctl start php8.4-fpm nginx msg_ok "Started Services" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}${CL}" ================================================ FILE: ct/koillection.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: bvdberg01 # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://koillection.github.io/ APP="Koillection" var_tags="${var_tags:-network}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-1024}" var_disk="${var_disk:-8}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/koillection ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "koillection" "benjaminjonard/koillection"; then msg_info "Stopping Service" systemctl stop apache2 msg_ok "Stopped Service" PHP_VERSION="8.5" PHP_APACHE="YES" setup_php setup_composer msg_info "Creating a backup" mv /opt/koillection/ /opt/koillection-backup msg_ok "Backup created" fetch_and_deploy_gh_release "koillection" "benjaminjonard/koillection" "tarball" msg_info "Updating Koillection" cd /opt/koillection cp -r /opt/koillection-backup/.env.local /opt/koillection cp -r /opt/koillection-backup/public/uploads/. /opt/koillection/public/uploads/ # Ensure APP_RUNTIME is in .env.local for CLI commands (upgrades from older versions) if ! grep -q "APP_RUNTIME" /opt/koillection/.env.local 2>/dev/null; then # Ensure file ends with newline before appending to avoid concatenation [[ -s /opt/koillection/.env.local && -n "$(tail -c 1 /opt/koillection/.env.local)" ]] && echo "" >>/opt/koillection/.env.local echo 'APP_RUNTIME="Symfony\Component\Runtime\SymfonyRuntime"' >>/opt/koillection/.env.local fi export COMPOSER_ALLOW_SUPERUSER=1 export APP_RUNTIME='Symfony\Component\Runtime\SymfonyRuntime' $STD composer install --no-dev -o --no-interaction --classmap-authoritative $STD php bin/console doctrine:migrations:migrate --no-interaction $STD php bin/console app:translations:dump cd assets/ $STD yarn install $STD yarn build mkdir -p /opt/koillection/public/uploads mkdir -p /opt/koillection/var/log chown -R www-data:www-data /opt/koillection/var/log chown -R www-data:www-data /opt/koillection/public/uploads rm -r /opt/koillection-backup # Ensure APP_RUNTIME is set in Apache config (for upgrades from older versions) if ! grep -q "APP_RUNTIME" /etc/apache2/sites-available/koillection.conf 2>/dev/null; then sed -i '/"$TMP_UPDATE" bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/tools/addon/komodo.sh)" MIGRATION_EOF mv "$TMP_UPDATE" /usr/bin/update chmod +x /usr/bin/update ln -sf /usr/bin/update /usr/bin/update_komodo 2>/dev/null || true msg_ok "Migration complete" msg_info "Running addon update" type=update bash <(curl -fsSL "${ADDON_SCRIPT}") exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:9120${CL}" ================================================ FILE: ct/kubo.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) | Co-Author: ulmentflam # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/ipfs/kubo APP="Kubo" var_tags="${var_tags:-sharing}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-4096}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -f /usr/local/kubo/ipfs ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "kubo" "ipfs/kubo"; then msg_info "Stopping service" systemctl stop ipfs msg_ok "Stopped service" fetch_and_deploy_gh_release "kubo" "ipfs/kubo" "prebuild" "latest" "/usr/local/kubo" "kubo*linux-arm64.tar.gz" msg_info "Starting service" systemctl start ipfs msg_ok "Service started" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:5001/webui${CL}" ================================================ FILE: ct/kutt.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: tomfrenzel # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/thedevs-network/kutt APP="Kutt" var_tags="${var_tags:-sharing}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-1024}" var_disk="${var_disk:-2}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/kutt ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "kutt" "thedevs-network/kutt"; then msg_info "Stopping services" systemctl stop kutt msg_ok "Stopped services" msg_info "Backing up data" mkdir -p /opt/kutt-backup [ -d /opt/kutt/custom ] && cp -r /opt/kutt/custom /opt/kutt-backup/ [ -d /opt/kutt/db ] && cp -r /opt/kutt/db /opt/kutt-backup/ cp /opt/kutt/.env /opt/kutt-backup/ msg_ok "Backed up data" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "kutt" "thedevs-network/kutt" "tarball" "latest" msg_info "Restoring data" [ -d /opt/kutt-backup/custom ] && cp -r /opt/kutt-backup/custom /opt/kutt/ [ -d /opt/kutt-backup/db ] && cp -r /opt/kutt-backup/db /opt/kutt/ [ -f /opt/kutt-backup/.env ] && cp /opt/kutt-backup/.env /opt/kutt/ rm -rf /opt/kutt-backup msg_ok "Restored data" msg_info "Configuring Kutt" cd /opt/kutt $STD npm install $STD npm run migrate msg_ok "Configured Kutt" msg_info "Starting services" systemctl start kutt msg_ok "Started services" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}https://${IP} or https://${CL}" ================================================ FILE: ct/languagetool.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Slaviša Arežina (tremor021) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://languagetool.org/ APP="LanguageTool" var_tags="${var_tags:-spellcheck}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-4096}" var_disk="${var_disk:-16}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/LanguageTool ]]; then msg_error "No ${APP} Installation Found!" exit fi RELEASE=$(curl -fsSL https://languagetool.org/download/ | grep -oP 'LanguageTool-\K[0-9]+\.[0-9]+(\.[0-9]+)?(?=\.zip)' | sort -V | tail -n1) if [[ "${RELEASE}" != "$(cat ~/.languagetool 2>/dev/null)" ]] || [[ ! -f ~/.languagetool ]]; then msg_info "Stopping Service" systemctl stop language-tool msg_ok "Stopped Service" msg_info "Creating Backup" cp /opt/LanguageTool/server.properties /opt/server.properties msg_ok "Created Backup" msg_info "Updating LanguageTool" rm -rf /opt/LanguageTool download_file "https://languagetool.org/download/LanguageTool-stable.zip" /tmp/LanguageTool-stable.zip unzip -q /tmp/LanguageTool-stable.zip -d /opt mv /opt/LanguageTool-*/ /opt/LanguageTool/ mv /opt/server.properties /opt/LanguageTool/server.properties rm -f /tmp/LanguageTool-stable.zip echo "${RELEASE}" >~/.languagetool msg_ok "Updated LanguageTool" msg_info "Starting Service" systemctl start language-tool msg_ok "Started Service" msg_ok "Updated successfuly!" else msg_ok "No update required. ${APP} is already at v${RELEASE}" fi exit } start build_container description msg_ok "Completed successfully!" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8081/v2${CL}" ================================================ FILE: ct/lazylibrarian.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck | Co-Author: MountyMapleSyrup (MountyMapleSyrup) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://gitlab.com/LazyLibrarian/LazyLibrarian APP="LazyLibrarian" var_tags="${var_tags:-eBook}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-1024}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/LazyLibrarian/ ]]; then msg_error "No ${APP} Installation Found!" exit fi msg_info "Stopping LazyLibrarian" systemctl stop lazylibrarian msg_ok "LazyLibrarian Stopped" msg_info "Updating $APP LXC" $STD git -C /opt/LazyLibrarian pull origin master msg_ok "Updated $APP LXC" msg_info "Starting LazyLibrarian" systemctl start lazylibrarian msg_ok "Started LazyLibrarian" msg_ok "Updated successfully!" exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:5299${CL}" ================================================ FILE: ct/leantime.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Stroopwafe1 # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://leantime.io APP="Leantime" var_tags="${var_tags:-productivity}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-20}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/leantime ]]; then msg_error "No ${APP} Installation Found!" exit fi setup_mariadb if check_for_gh_release "leantime" "Leantime/leantime"; then msg_info "Creating Backup" mariadb-dump leantime >"/opt/leantime_db_backup_$(date +%F).sql" tar -czf "/opt/leantime_backup_$(date +%F).tar.gz" "/opt/leantime" mv /opt/leantime /opt/leantime_bak msg_ok "Backup Created" fetch_and_deploy_gh_release "leantime" "Leantime/leantime" "prebuild" "latest" "/opt/leantime" Leantime*.tar.gz msg_info "Restoring Config & Permissions" mv /opt/leantime_bak/config/.env /opt/leantime/config/.env chown -R www-data:www-data "/opt/leantime" chmod -R 750 "/opt/leantime" msg_ok "Restored Config & Permissions" msg_info "Removing Backup" rm -rf /opt/leantime_bak msg_ok "Removed Backup" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}/install${CL}" ================================================ FILE: ct/librechat.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/danny-avila/LibreChat APP="LibreChat" var_tags="${var_tags:-ai;chat}" var_cpu="${var_cpu:-4}" var_ram="${var_ram:-6144}" var_disk="${var_disk:-20}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/librechat ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_tag "librechat" "danny-avila/LibreChat" "v"; then msg_info "Stopping Services" systemctl stop librechat rag-api msg_ok "Stopped Services" msg_info "Backing up Configuration" cp /opt/librechat/.env /opt/librechat.env.bak msg_ok "Backed up Configuration" CLEAN_INSTALL=1 fetch_and_deploy_gh_tag "librechat" "danny-avila/LibreChat" msg_info "Installing Dependencies" cd /opt/librechat $STD npm ci msg_ok "Installed Dependencies" msg_info "Building Frontend" $STD npm run frontend $STD npm prune --production $STD npm cache clean --force msg_ok "Built Frontend" msg_info "Restoring Configuration" cp /opt/librechat.env.bak /opt/librechat/.env rm -f /opt/librechat.env.bak msg_ok "Restored Configuration" msg_info "Starting Services" systemctl start rag-api librechat msg_ok "Started Services" msg_ok "Updated LibreChat Successfully!" fi if check_for_gh_release "rag-api" "danny-avila/rag_api"; then msg_info "Stopping RAG API" systemctl stop rag-api msg_ok "Stopped RAG API" msg_info "Backing up RAG API Configuration" cp /opt/rag-api/.env /opt/rag-api.env.bak msg_ok "Backed up RAG API Configuration" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "rag-api" "danny-avila/rag_api" "tarball" msg_info "Updating RAG API Dependencies" cd /opt/rag-api $STD .venv/bin/pip install -r requirements.lite.txt msg_ok "Updated RAG API Dependencies" msg_info "Restoring RAG API Configuration" cp /opt/rag-api.env.bak /opt/rag-api/.env rm -f /opt/rag-api.env.bak msg_ok "Restored RAG API Configuration" msg_info "Starting RAG API" systemctl start rag-api msg_ok "Started RAG API" msg_ok "Updated RAG API Successfully!" fi exit } start build_container description msg_ok "Completed Successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:3080${CL}" ================================================ FILE: ct/librenms.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: michelroegl-brunner # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://librenms.org APP="LibreNMS" var_tags="${var_tags:-monitoring}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [ ! -d /opt/librenms ]; then msg_error "No ${APP} Installation Found!" exit fi setup_mariadb msg_info "Updating LibreNMS" su librenms cd /opt/librenms ./daily.sh msg_ok "Updated LibreNMS" exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}${CL}" ================================================ FILE: ct/librespeed-rust.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Joseph Stubberfield (stubbers) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/librespeed/speedtest-rust APP="Librespeed-Rust" var_tags="${var_tags:-network}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-512}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /var/lib/librespeed-rs ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "librespeed-rust" "librespeed/speedtest-rust"; then msg_info "Stopping Services" systemctl stop speedtest_rs msg_ok "Services Stopped" fetch_and_deploy_gh_release "librespeed-rust" "librespeed/speedtest-rust" "binary" "latest" "/opt/librespeed-rust" "librespeed-rs-aarch64-unknown-linux-gnu.deb" msg_info "Starting Service" systemctl start speedtest_rs msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8080${CL}" ================================================ FILE: ct/libretranslate.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Slaviša Arežina (tremor021) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/LibreTranslate/LibreTranslate APP="LibreTranslate" var_tags="${var_tags:-Arr}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-20}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" var_gpu="${var_gpu:-yes}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/libretranslate ]]; then msg_error "No ${APP} Installation Found!" exit fi PYTHON_VERSION="3.12" setup_uv if check_for_gh_release "libretranslate" "LibreTranslate/LibreTranslate"; then msg_info "Stopping Service" systemctl stop libretranslate msg_ok "Stopped Service" msg_info "Updating LibreTranslate" cd /opt/libretranslate source .venv/bin/activate $STD uv pip install -U libretranslate msg_ok "Updated LibreTranslate" msg_info "Starting Service" systemctl start libretranslate msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:5000${CL}" ================================================ FILE: ct/lidarr.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://lidarr.audio/ APP="Lidarr" var_tags="${var_tags:-arr;torrent;usenet}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-1024}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /var/lib/lidarr/ ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "lidarr" "Lidarr/Lidarr"; then apt-get install -y libicu76 &>/dev/null msg_info "Stopping service" systemctl stop lidarr msg_ok "Service stopped" fetch_and_deploy_gh_release "lidarr" "Lidarr/Lidarr" "prebuild" "latest" "/opt/Lidarr" "Lidarr.master*linux-core-arm64.tar.gz" chmod 775 /opt/Lidarr msg_info "Starting service" systemctl start lidarr msg_ok "Service started" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8686${CL}" ================================================ FILE: ct/limesurvey.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Slaviša Arežina (tremor021) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://community.limesurvey.org/ APP="LimeSurvey" var_tags="${var_tags:-os}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-512}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/limesurvey ]]; then msg_error "No ${APP} Installation Found!" exit fi setup_mariadb msg_warn "Application is updated via Web Interface" exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}${CL}" ================================================ FILE: ct/linkding.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (MickLesk) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://linkding.link/ APP="linkding" var_tags="${var_tags:-bookmarks;management}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-1024}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/linkding ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "linkding" "sissbruecker/linkding"; then msg_info "Stopping Services" systemctl stop nginx linkding linkding-tasks msg_ok "Stopped Services" msg_info "Backing up Data" cp -r /opt/linkding/data /opt/linkding_data_backup cp /opt/linkding/.env /opt/linkding_env_backup msg_ok "Backed up Data" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "linkding" "sissbruecker/linkding" "tarball" msg_info "Restoring Data" cp -r /opt/linkding_data_backup/. /opt/linkding/data cp /opt/linkding_env_backup /opt/linkding/.env rm -rf /opt/linkding_data_backup /opt/linkding_env_backup ln -sf /usr/lib/aarch64-linux-gnu/mod_icu.so /opt/linkding/libicu.so msg_ok "Restored Data" msg_info "Updating LinkDing" cd /opt/linkding rm -f bookmarks/settings/dev.py touch bookmarks/settings/custom.py $STD npm ci $STD npm run build $STD uv sync --no-dev --frozen $STD uv pip install gunicorn set -a && source /opt/linkding/.env && set +a $STD /opt/linkding/.venv/bin/python manage.py migrate $STD /opt/linkding/.venv/bin/python manage.py collectstatic --no-input msg_ok "Updated LinkDing" msg_info "Starting Services" systemctl start nginx linkding linkding-tasks msg_ok "Started Services" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed Successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:9090${CL}" ================================================ FILE: ct/linkstack.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Omar Minaya | MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://linkstack.org/ APP="LinkStack" var_tags="${var_tags:-os}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-1024}" var_disk="${var_disk:-5}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -f ~/.linkstack ]]; then msg_error "No ${APP} Installation Found!" exit fi PHP_VERSION="8.3" PHP_APACHE="YES" setup_php msg_warn "LinkStack should be updated via the user interface." exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}${CL}" ================================================ FILE: ct/linkwarden.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://linkwarden.app/ APP="Linkwarden" var_tags="${var_tags:-bookmark}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-4096}" var_disk="${var_disk:-12}" var_os="${var_os:-ubuntu}" var_version="${var_version:-24.04}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/linkwarden ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "linkwarden" "linkwarden/linkwarden"; then NODE_VERSION="22" NODE_MODULE="yarn@latest" setup_nodejs msg_info "Stopping Service" systemctl stop linkwarden msg_ok "Stopped Service" RUST_CRATES="monolith" setup_rust msg_info "Backing up data" mv /opt/linkwarden/.env /opt/.env [ -d /opt/linkwarden/data ] && mv /opt/linkwarden/data /opt/data.bak rm -rf /opt/linkwarden msg_ok "Backed up data" fetch_and_deploy_gh_release "linkwarden" "linkwarden/linkwarden" "tarball" msg_info "Updating Linkwarden" cd /opt/linkwarden yarn_ver="4.12.0" if [[ -f package.json ]]; then pkg_manager=$(jq -r '.packageManager // empty' package.json 2>/dev/null || true) if [[ -n "$pkg_manager" && "$pkg_manager" == yarn@* ]]; then yarn_spec="${pkg_manager#yarn@}" yarn_ver="${yarn_spec%%+*}" fi fi if command -v corepack >/dev/null 2>&1; then $STD corepack enable $STD corepack prepare "yarn@${yarn_ver}" --activate || true fi $STD yarn $STD npx playwright install-deps $STD npx playwright install mv /opt/.env /opt/linkwarden/.env $STD yarn prisma:generate $STD yarn web:build $STD yarn prisma:deploy [ -d /opt/data.bak ] && mv /opt/data.bak /opt/linkwarden/data rm -rf ~/.cargo/registry ~/.cargo/git ~/.cargo/.package-cache rm -rf /root/.cache/yarn rm -rf /opt/linkwarden/.next/cache msg_ok "Updated Linkwarden" msg_info "Starting Service" systemctl start linkwarden msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:3000${CL}" ================================================ FILE: ct/listmonk.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: bvdberg01 # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://listmonk.app/ APP="listmonk" var_tags="${var_tags:-newsletter}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-512}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -f /etc/systemd/system/listmonk.service ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "listmonk" "knadh/listmonk"; then msg_info "Stopping Service" systemctl stop listmonk msg_ok "Stopped Service" msg_info "Backing up data" mv /opt/listmonk/ /opt/listmonk-backup msg_ok "Backed up data" fetch_and_deploy_gh_release "listmonk" "knadh/listmonk" "prebuild" "latest" "/opt/listmonk" "listmonk*linux_arm64.tar.gz" msg_info "Configuring listmonk" mv /opt/listmonk-backup/config.toml /opt/listmonk/config.toml mv /opt/listmonk-backup/uploads /opt/listmonk/uploads $STD /opt/listmonk/listmonk --upgrade --yes --config /opt/listmonk/config.toml rm -rf /opt/listmonk-backup/ msg_ok "Configured listmonk" msg_info "Starting Service" systemctl start listmonk msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:9000${CL}" ================================================ FILE: ct/livebook.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: dkuku # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/livebook-dev/livebook APP="Livebook" var_tags="${var_tags:-development}" var_disk="${var_disk:-4}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-1024}" var_os="${var_os:-ubuntu}" var_version="${var_version:-24.04}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -f /opt/livebook/.mix/escripts/livebook ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "livebook" "livebook-dev/livebook"; then msg_info "Stopping Service" systemctl stop livebook msg_info "Stopped Service" msg_info "Updating Container" $STD apt update $STD apt upgrade -y msg_ok "Updated Container" msg_info "Updating Livebook" source /opt/livebook/.env cd /opt/livebook $STD mix escript.install hex livebook --force chown -R livebook:livebook /opt/livebook /data msg_info "Starting Service" systemctl start livebook msg_info "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8080${CL}" ================================================ FILE: ct/lldap.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) | Co-Author: remz1337 # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/lldap/lldap APP="lldap" var_tags="${var_tags:-ldap}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-512}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -f /lib/systemd/system/lldap.service ]]; then msg_error "No ${APP} Installation Found!" exit fi msg_info "Updating lldap" $STD apt update $STD apt upgrade -y lldap msg_ok "Updated lldap" msg_ok "Updated successfully!" exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:17170${CL}" ================================================ FILE: ct/loki.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: hoholms # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/grafana/loki APP="Loki" var_tags="${var_tags:-monitoring;logs}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-512}" var_disk="${var_disk:-2}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if ! dpkg -s loki >/dev/null 2>&1; then msg_error "No ${APP} Installation Found!" exit 233 fi CHOICE=$(msg_menu "Loki Update Options" \ "1" "Update Loki & Promtail" \ "2" "Allow 0.0.0.0 for listening" \ "3" "Allow only ${LOCAL_IP} for listening") case $CHOICE in 1) msg_info "Stopping Loki" systemctl stop loki if systemctl is-active --quiet promtail 2>/dev/null || dpkg -s promtail >/dev/null 2>&1; then systemctl stop promtail fi msg_ok "Stopped Loki" msg_info "Updating Loki" $STD apt update $STD apt install -y --only-upgrade loki if dpkg -s promtail >/dev/null 2>&1; then $STD apt install -y --only-upgrade promtail fi msg_ok "Updated Loki" msg_info "Starting Loki" systemctl start loki if dpkg -s promtail >/dev/null 2>&1; then systemctl start promtail fi msg_ok "Started Loki" msg_ok "Updated successfully!" exit ;; 2) msg_info "Configuring Loki to listen on 0.0.0.0" sed -i 's/http_listen_address:.*/http_listen_address: 0.0.0.0/' /etc/loki/config.yml sed -i 's/http_listen_port:.*/http_listen_port: 3100/' /etc/loki/config.yml systemctl restart loki msg_ok "Configured Loki to listen on 0.0.0.0" exit ;; 3) msg_info "Configuring Loki to listen on ${LOCAL_IP}" sed -i "s/http_listen_address:.*/http_listen_address: $LOCAL_IP/" /etc/loki/config.yml sed -i 's/http_listen_port:.*/http_listen_port: 3100/' /etc/loki/config.yml systemctl restart loki msg_ok "Configured Loki to listen on ${LOCAL_IP}" exit ;; esac exit 0 } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access loki using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:3100${CL}\n" if dpkg -s promtail >/dev/null 2>&1; then echo -e "${INFO}${YW} Access promtail using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:9080${CL}" fi ================================================ FILE: ct/lubelogger.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: kristocopani # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://lubelogger.com/ APP="LubeLogger" var_tags="${var_tags:-vehicle;car}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-512}" var_disk="${var_disk:-2}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -f /etc/systemd/system/lubelogger.service ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "lubelogger" "hargata/lubelog"; then msg_info "Stopping Service" systemctl stop lubelogger msg_ok "Stopped Service" msg_info "Backing up data" mkdir -p /tmp/lubeloggerData/data cp /opt/lubelogger/appsettings.json /tmp/lubeloggerData/appsettings.json cp -r /opt/lubelogger/data/ /tmp/lubeloggerData/ # Lubelogger has moved multiples folders to the 'data' folder, and we need to move them before the update to keep the user data # Github Discussion: https://github.com/hargata/lubelog/discussions/787 [[ -e /opt/lubelogger/config ]] && cp -r /opt/lubelogger/config /tmp/lubeloggerData/data/ [[ -e /opt/lubelogger/wwwroot/translations ]] && cp -r /opt/lubelogger/wwwroot/translations /tmp/lubeloggerData/data/ [[ -e /opt/lubelogger/wwwroot/documents ]] && cp -r /opt/lubelogger/wwwroot/documents /tmp/lubeloggerData/data/ [[ -e /opt/lubelogger/wwwroot/images ]] && cp -r /opt/lubelogger/wwwroot/images /tmp/lubeloggerData/data/ [[ -e /opt/lubelogger/wwwroot/temp ]] && cp -r /opt/lubelogger/wwwroot/temp /tmp/lubeloggerData/data/ [[ -e /opt/lubelogger/log ]] && cp -r /opt/lubelogger/log /tmp/lubeloggerData/ rm -rf /opt/lubelogger msg_ok "Backed up data" fetch_and_deploy_gh_release "lubelogger" "hargata/lubelog" "prebuild" "latest" "/opt/lubelogger" "LubeLogger*linux_x64.zip" msg_info "Configuring LubeLogger" chmod 700 /opt/lubelogger/CarCareTracker cp -rf /tmp/lubeloggerData/* /opt/lubelogger/ rm -rf /tmp/lubeloggerData msg_ok "Configured LubeLogger" msg_info "Starting Service" systemctl start lubelogger msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:5000${CL}" ================================================ FILE: ct/lychee.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/LycheeOrg/Lychee APP="Lychee" var_tags="${var_tags:-media;photos;gallery}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-8}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/lychee ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "lychee" "LycheeOrg/Lychee"; then msg_info "Stopping Services" systemctl stop caddy msg_ok "Stopped Services" msg_info "Backing up Data" cp /opt/lychee/.env /opt/lychee.env.bak cp -r /opt/lychee/storage /opt/lychee_storage_backup msg_ok "Backed up Data" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "lychee" "LycheeOrg/Lychee" "prebuild" "latest" "/opt/lychee" "Lychee.zip" msg_info "Restoring Data" cp /opt/lychee.env.bak /opt/lychee/.env rm -f /opt/lychee.env.bak cp -r /opt/lychee_storage_backup/. /opt/lychee/storage rm -rf /opt/lychee_storage_backup msg_ok "Restored Data" msg_info "Updating Application" cd /opt/lychee $STD php artisan migrate --force $STD php artisan optimize:clear chmod -R 775 /opt/lychee/storage /opt/lychee/bootstrap/cache msg_ok "Updated Application" msg_info "Starting Services" systemctl start caddy msg_ok "Started Services" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed Successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}${CL}" ================================================ FILE: ct/lyrionmusicserver.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Omar Minaya # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://lyrion.org/getting-started/ APP="Lyrion Music Server" var_tags="${var_tags:-media}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-1024}" var_disk="${var_disk:-3}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -f /lib/systemd/system/lyrionmusicserver.service ]]; then msg_error "No ${APP} Installation Found!" exit fi DEB_URL=$(curl -s 'https://lyrion.org/getting-started/' | grep -oP ']*href="\K[^"]*arm\.deb(?="[^>]*>)' | head -n 1) RELEASE=$(echo "$DEB_URL" | grep -oP 'lyrionmusicserver_\K[0-9.]+(?=_arm\.deb)') DEB_FILE="/tmp/lyrionmusicserver_${RELEASE}_arm.deb" if [[ ! -f /opt/lyrion_version.txt ]] || [[ "${RELEASE}" != "$(cat /opt/lyrion_version.txt)" ]]; then msg_info "Updating $APP to ${RELEASE}" curl_with_retry "$DEB_URL" "$DEB_FILE" $STD apt install "$DEB_FILE" -y systemctl restart lyrionmusicserver rm -f "$DEB_FILE" echo "${RELEASE}" >/opt/lyrion_version.txt msg_ok "Updated $APP to ${RELEASE}" msg_ok "Updated successfully!" else msg_ok "$APP is already up to date (${RELEASE})" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access the web interface at:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:9000${CL}" ================================================ FILE: ct/mafl.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://mafl.hywax.space/ APP="Mafl" var_tags="${var_tags:-dashboard}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-6}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/mafl ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "mafl" "hywax/mafl"; then msg_info "Stopping Mafl service" systemctl stop mafl msg_ok "Service stopped" msg_info "Backing up data" mkdir -p /opt/mafl-backup/data mv /opt/mafl/data /opt/mafl-backup/data rm -rf /opt/mafl msg_ok "Backup complete" fetch_and_deploy_gh_release "mafl" "hywax/mafl" "tarball" msg_info "Updating Mafl" cd /opt/mafl $STD yarn install $STD yarn build mv /opt/mafl-backup/data /opt/mafl/data msg_ok "Mafl updated" msg_info "Starting Service" systemctl start mafl msg_ok "Service started" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:3000${CL}" ================================================ FILE: ct/magicmirror.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) | Co-Author Slaviša Arežina (tremor021) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://magicmirror.builders/ APP="MagicMirror" var_tags="${var_tags:-smarthome}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-512}" var_disk="${var_disk:-3}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/magicmirror ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "magicmirror" "MagicMirrorOrg/MagicMirror"; then msg_info "Stopping Service" systemctl stop magicmirror msg_ok "Stopped Service" NODE_VERSION="24" setup_nodejs msg_info "Backing up data" rm -rf /opt/magicmirror-backup mkdir /opt/magicmirror-backup cp /opt/magicmirror/config/config.js /opt/magicmirror-backup if [[ -f /opt/magicmirror/css/custom.css ]]; then cp /opt/magicmirror/css/custom.css /opt/magicmirror-backup fi cp -r /opt/magicmirror/modules /opt/magicmirror-backup msg_ok "Backed up data" fetch_and_deploy_gh_release "magicmirror" "MagicMirrorOrg/MagicMirror" "tarball" msg_info "Configuring MagicMirror" cd /opt/magicmirror sed -i -E 's/("postinstall": )".*"/\1""/; s/("prepare": )".*"/\1""/' package.json $STD npm run install-mm cp /opt/magicmirror-backup/config.js /opt/magicmirror/config/ if [[ -f /opt/magicmirror-backup/custom.css ]]; then cp /opt/magicmirror-backup/custom.css /opt/magicmirror/css/ fi msg_ok "Configured MagicMirror" msg_info "Starting Service" systemctl start magicmirror msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8080${CL}" ================================================ FILE: ct/mail-archiver.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Slaviša Arežina (tremor021) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/s1t5/mail-archiver APP="Mail-Archiver" var_tags="${var_tags:-mail-archiver}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-8}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/mail-archiver ]]; then msg_error "No ${APP} Installation Found!" exit fi ensure_dependencies libgssapi-krb5-2 if check_for_gh_release "mail-archiver" "s1t5/mail-archiver"; then msg_info "Stopping Mail-Archiver" systemctl stop mail-archiver msg_ok "Stopped Mail-Archiver" msg_info "Creating Backup" cp /opt/mail-archiver/appsettings.json /opt/mail-archiver/.env /opt/ [[ -d /opt/mail-archiver/DataProtection-Keys ]] && cp -r /opt/mail-archiver/DataProtection-Keys /opt msg_ok "Created Backup" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "mail-archiver" "s1t5/mail-archiver" "tarball" msg_info "Updating Mail-Archiver" mv /opt/mail-archiver /opt/mail-archiver-build cd /opt/mail-archiver-build $STD dotnet restore $STD dotnet publish -c Release -o /opt/mail-archiver rm -rf /opt/mail-archiver-build msg_ok "Updated Mail-Archiver" msg_info "Restoring Backup" cp /opt/appsettings.json /opt/.env /opt/mail-archiver [[ -d /opt/DataProtection-Keys ]] && cp -r /opt/DataProtection-Keys /opt/mail-archiver/ msg_ok "Restored Backup" msg_info "Starting Mail-Archiver" systemctl start mail-archiver msg_ok "Started Mail-Archiver" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:5000${CL}" ================================================ FILE: ct/managemydamnlife.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: vhsdream # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/intri-in/manage-my-damn-life-nextjs APP="Manage My Damn Life" var_tags="${var_tags:-calendar;tasks}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-6}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/mmdl ]]; then msg_error "No ${APP} Installation Found!" exit fi setup_mariadb if check_for_gh_release "mmdl" "intri-in/manage-my-damn-life-nextjs"; then msg_info "Stopping service" systemctl stop mmdl msg_ok "Stopped service" msg_info "Creating Backup" cp /opt/mmdl/.env /opt/mmdl.env rm -rf /opt/mmdl msg_ok "Backup Created" fetch_and_deploy_gh_release "mmdl" "intri-in/manage-my-damn-life-nextjs" "tarball" NODE_VERSION="22" setup_nodejs msg_info "Configuring ${APP}" cd /opt/mmdl export NEXT_TELEMETRY_DISABLED=1 $STD npm install $STD npm run migrate $STD npm run build msg_ok "Configured ${APP}" msg_info "Starting service" systemctl start mmdl msg_ok "Started service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:3000${CL}" ================================================ FILE: ct/manyfold.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: bvdberg01 | Co-Author: SunFlowerOwl # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/manyfold3d/manyfold APP="Manyfold" var_tags="${var_tags:-3d}" var_cpu="${var_cpu:-4}" var_ram="${var_ram:-4096}" var_disk="${var_disk:-15}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/manyfold ]]; then msg_error "No ${APP} Installation Found!" exit fi NODE_VERSION="24" NODE_MODULE="yarn" setup_nodejs if check_for_gh_release "manyfold" "manyfold3d/manyfold"; then msg_info "Stopping Services" systemctl stop manyfold.target manyfold-rails.1 manyfold-default_worker.1 manyfold-performance_worker.1 msg_ok "Stopped Services" msg_info "Backing up Data" CURRENT_VERSION=$(grep -oP 'APP_VERSION=\K[^ ]+' /opt/manyfold/.env || echo "unknown") cp -r /opt/manyfold/app/storage /opt/manyfold_storage_backup 2>/dev/null || true cp -r /opt/manyfold/app/tmp /opt/manyfold_tmp_backup 2>/dev/null || true cp /opt/manyfold/app/config/credentials.yml.enc /opt/manyfold_credentials.yml.enc 2>/dev/null || true cp /opt/manyfold/app/config/master.key /opt/manyfold_master.key 2>/dev/null || true $STD tar -czf "/opt/manyfold_${CURRENT_VERSION}_backup.tar.gz" -C /opt/manyfold app msg_ok "Backed up Data" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "manyfold" "manyfold3d/manyfold" "tarball" "latest" "/opt/manyfold/app" msg_info "Configuring Manyfold" RUBY_INSTALL_VERSION=$(cat /opt/manyfold/app/.ruby-version) YARN_VERSION=$(grep '"packageManager":' /opt/manyfold/app/package.json | sed -E 's/.*"(yarn@[0-9\.]+)".*/\1/') RELEASE=$(get_latest_github_release "manyfold3d/manyfold") sed -i "s/^export APP_VERSION=.*/export APP_VERSION=$RELEASE/" "/opt/manyfold/.env" msg_ok "Configured Manyfold" RUBY_VERSION=${RUBY_INSTALL_VERSION} RUBY_INSTALL_RAILS="true" HOME=/home/manyfold setup_ruby msg_info "Installing Manyfold" chown -R manyfold:manyfold {/home/manyfold,/opt/manyfold} chown -R manyfold:manyfold /opt/manyfold sudo -u manyfold bash -c ' source /opt/manyfold/.env export PATH="/home/manyfold/.rbenv/bin:$PATH" eval "$(/home/manyfold/.rbenv/bin/rbenv init - bash)" cd /opt/manyfold/app gem install bundler sidekiq foreman bundle install corepack enable yarn corepack prepare '"$YARN_VERSION"' --activate corepack use '"$YARN_VERSION"' bin/rails db:migrate bin/rails assets:precompile ' msg_ok "Installed Manyfold" msg_info "Restoring Data" rm -rf /opt/manyfold/app/{storage,tmp,config/credentials.yml.enc,config/master.key} cp -r /opt/manyfold_storage_backup /opt/manyfold/app/storage 2>/dev/null || true cp -r /opt/manyfold_tmp_backup /opt/manyfold/app/tmp 2>/dev/null || true cp /opt/manyfold_credentials.yml.enc /opt/manyfold/app/config/credentials.yml.enc 2>/dev/null || true cp /opt/manyfold_master.key /opt/manyfold/app/config/master.key 2>/dev/null || true chown -R manyfold:manyfold /opt/manyfold/app/storage /opt/manyfold/app/tmp /opt/manyfold/app/config rm -rf /opt/manyfold_storage_backup /opt/manyfold_tmp_backup /opt/manyfold_credentials.yml.enc /opt/manyfold_master.key msg_ok "Restored Data" msg_info "Restarting Services" source /opt/manyfold/.env export PATH="/home/manyfold/.rbenv/shims:/home/manyfold/.rbenv/bin:$PATH" $STD foreman export systemd /etc/systemd/system -a manyfold -u manyfold -f /opt/manyfold/app/Procfile for f in /etc/systemd/system/manyfold-*.service; do sed -i "s|/bin/bash -lc '|/bin/bash -lc 'source /opt/manyfold/.env \&\& |" "$f" done systemctl daemon-reload systemctl enable -q --now manyfold.target manyfold-rails.1 manyfold-default_worker.1 manyfold-performance_worker.1 msg_ok "Restarted Services" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}${CL}" ================================================ FILE: ct/mariadb.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://mariadb.org/ APP="MariaDB" var_tags="${var_tags:-database}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-1024}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /etc/mysql/mariadb.conf.d ]]; then msg_error "No ${APP} Installation Found!" exit fi setup_mariadb msg_info "Updating ${APP} LXC" $STD apt update $STD apt -y upgrade msg_ok "Updated successfully!" exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following IP:${CL}" echo -e "${TAB}${GATEWAY}${BGN}${IP}:3306${CL}" ================================================ FILE: ct/matomo.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://matomo.org/ APP="Matomo" var_tags="${var_tags:-analytics;tracking;privacy}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-16}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/matomo ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "matomo" "matomo-org/matomo"; then msg_info "Stopping Services" systemctl stop caddy msg_ok "Stopped Services" msg_info "Backing up Data" [[ -f /opt/matomo/config/config.ini.php ]] && cp /opt/matomo/config/config.ini.php /opt/matomo_config.bak [[ -d /opt/matomo/misc/user ]] && cp -r /opt/matomo/misc/user /opt/matomo_user_backup [[ -f /root/matomo.creds ]] && cp /root/matomo.creds /opt/matomo_db_creds.bak msg_ok "Backed up Data" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "matomo" "matomo-org/matomo" "prebuild" "latest" "/opt/matomo" "matomo-*.zip" msg_info "Restoring Data" if [[ -f /opt/matomo_config.bak ]]; then mkdir -p /opt/matomo/config cp /opt/matomo_config.bak /opt/matomo/config/config.ini.php fi if [[ -d /opt/matomo_user_backup ]]; then mkdir -p /opt/matomo/misc/user cp -r /opt/matomo_user_backup/. /opt/matomo/misc/user fi [[ -f /opt/matomo_db_creds.bak ]] && cp /opt/matomo_db_creds.bak /root/matomo.creds rm -f /opt/matomo_config.bak /opt/matomo_db_creds.bak rm -rf /opt/matomo_user_backup chown -R www-data:www-data /opt/matomo msg_ok "Restored Data" msg_info "Starting Services" systemctl start caddy msg_ok "Started Services" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed Successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}${CL}" ================================================ FILE: ct/matter-server.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/matter-js/python-matter-server APP="Matter-Server" var_tags="${var_tags:-matter;iot;smart-home}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/matter-server ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "matter-server" "matter-js/python-matter-server"; then msg_info "Stopping Service" systemctl stop matter-server msg_ok "Stopped Service" msg_info "Updating Matter Server" MATTER_VERSION=$(get_latest_github_release "matter-js/python-matter-server") $STD uv pip install --python /opt/matter-server/.venv/bin/python --upgrade "python-matter-server[server]==${MATTER_VERSION}" echo "${MATTER_VERSION}" >~/.matter-server msg_ok "Updated Matter Server" fetch_and_deploy_gh_release "chip-ota-provider-app" "home-assistant-libs/matter-linux-ota-provider" "singlefile" "latest" "/usr/local/bin" "chip-ota-provider-app-x86-64" msg_info "Starting Service" systemctl start matter-server msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed Successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Matter Server WebSocket API is running on port 5580.${CL}" echo -e "${TAB}${GATEWAY}${BGN}ws://${IP}:5580/ws${CL}" ================================================ FILE: ct/matterbridge.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: MickLesk (Canbiz) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/Luligu/matterbridge APP="Matterbridge" var_tags="${var_tags:-matter;smarthome}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-1024}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /root/Matterbridge ]]; then msg_error "No ${APP} Installation Found!" exit fi $STD apt update $STD apt upgrade -y NODE_VERSION="24" NODE_MODULE="matterbridge" setup_nodejs msg_ok "Updated successfully!" exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8283${CL}" ================================================ FILE: ct/mattermost.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Kaedon Cleland-Host (dracentis) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://mattermost.com/ APP="Mattermost" var_tags="${var_tags:-collaboration}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-8}" var_os="${var_os:-ubuntu}" var_version="${var_version:-24.04}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -f /etc/apt/sources.list.d/mattermost.list ]]; then msg_error "No ${APP} Installation Found!" exit fi msg_info "Updating ${APP} LXC" $STD apt update $STD apt -y upgrade msg_ok "Updated successfully!" exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8065${CL}" ================================================ FILE: ct/mealie.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://mealie.io APP="Mealie" var_tags="${var_tags:-recipes}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-3072}" var_disk="${var_disk:-10}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/mealie ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "mealie" "mealie-recipes/mealie"; then PYTHON_VERSION="3.12" setup_uv NODE_MODULE="yarn" NODE_VERSION="24" setup_nodejs msg_info "Stopping Service" systemctl stop mealie msg_ok "Stopped Service" msg_info "Backing up Configuration" cp -f /opt/mealie/mealie.env /opt/mealie.env [[ -f /opt/mealie/start.sh ]] && cp -f /opt/mealie/start.sh /opt/mealie.start.sh msg_ok "Backup completed" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "mealie" "mealie-recipes/mealie" "tarball" msg_info "Restoring Configuration" mv -f /opt/mealie.env /opt/mealie/mealie.env if [[ -f /opt/mealie.start.sh ]]; then mv -f /opt/mealie.start.sh /opt/mealie/start.sh else cat <<'STARTEOF' >/opt/mealie/start.sh #!/bin/bash set -a source /opt/mealie/mealie.env set +a exec uv run mealie STARTEOF fi chmod +x /opt/mealie/start.sh msg_ok "Configuration restored" msg_info "Installing Python Dependencies with uv" cd /opt/mealie $STD uv sync --frozen --extra pgsql msg_ok "Installed Python Dependencies" msg_info "Building Frontend" MEALIE_VERSION=$(<$HOME/.mealie) SITE_SETTINGS=$(find /opt/mealie/frontend -name "site-settings.vue" -path "*/admin/*" | head -1) $STD sed -i "s|https://github.com/mealie-recipes/mealie/commit/|https://github.com/mealie-recipes/mealie/releases/tag/|g" "$SITE_SETTINGS" $STD sed -i "s|value: data.buildId,|value: \"v${MEALIE_VERSION}\",|g" "$SITE_SETTINGS" $STD sed -i "s|value: data.production ? i18n.t(\"about.production\") : i18n.t(\"about.development\"),|value: \"bare-metal\",|g" "$SITE_SETTINGS" export NUXT_TELEMETRY_DISABLED=1 cd /opt/mealie/frontend $STD yarn install --prefer-offline --frozen-lockfile --non-interactive --production=false --network-timeout 1000000 $STD yarn generate msg_ok "Built Frontend" msg_info "Copying Built Frontend" mkdir -p /opt/mealie/mealie/frontend cp -r /opt/mealie/frontend/dist/* /opt/mealie/mealie/frontend/ msg_ok "Copied Frontend" setup_nltk "averaged_perceptron_tagger_eng" "/nltk_data" msg_info "Starting Service" systemctl start mealie msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:9000${CL}" ================================================ FILE: ct/mediamanager.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: vhsdream # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/maxdorninger/MediaManager APP="MediaManager" var_tags="${var_tags:-arr}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-3072}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/mediamanager ]]; then msg_error "No ${APP} Installation Found!" exit fi setup_uv if check_for_gh_release "mediamanager" "maxdorninger/MediaManager"; then msg_info "Stopping Service" systemctl stop mediamanager msg_ok "Stopped Service" fetch_and_deploy_gh_release "MediaManager" "maxdorninger/MediaManager" "tarball" "latest" "/opt/mediamanager" msg_info "Updating MediaManager" MM_DIR="/opt/mm" export CONFIG_DIR="${MM_DIR}/config" export FRONTEND_FILES_DIR="${MM_DIR}/web/build" export PUBLIC_VERSION="" export PUBLIC_API_URL="" export BASE_PATH="/web" cd /opt/mediamanager/web $STD npm install --no-fund --no-audit $STD npm run build rm -rf "$FRONTEND_FILES_DIR"/build cp -r build "$FRONTEND_FILES_DIR" export BASE_PATH="" export VIRTUAL_ENV="/opt/${MM_DIR}/venv" cd /opt/mediamanager rm -rf "$MM_DIR"/{media_manager,alembic*} cp -r {media_manager,alembic*} "$MM_DIR" $STD /usr/local/bin/uv sync --locked --active -n -p cpython3.13 --managed-python if ! grep -q "LOG_FILE" "$MM_DIR"/start.sh; then sed -i "\|build\"$|a\export LOG_FILE=\"$CONFIG_DIR/media_manager.log\"" "$MM_DIR"/start.sh fi msg_ok "Updated MediaManager" msg_info "Starting Service" systemctl start mediamanager msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8000${CL}" ================================================ FILE: ct/mediamtx.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/bluenviron/mediamtx APP="MediaMTX" var_tags="${var_tags:-media}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" var_gpu="${var_gpu:-yes}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/mediamtx/ ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "mediamtx" "bluenviron/mediamtx"; then msg_info "Stopping service" systemctl stop mediamtx msg_ok "Service stopped" fetch_and_deploy_gh_release "mediamtx" "bluenviron/mediamtx" "prebuild" "latest" "/opt/mediamtx" "mediamtx*linux_arm64.tar.gz" msg_info "Starting service" systemctl start mediamtx msg_ok "Service started" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" ================================================ FILE: ct/medusa.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/pymedusa/Medusa.git APP="Medusa" var_tags="${var_tags:-media}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-1024}" var_disk="${var_disk:-6}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/medusa ]]; then msg_error "No ${APP} Installation Found!" exit fi msg_info "Stopping Service" systemctl stop medusa msg_ok "Stopped Service" msg_info "Updating ${APP}" cd /opt/medusa output=$(git pull --no-rebase) if echo "$output" | grep -q "Already up to date."; then msg_ok "$APP is already up to date." exit fi msg_ok "Updated successfully!" msg_info "Starting Service" systemctl start medusa msg_ok "Started Service" exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8081${CL}" ================================================ FILE: ct/meilisearch.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.meilisearch.com/ APP="Meilisearch" var_tags="${var_tags:-full-text-search}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-4096}" var_disk="${var_disk:-7}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources setup_meilisearch if [[ -d /opt/meilisearch-ui ]]; then if check_for_gh_release "meilisearch-ui" "riccox/meilisearch-ui"; then msg_info "Stopping Meilisearch-UI" systemctl stop meilisearch-ui msg_ok "Stopped Meilisearch-UI" cp /opt/meilisearch-ui/.env.local /tmp/.env.local.bak rm -rf /opt/meilisearch-ui fetch_and_deploy_gh_release "meilisearch-ui" "riccox/meilisearch-ui" "tarball" msg_info "Configuring Meilisearch-UI" cd /opt/meilisearch-ui sed -i 's|const hash = execSync("git rev-parse HEAD").toString().trim();|const hash = "unknown";|' /opt/meilisearch-ui/vite.config.ts mv /tmp/.env.local.bak /opt/meilisearch-ui/.env.local $STD pnpm install msg_ok "Configured Meilisearch-UI" msg_info "Starting Meilisearch-UI" systemctl start meilisearch-ui msg_ok "Started Meilisearch-UI" fi fi msg_ok "Updated successfully!" exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}meilisearch: http://${IP}:7700$ | meilisearch-ui: http://${IP}:24900${CL}" ================================================ FILE: ct/memos.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: MickLesk (Canbiz) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.usememos.com/ APP="Memos" var_tags="${var_tags:-notes}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-1024}" var_disk="${var_disk:-3}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/memos ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "memos" "usememos/memos"; then msg_info "Stopping service" systemctl stop memos msg_ok "Service stopped" fetch_and_deploy_gh_release "memos" "usememos/memos" "prebuild" "v0.25.3" "/opt/memos" "memos*linux_arm64.tar.gz" msg_info "Starting service" systemctl start memos msg_ok "Service started" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:9030${CL}" ================================================ FILE: ct/meshcentral.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://meshcentral.com/ APP="MeshCentral" var_tags="${var_tags:-remote-management}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-512}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/meshcentral ]]; then msg_error "No ${APP} Installation Found!" exit fi msg_info "Updating ${APP} LXC" $STD apt update $STD apt -y upgrade msg_ok "Updated successfully!" exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}${CL}" ================================================ FILE: ct/metabase.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Slaviša Arežina (tremor021) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.metabase.com/ APP="Metabase" var_tags="${var_tags:-analytics}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-6}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/metabase ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "metabase" "metabase/metabase"; then msg_info "Stopping Service" systemctl stop metabase msg_info "Stopped Service" msg_info "Creating backup" mv /opt/metabase/.env /opt msg_ok "Created backup" msg_info "Updating Metabase" RELEASE=$(get_latest_github_release "metabase/metabase") curl -fsSL "https://downloads.metabase.com/v${RELEASE}.x/metabase.jar" -o /opt/metabase/metabase.jar echo $RELEASE >~/.metabase msg_ok "Updated Metabase" msg_info "Restoring backup" mv /opt/.env /opt/metabase msg_ok "Restored backup" msg_info "Starting Service" systemctl start metabase msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:3000${CL}" ================================================ FILE: ct/metube.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (Canbiz) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/alexta69/metube APP="MeTube" var_tags="${var_tags:-media;youtube}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-10}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/metube ]]; then msg_error "No ${APP} Installation Found!" exit fi if [[ $(echo ":$PATH:" != *":/usr/local/bin:"*) ]]; then echo -e "\nexport PATH=\"/usr/local/bin:\$PATH\"" >>~/.bashrc source ~/.bashrc if ! command -v deno &>/dev/null; then export DENO_INSTALL="/usr/local" curl -fsSL https://deno.land/install.sh | $STD sh -s -- -y else $STD deno upgrade fi fi NODE_VERSION="24" NODE_MODULE="pnpm" setup_nodejs if check_for_gh_release "metube" "alexta69/metube"; then msg_info "Stopping Service" systemctl stop metube msg_ok "Stopped Service" msg_info "Backing up Old Installation" if [[ -d /opt/metube_bak ]]; then rm -rf /opt/metube_bak fi mv /opt/metube /opt/metube_bak msg_ok "Backup created" fetch_and_deploy_gh_release "metube" "alexta69/metube" "tarball" "latest" msg_info "Building Frontend" cd /opt/metube/ui if command -v corepack >/dev/null 2>&1; then $STD corepack enable $STD corepack prepare pnpm --activate || true fi echo 'onlyBuiltDependencies=*' >> .npmrc $STD pnpm install --frozen-lockfile $STD pnpm run build msg_ok "Built Frontend" PYTHON_VERSION="3.13" setup_uv msg_info "Installing Backend Requirements" cd /opt/metube $STD uv sync msg_ok "Installed Backend" msg_info "Restoring .env" if [[ -f /opt/metube_bak/.env ]]; then cp /opt/metube_bak/.env /opt/metube/.env fi rm -rf /opt/metube_bak msg_ok "Restored .env" if grep -q 'pipenv' /etc/systemd/system/metube.service; then msg_info "Patching systemd Service" cat </etc/systemd/system/metube.service [Unit] Description=Metube - YouTube Downloader After=network.target [Service] Type=simple WorkingDirectory=/opt/metube EnvironmentFile=/opt/metube/.env ExecStart=/opt/metube/.venv/bin/python3 app/main.py Restart=always User=root [Install] WantedBy=multi-user.target EOF msg_ok "Patched systemd Service" fi $STD systemctl daemon-reload msg_ok "Service Updated" msg_info "Starting Service" systemctl start metube msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8081${CL}" ================================================ FILE: ct/minarca.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Slaviša Arežina (tremor021) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://minarca.org/en_CA APP="Minarca" var_tags="${var_tags:-backup}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-4096}" var_disk="${var_disk:-10}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" var_fuse="${var_fuse:-yes}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/minarca-server ]]; then msg_error "No ${APP} Installation Found!" exit fi msg_info "Stopping Service" systemctl stop minarca-server msg_ok "Stopped Service" msg_info "Updating ${APP} LXC" $STD apt update $STD apt upgrade -y msg_ok "Updated ${APP} LXC" msg_info "Starting Service" systemctl start minarca-server msg_ok "Started Service" msg_ok "Updated successfully!" exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8080${CL}" ================================================ FILE: ct/mini-qr.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: doge0420 # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/lyqht/mini-qr APP="Mini-QR" var_tags="${var_tags:-QRcode;}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-6}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/mini-qr ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "mini-qr" "lyqht/mini-qr"; then msg_info "Stopping Service" systemctl stop caddy msg_ok "Stopped Service" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "mini-qr" "lyqht/mini-qr" "tarball" msg_info "Installing Dependencies" cd /opt/mini-qr $STD npm install msg_ok "Installed Dependencies" msg_info "Building MiniQR" $STD npm run build msg_ok "Built MiniQR" msg_info "Starting Service" systemctl start caddy msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}${CL}" ================================================ FILE: ct/miniflux.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: omernaveedxyz # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://miniflux.app/ APP="Miniflux" var_tags="${var_tags:-media}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-8}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if ! systemctl -q is-enabled miniflux 2>/dev/null; then msg_error "No ${APP} Installation Found!" exit fi msg_info "Stopping Service" $STD miniflux -flush-sessions -config-file /etc/miniflux.conf systemctl stop miniflux msg_ok "Service Stopped" fetch_and_deploy_gh_release "miniflux" "miniflux/v2" "binary" "latest" msg_info "Updating Miniflux" $STD miniflux -migrate -config-file /etc/miniflux.conf msg_ok "Updated Miniflux" msg_info "Starting Service" $STD systemctl start miniflux msg_ok "Started Service" msg_ok "Updated successfully!" exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8080${CL}" ================================================ FILE: ct/minio.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/minio/minio APP="MinIO" var_tags="${var_tags:-object-storage}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-1024}" var_disk="${var_disk:-5}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -f /usr/local/bin/minio ]]; then msg_error "No ${APP} Installation Found!" exit fi FEATURE_RICH_VERSION="2025-04-22T22-12-26Z" RELEASE=$(curl -fsSL https://api.github.com/repos/minio/minio/releases/latest | grep '"tag_name"' | awk -F '"' '{print $4}') CURRENT_VERSION="" [[ -f /opt/${APP}_version.txt ]] && CURRENT_VERSION=$(cat /opt/${APP}_version.txt) RELEASE=$(curl -fsSL https://api.github.com/repos/minio/minio/releases/latest | grep '"tag_name"' | awk -F '"' '{print $4}') if [[ "${CURRENT_VERSION}" == "${FEATURE_RICH_VERSION}" && "${RELEASE}" != "${FEATURE_RICH_VERSION}" ]]; then echo echo "You are currently running the last feature-rich community version: ${FEATURE_RICH_VERSION}" echo "WARNING: Updating to the latest version will REMOVE most management features from the Console UI." echo "Do you still want to upgrade to the latest version? [y/N]: " read -n 1 -r echo if [[ ! $REPLY =~ ^[Yy]$ ]]; then msg_ok "No update performed. Staying on the feature-rich version." exit fi fi if [[ "${CURRENT_VERSION}" != "${RELEASE}" ]]; then msg_info "Stopping Service" systemctl stop minio msg_ok "Stopped Service" msg_info "Updating ${APP} to ${RELEASE}" mv /usr/local/bin/minio /usr/local/bin/minio_bak curl -fsSL "https://dl.min.io/server/minio/release/linux-arm64/minio" -o /usr/local/bin/minio chmod +x /usr/local/bin/minio rm -f /usr/local/bin/minio_bak echo "${RELEASE}" >/opt/${APP}_version.txt msg_ok "Updated ${APP}" msg_info "Starting Service" systemctl start minio msg_ok "Started Service" msg_ok "Updated successfully!" else msg_ok "No update required. ${APP} is already at ${RELEASE}" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:9000${CL}" ================================================ FILE: ct/minthcm.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: MintHCM # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/minthcm/minthcm APP="MintHCM" var_tags="${var_tags:-hcm}" var_disk="${var_disk:-20}" var_cpu="${var_cpu:-4}" var_ram="${var_ram:-4096}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /var/www/MintHCM ]]; then msg_error "No ${APP} Installation Found!" exit fi msg_custom "🚀" "${GN}" "The app offers a built-in updater. Please use it." exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}${CL}" ================================================ FILE: ct/mongodb.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.mongodb.com/de-de APP="MongoDB" var_tags="${var_tags:-database}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-512}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if ! command -v mongod &>/dev/null; then msg_error "No ${APP} Installation Found!" exit fi msg_info "Updating MongoDB LXC" $STD apt update $STD apt upgrade -y msg_ok "Updated successfully!" exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" ================================================ FILE: ct/monica.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: bvdberg01 # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.monicahq.com/ APP="Monica" var_tags="${var_tags:-network}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-8}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/monica ]]; then msg_error "No ${APP} Installation Found!" exit fi setup_mariadb NODE_VERSION="22" NODE_MODULE="yarn@latest" setup_nodejs # Fix for previous versions not having cronjob if ! grep -Fq 'php /opt/monica/artisan schedule:run' /etc/crontab; then echo '* * * * * root php /opt/monica/artisan schedule:run >> /dev/null 2>&1' >>/etc/crontab fi if check_for_gh_release "monica" "monicahq/monica"; then msg_info "Stopping Service" systemctl stop apache2 msg_ok "Stopped Service" msg_info "Creating backup" mv /opt/monica/ /opt/monica-backup msg_ok "Backup created" fetch_and_deploy_gh_release "monica" "monicahq/monica" "prebuild" "latest" "/opt/monica" "monica-v*.tar.bz2" msg_info "Configuring monica" cd /opt/monica/ cp -r /opt/monica-backup/.env /opt/monica cp -r /opt/monica-backup/storage/* /opt/monica/storage/ $STD composer install --no-interaction --no-dev $STD yarn config set ignore-engines true $STD yarn install $STD yarn run production $STD php artisan monica:update --force chown -R www-data:www-data /opt/monica chmod -R 775 /opt/monica/storage rm -r /opt/monica-backup msg_ok "Configured monica" msg_info "Starting Service" systemctl start apache2 msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}${CL}" ================================================ FILE: ct/motioneye.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/motioneye-project/motioneye APP="Motioneye" var_tags="${var_tags:-nvr}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-8}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" var_gpu="${var_gpu:-yes}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -f /etc/systemd/system/motioneye.service ]]; then msg_error "No ${APP} Installation Found!" exit fi msg_info "Updating ${APP} LXC" $STD pip install motioneye --upgrade msg_ok "Updated successfully!" exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8765${CL}" ================================================ FILE: ct/mqtt.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://mosquitto.org/ APP="MQTT" var_tags="${var_tags:-mqtt}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-512}" var_disk="${var_disk:-2}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -f /etc/mosquitto/conf.d/default.conf ]]; then msg_error "No ${APP} Installation Found!" exit fi msg_info "Updating ${APP} LXC" $STD apt update $STD apt -y upgrade msg_ok "Updated successfully!" exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following IP:${CL}" echo -e "${TAB}${GATEWAY}${BGN}${IP}:1883${CL}" ================================================ FILE: ct/myip.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Slaviša Arežina (tremor021) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://ipcheck.ing/ APP="MyIP" var_tags="${var_tags:-network}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-1024}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/myip ]]; then msg_error "No ${APP} Installation Found!" exit fi NODE_VERSION="24" setup_nodejs if check_for_gh_release "myip" "jason5ng32/MyIP"; then msg_info "Stopping Services" systemctl stop myip msg_ok "Stopped Services" cp /opt/myip/.env /opt CLEAN_INSTALL=1 fetch_and_deploy_gh_release "myip" "jason5ng32/MyIP" "tarball" mv /opt/.env /opt/myip msg_info "Starting Services" systemctl start myip msg_ok "Started Services" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:18966${CL}" ================================================ FILE: ct/mylar3.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: davalanche | Co-Author: Slaviša Arežina (tremor021) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/mylar3/mylar3 APP="Mylar3" var_tags="${var_tags:-torrent;downloader;comic}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-512}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info if [[ ! -d /opt/mylar3 ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "mylar3" "mylar3/mylar3"; then fetch_and_deploy_gh_release "mylar3" "mylar3/mylar3" "tarball" systemctl restart mylar3 msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8090${CL}" ================================================ FILE: ct/myspeed.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) | Co-Author: MickLesk (Canbiz) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://myspeed.dev/ APP="MySpeed" var_tags="${var_tags:-tracking}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-1024}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/myspeed ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "myspeed" "gnmyt/myspeed"; then msg_info "Stopping Service" systemctl stop myspeed msg_ok "Stopped Service" msg_info "Creating backup" cd /opt rm -rf myspeed_bak mv myspeed myspeed_bak msg_ok "Backup created" fetch_and_deploy_gh_release "myspeed" "gnmyt/myspeed" "prebuild" "latest" "/opt/myspeed" "MySpeed-*.zip" msg_info "Updating ${APP}" cd /opt/myspeed $STD npm install if [[ -d /opt/myspeed_bak/data ]]; then mkdir -p /opt/myspeed/data/ cp -r /opt/myspeed_bak/data/* /opt/myspeed/data/ fi msg_ok "Updated ${APP}" msg_info "Starting Service" systemctl start myspeed msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:5216${CL}" ================================================ FILE: ct/mysql.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck | Co-Author: MickLesk (Canbiz) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.mysql.com/ APP="MySQL" var_tags="${var_tags:-database}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-1024}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-12}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -f /usr/share/keyrings/mysql.gpg ]]; then msg_error "No ${APP} Installation Found!" exit fi msg_info "Updating ${APP} LXC" $STD apt update $STD apt -y upgrade msg_ok "Updated successfully!" exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following IP:${CL}" echo -e "${TAB}${GATEWAY}${BGN}${IP}:3306${CL}" ================================================ FILE: ct/n8n.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://n8n.io/ APP="n8n" var_tags="${var_tags:-automation}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-10}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -f /etc/systemd/system/n8n.service ]]; then msg_error "No ${APP} Installation Found!" exit fi ensure_dependencies build-essential python3-setuptools graphicsmagick NODE_VERSION="24" setup_nodejs msg_info "Updating n8n" if [ ! -f /opt/n8n.env ]; then sed -i 's|^Environment="N8N_SECURE_COOKIE=false"$|EnvironmentFile=/opt/n8n.env|' /etc/systemd/system/n8n.service mkdir -p /opt cat </opt/n8n.env N8N_SECURE_COOKIE=false N8N_PORT=5678 N8N_PROTOCOL=http N8N_HOST=$LOCAL_IP EOF systemctl daemon-reload fi $STD npm update -g n8n systemctl restart n8n msg_ok "Updated n8n" msg_ok "Updated successfully!" exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:5678${CL}" ================================================ FILE: ct/nagios.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: CanbiZ (MickLesk) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/NagiosEnterprises/nagioscore APP="Nagios" var_tags="${var_tags:-monitoring;alerts;infrastructure}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-20}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -f /usr/local/nagios/etc/nagios.cfg ]]; then msg_error "No ${APP} Installation Found!" exit fi msg_info "Backing up Configuration" cp -a /usr/local/nagios/etc /opt/nagios-etc-backup msg_ok "Backed up Configuration" if check_for_gh_release "nagios" "NagiosEnterprises/nagioscore"; then msg_info "Stopping Nagios" systemctl stop nagios msg_ok "Stopped Nagios" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "nagios" "NagiosEnterprises/nagioscore" "tarball" msg_info "Building Nagios Core" cd /opt/nagios $STD ./configure --with-httpd-conf=/etc/apache2/sites-enabled $STD make all $STD make install-groups-users usermod -a -G nagios www-data $STD make install $STD make install-daemoninit $STD make install-commandmode $STD make install-webconf $STD a2enmod rewrite $STD a2enmod cgi setcap cap_net_raw+p /bin/ping msg_ok "Built Nagios Core" msg_info "Starting Nagios" systemctl restart apache2 systemctl start nagios msg_ok "Started Nagios" fi if check_for_gh_release "nagios-plugins" "nagios-plugins/nagios-plugins"; then CLEAN_INSTALL=1 fetch_and_deploy_gh_release "nagios-plugins" "nagios-plugins/nagios-plugins" "tarball" msg_info "Building Nagios Plugins" cd /opt/nagios-plugins $STD ./tools/setup $STD ./configure $STD make $STD make install msg_ok "Built Nagios Plugins" fi msg_info "Restoring Configuration" rm -rf /usr/local/nagios/etc cp -a /opt/nagios-etc-backup /usr/local/nagios/etc rm -rf /opt/nagios-etc-backup msg_ok "Restored Configuration" msg_ok "Updated successfully!" exit } start build_container description msg_ok "Completed Successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}/nagios${CL}" ================================================ FILE: ct/nametag.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/mattogodoy/nametag APP="Nametag" var_tags="${var_tags:-contacts;crm}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-8}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/nametag ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "nametag" "mattogodoy/nametag"; then msg_info "Stopping Service" systemctl stop nametag msg_ok "Stopped Service" msg_info "Backing up Data" cp /opt/nametag/.env /opt/nametag.env.bak cp -r /opt/nametag/data /opt/nametag_data_bak msg_ok "Backed up Data" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "nametag" "mattogodoy/nametag" "tarball" "latest" "/opt/nametag" msg_info "Rebuilding Application" cd /opt/nametag $STD npm ci set -a source /opt/nametag/.env set +a $STD npx prisma generate $STD npm run build cp -r /opt/nametag/.next/static /opt/nametag/.next/standalone/.next/static cp -r /opt/nametag/public /opt/nametag/.next/standalone/public msg_ok "Rebuilt Application" msg_info "Restoring Data" cp /opt/nametag.env.bak /opt/nametag/.env cp -r /opt/nametag_data_bak/. /opt/nametag/data/ rm -f /opt/nametag.env.bak rm -rf /opt/nametag_data_bak msg_ok "Restored Data" msg_info "Running Migrations" cd /opt/nametag $STD npx prisma migrate deploy msg_ok "Ran Migrations" msg_info "Starting Service" systemctl start nametag msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed Successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:3000${CL}" ================================================ FILE: ct/navidrome.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/navidrome/navidrome APP="Navidrome" var_tags="${var_tags:-music}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-1024}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /var/lib/navidrome ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "navidrome" "navidrome/navidrome"; then msg_info "Stopping Services" systemctl stop navidrome msg_ok "Services Stopped" fetch_and_deploy_gh_release "navidrome" "navidrome/navidrome" "binary" msg_info "Starting Services" systemctl start navidrome msg_ok "Started Services" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:4533${CL}" ================================================ FILE: ct/neko.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: CanbiZ (MickLesk) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://neko.m1k1o.net/ APP="Neko" var_tags="${var_tags:-virtual-browser;webrtc;streaming}" var_cpu="${var_cpu:-4}" var_ram="${var_ram:-4096}" var_disk="${var_disk:-12}" var_os="${var_os:-debian}" var_version="${var_version:-12}" var_unprivileged="${var_unprivileged:-1}" var_gpu="${var_gpu:-yes}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/neko ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "neko" "m1k1o/neko"; then msg_info "Stopping Service" systemctl stop neko msg_ok "Stopped Service" msg_info "Backing up Data" cp /etc/neko/neko.yaml /opt/neko.yaml.bak msg_ok "Backed up Data" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "neko" "m1k1o/neko" "tarball" msg_info "Building Client" cd /opt/neko/client $STD npm install $STD npm run build cp -r /opt/neko/client/dist/* /var/www/ msg_ok "Built Client" msg_info "Building Server" cd /opt/neko/server $STD ./build cp /opt/neko/server/bin/neko /usr/bin/neko cp -r /opt/neko/server/bin/plugins/* /etc/neko/plugins/ 2>/dev/null || true msg_ok "Built Server" msg_info "Restoring Data" cp /opt/neko.yaml.bak /etc/neko/neko.yaml rm -f /opt/neko.yaml.bak msg_ok "Restored Data" msg_info "Starting Service" systemctl start neko msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed Successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8080${CL}" ================================================ FILE: ct/neo4j.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck | Co-Author: havardthom # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://neo4j.com/product/neo4j-graph-database/ APP="Neo4j" var_tags="${var_tags:-database}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-1024}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /etc/neo4j ]]; then msg_error "No ${APP} Installation Found!" exit fi JAVA_VERSION="21" setup_java msg_info "Updating ${APP}" $STD apt update $STD apt -y upgrade msg_ok "Updated successfully!" exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:7474${CL}" ================================================ FILE: ct/netbird.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: TechHutTV # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://netbird.io/ APP="NetBird" var_tags="${var_tags:-network;vpn}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-512}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" var_tun="${var_tun:-yes}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /var/lib/netbird/ ]]; then msg_error "No ${APP} Installation Found!" exit fi msg_info "Updating Netbird" $STD apt update $STD apt upgrade -y msg_ok "Updated successfully!" exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access NetBird by entering the container and running:${CL}" echo -e "${TAB}${GATEWAY}${BGN}netbird up${CL}" ================================================ FILE: ct/netboot-xyz.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://netboot.xyz APP="netboot.xyz" var_tags="${var_tags:-network;pxe;boot}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-512}" var_disk="${var_disk:-8}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables NSAPP="netboot-xyz" var_install="${NSAPP}-install" color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -f ~/.netboot-xyz ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "netboot-xyz" "netbootxyz/netboot.xyz"; then msg_info "Backing up Configuration" cp /var/www/html/boot.cfg /opt/netboot-xyz-boot.cfg.bak msg_ok "Backed up Configuration" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "netboot-xyz" "netbootxyz/netboot.xyz" "prebuild" "latest" "/var/www/html" "menus.tar.gz" USE_ORIGINAL_FILENAME=true fetch_and_deploy_gh_release "netboot-xyz-efi" "netbootxyz/netboot.xyz" "singlefile" "latest" "/var/www/html" "netboot.xyz.efi" USE_ORIGINAL_FILENAME=true fetch_and_deploy_gh_release "netboot-xyz-efi-dsk" "netbootxyz/netboot.xyz" "singlefile" "latest" "/var/www/html" "netboot.xyz.efi.dsk" USE_ORIGINAL_FILENAME=true fetch_and_deploy_gh_release "netboot-xyz-snp" "netbootxyz/netboot.xyz" "singlefile" "latest" "/var/www/html" "netboot.xyz-snp.efi" USE_ORIGINAL_FILENAME=true fetch_and_deploy_gh_release "netboot-xyz-snp-dsk" "netbootxyz/netboot.xyz" "singlefile" "latest" "/var/www/html" "netboot.xyz-snp.efi.dsk" USE_ORIGINAL_FILENAME=true fetch_and_deploy_gh_release "netboot-xyz-snponly" "netbootxyz/netboot.xyz" "singlefile" "latest" "/var/www/html" "netboot.xyz-snponly.efi" USE_ORIGINAL_FILENAME=true fetch_and_deploy_gh_release "netboot-xyz-metal" "netbootxyz/netboot.xyz" "singlefile" "latest" "/var/www/html" "netboot.xyz-metal.efi" USE_ORIGINAL_FILENAME=true fetch_and_deploy_gh_release "netboot-xyz-metal-dsk" "netbootxyz/netboot.xyz" "singlefile" "latest" "/var/www/html" "netboot.xyz-metal.efi.dsk" USE_ORIGINAL_FILENAME=true fetch_and_deploy_gh_release "netboot-xyz-metal-snp" "netbootxyz/netboot.xyz" "singlefile" "latest" "/var/www/html" "netboot.xyz-metal-snp.efi" USE_ORIGINAL_FILENAME=true fetch_and_deploy_gh_release "netboot-xyz-metal-snp-dsk" "netbootxyz/netboot.xyz" "singlefile" "latest" "/var/www/html" "netboot.xyz-metal-snp.efi.dsk" USE_ORIGINAL_FILENAME=true fetch_and_deploy_gh_release "netboot-xyz-metal-snponly" "netbootxyz/netboot.xyz" "singlefile" "latest" "/var/www/html" "netboot.xyz-metal-snponly.efi" USE_ORIGINAL_FILENAME=true fetch_and_deploy_gh_release "netboot-xyz-kpxe" "netbootxyz/netboot.xyz" "singlefile" "latest" "/var/www/html" "netboot.xyz.kpxe" USE_ORIGINAL_FILENAME=true fetch_and_deploy_gh_release "netboot-xyz-undionly" "netbootxyz/netboot.xyz" "singlefile" "latest" "/var/www/html" "netboot.xyz-undionly.kpxe" USE_ORIGINAL_FILENAME=true fetch_and_deploy_gh_release "netboot-xyz-metal-kpxe" "netbootxyz/netboot.xyz" "singlefile" "latest" "/var/www/html" "netboot.xyz-metal.kpxe" USE_ORIGINAL_FILENAME=true fetch_and_deploy_gh_release "netboot-xyz-lkrn" "netbootxyz/netboot.xyz" "singlefile" "latest" "/var/www/html" "netboot.xyz.lkrn" USE_ORIGINAL_FILENAME=true fetch_and_deploy_gh_release "netboot-xyz-linux-bin" "netbootxyz/netboot.xyz" "singlefile" "latest" "/var/www/html" "netboot.xyz-linux.bin" USE_ORIGINAL_FILENAME=true fetch_and_deploy_gh_release "netboot-xyz-dsk" "netbootxyz/netboot.xyz" "singlefile" "latest" "/var/www/html" "netboot.xyz.dsk" USE_ORIGINAL_FILENAME=true fetch_and_deploy_gh_release "netboot-xyz-pdsk" "netbootxyz/netboot.xyz" "singlefile" "latest" "/var/www/html" "netboot.xyz.pdsk" USE_ORIGINAL_FILENAME=true fetch_and_deploy_gh_release "netboot-xyz-arm64" "netbootxyz/netboot.xyz" "singlefile" "latest" "/var/www/html" "netboot.xyz-arm64.efi" USE_ORIGINAL_FILENAME=true fetch_and_deploy_gh_release "netboot-xyz-arm64-snp" "netbootxyz/netboot.xyz" "singlefile" "latest" "/var/www/html" "netboot.xyz-arm64-snp.efi" USE_ORIGINAL_FILENAME=true fetch_and_deploy_gh_release "netboot-xyz-arm64-snponly" "netbootxyz/netboot.xyz" "singlefile" "latest" "/var/www/html" "netboot.xyz-arm64-snponly.efi" USE_ORIGINAL_FILENAME=true fetch_and_deploy_gh_release "netboot-xyz-metal-arm64" "netbootxyz/netboot.xyz" "singlefile" "latest" "/var/www/html" "netboot.xyz-metal-arm64.efi" USE_ORIGINAL_FILENAME=true fetch_and_deploy_gh_release "netboot-xyz-metal-arm64-snp" "netbootxyz/netboot.xyz" "singlefile" "latest" "/var/www/html" "netboot.xyz-metal-arm64-snp.efi" USE_ORIGINAL_FILENAME=true fetch_and_deploy_gh_release "netboot-xyz-metal-arm64-snponly" "netbootxyz/netboot.xyz" "singlefile" "latest" "/var/www/html" "netboot.xyz-metal-arm64-snponly.efi" USE_ORIGINAL_FILENAME=true fetch_and_deploy_gh_release "netboot-xyz-iso" "netbootxyz/netboot.xyz" "singlefile" "latest" "/var/www/html" "netboot.xyz.iso" USE_ORIGINAL_FILENAME=true fetch_and_deploy_gh_release "netboot-xyz-img" "netbootxyz/netboot.xyz" "singlefile" "latest" "/var/www/html" "netboot.xyz.img" USE_ORIGINAL_FILENAME=true fetch_and_deploy_gh_release "netboot-xyz-arm64-iso" "netbootxyz/netboot.xyz" "singlefile" "latest" "/var/www/html" "netboot.xyz-arm64.iso" USE_ORIGINAL_FILENAME=true fetch_and_deploy_gh_release "netboot-xyz-arm64-img" "netbootxyz/netboot.xyz" "singlefile" "latest" "/var/www/html" "netboot.xyz-arm64.img" USE_ORIGINAL_FILENAME=true fetch_and_deploy_gh_release "netboot-xyz-multiarch-iso" "netbootxyz/netboot.xyz" "singlefile" "latest" "/var/www/html" "netboot.xyz-multiarch.iso" USE_ORIGINAL_FILENAME=true fetch_and_deploy_gh_release "netboot-xyz-multiarch-img" "netbootxyz/netboot.xyz" "singlefile" "latest" "/var/www/html" "netboot.xyz-multiarch.img" USE_ORIGINAL_FILENAME=true fetch_and_deploy_gh_release "netboot-xyz-checksums" "netbootxyz/netboot.xyz" "singlefile" "latest" "/var/www/html" "netboot.xyz-sha256-checksums.txt" msg_info "Restoring Configuration" cp /opt/netboot-xyz-boot.cfg.bak /var/www/html/boot.cfg rm -f /opt/netboot-xyz-boot.cfg.bak msg_ok "Restored Configuration" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed Successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}${CL}" ================================================ FILE: ct/netbox.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: bvdberg01 # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://netboxlabs.com/ APP="NetBox" var_tags="${var_tags:-network}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -f /etc/systemd/system/netbox.service ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "netbox" "netbox-community/netbox"; then msg_info "Stopping Services" systemctl stop netbox netbox-rq msg_ok "Stopped Services" msg_info "Backing up NetBox configurations" mv /opt/netbox/ /opt/netbox-backup msg_ok "Backed up NetBox configurations" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "netbox" "netbox-community/netbox" "tarball" cp -r /opt/netbox-backup/netbox/netbox/configuration.py /opt/netbox/netbox/netbox/ cp -r /opt/netbox-backup/netbox/{media,scripts,reports}/ /opt/netbox/netbox/ cp -r /opt/netbox-backup/gunicorn.py /opt/netbox/ [[ -f /opt/netbox-backup/local_requirements.txt ]] && cp -r /opt/netbox-backup/local_requirements.txt /opt/netbox/ [[ -f /opt/netbox-backup/netbox/netbox/ldap_config.py ]] && cp -r /opt/netbox-backup/netbox/netbox/ldap_config.py /opt/netbox/netbox/netbox/ $STD /opt/netbox/upgrade.sh rm -r /opt/netbox-backup msg_info "Starting Services" systemctl start netbox netbox-rq msg_ok "Started Services" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}https://${IP}${CL}" ================================================ FILE: ct/netvisor.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: vhsdream # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/scanopy/scanopy APP="Scanopy" var_tags="${var_tags:-analytics}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-3072}" var_disk="${var_disk:-6}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/netvisor ]]; then msg_error "No ${APP} Installation Found!" exit fi msg_info "Stopping services" systemctl -q disable --now netvisor-daemon netvisor-server msg_ok "Stopped services" NODE_VERSION="24" setup_nodejs CLEAN_INSTALL=1 fetch_and_deploy_gh_release "scanopy" "scanopy/scanopy" "tarball" "latest" "/opt/scanopy" ensure_dependencies pkg-config libssl-dev TOOLCHAIN="$(grep "channel" /opt/scanopy/backend/rust-toolchain.toml | awk -F\" '{print $2}')" RUST_TOOLCHAIN=$TOOLCHAIN setup_rust mv /opt/netvisor/.env /opt/scanopy/.env if [[ -f /opt/netvisor/oidc.toml ]]; then mv /opt/netvisor/oidc.toml /opt/scanopy/oidc.toml fi if ! grep -q "PUBLIC_URL" /opt/scanopy/.env; then sed -i "\|_PATH=|a\NETVISOR_PUBLIC_URL=http://${LOCAL_IP}:60072" /opt/scanopy/.env fi sed -i 's|_TARGET=.*$|_URL=http://127.0.0.1:60072|' /opt/scanopy/.env sed -i 's/NETVISOR/SCANOPY/g; s|netvisor/|scanopy/|' /opt/scanopy/.env msg_info "Creating frontend UI" export PUBLIC_SERVER_HOSTNAME=default export PUBLIC_SERVER_PORT="" cd /opt/scanopy/ui $STD npm ci --no-fund --no-audit $STD npm run build msg_ok "Created frontend UI" msg_info "Building Scanopy-server (patience)" cd /opt/scanopy/backend $STD cargo build --release --bin server mv ./target/release/server /usr/bin/scanopy-server msg_ok "Built Scanopy-server" msg_info "Building Scanopy-daemon" $STD cargo build --release --bin daemon cp ./target/release/daemon /usr/bin/scanopy-daemon msg_ok "Built Scanopy-daemon" sed -i '/^ \"server_target.*$/d' /root/.config/daemon/config.json sed -i -e 's|-target|-url|' \ -e 's| --server-port |:|' \ -e 's/NetVisor/Scanopy/' \ -e 's/netvisor/scanopy/' \ /etc/systemd/system/netvisor-daemon.service mv /etc/systemd/system/netvisor-daemon.service /etc/systemd/system/scanopy-daemon.service sed -i -e 's/NetVisor/Scanopy/' \ -e 's/netvisor/scanopy/g' \ /etc/systemd/system/netvisor-server.service mv /etc/systemd/system/netvisor-server.service /etc/systemd/system/scanopy-server.service systemctl daemon-reload msg_info "Starting services" systemctl -q enable --now scanopy-server scanopy-daemon msg_ok "Updated successfully!" sed -i 's/netvisor/scanopy/' /usr/bin/update mv ~/NetVisor.creds ~/scanopy.creds rm ~/.netvisor rm -rf /opt/netvisor exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:60072${CL}" echo -e "${INFO}${YW} Then create your account, and run the 'configure_daemon.sh' script to setup the daemon.${CL}" ================================================ FILE: ct/nextcloudpi.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.turnkeylinux.org/nextcloud APP="NextCloudPi" var_tags="${var_tags:-cloud}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-8}" var_os="${var_os:-debian}" var_version="${var_version:-12}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -f /lib/systemd/system/nextcloud-domain.service ]]; then msg_error "No ${APP} Installation Found!" exit fi msg_info "Updating ${APP} LXC" $STD apt update $STD apt -y upgrade msg_ok "Updated successfully!" exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}${CL}" ================================================ FILE: ct/nextexplorer.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: vhsdream # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/nxzai/nextExplorer APP="nextExplorer" var_tags="${var_tags:-files;documents}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-3072}" var_disk="${var_disk:-8}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/nextExplorer ]]; then msg_error "No ${APP} Installation Found!" exit fi NODE_VERSION="24" setup_nodejs if check_for_gh_release "nextExplorer" "nxzai/nextExplorer"; then msg_info "Stopping nextExplorer" $STD systemctl stop nextexplorer msg_ok "Stopped nextExplorer" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "nextExplorer" "nxzai/nextExplorer" "tarball" "latest" "/opt/nextExplorer" msg_info "Updating nextExplorer" APP_DIR="/opt/nextExplorer/app" mkdir -p "$APP_DIR" cd /opt/nextExplorer export NODE_ENV=production $STD npm ci --omit=dev --workspace backend mv node_modules "$APP_DIR" mv backend/{src,package.json} "$APP_DIR" unset NODE_ENV export NODE_ENV=development $STD npm ci --workspace frontend $STD npm run -w frontend build -- --sourcemap false unset NODE_ENV mv frontend/dist/ "$APP_DIR"/src/public chown -R explorer:explorer "$APP_DIR" /etc/nextExplorer sed -i "\|version|s|$(jq -cr '.version' ${APP_DIR}/package.json)|$(cat ~/.nextexplorer)|" "$APP_DIR"/package.json sed -i 's/app.js/server.js/' /etc/systemd/system/nextexplorer.service && systemctl daemon-reload msg_ok "Updated nextExplorer" msg_info "Starting nextExplorer" $STD systemctl start nextexplorer msg_ok "Started nextExplorer" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:3000${CL}" ================================================ FILE: ct/nextpvr.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (Canbiz) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://nextpvr.com/ APP="NextPVR" var_tags="${var_tags:-pvr}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-1024}" var_disk="${var_disk:-5}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" var_gpu="${var_gpu:-yes}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/nextpvr ]]; then msg_error "No ${APP} Installation Found!" exit fi msg_info "Stopping Service" systemctl stop nextpvr-server msg_ok "Stopped Service" msg_info "Updating LXC packages" $STD apt update $STD apt -y upgrade msg_ok "Updated LXC packages" msg_info "Updating ${APP}" cd /opt curl -fsSL "https://nextpvr.com/nextpvr-helper.deb" -o $(basename "https://nextpvr.com/nextpvr-helper.deb") $STD dpkg -i nextpvr-helper.deb rm -rf /opt/nextpvr-helper.deb msg_ok "Updated ${APP}" msg_info "Starting Service" systemctl start nextpvr-server msg_ok "Started Service" msg_ok "Updated successfully!" exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8866${CL}" ================================================ FILE: ct/nginx-ui.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://nginxui.com APP="Nginx-UI" var_tags="${var_tags:-webserver;nginx;proxy}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-512}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -f /usr/local/bin/nginx-ui ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "nginx-ui" "0xJacky/nginx-ui"; then msg_info "Stopping Service" systemctl stop nginx-ui msg_ok "Stopped Service" msg_info "Backing up Configuration" cp /usr/local/etc/nginx-ui/app.ini /tmp/nginx-ui-app.ini.bak msg_ok "Backed up Configuration" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "nginx-ui" "0xJacky/nginx-ui" "prebuild" "latest" "/opt/nginx-ui" "nginx-ui-linux-arm64.tar.gz" msg_info "Updating Binary" cp /opt/nginx-ui/nginx-ui /usr/local/bin/nginx-ui chmod +x /usr/local/bin/nginx-ui rm -rf /opt/nginx-ui msg_ok "Updated Binary" msg_info "Restoring Configuration" mv /tmp/nginx-ui-app.ini.bak /usr/local/etc/nginx-ui/app.ini msg_ok "Restored Configuration" msg_info "Starting Service" systemctl start nginx-ui msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed Successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:9000${CL}" ================================================ FILE: ct/nginxproxymanager.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: tteck (tteckster) | Co-Author: CrazyWolf13 # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://nginxproxymanager.com/ APP="Nginx Proxy Manager" var_tags="${var_tags:-proxy}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-8}" var_os="${var_os:-debian}" var_version="${var_version:-12}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -f /lib/systemd/system/npm.service ]]; then msg_error "No ${APP} Installation Found!" exit fi if command -v node &>/dev/null; then CURRENT_NODE_VERSION=$(node --version | cut -d'v' -f2 | cut -d'.' -f1) if [[ "$CURRENT_NODE_VERSION" != "22" ]]; then systemctl stop openresty $STD apt purge -y nodejs npm $STD apt autoremove -y rm -rf /usr/local/bin/node /usr/local/bin/npm rm -rf /usr/local/lib/node_modules rm -rf ~/.npm rm -rf /root/.npm fi fi NODE_VERSION="22" NODE_MODULE="yarn" setup_nodejs if dpkg -s openresty &>/dev/null 2>&1; then msg_info "Migrating from packaged OpenResty to source" rm -f /etc/apt/trusted.gpg.d/openresty-archive-keyring.gpg /etc/apt/trusted.gpg.d/openresty.gpg rm -f /etc/apt/sources.list.d/openresty.list /etc/apt/sources.list.d/openresty.sources $STD apt purge -y openresty $STD apt autoremove -y rm -f ~/.openresty msg_ok "Migrated from packaged OpenResty to source" fi local pcre_pkg="libpcre3-dev" if grep -qE 'VERSION_ID="1[3-9]"' /etc/os-release 2>/dev/null; then pcre_pkg="libpcre2-dev" fi $STD apt install -y build-essential "$pcre_pkg" libssl-dev zlib1g-dev if check_for_gh_release "openresty" "openresty/openresty"; then CLEAN_INSTALL=1 fetch_and_deploy_gh_release "openresty" "openresty/openresty" "prebuild" "${CHECK_UPDATE_RELEASE}" "/opt/openresty" "openresty-*.tar.gz" msg_info "Building OpenResty" cd /opt/openresty $STD ./configure \ --with-http_v2_module \ --with-http_realip_module \ --with-http_stub_status_module \ --with-http_ssl_module \ --with-http_sub_module \ --with-http_auth_request_module \ --with-pcre-jit \ --with-stream \ --with-stream_ssl_module $STD make -j"$(nproc)" $STD make install rm -rf /opt/openresty cat <<'EOF' >/lib/systemd/system/openresty.service [Unit] Description=The OpenResty Application Platform After=syslog.target network-online.target remote-fs.target nss-lookup.target Wants=network-online.target [Service] Type=simple ExecStartPre=-/bin/mkdir -p /tmp/nginx/body /run/nginx ExecStartPre=/usr/local/openresty/nginx/sbin/nginx -t ExecStart=/usr/local/openresty/nginx/sbin/nginx -g 'daemon off;' [Install] WantedBy=multi-user.target EOF if [ -f /opt/nginxproxymanager/docker/rootfs/etc/nginx/nginx.conf ]; then cp /opt/nginxproxymanager/docker/rootfs/etc/nginx/nginx.conf /usr/local/openresty/nginx/conf/nginx.conf sed -i 's+^daemon+#daemon+g' /usr/local/openresty/nginx/conf/nginx.conf sed -i 's+include conf.d+include /etc/nginx/conf.d+g' /usr/local/openresty/nginx/conf/nginx.conf fi sed -i 's/user npm/user root/g; s/^pid/#pid/g' /usr/local/openresty/nginx/conf/nginx.conf systemctl daemon-reload systemctl unmask openresty 2>/dev/null || true systemctl restart openresty msg_ok "Built OpenResty" fi cd /root if [ -d /opt/certbot ]; then msg_info "Updating Certbot" $STD /opt/certbot/bin/pip install --upgrade pip setuptools wheel $STD /opt/certbot/bin/pip install --upgrade certbot certbot-dns-cloudflare msg_ok "Updated Certbot" fi if check_for_gh_release "nginxproxymanager" "NginxProxyManager/nginx-proxy-manager"; then msg_info "Stopping Services" systemctl stop openresty systemctl stop npm msg_ok "Stopped Services" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "nginxproxymanager" "NginxProxyManager/nginx-proxy-manager" "tarball" "${CHECK_UPDATE_RELEASE}" "/opt/nginxproxymanager" msg_info "Cleaning old files" $STD rm -rf /app \ /var/www/html \ /etc/nginx \ /var/log/nginx \ /var/lib/nginx \ /var/cache/nginx msg_ok "Cleaned old files" local RELEASE="${CHECK_UPDATE_RELEASE#v}" msg_info "Setting up Environment" ln -sf /usr/bin/python3 /usr/bin/python ln -sf /usr/local/openresty/nginx/sbin/nginx /usr/sbin/nginx ln -sf /usr/local/openresty/nginx/ /etc/nginx sed -i "0,/\"version\": \"[^\"]*\"/s|\"version\": \"[^\"]*\"|\"version\": \"$RELEASE\"|" /opt/nginxproxymanager/backend/package.json sed -i "0,/\"version\": \"[^\"]*\"/s|\"version\": \"[^\"]*\"|\"version\": \"$RELEASE\"|" /opt/nginxproxymanager/frontend/package.json sed -i 's+^daemon+#daemon+g' /opt/nginxproxymanager/docker/rootfs/etc/nginx/nginx.conf NGINX_CONFS=$(find /opt/nginxproxymanager -type f -name "*.conf") for NGINX_CONF in $NGINX_CONFS; do sed -i 's+include conf.d+include /etc/nginx/conf.d+g' "$NGINX_CONF" done mkdir -p /var/www/html /etc/nginx/logs cp -r /opt/nginxproxymanager/docker/rootfs/var/www/html/* /var/www/html/ cp -r /opt/nginxproxymanager/docker/rootfs/etc/nginx/* /etc/nginx/ cp /opt/nginxproxymanager/docker/rootfs/etc/letsencrypt.ini /etc/letsencrypt.ini cp /opt/nginxproxymanager/docker/rootfs/etc/logrotate.d/nginx-proxy-manager /etc/logrotate.d/nginx-proxy-manager ln -sf /etc/nginx/nginx.conf /etc/nginx/conf/nginx.conf rm -f /etc/nginx/conf.d/dev.conf mkdir -p /tmp/nginx/body \ /run/nginx \ /data/nginx \ /data/custom_ssl \ /data/logs \ /data/access \ /data/nginx/default_host \ /data/nginx/default_www \ /data/nginx/proxy_host \ /data/nginx/redirection_host \ /data/nginx/stream \ /data/nginx/dead_host \ /data/nginx/temp \ /var/lib/nginx/cache/public \ /var/lib/nginx/cache/private \ /var/cache/nginx/proxy_temp chmod -R 777 /var/cache/nginx chown root /tmp/nginx echo resolver "$(awk 'BEGIN{ORS=" "} $1=="nameserver" {print ($2 ~ ":")? "["$2"]": $2}' /etc/resolv.conf);" >/etc/nginx/conf.d/include/resolvers.conf if [ ! -f /data/nginx/dummycert.pem ] || [ ! -f /data/nginx/dummykey.pem ]; then $STD openssl req -new -newkey rsa:2048 -days 3650 -nodes -x509 -subj "/O=Nginx Proxy Manager/OU=Dummy Certificate/CN=localhost" -keyout /data/nginx/dummykey.pem -out /data/nginx/dummycert.pem fi mkdir -p /app/frontend/images cp -r /opt/nginxproxymanager/backend/* /app msg_ok "Set up Environment" msg_info "Building Frontend" export NODE_OPTIONS="--max_old_space_size=2048 --openssl-legacy-provider" cd /opt/nginxproxymanager/frontend sed -E -i 's/"node-sass" *: *"([^"]*)"/"sass": "\1"/g' package.json $STD yarn install --network-timeout 600000 $STD yarn locale-compile $STD yarn build cp -r /opt/nginxproxymanager/frontend/dist/* /app/frontend cp -r /opt/nginxproxymanager/frontend/public/images/* /app/frontend/images msg_ok "Built Frontend" msg_info "Initializing Backend" rm -rf /app/config/default.json if [ ! -f /app/config/production.json ]; then cat <<'EOF' >/app/config/production.json { "database": { "engine": "knex-native", "knex": { "client": "better-sqlite3", "connection": { "filename": "/data/database.sqlite" }, "useNullAsDefault": true } } } EOF fi sed -i 's/"client": "sqlite3"/"client": "better-sqlite3"/' /app/config/production.json cd /app $STD yarn install --network-timeout 600000 msg_ok "Initialized Backend" msg_info "Starting Services" sed -i 's/user npm/user root/g; s/^pid/#pid/g' /usr/local/openresty/nginx/conf/nginx.conf sed -r -i 's/^([[:space:]]*)su npm npm/\1#su npm npm/g;' /etc/logrotate.d/nginx-proxy-manager systemctl daemon-reload systemctl enable -q --now openresty systemctl enable -q --now npm msg_ok "Started Services" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:81${CL}" ================================================ FILE: ct/nightscout.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: aendel # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/nightscout/cgm-remote-monitor APP="Nightscout" var_tags="${var_tags:-health}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-10}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/nightscout ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "nightscout" "nightscout/cgm-remote-monitor"; then msg_info "Stopping Service" systemctl stop nightscout msg_ok "Stopped Service" fetch_and_deploy_gh_release "nightscout" "nightscout/cgm-remote-monitor" "tarball" msg_info "Updating Nightscout" cd /opt/nightscout $STD npm install msg_ok "Updated Nightscout" msg_info "Starting Service" systemctl start nightscout msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:1337${CL}" ================================================ FILE: ct/nocodb.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.nocodb.com/ APP="NocoDB" var_tags="${var_tags:-noCode}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-1024}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources #RELEASE="0.301.1" if [[ ! -f /etc/systemd/system/nocodb.service ]]; then msg_error "No ${APP} Installation Found!" exit fi #if check_for_gh_release "nocodb" "nocodb/nocodb" "${RELEASE}"; then if check_for_gh_release "nocodb" "nocodb/nocodb"; then msg_info "Stopping Service" systemctl stop nocodb msg_ok "Stopped Service" fetch_and_deploy_gh_release "nocodb" "nocodb/nocodb" "singlefile" "latest" "/opt/nocodb/" "Noco-linux-x64" msg_info "Starting Service" systemctl start nocodb msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8080/dashboard${CL}" ================================================ FILE: ct/node-red.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://nodered.org/ APP="Node-Red" var_tags="${var_tags:-automation}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-1024}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /root/.node-red ]]; then msg_error "No ${APP} Installation Found!" exit fi UPD=$(msg_menu "Node-Red Update Options" \ "1" "Update ${APP}" \ "2" "Install Themes") if [ "$UPD" == "1" ]; then NODE_VERSION="22" setup_nodejs msg_info "Stopping Service" systemctl stop nodered msg_ok "Stopped Service" msg_info "Updating Node-Red" $STD npm install -g --unsafe-perm node-red msg_ok "Updated Node-Red" msg_info "Starting Service" systemctl start nodered msg_ok "Started Service" msg_ok "Updated successfully!" exit fi if [ "$UPD" == "2" ]; then THEME=$(msg_menu "Node-Red Themes" \ "midnight-red" "Midnight Red (default)" \ "aurora" "Aurora" \ "cobalt2" "Cobalt2" \ "dark" "Dark" \ "dracula" "Dracula" \ "espresso-libre" "Espresso Libre" \ "github-dark" "GitHub Dark" \ "github-dark-default" "GitHub Dark Default" \ "github-dark-dimmed" "GitHub Dark Dimmed" \ "monoindustrial" "Monoindustrial" \ "monokai" "Monokai" \ "monokai-dimmed" "Monokai Dimmed" \ "noctis" "Noctis" \ "oceanic-next" "Oceanic Next" \ "oled" "OLED" \ "one-dark-pro" "One Dark Pro" \ "one-dark-pro-darker" "One Dark Pro Darker" \ "solarized-dark" "Solarized Dark" \ "solarized-light" "Solarized Light" \ "tokyo-night" "Tokyo Night" \ "tokyo-night-light" "Tokyo Night Light" \ "tokyo-night-storm" "Tokyo Night Storm" \ "totallyinformation" "TotallyInformation" \ "zenburn" "Zenburn") header_info msg_info "Installing ${THEME} Theme" cd /root/.node-red sed -i 's|// theme: ".*",|theme: "",|g' /root/.node-red/settings.js $STD npm install @node-red-contrib-themes/theme-collection sed -i "{s/theme: ".*"/theme: '${THEME}',/g}" /root/.node-red/settings.js systemctl restart nodered msg_ok "Installed ${THEME} Theme" exit fi } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:1880${CL}" ================================================ FILE: ct/nodebb.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (Canbiz) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://nodebb.org/ APP="NodeBB" var_tags="${var_tags:-forum}" var_disk="${var_disk:-10}" var_cpu="${var_cpu:-4}" var_ram="${var_ram:-2048}" var_os="${var_os:-ubuntu}" var_version="${var_version:-24.04}" var_unprivileged="${var_unprivileged:-1}" # App Output & Base Settings header_info "$APP" # Core variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/nodebb ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "nodebb" "NodeBB/NodeBB"; then msg_info "Stopping Service" systemctl stop nodebb msg_ok "Stopped Service" msg_info "Updating ${APP}" cd /opt/nodebb $STD ./nodebb upgrade echo "${CHECK_UPDATE_RELEASE}" >~/.nodebb msg_ok "Updated ${APP}" msg_info "Starting Service" systemctl start nodebb msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:4567${CL}" ================================================ FILE: ct/nodecast-tv.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: luismco # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/technomancer702/nodecast-tv APP="nodecast-tv" var_tags="${var_tags:-media}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" var_gpu="${var_gpu:-yes}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/nodecast-tv ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "nodecast-tv" "technomancer702/nodecast-tv"; then msg_info "Stopping Service" systemctl stop nodecast-tv msg_ok "Stopped Service" fetch_and_deploy_gh_release "nodecast-tv" "technomancer702/nodecast-tv" "tarball" msg_info "Updating Modules" cd /opt/nodecast-tv $STD npm install msg_ok "Updated Modules" msg_info "Starting Service" systemctl start nodecast-tv msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:3000${CL}" ================================================ FILE: ct/notifiarr.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://notifiarr.com/ APP="Notifiarr" var_tags="${var_tags:-arr}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-512}" var_disk="${var_disk:-2}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -f /usr/lib/systemd/system/notifiarr.service ]]; then msg_error "No ${APP} Installation Found!" exit fi msg_info "Updating Notifiarr" $STD apt update $STD apt upgrade -y msg_ok "Updated Notifiarr" msg_ok "Updated successfully!" exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:5454${CL}" ================================================ FILE: ct/npmplus.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/ZoeyVid/NPMplus APP="NPMplus" var_tags="${var_tags:-proxy;nginx}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-512}" var_disk="${var_disk:-3}" var_os="${var_os:-alpine}" var_version="${var_version:-3.23}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info "$APP" msg_info "Updating Alpine OS" $STD apk -U upgrade msg_ok "System updated" msg_info "Pulling latest NPMplus container image" cd /opt $STD docker compose pull msg_info "Recreating container" $STD docker compose up -d msg_ok "Updated NPMplus container" msg_ok "Updated successfully!" exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}https://${IP}:81${CL}" ================================================ FILE: ct/ntfy.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: CrazyWolf13 # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://ntfy.sh/ APP="ntfy" var_tags="${var_tags:-notification}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-512}" var_disk="${var_disk:-2}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /etc/ntfy ]]; then msg_error "No ${APP} Installation Found!" exit fi if [ -f /etc/apt/keyrings/archive.heckel.io.gpg ]; then msg_info "Correcting old Ntfy Repository" rm -f /etc/apt/keyrings/archive.heckel.io.gpg rm -f /etc/apt/sources.list.d/archive.heckel.io.list rm -f /etc/apt/sources.list.d/archive.heckel.io.list.bak rm -f /etc/apt/sources.list.d/archive.heckel.io.sources setup_deb822_repo \ "ntfy" \ "https://archive.ntfy.sh/apt/keyring.gpg" \ "https://archive.ntfy.sh/apt/" \ "stable" msg_ok "Corrected old Ntfy Repository" fi msg_info "Updating ntfy" $STD apt update $STD apt upgrade -y msg_ok "Updated ntfy" msg_ok "Updated successfully!" exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}${CL}" ================================================ FILE: ct/nxwitness.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://nxvms.com/download/releases/linux APP="NxWitness" var_tags="${var_tags:-nvr}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-8}" var_os="${var_os:-ubuntu}" var_version="${var_version:-24.04}" var_unprivileged="${var_unprivileged:-0}" var_gpu="${var_gpu:-yes}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -f /etc/systemd/system/networkoptix-mediaserver.service ]]; then msg_error "No ${APP} Installation Found!" exit fi BASE_URL="https://updates.networkoptix.com/default/index.html" RELEASE=$(curl -fsSL "$BASE_URL" | grep -oP '(?<=)[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+(?=)' | head -n 1) DETAIL_PAGE=$(curl -fsSL "$BASE_URL#note_$RELEASE") DOWNLOAD_URL=$(echo "$DETAIL_PAGE" | grep -oP "https://updates.networkoptix.com/default/$RELEASE/linux/nxwitness-server-[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+-linux_arm64\.deb" | head -n 1) if [[ ! -f /opt/${APP}_version.txt ]] || [[ "${RELEASE}" != "$(cat /opt/${APP}_version.txt)" ]]; then msg_info "Stopping Service" systemctl stop networkoptix-root-tool networkoptix-mediaserver msg_ok "Stopped Service" msg_info "Updating ${APP} to ${RELEASE}" cd /tmp curl -fsSL "$DOWNLOAD_URL" -o ""nxwitness-server-$RELEASE-linux_arm64.deb"" export DEBIAN_FRONTEND=noninteractive export DEBCONF_NOWARNINGS=yes $STD dpkg -i nxwitness-server-$RELEASE-linux_arm64.deb rm -f /tmp/nxwitness-server-$RELEASE-linux_arm64.deb echo "${RELEASE}" >/opt/${APP}_version.txt msg_ok "Updated ${APP}" msg_info "Starting Service" systemctl start networkoptix-root-tool networkoptix-mediaserver msg_ok "Started Service" msg_ok "Updated successfully!" else msg_ok "No update required. ${APP} is already at ${RELEASE}" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:7001/${CL}" ================================================ FILE: ct/nzbget.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck | Co-Author: havardthom # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://nzbget.com/ APP="NZBGet" var_tags="${var_tags:-usenet;downloader}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -f /lib/systemd/system/nzbget.service ]]; then msg_error "No ${APP} Installation Found!" exit fi if ! command -v unrar &>/dev/null; then setup_nonfree $STD apt install -y unrar if grep -q "UnrarCmd=unrar-free" /var/lib/nzbget/nzbget.conf; then sed -i "s|UnrarCmd=unrar-free|UnrarCmd=unrar|g" /var/lib/nzbget/nzbget.conf fi fi msg_info "Updating NZBGet" $STD apt update $STD apt upgrade -y msg_ok "Updated NZBGet" msg_ok "Updated successfully!" exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:6789${CL}" ================================================ FILE: ct/oauth2-proxy.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: bvdberg01 # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/oauth2-proxy/oauth2-proxy/ APP="oauth2-proxy" var_tags="${var_tags:-os}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-512}" var_disk="${var_disk:-3}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/oauth2-proxy ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "oauth2-proxy" "oauth2-proxy/oauth2-proxy"; then msg_info "Stopping Service" systemctl stop oauth2-proxy msg_ok "Stopped Service" fetch_and_deploy_gh_release "oauth2-proxy" "oauth2-proxy/oauth2-proxy" "prebuild" "latest" "/opt/oauth2-proxy" "oauth2-proxy*linux-arm64.tar.gz" msg_info "Starting Service" systemctl start oauth2-proxy msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Now you can modify /opt/oauth2-proxy/config.toml with your needed config.${CL}" ================================================ FILE: ct/octoprint.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://octoprint.org/ APP="OctoPrint" var_tags="${var_tags:-3d-printing}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-1024}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-0}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/octoprint ]]; then msg_error "No ${APP} Installation Found!" exit fi msg_info "Stopping OctoPrint" systemctl stop octoprint msg_ok "Stopped OctoPrint" msg_info "Updating OctoPrint" source /opt/octoprint/bin/activate $STD pip3 install octoprint --upgrade msg_ok "Updated OctoPrint" msg_info "Starting OctoPrint" systemctl start octoprint msg_ok "Started OctoPrint" msg_ok "Updated successfully!" exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:5000${CL}" ================================================ FILE: ct/odoo.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/odoo/odoo APP="Odoo" var_tags="${var_tags:-erp}" var_disk="${var_disk:-6}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_os="${var_os:-debian}" var_version="${var_version:-12}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -f /etc/odoo/odoo.conf ]]; then msg_error "No ${APP} Installation Found!" exit fi ensure_dependencies python3-lxml if ! [[ $(dpkg -s python3-lxml-html-clean 2>/dev/null) ]]; then curl -fsSL "http://archive.ubuntu.com/ubuntu/pool/universe/l/lxml-html-clean/python3-lxml-html-clean_0.1.1-1_all.deb" -o /opt/python3-lxml-html-clean.deb $STD dpkg -i /opt/python3-lxml-html-clean.deb rm -f /opt/python3-lxml-html-clean.deb fi RELEASE=$(curl -fsSL https://nightly.odoo.com/ | grep -oE 'href="[0-9]+\.[0-9]+/nightly"' | head -n1 | cut -d'"' -f2 | cut -d/ -f1) LATEST_VERSION=$(curl -fsSL "https://nightly.odoo.com/${RELEASE}/nightly/deb/" | grep -oP "odoo_${RELEASE}\.\d+_all\.deb" | sed -E "s/odoo_(${RELEASE}\.[0-9]+)_all\.deb/\1/" | sort -V | tail -n1) if [[ "${LATEST_VERSION}" != "$(cat /opt/${APP}_version.txt)" ]] || [[ ! -f /opt/${APP}_version.txt ]]; then msg_info "Stopping ${APP} service" systemctl stop odoo msg_ok "Stopped Service" msg_info "Updating ${APP} to ${LATEST_VERSION}" curl -fsSL https://nightly.odoo.com/${RELEASE}/nightly/deb/odoo_${RELEASE}.latest_all.deb -o /opt/odoo.deb $STD apt install -y /opt/odoo.deb rm -f /opt/odoo.deb echo "$LATEST_VERSION" >/opt/${APP}_version.txt msg_ok "Updated ${APP} to ${LATEST_VERSION}" msg_info "Starting Service" systemctl start odoo msg_ok "Started Service" msg_ok "Updated successfully!" else msg_ok "No update required. ${APP} is already at ${LATEST_VERSION}" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8069${CL}" ================================================ FILE: ct/ollama.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: havardthom | Co-Author: MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://ollama.com/ APP="Ollama" var_tags="${var_tags:-ai}" var_cpu="${var_cpu:-4}" var_ram="${var_ram:-4096}" var_disk="${var_disk:-40}" var_os="${var_os:-ubuntu}" var_version="${var_version:-24.04}" var_gpu="${var_gpu:-yes}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /usr/local/lib/ollama ]]; then msg_error "No Ollama Installation Found!" exit fi RELEASE=$(curl -fsSL https://api.github.com/repos/ollama/ollama/releases/latest | grep "tag_name" | awk -F '"' '{print $4}') if [[ ! -f /opt/Ollama_version.txt ]] || [[ "${RELEASE}" != "$(cat /opt/Ollama_version.txt)" ]]; then if [[ ! -f /opt/Ollama_version.txt ]]; then touch /opt/Ollama_version.txt fi ensure_dependencies zstd msg_info "Stopping Services" systemctl stop ollama msg_ok "Services Stopped" TMP_TAR=$(mktemp --suffix=.tar.zst) curl -fL# -C - -o "${TMP_TAR}" "https://github.com/ollama/ollama/releases/download/${RELEASE}/ollama-linux-arm64.tar.zst" msg_info "Updating Ollama to ${RELEASE}" rm -rf /usr/local/lib/ollama rm -rf /usr/local/bin/ollama mkdir -p /usr/local/lib/ollama tar --zstd -xf "${TMP_TAR}" -C /usr/local/lib/ollama ln -sf /usr/local/lib/ollama/bin/ollama /usr/local/bin/ollama rm -f "${TMP_TAR}" echo "${RELEASE}" >/opt/Ollama_version.txt msg_ok "Updated Ollama to ${RELEASE}" msg_info "Starting Services" systemctl start ollama msg_ok "Started Services" msg_ok "Updated successfully!" else msg_ok "No update required. Ollama is already at ${RELEASE}" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:11434${CL}" ================================================ FILE: ct/omada.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.tp-link.com/us/support/download/omada-software-controller/ APP="Omada" var_tags="${var_tags:-tp-link;controller}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-3072}" var_disk="${var_disk:-8}" var_os="${var_os:-debian}" var_version="${var_version:-12}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/tplink ]]; then msg_error "No ${APP} Installation Found!" exit fi msg_info "Updating MongoDB" if lscpu | grep -q 'avx'; then MONGO_VERSION="8.0" else msg_error "No AVX detected (CPU-Flag)! We have discontinued support for this. You are welcome to try it manually with a Debian LXC, but due to the many issues with Omada, we currently only support AVX CPUs." exit 10 fi JAVA_VERSION="21" setup_java OMADA_URL=$(curl -fsSL "https://support.omadanetworks.com/en/download/software/omada-controller/" | grep -o 'https://static\.tp-link\.com/upload/software/[^"]*linux_x64[^"]*\.deb' | head -n1) OMADA_PKG=$(basename "${OMADA_URL}") VERSION=$(sed -n 's/.*_v\([0-9.]*\)_.*_\([0-9]\{14\}\)\.deb$/\1-\2/p' <<<"${OMADA_PKG}") CURRENT_VERSION=$(cat $HOME/.omada 2>/dev/null || echo "0") if dpkg --compare-versions "${VERSION}" gt "${CURRENT_VERSION}"; then msg_info "Updating Omada Controller" if [ -z "${OMADA_PKG}" ]; then msg_error "Could not retrieve Omada package – server may be down." exit fi curl -fsSL "${OMADA_URL}" -o "${OMADA_PKG}" export DEBIAN_FRONTEND=noninteractive $STD dpkg -i "${OMADA_PKG}" rm -f "${OMADA_PKG}" echo "${VERSION}" >$HOME/.omada msg_ok "Updated Omada Controller to ${VERSION}" msg_ok "Updated successfully!" else msg_ok "No update available: ${APP} (${CURRENT_VERSION})" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}https://${IP}:8043${CL}" ================================================ FILE: ct/ombi.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://ombi.io/ APP="Ombi" var_tags="${var_tags:-media}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-1024}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/ombi ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "ombi" "Ombi-app/Ombi"; then msg_info "Stopping Service" systemctl stop ombi msg_ok "Stopped Service" msg_info "Creating backup" [[ -f /opt/ombi/Ombi.db ]] && mv /opt/ombi/Ombi.db /opt [[ -f /opt/ombi/OmbiExternal.db ]] && mv /opt/ombi/OmbiExternal.db /opt [[ -f /opt/ombi/OmbiSettings.db ]] && mv /opt/ombi/OmbiSettings.db /opt [[ -f /opt/ombi/database.json ]] && mv /opt/ombi/database.json /opt msg_ok "Backup created" rm -rf /opt/ombi fetch_and_deploy_gh_release "ombi" "Ombi-app/Ombi" "prebuild" "latest" "/opt/ombi" "linux-x64.tar.gz" [[ -f /opt/Ombi.db ]] && mv /opt/Ombi.db /opt/ombi [[ -f /opt/OmbiExternal.db ]] && mv /opt/OmbiExternal.db /opt/ombi [[ -f /opt/OmbiSettings.db ]] && mv /opt/OmbiSettings.db /opt/ombi [[ -f /opt/database.json ]] && mv /opt/database.json /opt/ombi msg_info "Starting Service" systemctl start ombi msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:5000${CL}" ================================================ FILE: ct/omv.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.openmediavault.org/ APP="OMV" var_tags="${var_tags:-media}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-1024}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-12}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -f /etc/apt/sources.list.d/openmediavault.list ]]; then msg_error "No ${APP} Installation Found!" exit fi msg_info "Updating ${APP} LXC" $STD apt update $STD apt -y upgrade msg_ok "Updated successfully!" exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}${CL}" ================================================ FILE: ct/onedev.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: kristocopani # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://onedev.io/ APP="OneDev" var_tags="${var_tags:-git}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -f /etc/systemd/system/onedev.service ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "onedev" "theonedev/onedev"; then JAVA_VERSION="21" setup_java msg_info "Stopping Service" systemctl stop onedev msg_ok "Stopped Service" msg_info "Updating OneDev" cd /opt curl -fsSL "https://code.onedev.io/onedev/server/~site/onedev-latest.tar.gz" -o onedev-latest.tar.gz tar -xzf onedev-latest.tar.gz $STD /opt/onedev-latest/bin/upgrade.sh /opt/onedev rm -rf /opt/onedev-latest rm -rf /opt/onedev-latest.tar.gz echo "${CHECK_UPDATE_RELEASE}" >~/.onedev msg_ok "Updated OneDev" msg_info "Starting Service" systemctl start onedev msg_ok "Started Service" msg_ok "Updated successfully!" exit fi } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:6610${CL}" ================================================ FILE: ct/onlyoffice.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.onlyoffice.com/ APP="ONLYOFFICE" var_tags="${var_tags:-word;excel;powerpoint;pdf}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-10}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /var/www/onlyoffice ]]; then msg_error "No valid ${APP} installation found!" exit fi msg_info "Updating OnlyOffice Document Server" $STD apt update $STD apt -y --only-upgrade install onlyoffice-documentserver msg_ok "Updated OnlyOffice Document Server" if systemctl is-enabled --quiet onlyoffice-documentserver; then msg_info "Restarting OnlyOffice Document Server" $STD systemctl restart onlyoffice-documentserver msg_ok "OnlyOffice Document Server restarted" fi msg_ok "Updated successfully!" exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}${CL}" ================================================ FILE: ct/open-archiver.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Slaviša Arežina (tremor021) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://openarchiver.com/ APP="Open-Archiver" var_tags="${var_tags:-os}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-3072}" var_disk="${var_disk:-8}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/openarchiver ]]; then msg_error "No Open Archiver Installation Found!" exit fi setup_meilisearch if check_for_gh_release "openarchiver" "LogicLabs-OU/OpenArchiver"; then msg_info "Stopping Services" systemctl stop openarchiver msg_ok "Stopped Services" cp /opt/openarchiver/.env /opt/openarchiver.env CLEAN_INSTALL=1 fetch_and_deploy_gh_release "openarchiver" "LogicLabs-OU/OpenArchiver" "tarball" mv /opt/openarchiver.env /opt/openarchiver/.env msg_info "Updating Open Archiver" cd /opt/openarchiver $STD pnpm install --shamefully-hoist --frozen-lockfile --prod=false $STD pnpm run build:oss $STD pnpm db:migrate msg_ok "Updated Open Archiver" if grep -q '^ExecStart=/usr/bin/pnpm docker-start$' /etc/systemd/system/openarchiver.service; then sed -i 's|^ExecStart=/usr/bin/pnpm docker-start$|ExecStart=/usr/bin/pnpm docker-start:oss|' /etc/systemd/system/openarchiver.service systemctl daemon-reload fi msg_info "Starting Services" systemctl start openarchiver msg_ok "Started Services" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:3000${CL}" ================================================ FILE: ct/opencloud.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: vhsdream # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://opencloud.eu APP="OpenCloud" var_tags="${var_tags:-files;cloud}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-20}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /etc/opencloud ]]; then msg_error "No ${APP} Installation Found!" exit fi RELEASE="v6.1.0" if check_for_gh_release "OpenCloud" "opencloud-eu/opencloud" "${RELEASE}" "each release is tested individually before the version is updated. Please do not open issues for this"; then msg_info "Stopping services" systemctl stop opencloud opencloud-wopi msg_ok "Stopped services" msg_info "Updating packages" $STD apt-get update $STD apt-get dist-upgrade -y ensure_dependencies "inotify-tools" msg_ok "Updated packages" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "OpenCloud" "opencloud-eu/opencloud" "singlefile" "${RELEASE}" "/usr/bin" "opencloud-*-linux-arm64" if ! grep -q 'POSIX_WATCH' /etc/opencloud/opencloud.env; then sed -i '/^## External/i ## Uncomment below to enable PosixFS Collaborative Mode\ ## Increase inotify watch/instance limits on your PVE host:\ ### sysctl -w fs.inotify.max_user_watches=1048576\ ### sysctl -w fs.inotify.max_user_instances=1024\ # STORAGE_USERS_POSIX_ENABLE_COLLABORATION=true\ # STORAGE_USERS_POSIX_WATCH_TYPE=inotifywait\ # STORAGE_USERS_POSIX_WATCH_FS=true\ # STORAGE_USERS_POSIX_WATCH_PATH=' /etc/opencloud/opencloud.env fi msg_info "Starting services" systemctl start opencloud opencloud-wopi msg_ok "Started services" msg_ok "Updated successfully" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}https://${CL}" ================================================ FILE: ct/opengist.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Jonathan (jd-apprentice) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://opengist.io/ APP="Opengist" var_tags="${var_tags:-development}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-1024}" var_disk="${var_disk:-8}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/opengist ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "opengist" "thomiceli/opengist"; then msg_info "Stopping Service" systemctl stop opengist msg_ok "Stopped Service" msg_info "Creating backup" mv /opt/opengist /opt/opengist-backup msg_ok "Backup created" fetch_and_deploy_gh_release "opengist" "thomiceli/opengist" "prebuild" "latest" "/opt/opengist" "opengist*linux-arm64.tar.gz" msg_info "Restoring Configuration" mv /opt/opengist-backup/config.yml /opt/opengist/config.yml msg_ok "Configuration Restored" msg_info "Starting Service" systemctl start opengist msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:6157${CL}" ================================================ FILE: ct/openhab.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.openhab.org/ APP="openHAB" var_tags="${var_tags:-automation}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-8}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -f /usr/lib/systemd/system/openhab.service ]]; then msg_error "No ${APP} Installation Found!" exit fi msg_info "Updating ${APP} LXC" $STD apt update $STD apt upgrade -y msg_ok "Updated successfully!" exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using one of the following URLs:${CL}" echo -e "${TAB}${GATEWAY}${BGN}https://${IP}:8443${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8080${CL}" ================================================ FILE: ct/openobserve.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://openobserve.ai/ APP="OpenObserve" var_tags="${var_tags:-monitoring}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-512}" var_disk="${var_disk:-3}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/openobserve/ ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "openobserve" "openobserve/openobserve"; then msg_info "Updating OpenObserve" systemctl stop openobserve RELEASE=$(get_latest_github_release "openobserve/openobserve") tar zxf <(curl -fsSL https://downloads.openobserve.ai/releases/openobserve/v$RELEASE/openobserve-v$RELEASE-linux-arm64.tar.gz) -C /opt/openobserve systemctl start openobserve msg_ok "Updated OpenObserve" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:5080${CL}" ================================================ FILE: ct/openproject.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: michelroegl-brunner # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/opf/openproject APP="OpenProject" var_tags="${var_tags:-project-management;erp}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-4096}" var_disk="${var_disk:-8}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -f /etc/openproject/installer.dat ]]; then msg_error "No ${APP} Installation Found!" exit fi msg_info "Updating OpenProject" $STD apt update $STD apt install --only-upgrade -y openproject msg_ok "Updated OpenProject" msg_ok "Updated successfully!" exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}/openproject${CL}" ================================================ FILE: ct/openthread-br.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://openthread.io/guides/border-router APP="OpenThread-BR" var_tags="${var_tags:-thread;iot;border-router;matter}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-0}" var_tun="${var_tun:-yes}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/ot-br-posix ]]; then msg_error "No ${APP} Installation Found!" exit fi cd /opt/ot-br-posix LOCAL_COMMIT=$(git rev-parse HEAD) $STD git fetch --depth 1 origin main REMOTE_COMMIT=$(git rev-parse origin/main) if [[ "${LOCAL_COMMIT}" == "${REMOTE_COMMIT}" ]]; then msg_ok "Already up to date (${LOCAL_COMMIT:0:7})" exit fi msg_info "Stopping Services" systemctl stop otbr-web systemctl stop otbr-agent msg_ok "Stopped Services" msg_info "Updating Source" $STD git reset --hard origin/main $STD git submodule update --depth 1 --init --recursive msg_ok "Updated Source" msg_info "Rebuilding OpenThread Border Router (Patience)" cd /opt/ot-br-posix/build $STD cmake -GNinja \ -DBUILD_TESTING=OFF \ -DCMAKE_INSTALL_PREFIX=/usr \ -DOTBR_DBUS=ON \ -DOTBR_MDNS=openthread \ -DOTBR_REST=ON \ -DOTBR_WEB=ON \ -DOTBR_BORDER_ROUTING=ON \ -DOTBR_BACKBONE_ROUTER=ON \ -DOT_FIREWALL=ON \ -DOT_POSIX_NAT64_CIDR="192.168.255.0/24" \ .. $STD ninja $STD ninja install msg_ok "Rebuilt OpenThread Border Router" msg_info "Starting Services" systemctl start otbr-agent systemctl start otbr-web msg_ok "Started Services" msg_ok "Updated successfully!" exit } start build_container description msg_ok "Completed Successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}${CL}" ================================================ FILE: ct/openwebui.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck | Co-Author: havardthom | Co-Author: Slaviša Arežina (tremor021) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://openwebui.com/ APP="Open WebUI" var_tags="${var_tags:-ai;interface}" var_cpu="${var_cpu:-4}" var_ram="${var_ram:-8192}" var_disk="${var_disk:-50}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" var_gpu="${var_gpu:-yes}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources ensure_dependencies zstd build-essential libmariadb-dev if [[ -d /opt/open-webui ]]; then msg_warn "Legacy installation detected — migrating to uv based install..." msg_info "Stopping Service" systemctl stop open-webui msg_ok "Stopped Service" msg_info "Creating Backup" mkdir -p /opt/open-webui-backup cp -a /opt/open-webui/backend/data /opt/open-webui-backup/data || true cp -a /opt/open-webui/.env /opt/open-webui-backup/.env || true msg_ok "Created Backup" msg_info "Removing legacy installation" rm -rf /opt/open-webui rm -rf /root/.open-webui || true msg_ok "Removed legacy installation" msg_info "Installing uv-based Open-WebUI" PYTHON_VERSION="3.12" setup_uv $STD uv tool install --python 3.12 --constraint <(echo "numba>=0.60") open-webui[all] msg_ok "Installed uv-based Open-WebUI" msg_info "Restoring data" mkdir -p /root/.open-webui cp -a /opt/open-webui-backup/data/* /root/.open-webui/ || true cp -a /opt/open-webui-backup/.env /root/.env || true rm -rf /opt/open-webui-backup || true msg_ok "Restored data" msg_info "Recreating Service" cat </etc/systemd/system/open-webui.service [Unit] Description=Open WebUI Service After=network.target [Service] Type=simple Environment=DATA_DIR=/root/.open-webui EnvironmentFile=-/root/.env ExecStart=/root/.local/bin/open-webui serve WorkingDirectory=/root Restart=on-failure RestartSec=5 User=root [Install] WantedBy=multi-user.target EOF $STD systemctl daemon-reload systemctl enable -q --now open-webui msg_ok "Recreated Service" msg_ok "Migration completed" exit 0 fi if [[ ! -d /root/.open-webui ]]; then msg_error "No ${APP} Installation Found!" exit fi if [ -x "/usr/bin/ollama" ]; then msg_info "Checking for Ollama Update" OLLAMA_VERSION=$(ollama -v | awk '{print $NF}') RELEASE=$(curl -s https://api.github.com/repos/ollama/ollama/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4)}') if [ "$OLLAMA_VERSION" != "$RELEASE" ]; then msg_info "Ollama update available: v$OLLAMA_VERSION -> v$RELEASE" msg_info "Downloading Ollama v$RELEASE \n" curl -fS#LO https://github.com/ollama/ollama/releases/download/v${RELEASE}/ollama-linux-arm64.tar.zst msg_ok "Download Complete" if [ -f "ollama-linux-arm64.tar.zst" ]; then msg_info "Stopping Ollama Service" systemctl stop ollama msg_ok "Stopped Service" msg_info "Installing Ollama" rm -rf /usr/lib/ollama rm -rf /usr/bin/ollama tar --zstd -C /usr -xf ollama-linux-arm64.tar.zst rm -rf ollama-linux-arm64.tar.zst msg_ok "Installed Ollama" msg_info "Starting Ollama Service" systemctl start ollama msg_ok "Started Service" msg_ok "Ollama updated to version $RELEASE" else msg_error "Ollama download failed. Aborting update." fi else msg_ok "Ollama is already up to date." fi fi msg_info "Updating Open WebUI via uv" PYTHON_VERSION="3.12" setup_uv $STD uv tool install --force --python 3.12 --constraint <(echo "numba>=0.60") open-webui[all] systemctl restart open-webui msg_ok "Updated Open WebUI" msg_ok "Updated successfully!" exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8080${CL}" ================================================ FILE: ct/openziti-controller.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: emoscardini # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/openziti/ziti APP="openziti-controller" var_tags="${var_tags:-network;openziti-controller}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-1024}" var_disk="${var_disk:-8}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/openziti ]]; then msg_error "No ${APP} Installation Found!" exit fi msg_info "Updating $APP LXC" $STD apt update $STD apt -y upgrade msg_ok "Updated $APP LXC" msg_ok "Updated successfully!" exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}https://${IP}:/zac${CL}" ================================================ FILE: ct/openziti-tunnel.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: emoscardini # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/openziti/ziti APP="openziti-tunnel" var_tags="${var_tags:-network;openziti-tunnel}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-512}" var_disk="${var_disk:-2}" var_os="${var_os:-ubuntu}" var_version="${var_version:-24.04}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/openziti ]]; then msg_error "No ${APP} Installation Found!" exit fi msg_info "Updating $APP LXC" $STD apt update $STD apt -y upgrade msg_ok "Updated $APP LXC" msg_ok "Updated successfully!" exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Application was assigned the following IP:${CL}" echo -e "${TAB}${GATEWAY}${BGN}Address: ${IP}${CL}" ================================================ FILE: ct/ots.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: bvdberg01 # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/Luzifer/ots APP="OTS" var_tags="${var_tags:-secrets-sharer}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-512}" var_disk="${var_disk:-3}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/ots ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "ots" "Luzifer/ots"; then msg_info "Stopping Services" systemctl stop ots systemctl stop nginx msg_ok "Stopped Services" fetch_and_deploy_gh_release "ots" "Luzifer/ots" "prebuild" "latest" "/opt/ots" "ots_linux_arm64.tgz" msg_info "Starting Services" systemctl start ots systemctl start nginx msg_ok "Started Services" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}https://${IP}${CL}" ================================================ FILE: ct/outline.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Slaviša Arežina (tremor021) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/outline/outline APP="Outline" var_tags="${var_tags:-documentation}" var_disk="${var_disk:-8}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-4096}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/outline ]]; then msg_error "No ${APP} Installation Found!" exit fi NODE_VERSION="24" setup_nodejs if check_for_gh_release "outline" "outline/outline"; then msg_info "Stopping Services" systemctl stop outline msg_ok "Services Stopped" msg_info "Creating backup" cp /opt/outline/.env /opt msg_ok "Backup created" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "outline" "outline/outline" "tarball" msg_info "Updating Outline" cd /opt/outline mv /opt/.env /opt/outline export NODE_ENV=development export NODE_OPTIONS="--max-old-space-size=3584" export COREPACK_ENABLE_DOWNLOAD_PROMPT=0 $STD corepack enable $STD yarn install --immutable export NODE_ENV=production $STD yarn build msg_ok "Updated Outline" msg_info "Starting Services" systemctl start outline msg_ok "Started Services" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:3000${CL}" ================================================ FILE: ct/overseerr.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://overseerr.dev/ APP="Overseerr" var_tags="${var_tags:-media}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-4096}" var_disk="${var_disk:-8}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/overseerr ]]; then msg_error "No ${APP} Installation Found!" exit fi if [[ -f "$HOME/.overseerr" ]] && [[ "$(printf '%s\n' "1.35.0" "$(cat "$HOME/.overseerr")" | sort -V | head -n1)" == "1.35.0" ]]; then echo echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "Overseerr v1.34.0 detected." echo echo "Seerr is the new unified Jellyseerr and Overseerr." echo "More info: https://docs.seerr.dev/blog/seerr-release" echo read -rp "Do you want to migrate to Seerr now? (y/N): " MIGRATE echo if [[ ! "$MIGRATE" =~ ^[Yy]$ ]]; then msg_info "Migration cancelled. Exiting." exit 0 fi msg_info "Switching update script to Seerr" TMP_UPDATE=$(mktemp) cat <<'EOF' >"$TMP_UPDATE" bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/seerr.sh)" EOF mv "$TMP_UPDATE" /usr/bin/update chmod +x /usr/bin/update msg_ok "Switched update script to Seerr" msg_warn "Please type 'update' again to complete the migration" exit 0 fi if check_for_gh_release "overseerr" "sct/overseerr"; then msg_info "Stopping Service" systemctl stop overseerr msg_ok "Service stopped" msg_info "Creating backup" mv /opt/overseerr/config /opt/config_backup msg_ok "Backup created" fetch_and_deploy_gh_release "overseerr" "sct/overseerr" "tarball" rm -rf /opt/overseerr/config msg_info "Configuring ${APP} (Patience)" cd /opt/overseerr $STD yarn install $STD yarn build mv /opt/config_backup /opt/overseerr/config msg_ok "Configured ${APP}" msg_info "Starting Service" systemctl start overseerr msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:5055${CL}" ================================================ FILE: ct/owncast.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://owncast.online/ APP="Owncast" var_tags="${var_tags:-broadcasting}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-10}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" var_gpu="${var_gpu:-yes}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/owncast ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "owncast" "owncast/owncast"; then msg_info "Stopping Service" systemctl stop owncast msg_ok "Stopped Service" fetch_and_deploy_gh_release "owncast" "owncast/owncast" "prebuild" "latest" "/opt/owncast" "owncast*linux-64bit.zip" msg_info "Starting Service" systemctl start owncast msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8080/admin${CL}" ================================================ FILE: ct/ownfoil.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: pajjski # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/a1ex4/ownfoil APP="ownfoil" var_tags="${var_tags:-gaming}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-1024}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/ownfoil ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "ownfoil" "a1ex4/ownfoil"; then msg_info "Stopping Service" systemctl stop ownfoil msg_ok "Stopped Service" msg_info "Backing up Data" cp -r /opt/ownfoil/app/config /opt/ownfoil_data_backup msg_ok "Backed up Data" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "ownfoil" "a1ex4/ownfoil" "tarball" msg_info "Installing Dependencies" cd /opt/ownfoil $STD source .venv/bin/activate $STD uv pip install -r requirements.txt msg_ok "Installed Dependencies" msg_info "Restoring Data" cp -r /opt/ownfoil_data_backup /opt/ownfoil/app/config rm -rf /opt/ownfoil_data_backup msg_ok "Restored Data" msg_info "Starting Service" systemctl start ownfoil msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8465${CL}" ================================================ FILE: ct/pairdrop.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://pairdrop.net/ APP="PairDrop" var_tags="${var_tags:-sharing}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-512}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/pairdrop ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "pairdrop" "schlagmichdoch/PairDrop"; then msg_info "Stopping Service" systemctl stop pairdrop msg_ok "Stopped Service" fetch_and_deploy_gh_release "pairdrop" "schlagmichdoch/PairDrop" "tarball" msg_info "Configuring PairDrop" cd /opt/pairdrop $STD npm install msg_ok "Configured PairDrop" msg_info "Starting Service" systemctl start pairdrop msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:3000${CL}" ================================================ FILE: ct/pangolin.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Slaviša Arežina (tremor021) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://pangolin.net/ APP="Pangolin" PANGOLIN_VERSION="${PANGOLIN_VERSION:-1.18.3}" var_tags="${var_tags:-proxy}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-4096}" var_disk="${var_disk:-10}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" var_tun="${var_tun:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/pangolin ]]; then msg_error "No ${APP} Installation Found!" exit fi ensure_dependencies build-essential python3 NODE_VERSION="24" setup_nodejs if check_for_gh_release "pangolin" "fosrl/pangolin" "$PANGOLIN_VERSION" "Pinned to a tested release because Pangolin's schema changes have repeatedly broken unattended updates. To try a newer version at your own risk, run: 'export PANGOLIN_VERSION=' and re-run update. If it breaks, please open an issue at https://github.com/community-scripts/ProxmoxVE/issues with the error log."; then msg_info "Stopping Service" systemctl stop pangolin systemctl stop gerbil msg_info "Service stopped" msg_info "Creating backup" tar -czf /opt/pangolin_config_backup.tar.gz -C /opt/pangolin config if [[ -f /opt/pangolin/config/db/db.sqlite ]]; then cp -a /opt/pangolin/config/db/db.sqlite \ "/opt/pangolin/config/db/db.sqlite.pre-${PANGOLIN_VERSION}-$(date +%Y%m%d-%H%M%S).bak" fi msg_ok "Created backup" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "pangolin" "fosrl/pangolin" "tarball" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "gerbil" "fosrl/gerbil" "singlefile" "latest" "/usr/bin" "gerbil_linux_arm64" msg_info "Updating Pangolin" cd /opt/pangolin $STD npm ci $STD npm run set:sqlite $STD npm run set:oss rm -rf server/private $STD npm run db:generate $STD npm run build $STD npm run build:cli cp -R .next/standalone ./ chmod +x ./dist/cli.mjs cp server/db/names.json ./dist/names.json cp server/db/ios_models.json ./dist/ios_models.json cp server/db/mac_models.json ./dist/mac_models.json msg_ok "Updated Pangolin" msg_info "Restoring config" tar -xzf /opt/pangolin_config_backup.tar.gz -C /opt/pangolin --overwrite rm -f /opt/pangolin_config_backup.tar.gz msg_ok "Restored config" if ! grep -q '^ExecStartPre=/usr/bin/node dist/migrations.mjs' /etc/systemd/system/pangolin.service 2>/dev/null; then msg_info "Adding migration step to pangolin.service" sed -i '/^ExecStart=\/usr\/bin\/node --enable-source-maps dist\/server.mjs/i ExecStartPre=/usr/bin/node dist/migrations.mjs' /etc/systemd/system/pangolin.service systemctl daemon-reload msg_ok "Updated pangolin.service" fi msg_info "Running database migrations" cd /opt/pangolin ENVIRONMENT=prod $STD node dist/migrations.mjs msg_ok "Ran database migrations" msg_info "Updating Badger plugin version" BADGER_VERSION=$(get_latest_github_release "fosrl/badger" "false") sed -i "s/version: \"v[0-9.]*\"/version: \"$BADGER_VERSION\"/g" /opt/pangolin/config/traefik/traefik_config.yml msg_ok "Updated Badger plugin version" msg_info "Starting Services" systemctl start pangolin systemctl start gerbil msg_ok "Started Services" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}https://${CL}" ================================================ FILE: ct/paperless-ai.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/clusterzx/paperless-ai APP="Paperless-AI" var_tags="${var_tags:-ai;document}" var_cpu="${var_cpu:-4}" var_ram="${var_ram:-4096}" var_disk="${var_disk:-20}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/paperless-ai ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "paperless-ai" "clusterzx/paperless-ai"; then msg_info "Stopping Service" systemctl stop paperless-ai paperless-rag msg_ok "Stopped Service" msg_info "Backing up data" cp -r /opt/paperless-ai/data /opt/paperless-ai-data-backup msg_ok "Backed up data" fetch_and_deploy_gh_release "paperless-ai" "clusterzx/paperless-ai" "tarball" msg_info "Restoring data" cp -r /opt/paperless-ai-data-backup/* /opt/paperless-ai/data/ rm -rf /opt/paperless-ai-data-backup msg_ok "Restored data" msg_info "Updating Paperless-AI" cd /opt/paperless-ai if [[ ! -d /opt/paperless-ai/venv ]]; then msg_info "Recreating Python venv" $STD python3 -m venv /opt/paperless-ai/venv fi source /opt/paperless-ai/venv/bin/activate $STD pip install --upgrade pip $STD pip install --no-cache-dir -r requirements.txt mkdir -p data/chromadb $STD npm ci --only=production msg_ok "Updated Paperless-AI" msg_info "Starting Service" systemctl start paperless-rag sleep 3 systemctl start paperless-ai msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:3000${CL}" ================================================ FILE: ct/paperless-gpt.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/icereed/paperless-gpt APP="Paperless-GPT" var_tags="${var_tags:-os}" var_cpu="${var_cpu:-3}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-7}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/paperless-gpt ]]; then msg_error "No Paperless-GPT installation found!" exit fi if check_for_gh_release "paperless-gpt" "icereed/paperless-gpt"; then msg_info "Stopping Service" systemctl stop paperless-gpt msg_ok "Service Stopped" if should_update_tool "node" "24"; then NODE_VERSION="24" setup_nodejs fi fetch_and_deploy_gh_release "paperless-gpt" "icereed/paperless-gpt" "tarball" msg_info "Updating Paperless-GPT" cd /opt/paperless-gpt/web-app $STD npm install $STD npm run build cd /opt/paperless-gpt go mod download export CC=musl-gcc CGO_ENABLED=1 go build -tags musl -o /dev/null github.com/mattn/go-sqlite3 CGO_ENABLED=1 go build -tags musl -o paperless-gpt . msg_ok "Updated Paperless-GPT" msg_info "Starting Service" systemctl start paperless-gpt msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8080${CL}" ================================================ FILE: ct/paperless-ngx.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://docs.paperless-ngx.com/ APP="Paperless-ngx" var_tags="${var_tags:-document;management}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-3072}" var_disk="${var_disk:-12}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/paperless ]]; then msg_error "No ${APP} Installation Found!" exit fi # Check for old data structure and prompt migration (exclude symlinks) if [[ -f /opt/paperless/paperless.conf ]]; then local OLD_DIRS=() [[ -d /opt/paperless/consume && ! -L /opt/paperless/consume ]] && OLD_DIRS+=("consume") [[ -d /opt/paperless/data && ! -L /opt/paperless/data ]] && OLD_DIRS+=("data") [[ -d /opt/paperless/media && ! -L /opt/paperless/media ]] && OLD_DIRS+=("media") if [[ ${#OLD_DIRS[@]} -gt 0 ]]; then msg_error "Old data structure detected in /opt/paperless/" msg_custom "📂" "Found directories: ${OLD_DIRS[*]}" echo -e "" msg_custom "🔄" "Migration required to new data structure (/opt/paperless_data/)" msg_custom "📖" "Please follow the migration guide:" echo -e "${TAB}${GATEWAY}${BGN}https://github.com/community-scripts/ProxmoxVE/discussions/9223${CL}" echo -e "" msg_custom "⚠️" "Update aborted. Please migrate your data first." exit 253 fi fi if check_for_gh_release "paperless" "paperless-ngx/paperless-ngx"; then msg_info "Stopping all Paperless-ngx Services" systemctl stop paperless-consumer paperless-webserver paperless-scheduler paperless-task-queue msg_ok "Stopped all Paperless-ngx Services" if grep -q "uv run" /etc/systemd/system/paperless-webserver.service; then msg_info "Backing up configuration" local BACKUP_DIR="/opt/paperless_backup_$$" mkdir -p "$BACKUP_DIR" [[ -f /opt/paperless/paperless.conf ]] && cp /opt/paperless/paperless.conf "$BACKUP_DIR/" msg_ok "Backup completed to $BACKUP_DIR" PYTHON_VERSION="3.13" setup_uv CLEAN_INSTALL=1 fetch_and_deploy_gh_release "paperless" "paperless-ngx/paperless-ngx" "prebuild" "latest" "/opt/paperless" "paperless*tar.xz" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "jbig2enc" "ie13/jbig2enc" "tarball" "latest" "/opt/jbig2enc" . /etc/os-release if [ "$VERSION_CODENAME" = "bookworm" ]; then setup_gs else ensure_dependencies ghostscript fi msg_info "Updating Paperless-ngx" cp -r "$BACKUP_DIR"/* /opt/paperless/ cd /opt/paperless $STD uv sync --all-extras cd /opt/paperless/src $STD uv run -- python manage.py migrate msg_ok "Updated Paperless-ngx" rm -rf "$BACKUP_DIR" else msg_warn "You are about to migrate your Paperless-ngx installation to uv!" msg_custom "🔒" "It is strongly recommended to take a Proxmox snapshot first:" echo -e " 1. Stop the container: pct stop " echo -e " 2. Create a snapshot: pct snapshot pre-paperless-uv-migration" echo -e " 3. Start the container again\n" read -rp "Have you created a snapshot? [y/N]: " confirm if [[ ! "$confirm" =~ ^([yY]|[yY][eE][sS])$ ]]; then msg_error "Migration aborted. Please create a snapshot first." exit fi msg_info "Migrating old Paperless-ngx installation to uv" rm -rf /opt/paperless/venv find /opt/paperless -name "__pycache__" -type d -exec rm -rf {} + msg_info "Backing up configuration" local BACKUP_DIR="/opt/paperless_backup_$$" mkdir -p "$BACKUP_DIR" [[ -f /opt/paperless/paperless.conf ]] && cp /opt/paperless/paperless.conf "$BACKUP_DIR/" msg_ok "Backup completed to $BACKUP_DIR" declare -A PATCHES=( ["paperless-consumer.service"]="ExecStart=uv run -- python manage.py document_consumer" ["paperless-scheduler.service"]="ExecStart=uv run -- celery --app paperless beat --loglevel INFO" ["paperless-task-queue.service"]="ExecStart=uv run -- celery --app paperless worker --loglevel INFO" ["paperless-webserver.service"]="ExecStart=uv run -- granian --interface asgi --ws \"paperless.asgi:application\"" ) for svc in "${!PATCHES[@]}"; do path=$(systemctl show -p FragmentPath "$svc" | cut -d= -f2) if [[ -n "$path" && -f "$path" ]]; then sed -i "s|^ExecStart=.*|${PATCHES[$svc]}|" "$path" if [[ "$svc" == "paperless-webserver.service" ]]; then grep -q "^Environment=GRANIAN_HOST=" "$path" || sed -i '/^\[Service\]/a Environment=GRANIAN_HOST=::' "$path" grep -q "^Environment=GRANIAN_PORT=" "$path" || sed -i '/^\[Service\]/a Environment=GRANIAN_PORT=8000' "$path" grep -q "^Environment=GRANIAN_WORKERS=" "$path" || sed -i '/^\[Service\]/a Environment=GRANIAN_WORKERS=1' "$path" fi msg_ok "Patched $svc" else msg_error "Service file for $svc not found!" fi done $STD systemctl daemon-reload msg_info "Backing up configuration" BACKUP_DIR="/opt/paperless_backup_$$" mkdir -p "$BACKUP_DIR" [[ -f /opt/paperless/paperless.conf ]] && cp /opt/paperless/paperless.conf "$BACKUP_DIR/" msg_ok "Backup completed to $BACKUP_DIR" PYTHON_VERSION="3.13" setup_uv CLEAN_INSTALL=1 fetch_and_deploy_gh_release "paperless" "paperless-ngx/paperless-ngx" "prebuild" "latest" "/opt/paperless" "paperless*tar.xz" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "jbig2enc" "ie13/jbig2enc" "tarball" "latest" "/opt/jbig2enc" . /etc/os-release if [ "$VERSION_CODENAME" = "bookworm" ]; then setup_gs else msg_info "Installing Ghostscript" ensure_dependencies ghostscript msg_ok "Installed Ghostscript" fi msg_info "Updating Paperless-ngx" cp -r "$BACKUP_DIR"/* /opt/paperless/ cd /opt/paperless $STD uv sync --all-extras cd /opt/paperless/src $STD uv run -- python manage.py migrate msg_ok "Paperless-ngx migration and update completed" rm -rf "$BACKUP_DIR" if [[ -d /opt/paperless/backup ]]; then rm -rf /opt/paperless/backup msg_ok "Removed old backup directory" fi fi setup_nltk "snowball_data stopwords punkt_tab" "/usr/share/nltk_data" msg_info "Starting all Paperless-ngx Services" systemctl start paperless-consumer paperless-webserver paperless-scheduler paperless-task-queue sleep 1 msg_ok "Started all Paperless-ngx Services" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8000${CL}" ================================================ FILE: ct/papra.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/papra-hq/papra APP="Papra" var_tags="${var_tags:-document-management}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-10}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/papra ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "papra" "papra-hq/papra"; then msg_info "Stopping Service" systemctl stop papra msg_ok "Stopped Service" msg_info "Backing up Configuration" if [[ -f /opt/papra/apps/papra-server/.env ]]; then cp /opt/papra/apps/papra-server/.env /opt/papra_env.bak fi msg_ok "Backed up Configuration" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "papra" "papra-hq/papra" "tarball" msg_info "Building Application" cd /opt/papra if [[ -f /opt/papra_env.bak ]]; then cp /opt/papra_env.bak /opt/papra/apps/papra-server/.env else msg_warn ".env missing, regenerating from defaults" LOCAL_IP=$(hostname -I | awk '{print $1}') cat </opt/papra/apps/papra-server/.env NODE_ENV=production SERVER_SERVE_PUBLIC_DIR=true PORT=1221 DATABASE_URL=file:/opt/papra_data/db/db.sqlite DOCUMENT_STORAGE_FILESYSTEM_ROOT=/opt/papra_data/documents PAPRA_CONFIG_DIR=/opt/papra_data AUTH_SECRET=$(cat /opt/papra_data/.secret) BETTER_AUTH_SECRET=$(cat /opt/papra_data/.secret) BETTER_AUTH_TELEMETRY=0 CLIENT_BASE_URL=http://${LOCAL_IP}:1221 SERVER_BASE_URL=http://${LOCAL_IP}:1221 EMAILS_DRY_RUN=true INGESTION_FOLDER_IS_ENABLED=true INGESTION_FOLDER_ROOT_PATH=/opt/papra_data/ingestion EOF fi $STD pnpm install --frozen-lockfile $STD pnpm --filter "@papra/app-client..." run build $STD pnpm --filter "@papra/app-server..." run build ln -sf /opt/papra/apps/papra-client/dist /opt/papra/apps/papra-server/public rm -f /opt/papra_env.bak msg_ok "Built Application" msg_info "Starting Service" systemctl start papra msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:1221${CL}" ================================================ FILE: ct/part-db.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: bvdberg01 # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://docs.part-db.de/ APP="Part-DB" var_tags="${var_tags:-inventory;parts}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-8}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/partdb ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "partdb" "Part-DB/Part-DB-server"; then msg_info "Stopping Service" systemctl stop apache2 msg_ok "Stopped Service" mv /opt/partdb/ /opt/partdb-backup CLEAN_INSTALL=1 fetch_and_deploy_gh_release "partdb" "Part-DB/Part-DB-server" "prebuild" "latest" "/opt/partdb" "partdb_with_assets.zip" msg_info "Updating Part-DB" cd /opt/partdb/ cp -r /opt/partdb-backup/.env.local /opt/partdb/ cp -r /opt/partdb-backup/public/media /opt/partdb/public/ cp -r /opt/partdb-backup/config/banner.md /opt/partdb/config/ export COMPOSER_ALLOW_SUPERUSER=1 $STD composer install --no-dev -o --no-interaction $STD php bin/console cache:clear $STD php bin/console doctrine:migrations:migrate -n chown -R www-data:www-data /opt/partdb rm -r /opt/partdb-backup msg_ok "Updated Part-DB" msg_info "Starting Service" systemctl start apache2 msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}${CL}" ================================================ FILE: ct/passbolt.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Slaviša Arežina (tremor021) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.passbolt.com/ APP="Passbolt" var_tags="${var_tags:-auth}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-2}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /var ]]; then msg_error "No ${APP} Installation Found!" exit fi setup_mariadb msg_info "Updating $APP LXC" $STD apt update $STD apt upgrade -y msg_ok "Updated $APP LXC" exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}https://${IP}${CL}" ================================================ FILE: ct/patchmon.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: vhsdream # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/PatchMon/PatchMon APP="PatchMon" var_tags="${var_tags:-monitoring}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d "/opt/patchmon" ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "PatchMon" "PatchMon/PatchMon"; then msg_info "Stopping Service" systemctl stop patchmon-server msg_ok "Stopped Service" if [[ -d /opt/patchmon/backend ]]; then msg_info "Legacy install detected - creating full backup, please wait..." $STD tar czf ~/patchmon_legacy.tar.gz /opt/patchmon cp /opt/patchmon/backend/.env /opt/legacy.env msg_ok "Full backup saved in /root" msg_info "Starting migration to PatchMon v2.x.x" systemctl disable -q --now nginx $STD npm cache clean --force $STD apt autoremove --purge -y {nginx,nodejs} if [[ -f /etc/apt/sources.list.d/nodesource.sources ]]; then cp /etc/apt/sources.list.d/nodesource.sources /etc/apt/sources.list.d/nodesource.sources.bak rm -f /etc/apt/sources.list.d/nodesource.sources elif [[ -f /etc/apt/sources.list.d/nodesource.list ]]; then cp /etc/apt/sources.list.d/nodesource.list /etc/apt/sources.list.d/nodesource.list.bak rm -f /etc/apt/sources.list.d/nodesource.list fi rm -rf /opt/patchmon mkdir -p /opt/patchmon/agents cp /opt/legacy.env /opt/patchmon/.env sed -i -e 's/^PORT=.*/PORT=3000/' \ -e 's/^NODE_/APP_/' \ -e '/^SERVER_*/d' \ -e '/^# API*/,+2d' /opt/patchmon/.env { echo "" echo "SESSION_SECRET=$(openssl rand -hex 64)" echo "AI_ENCRYPTION_KEY=$(openssl rand -hex 64)" echo "AGENT_BINARIES_DIR=/opt/patchmon/agents" } >>/opt/patchmon/.env sed -i -e '\|Directory|s|/backend||' \ -e 's|^ExecStart=.*|ExecStart=/opt/patchmon/patchmon-server|' \ -e 's|^Environment=NODE_.*|EnvironmentFile=/opt/patchmon/.env|' \ /etc/systemd/system/patchmon-server.service systemctl daemon-reload rm /opt/legacy.env msg_ok "Migration complete!" fi CLEAN_INSTALL=1 fetch_and_deploy_gh_release "PatchMon" "PatchMon/PatchMon" "singlefile" "latest" "/opt/patchmon" "patchmon-server-linux-amd64" mv /opt/patchmon/PatchMon /opt/patchmon/patchmon-server msg_info "Fetching PatchMon agent binaries" RELEASE=$(get_latest_github_release "PatchMon/PatchMon") [[ ! -d /opt/patchmon/agents ]] && mkdir -p /opt/patchmon/agents FILE_URL="https://github.com/PatchMon/PatchMon/releases/download/v${RELEASE}/patchmon-agent-" AGENT_NAME=( "linux-amd64" "linux-arm64" "linux-arm" "linux-386" "freebsd-amd64" "freebsd-arm64" "freebsd-arm" "freebsd-386" "windows-amd64.exe" "windows-arm64.exe" ) for arch in "${AGENT_NAME[@]}"; do curl_with_retry "${FILE_URL}${arch}" "/opt/patchmon/agents/patchmon-agent-${arch}" [[ "${arch}" != *.exe ]] && chmod 755 "/opt/patchmon/agents/patchmon-agent-${arch}" done msg_ok "Fetched PatchMon agent binaries" msg_info "Starting Service" systemctl start patchmon-server msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:3000${CL}" ================================================ FILE: ct/paymenter.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Nícolas Pastorello (opastorello) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.paymenter.org APP="Paymenter" var_tags="${var_tags:-hosting;ecommerce;marketplace;}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-1024}" var_disk="${var_disk:-5}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/paymenter ]]; then msg_error "No ${APP} Installation Found!" exit fi setup_mariadb CURRENT_PHP=$(php -v 2>/dev/null | awk '/^PHP/{print $2}' | cut -d. -f1,2) if [[ "$CURRENT_PHP" != "8.3" ]]; then PHP_VERSION="8.3" PHP_FPM="YES" setup_php setup_composer sed -i 's|php8\.2-fpm\.sock|php8.3-fpm.sock|g' /etc/nginx/sites-available/paymenter.conf $STD systemctl reload nginx fi if check_for_gh_release "paymenter" "paymenter/paymenter"; then msg_info "Updating ${APP}" cd /opt/paymenter $STD php artisan app:upgrade --no-interaction echo "${CHECK_UPDATE_RELEASE}" >~/.paymenter msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:80${CL}" ================================================ FILE: ct/peanut.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) | Co-Author: remz1337 # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/Brandawg93/PeaNUT/ APP="PeaNUT" var_tags="${var_tags:-network;ups}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-4096}" var_disk="${var_disk:-7}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -f /etc/systemd/system/peanut.service ]]; then msg_error "No ${APP} Installation Found!" exit fi NODE_VERSION="24" NODE_MODULE="pnpm" setup_nodejs if check_for_gh_release "PeaNUT" "Brandawg93/PeaNUT"; then msg_info "Stopping Service" systemctl stop peanut msg_info "Stopped Service" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "PeaNUT" "Brandawg93/PeaNUT" "tarball" "latest" "/opt/peanut" if ! grep -q '/opt/peanut/entrypoint.mjs' /etc/systemd/system/peanut.service; then msg_info "Fixing entrypoint" cd /opt/peanut sed -i 's|/opt/peanut/.next/standalone/server.js|/opt/peanut/entrypoint.mjs|' /etc/systemd/system/peanut.service systemctl daemon-reload msg_ok "Fixed entrypoint" fi if [[ ! -f /etc/peanut/peanut.env ]]; then msg_info "Migrating service to EnvironmentFile" mkdir -p /etc/peanut cat </etc/peanut/peanut.env NODE_ENV=production #WEB_HOST=0.0.0.0 #WEB_PORT=8080 #NUT_HOST=localhost #NUT_PORT=3493 # Disable auth entirely: #AUTH_DISABLED=true # Bootstrap initial account on first start (ignored afterwards): #WEB_USERNAME=admin #WEB_PASSWORD=changeme EOF chmod 600 /etc/peanut/peanut.env sed -i '/^Environment=/d' /etc/systemd/system/peanut.service if ! grep -q '^EnvironmentFile=/etc/peanut/peanut.env' /etc/systemd/system/peanut.service; then sed -i '/^Type=simple/a EnvironmentFile=/etc/peanut/peanut.env' /etc/systemd/system/peanut.service fi systemctl daemon-reload msg_ok "Migrated to /etc/peanut/peanut.env" fi msg_info "Updating PeaNUT" cd /opt/peanut $STD pnpm i $STD pnpm run build:local cp -r .next/static .next/standalone/.next/ mkdir -p /opt/peanut/.next/standalone/config ln -sf /etc/peanut/settings.yml /opt/peanut/.next/standalone/config/settings.yml ln -sf .next/standalone/server.js server.js msg_ok "Updated PeaNUT" msg_info "Starting Service" systemctl start peanut msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8080${CL}" ================================================ FILE: ct/pelican-panel.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: bvdberg01 # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/pelican-dev/panel APP="Pelican-Panel" var_tags="${var_tags:-Gaming}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-1024}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/pelican-panel ]]; then msg_error "No ${APP} Installation Found!" exit fi setup_mariadb CURRENT_PHP=$(php -v 2>/dev/null | awk '/^PHP/{print $2}' | cut -d. -f1,2) setup_composer if [[ "$CURRENT_PHP" != "8.4" ]]; then msg_info "Migrating PHP $CURRENT_PHP to 8.4" $STD apt remove -y php"${CURRENT_PHP//./}"* PHP_VERSION="8.4" PHP_APACHE="YES" PHP_FPM="YES" setup_php msg_ok "Migrated PHP $CURRENT_PHP to 8.4" fi if check_for_gh_release "pelican-panel" "pelican-dev/panel"; then msg_info "Stopping Service" cd /opt/pelican-panel $STD php artisan down msg_ok "Stopped Service" mkdir -p /opt/backup cp -a /opt/pelican-panel/.env /opt/backup mkdir -p /opt/backup/storage/app/ cp -a /opt/pelican-panel/storage/app/public /opt/backup/storage/app/ SQLITE_INSTALL=$(ls /opt/pelican-panel/database/*.sqlite 1>/dev/null 2>&1 && echo "true" || echo "false") $SQLITE_INSTALL && cp -r /opt/pelican-panel/database/*.sqlite /opt/backup find /opt/pelican-panel -mindepth 1 -maxdepth 1 ! -name 'backup' ! -name 'plugins' -exec rm -rf {} + fetch_and_deploy_gh_release "pelican-panel" "pelican-dev/panel" "prebuild" "latest" "/opt/pelican-panel" "panel.tar.gz" msg_info "Updating Pelican Panel" cp -a /opt/backup/.env /opt/pelican-panel/ $SQLITE_INSTALL && mv /opt/backup/*.sqlite /opt/pelican-panel/database/ cp -a /opt/backup/storage/app/public /opt/pelican-panel/storage/app/ $STD composer install --no-dev --optimize-autoloader --no-interaction $STD php artisan p:environment:setup $STD php artisan view:clear $STD php artisan config:clear $STD php artisan filament:optimize $STD php artisan migrate --seed --force chown -R www-data:www-data /opt/pelican-panel chmod -R 755 /opt/pelican-panel/storage /opt/pelican-panel/bootstrap/cache/ msg_ok "Updated Pelican Panel" msg_info "Starting Service" $STD php artisan queue:restart $STD php artisan up msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}/installer${CL}" ================================================ FILE: ct/pelican-wings.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: bvdberg01 # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/pelican-dev/wings APP="Pelican-Wings" var_tags="${var_tags:-Gaming}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-4096}" var_disk="${var_disk:-8}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -f /usr/local/bin/wings ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "wings" "pelican-dev/wings"; then msg_info "Stopping Service" systemctl stop wings msg_ok "Stopped Service" fetch_and_deploy_gh_release "wings" "pelican-dev/wings" "singlefile" "latest" "/usr/local/bin" "wings_linux_arm64" msg_info "Starting Service" systemctl start wings msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" ================================================ FILE: ct/photoprism.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.photoprism.app/ APP="PhotoPrism" var_tags="${var_tags:-media;photo}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-3072}" var_disk="${var_disk:-8}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" var_gpu="${var_gpu:-yes}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/photoprism ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "photoprism" "photoprism/photoprism"; then msg_info "Stopping PhotoPrism" systemctl stop photoprism msg_ok "Stopped PhotoPrism" if ! grep -q "photoprism/config/.env" ~/.bashrc 2>/dev/null; then msg_info "Adding environment export for CLI tools" echo '# Load PhotoPrism environment variables for CLI tools' >>~/.bashrc echo 'export $(grep -v "^#" /opt/photoprism/config/.env | xargs)' >>~/.bashrc msg_ok "Added environment export" fi fetch_and_deploy_gh_release "photoprism" "photoprism/photoprism" "prebuild" "latest" "/opt/photoprism" "*linux-arm64.tar.gz" LIBHEIF_URL=$(curl -fsSL "https://dl.photoprism.app/dist/libheif/" | grep -oP "libheif-bookworm-arm64-v[0-9\.]+\.tar\.gz" | sort -V | tail -n 1) if [[ "${LIBHEIF_URL}" != "$(cat ~/.photoprism_libheif 2>/dev/null)" ]] || [[ ! -f ~/.photoprism_libheif ]]; then msg_info "Updating PhotoPrism LibHeif" ensure_dependencies libvips42 curl -fsSL "https://dl.photoprism.app/dist/libheif/$LIBHEIF_URL" -o /tmp/libheif.tar.gz tar -xzf /tmp/libheif.tar.gz -C /usr/local ldconfig echo "${LIBHEIF_URL}" >~/.photoprism_libheif msg_ok "Updated PhotoPrism LibHeif" fi msg_info "Starting PhotoPrism" systemctl start photoprism msg_ok "Started PhotoPrism" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:2342${CL}" ================================================ FILE: ct/pialert.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/leiweibau/Pi.Alert/ APP="PiAlert" var_tags="${var_tags:-network}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-512}" var_disk="${var_disk:-3}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/pialert ]]; then msg_error "No ${APP} Installation Found!" exit fi msg_info "Updating PiAlert" bash -c "$(curl -fsSL https://github.com/leiweibau/Pi.Alert/raw/main/install/pialert_update.sh)" -s --lxc msg_ok "Updated PiAlert" msg_ok "Updated successfully!" exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}/pialert${CL}" ================================================ FILE: ct/pihole.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://pi-hole.net/ APP="Pihole" var_tags="${var_tags:-adblock}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-512}" var_disk="${var_disk:-2}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /etc/pihole ]]; then msg_error "No ${APP} Installation Found!" exit fi msg_info "Updating PiHole" set +e $STD apt update $STD apt upgrade -y /usr/local/bin/pihole -up msg_ok "Updated PiHole" msg_ok "Updated successfully!" exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}/admin${CL}" ================================================ FILE: ct/planka.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Slaviša Arežina (tremor021) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/plankanban/planka APP="PLANKA" var_tags="${var_tags:-Todo;kanban}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-1024}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -f /etc/systemd/system/planka.service ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "planka" "plankanban/planka"; then msg_info "Stopping Service" systemctl stop planka msg_ok "Stopped Service" msg_info "Backing up data" BK="/opt/planka-backup" mkdir -p "$BK"/{favicons,user-avatars,background-images,attachments} [ -f /opt/planka/.env ] && mv /opt/planka/.env "$BK"/ # Support both old (pre-v2) and new (v2) directory layouts if [ -d /opt/planka/data/protected ]; then [ -d /opt/planka/data/protected/favicons ] && cp -a /opt/planka/data/protected/favicons/. "$BK/favicons/" [ -d /opt/planka/data/protected/user-avatars ] && cp -a /opt/planka/data/protected/user-avatars/. "$BK/user-avatars/" [ -d /opt/planka/data/protected/background-images ] && cp -a /opt/planka/data/protected/background-images/. "$BK/background-images/" [ -d /opt/planka/data/private/attachments ] && cp -a /opt/planka/data/private/attachments/. "$BK/attachments/" else [ -d /opt/planka/public/favicons ] && cp -a /opt/planka/public/favicons/. "$BK/favicons/" [ -d /opt/planka/public/user-avatars ] && cp -a /opt/planka/public/user-avatars/. "$BK/user-avatars/" [ -d /opt/planka/public/background-images ] && cp -a /opt/planka/public/background-images/. "$BK/background-images/" [ -d /opt/planka/private/attachments ] && cp -a /opt/planka/private/attachments/. "$BK/attachments/" fi rm -rf /opt/planka msg_ok "Backed up data" fetch_and_deploy_gh_release "planka" "plankanban/planka" "prebuild" "latest" "/opt/planka" "planka-prebuild.zip" msg_info "Update Frontend" cd /opt/planka $STD npm install msg_ok "Updated Frontend" msg_info "Restoring data" [ -f "$BK/.env" ] && mv "$BK/.env" /opt/planka/.env # Planka v2 uses unified data directory structure mkdir -p /opt/planka/data/protected/{favicons,user-avatars,background-images} /opt/planka/data/private/attachments [ -d "$BK/favicons" ] && cp -a "$BK/favicons/." /opt/planka/data/protected/favicons/ [ -d "$BK/user-avatars" ] && cp -a "$BK/user-avatars/." /opt/planka/data/protected/user-avatars/ [ -d "$BK/background-images" ] && cp -a "$BK/background-images/." /opt/planka/data/protected/background-images/ [ -d "$BK/attachments" ] && cp -a "$BK/attachments/." /opt/planka/data/private/attachments/ rm -rf "$BK" msg_ok "Restored data" msg_info "Migrate Database" cd /opt/planka $STD npm run db:upgrade $STD npm run db:migrate msg_ok "Migrated Database" msg_info "Starting Service" systemctl start planka msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:1337${CL}" ================================================ FILE: ct/plant-it.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://plant-it.org/ APP="Plant-it" var_tags="${var_tags:-plants;garden}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-5}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources RELEASE="0.10.0" if [[ ! -d /opt/plant-it ]]; then msg_error "No ${APP} Installation Found!" exit fi setup_mariadb if check_for_gh_release "plant-it" "MDeLuise/plant-it" "${RELEASE}" "last version that includes the web frontend"; then msg_info "Stopping Service" systemctl stop plant-it msg_info "Stopped Service" USE_ORIGINAL_FILENAME="true" fetch_and_deploy_gh_release "plant-it" "MDeLuise/plant-it" "singlefile" "${RELEASE}" "/opt/plant-it/backend" "server.jar" fetch_and_deploy_gh_release "plant-it-front" "MDeLuise/plant-it" "prebuild" "${RELEASE}" "/opt/plant-it/frontend" "client.tar.gz" msg_warn "Application is updated to latest Web version (v0.10.0). There will be no more updates available." msg_warn "Please read: https://github.com/MDeLuise/plant-it/releases/tag/1.0.0" msg_info "Starting Service" systemctl start plant-it msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:3000${CL}" ================================================ FILE: ct/plex.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.plex.tv/ APP="Plex" var_tags="${var_tags:-media}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-8}" var_os="${var_os:-ubuntu}" var_version="${var_version:-24.04}" var_unprivileged="${var_unprivileged:-1}" var_gpu="${var_gpu:-yes}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if ! dpkg -l plexmediaserver &>/dev/null; then msg_error "No ${APP} Installation Found!" exit fi # Migrate from old repository to new one if needed if [[ -f /etc/apt/sources.list.d/plexmediaserver.sources ]]; then local current_uri current_uri=$(grep -oP '(?<=URIs: ).*' /etc/apt/sources.list.d/plexmediaserver.sources 2>/dev/null || true) if [[ "$current_uri" == *"downloads.plex.tv/repo/deb"* ]]; then msg_info "Migrating to new Plex repository" rm -f /etc/apt/sources.list.d/plexmediaserver.sources rm -f /usr/share/keyrings/PlexSign.asc setup_deb822_repo \ "plexmediaserver" \ "https://downloads.plex.tv/plex-keys/PlexSign.v2.key" \ "https://repo.plex.tv/deb/" \ "public" \ "main" msg_ok "Migrated to new Plex repository" fi elif compgen -G "/etc/apt/sources.list.d/plex*.list" >/dev/null; then msg_info "Migrating to new Plex repository (deb822)" rm -f /etc/apt/sources.list.d/plex*.list rm -f /usr/share/keyrings/PlexSign.asc rm -f /usr/share/keyrings/plexmediaserver.v2.gpg setup_deb822_repo \ "plexmediaserver" \ "https://downloads.plex.tv/plex-keys/PlexSign.v2.key" \ "https://repo.plex.tv/deb/" \ "public" \ "main" msg_ok "Migrated to new Plex repository (deb822)" elif [[ ! -f /etc/apt/sources.list.d/plexmediaserver.sources ]]; then msg_info "Setting up Plex repository" setup_deb822_repo \ "plexmediaserver" \ "https://downloads.plex.tv/plex-keys/PlexSign.v2.key" \ "https://repo.plex.tv/deb/" \ "public" \ "main" msg_ok "Set up Plex repository" fi if [[ -f /usr/local/bin/plexupdate ]] || [[ -d /opt/plexupdate ]]; then msg_info "Removing legacy plexupdate" rm -rf /opt/plexupdate /usr/local/bin/plexupdate crontab -l 2>/dev/null | grep -v plexupdate | crontab - 2>/dev/null || true msg_ok "Removed legacy plexupdate" fi msg_info "Updating Plex Media Server" $STD apt update $STD apt install -y plexmediaserver msg_ok "Updated Plex Media Server" msg_info "Restarting Plex Media Server" systemctl restart plexmediaserver msg_ok "Restarted Plex Media Server" msg_ok "Updated successfully!" exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:32400/web${CL}" ================================================ FILE: ct/pocketbase.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://pocketbase.io/ APP="Pocketbase" var_tags="${var_tags:-database}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-512}" var_disk="${var_disk:-8}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -f /etc/systemd/system/pocketbase.service || ! -x /opt/pocketbase/pocketbase ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "pocketbase" "pocketbase/pocketbase"; then msg_info "Stopping Service" systemctl stop pocketbase msg_ok "Stopped Service" msg_info "Updating ${APP}" /opt/pocketbase/pocketbase update echo "${CHECK_UPDATE_RELEASE}" >~/.pocketbase msg_ok "Updated ${APP}" msg_info "Starting Service" systemctl start pocketbase msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8080/_/${CL}" ================================================ FILE: ct/pocketid.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Snarkenfaugister # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/pocket-id/pocket-id APP="PocketID" var_tags="${var_tags:-identity-provider}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/pocket-id ]]; then msg_error "No ${APP} Installation Found!" exit fi # Mandatory as of v2.x.x ENCRYPTION_KEY=$(openssl rand -base64 32) if ! grep -q '^ENCRYPTION_KEY=' /opt/pocket-id/.env; then echo "ENCRYPTION_KEY=$ENCRYPTION_KEY" >> /opt/pocket-id/.env fi if check_for_gh_release "pocket-id" "pocket-id/pocket-id"; then if [ "$(printf '%s\n%s' "$(cat ~/.pocket-id 2>/dev/null || echo 0.0.0)" "1.0.0" | sort -V | head -n1)" = "$(cat ~/.pocket-id 2>/dev/null || echo 0.0.0)" ] && [ "$(cat ~/.pocket-id 2>/dev/null || echo 0.0.0)" != "1.0.0" ]; then msg_info "Migrating ${APP}" systemctl -q disable --now pocketid-backend pocketid-frontend caddy mv /etc/caddy/Caddyfile ~/Caddyfile.bak $STD apt remove --purge caddy nodejs -y $STD apt autoremove -y rm /etc/apt/{keyrings/nodesource.gpg,sources.list.d/nodesource.list} rm -r /usr/local/go cp -r /opt/pocket-id/backend/data /opt/data cp /opt/pocket-id/backend/.env /opt/env sed -i -e 's/PUBLIC_//g' \ -e '/^SQLITE_DB_PATH/d' \ -e '/^POSTGRES/s/^/# /' \ -e '/^UPLOAD_PATH/d' \ -e 's/8080/1411/' /opt/env rm -r /opt/pocket-id rm /etc/systemd/system/pocketid-frontend.service BACKEND="/etc/systemd/system/pocketid-backend.service" sed -i -e 's/Backend/Service/' \ -e 's/\/backend\|-backend//g' "$BACKEND" mv "$BACKEND" ${BACKEND//-backend/} systemctl daemon-reload systemctl -q enable pocketid mkdir /opt/pocket-id mv /opt/data /opt/pocket-id msg_ok "Migration complete. The reverse proxy port has been changed to 1411." else msg_info "Stopping Service" systemctl stop pocketid msg_ok "Stopped Service" cp /opt/pocket-id/.env /opt/env fi fetch_and_deploy_gh_release "pocket-id" "pocket-id/pocket-id" "singlefile" "latest" "/opt/pocket-id/" "pocket-id-linux-arm64" mv /opt/env /opt/pocket-id/.env msg_info "Starting Service" systemctl start pocketid msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Configure your reverse proxy to point to:${BGN} ${IP}:1411${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}https://{PUBLIC_URL}/setup${CL}" ================================================ FILE: ct/podman-homeassistant.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.home-assistant.io/ APP="Podman-Home Assistant" var_tags="${var_tags:-podman;smarthome}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-16}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -f /etc/containers/systemd/homeassistant.container ]]; then msg_error "No ${APP} Installation Found!" exit fi UPD=$(msg_menu "Home Assistant Update Options" \ "1" "Update system and containers" \ "2" "Install HACS" \ "3" "Install FileBrowser" \ "4" "Remove ALL Unused Images") if [ "$UPD" == "1" ]; then msg_info "Updating ${APP} LXC" $STD apt update $STD apt upgrade -y msg_ok "Updated successfully!" msg_info "Updating All Containers\n" CONTAINER_LIST="${1:-$(podman ps -q)}" for container in ${CONTAINER_LIST}; do CONTAINER_IMAGE="$(podman inspect --format "{{.Config.Image}}" --type container ${container})" RUNNING_IMAGE="$(podman inspect --format "{{.Image}}" --type container "${container}")" podman pull "${CONTAINER_IMAGE}" LATEST_IMAGE="$(podman inspect --format "{{.Id}}" --type image "${CONTAINER_IMAGE}")" if [[ "${RUNNING_IMAGE}" != "${LATEST_IMAGE}" ]]; then echo "Updating ${container} image ${CONTAINER_IMAGE}" systemctl restart homeassistant fi done msg_ok "All containers updated." exit fi if [ "$UPD" == "2" ]; then msg_info "Installing Home Assistant Community Store (HACS)" $STD apt update cd /var/lib/containers/storage/volumes/hass_config/_data $STD bash <(curl -fsSL https://get.hacs.xyz) msg_ok "Installed Home Assistant Community Store (HACS)" echo -e "\n Reboot Home Assistant and clear browser cache then Add HACS integration.\n" exit fi if [ "$UPD" == "3" ]; then msg_info "Installing FileBrowser" $STD curl -fsSL https://raw.githubusercontent.com/filebrowser/get/master/get.sh | bash $STD filebrowser config init -a '0.0.0.0' $STD filebrowser config set -a '0.0.0.0' $STD filebrowser users add admin community-scripts.org --perm.admin msg_ok "Installed FileBrowser" msg_info "Creating Service" cat </etc/systemd/system/filebrowser.service [Unit] Description=Filebrowser After=network-online.target [Service] User=root WorkingDirectory=/root/ ExecStart=/usr/local/bin/filebrowser -r / [Install] WantedBy=default.target EOF systemctl enable -q --now filebrowser msg_ok "Created Service" msg_ok "Completed successfully!\n" echo -e "FileBrowser should be reachable by going to the following URL. ${BL}http://$LOCAL_IP:8080${CL} admin|community-scripts.org\n" exit fi if [ "$UPD" == "4" ]; then msg_info "Removing ALL Unused Images" podman image prune -a -f msg_ok "Removed ALL Unused Images" exit fi } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8123${CL}" ================================================ FILE: ct/podman.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://podman.io/ APP="Podman" var_tags="${var_tags:-container;kubernetes}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -f /etc/containers/registries.conf ]]; then msg_error "No ${APP} Installation Found!" exit fi msg_info "Updating ${APP} LXC" $STD apt update $STD apt upgrade -y msg_ok "Updated successfully!" exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" ================================================ FILE: ct/postgresql.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.postgresql.org/ APP="PostgreSQL" var_tags="${var_tags:-database}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-1024}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if ! command -v psql >/dev/null 2>&1; then msg_error "No ${APP} Installation Found!" exit fi msg_info "Updating ${APP} LXC" $STD apt update $STD apt -y upgrade msg_ok "Updated successfully!" exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following IP:${CL}" echo -e "${TAB}${GATEWAY}${BGN}${IP}:5432${CL}" ================================================ FILE: ct/powerdns.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Slaviša Arežina (tremor021) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.powerdns.com/ APP="PowerDNS" var_tags="${var_tags:-dns}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-1024}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/poweradmin ]]; then msg_error "No ${APP} Installation Found!" exit fi msg_info "Updating PowerDNS" $STD apt update $STD apt install -y --only-upgrade pdns-server pdns-backend-sqlite3 msg_ok "Updated PowerDNS" if check_for_gh_release "poweradmin" "poweradmin/poweradmin"; then msg_info "Backing up Configuration" cp /opt/poweradmin/config/settings.php /opt/poweradmin_settings.php.bak cp /opt/poweradmin/powerdns.db /opt/poweradmin_powerdns.db.bak msg_ok "Backed up Configuration" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "poweradmin" "poweradmin/poweradmin" "tarball" msg_info "Updating Poweradmin" cp /opt/poweradmin_settings.php.bak /opt/poweradmin/config/settings.php cp /opt/poweradmin_powerdns.db.bak /opt/poweradmin/powerdns.db rm -rf /opt/poweradmin/install rm -f /opt/poweradmin_settings.php.bak /opt/poweradmin_powerdns.db.bak chown -R www-data:pdns /opt/poweradmin chmod 775 /opt/poweradmin chown pdns:pdns /opt/poweradmin/powerdns.db chmod 664 /opt/poweradmin/powerdns.db msg_ok "Updated Poweradmin" msg_info "Restarting Services" systemctl restart pdns apache2 msg_ok "Restarted Services" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed Successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}${CL}" ================================================ FILE: ct/privatebin.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Nícolas Pastorello (opastorello) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://privatebin.info/ APP="PrivateBin" var_tags="${var_tags:-paste;secure}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-1024}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/privatebin ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "privatebin" "PrivateBin/PrivateBin"; then msg_info "Creating backup" cp -f /opt/privatebin/cfg/conf.php /tmp/privatebin_conf.bak msg_ok "Backup created" rm -rf /opt/privatebin/* fetch_and_deploy_gh_release "privatebin" "PrivateBin/PrivateBin" "tarball" msg_info "Configuring ${APP}" mkdir -p /opt/privatebin/data mv /tmp/privatebin_conf.bak /opt/privatebin/cfg/conf.php chown -R www-data:www-data /opt/privatebin chmod -R 0755 /opt/privatebin/data systemctl reload nginx php8.2-fpm msg_ok "Configured ${APP}" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}https://${IP}${CL}" ================================================ FILE: ct/profilarr.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: michelroegl-brunner # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/Dictionarry-Hub/profilarr APP="Profilarr" var_tags="${var_tags:-arr;radarr;sonarr;config}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-8}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/profilarr ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "profilarr" "Dictionarry-Hub/profilarr"; then msg_info "Stopping Service" systemctl stop profilarr msg_ok "Stopped Service" msg_info "Backing up Data" if [[ -d /config ]]; then cp -r /config /opt/profilarr_config_backup fi msg_ok "Backed up Data" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "profilarr" "Dictionarry-Hub/profilarr" "tarball" msg_info "Installing Python Dependencies" cd /opt/profilarr/backend $STD uv venv --clear /opt/profilarr/backend/.venv sed 's/==/>=/g' requirements.txt >requirements-relaxed.txt $STD uv pip install --python /opt/profilarr/backend/.venv/bin/python -r requirements-relaxed.txt rm -f requirements-relaxed.txt msg_ok "Installed Python Dependencies" msg_info "Building Frontend" if [[ -d /opt/profilarr/frontend ]]; then cd /opt/profilarr/frontend $STD npm install $STD npm run build cp -r dist /opt/profilarr/backend/app/static fi msg_ok "Built Frontend" msg_info "Restoring Data" if [[ -d /opt/profilarr_config_backup ]]; then mkdir -p /config cp -r /opt/profilarr_config_backup/. /config/ rm -rf /opt/profilarr_config_backup fi msg_ok "Restored Data" msg_info "Starting Service" systemctl start profilarr msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed Successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:6868${CL}" ================================================ FILE: ct/projectsend.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: bvdberg01 # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.projectsend.org/ APP="ProjectSend" var_tags="${var_tags:-media}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-1024}" var_disk="${var_disk:-8}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/projectsend ]]; then msg_error "No ${APP} Installation Found!" exit fi setup_mariadb if check_for_gh_release "projectsend" "projectsend/projectsend"; then msg_info "Stopping Service" systemctl stop apache2 msg_ok "Stopped Service" php_ver=$(php -v | head -n 1 | awk '{print $2}') if [[ ! $php_ver == "8.4"* ]]; then PHP_VERSION="8.4" PHP_APACHE="YES" setup_php fi mv /opt/projectsend/includes/sys.config.php /opt/sys.config.php CLEAN_INSTALL=1 fetch_and_deploy_gh_release "projectsend" "projectsend/projectsend" "prebuild" "latest" "/opt/projectsend" "projectsend-r*.zip" mv /opt/sys.config.php /opt/projectsend/includes/sys.config.php chown -R www-data:www-data /opt/projectsend chmod -R 775 /opt/projectsend msg_info "Starting Service" systemctl start apache2 msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}/install for the initial setup${CL}" ================================================ FILE: ct/prometheus-alertmanager.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Andy Grunwald (andygrunwald) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://prometheus.io/ APP="Prometheus-Alertmanager" var_tags="${var_tags:-monitoring;alerting}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-1024}" var_disk="${var_disk:-2}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -f /etc/systemd/system/prometheus-alertmanager.service ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "alertmanager" "prometheus/alertmanager"; then msg_info "Stopping Service" systemctl stop prometheus-alertmanager msg_ok "Stopped Service" fetch_and_deploy_gh_release "alertmanager" "prometheus/alertmanager" "prebuild" "latest" "/usr/local/bin/" "alertmanager*linux-arm64.tar.gz" msg_info "Starting Service" systemctl start prometheus-alertmanager msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:9093${CL}" ================================================ FILE: ct/prometheus-blackbox-exporter.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Marfnl # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/prometheus/blackbox_exporter APP="Prometheus-Blackbox-Exporter" var_tags="${var_tags:-monitoring;prometheus}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-512}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/blackbox-exporter ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "blackbox-exporter" "prometheus/blackbox_exporter"; then msg_info "Stopping Service" systemctl stop blackbox-exporter msg_ok "Stopped Service" msg_info "Creating backup" mv /opt/blackbox-exporter/blackbox.yml /opt msg_ok "Backup created" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "blackbox-exporter" "prometheus/blackbox_exporter" "prebuild" "latest" "/opt/blackbox-exporter" "blackbox_exporter-*.linux-arm64.tar.gz" msg_info "Restoring backup" cp -r /opt/blackbox.yml /opt/blackbox-exporter rm -f /opt/blackbox.yml msg_ok "Backup restored" msg_info "Starting Service" systemctl start blackbox-exporter msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:9115${CL}" ================================================ FILE: ct/prometheus-pve-exporter.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Andy Grunwald (andygrunwald) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/prometheus-pve/prometheus-pve-exporter APP="Prometheus-PVE-Exporter" var_tags="${var_tags:-monitoring}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-512}" var_disk="${var_disk:-2}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -f /etc/systemd/system/prometheus-pve-exporter.service ]]; then msg_error "No ${APP} Installation Found!" exit fi msg_info "Stopping Service" systemctl stop prometheus-pve-exporter msg_ok "Stopped Service" export PVE_VENV_PATH="/opt/prometheus-pve-exporter/.venv" export PVE_EXPORTER_BIN="${PVE_VENV_PATH}/bin/pve_exporter" if [[ ! -d "$PVE_VENV_PATH" || ! -x "$PVE_EXPORTER_BIN" ]]; then PYTHON_VERSION="3.12" setup_uv msg_info "Migrating to uv/venv" rm -rf "$PVE_VENV_PATH" mkdir -p /opt/prometheus-pve-exporter cd /opt/prometheus-pve-exporter $STD uv venv --clear "$PVE_VENV_PATH" $STD "$PVE_VENV_PATH/bin/python" -m ensurepip --upgrade $STD "$PVE_VENV_PATH/bin/python" -m pip install --upgrade pip $STD "$PVE_VENV_PATH/bin/python" -m pip install prometheus-pve-exporter msg_ok "Migrated to uv/venv" else msg_info "Updating Prometheus Proxmox VE Exporter" PYTHON_VERSION="3.12" setup_uv $STD "$PVE_VENV_PATH/bin/python" -m pip install --upgrade prometheus-pve-exporter msg_ok "Updated Prometheus Proxmox VE Exporter" fi local service_file="/etc/systemd/system/prometheus-pve-exporter.service" if ! grep -q "${PVE_VENV_PATH}/bin/pve_exporter" "$service_file"; then msg_info "Updating systemd service" cat <"$service_file" [Unit] Description=Prometheus Proxmox VE Exporter Documentation=https://github.com/znerol/prometheus-pve-exporter After=syslog.target network.target [Service] User=root Restart=always Type=simple ExecStart=${PVE_VENV_PATH}/bin/pve_exporter \\ --config.file=/opt/prometheus-pve-exporter/pve.yml \\ --web.listen-address=0.0.0.0:9221 ExecReload=/bin/kill -HUP \$MAINPID [Install] WantedBy=multi-user.target EOF $STD systemctl daemon-reload msg_ok "Updated systemd service" fi msg_info "Starting Service" systemctl start prometheus-pve-exporter msg_ok "Started Service" msg_ok "Updated successfully!" exit 0 } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:9221${CL}" ================================================ FILE: ct/prometheus.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://prometheus.io/ APP="Prometheus" var_tags="${var_tags:-monitoring}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -f /etc/systemd/system/prometheus.service ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "prometheus" "prometheus/prometheus"; then msg_info "Stopping Service" systemctl stop prometheus msg_ok "Stopped Service" fetch_and_deploy_gh_release "prometheus" "prometheus/prometheus" "prebuild" "latest" "/usr/local/bin" "*linux-arm64.tar.gz" rm -f /usr/local/bin/prometheus.yml msg_info "Starting Service" systemctl start prometheus msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:9090${CL}" ================================================ FILE: ct/protonmail-bridge.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Stephen Chin (steveonjava) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/ProtonMail/proton-bridge APP="ProtonMail-Bridge" var_tags="${var_tags:-mail;proton}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-1024}" var_disk="${var_disk:-8}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -x /usr/bin/protonmail-bridge ]]; then msg_error "No ${APP} Installation Found!" exit 1 fi if check_for_gh_release "protonmail-bridge" "ProtonMail/proton-bridge"; then local -a bridge_units=( protonmail-bridge protonmail-bridge-imap.socket protonmail-bridge-smtp.socket protonmail-bridge-imap-proxy protonmail-bridge-smtp-proxy ) local unit declare -A was_active for unit in "${bridge_units[@]}"; do if systemctl is-active --quiet "$unit" 2>/dev/null; then was_active["$unit"]=1 else was_active["$unit"]=0 fi done msg_info "Stopping Services" systemctl stop protonmail-bridge-imap.socket protonmail-bridge-smtp.socket protonmail-bridge-imap-proxy protonmail-bridge-smtp-proxy protonmail-bridge msg_ok "Stopped Services" fetch_and_deploy_gh_release "protonmail-bridge" "ProtonMail/proton-bridge" "binary" if [[ -f /home/protonbridge/.protonmailbridge-initialized ]]; then msg_info "Starting Services" for unit in "${bridge_units[@]}"; do if [[ "${was_active[$unit]:-0}" == "1" ]]; then systemctl start "$unit" fi done msg_ok "Started Services" else msg_ok "Initialization not completed. Services remain disabled." fi msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW}One-time configuration is required before Bridge services are enabled.${CL}" echo -e "${INFO}${YW}Run this command in the container: protonmailbridge-configure${CL}" ================================================ FILE: ct/prowlarr.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://prowlarr.com/ APP="Prowlarr" var_tags="${var_tags:-arr}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-1024}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /var/lib/prowlarr/ ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "prowlarr" "Prowlarr/Prowlarr"; then apt-get install -y libicu76 &>/dev/null msg_info "Stopping Service" systemctl stop prowlarr msg_ok "Stopped Service" rm -rf /opt/Prowlarr fetch_and_deploy_gh_release "prowlarr" "Prowlarr/Prowlarr" "prebuild" "latest" "/opt/Prowlarr" "Prowlarr.master*linux-core-arm64.tar.gz" chmod 775 /opt/Prowlarr msg_info "Starting Service" systemctl start prowlarr msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:9696${CL}" ================================================ FILE: ct/proxmox-backup-server.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.proxmox.com/en/proxmox-backup-server APP="Proxmox-Backup-Server" var_tags="${var_tags:-backup}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-10}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -e /usr/sbin/proxmox-backup-manager ]]; then msg_error "No ${APP} Installation Found!" exit fi msg_info "Updating $APP LXC" $STD apt update $STD apt -y upgrade msg_ok "Updated $APP LXC" msg_ok "Updated successfully!" exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}https://${IP}:8007${CL}" ================================================ FILE: ct/proxmox-datacenter-manager.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: CrazyWolf13 # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: Proxmox Server Solution GmbH APP="Proxmox-Datacenter-Manager" var_tags="${var_tags:-datacenter}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-10}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -e /usr/sbin/proxmox-datacenter-manager-admin ]]; then msg_error "No ${APP} Installation Found!" exit fi if grep -q 'Debian GNU/Linux 12' /etc/os-release && [ -f /etc/apt/sources.list.d/proxmox-release-bookworm.list ] && [ -f /etc/apt/sources.list.d/pdm-test.list ]; then msg_info "Updating outdated outdated source formats" echo "deb [signed-by=/usr/share/keyrings/proxmox-archive-keyring.gpg] http://download.proxmox.com/debian/pdm bookworm pdm-test" >/etc/apt/sources.list.d/pdm-test.list curl -fsSL https://enterprise.proxmox.com/debian/proxmox-archive-keyring-trixie.gpg -o /usr/share/keyrings/proxmox-archive-keyring.gpg rm -f /etc/apt/keyrings/proxmox-release-bookworm.gpg /etc/apt/sources.list.d/proxmox-release-bookworm.list $STD apt update msg_ok "Updated old sources" fi if grep -q 'Debian GNU/Linux 13' /etc/os-release; then if [ -f "/etc/apt/sources.list.d/pdm-test.sources" ]; then if ! grep -qx "Enabled: false" "/etc/apt/sources.list.d/pdm-test.sources"; then echo "Enabled: false" >> "/etc/apt/sources.list.d/pdm-test.sources" setup_deb822_repo \ "pdm" \ "https://enterprise.proxmox.com/debian/proxmox-archive-keyring-trixie.gpg" \ "http://download.proxmox.com/debian/pdm" \ "trixie" \ "pdm-no-subscription" fi fi fi msg_info "Updating $APP LXC" $STD apt update $STD apt -y upgrade msg_ok "Updated $APP LXC" msg_ok "Updated successfully!" exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}https://${IP}:8443${CL}" ================================================ FILE: ct/proxmox-mail-gateway.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: thost96 (thost96) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.proxmox.com/en/products/proxmox-mail-gateway APP="Proxmox-Mail-Gateway" var_tags="${var_tags:-mail}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-4096}" var_disk="${var_disk:-10}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -e /usr/bin/pmgproxy ]]; then msg_error "No ${APP} Installation Found!" exit fi msg_info "Updating Proxmox-Mail-Gateway" $STD apt update $STD apt upgrade -y msg_ok "Updated Proxmox-Mail-Gateway" msg_ok "Updated successfully!" exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}https://${IP}:8006/${CL}" ================================================ FILE: ct/ps5-mqtt.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: liecno # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/FunkeyFlo/ps5-mqtt/ APP="PS5-MQTT" var_tags="${var_tags:-smarthome;automation}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-512}" var_disk="${var_disk:-3}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/ps5-mqtt ]]; then msg_error "No ${APP} installation found!" exit fi if check_for_gh_release "ps5-mqtt" "FunkeyFlo/ps5-mqtt"; then msg_info "Stopping service" systemctl stop ps5-mqtt msg_ok "Stopped service" fetch_and_deploy_gh_release "ps5-mqtt" "FunkeyFlo/ps5-mqtt" "tarball" msg_info "Configuring ${APP}" cd /opt/ps5-mqtt/ps5-mqtt/ $STD npm install $STD npm run build msg_ok "Configured ${APP}" msg_info "Starting service" systemctl start ps5-mqtt msg_ok "Started service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8645${CL}" ================================================ FILE: ct/pterodactyl-panel.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: bvdberg01 # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/pterodactyl/panel APP="Pterodactyl-Panel" var_tags="${var_tags:-gaming}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-1024}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/pterodactyl-panel ]]; then msg_error "No ${APP} Installation Found!" exit fi setup_mariadb CURRENT_PHP=$(php -v 2>/dev/null | awk '/^PHP/{print $2}' | cut -d. -f1,2) if [[ "$CURRENT_PHP" != "8.4" ]]; then msg_info "Migrating PHP $CURRENT_PHP to 8.4" $STD curl -fsSLo /tmp/debsuryorg-archive-keyring.deb https://packages.sury.org/debsuryorg-archive-keyring.deb $STD dpkg -i /tmp/debsuryorg-archive-keyring.deb $STD sh -c 'echo "deb [signed-by=/usr/share/keyrings/deb.sury.org-php.gpg] https://packages.sury.org/php/ $(lsb_release -sc) main" > /etc/apt/sources.list.d/php.list' cat </etc/apt/sources.list.d/php.sources Types: deb URIs: https://packages.sury.org/php/ Suites: $(lsb_release -sc) Components: main Signed-By: /usr/share/keyrings/deb.sury.org-php.gpg EOF $STD apt update $STD apt remove -y php"${CURRENT_PHP//./}"* $STD apt install -y \ php8.4 \ php8.4-{gd,mysql,mbstring,bcmath,xml,curl,zip,intl,fpm} \ libapache2-mod-php8.4 msg_ok "Migrated PHP $CURRENT_PHP to 8.4" fi RELEASE=$(curl -fsSL https://api.github.com/repos/pterodactyl/panel/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4) }') if [[ ! -f /opt/${APP}_version.txt ]] || [[ "${RELEASE}" != "$(cat /opt/${APP}_version.txt)" ]]; then msg_info "Stopping Service" cd /opt/pterodactyl-panel $STD php artisan down msg_ok "Stopped Service" msg_info "Updating ${APP} to v${RELEASE}" cp -r /opt/pterodactyl-panel/.env /opt/ rm -rf * .* curl -fsSL "https://github.com/pterodactyl/panel/releases/download/v${RELEASE}/panel.tar.gz" -o $(basename "https://github.com/pterodactyl/panel/releases/download/v${RELEASE}/panel.tar.gz") tar -xzf "panel.tar.gz" mv /opt/.env /opt/pterodactyl-panel/ $STD composer install --no-dev --optimize-autoloader --no-interaction $STD php artisan view:clear $STD php artisan config:clear $STD php artisan migrate --seed --force --no-interaction chown -R www-data:www-data /opt/pterodactyl-panel/* chmod -R 755 /opt/pterodactyl-panel/storage /opt/pterodactyl-panel/bootstrap/cache/ ln -s /opt/pterodactyl-panel /var/www/pterodactyl rm -rf "/opt/pterodactyl-panel/panel.tar.gz" echo "${RELEASE}" >/opt/${APP}_version.txt msg_ok "Updated $APP to v${RELEASE}" msg_info "Starting Service" $STD php artisan queue:restart $STD php artisan up msg_ok "Started Service" msg_ok "Updated successfully!" else msg_ok "No update required. ${APP} is already at v${RELEASE}" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}${CL}" ================================================ FILE: ct/pterodactyl-wings.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: bvdberg01 # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/pterodactyl/wings APP="Pterodactyl-Wings" var_tags="${var_tags:-gaming}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-4096}" var_disk="${var_disk:-8}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -x /usr/local/bin/wings ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "wings" "pterodactyl/wings"; then msg_info "Stopping Service" systemctl stop wings msg_ok "Stopped Service" rm /usr/local/bin/wings fetch_and_deploy_gh_release "wings" "pterodactyl/wings" "singlefile" "latest" "/usr/local/bin" "wings_linux_arm64" msg_info "Starting Service" systemctl start wings msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" ================================================ FILE: ct/pulse.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: rcourtman & vhsdream # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/rcourtman/Pulse APP="Pulse" var_tags="${var_tags:-monitoring;proxmox}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-1024}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/pulse ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "pulse" "rcourtman/Pulse"; then SERVICE_PATH="/etc/systemd/system" msg_info "Stopping Services" systemctl stop pulse*.service msg_ok "Stopped Services" if [[ -f /opt/pulse/pulse ]]; then rm -f /opt/pulse/pulse fi CLEAN_INSTALL=1 fetch_and_deploy_gh_release "pulse" "rcourtman/Pulse" "prebuild" "latest" "/opt/pulse" "pulse-v*-linux-arm64.tar.gz" ln -sf /opt/pulse/bin/pulse /usr/local/bin/pulse mkdir -p /etc/pulse chown pulse:pulse /etc/pulse chown -R pulse:pulse /opt/pulse chmod 700 /etc/pulse if [[ -f "$SERVICE_PATH"/pulse-backend.service ]]; then mv "$SERVICE_PATH"/pulse-backend.service "$SERVICE_PATH"/pulse.service fi sed -i -e 's|pulse/pulse|pulse/bin/pulse|' \ -e 's/^Environment="API.*$//' "$SERVICE_PATH"/pulse.service systemctl daemon-reload if grep -q 'pulse-home:/bin/bash' /etc/passwd; then usermod -s /usr/sbin/nologin pulse fi msg_info "Starting Services" systemctl start pulse msg_ok "Started Services" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:7655${CL}" ================================================ FILE: ct/pve-scripts-local.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: michelroegl-brunner # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.debian.org/ APP="PVE-Scripts-Local" var_tags="${var_tags:-pve-scripts-local}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-4096}" var_disk="${var_disk:-10}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/ProxmoxVE-Local ]]; then msg_error "No ${APP} Installation Found!" exit fi msg_custom "🚀" "${GN}" "The app offers a built-in updater. Please use it." exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:3000${CL}" ================================================ FILE: ct/qbittorrent.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: tteck (tteckster) | Co-Author: Slaviša Arežina (tremor021) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.qbittorrent.org/ APP="qBittorrent" var_tags="${var_tags:-torrent}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-8}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -f /etc/systemd/system/qbittorrent-nox.service ]]; then msg_error "No ${APP} Installation Found!" exit fi if [[ ! -f ~/.qbittorrent ]]; then msg_error "Please create new qBittorrent LXC. Updating from v4.x to v5.x is not supported!" exit fi if check_for_gh_release "qbittorrent" "userdocs/qbittorrent-nox-static"; then msg_info "Stopping Service" systemctl stop qbittorrent-nox msg_ok "Stopped Service" rm -f /opt/qbittorrent/qbittorrent-nox fetch_and_deploy_gh_release "qbittorrent" "userdocs/qbittorrent-nox-static" "singlefile" "latest" "/opt/qbittorrent" "x86_64-qbittorrent-nox" mv /opt/qbittorrent/qbittorrent /opt/qbittorrent/qbittorrent-nox msg_info "Starting Service" systemctl start qbittorrent-nox msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8090${CL}" ================================================ FILE: ct/qdrant.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/qdrant/qdrant APP="Qdrant" var_tags="${var_tags:-database;vector}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-1024}" var_disk="${var_disk:-5}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /var/lib/qdrant ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "qdrant" "qdrant/qdrant"; then fetch_and_deploy_gh_release "qdrant" "qdrant/qdrant" "prebuild" "latest" "/opt/qdrant" "qdrant-aarch64-unknown-linux-musl.tar.gz" chown -R root:root /var/lib/qdrant chmod -R 755 /var/lib/qdrant fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:6333/dashboard${CL}" ================================================ FILE: ct/qui.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (Canbiz) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/autobrr/qui APP="Qui" var_tags="${var_tags:-torrent}" var_disk="${var_disk:-10}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-1024}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -f /usr/local/bin/qui ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "Qui" "autobrr/qui"; then msg_info "Stopping Service" systemctl stop qui msg_ok "Stopped Service" fetch_and_deploy_gh_release "qui" "autobrr/qui" "prebuild" "latest" "/tmp/qui" "qui_*_linux_aarch64.tar.gz" msg_info "Updating qui" mv /tmp/qui/qui /usr/local/bin/qui chmod +x /usr/local/bin/qui rm -rf /tmp/qui msg_ok "Updated qui" msg_info "Starting Service" systemctl start qui msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:7476${CL}" ================================================ FILE: ct/rabbitmq.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck | Co-Author: MickLesk (Canbiz) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.rabbitmq.com/ APP="RabbitMQ" var_tags="${var_tags:-mqtt}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-1024}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /etc/rabbitmq ]]; then msg_error "No ${APP} Installation Found!" exit fi if grep -q "dl.cloudsmith.io" /etc/apt/sources.list.d/rabbitmq.list; then rm -f /etc/apt/sources.list.d/rabbitmq.list cat </etc/apt/sources.list.d/rabbitmq.list ## Modern Erlang/OTP releases deb [arch=arm64 signed-by=/usr/share/keyrings/com.rabbitmq.team.gpg] https://deb1.rabbitmq.com/rabbitmq-erlang/debian/trixie trixie main deb [arch=arm64 signed-by=/usr/share/keyrings/com.rabbitmq.team.gpg] https://deb2.rabbitmq.com/rabbitmq-erlang/debian/trixie trixie main ## Provides modern RabbitMQ releases deb [arch=arm64 signed-by=/usr/share/keyrings/com.rabbitmq.team.gpg] https://deb1.rabbitmq.com/rabbitmq-server/debian/trixie trixie main deb [arch=arm64 signed-by=/usr/share/keyrings/com.rabbitmq.team.gpg] https://deb2.rabbitmq.com/rabbitmq-server/debian/trixie trixie main EOF $STD apt update fi msg_info "Stopping Service" systemctl stop rabbitmq-server msg_ok "Stopped Service" msg_info "Updating..." $STD apt install --only-upgrade rabbitmq-server msg_ok "Updated successfully!" msg_info "Starting Service" systemctl start rabbitmq-server msg_ok "Started Service" msg_ok "Updated successfully!" exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:15672${CL}" ================================================ FILE: ct/radarr.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://radarr.video/ APP="Radarr" var_tags="${var_tags:-arr}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-1024}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /var/lib/radarr/ ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "Radarr" "Radarr/Radarr"; then apt-get install -y libicu76 &>/dev/null msg_info "Stopping Service" systemctl stop radarr msg_ok "Stopped Service" rm -rf /opt/Radarr fetch_and_deploy_gh_release "Radarr" "Radarr/Radarr" "prebuild" "latest" "/opt/Radarr" "Radarr.master*linux-core-arm64.tar.gz" chmod 775 /opt/Radarr msg_info "Starting Service" systemctl start radarr msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:7878${CL}" ================================================ FILE: ct/radicale.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: tremor021 # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://radicale.org/ APP="Radicale" var_tags="${var_tags:-calendar}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-512}" var_disk="${var_disk:-2}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/radicale ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "Radicale" "Kozea/Radicale"; then msg_info "Stopping service" systemctl stop radicale msg_ok "Stopped service" msg_info "Backing up users file" cp /opt/radicale/users /opt/radicale_users_backup msg_ok "Backed up users file" PYTHON_VERSION="3.13" setup_uv CLEAN_INSTALL=1 fetch_and_deploy_gh_release "Radicale" "Kozea/Radicale" "tarball" "latest" "/opt/radicale" msg_info "Restoring users file" rm -f /opt/radicale/users mv /opt/radicale_users_backup /opt/radicale/users msg_ok "Restored users file" if grep -q 'start.sh' /etc/systemd/system/radicale.service; then sed -i -e '/^Description/i[Unit]' \ -e '\|^ExecStart|iWorkingDirectory=/opt/radicale' \ -e 's|^ExecStart=.*|ExecStart=/usr/local/bin/uv run -m radicale --config /etc/radicale/config|' /etc/systemd/system/radicale.service systemctl daemon-reload fi if [[ ! -f /etc/radicale/config ]]; then msg_info "Migrating to config file (/etc/radicale/config)" mkdir -p /etc/radicale cat </etc/radicale/config [server] hosts = 0.0.0.0:5232 [auth] type = htpasswd htpasswd_filename = /opt/radicale/users htpasswd_encryption = sha512 [storage] type = multifilesystem filesystem_folder = /var/lib/radicale/collections [web] type = internal EOF msg_ok "Migrated to config (/etc/radicale/config)" fi msg_info "Starting service" systemctl start radicale msg_ok "Started service" msg_ok "Updated Successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:5232${CL}" ================================================ FILE: ct/rclone.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Slaviša Arežina (tremor021) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/rclone/rclone APP="Rclone" var_tags="${var_tags:-os}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-512}" var_disk="${var_disk:-2}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" var_fuse="${var_fuse:-yes}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/rclone ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "rclone" "rclone/rclone"; then msg_info "Stopping Service" systemctl stop rclone-web msg_ok "Stopped Service" fetch_and_deploy_gh_release "rclone" "rclone/rclone" "prebuild" "latest" "/opt/rclone" "rclone*linux-arm64.zip" msg_info "Starting Service" systemctl start rclone-web msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:3000${CL}" ================================================ FILE: ct/rdtclient.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/rogerfar/rdt-client APP="RDTClient" var_tags="${var_tags:-torrent}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-1024}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/rdtc/ ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "rdt-client" "rogerfar/rdt-client"; then msg_info "Stopping Service" systemctl stop rdtc msg_ok "Stopped Service" msg_info "Creating backup" mkdir -p /opt/rdtc-backup cp -R /opt/rdtc/appsettings.json /opt/rdtc-backup/ msg_ok "Backup created" fetch_and_deploy_gh_release "rdt-client" "rogerfar/rdt-client" "prebuild" "latest" "/opt/rdtc" "RealDebridClient.zip" cp -R /opt/rdtc-backup/appsettings.json /opt/rdtc/ if dpkg-query -W aspnetcore-runtime-9.0 >/dev/null 2>&1; then $STD apt remove --purge -y aspnetcore-runtime-9.0 ensure_dependencies aspnetcore-runtime-10.0 fi rm -rf /opt/rdtc-backup msg_info "Starting Service" systemctl start rdtc msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:6500${CL}" ================================================ FILE: ct/reactive-resume.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: vhsdream # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://rxresume.org APP="Reactive-Resume" var_tags="${var_tags:-documents}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-4096}" var_disk="${var_disk:-8}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -f /etc/systemd/system/reactive-resume.service ]]; then msg_error "No $APP Installation Found!" exit fi if check_for_gh_release "reactive-resume" "amruthpillai/reactive-resume"; then msg_info "Stopping services" systemctl stop reactive-resume msg_ok "Stopped services" ensure_dependencies git cp /opt/reactive-resume/.env /opt/reactive-resume.env.bak NODE_VERSION="24" setup_nodejs CLEAN_INSTALL=1 fetch_and_deploy_gh_release "reactive-resume" "amruthpillai/reactive-resume" "tarball" "latest" "/opt/reactive-resume" msg_info "Updating Reactive Resume (Patience)" cd /opt/reactive-resume export COREPACK_ENABLE_DOWNLOAD_PROMPT=0 corepack enable corepack prepare --activate export CI="true" export NODE_ENV="production" $STD pnpm install --frozen-lockfile $STD pnpm run build $STD pnpm run prisma:generate mv /opt/rxresume.env /opt/Reactive-Resume/.env msg_ok "Updated Reactive-Resume" msg_info "Updating Minio" systemctl stop minio cd /tmp curl -fsSL https://dl.min.io/server/minio/release/linux-arm64/minio.deb -o minio.deb $STD dpkg -i minio.deb rm -f /tmp/minio.deb msg_ok "Updated Minio" msg_info "Updating Browserless (Patience)" systemctl stop browserless cp /opt/browserless/.env /opt/browserless.env rm -rf /opt/browserless brwsr_tmp=$(mktemp) TAG=$(curl -fsSL https://api.github.com/repos/browserless/browserless/tags?per_page=1 | grep "name" | awk '{print substr($2, 3, length($2)-4) }') curl -fsSL https://github.com/browserless/browserless/archive/refs/tags/v"$TAG".zip -o "$brwsr_tmp" $STD unzip "$brwsr_tmp" mv browserless-"$TAG"/ /opt/browserless cd /opt/browserless $STD npm install typescript $STD npm install esbuild $STD npm install rm -rf src/routes/{chrome,edge,firefox,webkit} $STD node_modules/playwright-core/cli.js install --with-deps chromium $STD npm run build $STD npm run build:function $STD npm prune production mv /opt/browserless.env /opt/browserless/.env rm -f "$brwsr_tmp" msg_ok "Updated Browserless" msg_info "Restarting services" systemctl start chromium-printer reactive-resume msg_ok "Restarted services" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:3000${CL}" ================================================ FILE: ct/readarr.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://readarr.com/ APP="Readarr" var_tags="${var_tags:-media;comic;eBook}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-1024}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /var/lib/readarr/ ]]; then msg_error "No ${APP} Installation Found!" exit fi msg_info "Updating $APP LXC" $STD apt update $STD apt -y upgrade apt-get install -y libicu76 &>/dev/null msg_ok "Updated $APP LXC" msg_ok "Updated successfully!" exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8787${CL}" ================================================ FILE: ct/readeck.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://readeck.org/en/ APP="Readeck" var_tags="${var_tags:-bookmark}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-512}" var_disk="${var_disk:-2}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/readeck ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_codeberg_release "readeck" "readeck/readeck"; then msg_info "Stopping Service" systemctl stop readeck msg_ok "Stopped Service" fetch_and_deploy_codeberg_release "readeck" "readeck/readeck" "singlefile" "latest" "/opt/readeck" "readeck-*-linux-arm64" msg_info "Starting Service" systemctl start readeck msg_ok "Started Service" msg_ok "Updated successfully!" else msg_ok "No update required. ${APP} is already at the latest version." fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8000${CL}" ================================================ FILE: ct/recyclarr.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: MrYadro # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://recyclarr.dev/wiki/ APP="Recyclarr" var_tags="${var_tags:-arr}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-512}" var_disk="${var_disk:-2}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -f /root/.config/recyclarr/recyclarr.yml ]] && [[ ! -d /root/.config/recyclarr/configs ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "recyclarr" "recyclarr/recyclarr"; then apt-get install -y libicu76 &>/dev/null msg_info "Updating ${APP}" fetch_and_deploy_gh_release "recyclarr" "recyclarr/recyclarr" "prebuild" "latest" "/usr/local/bin" "recyclarr-linux-arm64.tar.xz" # Migrate includes from configs/ to includes/ (recyclarr v8) RECYCLARR_DIR="/root/.config/recyclarr" mkdir -p "$RECYCLARR_DIR/includes" if [[ -d "$RECYCLARR_DIR/configs" ]]; then for item in "$RECYCLARR_DIR/configs"/*/; do [[ -d "$item" ]] || continue dir_name=$(basename "$item") # Only move subdirs that look like include dirs (not the configs themselves) if [[ "$dir_name" != "configs" ]] && [[ ! -d "$RECYCLARR_DIR/includes/$dir_name" ]]; then mv "$item" "$RECYCLARR_DIR/includes/" fi done fi msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following IP:${CL}" echo -e "${TAB}${GATEWAY}${BGN}${IP}${CL}" ================================================ FILE: ct/redis.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://redis.io/ APP="Redis" var_tags="${var_tags:-database}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-1024}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -f /lib/systemd/system/redis-server.service ]]; then msg_error "No ${APP} Installation Found!" exit fi msg_info "Updating $APP LXC" $STD apt update $STD apt upgrade -y msg_ok "Updated $APP LXC" msg_ok "Updated successfully!" exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following IP:${CL}" echo -e "${TAB}${GATEWAY}${BGN}${IP}:6379${CL}" ================================================ FILE: ct/reitti.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/dedicatedcode/reitti APP="Reitti" var_tags="${var_tags:-location-tracker}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-4096}" var_disk="${var_disk:-15}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -f /opt/reitti/reitti.jar ]]; then msg_error "No ${APP} Installation Found!" exit fi # Enable PostGIS extension if not already enabled if systemctl is-active --quiet postgresql; then if ! sudo -u postgres psql -d reitti_db -tAc "SELECT 1 FROM pg_extension WHERE extname='postgis'" 2>/dev/null | grep -q 1; then msg_info "Enabling PostGIS extension" sudo -u postgres psql -d reitti_db -c "CREATE EXTENSION IF NOT EXISTS postgis;" &>/dev/null msg_ok "Enabled PostGIS extension" fi fi # Migrate v3 -> v4: Remove RabbitMQ (no longer required) / Photon / Spring Settings if systemctl is-enabled --quiet rabbitmq-server 2>/dev/null; then msg_info "Migrating to v4: Removing RabbitMQ" systemctl stop rabbitmq-server systemctl disable rabbitmq-server $STD apt-get purge -y rabbitmq-server erlang-base $STD apt-get autoremove -y msg_ok "Removed RabbitMQ" fi if systemctl is-enabled --quiet photon 2>/dev/null; then msg_info "Migrating to v4: Removing Photon service" systemctl stop photon systemctl disable photon rm -f /etc/systemd/system/photon.service systemctl daemon-reload msg_ok "Removed Photon service" fi if grep -q "spring.rabbitmq\|PHOTON_BASE_URL\|PROCESSING_WAIT_TIME\|DANGEROUS_LIFE" /opt/reitti/application.properties 2>/dev/null; then msg_info "Migrating to v4: Rewriting application.properties" local DB_URL DB_USER DB_PASS DB_URL=$(grep '^spring.datasource.url=' /opt/reitti/application.properties | cut -d'=' -f2-) DB_USER=$(grep '^spring.datasource.username=' /opt/reitti/application.properties | cut -d'=' -f2-) DB_PASS=$(grep '^spring.datasource.password=' /opt/reitti/application.properties | cut -d'=' -f2-) cp /opt/reitti/application.properties /opt/reitti/application.properties.bak cat </opt/reitti/application.properties # Server configuration server.port=8080 server.servlet.context-path=/ server.forward-headers-strategy=framework server.compression.enabled=true server.compression.min-response-size=1024 server.compression.mime-types=text/plain,application/json # Logging configuration logging.level.root=INFO logging.level.org.hibernate.engine.jdbc.spi.SqlExceptionHelper=FATAL logging.level.com.dedicatedcode.reitti=INFO # Internationalization spring.messages.basename=messages spring.messages.encoding=UTF-8 spring.messages.cache-duration=3600 spring.messages.fallback-to-system-locale=false # PostgreSQL configuration spring.datasource.url=${DB_URL} spring.datasource.username=${DB_USER} spring.datasource.password=${DB_PASS} spring.datasource.hikari.maximum-pool-size=20 # Redis configuration spring.data.redis.host=127.0.0.1 spring.data.redis.port=6379 spring.data.redis.username= spring.data.redis.password= spring.data.redis.database=0 spring.cache.redis.key-prefix= spring.cache.cache-names=processed-visits,significant-places,users,magic-links,configurations,transport-mode-configs,avatarThumbnails,avatarData,user-settings spring.cache.redis.time-to-live=1d # Upload configuration spring.servlet.multipart.max-file-size=5GB spring.servlet.multipart.max-request-size=5GB server.tomcat.max-part-count=100 # Rqueue configuration rqueue.web.enable=false rqueue.job.enabled=false rqueue.message.durability.in-terminal-state=0 rqueue.key.prefix=\${spring.cache.redis.key-prefix} rqueue.message.converter.provider.class=com.dedicatedcode.reitti.config.RQueueCustomMessageConverter # Application-specific settings reitti.server.advertise-uri= reitti.security.local-login.disable=false # OIDC / Security Settings reitti.security.oidc.enabled=false reitti.security.oidc.registration.enabled=false reitti.import.batch-size=10000 reitti.import.processing-idle-start-time=10 reitti.geo-point-filter.max-speed-kmh=1000 reitti.geo-point-filter.max-accuracy-meters=100 reitti.geo-point-filter.history-lookback-hours=24 reitti.geo-point-filter.window-size=50 reitti.process-data.schedule=0 */10 * * * * reitti.process-data.refresh-views.schedule=0 0 4 * * * reitti.imports.schedule=0 5/10 * * * * reitti.imports.owntracks-recorder.schedule=\${reitti.imports.schedule} # Geocoding service configuration reitti.geocoding.max-errors=10 reitti.geocoding.photon.base-url= # Tiles Configuration reitti.ui.tiles.cache.url=http://127.0.0.1 reitti.ui.tiles.default.service=https://tile.openstreetmap.org/{z}/{x}/{y}.png reitti.ui.tiles.default.attribution=© OpenStreetMap contributors # Data management configuration reitti.data-management.enabled=false reitti.data-management.preview-cleanup.cron=0 0 4 * * * reitti.storage.path=data/ reitti.storage.cleanup.cron=0 0 4 * * * # Location data density normalization reitti.location.density.target-points-per-minute=4 # Logging buffer reitti.logging.buffer-size=1000 reitti.logging.max-buffer-size=10000 spring.config.import=optional:oidc.properties PROPEOF # Update reitti.service dependencies if [[ -f /etc/systemd/system/reitti.service ]]; then sed -i 's/ rabbitmq-server\.service//g; s/ photon\.service//g' /etc/systemd/system/reitti.service systemctl daemon-reload fi msg_ok "Rewrote application.properties (backup: application.properties.bak)" fi if check_for_gh_release "reitti" "dedicatedcode/reitti"; then msg_info "Stopping Service" systemctl stop reitti msg_ok "Stopped Service" JAVA_VERSION="25" setup_java rm -f /opt/reitti/reitti.jar USE_ORIGINAL_FILENAME="true" fetch_and_deploy_gh_release "reitti" "dedicatedcode/reitti" "singlefile" "latest" "/opt/reitti" "reitti-app.jar" mv /opt/reitti/reitti-*.jar /opt/reitti/reitti.jar msg_info "Starting Service" systemctl start reitti msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8080${CL}" ================================================ FILE: ct/resiliosync.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: David Bennett (dbinit) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.resilio.com/sync APP="Resilio Sync" var_tags="${var_tags:-sync}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-8}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /var/lib/resilio-sync ]]; then msg_error "No ${APP} Installation Found!" exit fi msg_info "Updating Resilio Sync" $STD apt update $STD apt upgrade -y msg_ok "Updated successfully!" exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}https://${IP}:8888${CL}" ================================================ FILE: ct/revealjs.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Slaviša Arežina (tremor021) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/hakimel/reveal.js APP="RevealJS" var_tags="${var_tags:-presentation}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-1024}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d "/opt/revealjs" ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "revealjs" "hakimel/reveal.js"; then msg_info "Stopping Service" systemctl stop revealjs msg_info "Stopped Service" cp /opt/revealjs/index.html /opt fetch_and_deploy_gh_release "revealjs" "hakimel/reveal.js" "tarball" msg_info "Updating RevealJS" cd /opt/revealjs $STD npm install cp -f /opt/index.html /opt/revealjs sed -i 's/"vite"/"vite --host"/g' package.json rm -f /opt/index.html msg_ok "Updated RevealJS" msg_info "Starting Service" systemctl start revealjs msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8000${CL}" ================================================ FILE: ct/romm.sh ================================================ #!/usr/bin/env bash source <(curl -s https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) | DevelopmentCats | AlphaLawless # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://romm.app APP="RomM" var_tags="${var_tags:-emulation}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-4096}" var_disk="${var_disk:-20}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/romm ]]; then msg_error "No ${APP} Installation Found!" exit fi NODE_VERSION="24" setup_nodejs if check_for_gh_release "romm" "rommapp/romm"; then msg_info "Stopping Services" systemctl stop romm-backend romm-worker romm-scheduler romm-watcher msg_ok "Stopped Services" msg_info "Backing up configuration" cp /opt/romm/.env /opt/romm/.env.backup msg_ok "Backed up configuration" fetch_and_deploy_gh_release "romm" "rommapp/romm" "tarball" "latest" "/opt/romm" msg_info "Updating ROMM" cp /opt/romm/.env.backup /opt/romm/.env cd /opt/romm $STD uv sync --all-extras cd /opt/romm/backend $STD uv run alembic upgrade head cd /opt/romm/frontend $STD npm install $STD npm run build # Merge static assets into dist folder cp -rf /opt/romm/frontend/assets/* /opt/romm/frontend/dist/assets/ mkdir -p /opt/romm/frontend/dist/assets/romm ROMM_BASE=$(grep '^ROMM_BASE_PATH=' /opt/romm/.env | cut -d'=' -f2) ROMM_BASE=${ROMM_BASE:-/var/lib/romm} ln -sfn "$ROMM_BASE"/resources /opt/romm/frontend/dist/assets/romm/resources ln -sfn "$ROMM_BASE"/assets /opt/romm/frontend/dist/assets/romm/assets sed -i "s|alias .*/library/;|alias ${ROMM_BASE}/library/;|" /etc/nginx/sites-available/romm systemctl reload nginx msg_ok "Updated ROMM" msg_info "Starting Services" systemctl start romm-backend romm-worker romm-scheduler romm-watcher msg_ok "Started Services" msg_ok "Updated successfully" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}${CL}" ================================================ FILE: ct/runtipi.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://runtipi.io/ APP="Runtipi" var_tags="${var_tags:-os}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-8}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors ADDON_SCRIPT="https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/tools/addon/runtipi.sh" function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/runtipi ]]; then msg_error "No ${APP} Installation Found!" exit fi msg_warn "⚠️ ${APP} has been migrated to an addon script." echo "" msg_info "This is a one-time migration. After this, you can update ${APP} anytime with:" echo -e "${TAB}${TAB}${GN}update_runtipi${CL} or ${GN}bash <(curl -fsSL ${ADDON_SCRIPT})${CL}" echo "" read -r -p "${TAB}Migrate update function now? [y/N]: " CONFIRM if [[ ! "${CONFIRM,,}" =~ ^(y|yes)$ ]]; then msg_warn "Migration skipped. The old update will continue to work for now." msg_info "Updating ${APP} (legacy)" cd /opt/runtipi && ./runtipi-cli update latest msg_ok "Updated ${APP}" exit fi msg_info "Migrating update function" TMP_UPDATE=$(mktemp) cat <<'MIGRATION_EOF' >"$TMP_UPDATE" bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/tools/addon/runtipi.sh)" MIGRATION_EOF mv "$TMP_UPDATE" /usr/bin/update chmod +x /usr/bin/update ln -sf /usr/bin/update /usr/bin/update_runtipi 2>/dev/null || true msg_ok "Migration complete" msg_info "Running addon update" type=update bash <(curl -fsSL "${ADDON_SCRIPT}") exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}${CL}" ================================================ FILE: ct/rustdeskserver.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Slaviša Arežina (tremor021) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/rustdesk/rustdesk-server APP="RustDesk Server" var_tags="${var_tags:-remote-desktop}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-512}" var_disk="${var_disk:-2}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -x /usr/bin/hbbr ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "rustdesk-hbbs" "lejianwen/rustdesk-server"; then msg_info "Stopping Service" systemctl stop rustdesk-hbbr systemctl stop rustdesk-hbbs if [[ -f /lib/systemd/system/rustdesk-api.service ]]; then systemctl stop rustdesk-api fi msg_info "Stopped Service" fetch_and_deploy_gh_release "rustdesk-hbbr" "rustdesk/rustdesk-server" "binary" "latest" "/opt/rustdesk" "rustdesk-server-hbbr*arm64.deb" fetch_and_deploy_gh_release "rustdesk-hbbs" "rustdesk/rustdesk-server" "binary" "latest" "/opt/rustdesk" "rustdesk-server-hbbs*arm64.deb" fetch_and_deploy_gh_release "rustdesk-utils" "rustdesk/rustdesk-server" "binary" "latest" "/opt/rustdesk" "rustdesk-server-utils*arm64.deb" fetch_and_deploy_gh_release "rustdesk-api" "lejianwen/rustdesk-api" "binary" "latest" "/opt/rustdesk" "rustdesk-api-server*arm64.deb" msg_info "Starting services" systemctl start -q rustdesk-* msg_ok "Services started" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}${IP}:21114${CL}" ================================================ FILE: ct/rustypaste.sh ================================================ #!/usr/bin/env bash source <(curl -s https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: GoldenSpringness # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/orhun/rustypaste APP="rustypaste" var_tags="${var_tags:-pastebin;storage}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-1024}" var_disk="${var_disk:-20}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -f /opt/rustypaste/rustypaste ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "rustypaste" "orhun/rustypaste"; then msg_info "Stopping Services" systemctl stop rustypaste msg_ok "Stopped Services" msg_info "Creating Backup" tar -czf "/opt/rustypaste_backup_$(date +%F).tar.gz" /opt/rustypaste/upload 2>/dev/null || true cp /opt/rustypaste/config.toml /tmp/rustypaste_config.toml.bak msg_ok "Backup Created" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "rustypaste" "orhun/rustypaste" "prebuild" "latest" "/opt/rustypaste" "*aarch64-unknown-linux-gnu.tar.gz" msg_info "Restoring Data" mv /tmp/rustypaste_config.toml.bak /opt/rustypaste/config.toml tar -xzf "/opt/rustypaste_backup_$(date +%F).tar.gz" -C /opt/rustypaste/upload 2>/dev/null || true rm -rf /opt/rustypaste_backup_$(date +%F).tar.gz msg_ok "Restored Data" msg_info "Starting Services" systemctl start rustypaste msg_ok "Started Services" msg_ok "Updated successfully!" fi if check_for_gh_release "rustypaste-cli" "orhun/rustypaste-cli"; then fetch_and_deploy_gh_release "rustypaste-cli" "orhun/rustypaste-cli" "prebuild" "latest" "/usr/local/bin" "*aarch64-unknown-linux-gnu.tar.gz" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}rustypaste setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8000${CL}" ================================================ FILE: ct/rwmarkable.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: vhsdream # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/fccview/rwMarkable APP="rwMarkable" var_tags="${var_tags:-tasks;notes}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-3072}" var_disk="${var_disk:-6}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/rwmarkable ]]; then msg_error "No ${APP} Installation Found!" exit fi msg_info "Stopping service" systemctl -q disable --now rwmarkable msg_ok "Stopped Service" NODE_VERSION="22" NODE_MODULE="yarn" setup_nodejs CLEAN_INSTALL=1 fetch_and_deploy_gh_release "jotty" "fccview/jotty" "tarball" "latest" "/opt/jotty" msg_info "Updating app" cd /opt/jotty $STD yarn --frozen-lockfile $STD yarn next telemetry disable $STD yarn build msg_ok "Updated app" msg_info "Migrating configuration & data" cp /opt/rwmarkable/.env /opt/jotty/.env mkdir -p /opt/jotty/data cp -r /opt/rwmarkable/data/* /opt/jotty/data cp -r /opt/rwmarkable/config/* /opt/jotty/config msg_ok "Migrated configuration & data" msg_info "Patching systemd service file" sed -i 's/rw[M|m]arkable/jotty/g' /etc/systemd/system/rwmarkable.service mv /etc/systemd/system/rwmarkable.service /etc/systemd/system/jotty.service systemctl daemon-reload msg_ok "Patched systemd service file" msg_info "Patching update script" sed -i 's/rwmarkable/jotty/g' /usr/bin/update msg_ok "Patched update script" msg_info "Starting jotty service" systemctl -q enable --now jotty msg_ok "Started jotty service" msg_ok "Migrated Successfully!" exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:3000${CL}" ================================================ FILE: ct/sabnzbd.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) | Co-Author: MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://sabnzbd.org/ APP="SABnzbd" var_tags="${var_tags:-downloader}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-5}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if par2 --version | grep -q "par2cmdline-turbo"; then fetch_and_deploy_gh_release "par2cmdline-turbo" "animetosho/par2cmdline-turbo" "prebuild" "latest" "/usr/bin/" "*-linux-arm64.zip" fi if [[ ! -d /opt/sabnzbd ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "sabnzbd-org" "sabnzbd/sabnzbd"; then PYTHON_VERSION="3.13" setup_uv systemctl stop sabnzbd cp -r /opt/sabnzbd /opt/sabnzbd_backup_$(date +%s) fetch_and_deploy_gh_release "sabnzbd-org" "sabnzbd/sabnzbd" "prebuild" "latest" "/opt/sabnzbd" "SABnzbd-*-src.tar.gz" # Always ensure venv exists if [[ ! -d /opt/sabnzbd/venv ]]; then msg_info "Migrating SABnzbd to uv virtual environment" $STD uv venv --clear /opt/sabnzbd/venv msg_ok "Created uv venv at /opt/sabnzbd/venv" fi # Always check and fix service file if needed if [[ -f /etc/systemd/system/sabnzbd.service ]] && grep -q "ExecStart=python3 SABnzbd.py" /etc/systemd/system/sabnzbd.service; then sed -i "s|ExecStart=python3 SABnzbd.py|ExecStart=/opt/sabnzbd/venv/bin/python SABnzbd.py|" /etc/systemd/system/sabnzbd.service systemctl daemon-reload msg_ok "Updated SABnzbd service to use uv venv" fi $STD uv pip install --upgrade pip --python=/opt/sabnzbd/venv/bin/python $STD uv pip install -r /opt/sabnzbd/requirements.txt --python=/opt/sabnzbd/venv/bin/python systemctl start sabnzbd msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:7777${CL}" ================================================ FILE: ct/salt.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: bvdberg01 # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/saltstack/salt APP="Salt" var_tags="${var_tags:-automations}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-1024}" var_disk="${var_disk:-3}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /etc/salt ]]; then msg_error "No ${APP} Installation Found!" exit fi RELEASE=$(get_latest_github_release "saltstack/salt") if check_for_gh_release "salt" "saltstack/salt"; then msg_info "Updating Salt" sed -i "s/^\(Pin: version \).*/\1${RELEASE}/" /etc/apt/preferences.d/salt-pin-1001 $STD apt update $STD apt upgrade -y echo "${RELEASE}" >/~.salt msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" ================================================ FILE: ct/scanopy.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: vhsdream # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/scanopy/scanopy APP="Scanopy" var_tags="${var_tags:-analytics}" var_cpu="${var_cpu:-4}" var_ram="${var_ram:-4096}" var_disk="${var_disk:-8}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/scanopy ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "Scanopy" "scanopy/scanopy"; then msg_info "Stopping services" systemctl stop scanopy-server [[ -f /etc/systemd/system/scanopy-daemon.service ]] && systemctl stop scanopy-daemon msg_ok "Stopped services" msg_info "Backing up configurations" cp /opt/scanopy/.env /opt/scanopy.env [[ -f /opt/scanopy/oidc.toml ]] && cp /opt/scanopy/oidc.toml /opt/scanopy.oidc.toml msg_ok "Backed up configurations" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "Scanopy" "scanopy/scanopy" "tarball" "latest" "/opt/scanopy" ensure_dependencies pkg-config libssl-dev TOOLCHAIN="$(grep "channel" /opt/scanopy/backend/rust-toolchain.toml | awk -F\" '{print $2}')" RUST_TOOLCHAIN=$TOOLCHAIN setup_rust [[ -f /opt/scanopy.env ]] && mv /opt/scanopy.env /opt/scanopy/.env [[ -f /opt/scanopy.oidc.toml ]] && mv /opt/scanopy.oidc.toml /opt/scanopy/oidc.toml if ! grep -q "PUBLIC_URL" /opt/scanopy/.env; then sed -i "\|_PATH=|a\\scanopy_PUBLIC_URL=http://${LOCAL_IP}:60072" /opt/scanopy/.env fi sed -i 's|_TARGET=.*$|_URL=http://127.0.0.1:60072|' /opt/scanopy/.env msg_info "Building Scanopy Server (patience)" cd /opt/scanopy/backend $STD cargo build --release --bin server --bin generate-fixtures $STD ./target/release/generate-fixtures --output-dir /opt/scanopy/ui/src/lib/data mv ./target/release/server /usr/bin/scanopy-server msg_ok "Built Scanopy Server" msg_info "Creating frontend UI" export PUBLIC_SERVER_HOSTNAME=default export PUBLIC_SERVER_PORT="" cd /opt/scanopy/ui $STD npm ci --no-fund --no-audit $STD npm run build msg_ok "Created frontend UI" if [[ -f /etc/systemd/system/scanopy-daemon.service ]]; then fetch_and_deploy_gh_release "Scanopy Daemon" "scanopy/scanopy" "singlefile" "latest" "/usr/local/bin" "scanopy-daemon-linux-amd64" mv "/usr/local/bin/Scanopy Daemon" /usr/local/bin/scanopy-daemon rm -f /usr/bin/scanopy-daemon ~/configure_daemon.sh sed -i -e 's|usr/bin|usr/local/bin|' \ -e 's/push/daemon_poll/' \ -e 's/pull/server_poll/' /etc/systemd/system/scanopy-daemon.service systemctl daemon-reload msg_ok "Updated Scanopy Daemon" fi msg_info "Starting services" systemctl start scanopy-server [[ -f /etc/systemd/system/scanopy-daemon.service ]] && systemctl start scanopy-daemon msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:60072${CL}" echo -e "${INFO}${YW} Then create your account, and create a daemon in the UI.${CL}" ================================================ FILE: ct/scraparr.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: JasonGreenC # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/thecfu/scraparr APP="Scraparr" var_tags="${var_tags:-arr;monitoring}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-1024}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/scraparr/ ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "scraparr" "thecfu/scraparr"; then apt-get install -y libicu76 &>/dev/null msg_info "Stopping Services" systemctl stop scraparr msg_ok "Services Stopped" PYTHON_VERSION="3.12" setup_uv fetch_and_deploy_gh_release "scrappar" "thecfu/scraparr" "tarball" "latest" "/opt/scraparr" msg_info "Updating Scraparr" cd /opt/scraparr $STD uv venv --clear /opt/scraparr/.venv $STD /opt/scraparr/.venv/bin/python -m ensurepip --upgrade $STD /opt/scraparr/.venv/bin/python -m pip install --upgrade pip $STD /opt/scraparr/.venv/bin/python -m pip install -r /opt/scraparr/src/scraparr/requirements.txt chmod -R 755 /opt/scraparr msg_ok "Updated Scraparr" msg_info "Starting Services" systemctl start scraparr msg_ok "Services Started" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:7100${CL}" ================================================ FILE: ct/searxng.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (Canbiz) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/searxng/searxng APP="SearXNG" var_tags="${var_tags:-search}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-7}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /usr/local/searxng/searxng-src ]]; then msg_error "No ${APP} Installation Found!" exit fi chown -R searxng:searxng /usr/local/searxng/searxng-src if su -s /bin/bash -c "git -C /usr/local/searxng/searxng-src pull" searxng | grep -q 'Already up to date'; then msg_ok "There is currently no update available." exit fi msg_info "Updating SearXNG installation" msg_info "Stopping Service" systemctl stop searxng msg_ok "Stopped Service" msg_info "Updating SearXNG" $STD su -s /bin/bash searxng -c ' python3 -m venv /usr/local/searxng/searx-pyenv && . /usr/local/searxng/searx-pyenv/bin/activate && pip install -U pip setuptools wheel pyyaml lxml msgspec typing_extensions && pip install --use-pep517 --no-build-isolation -e /usr/local/searxng/searxng-src ' msg_ok "Updated SearXNG" msg_info "Starting Services" systemctl start searxng msg_ok "Started Services" msg_ok "Updated successfully!" exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8888${CL}" ================================================ FILE: ct/seaweedfs.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/seaweedfs/seaweedfs APP="SeaweedFS" var_tags="${var_tags:-storage;s3;filesystem}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-16}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" var_fuse="${var_fuse:-yes}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -f /opt/seaweedfs/weed ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "seaweedfs" "seaweedfs/seaweedfs"; then msg_info "Stopping Service" systemctl stop seaweedfs msg_ok "Stopped Service" fetch_and_deploy_gh_release "seaweedfs" "seaweedfs/seaweedfs" "prebuild" "latest" "/opt/seaweedfs" "linux_amd64.tar.gz" msg_info "Starting Service" systemctl start seaweedfs msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed Successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:9333${CL}" ================================================ FILE: ct/seelf.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Slaviša Arežina (tremor021) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/YuukanOO/seelf APP="seelf" var_tags="${var_tags:-server;docker}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-4096}" var_disk="${var_disk:-10}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/seelf ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "seelf" "YuukanOO/seelf"; then msg_info "Stopping Service" systemctl stop seelf msg_info "Stopped Service" msg_info "Updating seelf" cd /opt/seelf $STD make build msg_ok "Updated seelf" msg_info "Starting Service" systemctl start seelf msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8080${CL}" ================================================ FILE: ct/seerr.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: CrazyWolf13 # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://docs.seerr.dev/ APP="Seerr" var_tags="${var_tags:-media}" var_cpu="${var_cpu:-4}" var_ram="${var_ram:-4096}" var_disk="${var_disk:-12}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/seerr && ! -d /opt/jellyseerr && ! -d /opt/overseerr ]]; then msg_error "No ${APP} Installation Found!" exit fi # Start Migration from Jellyseerr if [[ -f /etc/systemd/system/jellyseerr.service ]]; then msg_info "Stopping Jellyseerr" $STD systemctl stop jellyseerr || true $STD systemctl disable jellyseerr || true [ -f /etc/systemd/system/jellyseerr.service ] && rm -f /etc/systemd/system/jellyseerr.service msg_ok "Stopped Jellyseerr" msg_info "Creating Backup (Patience)" tar -czf /opt/jellyseerr_backup_$(date +%Y%m%d_%H%M%S).tar.gz -C /opt jellyseerr msg_ok "Created Backup" msg_info "Migrating Jellyseerr to seerr" [ -d /opt/jellyseerr ] && mv /opt/jellyseerr /opt/seerr [ -d /etc/jellyseerr ] && mv /etc/jellyseerr /etc/seerr [ -f /etc/seerr/jellyseerr.conf ] && mv /etc/seerr/jellyseerr.conf /etc/seerr/seerr.conf cat </etc/systemd/system/seerr.service [Unit] Description=Seerr Service Wants=network-online.target After=network-online.target [Service] EnvironmentFile=/etc/seerr/seerr.conf Environment=NODE_ENV=production Type=exec Restart=on-failure WorkingDirectory=/opt/seerr ExecStart=/usr/bin/node dist/index.js [Install] WantedBy=multi-user.target EOF systemctl daemon-reload systemctl enable -q --now seerr msg_ok "Migrated Jellyserr to Seerr" fi # END Jellyseerr Migration # Start Migration from Overseerr if [[ -f /etc/systemd/system/overseerr.service ]]; then msg_info "Stopping Overseerr" $STD systemctl stop overseerr || true $STD systemctl disable overseerr || true [ -f /etc/systemd/system/overseerr.service ] && rm -f /etc/systemd/system/overseerr.service msg_ok "Stopped Overseerr" msg_info "Creating Backup (Patience)" tar -czf /opt/overseerr_backup_$(date +%Y%m%d_%H%M%S).tar.gz -C /opt overseerr msg_ok "Created Backup" msg_info "Migrating Overseerr to seerr" [ -d /opt/overseerr ] && mv /opt/overseerr /opt/seerr mkdir -p /etc/seerr cat </etc/seerr/seerr.conf ## Seerr's default port is 5055, if you want to use both, change this. ## specify on which port to listen PORT=5055 ## specify on which interface to listen, by default seerr listens on all interfaces #HOST=127.0.0.1 ## Uncomment if you want to force Node.js to resolve IPv4 before IPv6 (advanced users only) # FORCE_IPV4_FIRST=true EOF cat </etc/systemd/system/seerr.service [Unit] Description=Seerr Service Wants=network-online.target After=network-online.target [Service] EnvironmentFile=/etc/seerr/seerr.conf Environment=NODE_ENV=production Type=exec Restart=on-failure WorkingDirectory=/opt/seerr ExecStart=/usr/bin/node dist/index.js [Install] WantedBy=multi-user.target EOF systemctl daemon-reload systemctl enable -q --now seerr msg_ok "Migrated Overseerr to Seerr" fi # END Overseerr Migration if check_for_gh_release "seerr" "seerr-team/seerr"; then msg_info "Stopping Service" systemctl stop seerr msg_ok "Stopped Service" msg_info "Creating Backup" cp -a /opt/seerr/config /opt/seerr_backup msg_ok "Created Backup" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "seerr" "seerr-team/seerr" "tarball" ensure_dependencies build-essential python3-setuptools msg_info "Updating PNPM Version" pnpm_desired=$(grep -Po '"pnpm":\s*"\K[^"]+' /opt/seerr/package.json) NODE_VERSION="22" NODE_MODULE="pnpm@$pnpm_desired" setup_nodejs msg_ok "Updated PNPM Version" msg_info "Updating Seerr" cd /opt/seerr rm -rf dist .next node_modules export CYPRESS_INSTALL_BINARY=0 $STD pnpm install --frozen-lockfile export NODE_OPTIONS="--max-old-space-size=3072" $STD pnpm build msg_ok "Updated Seerr" msg_info "Restoring Backup" rm -rf /opt/seerr/config mv /opt/seerr_backup /opt/seerr/config msg_ok "Restored Backup" msg_info "Starting Service" systemctl start seerr msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:5055${CL}" ================================================ FILE: ct/semaphore.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: kristocopani # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://semaphoreui.com/ APP="Semaphore" var_tags="${var_tags:-dev_ops}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-4}" var_os="${var_os:-ubuntu}" var_version="${var_version:-24.04}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -f /etc/systemd/system/semaphore.service ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "semaphore" "semaphoreui/semaphore"; then if [[ -f /opt/semaphore/semaphore_db.bolt ]]; then msg_warn "WARNING: Due to bugs with BoltDB database, update script will move your application" msg_warn "to use SQLite database instead. Make sure you have a backup of your data!" echo "" read -r -p "${TAB3}Do you want to continue? (y/N): " CONFIRM if [[ ! "$CONFIRM" =~ ^[Yy]$ ]]; then exit 0 else msg_info "Moving from BoltDB to SQLite" sed -i \ -e 's|"bolt": {|"sqlite": {|' \ -e 's|/semaphore_db.bolt"|/database.sqlite"|' \ -e '/semaphore_db.bolt/d' \ -e '/"dialect"/d' \ -e '/^ },$/a\ "dialect": "sqlite",' \ /opt/semaphore/config.json msg_ok "Moved from BoltDB to SQLite" fi fi msg_info "Stopping Service" systemctl stop semaphore msg_ok "Stopped Service" fetch_and_deploy_gh_release "semaphore" "semaphoreui/semaphore" "binary" "latest" "/opt/semaphore" "semaphore_*_linux_arm64.deb" if [[ -f /opt/semaphore/semaphore_db.bolt ]]; then $STD semaphore migrate --from-boltdb /opt/semaphore/semaphore_db.bolt --config /opt/semaphore/config.json rm -f /opt/semaphore/semaphore_db.bolt fi msg_info "Starting Service" systemctl start semaphore msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:3000${CL}" ================================================ FILE: ct/sftpgo.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://sftpgo.com/ APP="SFTPGo" var_tags="${var_tags:-ftp;sftp}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /var ]]; then msg_error "No ${APP} Installation Found!" exit fi msg_info "Updating SFTPGo" $STD apt update $STD apt upgrade -y msg_ok "Updated SFTPGo" msg_ok "Updated successfully!" exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8080/web/admin${CL}" ================================================ FILE: ct/shelfmark.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: vhsdream # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/calibrain/shelfmark APP="shelfmark" var_tags="${var_tags:-ebooks}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-8}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/shelfmark ]]; then msg_error "No ${APP} Installation Found!" exit fi NODE_VERSION="24" setup_nodejs PYTHON_VERSION="3.14" setup_uv if check_for_gh_release "shelfmark" "calibrain/shelfmark"; then msg_info "Stopping Service(s)" systemctl stop shelfmark [[ -f /etc/systemd/system/chromium.service ]] && systemctl stop chromium msg_ok "Stopped Service(s)" [[ -f /etc/systemd/system/flaresolverr.service ]] && if check_for_gh_release "flaresolverr" "Flaresolverr/Flaresolverr"; then msg_info "Stopping FlareSolverr service" systemctl stop flaresolverr msg_ok "Stopped FlareSolverr service" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "flaresolverr" "FlareSolverr/FlareSolverr" "prebuild" "latest" "/opt/flaresolverr" "flaresolverr_linux_x64.tar.gz" msg_info "Starting FlareSolverr Service" systemctl start flaresolverr msg_ok "Started FlareSolverr Service" msg_ok "Updated FlareSolverr" fi cp /opt/shelfmark/start.sh /opt/start.sh.bak if command -v chromedriver &>/dev/null; then $STD apt remove -y chromium-driver fi CLEAN_INSTALL=1 fetch_and_deploy_gh_release "shelfmark" "calibrain/shelfmark" "tarball" "latest" "/opt/shelfmark" RELEASE_VERSION=$(cat "$HOME/.shelfmark") msg_info "Updating Shelfmark" export VIRTUAL_ENV=/opt/shelfmark/venv sed -i "s/^RELEASE_VERSION=.*/RELEASE_VERSION=$RELEASE_VERSION/" /etc/shelfmark/.env cd /opt/shelfmark/src/frontend $STD npm ci $STD npm run build mv /opt/shelfmark/src/frontend/dist /opt/shelfmark/frontend-dist cd /opt/shelfmark $STD uv venv -c ./venv $STD source ./venv/bin/activate if [[ $(sed -n '/_BYPASS=/s/[^=]*=//p' /etc/shelfmark/.env) == "true" ]] && [[ $(sed -n '/BYPASSER=/s/[^=]*=//p' /etc/shelfmark/.env) == "false" ]]; then $STD uv sync --active --locked --no-default-groups --extra browser else $STD uv sync --active --locked --no-default-groups fi mv /opt/start.sh.bak /opt/shelfmark/start.sh msg_ok "Updated Shelfmark" msg_info "Starting Service(s)" systemctl start shelfmark [[ -f /etc/systemd/system/chromium.service ]] && systemctl start chromium msg_ok "Started Service(s)" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8084${CL}" ================================================ FILE: ct/shinobi.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://shinobi.video/ APP="Shinobi" var_tags="${var_tags:-nvr}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-8}" var_os="${var_os:-ubuntu}" var_version="${var_version:-24.04}" var_unprivileged="${var_unprivileged:-0}" var_gpu="${var_gpu:-yes}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/Shinobi ]]; then msg_error "No ${APP} Installation Found!" exit fi setup_mariadb msg_info "Updating Shinobi" cd /opt/Shinobi $STD sh UPDATE.sh $STD pm2 flush $STD pm2 restart camera $STD pm2 restart cron msg_ok "Updated Shinobi" msg_ok "Updated successfully!" exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8080/super${CL}" ================================================ FILE: ct/shlink.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://shlink.io/ APP="Shlink" var_tags="${var_tags:-url-shortener;analytics;php}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/shlink ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "shlink" "shlinkio/shlink"; then msg_info "Stopping Service" systemctl stop shlink msg_ok "Stopped Service" msg_info "Backing up Data" cp /opt/shlink/.env /opt/shlink.env.bak cp -r /opt/shlink/data /opt/shlink_data_backup msg_ok "Backed up Data" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "shlink" "shlinkio/shlink" "prebuild" "latest" "/opt/shlink" "shlink*_php8.5_dist.zip" msg_info "Restoring Data" cp /opt/shlink.env.bak /opt/shlink/.env rm -f /opt/shlink.env.bak cp -r /opt/shlink_data_backup/. /opt/shlink/data rm -rf /opt/shlink_data_backup msg_ok "Restored Data" msg_info "Updating Application" cd /opt/shlink $STD php ./vendor/bin/rr get --no-interaction --location bin/ chmod +x bin/rr set -a source /opt/shlink/.env set +a $STD php vendor/bin/shlink-installer init --no-interaction --clear-db-cache --skip-download-geolite msg_ok "Updated Application" msg_info "Starting Service" systemctl start shlink msg_ok "Started Service" msg_ok "Updated successfully!" fi if [[ -d /opt/shlink-web-client ]]; then if check_for_gh_release "shlink-web-client" "shlinkio/shlink-web-client"; then CLEAN_INSTALL=1 fetch_and_deploy_gh_release "shlink-web-client" "shlinkio/shlink-web-client" "prebuild" "latest" "/opt/shlink-web-client" "shlink-web-client_*_dist.zip" msg_ok "Updated Web Client" fi fi exit } start build_container description msg_ok "Completed Successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access Shlink Web Client using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:3000${CL}" echo -e "${INFO}${YW} Shlink HTTP API:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8080${CL}" ================================================ FILE: ct/signoz.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Slaviša Arežina (tremor021) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://signoz.io/ APP="SigNoz" var_tags="${var_tags:-notes}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-4096}" var_disk="${var_disk:-20}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/signoz ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "signoz" "SigNoz/signoz"; then msg_info "Stopping Services" systemctl stop signoz systemctl stop signoz-otel-collector msg_ok "Stopped Services" fetch_and_deploy_gh_release "signoz" "SigNoz/signoz" "prebuild" "latest" "/opt/signoz" "signoz-community_linux_arm64.tar.gz" fetch_and_deploy_gh_release "signoz-otel-collector" "SigNoz/signoz-otel-collector" "prebuild" "latest" "/opt/signoz-otel-collector" "signoz-otel-collector_linux_arm64.tar.gz" fetch_and_deploy_gh_release "signoz-schema-migrator" "SigNoz/signoz-otel-collector" "prebuild" "latest" "/opt/signoz-schema-migrator" "signoz-schema-migrator_linux_arm64.tar.gz" msg_info "Updating SigNoz" cd /opt/signoz-schema-migrator/bin $STD ./signoz-schema-migrator sync --dsn="tcp://localhost:9000?password=" --replication=true --up= $STD ./signoz-schema-migrator async --dsn="tcp://localhost:9000?password=" --replication=true --up= msg_ok "Updated SigNoz" msg_info "Starting Services" systemctl start signoz-otel-collector systemctl start signoz msg_ok "Started Services" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8080${CL}" ================================================ FILE: ct/silverbullet.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Dominik Siebel (dsiebel) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://silverbullet.md APP="Silverbullet" var_tags="${var_tags:-notes}" var_cpu="${var_cpu:-1}" var_disk="${var_disk:-2}" var_ram="${var_ram:-512}" var_os="${var_os:-debian}" var_version="${var_version:-13}" header_info "${APP}" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/silverbullet ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "silverbullet" "silverbulletmd/silverbullet"; then msg_info "Stopping Service" systemctl stop silverbullet msg_ok "Stopped Service" fetch_and_deploy_gh_release "silverbullet" "silverbulletmd/silverbullet" "prebuild" "latest" "/opt/silverbullet/bin" "silverbullet-server-linux-aarch64.zip" msg_info "Starting Service" systemctl start silverbullet msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:3000${CL}" ================================================ FILE: ct/slskd.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: vhsdream # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/slskd/slskd, https://github.com/mrusse/soularr APP="slskd" var_tags="${var_tags:-arr;p2p}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-512}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/slskd ]]; then msg_error "No Slskd Installation Found!" exit fi if check_for_gh_release "Slskd" "slskd/slskd"; then msg_info "Stopping Service(s)" systemctl stop slskd [[ -f /etc/systemd/system/soularr.service ]] && systemctl stop soularr.timer soularr.service msg_ok "Stopped Service(s)" msg_info "Backing up config" cp /opt/slskd/config/slskd.yml /opt/slskd.yml.bak msg_ok "Backed up config" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "Slskd" "slskd/slskd" "prebuild" "latest" "/opt/slskd" "slskd-*-linux-arm64.zip" msg_info "Restoring config" mv /opt/slskd.yml.bak /opt/slskd/config/slskd.yml # Migrate 0.25.0 breaking config key renames sed -i 's/^global:/transfers:/' /opt/slskd/config/slskd.yml sed -i 's/^integration:/integrations:/' /opt/slskd/config/slskd.yml msg_ok "Restored config" msg_info "Starting Service(s)" systemctl start slskd [[ -f /etc/systemd/system/soularr.service ]] && systemctl start soularr.timer msg_ok "Started Service(s)" msg_ok "Updated Slskd successfully!" fi [[ -d /opt/soularr ]] && if check_for_gh_release "Soularr" "mrusse/soularr"; then if systemctl is-active soularr.timer >/dev/null; then msg_info "Stopping Timer and Service" systemctl stop soularr.timer soularr.service msg_ok "Stopped Timer and Service" fi msg_info "Backing up Soularr config" cp /opt/soularr/config.ini /opt/soularr_config.ini.bak cp /opt/soularr/run.sh /opt/soularr_run.sh.bak msg_ok "Backed up Soularr config" PYTHON_VERSION="3.11" setup_uv CLEAN_INSTALL=1 fetch_and_deploy_gh_release "Soularr" "mrusse/soularr" "tarball" "latest" "/opt/soularr" msg_info "Updating Soularr" cd /opt/soularr $STD uv venv -c venv $STD source venv/bin/activate $STD uv pip install -r requirements.txt deactivate msg_ok "Updated Soularr" msg_info "Restoring Soularr config" mv /opt/soularr_config.ini.bak /opt/soularr/config.ini mv /opt/soularr_run.sh.bak /opt/soularr/run.sh msg_ok "Restored Soularr config" msg_info "Starting Soularr Timer" systemctl restart soularr.timer msg_ok "Started Soularr Timer" msg_ok "Updated Soularr successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:5030${CL}" ================================================ FILE: ct/smokeping.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://oss.oetiker.ch/smokeping/ APP="SmokePing" var_tags="${var_tags:-network}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-512}" var_disk="${var_disk:-2}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if ! command -v smokeping &>/dev/null; then msg_error "No ${APP} Installation Found!" exit fi msg_info "Updating ${APP}" $STD apt update $STD apt -y upgrade msg_ok "Updated successfully!" exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}/smokeping${CL}" ================================================ FILE: ct/snipeit.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Michel Roegl-Brunner (michelroegl-brunner) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://snipeitapp.com/ APP="SnipeIT" var_tags="${var_tags:-asset-management;foss}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/snipe-it ]]; then msg_error "No ${APP} Installation Found!" exit fi setup_mariadb if ! grep -q "client_max_body_size[[:space:]]\+100M;" /etc/nginx/conf.d/snipeit.conf; then sed -i '/index index.php;/i \ client_max_body_size 100M;' /etc/nginx/conf.d/snipeit.conf fi if check_for_gh_release "snipe-it" "grokability/snipe-it"; then msg_info "Stopping Services" systemctl stop nginx msg_ok "Services Stopped" msg_info "Creating Backup" mv /opt/snipe-it /opt/snipe-it-backup msg_ok "Created Backup" fetch_and_deploy_gh_release "snipe-it" "grokability/snipe-it" "tarball" [[ "$(php -v 2>/dev/null)" == PHP\ 8.2* ]] && PHP_VERSION="8.3" PHP_FPM="YES" PHP_MODULE="ldap,soap,xsl" setup_php sed -i 's/php8.2/php8.3/g' /etc/nginx/conf.d/snipeit.conf setup_composer msg_info "Updating Snipe-IT" $STD apt update $STD apt -y upgrade cp /opt/snipe-it-backup/.env /opt/snipe-it/.env cp -r /opt/snipe-it-backup/public/uploads/. /opt/snipe-it/public/uploads/ cp -r /opt/snipe-it-backup/storage/private_uploads/. /opt/snipe-it/storage/private_uploads/ cd /opt/snipe-it/ export COMPOSER_ALLOW_SUPERUSER=1 $STD composer install --no-dev --optimize-autoloader --no-interaction $STD composer dump-autoload $STD php artisan migrate --force $STD php artisan config:clear $STD php artisan route:clear $STD php artisan cache:clear $STD php artisan view:clear chown -R www-data: /opt/snipe-it chmod -R 755 /opt/snipe-it rm -rf /opt/snipe-it-backup msg_ok "Updated Snipe-IT" msg_info "Starting Service" systemctl start nginx msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}${CL}" ================================================ FILE: ct/snowshare.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: TuroYT # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/TuroYT/snowshare APP="SnowShare" var_tags="${var_tags:-file-sharing}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-20}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/snowshare ]]; then msg_error "No ${APP} Installation Found!" exit fi NODE_VERSION="24" setup_nodejs if check_for_gh_release "snowshare" "TuroYT/snowshare"; then msg_info "Stopping Service" systemctl stop snowshare msg_ok "Stopped Service" msg_info "Backing up uploads" [ -d /opt/snowshare/uploads ] && cp -a /opt/snowshare/uploads /opt/.snowshare_uploads_backup msg_ok "Uploads backed up" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "snowshare" "TuroYT/snowshare" "tarball" msg_info "Restoring uploads" [ -d /opt/.snowshare_uploads_backup ] && rm -rf /opt/snowshare/uploads && cp -a /opt/.snowshare_uploads_backup /opt/snowshare/uploads msg_ok "Uploads restored" msg_info "Updating Snowshare" cd /opt/snowshare $STD npm ci $STD npx prisma generate $STD npm run build msg_ok "Updated Snowshare" msg_info "Starting Service" systemctl start snowshare msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:3000${CL}" ================================================ FILE: ct/solidtime.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://www.solidtime.io/ APP="SolidTime" var_tags="${var_tags:-time-tracking;productivity;business}" var_cpu="${var_cpu:-4}" var_ram="${var_ram:-4096}" var_disk="${var_disk:-8}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/solidtime ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "solidtime" "solidtime-io/solidtime"; then msg_info "Stopping Services" systemctl stop caddy msg_ok "Stopped Services" msg_info "Backing up Data" cp /opt/solidtime/.env /opt/solidtime.env.bak cp -r /opt/solidtime/storage /opt/solidtime_storage_backup msg_ok "Backed up Data" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "solidtime" "solidtime-io/solidtime" "tarball" msg_info "Restoring Data" cp /opt/solidtime.env.bak /opt/solidtime/.env rm -f /opt/solidtime.env.bak cp -r /opt/solidtime_storage_backup/. /opt/solidtime/storage rm -rf /opt/solidtime_storage_backup msg_ok "Restored Data" msg_info "Updating Application" cd /opt/solidtime $STD composer install --no-dev --optimize-autoloader $STD npm install $STD npm run build $STD php artisan migrate --force $STD php artisan optimize:clear chown -R www-data:www-data /opt/solidtime msg_ok "Updated Application" msg_info "Starting Services" systemctl start caddy msg_ok "Started Services" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed Successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}${CL}" echo -e "${INFO}${YW}HTTPS is not enabled by default (use domain + reverse proxy/TLS if needed).${CL}" ================================================ FILE: ct/sonarqube.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: prop4n # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://docs.sonarsource.com/sonarqube-server APP="SonarQube" var_tags="${var_tags:-automation}" var_cpu="${var_cpu:-4}" var_ram="${var_ram:-6144}" var_disk="${var_disk:-25}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/sonarqube ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "sonarqube" "SonarSource/sonarqube"; then msg_info "Stopping Service" systemctl stop sonarqube msg_ok "Stopped Service" msg_info "Creating Backup" BACKUP_DIR="/opt/sonarqube-backup" mv /opt/sonarqube ${BACKUP_DIR} msg_ok "Created Backup" msg_info "Updating SonarQube" temp_file=$(mktemp) RELEASE=$(get_latest_github_release "SonarSource/sonarqube") curl -fsSL "https://binaries.sonarsource.com/Distribution/sonarqube/sonarqube-${RELEASE}.zip" -o $temp_file unzip -q "$temp_file" -d /opt rm -f "$temp_file" mv /opt/sonarqube-${RELEASE} /opt/sonarqube echo "${RELEASE}" > ~/.sonarqube msg_ok "Updated SonarQube" msg_info "Restoring Backup" cp -rp ${BACKUP_DIR}/data/ /opt/sonarqube/data/ cp -rp ${BACKUP_DIR}/extensions/ /opt/sonarqube/extensions/ cp -p ${BACKUP_DIR}/conf/sonar.properties /opt/sonarqube/conf/sonar.properties rm -rf ${BACKUP_DIR} chown -R sonarqube:sonarqube /opt/sonarqube msg_ok "Restored Backup" msg_info "Starting Service" systemctl start sonarqube msg_ok "Service Started" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:9000${CL}" ================================================ FILE: ct/sonarr.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://sonarr.tv/ APP="Sonarr" var_tags="${var_tags:-arr}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-1024}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /var/lib/sonarr/ ]]; then msg_error "No ${APP} Installation Found!" exit fi apt-get install -y libicu76 &>/dev/null msg_info "Stopping Service" systemctl stop sonarr msg_ok "Stopped Service" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "Sonarr" "Sonarr/Sonarr" "prebuild" "latest" "/opt/Sonarr" "Sonarr.main.*.linux-arm64.tar.gz" msg_info "Starting Service" systemctl start sonarr msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8989${CL}" ================================================ FILE: ct/sonobarr.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: GoldenSpringness # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/Dodelidoo-Labs/sonobarr APP="sonobarr" var_tags="${var_tags:-music;discovery}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-1024}" var_disk="${var_disk:-20}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d "/opt/sonobarr" ]]; then msg_error "No sonobarr Installation Found!" exit fi PYTHON_VERSION="3.12" setup_uv if check_for_gh_release "sonobarr" "Dodelidoo-Labs/sonobarr"; then msg_info "Stopping Service" systemctl stop sonobarr msg_ok "Stopped Service" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "sonobarr" "Dodelidoo-Labs/sonobarr" "tarball" msg_info "Updating sonobarr" $STD uv venv -c /opt/sonobarr/venv $STD source /opt/sonobarr/venv/bin/activate $STD uv pip install --no-cache-dir -r /opt/sonobarr/requirements.txt sed -i "/release_version/s/=.*/=$(cat ~/.sonobarr)/" /etc/sonobarr/.env msg_ok "Updated sonobarr" msg_info "Starting Service" systemctl start sonobarr msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed Successfully!\n" echo -e "${CREATING}${GN}sonobarr setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:5000${CL}" ================================================ FILE: ct/soulsync.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/Nezreka/SoulSync APP="SoulSync" var_tags="${var_tags:-music;automation;media}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-8}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -f ~/.soulsync ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "soulsync" "Nezreka/SoulSync"; then msg_info "Stopping Service" systemctl stop soulsync msg_ok "Stopped Service" msg_info "Backing up Data" mv /opt/soulsync/config /opt/soulsync-config.bak mv /opt/soulsync/data /opt/soulsync-data.bak msg_ok "Backed up Data" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "soulsync" "Nezreka/SoulSync" "tarball" msg_info "Updating Python Dependencies" cd /opt/soulsync $STD uv venv --clear /opt/soulsync/.venv --python 3.11 $STD /opt/soulsync/.venv/bin/pip install -r requirements.txt msg_ok "Updated Python Dependencies" mv /opt/soulsync-config.bak /opt/soulsync/config mv /opt/soulsync-data.bak /opt/soulsync/data msg_info "Starting Service" systemctl start soulsync msg_ok "Started Service" msg_ok "Updated ${APP}" fi exit } start build_container description msg_ok "Completed Successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8008${CL}" ================================================ FILE: ct/sparkyfitness.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Tom Frenzel (tomfrenzel) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/CodeWithCJ/SparkyFitness APP="SparkyFitness" var_tags="${var_tags:-health;fitness}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/sparkyfitness ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "sparkyfitness" "CodeWithCJ/SparkyFitness"; then msg_info "Stopping Services" systemctl stop sparkyfitness-server nginx msg_ok "Stopped Services" msg_info "Backing up data" mkdir -p /opt/sparkyfitness_backup if [[ -d /opt/sparkyfitness/SparkyFitnessServer/uploads ]]; then cp -r /opt/sparkyfitness/SparkyFitnessServer/uploads /opt/sparkyfitness_backup/ fi if [[ -d /opt/sparkyfitness/SparkyFitnessServer/backup ]]; then cp -r /opt/sparkyfitness/SparkyFitnessServer/backup /opt/sparkyfitness_backup/ fi msg_ok "Backed up data" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "sparkyfitness" "CodeWithCJ/SparkyFitness" "tarball" PNPM_VERSION="$(jq -r '.packageManager | split("@")[1]' /opt/sparkyfitness/package.json)" NODE_VERSION="25" NODE_MODULE="pnpm@${PNPM_VERSION}" setup_nodejs msg_info "Updating Sparky Fitness Backend" cd /opt/sparkyfitness/SparkyFitnessServer $STD pnpm install msg_ok "Updated Sparky Fitness Backend" msg_info "Updating Sparky Fitness Frontend (Patience)" cd /opt/sparkyfitness $STD pnpm install cd /opt/sparkyfitness/SparkyFitnessFrontend $STD pnpm run build cp -a /opt/sparkyfitness/SparkyFitnessFrontend/dist/. /var/www/sparkyfitness/ msg_ok "Updated Sparky Fitness Frontend" msg_info "Refreshing SparkyFitness Service" cat </etc/systemd/system/sparkyfitness-server.service [Unit] Description=SparkyFitness Backend Service After=network.target postgresql.service Requires=postgresql.service [Service] Type=simple WorkingDirectory=/opt/sparkyfitness/SparkyFitnessServer EnvironmentFile=/etc/sparkyfitness/.env ExecStart=/opt/sparkyfitness/SparkyFitnessServer/node_modules/.bin/tsx SparkyFitnessServer.js Restart=always RestartSec=5 [Install] WantedBy=multi-user.target EOF systemctl daemon-reload msg_ok "Refreshed SparkyFitness Service" msg_info "Restoring data" cp -r /opt/sparkyfitness_backup/. /opt/sparkyfitness/SparkyFitnessServer/ rm -rf /opt/sparkyfitness_backup msg_ok "Restored data" msg_info "Starting Services" $STD systemctl start sparkyfitness-server nginx msg_ok "Started Services" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}${CL}" ================================================ FILE: ct/speedtest-tracker.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: AlphaLawless # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/alexjustesen/speedtest-tracker APP="Speedtest-Tracker" var_tags="${var_tags:-monitoring}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/speedtest-tracker ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "speedtest-tracker" "alexjustesen/speedtest-tracker"; then PHP_VERSION="8.4" PHP_FPM="YES" setup_php setup_composer NODE_VERSION="22" setup_nodejs setcap cap_net_raw+ep /bin/ping msg_info "Stopping Service" systemctl stop speedtest-tracker msg_ok "Stopped Service" msg_info "Updating Speedtest CLI" $STD apt update $STD apt --only-upgrade install -y speedtest msg_ok "Updated Speedtest CLI" msg_info "Creating Backup" cp -r /opt/speedtest-tracker /opt/speedtest-tracker-backup msg_ok "Backup Created" fetch_and_deploy_gh_release "speedtest-tracker" "alexjustesen/speedtest-tracker" "tarball" "latest" "/opt/speedtest-tracker" msg_info "Updating Speedtest Tracker" cp -r /opt/speedtest-tracker-backup/.env /opt/speedtest-tracker/.env cd /opt/speedtest-tracker export COMPOSER_ALLOW_SUPERUSER=1 $STD composer install --optimize-autoloader --no-dev $STD npm ci $STD npm run build $STD php artisan migrate --force $STD php artisan config:clear $STD php artisan cache:clear $STD php artisan view:clear chown -R www-data:www-data /opt/speedtest-tracker chmod -R 755 /opt/speedtest-tracker/storage chmod -R 755 /opt/speedtest-tracker/bootstrap/cache msg_ok "Updated Speedtest Tracker" msg_info "Starting Service" systemctl start speedtest-tracker msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}${CL}" ================================================ FILE: ct/split-pro.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: johanngrobe # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/oss-apps/split-pro APP="Split-Pro" var_tags="${var_tags:-finance;expense-sharing}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-4096}" var_disk="${var_disk:-6}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/split-pro ]]; then msg_error "No Split Pro Installation Found!" exit fi if check_for_gh_release "split-pro" "oss-apps/split-pro"; then msg_info "Stopping Service" systemctl stop split-pro msg_ok "Stopped Service" msg_info "Backing up Data" cp /opt/split-pro/.env /opt/split-pro.env msg_ok "Backed up Data" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "split-pro" "oss-apps/split-pro" "tarball" msg_info "Building Application" cd /opt/split-pro $STD pnpm install --frozen-lockfile $STD pnpm build cp /opt/split-pro.env /opt/split-pro/.env rm -f /opt/split-pro.env ln -sf /opt/split-pro_data/uploads /opt/split-pro/uploads $STD pnpm exec prisma migrate deploy msg_ok "Built Application" msg_info "Starting Service" systemctl start split-pro msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:3000${CL}" ================================================ FILE: ct/splunk-enterprise.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: rcastley # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.splunk.com/en_us/download.html APP="Splunk-Enterprise" var_tags="${var_tags:-monitoring}" var_cpu="${var_cpu:-4}" var_ram="${var_ram:-8192}" var_disk="${var_disk:-40}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/splunk ]]; then msg_error "No ${APP} Installation Found!" exit fi msg_error "Currently we don't provide an update function for this ${APP}." exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW}Access the Splunk Enterprise Web interface using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8000${CL}" ================================================ FILE: ct/spoolman.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: MickLesk (Canbiz) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/Donkie/Spoolman APP="Spoolman" var_tags="${var_tags:-3d-printing}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-1024}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/spoolman ]]; then msg_error "No ${APP} Installation Found!" exit fi PYTHON_VERSION="3.14" setup_uv if check_for_gh_release "spoolman" "Donkie/Spoolman"; then msg_info "Stopping Service" systemctl stop spoolman msg_ok "Stopped Service" msg_info "Creating Backup" [ -d /opt/spoolman_bak ] && rm -rf /opt/spoolman_bak mv /opt/spoolman /opt/spoolman_bak msg_ok "Created Backup" fetch_and_deploy_gh_release "spoolman" "Donkie/Spoolman" "prebuild" "latest" "/opt/spoolman" "spoolman.zip" msg_info "Updating Spoolman" cd /opt/spoolman $STD uv sync --locked --no-install-project $STD uv sync --locked cp /opt/spoolman_bak/.env /opt/spoolman sed -i 's|^ExecStart=.*|ExecStart=/usr/bin/bash /opt/spoolman/scripts/start.sh|' /etc/systemd/system/spoolman.service msg_ok "Updated Spoolman" msg_info "Starting Service" systemctl start spoolman msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:7912${CL}" ================================================ FILE: ct/sportarr.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Slaviša Arežina (tremor021) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/Sportarr/Sportarr APP="Sportarr" var_tags="${var_tags:-arr}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-8}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/sportarr ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "sportarr" "Sportarr/Sportarr"; then msg_info "Stopping Services" systemctl stop sportarr msg_ok "Stopped Services" fetch_and_deploy_gh_release "sportarr" "Sportarr/Sportarr" "prebuild" "latest" "/opt/sportarr" "Sportarr-linux-arm64-*.tar.gz" msg_info "Starting Services" systemctl start sportarr msg_ok "Started Services" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:1867${CL}" ================================================ FILE: ct/sqlserver2022.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Kristian Skov # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.microsoft.com/en-us/sql-server/sql-server-2022 APP="SQL Server 2022" var_tags="${var_tags:-sql}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-10}" var_os="${var_os:-ubuntu}" var_version="${var_version:-22.04}" var_unprivileged="${var_unprivileged:-0}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/mssql ]]; then msg_error "No ${APP} Installation Found!" exit fi msg_info "Updating SQL Server 2022" rm -f /etc/profile.d/debuginfod.sh /etc/profile.d/debuginfod.csh $STD apt update $STD apt -y upgrade msg_ok "Updated successfully!" exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following IP:${CL}" echo -e "${TAB}${GATEWAY}${BGN}${IP}:1433${CL}" ================================================ FILE: ct/sqlserver2025.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.microsoft.com/en-us/sql-server/sql-server-2025 APP="SQL Server 2025" var_tags="${var_tags:-sql;database}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-10}" var_os="${var_os:-ubuntu}" var_version="${var_version:-24.04}" var_unprivileged="${var_unprivileged:-0}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/mssql ]]; then msg_error "No ${APP} Installation Found!" exit fi msg_info "Updating SQL Server 2025" rm -f /etc/profile.d/debuginfod.sh /etc/profile.d/debuginfod.csh $STD apt update $STD apt -y upgrade msg_ok "Updated successfully!" exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following IP:${CL}" echo -e "${TAB}${GATEWAY}${BGN}${IP}:1433${CL}" ================================================ FILE: ct/step-ca.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Joerg Heinemann (heinemannj) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/smallstep/certificates APP="step-ca" var_tags="${var_tags:-certificate-authority;pki;acme-server}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-512}" var_disk="${var_disk:-2}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -f /etc/apt/sources.list.d/smallstep.sources ]]; then msg_error "No ${APP} Installation Found!" exit fi msg_info "Updating step-ca and step-cli" $STD apt update $STD apt upgrade -y step-ca step-cli # Patch for making $STD happy (/usr/bin/step is a symlink to /usr/bin/step-cli) STEPBIN="$(which step)" rm -f "$STEPBIN" cp -f "$(which step-cli)" "$STEPBIN" $STD systemctl restart step-ca msg_ok "Updated step-ca and step-cli" if check_for_gh_release "step-badger" "lukasz-lobocki/step-badger"; then fetch_and_deploy_gh_release "step-badger" "lukasz-lobocki/step-badger" "prebuild" "latest" "/opt/step-badger" "step-badger_Linux_x86_64.tar.gz" msg_ok "Updated step-badger" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}https://${IP}/provisioners${CL}" ================================================ FILE: ct/stirling-pdf.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.stirlingpdf.com/ APP="Stirling-PDF" var_tags="${var_tags:-pdf-editor}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-8}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/Stirling-PDF ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "stirling-pdf" "Stirling-Tools/Stirling-PDF"; then if [[ ! -f /etc/systemd/system/unoserver.service ]]; then msg_custom "⚠️ " "\e[33m" "Legacy installation detected – please recreate the container using the latest install script." exit 0 fi PYTHON_VERSION="3.12" setup_uv JAVA_VERSION="25" setup_java msg_info "Stopping Services" systemctl stop stirlingpdf libreoffice-listener unoserver msg_ok "Stopped Services" if [[ -f ~/.Stirling-PDF-login ]]; then USE_ORIGINAL_FILENAME=true fetch_and_deploy_gh_release "stirling-pdf" "Stirling-Tools/Stirling-PDF" "singlefile" "latest" "/opt/Stirling-PDF" "Stirling-PDF-with-login.jar" mv /opt/Stirling-PDF/Stirling-PDF-with-login.jar /opt/Stirling-PDF/Stirling-PDF.jar else USE_ORIGINAL_FILENAME=true fetch_and_deploy_gh_release "stirling-pdf" "Stirling-Tools/Stirling-PDF" "singlefile" "latest" "/opt/Stirling-PDF" "Stirling-PDF.jar" fi msg_info "Refreshing Font Cache" $STD fc-cache -fv msg_ok "Font Cache Updated" msg_info "Starting Services" systemctl start stirlingpdf libreoffice-listener unoserver msg_ok "Started Services" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8080${CL}" ================================================ FILE: ct/storybook.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/storybookjs/storybook APP="Storybook" var_tags="${var_tags:-dev-tools;frontend;ui}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-8}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -f /opt/storybook/.projectpath ]]; then msg_error "No ${APP} Installation Found!" exit fi PROJECT_PATH=$(cat /opt/storybook/.projectpath) if [[ ! -d "$PROJECT_PATH" ]]; then msg_error "Project directory not found: $PROJECT_PATH" exit fi msg_info "Updating Storybook" cd "$PROJECT_PATH" $STD npx storybook@latest upgrade --yes msg_ok "Updated Storybook" exit } start build_container description msg_ok "Completed Successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:6006${CL}" ================================================ FILE: ct/storyteller.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://gitlab.com/storyteller-platform/storyteller APP="Storyteller" var_tags="${var_tags:-media;ebook;audiobook}" var_cpu="${var_cpu:-4}" var_ram="${var_ram:-10240}" var_disk="${var_disk:-20}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/storyteller ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gl_release "storyteller" "storyteller-platform/storyteller"; then msg_info "Stopping Service" systemctl stop storyteller msg_ok "Stopped Service" msg_info "Backing up Data" cp /opt/storyteller/.env /opt/storyteller_env.bak msg_ok "Backed up Data" CLEAN_INSTALL=1 fetch_and_deploy_gl_release "storyteller" "storyteller-platform/storyteller" "tarball" "latest" "/opt/storyteller" msg_info "Restoring Configuration" mv /opt/storyteller_env.bak /opt/storyteller/.env msg_ok "Restored Configuration" msg_info "Rebuilding Storyteller" cd /opt/storyteller export NODE_OPTIONS="--max-old-space-size=4096" $STD yarn install --network-timeout 600000 $STD gcc -g -fPIC -rdynamic -shared web/sqlite/uuid.c -o web/sqlite/uuid.c.so export CI=1 export NODE_ENV=production export NEXT_TELEMETRY_DISABLED=1 export SQLITE_NATIVE_BINDING=/opt/storyteller/node_modules/better-sqlite3/build/Release/better_sqlite3.node $STD yarn workspaces foreach -Rpt --from @storyteller-platform/web --exclude @storyteller-platform/eslint run build mkdir -p /opt/storyteller/web/.next/standalone/web/.next/static cp -rT /opt/storyteller/web/.next/static /opt/storyteller/web/.next/standalone/web/.next/static if [[ -d /opt/storyteller/web/public ]]; then mkdir -p /opt/storyteller/web/.next/standalone/web/public cp -rT /opt/storyteller/web/public /opt/storyteller/web/.next/standalone/web/public fi mkdir -p /opt/storyteller/web/.next/standalone/web/migrations cp -rT /opt/storyteller/web/migrations /opt/storyteller/web/.next/standalone/web/migrations mkdir -p /opt/storyteller/web/.next/standalone/web/sqlite cp -rT /opt/storyteller/web/sqlite /opt/storyteller/web/.next/standalone/web/sqlite ln -sf /opt/storyteller/.env /opt/storyteller/web/.next/standalone/web/.env msg_ok "Rebuilt Storyteller" msg_info "Starting Service" systemctl start storyteller msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed Successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8001${CL}" ================================================ FILE: ct/strapi.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: pespinel # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://strapi.io/ APP="Strapi" var_tags="${var_tags:-cms}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-4096}" var_disk="${var_disk:-8}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -f /etc/systemd/system/strapi.service ]]; then msg_error "No ${APP} Installation Found!" exit fi NODE_VERSION="24" setup_nodejs msg_info "Stopping Strapi" systemctl stop strapi msg_ok "Stopped Strapi" msg_info "Updating Strapi" cd /opt/strapi $STD npx @strapi/upgrade minor --yes msg_ok "Updated Strapi" msg_info "Building Strapi" export NODE_OPTIONS="--max-old-space-size=3072" $STD npm run build msg_ok "Built Strapi" msg_info "Starting Strapi" systemctl start strapi msg_ok "Started Strapi" msg_ok "Updated successfully!" exit } start build_container description msg_ok "Completed Successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:1337${CL}" ================================================ FILE: ct/streamlink-webui.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: CrazyWolf13 # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/CrazyWolf13/streamlink-webui APP="streamlink-webui" var_tags="${var_tags:-download;streaming}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-10}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/streamlink-webui ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "streamlink-webui" "CrazyWolf13/streamlink-webui"; then msg_info "Stopping Service" systemctl stop streamlink-webui msg_info "Stopped Service" NODE_VERSION="22" NODE_MODULE="yarn" setup_nodejs setup_uv CLEAN_INSTALL=1 fetch_and_deploy_gh_release "streamlink-webui" "CrazyWolf13/streamlink-webui" "tarball" msg_info "Updating streamlink-webui" $STD uv venv --clear /opt/streamlink-webui/backend/src/.venv source /opt/streamlink-webui/backend/src/.venv/bin/activate $STD uv pip install -r /opt/streamlink-webui/backend/src/requirements.txt --python=/opt/streamlink-webui/backend/src/.venv cd /opt/streamlink-webui/frontend/src $STD yarn install $STD yarn build chmod +x /opt/streamlink-webui/start.sh msg_ok "Updated streamlink-webui" msg_info "Starting Service" systemctl start streamlink-webui msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8000${CL}" ================================================ FILE: ct/stylus.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: luismco # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/mmastrac/stylus APP="Stylus" var_tags="${var_tags:-network}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-1024}" var_disk="${var_disk:-2}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" var_fuse="${var_fuse:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/stylus ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "stylus" "mmastrac/stylus"; then msg_info "Stopping Service" systemctl stop stylus msg_info "Stopped Service" fetch_and_deploy_gh_release "stylus" "mmastrac/stylus" "singlefile" "latest" "/usr/bin/" "*_linux_arm64" msg_info "Starting Service" systemctl start stylus msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8000${CL}" ================================================ FILE: ct/sure.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: vhsdream # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://sure.am APP="Sure" var_tags="${var_tags:-finance}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-6}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/sure ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "Sure" "we-promise/sure"; then if [[ ! -f /etc/systemd/system/sure-worker.service ]]; then cat </etc/systemd/system/sure-worker.service [Unit] Description=Sure Background Worker (Sidekiq) After=network.target redis-server.service [Service] Type=simple WorkingDirectory=/opt/sure Environment=RAILS_ENV=production Environment=BUNDLE_DEPLOYMENT=1 Environment=BUNDLE_WITHOUT=development Environment=PATH=/root/.rbenv/shims:/root/.rbenv/bin:/usr/bin:/usr/local/bin:/sbin:/bin EnvironmentFile=/etc/sure/.env ExecStart=/opt/sure/bin/bundle exec sidekiq -e production Restart=always RestartSec=5 StandardOutput=journal StandardError=journal [Install] WantedBy=multi-user.target EOF systemctl enable -q sure-worker msg_info "Stopping Service" $STD systemctl stop sure msg_ok "Stopped Service" else msg_info "Stopping services" $STD systemctl stop sure-worker sure msg_ok "Stopped services" fi CLEAN_INSTALL=1 fetch_and_deploy_gh_release "Sure" "we-promise/sure" "tarball" "latest" "/opt/sure" RUBY_VERSION="$(cat /opt/sure/.ruby-version)" RUBY_INSTALL_RAILS=false setup_ruby msg_info "Updating Sure" source ~/.profile cd /opt/sure export RAILS_ENV=production export BUNDLE_DEPLOYMENT=1 export BUNDLE_WITHOUT=development $STD ./bin/bundle install $STD ./bin/bundle exec bootsnap precompile --gemfile -j 0 $STD ./bin/bundle exec bootsnap precompile -j 0 app/ lib/ export SECRET_KEY_BASE_DUMMY=1 && $STD ./bin/rails assets:precompile unset SECRET_KEY_BASE_DUMMY msg_ok "Updated Sure" msg_info "Starting Services" $STD systemctl start sure sure-worker msg_ok "Started Services" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:3000${CL}" ================================================ FILE: ct/swizzin.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: EEJoshua # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://swizzin.ltd/ APP="Swizzin" var_tags="${var_tags:-seedbox}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-4096}" var_disk="${var_disk:-20}" var_os="${var_os:-debian}" var_version="${var_version:-12}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if ! command -v sudo box >/dev/null 2>&1; then msg_error "No ${APP} installation found!" exit fi msg_info "Running 'sudo box update' inside the container" $STD sudo box update msg_ok "Updated successfully!" exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW}If installed panel, access through the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}${CL}" ================================================ FILE: ct/syncthing.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://syncthing.net/ APP="Syncthing" var_tags="${var_tags:-sync}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-8}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -f /etc/apt/sources.list.d/syncthing.list && ! -f /etc/apt/sources.list.d/syncthing.sources ]]; then msg_error "No ${APP} Installation Found!" exit fi msg_info "Updating Syncthing" $STD apt update $STD apt upgrade -y msg_ok "Updated successfully!" exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8384${CL}" ================================================ FILE: ct/tandoor.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (Canbiz) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://tandoor.dev/ APP="Tandoor" var_tags="${var_tags:-recipes}" var_cpu="${var_cpu:-4}" var_ram="${var_ram:-4096}" var_disk="${var_disk:-10}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/tandoor ]]; then msg_error "No ${APP} Installation Found!" exit fi if [[ ! -f ~/.tandoor ]]; then msg_error "v1 Installation found, please export your data and create an new LXC." exit fi if ! grep -q "^ALLOWED_HOSTS=" /opt/tandoor/.env; then echo "ALLOWED_HOSTS=${LOCAL_IP}" >>/opt/tandoor/.env fi if check_for_gh_release "tandoor" "TandoorRecipes/recipes"; then msg_info "Stopping Service" systemctl stop tandoor msg_ok "Stopped Service" msg_info "Creating Backup" mv /opt/tandoor /opt/tandoor.bak msg_ok "Backup Created" NODE_VERSION="22" NODE_MODULE="yarn" setup_nodejs PYTHON_VERSION="3.13" setup_uv fetch_and_deploy_gh_release "tandoor" "TandoorRecipes/recipes" "tarball" "latest" "/opt/tandoor" msg_info "Updating Tandoor" cp -r /opt/tandoor.bak/{config,api,mediafiles,staticfiles} /opt/tandoor/ mv /opt/tandoor.bak/.env /opt/tandoor/.env cd /opt/tandoor $STD uv venv --clear .venv --python=python3 $STD uv pip install -r requirements.txt --python .venv/bin/python cd /opt/tandoor/vue3 $STD yarn install $STD yarn build TANDOOR_VERSION=$(get_latest_github_release "TandoorRecipes/recipes") cat </opt/tandoor/cookbook/version_info.py TANDOOR_VERSION = "$TANDOOR_VERSION" TANDOOR_REF = "bare-metal" VERSION_INFO = [] EOF cd /opt/tandoor $STD /opt/tandoor/.venv/bin/python manage.py migrate $STD /opt/tandoor/.venv/bin/python manage.py collectstatic --no-input rm -rf /opt/tandoor.bak msg_ok "Updated Tandoor" msg_info "Starting Service" systemctl start tandoor systemctl reload nginx msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8002${CL}" ================================================ FILE: ct/tasmoadmin.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/TasmoAdmin/TasmoAdmin APP="TasmoAdmin" var_tags="${var_tags:-tasmota;smarthome}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-512}" var_disk="${var_disk:-2}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /var ]]; then msg_error "No ${APP} Installation Found!" exit fi msg_info "Updating TasmoAdmin" $STD apt update $STD apt -y upgrade msg_ok "Updated TasmoAdmin" msg_ok "Updated successfully!" exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:9999${CL}" ================================================ FILE: ct/tasmocompiler.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Slaviša Arežina (tremor021) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/benzino77/tasmocompiler APP="TasmoCompiler" var_tags="${var_tags:-compiler}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-10}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/tasmocompiler ]]; then msg_error "No ${APP} Installation Found!" exit fi RELEASE=$(curl -fsSL https://api.github.com/repos/benzino77/tasmocompiler/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4) }') if [[ ! -f /opt/${APP}_version.txt ]] || [[ "${RELEASE}" != "$(cat /opt/${APP}_version.txt)" ]]; then msg_info "Stopping Service" systemctl stop tasmocompiler msg_ok "Stopped Service" msg_info "Updating TasmoCompiler" cd /opt rm -rf /opt/tasmocompiler RELEASE=$(curl -fsSL https://api.github.com/repos/benzino77/tasmocompiler/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4) }') curl -fsSL "https://github.com/benzino77/tasmocompiler/archive/refs/tags/v${RELEASE}.tar.gz" -o $(basename "https://github.com/benzino77/tasmocompiler/archive/refs/tags/v${RELEASE}.tar.gz") tar xzf v${RELEASE}.tar.gz mv tasmocompiler-${RELEASE}/ /opt/tasmocompiler/ cd /opt/tasmocompiler $STD yarn install export NODE_OPTIONS=--openssl-legacy-provider $STD npm i $STD yarn build rm -r "/opt/v${RELEASE}.tar.gz" echo "${RELEASE}" >/opt/${APP}_version.txt msg_ok "Updated TasmoCompiler" msg_info "Starting Service" systemctl start tasmocompiler msg_ok "Started Service" msg_ok "Updated successfully!" else msg_ok "No update required. ${APP} is already at v${RELEASE}" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:3000${CL}" ================================================ FILE: ct/tautulli.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://tautulli.com/ APP="Tautulli" var_tags="${var_tags:-media}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-1024}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/Tautulli/ ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "Tautulli" "Tautulli/Tautulli"; then PYTHON_VERSION="3.13" setup_uv msg_info "Stopping Service" systemctl stop tautulli msg_ok "Stopped Service" msg_info "Backing up config and database" cp /opt/Tautulli/config.ini /opt/tautulli_config.ini.backup cp /opt/Tautulli/tautulli.db /opt/tautulli.db.backup msg_ok "Backed up config and database" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "Tautulli" "Tautulli/Tautulli" "tarball" msg_info "Updating Tautulli" cd /opt/Tautulli TAUTULLI_VERSION=$(get_latest_github_release "Tautulli/Tautulli" "false") echo "${TAUTULLI_VERSION}" >/opt/Tautulli/version.txt echo "master" >/opt/Tautulli/branch.txt $STD uv venv -c $STD source /opt/Tautulli/.venv/bin/activate $STD uv pip install -r requirements.txt $STD uv pip install pyopenssl $STD uv pip install "setuptools<81" msg_ok "Updated Tautulli" msg_info "Restoring config and database" cp /opt/tautulli_config.ini.backup /opt/Tautulli/config.ini cp /opt/tautulli.db.backup /opt/Tautulli/tautulli.db rm -f /opt/{tautulli_config.ini.backup,tautulli.db.backup} msg_ok "Restored config and database" msg_info "Starting Service" systemctl start tautulli msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8181${CL}" ================================================ FILE: ct/tdarr.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://home.tdarr.io/ APP="Tdarr" var_tags="${var_tags:-arr}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" var_gpu="${var_gpu:-yes}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/tdarr ]]; then msg_error "No ${APP} Installation Found!" exit fi msg_info "Updating Tdarr" $STD apt update $STD apt upgrade -y rm -rf /opt/tdarr/Tdarr_Updater cd /opt/tdarr RELEASE=$(curl_with_retry "https://f000.backblazeb2.com/file/tdarrs/versions.json" "-" | grep -oP '(?<="Tdarr_Updater": ")[^"]+' | grep linux_x64 | head -n 1) curl_with_retry "$RELEASE" "Tdarr_Updater.zip" $STD unzip Tdarr_Updater.zip chmod +x Tdarr_Updater $STD ./Tdarr_Updater rm -rf /opt/tdarr/Tdarr_Updater.zip [[ -f /opt/tdarr/Tdarr_Server/Tdarr_Server ]] || { msg_error "Tdarr_Updater failed — tdarr.io may be blocked by local DNS" exit 250 } msg_ok "Updated Tdarr" msg_ok "Updated successfully!" exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8265${CL}" ================================================ FILE: ct/teable.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/teableio/teable APP="Teable" var_tags="${var_tags:-database;no-code;spreadsheet}" var_cpu="${var_cpu:-4}" var_ram="${var_ram:-10240}" var_disk="${var_disk:-25}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/teable ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "teable" "teableio/teable"; then msg_info "Stopping Service" systemctl stop teable msg_ok "Stopped Service" msg_info "Backing up Configuration" cp /opt/teable/.env /opt/teable.env.bak msg_ok "Backed up Configuration" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "teable" "teableio/teable" "tarball" msg_info "Restoring Configuration" mv /opt/teable.env.bak /opt/teable/.env msg_ok "Restored Configuration" msg_info "Rebuilding Teable" cd /opt/teable TEABLE_VERSION=$(cat ~/.teable) echo "NEXT_PUBLIC_BUILD_VERSION=\"${TEABLE_VERSION}\"" >>apps/nextjs-app/.env export HUSKY=0 export NODE_OPTIONS="--max-old-space-size=8192" $STD pnpm install --frozen-lockfile $STD pnpm -F @teable/db-main-prisma prisma-generate --schema ./prisma/postgres/schema.prisma NODE_ENV=production NEXT_BUILD_ENV_TYPECHECK=false \ $STD pnpm -r --filter '!playground' run build msg_ok "Rebuilt Teable" msg_info "Running Database Migrations" source /opt/teable/.env $STD pnpm -F @teable/db-main-prisma prisma-migrate deploy --schema ./prisma/postgres/schema.prisma msg_ok "Ran Database Migrations" msg_info "Starting Service" systemctl start teable msg_ok "Started Service" msg_ok "Updated successfully!" else msg_ok "No update available." fi exit } start build_container description msg_ok "Completed Successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:3000${CL}" ================================================ FILE: ct/teamspeak-server.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: tremor021 (Slaviša Arežina) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://teamspeak.com/en/ APP="Teamspeak-Server" var_tags="${var_tags:-voice;communication}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-512}" var_disk="${var_disk:-2}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/teamspeak-server ]]; then msg_error "No ${APP} Installation Found!" exit fi RELEASE=$(curl -fsSL https://teamspeak.com/en/downloads/#server | grep -oP 'teamspeak3-server_linux_arm64-\K[0-9]+\.[0-9]+\.[0-9]+' | head -1) if [[ "${RELEASE}" != "$(cat ~/.teamspeak-server 2>/dev/null)" ]] || [[ ! -f ~/.teamspeak-server ]]; then msg_info "Stopping Service" systemctl stop teamspeak-server msg_ok "Stopped Service" msg_info "Updating Teamspeak Server" curl -fsSL "https://files.teamspeak-services.com/releases/server/${RELEASE}/teamspeak3-server_linux_arm64-${RELEASE}.tar.bz2" -o ts3server.tar.bz2 tar -xf ./ts3server.tar.bz2 cp -ru teamspeak3-server_linux_arm64/* /opt/teamspeak-server/ rm -f ~/ts3server.tar.bz* echo "${RELEASE}" >~/.teamspeak-server msg_ok "Updated Teamspeak Server" msg_info "Starting Service" systemctl start teamspeak-server msg_ok "Started Service" msg_ok "Updated successfully!" else msg_ok "Already up to date" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}${IP}:9987${CL}" ================================================ FILE: ct/technitiumdns.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://technitium.com/dns/ APP="Technitium DNS" var_tags="${var_tags:-dns}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-512}" var_disk="${var_disk:-2}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /etc/dns ]]; then msg_error "No ${APP} Installation Found!" exit fi if [[ -f /etc/systemd/system/dns.service ]]; then mv /etc/systemd/system/dns.service /etc/systemd/system/technitium.service systemctl daemon-reload systemctl enable -q --now technitium fi if ! is_package_installed "aspnetcore-runtime-10.0"; then $STD apt remove -y aspnetcore-runtime-8.0 aspnetcore-runtime-9.0 2>/dev/null || true [ -f /etc/apt/sources.list.d/microsoft-prod.list ] && rm -f /etc/apt/sources.list.d/microsoft-prod.list [ -f /usr/share/keyrings/microsoft-prod.gpg ] && rm -f /usr/share/keyrings/microsoft-prod.gpg setup_deb822_repo \ "microsoft" \ "https://packages.microsoft.com/keys/microsoft-2025.asc" \ "https://packages.microsoft.com/debian/13/prod/" \ "trixie" \ "main" $STD apt install -y aspnetcore-runtime-10.0 fi RELEASE=$(curl -fsSL https://technitium.com/dns/ | grep -oP 'Version \K[\d.]+') if [[ ! -f ~/.technitium || ${RELEASE} != "$(cat ~/.technitium 2>/dev/null)" ]]; then systemctl stop technitium fetch_and_deploy_from_url "https://download.technitium.com/dns/DnsServerPortable.tar.gz" /opt/technitium/dns echo "${RELEASE}" >~/.technitium systemctl start technitium msg_ok "Updated successfully!" else msg_ok "No update required. Technitium DNS is already at v${RELEASE}." fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:5380${CL}" ================================================ FILE: ct/teddycloud.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Dominik Siebel (dsiebel) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/toniebox-reverse-engineering/teddycloud APP="TeddyCloud" var_tags="${var_tags:-media}" var_cpu="${var_cpu:-2}" var_disk="${var_disk:-8}" var_ram="${var_ram:-1024}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "${APP}" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/teddycloud ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "teddycloud" "toniebox-reverse-engineering/teddycloud"; then msg_info "Stopping Service" systemctl stop teddycloud msg_ok "Stopped Service" msg_info "Creating backup" mv /opt/teddycloud /opt/teddycloud_bak msg_ok "Backup created" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "teddycloud" "toniebox-reverse-engineering/teddycloud" "prebuild" "latest" "/opt/teddycloud" "teddycloud.arm64.release*.zip" msg_info "Restoring data" cp -R /opt/teddycloud_bak/certs /opt/teddycloud_bak/config /opt/teddycloud_bak/data /opt/teddycloud rm -rf /opt/teddycloud_bak msg_ok "Data restored" msg_info "Starting Service" systemctl start teddycloud msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}${CL}" ================================================ FILE: ct/telegraf.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: CrazyWolf13 # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/influxdata/telegraf APP="telegraf" var_tags="${var_tags:-collector;metrics}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-1024}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -f /etc/telegraf/telegraf.conf ]]; then msg_error "No ${APP} Installation Found!" exit fi msg_info "Stopping Service" systemctl stop telegraf msg_ok "Stopped Service" msg_info "Updating Telegraf" $STD apt update $STD apt upgrade -y telegraf msg_ok "Updated Telegraf" msg_info "Starting Service" systemctl start telegraf msg_ok "Started Service" msg_ok "Updated successfully!" exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" ================================================ FILE: ct/teleport.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Slaviša Arežina (tremor021) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://goteleport.com/ APP="Teleport" var_tags="${var_tags:-zero-trust}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-1024}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -f /etc/teleport.yaml ]]; then msg_error "No ${APP} Installation Found!" exit fi msg_info "Updating Teleport" $STD apt update $STD apt upgrade -y msg_ok "Updated successfully!" exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}https://${IP}:3080${CL}" ================================================ FILE: ct/termix.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/Termix-SSH/Termix APP="Termix" var_tags="${var_tags:-ssh;terminal;management}" var_cpu="${var_cpu:-4}" var_ram="${var_ram:-4096}" var_disk="${var_disk:-10}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/termix ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_tag "guacd" "apache/guacamole-server"; then msg_info "Stopping guacd" systemctl stop guacd 2>/dev/null || true msg_ok "Stopped guacd" ensure_dependencies \ libcairo2-dev \ libjpeg62-turbo-dev \ libpng-dev \ libtool-bin \ uuid-dev \ libvncserver-dev \ freerdp3-dev \ libssh2-1-dev \ libtelnet-dev \ libwebsockets-dev \ libpulse-dev \ libvorbis-dev \ libwebp-dev \ libssl-dev \ libpango1.0-dev \ libswscale-dev \ libavcodec-dev \ libavutil-dev \ libavformat-dev msg_info "Updating Guacamole Server (guacd)" fetch_and_deploy_gh_tag "guacd" "apache/guacamole-server" "${CHECK_UPDATE_RELEASE}" "/opt/guacamole-server" cd /opt/guacamole-server export CPPFLAGS="-Wno-error=deprecated-declarations" $STD autoreconf -fi $STD ./configure --with-init-dir=/etc/init.d --enable-allow-freerdp-snapshots $STD make $STD make install $STD ldconfig cd /opt rm -rf /opt/guacamole-server msg_ok "Updated Guacamole Server (guacd) to ${CHECK_UPDATE_RELEASE}" if [[ ! -f /etc/guacamole/guacd.conf ]]; then mkdir -p /etc/guacamole cat </etc/guacamole/guacd.conf [server] bind_host = 127.0.0.1 bind_port = 4822 EOF fi if [[ ! -f /etc/systemd/system/guacd.service ]] || grep -q "Type=forking" /etc/systemd/system/guacd.service 2>/dev/null; then cat </etc/systemd/system/guacd.service [Unit] Description=Guacamole Proxy Daemon (guacd) After=network.target [Service] Type=simple ExecStart=/usr/local/sbin/guacd -f -b 127.0.0.1 -l 4822 Restart=on-failure RestartSec=5 [Install] WantedBy=multi-user.target EOF fi if ! grep -q "guacd.service" /etc/systemd/system/termix.service 2>/dev/null; then sed -i '/^After=network.target/s/$/ guacd.service/' /etc/systemd/system/termix.service sed -i '/^\[Unit\]/a Wants=guacd.service' /etc/systemd/system/termix.service fi systemctl daemon-reload systemctl enable -q --now guacd fi if check_for_gh_release "termix" "Termix-SSH/Termix"; then msg_info "Stopping Termix" systemctl stop termix msg_ok "Stopped Termix" msg_info "Migrating Configuration" if [[ ! -f /opt/termix/.env ]]; then cat </opt/termix/.env NODE_ENV=production DATA_DIR=/opt/termix/data GUACD_HOST=127.0.0.1 GUACD_PORT=4822 EOF fi if ! grep -q "EnvironmentFile" /etc/systemd/system/termix.service 2>/dev/null; then cat </etc/systemd/system/termix.service [Unit] Description=Termix Backend After=network.target guacd.service Wants=guacd.service [Service] Type=simple User=root WorkingDirectory=/opt/termix EnvironmentFile=/opt/termix/.env ExecStart=/usr/bin/node /opt/termix/dist/backend/backend/starter.js Restart=on-failure RestartSec=5 [Install] WantedBy=multi-user.target EOF systemctl daemon-reload fi msg_ok "Migrated Configuration" msg_info "Backing up Data" cp -r /opt/termix/data /opt/termix_data_backup cp -r /opt/termix/uploads /opt/termix_uploads_backup msg_ok "Backed up Data" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "termix" "Termix-SSH/Termix" "tarball" msg_info "Recreating Directories" mkdir -p /opt/termix/html \ /opt/termix/nginx \ /opt/termix/nginx/logs \ /opt/termix/nginx/cache \ /opt/termix/nginx/client_body msg_ok "Recreated Directories" NODE_VERSION="24" setup_nodejs msg_info "Building Frontend" cd /opt/termix export COREPACK_ENABLE_DOWNLOAD_PROMPT=0 find public/fonts -name "*.ttf" ! -name "*Regular.ttf" ! -name "*Bold.ttf" ! -name "*Italic.ttf" -delete 2>/dev/null || true $STD npm install --ignore-scripts --force $STD npm run build msg_ok "Built Frontend" msg_info "Building Backend" $STD npm rebuild better-sqlite3 --force $STD npm run build:backend msg_ok "Built Backend" msg_info "Setting up Production Dependencies" $STD npm ci --only=production --ignore-scripts --force $STD npm rebuild better-sqlite3 bcryptjs --force $STD npm cache clean --force msg_ok "Set up Production Dependencies" msg_info "Restoring Data" cp -r /opt/termix_data_backup /opt/termix/data cp -r /opt/termix_uploads_backup /opt/termix/uploads rm -rf /opt/termix_data_backup /opt/termix_uploads_backup msg_ok "Restored Data" msg_info "Updating Frontend Files" rm -rf /opt/termix/html/* cp -r /opt/termix/dist/* /opt/termix/html/ 2>/dev/null || true cp -r /opt/termix/src/locales /opt/termix/html/locales 2>/dev/null || true cp -r /opt/termix/public/fonts /opt/termix/html/fonts 2>/dev/null || true msg_ok "Updated Frontend Files" msg_warn "The Nginx configuration may need to be updated for new features to work." msg_custom "💾" "Your current config will be backed up to nginx.conf.bak" msg_custom "⚠️ " "Note: Custom modifications (reverse proxy, SSL) will be overwritten!" echo "" read -rp "${TAB3}Update Nginx configuration? [Y/n]: " REPLY if [[ "${REPLY,,}" =~ ^(y|yes|)$ ]]; then msg_info "Updating Nginx Configuration" cp /etc/nginx/nginx.conf /etc/nginx/nginx.conf.bak curl -fsSL "https://raw.githubusercontent.com/Termix-SSH/Termix/main/docker/nginx.conf" -o /etc/nginx/nginx.conf sed -i '/^master_process/d' /etc/nginx/nginx.conf sed -i 's|pid /tmp/nginx/nginx.pid;|pid /run/nginx.pid;|' /etc/nginx/nginx.conf sed -i 's|error_log /tmp/nginx/error.log|error_log /var/log/nginx/error.log|' /etc/nginx/nginx.conf sed -i 's|access_log /tmp/nginx/access.log|access_log /var/log/nginx/access.log|' /etc/nginx/nginx.conf sed -i 's|/app/html|/opt/termix/html|g' /etc/nginx/nginx.conf sed -i 's|/app/nginx|/opt/termix/nginx|g' /etc/nginx/nginx.conf sed -i 's|listen ${PORT};|listen 80;|g' /etc/nginx/nginx.conf nginx -t && systemctl reload nginx msg_ok "Updated Nginx Configuration" else msg_warn "Nginx configuration not updated. If Termix doesn't work, restore from backup or update manually." fi msg_info "Starting Termix" systemctl start termix msg_ok "Started Termix" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed Successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}${CL}" ================================================ FILE: ct/the-lounge.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: kristocopani # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://thelounge.chat/ APP="The-Lounge" var_tags="${var_tags:-irc}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -f /usr/lib/systemd/system/thelounge.service ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "thelounge" "thelounge/thelounge-deb"; then msg_info "Stopping Service" systemctl stop thelounge msg_ok "Stopped Service" fetch_and_deploy_gh_release "thelounge" "thelounge/thelounge-deb" "binary" msg_info "Starting Service" systemctl start thelounge msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:9000${CL}" ================================================ FILE: ct/thingsboard.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/thingsboard/thingsboard APP="ThingsBoard" var_tags="${var_tags:-iot;platform}" var_cpu="${var_cpu:-4}" var_ram="${var_ram:-4096}" var_disk="${var_disk:-10}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /usr/share/thingsboard ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "thingsboard" "thingsboard/thingsboard"; then msg_info "Stopping Service" systemctl stop thingsboard msg_ok "Stopped Service" fetch_and_deploy_gh_release "thingsboard" "thingsboard/thingsboard" "binary" "latest" "/tmp" "thingsboard-*.deb" msg_info "Running Database Upgrade" $STD /usr/share/thingsboard/bin/install/upgrade.sh msg_ok "Ran Database Upgrade" msg_info "Starting Service" systemctl start thingsboard msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8080${CL}" ================================================ FILE: ct/threadfin.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/Threadfin/Threadfin APP="Threadfin" var_tags="${var_tags:-media}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-1024}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" var_gpu="${var_gpu:-yes}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/threadfin ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "threadfin-app" "threadfin/threadfin"; then msg_info "Stopping Service" systemctl stop threadfin msg_ok "Stopped Service" fetch_and_deploy_gh_release "threadfin" "threadfin/threadfin" "singlefile" "latest" "/opt/threadfin" "Threadfin_linux_arm64" msg_info "Starting Service" systemctl start threadfin msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:34400/web${CL}" ================================================ FILE: ct/tianji.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: MickLesk (Canbiz) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://tianji.msgbyte.com/ APP="Tianji" var_tags="${var_tags:-monitoring}" var_cpu="${var_cpu:-4}" var_ram="${var_ram:-4096}" var_disk="${var_disk:-12}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/tianji ]]; then msg_error "No ${APP} Installation Found!" exit fi setup_uv if check_for_gh_release "tianji" "msgbyte/tianji"; then NODE_VERSION="22" NODE_MODULE="pnpm@$(curl -s https://raw.githubusercontent.com/msgbyte/tianji/master/package.json | jq -r '.packageManager | split("@")[1]')" setup_nodejs msg_info "Stopping Service" systemctl stop tianji msg_ok "Stopped Service" msg_info "Backing up data" cp /opt/tianji/src/server/.env /opt/.env mv /opt/tianji /opt/tianji_bak msg_ok "Backed up data" fetch_and_deploy_gh_release "tianji" "msgbyte/tianji" "tarball" msg_info "Updating Tianji" cd /opt/tianji export NODE_OPTIONS="--max_old_space_size=4096" $STD pnpm install --filter @tianji/client... --config.dedupe-peer-dependents=false --frozen-lockfile $STD pnpm build:static $STD pnpm install --filter @tianji/server... --config.dedupe-peer-dependents=false mkdir -p ./src/server/public cp -r ./geo ./src/server/public $STD pnpm build:server mv /opt/.env /opt/tianji/src/server/.env cd src/server $STD pnpm db:migrate:apply rm -rf /opt/tianji_bak rm -rf /opt/tianji/src/client rm -rf /opt/tianji/website rm -rf /opt/tianji/reporter msg_ok "Updated Tianji" msg_info "Updating AppRise" $STD uv pip install apprise cryptography --system msg_ok "Updated AppRise" msg_info "Starting Service" systemctl start tianji msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:12345${CL}" ================================================ FILE: ct/tinyauth.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: BiluliB # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/plexguide/Huntarr.io APP="Tinyauth" var_tags="${var_tags:-auth}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-512}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/tinyauth ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "tinyauth" "steveiliop56/tinyauth"; then msg_info "Stopping Service" systemctl stop tinyauth msg_ok "Stopped Service" local arch asset arch=$(dpkg --print-architecture 2>/dev/null || uname -m) asset="tinyauth-amd64" [[ "$arch" == "arm64" || "$arch" == "aarch64" ]] && asset="tinyauth-arm64" fetch_and_deploy_gh_release "tinyauth" "steveiliop56/tinyauth" "singlefile" "latest" "/opt/tinyauth" "$asset" msg_info "Stopping Service" systemctl stop tinyauth msg_ok "Stopped Service" fetch_and_deploy_gh_release "tinyauth" "steveiliop56/tinyauth" "singlefile" "latest" "/opt/tinyauth" "tinyauth-amd64" msg_info "Starting Service" systemctl start tinyauth msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:3000${CL}" ================================================ FILE: ct/traccar.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.traccar.org/ APP="Traccar" var_tags="${var_tags:-gps;tracker}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-1024}" var_disk="${var_disk:-2}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/traccar ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "traccar" "traccar/traccar"; then msg_info "Stopping Service" systemctl stop traccar msg_ok "Stopped Service" msg_info "Creating backup" mv /opt/traccar/conf/traccar.xml /opt [[ -d /opt/traccar/data ]] && mv /opt/traccar/data /opt [[ -d /opt/traccar/media ]] && mv /opt/traccar/media /opt msg_ok "Backup created" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "traccar" "traccar/traccar" "prebuild" "latest" "/opt/traccar" "traccar-linux-64*.zip" msg_info "Perform Update" cd /opt/traccar $STD ./traccar.run msg_ok "App-Update completed" msg_info "Restoring data" mv /opt/traccar.xml /opt/traccar/conf [[ -d /opt/data ]] && mv /opt/data /opt/traccar [[ -d /opt/media ]] && mv /opt/media /opt/traccar [ -f README.txt ] || [ -f traccar.run ] && rm -f README.txt traccar.run msg_ok "Data restored" msg_info "Starting Service" systemctl start traccar msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8082${CL}" ================================================ FILE: ct/tracearr.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: durzo # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/connorgallopo/Tracearr APP="Tracearr" var_tags="${var_tags:-media}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-8192}" var_disk="${var_disk:-10}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -f /lib/systemd/system/tracearr.service ]]; then msg_error "No ${APP} Installation Found!" exit fi NODE_VERSION="24" setup_nodejs msg_info "Updating prestart script" cat </data/tracearr/prestart.sh #!/usr/bin/env bash # ============================================================================= # Tune PostgreSQL for available resources (runs every startup) # ============================================================================= # timescaledb-tune automatically optimizes PostgreSQL settings based on # available RAM and CPU. Safe to run repeatedly - recalculates if resources change. if command -v timescaledb-tune &> /dev/null; then total_ram_kb=\$(grep MemTotal /proc/meminfo | awk '{print \$2}') ram_for_tsdb=\$((total_ram_kb / 1024 / 2)) timescaledb-tune -yes -memory "\$ram_for_tsdb"MB --quiet 2>/dev/null \ || echo "Warning: timescaledb-tune failed (non-fatal)" fi # ============================================================================= # Ensure required PostgreSQL settings for Tracearr # ============================================================================= pg_config_file="/etc/postgresql/18/main/postgresql.conf" if [ -f \$pg_config_file ]; then # Ensure max_tuples_decompressed_per_dml_transaction is set if grep -q "^timescaledb\.max_tuples_decompressed_per_dml_transaction" \$pg_config_file; then # Setting exists (uncommented) - update if not 0 current_value=\$(grep "^timescaledb\.max_tuples_decompressed_per_dml_transaction" \$pg_config_file | grep -oE '[0-9]+' | head -1) if [ -n "\$current_value" ] && [ "\$current_value" -ne 0 ]; then sed -i "s/^timescaledb\.max_tuples_decompressed_per_dml_transaction.*/timescaledb.max_tuples_decompressed_per_dml_transaction = 0/" \$pg_config_file fi elif ! grep -q "^timescaledb\.max_tuples_decompressed_per_dml_transaction" \$pg_config_file; then echo "" >> \$pg_config_file echo "# Allow unlimited tuple decompression for migrations on compressed hypertables" >> \$pg_config_file echo "timescaledb.max_tuples_decompressed_per_dml_transaction = 0" >> \$pg_config_file fi # Ensure max_locks_per_transaction is set (for existing databases) if grep -q "^max_locks_per_transaction" \$pg_config_file; then # Setting exists (uncommented) - update if below 4096 current_value=\$(grep "^max_locks_per_transaction" \$pg_config_file | grep -oE '[0-9]+' | head -1) if [ -n "\$current_value" ] && [ "\$current_value" -lt 4096 ]; then sed -i "s/^max_locks_per_transaction.*/max_locks_per_transaction = 4096/" \$pg_config_file fi elif ! grep -q "^max_locks_per_transaction" \$pg_config_file; then echo "" >> \$pg_config_file echo "# Increase lock table size for TimescaleDB hypertables with many chunks" >> \$pg_config_file echo "max_locks_per_transaction = 4096" >> \$pg_config_file fi fi systemctl restart postgresql sudo -u postgres psql -c "ALTER USER tracearr WITH SUPERUSER;" EOF chmod +x /data/tracearr/prestart.sh msg_ok "Updated prestart script" # check if tailscale is installed if command -v tailscale >/dev/null 2>&1; then # Tracearr runs tailscaled in user mode, disable the service. $STD systemctl disable --now tailscaled $STD systemctl stop tailscaled msg_ok "Tailscale already installed" else msg_info "Installing tailscale" setup_deb822_repo \ "tailscale" \ "https://pkgs.tailscale.com/stable/$(get_os_info id)/$(get_os_info codename).noarmor.gpg" \ "https://pkgs.tailscale.com/stable/$(get_os_info id)/" \ "$(get_os_info codename)" $STD apt install -y tailscale # Tracearr runs tailscaled in user mode, disable the service. $STD systemctl disable --now tailscaled $STD systemctl stop tailscaled msg_ok "Installed tailscale" fi if check_for_gh_release "tracearr" "connorgallopo/Tracearr"; then msg_info "Stopping Services" systemctl stop tracearr postgresql redis-server msg_ok "Stopped Services" msg_info "Updating pnpm" PNPM_VERSION="$(curl -fsSL "https://raw.githubusercontent.com/connorgallopo/Tracearr/refs/heads/main/package.json" | jq -r '.packageManager | split("@")[1]' | cut -d'+' -f1)" export COREPACK_ENABLE_DOWNLOAD_PROMPT=0 $STD corepack prepare pnpm@${PNPM_VERSION} --activate msg_ok "Updated pnpm" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "tracearr" "connorgallopo/Tracearr" "tarball" "latest" "/opt/tracearr.build" msg_info "Building Tracearr" export TZ=$(cat /etc/timezone) export NODE_OPTIONS="--max-old-space-size=4096" cd /opt/tracearr.build $STD pnpm install --frozen-lockfile --force $STD pnpm turbo telemetry disable $STD pnpm turbo run build --no-daemon --filter=@tracearr/shared --filter=@tracearr/server --filter=@tracearr/web rm -rf /opt/tracearr mkdir -p /opt/tracearr/{packages/shared,apps/server,apps/web,apps/server/src/db} cp -rf package.json /opt/tracearr/ cp -rf pnpm-workspace.yaml /opt/tracearr/ cp -rf pnpm-lock.yaml /opt/tracearr/ cp -rf apps/server/package.json /opt/tracearr/apps/server/ cp -rf apps/server/dist /opt/tracearr/apps/server/dist cp -rf apps/server/scripts /opt/tracearr/apps/server/scripts cp -rf apps/web/dist /opt/tracearr/apps/web/dist cp -rf packages/shared/package.json /opt/tracearr/packages/shared/ cp -rf packages/shared/dist /opt/tracearr/packages/shared/dist cp -rf apps/server/src/db/migrations /opt/tracearr/apps/server/src/db/migrations cp -rf data /opt/tracearr/data mkdir -p /opt/tracearr/data/image-cache rm -rf /opt/tracearr.build cd /opt/tracearr $STD pnpm install --prod --frozen-lockfile --ignore-scripts $STD chown -R tracearr:tracearr /opt/tracearr msg_ok "Built Tracearr" msg_info "Configuring Tracearr" sed -i "s|^APP_VERSION=.*|APP_VERSION=${CHECK_UPDATE_RELEASE#v}|" /data/tracearr/.env chmod 600 /data/tracearr/.env chown -R tracearr:tracearr /data/tracearr mkdir -p /data/backup chown -R tracearr:tracearr /data/backup msg_ok "Configured Tracearr" msg_info "Starting services" systemctl start postgresql redis-server tracearr msg_ok "Started services" msg_ok "Updated successfully!" else # no new release, just restart service to apply prestart changes msg_info "Restarting service" systemctl restart tracearr msg_ok "Restarted service" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:3000${CL}" ================================================ FILE: ct/tracktor.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: CrazyWolf13 # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://tracktor.bytedge.in/ APP="tracktor" var_tags="${var_tags:-car;monitoring}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-4096}" var_disk="${var_disk:-6}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/tracktor ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "tracktor" "javedh-dev/tracktor"; then msg_info "Stopping Service" systemctl stop tracktor msg_ok "Stopped Service" msg_info "Correcting Services" if [ ! -d "/opt/tracktor-data/uploads" ]; then mkdir -p /opt/tracktor-data/{uploads,logs} fi if ! grep -qxF 'BODY_SIZE_LIMIT=Infinity' /opt/tracktor.env; then rm /opt/tracktor.env cat </opt/tracktor.env cat </opt/tracktor.env NODE_ENV=production # Set this to the path of the database file. Default - ./tracktor.db DB_PATH=/opt/tracktor-data/tracktor.db # Set this to the path of the uploads directory. Default - ./uploads UPLOADS_DIR="/opt/tracktor-data/uploads" # Set this to the path of the logs directory. Default - ./logs LOG_DIR="/opt/tracktor-data/logs" # Hostname to bind the server to. Default - 0.0.0.0 #HOST="0.0.0.0" # Port to bind the server to. Default - 3000 #PORT=3000 # Set this to remove upload size limitations. Default - 512 Kb BODY_SIZE_LIMIT=Infinity # Enable request logging. Default - true #LOG_REQUESTS=true # Set the logging level. Options - error, warn, info, verbose, debug, silly. Default - info #LOG_LEVEL="info" # Enable demo mode. Default - false #TRACKTOR_DEMO_MODE=false # Force reseeding of data on every startup. Default - false #FORCE_DATA_SEED=false EOF fi msg_ok "Corrected Services" NODE_VERSION="24" setup_nodejs CLEAN_INSTALL=1 fetch_and_deploy_gh_release "tracktor" "javedh-dev/tracktor" "tarball" "latest" "/opt/tracktor" msg_info "Updating tracktor" cd /opt/tracktor $STD npm install $STD npm run build msg_ok "Updated tracktor" msg_info "Starting Service" systemctl start tracktor msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:3000${CL}" ================================================ FILE: ct/traefik.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://traefik.io/ APP="Traefik" var_tags="${var_tags:-proxy}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-512}" var_disk="${var_disk:-2}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -f /etc/systemd/system/traefik.service ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "traefik" "traefik/traefik"; then msg_info "Stopping Service" systemctl stop traefik msg_ok "Stopped Service" fetch_and_deploy_gh_release "traefik" "traefik/traefik" "prebuild" "latest" "/usr/bin" "traefik_v*_linux_arm64.tar.gz" msg_info "Starting Service" systemctl start traefik msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8080${CL}" ================================================ FILE: ct/transmission.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://transmissionbt.com/ APP="Transmission" var_tags="${var_tags:-torrent}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-8}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -f /etc/transmission-daemon/settings.json ]]; then msg_error "No ${APP} Installation Found!" exit fi msg_info "Updating Transmission" $STD apt update $STD apt -y upgrade msg_ok "Updated Transmission" msg_ok "Updated successfully!" exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:9091/transmission${CL}" ================================================ FILE: ct/transmute.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/transmute-app/transmute APP="Transmute" var_tags="${var_tags:-files;converter}" var_cpu="${var_cpu:-4}" var_ram="${var_ram:-4096}" var_disk="${var_disk:-16}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/transmute ]]; then msg_error "No ${APP} Installation Found!" exit fi fetch_and_deploy_gh_release "calibre" "kovidgoyal/calibre" "prebuild" "latest" "/opt/calibre" "calibre-*-x86_64.txz" ln -sf /opt/calibre/ebook-convert /usr/bin/ebook-convert fetch_and_deploy_gh_release "drawio" "jgraph/drawio-desktop" "binary" "latest" "" "drawio-amd64-*.deb" fetch_and_deploy_gh_release "pandoc" "jgm/pandoc" "binary" "latest" "" "pandoc-*-amd64.deb" if check_for_gh_release "transmute" "transmute-app/transmute"; then msg_info "Stopping Service" systemctl stop transmute msg_ok "Stopped Service" msg_info "Backing up Data" cp /opt/transmute/backend/.env /opt/transmute.env.bak cp -r /opt/transmute/data /opt/transmute_data_bak msg_ok "Backed up Data" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "transmute" "transmute-app/transmute" "tarball" msg_info "Updating Python Dependencies" cd /opt/transmute $STD uv venv --clear /opt/transmute/.venv $STD uv pip install --python /opt/transmute/.venv/bin/python -r requirements.txt msg_ok "Updated Python Dependencies" msg_info "Rebuilding Frontend" cd /opt/transmute/frontend $STD npm ci $STD npm run build msg_ok "Rebuilt Frontend" msg_info "Restoring Data" cp /opt/transmute.env.bak /opt/transmute/backend/.env cp -r /opt/transmute_data_bak/. /opt/transmute/data/ rm -f /opt/transmute.env.bak rm -rf /opt/transmute_data_bak msg_ok "Restored Data" msg_info "Starting Service" systemctl start transmute msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed Successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:3313${CL}" ================================================ FILE: ct/trek.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/mauriceboe/TREK APP="TREK" var_tags="${var_tags:-travel;planning;collaboration}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-8}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/trek ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "trek" "mauriceboe/TREK"; then msg_info "Stopping Service" systemctl stop trek msg_ok "Stopped Service" msg_info "Backing up Data" cp /opt/trek/server/.env /opt/trek.env.bak mv /opt/trek/data /opt/trek-data.bak mv /opt/trek/uploads /opt/trek-uploads.bak msg_ok "Backed up Data" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "trek" "mauriceboe/TREK" "tarball" msg_info "Building Client" cd /opt/trek/client $STD npm ci $STD npm run build mkdir -p /opt/trek/server/public cp -r /opt/trek/client/dist/* /opt/trek/server/public/ cp -r /opt/trek/client/public/fonts /opt/trek/server/public/fonts 2>/dev/null || true msg_ok "Built Client" msg_info "Installing Server Dependencies" cd /opt/trek/server $STD npm ci msg_ok "Installed Server Dependencies" msg_info "Restoring Data" mv /opt/trek-data.bak /opt/trek/data mv /opt/trek-uploads.bak /opt/trek/uploads rm -rf /opt/trek/server/data /opt/trek/server/uploads ln -s /opt/trek/data /opt/trek/server/data ln -s /opt/trek/uploads /opt/trek/server/uploads cp /opt/trek.env.bak /opt/trek/server/.env rm -f /opt/trek.env.bak msg_ok "Restored Data" msg_info "Starting Service" systemctl start trek msg_ok "Started Service" msg_ok "Updated Successfully!" fi exit } start build_container description msg_ok "Completed Successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:3000${CL}" ================================================ FILE: ct/trilium.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/TriliumNext/Trilium APP="Trilium" var_tags="${var_tags:-notes}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-512}" var_disk="${var_disk:-2}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/trilium ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "Trilium" "TriliumNext/Trilium"; then if [[ -d /opt/trilium/db ]]; then DB_PATH="/opt/trilium/db" DB_RESTORE_PATH="/opt/trilium/db" elif [[ -d /opt/trilium/assets/db ]]; then DB_PATH="/opt/trilium/assets/db" DB_RESTORE_PATH="/opt/trilium/assets/db" else msg_error "Database not found in either /opt/trilium/db or /opt/trilium/assets/db" exit fi msg_info "Stopping Service" systemctl stop trilium sleep 1 msg_ok "Stopped Service" msg_info "Backing up Database" mkdir -p /opt/trilium_backup cp -r "${DB_PATH}" /opt/trilium_backup/ rm -rf /opt/trilium msg_ok "Backed up Database" fetch_and_deploy_gh_release "Trilium" "TriliumNext/Trilium" "prebuild" "latest" "/opt/trilium" "TriliumNotes-Server-*linux-x64.tar.xz" msg_info "Restoring Database" mkdir -p "$(dirname "${DB_RESTORE_PATH}")" cp -r /opt/trilium_backup/$(basename "${DB_PATH}") "${DB_RESTORE_PATH}" rm -rf /opt/trilium_backup msg_ok "Restored Database" msg_info "Starting Service" systemctl start trilium sleep 1 msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8080${CL}" ================================================ FILE: ct/trip.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: CrazyWolf13 # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/itskovacs/TRIP APP="TRIP" var_tags="${var_tags:-maps;travel}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-1024}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/trip ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "trip" "itskovacs/TRIP"; then msg_info "Stopping Service" systemctl stop trip msg_ok "Stopped Service" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "trip" "itskovacs/TRIP" "tarball" msg_info "Updating Frontend" cd /opt/trip/src $STD npm install $STD npm run build mkdir -p /opt/trip/frontend cp -r /opt/trip/src/dist/trip/browser/* /opt/trip/frontend/ msg_ok "Updated Frontend" msg_info "Updating Backend" cd /opt/trip/backend $STD uv pip install --python /opt/trip/.venv/bin/python -r trip/requirements.txt msg_ok "Updated Backend" msg_info "Starting Service" systemctl start trip msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8000${CL}" ================================================ FILE: ct/tubearchivist.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/tubearchivist/tubearchivist APP="Tube Archivist" var_tags="${var_tags:-media;youtube;archiving}" var_cpu="${var_cpu:-4}" var_ram="${var_ram:-6144}" var_disk="${var_disk:-30}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/tubearchivist ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "tubearchivist" "tubearchivist/tubearchivist"; then msg_info "Stopping Services" systemctl stop tubearchivist tubearchivist-celery tubearchivist-beat msg_ok "Stopped Services" msg_info "Backing up Data" cp /opt/tubearchivist/.env /opt/tubearchivist_env.bak msg_ok "Backed up Data" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "tubearchivist" "tubearchivist/tubearchivist" "tarball" msg_info "Rebuilding Tube Archivist" cd /opt/tubearchivist/frontend $STD npm install $STD npm run build:deploy mkdir -p /opt/tubearchivist/backend/static cp -r /opt/tubearchivist/frontend/dist/* /opt/tubearchivist/backend/static/ cp /opt/tubearchivist/docker_assets/backend_start.py /opt/tubearchivist/backend/ $STD uv pip install --python /opt/tubearchivist/.venv/bin/python -r /opt/tubearchivist/backend/requirements.txt if [[ -f /opt/tubearchivist/backend/requirements.plugins.txt ]]; then mkdir -p /opt/yt_plugins/bgutil $STD uv pip install --python /opt/tubearchivist/.venv/bin/python --target /opt/yt_plugins/bgutil -r /opt/tubearchivist/backend/requirements.plugins.txt fi msg_ok "Rebuilt Tube Archivist" msg_info "Restoring Configuration" mv /opt/tubearchivist_env.bak /opt/tubearchivist/.env sed -i 's|^TA_APP_DIR=/opt/tubearchivist$|TA_APP_DIR=/opt/tubearchivist/backend|' /opt/tubearchivist/.env sed -i 's|^TA_CACHE_DIR=/opt/tubearchivist/cache$|TA_CACHE_DIR=/cache|' /opt/tubearchivist/.env sed -i 's|^TA_MEDIA_DIR=/opt/tubearchivist/media$|TA_MEDIA_DIR=/youtube|' /opt/tubearchivist/.env ln -sf /opt/tubearchivist/cache /cache ln -sf /opt/tubearchivist/media /youtube ln -sf /opt/tubearchivist/.env /opt/tubearchivist/backend/.env msg_ok "Restored Configuration" msg_info "Starting Services" systemctl start tubearchivist tubearchivist-celery tubearchivist-beat systemctl reload nginx msg_ok "Started Services" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed Successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8000${CL}" ================================================ FILE: ct/tududi.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: vhsdream # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://tududi.com APP="Tududi" var_tags="${var_tags:-todo-app}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/tududi ]]; then msg_error "No ${APP} Installation Found!" exit fi NODE_VERSION="22" setup_nodejs if check_for_gh_release "tududi" "chrisvel/tududi"; then msg_info "Stopping Service" systemctl stop tududi msg_ok "Stopped Service" msg_info "Backing up env file" if [[ -f /opt/tududi/backend/.env ]]; then cp /opt/tududi/backend/.env /opt/tududi.env else cp /opt/tududi/.env /opt/tududi.env fi msg_ok "Backed up env file" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "tududi" "chrisvel/tududi" "tarball" "latest" "/opt/tududi" msg_info "Updating Tududi" cd /opt/tududi $STD npm install export NODE_ENV=production $STD npm run frontend:build mv ./dist ./backend mv /opt/tududi.env /opt/tududi/backend/.env DB="$(sed -n '/^DB_FILE/s/[^=]*=//p' /opt/tududi/backend/.env)" export DB_FILE="$DB" sed -i -e 's|/tududi$|/tududi/backend|' \ -e 's|npm run start|bash /opt/tududi/backend/cmd/start.sh|' \ /etc/systemd/system/tududi.service systemctl daemon-reload msg_ok "Updated Tududi" msg_info "Starting Service" systemctl start tududi msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:3002${CL}" ================================================ FILE: ct/tunarr.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: chrisbenincasa # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://tunarr.com/ APP="Tunarr" var_tags="${var_tags:-iptv}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-1024}" var_disk="${var_disk:-5}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" var_gpu="${var_gpu:-yes}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/tunarr ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "tunarr" "chrisbenincasa/tunarr"; then msg_info "Stopping Service" systemctl stop tunarr msg_ok "Stopped Service" msg_info "Creating Backup" if [ -d "/usr/local/share/tunarr" ]; then tar -czf "/opt/${APP}_backup_$(date +%F).tar.gz" /usr/local/share/tunarr $STD msg_ok "Backup Created" else msg_error "Backup failed: /usr/local/share/tunarr does not exist" fi CLEAN_INSTALL=1 fetch_and_deploy_gh_release "tunarr" "chrisbenincasa/tunarr" "prebuild" "latest" "/opt/tunarr" "*linux-arm64.tar.gz" cd /opt/tunarr mv tunarr* tunarr msg_info "Starting Service" systemctl start tunarr msg_ok "Started Service" msg_ok "Updated successfully!" fi if check_for_gh_release "ersatztv-ffmpeg" "ErsatzTV/ErsatzTV-ffmpeg"; then msg_info "Stopping Service" systemctl stop tunarr msg_ok "Stopped Service" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "ersatztv-ffmpeg" "ErsatzTV/ErsatzTV-ffmpeg" "prebuild" "latest" "/opt/ErsatzTV-ffmpeg" "*-linuxarm64-gpl-7.1.tar.xz" msg_info "Set ErsatzTV-ffmpeg links" chmod +x /opt/ErsatzTV-ffmpeg/bin/* ln -sf /opt/ErsatzTV-ffmpeg/bin/ffmpeg /usr/local/bin/ffmpeg ln -sf /opt/ErsatzTV-ffmpeg/bin/ffplay /usr/local/bin/ffplay ln -sf /opt/ErsatzTV-ffmpeg/bin/ffprobe /usr/local/bin/ffprobe msg_ok "ffmpeg links set" msg_info "Starting Service" systemctl start tunarr msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8000${CL}" ================================================ FILE: ct/twingate-connector.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: twingate-andrewb # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.twingate.com/docs/ APP="Twingate-Connector" var_tags="${var_tags:-network;connector;twingate}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-1024}" var_disk="${var_disk:-3}" var_os="${var_os:-ubuntu}" var_version="${var_version:-24.04}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -f /lib/systemd/system/twingate-connector.service ]]; then msg_error "No ${APP} Installation Found!" exit fi msg_info "Updating Twingate Connector" $STD apt update $STD apt install -y --only-upgrade twingate-connector $STD systemctl restart twingate-connector msg_ok "Updated successfully!" exit } start build_container description msg_ok "All Finished! If you need to update your access or refresh tokens, they can be found in /etc/twingate/connector.conf" ================================================ FILE: ct/ubuntu.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://ubuntu.com/ APP="Ubuntu" var_tags="${var_tags:-os}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-512}" var_disk="${var_disk:-2}" var_os="${var_os:-ubuntu}" var_version="${var_version:-24.04}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /var ]]; then msg_error "No ${APP} Installation Found!" exit fi msg_info "Updating ${APP} LXC" $STD apt-get update $STD apt-get -y upgrade msg_ok "Updated ${APP} LXC" msg_ok "Updated successfully!" exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" ================================================ FILE: ct/uhf.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: zackwithak13 # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.uhfapp.com/server APP="UHF" var_tags="${var_tags:-media}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-8}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" var_gpu="${var_gpu:-yes}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/uhf-server ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "uhf-server" "swapplications/uhf-server-dist"; then msg_info "Stopping Service" systemctl stop uhf-server msg_ok "Stopped Service" msg_info "Updating LXC" $STD apt update $STD apt -y upgrade msg_ok "Updated LXC" fetch_and_deploy_gh_release "comskip" "swapplications/comskip" "prebuild" "latest" "/opt/comskip" "comskip-arm64-*.zip" fetch_and_deploy_gh_release "uhf-server" "swapplications/uhf-server-dist" "prebuild" "latest" "/opt/uhf-server" "UHF.Server-linux-arm64-*.zip" msg_info "Starting Service" systemctl start uhf-server msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:7568${CL}" ================================================ FILE: ct/umami.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://umami.is/ APP="Umami" var_tags="${var_tags:-analytics}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-12}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/umami ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "umami" "umami-software/umami"; then msg_info "Stopping Service" systemctl stop umami msg_ok "Stopped Service" mv /opt/umami/.env /opt/.env.bak CLEAN_INSTALL=1 fetch_and_deploy_gh_release "umami" "umami-software/umami" "tarball" mv /opt/.env.bak /opt/umami/.env msg_info "Updating Umami" cd /opt/umami $STD pnpm install $STD pnpm run build msg_ok "Updated Umami" msg_info "Starting Service" systemctl start umami msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:3000${CL}" ================================================ FILE: ct/umlautadaptarr.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: elvito # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/PCJones/UmlautAdaptarr APP="UmlautAdaptarr" var_tags="${var_tags:-arr}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-512}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/UmlautAdaptarr ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "UmlautAdaptarr" "PCJones/Umlautadaptarr"; then msg_info "Stopping Service" systemctl stop umlautadaptarr msg_ok "Stopped Service" cp /opt/UmlautAdaptarr/appsettings.json /opt/UmlautAdaptarr/appsettings.json.bak fetch_and_deploy_gh_release "UmlautAdaptarr" "PCJones/Umlautadaptarr" "prebuild" "latest" "/opt/UmlautAdaptarr" "linux-x64.zip" cp /opt/UmlautAdaptarr/appsettings.json.bak /opt/UmlautAdaptarr/appsettings.json msg_info "Starting Service" systemctl start umlautadaptarr msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:5005${CL}" ================================================ FILE: ct/unbound.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: wimb0 # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/NLnetLabs/unbound APP="Unbound" var_tags="${var_tags:-dns}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-512}" var_disk="${var_disk:-2}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /etc/unbound ]]; then msg_error "No ${APP} Installation Found!" exit fi msg_info "Updating Unbound" $STD apt update $STD apt -y upgrade msg_ok "Updated Unbound" msg_ok "Updated successfully!" exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:5335${CL}" ================================================ FILE: ct/unifi-os-server.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://ui.com/ APP="UniFi-OS-Server" var_tags="${var_tags:-network}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-4096}" var_disk="${var_disk:-20}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-0}" var_tun="${var_tun:-yes}" var_nesting="${var_nesting:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -f /usr/local/sbin/unifi-os-server.bin && ! -d /data/unifi ]]; then msg_error "No ${APP} Installation Found!" exit fi msg_custom "🚀" "${GN}" "The app offers a built-in updater. Please use it." exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}https://${IP}:11443${CL}" ================================================ FILE: ct/unmanic.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://docs.unmanic.app/ APP="Unmanic" var_tags="${var_tags:-file;media}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-0}" var_gpu="${var_gpu:-yes}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -f /etc/systemd/system/unmanic.service ]]; then msg_error "No ${APP} Installation Found!" exit fi msg_info "Updating $APP LXC" $STD pip3 install -U unmanic $STD apt -y upgrade msg_ok "Updated $APP LXC" msg_ok "Updated successfully!" exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8888${CL}" ================================================ FILE: ct/upgopher.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Eduard González (wanetty) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/wanetty/upgopher APP="Upgopher" var_tags="${var_tags:-file-sharing}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-512}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/upgopher ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "upgopher" "wanetty/upgopher"; then msg_info "Stopping Service" systemctl stop upgopher msg_ok "Stopped Service" fetch_and_deploy_gh_release "upgopher" "wanetty/upgopher" "prebuild" "latest" "/opt/upgopher" "upgopher_*_linux_arm64.tar.gz" chmod +x /opt/upgopher/upgopher msg_info "Starting Service" systemctl start upgopher msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:9090${CL}" ================================================ FILE: ct/upsnap.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Slaviša Arežina (tremor021) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/seriousm4x/UpSnap APP="UpSnap" var_tags="${var_tags:-network}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-512}" var_disk="${var_disk:-2}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/upsnap ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "upsnap" "seriousm4x/UpSnap"; then msg_info "Stopping Services" systemctl stop upsnap msg_ok "Stopped Services" fetch_and_deploy_gh_release "upsnap" "seriousm4x/UpSnap" "prebuild" "latest" "/opt/upsnap" "UpSnap_*_linux_arm64.zip" msg_info "Starting Services" systemctl start upsnap msg_ok "Started Services" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8090${CL}" ================================================ FILE: ct/uptimekuma.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://uptime.kuma.pet/ APP="Uptime Kuma" var_tags="${var_tags:-analytics;monitoring}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-1024}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/uptime-kuma ]]; then msg_error "No ${APP} Installation Found!" exit fi NODE_VERSION="22" setup_nodejs ensure_dependencies chromium if [[ ! -L /opt/uptime-kuma/chromium ]]; then ln -s /usr/bin/chromium /opt/uptime-kuma/chromium fi if check_for_gh_release "uptime-kuma" "louislam/uptime-kuma"; then msg_info "Stopping Service" systemctl stop uptime-kuma msg_ok "Stopped Service" fetch_and_deploy_gh_release "uptime-kuma" "louislam/uptime-kuma" "tarball" msg_info "Updating Uptime Kuma" cd /opt/uptime-kuma $STD npm install --omit dev $STD npm run download-dist msg_ok "Updated Uptime Kuma" msg_info "Starting Service" systemctl start uptime-kuma msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:3001${CL}" ================================================ FILE: ct/urbackupserver.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Kristian Skov # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.urbackup.org/ APP="UrBackup Server" var_tags="${var_tags:-backup}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-1024}" var_disk="${var_disk:-16}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-0}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /var/urbackup ]]; then msg_error "No ${APP} Installation Found!" exit fi msg_info "Updating ${APP} LXC" $STD apt update $STD apt upgrade -y msg_ok "Updated successfully!" exit } start build_container description pct set "$CTID" -features fuse=1,nesting=1 pct reboot "$CTID" msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following IP:${CL}" echo -e "${TAB}${GATEWAY}${BGN}${IP}:55414${CL}" ================================================ FILE: ct/valkey.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: pshankinclarke (lazarillo) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://valkey.io/ APP="Valkey" var_tags="${var_tags:-database}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-1024}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -f /lib/systemd/system/valkey-server.service ]]; then msg_error "No Valkey Installation Found!" exit fi msg_info "Updating Valkey LXC" $STD apt update $STD apt -y upgrade msg_ok "Updated Valkey LXC" msg_ok "Updated successfully!" exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:6379${CL}" ================================================ FILE: ct/vaultwarden.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/dani-garcia/vaultwarden APP="Vaultwarden" var_tags="${var_tags:-password-manager}" var_cpu="${var_cpu:-4}" var_ram="${var_ram:-6144}" var_disk="${var_disk:-20}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -f /etc/systemd/system/vaultwarden.service ]]; then msg_error "No ${APP} Installation Found!" exit fi VAULT=$(get_latest_github_release "dani-garcia/vaultwarden") WVRELEASE=$(get_latest_github_release "dani-garcia/bw_web_builds") UPD=$(msg_menu "Vaultwarden Update Options" \ "1" "Update VaultWarden + Web-Vault" \ "2" "Set Admin Token") if [ "$UPD" == "1" ]; then if check_for_gh_release "vaultwarden" "dani-garcia/vaultwarden"; then msg_info "Stopping Service" systemctl stop vaultwarden msg_ok "Stopped Service" fetch_and_deploy_gh_release "vaultwarden" "dani-garcia/vaultwarden" "tarball" "latest" "/tmp/vaultwarden-src" msg_info "Updating VaultWarden to $VAULT (Patience)" cd /tmp/vaultwarden-src VW_VERSION="$VAULT" export VW_VERSION $STD cargo build --features "sqlite,mysql,postgresql" --release if [[ -f /usr/bin/vaultwarden ]]; then cp target/release/vaultwarden /usr/bin/ else cp target/release/vaultwarden /opt/vaultwarden/bin/ fi cd ~ && rm -rf /tmp/vaultwarden-src msg_ok "Updated VaultWarden to ${VAULT}" msg_info "Starting Service" systemctl start vaultwarden msg_ok "Started Service" else msg_ok "VaultWarden is already up-to-date" fi if check_for_gh_release "vaultwarden_webvault" "dani-garcia/bw_web_builds"; then msg_info "Stopping Service" systemctl stop vaultwarden msg_ok "Stopped Service" msg_info "Updating Web-Vault to $WVRELEASE" rm -rf /opt/vaultwarden/web-vault mkdir -p /opt/vaultwarden/web-vault fetch_and_deploy_gh_release "vaultwarden_webvault" "dani-garcia/bw_web_builds" "prebuild" "latest" "/opt/vaultwarden/web-vault" "bw_web_*.tar.gz" chown -R root:root /opt/vaultwarden/web-vault/ msg_ok "Updated Web-Vault to ${WVRELEASE}" msg_info "Starting Service" systemctl start vaultwarden msg_ok "Started Service" else msg_ok "Web-Vault is already up-to-date" fi msg_ok "Updated successfully!" exit fi if [ "$UPD" == "2" ]; then if [[ "${PHS_SILENT:-0}" == "1" ]]; then msg_warn "Set Admin Token requires interactive mode, skipping." exit fi read -r -s -p "Set the ADMIN_TOKEN: " NEWTOKEN echo "" if [[ -n "$NEWTOKEN" ]]; then ensure_dependencies argon2 TOKEN=$(echo -n "${NEWTOKEN}" | argon2 "$(openssl rand -base64 32)" -t 2 -m 16 -p 4 -l 64 -e) sed -i "s|ADMIN_TOKEN=.*|ADMIN_TOKEN='${TOKEN}'|" /opt/vaultwarden/.env if [[ -f /opt/vaultwarden/data/config.json ]]; then sed -i "s|\"admin_token\":.*|\"admin_token\": \"${TOKEN}\"|" /opt/vaultwarden/data/config.json fi systemctl restart vaultwarden msg_ok "Admin token updated" fi exit fi } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}https://${IP}:8000${CL}" ================================================ FILE: ct/versitygw.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/versity/versitygw APP="VersityGW" var_tags="${var_tags:-s3;storage;gateway}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-8}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -f /usr/bin/versitygw ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "versitygw" "versity/versitygw"; then msg_info "Stopping Service" systemctl stop versitygw@gateway msg_ok "Stopped Service" fetch_and_deploy_gh_release "versitygw" "versity/versitygw" "binary" msg_info "Starting Service" systemctl start versitygw@gateway msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed Successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:7070 (Gateway) or http://${IP}:7070 (WebUI)${CL}" ================================================ FILE: ct/victoriametrics.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Slaviša Arežina (tremor021) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/VictoriaMetrics/VictoriaMetrics APP="VictoriaMetrics" var_tags="${var_tags:-database}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-16}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/victoriametrics ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "victoriametrics" "VictoriaMetrics/VictoriaMetrics"; then msg_info "Stopping Service" systemctl stop victoriametrics [[ -f /etc/systemd/system/victoriametrics-logs.service ]] && systemctl stop victoriametrics-logs [[ -f /etc/systemd/system/vmagent.service ]] && systemctl stop vmagent [[ -f /etc/systemd/system/vmalert.service ]] && systemctl stop vmalert msg_ok "Stopped Service" victoriametrics_filename=$(curl -fsSL "https://api.github.com/repos/VictoriaMetrics/VictoriaMetrics/releases/latest" | jq -r '.assets[].name' | grep -E '^victoria-metrics-linux-arm64-v[0-9.]+\.tar\.gz$') vmutils_filename=$(curl -fsSL "https://api.github.com/repos/VictoriaMetrics/VictoriaMetrics/releases/latest" | jq -r '.assets[].name' | grep -E '^vmutils-linux-arm64-v[0-9.]+\.tar\.gz$') msg_debug "Using release $victoriametrics_release" victoriametrics_filename="victoria-metrics-linux-amd64-${victoriametrics_release}.tar.gz" vmutils_filename="vmutils-linux-amd64-${victoriametrics_release}.tar.gz" fetch_and_deploy_gh_release "victoriametrics" "VictoriaMetrics/VictoriaMetrics" "prebuild" "$victoriametrics_release" "/opt/victoriametrics" "$victoriametrics_filename" fetch_and_deploy_gh_release "vmutils" "VictoriaMetrics/VictoriaMetrics" "prebuild" "$victoriametrics_release" "/opt/victoriametrics" "$vmutils_filename" if [[ -f /etc/systemd/system/victoriametrics-logs.service ]]; then vmlogs_filename=$(curl -fsSL "https://api.github.com/repos/VictoriaMetrics/VictoriaLogs/releases/latest" | jq -r '.assets[].name' | grep -E '^victoria-logs-linux-arm64-v[0-9.]+\.tar\.gz$') vlutils_filename=$(curl -fsSL "https://api.github.com/repos/VictoriaMetrics/VictoriaLogs/releases/latest" | jq -r '.assets[].name' | grep -E '^vlutils-linux-arm64-v[0-9.]+\.tar\.gz$') fetch_and_deploy_gh_release "victorialogs" "VictoriaMetrics/VictoriaLogs" "prebuild" "latest" "/opt/victoriametrics" "$vmlogs_filename" fetch_and_deploy_gh_release "vlutils" "VictoriaMetrics/VictoriaLogs" "prebuild" "latest" "/opt/victoriametrics" "$vlutils_filename" fi chmod +x /opt/victoriametrics/* msg_info "Starting Service" systemctl start victoriametrics [[ -f /etc/systemd/system/victoriametrics-logs.service ]] && systemctl start victoriametrics-logs [[ -f /etc/systemd/system/vmagent.service ]] && systemctl start vmagent [[ -f /etc/systemd/system/vmalert.service ]] && systemctl start vmalert msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8428/vmui${CL}" ================================================ FILE: ct/vikunja.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (Canbiz) | Co-Author: CrazyWolf13 # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://vikunja.io/ APP="Vikunja" var_tags="${var_tags:-todo-app}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-1024}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/vikunja ]]; then msg_error "No ${APP} Installation Found!" exit fi RELEASE="$( [[ -f "$HOME/.vikunja" ]] && cat "$HOME/.vikunja" 2>/dev/null || [[ -f /opt/Vikunja_version ]] && cat /opt/Vikunja_version 2>/dev/null || true)" if [[ -z "$RELEASE" ]] || [[ "$RELEASE" == "unstable" ]] || dpkg --compare-versions "${RELEASE:-0.0.0}" lt "1.0.0"; then msg_warn "You are upgrading from Vikunja '$RELEASE'." msg_warn "This requires MANUAL config changes in /etc/vikunja/config.yml." msg_warn "See: https://vikunja.io/changelog/whats-new-in-vikunja-1.0.0/#config-changes" read -rp "Continue with update? (y to proceed): " -t 30 CONFIRM1 || exit 254 [[ "$CONFIRM1" =~ ^[yY]$ ]] || exit 0 echo msg_warn "Vikunja may not start after the update until you manually adjust the config." msg_warn "Details: https://vikunja.io/changelog/whats-new-in-vikunja-1.0.0/#config-changes" read -rp "Acknowledge and continue? (y): " -t 30 CONFIRM2 || exit 254 [[ "$CONFIRM2" =~ ^[yY]$ ]] || exit 0 fi if check_for_gh_release "vikunja" "go-vikunja/vikunja"; then echo msg_warn "The package update may include config file changes." echo -e "${TAB}${YW}How do you want to handle /etc/vikunja/config.yml?${CL}" echo -e "${TAB} 1) Keep your current config" echo -e "${TAB} 2) Install the new package maintainer's config" read -rp " Choose [1/2] (default: 1): " -t 60 CONFIG_CHOICE || CONFIG_CHOICE="1" [[ -z "$CONFIG_CHOICE" ]] && CONFIG_CHOICE="1" if [[ "$CONFIG_CHOICE" == "2" ]]; then export DPKG_FORCE_CONFNEW="1" else export DPKG_FORCE_CONFOLD="1" fi msg_info "Stopping Service" systemctl stop vikunja msg_ok "Stopped Service" fetch_and_deploy_gh_release "vikunja" "go-vikunja/vikunja" "binary" $STD systemctl daemon-reload msg_info "Starting Service" systemctl start vikunja msg_ok "Started Service" msg_ok "Updated successfully!" fi exit 0 } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:3456${CL}" ================================================ FILE: ct/wallabag.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://wallabag.org/ APP="Wallabag" var_tags="${var_tags:-productivity;read-it-later}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-6}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/wallabag ]]; then msg_error "No ${APP} Installation Found!" exit fi setup_mariadb if check_for_gh_release "wallabag" "wallabag/wallabag"; then msg_info "Stopping Services" systemctl stop nginx php8.3-fpm msg_ok "Stopped Services" msg_info "Creating Backup" cp /opt/wallabag/app/config/parameters.yml /tmp/wallabag_parameters.yml.bak msg_ok "Created Backup" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "wallabag" "wallabag/wallabag" "prebuild" "latest" "/opt/wallabag" "wallabag-*.tar.gz" msg_info "Restoring Configuration" cp /tmp/wallabag_parameters.yml.bak /opt/wallabag/app/config/parameters.yml rm -f /tmp/wallabag_parameters.yml.bak msg_ok "Restored Configuration" msg_info "Running Migrations" cd /opt/wallabag $STD php bin/console cache:clear --env=prod $STD php bin/console doctrine:migrations:migrate --env=prod --no-interaction chown -R www-data:www-data /opt/wallabag chmod -R 755 /opt/wallabag/{var,web/assets} msg_ok "Ran Migrations" msg_info "Starting Services" systemctl start php8.3-fpm nginx msg_ok "Started Services" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8000${CL}" ================================================ FILE: ct/wallos.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: MickLesk (Canbiz) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://wallosapp.com/ APP="Wallos" var_tags="${var_tags:-finance}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-1024}" var_disk="${var_disk:-5}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/wallos ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "wallos" "ellite/Wallos"; then msg_info "Creating backup" mkdir -p /opt/logos mv /opt/wallos/db/wallos.db /opt/wallos.db mv /opt/wallos/images/uploads/logos /opt/logos/ msg_ok "Backup created" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "wallos" "ellite/Wallos" "tarball" msg_info "Configuring Wallos" rm -rf /opt/wallos/db/wallos.empty.db mv /opt/wallos.db /opt/wallos/db/wallos.db mv /opt/logos/* /opt/wallos/images/uploads/logos if ! grep -q "storetotalyearlycost.php" /opt/wallos.cron; then echo "30 1 * * 1 php /opt/wallos/endpoints/cronjobs/storetotalyearlycost.php >> /var/log/cron/storetotalyearlycost.log 2>&1" >>/opt/wallos.cron fi chown -R www-data:www-data /opt/wallos chmod -R 755 /opt/wallos mkdir -p /var/log/cron $STD curl http://localhost/endpoints/db/migrate.php msg_ok "Configured Wallos" msg_info "Reload Apache2" systemctl reload apache2 msg_ok "Apache2 Reloaded" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}${CL}" ================================================ FILE: ct/wanderer.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: rrole # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://wanderer.to APP="Wanderer" var_tags="${var_tags:-travelling;sport}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-4096}" var_disk="${var_disk:-8}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -f /opt/wanderer/start.sh ]]; then msg_error "No wanderer Installation Found!" exit fi if check_for_gh_release "wanderer" "Flomp/wanderer"; then msg_info "Stopping service" systemctl stop wanderer-web msg_ok "Stopped service" fetch_and_deploy_gh_release "wanderer" "open-wanderer/wanderer" "tarball" "latest" "/opt/wanderer/source" msg_info "Updating wanderer" cd /opt/wanderer/source/db $STD go mod tidy $STD go build cd /opt/wanderer/source/web $STD npm ci --omit=dev $STD npm run build msg_ok "Updated wanderer" msg_info "Starting service" systemctl start wanderer-web msg_ok "Started service" msg_ok "Update Successful" fi if check_for_gh_release "meilisearch" "meilisearch/meilisearch"; then msg_info "Stopping service" systemctl stop wanderer-web msg_ok "Stopped service" fetch_and_deploy_gh_release "meilisearch" "meilisearch/meilisearch" "singlefile" "latest" "/opt/wanderer/source/search" "meilisearch-linux-aarch64" grep -q -- '--experimental-dumpless-upgrade' /opt/wanderer/start.sh || sed -i 's|meilisearch --master-key|meilisearch --experimental-dumpless-upgrade --master-key|' /opt/wanderer/start.sh msg_info "Starting service" systemctl start wanderer-web msg_ok "Started service" msg_ok "Update Successful" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:3000${CL}" ================================================ FILE: ct/warracker.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: BvdBerg01 # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/sassanix/Warracker/ APP="Warracker" var_tags="${var_tags:-warranty}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-512}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/warracker ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "warracker" "sassanix/Warracker"; then msg_info "Stopping Services" systemctl stop warrackermigration systemctl stop warracker systemctl stop nginx msg_ok "Stopped Services" fetch_and_deploy_gh_release "warracker" "sassanix/Warracker" "tarball" "latest" "/opt/warracker" msg_info "Updating Warracker" cd /opt/warracker/backend $STD uv venv --clear .venv $STD source .venv/bin/activate $STD uv pip install -r requirements.txt msg_ok "Updated Warracker" msg_info "Starting Services" systemctl start warracker systemctl start nginx msg_ok "Started Services" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}${CL}" ================================================ FILE: ct/wastebin.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: MickLesk (Canbiz) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/matze/wastebin APP="Wastebin" var_tags="${var_tags:-file;code}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-1024}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/wastebin ]]; then msg_error "No ${APP} Installation Found!" exit fi ensure_dependencies zstd RELEASE=$(curl -fsSL https://api.github.com/repos/matze/wastebin/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }') # Dirty-Fix 03/2025 for missing APP_version.txt on old installations, set to pre-latest release msg_info "Running Migration" if [[ ! -f /opt/${APP}_version.txt ]]; then echo "2.7.1" >/opt/${APP}_version.txt mkdir -p /opt/wastebin-data cat </opt/wastebin-data/.env WASTEBIN_DATABASE_PATH=/opt/wastebin-data/wastebin.db WASTEBIN_CACHE_SIZE=1024 WASTEBIN_HTTP_TIMEOUT=30 WASTEBIN_SIGNING_KEY=$(openssl rand -hex 32) WASTEBIN_PASTE_EXPIRATIONS=0,600,3600=d,86400,604800,2419200,29030400 EOF systemctl stop wastebin cat </etc/systemd/system/wastebin.service [Unit] Description=Wastebin Service After=network.target [Service] WorkingDirectory=/opt/wastebin ExecStart=/opt/wastebin/wastebin EnvironmentFile=/opt/wastebin-data/.env [Install] WantedBy=multi-user.target EOF systemctl daemon-reload fi msg_ok "Migration Done" if [[ ! -f /opt/${APP}_version.txt ]] || [[ "${RELEASE}" != "$(cat /opt/${APP}_version.txt)" ]]; then msg_info "Stopping Wastebin" systemctl stop wastebin msg_ok "Wastebin Stopped" msg_info "Updating Wastebin" temp_file=$(mktemp) curl -fsSL "https://github.com/matze/wastebin/releases/download/${RELEASE}/wastebin_${RELEASE}_aarch64-unknown-linux-musl.tar.zst" -o "$temp_file" tar -xf "$temp_file" cp -f wastebin* /opt/wastebin/ chmod +x /opt/wastebin/wastebin chmod +x /opt/wastebin/wastebin-ctl rm -f "$temp_file" echo "${RELEASE}" >/opt/${APP}_version.txt msg_ok "Updated Wastebin" msg_info "Starting Wastebin" systemctl start wastebin msg_ok "Started Wastebin" msg_ok "Updated successfully!" else msg_ok "No update required. ${APP} is already at v${RELEASE}" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8088${CL}" ================================================ FILE: ct/watcharr.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Slaviša Arežina (tremor021) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/sbondCo/Watcharr APP="Watcharr" var_tags="${var_tags:-media}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-1024}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/watcharr ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "watcharr" "sbondCo/Watcharr"; then msg_info "Stopping Service" systemctl stop watcharr msg_ok "Stopped Service" rm -f /opt/watcharr/server/watcharr rm -rf /opt/watcharr/server/ui fetch_and_deploy_gh_release "watcharr" "sbondCo/Watcharr" "tarball" msg_info "Updating Watcharr" cd /opt/watcharr export GOOS=linux $STD npm i $STD npm run build mv ./build ./server/ui cd server $STD go mod download $STD go build -o ./watcharr msg_ok "Updated Watcharr" msg_info "Starting Service" systemctl start watcharr msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:3080${CL}" ================================================ FILE: ct/watchyourlan.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/aceberg/WatchYourLAN APP="WatchYourLAN" var_tags="${var_tags:-network}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-512}" var_disk="${var_disk:-2}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -f /lib/systemd/system/watchyourlan.service ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "watchyourlan" "aceberg/WatchYourLAN"; then msg_info "Stopping service" systemctl stop watchyourlan msg_ok "Service stopped" cp -R /data/config.yaml ~/config.yaml fetch_and_deploy_gh_release "watchyourlan" "aceberg/WatchYourLAN" "binary" cp -R config.yaml /data/config.yaml sed -i 's|/etc/watchyourlan/config.yaml|/data/config.yaml|' /lib/systemd/system/watchyourlan.service rm ~/config.yaml msg_info "Starting service" systemctl enable -q --now watchyourlan msg_ok "Service started" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8840${CL}" ================================================ FILE: ct/wavelog.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Don Locke (DonLocke) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.wavelog.org/ APP="Wavelog" var_tags="${var_tags:-radio-logging}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-512}" var_disk="${var_disk:-2}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/wavelog ]]; then msg_error "No ${APP} Installation Found!" exit fi setup_mariadb if check_for_gh_release "wavelog" "wavelog/wavelog"; then msg_info "Stopping Services" systemctl stop apache2 msg_ok "Services Stopped" msg_info "Creating backup" cp /opt/wavelog/application/config/config.php /opt/config.php cp /opt/wavelog/application/config/database.php /opt/database.php cp -r /opt/wavelog/userdata /opt/userdata if [[ -f /opt/wavelog/assets/js/sections/custom.js ]]; then cp /opt/wavelog/assets/js/sections/custom.js /opt/custom.js fi msg_ok "Backup created" rm -rf /opt/wavelog fetch_and_deploy_gh_release "wavelog" "wavelog/wavelog" "tarball" msg_info "Updating Wavelog" rm -rf /opt/wavelog/install mv /opt/config.php /opt/wavelog/application/config/config.php mv /opt/database.php /opt/wavelog/application/config/database.php cp -r /opt/userdata/* /opt/wavelog/userdata rm -rf /opt/userdata if [[ -f /opt/custom.js ]]; then mv /opt/custom.js /opt/wavelog/assets/js/sections/custom.js fi chown -R www-data:www-data /opt/wavelog/ find /opt/wavelog/ -type d -exec chmod 755 {} \; find /opt/wavelog/ -type f -exec chmod 664 {} \; msg_ok "Updated Wavelog" msg_info "Starting Services" systemctl start apache2 msg_ok "Started Services" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}${CL}" ================================================ FILE: ct/wazuh.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Omar Minaya # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://wazuh.com/ echo -e "Wazuh not supported on ARM64.\n" exit APP="Wazuh" var_tags="${var_tags:-security;monitoring}" var_cpu="${var_cpu:-4}" var_ram="${var_ram:-4096}" var_disk="${var_disk:-25}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -f /lib/systemd/system/wazuh-manager.service ]]; then msg_error "No ${APP} Installation Found!" exit fi msg_info "Updating Wazuh LXC" $STD apt update $STD apt upgrade -y msg_ok "Updated Wazuh LXC" msg_ok "Updated successfully!" exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}https://${IP}:443${CL}" ================================================ FILE: ct/wealthfolio.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: CrazyWolf13 # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://wealthfolio.app/ APP="Wealthfolio" var_tags="${var_tags:-finance;portfolio}" var_cpu="${var_cpu:-4}" var_ram="${var_ram:-4096}" var_disk="${var_disk:-10}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/wealthfolio ]]; then msg_error "No ${APP} Installation Found!" exit fi NODE_VERSION="24" NODE_MODULE="pnpm" setup_nodejs if grep -q '^WF_CORS_ALLOW_ORIGINS=\*$' /opt/wealthfolio/.env; then sed -i "s|^WF_CORS_ALLOW_ORIGINS=\*$|WF_CORS_ALLOW_ORIGINS=http://${LOCAL_IP}:8080|" /opt/wealthfolio/.env fi if check_for_gh_release "wealthfolio" "afadil/wealthfolio"; then msg_info "Stopping Service" systemctl stop wealthfolio msg_ok "Stopped Service" msg_info "Backing up Data" cp -r /opt/wealthfolio_data /opt/wealthfolio_data_backup cp /opt/wealthfolio/.env /opt/wealthfolio_env_backup msg_ok "Backed up Data" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "wealthfolio" "afadil/wealthfolio" "tarball" msg_info "Building Frontend (patience)" cd /opt/wealthfolio export BUILD_TARGET=web $STD pnpm install --frozen-lockfile $STD pnpm --filter frontend... build msg_ok "Built Frontend" msg_info "Building Backend (patience)" source ~/.cargo/env $STD cargo build --release --manifest-path apps/server/Cargo.toml cp /opt/wealthfolio/target/release/wealthfolio-server /usr/local/bin/wealthfolio-server chmod +x /usr/local/bin/wealthfolio-server msg_ok "Built Backend" msg_info "Restoring Data" cp -r /opt/wealthfolio_data_backup/. /opt/wealthfolio_data cp /opt/wealthfolio_env_backup /opt/wealthfolio/.env rm -rf /opt/wealthfolio_data_backup /opt/wealthfolio_env_backup msg_ok "Restored Data" msg_info "Cleaning Up" rm -rf /opt/wealthfolio/target rm -rf /root/.cargo/registry rm -rf /opt/wealthfolio/node_modules msg_ok "Cleaned Up" msg_info "Starting Service" systemctl start wealthfolio msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8080${CL}" ================================================ FILE: ct/web-check.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: CrazyWolf13 # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/Lissy93/web-check APP="web-check" var_tags="${var_tags:-network;analysis}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-12}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/web-check ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "web-check" "Lissy93/web-check"; then msg_info "Stopping Service" systemctl stop web-check msg_ok "Stopped Service" msg_info "Creating backup" mv /opt/web-check/.env /opt msg_ok "Created backup" NODE_VERSION="22" NODE_MODULE="yarn" setup_nodejs CLEAN_INSTALL=1 fetch_and_deploy_gh_release "web-check" "Lissy93/web-check" "tarball" msg_info "Restoring backup" mv /opt/.env /opt/web-check msg_ok "Restored backup" msg_info "Building Web-Check" cd /opt/web-check $STD yarn install --frozen-lockfile --network-timeout 100000 $STD yarn build --production $STD npm cache clean --force msg_ok "Built Web-Check" msg_info "Starting Service" systemctl start web-check msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:3000${CL}" ================================================ FILE: ct/wger.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Slaviša Arežina (tremor021) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/wger-project/wger APP="wger" var_tags="${var_tags:-management;fitness}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-8}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/wger ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "wger" "wger-project/wger"; then msg_info "Stopping Service" systemctl stop redis-server nginx celery celery-beat wger msg_ok "Stopped Service" msg_info "Backing up Data" cp -r /opt/wger/media /opt/wger_media_backup cp /opt/wger/.env /opt/wger_env_backup msg_ok "Backed up Data" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "wger" "wger-project/wger" "tarball" msg_info "Restoring Data" cp -r /opt/wger_media_backup/. /opt/wger/media cp /opt/wger_env_backup /opt/wger/.env rm -rf /opt/wger_media_backup /opt/wger_env_backup msg_ok "Restored Data" msg_info "Updating wger" cd /opt/wger set -a && source /opt/wger/.env && set +a export DJANGO_SETTINGS_MODULE=settings.main $STD uv pip install . $STD npm install $STD npm run build:css:sass $STD uv run python manage.py migrate $STD uv run python manage.py collectstatic --no-input msg_ok "Updated wger" msg_info "Starting Services" systemctl start redis-server nginx celery celery-beat wger msg_ok "Started Services" msg_ok "Updated Successfully" fi exit } start build_container description msg_ok "Completed Successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:3000${CL}" ================================================ FILE: ct/whisparr.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/Whisparr/Whisparr APP="Whisparr" var_tags="${var_tags:-arr}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-1024}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /var/lib/whisparr ]]; then msg_error "No ${APP} Installation Found!" exit fi msg_custom "🚀" "${GN}" "The app offers a built-in updater. Please use it." exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:6969${CL}" ================================================ FILE: ct/whodb.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://whodb.com/ APP="WhoDB" var_tags="${var_tags:-database;management;gui}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-512}" var_disk="${var_disk:-2}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -f /opt/whodb/whodb ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "whodb" "clidey/whodb"; then msg_info "Stopping Service" systemctl stop whodb msg_ok "Stopped Service" fetch_and_deploy_gh_release "whodb" "clidey/whodb" "singlefile" "latest" "/opt/whodb" "whodb-*-linux-amd64" msg_info "Starting Service" systemctl start whodb msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed Successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8080${CL}" ================================================ FILE: ct/wikijs.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://js.wiki/ APP="Wikijs" var_tags="${var_tags:-wiki}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-10}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/wikijs ]]; then msg_error "No ${APP} Installation Found!" exit fi NODE_VERSION="22" NODE_MODULE="yarn,node-gyp" setup_nodejs if check_for_gh_release "wikijs" "requarks/wiki"; then msg_info "Verifying whether ${APP}' new release is v3.x+ and current install uses SQLite." SQLITE_INSTALL=$([ -f /opt/wikijs/db.sqlite ] && echo "true" || echo "false") if [[ "${SQLITE_INSTALL}" == "true" && "${CHECK_UPDATE_RELEASE}" =~ ^3.* ]]; then echo "SQLite is not supported in v3.x+, currently there is no update path availble." exit fi msg_ok "There is an update path available for ${APP}" msg_info "Stopping Service" systemctl stop wikijs msg_ok "Stopped Service" msg_info "Backing up Data" mkdir /opt/wikijs-backup $SQLITE_INSTALL && cp /opt/wikijs/db.sqlite /opt/wikijs-backup cp -R /opt/wikijs/{config.yml,/data} /opt/wikijs-backup msg_ok "Backed up Data" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "wikijs" "requarks/wiki" "prebuild" "latest" "/opt/wikijs" "wiki-js.tar.gz" msg_info "Restoring Data" cp -R /opt/wikijs-backup/* /opt/wikijs $SQLITE_INSTALL && $STD npm rebuild sqlite3 rm -rf /opt/wikijs-backup msg_ok "Restored Data" msg_info "Starting Service" systemctl start wikijs msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:3000${CL}" ================================================ FILE: ct/wireguard.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.wireguard.com/ APP="Wireguard" var_tags="${var_tags:-network;vpn}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-512}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" var_tun="${var_tun:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /etc/wireguard ]]; then msg_error "No ${APP} Installation Found!" exit fi ensure_dependencies git msg_info "Updating LXC" $STD apt update $STD apt upgrade -y if [[ -d /etc/wgdashboard ]]; then sleep 2 cd /etc/wgdashboard/src $STD ./wgd.sh update -y $STD ./wgd.sh start fi msg_ok "Updated LXC" msg_ok "Updated successfully!" exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW}Access WGDashboard (if installed) using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:10086${CL}" ================================================ FILE: ct/wishlist.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Dunky13 # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/cmintey/wishlist APP="Wishlist" var_tags="${var_tags:-sharing}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-5}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/wishlist ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "wishlist" "cmintey/wishlist"; then NODE_VERSION="24" NODE_MODULE="pnpm" setup_nodejs msg_info "Stopping Service" systemctl stop wishlist msg_ok "Stopped Service" msg_info "Creating Backup" mkdir -p /opt/wishlist-backup cp /opt/wishlist/.env /opt/wishlist-backup/.env cp -a /opt/wishlist/uploads /opt/wishlist-backup cp -a /opt/wishlist/data /opt/wishlist-backup msg_ok "Created Backup" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "wishlist" "cmintey/wishlist" "tarball" LATEST_APP_VERSION=$(get_latest_github_release "cmintey/wishlist") msg_info "Updating Wishlist" cd /opt/wishlist $STD pnpm install --frozen-lockfile $STD pnpm svelte-kit sync $STD pnpm prisma generate sed -i 's|/usr/src/app/|/opt/wishlist/|g' $(grep -rl '/usr/src/app/' /opt/wishlist) export VERSION="v${LATEST_APP_VERSION}" export SHA="v${LATEST_APP_VERSION}" $STD pnpm run build $STD pnpm prune --prod chmod +x /opt/wishlist/entrypoint.sh msg_info "Restoring Backup" cp /opt/wishlist-backup/.env /opt/wishlist/.env cp -a /opt/wishlist-backup/uploads /opt/wishlist cp -a /opt/wishlist-backup/data /opt/wishlist rm -rf /opt/wishlist-backup msg_ok "Restored Backup" msg_ok "Updated Wishlist" msg_info "Starting Service" systemctl start wishlist msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:3280${CL}" ================================================ FILE: ct/wizarr.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: vhsdream # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/wizarrrr/wizarr APP="Wizarr" var_tags="${var_tags:-media;arr}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-1024}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/wizarr ]]; then msg_error "No ${APP} Installation Found!" exit fi setup_uv if check_for_gh_release "wizarr" "wizarrrr/wizarr"; then msg_info "Stopping Service" systemctl stop wizarr msg_ok "Stopped Service" msg_info "Creating Backup" BACKUP_FILE="/opt/wizarr_backup_$(date +%F).tar.gz" $STD tar -czf "$BACKUP_FILE" /opt/wizarr/{.env,start.sh} /opt/wizarr/database/ &>/dev/null rm -rf /opt/wizarr/migrations/versions/* msg_ok "Backup Created" fetch_and_deploy_gh_release "wizarr" "wizarrrr/wizarr" "tarball" msg_info "Updating Wizarr" cd /opt/wizarr $STD /usr/local/bin/uv sync --frozen $STD /usr/local/bin/uv run --frozen pybabel compile -d app/translations $STD npm --prefix app/static install $STD npm --prefix app/static run build:css mkdir -p ./.cache $STD tar -xf "$BACKUP_FILE" --directory=/ if grep -q 'workers' /opt/wizarr/start.sh; then sed -i 's/--workers 4//' /opt/wizarr/start.sh fi if ! grep -qE 'FLASK|WORKERS|VERSION' /opt/wizarr/.env; then { echo "FLASK_ENV=production" echo "GUNICORN_WORKERS=4" echo "APP_VERSION=$(sed 's/^20/v&/' ~/.wizarr)" } >>/opt/wizarr/.env else sed -i "s/_VERSION=v.*$/_VERSION=v$(cat ~/.wizarr)/" /opt/wizarr/.env fi rm -rf "$BACKUP_FILE" export FLASK_SKIP_SCHEDULER=true $STD /usr/local/bin/uv run --frozen flask db upgrade msg_ok "Updated Wizarr" msg_info "Starting Service" systemctl start wizarr msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:5690${CL}" ================================================ FILE: ct/wordpress.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (Canbiz) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://wordpress.org/ ## App Default Values APP="Wordpress" var_tags="${var_tags:-blog;cms}" var_disk="${var_disk:-5}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_os="${var_os:-debian}" var_version="${var_version:-13}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /var/www/html/wordpress ]]; then msg_error "No ${APP} Installation Found!" exit fi setup_mariadb msg_error "Wordpress should be updated via the user interface." exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN} ${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}/${CL}" ================================================ FILE: ct/writefreely.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: StellaeAlis # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/writefreely/writefreely APP="WriteFreely" var_tags="${var_tags:-writing}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-1024}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/writefreely ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "writefreely" "writefreely/writefreely"; then msg_info "Stopping Services" systemctl stop writefreely msg_ok "Stopped Services" msg_info "Creating Backup" mkdir -p /tmp/writefreely_backup cp /opt/writefreely/keys /tmp/writefreely_backup/ 2>/dev/null cp /opt/writefreely/config.ini /tmp/writefreely_backup/ 2>/dev/null msg_ok "Created Backup" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "writefreely" "writefreely/writefreely" "prebuild" "latest" "/opt/writefreely" "writefreely_*_linux_arm64.tar.gz" msg_info "Restoring Data" cp /tmp/writefreely_backup/config.ini /opt/writefreely/ 2>/dev/null cp /tmp/writefreely_backup/keys/* /opt/writefreely/keys/ 2>/dev/null rm -rf /tmp/writefreely_backup msg_ok "Restored Data" msg_info "Running Post-Update Tasks" cd /opt/writefreely $STD ./writefreely db migrate ln -s /opt/writefreely/writefreely /usr/local/bin/writefreely msg_ok "Ran Post-Update Tasks" msg_info "Starting Services" systemctl start writefreely msg_ok "Started Services" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}${CL}" ================================================ FILE: ct/yamtrack.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/FuzzyGrim/Yamtrack APP="Yamtrack" var_tags="${var_tags:-media;tracker;movies;anime}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-8}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/yamtrack ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "yamtrack" "FuzzyGrim/Yamtrack"; then msg_info "Stopping Services" systemctl stop yamtrack yamtrack-celery msg_ok "Stopped Services" msg_info "Backing up Data" cp /opt/yamtrack/src/.env /opt/yamtrack_env.bak msg_ok "Backed up Data" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "yamtrack" "FuzzyGrim/Yamtrack" "tarball" msg_info "Installing Python Dependencies" cd /opt/yamtrack $STD uv venv --clear .venv $STD uv pip install --no-cache-dir -r requirements.txt msg_ok "Installed Python Dependencies" msg_info "Restoring Data" cp /opt/yamtrack_env.bak /opt/yamtrack/src/.env rm -f /opt/yamtrack_env.bak msg_ok "Restored Data" msg_info "Updating Yamtrack" cd /opt/yamtrack/src $STD /opt/yamtrack/.venv/bin/python manage.py migrate $STD /opt/yamtrack/.venv/bin/python manage.py collectstatic --noinput msg_ok "Updated Yamtrack" msg_info "Updating Nginx Configuration" cp /opt/yamtrack/nginx.conf /etc/nginx/nginx.conf sed -i 's|user abc;|user www-data;|' /etc/nginx/nginx.conf sed -i 's|pid /tmp/nginx.pid;|pid /run/nginx.pid;|' /etc/nginx/nginx.conf sed -i 's|/yamtrack/staticfiles/|/opt/yamtrack/src/staticfiles/|' /etc/nginx/nginx.conf sed -i 's|error_log /dev/stderr|error_log /var/log/nginx/error.log|' /etc/nginx/nginx.conf sed -i 's|access_log /dev/stdout|access_log /var/log/nginx/access.log|' /etc/nginx/nginx.conf $STD systemctl reload nginx msg_ok "Updated Nginx Configuration" msg_info "Starting Services" systemctl start yamtrack yamtrack-celery msg_ok "Started Services" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed Successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8000${CL}" ================================================ FILE: ct/yourls.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://yourls.org/ APP="YOURLS" var_tags="${var_tags:-url-shortener;php}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-512}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -f /opt/yourls/yourls-loader.php ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "yourls" "YOURLS/YOURLS"; then msg_info "Stopping Service" systemctl stop nginx msg_ok "Stopped Service" msg_info "Backing up Configuration" cp -r /opt/yourls/user /opt/yourls_user.bak msg_ok "Backed up Configuration" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "yourls" "YOURLS/YOURLS" "tarball" chown -R www-data:www-data /opt/yourls msg_info "Restoring Configuration" cp -r /opt/yourls_user.bak/. /opt/yourls/user/ rm -rf /opt/yourls_user.bak msg_ok "Restored Configuration" msg_info "Starting Service" systemctl start nginx msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed Successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} First, complete the database setup at:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}/admin/install.php${CL}" echo -e "${INFO}${YW} Admin credentials are in the install log:${CL}" echo -e "${TAB}${GATEWAY}${BGN}grep -A2 'admin' /opt/yourls/user/config.php${CL}" ================================================ FILE: ct/yt-dlp-webui.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: CrazyWolf13 # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/marcopiovanello/yt-dlp-web-ui APP="yt-dlp-webui" var_tags="${var_tags:-downloads;yt-dlp}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-1024}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -f /usr/local/bin/yt-dlp-webui ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "yt-dlp-webui" "marcopiovanello/yt-dlp-web-ui"; then msg_info "Stopping Service" systemctl stop yt-dlp-webui msg_ok "Stopped Service" msg_info "Updating yt-dlp" $STD yt-dlp -U msg_ok "Updated yt-dlp" rm -rf /usr/local/bin/yt-dlp-webui fetch_and_deploy_gh_release "yt-dlp-webui" "marcopiovanello/yt-dlp-web-ui" "singlefile" "latest" "/usr/local/bin" "yt-dlp-webui_linux-arm64" msg_info "Starting Service" systemctl start yt-dlp-webui msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:3033${CL}" ================================================ FILE: ct/yubal.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Crazywolf13 # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/guillevc/yubal APP="Yubal" var_tags="${var_tags:-music;media}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-15}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/yubal ]]; then msg_error "No ${APP} Installation Found!" exit fi ensure_dependencies git if check_for_gh_release "yubal" "guillevc/yubal"; then msg_info "Stopping Services" systemctl stop yubal msg_ok "Stopped Services" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "yubal" "guillevc/yubal" "tarball" "latest" "/opt/yubal" msg_info "Building Frontend" cd /opt/yubal/web $STD bun install --frozen-lockfile VERSION=$(get_latest_github_release "guillevc/yubal") VITE_VERSION=$VERSION VITE_COMMIT_SHA=$VERSION VITE_IS_RELEASE=true $STD bun run build msg_ok "Built Frontend" msg_info "Installing Python Dependencies" cd /opt/yubal $STD uv sync --package yubal-api --no-dev --frozen msg_ok "Installed Python Dependencies" msg_info "Starting Services" systemctl start yubal msg_ok "Started Services" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8000${CL}" ================================================ FILE: ct/yunohost.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://yunohost.org/ APP="YunoHost" var_tags="${var_tags:-os}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-20}" var_os="${var_os:-debian}" var_version="${var_version:-12}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -f /etc/apt/trusted.gpg.d/php.gpg ]]; then msg_error "No ${APP} Installation Found!" exit fi msg_info "Updating OS" $STD apt-get update $STD apt-get -y upgrade msg_ok "Updated OS" msg_info "Updating $APP LXC" $STD yunohost tools update $STD yunohost tools upgrade system $STD yunohost tools upgrade apps msg_ok "Updated $APP LXC" msg_ok "Updated successfully!" exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}${CL}" ================================================ FILE: ct/zabbix.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.zabbix.com/ APP="Zabbix" var_tags="${var_tags:-monitoring}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-4096}" var_disk="${var_disk:-6}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -f /etc/zabbix/zabbix_server.conf ]]; then msg_error "No ${APP} Installation Found!" exit fi . /etc/os-release if [ "$VERSION_CODENAME" != "trixie" ]; then msg_error "Unsupported Debian version: $VERSION_CODENAME – please upgrade to Debian 13 (Trixie) before updating Zabbix." exit fi if systemctl cat zabbix-agent2.service &>/dev/null; then AGENT_SERVICE="zabbix-agent2" elif systemctl cat zabbix-agent.service &>/dev/null; then AGENT_SERVICE="zabbix-agent" else AGENT_SERVICE="" msg_warn "No Zabbix Agent service found, skipping agent actions" fi msg_info "Stopping Services" systemctl stop zabbix-server [[ -n "$AGENT_SERVICE" ]] && systemctl stop "$AGENT_SERVICE" msg_ok "Stopped Services" read -rp "Choose Zabbix version [1] 7.0 LTS [2] 7.4 (Latest Stable) [3] Latest available (default: 2): " ZABBIX_CHOICE ZABBIX_CHOICE=${ZABBIX_CHOICE:-2} case "$ZABBIX_CHOICE" in 1) ZABBIX_VERSION="7.0" ;; 2) ZABBIX_VERSION="7.4" ;; 3) ZABBIX_VERSION=$(curl -fsSL https://repo.zabbix.com/zabbix/ | grep -oP '(?<=href=")[0-9]+\.[0-9]+(?=/")' | sort -V | tail -n1) ;; *) ZABBIX_VERSION="7.4" echo "Invalid choice. Defaulting to 7.4." ;; esac msg_info "Updating Zabbix to $ZABBIX_VERSION" mkdir -p /opt/zabbix-backup/ cp /etc/zabbix/zabbix_server.conf /opt/zabbix-backup/ cp /etc/apache2/conf-enabled/zabbix.conf /opt/zabbix-backup/ cp -R /usr/share/zabbix/ /opt/zabbix-backup/ rm -Rf /etc/apt/sources.list.d/zabbix.list cd /tmp if [[ "$ZABBIX_VERSION" == "7.0" ]]; then ZABBIX_DEB_URL="https://repo.zabbix.com/zabbix/${ZABBIX_VERSION}/debian/pool/main/z/zabbix-release/zabbix-release_latest_${ZABBIX_VERSION}+debian13_all.deb" ZABBIX_DEB_FILE="zabbix-release_latest_${ZABBIX_VERSION}+debian13_all.deb" else ZABBIX_DEB_URL="https://repo.zabbix.com/zabbix/${ZABBIX_VERSION}/release/debian/pool/main/z/zabbix-release/zabbix-release_latest+debian13_all.deb" ZABBIX_DEB_FILE="zabbix-release_latest+debian13_all.deb" fi curl -fsSL "$ZABBIX_DEB_URL" -o /tmp/"$ZABBIX_DEB_FILE" $STD dpkg -i /tmp/"$ZABBIX_DEB_FILE" rm -rf /tmp/zabbix-release_*.deb $STD apt update $STD apt install --only-upgrade zabbix-server-pgsql zabbix-frontend-php php8.4-pgsql if [[ "$AGENT_SERVICE" == "zabbix-agent2" ]]; then $STD apt install --only-upgrade zabbix-agent2 zabbix-agent2-plugin-postgresql if [ -f /etc/zabbix/zabbix_agent2.d/plugins.d/nvidia.conf ]; then sed -i 's|^Plugins.NVIDIA.System.Path=.*|# Plugins.NVIDIA.System.Path=/usr/libexec/zabbix/zabbix-agent2-plugin-nvidia-gpu|' \ /etc/zabbix/zabbix_agent2.d/plugins.d/nvidia.conf fi elif [[ "$AGENT_SERVICE" == "zabbix-agent" ]]; then $STD apt install --only-upgrade zabbix-agent fi if command -v fping >/dev/null 2>&1; then FPING_PATH=$(command -v fping) sed -i "s|^#\?FpingLocation=.*|FpingLocation=$FPING_PATH|" /etc/zabbix/zabbix_server.conf fi if command -v fping6 >/dev/null 2>&1; then FPING6_PATH=$(command -v fping6) sed -i "s|^#\?Fping6Location=.*|Fping6Location=$FPING6_PATH|" /etc/zabbix/zabbix_server.conf fi msg_ok "Updated Zabbix" msg_info "Starting Services" systemctl start zabbix-server [[ -n "$AGENT_SERVICE" ]] && systemctl start "$AGENT_SERVICE" systemctl restart apache2 msg_ok "Started Services" msg_ok "Updated successfully!" exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}/zabbix${CL}" ================================================ FILE: ct/zammad.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Michel Roegl-Brunner (michelroegl-brunner) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://zammad.com APP="Zammad" var_tags="${var_tags:-webserver;ticket-system}" var_disk="${var_disk:-8}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-4096}" var_os="${var_os:-debian}" var_version="${var_version:-12}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/zammad ]]; then msg_error "No ${APP} Installation Found!" exit fi msg_info "Stopping Service" systemctl stop zammad msg_ok "Stopped Service" msg_info "Updating Zammad" $STD apt update $STD apt-mark hold zammad $STD apt upgrade -y $STD apt-mark unhold zammad $STD apt upgrade -y msg_ok "Updated Zammad" msg_info "Starting Service" systemctl start zammad msg_ok "Started Service" msg_ok "Updated successfully!" exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}${CL}" ================================================ FILE: ct/zerobyte.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: community-scripts # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/nicotsx/zerobyte APP="Zerobyte" var_tags="${var_tags:-backup;encryption;restic}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-6144}" var_disk="${var_disk:-10}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/zerobyte ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "zerobyte" "nicotsx/zerobyte"; then msg_info "Stopping Service" systemctl stop zerobyte msg_ok "Stopped Service" msg_info "Backing up Configuration" cp /opt/zerobyte/.env /opt/zerobyte.env.bak msg_ok "Backed up Configuration" ensure_dependencies git NODE_VERSION="24" setup_nodejs CLEAN_INSTALL=1 fetch_and_deploy_gh_release "zerobyte" "nicotsx/zerobyte" "tarball" msg_info "Building Zerobyte" export NODE_OPTIONS="--max-old-space-size=3072" cd /opt/zerobyte $STD bun install $STD node ./node_modules/vite/bin/vite.js build msg_ok "Built Zerobyte" msg_info "Restoring Configuration" cp /opt/zerobyte.env.bak /opt/zerobyte/.env rm -f /opt/zerobyte.env.bak msg_ok "Restored Configuration" msg_info "Starting Service" systemctl start zerobyte msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed Successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:4096${CL}" ================================================ FILE: ct/zerotier-one.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: tremor021 # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.zerotier.com/ APP="Zerotier-One" var_tags="${var_tags:-networking}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-512}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -f /usr/sbin/zerotier-one ]]; then msg_error "No ${APP} Installation Found!" exit fi msg_info "Stopping Service" systemctl stop zerotier-one msg_ok "Stopping Service" msg_info "Updating Zerotier-One" $STD apt update $STD apt upgrade -y msg_ok "Updated Zerotier-One" msg_info "Starting Service" systemctl start zerotier-one msg_ok "Started Service" msg_ok "Updated successfully!" exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following IP:${CL}" echo -e "${TAB}${GATEWAY}${BGN}https://${IP}:3443${CL}" ================================================ FILE: ct/zigbee2mqtt.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.zigbee2mqtt.io/ APP="Zigbee2MQTT" var_tags="${var_tags:-smarthome;zigbee;mqtt}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-1024}" var_disk="${var_disk:-5}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-0}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/zigbee2mqtt ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "Zigbee2MQTT" "Koenkk/zigbee2mqtt"; then NODE_VERSION="24" NODE_MODULE="pnpm@$(curl -fsSL https://raw.githubusercontent.com/Koenkk/zigbee2mqtt/master/package.json | jq -r '.packageManager | split("@")[1]')" setup_nodejs msg_info "Stopping Service" systemctl stop zigbee2mqtt msg_ok "Stopped Service" msg_info "Creating Backup" ensure_dependencies zstd mkdir -p /opt/{backups,z2m_backup} BACKUP_VERSION="$(<"$HOME/.zigbee2mqtt")" BACKUP_FILE="/opt/backups/${APP}_backup_${BACKUP_VERSION}.tar.zst" $STD tar -cf - -C /opt zigbee2mqtt | zstd -q -o "$BACKUP_FILE" ls -t /opt/backups/${APP}_backup_*.tar.zst 2>/dev/null | tail -n +6 | xargs -r rm -f mv /opt/zigbee2mqtt/data /opt/z2m_backup/data msg_ok "Backup Created (${BACKUP_VERSION})" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "Zigbee2MQTT" "Koenkk/zigbee2mqtt" "tarball" "latest" "/opt/zigbee2mqtt" msg_info "Updating Zigbee2MQTT" rm -rf /opt/zigbee2mqtt/data mv /opt/z2m_backup/data /opt/zigbee2mqtt cd /opt/zigbee2mqtt grep -q "^packageImportMethod" ./pnpm-workspace.yaml 2>/dev/null || echo "packageImportMethod: hardlink" >>./pnpm-workspace.yaml $STD pnpm install --frozen-lockfile $STD pnpm build rm -rf /opt/z2m_backup msg_ok "Updated Zigbee2MQTT" msg_info "Starting Service" systemctl start zigbee2mqtt msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:9442${CL}" ================================================ FILE: ct/zipline.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: MickLesk (Canbiz) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://zipline.diced.sh/ APP="Zipline" var_tags="${var_tags:-file;sharing}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-5}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/zipline ]]; then msg_error "No ${APP} Installation Found!" exit fi NODE_VERSION="22" NODE_MODULE="pnpm" setup_nodejs if check_for_gh_release "zipline" "diced/zipline"; then msg_info "Stopping Service" systemctl stop zipline msg_ok "Service Stopped" mkdir -p /opt/zipline-uploads if [ -d /opt/zipline/uploads ] && [ "$(ls -A /opt/zipline/uploads)" ]; then cp -R /opt/zipline/uploads/* /opt/zipline-uploads/ fi cp /opt/zipline/.env /opt/ rm -R /opt/zipline fetch_and_deploy_gh_release "zipline" "diced/zipline" "tarball" msg_info "Updating ${APP}" cd /opt/zipline mv /opt/.env /opt/zipline/.env $STD pnpm install $STD pnpm build msg_ok "Updated ${APP}" msg_info "Starting Service" systemctl start zipline msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:3000${CL}" ================================================ FILE: ct/zitadel.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: dave-yap (dave-yap) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://zitadel.com/ APP="Zitadel" var_tags="${var_tags:-identity-provider}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-1024}" var_disk="${var_disk:-8}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -f /etc/systemd/system/zitadel.service ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "zitadel" "zitadel/zitadel"; then msg_info "Stopping Service" systemctl stop zitadel msg_ok "Stopped Service" rm -f /usr/local/bin/zitadel fetch_and_deploy_gh_release "zitadel" "zitadel/zitadel" "prebuild" "latest" "/usr/local/bin" "zitadel-linux-arm64.tar.gz" msg_info "Updating Zitadel" $STD zitadel setup --masterkeyFile /opt/zitadel/.masterkey --config /opt/zitadel/config.yaml --init-projections=true msg_ok "Updated Zitadel" msg_info "Starting Service" systemctl start zitadel msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8080/ui/console${CL}" ================================================ FILE: ct/zoraxy.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://zoraxy.aroz.org/ APP="Zoraxy" var_tags="${var_tags:-network}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-6}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/zoraxy/ ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "zoraxy" "tobychui/zoraxy"; then msg_info "Stopping service" systemctl stop zoraxy msg_ok "Service stopped" rm -rf /opt/zoraxy/zoraxy fetch_and_deploy_gh_release "zoraxy" "tobychui/zoraxy" "singlefile" "latest" "/opt/zoraxy" "zoraxy_linux_arm64" msg_info "Starting service" systemctl start zoraxy msg_ok "Service started" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8000${CL}" ================================================ FILE: ct/zot-registry.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://zotregistry.dev/ APP="Zot-Registry" var_tags="${var_tags:-registry;oci}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-4096}" var_disk="${var_disk:-5}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -f /usr/bin/zot ]]; then msg_error "No ${APP} installation found!" exit fi if check_for_gh_release "zot" "project-zot/zot"; then msg_info "Stopping Zot service" systemctl stop zot msg_ok "Stopped Zot service" rm -f /usr/bin/zot fetch_and_deploy_gh_release "zot" "project-zot/zot" "singlefile" "latest" "/usr/bin" "zot-linux-arm64" msg_info "Configuring Zot Registry" chown root:root /usr/bin/zot msg_ok "Configured Zot Registry" msg_info "Starting service" systemctl start zot msg_ok "Service started" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8080${CL}" ================================================ FILE: ct/zwave-js-ui.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://zwave-js.github.io/zwave-js-ui/#/ APP="Zwave-JS-UI" var_tags="${var_tags:-smarthome;zwave}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-1024}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-0}" header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/zwave-js-ui ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "zwave-js-ui" "zwave-js/zwave-js-ui"; then msg_info "Stopping Service" systemctl stop zwave-js-ui msg_ok "Stopped Service" rm -rf /opt/zwave-js-ui/* fetch_and_deploy_gh_release "zwave-js-ui" "zwave-js/zwave-js-ui" "prebuild" "latest" "/opt/zwave-js-ui" "zwave-js-ui*-linux.zip" msg_info "Starting Service" systemctl start zwave-js-ui msg_ok "Started Service" msg_info "Cleanup" rm -rf /opt/zwave-js-ui/store msg_ok "Cleaned" msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8091${CL}" ================================================ FILE: install/2fauth-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: jkrgr0 # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://docs.2fauth.app/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y nginx msg_ok "Installed Dependencies" export PHP_VERSION="8.4" PHP_FPM="YES" setup_php setup_composer setup_mariadb MARIADB_DB_NAME="2fauth_db" MARIADB_DB_USER="2fauth" setup_mariadb_db fetch_and_deploy_gh_release "2fauth" "Bubka/2FAuth" "tarball" msg_info "Setup 2FAuth" cd /opt/2fauth cp .env.example .env sed -i -e "s|^APP_URL=.*|APP_URL=http://$LOCAL_IP|" \ -e "s|^DB_CONNECTION=$|DB_CONNECTION=mysql|" \ -e "s|^DB_DATABASE=$|DB_DATABASE=$MARIADB_DB_NAME|" \ -e "s|^DB_HOST=$|DB_HOST=127.0.0.1|" \ -e "s|^DB_PORT=$|DB_PORT=3306|" \ -e "s|^DB_USERNAME=$|DB_USERNAME=$MARIADB_DB_USER|" \ -e "s|^DB_PASSWORD=$|DB_PASSWORD=$MARIADB_DB_PASS|" .env export COMPOSER_ALLOW_SUPERUSER=1 $STD composer update --no-plugins --no-scripts $STD composer install --no-dev --prefer-dist --no-plugins --no-scripts $STD php artisan key:generate --force $STD php artisan migrate:refresh $STD php artisan passport:install -q -n $STD php artisan storage:link $STD php artisan config:cache chown -R www-data: /opt/2fauth chmod -R 755 /opt/2fauth msg_ok "Setup 2fauth" msg_info "Configure Service" cat </etc/nginx/conf.d/2fauth.conf server { listen 80; root /opt/2fauth/public; server_name $LOCAL_IP; index index.php; charset utf-8; location / { try_files \$uri \$uri/ /index.php?\$query_string; } location = /favicon.ico { access_log off; log_not_found off; } location = /robots.txt { access_log off; log_not_found off; } error_page 404 /index.php; location ~ \.php\$ { fastcgi_pass unix:/var/run/php/php${PHP_VERSION}-fpm.sock; fastcgi_param SCRIPT_FILENAME \$realpath_root\$fastcgi_script_name; include fastcgi_params; } location ~ /\.(?!well-known).* { deny all; } } EOF systemctl reload nginx msg_ok "Configured Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/actualbudget-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://actualbudget.org/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ make \ g++ msg_ok "Installed Dependencies" NODE_VERSION="22" setup_nodejs create_self_signed_cert msg_info "Installing Actual Budget" cd /opt RELEASE=$(get_latest_github_release "actualbudget/actual") mkdir -p /opt/actualbudget-data/{server-files,upload,migrate,user-files,migrations,config} chown -R root:root /opt/actualbudget-data chmod -R 755 /opt/actualbudget-data cat </opt/actualbudget-data/config.json { "port": 5006, "hostname": "::", "serverFiles": "/opt/actualbudget-data/server-files", "userFiles": "/opt/actualbudget-data/user-files", "trustedProxies": [ "10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16", "127.0.0.0/8", "::1/128", "fc00::/7" ], "https": { "key": "/etc/ssl/actualbudget/actualbudget.key", "cert": "/etc/ssl/actualbudget/actualbudget.crt" } } EOF mkdir -p /opt/actualbudget cd /opt/actualbudget $STD npm install --location=global @actual-app/sync-server echo "${RELEASE}" >~/.actualbudget msg_ok "Installed Actual Budget" msg_info "Creating Service" cat </etc/systemd/system/actualbudget.service [Unit] Description=Actual Budget Service After=network.target [Service] Type=simple User=root Group=root WorkingDirectory=/opt/actualbudget Environment=ACTUAL_UPLOAD_FILE_SIZE_LIMIT_MB=20 Environment=ACTUAL_UPLOAD_SYNC_ENCRYPTED_FILE_SYNC_SIZE_LIMIT_MB=50 Environment=ACTUAL_UPLOAD_FILE_SYNC_SIZE_LIMIT_MB=20 ExecStart=/usr/bin/actual-server --config /opt/actualbudget-data/config.json Restart=always RestartSec=10 [Install] WantedBy=multi-user.target EOF systemctl enable -q --now actualbudget msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/adguard-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://adguard.com/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os fetch_and_deploy_gh_release "AdGuardHome" "AdguardTeam/AdGuardHome" "prebuild" "latest" "/opt/AdGuardHome" "AdGuardHome_linux_arm64.tar.gz" msg_info "Creating Service" cat </etc/systemd/system/AdGuardHome.service [Unit] Description=AdGuard Home: Network-level blocker ConditionFileIsExecutable=/opt/AdGuardHome/AdGuardHome After=syslog.target network-online.target [Service] StartLimitInterval=5 StartLimitBurst=10 ExecStart=/opt/AdGuardHome/AdGuardHome "-s" "run" WorkingDirectory=/opt/AdGuardHome StandardOutput=file:/var/log/AdGuardHome.out StandardError=file:/var/log/AdGuardHome.err Restart=always RestartSec=10 EnvironmentFile=-/etc/sysconfig/AdGuardHome [Install] WantedBy=multi-user.target EOF systemctl enable -q --now AdGuardHome msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/adventurelog-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/seanmorley15/AdventureLog source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ gdal-bin \ libgdal-dev \ git \ memcached \ libmemcached-tools msg_ok "Installed Dependencies" PYTHON_VERSION="3.13" setup_uv NODE_VERSION="22" NODE_MODULE="pnpm@latest" setup_nodejs PG_VERSION="17" PG_MODULES="postgis" setup_postgresql PG_DB_NAME="adventurelog_db" PG_DB_USER="adventurelog_user" PG_DB_EXTENSIONS="postgis" setup_postgresql_db fetch_and_deploy_gh_release "adventurelog" "seanmorley15/adventurelog" "tarball" msg_info "Installing AdventureLog (Patience)" SECRET_KEY="$(openssl rand -base64 32 | tr -dc 'a-zA-Z0-9' | cut -c1-32)" echo "AdventureLog Secret: $SECRET_KEY" >>~/adventurelog.creds DJANGO_ADMIN_USER="djangoadmin" DJANGO_ADMIN_PASS="$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | cut -c1-13)" cat </opt/adventurelog/backend/server/.env PGHOST='localhost' PGDATABASE='${PG_DB_NAME}' PGUSER='${PG_DB_USER}' PGPASSWORD='${PG_DB_PASS}' SECRET_KEY='${SECRET_KEY}' PUBLIC_URL='http://$LOCAL_IP:8000' DEBUG=True FRONTEND_URL='http://$LOCAL_IP:3000' CSRF_TRUSTED_ORIGINS='http://127.0.0.1:3000,http://localhost:3000,http://$LOCAL_IP:3000' DJANGO_ADMIN_USERNAME='${DJANGO_ADMIN_USER}' DJANGO_ADMIN_PASSWORD='${DJANGO_ADMIN_PASS}' DISABLE_REGISTRATION=False # EMAIL_BACKEND='email' # EMAIL_HOST='smtp.gmail.com' # EMAIL_USE_TLS=False # EMAIL_PORT=587 # EMAIL_USE_SSL=True # EMAIL_HOST_USER='user' # EMAIL_HOST_PASSWORD='password' # DEFAULT_FROM_EMAIL='user@example.com' EOF cd /opt/adventurelog/backend/server mkdir -p /opt/adventurelog/backend/server/media $STD uv venv --clear /opt/adventurelog/backend/server/.venv $STD /opt/adventurelog/backend/server/.venv/bin/python -m ensurepip --upgrade $STD /opt/adventurelog/backend/server/.venv/bin/python -m pip install --upgrade pip $STD /opt/adventurelog/backend/server/.venv/bin/python -m pip install -r requirements.txt $STD /opt/adventurelog/backend/server/.venv/bin/python -m pip install 'djangorestframework<3.15' $STD /opt/adventurelog/backend/server/.venv/bin/python -m manage collectstatic --noinput $STD /opt/adventurelog/backend/server/.venv/bin/python -m manage migrate $STD /opt/adventurelog/backend/server/.venv/bin/python -m manage download-countries cat </opt/adventurelog/frontend/.env PUBLIC_SERVER_URL=http://$LOCAL_IP:8000 BODY_SIZE_LIMIT=Infinity ORIGIN='http://$LOCAL_IP:3000' EOF cd /opt/adventurelog/frontend $STD pnpm i $STD pnpm build msg_ok "Installed AdventureLog" msg_info "Setting up Django Admin" cd /opt/adventurelog/backend/server $STD .venv/bin/python -m manage shell <>~/adventurelog.creds msg_ok "Setup Django Admin" msg_info "Creating Service" cat </etc/systemd/system/adventurelog-backend.service [Unit] Description=AdventureLog Backend Service After=network.target postgresql.service [Service] WorkingDirectory=/opt/adventurelog/backend/server ExecStart=/opt/adventurelog/backend/server/.venv/bin/python -m manage runserver 0.0.0.0:8000 Restart=always EnvironmentFile=/opt/adventurelog/backend/server/.env [Install] WantedBy=multi-user.target EOF cat </etc/systemd/system/adventurelog-frontend.service [Unit] Description=AdventureLog SvelteKit Frontend Service After=network.target [Service] WorkingDirectory=/opt/adventurelog/frontend ExecStart=/usr/bin/node build 127.0.0.1:3000 Restart=always EnvironmentFile=/opt/adventurelog/frontend/.env [Install] WantedBy=multi-user.target EOF systemctl enable -q --now adventurelog-backend systemctl enable -q --now adventurelog-frontend msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/agentdvr-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.ispyconnect.com/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os setup_hwaccel msg_info "Installing Dependencies" $STD apt install -y \ apt-transport-https \ alsa-utils \ libxext-dev \ fontconfig \ libva-drm2 msg_ok "Installed Dependencies" msg_info "Installing AgentDVR" mkdir -p /opt/agentdvr/agent RELEASE=$(curl -fsSL "https://www.ispyconnect.com/api/Agent/DownloadLocation4?platform=LinuxARM64&fromVersion=0" | tr -d '\r' | grep -Eo 'https://[^"[:space:]]+\.zip' | head -n1) cd /opt/agentdvr/agent $STD curl -fsSL "$RELEASE" -o "$(basename "${RELEASE%%\?*}")" $STD unzip -o "$(basename "${RELEASE%%\?*}")" chmod +x ./Agent echo "$RELEASE" >~/.agentdvr rm -f "$(basename "${RELEASE%%\?*}")" msg_ok "Installed AgentDVR" msg_info "Creating Service" cat </etc/systemd/system/AgentDVR.service [Unit] Description=AgentDVR [Service] WorkingDirectory=/opt/agentdvr/agent ExecStart=/opt/agentdvr/agent/./Agent Environment="MALLOC_TRIM_THRESHOLD_=100000" SyslogIdentifier=AgentDVR Restart=always RestartSec=5 [Install] WantedBy=multi-user.target EOF systemctl enable -q --now AgentDVR msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/alpine-adguard-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://adguardhome.com/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Downloading AdGuard Home" $STD curl -fsSL -o /tmp/AdGuardHome_linux_arm64.tar.gz \ "https://github.com/AdguardTeam/AdGuardHome/releases/latest/download/AdGuardHome_linux_arm64.tar.gz" msg_ok "Downloaded AdGuard Home" msg_info "Installing AdGuard Home" $STD tar -xzf /tmp/AdGuardHome_linux_arm64.tar.gz -C /opt $STD rm /tmp/AdGuardHome_linux_arm64.tar.gz msg_ok "Installed AdGuard Home" msg_info "Creating AdGuard Home Service" cat </etc/init.d/adguardhome #!/sbin/openrc-run name="AdGuardHome" description="AdGuard Home Service" command="/opt/AdGuardHome/AdGuardHome" command_background="yes" pidfile="/run/adguardhome.pid" EOF $STD chmod +x /etc/init.d/adguardhome msg_ok "Created AdGuard Home Service" msg_info "Enabling AdGuard Home Service" $STD rc-update add adguardhome default msg_ok "Enabled AdGuard Home Service" msg_info "Starting AdGuard Home" $STD rc-service adguardhome start msg_ok "Started AdGuard Home" motd_ssh customize ================================================ FILE: install/alpine-bitmagnet-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: Slaviša Arežina (tremor021) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/bitmagnet-io/bitmagnet source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing dependencies" $STD apk add --no-cache \ gcc \ musl-dev \ git \ iproute2-ss \ sudo $STD apk add --no-cache --repository=https://dl-cdn.alpinelinux.org/alpine/edge/community go msg_ok "Installed dependencies" msg_info "Installing PostgreSQL" $STD apk add --no-cache \ postgresql16 \ postgresql16-contrib \ postgresql16-openrc $STD rc-update add postgresql $STD rc-service postgresql start msg_ok "Installed PostreSQL" RELEASE=$(curl -fsSL https://api.github.com/repos/bitmagnet-io/bitmagnet/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4) }') msg_info "Installing bitmagnet v${RELEASE}" mkdir -p /opt/bitmagnet temp_file=$(mktemp) curl -fsSL "https://github.com/bitmagnet-io/bitmagnet/archive/refs/tags/v${RELEASE}.tar.gz" -o "$temp_file" tar zxf "$temp_file" --strip-components=1 -C /opt/bitmagnet cd /opt/bitmagnet VREL=v$RELEASE $STD go build -ldflags "-s -w -X github.com/bitmagnet-io/bitmagnet/internal/version.GitTag=$VREL" chmod +x bitmagnet $STD su - postgres -c "psql -c 'CREATE DATABASE bitmagnet;'" echo "${RELEASE}" >/opt/bitmagnet_version.txt msg_ok "Installed bitmagnet v${RELEASE}" read -rp "${TAB3}Enter your TMDB API key if you have one: " tmdbapikey msg_info "Enabling bitmagnet Service" cat </etc/init.d/bitmagnet #!/sbin/openrc-run description="bitmagnet Service" directory="/opt/bitmagnet" command="/opt/bitmagnet/bitmagnet" command_args="worker run --all" command_background="true" command_user="root" pidfile="/var/run/bitmagnet.pid" depend() { use net } start_pre() { export TMDB_API_KEY="$tmdbapikey" } EOF chmod +x /etc/init.d/bitmagnet $STD rc-update add bitmagnet default msg_ok "Enabled bitmagnet Service" msg_info "Starting bitmagnet" $STD service bitmagnet start msg_ok "Started bitmagnet" motd_ssh customize msg_info "Cleaning up" rm -f "$temp_file" $STD apk cache clean msg_ok "Cleaned" ================================================ FILE: install/alpine-borgbackup-server-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: Sander Koenders (sanderkoenders) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://www.borgbackup.org/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing BorgBackup" $STD apk add --no-cache borgbackup openssh $STD rc-update add sshd $STD rc-service sshd start msg_ok "Installed BorgBackup" msg_info "Creating backup user" $STD adduser -D -s /bin/bash -h /home/backup backup $STD passwd -d backup msg_ok "Created backup user" msg_info "Configure SSH, disabling password authentication and enabling public key authentication" $STD sed -i -e 's/^#\?PasswordAuthentication.*/PasswordAuthentication no/' /etc/ssh/sshd_config $STD rc-service sshd restart msg_ok "Configured SSH" motd_ssh customize cleanup_lxc ================================================ FILE: install/alpine-caddy-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: cobalt (cobaltgit) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://caddyserver.com/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Caddy" $STD apk add --no-cache caddy caddy-openrc cat </etc/caddy/Caddyfile :80 { # Set this path to your site's directory. root * /var/www/html # Enable the static file server. file_server # Another common task is to set up a reverse proxy: # reverse_proxy localhost:8080 # Or serve a PHP site through php-fpm: # php_fastcgi localhost:9000 } EOF mkdir -p /var/www/html cat </var/www/html/index.html Caddy works!

Hello Caddy!

For more information, refer to the Caddy documentation

EOF msg_ok "Installed Caddy" read -r -p "${TAB3}Would you like to install xCaddy Addon? " prompt if [[ "${prompt,,}" =~ ^(y|yes)$ ]]; then GO_VERSION="$(curl -fsSL https://go.dev/VERSION?m=text | head -1 | cut -c3-)" setup_go msg_info "Setup xCaddy" cd /opt RELEASE=$(curl -fsSL https://api.github.com/repos/caddyserver/xcaddy/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }') curl -fsSL "https://github.com/caddyserver/xcaddy/releases/download/${RELEASE}/xcaddy_${RELEASE:1}_linux_arm64.tar.gz" -o "xcaddy_${RELEASE:1}_linux_arm64.tar.gz" $STD tar xzf xcaddy_"${RELEASE:1}"_linux_arm64.tar.gz -C /usr/local/bin xcaddy rm -rf /opt/xcaddy* $STD xcaddy build msg_ok "Setup xCaddy" fi msg_info "Enabling Caddy Service" $STD rc-update add caddy default msg_ok "Enabled Caddy Service" msg_info "Starting Caddy" $STD service caddy start msg_ok "Started Caddy" motd_ssh customize ================================================ FILE: install/alpine-docker-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.docker.com/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apk add tzdata openssl msg_ok "Installed Dependencies" msg_info "Installing Docker" $STD apk add docker $STD rc-service docker start $STD rc-update add docker default msg_ok "Installed Docker" get_latest_release() { curl -fsSL https://api.github.com/repos/"$1"/releases/latest | grep '"tag_name":' | cut -d'"' -f4 } PORTAINER_LATEST_VERSION=$(get_latest_release "portainer/portainer") DOCKER_COMPOSE_LATEST_VERSION=$(get_latest_release "docker/compose") PORTAINER_AGENT_LATEST_VERSION=$(get_latest_release "portainer/agent") read -r -p "${TAB3}Would you like to add Portainer? " prompt if [[ "${prompt,,}" =~ ^(y|yes)$ ]]; then msg_info "Installing Portainer $PORTAINER_LATEST_VERSION" docker volume create portainer_data >/dev/null $STD docker run -d \ -p 8000:8000 \ -p 9443:9443 \ --name=portainer \ --restart=always \ -v /var/run/docker.sock:/var/run/docker.sock \ -v portainer_data:/data \ portainer/portainer-ce:latest msg_ok "Installed Portainer $PORTAINER_LATEST_VERSION" else read -r -p "${TAB3}Would you like to add the Portainer Agent? " prompt if [[ "${prompt,,}" =~ ^(y|yes)$ ]]; then msg_info "Installing Portainer agent $PORTAINER_AGENT_LATEST_VERSION" $STD docker run -d \ -p 9001:9001 \ --name portainer_agent \ --restart=always \ -v /var/run/docker.sock:/var/run/docker.sock \ -v /var/lib/docker/volumes:/var/lib/docker/volumes \ portainer/agent msg_ok "Installed Portainer Agent $PORTAINER_AGENT_LATEST_VERSION" fi fi read -r -p "${TAB3}Would you like to add Docker Compose? " prompt if [[ "${prompt,,}" =~ ^(y|yes)$ ]]; then msg_info "Installing Docker Compose $DOCKER_COMPOSE_LATEST_VERSION" DOCKER_CONFIG=${DOCKER_CONFIG:-$HOME/.docker} mkdir -p "$DOCKER_CONFIG"/cli-plugins curl -fsSL https://github.com/docker/compose/releases/download/"$DOCKER_COMPOSE_LATEST_VERSION"/docker-compose-linux-aarch64 -o ~/.docker/cli-plugins/docker-compose chmod +x "$DOCKER_CONFIG"/cli-plugins/docker-compose msg_ok "Installed Docker Compose $DOCKER_COMPOSE_LATEST_VERSION" fi read -r -p "${TAB3}Would you like to expose the Docker TCP socket? " prompt if [[ "${prompt,,}" =~ ^(y|yes)$ ]]; then msg_info "Exposing Docker TCP socket" $STD mkdir -p /etc/docker $STD echo '{ "hosts": ["unix:///var/run/docker.sock", "tcp://0.0.0.0:2375"] }' > /etc/docker/daemon.json $STD rc-service docker restart msg_ok "Exposed Docker TCP socket at tcp://+:2375" fi motd_ssh customize ================================================ FILE: install/alpine-forgejo-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: Johann3s-H (An!ma) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://forgejo.org/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Forgejo" $STD apk add --no-cache forgejo msg_ok "Installed Forgejo" msg_info "Enabling Forgejo Service" $STD rc-update add forgejo default msg_ok "Enabled Forgejo Service" msg_info "Starting Forgejo" $STD service forgejo start msg_ok "Started Forgejo" motd_ssh customize ================================================ FILE: install/alpine-garage-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://garagehq.deuxfleurs.fr/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apk add --no-cache openssl msg_ok "Installed Dependencies" GITEA_RELEASE=$(curl -s https://api.github.com/repos/deuxfleurs-org/garage/tags | jq -r '.[0].name') curl -fsSL "https://garagehq.deuxfleurs.fr/_releases/${GITEA_RELEASE}/aarch64-unknown-linux-musl/garage" -o /usr/local/bin/garage chmod +x /usr/local/bin/garage mkdir -p /var/lib/garage/{data,meta,snapshots} mkdir -p /etc/garage RPC_SECRET=$(openssl rand -hex 64 | cut -c1-64) ADMIN_TOKEN=$(openssl rand -base64 32) METRICS_TOKEN=$(openssl rand -base64 32) { echo "Garage Tokens and Secrets" echo "RPC Secret: $RPC_SECRET" echo "Admin Token: $ADMIN_TOKEN" echo "Metrics Token: $METRICS_TOKEN" } >~/garage.creds echo $GITEA_RELEASE >>~/.garage cat </etc/garage.toml metadata_dir = "/var/lib/garage/meta" data_dir = "/var/lib/garage/data" db_engine = "sqlite" replication_factor = 1 rpc_bind_addr = "0.0.0.0:3901" rpc_public_addr = "127.0.0.1:3901" rpc_secret = "${RPC_SECRET}" [s3_api] s3_region = "garage" api_bind_addr = "0.0.0.0:3900" root_domain = ".s3.garage" [s3_web] bind_addr = "0.0.0.0:3902" root_domain = ".web.garage" index = "index.html" [k2v_api] api_bind_addr = "0.0.0.0:3904" [admin] api_bind_addr = "0.0.0.0:3903" admin_token = "${ADMIN_TOKEN}" metrics_token = "${METRICS_TOKEN}" EOF msg_ok "Configured Garage" msg_info "Creating Service" cat <<'EOF' >/etc/init.d/garage #!/sbin/openrc-run name="Garage Object Storage" command="/usr/local/bin/garage" command_args="server" command_background="yes" pidfile="/run/garage.pid" depend() { need net } EOF chmod +x /etc/init.d/garage $STD rc-update add garage default $STD rc-service garage restart || rc-service garage start msg_ok "Service active" motd_ssh customize ================================================ FILE: install/alpine-gatus-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: Slaviša Arežina (tremor021) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/TwiN/gatus source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing dependencies" $STD apk add --no-cache \ ca-certificates \ libcap-setcap $STD apk add --no-cache --repository=https://dl-cdn.alpinelinux.org/alpine/edge/community go msg_ok "Installed dependencies" RELEASE=$(curl -s https://api.github.com/repos/TwiN/gatus/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4) }') msg_info "Installing gatus v${RELEASE}" temp_file=$(mktemp) mkdir -p /opt/gatus curl -fsSL "https://github.com/TwiN/gatus/archive/refs/tags/v${RELEASE}.tar.gz" -o "$temp_file" tar zxf "$temp_file" --strip-components=1 -C /opt/gatus cd /opt/gatus $STD go mod tidy CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o gatus . setcap CAP_NET_RAW+ep gatus mv config.yaml config echo "${RELEASE}" >/opt/gatus_version.txt msg_ok "Installed gatus v${RELEASE}" msg_info "Enabling gatus Service" cat </etc/init.d/gatus #!/sbin/openrc-run description="gatus Service" directory="/opt/gatus" command="/opt/gatus/gatus" command_args="" command_background="true" command_user="root" pidfile="/var/run/gatus.pid" export GATUS_CONFIG_PATH="" export GATUS_LOG_LEVEL="INFO" export PORT="8080" depend() { use net } EOF chmod +x /etc/init.d/gatus $STD rc-update add gatus default msg_ok "Enabled gatus Service" msg_info "Starting gatus" $STD service gatus start msg_ok "Started gatus" motd_ssh customize msg_info "Cleaning up" rm -f "$temp_file" $STD apk cache clean msg_ok "Cleaned" ================================================ FILE: install/alpine-gitea-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://gitea.io/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Gitea" $STD apk add --no-cache gitea msg_ok "Installed Gitea" msg_info "Enabling Gitea Service" $STD rc-update add gitea default msg_ok "Enabled Gitea Service" msg_info "Starting Gitea" $STD service gitea start msg_ok "Started Gitea" motd_ssh customize ================================================ FILE: install/alpine-grafana-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://grafana.com/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Grafana" $STD apk add grafana $STD sed -i '/http_addr/s/127.0.0.1/0.0.0.0/g' /etc/conf.d/grafana $STD rc-service grafana start $STD rc-update add grafana default msg_ok "Installed Grafana" motd_ssh customize ================================================ FILE: install/alpine-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://alpinelinux.org/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apk add sudo msg_ok "Installed Dependencies" motd_ssh customize ================================================ FILE: install/alpine-ironclaw-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/nearai/ironclaw source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apk add openssl dbus gnome-keyring msg_ok "Installed Dependencies" msg_info "Installing PostgreSQL" $STD apk add postgresql17 postgresql17-openrc postgresql-pgvector postgresql-common $STD rc-service postgresql setup $STD rc-update add postgresql default $STD rc-service postgresql start msg_ok "Installed PostgreSQL" msg_info "Setting up Database" PG_PASS=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | cut -c1-13) $STD su -s /bin/sh postgres -c "psql -c \"CREATE ROLE ironclaw WITH LOGIN PASSWORD '${PG_PASS}';\"" $STD su -s /bin/sh postgres -c "psql -c \"CREATE DATABASE ironclaw WITH OWNER ironclaw;\"" $STD su -s /bin/sh postgres -c "psql -d ironclaw -c \"CREATE EXTENSION IF NOT EXISTS vector;\"" msg_ok "Set up Database" fetch_and_deploy_gh_release "ironclaw-bin" "nearai/ironclaw" "prebuild" "latest" "/usr/local/bin" \ "ironclaw-$(uname -m)-unknown-linux-musl.tar.gz" chmod +x /usr/local/bin/ironclaw msg_info "Configuring IronClaw" mkdir -p /root/.ironclaw GATEWAY_TOKEN=$(openssl rand -hex 32) cat </root/.ironclaw/.env DATABASE_URL=postgresql://ironclaw:${PG_PASS}@localhost:5432/ironclaw?sslmode=disable GATEWAY_ENABLED=true GATEWAY_HOST=0.0.0.0 GATEWAY_PORT=3000 GATEWAY_AUTH_TOKEN=${GATEWAY_TOKEN} CLI_ENABLED=false AGENT_NAME=ironclaw RUST_LOG=ironclaw=info,tower_http=info EOF chmod 600 /root/.ironclaw/.env msg_ok "Configured IronClaw" msg_info "Creating Service" cat </etc/init.d/ironclaw #!/sbin/openrc-run name="IronClaw" description="IronClaw AI Agent" command="/usr/bin/dbus-run-session" command_args="/usr/local/bin/ironclaw" command_background=true pidfile="/run/ironclaw.pid" directory="/root" supervise_daemon_args="--env-file /root/.ironclaw/.env" depend() { need net postgresql } EOF chmod +x /etc/init.d/ironclaw $STD rc-update add ironclaw default msg_ok "Created Service" motd_ssh customize ================================================ FILE: install/alpine-it-tools-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: nicedevil007 (NiceDevil) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://it-tools.tech/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apk add --no-cache \ nginx \ python3 msg_ok "Installed Dependencies" msg_info "Installing IT-Tools" RELEASE=$(curl -fsSL https://api.github.com/repos/sharevb/it-tools/releases/latest | grep '"tag_name":' | cut -d '"' -f4) curl -fsSL "https://github.com/sharevb/it-tools/releases/download/${RELEASE}/it-tools-${RELEASE#v}.zip" -o it-tools.zip mkdir -p /usr/share/nginx/html $STD unzip it-tools.zip -d /tmp/ mv /tmp/dist/* /usr/share/nginx/html cat <<'EOF' >/etc/nginx/http.d/default.conf server { listen 80; server_name localhost; root /usr/share/nginx/html; index index.html; location / { try_files $uri $uri/ /index.html; } } EOF $STD rc-update add nginx default $STD rc-service nginx start echo "${RELEASE}" >/opt/"${APPLICATION}"_version.txt msg_ok "Installed IT-Tools" motd_ssh customize msg_info "Cleaning up" rm -rf /tmp/dist rm -f it-tools.zip $STD apk cache clean msg_ok "Cleaned" ================================================ FILE: install/alpine-loki-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2025 community-scripts ORG # Author: hoholms # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/grafana/loki source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Loki" $STD apk add loki $STD sed -i '/http_addr/s/127.0.0.1/0.0.0.0/g' /etc/conf.d/loki mkdir -p /var/lib/loki/{chunks,boltdb-shipper-active,boltdb-shipper-cache} chown -R loki:grafana /var/lib/loki mkdir -p /var/log/loki chown -R loki:grafana /var/log/loki cat </etc/loki/loki-local-config.yaml auth_enabled: false server: http_listen_port: 3100 log_level: info common: instance_addr: 127.0.0.1 path_prefix: /var/lib/loki storage: filesystem: chunks_directory: /var/lib/loki/chunks rules_directory: /var/lib/loki/rules replication_factor: 1 ring: kvstore: store: inmemory schema_config: configs: - from: 2020-10-24 store: tsdb object_store: filesystem schema: v13 index: prefix: index_ period: 24h query_range: results_cache: cache: embedded_cache: enabled: true max_size_mb: 100 limits_config: metric_aggregation_enabled: true ruler: alertmanager_url: http://localhost:9093 EOF chown loki:grafana /etc/loki/loki-local-config.yaml chmod 644 /etc/loki/loki-local-config.yaml echo "output_log=\"\${output_log:-/var/log/loki/output.log}\"" >> /etc/init.d/loki echo "error_log=\"\${error_log:-/var/log/loki/error.log}\"" >> /etc/init.d/loki echo "start_stop_daemon_args=\"\${SSD_OPTS} -1 \${output_log} -2 \${error_log}\"" >> /etc/init.d/loki $STD rc-update add loki default $STD rc-service loki start msg_ok "Installed Loki" read -rp "Would you like to install Promtail? (y/N): " INSTALL_PROMTAIL if [[ "${INSTALL_PROMTAIL,,}" =~ ^(y|yes)$ ]]; then msg_info "Installing Promtail" $STD apk add loki-promtail $STD sed -i '/http_addr/s/127.0.0.1/0.0.0.0/g' /etc/conf.d/loki-promtail $STD rc-update add loki-promtail default $STD rc-service loki-promtail start msg_ok "Installed Promtail" fi motd_ssh customize cleanup_lxc ================================================ FILE: install/alpine-mariadb-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://mariadb.org/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing MariaDB" $STD apk add --no-cache mariadb mariadb-client $STD rc-update add mariadb default msg_ok "Installed MariaDB" msg_info "Configuring MariaDB" mysql_install_db --user=mysql --basedir=/usr --datadir=/var/lib/mysql >/dev/null 2>&1 $STD rc-service mariadb start msg_ok "MariaDB Configured" read -r -p "${TAB3}Would you like to install Adminer with lighttpd? : " prompt if [[ ${prompt,,} =~ ^(y|yes)$ ]]; then msg_info "Installing Adminer and dependencies" $STD apk add --no-cache \ lighttpd \ lighttpd-openrc \ php83 \ php83-cgi \ php83-common \ php83-curl \ php83-gd \ php83-mbstring \ php83-mysqli \ php83-mysqlnd \ php83-openssl \ php83-zip \ php83-session \ jq sed -i 's|# *include "mod_fastcgi.conf"|include "mod_fastcgi.conf"|' /etc/lighttpd/lighttpd.conf sed -i 's|/usr/bin/php-cgi|/usr/bin/php-cgi83|g' /etc/lighttpd/mod_fastcgi.conf mkdir -p /var/www/localhost/htdocs ADMINER_VERSION=$(curl -fsSL https://api.github.com/repos/vrana/adminer/releases/latest | jq -r '.tag_name' | sed 's/^v//') curl -fsSL "https://github.com/vrana/adminer/releases/download/v${ADMINER_VERSION}/adminer-${ADMINER_VERSION}.php" -o /var/www/localhost/htdocs/adminer.php chown lighttpd:lighttpd /var/www/localhost/htdocs/adminer.php chmod 755 /var/www/localhost/htdocs/adminer.php msg_ok "Adminer Installed" msg_info "Starting Lighttpd" $STD rc-update add lighttpd default $STD rc-service lighttpd restart msg_ok "Lighttpd Started" fi motd_ssh customize msg_info "Cleaning up" msg_ok "Cleaned" ================================================ FILE: install/alpine-nextcloud-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://nextcloud.com/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apk add openssl $STD apk add nginx msg_ok "Installed Dependencies" msg_info "Installing PHP/Redis" $STD apk add php83-opcache $STD apk add php83-redis $STD apk add php83-apcu $STD apk add php83-fpm $STD apk add php83-sysvsem $STD apk add php83-ftp $STD apk add php83-pecl-smbclient $STD apk add php83-pecl-imagick $STD apk add php83-pecl-vips $STD apk add php83-exif $STD apk add php83-sodium $STD apk add php83-bz2 $STD apk add redis msg_ok "Installed PHP/Redis" msg_info "Installing MySQL Database" DB_NAME=nextcloud DB_USER=nextcloud DB_PASS="$(openssl rand -base64 18 | cut -c1-13)" ADMIN_PASS="$(openssl rand -base64 18 | cut -c1-13)" echo "" >>~/nextcloud.creds echo -e "MySQL Admin Password: \e[32m$ADMIN_PASS\e[0m" >>~/nextcloud.creds echo -e "Nextcloud Database Username: \e[32m$DB_USER\e[0m" >>~/nextcloud.creds echo -e "Nextcloud Database Password: \e[32m$DB_PASS\e[0m" >>~/nextcloud.creds echo -e "Nextcloud Database Name: \e[32m$DB_NAME\e[0m" >>~/nextcloud.creds $STD apk add nextcloud-mysql mariadb mariadb-client $STD mariadb-install-db --user=mysql --datadir=/var/lib/mysql $STD service mariadb start $STD rc-update add mariadb $STD mariadb -uroot -p"$ADMIN_PASS" -e "GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' IDENTIFIED BY '$ADMIN_PASS' WITH GRANT OPTION; DELETE FROM mysql.user WHERE User=''; DELETE FROM mysql.user WHERE User='root' AND Host NOT IN ('localhost', '127.0.0.1', '::1'); DROP DATABASE test; DELETE FROM mysql.db WHERE Db='test' OR Db='test\_%'; CREATE DATABASE $DB_NAME; GRANT ALL ON $DB_NAME.* TO '$DB_USER'@'localhost' IDENTIFIED BY '$DB_PASS'; GRANT ALL ON $DB_NAME.* TO '$DB_USER'@'localhost.localdomain' IDENTIFIED BY '$DB_PASS'; FLUSH PRIVILEGES;" $STD apk del mariadb-client msg_ok "Installed MySQL Database" msg_info "Installing Nextcloud" ADMIN_USER=ncAdmin echo "" >>~/nextcloud.creds echo -e "Nextcloud Admin Username: \e[32m$ADMIN_USER\e[0m" >>~/nextcloud.creds echo -e "Nextcloud Admin Password: \e[32m$ADMIN_PASS\e[0m (Initially enter twice)" >>~/nextcloud.creds $STD apk add nextcloud-initscript $STD openssl req -x509 -nodes -days 365 -newkey rsa:4096 -keyout /etc/ssl/private/nextcloud-selfsigned.key -out /etc/ssl/certs/nextcloud-selfsigned.crt -subj "/C=US/O=Nextcloud/OU=Domain Control Validated/CN=nextcloud.local" cat <<'EOF' >/usr/share/webapps/nextcloud/config/config.php '/var/lib/nextcloud/data', 'logfile' => '/var/log/nextcloud/nextcloud.log', 'logdateformat' => 'F d, Y H:i:s', 'log_rotate_size' => 104857600, 'apps_paths' => array ( 0 => array ( 'path' => '/usr/share/webapps/nextcloud/apps', 'url' => '/apps', 'writable' => false, ), 1 => array ( 'path' => '/var/lib/nextcloud/apps', 'url' => '/apps-appstore', 'writable' => true, ), ), 'updatechecker' => false, 'check_for_working_htaccess' => false, 'memcache.local' => '\\OC\\Memcache\\Redis', 'memcache.locking' => '\\OC\\Memcache\\Redis', 'redis' => array( 'host' => 'localhost', 'port' => 6379, 'dbindex' => 0, 'timeout' => 1.5, ), 'installed' => false, ); EOF rm -rf /etc/nginx/http.d/default.conf cat <<'EOF' >/etc/nginx/http.d/nextcloud.conf server { listen [::]:80; listen 80; return 301 https://$host$request_uri; server_name localhost; client_max_body_size 16G; fastcgi_read_timeout 120s; } server { listen 443 ssl; listen [::]:443 ssl; http2 on; server_name localhost; root /usr/share/webapps/nextcloud; index index.php index.html index.htm; disable_symlinks off; ssl_certificate /etc/ssl/certs/nextcloud-selfsigned.crt; ssl_certificate_key /etc/ssl/private/nextcloud-selfsigned.key; ssl_session_timeout 5m; ssl_ciphers ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA; ssl_prefer_server_ciphers on; location / { try_files $uri $uri/ /index.html; } location ~ [^/]\.php(/|$) { fastcgi_split_path_info ^(.+?\.php)(/.*)$; if (!-f $document_root$fastcgi_script_name) { return 404; } fastcgi_pass unix:/run/nextcloud/fastcgi.sock; # From the nextcloud-initscript package fastcgi_index index.php; include fastcgi.conf; fastcgi_read_timeout 120s; client_max_body_size 16G; } location ^~ /.well-known/carddav { return 301 /remote.php/dav/; } location ^~ /.well-known/caldav { return 301 /remote.php/dav/; } location ^~ /.well-known/webfinger { return 301 /index.php/.well-known/webfinger; } location ^~ /.well-known/nodeinfo { return 301 /index.php/.well-known/nodeinfo; } } EOF sed -i -e 's|memory_limit = 128M|memory_limit = 512M|; $aapc.enable_cli=1' /etc/php83/php.ini sed -i -e 's|upload_max_file_size = 2M|upload_max_file_size = 16G|' /etc/php83/php.ini sed -i -E '/^php_admin_(flag|value)\[opcache/s/^/;/' /etc/php83/php-fpm.d/nextcloud.conf sed -i -e 's| js;| mjs js;|' /etc/nginx/mime.types msg_ok "Installed Nextcloud" msg_info "Adding Additional Nextcloud Packages" $STD apk add nextcloud-occ $STD apk add nextcloud-default-apps $STD apk add nextcloud-activity $STD apk add nextcloud-admin_audit $STD apk add nextcloud-comments $STD apk add nextcloud-dashboard $STD apk add nextcloud-doc $STD apk add nextcloud-encryption $STD apk add nextcloud-federation $STD apk add nextcloud-files_external $STD apk add nextcloud-files_sharing $STD apk add nextcloud-files_trashbin $STD apk add nextcloud-files_versions $STD apk add nextcloud-notifications $STD apk add nextcloud-sharebymail $STD apk add nextcloud-suspicious_login $STD apk add nextcloud-support $STD apk add nextcloud-systemtags $STD apk add nextcloud-user_status $STD apk add nextcloud-weather_status msg_ok "Added Additional Nextcloud Packages" msg_info "Starting Services" $STD rc-service redis start $STD rc-update add redis default $STD rc-service php-fpm83 start chown -R nextcloud:www-data /var/log/nextcloud/ chown -R nextcloud:www-data /usr/share/webapps/nextcloud/ $STD rc-service php-fpm83 restart $STD rc-service nginx start $STD rc-service nextcloud start $STD rc-update add nginx default $STD rc-update add nextcloud default msg_ok "Started Services" msg_info "Start Nextcloud Setup-Wizard" echo -e "export VISUAL=nano\nexport EDITOR=nano" >>/etc/profile cd /usr/share/webapps/nextcloud $STD su nextcloud -s /bin/sh -c "php83 occ maintenance:install \ --database='mysql' --database-name $DB_NAME \ --database-user '$DB_USER' --database-pass '$DB_PASS' \ --admin-user '$ADMIN_USER' --admin-pass '$ADMIN_PASS' \ --data-dir '/var/lib/nextcloud/data'" $STD su nextcloud -s /bin/sh -c 'php83 occ background:cron' rm -rf /usr/share/webapps/nextcloud/apps/serverinfo IP4=$(/sbin/ip -o -4 addr list eth0 | awk '{print $4}' | cut -d/ -f1) sed -i "/0 => \'localhost\',/a \ \1 => '$IP4'," /usr/share/webapps/nextcloud/config/config.php su nextcloud -s /bin/sh -c 'php83 -f /usr/share/webapps/nextcloud/cron.php' msg_ok "Finished Nextcloud Setup-Wizard" motd_ssh customize ================================================ FILE: install/alpine-node-red-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://nodered.org/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apk add --no-cache \ git \ nodejs \ npm msg_ok "Installed Dependencies" msg_info "Creating Node-RED User" adduser -D -H -s /sbin/nologin -G users nodered msg_ok "Created Node-RED User" msg_info "Installing Node-RED" $STD npm install -g --unsafe-perm node-red msg_ok "Installed Node-RED" msg_info "Creating /home/nodered" mkdir -p /home/nodered chown -R nodered:users /home/nodered chmod 750 /home/nodered msg_ok "Created /home/nodered" msg_info "Creating Node-RED Service" service_path="/etc/init.d/nodered" echo '#!/sbin/openrc-run description="Node-RED Service" command="/usr/local/bin/node-red" command_args="--max-old-space-size=128 -v" command_user="nodered" pidfile="/var/run/nodered.pid" command_background="yes" depend() { use net }' >$service_path chmod +x $service_path msg_ok "Created Node-RED Service" msg_info "Starting Node-RED" $STD rc-update add nodered $STD rc-service nodered start msg_ok "Started Node-RED" motd_ssh customize ================================================ FILE: install/alpine-ntfy-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: cobalt (cobaltgit) # License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE # Source: https://ntfy.sh/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing ntfy" $STD apk add --no-cache ntfy ntfy-openrc libcap sed -i '/^listen-http/s/^\(.*\)$/#\1\n/' /etc/ntfy/server.yml setcap 'cap_net_bind_service=+ep' /usr/bin/ntfy $STD rc-update add ntfy default $STD service ntfy start msg_ok "Installed ntfy" motd_ssh customize ================================================ FILE: install/alpine-postgresql-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.postgresql.org/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os read -r -p "${TAB3}Enter PostgreSQL version (15/16/17): " ver [[ $ver =~ ^(15|16|17)$ ]] || { echo "Invalid version"; exit 64; } msg_info "Installing PostgreSQL ${ver}" $STD apk add --no-cache postgresql${ver} postgresql${ver}-contrib postgresql${ver}-openrc sudo msg_ok "Installed PostgreSQL ${ver}" msg_info "Enabling PostgreSQL Service" $STD rc-update add postgresql default msg_ok "Enabled PostgreSQL Service" msg_info "Starting PostgreSQL" $STD rc-service postgresql start msg_ok "Started PostgreSQL" msg_info "Configuring PostgreSQL for External Access" conf_file="/etc/postgresql${ver}/postgresql.conf" hba_file="/etc/postgresql${ver}/pg_hba.conf" sed -i 's/^#listen_addresses =.*/listen_addresses = '\''*'\''/' "$conf_file" sed -i '/^host\s\+all\s\+all\s\+127.0.0.1\/32\s\+md5/ s/.*/host all all 0.0.0.0\/0 md5/' "$hba_file" $STD rc-service postgresql restart msg_ok "Configured and Restarted PostgreSQL" read -r -p "${TAB3}Would you like to install Adminer with lighttpd? : " prompt if [[ ${prompt,,} =~ ^(y|yes)$ ]]; then msg_info "Installing Adminer and dependencies" $STD apk add --no-cache \ lighttpd \ lighttpd-openrc \ php83 \ php83-cgi \ php83-common \ php83-curl \ php83-gd \ php83-mbstring \ php83-pdo \ php83-pgsql \ php83-openssl \ php83-zip \ php83-session \ jq sed -i 's|# *include "mod_fastcgi.conf"|include "mod_fastcgi.conf"|' /etc/lighttpd/lighttpd.conf sed -i 's|/usr/bin/php-cgi|/usr/bin/php-cgi83|g' /etc/lighttpd/mod_fastcgi.conf mkdir -p /var/www/localhost/htdocs ADMINER_VERSION=$(curl -fsSL https://api.github.com/repos/vrana/adminer/releases/latest | jq -r '.tag_name' | sed 's/^v//') curl -fsSL "https://github.com/vrana/adminer/releases/download/v${ADMINER_VERSION}/adminer-${ADMINER_VERSION}.php" -o /var/www/localhost/htdocs/adminer.php chown lighttpd:lighttpd /var/www/localhost/htdocs/adminer.php chmod 755 /var/www/localhost/htdocs/adminer.php msg_ok "Adminer Installed" msg_info "Starting Lighttpd" $STD rc-update add lighttpd default $STD rc-service lighttpd restart msg_ok "Lighttpd Started" fi motd_ssh customize ================================================ FILE: install/alpine-prometheus-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://prometheus.io/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Prometheus" $STD apk add --no-cache prometheus msg_ok "Installed Prometheus" msg_info "Enabling Prometheus Service" $STD rc-update add prometheus default msg_ok "Enabled Prometheus Service" msg_info "Starting Prometheus" $STD service prometheus start msg_ok "Started Prometheus" motd_ssh customize ================================================ FILE: install/alpine-rclone-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: Slaviša Arežina (tremor021) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/rclone/rclone source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing dependencies" $STD apk add --no-cache \ apache2-utils fuse3 msg_ok "Installed dependencies" msg_info "Installing rclone" temp_file=$(mktemp) mkdir -p /opt/rclone RELEASE=$(curl -s https://api.github.com/repos/rclone/rclone/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4) }') curl -fsSL "https://github.com/rclone/rclone/releases/download/v${RELEASE}/rclone-v${RELEASE}-linux-arm64.zip" -o "$temp_file" $STD unzip -j "$temp_file" '*/**' -d /opt/rclone cd /opt/rclone RCLONE_PASSWORD=$(head -c 16 /dev/urandom | xxd -p -c 16) $STD htpasswd -cb -B /opt/login.pwd admin "$RCLONE_PASSWORD" { echo "rclone-Credentials" echo "rclone User Name: admin" echo "rclone Password: $RCLONE_PASSWORD" } >>~/rclone.creds echo "${RELEASE}" >/opt/rclone_version.txt rm -f "$temp_file" msg_ok "Installed rclone" msg_info "Enabling rclone Service" cat </etc/init.d/rclone #!/sbin/openrc-run description="rclone Service" command="/opt/rclone/rclone" command_args="rcd --rc-web-gui --rc-web-gui-no-open-browser --rc-addr :3000 --rc-htpasswd /opt/login.pwd" command_background="true" command_user="root" pidfile="/var/run/rclone.pid" depend() { use net } EOF chmod +x /etc/init.d/rclone $STD rc-update add rclone default msg_ok "Enabled rclone Service" msg_info "Starting rclone" $STD service rclone start msg_ok "Started rclone" motd_ssh customize msg_info "Cleaning up" rm -rf "$temp_file" $STD apk cache clean msg_ok "Cleaned" ================================================ FILE: install/alpine-redis-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://redis.io/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Redis" $STD apk add redis $STD sed -i 's/^bind .*/bind 0.0.0.0/' /etc/redis.conf $STD rc-update add redis default $STD rc-service redis start msg_ok "Installed Redis" motd_ssh customize ================================================ FILE: install/alpine-redlib-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: andrej-kocijan (Andrej Kocijan) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/redlib-org/redlib source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os fetch_and_deploy_gh_release "redlib" "redlib-org/redlib" "prebuild" "latest" "/opt/redlib" "redlib-aarch64-unknown-linux-musl.tar.gz" msg_info "Configuring Redlib" cat </opt/redlib/redlib.conf ############################################ # Redlib Instance Configuration File # Uncomment and edit values as needed ############################################ ## Instance settings ADDRESS=0.0.0.0 PORT=5252 # Integer (0-65535) - Internal port #REDLIB_SFW_ONLY=off # ["on", "off"] - Filter all NSFW content #REDLIB_BANNER= # String - Displayed on instance info page #REDLIB_ROBOTS_DISABLE_INDEXING=off # ["on", "off"] - Disable search engine indexing #REDLIB_PUSHSHIFT_FRONTEND=undelete.pullpush.io # Pushshift frontend for removed links #REDLIB_ENABLE_RSS=off # ["on", "off"] - Enable RSS feed generation #REDLIB_FULL_URL= # String - Needed for proper RSS URLs ## Default user settings #REDLIB_DEFAULT_THEME=system # Theme (system, light, dark, black, dracula, nord, laserwave, violet, gold, rosebox, gruvboxdark, gruvboxlight, tokyoNight, icebergDark, doomone, libredditBlack, libredditDark, libredditLight) #REDLIB_DEFAULT_FRONT_PAGE=default # ["default", "popular", "all"] #REDLIB_DEFAULT_LAYOUT=card # ["card", "clean", "compact"] #REDLIB_DEFAULT_WIDE=off # ["on", "off"] #REDLIB_DEFAULT_POST_SORT=hot # ["hot", "new", "top", "rising", "controversial"] #REDLIB_DEFAULT_COMMENT_SORT=confidence # ["confidence", "top", "new", "controversial", "old"] #REDLIB_DEFAULT_BLUR_SPOILER=off # ["on", "off"] #REDLIB_DEFAULT_SHOW_NSFW=off # ["on", "off"] #REDLIB_DEFAULT_BLUR_NSFW=off # ["on", "off"] #REDLIB_DEFAULT_USE_HLS=off # ["on", "off"] #REDLIB_DEFAULT_HIDE_HLS_NOTIFICATION=off # ["on", "off"] #REDLIB_DEFAULT_AUTOPLAY_VIDEOS=off # ["on", "off"] #REDLIB_DEFAULT_SUBSCRIPTIONS= # Example: sub1+sub2+sub3 #REDLIB_DEFAULT_HIDE_AWARDS=off # ["on", "off"] #REDLIB_DEFAULT_DISABLE_VISIT_REDDIT_CONFIRMATION=off # ["on", "off"] #REDLIB_DEFAULT_HIDE_SCORE=off # ["on", "off"] #REDLIB_DEFAULT_HIDE_SIDEBAR_AND_SUMMARY=off # ["on", "off"] #REDLIB_DEFAULT_FIXED_NAVBAR=on # ["on", "off"] #REDLIB_DEFAULT_REMOVE_DEFAULT_FEEDS=off # ["on", "off"] EOF msg_ok "Configured Redlib" msg_info "Creating Redlib Service" cat </etc/init.d/redlib #!/sbin/openrc-run name="Redlib" description="Redlib Service" command="/opt/redlib/redlib" pidfile="/run/redlib.pid" supervisor="supervise-daemon" command_background="yes" depend() { need net } start_pre() { set -a . /opt/redlib/redlib.conf set +a : ${ADDRESS:=0.0.0.0} : ${PORT:=5252} command_args="-a ${ADDRESS} -p ${PORT}" } EOF $STD chmod +x /etc/init.d/redlib $STD rc-update add redlib default msg_ok "Created Redlib Service" msg_info "Starting Redlib Service" $STD rc-service redlib start msg_ok "Started Redlib Service" motd_ssh customize msg_info "Cleaning up" $STD apk cache clean msg_ok "Cleaned" ================================================ FILE: install/alpine-rustdeskserver-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: Slaviša Arežina (tremor021) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/rustdesk/rustdesk-server source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os RELEASE=$(curl -s https://api.github.com/repos/lejianwen/rustdesk-server/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }') msg_info "Installing RustDesk Server v${RELEASE}" temp_file1=$(mktemp) curl -fsSL "https://github.com/rustdesk/rustdesk-server/releases/download/${RELEASE}/rustdesk-server-linux-arm64.zip" -o "$temp_file1" $STD unzip "$temp_file1" mv arm64 /opt/rustdesk-server mkdir -p /root/.config/rustdesk cd /opt/rustdesk-server ./rustdesk-utils genkeypair >/tmp/rustdesk_keys.txt grep "Public Key" /tmp/rustdesk_keys.txt | awk '{print $3}' >/root/.config/rustdesk/id_ed25519.pub grep "Secret Key" /tmp/rustdesk_keys.txt | awk '{print $3}' >/root/.config/rustdesk/id_ed25519 chmod 600 /root/.config/rustdesk/id_ed25519 chmod 644 /root/.config/rustdesk/id_ed25519.pub rm /tmp/rustdesk_keys.txt echo "${RELEASE}" >~/.rustdesk-server msg_ok "Installed RustDesk Server v${RELEASE}" APIRELEASE=$(curl -s https://api.github.com/repos/lejianwen/rustdesk-api/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4) }') msg_info "Installing RustDesk API v${APIRELEASE}" temp_file2=$(mktemp) curl -fsSL "https://github.com/lejianwen/rustdesk-api/releases/download/v${APIRELEASE}/linux-arm64.tar.gz" -o "$temp_file2" $STD tar zxvf "$temp_file2" mv release /opt/rustdesk-api cd /opt/rustdesk-api ADMINPASS=$(head -c 16 /dev/urandom | xxd -p -c 16) $STD ./apimain reset-admin-pwd "$ADMINPASS" { echo "RustDesk WebUI" echo "" echo "Username: admin" echo "Password: $ADMINPASS" } >>~/rustdesk.creds echo "${APIRELEASE}" >~/.rustdesk-api msg_ok "Installed RustDesk API v${APIRELEASE}" msg_info "Enabling RustDesk Server Services" cat </etc/init.d/rustdesk-server-hbbs #!/sbin/openrc-run description="RustDesk HBBS Service" directory="/opt/rustdesk-server" command="/opt/rustdesk-server/hbbs" command_args="" command_background="true" command_user="root" pidfile="/var/run/rustdesk-server-hbbs.pid" output_log="/var/log/rustdesk-hbbs.log" error_log="/var/log/rustdesk-hbbs.err" depend() { use net } EOF cat </etc/init.d/rustdesk-server-hbbr #!/sbin/openrc-run description="RustDesk HBBR Service" directory="/opt/rustdesk-server" command="/opt/rustdesk-server/hbbr" command_args="" command_background="true" command_user="root" pidfile="/var/run/rustdesk-server-hbbr.pid" output_log="/var/log/rustdesk-hbbr.log" error_log="/var/log/rustdesk-hbbr.err" depend() { use net } EOF cat </etc/init.d/rustdesk-api #!/sbin/openrc-run description="RustDesk API Service" directory="/opt/rustdesk-api" command="/opt/rustdesk-api/apimain" command_args="" command_background="true" command_user="root" pidfile="/var/run/rustdesk-api.pid" output_log="/var/log/rustdesk-api.log" error_log="/var/log/rustdesk-api.err" depend() { use net } EOF chmod +x /etc/init.d/rustdesk-server-hbbs chmod +x /etc/init.d/rustdesk-server-hbbr chmod +x /etc/init.d/rustdesk-api $STD rc-update add rustdesk-server-hbbs default $STD rc-update add rustdesk-server-hbbr default $STD rc-update add rustdesk-api default msg_ok "Enabled RustDesk Server Services" msg_info "Starting RustDesk Server" $STD service rustdesk-server-hbbs start $STD service rustdesk-server-hbbr start $STD service rustdesk-api start msg_ok "Started RustDesk Server" motd_ssh customize msg_info "Cleaning up" rm -f "$temp_file1" "$temp_file2" $STD apk cache clean msg_ok "Cleaned" ================================================ FILE: install/alpine-rustypaste-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/orhun/rustypaste source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing RustyPaste" $STD apk add --no-cache rustypaste --repository=https://dl-cdn.alpinelinux.org/alpine/edge/community msg_ok "Installed RustyPaste" msg_info "Configuring RustyPaste" mkdir -p /var/lib/rustypaste sed -i 's|^address = ".*"|address = "0.0.0.0:8000"|' /etc/rustypaste/config.toml msg_ok "Configured RustyPaste" msg_info "Creating Service" cat <<'EOF' >/etc/init.d/rustypaste #!/sbin/openrc-run name="rustypaste" description="RustyPaste - A minimal file upload/pastebin service" command="/usr/bin/rustypaste" command_args="" command_user="root" command_background=true pidfile="/run/${RC_SVCNAME}.pid" directory="/var/lib/rustypaste" depend() { need net after firewall } start_pre() { export CONFIG=/etc/rustypaste/config.toml checkpath --directory --owner root:root --mode 0755 /var/lib/rustypaste } EOF chmod +x /etc/init.d/rustypaste $STD rc-update add rustypaste default $STD rc-service rustypaste start msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/alpine-syncthing-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://syncthing.net/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Setup Syncthing" $STD apk add --no-cache syncthing rc-service syncthing start sleep 3 rc-service syncthing stop sed -i "{s/127.0.0.1:8384/0.0.0.0:8384/g}" /var/lib/syncthing/.local/state/syncthing/config.xml msg_ok "Setup Syncthing" msg_info "Enabling Syncthing Service" $STD rc-update add syncthing default msg_ok "Enabled Syncthing Service" msg_info "Starting Syncthing" $STD rc-service syncthing start msg_ok "Started Syncthing" motd_ssh customize ================================================ FILE: install/alpine-teamspeak-server-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: tremor021 (Slaviša Arežina) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://teamspeak.com/en/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing dependencies" $STD apk add --no-cache \ ca-certificates \ libstdc++ \ libc6-compat msg_ok "Installed dependencies" RELEASE=$(curl -fsSL https://teamspeak.com/en/downloads/#server | sed -n 's/.*teamspeak3-server_linux_amd64-\([0-9.]*[0-9]\).*/\1/p' | awk 'NR==1') msg_info "Installing Teamspeak Server v${RELEASE}" mkdir -p /opt/teamspeak-server cd /opt/teamspeak-server curl -fsSL "https://files.teamspeak-services.com/releases/server/${RELEASE}/teamspeak3-server_linux_arm64-${RELEASE}.tar.bz2" -o ts3server.tar.bz2 tar xf ts3server.tar.bz2 --strip-components=1 mkdir -p logs data lib mv *.so lib touch data/ts3server.sqlitedb data/query_ip_blacklist.txt data/query_ip_whitelist.txt .ts3server_license_accepted echo "${RELEASE}" >~/.teamspeak-server msg_ok "Installed TeamSpeak Server v${RELEASE}" msg_info "Enabling TeamSpeak Server Service" cat </etc/init.d/teamspeak #!/sbin/openrc-run name="TeamSpeak Server" description="TeamSpeak 3 Server" command="/opt/teamspeak-server/ts3server_startscript.sh" command_args="start" output_log="/var/log/teamspeak.out.log" error_log="/var/log/teamspeak.err.log" command_background=true pidfile="/run/teamspeak-server.pid" directory="/opt/teamspeak-server" depend() { need net use dns } EOF chmod +x /etc/init.d/teamspeak $STD rc-update add teamspeak default msg_ok "Enabled TeamSpeak Server Service" msg_info "Starting TeamSpeak Server" $STD service teamspeak start msg_ok "Started TeamSpeak Server" motd_ssh customize msg_info "Cleaning up" rm -r ts3server.tar.bz* LICENSE* CHANGELOG doc serverquerydocs tsdns redist $STD apk cache clean msg_ok "Cleaned" ================================================ FILE: install/alpine-tinyauth-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: Slaviša Arežina (tremor021) | Co-Author: Stavros (steveiliop56) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/steveiliop56/tinyauth source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apk add --no-cache openssl apache2-utils msg_ok "Installed Dependencies" msg_info "Installing Tinyauth" mkdir -p /opt/tinyauth RELEASE=$(curl -s https://api.github.com/repos/steveiliop56/tinyauth/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4) }') curl -fsSL "https://github.com/steveiliop56/tinyauth/releases/download/v${RELEASE}/tinyauth-arm64" -o /opt/tinyauth/tinyauth chmod +x /opt/tinyauth/tinyauth PASS=$(openssl rand -base64 8 | tr -dc 'a-zA-Z0-9' | head -c 8) USER=$(htpasswd -Bbn "tinyauth" "${PASS}") cat </opt/tinyauth/credentials.txt Tinyauth Credentials Username: tinyauth Password: ${PASS} EOF echo "${RELEASE}" >~/.tinyauth msg_ok "Installed Tinyauth" read -r -p "${TAB3}Enter your Tinyauth subdomain (e.g. https://tinyauth.example.com): " app_url cat </opt/tinyauth/.env TINYAUTH_DATABASE_PATH=/opt/tinyauth/database.db TINYAUTH_AUTH_USERS='${USER}' TINYAUTH_APPURL=${app_url} EOF msg_info "Creating Service" cat <<'EOF' >/etc/init.d/tinyauth #!/sbin/openrc-run description="Tinyauth Service" set -a ENV_FILE="/opt/tinyauth/.env" [ -f "$ENV_FILE" ] && . "$ENV_FILE" set +a command="/opt/tinyauth/tinyauth" directory="/opt/tinyauth" command_user="root" command_background="true" pidfile="/var/run/tinyauth.pid" depend() { use net } EOF chmod +x /etc/init.d/tinyauth $STD rc-update add tinyauth default msg_ok "Enabled Tinyauth Service" msg_info "Starting Tinyauth" $STD service tinyauth start msg_ok "Started Tinyauth" motd_ssh customize ================================================ FILE: install/alpine-traefik-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apk add ca-certificates $STD update-ca-certificates msg_ok "Installed Dependencies" msg_info "Installing Traefik" $STD apk add traefik --repository=https://dl-cdn.alpinelinux.org/alpine/edge/community msg_ok "Installed Traefik" read -p "${TAB3}Enable Traefik WebUI (Port 8080)? [y/N]: " enable_webui if [[ "$enable_webui" =~ ^[Yy]$ ]]; then msg_info "Configuring Traefik WebUI" sed -i 's/localhost//g' /etc/traefik/traefik.yaml msg_ok "Configured Traefik WebUI" fi msg_info "Enabling and starting Traefik service" $STD rc-update add traefik default sed -i '/^command=.*/i directory="/etc/traefik"' /etc/init.d/traefik $STD rc-service traefik start msg_ok "Traefik service started" motd_ssh customize ================================================ FILE: install/alpine-transmission-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: Slaviša Arežina (tremor021) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://transmissionbt.com/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Transmission" $STD apk add --no-cache transmission-cli transmission-daemon $STD rc-service transmission-daemon start sleep 5 $STD rc-service transmission-daemon stop sed -i '{s/"rpc-whitelist-enabled": true/"rpc-whitelist-enabled": false/g; s/"rpc-host-whitelist-enabled": true,/"rpc-host-whitelist-enabled": false,/g}' /var/lib/transmission/config/settings.json msg_ok "Installed Transmission" msg_info "Enabling Transmission Service" $STD rc-update add transmission-daemon default msg_ok "Enabled Transmission Service" msg_info "Starting Transmission" $STD rc-service transmission-daemon start msg_ok "Started Transmission" motd_ssh customize ================================================ FILE: install/alpine-valkey-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: pshankinclarke (lazarillo) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://valkey.io/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Valkey" $STD apk add valkey valkey-openrc valkey-cli sed -i 's/^bind .*/bind 0.0.0.0/' /etc/valkey/valkey.conf PASS="$(head -c 100 /dev/urandom | tr -dc 'a-zA-Z0-9' | head -c32)" echo "requirepass $PASS" >>/etc/valkey/valkey.conf echo "$PASS" >~/valkey.creds chmod 600 ~/valkey.creds MEMTOTAL_MB=$(free -m | grep ^Mem: | awk '{print $2}') MAXMEMORY_MB=$((MEMTOTAL_MB * 75 / 100)) { echo "" echo "# Memory-optimized settings for small-scale deployments" echo "maxmemory ${MAXMEMORY_MB}mb" echo "maxmemory-policy allkeys-lru" echo "maxmemory-samples 10" } >>/etc/valkey/valkey.conf msg_ok "Installed Valkey" # Note: Alpine's valkey package is compiled without TLS support # For TLS, use the Debian-based valkey script instead $STD rc-update add valkey default $STD rc-service valkey start motd_ssh customize cleanup_lxc ================================================ FILE: install/alpine-vaultwarden-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) | Co-Author: Slaviša Arežina (tremor021) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/dani-garcia/vaultwarden source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apk add --no-cache \ openssl \ argon2 msg_ok "Installed Dependencies" msg_info "Installing Alpine-Vaultwarden" $STD apk add --no-cache vaultwarden sed -i 's|export WEB_VAULT_ENABLED=.*|export WEB_VAULT_ENABLED=true|' /etc/conf.d/vaultwarden echo -e "export ADMIN_TOKEN=''" >>/etc/conf.d/vaultwarden echo -e "export ROCKET_ADDRESS=0.0.0.0" >>/etc/conf.d/vaultwarden echo -e "export ROCKET_TLS='{certs=\"/etc/ssl/certs/vaultwarden-selfsigned.crt\",key=\"/etc/ssl/private/vaultwarden-selfsigned.key\"}'" >>/etc/conf.d/vaultwarden $STD openssl req -x509 -nodes -days 365 -newkey rsa:4096 -keyout /etc/ssl/private/vaultwarden-selfsigned.key -out /etc/ssl/certs/vaultwarden-selfsigned.crt -subj "/CN=localhost" -addext "subjectAltName=DNS:localhost" chown vaultwarden:vaultwarden /etc/ssl/certs/vaultwarden-selfsigned.crt chown vaultwarden:vaultwarden /etc/ssl/private/vaultwarden-selfsigned.key msg_ok "Installed Alpine-Vaultwarden" msg_info "Installing Web-Vault" $STD apk add --no-cache vaultwarden-web-vault msg_ok "Installed Web-Vault" msg_info "Starting Alpine-Vaultwarden" $STD rc-service vaultwarden start $STD rc-update add vaultwarden default msg_ok "Started Alpine-Vaultwarden" motd_ssh customize ================================================ FILE: install/alpine-wakapi-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: Slaviša Arežina (tremor021) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://wakapi.dev/ | https://github.com/muety/wakapi source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apk add --no-cache \ ca-certificates \ tzdata $STD update-ca-certificates msg_ok "Installed Dependencies" fetch_and_deploy_gh_release "wakapi" "muety/wakapi" "prebuild" "latest" "/opt/wakapi" "wakapi_linux_amd64.zip" msg_info "Configuring Wakapi" LOCAL_IP=$(/sbin/ip -o -4 addr list eth0 | awk '{print $4}' | cut -d/ -f1) cd /opt/wakapi sed -i 's/listen_ipv6: ::1/listen_ipv6: "-"/g' config.yml sed -i 's/listen_ipv4: 127.0.0.1/listen_ipv4: "0.0.0.0"/g' config.yml sed -i "s/public_url: http:\/\/localhost:3000/public_url: http:\/\/$LOCAL_IP:3000/g" config.yml msg_ok "Configured Wakapi" msg_info "Enabling Wakapi Service" cat </etc/init.d/wakapi #!/sbin/openrc-run description="Wakapi Service" directory="/opt/wakapi" command="/opt/wakapi/wakapi" command_args="-config config.yml" command_background="true" command_user="root" pidfile="/var/run/wakapi.pid" depend() { use net } EOF chmod +x /etc/init.d/wakapi $STD rc-update add wakapi default msg_ok "Enabled Wakapi Service" msg_info "Starting Wakapi" $STD rc-service wakapi start msg_ok "Started Wakapi" motd_ssh customize ================================================ FILE: install/alpine-wireguard-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.wireguard.com/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apk add \ iptables \ openrc msg_ok "Installed Dependencies" msg_info "Installing WireGuard" $STD apk add --no-cache wireguard-tools if [[ ! -L /etc/init.d/wg-quick.wg0 ]]; then ln -s /etc/init.d/wg-quick /etc/init.d/wg-quick.wg0 fi private_key=$(wg genkey) cat </etc/wireguard/wg0.conf [Interface] PrivateKey = ${private_key} Address = 10.0.0.1/24 SaveConfig = true PostUp = iptables -A FORWARD -i wg0 -j ACCEPT; iptables -A FORWARD -o wg0 -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE; PostDown = iptables -D FORWARD -i wg0 -j ACCEPT; iptables -D FORWARD -o wg0 -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE; ListenPort = 51820 EOF echo "net.ipv4.ip_forward=1" >>/etc/sysctl.conf $STD rc-update add sysctl $STD sysctl -p /etc/sysctl.conf msg_ok "Installed WireGuard" read -rp "${TAB3}Do you want to install WGDashboard? (y/N): " INSTALL_WGD if [[ "$INSTALL_WGD" =~ ^[Yy]$ ]]; then msg_info "Installing additional dependencies for WGDashboard" $STD apk add --no-cache \ python3 \ py3-pip \ git \ sudo \ musl-dev \ linux-headers \ gcc \ python3-dev msg_ok "Installed additional dependencies for WGDashboard" msg_info "Installing WGDashboard" git clone -q https://github.com/WGDashboard/WGDashboard.git /etc/wgdashboard cd /etc/wgdashboard/src chmod u+x wgd.sh $STD ./wgd.sh install msg_ok "Installed WGDashboard" msg_info "Creating Service for WGDashboard" cat </etc/init.d/wg-dashboard #!/sbin/openrc-run description="WireGuard Dashboard Service" depend() { need net after firewall } start() { ebegin "Starting WGDashboard" cd /etc/wgdashboard/src/ ./wgd.sh start & eend $? } stop() { ebegin "Stopping WGDashboard" pkill -f "wgd.sh" eend $? } EOF chmod +x /etc/init.d/wg-dashboard $STD rc-update add wg-dashboard default $STD rc-service wg-dashboard start msg_ok "Created Service for WGDashboard" fi msg_info "Starting Services" $STD rc-update add wg-quick.wg0 default $STD rc-service wg-quick.wg0 start msg_ok "Started Services" motd_ssh customize ================================================ FILE: install/alpine-zigbee2mqtt-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.zigbee2mqtt.io/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Alpine-Zigbee2MQTT" mkdir -p /root/.z2m /etc/zigbee2mqtt $STD apk add zigbee2mqtt ln -s /etc/zigbee2mqtt/ /root/.z2m chown -R root:root /etc/zigbee2mqtt /root/.z2m sed -i -e 's/#datadir="\/var\/lib\/zigbee2mqtt"/datadir="\/etc\/zigbee2mqtt"/' -e 's/#command_user="zigbee2mqtt"/command_user="root"/' /etc/conf.d/zigbee2mqtt $STD rc-update add zigbee2mqtt $STD rc-service zigbee2mqtt restart msg_ok "Installed Alpine-Zigbee2MQTT" motd_ssh customize ================================================ FILE: install/ampache-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (Canbiz) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/ampache/ampache source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing dependencies" $STD apt install -y \ flac \ vorbis-tools \ lame \ ffmpeg \ inotify-tools \ libavcodec-extra \ libmp3lame-dev \ libtheora-dev \ libvorbis-dev \ libvpx-dev msg_ok "Installed dependencies" PHP_VERSION="8.4" PHP_APACHE="YES" setup_php setup_mariadb MARIADB_DB_USER="ampache" MARIADB_DB_NAME="ampache" setup_mariadb_db fetch_and_deploy_gh_release "ampache" "ampache/ampache" "prebuild" "latest" "/opt/ampache" "ampache-*_all_php8.4.zip" msg_info "Setting up Ampache" rm -rf /var/www/html ln -s /opt/ampache/public /var/www/html mv /opt/ampache/public/rest/.htaccess.dist /opt/ampache/public/rest/.htaccess mv /opt/ampache/public/play/.htaccess.dist /opt/ampache/public/play/.htaccess cp /opt/ampache/config/ampache.cfg.php.dist /opt/ampache/config/ampache.cfg.php chmod 664 /opt/ampache/public/rest/.htaccess /opt/ampache/public/play/.htaccess msg_ok "Set up Ampache" msg_info "Configuring Database Connection" sed -i -e 's|^database_hostname = .*|database_hostname = "localhost"|' \ -e 's|^database_name = .*|database_name = "ampache"|' \ -e 's|^database_username = .*|database_username = "ampache"|' \ -e "s|^database_password = .*|database_password = \"${MARIADB_DB_PASS}\"|" /opt/ampache/config/ampache.cfg.php chown -R www-data:www-data /opt/ampache msg_ok "Configured Database Connection" msg_info "Importing Database Schema" mariadb -u ampache -p"${MARIADB_DB_PASS}" ampache /opt/anchor/.env APP_URL=http://${LOCAL_IP}:3000 JWT_SECRET=${JWT_SECRET} DATABASE_URL=postgresql://anchor:${PG_DB_PASS}@localhost:5432/anchor PG_HOST=localhost PG_USER=anchor PG_PASSWORD=${PG_DB_PASS} PG_DATABASE=anchor PG_PORT=5432 EOF msg_ok "Configured Application" msg_info "Running Database Migrations" cd /opt/anchor/server set -a && source /opt/anchor/.env && set +a $STD pnpm prisma migrate deploy msg_ok "Ran Database Migrations" msg_info "Creating Services" cat </etc/systemd/system/anchor-server.service [Unit] Description=Anchor API Server After=network.target postgresql.service [Service] Type=simple User=root WorkingDirectory=/opt/anchor/server EnvironmentFile=/opt/anchor/.env ExecStart=/usr/bin/node dist/src/main.js Restart=on-failure RestartSec=5 [Install] WantedBy=multi-user.target EOF cat </etc/systemd/system/anchor-web.service [Unit] Description=Anchor Web Interface After=network.target anchor-server.service [Service] Type=simple User=root WorkingDirectory=/opt/anchor/web/.next/standalone EnvironmentFile=/opt/anchor/.env Environment=PORT=3000 HOSTNAME=0.0.0.0 NODE_ENV=production ExecStart=/usr/bin/node server.js Restart=on-failure RestartSec=5 [Install] WantedBy=multi-user.target EOF systemctl enable -q --now anchor-server anchor-web msg_ok "Created Services" motd_ssh customize cleanup_lxc ================================================ FILE: install/anytype-server-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://anytype.io source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os setup_mongodb msg_info "Configuring MongoDB Replica Set" cat <>/etc/mongod.conf replication: replSetName: "rs0" EOF systemctl restart mongod for i in $(seq 1 30); do if mongosh --quiet --eval "db.adminCommand('ping')" &>/dev/null; then break fi sleep 2 done $STD mongosh --eval 'rs.initiate({_id: "rs0", members: [{_id: 0, host: "127.0.0.1:27017"}]})' msg_ok "Configured MongoDB Replica Set" msg_info "Installing Redis Stack" setup_deb822_repo \ "redis-stack" \ "https://packages.redis.io/gpg" \ "https://packages.redis.io/deb" \ "jammy" \ "main" $STD apt install -y redis-stack-server systemctl enable -q --now redis-stack-server msg_ok "Installed Redis Stack" fetch_and_deploy_gh_release "anytype" "grishy/any-sync-bundle" "prebuild" "latest" "/opt/anytype" "any-sync-bundle_*_linux_amd64.tar.gz" chmod +x /opt/anytype/any-sync-bundle msg_info "Configuring Anytype" mkdir -p /opt/anytype/data/storage cat </opt/anytype/.env ANY_SYNC_BUNDLE_CONFIG=/opt/anytype/data/bundle-config.yml ANY_SYNC_BUNDLE_CLIENT_CONFIG=/opt/anytype/data/client-config.yml ANY_SYNC_BUNDLE_INIT_STORAGE=/opt/anytype/data/storage/ ANY_SYNC_BUNDLE_INIT_EXTERNAL_ADDRS=${LOCAL_IP} ANY_SYNC_BUNDLE_INIT_MONGO_URI=mongodb://127.0.0.1:27017/ ANY_SYNC_BUNDLE_INIT_REDIS_URI=redis://127.0.0.1:6379/ ANY_SYNC_BUNDLE_LOG_LEVEL=info EOF msg_ok "Configured Anytype" msg_info "Creating Service" cat </etc/systemd/system/anytype.service [Unit] Description=Anytype Sync Server (any-sync-bundle) After=network-online.target mongod.service redis-stack-server.service Wants=network-online.target Requires=mongod.service redis-stack-server.service [Service] Type=simple User=root WorkingDirectory=/opt/anytype EnvironmentFile=/opt/anytype/.env ExecStart=/opt/anytype/any-sync-bundle start-bundle Restart=on-failure RestartSec=10 [Install] WantedBy=multi-user.target EOF systemctl enable -q --now anytype msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/apache-cassandra-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://cassandra.apache.org/_/index.html source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os JAVA_VERSION="11" setup_java msg_info "Installing Apache Cassandra" setup_deb822_repo \ "cassandra" \ "https://downloads.apache.org/cassandra/KEYS" \ "https://debian.cassandra.apache.org" \ "41x" \ "main" $STD apt install -y cassandra cassandra-tools sed -i -e 's/^rpc_address: localhost/#rpc_address: localhost/g' -e 's/^# rpc_interface: eth1/rpc_interface: eth0/g' /etc/cassandra/cassandra.yaml msg_ok "Installed Apache Cassandra" motd_ssh customize cleanup_lxc ================================================ FILE: install/apache-couchdb-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://couchdb.apache.org/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Apache CouchDB" ERLANG_COOKIE=$(openssl rand -base64 32) ADMIN_PASS="$(openssl rand -base64 18 | cut -c1-13)" debconf-set-selections <<<"couchdb couchdb/cookie string $ERLANG_COOKIE" debconf-set-selections <<<"couchdb couchdb/mode select standalone" debconf-set-selections <<<"couchdb couchdb/bindaddress string 0.0.0.0" debconf-set-selections <<<"couchdb couchdb/adminpass password $ADMIN_PASS" debconf-set-selections <<<"couchdb couchdb/adminpass_again password $ADMIN_PASS" setup_deb822_repo \ "couchdb" \ "https://couchdb.apache.org/repo/keys.asc" \ "https://apache.jfrog.io/artifactory/couchdb-deb" \ "$(get_os_info codename)" \ "main" $STD apt install -y couchdb { echo "CouchDB Credentials" echo "CouchDB Erlang Cookie: $ERLANG_COOKIE" echo "CouchDB Admin Password: $ADMIN_PASS" } >>~/couchdb.creds msg_ok "Installed Apache CouchDB" motd_ssh customize cleanup_lxc ================================================ FILE: install/apache-guacamole-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: Michel Roegl-Brunner (michelroegl-brunner) | MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://guacamole.apache.org/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ build-essential \ libcairo2-dev \ libjpeg62-turbo-dev \ libpng-dev \ libtool-bin \ uuid-dev \ libvncserver-dev \ freerdp3-dev \ libssh2-1-dev \ libtelnet-dev \ libwebsockets-dev \ libpulse-dev \ libvorbis-dev \ libwebp-dev \ libssl-dev \ libpango1.0-dev \ libswscale-dev \ libavcodec-dev \ libavutil-dev \ libavformat-dev msg_ok "Installed Dependencies" JAVA_VERSION="17" setup_java setup_mariadb MARIADB_DB_NAME="guacamole_db" MARIADB_DB_USER="guacamole_user" setup_mariadb_db msg_info "Setup Apache Tomcat" TOMCAT_VERSION=$(curl -fsSL https://dlcdn.apache.org/tomcat/tomcat-9/ | grep -oP '(?<=href=")v[^"/]+(?=/")' | sed 's/^v//' | sort -V | tail -n1) mkdir -p /opt/apache-guacamole/{tomcat9,server} curl -fsSL "https://dlcdn.apache.org/tomcat/tomcat-9/v${TOMCAT_VERSION}/bin/apache-tomcat-${TOMCAT_VERSION}.tar.gz" | tar -xz -C /opt/apache-guacamole/tomcat9 --strip-components=1 useradd -r -d /opt/apache-guacamole/tomcat9 -s /bin/false tomcat chown -R tomcat: /opt/apache-guacamole/tomcat9 chmod -R g+r /opt/apache-guacamole/tomcat9/conf chmod g+x /opt/apache-guacamole/tomcat9/conf echo "${TOMCAT_VERSION}" >~/.guacamole_tomcat msg_ok "Setup Apache Tomcat ${TOMCAT_VERSION}" msg_info "Setup Apache Guacamole" mkdir -p /etc/guacamole/{extensions,lib} GUAC_SERVER_VERSION=$(curl -fsSL https://api.github.com/repos/apache/guacamole-server/tags | jq -r '.[].name' | grep -v -- '-RC' | head -n 1) GUAC_CLIENT_VERSION=$(curl -fsSL https://api.github.com/repos/apache/guacamole-client/tags | jq -r '.[].name' | grep -v -- '-RC' | head -n 1) MYSQL_CONNECTOR_VERSION=$(curl -fsSL "https://repo1.maven.org/maven2/com/mysql/mysql-connector-j/maven-metadata.xml" | grep -oP '\K[^<]+') curl -fsSL "https://api.github.com/repos/apache/guacamole-server/tarball/refs/tags/${GUAC_SERVER_VERSION}" | tar -xz --strip-components=1 -C /opt/apache-guacamole/server cd /opt/apache-guacamole/server export CPPFLAGS="-Wno-error=deprecated-declarations" $STD autoreconf -fi $STD ./configure --with-init-dir=/etc/init.d --enable-allow-freerdp-snapshots $STD make $STD make install $STD ldconfig echo "${GUAC_SERVER_VERSION}" >~/.guacamole_server curl -fsSL "https://downloads.apache.org/guacamole/${GUAC_CLIENT_VERSION}/binary/guacamole-${GUAC_CLIENT_VERSION}.war" -o "/opt/apache-guacamole/tomcat9/webapps/guacamole.war" echo "${GUAC_CLIENT_VERSION}" >~/.guacamole_client curl -fsSL "https://repo1.maven.org/maven2/com/mysql/mysql-connector-j/${MYSQL_CONNECTOR_VERSION}/mysql-connector-j-${MYSQL_CONNECTOR_VERSION}.jar" -o "/etc/guacamole/lib/mysql-connector-j.jar" echo "${MYSQL_CONNECTOR_VERSION}" >~/.guacamole_mysql_connector cd /root curl -fsSL "https://downloads.apache.org/guacamole/${GUAC_SERVER_VERSION}/binary/guacamole-auth-jdbc-${GUAC_SERVER_VERSION}.tar.gz" -o "/root/guacamole-auth-jdbc-${GUAC_SERVER_VERSION}.tar.gz" $STD tar -xf ~/guacamole-auth-jdbc-"$GUAC_SERVER_VERSION".tar.gz mv ~/guacamole-auth-jdbc-"$GUAC_SERVER_VERSION"/mysql/guacamole-auth-jdbc-mysql-"$GUAC_SERVER_VERSION".jar /etc/guacamole/extensions/ echo "${GUAC_SERVER_VERSION}" >~/.guacamole_auth_jdbc msg_ok "Setup Apache Guacamole" msg_info "Importing Database Schema" cd ~/guacamole-auth-jdbc-"${GUAC_SERVER_VERSION}"/mysql/schema cat *.sql | mariadb -u root ${MARIADB_DB_NAME} { echo "mysql-hostname: 127.0.0.1" echo "mysql-port: 3306" echo "mysql-database: $MARIADB_DB_NAME" echo "mysql-username: $MARIADB_DB_USER" echo "mysql-password: $MARIADB_DB_PASS" } >>/etc/guacamole/guacamole.properties rm -rf ~/guacamole-auth-jdbc-"$GUAC_SERVER_VERSION"{,.tar.gz} msg_ok "Imported Database Schema" msg_info "Setup Service" cat </etc/guacamole/guacd.conf [server] bind_host = 127.0.0.1 bind_port = 4822 EOF JAVA_HOME=$(update-alternatives --query javadoc | grep Value: | head -n1 | sed 's/Value: //' | sed 's@bin/javadoc$@@') cat </etc/systemd/system/tomcat.service [Unit] Description=Apache Tomcat Web Application Container After=network.target [Service] Type=forking Environment="JAVA_HOME=${JAVA_HOME}" Environment="CATALINA_PID=/opt/apache-guacamole/tomcat9/temp/tomcat.pid" Environment="CATALINA_HOME=/opt/apache-guacamole/tomcat9/" Environment="CATALINA_BASE=/opt/apache-guacamole/tomcat9/" Environment="CATALINA_OPTS=-Xms512M -Xmx1024M -server -XX:+UseParallelGC" Environment="JAVA_OPTS=-Djava.awt.headless=true -Djava.security.egd=file:/dev/./urandom" ExecStart=/opt/apache-guacamole/tomcat9/bin/startup.sh ExecStop=/opt/apache-guacamole/tomcat9/bin/shutdown.sh User=tomcat Group=tomcat UMask=0007 RestartSec=10 Restart=always [Install] WantedBy=multi-user.target EOF cat </etc/systemd/system/guacd.service [Unit] Description=Guacamole Proxy Daemon (guacd) After=mysql.service tomcat.service Requires=mysql.service tomcat.service [Service] Type=forking ExecStart=/etc/init.d/guacd start ExecStop=/etc/init.d/guacd stop ExecReload=/etc/init.d/guacd restart PIDFile=/var/run/guacd.pid [Install] WantedBy=multi-user.target EOF systemctl enable -q --now mysql tomcat guacd msg_ok "Setup Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/apache-tika-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: Andy Grunwald (andygrunwald) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/apache/tika/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt-get install -y \ software-properties-common \ gdal-bin \ tesseract-ocr \ tesseract-ocr-eng \ tesseract-ocr-ita \ tesseract-ocr-fra \ tesseract-ocr-spa \ tesseract-ocr-deu $STD echo ttf-mscorefonts-installer msttcorefonts/accepted-mscorefonts-eula select true | debconf-set-selections echo "deb http://deb.debian.org/debian bookworm contrib non-free" > /etc/apt/sources.list.d/contrib.list $STD apt-get update $STD apt-get install -y \ xfonts-utils \ fonts-freefont-ttf \ fonts-liberation \ ttf-mscorefonts-installer \ cabextract msg_ok "Installed Dependencies" msg_info "Setup OpenJDK" $STD apt-get install -y \ openjdk-17-jre-headless msg_ok "Setup OpenJDK" msg_info "Installing Apache Tika" mkdir -p /opt/apache-tika cd /opt/apache-tika RELEASE="$(curl -fsSL https://dlcdn.apache.org/tika/ | grep -oP '(?<=href=")[0-9]+\.[0-9]+\.[0-9]+(?=/")' | sort -V | tail -n1)" curl -fsSL "https://dlcdn.apache.org/tika/${RELEASE}/tika-server-standard-${RELEASE}.jar" -o tika-server-standard-${RELEASE}.jar mv tika-server-standard-${RELEASE}.jar tika-server-standard.jar echo "${RELEASE}" >/opt/${APPLICATION}_version.txt msg_ok "Installed Apache Tika" msg_info "Creating Service" cat </etc/systemd/system/apache-tika.service [Unit] Description=Apache Tika Documentation=https://tika.apache.org/ After=syslog.target network.target [Service] User=root Restart=always Type=simple ExecStart=java -jar /opt/apache-tika/tika-server-standard.jar --host 0.0.0.0 --port 9998 ExecReload=/bin/kill -HUP \$MAINPID [Install] WantedBy=multi-user.target EOF systemctl enable -q --now apache-tika msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/apache-tomcat-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://tomcat.apache.org/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os read -r -p "${TAB3}Which Tomcat version would you like to install? (9, 10.1, 11): " version case $version in 9) TOMCAT_VERSION="9" echo "Which LTS Java version would you like to use? (8, 11, 17, 21): " read -r jdk_version case $jdk_version in 8 | 11 | 17 | 21) JAVA_VERSION="$jdk_version" setup_java ;; *) msg_error "Invalid JDK version selected. Please enter 8, 11, 17 or 21." exit 64 ;; esac ;; 10 | 10.1) TOMCAT_VERSION="10" echo "Which LTS Java version would you like to use? (11, 17, 21): " read -r jdk_version case $jdk_version in 11 | 17 | 21) JAVA_VERSION="$jdk_version" setup_java ;; *) msg_error "Invalid JDK version selected. Please enter 11, 17 or 21." exit 64 ;; esac ;; 11) TOMCAT_VERSION="11" echo "Which LTS Java version would you like to use? (17, 21): " read -r jdk_version case $jdk_version in 17 | 21) JAVA_VERSION="$jdk_version" setup_java ;; *) msg_error "Invalid JDK version selected. Please enter 17 or 21." exit 64 ;; esac ;; *) msg_error "Invalid Tomcat version selected. Please enter 9, 10.1 or 11." exit 64 ;; esac msg_info "Installing Tomcat $TOMCAT_VERSION" LATEST_VERSION=$(curl -fsSL "https://dlcdn.apache.org/tomcat/tomcat-$TOMCAT_VERSION/" | grep -oP 'v[0-9]+\.[0-9]+\.[0-9]+(-M[0-9]+)?/' | sort -V | tail -n 1 | sed 's/\/$//; s/v//') TOMCAT_URL="https://dlcdn.apache.org/tomcat/tomcat-$TOMCAT_VERSION/v$LATEST_VERSION/bin/apache-tomcat-$LATEST_VERSION.tar.gz" curl -fsSL "$TOMCAT_URL" -o "/tmp/tomcat.tar.gz" mkdir -p /opt/tomcat-$TOMCAT_VERSION tar --strip-components=1 -xzf /tmp/tomcat.tar.gz -C /opt/tomcat-$TOMCAT_VERSION chown -R root:root /opt/tomcat-$TOMCAT_VERSION rm -f /tmp/tomcat.tar.gz cat </etc/systemd/system/tomcat.service [Unit] Description=Apache Tomcat Web Application Container After=network.target [Service] Type=forking User=$(whoami) Group=$(whoami) Environment=JAVA_HOME=/usr/lib/jvm/temurin-${jdk_version}-jdk-arm64 Environment=CATALINA_HOME=/opt/tomcat-$TOMCAT_VERSION Environment=CATALINA_BASE=/opt/tomcat-$TOMCAT_VERSION Environment=CATALINA_PID=/opt/tomcat-$TOMCAT_VERSION/temp/tomcat.pid ExecStart=/opt/tomcat-$TOMCAT_VERSION/bin/catalina.sh start ExecStop=/opt/tomcat-$TOMCAT_VERSION/bin/catalina.sh stop PIDFile=/opt/tomcat-$TOMCAT_VERSION/temp/tomcat.pid SuccessExitStatus=143 Restart=on-abnormal [Install] WantedBy=multi-user.target EOF systemctl enable -q --now tomcat msg_ok "Tomcat $LATEST_VERSION installed and started" motd_ssh customize cleanup_lxc ================================================ FILE: install/apprise-api-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: SystemIdleProcess # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/caronc/apprise-api source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ nginx \ git msg_ok "Installed Dependencies" PYTHON_VERSION="3.12" setup_uv fetch_and_deploy_gh_release "apprise" "caronc/apprise-api" "tarball" msg_info "Setup Apprise-API" cd /opt/apprise cp ./requirements.txt /etc/requirements.txt $STD uv pip install -r requirements.txt gunicorn supervisor --system cp -fr apprise_api/static /usr/share/nginx/html/s/ mv apprise_api/ webapp touch /etc/nginx/server-override.conf touch /etc/nginx/location-override.conf mkdir -p /config/store /attach /plugin /tmp/apprise /opt/apprise/logs chmod 1777 /tmp/apprise && chmod 777 /config /config/store /attach /plugin /opt/apprise/logs sed -i \ -e '/[[]program:nginx]/,/^[[]/ s|stdout_logfile=/dev/stdout|stdout_logfile=/opt/apprise/logs/nginx.log|' \ -e '/[[]program:nginx]/,/^[[]/ s|stderr_logfile=/dev/stderr|stderr_logfile=/opt/apprise/logs/nginx_error.log|' \ -e '/[[]program:gunicorn]/,/^[[]/ s|stdout_logfile=/dev/stdout|stdout_logfile=/opt/apprise/logs/gunicorn.log|' \ -e '/[[]program:gunicorn]/,/^[[]/ s|stderr_logfile=/dev/stderr|stderr_logfile=/opt/apprise/logs/gunicorn_error.log|' \ -e '/[[]supervisord]/,/^[[]/ s|logfile=/dev/null|logfile=/opt/apprise/logs/supervisor.log|' \ -e 's|_maxbytes=0|_maxbytes=10485760|g' \ /opt/apprise/webapp/etc/supervisord.conf msg_ok "Setup Apprise-API" msg_info "Creating Service" cat </etc/systemd/system/apprise-api.service [Unit] Description=Apprise-API Service After=network-online.target [Service] Type=simple WorkingDirectory=/opt/apprise ExecStart=/opt/apprise/webapp/supervisord-startup Restart=always RestartSec=30 [Install] WantedBy=multi-user.target EOF systemctl enable -q --now apprise-api msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/apt-cacher-ng-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://wiki.debian.org/AptCacherNg source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Apt-Cacher NG" DEBIAN_FRONTEND=noninteractive $STD apt -o Dpkg::Options::="--force-confold" install -y apt-cacher-ng avahi-daemon sed -i 's/# PassThroughPattern: .* # this would allow CONNECT to everything/PassThroughPattern: .*/' /etc/apt-cacher-ng/acng.conf cat </etc/apt/apt.conf.d/00aptproxy.conf Acquire::http::Proxy "http://localhost:3142"; EOF systemctl enable -q --now apt-cacher-ng msg_ok "Installed Apt-Cacher NG" motd_ssh customize cleanup_lxc ================================================ FILE: install/archivebox-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://archivebox.io/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt-get install -y \ git \ expect \ libssl-dev \ libldap2-dev \ libsasl2-dev \ procps \ dnsutils \ ripgrep \ chromium msg_ok "Installed Dependencies" msg_info "Installing Python Dependencies" $STD apt-get install -y \ python3-ldap \ python3-msgpack \ python3-regex rm -rf /usr/lib/python3.*/EXTERNALLY-MANAGED msg_ok "Installed Python Dependencies" NODE_VERSION="22" NODE_MODULE="@postlight/parser@latest,single-file-cli@latest" setup_nodejs PYTHON_VERSION="3.13" setup_uv msg_info "Installing Playwright" $STD uv pip install playwright --system $STD playwright install-deps chromium msg_ok "Installed Playwright" msg_info "Installing ArchiveBox" mkdir -p /opt/archivebox/{data,.npm,.cache,.local} $STD adduser --system --shell /bin/bash --gecos 'Archive Box User' --group --disabled-password --home /home/archivebox archivebox chown -R archivebox:archivebox /opt/archivebox/{data,.npm,.cache,.local} chmod -R 755 /opt/archivebox/data $STD uv pip install archivebox --system cd /opt/archivebox/data expect </etc/systemd/system/archivebox.service [Unit] Description=ArchiveBox Server After=network.target [Service] User=archivebox WorkingDirectory=/opt/archivebox/data ExecStart=/usr/local/bin/archivebox server 0.0.0.0:8000 Restart=always [Install] WantedBy=multi-user.target EOF systemctl enable -q --now archivebox msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/argus-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://release-argus.io/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os fetch_and_deploy_gh_release "Argus" "release-argus/Argus" "singlefile" "latest" "/opt/argus" "Argus*linux-arm64" msg_info "Setup Argus Config" cat </opt/argus/config.yml settings: log: level: INFO timestamps: false data: database_file: data/argus.db web: listen_host: 0.0.0.0 listen_port: 8080 route_prefix: / defaults: service: options: interval: 30m semantic_versioning: true latest_version: allow_invalid_certs: false use_prerelease: false dashboard: auto_approve: true webhook: desired_status_code: 201 service: release-argus/argus: latest_version: type: github url: release-argus/argus dashboard: icon: https://raw.githubusercontent.com/release-argus/Argus/master/web/ui/react-app/public/favicon.svg icon_link_to: https://release-argus.io web_url: https://github.com/release-argus/Argus/blob/master/CHANGELOG.md community-scripts/ProxmoxVE: latest_version: type: github url: community-scripts/ProxmoxVE use_prerelease: false dashboard: icon: https://raw.githubusercontent.com/community-scripts/ProxmoxVE/refs/heads/main/misc/images/logo.png icon_link_to: https://community-scripts.org/ web_url: https://github.com/community-scripts/ProxmoxVE/releases EOF msg_ok "Setup Config" msg_info "Creating Service" cat </etc/systemd/system/argus.service [Unit] Description=Argus After=network.target [Service] Type=simple WorkingDirectory=/opt/argus ExecStart=/opt/argus/Argus Restart=on-failure RestartSec=5 [Install] WantedBy=multi-user.target EOF systemctl enable -q --now argus msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/aria2-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://aria2.github.io/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Aria2" $STD apt install -y aria2 msg_ok "Installed Aria2" read -r -p "${TAB3}Would you like to add AriaNG? " prompt if [[ ${prompt,,} =~ ^(y|yes)$ ]]; then msg_info "Installing Dependencies" $STD apt-get install -y nginx $STD apt-get install -y unzip systemctl disable -q --now nginx rm /etc/nginx/sites-enabled/* msg_ok "Installed Dependencies" fetch_and_deploy_gh_release "ariang" "mayswind/ariang" "prebuild" "latest" "/var/www" "AriaNg-*-AllInOne.zip" msg_info "Configure nginx" cat </etc/nginx/conf.d/ariang.conf server { listen 6880 default_server; listen [::]:6880 default_server; server_name _; root /var/www; index index.html; location / { try_files \$uri \$uri/ =404; } } EOF cp /lib/systemd/system/nginx.service /lib/systemd/system/ariang.service systemctl enable -q --now ariang msg_ok "Configured nginx" fi msg_info "Creating Service" mkdir -p /root/downloads rpc_secret=$(openssl rand -base64 8) echo "rpc-secret: $rpc_secret" >>~/rpc.secret cat </root/aria2.daemon dir=/root/downloads file-allocation=falloc max-connection-per-server=4 max-concurrent-downloads=2 max-overall-download-limit=0 min-split-size=25M rpc-allow-origin-all=true rpc-secret=${rpc_secret} input-file=/var/tmp/aria2c.session save-session=/var/tmp/aria2c.session EOF cat </etc/systemd/system/aria2.service [Unit] Description=Aria2c download manager After=network.target [Service] Type=simple User=root Group=root ExecStartPre=/usr/bin/env touch /var/tmp/aria2c.session ExecStart=/usr/bin/aria2c --console-log-level=warn --enable-rpc --rpc-listen-all --conf-path=/root/aria2.daemon TimeoutStopSec=20 Restart=on-failure [Install] WantedBy=multi-user.target EOF systemctl enable -q --now aria2 msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/asterisk-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: michelroegl-brunner # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://asterisk.org source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ libsrtp2-dev \ build-essential \ libedit-dev \ uuid-dev \ libjansson-dev \ libxml2-dev \ libsqlite3-dev msg_ok "Installed Dependencies" msg_info "Fetching Asterisk Versions" ASTERISK_LIST=$(curl -fsSL https://downloads.asterisk.org/pub/telephony/asterisk/ \ | grep -oE 'asterisk-[0-9]+\.[0-9]+\.[0-9]+\.tar\.gz' \ | sed 's/asterisk-//' \ | sed 's/\.tar\.gz//' \ | sort -V) # LTS: Major 20, 22, 24, 26 LTS_VERSION=$(echo "$ASTERISK_LIST" | grep -E '^2(0|2|4|6)\.' | tail -n1 || true) # Standard: Major 21, 23, 25, 27 STD_VERSION=$(echo "$ASTERISK_LIST" | grep -E '^2(1|3|5|7)\.' | tail -n1 || true) CERT_VERSION=$(curl -fsSL https://downloads.asterisk.org/pub/telephony/certified-asterisk/ \ | grep -oE 'asterisk-certified-[0-9]+\.[0-9]+-cert[0-9]+\.tar\.gz' \ | sed -E 's/asterisk-certified-//' \ | sed -E 's/\.tar\.gz//' \ | sort -V | tail -n1 || true) msg_ok "Fetched Versions" cat </etc/systemd/system/audiobookshelf_client.service [Unit] Description=Audiobookshelf Client Service After=network.target [Service] Type=simple WorkingDirectory=/opt/audiobookshelf/client ExecStart=/usr/bin/npm run dev Restart=on-failure [Install] WantedBy=multi-user.target EOF cat </etc/systemd/system/audiobookshelf_server.service [Unit] Description=Audiobookshelf Server Service After=network.target [Service] Type=simple WorkingDirectory=/opt/audiobookshelf/ ExecStart=/usr/bin/npm run dev Restart=on-failure [Install] WantedBy=multi-user.target EOF systemctl enable -q --now audiobookshelf_server.service systemctl enable -q --now audiobookshelf_client.service msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/authelia-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: thost96 (thost96) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.authelia.com/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os fetch_and_deploy_gh_release "authelia" "authelia/authelia" "binary" MAX_ATTEMPTS=3 attempt=0 while true; do attempt=$((attempt + 1)) read -rp "${TAB3}Enter your domain or IP (ex. example.com or 192.168.1.100): " DOMAIN if [[ -z "$DOMAIN" ]]; then if ((attempt >= MAX_ATTEMPTS)); then DOMAIN="${LOCAL_IP:-localhost}" msg_warn "Using fallback: $DOMAIN" break fi msg_warn "Domain cannot be empty! (Attempt $attempt/$MAX_ATTEMPTS)" elif [[ "$DOMAIN" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then valid_ip=true IFS='.' read -ra octets <<<"$DOMAIN" for octet in "${octets[@]}"; do if ((octet > 255)); then valid_ip=false break fi done if $valid_ip; then break else msg_warn "Invalid IP address!" fi elif [[ "$DOMAIN" =~ ^[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(\.[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*\.[a-zA-Z]{2,}$ ]]; then break else msg_warn "Invalid domain format!" fi done msg_info "Setting Authelia up" touch /etc/authelia/emails.txt JWT_SECRET=$(openssl rand -hex 64) SESSION_SECRET=$(openssl rand -hex 64) STORAGE_KEY=$(openssl rand -hex 64) if [[ "$DOMAIN" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then AUTHELIA_URL="https://${DOMAIN}:9091" else AUTHELIA_URL="https://auth.${DOMAIN}" fi echo "$AUTHELIA_URL" >/etc/authelia/.authelia_url cat </etc/authelia/users.yml users: authelia: disabled: false displayname: "Authelia Admin" password: "\$argon2id\$v=19\$m=65536,t=3,p=4\$ZBopMzXrzhHXPEZxRDVT2w\$SxWm96DwhOsZyn34DLocwQEIb4kCDsk632PuiMdZnig" groups: [] EOF cat </etc/authelia/configuration.yml authentication_backend: file: path: /etc/authelia/users.yml access_control: default_policy: one_factor session: secret: "${SESSION_SECRET}" name: 'authelia_session' same_site: 'lax' inactivity: '5m' expiration: '1h' remember_me: '1M' cookies: - domain: "${DOMAIN}" authelia_url: "${AUTHELIA_URL}" storage: encryption_key: "${STORAGE_KEY}" local: path: /etc/authelia/db.sqlite identity_validation: reset_password: jwt_secret: "${JWT_SECRET}" jwt_lifespan: '5 minutes' jwt_algorithm: 'HS256' notifier: filesystem: filename: /etc/authelia/emails.txt EOF touch /etc/authelia/emails.txt chown -R authelia:authelia /etc/authelia systemctl enable -q --now authelia msg_ok "Authelia Setup completed" motd_ssh customize cleanup_lxc ================================================ FILE: install/autobrr-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://autobrr.com/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os fetch_and_deploy_gh_release "autobrr" "autobrr/autobrr" "prebuild" "latest" "/usr/local/bin" "autobrr_*_linux_arm64.tar.gz" msg_info "Configuring Autobrr" mkdir -p /root/.config/autobrr cat <>/root/.config/autobrr/config.toml # https://autobrr.com/configuration/autobrr host = "0.0.0.0" port = 7474 logLevel = "DEBUG" sessionSecret = "$(openssl rand -base64 24)" EOF msg_ok "Configured Autobrr" msg_info "Creating Service" cat </etc/systemd/system/autobrr.service [Unit] Description=autobrr service After=syslog.target network-online.target [Service] Type=simple User=root Group=root ExecStart=/usr/local/bin/autobrr --config=/root/.config/autobrr/ [Install] WantedBy=multi-user.target EOF systemctl enable --now -q autobrr msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/autocaliweb-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: vhsdream # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://codeberg.org/gelbphoenix/autocaliweb source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing dependencies" $STD apt install -y --no-install-recommends \ python3-dev \ sqlite3 \ build-essential \ libldap2-dev \ libssl-dev \ libsasl2-dev \ imagemagick \ ghostscript \ libmagic1 \ libxi6 \ libxslt1.1 \ libxtst6 \ libxrandr2 \ libxkbfile1 \ libxcomposite1 \ libopengl0 \ libnss3 \ libxkbcommon0 \ libegl1 \ libxdamage1 \ libgl1 \ libglx-mesa0 \ xz-utils \ xdg-utils \ inotify-tools \ binutils \ unrar-free \ zip msg_ok "Installed dependencies" fetch_and_deploy_gh_release "kepubify" "pgaskin/kepubify" "singlefile" "latest" "/usr/bin" "kepubify-linux-arm64" KEPUB_VERSION="$(/usr/bin/kepubify --version | awk '{print $2}')" fetch_and_deploy_gh_release "calibre" "kovidgoyal/calibre" "prebuild" "latest" "/opt/calibre" "calibre-*-arm64.txz" msg_info "Installing Calibre" $STD /opt/calibre/calibre_postinstall CALIBRE_VERSION=$(cat ~/.calibre) msg_ok "Installed Calibre" setup_uv fetch_and_deploy_codeberg_release "autocaliweb" "gelbphoenix/autocaliweb" "tarball" "latest" "/opt/autocaliweb" msg_info "Configuring Autocaliweb" INSTALL_DIR="/opt/autocaliweb" CONFIG_DIR="/etc/autocaliweb" CALIBRE_LIB_DIR="/opt/calibre-library" INGEST_DIR="/opt/acw-book-ingest" SERVICE_USER="acw" SERVICE_GROUP="acw" SCRIPTS_DIR="${INSTALL_DIR}/scripts" export VIRTUAL_ENV="${INSTALL_DIR}/venv" mkdir -p "$CONFIG_DIR"/{.config/calibre/plugins,log_archive,.acw_conversion_tmp} mkdir -p "$CONFIG_DIR"/processed_books/{converted,imported,failed,fixed_originals} mkdir -p "$INSTALL_DIR"/{metadata_change_logs,metadata_temp} mkdir -p {"$CALIBRE_LIB_DIR","$INGEST_DIR"} echo "$CALIBRE_VERSION" >"$INSTALL_DIR"/CALIBRE_RELEASE echo "${KEPUB_VERSION#v}" >"$INSTALL_DIR"/KEPUBIFY_RELEASE sed 's/^/v/' ~/.autocaliweb >"$INSTALL_DIR"/ACW_RELEASE cd "$INSTALL_DIR" $STD uv venv --clear "$VIRTUAL_ENV" $STD uv sync --all-extras --active cat <./dirs.json { "ingest_folder": "$INGEST_DIR", "calibre_library_dir": "$CALIBRE_LIB_DIR", "tmp_conversion_dir": "$CONFIG_DIR/.acw_conversion_tmp" } EOF useradd -s /usr/sbin/nologin -d "$CONFIG_DIR" -M "$SERVICE_USER" ln -sf "$CONFIG_DIR"/.config/calibre/plugins "$CONFIG_DIR"/calibre_plugins cat <"$INSTALL_DIR"/.env ACW_INSTALL_DIR=$INSTALL_DIR ACW_CONFIG_DIR=$CONFIG_DIR ACW_USER=$SERVICE_USER ACW_GROUP=$SERVICE_GROUP LIBRARY_DIR=$CALIBRE_LIB_DIR EOF msg_ok "Configured Autocaliweb" msg_info "Creating ACWSync Plugin for KOReader" cd "$INSTALL_DIR"/koreader/plugins PLUGIN_DIGEST="$(find acwsync.koplugin -type f -name "*.lua" -o -name "*.json" | sort | xargs sha256sum | sha256sum | cut -d' ' -f1)" echo "Plugin files digest: $PLUGIN_DIGEST" >acwsync.koplugin/${PLUGIN_DIGEST}.digest echo "Build date: $(date)" >>acwsync.koplugin/${PLUGIN_DIGEST}.digest echo "Files included:" >>acwsync.koplugin/${PLUGIN_DIGEST}.digest $STD zip -r koplugin.zip acwsync.koplugin/ cp -r koplugin.zip "$INSTALL_DIR"/cps/static msg_ok "Created ACWSync Plugin" msg_info "Initializing databases" KEPUBIFY_PATH=$(command -v kepubify 2>/dev/null || echo "/usr/bin/kepubify") EBOOK_CONVERT_PATH=$(command -v ebook-convert 2>/dev/null || echo "/usr/bin/ebook-convert") CALIBRE_BIN_DIR=$(dirname "$EBOOK_CONVERT_PATH") curl -fsSL https://codeberg.org/gelbphoenix/autocaliweb/raw/branch/main/library/metadata.db -o "$CALIBRE_LIB_DIR"/metadata.db curl -fsSL https://codeberg.org/gelbphoenix/autocaliweb/raw/branch/main/library/app.db -o "$CONFIG_DIR"/app.db sqlite3 "$CONFIG_DIR/app.db" <"$SCRIPTS_DIR"/ingest_watcher.sh #!/bin/bash INSTALL_PATH="$INSTALL_DIR" WATCH_FOLDER=\$(grep -o '"ingest_folder": "[^"]*' \${INSTALL_PATH}/dirs.json | grep -o '[^"]*\$') echo "[acw-ingest-service] Watching folder: \$WATCH_FOLDER" # Monitor the folder for new files /usr/bin/inotifywait -m -r --format="%e %w%f" -e close_write -e moved_to "\$WATCH_FOLDER" | while read -r events filepath ; do echo "[acw-ingest-service] New files detected - \$filepath - Starting Ingest Processor..." # Use the Python interpreter from the virtual environment \${INSTALL_PATH}/venv/bin/python \${INSTALL_PATH}/scripts/ingest_processor.py "\$filepath" done EOF # auto-zipper cat <"$SCRIPTS_DIR"/auto_zipper_wrapper.sh #!/bin/bash # Source virtual environment source ${INSTALL_DIR}/venv/bin/activate WAKEUP="23:59" while true; do # Replace expr with modern Bash arithmetic (safer and less prone to parsing issues) # fix: expr: non-integer argument and sleep: missing operand SECS=\$(( \$(date -d "\$WAKEUP" +%s) - \$(date -d "now" +%s) )) if [[ \$SECS -lt 0 ]]; then SECS=\$(( \$(date -d "tomorrow \$WAKEUP" +%s) - \$(date -d "now" +%s) )) fi echo "[acw-auto-zipper] Next run in \$SECS seconds." sleep \$SECS & wait \$! # Use virtual environment python python ${SCRIPTS_DIR}/auto_zip.py if [[ \$? == 1 ]]; then echo "[acw-auto-zipper] Error occurred during script initialisation." elif [[ \$? == 2 ]]; then echo "[acw-auto-zipper] Error occurred while zipping today's files." elif [[ \$? == 3 ]]; then echo "[acw-auto-zipper] Error occurred while trying to remove zipped files." fi sleep 60 done EOF # metadata change detector cat <"$SCRIPTS_DIR"/metadata_change_detector_wrapper.sh #!/bin/bash # metadata_change_detector_wrapper.sh - Wrapper for periodic metadata enforcement # Source virtual environment source ${INSTALL_DIR}/venv/bin/activate # Configuration CHECK_INTERVAL=300 # Check every 5 minutes (300 seconds) METADATA_LOGS_DIR="${INSTALL_DIR}/metadata_change_logs" echo "[metadata-change-detector] Starting metadata change detector service..." echo "[metadata-change-detector] Checking for changes every \$CHECK_INTERVAL seconds" while true; do # Check if there are any log files to process if [ -d "\$METADATA_LOGS_DIR" ] && [ "\$(ls -A \$METADATA_LOGS_DIR 2>/dev/null)" ]; then echo "[metadata-change-detector] Found metadata change logs, processing..." # Process each log file for log_file in "\$METADATA_LOGS_DIR"/*.json; do if [ -f "\$log_file" ]; then log_name=\$(basename "\$log_file") echo "[metadata-change-detector] Processing log: \$log_name" # Call cover_enforcer.py with the log file ${INSTALL_DIR}/venv/bin/python ${SCRIPTS_DIR}/cover_enforcer.py --log "\$log_name" if [ \$? -eq 0 ]; then echo "[metadata-change-detector] Successfully processed \$log_name" else echo "[metadata-change-detector] Error processing \$log_name" fi fi done else echo "[metadata-change-detector] No metadata changes detected" fi echo "[metadata-change-detector] Sleeping for \$CHECK_INTERVAL seconds..." sleep \$CHECK_INTERVAL done EOF chmod +x "$SCRIPTS_DIR"/{ingest_watcher.sh,auto_zipper_wrapper.sh,metadata_change_detector_wrapper.sh} chown -R "$SERVICE_USER":"$SERVICE_GROUP" {"$INSTALL_DIR","$CONFIG_DIR","$INGEST_DIR","$CALIBRE_LIB_DIR"} cat </etc/systemd/system/autocaliweb.service [Unit] Description=Autocaliweb After=network.target Wants=network-online.target After=network-online.target [Service] Type=simple User=$SERVICE_USER Group=$SERVICE_GROUP WorkingDirectory=$INSTALL_DIR Environment=PATH=$INSTALL_DIR/venv/bin:/usr/bin:/bin Environment=PYTHONPATH=$SCRIPTS_DIR:$INSTALL_DIR Environment=PYTHONDONTWRITEBYTECODE=1 Environment=PYTHONUNBUFFERED=1 Environment=CALIBRE_DBPATH=$CONFIG_DIR EnvironmentFile=$INSTALL_DIR/.env ExecStart=$INSTALL_DIR/venv/bin/python $INSTALL_DIR/cps.py -p $CONFIG_DIR/app.db Restart=always RestartSec=10 StandardOutput=journal StandardError=journal [Install] WantedBy=multi-user.target EOF cat </etc/systemd/system/acw-ingest-service.service [Unit] Description=Autocaliweb Ingest Processor Service After=autocaliweb.service Requires=autocaliweb.service [Service] User=${SERVICE_USER} Group=${SERVICE_GROUP} WorkingDirectory=${INSTALL_DIR} Environment=CALIBRE_DBPATH=${CONFIG_DIR} Environment=HOME=${CONFIG_DIR} ExecStart=/bin/bash ${SCRIPTS_DIR}/ingest_watcher.sh Restart=always StandardOutput=journal StandardError=journal [Install] WantedBy=multi-user.target EOF cat </etc/systemd/system/acw-auto-zipper.service [Unit] Description=Autocaliweb Auto Zipper Service After=network.target [Service] User=${SERVICE_USER} Group=${SERVICE_GROUP} WorkingDirectory=${INSTALL_DIR} Environment=CALIBRE_DBPATH=${CONFIG_DIR} ExecStart=${SCRIPTS_DIR}/auto_zipper_wrapper.sh Restart=always StandardOutput=journal StandardError=journal [Install] WantedBy=multi-user.target EOF cat </etc/systemd/system/metadata-change-detector.service [Unit] Description=Autocaliweb Metadata Change Detector After=network.target [Service] User=${SERVICE_USER} Group=${SERVICE_GROUP} WorkingDirectory=${INSTALL_DIR} ExecStart=/bin/bash ${SCRIPTS_DIR}/metadata_change_detector_wrapper.sh Restart=always StandardOutput=journal StandardError=journal Environment=CALIBRE_DBPATH=${CONFIG_DIR} Environment=HOME=${CONFIG_DIR} [Install] WantedBy=multi-user.target EOF systemctl -q enable --now autocaliweb acw-ingest-service acw-auto-zipper metadata-change-detector msg_ok "Created scripts and service files" motd_ssh customize cleanup_lxc ================================================ FILE: install/babybuddy-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/babybuddy/babybuddy source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt-get install -y \ uwsgi \ uwsgi-plugin-python3 \ libopenjp2-7-dev \ libpq-dev \ nginx \ python3 msg_ok "Installed Dependencies" setup_uv fetch_and_deploy_gh_release "babybuddy" "babybuddy/babybuddy" "tarball" msg_info "Installing Babybuddy" mkdir -p /opt/data cd /opt/babybuddy $STD uv venv --clear .venv $STD source .venv/bin/activate $STD uv pip install -r requirements.txt cp babybuddy/settings/production.example.py babybuddy/settings/production.py SECRET_KEY=$(openssl rand -base64 32 | tr -dc 'a-zA-Z0-9' | cut -c1-32) ALLOWED_HOSTS=$(hostname -I | tr ' ' ',' | sed 's/,$//')",127.0.0.1,localhost" sed -i \ -e "s/^SECRET_KEY = \"\"/SECRET_KEY = \"$SECRET_KEY\"/" \ -e "s/^ALLOWED_HOSTS = \[\"\"\]/ALLOWED_HOSTS = \[$(echo \"$ALLOWED_HOSTS\" | sed 's/,/\",\"/g')\]/" \ babybuddy/settings/production.py export DJANGO_SETTINGS_MODULE=babybuddy.settings.production $STD python manage.py migrate chown -R www-data:www-data /opt/data chmod 640 /opt/data/db.sqlite3 chmod 750 /opt/data msg_ok "Installed Babybuddy" msg_info "Configuring uWSGI" cat </etc/uwsgi/apps-available/babybuddy.ini [uwsgi] plugins = python3 project = babybuddy base_dir = /opt/babybuddy chdir = %(base_dir) virtualenv = %(base_dir)/.venv module = %(project).wsgi:application env = DJANGO_SETTINGS_MODULE=%(project).settings.production master = True vacuum = True socket = /var/run/uwsgi/app/babybuddy/socket chmod-socket = 660 uid = www-data gid = www-data EOF ln -sf /etc/uwsgi/apps-available/babybuddy.ini /etc/uwsgi/apps-enabled/babybuddy.ini service uwsgi restart msg_ok "Configured uWSGI" msg_info "Configuring NGINX" cat </etc/nginx/sites-available/babybuddy upstream babybuddy { server unix:///var/run/uwsgi/app/babybuddy/socket; } server { listen 80; server_name _; location / { uwsgi_pass babybuddy; include uwsgi_params; } location /media { alias /opt/data/media; } } EOF ln -sf /etc/nginx/sites-available/babybuddy /etc/nginx/sites-enabled/babybuddy rm /etc/nginx/sites-enabled/default systemctl enable -q --now nginx service nginx reload msg_ok "Configured NGINX" motd_ssh customize cleanup_lxc ================================================ FILE: install/backrest-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: ksad (enirys31) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://garethgeorge.github.io/backrest/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os fetch_and_deploy_gh_release "backrest" "garethgeorge/backrest" "prebuild" "latest" "/opt/backrest/bin" "backrest_Linux_arm64.tar.gz" msg_info "Creating Service" cat </opt/backrest/.env BACKREST_PORT=9898 BACKREST_CONFIG=/opt/backrest/config/config.json BACKREST_DATA=/opt/backrest/data XDG_CACHE_HOME=/opt/backrest/cache EOF cat </etc/systemd/system/backrest.service [Unit] Description=Backrest After=network.target [Service] Type=simple ExecStart=/opt/backrest/bin/backrest EnvironmentFile=/opt/backrest/.env [Install] WantedBy=multi-user.target EOF systemctl enable -q --now backrest msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/baikal-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: bvdberg01 # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://sabre.io/baikal/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y git msg_ok "Installed Dependencies" PG_VERSION="16" setup_postgresql PHP_APACHE="YES" PHP_VERSION="8.3" setup_php setup_composer fetch_and_deploy_gh_release "baikal" "sabre-io/Baikal" "tarball" PG_DB_NAME="baikal_db" PG_DB_USER="baikal_user" PG_DB_PASS="$(openssl rand -base64 12)" setup_postgresql_db msg_info "Configuring Baikal" cd /opt/baikal $STD composer install cat </opt/baikal/config/baikal.yaml database: backend: pgsql pgsql_host: localhost pgsql_dbname: $PG_DB_NAME pgsql_username: $PG_DB_USER pgsql_password: $PG_DB_PASS EOF chown -R www-data:www-data /opt/baikal/ chmod -R 755 /opt/baikal/ msg_ok "Installed Baikal" msg_info "Creating Service" cat </etc/apache2/sites-available/baikal.conf ServerName baikal DocumentRoot /opt/baikal/html RewriteEngine on RewriteRule /.well-known/carddav /dav.php [R=308,L] RewriteRule /.well-known/caldav /dav.php [R=308,L] RewriteCond %{REQUEST_URI} ^/dav.php$ [NC] RewriteRule ^(.*)$ /dav.php/ [R=301,L] Options FollowSymLinks AllowOverride All Require all granted ExpiresActive Off ErrorLog /var/log/apache2/baikal_error.log CustomLog /var/log/apache2/baikal_access.log combined EOF $STD a2ensite baikal $STD a2enmod rewrite $STD a2dissite 000-default.conf $STD systemctl reload apache2 msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/bambuddy-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: Adrian-RDA # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/maziggy/bambuddy source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y libglib2.0-0 ffmpeg msg_ok "Installed Dependencies" PYTHON_VERSION="3.13" setup_uv NODE_VERSION="22" setup_nodejs fetch_and_deploy_gh_release "bambuddy" "maziggy/bambuddy" "tarball" "latest" "/opt/bambuddy" msg_info "Setting up Python Environment" cd /opt/bambuddy $STD uv venv $STD uv pip install -r requirements.txt msg_ok "Set up Python Environment" msg_info "Building Frontend" cd /opt/bambuddy/frontend $STD npm install $STD npm run build msg_ok "Built Frontend" msg_info "Configuring Bambuddy" mkdir -p /opt/bambuddy/data /opt/bambuddy/logs cat </opt/bambuddy/.env DEBUG=false LOG_LEVEL=INFO LOG_TO_FILE=true EOF msg_ok "Configured Bambuddy" msg_info "Creating Service" cat </etc/systemd/system/bambuddy.service [Unit] Description=Bambuddy - Bambu Lab Print Management Documentation=https://github.com/maziggy/bambuddy After=network.target [Service] Type=simple WorkingDirectory=/opt/bambuddy ExecStart=/opt/bambuddy/.venv/bin/uvicorn backend.app.main:app --host 0.0.0.0 --port 8000 Restart=on-failure RestartSec=5 [Install] WantedBy=multi-user.target EOF systemctl enable -q --now bambuddy msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/bar-assistant-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: bvdberg01 | CanbiZ # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/karlomikus/bar-assistant # Source: https://github.com/karlomikus/vue-salt-rim # Source: https://www.meilisearch.com/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ redis-server \ nginx \ lsb-release \ libvips msg_ok "Installed Dependencies" PHP_VERSION="8.4" PHP_FPM="YES" PHP_MODULE="pdo-sqlite" setup_php setup_composer NODE_VERSION="22" setup_nodejs setup_meilisearch fetch_and_deploy_gh_release "bar-assistant" "karlomikus/bar-assistant" "tarball" "latest" "/opt/bar-assistant" fetch_and_deploy_gh_release "vue-salt-rim" "karlomikus/vue-salt-rim" "tarball" "latest" "/opt/vue-salt-rim" msg_info "Configuring PHP" PHPVER=$(php -r 'echo PHP_MAJOR_VERSION . "." . PHP_MINOR_VERSION . "\n";') sed -i.bak -E 's/^\s*;?\s*ffi\.enable\s*=.*/ffi.enable=true/' /etc/php/${PHPVER}/fpm/php.ini $STD systemctl reload php${PHPVER}-fpm msg_info "configured PHP" msg_info "Installing Bar Assistant" cd /opt/bar-assistant cp /opt/bar-assistant/.env.dist /opt/bar-assistant/.env mkdir -p /opt/bar-assistant/resources/data curl -fsSL https://github.com/bar-assistant/data/archive/refs/heads/v5.tar.gz | tar -xz --strip-components=1 -C /opt/bar-assistant/resources/data sed -i -e "s|^APP_URL=|APP_URL=http://${LOCAL_IP}/bar/|" \ -e "s|^MEILISEARCH_HOST=|MEILISEARCH_HOST=http://127.0.0.1:7700|" \ -e "s|^MEILISEARCH_KEY=|MEILISEARCH_KEY=${MEILISEARCH_MASTER_KEY}|" \ -e "s|^MEILISEARCH_API_KEY=|MEILISEARCH_API_KEY=${MEILISEARCH_API_KEY}|" \ -e "s|^MEILISEARCH_API_KEY_UID=|MEILISEARCH_API_KEY_UID=${MEILISEARCH_API_KEY_UID}|" \ /opt/bar-assistant/.env $STD composer install --no-interaction $STD php artisan key:generate touch storage/bar-assistant/database.ba3.sqlite $STD php artisan migrate --force $STD php artisan storage:link $STD php artisan bar:setup-meilisearch $STD php artisan scout:sync-index-settings $STD php artisan config:cache $STD php artisan route:cache $STD php artisan event:cache mkdir /opt/bar-assistant/storage/bar-assistant/uploads/temp chown -R www-data:www-data /opt/bar-assistant msg_ok "Installed Bar Assistant" msg_info "Installing Salt Rim" cd /opt/vue-salt-rim cat </opt/vue-salt-rim/public/config.js window.srConfig = {} window.srConfig.API_URL = "http://${LOCAL_IP}/bar" window.srConfig.MEILISEARCH_URL = "http://${LOCAL_IP}/search" EOF $STD npm install $STD npm run build msg_ok "Installed Salt Rim" msg_info "Creating Service" cat </etc/nginx/sites-available/barassistant.conf server { listen 80 default_server; listen [::]:80 default_server; server_name _; location = /favicon.ico { access_log off; log_not_found off; } location = /robots.txt { access_log off; log_not_found off; } client_max_body_size 100M; location /bar/ { proxy_pass http://127.0.0.1:8080/; proxy_set_header Host \$host; proxy_set_header X-Real-IP \$remote_addr; proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto \$scheme; } location /search/ { proxy_pass http://127.0.0.1:7700/; } location / { proxy_pass http://127.0.0.1:8081/; proxy_set_header Host \$host; proxy_set_header X-Real-IP \$remote_addr; proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto \$scheme; } } server { listen 127.0.0.1:8080; server_name example.com; root /opt/bar-assistant/public; add_header X-Frame-Options "SAMEORIGIN"; add_header X-Content-Type-Options "nosniff"; index index.php; charset utf-8; location / { try_files \$uri \$uri/ /index.php?\$query_string; } location = /favicon.ico { access_log off; log_not_found off; } location = /robots.txt { access_log off; log_not_found off; } error_page 404 /index.php; location ~ ^/index\.php(/|$) { fastcgi_pass unix:/var/run/php/php$PHPVER-fpm.sock; fastcgi_param SCRIPT_FILENAME \$realpath_root\$fastcgi_script_name; include fastcgi_params; fastcgi_hide_header X-Powered-By; } location ~ /\.(?!well-known).* { deny all; } } server { listen 127.0.0.1:8081; server_name _; root /opt/vue-salt-rim/dist; location / { try_files \$uri \$uri/ /index.html; } } EOF ln -s /etc/nginx/sites-available/barassistant.conf /etc/nginx/sites-enabled/ rm -f /etc/nginx/sites-enabled/default $STD systemctl reload nginx msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/bazarr-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.bazarr.media/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y libicu76 msg_ok "Installed Dependencies" PYTHON_VERSION="3.12" setup_uv fetch_and_deploy_gh_release "bazarr" "morpheus65535/bazarr" "prebuild" "latest" "/opt/bazarr" "bazarr.zip" msg_info "Installing Bazarr" mkdir -p /var/lib/bazarr/ chmod 775 /opt/bazarr /var/lib/bazarr/ sed -i.bak 's/--only-binary=Pillow//g' /opt/bazarr/requirements.txt $STD uv venv --clear /opt/bazarr/venv --python 3.12 $STD uv pip install -r /opt/bazarr/requirements.txt --python /opt/bazarr/venv/bin/python3 msg_ok "Installed Bazarr" msg_info "Creating Service" cat </etc/systemd/system/bazarr.service [Unit] Description=Bazarr Daemon After=syslog.target network.target [Service] WorkingDirectory=/opt/bazarr/ UMask=0002 Restart=on-failure RestartSec=5 Type=simple ExecStart=/opt/bazarr/venv/bin/python3 /opt/bazarr/bazarr.py KillSignal=SIGINT TimeoutStopSec=20 SyslogIdentifier=bazarr [Install] WantedBy=multi-user.target EOF systemctl enable -q --now bazarr msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/bentopdf-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: vhsdream # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/alam00000/bentopdf source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ nginx \ openssl msg_ok "Installed Dependencies" NODE_VERSION="24" setup_nodejs fetch_and_deploy_gh_release "bentopdf" "alam00000/bentopdf" "tarball" "latest" "/opt/bentopdf" msg_info "Setup BentoPDF" cd /opt/bentopdf $STD npm ci --no-audit --no-fund cp ./.env.example ./.env.production export NODE_OPTIONS="--max-old-space-size=3072" export SIMPLE_MODE=true export VITE_USE_CDN=true $STD npm run build:all cat <<'EOF' >/opt/bentopdf/dist/config.json {} EOF msg_ok "Setup BentoPDF" msg_info "Creating Service" CERT_CN="$(hostname -I | awk '{print $1}')" $STD openssl req -x509 -nodes -newkey rsa:2048 -days 3650 \ -keyout /etc/ssl/private/bentopdf-selfsigned.key \ -out /etc/ssl/certs/bentopdf-selfsigned.crt \ -subj "/CN=${CERT_CN}" cat <<'EOF' >/etc/nginx/sites-available/bentopdf server { listen 8080; server_name _; return 301 https://$host:8443$request_uri; } server { listen 8443 ssl; server_name _; ssl_certificate /etc/ssl/certs/bentopdf-selfsigned.crt; ssl_certificate_key /etc/ssl/private/bentopdf-selfsigned.key; root /opt/bentopdf/dist; index index.html; # Required for LibreOffice WASM (Word/Excel/PowerPoint to PDF via SharedArrayBuffer) add_header Cross-Origin-Opener-Policy "same-origin" always; add_header Cross-Origin-Embedder-Policy "require-corp" always; add_header Cross-Origin-Resource-Policy "cross-origin" always; add_header X-Content-Type-Options "nosniff" always; add_header X-Frame-Options "SAMEORIGIN" always; gzip_static on; location ~* /libreoffice-wasm/soffice\.wasm\.gz$ { gzip off; types {} default_type application/wasm; add_header Content-Encoding gzip; add_header Vary "Accept-Encoding"; add_header Cache-Control "public, immutable"; } location ~* /libreoffice-wasm/soffice\.data\.gz$ { gzip off; types {} default_type application/octet-stream; add_header Content-Encoding gzip; add_header Vary "Accept-Encoding"; add_header Cache-Control "public, immutable"; } location ~* \.wasm$ { types {} default_type application/wasm; expires 1y; add_header Cache-Control "public, immutable"; } location ~* \.(wasm\.gz|data\.gz|data)$ { expires 1y; add_header Cache-Control "public, immutable"; } location / { try_files $uri $uri/ $uri.html =404; } error_page 404 /404.html; } EOF rm -f /etc/nginx/sites-enabled/default ln -sf /etc/nginx/sites-available/bentopdf /etc/nginx/sites-enabled/bentopdf systemctl stop nginx systemctl disable -q nginx sed -i '/application\/rss+xml/a\ application\/javascript mjs;' /etc/nginx/mime.types cat <<'EOF' >/etc/systemd/system/bentopdf.service [Unit] Description=BentoPDF Service After=network.target [Service] Type=simple ExecStart=/usr/sbin/nginx -g "daemon off;" ExecReload=/bin/kill -HUP $MAINPID Restart=always [Install] WantedBy=multi-user.target EOF systemctl enable -q --now bentopdf msg_ok "Created & started service" motd_ssh customize cleanup_lxc ================================================ FILE: install/beszel-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: Michelle Zitzerman (Sinofage) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://beszel.dev/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os fetch_and_deploy_gh_release "beszel" "henrygd/beszel" "prebuild" "latest" "/opt/beszel" "beszel_linux_arm64.tar.gz" msg_info "Creating Service" cat </etc/systemd/system/beszel-hub.service [Unit] Description=Beszel Hub Service After=network.target [Service] ExecStart=/opt/beszel/beszel serve --http "0.0.0.0:8090" WorkingDirectory=/opt/beszel Restart=always RestartSec=5 [Install] WantedBy=multi-user.target EOF systemctl enable -q --now beszel-hub msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/bichon-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: Slaviša Arežina (tremor021) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/rustmailer/bichon source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os fetch_and_deploy_gh_release "bichon" "rustmailer/bichon" "prebuild" "latest" "/opt/bichon" "bichon-*-aarch64-unknown-linux-gnu.tar.gz" read -r -p "${TAB3}Enter the public URL for Bichon (e.g., https://bichon.yourdomain.com) or leave empty to use container IP: " bichon_url if [[ -z "$bichon_url" ]]; then msg_info "No URL provided" BICHON_PUBLIC_URL="http://$LOCAL_IP:15630" msg_ok "Using local IP: http://$LOCAL_IP:15630\n" else msg_info "URL provided" BICHON_PUBLIC_URL="$bichon_url" msg_ok "Using provided URL: $BICHON_PUBLIC_URL\n" fi msg_info "Setting up Bichon" mkdir -p /opt/bichon-data BICHON_ENC_PASSWORD=$(openssl rand -base64 32 | tr -d "=+/" | cut -c1-32) cat </opt/bichon/bichon.env BICHON_ROOT_DIR=/opt/bichon-data BICHON_LOG_LEVEL=info BICHON_ENCRYPT_PASSWORD=$BICHON_ENC_PASSWORD BICHON_PUBLIC_URL=$BICHON_PUBLIC_URL BICHON_CORS_ORIGINS=$BICHON_PUBLIC_URL EOF msg_ok "Setup Bichon" msg_info "Creating Service" cat </etc/systemd/system/bichon.service [Unit] Description=Bichon service After=network.target [Service] Type=simple User=root EnvironmentFile=/opt/bichon/bichon.env WorkingDirectory=/opt/bichon ExecStart=/opt/bichon/bichon Restart=on-failure RestartSec=5 [Install] WantedBy=multi-user.target EOF systemctl enable -q --now bichon msg_info "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/birdnet-go-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/tphakala/birdnet-go source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ libasound2 \ sox \ alsa-utils \ ffmpeg msg_ok "Installed Dependencies" fetch_and_deploy_gh_release "birdnet" "tphakala/birdnet-go" "prebuild" "latest" "/opt/birdnet" "birdnet-go-linux-amd64.tar.gz" msg_info "Setting up BirdNET-Go" cp /opt/birdnet/birdnet-go /usr/local/bin/birdnet-go chmod +x /usr/local/bin/birdnet-go cp -r /opt/birdnet/libtensorflowlite_c.so /usr/local/lib/ || true ldconfig mkdir -p /opt/birdnet/data/clips msg_ok "Set up BirdNET-Go" msg_info "Creating Service" cat </etc/systemd/system/birdnet.service [Unit] Description=BirdNET After=network.target [Service] Type=simple User=root WorkingDirectory=/opt/birdnet/data ExecStart=/usr/local/bin/birdnet-go realtime Restart=on-failure RestartSec=5 [Install] WantedBy=multi-user.target EOF systemctl enable -q --now birdnet msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/bitmagnet-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: Slaviša Arežina (tremor021) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/bitmagnet-io/bitmagnet source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ iproute2 \ gcc \ musl-dev msg_ok "Installed Dependencies" PG_VERSION="16" setup_postgresql PG_DB_NAME="bitmagnet" PG_DB_USER="bitmagnet" setup_postgresql_db setup_go fetch_and_deploy_gh_release "bitmagnet" "bitmagnet-io/bitmagnet" "tarball" RELEASE=$(cat ~/.bitmagnet) msg_info "Configuring bitmagnet" cd /opt/bitmagnet $STD go build -ldflags "-s -w -X github.com/bitmagnet-io/bitmagnet/internal/version.GitTag=v${RELEASE}" chmod +x bitmagnet msg_ok "Configured bitmagnet" read -r -p "${TAB3}Enter your TMDB API key if you have one: " tmdbapikey cat </etc/bitmagnet.env POSTGRES_HOST=localhost POSTGRES_USER=${PG_DB_USER} POSTGRES_NAME=${PG_DB_NAME} POSTGRES_PASSWORD=${PG_DB_PASS} EOF if [ -z "$tmdbapikey" ]; then echo "TMDB_ENABLED=false" >>/etc/bitmagnet.env else echo "TMDB_API_KEY=$tmdbapikey" >>/etc/bitmagnet.env fi msg_info "Creating Service" cat </etc/systemd/system/bitmagnet-web.service [Unit] Description=bitmagnet Web GUI After=network-online.target [Service] Type=simple User=root WorkingDirectory=/opt/bitmagnet EnvironmentFile=/etc/bitmagnet.env ExecStart=/opt/bitmagnet/bitmagnet worker run --all Restart=on-failure [Install] WantedBy=multi-user.target EOF systemctl enable -q --now bitmagnet-web msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/blocky-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://0xerr0r.github.io/blocky source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os fetch_and_deploy_gh_release "blocky" "0xERR0R/blocky" "prebuild" "latest" "/opt/blocky" "blocky_*_Linux_arm64.tar.gz" msg_info "Configuring Blocky" if systemctl is-active systemd-resolved >/dev/null 2>&1; then systemctl disable -q --now systemd-resolved fi cat </opt/blocky/config.yml # configuration documentation: https://0xerr0r.github.io/blocky/latest/configuration/ upstreams: groups: # these external DNS resolvers will be used. Blocky picks 2 random resolvers from the list for each query # format for resolver: [net:]host:[port][/path]. net could be empty (default, shortcut for tcp+udp), tcp+udp, tcp, udp, tcp-tls or https (DoH). If port is empty, default port will be used (53 for udp and tcp, 853 for tcp-tls, 443 for https (Doh)) # this configuration is mandatory, please define at least one external DNS resolver default: # Cloudflare - 1.1.1.1 # Quad9 DNS-over-TLS server (DoT) - tcp-tls:dns.quad9.net # optional: use allow/denylists to block queries (for example ads, trackers, adult pages etc.) blocking: # definition of denylist groups. Can be external link (http/https) or local file denylists: ads: - https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts # definition: which groups should be applied for which client clientGroupsBlock: # default will be used, if no special definition for a client name exists default: - ads # optional: write query information (question, answer, client, duration etc.) to daily csv file queryLog: # optional one of: mysql, postgresql, csv, csv-client. If empty, log to console type: # optional: use these DNS servers to resolve denylist urls and upstream DNS servers. It is useful if no system DNS resolver is configured, and/or to encrypt the bootstrap queries. bootstrapDns: - upstream: tcp-tls:one.one.one.one ips: - 1.1.1.1 # optional: logging configuration log: # optional: Log level (one from trace, debug, info, warn, error). Default: info level: info EOF msg_ok "Configured Blocky" msg_info "Creating Service" cat </etc/systemd/system/blocky.service [Unit] Description=Blocky After=network.target [Service] User=root WorkingDirectory=/opt/blocky ExecStart=/opt/blocky/./blocky --config config.yml [Install] WantedBy=multi-user.target EOF $STD systemctl enable -q --now blocky msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/bookstack-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (Canbiz) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/BookStackApp/BookStack source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ make \ git msg_ok "Installed Dependencies" PHP_VERSION="8.3" PHP_APACHE="YES" PHP_FPM="YES" PHP_MODULE="ldap,tidy,mysqli" setup_php setup_composer setup_mariadb MARIADB_DB_NAME="bookstack_db" MARIADB_DB_USER="bookstack_user" setup_mariadb_db fetch_and_deploy_gh_release "bookstack" "BookStackApp/BookStack" "tarball" msg_info "Configuring Bookstack (Patience)" cd /opt/bookstack cp .env.example .env sudo sed -i "s|APP_URL=.*|APP_URL=http://$LOCAL_IP|g" /opt/bookstack/.env sudo sed -i "s/DB_DATABASE=.*/DB_DATABASE=$MARIADB_DB_NAME/" /opt/bookstack/.env sudo sed -i "s/DB_USERNAME=.*/DB_USERNAME=$MARIADB_DB_USER/" /opt/bookstack/.env sudo sed -i "s/DB_PASSWORD=.*/DB_PASSWORD=$MARIADB_DB_PASS/" /opt/bookstack/.env $STD composer install --no-dev --no-plugins --no-interaction $STD php artisan key:generate --no-interaction --force $STD php artisan migrate --no-interaction --force chown www-data:www-data -R /opt/bookstack /opt/bookstack/bootstrap/cache /opt/bookstack/public/uploads /opt/bookstack/storage chmod -R 755 /opt/bookstack /opt/bookstack/bootstrap/cache /opt/bookstack/public/uploads /opt/bookstack/storage chmod -R 775 /opt/bookstack/storage /opt/bookstack/bootstrap/cache /opt/bookstack/public/uploads chmod -R 640 /opt/bookstack/.env $STD a2enmod rewrite $STD a2enmod php8.3 msg_ok "Configured Bookstack" msg_info "Creating Service" cat </etc/apache2/sites-available/bookstack.conf ServerAdmin webmaster@localhost DocumentRoot /opt/bookstack/public/ Options -Indexes +FollowSymLinks AllowOverride None Require all granted Options -MultiViews -Indexes RewriteEngine On # Handle Authorization Header RewriteCond %{HTTP:Authorization} . RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}] # Redirect Trailing Slashes If Not A Folder... RewriteCond %{REQUEST_FILENAME} !-d RewriteCond %{REQUEST_URI} (.+)/$ RewriteRule ^ %1 [L,R=301] # Handle Front Controller... RewriteCond %{REQUEST_FILENAME} !-d RewriteCond %{REQUEST_FILENAME} !-f RewriteRule ^ index.php [L] ErrorLog /var/log/apache2/error.log CustomLog /var/log/apache2/access.log combined EOF $STD a2ensite bookstack.conf $STD a2dissite 000-default.conf $STD systemctl reload apache2 msg_ok "Created Services" motd_ssh customize cleanup_lxc ================================================ FILE: install/bunkerweb-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.bunkerweb.io/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ apt-transport-https \ lsb-release msg_ok "Installed Dependencies" RELEASE=$(get_latest_github_release "bunkerity/bunkerweb") msg_warn "WARNING: This script will run an external installer from a third-party source (install-bunkerweb.sh)." msg_warn "The following code is NOT maintained or audited by our repository." msg_warn "If you have any doubts or concerns, please review the installer code before proceeding:" msg_custom "${TAB3}${GATEWAY}${BGN}${CL}" "\e[1;34m" "→ https://github.com/bunkerity/bunkerweb/raw/v${RELEASE}/misc/install-bunkerweb.sh" echo read -r -p "${TAB3}Do you want to continue? [y/N]: " CONFIRM if [[ ! "$CONFIRM" =~ ^([yY][eE][sS]|[yY])$ ]]; then msg_error "Aborted by user. No changes have been made." exit 10 fi msg_info "Installing BunkerWeb (Patience)" curl -fsSL -o install-bunkerweb.sh "https://github.com/bunkerity/bunkerweb/raw/v${RELEASE}/misc/install-bunkerweb.sh" chmod +x install-bunkerweb.sh $STD ./install-bunkerweb.sh --yes $STD apt-mark unhold bunkerweb nginx cat </etc/apt/preferences.d/bunkerweb Package: bunkerweb Pin: version ${RELEASE} Pin-Priority: 1001 EOF echo "${RELEASE}" >~/.bunkerweb msg_ok "Installed BunkerWeb" motd_ssh customize cleanup_lxc ================================================ FILE: install/byparr-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: luismco # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/ThePhaseless/Byparr source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt -y install --no-install-recommends \ ffmpeg \ libatk1.0-0 \ libcairo-gobject2 \ libcairo2 \ libdbus-glib-1-2 \ libfontconfig1 \ libfreetype6 \ libgdk-pixbuf-xlib-2.0-0 \ libglib2.0-0 \ libgtk-3-0 \ libpango-1.0-0 \ libpangocairo-1.0-0 \ libpangoft2-1.0-0 \ libx11-6 \ libx11-xcb1 \ libxcb-shm0 \ libxcb1 \ libxcomposite1 \ libxcursor1 \ libxdamage1 \ libxext6 \ libxfixes3 \ libxi6 \ libxrender1 \ libxt6 \ libxtst6 \ xvfb \ fonts-noto-color-emoji \ fonts-unifont \ xfonts-cyrillic \ xfonts-scalable \ fonts-liberation \ fonts-ipafont-gothic \ fonts-wqy-zenhei \ fonts-tlwg-loma-otf msg_ok "Installed Dependencies" setup_uv fetch_and_deploy_gh_release "Byparr" "ThePhaseless/Byparr" "tarball" "latest" msg_info "Configuring Byparr" cd /opt/Byparr $STD uv sync --link-mode copy $STD uv run camoufox fetch msg_ok "Configured Byparr" msg_info "Creating Service" cat </etc/systemd/system/byparr.service [Unit] Description=Byparr After=network.target [Service] Type=simple WorkingDirectory=/opt/Byparr ExecStart=/usr/local/bin/uv run python3 main.py Restart=on-failure RestartSec=10 [Install] WantedBy=multi-user.target EOF systemctl enable -q --now byparr msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/bytestash-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: Slaviša Arežina (tremor021) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/jordan-dalby/ByteStash source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os NODE_VERSION="22" setup_nodejs fetch_and_deploy_gh_release "bytestash" "jordan-dalby/ByteStash" "tarball" msg_info "Installing ByteStash" JWT_SECRET=$(openssl rand -base64 32 | tr -d '/+=') cd /opt/bytestash/server $STD npm install cd /opt/bytestash/client $STD npm install msg_ok "Installed ByteStash" read -rp "${TAB3}Do you want to allow registration of multiple accounts? [y/n]: " allowreg msg_info "Creating Service" cat </etc/systemd/system/bytestash-backend.service [Unit] Description=ByteStash Backend Service After=network.target [Service] WorkingDirectory=/opt/bytestash/server ExecStart=/usr/bin/node src/app.js Restart=always Environment=JWT_SECRET=$JWT_SECRET [Install] WantedBy=multi-user.target EOF if [[ "$allowreg" =~ ^[Yy]$ ]]; then sed -i '8i\Environment=ALLOW_NEW_ACCOUNTS=true' /etc/systemd/system/bytestash-backend.service fi cat </etc/systemd/system/bytestash-frontend.service [Unit] Description=ByteStash Frontend Service After=network.target bytestash-backend.service [Service] WorkingDirectory=/opt/bytestash/client ExecStart=/usr/bin/npx vite --host Restart=always [Install] WantedBy=multi-user.target EOF systemctl enable -q --now bytestash-backend systemctl enable -q --now bytestash-frontend msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/caddy-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) | Co-Author: MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://caddyserver.com/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ debian-keyring \ debian-archive-keyring \ apt-transport-https msg_ok "Installed Dependencies" msg_info "Installing Caddy" setup_deb822_repo \ "caddy" \ "https://dl.cloudsmith.io/public/caddy/stable/gpg.key" \ "https://dl.cloudsmith.io/public/caddy/stable/deb/debian" \ "any-version" $STD apt install -y caddy msg_ok "Installed Caddy" read -r -p "${TAB3}Would you like to install xCaddy Addon? " prompt if [[ "${prompt,,}" =~ ^(y|yes)$ ]]; then setup_go fetch_and_deploy_gh_release "xcaddy" "caddyserver/xcaddy" "binary" msg_info "Setup xCaddy" $STD apt install -y git $STD xcaddy build msg_ok "Setup xCaddy" fi motd_ssh customize cleanup_lxc ================================================ FILE: install/calibre-web-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: mikolaj92 # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/janeczku/calibre-web source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ build-essential \ python3 \ python3-dev \ libldap2-dev \ libsasl2-dev \ libssl-dev \ imagemagick \ libpango-1.0-0 \ libharfbuzz0b \ libpangoft2-1.0-0 \ fonts-liberation msg_ok "Installed Dependencies" msg_info "Installing Calibre (for eBook conversion)" $STD apt install -y calibre msg_ok "Installed Calibre" fetch_and_deploy_gh_release "Calibre-Web" "janeczku/calibre-web" "prebuild" "latest" "/opt/calibre-web" "calibre-web*.tar.gz" setup_uv msg_info "Installing Python Dependencies" cd /opt/calibre-web $STD uv venv $STD uv pip install --python /opt/calibre-web/.venv/bin/python --no-cache-dir --upgrade pip setuptools wheel $STD uv pip install --python /opt/calibre-web/.venv/bin/python --no-cache-dir -r requirements.txt msg_ok "Installed Python Dependencies" msg_info "Creating Service" mkdir -p /opt/calibre-web/data cat </etc/systemd/system/calibre-web.service [Unit] Description=Calibre-Web Service After=network.target [Service] Type=simple User=root WorkingDirectory=/opt/calibre-web ExecStart=/opt/calibre-web/.venv/bin/python /opt/calibre-web/cps.py Restart=on-failure RestartSec=5 [Install] WantedBy=multi-user.target EOF systemctl enable -q --now calibre-web msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/casaos-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://casaos.io/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_warn "WARNING: This script will run an external installer from a third-party source (https://casaos.zimaspace.com/)." msg_warn "The following code is NOT maintained or audited by our repository." msg_warn "If you have any doubts or concerns, please review the installer code before proceeding:" msg_custom "${TAB3}${GATEWAY}${BGN}${CL}" "\e[1;34m" "→ https://get.casaos.io/" echo read -r -p "${TAB3}Do you want to continue? [y/N]: " CONFIRM if [[ ! "$CONFIRM" =~ ^([yY][eE][sS]|[yY])$ ]]; then msg_error "Aborted by user. No changes have been made." exit 10 fi msg_info "Installing CasaOS (Patience)" DOCKER_CONFIG_PATH='/etc/docker/daemon.json' mkdir -p $(dirname $DOCKER_CONFIG_PATH) echo -e '{\n "log-driver": "journald"\n}' >/etc/docker/daemon.json $STD bash <(curl -fsSL https://get.casaos.io/) msg_ok "Installed CasaOS" motd_ssh customize cleanup_lxc ================================================ FILE: install/changedetection-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://changedetection.io/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies (Patience)" $STD apt-get install -y \ git \ build-essential \ dumb-init \ gconf-service \ libjpeg-dev \ libatk-bridge2.0-0 \ libasound2 \ libatk1.0-0 \ libcairo2 \ libcups2 \ libdbus-1-3 \ libexpat1 \ libgbm-dev \ libgbm1 \ libgconf-2-4 \ libgdk-pixbuf2.0-0 \ libglib2.0-0 \ libgtk-3-0 \ libnspr4 \ libnss3 \ libpango-1.0-0 \ libpangocairo-1.0-0 \ qpdf \ xdg-utils \ xvfb \ ca-certificates msg_ok "Installed Dependencies" msg_info "Setup Python3" $STD apt-get install -y \ python3 \ python3-dev \ python3-pip rm -rf /usr/lib/python3.*/EXTERNALLY-MANAGED msg_ok "Setup Python3" NODE_VERSION="24" setup_nodejs msg_info "Installing Change Detection" mkdir /opt/changedetection $STD pip3 install changedetection.io cat </opt/changedetection/.env WEBDRIVER_URL=http://127.0.0.1:4444/wd/hub PLAYWRIGHT_DRIVER_URL=ws://localhost:3000/chrome?launch=eyJkZWZhdWx0Vmlld3BvcnQiOnsiaGVpZ2h0Ijo3MjAsIndpZHRoIjoxMjgwfSwiaGVhZGxlc3MiOmZhbHNlLCJzdGVhbHRoIjp0cnVlfQ==&blockAds=true EOF msg_ok "Installed Change Detection" msg_info "Installing Browserless & Playwright" mkdir /opt/browserless $STD python3 -m pip install playwright $STD git clone https://github.com/browserless/chrome /opt/browserless $STD npm ci --include=optional --include=dev --prefix /opt/browserless $STD /opt/browserless/node_modules/playwright-core/cli.js install --with-deps &>/dev/null $STD /opt/browserless/node_modules/playwright-core/cli.js install --force chrome &>/dev/null $STD /opt/browserless/node_modules/playwright-core/cli.js install chromium firefox webkit &>/dev/null $STD /opt/browserless/node_modules/playwright-core/cli.js install --force msedge $STD npm run build --prefix /opt/browserless $STD npm run build:function --prefix /opt/browserless $STD npm prune production --prefix /opt/browserless msg_ok "Installed Browserless & Playwright" msg_info "Installing Font Packages" $STD apt-get install -y \ fontconfig \ libfontconfig1 \ fonts-freefont-ttf \ fonts-gfs-neohellenic \ fonts-indic fonts-ipafont-gothic \ fonts-kacst fonts-liberation \ fonts-noto-cjk \ fonts-noto-color-emoji \ msttcorefonts \ fonts-roboto \ fonts-thai-tlwg \ fonts-wqy-zenhei msg_ok "Installed Font Packages" msg_info "Installing X11 Packages" $STD apt-get install -y \ libx11-6 \ libx11-xcb1 \ libxcb1 \ libxcomposite1 \ libxcursor1 \ libxdamage1 \ libxext6 \ libxfixes3 \ libxi6 \ libxrandr2 \ libxrender1 \ libxss1 \ libxtst6 msg_ok "Installed X11 Packages" msg_info "Creating Services" cat </etc/systemd/system/changedetection.service [Unit] Description=Change Detection After=network-online.target After=network.target browserless.service Wants=browserless.service [Service] Type=simple EnvironmentFile=/opt/changedetection/.env WorkingDirectory=/opt/changedetection ExecStart=changedetection.io -d /opt/changedetection -p 5000 [Install] WantedBy=multi-user.target EOF cat </etc/systemd/system/browserless.service [Unit] Description=browserless service After=network.target [Service] Environment=CONNECTION_TIMEOUT=60000 WorkingDirectory=/opt/browserless ExecStart=/opt/browserless/scripts/start.sh SyslogIdentifier=browserless [Install] WantedBy=default.target EOF systemctl enable -q --now browserless systemctl enable -q --now changedetection msg_ok "Created Services" motd_ssh customize cleanup_lxc ================================================ FILE: install/channels-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://getchannels.com/dvr-server/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ chromium \ xvfb msg_ok "Installed Dependencies" msg_warn "WARNING: This script will run an external installer from a third-party source (https://getchannels.com)." msg_warn "The following code is NOT maintained or audited by our repository." msg_warn "If you have any doubts or concerns, please review the installer code before proceeding:" msg_custom "${TAB3}${GATEWAY}${BGN}${CL}" "\e[1;34m" "→ https://getchannels.com/dvr/setup.sh" echo read -r -p "${TAB3}Do you want to continue? [y/N]: " CONFIRM if [[ ! "$CONFIRM" =~ ^([yY][eE][sS]|[yY])$ ]]; then msg_error "Aborted by user. No changes have been made." exit 10 fi setup_hwaccel msg_info "Installing Channels DVR Server (Patience)" cd /opt $STD bash <(curl -fsSL https://getchannels.com/dvr/setup.sh) msg_ok "Installed Channels DVR Server" motd_ssh customize cleanup_lxc ================================================ FILE: install/checkmate-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/bluewave-labs/Checkmate source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ build-essential \ openssl \ nginx msg_ok "Installed Dependencies" MONGO_VERSION="8.0" setup_mongodb NODE_VERSION="22" setup_nodejs fetch_and_deploy_gh_release "checkmate" "bluewave-labs/Checkmate" "tarball" msg_info "Configuring Checkmate" JWT_SECRET="$(openssl rand -hex 32)" cat </opt/checkmate/server/.env CLIENT_HOST="http://${LOCAL_IP}" JWT_SECRET="${JWT_SECRET}" DB_CONNECTION_STRING="mongodb://localhost:27017/checkmate_db" TOKEN_TTL="99d" ORIGIN="${LOCAL_IP}" LOG_LEVEL="info" SERVER_HOST=0.0.0.0 SERVER_PORT=52345 EOF cat </opt/checkmate/client/.env.local VITE_APP_API_BASE_URL="/api/v1" UPTIME_APP_API_BASE_URL="/api/v1" VITE_APP_LOG_LEVEL="warn" EOF msg_ok "Configured Checkmate" msg_info "Installing Checkmate Server" cd /opt/checkmate/server $STD npm install $STD npm run build msg_ok "Installed Checkmate Server" msg_info "Installing Checkmate Client" cd /opt/checkmate/client $STD npm install VITE_APP_API_BASE_URL="/api/v1" UPTIME_APP_API_BASE_URL="/api/v1" VITE_APP_LOG_LEVEL="warn" $STD npm run build msg_ok "Installed Checkmate Client" msg_info "Creating Services" cat </etc/systemd/system/checkmate-server.service [Unit] Description=Checkmate Server After=network.target mongod.service [Service] Type=simple User=root WorkingDirectory=/opt/checkmate/server EnvironmentFile=/opt/checkmate/server/.env ExecStart=/usr/bin/npm start Restart=on-failure RestartSec=5 [Install] WantedBy=multi-user.target EOF cat </etc/systemd/system/checkmate-client.service [Unit] Description=Checkmate Client After=network.target [Service] Type=simple User=root WorkingDirectory=/opt/checkmate/client ExecStart=/usr/bin/npm run preview -- --host 127.0.0.1 --port 5173 Restart=on-failure RestartSec=5 [Install] WantedBy=multi-user.target EOF $STD systemctl enable -q --now checkmate-server $STD systemctl enable -q --now checkmate-client msg_ok "Created Services" msg_info "Configuring Nginx Reverse Proxy" cat </etc/nginx/sites-available/checkmate server { listen 80 default_server; server_name _; client_max_body_size 100M; # Client UI location / { proxy_pass http://127.0.0.1:5173; proxy_http_version 1.1; proxy_set_header Upgrade \$http_upgrade; proxy_set_header Connection "upgrade"; proxy_set_header Host \$host; proxy_set_header X-Real-IP \$remote_addr; proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for; } # API Server location /api/v1/ { proxy_pass http://127.0.0.1:52345/api/v1/; proxy_http_version 1.1; proxy_set_header Host \$host; proxy_set_header X-Real-IP \$remote_addr; proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for; } } EOF ln -sf /etc/nginx/sites-available/checkmate /etc/nginx/sites-enabled/checkmate rm -f /etc/nginx/sites-enabled/default $STD systemctl reload nginx msg_ok "Configured Nginx Reverse Proxy" motd_ssh customize cleanup_lxc ================================================ FILE: install/checkmk-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: Michel Roegl-Brunner (michelroegl-brunner) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://checkmk.com/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Install Checkmk" RELEASE=$(curl -fsSL https://api.github.com/repos/checkmk/checkmk/tags | grep "name" | awk '{print substr($2, 3, length($2)-4) }' | tr ' ' '\n' | grep -Ev 'rc|b' | sort -V | tail -n 1) curl -fsSL "https://download.checkmk.com/checkmk/${RELEASE}/check-mk-raw-${RELEASE}_0.bookworm_arm64.deb" -o "/opt/checkmk.deb" $STD apt-get install -y /opt/checkmk.deb rm -rf /opt/checkmk.deb echo "${RELEASE}" >"/opt/checkmk_version.txt" msg_ok "Installed Checkmk" msg_info "Creating Service" SITE_NAME="monitoring" $STD omd create "$SITE_NAME" MKPASSWORD=$(openssl rand -base64 18 | tr -d '/+=' | cut -c1-16) echo -e "$MKPASSWORD\n$MKPASSWORD" | su - "$SITE_NAME" -c "cmk-passwd cmkadmin --stdin" $STD omd start "$SITE_NAME" { echo "Application-Credentials" echo "Username: cmkadmin" echo "Password: $MKPASSWORD" echo "Site: $SITE_NAME" } >>~/checkmk.creds msg_ok "Created Service" cleanup_lxc motd_ssh customize ================================================ FILE: install/cleanuparr-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: Lucas Zampieri (zampierilucas) | MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/Cleanuparr/Cleanuparr source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os fetch_and_deploy_gh_release "Cleanuparr" "Cleanuparr/Cleanuparr" "prebuild" "latest" "/opt/cleanuparr" "*linux-arm64.zip" msg_info "Creating Service" cat </etc/systemd/system/cleanuparr.service [Unit] Description=Cleanuparr Daemon After=syslog.target network.target [Service] Type=simple User=root WorkingDirectory=/opt/cleanuparr ExecStart=/opt/cleanuparr/Cleanuparr Restart=on-failure RestartSec=5 Environment="PORT=11011" Environment="CONFIG_DIR=/opt/cleanuparr/config" [Install] WantedBy=multi-user.target EOF systemctl enable -q --now cleanuparr msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/cloudflare-ddns-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: edoardop13 # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/favonia/cloudflare-ddns source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os setup_go var_cf_api_token="default" read -rp "${TAB3}Enter the Cloudflare API token: " var_cf_api_token var_cf_domains="default" read -rp "${TAB3}Enter the domains separated with a comma (*.example.org,www.example.org) " var_cf_domains var_cf_proxied="false" while true; do read -rp "${TAB3}Proxied? (y/n): " answer case "$answer" in [Yy]*) var_cf_proxied="true" break ;; [Nn]*) var_cf_proxied="false" break ;; *) echo "Please answer y or n." ;; esac done var_cf_ip6_provider="none" while true; do read -rp "${TAB3}Enable IPv6 support? (y/n): " answer case "$answer" in [Yy]*) var_cf_ip6_provider="cloudflare.trace" break ;; [Nn]*) var_cf_ip6_provider="none" break ;; *) echo "Please answer y or n." ;; esac done msg_ok "Configured Application" msg_info "Setting up service" mkdir -p /root/go cat </etc/systemd/system/cloudflare-ddns.service [Unit] Description=Cloudflare DDNS Service (Go run) After=network.target [Service] Environment="CLOUDFLARE_API_TOKEN=${var_cf_api_token}" Environment="DOMAINS=${var_cf_domains}" Environment="PROXIED=${var_cf_proxied}" Environment="IP6_PROVIDER=${var_cf_ip6_provider}" Environment="GOPATH=/root/go" Environment="GOCACHE=/tmp/go-build" ExecStart=/usr/local/bin/go run github.com/favonia/cloudflare-ddns/cmd/ddns@latest Restart=always RestartSec=300 [Install] WantedBy=multi-user.target EOF systemctl enable -q --now cloudflare-ddns msg_ok "Setup Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/cloudflared-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.cloudflare.com/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Cloudflared" setup_deb822_repo \ "cloudflared" \ "https://pkg.cloudflare.com/cloudflare-main.gpg" \ "https://pkg.cloudflare.com/cloudflared/" \ "any" \ "main" $STD apt install -y cloudflared msg_ok "Installed Cloudflared" motd_ssh customize cleanup_lxc ================================================ FILE: install/cloudreve-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://cloudreve.org/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os fetch_and_deploy_gh_release "cloudreve" "cloudreve/cloudreve" "prebuild" "latest" "/opt/cloudreve" "*linux_arm64.tar.gz" msg_info "Setup Service" cat </etc/systemd/system/cloudreve.service [Unit] Description=Cloudreve Service After=network.target [Service] Type=simple ExecStart=/opt/cloudreve/cloudreve Restart=on-failure RestartSec=5 [Install] WantedBy=multi-user.target EOF systemctl enable -q --now cloudreve msg_ok "Service Setup" motd_ssh customize cleanup_lxc ================================================ FILE: install/cockpit-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck # Co-Author: havardthom # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/cockpit-project/cockpit source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Cockpit" source /etc/os-release cat </etc/apt/sources.list.d/debian-backports.sources Types: deb deb-src URIs: http://deb.debian.org/debian Suites: ${VERSION_CODENAME}-backports Components: main Enabled: yes Signed-By: /usr/share/keyrings/debian-archive-keyring.gpg EOF $STD apt update $STD apt install -t ${VERSION_CODENAME}-backports cockpit cracklib-runtime --no-install-recommends -y sed -i "s/root//g" /etc/cockpit/disallowed-users msg_ok "Installed Cockpit" read -r -p "Would you like to install 45Drives' cockpit-file-sharing, cockpit-identities, and cockpit-navigator " prompt if [[ "${prompt,,}" =~ ^(y|yes)$ ]]; then install_45drives=true if [[ "${VERSION_ID}" -ge 13 ]]; then read -r -p "Debian ${VERSION_ID} is not officially supported by 45Drives yet, would you like to continue anyway? " prompt if [[ ! "${prompt,,}" =~ ^(y|yes)$ ]]; then install_45drives=false fi fi if [[ "$install_45drives" == "true" ]]; then msg_info "Installing 45Drives' cockpit extensions" setup_deb822_repo "45drives" \ "https://repo.45drives.com/key/gpg.asc" \ "https://repo.45drives.com/enterprise/debian" \ "bookworm" \ "main" \ "arm64" $STD apt install -y cockpit-file-sharing cockpit-identities cockpit-navigator msg_ok "Installed 45Drives' cockpit extensions" fi fi motd_ssh customize cleanup_lxc ================================================ FILE: install/comfyui-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: jdacode # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/comfyanonymous/ComfyUI source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os setup_hwaccel echo echo "${TAB3}Choose the GPU type for ComfyUI:" echo "${TAB3}[1]-None [2]-NVIDIA [3]-AMD [4]-Intel" read -rp "${TAB3}Enter your choice [1-4] (default: 1): " gpu_choice gpu_choice=${gpu_choice:-1} case "$gpu_choice" in 1) comfyui_gpu_type="none" ;; 2) comfyui_gpu_type="nvidia" ;; 3) comfyui_gpu_type="amd" ;; 4) comfyui_gpu_type="intel" ;; *) comfyui_gpu_type="none" echo "${TAB3}Invalid choice. Defaulting to ${comfyui_gpu_type}." ;; esac echo PYTHON_VERSION="3.12" setup_uv fetch_and_deploy_gh_release "ComfyUI" "comfyanonymous/ComfyUI" "tarball" "latest" "/opt/ComfyUI" msg_info "Python dependencies" $STD uv venv --clear "/opt/ComfyUI/venv" if [[ "${comfyui_gpu_type,,}" == "nvidia" ]]; then pytorch_url="https://download.pytorch.org/whl/cu130" if [[ -f "/opt/ComfyUI/README.md" ]]; then extracted=$(grep -oP 'pip install.*?--extra-index-url\s+\Khttps://download\.pytorch\.org/whl/cu\d+' /opt/ComfyUI/README.md | head -1 || true) [[ -n "$extracted" ]] && pytorch_url="$extracted" fi $STD uv pip install \ torch \ torchvision \ torchaudio \ --extra-index-url "$pytorch_url" \ --python="/opt/ComfyUI/venv/bin/python" elif [[ "${comfyui_gpu_type,,}" == "amd" ]]; then pytorch_url="https://download.pytorch.org/whl/rocm6.4" if [[ -f "/opt/ComfyUI/README.md" ]]; then extracted=$(grep -oP 'pip install.*?--index-url\s+\Khttps://download\.pytorch\.org/whl/rocm[\d.]+' /opt/ComfyUI/README.md | grep -v 'nightly' | head -1 || true) [[ -n "$extracted" ]] && pytorch_url="$extracted" fi $STD uv pip install \ torch \ torchvision \ torchaudio \ --index-url "$pytorch_url" \ --python="/opt/ComfyUI/venv/bin/python" elif [[ "${comfyui_gpu_type,,}" == "intel" ]]; then pytorch_url="https://download.pytorch.org/whl/xpu" if [[ -f "/opt/ComfyUI/README.md" ]]; then extracted=$(grep -oP 'pip install.*?--index-url\s+\Khttps://download\.pytorch\.org/whl/xpu' /opt/ComfyUI/README.md | head -1 || true) [[ -n "$extracted" ]] && pytorch_url="$extracted" fi $STD uv pip install \ torch \ torchvision \ torchaudio \ --index-url "$pytorch_url" \ --python="/opt/ComfyUI/venv/bin/python" fi $STD uv pip install -r "/opt/ComfyUI/requirements.txt" --python="/opt/ComfyUI/venv/bin/python" msg_ok "Python dependencies" msg_info "Creating Service" cat </etc/systemd/system/comfyui.service [Unit] Description=ComfyUI Service After=network.target [Service] Type=simple User=root WorkingDirectory=/opt/ComfyUI ExecStart=/opt/ComfyUI/venv/bin/python /opt/ComfyUI/main.py --listen --port 8188 Restart=on-failure [Install] WantedBy=multi-user.target EOF systemctl enable -q --now comfyui msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/commafeed-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.commafeed.com/#/welcome source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y rsync msg_ok "Installed Dependencies" JAVA_VERSION="25" setup_java fetch_and_deploy_gh_release "commafeed" "Athou/commafeed" "prebuild" "latest" "/opt/commafeed" "commafeed-*-h2-jvm.zip" msg_info "Creating Service" cat </etc/systemd/system/commafeed.service [Unit] Description=CommaFeed Service After=network.target [Service] ExecStart=java -Xminf0.05 -Xmaxf0.1 -jar quarkus-run.jar WorkingDirectory=/opt/commafeed/ Restart=always [Install] WantedBy=multi-user.target EOF systemctl enable -q --now commafeed msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/configarr-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: finkerle # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/raydak-labs/configarr source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y git libicu76 msg_ok "Installed Dependencies" fetch_and_deploy_gh_release "configarr" "raydak-labs/configarr" "prebuild" "latest" "/opt/configarr" "configarr-linux-x64.tar.xz" msg_info "Setup Configarr" cat </opt/configarr/.env ROOT_PATH=/opt/configarr CUSTOM_REPO_ROOT=/opt/configarr/repos CONFIG_LOCATION=/opt/configarr/config.yml SECRETS_LOCATION=/opt/configarr/secrets.yml EOF cd /opt/configarr curl -fsSLO https://raw.githubusercontent.com/raydak-labs/configarr/refs/heads/main/examples/full/config/config.yml curl -fsSLO https://raw.githubusercontent.com/raydak-labs/configarr/refs/heads/main/examples/full/config/secrets.yml sed 's|#localConfigTemplatesPath: /app/templates|#localConfigTemplatesPath: /opt/configarr/templates|' /opt/configarr/config.yml msg_ok "Setup Configarr" msg_info "Creating Service" cat </etc/systemd/system/configarr-task.service [Unit] Description=Run Configarr Task [Service] Type=simple WorkingDirectory=/opt/configarr ExecStart=/opt/configarr/configarr EOF cat </etc/systemd/system/configarr-task.timer [Unit] Description=Run Configarr every 5 minutes [Timer] OnBootSec=2min OnUnitActiveSec=5min Persistent=true [Install] WantedBy=timers.target EOF systemctl enable -q --now configarr-task.timer configarr-task.service msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/convertx-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: Omar Minaya | MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/C4illin/ConvertX source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os setup_hwaccel setup_imagemagick msg_info "Installing Dependencies" $STD apt install -y \ assimp-utils \ calibre \ dcraw \ dvisvgm \ ffmpeg \ inkscape \ libreoffice-writer \ libva2 \ libvips-tools \ lmodern \ mupdf-tools \ pandoc \ poppler-utils \ potrace \ python3-numpy \ texlive \ texlive-fonts-recommended \ texlive-latex-extra \ texlive-latex-recommended \ texlive-xetex msg_ok "Installed Dependencies" NODE_VERSION="22" NODE_MODULE="bun" setup_nodejs fetch_and_deploy_gh_release "ConvertX" "C4illin/ConvertX" "tarball" "latest" "/opt/convertx" msg_info "Installing ConvertX" cd /opt/convertx mkdir -p data $STD bun install JWT_SECRET=$(openssl rand -base64 32 | tr -dc 'a-zA-Z0-9' | head -c 32) cat </opt/convertx/.env JWT_SECRET=$JWT_SECRET HTTP_ALLOWED=true PORT=3000 EOF msg_ok "Installed ConvertX" msg_info "Creating Services" cat </etc/systemd/system/convertx.service [Unit] Description=ConvertX File Converter After=network.target [Service] Type=exec WorkingDirectory=/opt/convertx EnvironmentFile=/opt/convertx/.env ExecStart=/bin/bun dev Restart=always [Install] WantedBy=multi-user.target EOF systemctl enable -q --now convertx msg_ok "Service Created" motd_ssh customize cleanup_lxc ================================================ FILE: install/coredns-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/coredns/coredns source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os fetch_and_deploy_gh_release "coredns" "coredns/coredns" "prebuild" "latest" "/usr/local/bin" \ "coredns_*_linux_$(dpkg --print-architecture).tgz" chmod +x /usr/local/bin/coredns msg_info "Configuring CoreDNS" mkdir -p /etc/coredns cat </etc/coredns/Corefile . { forward . 1.1.1.1 1.0.0.1 cache 30 log errors health :8080 ready :8181 } EOF msg_ok "Configured CoreDNS" msg_info "Creating Service" cat </etc/systemd/system/coredns.service [Unit] Description=CoreDNS DNS Server After=network.target [Service] Type=simple ExecStart=/usr/local/bin/coredns -conf /etc/coredns/Corefile Restart=on-failure RestartSec=5 LimitNOFILE=1048576 [Install] WantedBy=multi-user.target EOF systemctl enable -q --now coredns msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/cosmos-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: Michel Roegl-Brunner (michelroegl-brunner) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://https://cosmos-cloud.io/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ ca-certificates \ openssl \ snapraid \ avahi-daemon \ fdisk \ mergerfs \ unzip msg_ok "Installed Dependencies" setup_docker fetch_and_deploy_gh_release "cosmos" "azukaar/Cosmos-Server" "prebuild" "latest" "/opt/cosmos" "cosmos-cloud-*-arm64.zip" msg_info "Setting up Cosmos" cd /opt/cosmos chmod +x /opt/cosmos/cosmos msg_ok "Set up Cosmos" msg_info "Creating Service" cat </etc/systemd/system/cosmos.service [Unit] Description=Cosmos Cloud service ConditionFileIsExecutable=/opt/cosmos/start.sh [Service] StartLimitInterval=10 StartLimitBurst=5 ExecStart=/opt/cosmos/start.sh WorkingDirectory=/opt/cosmos Restart=always RestartSec=2 EnvironmentFile=-/etc/sysconfig/CosmosCloud [Install] WantedBy=multi-user.target EOF systemctl enable -q --now cosmos msg_info "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/crafty-controller-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: CrazyWolf13 # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://docs.craftycontrol.com/pages/getting-started/installation/linux/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Setting up TemurinJDK" setup_java $STD apt install -y temurin-{8,11,17,21}-jre sudo update-alternatives --set java /usr/lib/jvm/temurin-21-jre-arm64/bin/java msg_ok "Installed TemurinJDK" msg_info "Setup Python3" $STD apt install -y \ python3 \ python3-dev \ python3-pip \ python3-venv rm -rf /usr/lib/python3.*/EXTERNALLY-MANAGED msg_ok "Setup Python3" msg_info "Installing Crafty-Controller (Patience)" useradd crafty -m -s /bin/bash cd /opt mkdir -p /opt/crafty-controller/crafty /opt/crafty-controller/server RELEASE=$(curl -fsSL "https://gitlab.com/api/v4/projects/20430749/releases" | grep -o '"tag_name":"v[^"]*"' | head -n 1 | sed 's/"tag_name":"v//;s/"//') echo "${RELEASE}" >"/opt/crafty-controller_version.txt" curl -fsSL "https://gitlab.com/crafty-controller/crafty-4/-/archive/v${RELEASE}/crafty-4-v${RELEASE}.zip" -o "crafty-4-v${RELEASE}.zip" $STD unzip crafty-4-v"${RELEASE}".zip cp -a crafty-4-v"${RELEASE}"/. /opt/crafty-controller/crafty/crafty-4/ rm -rf crafty-4-v"${RELEASE}" cd /opt/crafty-controller/crafty python3 -m venv .venv chown -R crafty:crafty /opt/crafty-controller/ $STD sudo -u crafty bash -c ' source /opt/crafty-controller/crafty/.venv/bin/activate cd /opt/crafty-controller/crafty/crafty-4 pip3 install --no-cache-dir -r requirements.txt ' msg_ok "Installed Craft-Controller and dependencies" msg_info "Setting up service" cat </etc/systemd/system/crafty-controller.service [Unit] Description=Crafty 4 After=network.target [Service] Type=simple User=crafty WorkingDirectory=/opt/crafty-controller/crafty/crafty-4 Environment=PATH=/usr/lib/jvm/temurin-21-jre-arm64/bin:/opt/crafty-controller/crafty/.venv/bin:$PATH ExecStart=/opt/crafty-controller/crafty/.venv/bin/python3 main.py -d Restart=on-failure [Install] WantedBy=multi-user.target EOF $STD systemctl enable -q --now crafty-controller CREDS_FILE="/opt/crafty-controller/crafty/crafty-4/app/config/default-creds.txt" for i in $(seq 1 30); do [[ -f "$CREDS_FILE" ]] && break sleep 2 done if [[ -f "$CREDS_FILE" ]]; then { echo "Crafty-Controller-Credentials" echo "Username: $(grep -oP '(?<="username": ")[^"]*' "$CREDS_FILE")" echo "Password: $(grep -oP '(?<="password": ")[^"]*' "$CREDS_FILE")" } >>~/crafty-controller.creds fi msg_ok "Service started" motd_ssh customize cleanup_lxc ================================================ FILE: install/cronicle-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://cronicle.net/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os NODE_VERSION="22" setup_nodejs fetch_and_deploy_gh_release "cronicle" "jhuckaby/Cronicle" "tarball" msg_info "Configuring Cronicle Primary Server" cd /opt/cronicle $STD npm install $STD node bin/build.js dist sed -i "s/localhost:3012/${LOCAL_IP}:3012/g" /opt/cronicle/conf/config.json $STD /opt/cronicle/bin/control.sh setup $STD /opt/cronicle/bin/control.sh start msg_ok "Configured Cronicle Primary Server" motd_ssh customize cleanup_lxc ================================================ FILE: install/cross-seed-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: Jakub Matraszek (jmatraszek) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.cross-seed.org source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y build-essential msg_ok "Installed Dependencies" NODE_VERSION="24" setup_nodejs msg_info "Setup Cross-Seed" $STD npm install cross-seed@latest -g $STD cross-seed gen-config msg_ok "Setup Cross-Seed" msg_info "Creating Service" cat </etc/systemd/system/cross-seed.service [Unit] Description=Cross-Seed daemon Service After=network.target [Service] ExecStart=/usr/bin/cross-seed daemon Restart=on-failure RestartSec=30 User=root [Install] WantedBy=multi-user.target EOF systemctl enable -q --now cross-seed msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/cryptpad-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: Slaviša Arežina (tremor021) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/cryptpad/cryptpad source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y git msg_ok "Installed Dependencies" NODE_VERSION="22" setup_nodejs read -rp "${TAB3}Install OnlyOffice components instead of CKEditor? (Y/N): " onlyoffice fetch_and_deploy_gh_release "cryptpad" "cryptpad/cryptpad" "tarball" msg_info "Setup CryptPad" cd /opt/cryptpad $STD npm ci $STD npm run install:components if [[ "$onlyoffice" =~ ^[Yy]$ ]]; then $STD bash -c "./install-onlyoffice.sh --accept-license" fi cp config/config.example.js config/config.js sed -i "51s/localhost/${LOCAL_IP}/g" /opt/cryptpad/config/config.js sed -i "80s#//httpAddress: 'localhost'#httpAddress: '0.0.0.0'#g" /opt/cryptpad/config/config.js $STD npm run build msg_ok "Setup CryptPad" msg_info "Creating Service" cat </etc/systemd/system/cryptpad.service [Unit] Description=CryptPad Service After=network.target [Service] Type=simple User=root WorkingDirectory=/opt/cryptpad ExecStart=/usr/bin/node server Environment='PWD="/opt/cryptpad"' StandardOutput=journal StandardError=journal+console LimitNOFILE=1000000 Restart=always [Install] WantedBy=multi-user.target EOF systemctl enable -q --now cryptpad msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/dagu-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://dagu.sh/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os fetch_and_deploy_gh_release "dagu" "dagucloud/dagu" "prebuild" "latest" "/opt/dagu" "dagu_*_linux_amd64.tar.gz" msg_info "Setting up Dagu" mkdir -p /opt/dagu/data msg_ok "Set up Dagu" msg_info "Creating Service" cat </etc/systemd/system/dagu.service [Unit] Description=Dagu Workflow Engine After=network.target [Service] Type=simple User=root WorkingDirectory=/opt/dagu Environment=DAGU_HOME=/opt/dagu/data Environment=DAGU_HOST=0.0.0.0 Environment=DAGU_PORT=8080 ExecStart=/opt/dagu/dagu start-all Restart=on-failure RestartSec=5 [Install] WantedBy=multi-user.target EOF systemctl enable -q --now dagu msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/dashy-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: tteck (tteckster) | Co-Author: CrazyWolf13 # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://dashy.to/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os NODE_VERSION="24" NODE_MODULE="yarn" setup_nodejs fetch_and_deploy_gh_release "dashy" "Lissy93/dashy" "prebuild" "latest" "/opt/dashy" "dashy-*.tar.gz" msg_info "Installing Dashy" cd /opt/dashy $STD yarn install --ignore-engines --network-timeout 300000 msg_ok "Installed Dashy" msg_info "Creating Service" cat </etc/systemd/system/dashy.service [Unit] Description=dashy [Service] Type=simple WorkingDirectory=/opt/dashy Environment=NODE_OPTIONS=--openssl-legacy-provider ExecStart=/usr/bin/node server.js [Install] WantedBy=multi-user.target EOF systemctl enable -q --now dashy msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/databasus-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/databasus/databasus source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ nginx \ valkey \ mariadb-client \ rclone msg_ok "Installed Dependencies" PG_VERSION="17" setup_postgresql setup_go NODE_VERSION="24" setup_nodejs msg_info "Installing Database Clients" # Create PostgreSQL version symlinks for compatibility for v in 12 13 14 15 16 18; do ln -sf /usr/lib/postgresql/17 /usr/lib/postgresql/$v done # Install MongoDB Database Tools via direct .deb (no APT repo for Debian 13) [[ "$(get_os_info id)" == "ubuntu" ]] && MONGO_DIST="ubuntu2204" || MONGO_DIST="debian12" MONGO_VERSION=$(get_latest_gh_tag "mongodb/mongo-tools" "100." || echo "100.16.1") fetch_and_deploy_from_url "https://fastdl.mongodb.org/tools/db/mongodb-database-tools-${MONGO_DIST}-x86_64-${MONGO_VERSION}.deb" "" mkdir -p /usr/local/mongodb-database-tools/bin [[ -f /usr/bin/mongodump ]] && ln -sf /usr/bin/mongodump /usr/local/mongodb-database-tools/bin/mongodump [[ -f /usr/bin/mongorestore ]] && ln -sf /usr/bin/mongorestore /usr/local/mongodb-database-tools/bin/mongorestore # Create MariaDB and MySQL client symlinks for compatibility mkdir -p /usr/local/mariadb-{10.6,12.1}/bin /usr/local/mysql-{5.7,8.0,8.4,9}/bin for dir in /usr/local/mariadb-{10.6,12.1}/bin; do ln -sf /usr/bin/mariadb-dump "$dir/mariadb-dump" ln -sf /usr/bin/mariadb "$dir/mariadb" done for dir in /usr/local/mysql-{5.7,8.0,8.4,9}/bin; do ln -sf /usr/bin/mariadb-dump "$dir/mysqldump" ln -sf /usr/bin/mariadb "$dir/mysql" done msg_ok "Installed Database Clients" fetch_and_deploy_gh_release "databasus" "databasus/databasus" "tarball" "latest" "/opt/databasus" msg_info "Building Databasus (Patience)" export COREPACK_ENABLE_DOWNLOAD_PROMPT=0 cd /opt/databasus/frontend $STD corepack enable $STD corepack prepare pnpm@latest --activate $STD pnpm install --frozen-lockfile $STD pnpm run build cd /opt/databasus/backend $STD go mod tidy $STD go mod download $STD go install github.com/swaggo/swag/cmd/swag@latest $STD /root/go/bin/swag init -g cmd/main.go -o swagger $STD env CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o databasus ./cmd/main.go mv /opt/databasus/backend/databasus /opt/databasus/databasus mkdir -p /databasus-data/{pgdata,temp,backups,data,logs} mkdir -p /opt/databasus/ui/build mkdir -p /opt/databasus/migrations cp -r /opt/databasus/frontend/dist/* /opt/databasus/ui/build/ cp -r /opt/databasus/backend/migrations/* /opt/databasus/migrations/ chown -R postgres:postgres /databasus-data msg_ok "Built Databasus" msg_info "Configuring Databasus" JWT_SECRET=$(openssl rand -hex 32) ENCRYPTION_KEY=$(openssl rand -hex 32) # Install goose for migrations $STD go install github.com/pressly/goose/v3/cmd/goose@latest ln -sf /root/go/bin/goose /usr/local/bin/goose cat </.env # Environment ENV_MODE=production # Server SERVER_PORT=4005 SERVER_HOST=0.0.0.0 # Database DATABASE_DSN=host=localhost user=postgres password=postgres dbname=databasus port=5432 sslmode=disable DATABASE_URL=postgres://postgres:postgres@localhost:5432/databasus?sslmode=disable # Migrations GOOSE_DRIVER=postgres GOOSE_DBSTRING=postgres://postgres:postgres@localhost:5432/databasus?sslmode=disable GOOSE_MIGRATION_DIR=/opt/databasus/migrations # Valkey (Redis-compatible cache) VALKEY_HOST=localhost VALKEY_PORT=6379 # Security JWT_SECRET=${JWT_SECRET} ENCRYPTION_KEY=${ENCRYPTION_KEY} # Paths DATA_DIR=/databasus-data/data BACKUP_DIR=/databasus-data/backups LOG_DIR=/databasus-data/logs EOF chmod 600 /.env msg_ok "Configured Databasus" msg_info "Configuring Valkey" cat </etc/valkey/valkey.conf port 6379 bind 127.0.0.1 protected-mode yes save "" maxmemory 256mb maxmemory-policy allkeys-lru EOF systemctl enable -q --now valkey-server systemctl restart valkey-server msg_ok "Configured Valkey" msg_info "Creating Database" # Configure PostgreSQL to allow local password auth for databasus PG_HBA="/etc/postgresql/17/main/pg_hba.conf" if ! grep -q "databasus" "$PG_HBA"; then sed -i '/^local\s*all\s*all/i local databasus postgres trust' "$PG_HBA" sed -i '/^host\s*all\s*all\s*127/i host databasus postgres 127.0.0.1/32 trust' "$PG_HBA" systemctl reload postgresql fi $STD sudo -u postgres psql -c "CREATE DATABASE databasus;" 2>/dev/null || true $STD sudo -u postgres psql -c "ALTER USER postgres WITH SUPERUSER CREATEROLE CREATEDB;" 2>/dev/null || true msg_ok "Created Database" msg_info "Creating Databasus Service" cat </etc/systemd/system/databasus.service [Unit] Description=Databasus - Database Backup Management After=network.target postgresql.service valkey.service Requires=postgresql.service valkey.service [Service] Type=simple WorkingDirectory=/opt/databasus EnvironmentFile=/.env ExecStart=/opt/databasus/databasus Restart=always RestartSec=5 StandardOutput=journal StandardError=journal [Install] WantedBy=multi-user.target EOF $STD systemctl daemon-reload $STD systemctl enable -q --now databasus msg_ok "Created Databasus Service" msg_info "Configuring Nginx" cat </etc/nginx/sites-available/databasus server { listen 80; server_name _; location / { proxy_pass http://127.0.0.1:4005; proxy_http_version 1.1; proxy_set_header Upgrade \$http_upgrade; proxy_set_header Connection 'upgrade'; proxy_set_header Host \$host; proxy_set_header X-Real-IP \$remote_addr; proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto \$scheme; proxy_cache_bypass \$http_upgrade; proxy_buffering off; proxy_read_timeout 86400s; proxy_send_timeout 86400s; } } EOF ln -sf /etc/nginx/sites-available/databasus /etc/nginx/sites-enabled/databasus rm -f /etc/nginx/sites-enabled/default $STD nginx -t $STD systemctl enable -q --now nginx $STD systemctl reload nginx msg_ok "Configured Nginx" motd_ssh customize cleanup_lxc ================================================ FILE: install/dawarich-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/Freika/dawarich source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ build-essential \ cmake \ git \ imagemagick \ libffi-dev \ libgeos-dev \ libgeos++-dev \ libjemalloc2 \ libjemalloc-dev \ libmagickwand-dev \ libpq-dev \ libssl-dev \ libvips-dev \ libxml2-dev \ libxslt-dev \ libyaml-dev \ nginx \ redis-server msg_ok "Installed Dependencies" PG_VERSION="17" PG_MODULES="postgis-3" setup_postgresql PG_DB_NAME="dawarich_db" PG_DB_USER="dawarich" PG_DB_EXTENSIONS="postgis" setup_postgresql_db fetch_and_deploy_gh_release "dawarich" "Freika/dawarich" "tarball" "latest" "/opt/dawarich/app" msg_info "Setting up Directories" mkdir -p /opt/dawarich/app/{storage,log,tmp/pids,tmp/cache,tmp/sockets} msg_ok "Set up Directories" msg_info "Configuring Environment" SECRET_KEY_BASE=$(openssl rand -hex 64) OTP_ENCRYPTION_PRIMARY_KEY=$(openssl rand -hex 64) OTP_ENCRYPTION_DETERMINISTIC_KEY=$(openssl rand -hex 64) OTP_ENCRYPTION_KEY_DERIVATION_SALT=$(openssl rand -hex 64) RELEASE=$(get_latest_github_release "Freika/dawarich") cat </opt/dawarich/.env RAILS_ENV=production SECRET_KEY_BASE=${SECRET_KEY_BASE} OTP_ENCRYPTION_PRIMARY_KEY=${OTP_ENCRYPTION_PRIMARY_KEY} OTP_ENCRYPTION_DETERMINISTIC_KEY=${OTP_ENCRYPTION_DETERMINISTIC_KEY} OTP_ENCRYPTION_KEY_DERIVATION_SALT=${OTP_ENCRYPTION_KEY_DERIVATION_SALT} DATABASE_HOST=localhost DATABASE_USERNAME=${PG_DB_USER} DATABASE_PASSWORD=${PG_DB_PASS} DATABASE_NAME=${PG_DB_NAME} REDIS_URL=redis://127.0.0.1:6379/0 BACKGROUND_PROCESSING_CONCURRENCY=10 APPLICATION_HOST=${LOCAL_IP} APPLICATION_HOSTS=${LOCAL_IP},localhost TIME_ZONE=UTC DISABLE_TELEMETRY=true APP_VERSION=${RELEASE} EOF msg_ok "Configured Environment" NODE_VERSION="22" setup_nodejs RUBY_VERSION=$(cat /opt/dawarich/app/.ruby-version 2>/dev/null || echo "3.4.6") RUBY_VERSION=${RUBY_VERSION} RUBY_INSTALL_RAILS="false" setup_ruby msg_info "Installing Dawarich" cd /opt/dawarich/app source /root/.profile export PATH="/root/.rbenv/shims:/root/.rbenv/bin:$PATH" eval "$(/root/.rbenv/bin/rbenv init - bash)" set -a && source /opt/dawarich/.env && set +a $STD gem install bundler $STD bundle config set --local deployment 'true' $STD bundle config set --local without 'development test' $STD bundle install if [[ -f /opt/dawarich/package.json ]]; then cd /opt/dawarich $STD npm install cd /opt/dawarich/app elif [[ -f /opt/dawarich/app/package.json ]]; then $STD npm install fi $STD bundle exec rake assets:precompile $STD bundle exec rails db:schema:load $STD bundle exec rails db:seed || msg_warn "Database seed failed (upstream rgeo-geojson issue), app will still work" $STD bundle exec rake data:migrate msg_ok "Installed Dawarich" msg_info "Creating Services" cat </etc/systemd/system/dawarich-web.service [Unit] Description=Dawarich Web Server After=network.target postgresql.service redis-server.service Requires=postgresql.service redis-server.service [Service] Type=simple WorkingDirectory=/opt/dawarich/app EnvironmentFile=/opt/dawarich/.env ExecStart=/root/.rbenv/shims/bundle exec puma -C config/puma.rb Restart=always RestartSec=5 [Install] WantedBy=multi-user.target EOF cat </etc/systemd/system/dawarich-worker.service [Unit] Description=Dawarich Sidekiq Worker After=network.target postgresql.service redis-server.service Requires=postgresql.service redis-server.service [Service] Type=simple WorkingDirectory=/opt/dawarich/app EnvironmentFile=/opt/dawarich/.env ExecStart=/root/.rbenv/shims/bundle exec sidekiq -C config/sidekiq.yml Restart=always RestartSec=5 [Install] WantedBy=multi-user.target EOF systemctl enable -q --now redis-server dawarich-web dawarich-worker msg_ok "Created Services" msg_info "Configuring Nginx" cat </etc/nginx/sites-available/dawarich.conf upstream dawarich { server 127.0.0.1:3000; } server { listen 80; server_name _; root /opt/dawarich/app/public; client_max_body_size 100M; location ~ ^/(assets|packs)/ { expires max; add_header Cache-Control "public, immutable"; try_files \$uri =404; } location / { try_files \$uri @rails; } location @rails { proxy_pass http://dawarich; proxy_http_version 1.1; proxy_set_header Host \$host; proxy_set_header X-Real-IP \$remote_addr; proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto \$scheme; proxy_set_header Upgrade \$http_upgrade; proxy_set_header Connection "upgrade"; proxy_redirect off; proxy_buffering off; } } EOF ln -sf /etc/nginx/sites-available/dawarich.conf /etc/nginx/sites-enabled/ rm -f /etc/nginx/sites-enabled/default systemctl enable -q --now nginx msg_ok "Configured Nginx" motd_ssh customize cleanup_lxc ================================================ FILE: install/ddclient-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 mitchscobell # Author: mitchscobell # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://ddclient.net/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing ddclient" DEBIAN_FRONTEND=noninteractive $STD apt -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" install -y ddclient msg_ok "Installed ddclient" msg_info "Creating ddclient service" cat << EOF >/etc/ddclient.conf protocol=namecheap use=web, web=dynamicdns.park-your-domain.com/getip protocol=namecheap use=web, web=dynamicdns.park-your-domain.com/getip server=dynamicdns.park-your-domain.com login=yourdomain.com password='your-ddns-password' @,www EOF chmod 600 /etc/ddclient.conf systemctl enable -q --now ddclient msg_ok "Created ddclient service" motd_ssh customize cleanup_lxc ================================================ FILE: install/debian-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.debian.org/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os motd_ssh customize cleanup_lxc ================================================ FILE: install/deconz-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.phoscon.de/en/conbee2/software#deconz source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Setting Phoscon Repository" setup_deb822_repo \ "deconz" \ "http://phoscon.de/apt/deconz.pub.key" \ "http://phoscon.de/apt/deconz" \ "generic" msg_ok "Setup Phoscon Repository" msg_info "Installing deConz" curl -fsSL "http://ports.ubuntu.com/pool/main/o/openssl/libssl1.1_1.1.1f-1ubuntu2.24_arm64.deb" -o $(basename "http://ports.ubuntu.com/pool/main/o/openssl/libssl1.1_1.1.1f-1ubuntu2.24_arm64.deb") $STD dpkg -i libssl1.1_1.1.1f-1ubuntu2.24_arm64.deb $STD apt-get update $STD apt-get install -y deconz rm -rf "$libssl" msg_ok "Installed deConz" msg_info "Creating Service" cat </lib/systemd/system/deconz.service [Unit] Description=deCONZ: ZigBee gateway -- REST API Wants=deconz-init.service deconz-update.service StartLimitIntervalSec=0 [Service] User=root ExecStart=/usr/bin/deCONZ -platform minimal --http-port=80 Restart=on-failure RestartSec=30 AmbientCapabilities=CAP_NET_BIND_SERVICE CAP_KILL CAP_SYS_BOOT CAP_SYS_TIME [Install] WantedBy=multi-user.target EOF systemctl enable -q --now deconz msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/deluge-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.deluge-torrent.org/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ python3-pip \ python3-libtorrent \ python3-setuptools msg_ok "Installed Dependencies" msg_info "Installing Deluge" mkdir -p ~/.config/pip cat >~/.config/pip/pip.conf </etc/systemd/system/deluged.service [Unit] Description=Deluge Bittorrent Client Daemon Documentation=man:deluged After=network-online.target [Service] Type=simple UMask=007 ExecStart=/usr/local/bin/deluged -d Restart=on-failure TimeoutStopSec=300 [Install] WantedBy=multi-user.target EOF cat </etc/systemd/system/deluge-web.service [Unit] Description=Deluge Bittorrent Client Web Interface Documentation=man:deluge-web After=deluged.service Wants=deluged.service [Service] Type=simple UMask=027 ExecStart=/usr/local/bin/deluge-web -d Restart=on-failure [Install] WantedBy=multi-user.target EOF systemctl enable --now -q deluged.service deluge-web.service msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/discopanel-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: DragoQC # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://discopanel.app/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os fetch_and_deploy_gh_release "discopanel" "nickheyer/discopanel" "prebuild" "latest" "/opt/discopanel" "discopanel-linux-amd64.tar.gz" setup_docker msg_info "Creating Service" cat </etc/systemd/system/discopanel.service [Unit] Description=DiscoPanel Service After=network.target [Service] WorkingDirectory=/opt/discopanel ExecStart=/opt/discopanel/discopanel-linux-amd64 Restart=always [Install] WantedBy=multi-user.target EOF systemctl enable -q --now discopanel msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/dispatcharr-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: ekke85 # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/Dispatcharr/Dispatcharr source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os setup_hwaccel msg_info "Installing Dependencies" $STD apt install -y \ build-essential \ python3-dev \ libpq-dev \ nginx \ redis-server \ ffmpeg \ procps \ vlc-bin \ vlc-plugin-base \ streamlink msg_ok "Installed Dependencies" setup_uv NODE_VERSION="24" setup_nodejs PG_VERSION="16" setup_postgresql PG_DB_NAME="dispatcharr_db" PG_DB_USER="dispatcharr_usr" setup_postgresql_db fetch_and_deploy_gh_release "dispatcharr" "Dispatcharr/Dispatcharr" "tarball" msg_info "Installing Python Dependencies with uv" cd /opt/dispatcharr $STD uv venv --clear $STD uv sync $STD uv pip install gunicorn gevent celery redis daphne msg_ok "Installed Python Dependencies" msg_info "Configuring Dispatcharr" install -d -m 755 \ /data/{logos,recordings,plugins,db} \ /data/uploads/{m3us,epgs} \ /data/{m3us,epgs} chown -R root:root /data DJANGO_SECRET=$(openssl rand -base64 48 | tr -dc 'a-zA-Z0-9' | cut -c1-50) export DATABASE_URL="postgresql://${PG_DB_USER}:${PG_DB_PASS}@localhost:5432/${PG_DB_NAME}" export POSTGRES_DB=$PG_DB_NAME export POSTGRES_USER=$PG_DB_USER export POSTGRES_PASSWORD=$PG_DB_PASS export POSTGRES_HOST=localhost export DJANGO_SECRET_KEY=$DJANGO_SECRET $STD uv run python manage.py migrate --noinput $STD uv run python manage.py collectstatic --noinput cat </opt/dispatcharr/.env DATABASE_URL=postgresql://${PG_DB_USER}:${PG_DB_PASS}@localhost:5432/${PG_DB_NAME} POSTGRES_DB=$PG_DB_NAME POSTGRES_USER=$PG_DB_USER POSTGRES_PASSWORD=$PG_DB_PASS POSTGRES_HOST=localhost CELERY_BROKER_URL=redis://localhost:6379/0 DJANGO_SECRET_KEY=$DJANGO_SECRET EOF cd /opt/dispatcharr/frontend node -e "const p=require('./package.json');p.overrides=p.overrides||{};p.overrides['webworkify-webpack']='2.1.3';require('fs').writeFileSync('package.json',JSON.stringify(p,null,2));" rm -f package-lock.json $STD npm install --no-audit --progress=false $STD npm run build msg_ok "Configured Dispatcharr" msg_info "Configuring Nginx" cat </etc/nginx/sites-available/dispatcharr.conf server { listen 9191; server_name _; client_max_body_size 100M; # Serve static assets with correct MIME types location /assets/ { alias /opt/dispatcharr/frontend/dist/assets/; expires 30d; add_header Cache-Control "public, immutable"; # Explicitly set MIME types for webpack-built assets types { text/javascript js; text/css css; image/png png; image/svg+xml svg svgz; font/woff2 woff2; font/woff woff; font/ttf ttf; } } location /static/ { alias /opt/dispatcharr/static/; expires 30d; add_header Cache-Control "public, immutable"; } location /media/ { alias /opt/dispatcharr/media/; } location /ws/ { proxy_pass http://127.0.0.1:8001; proxy_http_version 1.1; proxy_set_header Upgrade \$http_upgrade; proxy_set_header Connection "Upgrade"; proxy_set_header Host \$host; proxy_set_header X-Real-IP \$remote_addr; proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto \$scheme; } # All other requests proxy to Gunicorn location / { include proxy_params; proxy_pass http://127.0.0.1:5656; } } EOF ln -sf /etc/nginx/sites-available/dispatcharr.conf /etc/nginx/sites-enabled/dispatcharr.conf rm -f /etc/nginx/sites-enabled/default systemctl restart nginx msg_ok "Configured Nginx" msg_info "Creating Services" cat </opt/dispatcharr/start-gunicorn.sh #!/usr/bin/env bash cd /opt/dispatcharr set -a source .env set +a exec uv run gunicorn \\ --workers=4 \\ --worker-class=gevent \\ --timeout=300 \\ --bind 0.0.0.0:5656 \\ dispatcharr.wsgi:application EOF chmod +x /opt/dispatcharr/start-gunicorn.sh cat </opt/dispatcharr/start-celery.sh #!/usr/bin/env bash cd /opt/dispatcharr set -a source .env set +a exec uv run celery -A dispatcharr worker -l info -c 4 EOF chmod +x /opt/dispatcharr/start-celery.sh cat </opt/dispatcharr/start-celerybeat.sh #!/usr/bin/env bash cd /opt/dispatcharr set -a source .env set +a exec uv run celery -A dispatcharr beat -l info EOF chmod +x /opt/dispatcharr/start-celerybeat.sh cat </opt/dispatcharr/start-daphne.sh #!/usr/bin/env bash cd /opt/dispatcharr set -a source .env set +a exec uv run daphne -b 0.0.0.0 -p 8001 dispatcharr.asgi:application EOF chmod +x /opt/dispatcharr/start-daphne.sh cat </etc/systemd/system/dispatcharr.service [Unit] Description=Dispatcharr Web Server After=network.target postgresql.service redis-server.service [Service] Type=simple WorkingDirectory=/opt/dispatcharr ExecStart=/opt/dispatcharr/start-gunicorn.sh Restart=on-failure RestartSec=10 User=root [Install] WantedBy=multi-user.target EOF cat </etc/systemd/system/dispatcharr-celery.service [Unit] Description=Dispatcharr Celery Worker After=network.target redis-server.service Requires=dispatcharr.service [Service] Type=simple WorkingDirectory=/opt/dispatcharr ExecStart=/opt/dispatcharr/start-celery.sh Restart=on-failure RestartSec=10 User=root [Install] WantedBy=multi-user.target EOF cat </etc/systemd/system/dispatcharr-celerybeat.service [Unit] Description=Dispatcharr Celery Beat Scheduler After=network.target redis-server.service Requires=dispatcharr.service [Service] Type=simple WorkingDirectory=/opt/dispatcharr ExecStart=/opt/dispatcharr/start-celerybeat.sh Restart=on-failure RestartSec=10 User=root [Install] WantedBy=multi-user.target EOF cat </etc/systemd/system/dispatcharr-daphne.service [Unit] Description=Dispatcharr WebSocket Server (Daphne) After=network.target Requires=dispatcharr.service [Service] Type=simple WorkingDirectory=/opt/dispatcharr ExecStart=/opt/dispatcharr/start-daphne.sh Restart=on-failure RestartSec=10 User=root [Install] WantedBy=multi-user.target EOF systemctl enable -q --now dispatcharr dispatcharr-celery dispatcharr-celerybeat dispatcharr-daphne msg_ok "Created Services" motd_ssh customize cleanup_lxc ================================================ FILE: install/docker-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.docker.com/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os DOCKER_LATEST_VERSION=$(get_latest_github_release "moby/moby") PORTAINER_LATEST_VERSION=$(get_latest_github_release "portainer/portainer") PORTAINER_AGENT_LATEST_VERSION=$(get_latest_github_release "portainer/agent") msg_info "Installing Docker $DOCKER_LATEST_VERSION (with Compose, Buildx)" DOCKER_CONFIG_PATH='/etc/docker/daemon.json' mkdir -p $(dirname $DOCKER_CONFIG_PATH) echo -e '{\n "log-driver": "journald"\n}' >/etc/docker/daemon.json $STD sh <(curl -fsSL https://get.docker.com) msg_ok "Installed Docker $DOCKER_LATEST_VERSION" read -r -p "${TAB3}Would you like to add Portainer (UI)? " prompt if [[ ${prompt,,} =~ ^(y|yes)$ ]]; then msg_info "Installing Portainer $PORTAINER_LATEST_VERSION" docker volume create portainer_data >/dev/null $STD docker run -d \ -p 8000:8000 \ -p 9443:9443 \ --name=portainer \ --restart=always \ -v /var/run/docker.sock:/var/run/docker.sock \ -v portainer_data:/data \ portainer/portainer-ce:latest msg_ok "Installed Portainer $PORTAINER_LATEST_VERSION" else read -r -p "${TAB3}Would you like to install the Portainer Agent (for remote management)? " prompt_agent if [[ ${prompt_agent,,} =~ ^(y|yes)$ ]]; then msg_info "Installing Portainer Agent $PORTAINER_AGENT_LATEST_VERSION" $STD docker run -d \ -p 9001:9001 \ --name portainer_agent \ --restart=always \ -v /var/run/docker.sock:/var/run/docker.sock \ -v /var/lib/docker/volumes:/var/lib/docker/volumes \ portainer/agent msg_ok "Installed Portainer Agent $PORTAINER_AGENT_LATEST_VERSION" fi fi read -r -p "${TAB3}Expose Docker TCP socket (insecure) ? [n = No, l = Local only (127.0.0.1), a = All interfaces (0.0.0.0)] : " socket_choice case "${socket_choice,,}" in l) socket="tcp://127.0.0.1:2375" ;; a) socket="tcp://0.0.0.0:2375" ;; *) socket="" ;; esac if [[ -n "$socket" ]]; then msg_info "Enabling Docker TCP socket on $socket" $STD apt-get install -y jq tmpfile=$(mktemp) jq --arg sock "$socket" '. + { "hosts": ["unix:///var/run/docker.sock", $sock] }' /etc/docker/daemon.json >"$tmpfile" && mv "$tmpfile" /etc/docker/daemon.json mkdir -p /etc/systemd/system/docker.service.d cat </etc/systemd/system/docker.service.d/override.conf [Service] ExecStart= ExecStart=/usr/bin/dockerd EOF $STD systemctl daemon-reexec $STD systemctl daemon-reload if systemctl restart docker; then msg_ok "Docker TCP socket available on $socket" else msg_error "Docker failed to restart. Check journalctl -xeu docker.service" exit 150 fi fi motd_ssh customize cleanup_lxc ================================================ FILE: install/docmost-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (Canbiz) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://docmost.com/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ redis \ make msg_ok "Installed Dependencies" NODE_VERSION="22" NODE_MODULE="pnpm@$(curl -s https://raw.githubusercontent.com/docmost/docmost/main/package.json | jq -r '.packageManager | split("@")[1]')" setup_nodejs PG_VERSION="16" setup_postgresql PG_DB_NAME="docmost_db" PG_DB_USER="docmost_user" setup_postgresql_db fetch_and_deploy_gh_release "docmost" "docmost/docmost" "tarball" msg_info "Configuring Docmost (Patience)" cd /opt/docmost # Fix: Docmost EE (audit logs etc.) lives in a git submodule that is NOT # included in GitHub tarballs. The community NoopAuditService exists but # is only exported by CoreModule – child modules such as UserModule cannot # resolve it. Making CoreModule @Global() exposes the token app-wide. if [[ ! -f /opt/docmost/apps/server/src/ee/ee.module.ts ]] \ && ! grep -q '@Global()' /opt/docmost/apps/server/src/core/core.module.ts 2>/dev/null; then sed -i '/^ Module,$/a\ Global,' /opt/docmost/apps/server/src/core/core.module.ts sed -i '/^@Module({$/i @Global()' /opt/docmost/apps/server/src/core/core.module.ts fi mv .env.example .env mkdir data sed -i -e "s|APP_SECRET=.*|APP_SECRET=$(openssl rand -base64 32 | tr -dc 'a-zA-Z0-9' | cut -c1-32)|" \ -e "s|DATABASE_URL=.*|DATABASE_URL=\"postgres://$PG_DB_USER:$PG_DB_PASS@localhost:5432/$PG_DB_NAME?schema=public\"|" \ -e "s|FILE_UPLOAD_SIZE_LIMIT=.*|FILE_UPLOAD_SIZE_LIMIT=50mb|" \ -e "s|DRAWIO_URL=.*|DRAWIO_URL=https://embed.diagrams.net|" \ -e "s|DISABLE_TELEMETRY=.*|DISABLE_TELEMETRY=true|" \ -e "s|APP_URL=.*|APP_URL=http://$LOCAL_IP:3000|" \ /opt/docmost/.env export NODE_OPTIONS="--max-old-space-size=2048" $STD pnpm install $STD pnpm build msg_ok "Configured Docmost" msg_info "Creating Service" cat </etc/systemd/system/docmost.service [Unit] Description=Docmost Service After=network.target postgresql.service [Service] WorkingDirectory=/opt/docmost ExecStart=/usr/bin/pnpm start Restart=always EnvironmentFile=/opt/docmost/.env [Install] WantedBy=multi-user.target EOF systemctl enable -q --now docmost msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/dolibarr-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: Slaviša Arežina (tremor021) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/Dolibarr/dolibarr/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt-get install -y \ php-imap \ debconf-utils msg_ok "Installed Dependencies" setup_mariadb msg_info "Setting up Database" ROOT_PASS=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | head -c13) $STD mariadb -u root -e "ALTER USER 'root'@'localhost' IDENTIFIED BY '$ROOT_PASS'; flush privileges;" { echo "Dolibarr DB Credentials" echo "MariaDB Root Password: $ROOT_PASS" } >>~/dolibarr.creds msg_ok "Set up database" msg_info "Setup Dolibarr" BASE="https://sourceforge.net/projects/dolibarr/files/Dolibarr%20installer%20for%20Debian-Ubuntu%20(DoliDeb)/" RELEASE=$(curl -fsSL "$BASE" | grep -oP '(?<=/Dolibarr%20installer%20for%20Debian-Ubuntu%20%28DoliDeb%29/)\d+(\.\d+)+(?=/)' | sort -V | tail -n1) FILE=$(curl -fsSL "${BASE}${RELEASE}/" | grep -oP 'dolibarr_[^"]+_all.deb' | head -n1) curl -fsSL "https://altushost-swe.dl.sourceforge.net/project/dolibarr/Dolibarr%20installer%20for%20Debian-Ubuntu%20(DoliDeb)/${RELEASE}/${FILE}?viasf=1" -o ""$FILE"" echo "dolibarr dolibarr/reconfigure-webserver multiselect apache2" | debconf-set-selections $STD apt-get install ./$FILE -y $STD apt install -f rm -rf ~/$FILE echo "${RELEASE}" >"/opt/${APPLICATION}_version.txt" msg_ok "Setup Dolibarr" motd_ssh customize cleanup_lxc ================================================ FILE: install/domain-locker-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: CrazyWolf13 # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/Lissy93/domain-locker source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing dependencies" $STD apt install -y whois msg_ok "Installed dependencies" PG_VERSION="17" setup_postgresql PG_DB_NAME="domainlocker_db" PG_DB_USER="domainlocker" setup_postgresql_db NODE_VERSION="22" setup_nodejs fetch_and_deploy_gh_release "domain-locker" "Lissy93/domain-locker" "tarball" msg_info "Installing Modules (patience)" cd /opt/domain-locker $STD npm install msg_ok "Installed Modules" msg_info "Building Domain-Locker (a lot of patience)" cat </opt/domain-locker.env # Database connection DL_PG_HOST=localhost DL_PG_PORT=5432 DL_PG_USER=$PG_DB_USER DL_PG_PASSWORD=$PG_DB_PASS DL_PG_NAME=$PG_DB_NAME # Build + Runtime DL_ENV_TYPE=selfHosted NITRO_PRESET=node_server NODE_ENV=production EOF set -a source /opt/domain-locker.env set +a $STD npm run build msg_info "Built Domain-Locker" msg_info "Building Database schema" export PGPASSWORD="$DL_PG_PASSWORD" $STD psql -h "$DL_PG_HOST" -p "$DL_PG_PORT" -U "$DL_PG_USER" -d "$DL_PG_NAME" -f "/opt/domain-locker/db/schema.sql" msg_ok "Built Database schema" msg_info "Creating Service" cat </etc/systemd/system/domain-locker.service [Unit] Description=Domain-Locker Service After=network.target [Service] EnvironmentFile=/opt/domain-locker.env WorkingDirectory=/opt/domain-locker ExecStart=/opt/domain-locker/start.sh Restart=always [Install] WantedBy=multi-user.target EOF systemctl enable -q --now domain-locker msg_info "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/domain-monitor-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: Slaviša Arežina (tremor021) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/Hosteroid/domain-monitor source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y --no-install-recommends \ libicu-dev \ libzip-dev \ libpng-dev \ libjpeg62-turbo-dev \ libfreetype6-dev \ libxml2-dev \ libcurl4-openssl-dev \ libonig-dev \ pkg-config msg_ok "Installed Dependencies" PHP_VERSION="8.4" PHP_APACHE="YES" PHP_FPM="YES" setup_php setup_composer setup_mariadb MARIADB_DB_NAME="domain_monitor" MARIADB_DB_USER="domainmonitor" setup_mariadb_db fetch_and_deploy_gh_release "domain-monitor" "Hosteroid/domain-monitor" "prebuild" "latest" "/opt/domain-monitor" "domain-monitor-v*.zip" msg_info "Setting up Domain Monitor" ENC_KEY=$(openssl rand -base64 32 | tr -d '\n') cd /opt/domain-monitor $STD composer install cp env.example.txt .env sed -i -e "s|^APP_ENV=.*|APP_ENV=production|" \ -e "s|^APP_ENCRYPTION_KEY=.*|APP_ENCRYPTION_KEY=$ENC_KEY|" \ -e "s|^SESSION_COOKIE_HTTPONLY=.*|SESSION_COOKIE_HTTPONLY=0|" \ -e "s|^DB_USERNAME=.*|DB_USERNAME=$MARIADB_DB_USER|" \ -e "s|^DB_PASSWORD=.*|DB_PASSWORD=$MARIADB_DB_PASS|" \ -e "s|^DB_DATABASE=.*|DB_DATABASE=$MARIADB_DB_NAME|" .env echo "0 0 * * * www-data /usr/bin/php /opt/domain-monitor/cron/check_domains.php" >>/etc/crontab cat </etc/apache2/sites-enabled/000-default.conf ServerName domainmonitor.local DocumentRoot "/opt/domain-monitor/public" AllowOverride All Require all granted EOF chown -R www-data:www-data /opt/domain-monitor $STD a2enmod rewrite headers $STD systemctl reload apache2 msg_ok "Setup Domain Monitor" motd_ssh customize cleanup_lxc ================================================ FILE: install/donetick-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: fstof # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/donetick/donetick source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y ca-certificates msg_ok "Installed Dependencies" fetch_and_deploy_gh_release "donetick" "donetick/donetick" "prebuild" "latest" "/opt/donetick" "donetick_Linux_arm64.tar.gz" msg_info "Setup Donetick" cd /opt/donetick TOKEN=$(openssl rand -hex 16) sed -i -e "s/change_this_to_a_secure_random_string_32_characters_long/${TOKEN}/g" config/selfhosted.yaml msg_ok "Setup Donetick" msg_info "Creating Service" cat </etc/systemd/system/donetick.service [Unit] Description=donetick Service After=network.target [Service] Environment="DT_ENV=selfhosted" WorkingDirectory=/opt/donetick ExecStart=/opt/donetick/donetick Restart=always [Install] WantedBy=multi-user.target EOF systemctl enable -q --now donetick msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/dotnetaspwebapi-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: Kristian Skov # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://learn.microsoft.com/en-us/aspnet/core/host-and-deploy/linux-nginx?view=aspnetcore-9.0&tabs=linux-ubuntu source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt-get update $STD apt-get install -y \ ssh \ software-properties-common $STD add-apt-repository -y ppa:dotnet/backports $STD apt-get install -y \ dotnet-sdk-9.0 \ vsftpd \ nginx msg_ok "Installed Dependencies" var_project_name="default" read -r -p "${TAB3}Type the assembly name of the project: " var_project_name msg_info "Setting up FTP Server" useradd ftpuser FTP_PASS=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | head -c13) usermod --password $(echo ${FTP_PASS} | openssl passwd -1 -stdin) ftpuser mkdir -p /var/www/html usermod -d /var/www/html ftp usermod -d /var/www/html ftpuser chown ftpuser /var/www/html sed -i "s|#write_enable=YES|write_enable=YES|g" /etc/vsftpd.conf sed -i "s|#chroot_local_user=YES|chroot_local_user=NO|g" /etc/vsftpd.conf systemctl restart -q vsftpd.service { echo "FTP-Credentials" echo "Username: ftpuser" echo "Password: $FTP_PASS" } >>~/ftp.creds msg_ok "FTP server setup completed" msg_info "Setting up Nginx Server" rm -f /var/www/html/index.nginx-debian.html sed "s/\$var_project_name/$var_project_name/g" >myfile <<'EOF' >/etc/nginx/sites-available/default map $http_connection $connection_upgrade { "~*Upgrade" $http_connection; default keep-alive; } server { listen 80; server_name $var_project_name.com *.$var_project_name.com; location / { proxy_pass http://127.0.0.1:5000/; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $connection_upgrade; proxy_set_header Host $host; proxy_cache_bypass $http_upgrade; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } } EOF systemctl reload nginx msg_ok "Nginx Server Created" msg_info "Creating Service" cat </etc/systemd/system/kestrel-aspnetapi.service [Unit] Description=.NET Web API App running on Linux [Service] WorkingDirectory=/var/www/html ExecStart=/usr/bin/dotnet /var/www/html/$var_project_name.dll Restart=always # Restart service after 10 seconds if the dotnet service crashes: RestartSec=10 KillSignal=SIGINT SyslogIdentifier=dotnet-${var_project_name} User=root Environment=ASPNETCORE_ENVIRONMENT=Production Environment=DOTNET_NOLOGO=true [Install] WantedBy=multi-user.target EOF systemctl enable -q --now kestrel-aspnetapi msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/drawdb-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/drawdb-io/drawdb source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y nginx msg_ok "Installed Dependencies" NODE_VERSION="20" setup_nodejs fetch_and_deploy_gh_tag "drawdb" "drawdb-io/drawdb" "latest" "/opt/drawdb" msg_info "Building Frontend" cd /opt/drawdb $STD npm ci NODE_OPTIONS="--max-old-space-size=4096" $STD npm run build msg_ok "Built Frontend" msg_info "Applying crypto.randomUUID Polyfill" sed -i '//a ' /opt/drawdb/dist/index.html msg_ok "Applied Polyfill" msg_info "Configuring Nginx" cat </etc/nginx/conf.d/drawdb.conf server { listen 3000; server_name _; root /opt/drawdb/dist; location / { try_files \$uri /index.html; } } EOF rm -f /etc/nginx/sites-enabled/default systemctl enable -q --now nginx systemctl reload nginx msg_ok "Configured Nginx" motd_ssh customize cleanup_lxc ================================================ FILE: install/drawio-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: Slaviša Arežina (tremor021) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.drawio.com/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os setup_hwaccel msg_info "Installing Dependencies" $STD apt install -y tomcat11 msg_ok "Installed Dependencies" USE_ORIGINAL_FILENAME=true fetch_and_deploy_gh_release "drawio" "jgraph/drawio" "singlefile" "latest" "/var/lib/tomcat11/webapps" "draw.war" motd_ssh customize cleanup_lxc ================================================ FILE: install/duplicati-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: tremor021 # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/duplicati/duplicati/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ libice6 \ libsm6 \ libfontconfig1 msg_ok "Installed Dependencies" fetch_and_deploy_gh_release "duplicati" "duplicati/duplicati" "binary" "latest" "/opt/duplicati" "duplicati-*-linux-arm64-gui.deb" msg_info "Configuring duplicati" DECRYPTKEY=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | head -c13) ADMINPASS=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | head -c13) { echo "Admin password = ${ADMINPASS}" echo "Database encryption key = ${DECRYPTKEY}" } >>~/duplicati.creds msg_ok "Configured duplicati" msg_info "Creating Service" cat </etc/systemd/system/duplicati.service [Unit] Description=Duplicati Service After=network.target [Service] ExecStart=/usr/bin/duplicati-server --webservice-interface=any --webservice-password=$ADMINPASS --settings-encryption-key=$DECRYPTKEY Restart=always [Install] WantedBy=multi-user.target EOF systemctl enable -q --now duplicati msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/ebusd-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: Joerg Heinemann (heinemannj) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/john30/ebusd source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing ebusd" fetch_and_deploy_gh_release "ebusd" "john30/ebusd" "binary" "latest" "" "ebusd-*_arm64-trixie_mqtt1.deb" systemctl enable -q ebusd msg_ok "Installed ebusd" motd_ssh customize cleanup_lxc ================================================ FILE: install/elementsynapse-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: tremor021 # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/element-hq/synapse source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ apt-transport-https \ debconf-utils msg_ok "Installed Dependencies" NODE_VERSION="22" NODE_MODULE="yarn" setup_nodejs echo "${TAB3}It is important to choose the name for your server before you install Synapse, because it cannot be changed later." echo "${TAB3}The server name determines the “domain” part of user-ids for users on your server: these will all be of the format @user:my.domain.name. It also determines how other matrix servers will reach yours for federation." read -p "${TAB3}Please enter the name for your server: " servername msg_info "Installing Element Synapse" setup_deb822_repo "matrix-org" \ "https://packages.matrix.org/debian/matrix-org-archive-keyring.gpg" \ "https://packages.matrix.org/debian/" \ "$(get_os_info codename)" \ "main" echo "matrix-synapse-py3 matrix-synapse/server-name string $servername" | debconf-set-selections echo "matrix-synapse-py3 matrix-synapse/report-stats boolean false" | debconf-set-selections echo "exit 101" >/usr/sbin/policy-rc.d chmod +x /usr/sbin/policy-rc.d $STD apt install matrix-synapse-py3 -y rm -f /usr/sbin/policy-rc.d sed -i 's/127.0.0.1/0.0.0.0/g' /etc/matrix-synapse/homeserver.yaml sed -i 's/'\''::1'\'', //g' /etc/matrix-synapse/homeserver.yaml SECRET=$(openssl rand -hex 32) ADMIN_PASS="$(openssl rand -base64 18 | cut -c1-13)" echo "enable_registration_without_verification: true" >>/etc/matrix-synapse/homeserver.yaml echo "registration_shared_secret: ${SECRET}" >>/etc/matrix-synapse/homeserver.yaml cat <>/etc/matrix-synapse/homeserver.yaml # MatrixRTC / Element Call configuration experimental_features: msc3266_enabled: true msc4222_enabled: true max_event_delay_duration: 24h rc_message: per_second: 0.5 burst_count: 30 rc_delayed_event_mgmt: per_second: 1 burst_count: 20 EOF systemctl enable -q --now matrix-synapse $STD register_new_matrix_user -a --user admin --password "$ADMIN_PASS" --config /etc/matrix-synapse/homeserver.yaml { echo "Matrix-Credentials" echo "Admin username: admin" echo "Admin password: $ADMIN_PASS" } >>~/matrix.creds systemctl stop matrix-synapse sed -i '34d' /etc/matrix-synapse/homeserver.yaml systemctl start matrix-synapse msg_ok "Installed Element Synapse" fetch_and_deploy_gh_release "synapse-admin" "etkecc/synapse-admin" "tarball" msg_info "Installing Synapse-Admin" cd /opt/synapse-admin $STD yarn global add serve $STD yarn install --ignore-engines $STD yarn build mv ./dist ../ && rm -rf * && mv ../dist ./ msg_ok "Installed Synapse-Admin" msg_info "Creating Service" cat </etc/systemd/system/synapse-admin.service [Unit] Description=Synapse-Admin Service After=network.target Requires=matrix-synapse.service [Service] Type=simple WorkingDirectory=/opt/synapse-admin ExecStart=/usr/local/bin/serve -s dist -l 5173 Restart=always [Install] WantedBy=multi-user.target EOF systemctl enable -q --now synapse-admin msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/emby-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://emby.media/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os fetch_and_deploy_gh_release "emby" "MediaBrowser/Emby.Releases" "binary" setup_hwaccel "emby" motd_ssh customize cleanup_lxc ================================================ FILE: install/emqx-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.emqx.com/en source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing dependencies" $STD apt install -y ca-certificates msg_ok "Installed dependencies" msg_info "Fetching latest EMQX Enterprise version" LATEST_VERSION=$(curl -fsSL https://www.emqx.com/en/downloads/enterprise | grep -oP '/en/downloads/enterprise/v\K[0-9]+\.[0-9]+\.[0-9]+' | sort -V | tail -n1) if [[ -z "$LATEST_VERSION" ]]; then msg_error "Failed to determine latest EMQX version" exit 250 fi msg_ok "Latest version: v$LATEST_VERSION" DOWNLOAD_URL="https://www.emqx.com/en/downloads/enterprise/v$LATEST_VERSION/emqx-enterprise-${LATEST_VERSION}-debian12-arm64.deb" DEB_FILE="/tmp/emqx-enterprise-${LATEST_VERSION}-debian12-arm64.deb" msg_info "Downloading EMQX v$LATEST_VERSION" $STD curl -fsSL -o "$DEB_FILE" "$DOWNLOAD_URL" msg_ok "Downloaded EMQX" msg_info "Installing EMQX" $STD apt install -y "$DEB_FILE" rm -f "$DEB_FILE" echo "$LATEST_VERSION" >~/.emqx msg_ok "Installed EMQX" read -r -p "${TAB3}Would you like to disable the EMQX MQ feature? (reduces disk/CPU usage) " prompt if [[ ${prompt,,} =~ ^(y|yes)$ ]]; then msg_info "Disabling EMQX MQ feature" mkdir -p /etc/emqx if ! grep -q "^mq.enable" /etc/emqx/emqx.conf 2>/dev/null; then echo "mq.enable = false" >>/etc/emqx/emqx.conf else sed -i 's/^mq.enable.*/mq.enable = false/' /etc/emqx/emqx.conf fi msg_ok "Disabled EMQX MQ feature" fi msg_info "Starting EMQX service" $STD systemctl enable -q --now emqx msg_ok "Enabled EMQX service" motd_ssh customize cleanup_lxc ================================================ FILE: install/endurain-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: johanngrobe # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/joaovitoriasilva/endurain source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y build-essential msg_ok "Installed Dependencies" PYTHON_VERSION="3.13" setup_uv NODE_VERSION="24" setup_nodejs PG_VERSION="17" PG_MODULES="postgis" setup_postgresql PG_DB_NAME="enduraindb" PG_DB_USER="endurain" setup_postgresql_db fetch_and_deploy_codeberg_release "endurain" "endurain-project/endurain" "tarball" "latest" "/opt/endurain" msg_info "Setting up Endurain" cd /opt/endurain rm -rf \ /opt/endurain/{docs,example.env,screenshot_01.png} \ /opt/endurain/docker* \ /opt/endurain/*.yml mkdir -p /opt/endurain_data/{data,logs} SECRET_KEY=$(openssl rand -hex 32) FERNET_KEY=$(openssl rand -base64 32) ENDURAIN_HOST=http://${LOCAL_IP}:8080 cat </opt/endurain/.env DB_PASSWORD=${PG_DB_PASS} SECRET_KEY=${SECRET_KEY} FERNET_KEY=${FERNET_KEY} TZ=Europe/Berlin ENDURAIN_HOST=${ENDURAIN_HOST} BEHIND_PROXY=false POSTGRES_DB=${PG_DB_NAME} POSTGRES_USER=${PG_DB_USER} PGDATA=/var/lib/postgresql/${PG_DB_NAME} DB_DATABASE=${PG_DB_NAME} DB_USER=${PG_DB_USER} DB_PORT=5432 DB_HOST=localhost DATABASE_URL=postgresql+psycopg://${PG_DB_USER}:${PG_DB_PASS}@localhost:5432/${PG_DB_NAME} BACKEND_DIR="/opt/endurain/backend/app" FRONTEND_DIR="/opt/endurain/frontend/app/dist" DATA_DIR="/opt/endurain_data/data" LOGS_DIR="/opt/endurain_data/logs" #SMTP_HOST=smtp.protonmail.ch #SMTP_PORT=587 #SMTP_USERNAME=your-email@example.com #SMTP_PASSWORD=your-app-password #SMTP_SECURE=true #SMTP_SECURE_TYPE=starttls EOF msg_ok "Setup Endurain" msg_info "Building Frontend" cd /opt/endurain/frontend/app $STD npm ci --prefer-offline $STD npm run build cat </opt/endurain/frontend/app/dist/env.js window.env = { ENDURAIN_HOST: "${ENDURAIN_HOST}" } EOF msg_ok "Built Frontend" msg_info "Setting up Backend" cd /opt/endurain/backend $STD uv tool install poetry $STD uv tool update-shell export PATH="/root/.local/bin:$PATH" $STD poetry self add poetry-plugin-export $STD poetry export -f requirements.txt --output requirements.txt --without-hashes $STD uv venv --clear $STD uv pip install -r requirements.txt msg_ok "Setup Backend" msg_info "Creating Service" cat </etc/systemd/system/endurain.service [Unit] Description=Endurain FastAPI Backend After=network.target postgresql.service [Service] WorkingDirectory=/opt/endurain/backend/app EnvironmentFile=/opt/endurain/.env ExecStart=/opt/endurain/backend/.venv/bin/uvicorn main:app --host 0.0.0.0 --port 8080 Restart=always RestartSec=5 User=root StandardOutput=journal StandardError=journal [Install] WantedBy=multi-user.target EOF systemctl enable -q --now endurain msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/erpnext-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/frappe/erpnext source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ git \ build-essential \ python3-dev \ libffi-dev \ libssl-dev \ redis-server \ nginx \ supervisor \ fail2ban \ xvfb \ libfontconfig1 \ libxrender1 \ fontconfig \ libjpeg-dev \ libmariadb-dev \ python3-pip msg_ok "Installed Dependencies" NODE_VERSION="24" NODE_MODULE="yarn" setup_nodejs UV_PYTHON="3.13" setup_uv setup_mariadb msg_info "Configuring MariaDB for ERPNext" cat </etc/mysql/mariadb.conf.d/50-erpnext.cnf [mysqld] character-set-server=utf8mb4 collation-server=utf8mb4_unicode_ci [client] default-character-set=utf8mb4 EOF $STD systemctl restart mariadb msg_ok "Configured MariaDB for ERPNext" msg_info "Installing wkhtmltopdf" WKHTMLTOPDF_URL="https://github.com/wkhtmltopdf/packaging/releases/download/0.12.6.1-3/wkhtmltox_0.12.6.1-3.bookworm_amd64.deb" $STD curl -fsSL -o /tmp/wkhtmltox.deb "$WKHTMLTOPDF_URL" $STD apt install -y /tmp/wkhtmltox.deb rm -f /tmp/wkhtmltox.deb msg_ok "Installed wkhtmltopdf" msg_info "Installing Frappe Bench" useradd -m -s /bin/bash frappe chown frappe:frappe /opt echo "frappe ALL=(ALL) NOPASSWD:ALL" >/etc/sudoers.d/frappe $STD sudo -u frappe bash -c 'export PATH="$HOME/.local/bin:$PATH"; uv tool install frappe-bench' msg_ok "Installed Frappe Bench" msg_info "Initializing Frappe Bench" ADMIN_PASS=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | head -c13) DB_ROOT_PASS=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | head -c13) mysql -u root -e "ALTER USER 'root'@'localhost' IDENTIFIED BY '${DB_ROOT_PASS}'; FLUSH PRIVILEGES;" $STD sudo -u frappe bash -c 'export PATH="$HOME/.local/bin:$PATH"; cd /opt && bench init --frappe-branch version-15 frappe-bench' $STD sudo -u frappe bash -c 'export PATH="$HOME/.local/bin:$PATH"; cd /opt/frappe-bench && bench get-app erpnext --branch version-15' msg_info "Starting Redis Services for Site Setup" $STD sudo -u frappe bash -c 'redis-server /opt/frappe-bench/config/redis_queue.conf --daemonize yes' $STD sudo -u frappe bash -c 'redis-server /opt/frappe-bench/config/redis_cache.conf --daemonize yes' sleep 3 msg_ok "Started Redis Services for Site Setup" $STD sudo -u frappe bash -c "export PATH=\"\$HOME/.local/bin:\$PATH\"; cd /opt/frappe-bench && bench new-site site1.local --db-root-username root --db-root-password \"$DB_ROOT_PASS\" --admin-password \"$ADMIN_PASS\" --install-app erpnext --set-default" msg_ok "Initialized Frappe Bench" msg_info "Configuring ERPNext" cat </opt/frappe-bench/.env ADMIN_PASSWORD=${ADMIN_PASS} DB_ROOT_PASSWORD=${DB_ROOT_PASS} SITE_NAME=site1.local EOF { echo "ERPNext Credentials" echo "==================" echo "Admin Username: Administrator" echo "Admin Password: ${ADMIN_PASS}" echo "DB Root Password: ${DB_ROOT_PASS}" echo "Site Name: site1.local" } >~/erpnext.creds $STD systemctl enable --now redis-server msg_ok "Configured ERPNext" msg_info "Setting up Production" BENCH_PY="/home/frappe/.local/share/uv/tools/frappe-bench/bin/python" $STD sudo -u frappe bash -c "curl -fsSL https://bootstrap.pypa.io/get-pip.py | \"${BENCH_PY}\"" $STD sudo -u frappe bash -c 'export PATH="$HOME/.local/bin:$PATH"; uv tool install ansible' ln -sf /home/frappe/.local/bin/ansible* /usr/local/bin/ $STD bash -c 'export PATH="/home/frappe/.local/bin:$PATH"; cd /opt/frappe-bench && bench setup production frappe --yes' ln -sf /opt/frappe-bench/config/supervisor.conf /etc/supervisor/conf.d/frappe-bench.conf $STD supervisorctl reread $STD supervisorctl update $STD systemctl enable --now supervisor msg_ok "Set up Production" motd_ssh customize cleanup_lxc ================================================ FILE: install/ersatztv-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: MickLesk (Canbiz) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://ersatztv.org/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os setup_hwaccel fetch_and_deploy_gh_release "ersatztv" "ErsatzTV/ErsatzTV" "prebuild" "latest" "/opt/ErsatzTV" "*linux-arm64.tar.gz" fetch_and_deploy_gh_release "ersatztv-ffmpeg" "ErsatzTV/ErsatzTV-ffmpeg" "prebuild" "latest" "/opt/ErsatzTV-ffmpeg" "*-linuxarm64-gpl-7.1.tar.xz" msg_info "Set ErsatzTV-ffmpeg links" chmod +x /opt/ErsatzTV-ffmpeg/bin/* ln -sf /opt/ErsatzTV-ffmpeg/bin/ffmpeg /usr/local/bin/ffmpeg ln -sf /opt/ErsatzTV-ffmpeg/bin/ffplay /usr/local/bin/ffplay ln -sf /opt/ErsatzTV-ffmpeg/bin/ffprobe /usr/local/bin/ffprobe msg_ok "ffmpeg links set" msg_info "Creating Service" cat </etc/systemd/system/ersatzTV.service [Unit] Description=ErsatzTV Service After=multi-user.target [Service] Type=simple User=root WorkingDirectory=/opt/ErsatzTV ExecStart=/opt/ErsatzTV/ErsatzTV Restart=always RestartSec=30 [Install] WantedBy=multi-user.target EOF systemctl enable -q --now ersatzTV msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/esphome-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://esphome.io/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y git msg_ok "Installed Dependencies" PYTHON_VERSION="3.12" setup_uv msg_info "Setting up Virtual Environment" mkdir -p /opt/esphome mkdir -p /root/config cd /opt/esphome $STD uv venv --clear /opt/esphome/.venv $STD /opt/esphome/.venv/bin/python -m ensurepip --upgrade $STD /opt/esphome/.venv/bin/python -m pip install --upgrade pip $STD /opt/esphome/.venv/bin/python -m pip install esphome tornado esptool msg_ok "Setup and Installed ESPHome" msg_info "Linking esphome to /usr/local/bin" rm -f /usr/local/bin/esphome ln -s /opt/esphome/.venv/bin/esphome /usr/local/bin/esphome msg_ok "Linked esphome binary" msg_info "Creating Service" mkdir -p /root/config cat </etc/systemd/system/esphomeDashboard.service [Unit] Description=ESPHome Dashboard After=network.target [Service] ExecStart=/opt/esphome/.venv/bin/esphome dashboard /root/config/ Restart=always User=root [Install] WantedBy=multi-user.target EOF systemctl enable -q --now esphomeDashboard msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/evcc-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck # Co-Author: MickLesk (Canbiz) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/evcc-io/evcc source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Setting up evcc Repository" setup_deb822_repo \ "evcc-stable" \ "https://dl.evcc.io/public/evcc/stable/gpg.EAD5D0E07B0EC0FD.key" \ "https://dl.evcc.io/public/evcc/stable/deb/debian/" \ "$(get_os_info codename)" \ "main" $STD apt update msg_ok "evcc Repository setup sucessfully" msg_info "Installing evcc" $STD apt install -y evcc systemctl enable -q --now evcc msg_ok "Installed evcc" motd_ssh customize cleanup_lxc ================================================ FILE: install/excalidraw-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: Slaviša Arežina (tremor021) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/excalidraw/excalidraw source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y xdg-utils msg_ok "Installed Dependencies" NODE_VERSION="22" NODE_MODULE="yarn" setup_nodejs fetch_and_deploy_gh_release "excalidraw" "excalidraw/excalidraw" "tarball" msg_info "Configuring Excalidraw" cd /opt/excalidraw $STD yarn msg_ok "Setup Excalidraw" msg_info "Creating Service" cat </etc/systemd/system/excalidraw.service [Unit] Description=Excalidraw Service After=network.target [Service] Type=simple WorkingDirectory=/opt/excalidraw ExecStart=/usr/bin/yarn start --host Restart=always [Install] WantedBy=multi-user.target EOF systemctl enable -q --now excalidraw msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/fhem-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://fhem.de/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y avahi-daemon msg_ok "Installed Dependencies" setup_deb822_repo \ "fhem" \ "https://debian.fhem.de/archive.key" \ "https://debian.fhem.de/nightly/" \ "/" \ " " msg_info "Setting up FHEM" $STD apt install -y fhem msg_ok "Setup FHEM" motd_ssh customize cleanup_lxc ================================================ FILE: install/fileflows-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: kkroboth # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://fileflows.com/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ ffmpeg \ pciutils \ imagemagick msg_ok "Installed Dependencies" setup_hwaccel msg_info "Installing ASP.NET Core Runtime" curl -SL -o aspnet.tar.gz https://builds.dotnet.microsoft.com/dotnet/aspnetcore/Runtime/8.0.16/aspnetcore-runtime-8.0.16-linux-arm64.tar.gz $STD mkdir -p /usr/share/dotnet $STD tar -zxf aspnet.tar.gz -C /usr/share/dotnet ln -s /usr/share/dotnet/dotnet /usr/bin/dotnet $STD rm -f aspnet.tar.gz msg_ok "Installed ASP.NET Core Runtime" fetch_and_deploy_from_url "https://fileflows.com/downloads/zip" "/opt/fileflows" $STD ln -svf /usr/bin/ffmpeg /usr/local/bin/ffmpeg $STD ln -svf /usr/bin/ffprobe /usr/local/bin/ffprobe $STD rm -rf /opt/fileflows/Server/runtimes/win-* read -r -p "${TAB3}Do you want to install FileFlows Server or Node? (S/N): " install_server if [[ "$install_server" =~ ^[Ss]$ ]]; then msg_info "Installing FileFlows Server" cd /opt/fileflows/Server $STD dotnet FileFlows.Server.dll --systemd install --root true systemctl enable -q --now fileflows msg_ok "Installed FileFlows Server" else msg_info "Installing FileFlows Node" cd /opt/fileflows/Node $STD dotnet FileFlows.Node.dll $STD dotnet FileFlows.Node.dll --systemd install --root true systemctl enable -q --now fileflows-node msg_ok "Installed FileFlows Node" fi motd_ssh customize cleanup_lxc ================================================ FILE: install/firefly-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: quantumryuu | Co-Author: Slaviša Arežina (tremor021) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://firefly-iii.org/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os PHP_VERSION="8.5" PHP_APACHE="YES" setup_php setup_composer setup_mariadb MARIADB_DB_NAME="firefly" MARIADB_DB_USER="firefly" setup_mariadb_db fetch_and_deploy_gh_release "firefly" "firefly-iii/firefly-iii" "prebuild" "latest" "/opt/firefly" "FireflyIII-*.zip" fetch_and_deploy_gh_release "dataimporter" "firefly-iii/data-importer" "prebuild" "latest" "/opt/firefly/dataimporter" "DataImporter-v*.tar.gz" msg_info "Configuring Firefly III (Patience)" chown -R www-data:www-data /opt/firefly chmod -R 775 /opt/firefly/storage cd /opt/firefly cp .env.example .env sed -i "s/DB_HOST=.*/DB_HOST=localhost/" /opt/firefly/.env sed -i "s/DB_PASSWORD=.*/DB_PASSWORD=$MARIADB_DB_PASS/" /opt/firefly/.env $STD composer install --no-dev --no-plugins --no-interaction $STD php artisan firefly:upgrade-database $STD php artisan firefly:correct-database $STD php artisan firefly:report-integrity $STD php artisan firefly:laravel-passport-keys msg_ok "Configured Firefly III" msg_info "Configuring Data Importer" cp /opt/firefly/dataimporter/.env.example /opt/firefly/dataimporter/.env sed -i "s#FIREFLY_III_URL=#FIREFLY_III_URL=http://${LOCAL_IP}#g" /opt/firefly/dataimporter/.env chown -R www-data:www-data /opt/firefly msg_ok "Configured Data Importer" msg_info "Creating Service" cat </etc/apache2/sites-available/firefly.conf ServerAdmin webmaster@localhost DocumentRoot /opt/firefly/public/ Options FollowSymLinks AllowOverride All Require all granted RedirectMatch 301 ^/dataimporter$ /dataimporter/ Alias /dataimporter/ /opt/firefly/dataimporter/public/ Options Indexes FollowSymLinks AllowOverride All Require all granted SetHandler application/x-httpd-php ErrorLog /var/log/apache2/error.log CustomLog /var/log/apache2/access.log combined EOF chown www-data:www-data /opt/firefly/storage/oauth-*.key $STD a2enmod php8.5 $STD a2enmod rewrite $STD a2ensite firefly.conf $STD a2dissite 000-default.conf $STD systemctl reload apache2 msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/fireshare-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: Slaviša Arežina (tremor021) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/ShaneIsrael/fireshare source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os setup_hwaccel msg_info "Installing Dependencies" $STD apt install -y \ git \ build-essential \ cmake \ pkg-config \ yasm \ nasm \ libx264-dev \ libx265-dev \ libvpx-dev \ libaom-dev \ libopus-dev \ libvorbis-dev \ libass-dev \ libfreetype6-dev \ libmp3lame-dev \ nginx-extras \ supervisor \ libldap2-dev \ libsasl2-dev \ libssl-dev \ libffi-dev \ libc-dev msg_ok "Installed Dependencies" NODE_VERSION=24 setup_nodejs PYTHON_VERSION=3.14 setup_uv fetch_and_deploy_gh_release "fireshare" "ShaneIsrael/fireshare" "tarball" msg_info "Compiling SVT-AV1 (Patience)" cd /tmp $STD git clone --depth 1 --branch v1.8.0 https://gitlab.com/AOMediaCodec/SVT-AV1.git cd SVT-AV1/Build $STD cmake .. -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=Release $STD make -j$(nproc) $STD make install msg_ok "Compiled SVT-AV1" msg_info "Installing NVDEC headers" cd /tmp $STD git clone --depth 1 --branch n12.1.14.0 https://github.com/FFmpeg/nv-codec-headers.git cd nv-codec-headers $STD make install $STD ldconfig msg_ok "Installed NVDEC headers" msg_info "Compiling ffmpeg (Patience)" cd /tmp curl -fsSL https://ffmpeg.org/releases/ffmpeg-6.1.tar.xz -o "ffmpeg-6.1.tar.xz" $STD tar -xf ffmpeg-6.1.tar.xz cd ffmpeg-6.1 $STD ./configure \ --prefix=/usr/local \ --enable-gpl \ --enable-version3 \ --enable-nonfree \ --enable-ffnvcodec \ --enable-libx264 \ --enable-libx265 \ --enable-libvpx \ --enable-libaom \ --enable-libopus \ --enable-libvorbis \ --enable-libmp3lame \ --enable-libass \ --enable-libfreetype \ --enable-libsvtav1 \ --disable-debug \ --disable-doc $STD make -j$(nproc) $STD make install $STD ldconfig msg_ok "Compiled ffmpeg" msg_info "Configuring Fireshare (Patience)" ADMIN_PASSWORD=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | head -c13) SECRET=$(openssl rand -base64 48) mkdir -p /opt/fireshare-{data,videos,images,processed} cd /opt/fireshare $STD uv venv $STD .venv/bin/python -m ensurepip --upgrade $STD .venv/bin/python -m pip install --upgrade --break-system-packages pip ln -sf /usr/local/bin/ffmpeg /usr/bin/ffmpeg ln -sf /usr/local/bin/ffprobe /usr/bin/ffprobe echo "/usr/local/lib" >/etc/ld.so.conf.d/usr-local.conf echo "/usr/local/cuda/lib64" >>/etc/ld.so.conf.d/usr-local.conf echo "/usr/local/nvidia/lib" >>/etc/ld.so.conf.d/nvidia.conf echo "/usr/local/nvidia/lib64" >>/etc/ld.so.conf.d/nvidia.conf ldconfig $STD .venv/bin/python -m pip install --no-cache-dir --break-system-packages --ignore-installed app/server cp .venv/bin/fireshare /usr/local/bin/fireshare export FLASK_APP="/opt/fireshare/app/server/fireshare:create_app()" export DATA_DIRECTORY=/opt/fireshare-data export IMAGE_DIRECTORY=/opt/fireshare-images export VIDEO_DIRECTORY=/opt/fireshare-videos export PROCESSED_DIRECTORY=/opt/fireshare-processed $STD uv run flask db upgrade cat </opt/fireshare/fireshare.env FLASK_APP="/opt/fireshare/app/server/fireshare:create_app()" DOMAIN= ENVIRONMENT=production DATA_DIRECTORY=/opt/fireshare-data IMAGE_DIRECTORY=/opt/fireshare-images VIDEO_DIRECTORY=/opt/fireshare-videos PROCESSED_DIRECTORY=/opt/fireshare-processed TEMPLATE_PATH=/opt/fireshare/app/server/fireshare/templates SECRET_KEY=${SECRET} ADMIN_PASSWORD=${ADMIN_PASSWORD} TZ=UTC LD_LIBRARY_PATH=/usr/local/nvidia/lib:/usr/local/nvidia/lib64:/usr/local/lib:/usr/local/cuda/lib64:\$LD_LIBRARY_PATH PATH=/usr/local/bin:$PATH ENABLE_TRANSCODING= TRANSCODE_GPU= NVIDIA_DRIVER_CAPABILITIES= EOF cd /opt/fireshare/app/client $STD npm install $STD npm run build systemctl stop nginx cp /opt/fireshare/app/nginx/prod.conf /etc/nginx/nginx.conf sed -i 's|root /processed/|root /opt/fireshare-processed/|g' /etc/nginx/nginx.conf sed -i 's/^user[[:space:]]\+nginx;/user root;/' /etc/nginx/nginx.conf sed -i 's|root[[:space:]]\+/app/build;|root /opt/fireshare/app/client/build;|' /etc/nginx/nginx.conf systemctl start nginx cat <~/fireshare.creds Fireshare Admin Credentials ======================== Username: admin Password: ${ADMIN_PASSWORD} EOF msg_ok "Configured Fireshare" msg_info "Creating services" cat </etc/systemd/system/fireshare.service [Unit] Description=Fireshare Service After=network.target [Service] WorkingDirectory=/opt/fireshare/app/server ExecStart=/opt/fireshare/.venv/bin/gunicorn --bind=127.0.0.1:5000 "fireshare:create_app(init_schedule=True)" --workers 3 --threads 3 --preload Restart=always EnvironmentFile=/opt/fireshare/fireshare.env [Install] WantedBy=multi-user.target EOF systemctl enable -q --now fireshare msg_ok "Created services" motd_ssh customize cleanup_lxc ================================================ FILE: install/fladder-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2025 community-scripts ORG # Author: wendyliga # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/DonutWare/Fladder source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y nginx msg_ok "Installed Dependencies" fetch_and_deploy_gh_release "Fladder" "DonutWare/Fladder" "prebuild" "latest" "/opt/fladder" "Fladder-Web-*.zip" msg_info "Configuring Nginx" cat </etc/nginx/conf.d/fladder.conf server { listen 80 default_server; listen [::]:80 default_server; server_name _; root /opt/fladder; index index.html; location / { try_files \$uri \$uri/ /index.html; } } EOF rm -f /etc/nginx/sites-enabled/default rm -f /etc/nginx/sites-available/default systemctl enable -q --now nginx systemctl reload nginx msg_ok "Configured Nginx" motd_ssh customize cleanup_lxc ================================================ FILE: install/flaresolverr-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # Co-Author: remz1337 # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/FlareSolverr/FlareSolverr source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt-get install -y apt-transport-https $STD apt-get install -y xvfb $STD apt-get install -y wget $STD apt-get install -y git $STD apt-get install -y openssh-server $STD apt-get install -y chromium-common $STD apt-mark hold chromium msg_ok "Installed Dependencies" msg_info "Installing Chrome" setup_deb822_repo \ "google-chrome" \ "https://dl.google.com/linux/linux_signing_key.pub" \ "https://dl.google.com/linux/chrome/deb/" \ "stable" $STD apt update $STD apt install -y google-chrome-stable # remove google-chrome.list added by google-chrome-stable if [ -f /etc/apt/sources.list.d/google-chrome.list ]; then rm /etc/apt/sources.list.d/google-chrome.list fi msg_ok "Installed Chrome" fetch_and_deploy_gh_release "flaresolverr" "FlareSolverr/FlareSolverr" "prebuild" "latest" "/opt/flaresolverr" "flaresolverr_linux_arm64.tar.gz" msg_info "Creating Service" cat </etc/systemd/system/flaresolverr.service [Unit] Description=FlareSolverr After=network.target [Service] SyslogIdentifier=flaresolverr Restart=always RestartSec=5 Type=simple Environment="LOG_LEVEL=info" Environment="CAPTCHA_SOLVER=none" WorkingDirectory=/opt/flaresolverr ExecStart=python3 /opt/flaresolverr/src/flaresolverr.py TimeoutStopSec=30 [Install] WantedBy=multi-user.target EOF systemctl enable -q --now flaresolverr msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/flatnotes-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: luismco # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/dullage/flatnotes source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os fetch_and_deploy_gh_release "flatnotes" "dullage/flatnotes" "tarball" USE_UVX="YES" setup_uv NODE_VERSION="22" setup_nodejs msg_info "Setting up Flatnotes" cd /opt/flatnotes $STD /usr/local/bin/uvx migrate-to-uv $STD /usr/local/bin/uv sync mkdir -p /opt/flatnotes/data cd /opt/flatnotes/client $STD npm install $STD npm run build cat </opt/flatnotes/.env FLATNOTES_AUTH_TYPE='none' FLATNOTES_PATH='/opt/flatnotes/data/' #FLATNOTES_USERNAME='username' #FLATNOTES_PASSWORD='password' #FLATNOTES_SECRET_KEY='secret-key' EOF msg_ok "Setup Flatnotes" msg_info "Creating Service" cat </etc/systemd/system/flatnotes.service [Unit] Description=Flatnotes After=network.target [Service] Type=simple WorkingDirectory=/opt/flatnotes EnvironmentFile=/opt/flatnotes/.env ExecStart=/opt/flatnotes/.venv/bin/python -m uvicorn main:app --app-dir server --host 0.0.0.0 --port 8080 --proxy-headers Restart=on-failure RestartSec=10 [Install] WantedBy=multi-user.target EOF systemctl enable -q --now flatnotes msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/flowiseai-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://flowiseai.com/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os NODE_VERSION="20" NODE_MODULE="pnpm" setup_nodejs msg_info "Installing FlowiseAI (Patience)" $STD pnpm add -g flowise \ @opentelemetry/exporter-trace-otlp-grpc \ @opentelemetry/exporter-trace-otlp-proto \ @opentelemetry/sdk-trace-node \ langchainhub mkdir -p /opt/flowiseai curl -fsSL "https://raw.githubusercontent.com/FlowiseAI/Flowise/main/packages/server/.env.example" -o "/opt/flowiseai/.env" msg_ok "Installed FlowiseAI" msg_info "Creating Service" cat </etc/systemd/system/flowise.service [Unit] Description=FlowiseAI After=network.target [Service] EnvironmentFile=/opt/flowiseai/.env ExecStart=flowise start Restart=always [Install] WantedBy=multi-user.target EOF systemctl enable -q --now flowise msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/fluid-calendar-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: vhsdream # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/dotnetfactory/fluid-calendar source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ build-essential \ zip msg_ok "Installed Dependencies" PG_VERSION="17" setup_postgresql PG_DB_NAME="fluiddb" PG_DB_USER="fluiduser" setup_postgresql_db NODE_VERSION="24" setup_nodejs fetch_and_deploy_gh_release "fluid-calendar" "dotnetfactory/fluid-calendar" "tarball" msg_info "Configuring fluid-calendar" NEXTAUTH_SECRET="$(openssl rand -base64 44 | tr -dc 'a-zA-Z0-9' | cut -c1-32)" echo "NextAuth Secret: $NEXTAUTH_SECRET" >>~/$APPLICATION.creds cat </opt/fluid-calendar/.env DATABASE_URL="postgresql://${PG_DB_USER}:${PG_DB_PASS}@localhost:5432/${PG_DB_NAME}" # Change the URL below to your external URL NEXTAUTH_URL="http://localhost:3000" NEXT_PUBLIC_APP_URL="http://localhost:3000" NEXTAUTH_SECRET="${NEXTAUTH_SECRET}" NEXT_PUBLIC_SITE_URL="http://localhost:3000" NEXT_PUBLIC_ENABLE_SAAS_FEATURES=false RESEND_API_KEY= RESEND_EMAIL= EOF export NEXT_TELEMETRY_DISABLED=1 cd /opt/fluid-calendar $STD npm install --legacy-peer-deps $STD npm run prisma:generate $STD npx prisma migrate deploy $STD npm run build:os msg_ok "Configured fluid-calendar" msg_info "Creating Service" cat </etc/systemd/system/fluid-calendar.service [Unit] Description=Fluid Calendar Application After=network.target postgresql.service [Service] Restart=always WorkingDirectory=/opt/fluid-calendar EnvironmentFile=/opt/fluid-calendar/.env ExecStart=/usr/bin/npm run start [Install] WantedBy=multi-user.target EOF systemctl enable -q --now fluid-calendar msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/foldergram-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: Slaviša Arežina (tremor021) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/foldergram/foldergram source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y --no-install-recommends ffmpeg msg_ok "Installed Dependencies" NODE_VERSION=25 NODE_MODULE="corepack" setup_nodejs fetch_and_deploy_gh_release "foldergram" "foldergram/foldergram" "tarball" msg_info "Configuring Foldergram" export COREPACK_ENABLE_DOWNLOAD_PROMPT=0 $STD corepack enable cd /opt/foldergram $STD pnpm install $STD pnpm run build mkdir -p /opt/foldergram_media cat </opt/foldergram_media/foldergram.env NODE_ENV=production SERVER_PORT=4141 DATA_ROOT=/opt/foldergram_media GALLERY_ROOT=/opt/foldergram_media/gallery DB_DIR=/opt/foldergram_media/db THUMBNAILS_DIR=/opt/foldergram_media/thumbnails PREVIEWS_DIR=/opt/foldergram_media/previews IMAGE_DETAIL_SOURCE=preview DERIVATIVE_MODE=eager GALLERY_EXCLUDED_FOLDERS= EOF msg_ok "Configured Foldergram" msg_info "Creating services" cat </etc/systemd/system/foldergram.service [Unit] Description=Foldergram Service After=network.target [Service] WorkingDirectory=/opt/foldergram ExecStart=/usr/bin/pnpm start Restart=always EnvironmentFile=/opt/foldergram_media/foldergram.env [Install] WantedBy=multi-user.target EOF systemctl enable -q --now foldergram msg_ok "Created services" motd_ssh customize cleanup_lxc ================================================ FILE: install/forgejo-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://forgejo.org/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ git \ git-lfs msg_ok "Installed Dependencies" fetch_and_deploy_codeberg_release "forgejo" "forgejo/forgejo" "singlefile" "latest" "/opt/forgejo" "forgejo-*-linux-arm64" ln -sf /opt/forgejo/forgejo /usr/local/bin/forgejo msg_info "Setting up Forgejo" $STD adduser --system --shell /bin/bash --gecos 'Git Version Control' --group --disabled-password --home /home/git git mkdir /var/lib/forgejo chown git:git /var/lib/forgejo chmod 750 /var/lib/forgejo mkdir /etc/forgejo chown root:git /etc/forgejo chmod 770 /etc/forgejo msg_ok "Setup Forgejo" msg_info "Creating Service" cat </etc/systemd/system/forgejo.service [Unit] Description=Forgejo After=syslog.target After=network.target [Service] RestartSec=2s Type=simple User=git Group=git WorkingDirectory=/var/lib/forgejo/ ExecStart=/usr/local/bin/forgejo web --config /etc/forgejo/app.ini Restart=always Environment=USER=git HOME=/home/git FORGEJO_WORK_DIR=/var/lib/forgejo [Install] WantedBy=multi-user.target EOF systemctl enable -q --now forgejo msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/freepbx-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: Arian Nasr (arian-nasr) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.freepbx.org/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing FreePBX (Patience)" curl -fsSL https://github.com/FreePBX/sng_freepbx_debian_install/raw/master/sng_freepbx_debian_install.sh -o /opt/sng_freepbx_debian_install.sh $STD bash /opt/sng_freepbx_debian_install.sh rm /opt/sng_freepbx_debian_install.sh msg_ok "Installed FreePBX" motd_ssh customize cleanup_lxc ================================================ FILE: install/freshrss-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: bvdberg01 # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/FreshRSS/FreshRSS source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os PHP_VERSION="8.4" PHP_APACHE="YES" setup_php PG_VERSION="16" setup_postgresql PG_DB_NAME="freshrss" PG_DB_USER="freshrss_usr" setup_postgresql_db fetch_and_deploy_gh_release "freshrss" "FreshRSS/FreshRSS" "tarball" msg_info "Configuring FreshRSS" cd /opt/freshrss chown -R www-data:www-data /opt/freshrss chmod -R g+rX /opt/freshrss chmod -R g+w /opt/freshrss/data/ msg_ok "Configured FreshRSS" msg_info "Setting up cron job for feed refresh" cat </etc/cron.d/freshrss-actualize */15 * * * * www-data /bin/php -f /opt/freshrss/app/actualize_script.php > /tmp/FreshRSS.log 2>&1 EOF chmod 644 /etc/cron.d/freshrss-actualize msg_ok "Set up Cron - if you need to modify the timing edit file /etc/cron.d/freshrss-actualize" msg_info "Creating Service" cat </etc/apache2/sites-available/freshrss.conf ServerName freshrss DocumentRoot /opt/freshrss/p Options FollowSymLinks AllowOverride All Require all granted ErrorLog /var/log/apache2/freshrss_error.log CustomLog /var/log/apache2/freshrss_access.log combined AllowEncodedSlashes On EOF $STD a2ensite freshrss $STD a2enmod rewrite deflate expires headers mime setenvif $STD a2dissite 000-default.conf $STD systemctl reload apache2 msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/frigate-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # Co-Author: remz1337 # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://frigate.video/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os source /etc/os-release if [[ "$VERSION_ID" != "12" ]]; then msg_error "Frigate requires Debian 12 (Bookworm) due to Python 3.11 dependencies" exit 238 fi msg_info "Converting APT sources to DEB822 format" if [ -f /etc/apt/sources.list ]; then cat >/etc/apt/sources.list.d/debian.sources <<'EOF' Types: deb URIs: http://deb.debian.org/debian Suites: bookworm Components: main contrib Signed-By: /usr/share/keyrings/debian-archive-keyring.gpg Types: deb URIs: http://deb.debian.org/debian Suites: bookworm-updates Components: main contrib Signed-By: /usr/share/keyrings/debian-archive-keyring.gpg Types: deb URIs: http://security.debian.org Suites: bookworm-security Components: main contrib Signed-By: /usr/share/keyrings/debian-archive-keyring.gpg EOF mv /etc/apt/sources.list /etc/apt/sources.list.bak $STD apt update fi msg_ok "Converted APT sources" msg_info "Installing Dependencies" $STD apt install -y \ xz-utils \ python3 \ python3-dev \ python3-pip \ gcc \ pkg-config \ libhdf5-dev \ build-essential \ automake \ libtool \ ccache \ libusb-1.0-0-dev \ apt-transport-https \ cmake \ git \ libgtk-3-dev \ libavcodec-dev \ libavformat-dev \ libswscale-dev \ libv4l-dev \ libxvidcore-dev \ libx264-dev \ libjpeg-dev \ libpng-dev \ libtiff-dev \ gfortran \ openexr \ libssl-dev \ libtbbmalloc2 \ libtbb-dev \ libdc1394-dev \ libopenexr-dev \ libgstreamer-plugins-base1.0-dev \ libgstreamer1.0-dev \ tclsh \ libopenblas-dev \ liblapack-dev \ libgomp1 \ make \ moreutils msg_ok "Installed Dependencies" setup_hwaccel ARCH=$(dpkg --print-architecture 2>/dev/null || uname -m) TARGETARCH="amd64" [[ "$ARCH" == "arm64" || "$ARCH" == "aarch64" ]] && TARGETARCH="arm64" export TARGETARCH export CCACHE_DIR=/root/.ccache export CCACHE_MAXSIZE=2G export APT_KEY_DONT_WARN_ON_DANGEROUS_USAGE=DontWarn export PIP_BREAK_SYSTEM_PACKAGES=1 export NVIDIA_VISIBLE_DEVICES=all export NVIDIA_DRIVER_CAPABILITIES="compute,video,utility" export TOKENIZERS_PARALLELISM=true export TRANSFORMERS_NO_ADVISORY_WARNINGS=1 export OPENCV_FFMPEG_LOGLEVEL=8 export PYTHONWARNINGS="ignore:::numpy.core.getlimits" export HAILORT_LOGGER_PATH=NONE export TF_CPP_MIN_LOG_LEVEL=3 export TF_CPP_MIN_VLOG_LEVEL=3 export TF_ENABLE_ONEDNN_OPTS=0 export AUTOGRAPH_VERBOSITY=0 export GLOG_minloglevel=3 export GLOG_logtostderr=0 fetch_and_deploy_gh_release "frigate" "blakeblackshear/frigate" "tarball" "v0.17.1" "/opt/frigate" msg_info "Building Nginx" $STD bash /opt/frigate/docker/main/build_nginx.sh sed -e '/s6-notifyoncheck/ s/^#*/#/' -i /opt/frigate/docker/main/rootfs/etc/s6-overlay/s6-rc.d/nginx/run ln -sf /usr/local/nginx/sbin/nginx /usr/local/bin/nginx msg_ok "Built Nginx" msg_info "Building SQLite Extensions" $STD bash /opt/frigate/docker/main/build_sqlite_vec.sh msg_ok "Built SQLite Extensions" fetch_and_deploy_gh_release "go2rtc" "AlexxIT/go2rtc" "singlefile" "latest" "/usr/local/go2rtc/bin" "go2rtc_linux_${TARGETARCH}" msg_info "Installing Tempio" sed -i 's|/rootfs/usr/local|/usr/local|g' /opt/frigate/docker/main/install_tempio.sh $STD bash /opt/frigate/docker/main/install_tempio.sh ln -sf /usr/local/tempio/bin/tempio /usr/local/bin/tempio msg_ok "Installed Tempio" msg_info "Building libUSB" fetch_and_deploy_gh_release "libusb" "libusb/libusb" "tarball" "v1.0.26" "/opt/libusb" cd /opt/libusb $STD ./bootstrap.sh $STD ./configure CC='ccache gcc' CCX='ccache g++' --disable-udev --enable-shared $STD make -j "$(nproc)" cd /opt/libusb/libusb mkdir -p /usr/local/lib /usr/local/include/libusb-1.0 /usr/local/lib/pkgconfig $STD bash ../libtool --mode=install /usr/bin/install -c libusb-1.0.la /usr/local/lib install -c -m 644 libusb.h /usr/local/include/libusb-1.0 cd /opt/libusb/ install -c -m 644 libusb-1.0.pc /usr/local/lib/pkgconfig ldconfig msg_ok "Built libUSB" msg_info "Bootstrapping pip" curl_with_retry "https://bootstrap.pypa.io/get-pip.py" "/tmp/get-pip.py" sed -i 's/args.append("setuptools")/args.append("setuptools==77.0.3")/' /tmp/get-pip.py $STD python3 /tmp/get-pip.py "pip" rm -f /tmp/get-pip.py msg_ok "Bootstrapped pip" msg_info "Installing Python Dependencies" $STD pip3 install -r /opt/frigate/docker/main/requirements.txt msg_ok "Installed Python Dependencies" msg_info "Building Python Wheels (Patience)" mkdir -p /wheels $STD bash /opt/frigate/docker/main/build_pysqlite3.sh for i in {1..3}; do $STD pip3 wheel --wheel-dir=/wheels -r /opt/frigate/docker/main/requirements-wheels.txt --default-timeout=300 --retries=3 && break [[ $i -lt 3 ]] && sleep 10 done msg_ok "Built Python Wheels" NODE_VERSION="20" setup_nodejs msg_info "Downloading Inference Models" mkdir -p /models /openvino-model curl_with_retry "https://github.com/google-coral/test_data/raw/release-frogfish/ssdlite_mobiledet_coco_qat_postprocess_edgetpu.tflite" "/edgetpu_model.tflite" curl_with_retry "https://github.com/google-coral/test_data/raw/release-frogfish/ssdlite_mobiledet_coco_qat_postprocess.tflite" "/models/cpu_model.tflite" cp /opt/frigate/labelmap.txt /labelmap.txt msg_ok "Downloaded Inference Models" msg_info "Downloading Audio Model" curl_with_retry "https://www.kaggle.com/api/v1/models/google/yamnet/tfLite/classification-tflite/1/download" "/tmp/yamnet.tar.gz" $STD tar xzf /tmp/yamnet.tar.gz -C / mv /1.tflite /cpu_audio_model.tflite cp /opt/frigate/audio-labelmap.txt /audio-labelmap.txt rm -f /tmp/yamnet.tar.gz msg_ok "Downloaded Audio Model" msg_info "Installing OpenVino" $STD pip3 install -r /opt/frigate/docker/main/requirements-ov.txt msg_ok "Installed OpenVino" msg_info "Building OpenVino Model" cd /models curl_with_retry "http://download.tensorflow.org/models/object_detection/ssdlite_mobilenet_v2_coco_2018_05_09.tar.gz" "ssdlite_mobilenet_v2_coco_2018_05_09.tar.gz" $STD tar -zxf ssdlite_mobilenet_v2_coco_2018_05_09.tar.gz --no-same-owner if python3 /opt/frigate/docker/main/build_ov_model.py &>/dev/null; then mkdir -p /openvino-model cp /models/ssdlite_mobilenet_v2.xml /openvino-model/ cp /models/ssdlite_mobilenet_v2.bin /openvino-model/ OV_LABELS=$(python3 -c "import omz_tools; import os; print(os.path.join(omz_tools.__path__[0], 'data/dataset_classes/coco_91cl_bkgr.txt'))" 2>/dev/null) if [[ -n "$OV_LABELS" && -f "$OV_LABELS" ]]; then ln -sf "$OV_LABELS" /openvino-model/coco_91cl_bkgr.txt else OV_LABELS=$(find /usr/local/lib -name "coco_91cl_bkgr.txt" 2>/dev/null | head -1) if [[ -n "$OV_LABELS" ]]; then ln -sf "$OV_LABELS" /openvino-model/coco_91cl_bkgr.txt else curl_with_retry "https://raw.githubusercontent.com/openvinotoolkit/open_model_zoo/master/data/dataset_classes/coco_91cl_bkgr.txt" "/openvino-model/coco_91cl_bkgr.txt" fi fi sed -i 's/truck/car/g' /openvino-model/coco_91cl_bkgr.txt msg_ok "Built OpenVino Model" else msg_warn "OpenVino build failed (CPU may not support required instructions). Frigate will use CPU model." fi msg_info "Installing HailoRT Runtime" $STD bash /opt/frigate/docker/main/install_hailort.sh cp -a /opt/frigate/docker/main/rootfs/. / sed -i '/^.*unset DEBIAN_FRONTEND.*$/d' /opt/frigate/docker/main/install_deps.sh echo "libedgetpu1-max libedgetpu/accepted-eula boolean true" | debconf-set-selections echo "libedgetpu1-max libedgetpu/install-confirm-max boolean true" | debconf-set-selections echo 'force-overwrite' >/etc/dpkg/dpkg.cfg.d/force-overwrite $STD bash /opt/frigate/docker/main/install_deps.sh rm -f /etc/dpkg/dpkg.cfg.d/force-overwrite $STD pip3 install -U /wheels/*.whl ldconfig msg_ok "Installed HailoRT Runtime" msg_info "Installing MemryX Runtime" $STD bash /opt/frigate/docker/main/install_memryx.sh msg_ok "Installed MemryX Runtime" msg_info "Building Frigate Application (Patience)" cd /opt/frigate $STD pip3 install -r /opt/frigate/docker/main/requirements-dev.txt $STD bash /opt/frigate/.devcontainer/initialize.sh $STD make version cd /opt/frigate/web $STD npm install $STD npm run build mv /opt/frigate/web/dist/BASE_PATH/monacoeditorwork/* /opt/frigate/web/dist/assets/ rm -rf /opt/frigate/web/dist/BASE_PATH cp -r /opt/frigate/web/dist/* /opt/frigate/web/ sed -i '/^s6-svc -O \.$/s/^/#/' /opt/frigate/docker/main/rootfs/etc/s6-overlay/s6-rc.d/frigate/run msg_ok "Built Frigate Application" msg_info "Configuring Frigate" mkdir -p /config /media/frigate cp -r /opt/frigate/config/. /config curl_with_retry "https://github.com/intel-iot-devkit/sample-videos/raw/master/person-bicycle-car-detection.mp4" "/media/frigate/person-bicycle-car-detection.mp4" echo "tmpfs /tmp/cache tmpfs defaults 0 0" >>/etc/fstab cat </etc/frigate.env DEFAULT_FFMPEG_VERSION="7.0" INCLUDED_FFMPEG_VERSIONS="7.0:5.0" NVIDIA_VISIBLE_DEVICES=all NVIDIA_DRIVER_CAPABILITIES="compute,video,utility" TOKENIZERS_PARALLELISM=true TRANSFORMERS_NO_ADVISORY_WARNINGS=1 OPENCV_FFMPEG_LOGLEVEL=8 PYTHONWARNINGS="ignore:::numpy.core.getlimits" HAILORT_LOGGER_PATH=NONE TF_CPP_MIN_LOG_LEVEL=3 TF_CPP_MIN_VLOG_LEVEL=3 TF_ENABLE_ONEDNN_OPTS=0 AUTOGRAPH_VERBOSITY=0 GLOG_minloglevel=3 GLOG_logtostderr=0 EOF cat </config/config.yml mqtt: enabled: false cameras: test: ffmpeg: inputs: - path: /media/frigate/person-bicycle-car-detection.mp4 input_args: -re -stream_loop -1 -fflags +genpts roles: - detect detect: height: 1080 width: 1920 fps: 5 auth: enabled: false detect: enabled: false EOF if grep -q -o -m1 -E 'avx[^ ]*|sse4_2' /proc/cpuinfo && [[ -f /openvino-model/ssdlite_mobilenet_v2.xml ]] && [[ -f /openvino-model/coco_91cl_bkgr.txt ]]; then cat <>/config/config.yml ffmpeg: hwaccel_args: auto detectors: detector01: type: openvino device: AUTO model: width: 300 height: 300 input_tensor: nhwc input_pixel_format: bgr path: /openvino-model/ssdlite_mobilenet_v2.xml labelmap_path: /openvino-model/coco_91cl_bkgr.txt EOF else cat <>/config/config.yml ffmpeg: hwaccel_args: auto model: path: /models/cpu_model.tflite EOF fi msg_ok "Configured Frigate" msg_info "Creating Services" cat </etc/systemd/system/create_directories.service [Unit] Description=Create necessary directories for Frigate logs Before=frigate.service go2rtc.service nginx.service [Service] Type=oneshot ExecStart=/bin/bash -c '/bin/mkdir -p /dev/shm/logs/{frigate,go2rtc,nginx} && /bin/touch /dev/shm/logs/{frigate/current,go2rtc/current,nginx/current} && /bin/chmod -R 777 /dev/shm/logs' [Install] WantedBy=multi-user.target EOF cat </etc/systemd/system/go2rtc.service [Unit] Description=go2rtc streaming service After=network.target create_directories.service StartLimitIntervalSec=0 [Service] Type=simple Restart=always RestartSec=1 User=root EnvironmentFile=/etc/frigate.env ExecStartPre=+rm -f /dev/shm/logs/go2rtc/current ExecStart=/bin/bash -c "bash /opt/frigate/docker/main/rootfs/etc/s6-overlay/s6-rc.d/go2rtc/run 2> >(/usr/bin/ts '%%Y-%%m-%%d %%H:%%M:%%.S ' >&2) | /usr/bin/ts '%%Y-%%m-%%d %%H:%%M:%%.S '" StandardOutput=file:/dev/shm/logs/go2rtc/current StandardError=file:/dev/shm/logs/go2rtc/current [Install] WantedBy=multi-user.target EOF cat </etc/systemd/system/frigate.service [Unit] Description=Frigate NVR service After=go2rtc.service create_directories.service StartLimitIntervalSec=0 [Service] Type=simple Restart=always RestartSec=1 User=root EnvironmentFile=/etc/frigate.env ExecStartPre=+rm -f /dev/shm/logs/frigate/current ExecStart=/bin/bash -c "bash /opt/frigate/docker/main/rootfs/etc/s6-overlay/s6-rc.d/frigate/run 2> >(/usr/bin/ts '%%Y-%%m-%%d %%H:%%M:%%.S ' >&2) | /usr/bin/ts '%%Y-%%m-%%d %%H:%%M:%%.S '" StandardOutput=file:/dev/shm/logs/frigate/current StandardError=file:/dev/shm/logs/frigate/current [Install] WantedBy=multi-user.target EOF cat </etc/systemd/system/nginx.service [Unit] Description=Nginx reverse proxy for Frigate After=frigate.service create_directories.service StartLimitIntervalSec=0 [Service] Type=simple Restart=always RestartSec=1 User=root ExecStartPre=+rm -f /dev/shm/logs/nginx/current ExecStart=/bin/bash -c "bash /opt/frigate/docker/main/rootfs/etc/s6-overlay/s6-rc.d/nginx/run 2> >(/usr/bin/ts '%%Y-%%m-%%d %%H:%%M:%%.S ' >&2) | /usr/bin/ts '%%Y-%%m-%%d %%H:%%M:%%.S '" StandardOutput=file:/dev/shm/logs/nginx/current StandardError=file:/dev/shm/logs/nginx/current [Install] WantedBy=multi-user.target EOF systemctl daemon-reload systemctl enable -q --now create_directories sleep 2 systemctl enable -q --now go2rtc sleep 2 systemctl enable -q --now frigate sleep 2 systemctl enable -q --now nginx msg_ok "Created Services" msg_info "Cleaning Up" rm -rf /opt/libusb /wheels /models/*.tar.gz msg_ok "Cleaned Up" motd_ssh customize cleanup_lxc ================================================ FILE: install/fumadocs-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ ca-certificates \ git msg_ok "Installed Dependencies" NODE_VERSION="22" NODE_MODULE="pnpm" setup_nodejs msg_info "Preparing Fumadocs - " mkdir -p /opt/fumadocs cd /opt/fumadocs msg_ok "Important: Manual configuration is required after this step." pnpm create fumadocs-app PROJECT_NAME=$(find . -maxdepth 1 -type d ! -name '.' ! -name '..' | sed 's|^\./||') echo "$PROJECT_NAME" >/opt/fumadocs/.projectname msg_info "Creating Service" cat </etc/systemd/system/fumadocs_$PROJECT_NAME.service [Unit] Description=Fumadocs Documentation Server After=network.target [Service] WorkingDirectory=/opt/fumadocs/$PROJECT_NAME ExecStart=/usr/bin/pnpm run dev Restart=always [Install] WantedBy=multi-user.target EOF systemctl enable -q --now fumadocs_$PROJECT_NAME msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/garage-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://garagehq.deuxfleurs.fr/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Setup Garage" GITEA_RELEASE=$(curl -s https://api.github.com/repos/deuxfleurs-org/garage/tags | jq -r '.[0].name') curl -fsSL "https://garagehq.deuxfleurs.fr/_releases/${GITEA_RELEASE}/aarch64-unknown-linux-musl/garage" -o /usr/local/bin/garage chmod +x /usr/local/bin/garage mkdir -p /var/lib/garage/{data,meta,snapshots} mkdir -p /etc/garage RPC_SECRET=$(openssl rand -hex 32) ADMIN_TOKEN=$(openssl rand -base64 32) METRICS_TOKEN=$(openssl rand -base64 32) { echo "Garage Tokens and Secrets" echo "RPC Secret: $RPC_SECRET" echo "Admin Token: $ADMIN_TOKEN" echo "Metrics Token: $METRICS_TOKEN" } >>~/garage.creds echo $GITEA_RELEASE >>~/.garage cat </etc/garage.toml metadata_dir = "/var/lib/garage/meta" data_dir = "/var/lib/garage/data" db_engine = "sqlite" replication_factor = 1 rpc_bind_addr = "[::]:3901" rpc_public_addr = "127.0.0.1:3901" rpc_secret = "${RPC_SECRET}" [s3_api] s3_region = "garage" api_bind_addr = "[::]:3900" root_domain = ".s3.garage.localhost" [s3_web] bind_addr = "[::]:3902" root_domain = ".web.garage.localhost" index = "index.html" [k2v_api] api_bind_addr = "[::]:3904" [admin] api_bind_addr = "[::]:3903" admin_token = "${ADMIN_TOKEN}" metrics_token = "${METRICS_TOKEN}" EOF msg_ok "Set up Garage" msg_info "Creating service" cat <<'EOF' >/etc/systemd/system/garage.service [Unit] Description=Garage Object Storage (Deuxfleurs) After=network-online.target Wants=network-online.target [Service] Type=simple ExecStart=/usr/local/bin/garage -c /etc/garage.toml server Restart=always RestartSec=5 User=root WorkingDirectory=/var/lib/garage Environment=RUST_LOG=info StandardOutput=append:/var/log/garage.log StandardError=append:/var/log/garage.log LimitNOFILE=65536 [Install] WantedBy=multi-user.target EOF $STD systemctl enable -q --now garage msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/gatus-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: Slaviša Arežina (tremor021) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/TwiN/gatus source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ ca-certificates \ libcap2-bin msg_ok "Installed Dependencies" setup_go fetch_and_deploy_gh_release "gatus" "TwiN/gatus" "tarball" msg_info "Configuring gatus" cd /opt/gatus $STD go mod tidy CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o gatus . setcap CAP_NET_RAW+ep gatus mv config.yaml config msg_ok "Configured gatus" msg_info "Creating Service" cat </etc/systemd/system/gatus.service [Unit] Description=gatus Service After=network.target [Service] Type=simple User=root WorkingDirectory=/opt/gatus ExecStart=/opt/gatus/gatus Restart=always [Install] WantedBy=multi-user.target EOF systemctl enable -q --now gatus msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/geopulse-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: CrazyWolf13 # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/tess1o/geopulse source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ openssl \ nginx msg_ok "Installed Dependencies" PG_VERSION="17" PG_MODULES="postgis" setup_postgresql PG_DB_NAME="geopulse" PG_DB_USER="geopulse" PG_DB_EXTENSIONS="postgis,postgis_topology" setup_postgresql_db msg_info "Generating Security Keys" mkdir -p /opt/geopulse/{backend,keys} mkdir -p /etc/geopulse /var/www/geopulse /var/lib/geopulse/dumps mkdir -p /var/log/geopulse/{backend,nginx} openssl genpkey -algorithm RSA -out /opt/geopulse/keys/jwt-private-key.pem 2>/dev/null openssl rsa -pubout -in /opt/geopulse/keys/jwt-private-key.pem -out /opt/geopulse/keys/jwt-public-key.pem 2>/dev/null openssl rand -base64 32 >/opt/geopulse/keys/ai-encryption-key.txt chmod 640 /opt/geopulse/keys/jwt-private-key.pem /opt/geopulse/keys/jwt-public-key.pem /opt/geopulse/keys/ai-encryption-key.txt msg_ok "Generated Security Keys" if [[ "$(uname -m)" == "aarch64" ]]; then if grep -qi "raspberry\|bcm" /proc/cpuinfo 2>/dev/null; then BINARY_PATTERN="geopulse-backend-native-arm64-compat-*" else BINARY_PATTERN="geopulse-backend-native-arm64-[!c]*" fi else if grep -q avx2 /proc/cpuinfo && grep -q bmi2 /proc/cpuinfo && grep -q fma /proc/cpuinfo; then BINARY_PATTERN="geopulse-backend-native-amd64-[!c]*" else BINARY_PATTERN="geopulse-backend-native-amd64-compat-*" fi fi fetch_and_deploy_gh_release "geopulse-backend" "tess1o/geopulse" "singlefile" "latest" "/opt/geopulse/backend" "${BINARY_PATTERN}" fetch_and_deploy_gh_release "geopulse-frontend" "tess1o/geopulse" "prebuild" "latest" "/var/www/geopulse" "geopulse-frontend-*.tar.gz" msg_info "Configuring GeoPulse" cat </etc/geopulse/geopulse.env GEOPULSE_PUBLIC_BASE_URL=http://${LOCAL_IP} GEOPULSE_UI_URL=http://${LOCAL_IP} GEOPULSE_CORS_ENABLED=false GEOPULSE_CORS_ORIGINS= QUARKUS_HTTP_PORT=8080 GEOPULSE_POSTGRES_URL=jdbc:postgresql://localhost:5432/${PG_DB_NAME} GEOPULSE_POSTGRES_HOST=localhost GEOPULSE_POSTGRES_PORT=5432 GEOPULSE_POSTGRES_DB=${PG_DB_NAME} GEOPULSE_POSTGRES_USERNAME=${PG_DB_USER} GEOPULSE_POSTGRES_PASSWORD=${PG_DB_PASS} GEOPULSE_JWT_PRIVATE_KEY_LOCATION=file:/opt/geopulse/keys/jwt-private-key.pem GEOPULSE_JWT_PUBLIC_KEY_LOCATION=file:/opt/geopulse/keys/jwt-public-key.pem GEOPULSE_AI_ENCRYPTION_KEY_LOCATION=file:/opt/geopulse/keys/ai-encryption-key.txt QUARKUS_LOG_FILE_ENABLE=true QUARKUS_LOG_FILE_PATH=/var/log/geopulse/backend/geopulse.log QUARKUS_LOG_FILE_ROTATION_MAX_FILE_SIZE=10M QUARKUS_LOG_FILE_ROTATION_MAX_BACKUP_INDEX=5 EOF chmod 640 /etc/geopulse/geopulse.env msg_ok "Configured GeoPulse" msg_info "Creating Service" cat </etc/systemd/system/geopulse-backend.service [Unit] Description=GeoPulse Backend After=network.target postgresql.service Wants=postgresql.service [Service] Type=simple User=root WorkingDirectory=/opt/geopulse/backend EnvironmentFile=/etc/geopulse/geopulse.env ExecStart=/opt/geopulse/backend/geopulse-backend -Dquarkus.http.host=0.0.0.0 -XX:MaximumHeapSizePercent=70 -XX:MaximumYoungGenerationSizePercent=15 Restart=on-failure RestartSec=10 StandardOutput=append:/var/log/geopulse/backend/geopulse-stdout.log StandardError=append:/var/log/geopulse/backend/geopulse-stderr.log [Install] WantedBy=multi-user.target EOF systemctl enable -q --now geopulse-backend msg_ok "Created Service" msg_info "Configuring Nginx" mkdir -p /var/cache/nginx/osm_tiles cat <<'EOF' >/etc/nginx/sites-available/geopulse.conf proxy_cache_path /var/cache/nginx/osm_tiles levels=1:2 keys_zone=osm_cache:100m max_size=10g inactive=30d use_temp_path=off; map $uri $osm_subdomain { ~^/osm/tiles/a/ "a"; ~^/osm/tiles/b/ "b"; ~^/osm/tiles/c/ "c"; default "a"; } server { listen 80; server_name _; root /var/www/geopulse; index index.html; client_max_body_size 100M; gzip on; gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript; gzip_comp_level 6; gzip_min_length 1000; location ~* ^/(?!osm/).*\.(jpg|jpeg|png|gif|ico|css|js)$ { expires 1y; add_header Cache-Control "public, max-age=31536000"; } location ^~ /osm/tiles/ { resolver 8.8.8.8 valid=300s; resolver_timeout 10s; rewrite ^/osm/tiles/[abc]/(.*)$ /$1 break; proxy_pass https://$osm_subdomain.tile.openstreetmap.org; proxy_cache osm_cache; proxy_cache_key "$scheme$proxy_host$uri"; proxy_cache_valid 200 30d; proxy_cache_valid 404 1m; proxy_cache_valid 502 503 504 1m; proxy_ignore_headers Cache-Control Expires Set-Cookie; proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504; proxy_cache_background_update on; proxy_cache_lock on; proxy_set_header Cookie ""; proxy_set_header Authorization ""; proxy_set_header User-Agent "GeoPulse/1.0"; proxy_set_header Host $osm_subdomain.tile.openstreetmap.org; proxy_http_version 1.1; proxy_set_header Connection ""; proxy_connect_timeout 10s; proxy_read_timeout 10s; expires 30d; add_header Cache-Control "public, immutable"; add_header X-Cache-Status $upstream_cache_status always; } location /api/ { proxy_pass http://localhost:8080/api/; proxy_connect_timeout 3600s; proxy_send_timeout 3600s; proxy_read_timeout 3600s; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } location / { try_files $uri $uri/ /index.html; } add_header X-Frame-Options "SAMEORIGIN" always; add_header X-Content-Type-Options "nosniff" always; add_header X-XSS-Protection "1; mode=block" always; access_log /var/log/geopulse/nginx/access.log; error_log /var/log/geopulse/nginx/error.log; } EOF ln -sf /etc/nginx/sites-available/geopulse.conf /etc/nginx/sites-enabled/ rm -f /etc/nginx/sites-enabled/default systemctl enable -q --now nginx systemctl reload nginx msg_ok "Configured Nginx" msg_info "Creating Admin Helper" cat <<'EOF' >/usr/local/bin/create-geopulse-admin #!/usr/bin/env bash read -rp "Enter admin email address: " ADMIN_EMAIL if [[ -z "$ADMIN_EMAIL" ]]; then echo "No email provided. Aborting." exit 1 fi sed -i '/^GEOPULSE_ADMIN_EMAIL=/d' /etc/geopulse/geopulse.env echo "GEOPULSE_ADMIN_EMAIL=${ADMIN_EMAIL}" >>/etc/geopulse/geopulse.env systemctl restart geopulse-backend echo "Admin email set to '${ADMIN_EMAIL}'. Register with this email in the GeoPulse UI to receive admin privileges." EOF chmod +x /usr/local/bin/create-geopulse-admin msg_ok "Created Admin Helper" motd_ssh customize cleanup_lxc ================================================ FILE: install/ghost-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: fabrice1236 # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://ghost.org/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ nginx \ ca-certificates \ libjemalloc2 \ git msg_ok "Installed Dependencies" setup_mariadb MARIADB_DB_NAME="ghost" MARIADB_DB_USER="ghostuser" setup_mariadb_db NODE_VERSION="22" NODE_MODULE="pnpm" setup_nodejs msg_info "Installing Ghost CLI" $STD npm install ghost-cli@latest -g msg_ok "Installed Ghost CLI" msg_info "Creating Service" $STD adduser --disabled-password --gecos "Ghost user" ghost-user $STD usermod -aG sudo ghost-user echo "ghost-user ALL=(ALL) NOPASSWD:ALL" | tee /etc/sudoers.d/ghost-user mkdir -p /var/www/ghost chown -R ghost-user:ghost-user /var/www/ghost chmod 775 /var/www/ghost $STD sudo -u ghost-user -H sh -c "cd /var/www/ghost && ghost install --db=mysql --dbhost=localhost --dbuser=$MARIADB_DB_USER --dbpass=$MARIADB_DB_PASS --dbname=$MARIADB_DB_NAME --url=http://localhost:2368 --no-prompt --no-setup-nginx --no-setup-ssl --no-setup-mysql --enable --start --ip 0.0.0.0" rm /etc/sudoers.d/ghost-user msg_ok "Creating Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/ghostfolio-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: lucasfell # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://ghostfol.io/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ build-essential \ openssl \ ca-certificates \ redis-server msg_ok "Installed Dependencies" PG_VERSION="17" setup_postgresql NODE_VERSION="24" setup_nodejs msg_info "Setting up Database" PG_DB_NAME="ghostfolio" PG_DB_USER="ghostfolio" PG_DB_SCHEMA_PERMS="true" setup_postgresql_db REDIS_PASS=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | head -c13) ACCESS_TOKEN_SALT=$(openssl rand -base64 32) JWT_SECRET_KEY=$(openssl rand -base64 32) { echo "Ghostfolio Credentials" echo "Redis Password: $REDIS_PASS" echo "Access Token Salt: $ACCESS_TOKEN_SALT" echo "JWT Secret Key: $JWT_SECRET_KEY" } >>~/ghostfolio.creds msg_ok "Set up Database" fetch_and_deploy_gh_release "ghostfolio" "ghostfolio/ghostfolio" "tarball" "latest" "/opt/ghostfolio" msg_info "Setup Ghostfolio" sed -i "s/# requirepass foobared/requirepass $REDIS_PASS/" /etc/redis/redis.conf systemctl restart redis-server cd /opt/ghostfolio $STD npm ci $STD npm run build:production msg_ok "Built Ghostfolio" echo -e "" msg_custom "🪙" "$YW" "CoinGecko API keys are optional but provide better cryptocurrency data." msg_custom "🪙" "$YW" "You can skip this and add them later by editing /opt/ghostfolio/.env" echo -e "" read -rp "${TAB3}CoinGecko Demo API key (press Enter to skip): " COINGECKO_DEMO_KEY read -rp "${TAB3}CoinGecko Pro API key (press Enter to skip): " COINGECKO_PRO_KEY msg_info "Setting up Environment" cat </opt/ghostfolio/.env DATABASE_URL=postgresql://$PG_DB_USER:$PG_DB_PASS@localhost:5432/$PG_DB_NAME?connect_timeout=300 REDIS_HOST=localhost REDIS_PORT=6379 REDIS_PASSWORD=$REDIS_PASS ACCESS_TOKEN_SALT=$ACCESS_TOKEN_SALT JWT_SECRET_KEY=$JWT_SECRET_KEY NODE_ENV=production PORT=3333 HOST=0.0.0.0 TZ=Etc/UTC EOF if [[ -n "${COINGECKO_DEMO_KEY:-}" ]]; then echo "API_KEY_COINGECKO_DEMO=$COINGECKO_DEMO_KEY" >>/opt/ghostfolio/.env fi if [[ -n "${COINGECKO_PRO_KEY:-}" ]]; then echo "API_KEY_COINGECKO_PRO=$COINGECKO_PRO_KEY" >>/opt/ghostfolio/.env fi msg_ok "Set up Environment" msg_info "Running Database Migrations" cd /opt/ghostfolio $STD npx prisma migrate deploy $STD npx prisma db seed msg_ok "Database Migrations Complete" msg_info "Creating Service" cat </etc/systemd/system/ghostfolio.service [Unit] Description=Ghostfolio Investment Tracker After=network.target postgresql.service redis-server.service Wants=postgresql.service redis-server.service [Service] Type=simple User=root WorkingDirectory=/opt/ghostfolio/dist/apps/api Environment=NODE_ENV=production EnvironmentFile=/opt/ghostfolio/.env ExecStart=/usr/bin/node main.js Restart=always RestartSec=10 [Install] WantedBy=multi-user.target EOF systemctl enable -q --now ghostfolio msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/gitea-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # Co-author: Rogue-King # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://about.gitea.com/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ git \ sqlite3 msg_ok "Installed Dependencies" fetch_and_deploy_gh_release "gitea" "go-gitea/gitea" "singlefile" "latest" "/usr/local/bin" "gitea-*-linux-arm64" msg_info "Configuring Gitea" chmod +x /usr/local/bin/gitea $STD adduser --system --group --disabled-password --shell /bin/bash --home /etc/gitea gitea mkdir -p /var/lib/gitea/{custom,data,log} chown -R gitea:gitea /var/lib/gitea/ chmod -R 750 /var/lib/gitea/ chown root:gitea /etc/gitea chmod 770 /etc/gitea sudo -u gitea ln -s /var/lib/gitea/data/.ssh/ /etc/gitea/.ssh msg_ok "Configured Gitea" msg_info "Creating Service" cat </etc/systemd/system/gitea.service [Unit] Description=Gitea (Git with a cup of tea) After=syslog.target After=network.target [Service] # Uncomment notify and watchdog if you want to use them # Uncomment the next line if you have repos with lots of files and get a HTTP 500 error because of that # LimitNOFILE=524288:524288 RestartSec=2s Type=simple #Type=notify User=gitea Group=gitea #The mount point we added to the container WorkingDirectory=/var/lib/gitea #Create directory in /run RuntimeDirectory=gitea ExecStart=/usr/local/bin/gitea web --config /etc/gitea/app.ini Restart=always Environment=USER=gitea HOME=/var/lib/gitea/data GITEA_WORK_DIR=/var/lib/gitea #WatchdogSec=30s #Capabilities to bind to low-numbered ports #CapabilityBoundingSet=CAP_NET_BIND_SERVICE #AmbientCapabilities=CAP_NET_BIND_SERVICE [Install] WantedBy=multi-user.target EOF systemctl enable -q --now gitea msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/gitea-mirror-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: CrazyWolf13 # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/RayLabsHQ/gitea-mirror source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing dependencies" $STD apt install -y \ build-essential \ openssl \ sqlite3 \ unzip \ git msg_ok "Installed Dependencies" msg_info "Installing Bun" export BUN_INSTALL=/opt/bun curl -fsSL https://bun.sh/install | $STD bash ln -sf /opt/bun/bin/bun /usr/local/bin/bun ln -sf /opt/bun/bin/bun /usr/local/bin/bunx msg_ok "Installed Bun" fetch_and_deploy_gh_release "gitea-mirror" "RayLabsHQ/gitea-mirror" "tarball" msg_info "Installing gitea-mirror" cd /opt/gitea-mirror $STD bun run setup $STD bun run build msg_ok "Installed gitea-mirror" msg_info "Creating Services" APP_SECRET=$(openssl rand -base64 32) APP_VERSION=$(grep -o '"version": *"[^"]*"' package.json | cut -d'"' -f4) cat </opt/gitea-mirror.env # See here for config options: https://github.com/RayLabsHQ/gitea-mirror/blob/main/docs/ENVIRONMENT_VARIABLES.md NODE_ENV=production HOST=0.0.0.0 PORT=4321 DATABASE_URL=sqlite://data/gitea-mirror.db BETTER_AUTH_URL=http://${LOCAL_IP}:4321 BETTER_AUTH_SECRET=${APP_SECRET} npm_package_version=${APP_VERSION} EOF cat </etc/systemd/system/gitea-mirror.service [Unit] Description=Gitea Mirror After=network.target [Service] Type=simple WorkingDirectory=/opt/gitea-mirror ExecStart=/usr/local/bin/bun dist/server/entry.mjs Restart=on-failure RestartSec=10 EnvironmentFile=/opt/gitea-mirror.env [Install] WantedBy=multi-user.target EOF systemctl enable -q --now gitea-mirror msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/github-runner-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://docs.github.com/en/actions/hosting-your-own-runners source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ git \ gh msg_ok "Installed Dependencies" NODE_VERSION="24" setup_nodejs msg_info "Creating runner user (no sudo)" useradd -m -s /bin/bash runner msg_ok "Runner user ready" fetch_and_deploy_gh_release "actions-runner" "actions/runner" "prebuild" "latest" "/opt/actions-runner" "actions-runner-linux-x64-*.tar.gz" msg_info "Setting ownership for runner user" chown -R runner:runner /opt/actions-runner msg_ok "Ownership set" msg_info "Creating Service" cat </etc/systemd/system/actions-runner.service [Unit] Description=GitHub Actions self-hosted runner Documentation=https://docs.github.com/en/actions/hosting-your-own-runners After=network-online.target Wants=network-online.target [Service] Type=simple User=runner WorkingDirectory=/opt/actions-runner ExecStart=/opt/actions-runner/run.sh Restart=on-failure RestartSec=10 [Install] WantedBy=multi-user.target EOF systemctl enable -q actions-runner msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/glance-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: kristocopani # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/glanceapp/glance source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os fetch_and_deploy_gh_release "glance" "glanceapp/glance" "prebuild" "latest" "/opt/glance" "glance-linux-arm64.tar.gz" msg_info "Configuring Glance" cat </opt/glance/glance.yml pages: - name: Startpage width: slim hide-desktop-navigation: true center-vertically: true columns: - size: full widgets: - type: search autofocus: true - type: bookmarks groups: - title: General links: - title: Google url: https://www.google.com/ - title: Helper Scripts url: https://github.com/community-scripts/ProxmoxVE EOF msg_ok "Configured Glance" msg_info "Creating Service" service_path="/etc/systemd/system/glance.service" echo "[Unit] Description=Glance Daemon After=network.target [Service] Type=simple WorkingDirectory=/opt/glance ExecStart=/opt/glance/glance --config /opt/glance/glance.yml TimeoutStopSec=20 KillMode=process Restart=on-failure [Install] WantedBy=multi-user.target" >$service_path systemctl enable -q --now glance msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/globaleaks-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: Giovanni Pellerano (evilaliv3) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/globaleaks/globaleaks-whistleblowing-software source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Setup GlobaLeaks" DISTRO_CODENAME="$(awk -F= '/^VERSION_CODENAME=/{print $2}' /etc/os-release)" curl -fsSL https://deb.globaleaks.org/globaleaks.asc | gpg --dearmor -o /etc/apt/trusted.gpg.d/globaleaks.gpg echo "deb [signed-by=/etc/apt/trusted.gpg.d/globaleaks.gpg] http://deb.globaleaks.org $DISTRO_CODENAME/" >/etc/apt/sources.list.d/globaleaks.list echo 'APPARMOR_SANDBOXING=0' >/etc/default/globaleaks $STD apt update $STD apt -y -o Dpkg::Options::=--force-confdef -o Dpkg::Options::=--force-confold install globaleaks msg_ok "Setup GlobaLeaks" motd_ssh customize cleanup_lxc ================================================ FILE: install/glpi-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: Nícolas Pastorello (opastorello) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.glpi-project.org/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ git \ apache2 \ php8.4-{apcu,cli,common,curl,gd,ldap,mysql,xmlrpc,xml,mbstring,bcmath,intl,zip,redis,bz2,soap} \ php-cas \ libapache2-mod-php msg_ok "Installed Dependencies" setup_mariadb msg_info "Setting up database" DB_NAME=glpi_db DB_USER=glpi DB_PASS=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | head -c13) mariadb-tzinfo-to-sql /usr/share/zoneinfo | mariadb mysql $STD mariadb -u root -e "CREATE DATABASE $DB_NAME;" $STD mariadb -u root -e "CREATE USER '$DB_USER'@'localhost' IDENTIFIED BY '$DB_PASS';" $STD mariadb -u root -e "GRANT ALL PRIVILEGES ON $DB_NAME.* TO '$DB_USER'@'localhost';" $STD mariadb -u root -e "GRANT SELECT ON \`mysql\`.\`time_zone_name\` TO '$DB_USER'@'localhost'; FLUSH PRIVILEGES;" { echo "GLPI Database Credentials" echo "Database: $DB_NAME" echo "Username: $DB_USER" echo "Password: $DB_PASS" } >>~/glpi_db.creds msg_ok "Set up database" msg_info "Installing GLPi" cd /opt RELEASE=$(curl -fsSL https://api.github.com/repos/glpi-project/glpi/releases/latest | grep '"tag_name"' | sed -E 's/.*"tag_name": "([^"]+)".*/\1/') curl -fsSL "https://github.com/glpi-project/glpi/releases/download/${RELEASE}/glpi-${RELEASE}.tgz" -o $(basename "https://github.com/glpi-project/glpi/releases/download/${RELEASE}/glpi-${RELEASE}.tgz") $STD tar -xzvf glpi-${RELEASE}.tgz cd /opt/glpi echo "${RELEASE}" >/opt/${APPLICATION}_version.txt msg_ok "Installed GLPi" msg_info "Setting Downstream file" cat </opt/glpi/inc/downstream.php /etc/glpi/local_define.php /etc/apache2/sites-available/glpi.conf ServerName localhost DocumentRoot /opt/glpi/public Require all granted RewriteEngine On RewriteCond %{HTTP:Authorization} ^(.+)$ RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}] RewriteCond %{REQUEST_FILENAME} !-f RewriteRule ^(.*)$ index.php [QSA,L] ErrorLog \${APACHE_LOG_DIR}/glpi_error.log CustomLog \${APACHE_LOG_DIR}/glpi_access.log combined EOF $STD a2dissite 000-default.conf $STD a2enmod rewrite $STD a2ensite glpi.conf rm -rf /opt/glpi/install/install.php rm -rf /opt/glpi-${RELEASE}.tgz msg_ok "Setup Service" msg_info "Setup Cronjob" echo "* * * * * php /opt/glpi/front/cron.php" | crontab -u www-data - msg_ok "Setup Cronjob" msg_info "Update PHP Params" PHP_VERSION=$(ls /etc/php/ | grep -E '^[0-9]+\.[0-9]+$' | head -n 1) PHP_INI="/etc/php/$PHP_VERSION/apache2/php.ini" sed -i 's/^upload_max_filesize = .*/upload_max_filesize = 20M/' $PHP_INI sed -i 's/^post_max_size = .*/post_max_size = 20M/' $PHP_INI sed -i 's/^max_execution_time = .*/max_execution_time = 60/' $PHP_INI sed -i 's/^max_input_vars = .*/max_input_vars = 5000/' $PHP_INI sed -i 's/^memory_limit = .*/memory_limit = 256M/' $PHP_INI sed -i 's/^;\?\s*session.cookie_httponly\s*=.*/session.cookie_httponly = On/' $PHP_INI systemctl restart apache2 msg_ok "Update PHP Params" motd_ssh customize cleanup_lxc ================================================ FILE: install/gluetun-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/qdm12/gluetun source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ openvpn \ wireguard-tools \ iptables msg_ok "Installed Dependencies" msg_info "Configuring iptables" $STD update-alternatives --set iptables /usr/sbin/iptables-legacy $STD update-alternatives --set ip6tables /usr/sbin/ip6tables-legacy ln -sf /usr/sbin/openvpn /usr/sbin/openvpn2.6 msg_ok "Configured iptables" setup_go fetch_and_deploy_gh_release "gluetun" "qdm12/gluetun" "tarball" msg_info "Building Gluetun" cd /opt/gluetun $STD go mod download CGO_ENABLED=0 $STD go build -trimpath -ldflags="-s -w" -o /usr/local/bin/gluetun ./cmd/gluetun/ msg_ok "Built Gluetun" msg_info "Configuring Gluetun" mkdir -p /opt/gluetun-data touch /etc/alpine-release ln -sf /opt/gluetun-data /gluetun cat </opt/gluetun-data/.env VPN_SERVICE_PROVIDER=custom VPN_TYPE=openvpn OPENVPN_CUSTOM_CONFIG=/opt/gluetun-data/custom.ovpn OPENVPN_USER= OPENVPN_PASSWORD= OPENVPN_PROCESS_USER=root PUID=0 PGID=0 HTTP_CONTROL_SERVER_ADDRESS=:8000 HTTPPROXY=off SHADOWSOCKS=off PPROF_ENABLED=no PPROF_BLOCK_PROFILE_RATE=0 PPROF_MUTEX_PROFILE_RATE=0 PPROF_HTTP_SERVER_ADDRESS=:6060 FIREWALL_ENABLED_DISABLING_IT_SHOOTS_YOU_IN_YOUR_FOOT=on HEALTH_SERVER_ADDRESS=127.0.0.1:9999 DNS_UPSTREAM_RESOLVERS=cloudflare LOG_LEVEL=info STORAGE_FILEPATH=/gluetun/servers.json PUBLICIP_FILE=/gluetun/ip VPN_PORT_FORWARDING_STATUS_FILE=/gluetun/forwarded_port TZ=UTC EOF msg_ok "Configured Gluetun" msg_info "Creating Service" cat </etc/systemd/system/gluetun.service [Unit] Description=Gluetun VPN Client After=network.target [Service] Type=simple User=root WorkingDirectory=/opt/gluetun-data EnvironmentFile=/opt/gluetun-data/.env UnsetEnvironment=USER ExecStartPre=/bin/sh -c 'rm -f /etc/openvpn/target.ovpn' ExecStart=/usr/local/bin/gluetun Restart=on-failure RestartSec=5 AmbientCapabilities=CAP_NET_ADMIN [Install] WantedBy=multi-user.target EOF systemctl enable -q --now gluetun msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/go2rtc-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/AlexxIT/go2rtc source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os setup_hwaccel USE_ORIGINAL_FILENAME="true" fetch_and_deploy_gh_release "go2rtc" "AlexxIT/go2rtc" "singlefile" "latest" "/opt/go2rtc" "go2rtc_linux_arm64" msg_info "Creating Service" cat </etc/systemd/system/go2rtc.service echo "[Unit] Description=go2rtc service After=network.target [Service] Type=simple User=root WorkingDirectory=/opt/go2rtc ExecStart=/opt/go2rtc/go2rtc_linux_arm64 [Install] WantedBy=multi-user.target EOF systemctl enable -q --now go2rtc msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/gogs-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://gogs.io/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y git msg_ok "Installed Dependencies" fetch_and_deploy_gh_release "gogs" "gogs/gogs" "prebuild" "latest" "/opt/gogs" "gogs_*_linux_amd64.tar.gz" msg_info "Setting up Gogs" mkdir -p /opt/gogs/{custom/conf,data,log} msg_ok "Set up Gogs" msg_info "Creating Service" cat </etc/systemd/system/gogs.service [Unit] Description=Gogs Git Service After=network.target [Service] Type=simple User=root WorkingDirectory=/opt/gogs ExecStart=/opt/gogs/gogs web Restart=on-failure RestartSec=5 Environment=USER=root Environment=HOME=/root [Install] WantedBy=multi-user.target EOF systemctl enable -q --now gogs msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/gokapi-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/Forceu/Gokapi source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os fetch_and_deploy_gh_release "gokapi" "Forceu/Gokapi" "prebuild" "latest" "/opt/gokapi" "gokapi-linux_arm64.zip" msg_info "Configuring Gokapi" mkdir -p /opt/gokapi/{data,config} chmod +x /opt/gokapi/gokapi-linux_arm64 msg_ok "Configured Gokapi" msg_info "Creating Service" cat </etc/systemd/system/gokapi.service [Unit] Description=gokapi [Service] Type=simple Environment=GOKAPI_DATA_DIR=/opt/gokapi/data Environment=GOKAPI_CONFIG_DIR=/opt/gokapi/config ExecStart=/opt/gokapi/gokapi-linux_arm64 [Install] WantedBy=multi-user.target EOF systemctl enable -q --now gokapi msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/gotify-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://gotify.net/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os fetch_and_deploy_gh_release "gotify" "gotify/server" "prebuild" "latest" "/opt/gotify" "gotify-linux-arm64.zip" chmod +x /opt/gotify/gotify-linux-arm64 msg_info "Creating Service" cat </etc/systemd/system/gotify.service [Unit] Description=Gotify Requires=network.target After=network.target [Service] Type=simple User=root WorkingDirectory=/opt/gotify ExecStart=/opt/gotify/./gotify-linux-arm64 Restart=always RestartSec=3 [Install] WantedBy=multi-user.target EOF systemctl enable -q --now gotify msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/grafana-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://grafana.com/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y apt-transport-https msg_ok "Installed Dependencies" msg_info "Setting up Grafana Repository" setup_deb822_repo \ "grafana" \ "https://apt.grafana.com/gpg.key" \ "https://apt.grafana.com" \ "stable" \ "main" msg_ok "Grafana Repository setup sucessfully" msg_info "Installing Grafana" $STD apt install -y grafana systemctl enable -q --now grafana-server msg_ok "Installed Grafana" motd_ssh customize cleanup_lxc ================================================ FILE: install/gramps-web-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.grampsweb.org/ | Github: https://github.com/gramps-project/gramps-web source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ appstream \ build-essential \ ffmpeg \ gettext \ gobject-introspection \ gir1.2-gexiv2-0.10 \ gir1.2-gtk-3.0 \ gir1.2-osmgpsmap-1.0 \ gir1.2-pango-1.0 \ git \ graphviz \ libcairo2-dev \ libgirepository1.0-dev \ libglib2.0-dev \ libicu-dev \ libopencv-dev \ pkg-config \ poppler-utils \ python3-dev \ tesseract-ocr msg_ok "Installed Dependencies" PYTHON_VERSION="3.12" setup_uv NODE_VERSION="22" setup_nodejs fetch_and_deploy_gh_release "gramps-web-api" "gramps-project/gramps-web-api" "tarball" "latest" "/opt/gramps-web-api" fetch_and_deploy_gh_release "gramps-web" "gramps-project/gramps-web" "tarball" "latest" "/opt/gramps-web/frontend" msg_info "Setting up Gramps Web" mkdir -p \ /opt/gramps-web/config \ /opt/gramps-web/data/cache/export \ /opt/gramps-web/data/cache/persistent \ /opt/gramps-web/data/cache/report \ /opt/gramps-web/data/cache/request \ /opt/gramps-web/data/cache/thumbnail \ /opt/gramps-web/data/gramps/grampsdb \ /opt/gramps-web/data/indexdir \ /opt/gramps-web/data/media \ /opt/gramps-web/data/users SECRET_KEY="$(openssl rand -hex 32)" cat </opt/gramps-web/config/config.cfg TREE="Gramps Web" SECRET_KEY="${SECRET_KEY}" BASE_URL="http://${LOCAL_IP}:5000" USER_DB_URI="sqlite:////opt/gramps-web/data/users/users.sqlite" SEARCH_INDEX_DB_URI="sqlite:////opt/gramps-web/data/indexdir/search_index.db" MEDIA_BASE_DIR="/opt/gramps-web/data/media" STATIC_PATH="/opt/gramps-web/frontend/dist" THUMBNAIL_CACHE_CONFIG={"CACHE_TYPE":"FileSystemCache","CACHE_DIR":"/opt/gramps-web/data/cache/thumbnail","CACHE_THRESHOLD":1000,"CACHE_DEFAULT_TIMEOUT":0} REQUEST_CACHE_CONFIG={"CACHE_TYPE":"FileSystemCache","CACHE_DIR":"/opt/gramps-web/data/cache/request","CACHE_THRESHOLD":1000,"CACHE_DEFAULT_TIMEOUT":0} PERSISTENT_CACHE_CONFIG={"CACHE_TYPE":"FileSystemCache","CACHE_DIR":"/opt/gramps-web/data/cache/persistent","CACHE_THRESHOLD":0,"CACHE_DEFAULT_TIMEOUT":0} REPORT_DIR="/opt/gramps-web/data/cache/report" EXPORT_DIR="/opt/gramps-web/data/cache/export" EOF $STD uv venv -c -p python3.12 /opt/gramps-web/venv source /opt/gramps-web/venv/bin/activate $STD uv pip install --no-cache-dir --upgrade pip setuptools wheel $STD uv pip install --no-cache-dir gunicorn $STD uv pip install --no-cache-dir /opt/gramps-web-api GRAMPS_VERSION=$(/opt/gramps-web/venv/bin/python3 -c "import gramps.version; print('%s%s' % (gramps.version.VERSION_TUPLE[0], gramps.version.VERSION_TUPLE[1]))" 2>/dev/null || echo "60") GRAMPS_PLUGINS_DIR="/opt/gramps-web/data/gramps/gramps${GRAMPS_VERSION}/plugins" mkdir -p "$GRAMPS_PLUGINS_DIR" msg_info "Installing Gramps Addons (gramps${GRAMPS_VERSION})" $STD wget -q https://github.com/gramps-project/addons/archive/refs/heads/master.zip -O /tmp/gramps-addons.zip for addon in FilterRules JSON; do unzip -p /tmp/gramps-addons.zip "addons-master/gramps${GRAMPS_VERSION}/download/${addon}.addon.tgz" | \ tar -xz -C "$GRAMPS_PLUGINS_DIR" done rm -f /tmp/gramps-addons.zip msg_ok "Installed Gramps Addons" cd /opt/gramps-web/frontend export COREPACK_ENABLE_DOWNLOAD_PROMPT=0 $STD corepack enable $STD npm install $STD npm run build cd /opt/gramps-web-api GRAMPS_API_CONFIG=/opt/gramps-web/config/config.cfg \ ALEMBIC_CONFIG=/opt/gramps-web-api/alembic.ini \ GRAMPSHOME=/opt/gramps-web/data \ GRAMPS_DATABASE_PATH=/opt/gramps-web/data/gramps/grampsdb \ $STD /opt/gramps-web/venv/bin/python3 -m gramps_webapi user migrate msg_ok "Set up Gramps Web" msg_info "Creating Service" cat </etc/systemd/system/gramps-web.service [Unit] Description=Gramps Web Service After=network.target [Service] Type=simple User=root WorkingDirectory=/opt/gramps-web-api Environment=GRAMPS_API_CONFIG=/opt/gramps-web/config/config.cfg Environment=GRAMPSHOME=/opt/gramps-web/data Environment=GRAMPS_DATABASE_PATH=/opt/gramps-web/data/gramps/grampsdb Environment=PATH=/opt/gramps-web/venv/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin ExecStart=/opt/gramps-web/venv/bin/gunicorn -w 2 -b 0.0.0.0:5000 gramps_webapi.wsgi:app --timeout 120 --limit-request-line 8190 Restart=on-failure RestartSec=5 [Install] WantedBy=multi-user.target EOF systemctl enable -q --now gramps-web msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/graylog-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: Slaviša Arežina (tremor021) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://graylog.org/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os MONGO_VERSION="8.2" setup_mongodb msg_info "Setup Graylog Data Node" PASSWORD_SECRET=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | head -c16) curl -fsSL "https://packages.graylog2.org/repo/packages/graylog-7.0-repository_latest.deb" -o "graylog-7.0-repository_latest.deb" $STD dpkg -i graylog-7.0-repository_latest.deb $STD apt-get update $STD apt-get install graylog-datanode -y sed -i "s/password_secret =/password_secret = $PASSWORD_SECRET/g" /etc/graylog/datanode/datanode.conf systemctl enable -q --now graylog-datanode msg_ok "Setup Graylog Data Node" msg_info "Setup ${APPLICATION}" $STD apt-get install graylog-server ROOT_PASSWORD=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | head -c16) { echo "${APPLICATION} Credentials" echo "Admin User: admin" echo "Admin Password: ${ROOT_PASSWORD}" } >>~/graylog.creds ROOT_PASSWORD=$(echo -n $ROOT_PASSWORD | shasum -a 256 | awk '{print $1}') sed -i "s/password_secret =/password_secret = $PASSWORD_SECRET/g" /etc/graylog/server/server.conf sed -i "s/root_password_sha2 =/root_password_sha2 = $ROOT_PASSWORD/g" /etc/graylog/server/server.conf sed -i 's/#http_bind_address = 127.0.0.1.*/http_bind_address = 0.0.0.0:9000/g' /etc/graylog/server/server.conf systemctl enable -q --now graylog-server sleep 5 sed -i "s/0\.0\.0\.0:9000/$LOCAL_IP:9000/g" /var/log/graylog-server/server.log msg_ok "Setup ${APPLICATION}" motd_ssh customize cleanup_lxc ================================================ FILE: install/grist-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: cfurrow | Co-Author: Slaviša Arežina (tremor021) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/gristlabs/grist-core source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ make \ ca-certificates \ python3-venv \ git msg_ok "Installed Dependencies" NODE_VERSION="22" NODE_MODULE="yarn@latest" setup_nodejs fetch_and_deploy_gh_release "grist" "gristlabs/grist-core" "tarball" msg_info "Installing Grist" export CYPRESS_INSTALL_BINARY=0 export NODE_OPTIONS="--max-old-space-size=2048" cd /opt/grist $STD yarn install $STD yarn run build:prod $STD yarn run install:python cat </opt/grist/.env NODE_ENV=production GRIST_HOST=0.0.0.0 EOF msg_ok "Installed Grist" msg_info "Create Service" cat </etc/systemd/system/grist.service [Unit] Description=Grist After=network.target [Service] Type=simple WorkingDirectory=/opt/grist ExecStart=/usr/bin/yarn run start:prod EnvironmentFile=-/opt/grist/.env [Install] WantedBy=multi-user.target EOF systemctl enable -q --now grist msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/grocy-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://grocy.info/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y apt-transport-https msg_ok "Installed Dependencies" PHP_VERSION="8.5" PHP_APACHE="YES" setup_php fetch_and_deploy_gh_release "grocy" "grocy/grocy" "prebuild" "latest" "/var/www/html" "grocy*.zip" msg_info "Configuring grocy" chown -R www-data:www-data /var/www/html cp /var/www/html/config-dist.php /var/www/html/data/config.php chmod +x /var/www/html/update.sh cat </etc/apache2/sites-available/grocy.conf ServerAdmin webmaster@localhost DocumentRoot /var/www/html/public ErrorLog /var/log/apache2/error.log Options Indexes FollowSymLinks MultiViews AllowOverride All Order allow,deny allow from all EOF $STD a2dissite 000-default.conf $STD a2ensite grocy.conf $STD a2enmod rewrite systemctl reload apache2 msg_ok "Installed grocy" motd_ssh customize cleanup_lxc ================================================ FILE: install/guardian-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: HydroshieldMKII # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/HydroshieldMKII/Guardian source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y sqlite3 msg_ok "Installed Dependencies" NODE_VERSION="24" setup_nodejs fetch_and_deploy_gh_release "guardian" "HydroshieldMKII/Guardian" "tarball" "latest" "/opt/guardian" msg_info "Configuring ${APPLICATION}" cd /opt/guardian/backend $STD npm ci $STD npm run build cd /opt/guardian/frontend $STD npm ci export DEPLOYMENT_MODE=standalone $STD npm run build msg_ok "Configured ${APPLICATION}" msg_info "Creating Service" cat </etc/systemd/system/guardian-backend.service [Unit] Description=Guardian Backend After=network.target [Service] WorkingDirectory=/opt/guardian/backend ExecStart=/usr/bin/node dist/main.js Restart=always RestartSec=3 [Install] WantedBy=multi-user.target EOF cat </etc/systemd/system/guardian-frontend.service [Unit] Description=Guardian Frontend After=guardian-backend.service network.target Wants=guardian-backend.service [Service] WorkingDirectory=/opt/guardian/frontend Environment=DEPLOYMENT_MODE=standalone ExecStart=/usr/bin/npm run start Restart=always RestartSec=3 [Install] WantedBy=multi-user.target EOF systemctl enable -q --now guardian-backend systemctl enable -q --now guardian-frontend msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/gwn-manager-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: Slaviša Arežina (tremor021) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.grandstream.com/products/networking-solutions/wi-fi-management/product/gwn-manager source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ xfonts-utils \ fontconfig msg_ok "Installed Dependencies" msg_info "Setting up GWN Manager (Patience)" RELEASE=$(curl -fsSL https://www.grandstream.com/support/tools#gwntools \ | grep -oP 'https://firmware\.grandstream\.com/GWN_Manager-[^"]+-Ubuntu\.tar\.gz') download_file "$RELEASE" "/tmp/gwnmanager.tar.gz" cd /tmp tar -xzf gwnmanager.tar.gz --strip-components=1 $STD ./install msg_ok "Setup GWN Manager" msg_info "Creating Service" cat </etc/systemd/system/gwnmanager.service [Unit] Description=GWN Manager After=network.target Requires=network.target [Service] Type=simple WorkingDirectory=/gwn ExecStart=/gwn/gwn start Restart=on-failure RestartSec=10 [Install] WantedBy=multi-user.target EOF systemctl enable -q gwnmanager msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/headscale-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/juanfont/headscale source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os fetch_and_deploy_gh_release "headscale" "juanfont/headscale" "binary" read -r -p "${TAB3}Would you like to add headscale-admin UI? " prompt if [[ ${prompt,,} =~ ^(y|yes)$ ]]; then fetch_and_deploy_gh_release "headscale-admin" "GoodiesHQ/headscale-admin" "prebuild" "latest" "/opt/headscale-admin" "admin.zip" msg_info "Configuring headscale-admin" $STD apt install -y caddy $STD caddy stop rm /etc/caddy/Caddyfile cat <<'EOF' >/etc/caddy/Caddyfile :80 redir /admin /admin/ handle_path /admin/* { root * /opt/headscale-admin encode gzip zstd header { X-Content-Type-Options nosniff } try_files {path} /opt/headscale-admin/index.html file_server } reverse_proxy localhost:8080 EOF caddy fmt --overwrite /etc/caddy/Caddyfile systemctl start caddy msg_ok "Configured headscale-admin" fi msg_info "Starting service" systemctl enable -q --now headscale msg_ok "Service started" motd_ssh customize cleanup_lxc ================================================ FILE: install/healthchecks-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (Canbiz) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://healthchecks.io/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ gcc \ python3 \ python3-dev \ python3-venv \ libpq-dev \ libcurl4-openssl-dev \ libssl-dev \ caddy mkdir -p ~/.config/pip cat >~/.config/pip/pip.conf <>~/healthchecks.creds msg_ok "Set up Keys" fetch_and_deploy_gh_release "healthchecks" "healthchecks/healthchecks" "tarball" msg_info "Installing Healthchecks (venv)" cd /opt/healthchecks python3 -m venv venv source venv/bin/activate $STD pip install --upgrade pip wheel $STD pip install gunicorn -r requirements.txt msg_ok "Installed Python packages" cat </opt/healthchecks/hc/local_settings.py DEBUG = False ALLOWED_HOSTS = ["${LOCAL_IP}", "127.0.0.1", "localhost"] CSRF_TRUSTED_ORIGINS = ["http://${LOCAL_IP}", "https://${LOCAL_IP}"] SECRET_KEY = "${SECRET_KEY}" SITE_ROOT = "http://${LOCAL_IP}:8000" SITE_NAME = "MyChecks" DEFAULT_FROM_EMAIL = "healthchecks@${LOCAL_IP}" STATIC_ROOT = "/opt/healthchecks/static-collected" COMPRESS_OFFLINE = True DATABASES = { 'default': { 'ENGINE': 'django.db.backends.postgresql', 'NAME': '${PG_DB_NAME}', 'USER': '${PG_DB_USER}', 'PASSWORD': '${PG_DB_PASS}', 'HOST': '127.0.0.1', 'PORT': '5432', 'TEST': {'CHARSET': 'UTF8'} } } EOF msg_info "Running Django setup" $STD python manage.py makemigrations $STD python manage.py migrate --noinput $STD python manage.py collectstatic --noinput $STD python manage.py compress $STD python manage.py shell </etc/caddy/Caddyfile { email admin@example.com } ${LOCAL_IP} { reverse_proxy 127.0.0.1:8000 } EOF msg_ok "Configured Caddy" msg_info "Creating systemd services" cat </etc/systemd/system/healthchecks.service [Unit] Description=Healthchecks Service After=network.target postgresql.service [Service] WorkingDirectory=/opt/healthchecks/ ExecStart=/opt/healthchecks/venv/bin/gunicorn hc.wsgi:application --bind 127.0.0.1:8000 Restart=always [Install] WantedBy=multi-user.target EOF cat </etc/systemd/system/healthchecks-sendalerts.service [Unit] Description=Healthchecks Sendalerts Service After=network.target postgresql.service healthchecks.service [Service] WorkingDirectory=/opt/healthchecks/ ExecStart=/opt/healthchecks/venv/bin/python manage.py sendalerts Restart=always [Install] WantedBy=multi-user.target EOF systemctl enable -q --now healthchecks healthchecks-sendalerts caddy systemctl reload caddy msg_ok "Created Services" motd_ssh customize cleanup_lxc ================================================ FILE: install/heimdall-dashboard-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://heimdall.site/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y apt-transport-https msg_ok "Installed Dependencies" PHP_VERSION="8.4" PHP_FPM="YES" setup_php setup_composer fetch_and_deploy_gh_release "Heimdall" "linuxserver/Heimdall" "tarball" msg_info "Setting up Heimdall-Dashboard" cd /opt/Heimdall cp .env.example .env $STD php artisan key:generate msg_ok "Setup Heimdall-Dashboard" msg_info "Creating Service" cat </etc/systemd/system/heimdall.service [Unit] Description=Heimdall After=network.target [Service] Restart=always RestartSec=5 Type=simple User=root WorkingDirectory=/opt/Heimdall ExecStart=/usr/bin/php artisan serve --port 7990 --host 0.0.0.0 TimeoutStopSec=30 [Install] WantedBy=multi-user.target EOF systemctl enable -q --now heimdall cd /opt/Heimdall export COMPOSER_ALLOW_SUPERUSER=1 $STD composer dump-autoload systemctl restart heimdall.service msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/hev-socks5-server-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: miviro # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/heiher/hev-socks5-server source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os fetch_and_deploy_gh_release "hev-socks5-server" "heiher/hev-socks5-server" "singlefile" "latest" "/opt" "hev-socks5-server-linux-arm64" msg_info "Setup hev-socks5-server" mkdir -p /etc/hev-socks5-server download_file "https://raw.githubusercontent.com/heiher/hev-socks5-server/refs/heads/main/conf/main.yml" "/etc/hev-socks5-server/main.yml" sed -i 's/^#auth:/auth:/; s/^# file: conf\/auth.txt/ file: \/root\/hev.creds/' /etc/hev-socks5-server/main.yml PASSWORD=$(openssl rand -base64 16) echo "admin $PASSWORD 0" >/root/hev.creds msg_ok "Setup hev-socks5-server" msg_info "Creating Service" cat </etc/systemd/system/hev-socks5-server.service [Unit] Description=hev-socks5-server Service After=network.target [Service] ExecStart=/opt/hev-socks5-server /etc/hev-socks5-server/main.yml Restart=always [Install] WantedBy=multi-user.target EOF systemctl enable -q --now hev-socks5-server msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/hivemq-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.hivemq.com/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os JAVA_VERSION="21" setup_java fetch_and_deploy_gh_release "hivemq" "hivemq/hivemq-community-edition" "prebuild" "latest" "/opt/hivemq" "hivemq-ce-*.zip" msg_info "Configuring HiveMQ CE" useradd -d /opt/hivemq hivemq chown -R hivemq:hivemq /opt/hivemq chmod +x /opt/hivemq/bin/run.sh cp /opt/hivemq/bin/init-script/hivemq.service /etc/systemd/system/hivemq.service rm /opt/hivemq/conf/config.xml mv /opt/hivemq/conf/examples/configuration/config-sample-tcp-and-websockets.xml /opt/hivemq/conf/config.xml msg_ok "Configured HiveMQ CE" msg_info "Starting service" systemctl enable -q --now hivemq msg_ok "Service started" motd_ssh customize cleanup_lxc ================================================ FILE: install/homarr-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) | Co-Author: CrazyWolf13 # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/homarr-labs/homarr source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ redis-server \ nginx \ gettext \ openssl msg_ok "Installed Dependencies" NODE_VERSION=$(curl -s https://raw.githubusercontent.com/homarr-labs/homarr/dev/package.json | jq -r '.engines.node | split(">=")[1] | split(".")[0]') setup_nodejs fetch_and_deploy_gh_release "homarr" "homarr-labs/homarr" "prebuild" "latest" "/opt/homarr" "build-debian-arm64.tar.gz" msg_info "Installing Homarr" mkdir -p /opt/homarr_db touch /opt/homarr_db/db.sqlite SECRET_ENCRYPTION_KEY="$(openssl rand -hex 32)" cd /opt/homarr cat </opt/homarr.env DB_DRIVER='better-sqlite3' DB_DIALECT='sqlite' SECRET_ENCRYPTION_KEY='${SECRET_ENCRYPTION_KEY}' DB_URL='/opt/homarr_db/db.sqlite' TURBO_TELEMETRY_DISABLED=1 AUTH_PROVIDERS='credentials' NODE_ENV='production' REDIS_IS_EXTERNAL='true' EOF msg_ok "Installed Homarr" msg_info "Copying config files" mkdir -p /appdata/redis chown -R redis:redis /appdata/redis chmod 744 /appdata/redis cp /opt/homarr/redis.conf /etc/redis/redis.conf sed -i -e '$a\' /etc/redis/redis.conf grep -q '^bind 127.0.0.1 -::1$' /etc/redis/redis.conf || echo "bind 127.0.0.1 -::1" >>/etc/redis/redis.conf rm /etc/nginx/nginx.conf mkdir -p /etc/nginx/templates cp /opt/homarr/nginx.conf /etc/nginx/templates/nginx.conf echo $'#!/bin/bash\ncd /opt/homarr/apps/cli && node ./cli.cjs "$@"' >/usr/bin/homarr chmod +x /usr/bin/homarr msg_ok "Copied config files" msg_info "Creating Services" mkdir -p /etc/systemd/system/redis-server.service.d/ cat </etc/systemd/system/redis-server.service.d/override.conf [Service] ReadWritePaths=-/appdata/redis -/var/lib/redis -/var/log/redis -/var/run/redis -/etc/redis EOF cat </etc/systemd/system/homarr.service [Unit] Requires=redis-server.service After=redis-server.service Description=Homarr Service After=network.target [Service] Type=exec WorkingDirectory=/opt/homarr EnvironmentFile=-/opt/homarr.env ExecStart=/opt/homarr/run.sh [Install] WantedBy=multi-user.target EOF chmod +x /opt/homarr/run.sh systemctl daemon-reload systemctl enable -q --now redis-server systemctl enable -q --now homarr systemctl disable -q --now nginx msg_ok "Created Services" motd_ssh customize cleanup_lxc ================================================ FILE: install/homeassistant-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.home-assistant.io/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Setup Python3" $STD apt install -y \ python3 \ python3-dev \ python3-pip \ python3-venv rm -rf /usr/lib/python3.*/EXTERNALLY-MANAGED msg_ok "Setup Python3" msg_info "Installing runlike" $STD pip install runlike msg_ok "Installed runlike" get_latest_release() { curl -fsSL https://api.github.com/repos/$1/releases/latest | grep '"tag_name":' | cut -d'"' -f4 } DOCKER_LATEST_VERSION=$(get_latest_release "moby/moby") CORE_LATEST_VERSION=$(get_latest_release "home-assistant/core") PORTAINER_LATEST_VERSION=$(get_latest_release "portainer/portainer") msg_info "Installing Docker $DOCKER_LATEST_VERSION" DOCKER_CONFIG_PATH='/etc/docker/daemon.json' mkdir -p $(dirname $DOCKER_CONFIG_PATH) echo -e '{\n "log-driver": "journald"\n}' >/etc/docker/daemon.json $STD sh <(curl -fsSL https://get.docker.com) msg_ok "Installed Docker $DOCKER_LATEST_VERSION" msg_info "Pulling Portainer $PORTAINER_LATEST_VERSION Image" $STD docker pull portainer/portainer-ce:latest msg_ok "Pulled Portainer $PORTAINER_LATEST_VERSION Image" msg_info "Installing Portainer $PORTAINER_LATEST_VERSION" $STD docker volume create portainer_data $STD docker run -d \ -p 8000:8000 \ -p 9443:9443 \ --name=portainer \ --restart=always \ -v /var/run/docker.sock:/var/run/docker.sock \ -v portainer_data:/data \ portainer/portainer-ce:latest msg_ok "Installed Portainer $PORTAINER_LATEST_VERSION" msg_info "Pulling Home Assistant $CORE_LATEST_VERSION Image" $STD docker pull ghcr.io/home-assistant/home-assistant:stable msg_ok "Pulled Home Assistant $CORE_LATEST_VERSION Image" msg_info "Installing Home Assistant $CORE_LATEST_VERSION" $STD docker volume create hass_config $STD docker run -d \ --name homeassistant \ --privileged \ --restart unless-stopped \ -v /var/run/docker.sock:/var/run/docker.sock \ -v /dev:/dev \ -v hass_config:/config \ -v /etc/localtime:/etc/localtime:ro \ --net=host \ ghcr.io/home-assistant/home-assistant:stable mkdir /root/hass_config msg_ok "Installed Home Assistant $CORE_LATEST_VERSION" motd_ssh customize cleanup_lxc ================================================ FILE: install/homebox-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck # Co-Author: MickLesk (Canbiz) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/sysadminsmedia/homebox source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os fetch_and_deploy_gh_release "homebox" "sysadminsmedia/homebox" "prebuild" "latest" "/opt/homebox" "homebox_Linux_arm64.tar.gz" msg_info "Configuring Homebox" chmod +x /opt/homebox/homebox cat </opt/homebox/.env # For possible environment variables check here: https://homebox.software/en/configure-homebox HBOX_MODE=production HBOX_WEB_PORT=7745 HBOX_WEB_HOST=0.0.0.0 EOF msg_ok "Configured Homebox" msg_info "Creating Service" cat </etc/systemd/system/homebox.service [Unit] Description=Start Homebox Service After=network.target [Service] WorkingDirectory=/opt/homebox ExecStart=/opt/homebox/homebox EnvironmentFile=/opt/homebox/.env Restart=on-failure [Install] WantedBy=multi-user.target EOF systemctl enable -q --now homebox msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/homebridge-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://homebridge.io/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y avahi-daemon msg_ok "Installed Dependencies" msg_info "Setting up Homebridge Repository" setup_deb822_repo \ "homebridge" \ "https://repo.homebridge.io/KEY.gpg" \ "https://repo.homebridge.io" \ "stable" msg_ok "Set up Homebridge Repository" msg_info "Installing Homebridge" $STD apt install -y homebridge msg_ok "Installed Homebridge" motd_ssh customize cleanup_lxc ================================================ FILE: install/homelable-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/Pouzor/homelable source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ nmap \ iputils-ping \ caddy msg_ok "Installed Dependencies" UV_PYTHON="3.13" setup_uv NODE_VERSION="20" setup_nodejs fetch_and_deploy_gh_release "homelable" "Pouzor/homelable" "tarball" "latest" "/opt/homelable" msg_info "Setting up Python Backend" cd /opt/homelable/backend $STD uv venv /opt/homelable/backend/.venv $STD uv pip install --python /opt/homelable/backend/.venv/bin/python -r requirements.txt msg_ok "Set up Python Backend" msg_info "Configuring Homelable" mkdir -p /opt/homelable/data SECRET_KEY=$(openssl rand -hex 32) BCRYPT_HASH=$(/opt/homelable/backend/.venv/bin/python -c "from passlib.context import CryptContext; print(CryptContext(schemes=['bcrypt']).hash('admin'))") cat </opt/homelable/backend/.env SECRET_KEY=${SECRET_KEY} SQLITE_PATH=/opt/homelable/data/homelab.db CORS_ORIGINS=["http://localhost:3000","http://${LOCAL_IP}:3000"] AUTH_USERNAME=admin AUTH_PASSWORD_HASH='${BCRYPT_HASH}' SCANNER_RANGES=["192.168.1.0/24"] STATUS_CHECKER_INTERVAL=60 EOF msg_ok "Configured Homelable" msg_info "Creating Password Reset Utility" cat <<'EOF' >/root/change_password.sh #!/usr/bin/env bash NEW_PASS="" while [[ -z "$NEW_PASS" ]]; do read -s -p "Enter new password: " NEW_PASS echo "" if [[ -z "$NEW_PASS" ]]; then echo "Error: Password cannot be blank. Try again." fi done HASH=$(/opt/homelable/backend/.venv/bin/python -c "from passlib.context import CryptContext; print(CryptContext(schemes=['bcrypt']).hash('${NEW_PASS}'))") sed -i "s|^AUTH_PASSWORD_HASH=.*|AUTH_PASSWORD_HASH='${HASH}'|" /opt/homelable/backend/.env systemctl restart homelable echo "Password updated and service restarted successfully!" EOF chmod +x /root/change_password.sh msg_ok "Created Password Reset Utility" msg_info "Building Frontend" cd /opt/homelable/frontend $STD npm ci $STD npm run build msg_ok "Built Frontend" msg_info "Creating Service" cat </etc/systemd/system/homelable.service [Unit] Description=Homelable Backend After=network.target [Service] Type=simple WorkingDirectory=/opt/homelable/backend EnvironmentFile=/opt/homelable/backend/.env ExecStart=/opt/homelable/backend/.venv/bin/uvicorn app.main:app --host 127.0.0.1 --port 8000 Restart=on-failure RestartSec=5 [Install] WantedBy=multi-user.target EOF systemctl enable -q --now homelable msg_ok "Created Service" msg_info "Configuring Caddy" cat </etc/caddy/Caddyfile :3000 { root * /opt/homelable/frontend/dist file_server @websocket path /api/v1/status/ws/* handle @websocket { reverse_proxy 127.0.0.1:8000 } handle /ws/* { reverse_proxy 127.0.0.1:8000 } handle /api/* { reverse_proxy 127.0.0.1:8000 } handle { try_files {path} {path}.html /index.html } } EOF systemctl reload caddy msg_ok "Configured Caddy" motd_ssh customize cleanup_lxc ================================================ FILE: install/homepage-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://gethomepage.dev/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt-get install -y jq msg_ok "Installed Dependencies" NODE_VERSION="22" NODE_MODULE="pnpm@latest" setup_nodejs fetch_and_deploy_gh_release "homepage" "gethomepage/homepage" "tarball" RELEASE=$(get_latest_github_release "gethomepage/homepage") msg_info "Installing Homepage (Patience)" mkdir -p /opt/homepage/config cd /opt/homepage cp /opt/homepage/src/skeleton/* /opt/homepage/config echo 'onlyBuiltDependencies=*' >> .npmrc $STD pnpm install export NEXT_PUBLIC_VERSION="v$RELEASE" export NEXT_PUBLIC_REVISION="source" export NEXT_PUBLIC_BUILDTIME=$(curl -fsSL https://api.github.com/repos/gethomepage/homepage/releases/latest | jq -r '.published_at') export NEXT_TELEMETRY_DISABLED=1 $STD pnpm build echo "HOMEPAGE_ALLOWED_HOSTS=localhost:3000,${LOCAL_IP}:3000" >/opt/homepage/.env msg_ok "Installed Homepage" msg_info "Creating Service" cat </etc/systemd/system/homepage.service [Unit] Description=Homepage After=network.target StartLimitIntervalSec=0 [Service] Type=simple Restart=always RestartSec=1 User=root WorkingDirectory=/opt/homepage/ ExecStart=pnpm start [Install] WantedBy=multi-user.target EOF systemctl enable -q --now homepage msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/homer-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/bastienwirtz/homer source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os fetch_and_deploy_gh_release "homer" "bastienwirtz/homer" "prebuild" "latest" "/opt/homer" "homer.zip" cp /opt/homer/assets/config.yml.dist /opt/homer/assets/config.yml msg_info "Creating Service" cat </etc/systemd/system/homer.service [Unit] Description=Homer Dashboard After=network-online.target Wants=network-online.target [Service] Type=simple WorkingDirectory=/opt/homer ExecStart=python3 -m http.server 8010 [Install] WantedBy=multi-user.target EOF systemctl enable -q --now homer msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/hoodik-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/hudikhq/hoodik source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os fetch_and_deploy_gh_release "hoodik" "hudikhq/hoodik" "prebuild" "latest" "/opt/hoodik" "*x86_64.tar.gz" msg_info "Configuring Hoodik" mkdir -p /opt/hoodik_data JWT_SECRET=$(openssl rand -base64 32 | tr -dc 'a-zA-Z0-9' | cut -c1-32) cat </opt/hoodik/.env DATA_DIR=/opt/hoodik_data HTTP_PORT=5443 HTTP_ADDRESS=0.0.0.0 JWT_SECRET=${JWT_SECRET} APP_URL=http://${LOCAL_IP}:5443 SSL_DISABLED=true COOKIE_SECURE=false COOKIE_HTTP_ONLY=false MAILER_TYPE=none RUST_LOG=hoodik=info,error=info EOF msg_ok "Configured Hoodik" msg_info "Creating Service" cat </etc/systemd/system/hoodik.service [Unit] Description=Hoodik - Encrypted File Storage After=network.target [Service] Type=simple User=root WorkingDirectory=/opt/hoodik_data EnvironmentFile=/opt/hoodik/.env ExecStart=/opt/hoodik/hoodik Restart=always RestartSec=5 [Install] WantedBy=multi-user.target EOF systemctl enable -q --now hoodik msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/hortusfox-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/danielbrendel/hortusfox-web source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os setup_mariadb MARIADB_DB_NAME="hortusfox" MARIADB_DB_USER="hortusfox" setup_mariadb_db PHP_VERSION="8.3" PHP_APACHE="YES" setup_php setup_composer fetch_and_deploy_gh_release "hortusfox" "danielbrendel/hortusfox-web" "tarball" msg_info "Configuring .env" cp /opt/hortusfox/.env.example /opt/hortusfox/.env sed -i "s|^DB_HOST=.*|DB_HOST=localhost|" /opt/hortusfox/.env sed -i "s|^DB_USER=.*|DB_USER=$MARIADB_DB_USER|" /opt/hortusfox/.env sed -i "s|^DB_PASSWORD=.*|DB_PASSWORD=$MARIADB_DB_PASS|" /opt/hortusfox/.env sed -i "s|^DB_DATABASE=.*|DB_DATABASE=$MARIADB_DB_NAME|" /opt/hortusfox/.env sed -i "s|^DB_ENABLE=.*|DB_ENABLE=true|" /opt/hortusfox/.env sed -i "s|^APP_TIMEZONE=.*|APP_TIMEZONE=Europe/Berlin|" /opt/hortusfox/.env msg_ok ".env configured" msg_info "Installing Composer dependencies" cd /opt/hortusfox $STD composer install --no-dev --optimize-autoloader msg_ok "Composer dependencies installed" msg_info "Running DB migration" $STD php asatru migrate:fresh msg_ok "Migration finished" msg_info "Setting up HortusFox" $STD mariadb -u root -D $MARIADB_DB_NAME -e "INSERT IGNORE INTO AppModel (workspace, language, created_at) VALUES ('Default Workspace', 'en', NOW());" $STD php asatru plants:attributes $STD php asatru calendar:classes ADMIN_EMAIL="admin@example.com" ADMIN_PASS="$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | head -c13)" ADMIN_HASH=$(php -r "echo password_hash('$ADMIN_PASS', PASSWORD_BCRYPT);") $STD mariadb -u root -D $MARIADB_DB_NAME -e "INSERT IGNORE INTO UserModel (name, email, password, admin) VALUES ('Admin', '$ADMIN_EMAIL', '$ADMIN_HASH', 1);" { echo "" echo "HortusFox-Admin-Creds:" echo "E-Mail: $ADMIN_EMAIL" echo "Passwort: $ADMIN_PASS" } >>~/hortusfox.creds $STD mariadb -u root -D $MARIADB_DB_NAME -e "INSERT IGNORE INTO LocationsModel (name, active, created_at) VALUES ('Home', 1, NOW());" msg_ok "Set up HortusFox" msg_info "Configuring Apache vHost" cat </etc/apache2/sites-available/hortusfox.conf ServerAdmin webmaster@localhost DocumentRoot /opt/hortusfox/public Options Indexes FollowSymLinks AllowOverride All Require all granted ErrorLog \${APACHE_LOG_DIR}/hortusfox_error.log CustomLog \${APACHE_LOG_DIR}/hortusfox_access.log combined EOF chown -R www-data:www-data /opt/hortusfox $STD a2dissite 000-default $STD a2ensite hortusfox $STD a2enmod rewrite systemctl restart apache2 msg_ok "Apache configured" motd_ssh customize cleanup_lxc ================================================ FILE: install/hyperhdr-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.hyperhdr.eu/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os setup_hwaccel msg_info "Installing HyperHDR" setup_deb822_repo \ "hyperhdr" \ "https://awawa-dev.github.io/hyperhdr.public.apt.gpg.key" \ "https://awawa-dev.github.io" \ "$(get_os_info codename)" $STD apt install -y hyperhdr msg_ok "Installed HyperHDR" msg_info "Creating Service" cat </etc/systemd/system/hyperhdr.service [Unit] Description=HyperHDR Service After=syslog.target network.target [Service] Restart=on-failure RestartSec=5 Type=simple ExecStart=/usr/bin/hyperhdr [Install] WantedBy=multi-user.target EOF systemctl enable -q --now hyperhdr msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/hyperion-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://hyperion-project.org/forum/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os setup_hwaccel msg_info "Installing Dependencies" $STD apt-get install -y lsb-release $STD apt-get install -y apt-transport-https $STD apt-get install -y libpython3.11 msg_ok "Installed Dependencies" msg_info "Setting up Hyperion repository" setup_deb822_repo \ "hyperion" \ "https://releases.hyperion-project.org/hyperion.pub.key" \ "https://apt.releases.hyperion-project.org" \ "$(get_os_info codename)" msg_ok "Set up Hyperion repository" msg_info "Installing Hyperion" $STD apt install -y hyperion systemctl enable -q --now hyperion@root msg_ok "Installed Hyperion" motd_ssh customize cleanup_lxc ================================================ FILE: install/igotify-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: pfassina # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/androidseb25/iGotify-Notification-Assistent source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" setup_deb822_repo \ "microsoft" \ "https://packages.microsoft.com/keys/microsoft-2025.asc" \ "https://packages.microsoft.com/debian/13/prod/" \ "trixie" \ "main" $STD apt install -y aspnetcore-runtime-10.0 msg_ok "Installed Dependencies" fetch_and_deploy_gh_release "igotify" "androidseb25/iGotify-Notification-Assistent" "prebuild" "latest" "/opt/igotify" "iGotify-Notification-Service-amd64-v*.zip" msg_info "Creating Service" cat </opt/igotify/.env ASPNETCORE_URLS=http://0.0.0.0:80 ASPNETCORE_ENVIRONMENT=Production GOTIFY_DEFAULTUSER_PASS= GOTIFY_URLS= GOTIFY_CLIENT_TOKENS= SECNTFY_TOKENS= EOF cat </etc/systemd/system/igotify.service [Unit] Description=iGotify Notification Service After=network.target [Service] EnvironmentFile=/opt/igotify/.env WorkingDirectory=/opt/igotify ExecStart=/usr/bin/dotnet "/opt/igotify/iGotify Notification Assist.dll" Restart=always RestartSec=10 KillSignal=SIGINT TimeoutStopSec=10 [Install] WantedBy=multi-user.target EOF systemctl enable -q --now igotify msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/immich-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: vhsdream # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://immich.app source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os if lscpu | grep -q 'GenuineIntel'; then echo "" echo "" echo -e "🤖 ${BL}Immich Machine-Learning Options${CL}" echo "─────────────────────────────────────────" echo "Please choose your machine-learning type:" echo "" echo " 1) CPU only (default)" echo " 2) **NEW** Intel OpenVINO CPU or iGPU" echo "" read -r -p "${TAB3}Select machine-learning type [1]: " ML_TYPE ML_TYPE="${ML_TYPE:-1}" if [[ "$ML_TYPE" == "2" ]]; then touch ~/.openvino $STD apt install -y --no-install-recommends patchelf if [[ -d /dev/dri ]]; then msg_info "Installing Intel OpenVINO dependencies" tmp_dir=$(mktemp -d) $STD pushd "$tmp_dir" curl_with_retry "https://raw.githubusercontent.com/immich-app/immich/refs/heads/main/machine-learning/Dockerfile" "Dockerfile" readarray -t INTEL_URLS < <( sed -n "/intel-[igc|opencl]/p" ./Dockerfile | awk '{print $3}' sed -n "/libigdgmm12/p" ./Dockerfile | awk '{print $3}' ) for url in "${INTEL_URLS[@]}"; do curl_with_retry "$url" "$(basename "$url")" done $STD apt install -y ./libigdgmm12*.deb rm ./libigdgmm12*.deb $STD apt install -y ./*.deb $STD apt-mark hold libigdgmm12 $STD popd rm -rf "$tmp_dir" dpkg-query -W -f='${Version}\n' intel-opencl-icd >~/.intel_version msg_ok "Installed Intel OpenVINO dependencies" fi fi fi msg_info "Installing dependencies" $STD apt install --no-install-recommends -y \ git \ redis \ autoconf \ build-essential \ python3-dev \ automake \ cmake \ jq \ libtool \ libltdl-dev \ libgdk-pixbuf-2.0-dev \ libbrotli-dev \ libexif-dev \ libexpat1-dev \ libglib2.0-dev \ libgsf-1-dev \ libjpeg62-turbo-dev \ libspng-dev \ liblcms2-dev \ libopenexr-dev \ libgif-dev \ librsvg2-dev \ libexpat1 \ libgcc-s1 \ libgomp1 \ liblqr-1-0 \ libltdl7 \ libopenjp2-7 \ meson \ ninja-build \ pkg-config \ mesa-utils \ mesa-va-drivers \ mesa-vulkan-drivers \ ocl-icd-libopencl1 \ tini \ zlib1g \ libio-compress-brotli-perl \ libwebp7 \ libwebpdemux2 \ libwebpmux3 \ libhwy1t64 \ libdav1d-dev \ libhwy-dev \ libwebp-dev \ libaom-dev \ ccache setup_deb822_repo \ "jellyfin" \ "https://repo.jellyfin.org/jellyfin_team.gpg.key" \ "https://repo.jellyfin.org/debian" \ "$(get_os_info codename)" $STD apt install -y jellyfin-ffmpeg7 ln -sf /usr/lib/jellyfin-ffmpeg/ffmpeg /usr/bin/ffmpeg ln -sf /usr/lib/jellyfin-ffmpeg/ffprobe /usr/bin/ffprobe # Set permissions for /dev/dri (only in privileged containers and if /dev/dri exists) if [[ "$CTTYPE" == "0" && -d /dev/dri ]]; then chgrp video /dev/dri 2>/dev/null || true chmod 755 /dev/dri 2>/dev/null || true chmod 660 /dev/dri/* 2>/dev/null || true $STD adduser "$(id -u -n)" video 2>/dev/null || true $STD adduser "$(id -u -n)" render 2>/dev/null || true fi msg_ok "Dependencies Installed" msg_info "Installing Mise" curl -fSs https://mise.jdx.dev/gpg-key.pub | tee /etc/apt/keyrings/mise-archive-keyring.pub 1>/dev/null echo "deb [signed-by=/etc/apt/keyrings/mise-archive-keyring.pub arch=arm64] https://mise.jdx.dev/deb stable main" >/etc/apt/sources.list.d/mise.list $STD apt update $STD apt install -y mise msg_ok "Installed Mise" msg_info "Configuring Debian Testing Repo" sed -i 's/ trixie-updates/ trixie-updates testing/g' /etc/apt/sources.list.d/debian.sources cat </etc/apt/preferences.d/preferences Package: * Pin: release a=unstable Pin-Priority: 450 Package: * Pin:release a=testing Pin-Priority: 450 EOF $STD apt update msg_ok "Configured Debian Testing repo" msg_info "Installing packages from Debian Testing repo" $STD apt install -t testing --no-install-recommends -yqq libmimalloc3 libde265-dev msg_ok "Installed packages from Debian Testing repo" setup_uv PG_VERSION="16" PG_MODULES="pgvector" setup_postgresql VCHORD_RELEASE="0.5.3" fetch_and_deploy_gh_release "VectorChord" "tensorchord/VectorChord" "binary" "${VCHORD_RELEASE}" "/tmp" "postgresql-16-vchord_*_arm64.deb" sed -i "s/^#shared_preload.*/shared_preload_libraries = 'vchord.so'/" /etc/postgresql/16/main/postgresql.conf systemctl restart postgresql.service PG_DB_NAME="immich" PG_DB_USER="immich" PG_DB_GRANT_SUPERUSER="true" PG_DB_SKIP_ALTER_ROLE="true" setup_postgresql_db msg_info "Installing GCC-13 (available as fallback compiler)" $STD apt install -y gcc-13 g++-13 msg_ok "Installed GCC-13" msg_warn "Compiling Custom Photo-processing Libraries (can take anywhere from 15min to 2h)" LD_LIBRARY_PATH=/usr/local/lib export LD_RUN_PATH=/usr/local/lib STAGING_DIR=/opt/staging BASE_REPO="https://github.com/immich-app/base-images" BASE_DIR=${STAGING_DIR}/base-images SOURCE_DIR=${STAGING_DIR}/image-source $STD git clone -b main "$BASE_REPO" "$BASE_DIR" mkdir -p "$SOURCE_DIR" msg_info "(1/5) Compiling libjxl" cd "$STAGING_DIR" SOURCE=${SOURCE_DIR}/libjxl JPEGLI_LIBJPEG_LIBRARY_SOVERSION="62" JPEGLI_LIBJPEG_LIBRARY_VERSION="62.3.0" LIBJXL_REVISION="794a5dcf0d54f9f0b20d288a12e87afb91d20dfc" # : "${LIBJXL_REVISION:=$(jq -cr '.revision' $BASE_DIR/server/sources/libjxl.json)}" $STD git clone https://github.com/libjxl/libjxl.git "$SOURCE" cd "$SOURCE" $STD git reset --hard "$LIBJXL_REVISION" $STD git submodule update --init --recursive --depth 1 --recommend-shallow $STD git apply "$BASE_DIR"/server/sources/libjxl-patches/jpegli-empty-dht-marker.patch $STD git apply "$BASE_DIR"/server/sources/libjxl-patches/jpegli-icc-warning.patch mkdir build cd build $STD cmake \ -DCMAKE_BUILD_TYPE=Release \ -DBUILD_TESTING=OFF \ -DJPEGXL_ENABLE_DOXYGEN=OFF \ -DJPEGXL_ENABLE_MANPAGES=OFF \ -DJPEGXL_ENABLE_PLUGIN_GIMP210=OFF \ -DJPEGXL_ENABLE_BENCHMARK=OFF \ -DJPEGXL_ENABLE_EXAMPLES=OFF \ -DJPEGXL_FORCE_SYSTEM_BROTLI=ON \ -DJPEGXL_FORCE_SYSTEM_HWY=ON \ -DJPEGXL_ENABLE_JPEGLI=ON \ -DJPEGXL_ENABLE_JPEGLI_LIBJPEG=ON \ -DJPEGXL_INSTALL_JPEGLI_LIBJPEG=ON \ -DJPEGXL_ENABLE_PLUGINS=ON \ -DJPEGLI_LIBJPEG_LIBRARY_SOVERSION="$JPEGLI_LIBJPEG_LIBRARY_SOVERSION" \ -DJPEGLI_LIBJPEG_LIBRARY_VERSION="$JPEGLI_LIBJPEG_LIBRARY_VERSION" \ -DLIBJPEG_TURBO_VERSION_NUMBER=2001005 \ .. $STD cmake --build . -- -j"$(nproc)" $STD cmake --install . ldconfig /usr/local/lib $STD make clean cd "$STAGING_DIR" rm -rf "$SOURCE"/{build,third_party} msg_ok "(1/5) Compiled libjxl" msg_info "(2/5) Compiling libheif" SOURCE=${SOURCE_DIR}/libheif LIBHEIF_REVISION="35dad50a9145332a7bfdf1ff6aef6801fb613d68" # : "${LIBHEIF_REVISION:=$(jq -cr '.revision' $BASE_DIR/server/sources/libheif.json)}" $STD git clone https://github.com/strukturag/libheif.git "$SOURCE" cd "$SOURCE" $STD git reset --hard "$LIBHEIF_REVISION" mkdir build cd build $STD cmake --preset=release-noplugins \ -DWITH_DAV1D=ON \ -DENABLE_PARALLEL_TILE_DECODING=ON \ -DWITH_LIBSHARPYUV=ON \ -DWITH_LIBDE265=ON \ -DWITH_AOM_DECODER=OFF \ -DWITH_AOM_ENCODER=ON \ -DWITH_X265=OFF \ -DWITH_EXAMPLES=OFF \ .. $STD make install -j"$(nproc)" ldconfig /usr/local/lib $STD make clean cd "$STAGING_DIR" rm -rf "$SOURCE"/build msg_ok "(2/5) Compiled libheif" msg_info "(3/5) Compiling libraw" SOURCE=${SOURCE_DIR}/libraw LIBRAW_REVISION="0b56545a4f828743f28a4345cdfdd4c49f9f9a2a" # : "${LIBRAW_REVISION:=$(jq -cr '.revision' $BASE_DIR/server/sources/libraw.json)}" $STD git clone https://github.com/LibRaw/LibRaw.git "$SOURCE" cd "$SOURCE" $STD git reset --hard "$LIBRAW_REVISION" $STD autoreconf --install $STD ./configure --disable-examples $STD make -j"$(nproc)" $STD make install ldconfig /usr/local/lib $STD make clean cd "$STAGING_DIR" msg_ok "(3/5) Compiled libraw" msg_info "(4/5) Compiling imagemagick" SOURCE=$SOURCE_DIR/imagemagick : "${IMAGEMAGICK_REVISION:=$(jq -cr '.revision' $BASE_DIR/server/sources/imagemagick.json)}" $STD git clone https://github.com/ImageMagick/ImageMagick.git "$SOURCE" cd "$SOURCE" $STD git reset --hard "$IMAGEMAGICK_REVISION" $STD ./configure --with-modules CPPFLAGS="-DMAGICK_LIBRAW_VERSION_TAIL=202502" $STD make -j"$(nproc)" $STD make install ldconfig /usr/local/lib $STD make clean cd "$STAGING_DIR" msg_ok "(4/5) Compiled imagemagick" msg_info "(5/5) Compiling libvips" SOURCE=$SOURCE_DIR/libvips LIBVIPS_REVISION="0c9151a4f416d2f8ae20a755db218f6637050eec" $STD git clone https://github.com/libvips/libvips.git "$SOURCE" cd "$SOURCE" $STD git reset --hard "$LIBVIPS_REVISION" $STD meson setup build --buildtype=release --libdir=lib -Dintrospection=disabled -Dtiff=disabled cd build $STD ninja install ldconfig /usr/local/lib cd "$STAGING_DIR" rm -rf "$SOURCE"/build msg_ok "(5/5) Compiled libvips" { echo "imagemagick: $IMAGEMAGICK_REVISION" echo "libheif: $LIBHEIF_REVISION" echo "libjxl: $LIBJXL_REVISION" echo "libraw: $LIBRAW_REVISION" echo "libvips: $LIBVIPS_REVISION" } >~/.immich_library_revisions msg_ok "Custom Photo-processing Libraries Compiled Successfully" INSTALL_DIR="/opt/${APPLICATION}" UPLOAD_DIR="${INSTALL_DIR}/upload" SRC_DIR="${INSTALL_DIR}/source" APP_DIR="${INSTALL_DIR}/app" PLUGIN_DIR="${APP_DIR}/corePlugin" ML_DIR="${APP_DIR}/machine-learning" GEO_DIR="${INSTALL_DIR}/geodata" mkdir -p {"${APP_DIR}","${UPLOAD_DIR}","${GEO_DIR}","${INSTALL_DIR}"/cache} fetch_and_deploy_gh_release "Immich" "immich-app/immich" "tarball" "v2.7.5" "$SRC_DIR" PNPM_VERSION="$(jq -r '.packageManager | split("@")[1] | split("+")[0]' ${SRC_DIR}/package.json)" NODE_VERSION="24" NODE_MODULE="pnpm@${PNPM_VERSION}" setup_nodejs msg_info "Installing Immich (patience)" cd "$SRC_DIR"/server export COREPACK_ENABLE_DOWNLOAD_PROMPT=0 export CI=1 corepack enable # server build export SHARP_IGNORE_GLOBAL_LIBVIPS=true $STD pnpm --filter immich --frozen-lockfile build unset SHARP_IGNORE_GLOBAL_LIBVIPS export SHARP_FORCE_GLOBAL_LIBVIPS=true $STD pnpm --filter immich --frozen-lockfile --prod --no-optional deploy "$APP_DIR" # Patch helmet.json: disable upgrade-insecure-requests for HTTP access if [[ -f "$APP_DIR/helmet.json" ]]; then jq '.contentSecurityPolicy.directives["upgrade-insecure-requests"] = null' "$APP_DIR/helmet.json" >"$APP_DIR/helmet.json.tmp" && mv "$APP_DIR/helmet.json.tmp" "$APP_DIR/helmet.json" fi cp "$APP_DIR"/package.json "$APP_DIR"/bin sed -i "s|^start|${APP_DIR}/bin/start|" "$APP_DIR"/bin/immich-admin # openapi & web build cd "$SRC_DIR" echo "packageImportMethod: hardlink" >>./pnpm-workspace.yaml $STD pnpm --filter @immich/sdk --filter immich-web --frozen-lockfile --force install unset SHARP_FORCE_GLOBAL_LIBVIPS export SHARP_IGNORE_GLOBAL_LIBVIPS=true $STD pnpm --filter @immich/sdk --filter immich-web build cp -a web/build "$APP_DIR"/www cp LICENSE "$APP_DIR" # cli build $STD pnpm --filter @immich/sdk --filter @immich/cli --frozen-lockfile install $STD pnpm --filter @immich/sdk --filter @immich/cli build $STD pnpm --filter @immich/cli --prod --no-optional deploy "$APP_DIR"/cli # plugins cd "$SRC_DIR" $STD mise trust --ignore ./mise.toml $STD mise trust ./plugins/mise.toml cd plugins $STD mise install $STD mise run build mkdir -p "$PLUGIN_DIR" cp -r ./dist "$PLUGIN_DIR"/dist cp ./manifest.json "$PLUGIN_DIR" msg_ok "Installed Immich Server, Web and Plugin Components" cd "$SRC_DIR"/machine-learning $STD useradd -U -s /usr/sbin/nologin -r -M -d "$INSTALL_DIR" immich mkdir -p "$ML_DIR" # chown excluding upload dir contents (may be a mount with restricted permissions) chown immich:immich "$INSTALL_DIR" find "$INSTALL_DIR" -maxdepth 1 -mindepth 1 ! -name upload -exec chown -R immich:immich {} + chown immich:immich "$UPLOAD_DIR" 2>/dev/null || true export VIRTUAL_ENV="${ML_DIR}/ml-venv" export UV_HTTP_TIMEOUT=300 if [[ -f ~/.openvino ]]; then ML_PYTHON="python3.13" msg_info "Pre-installing Python ${ML_PYTHON} for machine-learning" for attempt in $(seq 1 3); do $STD sudo --preserve-env=VIRTUAL_ENV -nu immich uv python install "${ML_PYTHON}" && break [[ $attempt -lt 3 ]] && msg_warn "Python download attempt $attempt failed, retrying..." && sleep 5 done msg_ok "Pre-installed Python ${ML_PYTHON}" msg_info "Installing HW-accelerated machine-learning" for attempt in $(seq 1 3); do $STD sudo --preserve-env=VIRTUAL_ENV,UV_HTTP_TIMEOUT -nu immich uv sync --extra openvino --no-dev --active --link-mode copy -n -p "${ML_PYTHON}" --managed-python && break [[ $attempt -lt 3 ]] && msg_warn "uv sync attempt $attempt failed, retrying..." && sleep 10 done sofile="${VIRTUAL_ENV}/lib/python${ML_PYTHON#python}/site-packages/onnxruntime/capi/onnxruntime_pybind11_state.cpython-313-x86_64-linux-gnu.so" if [[ "$(uname -m)" == "aarch64" || "$(uname -m)" == "arm64" ]]; then sofile="${VIRTUAL_ENV}/lib/python${ML_PYTHON#python}/site-packages/onnxruntime/capi/onnxruntime_pybind11_state.cpython-313-aarch64-linux-gnu.so" fi patchelf --clear-execstack "$sofile" msg_ok "Installed HW-accelerated machine-learning" else ML_PYTHON="python3.11" msg_info "Pre-installing Python ${ML_PYTHON} for machine-learning" for attempt in $(seq 1 3); do $STD sudo --preserve-env=VIRTUAL_ENV -nu immich uv python install "${ML_PYTHON}" && break [[ $attempt -lt 3 ]] && msg_warn "Python download attempt $attempt failed, retrying..." && sleep 5 done msg_ok "Pre-installed Python ${ML_PYTHON}" msg_info "Installing machine-learning" for attempt in $(seq 1 3); do $STD sudo --preserve-env=VIRTUAL_ENV,UV_HTTP_TIMEOUT -nu immich uv sync --extra cpu --no-dev --active --link-mode copy -n -p "${ML_PYTHON}" --managed-python && break [[ $attempt -lt 3 ]] && msg_warn "uv sync attempt $attempt failed, retrying..." && sleep 10 done msg_ok "Installed machine-learning" fi cd "$SRC_DIR" cp -a machine-learning/{ann,immich_ml} "$ML_DIR" [[ -f ~/.openvino ]] && sed -i "/intra_op/s/int = 0/int = os.cpu_count() or 0/" "$ML_DIR"/immich_ml/config.py ln -sf "$APP_DIR"/resources "$INSTALL_DIR" cd "$APP_DIR" grep -rl /usr/src | xargs -n1 sed -i "s|\/usr/src|$INSTALL_DIR|g" grep -rlE "'/build'" | xargs -n1 sed -i "s|'/build'|'$APP_DIR'|g" sed -i "s@\"/cache\"@\"$INSTALL_DIR/cache\"@g" "$ML_DIR"/immich_ml/config.py ln -s "$UPLOAD_DIR" "$APP_DIR"/upload ln -s "$UPLOAD_DIR" "$ML_DIR"/upload msg_info "Installing GeoNames data" cd "$GEO_DIR" curl_with_retry "https://download.geonames.org/export/dump/admin1CodesASCII.txt" "admin1CodesASCII.txt" curl_with_retry "https://download.geonames.org/export/dump/admin2Codes.txt" "admin2Codes.txt" curl_with_retry "https://download.geonames.org/export/dump/cities500.zip" "cities500.zip" curl_with_retry "https://raw.githubusercontent.com/nvkelso/natural-earth-vector/v5.1.2/geojson/ne_10m_admin_0_countries.geojson" "ne_10m_admin_0_countries.geojson" unzip -q cities500.zip date --iso-8601=seconds | tr -d "\n" >geodata-date.txt rm cities500.zip cd "$INSTALL_DIR" ln -s "$GEO_DIR" "$APP_DIR" msg_ok "Installed GeoNames data" mkdir -p /var/log/immich touch /var/log/immich/{web.log,ml.log} msg_ok "Installed Immich" msg_info "Modifying user, creating env file, scripts & services" usermod -aG video,render immich cat <"${INSTALL_DIR}"/.env TZ=$(cat /etc/timezone) IMMICH_VERSION=release NODE_ENV=production IMMICH_ALLOW_SETUP=true ## Change to 'false' to disable CSP IMMICH_HELMET_FILE=true DB_HOSTNAME=127.0.0.1 DB_USERNAME=${PG_DB_USER} DB_PASSWORD=${PG_DB_PASS} DB_DATABASE_NAME=${PG_DB_NAME} DB_VECTOR_EXTENSION=vectorchord REDIS_HOSTNAME=127.0.0.1 IMMICH_MACHINE_LEARNING_URL=http://127.0.0.1:3003 MACHINE_LEARNING_CACHE_FOLDER=${INSTALL_DIR}/cache ## - For OpenVINO only - uncomment below to increase ## - inference speed while reducing accuracy ## - Default is FP32 # MACHINE_LEARNING_OPENVINO_PRECISION=FP16 IMMICH_MEDIA_LOCATION=${UPLOAD_DIR} EOF cat <"${ML_DIR}"/ml_start.sh #!/usr/bin/env bash cd ${ML_DIR} . ${VIRTUAL_ENV}/bin/activate set -a . ${INSTALL_DIR}/.env set +a python3 -m immich_ml EOF cat <"$APP_DIR"/bin/start.sh #!/usr/bin/env bash set -a . ${INSTALL_DIR}/.env set +a /usr/bin/node --no-warnings ${APP_DIR}/dist/main.js "\$@" EOF chmod +x "$ML_DIR"/ml_start.sh "$APP_DIR"/bin/start.sh ln -sf "$APP_DIR"/cli/bin/immich /usr/bin/immich ln -sf "$APP_DIR"/bin/immich-admin /usr/bin/immich-admin cat </etc/systemd/system/immich-web.service [Unit] Description=Immich Web Service After=network.target Requires=redis-server.service Requires=postgresql.service Requires=immich-ml.service [Service] Type=simple User=immich Group=immich UMask=0077 WorkingDirectory=${APP_DIR} ExecStart=${APP_DIR}/bin/start.sh Restart=on-failure SyslogIdentifier=immich-web StandardOutput=append:/var/log/immich/web.log StandardError=append:/var/log/immich/web.log [Install] WantedBy=multi-user.target EOF cat </etc/systemd/system/immich-ml.service [Unit] Description=Immich Machine-Learning After=network.target [Service] Type=simple UMask=0077 User=immich Group=immich WorkingDirectory=${APP_DIR} EnvironmentFile=${INSTALL_DIR}/.env ExecStart=${ML_DIR}/ml_start.sh Restart=on-failure SyslogIdentifier=immich-machine-learning StandardOutput=append:/var/log/immich/ml.log StandardError=append:/var/log/immich/ml.log [Install] WantedBy=multi-user.target EOF chown -R immich:immich /var/log/immich # chown excluding upload dir contents (may be a mount with restricted permissions) chown immich:immich "$INSTALL_DIR" find "$INSTALL_DIR" -maxdepth 1 -mindepth 1 ! -name upload -exec chown -R immich:immich {} + chown immich:immich "$UPLOAD_DIR" 2>/dev/null || true systemctl enable -q --now immich-ml.service immich-web.service msg_ok "Modified user, created env file, scripts and services" motd_ssh customize cleanup_lxc ================================================ FILE: install/immichframe-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: Thiago Canozzo Lahr (tclahr) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/immichFrame/ImmichFrame source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" setup_deb822_repo \ "microsoft" \ "https://packages.microsoft.com/keys/microsoft-2025.asc" \ "https://packages.microsoft.com/debian/13/prod/" \ "trixie" \ "main" $STD apt install -y \ libicu-dev \ libssl-dev \ gettext-base \ dotnet-sdk-8.0 \ aspnetcore-runtime-8.0 msg_ok "Installed Dependencies" NODE_VERSION="22" setup_nodejs fetch_and_deploy_gh_release "immichframe" "immichFrame/ImmichFrame" "tarball" "latest" "/tmp/immichframe" msg_info "Setting up ImmichFrame" mkdir -p /opt/immichframe cd /tmp/immichframe $STD dotnet publish ImmichFrame.WebApi/ImmichFrame.WebApi.csproj \ --configuration Release \ --runtime linux-x64 \ --self-contained false \ --output /opt/immichframe cd /tmp/immichframe/immichFrame.Web $STD npm ci $STD npm run build cp -r build/* /opt/immichframe/wwwroot rm -rf /tmp/immichframe mkdir -p /opt/immichframe/Config curl -fsSL "https://raw.githubusercontent.com/immichFrame/ImmichFrame/main/docker/Settings.example.yml" -o /opt/immichframe/Config/Settings.yml useradd -r -s /sbin/nologin -d /opt/immichframe -M immichframe chown -R immichframe:immichframe /opt/immichframe msg_ok "Setup ImmichFrame" msg_info "Creating Service" cat </etc/systemd/system/immichframe.service [Unit] Description=ImmichFrame Digital Photo Frame After=network.target [Service] Type=simple User=immichframe Group=immichframe WorkingDirectory=/opt/immichframe ExecStart=/usr/bin/dotnet /opt/immichframe/ImmichFrame.WebApi.dll Environment=ASPNETCORE_URLS=http://0.0.0.0:8080 Environment=ASPNETCORE_ENVIRONMENT=Production Environment=DOTNET_CONTENTROOT=/opt/immichframe Restart=always RestartSec=5 StandardOutput=journal StandardError=journal SyslogIdentifier=immichframe [Install] WantedBy=multi-user.target EOF systemctl enable -q --now immichframe msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/infisical-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: Slaviša Arežina (tremor021) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://infisical.com/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ apt-transport-https \ redis msg_ok "Installed Dependencies" PG_VERSION="17" setup_postgresql PG_DB_NAME="infisical_db" PG_DB_USER="infisical" setup_postgresql_db msg_info "Setting up Infisical Repository" setup_deb822_repo \ "infisical" \ "https://artifacts-infisical-core.infisical.com/infisical.gpg" \ "https://artifacts-infisical-core.infisical.com/deb" \ "stable" msg_ok "Setup Infisical repository" msg_info "Setting up Infisical" AUTH_SECRET="$(openssl rand -base64 32 | tr -d '\n')" ENC_KEY="$(openssl rand -hex 16 | tr -d '\n')" $STD apt install -y infisical-core mkdir -p /etc/infisical cat </etc/infisical/infisical.rb infisical_core['ENCRYPTION_KEY'] = '$ENC_KEY' infisical_core['AUTH_SECRET'] = '$AUTH_SECRET' infisical_core['HOST'] = '$LOCAL_IP' infisical_core['DB_CONNECTION_URI'] = 'postgres://${PG_DB_USER}:${PG_DB_PASS}@localhost:5432/${PG_DB_NAME}' infisical_core['REDIS_URL'] = 'redis://localhost:6379' EOF $STD infisical-ctl reconfigure msg_ok "Setup Infisical" motd_ssh customize cleanup_lxc ================================================ FILE: install/influxdb-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.influxdata.com/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Setting up InfluxDB Repository" setup_deb822_repo \ "influxdata" \ "https://repos.influxdata.com/influxdata-archive.key" \ "https://repos.influxdata.com/debian" \ "stable" msg_ok "Set up InfluxDB Repository" read -r -p "${TAB3}Which version of InfluxDB to install? (1, 2 or 3) " prompt if [[ $prompt == "3" ]]; then INFLUX="3" elif [[ $prompt == "2" ]]; then INFLUX="2" else INFLUX="1" fi msg_info "Installing InfluxDB v${INFLUX}" if [[ $INFLUX == "3" ]]; then $STD apt install -y influxdb3-core systemctl enable -q --now influxdb3-core elif [[ $INFLUX == "2" ]]; then $STD apt install -y influxdb2 systemctl enable -q --now influxdb else $STD apt install -y influxdb download_file "https://dl.influxdata.com/chronograf/releases/chronograf_1.10.8_arm64.deb" "${HOME}/chronograf_1.10.8_arm64.deb" $STD dpkg -i "${HOME}/chronograf_1.10.8_arm64.deb" rm -rf "${HOME}/chronograf_1.10.8_arm64.deb" systemctl enable -q --now influxdb fi msg_ok "Installed InfluxDB" read -r -p "${TAB3}Would you like to add Telegraf? " prompt if [[ "${prompt,,}" =~ ^(y|yes)$ ]]; then msg_info "Installing Telegraf" $STD apt install -y telegraf msg_ok "Installed Telegraf" fi motd_ssh customize cleanup_lxc ================================================ FILE: install/inspircd-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: kristocopani # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.inspircd.org/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os fetch_and_deploy_gh_release "inspircd" "inspircd/inspircd" "binary" "latest" "/opt/inspircd" "inspircd_*.deb13u1_amd64.deb" msg_info "Configuring InspIRCd" cat </etc/inspircd/inspircd.conf EOF systemctl enable -q --now inspircd msg_ok "Installed InspIRCd" motd_ssh customize cleanup_lxc ================================================ FILE: install/inventree-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: Slaviša Arežina (tremor021) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/inventree/InvenTree source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" temp_file=$(mktemp) curl -fsSL "http://ports.ubuntu.com/pool/main/o/openssl/libssl1.1_1.1.1f-1ubuntu2.24_arm64.deb" -o "$temp_file" $STD dpkg -i $temp_file rm -f $temp_file msg_ok "Installed Dependencies" msg_info "Setting up InvenTree Repository" setup_deb822_repo \ "inventree" \ "https://dl.packager.io/srv/inventree/InvenTree/key" \ "https://dl.packager.io/srv/deb/inventree/InvenTree/stable/$(get_os_info id)" \ "$(get_os_info version)" \ "main" msg_ok "Set up InvenTree Repository" msg_info "Installing InvenTree (Patience)" export SETUP_NO_CALLS=true $STD apt install -y inventree msg_ok "Installed InvenTree" msg_info "Configuring InvenTree" if [[ -f /etc/inventree/config.yaml ]]; then sed -i "s|site_url:.*|site_url: http://${LOCAL_IP}|" /etc/inventree/config.yaml fi $STD inventree run invoke update msg_ok "Configured InvenTree" motd_ssh customize cleanup_lxc ================================================ FILE: install/investbrain-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: Benito Rodríguez (b3ni) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/investbrainapp/investbrain source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ nginx \ supervisor \ redis-server \ libfreetype-dev \ libjpeg62-turbo-dev \ libpng-dev \ zlib1g-dev \ libzip-dev \ libicu-dev \ libpq-dev msg_ok "Installed Dependencies" export PHP_VERSION="8.4" PHP_FPM="YES" PHP_MODULE="pdo-pgsql" setup_php setup_composer NODE_VERSION="22" setup_nodejs PG_VERSION="17" setup_postgresql PG_DB_NAME="investbrain" PG_DB_USER="investbrain" setup_postgresql_db fetch_and_deploy_gh_release "Investbrain" "investbrainapp/investbrain" "tarball" "latest" "/opt/investbrain" msg_info "Installing Investbrain" APP_KEY=$(openssl rand -base64 32) cd /opt/investbrain cat </opt/investbrain/.env APP_KEY=base64:${APP_KEY} APP_PORT=8000 APP_URL=http://${LOCAL_IP}:8000 ASSET_URL=http://${LOCAL_IP}:8000 LOG_CHANNEL=daily LOG_LEVEL=warning REGISTRATION_ENABLED=true AI_CHAT_ENABLED=false OPENAI_API_KEY= OPENAI_ORGANIZATION= MARKET_DATA_PROVIDER=yahoo ALPHAVANTAGE_API_KEY= FINNHUB_API_KEY= ALPACA_API_KEY= ALPACA_API_SECRET= TWELVEDATA_API_SECRET= MARKET_DATA_REFRESH=30 DAILY_CHANGE_TIME= DB_CONNECTION=pgsql DB_HOST=127.0.0.1 DB_PORT=5432 DB_DATABASE=${PG_DB_NAME} DB_USERNAME=${PG_DB_USER} DB_PASSWORD=${PG_DB_PASS} REDIS_CLIENT=phpredis REDIS_HOST=127.0.0.1 REDIS_PASSWORD=null REDIS_PORT=6379 CACHE_STORE=redis CACHE_PREFIX= SESSION_DRIVER=redis SESSION_LIFETIME=120 QUEUE_CONNECTION=redis MAIL_MAILER=log MAIL_HOST=127.0.0.1 MAIL_PORT=2525 MAIL_FROM_ADDRESS="investbrain@${LOCAL_IP}" VITE_APP_NAME=Investbrain # Reverse Proxy Support (uncomment and set APP_URL/ASSET_URL to your domain when using a reverse proxy) # APP_URL=https://your-domain.com # ASSET_URL=https://your-domain.com # TRUSTED_PROXIES=* EOF export COMPOSER_ALLOW_SUPERUSER=1 $STD /usr/local/bin/composer install --no-interaction --no-dev --optimize-autoloader $STD npm install $STD npm run build mkdir -p /opt/investbrain/storage/{framework/cache,framework/sessions,framework/views,app,logs} $STD php artisan migrate --force $STD php artisan storage:link $STD php artisan cache:clear $STD php artisan view:clear $STD php artisan route:clear $STD php artisan event:clear $STD php artisan route:cache $STD php artisan event:cache chown -R www-data:www-data /opt/investbrain chmod -R 775 /opt/investbrain/bootstrap/cache msg_ok "Installed Investbrain" msg_info "Configuring Nginx" cat </etc/nginx/sites-available/investbrain.conf server { listen 8000 default_server; listen [::]:8000 default_server; server_name _; root /opt/investbrain/public; index index.php; client_max_body_size 50M; charset utf-8; location = /favicon.ico { access_log off; log_not_found off; } location = /robots.txt { access_log off; log_not_found off; } location / { try_files \$uri \$uri/ /index.php?\$query_string; } location ~ \.php\$ { fastcgi_pass unix:/var/run/php/php${PHP_VERSION}-fpm.sock; fastcgi_param SCRIPT_FILENAME \$realpath_root\$fastcgi_script_name; include fastcgi_params; fastcgi_hide_header X-Powered-By; fastcgi_read_timeout 300; } location ~ /\.(?!well-known).* { deny all; } error_log /var/log/nginx/investbrain_error.log; access_log /var/log/nginx/investbrain_access.log; } EOF ln -sf /etc/nginx/sites-available/investbrain.conf /etc/nginx/sites-enabled/ rm -f /etc/nginx/sites-enabled/default $STD systemctl reload nginx msg_ok "Configured Nginx" msg_info "Setting up Supervisor" cat </etc/supervisor/conf.d/investbrain.conf [program:investbrain-queue] process_name=%%(program_name)s_%%(process_num)02d command=php /opt/investbrain/artisan queue:work --sleep=3 --tries=1 --memory=256 --timeout=3600 user=www-data autostart=true autorestart=true redirect_stderr=true stdout_logfile=/opt/investbrain/storage/logs/queue.log stdout_logfile_maxbytes=50MB stdout_logfile_backups=10 numprocs=1 EOF $STD supervisorctl reread $STD supervisorctl update $STD supervisorctl start all msg_ok "Setup Supervisor" msg_info "Setting up Cron for Scheduler" cat </etc/cron.d/investbrain-scheduler * * * * * www-data php /opt/investbrain/artisan schedule:run >> /dev/null 2>&1 EOF chmod 644 /etc/cron.d/investbrain-scheduler $STD systemctl restart cron msg_ok "Setup Cron for Scheduler" motd_ssh customize cleanup_lxc ================================================ FILE: install/invoiceninja-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://invoiceninja.com/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ nginx \ supervisor \ libnss3 \ libatk1.0-0 \ libatk-bridge2.0-0 \ libcups2 \ libdrm2 \ libxkbcommon0 \ libxcomposite1 \ libxdamage1 \ libxfixes3 \ libxrandr2 \ libgbm1 \ libasound2 \ libpango-1.0-0 \ libcairo2 msg_ok "Installed Dependencies" setup_mariadb MARIADB_DB_NAME="invoiceninja" MARIADB_DB_USER="invoiceninja" setup_mariadb_db PHP_VERSION="8.4" PHP_FPM="YES" PHP_MODULE="soap" setup_php fetch_and_deploy_gh_release "invoiceninja" "invoiceninja/invoiceninja" "prebuild" "latest" "/opt/invoiceninja" "invoiceninja.tar.gz" msg_info "Configuring InvoiceNinja" cd /opt/invoiceninja APP_KEY=$(php artisan key:generate --show) cat </opt/invoiceninja/.env APP_NAME="Invoice Ninja" APP_ENV=production APP_KEY=${APP_KEY} APP_DEBUG=false APP_URL=http://${LOCAL_IP}:8080 DB_CONNECTION=mysql DB_HOST=127.0.0.1 DB_PORT=3306 DB_DATABASE=${MARIADB_DB_NAME} DB_USERNAME=${MARIADB_DB_USER} DB_PASSWORD=${MARIADB_DB_PASS} MULTI_DB_ENABLED=false DEMO_MODE=false BROADCAST_DRIVER=log LOG_CHANNEL=stack CACHE_DRIVER=file QUEUE_CONNECTION=database SESSION_DRIVER=file SESSION_LIFETIME=120 MAIL_MAILER=log MAIL_HOST=null MAIL_PORT=null MAIL_USERNAME=null MAIL_PASSWORD=null MAIL_ENCRYPTION=null MAIL_FROM_ADDRESS="noreply@localhost" MAIL_FROM_NAME="Invoice Ninja" REQUIRE_HTTPS=false NINJA_ENVIRONMENT=selfhost PDF_GENERATOR=snappdf TRUSTED_PROXIES=* INTERNAL_QUEUE_ENABLED=false EOF mkdir -p /opt/invoiceninja/bootstrap/cache mkdir -p /opt/invoiceninja/storage/{app/public,framework/{cache/data,sessions,views},logs} chown -R www-data:www-data /opt/invoiceninja chown -R www-data:www-data /opt/invoiceninja/storage chown -R www-data:www-data /opt/invoiceninja/bootstrap/cache msg_ok "Configured InvoiceNinja" msg_info "Downloading Chromium for PDF Generation" cd /opt/invoiceninja $STD ./vendor/bin/snappdf download chown -R www-data:www-data /opt/invoiceninja/vendor/beganovich/snappdf/versions msg_ok "Downloaded Chromium for PDF Generation" msg_info "Setting up Database" cd /opt/invoiceninja $STD php artisan config:clear $STD php artisan cache:clear $STD php artisan route:clear $STD php artisan view:clear $STD php artisan migrate --force $STD php artisan db:seed --force $STD php artisan ninja:post-update $STD php artisan optimize chown -R www-data:www-data /opt/invoiceninja msg_ok "Set up Database" msg_info "Configuring Nginx" cat <<'EOF' >/etc/nginx/sites-available/invoiceninja server { listen 8080; server_name _; root /opt/invoiceninja/public; index index.php; client_max_body_size 50M; charset utf-8; gzip on; gzip_types application/javascript application/x-javascript text/javascript text/plain application/xml application/json; gzip_proxied no-cache no-store private expired auth; gzip_min_length 1000; location / { try_files $uri $uri/ /index.php?$query_string; } location = /index.php { fastcgi_pass unix:/run/php/php8.4-fpm.sock; fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name; include fastcgi_params; fastcgi_read_timeout 300; } location ~ \.php$ { return 403; } location ~ /\.ht { deny all; } error_log /var/log/nginx/invoiceninja_error.log; access_log /var/log/nginx/invoiceninja_access.log; } EOF ln -sf /etc/nginx/sites-available/invoiceninja /etc/nginx/sites-enabled/ rm -f /etc/nginx/sites-enabled/default $STD systemctl reload nginx msg_ok "Configured Nginx" msg_info "Setting up Queue Worker" cat <<'EOF' >/etc/supervisor/conf.d/invoiceninja-worker.conf [program:invoiceninja-worker] process_name=%(program_name)s_%(process_num)02d command=php /opt/invoiceninja/artisan queue:work --sleep=3 --tries=3 --max-time=3600 autostart=true autorestart=true stopasgroup=true killasgroup=true user=www-data numprocs=2 redirect_stderr=true stdout_logfile=/var/log/invoiceninja-worker.log stopwaitsecs=3600 EOF touch /var/log/invoiceninja-worker.log chown www-data:www-data /var/log/invoiceninja-worker.log $STD supervisorctl reread $STD supervisorctl update msg_ok "Set up Queue Worker" msg_info "Setting up Cron" cat <<'EOF' >/etc/cron.d/invoiceninja * * * * * www-data cd /opt/invoiceninja && php artisan schedule:run >> /dev/null 2>&1 EOF msg_ok "Set up Cron" msg_info "Enabling Services" systemctl enable -q --now php8.4-fpm nginx supervisor msg_ok "Enabled Services" motd_ssh customize cleanup_lxc ================================================ FILE: install/iobroker-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.iobroker.net/#en/intro source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y ca-certificates msg_ok "Installed Dependencies" msg_warn "WARNING: This script will run an external installer from a third-party source (https://iobroker.net/)." msg_warn "The following code is NOT maintained or audited by our repository." msg_warn "If you have any doubts or concerns, please review the installer code before proceeding:" msg_custom "${TAB3}${GATEWAY}${BGN}${CL}" "\e[1;34m" "→ https://iobroker.net/install.sh" echo read -r -p "${TAB3}Do you want to continue? [y/N]: " CONFIRM if [[ ! "$CONFIRM" =~ ^([yY][eE][sS]|[yY])$ ]]; then msg_error "Aborted by user. No changes have been made." exit 10 fi NODE_VERSION="24" setup_nodejs msg_info "Installing ioBroker (Patience)" $STD bash <(curl -fsSL https://iobroker.net/install.sh) msg_ok "Installed ioBroker" motd_ssh customize cleanup_lxc ================================================ FILE: install/ironclaw-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/nearai/ironclaw source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ dbus-user-session \ gnome-keyring \ libsecret-tools msg_ok "Installed Dependencies" PG_VERSION="17" PG_MODULES="pgvector" setup_postgresql PG_DB_NAME="ironclaw" PG_DB_USER="ironclaw" PG_DB_EXTENSIONS="vector" setup_postgresql_db fetch_and_deploy_gh_release "ironclaw-bin" "nearai/ironclaw" "prebuild" "latest" "/usr/local/bin" \ "ironclaw-$(uname -m)-unknown-linux-$([[ -f /etc/alpine-release ]] && echo "musl" || echo "gnu").tar.gz" chmod +x /usr/local/bin/ironclaw msg_info "Configuring IronClaw" mkdir -p /root/.ironclaw GATEWAY_TOKEN=$(openssl rand -hex 32) cat </root/.ironclaw/.env DATABASE_URL=postgresql://${PG_DB_USER}:${PG_DB_PASS}@localhost:5432/${PG_DB_NAME}?sslmode=disable GATEWAY_ENABLED=true GATEWAY_HOST=0.0.0.0 GATEWAY_PORT=3000 GATEWAY_AUTH_TOKEN=${GATEWAY_TOKEN} CLI_ENABLED=false AGENT_NAME=ironclaw RUST_LOG=ironclaw=info,tower_http=info EOF chmod 600 /root/.ironclaw/.env msg_ok "Configured IronClaw" msg_info "Creating Service" cat </etc/systemd/system/ironclaw.service [Unit] Description=IronClaw AI Agent After=network.target postgresql.service [Service] Type=simple User=root WorkingDirectory=/root EnvironmentFile=/root/.ironclaw/.env ExecStart=/usr/bin/dbus-run-session /usr/local/bin/ironclaw Restart=on-failure RestartSec=5 [Install] WantedBy=multi-user.target EOF systemctl enable -q ironclaw msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/isponsorblocktv-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: Matthew Stern (sternma) | MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/dmunozv04/iSponsorBlockTV source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os if ! grep -q ' avx ' /proc/cpuinfo 2>/dev/null; then msg_error "CPU does not support AVX instructions (required by iSponsorBlockTV/PyApp)" exit 106 fi fetch_and_deploy_gh_release "isponsorblocktv" "dmunozv04/iSponsorBlockTV" "singlefile" "latest" "/opt/isponsorblocktv" "iSponsorBlockTV-x86_64-linux" msg_info "Setting up iSponsorBlockTV" install -d /var/lib/isponsorblocktv msg_ok "Set up iSponsorBlockTV" msg_info "Creating Service" cat </etc/systemd/system/isponsorblocktv.service [Unit] Description=iSponsorBlockTV After=network-online.target Wants=network-online.target [Service] Type=simple User=root Group=root Environment=iSPBTV_data_dir=/var/lib/isponsorblocktv ExecStart=/opt/isponsorblocktv/isponsorblocktv Restart=on-failure RestartSec=5 [Install] WantedBy=multi-user.target EOF systemctl enable -q isponsorblocktv msg_ok "Created Service" msg_info "Creating CLI wrapper" cat <<'EOF' >/usr/local/bin/iSponsorBlockTV #!/usr/bin/env bash export iSPBTV_data_dir="/var/lib/isponsorblocktv" set +e /opt/isponsorblocktv/isponsorblocktv "$@" status=$? set -e case "${1:-}" in setup|setup-cli) systemctl restart isponsorblocktv >/dev/null 2>&1 || true ;; esac exit $status EOF chmod +x /usr/local/bin/iSponsorBlockTV ln -sf /usr/local/bin/iSponsorBlockTV /usr/bin/iSponsorBlockTV msg_ok "Created CLI wrapper" motd_ssh customize cleanup_lxc ================================================ FILE: install/itsm-ng-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: Florianb63 # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://itsm-ng.com/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os setup_mariadb msg_info "Loading timezone data" mariadb-tzinfo-to-sql /usr/share/zoneinfo | mariadb mysql msg_ok "Loaded timezone data" MARIADB_DB_NAME="itsmng_db" MARIADB_DB_USER="itsmng" MARIADB_DB_EXTRA_GRANTS="GRANT SELECT ON \`mysql\`.\`time_zone_name\`" setup_mariadb_db msg_info "Installing ITSM-NG" setup_deb822_repo \ "itsm-ng" \ "http://deb.itsm-ng.org/pubkey.gpg" \ "http://deb.itsm-ng.org/$(get_os_info id)/" \ "$(get_os_info codename)" $STD apt install -y itsm-ng cd /usr/share/itsm-ng $STD php bin/console db:install --db-name="$MARIADB_DB_NAME" --db-user="$MARIADB_DB_USER" --db-password="$MARIADB_DB_PASS" --no-interaction $STD a2dissite 000-default.conf echo "* * * * * www-data php /usr/share/itsm-ng/front/cron.php" | crontab - msg_ok "Installed ITSM-NG" msg_info "Setting permissions" chown -R www-data:www-data /var/lib/itsm-ng mkdir -p /usr/share/itsm-ng/css/palettes chown -R www-data:www-data /usr/share/itsm-ng/css chown -R www-data:www-data /usr/share/itsm-ng/css_compiled chown www-data:www-data /etc/itsm-ng/config_db.php msg_ok "Set permissions" msg_info "Configuring PHP" PHP_VERSION=$(ls /etc/php/ | grep -E '^[0-9]+\.[0-9]+$' | head -n 1) PHP_INI="/etc/php/$PHP_VERSION/apache2/php.ini" sed -i 's/^upload_max_filesize = .*/upload_max_filesize = 20M/' $PHP_INI sed -i 's/^post_max_size = .*/post_max_size = 20M/' $PHP_INI sed -i 's/^max_execution_time = .*/max_execution_time = 60/' $PHP_INI sed -i 's/^[;]*max_input_vars *=.*/max_input_vars = 5000/' "$PHP_INI" sed -i 's/^memory_limit = .*/memory_limit = 256M/' $PHP_INI sed -i 's/^;\?\s*session.cookie_httponly\s*=.*/session.cookie_httponly = On/' $PHP_INI systemctl restart apache2 rm -rf /usr/share/itsm-ng/install msg_ok "Configured PHP" motd_ssh customize cleanup_lxc ================================================ FILE: install/jackett-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/Jackett/Jackett source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os fetch_and_deploy_gh_release "jackett" "Jackett/Jackett" "prebuild" "latest" "/opt/Jackett" "Jackett.Binaries.LinuxARM64.tar.gz" msg_info "Creating Service" cat </etc/systemd/system/jackett.service [Unit] Description=Jackett Daemon After=network.target [Service] SyslogIdentifier=jackett Restart=always RestartSec=5 Type=simple WorkingDirectory=/opt/Jackett ExecStart=/bin/sh /opt/Jackett/jackett_launcher.sh TimeoutStopSec=30 EnvironmentFile="/opt/.env" [Install] WantedBy=multi-user.target EOF systemctl enable -q --now jackett msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/jeedom-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: Mips2648 # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://jeedom.com/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing dependencies" $STD apt install -y \ lsb-release \ git msg_ok "Dependencies installed" msg_warn "WARNING: This script will run an external installer from a third-party source (https://github.com/jeedom/)." msg_warn "The following code is NOT maintained or audited by our repository." msg_warn "If you have any doubts or concerns, please review the installer code before proceeding:" msg_custom "${TAB3}${GATEWAY}${BGN}${CL}" "\e[1;34m" "→ https://raw.githubusercontent.com/jeedom/core/master/install/install.sh" echo read -r -p "${TAB3}Do you want to continue? [y/N]: " CONFIRM if [[ ! "$CONFIRM" =~ ^([yY][eE][sS]|[yY])$ ]]; then msg_error "Aborted by user. No changes have been made." exit 10 fi DEFAULT_BRANCH="master" REPO_URL="https://github.com/jeedom/core.git" echo while true; do read -rp "${TAB3}Enter branch to use (master, beta, alpha...) (Default: ${DEFAULT_BRANCH}): " BRANCH BRANCH="${BRANCH:-$DEFAULT_BRANCH}" if git ls-remote --heads "$REPO_URL" "refs/heads/$BRANCH" | grep -q .; then break else msg_error "Branch '$BRANCH' does not exist on remote. Please try again." fi done msg_info "Downloading Jeedom installation script" cd /tmp wget -q https://raw.githubusercontent.com/jeedom/core/"${BRANCH}"/install/install.sh chmod +x install.sh msg_ok "Installation script downloaded" msg_info "Install Jeedom main dependencies, please wait" $STD ./install.sh -v "$BRANCH" -s 2 msg_ok "Installed Jeedom main dependencies" msg_info "Install Database" $STD ./install.sh -v "$BRANCH" -s 3 msg_ok "Database installed" msg_info "Install Apache" $STD ./install.sh -v "$BRANCH" -s 4 msg_ok "Apache installed" msg_info "Install PHP and dependencies" $STD ./install.sh -v "$BRANCH" -s 5 msg_ok "PHP installed" msg_info "Download Jeedom core" $STD ./install.sh -v "$BRANCH" -s 6 msg_ok "Download done" msg_info "Database customisation" $STD ./install.sh -v "$BRANCH" -s 7 msg_ok "Database customisation done" msg_info "Jeedom customisation" $STD ./install.sh -v "$BRANCH" -s 8 msg_ok "Jeedom customisation done" msg_info "Configuring Jeedom" $STD ./install.sh -v "$BRANCH" -s 9 msg_ok "Jeedom configured" msg_info "Installing Jeedom" $STD ./install.sh -v "$BRANCH" -s 10 msg_ok "Jeedom installed" msg_info "Post installation" $STD ./install.sh -v "$BRANCH" -s 11 msg_ok "Post installation done" msg_info "Check installation" $STD ./install.sh -v "$BRANCH" -s 12 msg_ok "Installation checked, everything is successfuly installed. A reboot is recommended." motd_ssh customize rm -rf /tmp/install.sh cleanup_lxc ================================================ FILE: install/jellyfin-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://jellyfin.org/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_custom "ℹ️" "${GN}" "If NVIDIA GPU passthrough is detected, you'll be asked whether to install drivers in the container" msg_info "Installing Dependencies" ensure_dependencies libjemalloc2 if [[ ! -f /usr/lib/libjemalloc.so ]]; then ln -sf /usr/lib/aarch64-linux-gnu/libjemalloc.so.2 /usr/lib/libjemalloc.so fi if [[ ! -d /etc/apt/keyrings ]]; then mkdir -p /etc/apt/keyrings fi curl -fsSL https://repo.jellyfin.org/jellyfin_team.gpg.key | gpg --dearmor --yes --output /etc/apt/keyrings/jellyfin.gpg cat </etc/apt/sources.list.d/jellyfin.sources Types: deb URIs: https://repo.jellyfin.org/${PCT_OSTYPE} Suites: ${VERSION} Components: main Architectures: arm64 Signed-By: /etc/apt/keyrings/jellyfin.gpg EOF msg_info "Setting up Jellyfin Repository" setup_deb822_repo \ "jellyfin" \ "https://repo.jellyfin.org/jellyfin_team.gpg.key" \ "https://repo.jellyfin.org/$(get_os_info id)" \ "$(get_os_info codename)" msg_ok "Set up Jellyfin Repository" msg_info "Installing Jellyfin" $STD apt install -y jellyfin jellyfin-ffmpeg7 ln -sf /usr/lib/jellyfin-ffmpeg/ffmpeg /usr/bin/ffmpeg ln -sf /usr/lib/jellyfin-ffmpeg/ffprobe /usr/bin/ffprobe msg_ok "Installed Jellyfin" setup_hwaccel "jellyfin" msg_info "Configuring Jellyfin" # Configure log rotation to prevent disk fill (keeps fail2ban compatibility) (PR: #1690 / Issue: #11224) cat </etc/logrotate.d/jellyfin /var/log/jellyfin/*.log { daily rotate 3 maxsize 100M missingok notifempty compress delaycompress copytruncate } EOF chown -R jellyfin:adm /etc/jellyfin sleep 10 systemctl restart jellyfin msg_ok "Configured Jellyfin" motd_ssh customize cleanup_lxc ================================================ FILE: install/jenkins-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: kristocopani # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.jenkins.io/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os JAVA_VERSION="21" setup_java setup_deb822_repo \ "jenkins" \ "https://pkg.jenkins.io/debian/jenkins.io-2026.key" \ "https://pkg.jenkins.io/debian" \ "binary/" \ " " msg_info "Setup Jenkins" $STD apt install -y jenkins msg_ok "Setup Jenkins" motd_ssh customize cleanup_lxc ================================================ FILE: install/jitsi-meet-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://jitsi.org/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y nginx msg_ok "Installed Dependencies" source /etc/os-release setup_deb822_repo "jitsi" \ "https://download.jitsi.org/jitsi-key.gpg.key" \ "https://download.jitsi.org" \ "stable/" \ "" msg_info "Installing Jitsi Meet" echo "jitsi-videobridge2 jitsi-videobridge/jvb-hostname string ${LOCAL_IP}" | debconf-set-selections echo "jitsi-meet-web-config jitsi-meet/cert-choice select Generate a new self-signed certificate" | debconf-set-selections DEBIAN_FRONTEND=noninteractive $STD apt install -y jitsi-meet msg_ok "Installed Jitsi Meet" motd_ssh customize cleanup_lxc ================================================ FILE: install/joplin-server-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: Slaviša Arežina (tremor021) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://joplinapp.org/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ git \ rsync msg_ok "Installed Dependencies" PG_VERSION="17" setup_postgresql PG_DB_NAME="joplin" PG_DB_USER="joplin" setup_postgresql_db NODE_VERSION="24" NODE_MODULE="yarn,npm,pm2" setup_nodejs mkdir -p /opt/pm2 export PM2_HOME=/opt/pm2 $STD pm2 install pm2-logrotate $STD pm2 set pm2-logrotate:max_size 100MB $STD pm2 set pm2-logrotate:retain 5 $STD pm2 set pm2-logrotate:compress tr fetch_and_deploy_gh_release "joplin-server" "laurent22/joplin" "tarball" msg_info "Setting up Joplin Server (Patience)" cd /opt/joplin-server sed -i "/onenote-converter/d" packages/lib/package.json $STD yarn config set --home enableTelemetry 0 export BUILD_SEQUENCIAL=1 $STD yarn workspaces focus @joplin/server $STD yarn workspaces foreach -R --topological-dev --from @joplin/server run build $STD yarn workspaces foreach -R --topological-dev --from @joplin/server run tsc cat </opt/joplin-server/.env PM2_HOME=/opt/pm2 NODE_ENV=production APP_BASE_URL=http://$LOCAL_IP:22300 APP_PORT=22300 DB_CLIENT=pg POSTGRES_PASSWORD=$PG_DB_PASS POSTGRES_DATABASE=$PG_DB_NAME POSTGRES_USER=$PG_DB_USER POSTGRES_PORT=5432 POSTGRES_HOST=localhost EOF msg_ok "Setup Joplin Server" msg_info "Setting up Service" cat </etc/systemd/system/joplin-server.service [Unit] Description=Joplin Server Service After=network.target [Service] Type=simple WorkingDirectory=/opt/joplin-server/packages/server EnvironmentFile=/opt/joplin-server/.env ExecStart=/usr/bin/yarn start-prod Restart=on-failure RestartSec=5 [Install] WantedBy=multi-user.target EOF systemctl enable -q --now joplin-server msg_ok "Service Setup" motd_ssh customize cleanup_lxc ================================================ FILE: install/jotty-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: vhsdream # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/fccview/jotty source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os NODE_VERSION="22" NODE_MODULE="yarn" setup_nodejs fetch_and_deploy_gh_release "jotty" "fccview/jotty" "prebuild" "latest" "/opt/jotty" "jotty_*_prebuild.tar.gz" msg_info "Setup jotty" mkdir -p /opt/jotty/data/{users,checklists,notes} cat </opt/jotty/.env NODE_ENV=production # --- Uncomment to enable # APP_URL=https://your-jotty-domain.com # INTERNAL_API_URL=http://localhost:3000 # HTTPS=true # SERVE_PUBLIC_IMAGES=yes # SERVE_PUBLIC_FILES=yes # SERVE_PUBLIC_VIDEOS=yes # STOP_CHECK_UPDATES=yes # --- For troubleshooting # DEBUGGER=true # --- SSO with OIDC (optional) # SSO_MODE=oidc # OIDC_ISSUER= # OIDC_CLIENT_ID= # SSO_FALLBACK_LOCAL=yes # OIDC_CLIENT_SECRET=your_client_secret # OIDC_ADMIN_GROUPS=admins EOF msg_ok "Setup jotty" msg_info "Creating Service" cat </etc/systemd/system/jotty.service [Unit] Description=jotty server After=network.target [Service] WorkingDirectory=/opt/jotty EnvironmentFile=/opt/jotty/.env ExecStart=/usr/bin/node server.js Restart=on-abnormal [Install] WantedBy=multi-user.target EOF systemctl enable -q --now jotty msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/jupyternotebook-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: Dave-code-creater (Tan Dat, Ta) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://jupyter.org/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os PYTHON_VERSION="3.12" setup_uv msg_info "Installing Jupyter" mkdir -p /opt/jupyter cd /opt/jupyter $STD uv venv --clear /opt/jupyter/.venv $STD /opt/jupyter/.venv/bin/python -m ensurepip --upgrade $STD /opt/jupyter/.venv/bin/python -m pip install --upgrade pip $STD /opt/jupyter/.venv/bin/python -m pip install jupyter ln -s /opt/jupyter/.venv/bin/jupyter /usr/local/bin/jupyter ln -s /opt/jupyter/.venv/bin/jupyter-lab /usr/local/bin/jupyter-lab ln -s /opt/jupyter/.venv/bin/jupyter-notebook /usr/local/bin/jupyter-notebook msg_ok "Installed Jupyter" msg_info "Creating Service" cat </etc/systemd/system/jupyternotebook.service [Unit] Description=Jupyter Notebook Server After=network.target [Service] Type=simple WorkingDirectory=/opt/jupyter ExecStart=/opt/jupyter/.venv/bin/jupyter notebook --ip=0.0.0.0 --port=8888 --allow-root Restart=always RestartSec=10 [Install] WantedBy=multi-user.target EOF systemctl enable -q --now jupyternotebook msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/kapowarr-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: Slaviša Arežina (tremor021) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/Casvt/Kapowarr source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y python3-pip msg_ok "Installed Dependencies" PYTHON_VERSION="3.12" setup_uv fetch_and_deploy_gh_release "kapowarr" "Casvt/Kapowarr" "tarball" msg_info "Setup Kapowarr" cd /opt/kapowarr $STD uv venv --clear .venv $STD source .venv/bin/activate $STD uv pip install --upgrade pip $STD uv pip install --no-cache-dir -r requirements.txt msg_ok "Installed Kapowarr" msg_info "Creating Service" cat </etc/systemd/system/kapowarr.service [Unit] Description=Kapowarr Service After=network.target [Service] WorkingDirectory=/opt/kapowarr/ ExecStart=/opt/kapowarr/.venv/bin/python3 Kapowarr.py Restart=always [Install] WantedBy=multi-user.target EOF systemctl enable -q --now kapowarr msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/karakeep-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (Canbiz) & vhsdream # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://karakeep.app/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ build-essential \ ca-certificates \ chromium \ graphicsmagick \ ghostscript \ ffmpeg msg_ok "Installed Dependencies" fetch_and_deploy_gh_release "monolith" "Y2Z/monolith" "singlefile" "latest" "/usr/bin" "monolith-gnu-linux-aarch64" fetch_and_deploy_gh_release "yt-dlp" "yt-dlp/yt-dlp-nightly-builds" "singlefile" "latest" "/usr/bin" "yt-dlp_linux" setup_meilisearch fetch_and_deploy_gh_release "karakeep" "karakeep-app/karakeep" "tarball" cd /opt/karakeep MODULE_VERSION="$(jq -r '.packageManager | split("@")[1]' /opt/karakeep/package.json)" NODE_VERSION="24" NODE_MODULE="pnpm@${MODULE_VERSION}" setup_nodejs msg_info "Installing karakeep" export PUPPETEER_SKIP_DOWNLOAD="true" export PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD="true" export NEXT_TELEMETRY_DISABLED=1 export CI="true" cd /opt/karakeep/apps/web $STD pnpm install --frozen-lockfile $STD pnpm build cd /opt/karakeep/apps/workers $STD pnpm install --frozen-lockfile $STD pnpm build cd /opt/karakeep/apps/cli $STD pnpm install --frozen-lockfile $STD pnpm build $STD pnpm store prune export DATA_DIR=/opt/karakeep_data karakeep_SECRET=$(openssl rand -base64 36 | cut -c1-24) mkdir -p /etc/karakeep cat </etc/karakeep/karakeep.env SERVER_VERSION="$(sed 's/^v//' ~/.karakeep)" NEXTAUTH_SECRET="$karakeep_SECRET" NEXTAUTH_URL="http://localhost:3000" DATA_DIR=${DATA_DIR} MEILI_ADDR="http://127.0.0.1:7700" MEILI_MASTER_KEY="$MEILISEARCH_MASTER_KEY" BROWSER_WEB_URL="http://127.0.0.1:9222" DB_WAL_MODE=true # If you're planning to use OpenAI for tagging. Uncomment the following line: # OPENAI_API_KEY="" # If you're planning to use ollama for tagging, uncomment the following lines: # OLLAMA_BASE_URL="" # OLLAMA_KEEP_ALIVE="5m" # You can change the models used by uncommenting the following lines, and changing them according to your needs: # INFERENCE_TEXT_MODEL="gpt-4o-mini" # INFERENCE_IMAGE_MODEL="gpt-4o-mini" # Additional inference defaults # INFERENCE_CONTEXT_LENGTH="2048" # INFERENCE_ENABLE_AUTO_TAGGING=true # INFERENCE_ENABLE_AUTO_SUMMARIZATION=false # Crawler defaults # CRAWLER_NUM_WORKERS="1" # CRAWLER_DOWNLOAD_BANNER_IMAGE=true # CRAWLER_STORE_SCREENSHOT=true # CRAWLER_FULL_PAGE_SCREENSHOT=false # CRAWLER_FULL_PAGE_ARCHIVE=false # CRAWLER_VIDEO_DOWNLOAD=false # CRAWLER_VIDEO_DOWNLOAD_MAX_SIZE="50" # CRAWLER_ENABLE_ADBLOCKER=true EOF msg_ok "Installed karakeep" msg_info "Running Database Migration" mkdir -p ${DATA_DIR} cd /opt/karakeep/packages/db $STD pnpm migrate msg_ok "Database Migration Completed" msg_info "Creating Services" cat </etc/systemd/system/karakeep-web.service [Unit] Description=karakeep Web Wants=network.target karakeep-workers.service After=network.target karakeep-workers.service [Service] ExecStart=pnpm start WorkingDirectory=/opt/karakeep/apps/web EnvironmentFile=/etc/karakeep/karakeep.env Restart=always [Install] WantedBy=multi-user.target EOF cat </etc/systemd/system/karakeep-browser.service [Unit] Description=karakeep Headless Browser After=network.target [Service] User=root ExecStart=/usr/bin/chromium --headless --no-sandbox --disable-gpu --disable-dev-shm-usage --remote-debugging-address=127.0.0.1 --remote-debugging-port=9222 --hide-scrollbars Restart=always [Install] WantedBy=multi-user.target EOF cat </etc/systemd/system/karakeep-workers.service [Unit] Description=karakeep Workers Wants=network.target karakeep-browser.service meilisearch.service After=network.target karakeep-browser.service meilisearch.service [Service] ExecStart=/usr/bin/node dist/index.js WorkingDirectory=/opt/karakeep/apps/workers EnvironmentFile=/etc/karakeep/karakeep.env Restart=always TimeoutStopSec=5 [Install] WantedBy=multi-user.target EOF systemctl enable -q --now karakeep-browser karakeep-workers karakeep-web msg_ok "Created Services" motd_ssh customize cleanup_lxc ================================================ FILE: install/kasm-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: Omar Minaya # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.kasmweb.com/docs/latest/index.html source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Docker" $STD sh <(curl -fsSL https://get.docker.com/) msg_ok "Installed Docker" msg_info "Detecting latest Kasm Workspaces release" KASM_VERSION=$(curl -s https://kasm.com/downloads | grep -oP ']*>.*?' | sed -E 's/<\/?h1[^>]*>//g' | grep -oP '\d+\.\d+\.\d+') KASM_URL="https://kasm-static-content.s3.amazonaws.com/kasm_release_${KASM_VERSION:-var_kasm_version}.tar.gz" # KASM_URL=$(curl -fsSL "https://www.kasm.com/downloads" | tr '\n' ' ' | grep -oE 'https://kasm-static-content[^"]*kasm_release_[0-9]+\.[0-9]+\.[0-9]+\.[a-z0-9]+\.tar\.gz' | head -n 1) # if [[ -z "$KASM_URL" ]]; then # SERVICE_IMAGE_URL=$(curl -fsSL "https://www.kasm.com/downloads" | tr '\n' ' ' | grep -oE 'https://kasm-static-content[^"]*kasm_release_service_images_amd64_[0-9]+\.[0-9]+\.[0-9]+\.tar\.gz' | head -n 1) # if [[ -n "$SERVICE_IMAGE_URL" ]]; then # KASM_VERSION=$(echo "$SERVICE_IMAGE_URL" | sed -E 's/.*kasm_release_service_images_amd64_([0-9]+\.[0-9]+\.[0-9]+).*/\1/') # KASM_URL="https://kasm-static-content.s3.amazonaws.com/kasm_release_${KASM_VERSION}.tar.gz" # fi # else # KASM_VERSION=$(echo "$KASM_URL" | sed -E 's/.*kasm_release_([0-9]+\.[0-9]+\.[0-9]+).*/\1/') # fi if [[ -z "$KASM_VERSION" ]] || [[ -z "$KASM_URL" ]]; then msg_error "Unable to detect latest Kasm release URL." exit 250 fi msg_ok "Detected Kasm Workspaces version $KASM_VERSION" msg_warn "WARNING: This script will run an external installer from a third-party source (https://www.kasmweb.com/)." msg_warn "The following code is NOT maintained or audited by our repository." msg_warn "If you have any doubts or concerns, please review the installer code before proceeding:" msg_custom "${TAB3}${GATEWAY}${BGN}${CL}" "\e[1;34m" "→ install.sh inside tar.gz $KASM_URL" echo read -r -p "${TAB3}Do you want to continue? [y/N]: " CONFIRM if [[ ! "$CONFIRM" =~ ^([yY][eE][sS]|[yY])$ ]]; then msg_error "Aborted by user. No changes have been made." exit 10 fi msg_info "Installing Kasm Workspaces" curl -fsSL -o "/opt/kasm_release_${KASM_VERSION}.tar.gz" "$KASM_URL" cd /opt tar -xf "kasm_release_${KASM_VERSION}.tar.gz" chmod +x /opt/kasm_release/install.sh printf 'y\ny\ny\n4\n' | bash /opt/kasm_release/install.sh >~/kasm-install.output 2>&1 awk ' /^Kasm UI Login Credentials$/ {capture=1} capture {print} /^Service Registration Token$/ {in_token=1} in_token && /^-+$/ {dash_count++} in_token && dash_count==2 {exit} ' ~/kasm-install.output >~/kasm.creds rm -f /opt/kasm_release_${KASM_VERSION}.tar.gz rm -f ~/kasm-install.output msg_ok "Installed Kasm Workspaces" motd_ssh customize cleanup_lxc ================================================ FILE: install/kavita-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.kavitareader.com/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os fetch_and_deploy_gh_release "Kavita" "Kareadita/Kavita" "prebuild" "latest" "/opt/Kavita" "kavita-linux-arm64.tar.gz" msg_info "Creating Service" cat </etc/systemd/system/kavita.service [Unit] Description=Kavita Server After=network.target [Service] Type=simple WorkingDirectory=/opt/Kavita ExecStart=/opt/Kavita/Kavita TimeoutStopSec=20 KillMode=process Restart=on-failure [Install] WantedBy=multi-user.target EOF chmod +x /opt/Kavita/Kavita && chown root:root /opt/Kavita/Kavita systemctl enable -q --now kavita msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/keycloak-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: tteck (tteckster) | Co-Author: Slaviša Arežina (tremor021), remz1337 # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/keycloak/keycloak source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os JAVA_VERSION=21 setup_java PG_VERSION=16 setup_postgresql msg_info "Configuring PostgreSQL" DB_NAME="keycloak" DB_USER="keycloak" DB_PASS="$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | cut -c1-13)" $STD sudo -u postgres psql -c "CREATE USER $DB_USER WITH PASSWORD '$DB_PASS';" $STD sudo -u postgres psql -c "CREATE DATABASE $DB_NAME WITH OWNER $DB_USER ENCODING 'UTF8';" $STD sudo -u postgres psql -c "GRANT ALL PRIVILEGES ON DATABASE $DB_NAME TO $DB_USER;" msg_ok "Configured PostgreSQL" fetch_and_deploy_gh_release "keycloak_app" "keycloak/keycloak" "prebuild" "latest" "/opt/keycloak" "keycloak-*.tar.gz" msg_info "Creating Service" cat </etc/systemd/system/keycloak.service [Unit] Description=Keycloak Service Requires=network.target After=syslog.target network-online.target [Service] Type=idle User=root WorkingDirectory=/opt/keycloak ExecStart=/opt/keycloak/bin/kc.sh start ExecStop=/opt/keycloak/bin/kc.sh stop Restart=always RestartSec=3 Environment="JAVA_HOME=/usr/lib/jvm/temurin-21-jdk-arm64" Environment="KC_DB=postgres" Environment="KC_DB_USERNAME=$DB_USER" Environment="KC_DB_PASSWORD=$DB_PASS" Environment="KC_HTTP_ENABLED=true" Environment="KC_BOOTSTRAP_ADMIN_USERNAME=tmpadm" Environment="KC_BOOTSTRAP_ADMIN_PASSWORD=admin123" # Comment following line and uncomment the next 2 if working behind a reverse proxy Environment="KC_HOSTNAME_STRICT=false" #Environment="KC_HOSTNAME=keycloak.example.com" #Environment="KC_PROXY_HEADERS=xforwarded" [Install] WantedBy=multi-user.target EOF systemctl enable -q --now keycloak msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/kima-hub-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/Chevron7Locked/kima-hub source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt-get install -y \ build-essential \ git \ openssl \ ffmpeg \ python3 \ python3-pip \ python3-dev \ python3-numpy \ redis-server msg_ok "Installed Dependencies" PG_VERSION="16" PG_MODULES="pgvector" setup_postgresql PG_DB_NAME="kima" PG_DB_USER="kima" PG_DB_GRANT_SUPERUSER="true" setup_postgresql_db NODE_VERSION="22" setup_nodejs msg_info "Configuring Redis" systemctl enable -q --now redis-server msg_ok "Configured Redis" fetch_and_deploy_gh_release "kima-hub" "Chevron7Locked/kima-hub" "tarball" msg_info "Installing Python Dependencies" export PIP_BREAK_SYSTEM_PACKAGES=1 $STD pip3 install --no-cache-dir \ tensorflow \ essentia-tensorflow \ redis \ psycopg2-binary \ laion-clap \ torch \ torchaudio \ librosa \ transformers \ pgvector \ python-dotenv \ requests msg_ok "Installed Python Dependencies" msg_info "Downloading Essentia ML Models" mkdir -p /opt/kima-hub/models cd /opt/kima-hub/models curl -fsSL -o msd-musicnn-1.pb "https://essentia.upf.edu/models/autotagging/msd/msd-musicnn-1.pb" curl -fsSL -o mood_happy-msd-musicnn-1.pb "https://essentia.upf.edu/models/classification-heads/mood_happy/mood_happy-msd-musicnn-1.pb" curl -fsSL -o mood_sad-msd-musicnn-1.pb "https://essentia.upf.edu/models/classification-heads/mood_sad/mood_sad-msd-musicnn-1.pb" curl -fsSL -o mood_relaxed-msd-musicnn-1.pb "https://essentia.upf.edu/models/classification-heads/mood_relaxed/mood_relaxed-msd-musicnn-1.pb" curl -fsSL -o mood_aggressive-msd-musicnn-1.pb "https://essentia.upf.edu/models/classification-heads/mood_aggressive/mood_aggressive-msd-musicnn-1.pb" curl -fsSL -o mood_party-msd-musicnn-1.pb "https://essentia.upf.edu/models/classification-heads/mood_party/mood_party-msd-musicnn-1.pb" curl -fsSL -o mood_acoustic-msd-musicnn-1.pb "https://essentia.upf.edu/models/classification-heads/mood_acoustic/mood_acoustic-msd-musicnn-1.pb" curl -fsSL -o mood_electronic-msd-musicnn-1.pb "https://essentia.upf.edu/models/classification-heads/mood_electronic/mood_electronic-msd-musicnn-1.pb" curl -fsSL -o danceability-msd-musicnn-1.pb "https://essentia.upf.edu/models/classification-heads/danceability/danceability-msd-musicnn-1.pb" curl -fsSL -o voice_instrumental-msd-musicnn-1.pb "https://essentia.upf.edu/models/classification-heads/voice_instrumental/voice_instrumental-msd-musicnn-1.pb" msg_ok "Downloaded Essentia ML Models" msg_info "Downloading CLAP Model" curl -fsSL -o /opt/kima-hub/models/music_audioset_epoch_15_esc_90.14.pt "https://huggingface.co/lukewys/laion_clap/resolve/main/music_audioset_epoch_15_esc_90.14.pt" msg_ok "Downloaded CLAP Model" msg_info "Building Backend" cd /opt/kima-hub/backend $STD npm ci $STD npm run build msg_ok "Built Backend" msg_info "Configuring Backend" SESSION_SECRET=$(openssl rand -hex 32) ENCRYPTION_KEY=$(openssl rand -hex 32) cat </opt/kima-hub/backend/.env NODE_ENV=production DATABASE_URL=postgresql://${PG_DB_USER}:${PG_DB_PASS}@localhost:5432/${PG_DB_NAME} REDIS_URL=redis://localhost:6379 PORT=3006 MUSIC_PATH=/music TRANSCODE_CACHE_PATH=/opt/kima-hub/cache/transcodes SESSION_SECRET=${SESSION_SECRET} SETTINGS_ENCRYPTION_KEY=${ENCRYPTION_KEY} INTERNAL_API_SECRET=$(openssl rand -hex 16) EOF msg_ok "Configured Backend" msg_info "Running Database Migrations" cd /opt/kima-hub/backend $STD npx prisma generate $STD npx prisma migrate deploy msg_ok "Ran Database Migrations" msg_info "Building Frontend" cd /opt/kima-hub/frontend $STD npm ci export NEXT_PUBLIC_BACKEND_URL=http://127.0.0.1:3006 $STD npm run build msg_ok "Built Frontend" msg_info "Configuring Frontend" cat </opt/kima-hub/frontend/.env NODE_ENV=production BACKEND_URL=http://localhost:3006 PORT=3030 EOF msg_ok "Configured Frontend" msg_info "Creating Directories" mkdir -p /opt/kima-hub/cache/transcodes mkdir -p /music msg_ok "Created Directories" msg_info "Creating Services" cat </etc/systemd/system/kima-backend.service [Unit] Description=Kima Hub Backend After=network.target postgresql.service redis-server.service Wants=postgresql.service redis-server.service [Service] Type=simple User=root WorkingDirectory=/opt/kima-hub/backend EnvironmentFile=/opt/kima-hub/backend/.env ExecStart=/usr/bin/node /opt/kima-hub/backend/dist/index.js Restart=on-failure RestartSec=5 [Install] WantedBy=multi-user.target EOF cat </etc/systemd/system/kima-frontend.service [Unit] Description=Kima Hub Frontend After=network.target kima-backend.service [Service] Type=simple User=root WorkingDirectory=/opt/kima-hub/frontend EnvironmentFile=/opt/kima-hub/frontend/.env ExecStart=/usr/bin/npm start Restart=on-failure RestartSec=5 [Install] WantedBy=multi-user.target EOF cat </etc/systemd/system/kima-analyzer.service [Unit] Description=Kima Hub Audio Analyzer (Essentia) After=network.target postgresql.service redis-server.service kima-backend.service [Service] Type=simple User=root WorkingDirectory=/opt/kima-hub/services/audio-analyzer Environment=DATABASE_URL=postgresql://${PG_DB_USER}:${PG_DB_PASS}@localhost:5432/${PG_DB_NAME} Environment=REDIS_URL=redis://localhost:6379 Environment=MUSIC_PATH=/music Environment=BATCH_SIZE=10 Environment=SLEEP_INTERVAL=5 Environment=NUM_WORKERS=2 Environment=THREADS_PER_WORKER=1 ExecStart=/usr/bin/python3 /opt/kima-hub/services/audio-analyzer/analyzer.py Restart=on-failure RestartSec=10 [Install] WantedBy=multi-user.target EOF cat </etc/systemd/system/kima-analyzer-clap.service [Unit] Description=Kima Hub CLAP Audio Analyzer After=network.target postgresql.service redis-server.service kima-backend.service kima-analyzer.service [Service] Type=simple User=root WorkingDirectory=/opt/kima-hub/services/audio-analyzer-clap Environment=DATABASE_URL=postgresql://${PG_DB_USER}:${PG_DB_PASS}@localhost:5432/${PG_DB_NAME} Environment=REDIS_URL=redis://localhost:6379 Environment=BACKEND_URL=http://localhost:3006 Environment=MUSIC_PATH=/music Environment=SLEEP_INTERVAL=5 Environment=NUM_WORKERS=1 ExecStart=/usr/bin/python3 /opt/kima-hub/services/audio-analyzer-clap/analyzer.py Restart=on-failure RestartSec=10 [Install] WantedBy=multi-user.target EOF systemctl enable -q --now kima-backend kima-frontend kima-analyzer kima-analyzer-clap msg_ok "Created Services" motd_ssh customize cleanup_lxc ================================================ FILE: install/kimai-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.kimai.org/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ apt-transport-https \ apache2 \ git \ expect msg_ok "Installed Dependencies" setup_mariadb PHP_VERSION="8.4" PHP_APACHE="YES" setup_php setup_composer msg_info "Setting up database" DB_NAME=kimai_db DB_USER=kimai DB_PASS=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | head -c13) MYSQL_VERSION=$(mariadb --version | grep -oE '[0-9]+\.[0-9]+\.[0-9]+') $STD mariadb -e "CREATE DATABASE $DB_NAME;" $STD mariadb -e "CREATE USER '$DB_USER'@'localhost' IDENTIFIED BY '$DB_PASS';" $STD mariadb -e "GRANT ALL ON $DB_NAME.* TO '$DB_USER'@'localhost'; FLUSH PRIVILEGES;" { echo "Kimai-Credentials" echo "Kimai Database User: $DB_USER" echo "Kimai Database Password: $DB_PASS" echo "Kimai Database Name: $DB_NAME" } >>~/kimai.creds msg_ok "Set up database" fetch_and_deploy_gh_release "kimai" "kimai/kimai" "tarball" msg_info "Setup Kimai" cd /opt/kimai echo "export COMPOSER_ALLOW_SUPERUSER=1" >>~/.bashrc source ~/.bashrc $STD composer install --no-dev --optimize-autoloader --no-interaction cp .env.dist .env sed -i "/^DATABASE_URL=/c\DATABASE_URL=mysql://$DB_USER:$DB_PASS@127.0.0.1:3306/$DB_NAME?charset=utf8mb4&serverVersion=mariadb-$MYSQL_VERSION" /opt/kimai/.env $STD bin/console kimai:install -n $STD expect </opt/kimai/config/packages/local.yaml kimai: timesheet: rounding: default: begin: 15 end: 15 EOF msg_ok "Installed Kimai" msg_info "Creating Service" cat </etc/apache2/sites-available/kimai.conf ServerAdmin webmaster@localhost DocumentRoot /opt/kimai/public/ Options FollowSymLinks AllowOverride All Require all granted ErrorLog /var/log/apache2/error.log CustomLog /var/log/apache2/access.log combined EOF $STD a2ensite kimai.conf $STD a2dissite 000-default.conf $STD systemctl reload apache2 msg_ok "Created Service" msg_info "Setup Permissions" chown -R :www-data /opt/* chmod -R g+r /opt/* chmod -R g+rw /opt/* chown -R www-data:www-data /opt/* chmod -R 777 /opt/* msg_ok "Setup Permissions" motd_ssh customize cleanup_lxc ================================================ FILE: install/kitchenowl-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2025 community-scripts ORG # Author: snazzybean # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/TomBursch/kitchenowl source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ nginx \ build-essential \ gfortran \ pkg-config \ ninja-build \ autoconf \ automake \ libpq-dev \ libffi-dev \ libssl-dev \ libpcre2-dev \ libre2-dev \ libxml2-dev \ libxslt-dev \ libopenblas-dev \ liblapack-dev \ zlib1g-dev \ libjpeg62-turbo-dev \ libsqlite3-dev \ libexpat1-dev \ libicu-dev msg_ok "Installed Dependencies" PYTHON_VERSION="3.14" setup_uv fetch_and_deploy_gh_release "kitchenowl" "TomBursch/kitchenowl" "tarball" "latest" "/opt/kitchenowl" rm -rf /opt/kitchenowl/web fetch_and_deploy_gh_release "kitchenowl-web" "TomBursch/kitchenowl" "prebuild" "latest" "/opt/kitchenowl/web" "kitchenowl_Web.tar.gz" msg_info "Setting up KitchenOwl" cd /opt/kitchenowl/backend $STD uv sync --no-dev sed -i 's/default=True/default=False/' /opt/kitchenowl/backend/wsgi.py setup_nltk "averaged_perceptron_tagger_eng" "/nltk_data" JWT_SECRET=$(openssl rand -hex 32) mkdir -p /opt/kitchenowl/data cat </opt/kitchenowl/kitchenowl.env STORAGE_PATH=/opt/kitchenowl/data JWT_SECRET_KEY=${JWT_SECRET} NLTK_DATA=/nltk_data FRONT_URL=http://${LOCAL_IP} FLASK_APP=wsgi.py FLASK_ENV=production EOF set -a source /opt/kitchenowl/kitchenowl.env set +a $STD uv run flask db upgrade msg_ok "Set up KitchenOwl" msg_info "Creating Systemd Service" cat </etc/systemd/system/kitchenowl.service [Unit] Description=KitchenOwl Backend After=network.target [Service] Type=simple User=root WorkingDirectory=/opt/kitchenowl/backend EnvironmentFile=/opt/kitchenowl/kitchenowl.env ExecStart=/usr/local/bin/uv run wsgi.py Restart=on-failure RestartSec=5 [Install] WantedBy=multi-user.target EOF systemctl enable -q --now kitchenowl msg_ok "Created and Started Service" msg_info "Configuring Nginx" rm -f /etc/nginx/sites-enabled/default cat <<'EOF' >/etc/nginx/sites-available/kitchenowl.conf server { listen 80; server_name _; root /opt/kitchenowl/web; index index.html; client_max_body_size 100M; # Security Headers add_header X-Frame-Options "SAMEORIGIN" always; add_header X-Content-Type-Options "nosniff" always; add_header X-XSS-Protection "1; mode=block" always; add_header Referrer-Policy "strict-origin-when-cross-origin" always; location / { try_files $uri $uri/ /index.html; } location /api { proxy_pass http://127.0.0.1:5000; proxy_http_version 1.1; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_connect_timeout 60s; proxy_send_timeout 60s; proxy_read_timeout 60s; } location /socket.io { proxy_pass http://127.0.0.1:5000; proxy_http_version 1.1; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; # WebSocket Timeouts - allow long-lived connections proxy_read_timeout 86400s; proxy_send_timeout 86400s; } } EOF ln -sf /etc/nginx/sites-available/kitchenowl.conf /etc/nginx/sites-enabled/ rm -f /etc/nginx/sites-enabled/default $STD systemctl reload nginx msg_ok "Configured Nginx" motd_ssh customize cleanup_lxc ================================================ FILE: install/koel-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://koel.dev/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ nginx \ ffmpeg \ cron \ locales msg_ok "Installed Dependencies" PG_VERSION="16" setup_postgresql PG_DB_NAME="koel" PG_DB_USER="koel" setup_postgresql_db PHP_VERSION="8.4" PHP_FPM="YES" setup_php NODE_VERSION="22" NODE_MODULE="pnpm" setup_nodejs setup_composer fetch_and_deploy_gh_release "koel" "koel/koel" "prebuild" "latest" "/opt/koel" "koel-*.tar.gz" msg_info "Configuring Koel" mkdir -p /opt/koel_media /opt/koel_sync cd /opt/koel cat </opt/koel/.env APP_NAME=Koel APP_ENV=production APP_DEBUG=false APP_URL=http://${LOCAL_IP} APP_KEY= TRUSTED_HOSTS= DB_CONNECTION=pgsql DB_HOST=127.0.0.1 DB_PORT=5432 DB_DATABASE=${PG_DB_NAME} DB_USERNAME=${PG_DB_USER} DB_PASSWORD=${PG_DB_PASS} STORAGE_DRIVER=local MEDIA_PATH=/opt/koel_media ARTIFACTS_PATH= IGNORE_DOT_FILES=true APP_MAX_SCAN_TIME=600 MEMORY_LIMIT= STREAMING_METHOD=php SCOUT_DRIVER=tntsearch USE_MUSICBRAINZ=true MUSICBRAINZ_USER_AGENT= LASTFM_API_KEY= LASTFM_API_SECRET= SPOTIFY_CLIENT_ID= SPOTIFY_CLIENT_SECRET= YOUTUBE_API_KEY= CDN_URL= TRANSCODE_FLAC=false FFMPEG_PATH=/usr/bin/ffmpeg TRANSCODE_BIT_RATE=128 ALLOW_DOWNLOAD=true BACKUP_ON_DELETE=true MEDIA_BROWSER_ENABLED=false PROXY_AUTH_ENABLED=false SYNC_LOG_LEVEL=error FORCE_HTTPS= MAIL_FROM_ADDRESS="noreply@localhost" MAIL_FROM_NAME="Koel" MAIL_MAILER=log MAIL_HOST=null MAIL_PORT=null MAIL_USERNAME=null MAIL_PASSWORD=null MAIL_ENCRYPTION=null BROADCAST_CONNECTION=log CACHE_DRIVER=file FILESYSTEM_DISK=local QUEUE_CONNECTION=sync SESSION_DRIVER=file SESSION_LIFETIME=120 EOF mkdir -p /opt/koel/storage/{app/public,framework/{cache/data,sessions,views},logs} chown -R www-data:www-data /opt/koel /opt/koel_media /opt/koel_sync chmod -R 775 /opt/koel/storage /opt/koel/bootstrap/cache msg_ok "Configured Koel" msg_info "Installing Koel (Patience)" export COMPOSER_ALLOW_SUPERUSER=1 cd /opt/koel $STD composer install --no-interaction --no-dev --optimize-autoloader $STD php artisan key:generate --force $STD php artisan config:clear $STD php artisan cache:clear $STD php artisan koel:init --no-assets --no-interaction chown -R www-data:www-data /opt/koel msg_ok "Installed Koel" msg_info "Tuning PHP-FPM" PHP_FPM_CONF="/etc/php/8.4/fpm/pool.d/www.conf" sed -i 's/^pm.max_children = .*/pm.max_children = 15/' "$PHP_FPM_CONF" sed -i 's/^pm.start_servers = .*/pm.start_servers = 4/' "$PHP_FPM_CONF" sed -i 's/^pm.min_spare_servers = .*/pm.min_spare_servers = 2/' "$PHP_FPM_CONF" sed -i 's/^pm.max_spare_servers = .*/pm.max_spare_servers = 8/' "$PHP_FPM_CONF" $STD systemctl restart php8.4-fpm msg_ok "Tuned PHP-FPM" msg_info "Configuring Nginx" cat <<'EOF' >/etc/nginx/sites-available/koel server { listen 80; server_name _; root /opt/koel/public; index index.php; client_max_body_size 50M; charset utf-8; gzip on; gzip_types text/plain text/css application/x-javascript text/xml application/xml application/xml+rss text/javascript application/json; gzip_comp_level 9; send_timeout 3600; location / { try_files $uri $uri/ /index.php?$args; } location /media/ { internal; alias $upstream_http_x_media_root; } location ~ \.php$ { try_files $uri $uri/ /index.php?$args; fastcgi_pass unix:/run/php/php8.4-fpm.sock; fastcgi_index index.php; fastcgi_split_path_info ^(.+\.php)(/.+)$; fastcgi_intercept_errors on; fastcgi_param PATH_INFO $fastcgi_path_info; fastcgi_param PATH_TRANSLATED $document_root$fastcgi_path_info; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params; } location ~ /\.(?!well-known).* { deny all; } } EOF rm -f /etc/nginx/sites-enabled/default ln -sf /etc/nginx/sites-available/koel /etc/nginx/sites-enabled/koel $STD systemctl reload nginx msg_ok "Configured Nginx" msg_info "Setting up Cron Job" cat <<'EOF' >/etc/cron.d/koel 0 * * * * www-data cd /opt/koel && /usr/bin/php artisan koel:scan >/dev/null 2>&1 EOF chmod 644 /etc/cron.d/koel msg_ok "Set up Cron Job" motd_ssh customize cleanup_lxc ================================================ FILE: install/koillection-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: bvdberg01 # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://koillection.github.io/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os NODE_VERSION="24" NODE_MODULE="yarn" setup_nodejs PG_VERSION="16" setup_postgresql PHP_VERSION="8.5" PHP_APACHE="YES" setup_php setup_composer PG_DB_NAME="koillection" PG_DB_USER="koillection" setup_postgresql_db fetch_and_deploy_gh_release "koillection" "benjaminjonard/koillection" "tarball" msg_info "Configuring Koillection" cd /opt/koillection cp /opt/koillection/.env /opt/koillection/.env.local APP_SECRET=$(openssl rand -base64 32) sed -i -e "s|^APP_ENV=.*|APP_ENV=prod|" \ -e "s|^APP_DEBUG=.*|APP_DEBUG=0|" \ -e "s|^APP_SECRET=.*|APP_SECRET=${APP_SECRET}|" \ -e "s|^DB_NAME=.*|DB_NAME=${PG_DB_NAME}|" \ -e "s|^DB_USER=.*|DB_USER=${PG_DB_USER}|" \ -e "s|^DB_PASSWORD=.*|DB_PASSWORD=${PG_DB_PASS}|" \ /opt/koillection/.env.local echo 'APP_RUNTIME="Symfony\Component\Runtime\SymfonyRuntime"' >>/opt/koillection/.env.local export COMPOSER_ALLOW_SUPERUSER=1 export APP_RUNTIME='Symfony\Component\Runtime\SymfonyRuntime' $STD composer install --no-dev -o --no-interaction --classmap-authoritative $STD php bin/console doctrine:migrations:migrate --no-interaction $STD php bin/console app:translations:dump cd assets/ $STD yarn install $STD yarn build mkdir -p /opt/koillection/public/uploads chown -R www-data:www-data /opt/koillection/public/uploads msg_ok "Configured Koillection" msg_info "Creating Service" cat </etc/apache2/sites-available/koillection.conf ServerName koillection DocumentRoot /opt/koillection/public SetEnv APP_RUNTIME "Symfony\\Component\\Runtime\\SymfonyRuntime" Options Indexes FollowSymLinks AllowOverride All Require all granted RewriteEngine On RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule ^(.*)$ index.php/\$1 [L] ErrorLog /var/log/apache2/koillection_error.log CustomLog /var/log/apache2/koillection_access.log combined EOF $STD a2ensite koillection $STD a2enmod rewrite $STD a2dissite 000-default.conf $STD systemctl reload apache2 msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/kometa-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: Slaviša Arežina (tremor021) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/Kometa-Team/Kometa source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os PYTHON_VERSION="3.13" setup_uv fetch_and_deploy_gh_release "kometa" "Kometa-Team/Kometa" "tarball" msg_info "Setup Kometa" cd /opt/kometa $STD uv pip install -r requirements.txt --system mkdir -p config/assets cp config/config.yml.template config/config.yml msg_ok "Setup Kometa" read -r -p "${TAB3}Enter your TMDb API key: " TMDBKEY read -r -p "${TAB3}Enter your Plex URL: " PLEXURL read -r -p "${TAB3}Enter your Plex token: " PLEXTOKEN sed -i '/^plex:/,/^[^ ]/{s| url:.*| url: '"$PLEXURL"'|}' /opt/kometa/config/config.yml sed -i '/^plex:/,/^[^ ]/{s| token:.*| token: '"$PLEXTOKEN"'|}' /opt/kometa/config/config.yml sed -i '/^tmdb:/,/^[^ ]/{s| apikey:.*| apikey: '"$TMDBKEY"'|}' /opt/kometa/config/config.yml fetch_and_deploy_gh_release "kometa-quickstart" "Kometa-Team/Quickstart" "tarball" msg_info "Installing Kometa Quickstart" cd /opt/kometa-quickstart $STD uv venv /opt/kometa-quickstart/.venv $STD uv pip install -r requirements.txt -p /opt/kometa-quickstart/.venv/bin/python msg_ok "Installed Kometa Quickstart" msg_info "Creating Service" cat </etc/systemd/system/kometa.service [Unit] Description=Kometa Service After=network-online.target [Service] Type=simple WorkingDirectory=/opt/kometa ExecStart=/usr/bin/python3 kometa.py Restart=always RestartSec=30 [Install] WantedBy=multi-user.target EOF cat </etc/systemd/system/kometa-quickstart.service [Unit] Description=Kometa Quickstart After=network-online.target [Service] Type=simple WorkingDirectory=/opt/kometa-quickstart ExecStart=/opt/kometa-quickstart/.venv/bin/python quickstart.py Restart=always RestartSec=10 [Install] WantedBy=multi-user.target EOF systemctl enable -q --now kometa kometa-quickstart msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/komga-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: madelyn (DysfunctionalProgramming) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://komga.org/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing dependencies" $STD apt -y install \ libarchive-dev \ libjxl-dev \ libheif-dev \ libwebp-dev msg_ok "Installed dependencies" JAVA_VERSION="23" setup_java fetch_and_deploy_gh_release "kepubify" "pgaskin/kepubify" "singlefile" "latest" "/usr/bin" "kepubify-linux-64bit" USE_ORIGINAL_FILENAME="true" fetch_and_deploy_gh_release "komga-org" "gotson/komga" "singlefile" "latest" "/opt/komga" "komga*.jar" mv /opt/komga/komga-*.jar /opt/komga/komga.jar msg_info "Creating Service" cat </etc/systemd/system/komga.service [Unit] Description=Komga After=syslog.target network.target [Service] Type=simple WorkingDirectory=/opt/komga/ Environment=LD_LIBRARY_PATH=/usr/lib/aarch64-linux-gnu ExecStart=/usr/bin/java --enable-native-access=ALL-UNNAMED -jar -Xmx2g komga.jar TimeoutStopSec=20 KillMode=process Restart=on-failure [Install] WantedBy=multi-user.target EOF systemctl enable -q --now komga msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/kubo-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # Co-Author: ulmentflam # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/ipfs/kubo source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os fetch_and_deploy_gh_release "kubo" "ipfs/kubo" "prebuild" "latest" "/usr/local/kubo" "kubo*linux-arm64.tar.gz" msg_info "Configuring IPFS" $STD ln -s /usr/local/kubo/ipfs /usr/local/bin/ipfs $STD ipfs init ipfs config Addresses.API /ip4/0.0.0.0/tcp/5001 ipfs config Addresses.Gateway /ip4/0.0.0.0/tcp/8080 ipfs config --json API.HTTPHeaders.Access-Control-Allow-Origin "[\"http://${LOCAL_IP}:5001\", \"http://localhost:3000\", \"http://127.0.0.1:5001\", \"https://webui.ipfs.io\", \"http://0.0.0.0:5001\"]" ipfs config --json API.HTTPHeaders.Access-Control-Allow-Methods '["PUT", "POST"]' echo "${RELEASE}" >"/opt/${APPLICATION}_version.txt" $STD rm "kubo_${RELEASE}_linux-arm64.tar.gz" msg_ok "Installed IPFS" msg_info "Creating Service" cat </etc/systemd/system/ipfs.service [Unit] Description=IPFS Daemon After=syslog.target network.target [Service] Type=simple ExecStart=/usr/local/bin/ipfs daemon Restart=on-failure Environment=HOME=/root [Install] WantedBy=multi-user.target EOF systemctl enable -q --now ipfs msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/kutt-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: tomfrenzel # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/thedevs-network/kutt source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os echo "${TAB3}How would you like to handle SSL termination?" echo "${TAB3}[i]-Internal (self-signed SSL Certificate) [e]-External (use your own reverse proxy)" read -rp "${TAB3}Enter your choice (default: i): " ssl_choice ssl_choice=${ssl_choice:-i} case "${ssl_choice,,}" in i) DEFAULT_HOST="$LOCAL_IP" msg_info "Configuring Caddy" $STD apt install -y caddy cat </etc/caddy/Caddyfile $LOCAL_IP { reverse_proxy localhost:3000 } EOF systemctl restart caddy msg_ok "Configured Caddy" ;; e) read -r -p "${TAB3}Enter the hostname you want to use for Kutt (eg. kutt.example.com): " custom_host if [[ "$custom_host" ]]; then DEFAULT_HOST="$custom_host" fi ;; esac NODE_VERSION="22" setup_nodejs fetch_and_deploy_gh_release "kutt" "thedevs-network/kutt" "tarball" msg_info "Configuring Kutt" cd /opt/kutt cp .example.env ".env" sed -i "s|JWT_SECRET=|JWT_SECRET=$(openssl rand -base64 32)|g" ".env" sed -i "s|DEFAULT_DOMAIN=.*|DEFAULT_DOMAIN=$DEFAULT_HOST|g" ".env" $STD npm install $STD npm run migrate msg_ok "Configured Kutt" msg_info "Creating Services" cat </etc/systemd/system/kutt.service [Unit] Description=Kutt server After=network-online.target [Service] Type=simple WorkingDirectory=/opt/kutt ExecStart=/usr/bin/npm start Restart=always [Install] WantedBy=multi-user.target EOF systemctl enable -q --now kutt msg_ok "Created Services" motd_ssh customize cleanup_lxc ================================================ FILE: install/languagetool-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: Slaviša Arežina (tremor021) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://languagetool.org/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing dependencies" $STD apt install -y fasttext msg_ok "Installed dependencies" JAVA_VERSION="21" setup_java msg_info "Setting up LanguageTool" RELEASE=$(curl -fsSL https://languagetool.org/download/ | grep -oP 'LanguageTool-\K[0-9]+\.[0-9]+(\.[0-9]+)?(?=\.zip)' | sort -V | tail -n1) download_file "https://languagetool.org/download/LanguageTool-stable.zip" /tmp/LanguageTool-stable.zip unzip -q /tmp/LanguageTool-stable.zip -d /opt mv /opt/LanguageTool-*/ /opt/LanguageTool/ download_file "https://dl.fbaipublicfiles.com/fasttext/supervised-models/lid.176.bin" /opt/lid.176.bin rm -f /tmp/LanguageTool-stable.zip msg_ok "Setup LanguageTool" ngram_dir="" lang_code="" max_attempts=3 attempt=0 while [[ $attempt -lt $max_attempts ]]; do read -r -p "${TAB3}Enter language code (en, de, es, fr, nl) to download ngrams or press ENTER to skip: " lang_code if [[ -z "$lang_code" ]]; then break fi if [[ "$lang_code" =~ [[:space:]] ]]; then ((attempt++)) remaining=$((max_attempts - attempt)) if [[ $remaining -gt 0 ]]; then msg_error "Please enter only ONE language code. You have $remaining attempt(s) remaining." else msg_error "Maximum attempts reached. Continuing without ngrams." lang_code="" fi continue fi break done if [[ -n "$lang_code" ]]; then if [[ "$lang_code" =~ ^(en|de|es|fr|nl)$ ]]; then msg_info "Searching for $lang_code ngrams..." filename=$(curl -fsSL https://languagetool.org/download/ngram-data/ | grep -oP "ngrams-${lang_code}-[0-9]+\.zip" | sort -uV | tail -n1) if [[ -n "$filename" ]]; then msg_info "Downloading $filename" download_file "https://languagetool.org/download/ngram-data/${filename}" "/tmp/${filename}" mkdir -p /opt/ngrams msg_info "Extracting $lang_code ngrams to /opt/ngrams" unzip -q "/tmp/${filename}" -d /opt/ngrams rm "/tmp/${filename}" ngram_dir="/opt/ngrams" msg_ok "Installed $lang_code ngrams" else msg_info "No ngram file found for ${lang_code}" fi else msg_error "Invalid language code: $lang_code" fi fi cat </opt/LanguageTool/server.properties fasttextModel=/opt/lid.176.bin fasttextBinary=/usr/bin/fasttext EOF if [[ -n "$ngram_dir" ]]; then echo "languageModel=/opt/ngrams" >> /opt/LanguageTool/server.properties fi echo "${RELEASE}" >~/.languagetool msg_ok "Setup LanguageTool" msg_info "Creating Service" cat <<'EOF' >/etc/systemd/system/language-tool.service [Unit] Description=LanguageTool Service After=network.target [Service] WorkingDirectory=/opt/LanguageTool ExecStart=java -cp languagetool-server.jar org.languagetool.server.HTTPServer --config server.properties --public --allow-origin "*" Restart=always [Install] WantedBy=multi-user.target EOF systemctl enable -q --now language-tool msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/lazylibrarian-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck # Co-Author: MountyMapleSyrup (MountyMapleSyrup) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://gitlab.com/LazyLibrarian/LazyLibrarian source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ git \ libpng-dev \ libjpeg-dev \ libtiff-dev \ imagemagick msg_ok "Installed Dependencies" msg_info "Setup Python3" $STD apt install -y \ pip \ python3-irc rm -rf /usr/lib/python3.*/EXTERNALLY-MANAGED $STD pip install jaraco.stream $STD pip install python-Levenshtein $STD pip install soupsieve $STD pip install pypdf msg_ok "Setup Python3" msg_info "Installing LazyLibrarian" $STD git clone https://gitlab.com/LazyLibrarian/LazyLibrarian /opt/LazyLibrarian cd /opt/LazyLibrarian $STD pip install . msg_ok "Installed LazyLibrarian" msg_info "Creating Service" cat </etc/systemd/system/lazylibrarian.service [Unit] Description=LazyLibrarian Daemon After=syslog.target network.target [Service] UMask=0002 Type=simple ExecStart=/usr/bin/python3 /opt/LazyLibrarian/LazyLibrarian.py TimeoutStopSec=20 KillMode=process Restart=on-failure [Install] WantedBy=multi-user.target EOF systemctl enable --now -q lazylibrarian msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/leantime-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: Stroopwafe1 # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://leantime.io source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os PHP_VERSION="8.4" PHP_APACHE="YES" PHP_FPM="YES" setup_php setup_mariadb MARIADB_DB_NAME="leantime" MARIADB_DB_USER="leantime" setup_mariadb_db fetch_and_deploy_gh_release "leantime" "Leantime/leantime" "prebuild" "latest" "/opt/leantime" Leantime*.tar.gz msg_info "Setup Leantime" chown -R www-data:www-data "/opt/leantime" chmod -R 750 "/opt/leantime" cat </etc/apache2/sites-enabled/000-default.conf ServerAdmin webmaster@localhost DocumentRoot /opt/leantime/public DirectoryIndex index.php index.html index.cgi index.pl index.xhtml Options +ExecCGI Options FollowSymLinks Require all granted AllowOverride All Require all granted ErrorLog /var/log/apache2/error.log CustomLog /var/log/apache2/access.log combined EOF mv "/opt/leantime/config/sample.env" "/opt/leantime/config/.env" sed -i -e "s|^LEAN_DB_DATABASE.*|LEAN_DB_DATABASE = '$MARIADB_DB_NAME'|" \ -e "s|^LEAN_DB_USER.*|LEAN_DB_USER = '$MARIADB_DB_USER'|" \ -e "s|^LEAN_DB_PASSWORD.*|LEAN_DB_PASSWORD = '$MARIADB_DB_PASS'|" \ -e "s|^LEAN_SESSION_PASSWORD.*|LEAN_SESSION_PASSWORD = '$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | head -c13)'|" \ "/opt/leantime/config/.env" $STD a2enmod -q proxy_fcgi setenvif rewrite $STD a2enconf -q "php8.4-fpm" sed -i -e "s/^;extension.\(curl\|fileinfo\|gd\|intl\|ldap\|mbstring\|exif\|mysqli\|odbc\|openssl\|pdo_mysql\)/extension=\1/g" "/etc/php/8.4/apache2/php.ini" systemctl restart apache2 msg_ok "Setup leantime" motd_ssh customize cleanup_lxc ================================================ FILE: install/librechat-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/danny-avila/LibreChat source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os MONGO_VERSION="8.0" setup_mongodb setup_meilisearch PG_VERSION="17" PG_MODULES="pgvector" setup_postgresql PG_DB_NAME="ragapi" PG_DB_USER="ragapi" PG_DB_EXTENSIONS="vector" setup_postgresql_db NODE_VERSION="24" setup_nodejs UV_PYTHON="3.12" setup_uv fetch_and_deploy_gh_tag "librechat" "danny-avila/LibreChat" fetch_and_deploy_gh_release "rag-api" "danny-avila/rag_api" "tarball" msg_info "Installing LibreChat Dependencies" cd /opt/librechat $STD npm ci msg_ok "Installed LibreChat Dependencies" msg_info "Building Frontend" $STD npm run frontend $STD npm prune --production $STD npm cache clean --force msg_ok "Built Frontend" msg_info "Installing RAG API Dependencies" cd /opt/rag-api $STD uv venv --python 3.12 --seed .venv $STD .venv/bin/pip install -r requirements.lite.txt mkdir -p /opt/rag-api/uploads msg_ok "Installed RAG API Dependencies" msg_info "Configuring LibreChat" JWT_SECRET=$(openssl rand -hex 32) JWT_REFRESH_SECRET=$(openssl rand -hex 32) CREDS_KEY=$(openssl rand -hex 32) CREDS_IV=$(openssl rand -hex 16) cat </opt/librechat/.env HOST=0.0.0.0 PORT=3080 MONGO_URI=mongodb://127.0.0.1:27017/LibreChat DOMAIN_CLIENT=http://${LOCAL_IP}:3080 DOMAIN_SERVER=http://${LOCAL_IP}:3080 NO_INDEX=true TRUST_PROXY=1 JWT_SECRET=${JWT_SECRET} JWT_REFRESH_SECRET=${JWT_REFRESH_SECRET} SESSION_EXPIRY=1000 * 60 * 15 REFRESH_TOKEN_EXPIRY=(1000 * 60 * 60 * 24) * 7 CREDS_KEY=${CREDS_KEY} CREDS_IV=${CREDS_IV} ALLOW_EMAIL_LOGIN=true ALLOW_REGISTRATION=true ALLOW_SOCIAL_LOGIN=false ALLOW_SOCIAL_REGISTRATION=false ALLOW_PASSWORD_RESET=false ALLOW_UNVERIFIED_EMAIL_LOGIN=true SEARCH=true MEILI_NO_ANALYTICS=true MEILI_HOST=http://127.0.0.1:7700 MEILI_MASTER_KEY=${MEILISEARCH_MASTER_KEY} RAG_PORT=8000 RAG_API_URL=http://127.0.0.1:8000 APP_TITLE=LibreChat ENDPOINTS=openAI,agents,assistants,anthropic,google # OPENAI_API_KEY=your-key-here # OPENAI_MODELS= # ANTHROPIC_API_KEY=your-key-here # GOOGLE_KEY=your-key-here EOF msg_ok "Configured LibreChat" msg_info "Configuring RAG API" cat </opt/rag-api/.env VECTOR_DB_TYPE=pgvector DB_HOST=127.0.0.1 DB_PORT=5432 POSTGRES_DB=${PG_DB_NAME} POSTGRES_USER=${PG_DB_USER} POSTGRES_PASSWORD=${PG_DB_PASS} RAG_HOST=0.0.0.0 RAG_PORT=8000 JWT_SECRET=${JWT_SECRET} RAG_UPLOAD_DIR=/opt/rag-api/uploads/ EOF msg_ok "Configured RAG API" msg_info "Creating Services" cat </etc/systemd/system/librechat.service [Unit] Description=LibreChat After=network.target mongod.service meilisearch.service rag-api.service [Service] Type=simple User=root WorkingDirectory=/opt/librechat EnvironmentFile=/opt/librechat/.env ExecStart=/usr/bin/npm run backend Restart=on-failure RestartSec=5 [Install] WantedBy=multi-user.target EOF cat </etc/systemd/system/rag-api.service [Unit] Description=LibreChat RAG API After=network.target postgresql.service [Service] Type=simple User=root WorkingDirectory=/opt/rag-api EnvironmentFile=/opt/rag-api/.env ExecStart=/opt/rag-api/.venv/bin/uvicorn main:app --host 0.0.0.0 --port 8000 Restart=on-failure RestartSec=5 [Install] WantedBy=multi-user.target EOF systemctl enable -q --now rag-api librechat msg_ok "Created Services" motd_ssh customize cleanup_lxc ================================================ FILE: install/librenms-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: michelroegl-brunner # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.librenms.org/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ acl \ fping \ graphviz \ imagemagick \ mtr-tiny \ nginx \ nmap \ rrdtool \ snmp \ snmpd \ whois msg_ok "Installed Dependencies" msg_info "Installing Python Dependencies" $STD apt install -y \ python3-dotenv \ python3-pymysql \ python3-redis \ python3-setuptools \ python3-systemd \ python3-pip \ python3-psutil \ python3-command-runner msg_ok "Installed Python Dependencies" PHP_VERSION="8.4" PHP_FPM="YES" PHP_MODULE="cli,snmp,gmp" setup_php setup_mariadb setup_composer PYTHON_VERSION="3.13" setup_uv MARIADB_DB_NAME="librenms" MARIADB_DB_USER="librenms" MARIADB_DB_PASS="$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | head -c13)" setup_mariadb_db fetch_and_deploy_gh_release "librenms" "librenms/librenms" "tarball" msg_info "Configuring LibreNMS" $STD useradd librenms -d /opt/librenms -M -r -s "$(which bash)" mkdir -p /opt/librenms/{rrd,logs,bootstrap/cache,storage,html} cd /opt/librenms APP_KEY=$(openssl rand -base64 40 | tr -dc 'a-zA-Z0-9') $STD uv venv --clear .venv $STD source .venv/bin/activate $STD uv pip install -r requirements.txt cat </opt/librenms/.env DB_DATABASE=${MARIADB_DB_NAME} DB_USERNAME=${MARIADB_DB_USER} DB_PASSWORD=${MARIADB_DB_PASS} APP_KEY=${APP_KEY} EOF chown -R librenms:librenms /opt/librenms chmod 771 /opt/librenms chmod -R ug=rwX /opt/librenms/bootstrap/cache /opt/librenms/storage /opt/librenms/logs /opt/librenms/rrd msg_ok "Configured LibreNMS" msg_info "Configure MariaDB" sed -i "/\[mariadb\]/a innodb_file_per_table=1\nlower_case_table_names=0" /etc/mysql/mariadb.conf.d/50-server.cnf systemctl enable -q --now mariadb msg_ok "Configured MariaDB" msg_info "Configure PHP-FPM" cp /etc/php/8.4/fpm/pool.d/www.conf /etc/php/8.4/fpm/pool.d/librenms.conf sed -i "s/\[www\]/\[librenms\]/g" /etc/php/8.4/fpm/pool.d/librenms.conf sed -i "s/user = www-data/user = librenms/g" /etc/php/8.4/fpm/pool.d/librenms.conf sed -i "s/group = www-data/group = librenms/g" /etc/php/8.4/fpm/pool.d/librenms.conf sed -i "s/listen = \/run\/php\/php8.4-fpm.sock/listen = \/run\/php-fpm-librenms.sock/g" /etc/php/8.4/fpm/pool.d/librenms.conf msg_ok "Configured PHP-FPM" msg_info "Configure Nginx" cat </etc/nginx/sites-enabled/librenms server { listen 80; server_name ${LOCAL_IP}; root /opt/librenms/html; index index.php; charset utf-8; gzip on; gzip_types text/css application/javascript text/javascript application/x-javascript image/svg+xml text/plain text/xsd text/xsl text/xml image/x-icon; location / { try_files \$uri \$uri/ /index.php?\$query_string; } location ~ [^/]\.php(/|$) { fastcgi_pass unix:/run/php-fpm-librenms.sock; fastcgi_split_path_info ^(.+\.php)(/.+)$; include fastcgi.conf; } location ~ /\.(?!well-known).* { deny all; } } EOF rm /etc/nginx/sites-enabled/default $STD systemctl reload nginx systemctl restart php8.4-fpm msg_ok "Configured Nginx" msg_info "Configure Services" ln -s /opt/librenms/lnms /usr/bin/lnms mkdir -p /etc/bash_completion.d/ cp /opt/librenms/misc/lnms-completion.bash /etc/bash_completion.d/ cp /opt/librenms/snmpd.conf.example /etc/snmp/snmpd.conf APP_PASSWORD=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | head -c13) APP_USER="admin" { echo "LibreNMS Credentials" echo "Username: ${APP_USER}" echo "Password: ${APP_PASSWORD}" } >>~/librenms.creds $STD su - librenms -s /bin/bash -c "cd /opt/librenms && COMPOSER_ALLOW_SUPERUSER=1 composer install --no-dev" $STD su - librenms -s /bin/bash -c "cd /opt/librenms && php8.4 artisan migrate --force" $STD su - librenms -s /bin/bash -c "cd /opt/librenms && php8.4 artisan key:generate --force" $STD su - librenms -s /bin/bash -c "cd /opt/librenms && lnms db:seed --force" $STD su - librenms -s /bin/bash -c "cd /opt/librenms && lnms user:add -p ${APP_PASSWORD} ${APP_USER} --role=admin" RANDOM_STRING=$(openssl rand -base64 16 | tr -dc 'a-zA-Z0-9') sed -i "s/RANDOMSTRINGHERE/$RANDOM_STRING/g" /etc/snmp/snmpd.conf echo "SNMP Community String: $RANDOM_STRING" >>~/librenms.creds curl -qso /usr/bin/distro https://raw.githubusercontent.com/librenms/librenms-agent/master/snmp/distro chmod +x /usr/bin/distro systemctl enable -q --now snmpd cp /opt/librenms/dist/librenms.cron /etc/cron.d/librenms cp /opt/librenms/dist/librenms-scheduler.service /opt/librenms/dist/librenms-scheduler.timer /etc/systemd/system/ systemctl enable -q --now librenms-scheduler.timer cp /opt/librenms/misc/librenms.logrotate /etc/logrotate.d/librenms msg_ok "Configured Services" motd_ssh customize cleanup_lxc ================================================ FILE: install/librespeed-rust-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: Joseph Stubberfield (stubbers) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/librespeed/speedtest-rust source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os fetch_and_deploy_gh_release "librespeed-rust" "librespeed/speedtest-rust" "binary" "latest" "/opt/librespeed-rust" "librespeed-rs-aarch64-unknown-linux-gnu.deb" msg_info "Enabling Service" systemctl enable -q --now speedtest_rs msg_ok "Enabled Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/libretranslate-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: Slaviša Arežina (tremor021) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/LibreTranslate/LibreTranslate source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os setup_hwaccel msg_info "Installing dependencies" $STD apt install -y \ pkg-config \ build-essential \ g++ \ cmake \ libprotobuf-dev \ protobuf-compiler \ libsentencepiece-dev \ libicu-dev msg_ok "Installed dependencies" msg_info "Setup Python3" $STD apt install -y \ python3-pip \ python3-dev \ python3-icu rm -rf /usr/lib/python3.*/EXTERNALLY-MANAGED msg_ok "Setup Python3" PYTHON_VERSION="3.12" setup_uv fetch_and_deploy_gh_release "libretranslate" "LibreTranslate/LibreTranslate" "tarball" msg_info "Setup LibreTranslate (Patience)" cd /opt/libretranslate $STD uv venv --clear .venv --python 3.12 $STD source .venv/bin/activate $STD uv pip install --upgrade pip $STD uv pip install "setuptools<81" $STD uv pip install Babel==2.12.1 $STD .venv/bin/python scripts/compile_locales.py $STD uv pip install "numpy<2" $STD uv pip install . $STD uv pip install libretranslate $STD .venv/bin/python scripts/install_models.py cat </opt/libretranslate/.env LT_PORT=5000 EOF msg_ok "Installed LibreTranslate" msg_info "Creating Service" cat </etc/systemd/system/libretranslate.service [Unit] Description=LibreTranslate After=network.target [Service] User=root Type=idle Restart=always Environment="PATH=/usr/local/lib/python3.11/dist-packages/libretranslate" EnvironmentFile=/opt/libretranslate/.env ExecStart=/opt/libretranslate/.venv/bin/python3 /opt/libretranslate/.venv/bin/libretranslate --host * --update-models ExecReload=/bin/kill -s HUP KillMode=mixed TimeoutStopSec=1 [Install] WantedBy=multi-user.target EOF systemctl enable -q --now libretranslate msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/lidarr-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://lidarr.audio/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ sqlite3 \ libchromaprint-tools \ mediainfo \ libicu76 msg_ok "Installed Dependencies" fetch_and_deploy_gh_release "lidarr" "Lidarr/Lidarr" "prebuild" "latest" "/opt/Lidarr" "Lidarr.master*linux-core-arm64.tar.gz" msg_info "Configuring Lidarr" mkdir -p /var/lib/lidarr/ chmod 775 /var/lib/lidarr/ chmod 775 /opt/Lidarr msg_ok "Configured Lidarr" msg_info "Creating Service" cat </etc/systemd/system/lidarr.service [Unit] Description=Lidarr Daemon After=syslog.target network.target [Service] UMask=0002 Type=simple ExecStart=/opt/Lidarr/Lidarr -nobrowser -data=/var/lib/lidarr/ TimeoutStopSec=20 KillMode=process Restart=on-failure [Install] WantedBy=multi-user.target EOF systemctl enable -q --now lidarr msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/limesurvey-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: Slaviša Arežina (tremor021) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://community.limesurvey.org/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os PHP_VERSION="8.3" PHP_APACHE="YES" PHP_FPM="YES" PHP_MODULE="imap,ldap" setup_php setup_mariadb msg_info "Configuring MariaDB Database" DB_NAME=limesurvey_db DB_USER=limesurvey DB_PASS=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | head -c13) $STD mariadb -u root -e "CREATE DATABASE $DB_NAME CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;" $STD mariadb -u root -e "CREATE USER '$DB_USER'@'localhost' IDENTIFIED BY '$DB_PASS';" $STD mariadb -u root -e "GRANT ALL ON $DB_NAME.* TO '$DB_USER'@'localhost'; FLUSH PRIVILEGES;" { echo "LimeSurvey-Credentials" echo "LimeSurvey Database User: $DB_USER" echo "LimeSurvey Database Password: $DB_PASS" echo "LimeSurvey Database Name: $DB_NAME" } >>~/limesurvey.creds msg_ok "Configured MariaDB Database" msg_info "Setting up LimeSurvey" temp_file=$(mktemp) RELEASE=$(curl -s https://community.limesurvey.org/downloads/ | grep -oE 'https://download\.limesurvey\.org/latest-master/limesurvey[0-9.+]+\.zip' | head -n1) curl -fsSL "$RELEASE" -o "$temp_file" unzip -q "$temp_file" -d /opt cat </etc/apache2/sites-enabled/000-default.conf ServerAdmin webmaster@localhost DocumentRoot /opt/limesurvey DirectoryIndex index.php index.html index.cgi index.pl index.xhtml Options +ExecCGI Options FollowSymLinks Require all granted AllowOverride All Require all granted ErrorLog /var/log/apache2/error.log CustomLog /var/log/apache2/access.log combined EOF chown -R www-data:www-data "/opt/limesurvey" chmod -R 750 "/opt/limesurvey" systemctl reload apache2 rm -rf "$temp_file" msg_ok "Set up LimeSurvey" motd_ssh customize cleanup_lxc ================================================ FILE: install/linkding-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (MickLesk) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://linkding.link/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ build-essential \ pkg-config \ python3-dev \ nginx \ libpq-dev \ libicu-dev \ libsqlite3-dev \ libffi-dev msg_ok "Installed Dependencies" NODE_VERSION="22" setup_nodejs setup_uv fetch_and_deploy_gh_release "linkding" "sissbruecker/linkding" "tarball" msg_info "Building Frontend" cd /opt/linkding $STD npm ci $STD npm run build ln -sf /usr/lib/aarch64-linux-gnu/mod_icu.so /opt/linkding/libicu.so msg_ok "Built Frontend" msg_info "Setting up LinkDing" rm -f bookmarks/settings/dev.py touch bookmarks/settings/custom.py $STD uv sync --no-dev --frozen $STD uv pip install gunicorn mkdir -p data/{favicons,previews,assets} ADMIN_PASS=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | cut -c1-13) cat </opt/linkding/.env LD_SUPERUSER_NAME=admin LD_SUPERUSER_PASSWORD=${ADMIN_PASS} LD_CSRF_TRUSTED_ORIGINS=http://${LOCAL_IP}:9090 EOF set -a && source /opt/linkding/.env && set +a $STD /opt/linkding/.venv/bin/python manage.py generate_secret_key $STD /opt/linkding/.venv/bin/python manage.py migrate $STD /opt/linkding/.venv/bin/python manage.py enable_wal $STD /opt/linkding/.venv/bin/python manage.py create_initial_superuser $STD /opt/linkding/.venv/bin/python manage.py collectstatic --no-input msg_ok "Set up LinkDing" msg_info "Creating Services" cat </etc/systemd/system/linkding.service [Unit] Description=linkding Bookmark Manager After=network.target [Service] User=root WorkingDirectory=/opt/linkding EnvironmentFile=/opt/linkding/.env ExecStart=/opt/linkding/.venv/bin/gunicorn \ --bind 127.0.0.1:8000 \ --workers 3 \ --threads 2 \ --timeout 120 \ bookmarks.wsgi:application Restart=on-failure RestartSec=5 [Install] WantedBy=multi-user.target EOF cat </etc/systemd/system/linkding-tasks.service [Unit] Description=linkding Background Tasks After=network.target [Service] User=root WorkingDirectory=/opt/linkding EnvironmentFile=/opt/linkding/.env ExecStart=/opt/linkding/.venv/bin/python manage.py run_huey Restart=on-failure RestartSec=5 [Install] WantedBy=multi-user.target EOF cat <<'EOF' >/etc/nginx/sites-available/linkding server { listen 9090; server_name _; client_max_body_size 20M; location /static/ { alias /opt/linkding/static/; expires 30d; } location / { proxy_pass http://127.0.0.1:8000; proxy_set_header Host $host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_redirect off; } } EOF $STD rm -f /etc/nginx/sites-enabled/default $STD ln -sf /etc/nginx/sites-available/linkding /etc/nginx/sites-enabled/linkding systemctl enable -q --now nginx linkding linkding-tasks systemctl restart nginx msg_ok "Created Services" motd_ssh customize cleanup_lxc ================================================ FILE: install/linkstack-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: Omar Minaya | MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://linkstack.org/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os PHP_VERSION="8.3" PHP_APACHE="YES" setup_php fetch_and_deploy_gh_release "linkstack" "linkstackorg/linkstack" "prebuild" "latest" "/var/www/html/" "linkstack.zip" msg_info "Configuring LinkStack" $STD a2enmod rewrite chown -R www-data:www-data /var/www/html/linkstack chmod -R 755 /var/www/html/linkstack cat </etc/apache2/sites-available/linkstack.conf ServerAdmin webmaster@localhost DocumentRoot /var/www/html/linkstack ErrorLog /var/log/apache2/linkstack-error.log CustomLog /var/log/apache2/linkstack-access.log combined Options Indexes FollowSymLinks AllowOverride All Require all granted EOF $STD a2dissite 000-default.conf $STD a2ensite linkstack.conf $STD systemctl restart apache2 msg_ok "Configured LinkStack" motd_ssh customize cleanup_lxc ================================================ FILE: install/linkwarden-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (Canbiz) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://linkwarden.app/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ make \ build-essential msg_ok "Installed Dependencies" NODE_VERSION="22" setup_nodejs PG_VERSION="16" setup_postgresql RUST_CRATES="monolith" setup_rust PG_DB_NAME="linkwardendb" PG_DB_USER="linkwarden" setup_postgresql_db read -r -p "${TAB3}Would you like to add Adminer? " prompt if [[ "${prompt,,}" =~ ^(y|yes)$ ]]; then setup_adminer fi fetch_and_deploy_gh_release "linkwarden" "linkwarden/linkwarden" "tarball" msg_info "Installing Linkwarden (Patience)" SECRET_KEY="$(head /dev/urandom | tr -dc A-Za-z0-9 | head -c 32)" echo "Linkwarden Secret: $SECRET_KEY" >>"${HOME}/linkwarden.creds" cd /opt/linkwarden yarn_ver="4.12.0" if [[ -f package.json ]]; then pkg_manager=$(jq -r '.packageManager // empty' package.json 2>/dev/null || true) if [[ -n "$pkg_manager" && "$pkg_manager" == yarn@* ]]; then yarn_spec="${pkg_manager#yarn@}" yarn_ver="${yarn_spec%%+*}" fi fi if command -v corepack >/dev/null 2>&1; then $STD corepack enable $STD corepack prepare "yarn@${yarn_ver}" --activate || true fi $STD yarn $STD npx playwright install-deps $STD npx playwright install cat </opt/linkwarden/.env NEXTAUTH_SECRET=${SECRET_KEY} NEXTAUTH_URL=http://${LOCAL_IP}:3000 DATABASE_URL=postgresql://${PG_DB_USER}:${PG_DB_PASS}@localhost:5432/${PG_DB_NAME} EOF $STD yarn prisma:generate $STD yarn web:build $STD yarn prisma:deploy rm -rf ~/.cargo/registry ~/.cargo/git ~/.cargo/.package-cache rm -rf /root/.cache/yarn rm -rf /opt/linkwarden/.next/cache msg_ok "Installed Linkwarden" msg_info "Creating Service" cat </etc/systemd/system/linkwarden.service [Unit] Description=Linkwarden Service After=network.target [Service] Type=exec Environment=PATH=$PATH WorkingDirectory=/opt/linkwarden ExecStart=/usr/bin/yarn concurrently:start [Install] WantedBy=multi-user.target EOF systemctl enable -q --now linkwarden msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/listmonk-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: bvdberg01 # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://listmonk.app/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os PG_VERSION="17" setup_postgresql msg_info "Configuring PostgreSQL" DB_NAME=listmonk DB_USER=listmonk DB_PASS=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | cut -c1-13) $STD sudo -u postgres psql -c "CREATE ROLE $DB_USER WITH LOGIN PASSWORD '$DB_PASS';" $STD sudo -u postgres psql -c "CREATE DATABASE $DB_NAME WITH OWNER $DB_USER TEMPLATE template0;" { echo "listmonk-Credentials" echo -e "listmonk Database User: \e[32m$DB_USER\e[0m" echo -e "listmonk Database Password: \e[32m$DB_PASS\e[0m" echo -e "listmonk Database Name: \e[32m$DB_NAME\e[0m" } >>~/listmonk.creds msg_ok "Configured PostgreSQL" fetch_and_deploy_gh_release "listmonk" "knadh/listmonk" "prebuild" "latest" "/opt/listmonk" "listmonk*linux_arm64.tar.gz" msg_info "Configuring listmonk" mkdir -p /opt/listmonk/uploads $STD /opt/listmonk/listmonk --new-config --config /opt/listmonk/config.toml sed -i -e 's/address = "localhost:9000"/address = "0.0.0.0:9000"/' -e 's/^password = ".*"/password = "'"$DB_PASS"'"/' /opt/listmonk/config.toml $STD /opt/listmonk/listmonk --install --yes --config /opt/listmonk/config.toml msg_ok "Configured listmonk" msg_info "Creating Service" cat </etc/systemd/system/listmonk.service [Unit] Description=Listmonk Service Wants=network.target After=postgresql.service [Service] Type=simple ExecStart=/opt/listmonk/listmonk --config /opt/listmonk/config.toml Restart=always RestartSec=3 WorkingDirectory=/opt/listmonk [Install] WantedBy=multi-user.target EOF systemctl enable -q --now listmonk msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/livebook-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: dkuku # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/livebook-dev/livebook source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt-get install -y \ build-essential \ ca-certificates \ cmake \ git \ libncurses5-dev msg_ok "Installed Dependencies" msg_info "Creating livebook user" mkdir -p /opt/livebook /data export HOME=/opt/livebook $STD adduser --system --group --home /opt/livebook --shell /bin/bash livebook msg_ok "Created livebook user" msg_warn "WARNING: This script will run an external installer from a third-party source (https://elixir-lang.org)." msg_warn "The following code is NOT maintained or audited by our repository." msg_warn "If you have any doubts or concerns, please review the installer code before proceeding:" msg_custom "${TAB3}${GATEWAY}${BGN}${CL}" "\e[1;34m" "→ https://elixir-lang.org/install.sh" echo read -r -p "${TAB3}Do you want to continue? [y/N]: " CONFIRM if [[ ! "$CONFIRM" =~ ^([yY][eE][sS]|[yY])$ ]]; then msg_error "Aborted by user. No changes have been made." exit 10 fi curl -fsSO https://elixir-lang.org/install.sh $STD sh install.sh elixir@latest otp@latest msg_info "Setup Erlang and Elixir" ERLANG_VERSION=$(ls /opt/livebook/.elixir-install/installs/otp/ | head -n1) ELIXIR_VERSION=$(ls /opt/livebook/.elixir-install/installs/elixir/ | head -n1) LIVEBOOK_PASSWORD=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | head -c16) export ERLANG_BIN="/opt/livebook/.elixir-install/installs/otp/$ERLANG_VERSION/bin" export ELIXIR_BIN="/opt/livebook/.elixir-install/installs/elixir/$ELIXIR_VERSION/bin" export PATH="$ERLANG_BIN:$ELIXIR_BIN:$PATH" $STD mix local.hex --force $STD mix local.rebar --force $STD mix escript.install hex livebook --force cat </opt/livebook/.env export HOME=/opt/livebook export ERLANG_VERSION=$ERLANG_VERSION export ELIXIR_VERSION=$ELIXIR_VERSION export LIVEBOOK_PORT=8080 export LIVEBOOK_IP="::" export LIVEBOOK_HOME=/data export LIVEBOOK_PASSWORD="$LIVEBOOK_PASSWORD" export ESCRIPTS_BIN=/opt/livebook/.mix/escripts export ERLANG_BIN="/opt/livebook/.elixir-install/installs/otp/\${ERLANG_VERSION}/bin" export ELIXIR_BIN="/opt/livebook/.elixir-install/installs/elixir/\${ELIXIR_VERSION}/bin" export PATH="\$ESCRIPTS_BIN:\$ERLANG_BIN:\$ELIXIR_BIN:\$PATH" EOF { echo "Livebook-Credentials" echo "Livebook Password: $LIVEBOOK_PASSWORD" } >>~/livebook.creds msg_ok "Installed Erlang $ERLANG_VERSION and Elixir $ELIXIR_VERSION" msg_info "Installing Livebook" cat </etc/systemd/system/livebook.service [Unit] Description=Livebook After=network.target [Service] Type=exec User=livebook Group=livebook WorkingDirectory=/data EnvironmentFile=-/opt/livebook/.env ExecStart=/bin/bash -c 'source /opt/livebook/.env && cd /opt/livebook && livebook server' Restart=always RestartSec=5 [Install] WantedBy=multi-user.target EOF chown -R livebook:livebook /opt/livebook /data systemctl enable -q --now livebook msg_ok "Installed Livebook" motd_ssh customize cleanup_lxc ================================================ FILE: install/lldap-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # Co-Author: remz1337 # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/lldap/lldap source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing lldap" source /etc/os-release os=$ID if [ "$os" == "ubuntu" ]; then DISTRO="xUbuntu" else DISTRO="${os^}" fi curl -fsSL https://download.opensuse.org/repositories/home:Masgalor:LLDAP/${DISTRO}_${VERSION_ID}/Release.key | gpg --dearmor >/usr/share/keyrings/home_Masgalor_LLDAP.gpg cat </etc/apt/sources.list.d/home:Masgalor:LLDAP.sources Types: deb URIs: http://download.opensuse.org/repositories/home:/Masgalor:/LLDAP/${DISTRO}_${VERSION_ID}/ Suites: / Signed-By: /usr/share/keyrings/home_Masgalor_LLDAP.gpg EOF $STD apt update $STD apt install -y lldap systemctl enable -q --now lldap msg_ok "Installed lldap" motd_ssh customize cleanup_lxc ================================================ FILE: install/loki-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2025 community-scripts ORG # Author: bysinka-95 # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/grafana/loki source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os setup_deb822_repo \ "grafana" \ "https://apt.grafana.com/gpg.key" \ "https://apt.grafana.com" \ "stable" \ "main" msg_info "Installing Loki" $STD apt install -y loki mkdir -p /var/lib/loki/{chunks,boltdb-shipper-active,boltdb-shipper-cache} chown -R loki /var/lib/loki cat </etc/loki/config.yml auth_enabled: false server: http_listen_port: 3100 log_level: info common: instance_addr: 127.0.0.1 path_prefix: /var/lib/loki storage: filesystem: chunks_directory: /var/lib/loki/chunks rules_directory: /var/lib/loki/rules replication_factor: 1 ring: kvstore: store: inmemory schema_config: configs: - from: 2020-10-24 store: tsdb object_store: filesystem schema: v13 index: prefix: index_ period: 24h query_range: results_cache: cache: embedded_cache: enabled: true max_size_mb: 100 limits_config: metric_aggregation_enabled: true ruler: alertmanager_url: http://localhost:9093 EOF chown loki /etc/loki/config.yml systemctl enable -q --now loki msg_ok "Installed Loki" read -rp "Would you like to install Promtail? (y/N): " INSTALL_PROMTAIL if [[ "${INSTALL_PROMTAIL,,}" =~ ^(y|yes)$ ]]; then msg_info "Installing Promtail" $STD apt install -y promtail systemctl enable -q --now promtail msg_ok "Installed Promtail" fi motd_ssh customize cleanup_lxc ================================================ FILE: install/lubelogger-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: kristocopani # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://lubelogger.com/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os fetch_and_deploy_gh_release "lubelogger" "hargata/lubelog" "prebuild" "latest" "/opt/lubelogger" "LubeLogger*linux_x64.zip" msg_info "Configuring LubeLogger" cd /opt/lubelogger chmod 700 /opt/lubelogger/CarCareTracker cp /opt/lubelogger/appsettings.json /opt/lubelogger/appsettings_bak.json jq '.Kestrel = {"Endpoints": {"Http": {"Url": "http://0.0.0.0:5000"}}}' /opt/lubelogger/appsettings_bak.json >/opt/lubelogger/appsettings.json rm -rf /opt/lubelogger/appsettings_bak.json msg_ok "Configured LubeLogger" msg_info "Creating Service" cat </etc/systemd/system/lubelogger.service [Unit] Description=LubeLogger Daemon After=network.target [Service] User=root Type=simple WorkingDirectory=/opt/lubelogger ExecStart=/opt/lubelogger/CarCareTracker TimeoutStopSec=20 KillMode=process Restart=on-failure [Install] WantedBy=multi-user.target EOF systemctl enable -q --now lubelogger msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/lychee-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/LycheeOrg/Lychee source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ caddy \ libimage-exiftool-perl \ jpegoptim msg_ok "Installed Dependencies" PHP_VERSION="8.4" PHP_FPM="YES" PHP_MODULE="bcmath,ldap,exif,gd,intl,imagick,redis,zip,pdo_pgsql,pcntl" setup_php PG_VERSION="16" setup_postgresql PG_DB_NAME="lychee" PG_DB_USER="lychee" setup_postgresql_db setup_ffmpeg setup_imagemagick fetch_and_deploy_gh_release "lychee" "LycheeOrg/Lychee" "prebuild" "latest" "/opt/lychee" "Lychee.zip" msg_info "Configuring Application" cd /opt/lychee cp .env.example .env APP_KEY=$($STD php artisan key:generate --show) sed -i "s|^APP_KEY=.*|APP_KEY=${APP_KEY}|" .env sed -i "s|^APP_ENV=.*|APP_ENV=production|" .env sed -i "s|^APP_DEBUG=.*|APP_DEBUG=false|" .env sed -i "s|^APP_URL=.*|APP_URL=http://${LOCAL_IP}|" .env sed -i "s|^DB_CONNECTION=.*|DB_CONNECTION=pgsql|" .env sed -i "s|^DB_HOST=.*|DB_HOST=127.0.0.1|" .env sed -i "s|^DB_PORT=.*|DB_PORT=5432|" .env sed -i "s|^#\?DB_DATABASE=.*|DB_DATABASE=${PG_DB_NAME}|" .env sed -i "s|^DB_USERNAME=.*|DB_USERNAME=${PG_DB_USER}|" .env sed -i "s|^DB_PASSWORD=.*|DB_PASSWORD=${PG_DB_PASS}|" .env mkdir -p storage/framework/{cache,sessions,views} storage/logs bootstrap/cache public/dist public/uploads public/sym touch public/dist/user.css public/dist/custom.js chmod -R 775 storage bootstrap/cache public/dist public/uploads public/sym msg_ok "Configured Application" msg_info "Running Database Migrations" cd /opt/lychee $STD php artisan migrate --force msg_ok "Ran Database Migrations" chown -R www-data:www-data /opt/lychee msg_info "Configuring Caddy" PHP_VER=$(php -r 'echo PHP_MAJOR_VERSION . "." . PHP_MINOR_VERSION;') cat </etc/caddy/Caddyfile :80 { root * /opt/lychee/public php_fastcgi unix//run/php/php${PHP_VER}-fpm.sock file_server encode gzip } EOF usermod -aG www-data caddy msg_ok "Configured Caddy" systemctl enable -q --now php${PHP_VER}-fpm systemctl restart caddy motd_ssh customize cleanup_lxc ================================================ FILE: install/lyrionmusicserver-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: Omar Minaya # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://lyrion.org/getting-started/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Setup Lyrion Music Server" DEB_URL=$(curl -fsSL 'https://lyrion.org/getting-started/' | grep -oP ']*href="\K[^"]*arm\.deb(?="[^>]*>)' | head -n 1) RELEASE=$(echo "$DEB_URL" | grep -oP 'lyrionmusicserver_\K[0-9.]+(?=_arm\.deb)') DEB_FILE="/tmp/lyrionmusicserver_${RELEASE}_arm.deb" curl -fsSL -o "$DEB_FILE" "$DEB_URL" $STD apt install "$DEB_FILE" -y rm -f "$DEB_FILE" echo "${RELEASE}" >"/opt/lyrion_version.txt" msg_ok "Setup Lyrion Music Server v${RELEASE}" motd_ssh customize cleanup_lxc ================================================ FILE: install/mafl-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://mafl.hywax.space/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ ca-certificates \ build-essential msg_ok "Installed Dependencies" NODE_VERSION="22" NODE_MODULE="yarn@latest" setup_nodejs fetch_and_deploy_gh_release "mafl" "hywax/mafl" "tarball" msg_info "Installing Mafl" mkdir -p /opt/mafl/data curl -fsSL "https://raw.githubusercontent.com/hywax/mafl/main/.example/config.yml" -o "/opt/mafl/data/config.yml" cd /opt/mafl export NUXT_TELEMETRY_DISABLED=true $STD yarn install $STD yarn build msg_ok "Installed Mafl" msg_info "Creating Service" cat </etc/systemd/system/mafl.service [Unit] Description=Mafl After=network.target StartLimitIntervalSec=0 [Service] Type=simple Restart=always RestartSec=1 User=root WorkingDirectory=/opt/mafl/ ExecStart=yarn preview [Install] WantedBy=multi-user.target EOF systemctl enable -q --now mafl msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/magicmirror-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) | Co-Author Slaviša Arežina (tremor021) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://magicmirror.builders/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os NODE_VERSION="24" setup_nodejs fetch_and_deploy_gh_release "magicmirror" "MagicMirrorOrg/MagicMirror" "tarball" msg_info "Configuring MagicMirror" cd /opt/magicmirror sed -i -E 's/("postinstall": )".*"/\1""/; s/("prepare": )".*"/\1""/' package.json $STD npm run install-mm cat </opt/magicmirror/config/config.js let config = { address: "0.0.0.0", port: 8080, basePath: "/", ipWhitelist: [], useHttps: false, httpsPrivateKey: "", httpsCertificate: "", language: "en", locale: "en-US", logLevel: ["INFO", "LOG", "WARN", "ERROR"], timeFormat: 24, units: "metric", serverOnly: true, modules: [ { module: "alert", }, { module: "updatenotification", position: "top_bar" }, { module: "clock", position: "top_left" }, { module: "calendar", header: "US Holidays", position: "top_left", config: { calendars: [ { symbol: "calendar-check", url: "webcal://www.calendarlabs.com/ical-calendar/ics/76/US_Holidays.ics" } ] } }, { module: "compliments", position: "lower_third" }, { module: "weather", position: "top_right", config: { weatherProvider: "openweathermap", type: "current", location: "New York", locationID: "5128581", //ID from http://bulk.openweathermap.org/sample/city.list.json.gz; unzip the gz file and find your city apiKey: "YOUR_OPENWEATHER_API_KEY" } }, { module: "weather", position: "top_right", header: "Weather Forecast", config: { weatherProvider: "openweathermap", type: "forecast", location: "New York", locationID: "5128581", //ID from http://bulk.openweathermap.org/sample/city.list.json.gz; unzip the gz file and find your city apiKey: "YOUR_OPENWEATHER_API_KEY" } }, { module: "newsfeed", position: "bottom_bar", config: { feeds: [ { title: "New York Times", url: "https://rss.nytimes.com/services/xml/rss/nyt/HomePage.xml" } ], showSourceTitle: true, showPublishDate: true, broadcastNewsFeeds: true, broadcastNewsUpdates: true } }, ] }; /*************** DO NOT EDIT THE LINE BELOW ***************/ if (typeof module !== "undefined") {module.exports = config;} EOF msg_ok "Configured MagicMirror" msg_info "Creating Service" cat </etc/systemd/system/magicmirror.service [Unit] Description=Magic Mirror After=network.target StartLimitIntervalSec=0 [Service] Type=simple Restart=always RestartSec=1 User=root WorkingDirectory=/opt/magicmirror/ ExecStart=/usr/bin/npm run server [Install] WantedBy=multi-user.target EOF systemctl enable -q --now magicmirror msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/mail-archiver-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: Slaviša Arežina (tremor021) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/s1t5/mail-archiver source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" curl -SL -o dotnet.tar.gz https://builds.dotnet.microsoft.com/dotnet/Sdk/10.0.101/dotnet-sdk-10.0.101-linux-arm64.tar.gz curl -SL -o aspnet.tar.gz https://builds.dotnet.microsoft.com/dotnet/aspnetcore/Runtime/10.0.1/aspnetcore-runtime-10.0.1-linux-arm64.tar.gz $STD mkdir -p /usr/share/dotnet $STD tar -zxf dotnet.tar.gz -C /usr/share/dotnet $STD tar -zxf aspnet.tar.gz -C /usr/share/dotnet ln -s /usr/share/dotnet/dotnet /usr/bin/dotnet msg_ok "Installed Dependencies" PG_VERSION="17" setup_postgresql PG_DB_NAME="mailarchiver_db" PG_DB_USER="mailarchiver" setup_postgresql_db fetch_and_deploy_gh_release "mail-archiver" "s1t5/mail-archiver" "tarball" msg_info "Setting up Mail-Archiver" mv /opt/mail-archiver /opt/mail-archiver-build cd /opt/mail-archiver-build $STD dotnet restore $STD dotnet publish -c Release -o /opt/mail-archiver cp /opt/mail-archiver-build/appsettings.json /opt/mail-archiver/appsettings.json sed -i "s|\"DefaultConnection\": \"[^\"]*\"|\"DefaultConnection\": \"Host=localhost;Database=mailarchiver_db;Username=mailarchiver;Password=$PG_DB_PASS\"|" /opt/mail-archiver/appsettings.json rm -rf /opt/mail-archiver-build cat </opt/mail-archiver/.env ASPNETCORE_URLS=http://+:5000 ASPNETCORE_ENVIRONMENT=Production TZ=UTC EOF msg_ok "Setup Mail-Archiver" msg_info "Creating Service" cat </etc/systemd/system/mail-archiver.service [Unit] Description=Mail-Archiver Service After=network.target [Service] EnvironmentFile=/opt/mail-archiver/.env WorkingDirectory=/opt/mail-archiver ExecStart=/usr/bin/dotnet MailArchiver.dll Restart=always [Install] WantedBy=multi-user.target EOF systemctl enable -q --now mail-archiver msg_info "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/managemydamnlife-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: vhsdream # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/intri-in/manage-my-damn-life-nextjs source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing dependencies" $STD apt install -y build-essential msg_ok "Installed dependencies" NODE_VERSION="22" setup_nodejs setup_mariadb msg_info "Setting up Database" DB_NAME="mmdl" DB_USER="mmdl" DB_PASS=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | head -c13) $STD mariadb -u root -e "CREATE DATABASE $DB_NAME;" $STD mariadb -u root -e "CREATE USER '$DB_USER'@'localhost' IDENTIFIED by '$DB_PASS';" $STD mariadb -u root -e "GRANT ALL ON $DB_NAME.* TO '$DB_USER'@'localhost'; FLUSH PRIVILEGES;" { echo "Manage My Damn Life Credentials" echo "Database User: $DB_USER" echo "Database Password: $DB_PASS" echo "Database Name: $DB_NAME" } >>~/mmdl.creds msg_ok "Set up Database" fetch_and_deploy_gh_release "mmdl" "intri-in/manage-my-damn-life-nextjs" "tarball" msg_info "Configuring ${APPLICATION}" cp /opt/mmdl/sample.env.local /opt/mmdl/.env sed -i -e 's|db|localhost|' \ -e "s|myuser|${DB_USER}|" \ -e "s|mypassword|${DB_PASS}|" \ -e 's|5433|3306|' \ -e 's|DB_DIALECT=postgres|DB_DIALECT=mysql|' \ -e "s|sample_install_mmdm|${DB_NAME}|" \ -e "s|=PASSWORD|=$(openssl rand -base64 40 | tr -dc 'a-zA-Z0-9' | head -c40)|" \ /opt/mmdl/.env cd /opt/mmdl export NEXT_TELEMETRY_DISABLED=1 export CI="true" $STD npm install $STD npm run migrate $STD npm run build msg_ok "Configured ${APPLICATION}" msg_info "Creating Service" cat </etc/systemd/system/mmdl.service [Unit] Description=${APPLICATION} Service After=network.target mariadb.service [Service] WorkingDirectory=/opt/mmdl EnvironmentFile=/opt/mmdl/.env ExecStart=/usr/bin/npm run start Restart=on-abnormal [Install] WantedBy=multi-user.target EOF systemctl enable -q --now mmdl msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/manyfold-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: bvdberg01 | Co-Author: SunFlowerOwl # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/manyfold3d/manyfold source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ libarchive-dev \ git \ libmariadb-dev \ redis-server \ nginx \ libassimp-dev msg_ok "Installed Dependencies" setup_imagemagick PG_VERSION="16" setup_postgresql PG_DB_NAME="manyfold" PG_DB_USER="manyfold" setup_postgresql_db NODE_VERSION="24" NODE_MODULE="yarn" setup_nodejs fetch_and_deploy_gh_release "manyfold" "manyfold3d/manyfold" "tarball" "latest" "/opt/manyfold/app" RUBY_INSTALL_VERSION=$(cat /opt/manyfold/app/.ruby-version) RUBY_VERSION=${RUBY_INSTALL_VERSION} RUBY_INSTALL_RAILS="true" HOME=/home/manyfold setup_ruby msg_info "Configuring Manyfold" YARN_VERSION=$(grep '"packageManager":' /opt/manyfold/app/package.json | sed -E 's/.*"(yarn@[0-9\.]+)".*/\1/') RELEASE=$(get_latest_github_release "manyfold3d/manyfold") useradd -m -s /usr/bin/bash manyfold cat </opt/manyfold/.env export APP_VERSION=${RELEASE} export GUID=1002 export PUID=1001 export PUBLIC_PORT=5000 export REDIS_URL=redis://127.0.0.1:6379/1 export DATABASE_ADAPTER=postgresql export DATABASE_HOST=127.0.0.1 export DATABASE_USER=${PG_DB_USER} export DATABASE_PASSWORD=${PG_DB_PASS} export DATABASE_NAME=${PG_DB_NAME} export DATABASE_CONNECTION_POOL=16 export MULTIUSER=enabled export HTTPS_ONLY=false export RAILS_ENV=production EOF cat </opt/manyfold/user_setup.sh #!/bin/bash source /opt/manyfold/.env export PATH="/home/manyfold/.rbenv/bin:\$PATH" eval "\$(/home/manyfold/.rbenv/bin/rbenv init - bash)" cd /opt/manyfold/app rbenv global $RUBY_INSTALL_VERSION gem install bundler bundle install gem install sidekiq gem install foreman corepack enable yarn rm -f /opt/manyfold/app/config/credentials.yml.enc corepack prepare $YARN_VERSION --activate corepack use $YARN_VERSION export VISUAL="code --wait" bin/rails credentials:edit bin/rails db:migrate bin/rails assets:precompile EOF $STD mkdir -p /opt/manyfold_data msg_ok "Configured Manyfold" msg_info "Installing Manyfold" chown -R manyfold:manyfold {/home/manyfold,/opt/manyfold} chmod +x /opt/manyfold/user_setup.sh $STD npm install --global corepack $STD sudo -u manyfold bash /opt/manyfold/user_setup.sh rm -f /opt/manyfold/user_setup.sh msg_ok "Installed Manyfold" msg_info "Creating Services" source /opt/manyfold/.env export PATH="/home/manyfold/.rbenv/shims:/home/manyfold/.rbenv/bin:$PATH" $STD foreman export systemd /etc/systemd/system -a manyfold -u manyfold -f /opt/manyfold/app/Procfile for f in /etc/systemd/system/manyfold-*.service; do sed -i "s|/bin/bash -lc '|/bin/bash -lc 'source /opt/manyfold/.env \&\& |" "$f" done systemctl enable -q --now manyfold.target manyfold-rails.1 manyfold-default_worker.1 manyfold-performance_worker.1 cat </etc/nginx/sites-available/manyfold.conf server { listen 80; server_name manyfold; root /opt/manyfold/app/public; location /cable { proxy_pass http://127.0.0.1:5000; proxy_set_header Host \$http_host; proxy_set_header X-Forwarded-Host \$http_host; proxy_set_header X-Forwarded-Port \$server_port; proxy_http_version 1.1; proxy_set_header Upgrade \$http_upgrade; proxy_set_header Connection "upgrade"; proxy_set_header X-Real-IP \$remote_addr; proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto \$scheme; } location / { try_files \$uri/index.html \$uri @rails; } location @rails { proxy_pass http://127.0.0.1:5000; proxy_set_header Host \$http_host; proxy_set_header X-Forwarded-Host \$http_host; proxy_set_header X-Forwarded-Port \$server_port; proxy_set_header X-Real-IP \$remote_addr; proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto \$scheme; } } EOF ln -s /etc/nginx/sites-available/manyfold.conf /etc/nginx/sites-enabled/ rm -f /etc/nginx/sites-enabled/default $STD systemctl reload nginx msg_ok "Created Services" motd_ssh customize msg_info "Cleaning up" $STD apt-get -y autoremove $STD apt-get -y autoclean msg_ok "Cleaned" ================================================ FILE: install/mariadb-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://mariadb.org/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os setup_mariadb msg_info "Setup MariaDB" sed -i 's/^# *\(port *=.*\)/\1/' /etc/mysql/my.cnf sed -i 's/^bind-address/#bind-address/g' /etc/mysql/mariadb.conf.d/50-server.cnf msg_ok "Setup MariaDB" read -r -p "${TAB3}Would you like to add PhpMyAdmin? " prompt if [[ ${prompt,,} =~ ^(y|yes)$ ]]; then msg_info "Installing phpMyAdmin" $STD apt install -y \ apache2 \ php \ php-mysqli \ php-mbstring \ php-zip \ php-gd \ php-json \ php-curl curl -fsSL "https://files.phpmyadmin.net/phpMyAdmin/5.2.2/phpMyAdmin-5.2.2-all-languages.tar.gz" -o "phpMyAdmin-5.2.2-all-languages.tar.gz" mkdir -p /var/www/html/phpMyAdmin tar xf phpMyAdmin-5.2.2-all-languages.tar.gz --strip-components=1 -C /var/www/html/phpMyAdmin cp /var/www/html/phpMyAdmin/config.sample.inc.php /var/www/html/phpMyAdmin/config.inc.php SECRET=$(openssl rand -base64 24) sed -i "s#\$cfg\['blowfish_secret'\] = '';#\$cfg['blowfish_secret'] = '${SECRET}';#" /var/www/html/phpMyAdmin/config.inc.php chmod 660 /var/www/html/phpMyAdmin/config.inc.php chown -R www-data:www-data /var/www/html/phpMyAdmin systemctl restart apache2 msg_ok "Installed phpMyAdmin" fi motd_ssh customize cleanup_lxc ================================================ FILE: install/matomo-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://matomo.org/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y caddy msg_ok "Installed Dependencies" mkdir -p /opt/matomo PHP_VERSION="8.3" PHP_FPM="YES" PHP_MODULES="pdo_mysql,gd,mbstring,xml,curl,intl,zip,ldap" setup_php setup_mariadb MARIADB_DB_NAME="matomo" MARIADB_DB_USER="matomo" setup_mariadb_db msg_info "Allowing Local TCP Database Access" $STD mariadb -u root -e "CREATE USER IF NOT EXISTS '$MARIADB_DB_USER'@'127.0.0.1' IDENTIFIED BY '$MARIADB_DB_PASS';" $STD mariadb -u root -e "ALTER USER '$MARIADB_DB_USER'@'127.0.0.1' IDENTIFIED BY '$MARIADB_DB_PASS';" $STD mariadb -u root -e "GRANT ALL ON \`$MARIADB_DB_NAME\`.* TO '$MARIADB_DB_USER'@'127.0.0.1';" $STD mariadb -u root -e "FLUSH PRIVILEGES;" msg_ok "Allowed Local TCP Database Access" fetch_and_deploy_gh_release "matomo" "matomo-org/matomo" "prebuild" "latest" "/opt/matomo" "matomo-*.zip" msg_info "Setting up Matomo" if [[ -d /opt/matomo/matomo ]]; then rm -rf /opt/matomo/tmp "/opt/matomo/How to install Matomo.html" find /opt/matomo/matomo -mindepth 1 -maxdepth 1 -exec mv -t /opt/matomo {} + rm -rf /opt/matomo/matomo fi mkdir -p /opt/matomo/tmp chown -R www-data:www-data /opt/matomo chmod -R 755 /opt/matomo/tmp msg_ok "Set up Matomo" msg_info "Configuring Caddy" PHP_VER=$(php -r 'echo PHP_MAJOR_VERSION . "." . PHP_MINOR_VERSION;') cat </etc/caddy/Caddyfile :80 { root * /opt/matomo @blocked path /config /config/* /tmp /tmp/* /.* /.*/* respond @blocked 403 php_fastcgi unix//run/php/php${PHP_VER}-fpm.sock file_server encode gzip } EOF usermod -aG www-data caddy msg_ok "Configured Caddy" systemctl enable -q --now php${PHP_VER}-fpm systemctl restart caddy motd_ssh customize cleanup_lxc ================================================ FILE: install/matter-server-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/matter-js/python-matter-server source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ libuv1 \ libjson-c5 \ libnl-3-200 \ libnl-route-3-200 \ iputils-ping \ iproute2 msg_ok "Installed Dependencies" UV_PYTHON="3.12" setup_uv msg_info "Setting up Matter Server" mkdir -p /opt/matter-server/data/credentials if [ -L /data ]; then rm -f /data fi if [ ! -e /data ]; then ln -s /opt/matter-server/data /data fi $STD uv venv /opt/matter-server/.venv MATTER_VERSION=$(get_latest_github_release "matter-js/python-matter-server") $STD uv pip install --python /opt/matter-server/.venv/bin/python "python-matter-server[server]==${MATTER_VERSION}" echo "${MATTER_VERSION}" >~/.matter-server msg_ok "Set up Matter Server" fetch_and_deploy_gh_release "chip-ota-provider-app" "home-assistant-libs/matter-linux-ota-provider" "singlefile" "latest" "/usr/local/bin" "chip-ota-provider-app-x86-64" msg_info "Configuring Network" cat </etc/sysctl.d/99-matter.conf net.ipv4.igmp_max_memberships=1024 EOF $STD sysctl -p /etc/sysctl.d/99-matter.conf msg_ok "Configured Network" msg_info "Creating Service" cat </etc/systemd/system/matter-server.service [Unit] Description=Matter Server After=network.target [Service] Type=simple User=root ExecStart=/opt/matter-server/.venv/bin/matter-server --storage-path /data --paa-root-cert-dir /data/credentials Restart=on-failure RestartSec=5 [Install] WantedBy=multi-user.target EOF systemctl enable -q --now matter-server msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/matterbridge-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: MickLesk (Canbiz) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/Luligu/matterbridge/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Install Matterbridge" mkdir -p /root/Matterbridge NODE_VERSION="24" NODE_MODULE="matterbridge" setup_nodejs msg_ok "Installed Matterbridge" msg_info "Creating Service" cat </etc/systemd/system/matterbridge.service [Unit] Description=matterbridge After=network-online.target [Service] Type=simple ExecStart=matterbridge -service WorkingDirectory=/root/Matterbridge StandardOutput=inherit StandardError=inherit Restart=always RestartSec=10s TimeoutStopSec=30s [Install] WantedBy=multi-user.target EOF systemctl enable -q --now matterbridge msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/mattermost-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: Kaedon Cleland-Host (dracentis) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://mattermost.com/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_warn "WARNING: This script will run an external installer from a third-party source (https://mattermost.com/)." msg_warn "The following code is NOT maintained or audited by our repository." msg_warn "If you have any doubts or concerns, please review the installer code before proceeding:" msg_custom "${TAB3}${GATEWAY}${BGN}${CL}" "\e[1;34m" "→ https://deb.packages.mattermost.com/repo-setup.sh" echo read -r -p "${TAB3}Do you want to continue? [y/N]: " CONFIRM if [[ ! "$CONFIRM" =~ ^([yY][eE][sS]|[yY])$ ]]; then msg_error "Aborted by user. No changes have been made." exit 10 fi PG_VERSION="16" setup_postgresql msg_info "Setting up PostgreSQL" DB_NAME=mattermost DB_USER=mmuser DB_PASS=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | head -c13) $STD sudo -u postgres psql -c "CREATE DATABASE $DB_NAME;" $STD sudo -u postgres psql -c "CREATE USER $DB_USER WITH PASSWORD '$DB_PASS';" $STD sudo -u postgres psql -c "GRANT ALL PRIVILEGES ON DATABASE $DB_NAME to $DB_USER;" $STD sudo -u postgres psql -c "ALTER DATABASE $DB_NAME OWNER TO $DB_USER;" $STD sudo -u postgres psql -c "GRANT USAGE, CREATE ON SCHEMA PUBLIC TO $DB_USER;" { echo "Mattermost Credentials" echo "Database User: $DB_USER" echo "Database Password: $DB_PASS" echo "Database Name: $DB_NAME" } >>~/mattermost.creds msg_ok "Set up PostgreSQL" msg_info "Installing Mattermost" curl -fsSL -o /usr/share/keyrings/mattermost-archive-keyring.gpg https://deb.packages.mattermost.com/pubkey.gpg sh -c 'curl -fsSL https://deb.packages.mattermost.com/repo-setup.sh | sudo bash -s mattermost' >/dev/null $STD apt update $STD apt install -y mattermost $STD install -C -m 600 -o mattermost -g mattermost /opt/mattermost/config/config.defaults.json /opt/mattermost/config/config.json sed -i -e "/DataSource/c\ \"DataSource\": \"postgres://$DB_USER:$DB_PASS@localhost:5432/$DB_NAME?sslmode=disable&connect_timeout=10\"," \ -e "/SiteURL/c\ \"SiteURL\": \"http://$LOCAL_IP:8065\"," /opt/mattermost/config/config.json systemctl enable -q --now mattermost msg_ok "Installed Mattermost" motd_ssh customize cleanup_lxc ================================================ FILE: install/mealie-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://mealie.io source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ build-essential \ libpq-dev \ libwebp-dev \ libsasl2-dev \ libldap2-dev \ libldap-common \ libssl-dev \ libldap2 \ gosu \ iproute2 msg_ok "Installed Dependencies" PYTHON_VERSION="3.12" setup_uv PG_VERSION="16" setup_postgresql NODE_MODULE="yarn" NODE_VERSION="24" setup_nodejs fetch_and_deploy_gh_release "mealie" "mealie-recipes/mealie" "tarball" PG_DB_NAME="mealie_db" PG_DB_USER="mealie_user" PG_DB_GRANT_SUPERUSER="true" setup_postgresql_db msg_info "Installing Python Dependencies with uv" cd /opt/mealie $STD uv sync --frozen --extra pgsql msg_ok "Installed Python Dependencies" msg_info "Building Frontend" MEALIE_VERSION=$(<$HOME/.mealie) export NUXT_TELEMETRY_DISABLED=1 cd /opt/mealie/frontend SITE_SETTINGS=$(find /opt/mealie/frontend -name "site-settings.vue" -path "*/admin/*" | head -1) $STD sed -i "s|https://github.com/mealie-recipes/mealie/commit/|https://github.com/mealie-recipes/mealie/releases/tag/|g" "$SITE_SETTINGS" $STD sed -i "s|value: data.buildId,|value: \"v${MEALIE_VERSION}\",|g" "$SITE_SETTINGS" $STD sed -i "s|value: data.production ? i18n.t(\"about.production\") : i18n.t(\"about.development\"),|value: \"bare-metal\",|g" "$SITE_SETTINGS" $STD yarn install --prefer-offline --frozen-lockfile --non-interactive --production=false --network-timeout 1000000 $STD yarn generate msg_ok "Built Frontend" msg_info "Copying Built Frontend" mkdir -p /opt/mealie/mealie/frontend cp -r /opt/mealie/frontend/dist/* /opt/mealie/mealie/frontend/ msg_ok "Copied Frontend" setup_nltk "averaged_perceptron_tagger_eng" "/nltk_data" msg_info "Writing Environment File" SECRET=$(openssl rand -hex 32) mkdir -p /run/secrets cat </opt/mealie/mealie.env MEALIE_HOME=/opt/mealie NLTK_DATA=/nltk_data SECRET=${SECRET} DB_ENGINE=postgres POSTGRES_SERVER=localhost POSTGRES_PORT=5432 POSTGRES_USER=${PG_DB_USER} POSTGRES_PASSWORD=${PG_DB_PASS} POSTGRES_DB=${PG_DB_NAME} PRODUCTION=true HOST=0.0.0.0 PORT=9000 BASE_URL=http://${LOCAL_IP}:9000 EOF msg_ok "Wrote Environment File" msg_info "Creating Start Script" cat <<'EOF' >/opt/mealie/start.sh #!/bin/bash set -a source /opt/mealie/mealie.env set +a exec uv run mealie EOF chmod +x /opt/mealie/start.sh msg_ok "Created Start Script" msg_info "Creating Systemd Service" cat <<'EOF' >/etc/systemd/system/mealie.service [Unit] Description=Mealie Recipe Manager After=network.target postgresql.service Wants=postgresql.service [Service] Type=simple User=root WorkingDirectory=/opt/mealie ExecStart=/opt/mealie/start.sh Restart=on-failure RestartSec=5 [Install] WantedBy=multi-user.target EOF systemctl enable -q --now mealie msg_ok "Created and Started Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/mediamanager-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: vhsdream # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/maxdorninger/MediaManager source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os read -r -p "${TAB3}Enter the email address of your first admin user: " admin_email if [[ "$admin_email" ]]; then EMAIL="$admin_email" fi setup_yq NODE_VERSION="24" setup_nodejs PYTHON_VERSION="3.13" setup_uv PG_VERSION="17" setup_postgresql msg_info "Setting up PostgreSQL" DB_NAME="mm_db" DB_USER="mm_user" DB_PASS="$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | cut -c1-13)" $STD sudo -u postgres psql -c "CREATE ROLE $DB_USER WITH LOGIN PASSWORD '$DB_PASS';" $STD sudo -u postgres psql -c "CREATE DATABASE $DB_NAME WITH OWNER $DB_USER TEMPLATE template0;" $STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET client_encoding TO 'utf8';" { echo "MediaManager Credentials" echo "MediaManager Database User: $DB_USER" echo "MediaManager Database Password: $DB_PASS" echo "MediaManager Database Name: $DB_NAME" } >>~/mediamanager.creds msg_ok "Set up PostgreSQL" fetch_and_deploy_gh_release "MediaManager" "maxdorninger/MediaManager" "tarball" "latest" "/opt/mediamanager" msg_info "Configuring MediaManager" MM_DIR="/opt/mm" MEDIA_DIR="${MM_DIR}/media" export CONFIG_DIR="${MM_DIR}/config" export FRONTEND_FILES_DIR="${MM_DIR}/web/build" export PUBLIC_VERSION="" export PUBLIC_API_URL="" export BASE_PATH="/web" cd /opt/mediamanager/web $STD npm install --no-fund --no-audit $STD npm run build mkdir -p {"$MM_DIR"/web,"$MEDIA_DIR","$CONFIG_DIR"} cp -r build "$FRONTEND_FILES_DIR" export BASE_PATH="" export VIRTUAL_ENV="${MM_DIR}/venv" cd /opt/mediamanager cp -r {media_manager,alembic*} "$MM_DIR" $STD /usr/local/bin/uv sync --locked --active -n -p cpython3.13 --managed-python msg_ok "Configured MediaManager" msg_info "Creating config and start script" SECRET="$(openssl rand -hex 32)" sed -e "s/localhost:8/$LOCAL_IP:8/g" \ -e "s|/data/|$MEDIA_DIR/|g" \ -e 's/"db"/"localhost"/' \ -e "s/user = \"MediaManager\"/user = \"$DB_USER\"/" \ -e "s/password = \"MediaManager\"/password = \"$DB_PASS\"/" \ -e "s/dbname = \"MediaManager\"/dbname = \"$DB_NAME\"/" \ -e "/^token_secret/s/=.*/= \"$SECRET\"/" \ -e "s/admin@example.com/$EMAIL/" \ -e '/^admin_emails/s/, .*/]/' \ /opt/mediamanager/config.example.toml >"$CONFIG_DIR"/config.toml mkdir -p "$MEDIA_DIR"/{images,tv,movies,torrents} cat <"$MM_DIR"/start.sh #!/usr/bin/env bash export CONFIG_DIR="$CONFIG_DIR" export FRONTEND_FILES_DIR="$FRONTEND_FILES_DIR" export LOG_FILE="$CONFIG_DIR/media_manager.log" export BASE_PATH="" cd $MM_DIR source ./venv/bin/activate /usr/local/bin/uv run alembic upgrade head /usr/local/bin/uv run fastapi run ./media_manager/main.py --port 8000 EOF chmod +x "$MM_DIR"/start.sh msg_ok "Created config and start script" msg_info "Creating service" cat </etc/systemd/system/mediamanager.service [Unit] Description=MediaManager Backend Service After=network.target [Service] Type=simple WorkingDirectory=${MM_DIR} ExecStart=/usr/bin/bash start.sh [Install] WantedBy=multi-user.target EOF systemctl enable -q --now mediamanager msg_ok "Created service" motd_ssh customize cleanup_lxc ================================================ FILE: install/mediamtx-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/bluenviron/mediamtx source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os setup_hwaccel msg_info "Installing Dependencies" $STD apt install -y ffmpeg msg_ok "Installed Dependencies" fetch_and_deploy_gh_release "mediamtx" "bluenviron/mediamtx" "prebuild" "latest" "/opt/mediamtx" "mediamtx*linux_arm64v8.tar.gz" msg_info "Creating Service" cat </etc/systemd/system/mediamtx.service [Unit] Description=MediaMTX After=syslog.target network-online.target [Service] ExecStart=/opt/mediamtx/./mediamtx WorkingDirectory=/opt/mediamtx Restart=always [Install] WantedBy=multi-user.target EOF systemctl enable -q --now mediamtx msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/medusa-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # Co-Author: MickLesk (Canbiz) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/pymedusa/Medusa source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ git-core \ mediainfo cat </etc/apt/sources.list.d/non-free.list deb http://deb.debian.org/debian bookworm main contrib non-free non-free-firmware EOF $STD apt update $STD apt install -y unrar rm /etc/apt/sources.list.d/non-free.list msg_ok "Installed Dependencies" msg_info "Installing Medusa" $STD git clone https://github.com/pymedusa/Medusa.git /opt/medusa msg_ok "Installed Medusa" msg_info "Creating Service" cat </etc/systemd/system/medusa.service [Unit] Description=Medusa Daemon After=network.target [Service] Type=simple ExecStart=/usr/bin/python3 /opt/medusa/start.py -q --nolaunch --datadir=/opt/medusa TimeoutStopSec=25 KillMode=process Restart=on-failure [Install] WantedBy=multi-user.target EOF systemctl enable -q --now medusa msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/meilisearch-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.meilisearch.com/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os MEILISEARCH_BIND="0.0.0.0:7700" setup_meilisearch read -r -p "${TAB3}Do you want add meilisearch-ui? [y/n]: " prompt if [[ ${prompt,,} =~ ^(y|yes)$ ]]; then NODE_VERSION="22" NODE_MODULE="pnpm@latest" setup_nodejs fetch_and_deploy_gh_release "meilisearch-ui" "riccox/meilisearch-ui" "tarball" msg_info "Configuring ${APPLICATION}-ui" cd /opt/meilisearch-ui sed -i 's|const hash = execSync("git rev-parse HEAD").toString().trim();|const hash = "unknown";|' /opt/meilisearch-ui/vite.config.ts $STD pnpm install cat </opt/meilisearch-ui/.env.local VITE_SINGLETON_MODE=true VITE_SINGLETON_HOST=http://${LOCAL_IP}:7700 VITE_SINGLETON_API_KEY=${MEILISEARCH_MASTER_KEY} EOF msg_ok "Configured ${APPLICATION}-ui" msg_info "Creating Meilisearch-UI service" cat </etc/systemd/system/meilisearch-ui.service [Unit] Description=Meilisearch UI Service After=network.target meilisearch.service Requires=meilisearch.service [Service] User=root WorkingDirectory=/opt/meilisearch-ui ExecStart=/usr/bin/pnpm start Restart=always RestartSec=5 StandardOutput=syslog StandardError=syslog SyslogIdentifier=meilisearch-ui [Install] WantedBy=multi-user.target EOF systemctl enable -q --now meilisearch-ui msg_ok "Created Meilisearch-UI service" fi motd_ssh customize cleanup_lxc ================================================ FILE: install/memos-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck # Co-Author: MickLesk (Canbiz) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/usememos/memos source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os ARCH=$(dpkg --print-architecture 2>/dev/null || uname -m) if [[ "$ARCH" == "arm64" || "$ARCH" == "aarch64" ]]; then fetch_and_deploy_gh_release "memos" "usememos/memos" "prebuild" "v0.25.3" "/opt/memos" "memos*linux_arm64.tar.gz" else fetch_and_deploy_gh_release "memos" "usememos/memos" "prebuild" "latest" "/opt/memos" "memos*linux_amd64.tar.gz" fi mkdir -p /opt/memos_data msg_info "Creating Service" cat </etc/systemd/system/memos.service [Unit] Description=Memos Server After=network.target [Service] ExecStart=/opt/memos/memos Environment="MEMOS_MODE=prod" Environment="MEMOS_PORT=9030" Environment="MEMOS_DATA=/opt/memos_data" WorkingDirectory=/opt/memos Restart=always [Install] WantedBy=multi-user.target EOF systemctl enable -q --now memos msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/meshcentral-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://meshcentral.com/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y ca-certificates msg_ok "Installed Dependencies" NODE_VERSION="22" setup_nodejs msg_info "Installing MeshCentral" mkdir /opt/meshcentral cd /opt/meshcentral $STD npm install meshcentral $STD node node_modules/meshcentral --install msg_ok "Installed MeshCentral" motd_ssh customize cleanup_lxc ================================================ FILE: install/metabase-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: Slaviša Arežina (tremor021) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.metabase.com/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os JAVA_VERSION="21" setup_java PG_VERSION="17" setup_postgresql PG_DB_NAME="metabase_db" PG_DB_USER="metabase" setup_postgresql_db msg_info "Setting up Metabase" mkdir -p /opt/metabase RELEASE=$(get_latest_github_release "metabase/metabase") curl -fsSL "https://downloads.metabase.com/v${RELEASE}.x/metabase.jar" -o /opt/metabase/metabase.jar cd /opt/metabase cat </opt/metabase/.env MB_DB_TYPE=postgres MB_DB_DBNAME=$PG_DB_NAME MB_DB_PORT=5432 MB_DB_USER=$PG_DB_USER MB_DB_PASS=$PG_DB_PASS MB_DB_HOST=localhost EOF echo $RELEASE >~/.metabase msg_ok "Setup Metabase" msg_info "Creating Service" cat </etc/systemd/system/metabase.service [Unit] Description=Metabase Service After=network.target [Service] EnvironmentFile=/opt/metabase/.env WorkingDirectory=/opt/metabase ExecStart=/usr/bin/java --add-opens java.base/java.nio=ALL-UNNAMED -jar metabase.jar Restart=always SuccessExitStatus=143 TimeoutStopSec=120 [Install] WantedBy=multi-user.target EOF systemctl enable -q --now metabase msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/metube-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (Canbiz) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/alexta69/metube source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ build-essential \ aria2 \ coreutils \ musl-dev \ ffmpeg msg_ok "Installed Dependencies" PYTHON_VERSION="3.13" setup_uv NODE_VERSION="24" NODE_MODULE="pnpm" setup_nodejs msg_info "Installing Deno" export DENO_INSTALL="/usr/local" curl -fsSL https://deno.land/install.sh | $STD sh -s -- -y [[ ":$PATH:" != *":/usr/local/bin:"* ]] && echo -e "\nexport PATH=\"/usr/local/bin:\$PATH\"" >>~/.bashrc && source ~/.bashrc msg_ok "Installed Deno" fetch_and_deploy_gh_release "metube" "alexta69/metube" "tarball" "latest" msg_info "Installing MeTube" cd /opt/metube/ui if command -v corepack >/dev/null 2>&1; then $STD corepack enable $STD corepack prepare pnpm --activate || true fi echo 'onlyBuiltDependencies=*' >> .npmrc $STD pnpm install --frozen-lockfile $STD pnpm run build cd /opt/metube $STD uv sync mkdir -p /opt/metube_downloads /opt/metube_downloads/.metube /opt/metube_downloads/music /opt/metube_downloads/videos cat </opt/metube/.env # Storage & Directories DOWNLOAD_DIR=/opt/metube_downloads AUDIO_DOWNLOAD_DIR=/opt/metube_downloads/music STATE_DIR=/opt/metube_downloads/.metube TEMP_DIR=/opt/metube_downloads # Download Behavior DOWNLOAD_MODE=limited MAX_CONCURRENT_DOWNLOADS=3 DELETE_FILE_ON_TRASHCAN=false DEFAULT_OPTION_PLAYLIST_STRICT_MODE=false DEFAULT_OPTION_PLAYLIST_ITEM_LIMIT=0 # File Naming & yt-dlp OUTPUT_TEMPLATE=%(title)s.%(ext)s OUTPUT_TEMPLATE_CHAPTER=%(title)s - %(section_number)s %(section_title)s.%(ext)s OUTPUT_TEMPLATE_PLAYLIST=%(playlist_title)s/%(title)s.%(ext)s YTDL_OPTIONS={"trim_file_name":200,"extractor_args":{"youtube":{"player_client":["default","-tv_simply"]}}} # Custom Directories CUSTOM_DIRS=true CREATE_CUSTOM_DIRS=true # Basic Setup DEFAULT_THEME=auto LOGLEVEL=INFO ENABLE_ACCESSLOG=false EOF msg_ok "Installed MeTube" msg_info "Creating Service" cat </etc/systemd/system/metube.service [Unit] Description=Metube - YouTube Downloader After=network.target [Service] Type=simple WorkingDirectory=/opt/metube EnvironmentFile=/opt/metube/.env ExecStart=/opt/metube/.venv/bin/python3 app/main.py Restart=always User=root [Install] WantedBy=multi-user.target EOF systemctl enable -q --now metube msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/minarca-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: Slaviša Arežina (tremor021) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://minarca.org/en_CA source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ apt-transport-https \ ca-certificates \ lsb-release msg_ok "Installed Dependencies" msg_info "Installing Minarca" curl -fsSL https://www.ikus-soft.com/archive/minarca/public.key | gpg --dearmor >/usr/share/keyrings/minarca-keyring.gpg cat </etc/apt/sources.list.d/minarca.sources Types: deb URIs: https://nexus.ikus-soft.com/repository/apt-release-$(lsb_release -sc)/ Suites: $(lsb_release -sc) Components: main Architectures: arm64 Signed-By: /usr/share/keyrings/minarca-keyring.gpg EOF $STD apt update $STD apt install -y minarca-server msg_ok "Installed Minarca" motd_ssh customize cleanup_lxc ================================================ FILE: install/mini-qr-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: doge0420 # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/lyqht/mini-qr source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ libharfbuzz0b \ caddy \ fontconfig msg_ok "Installed Dependencies" NODE_VERSION="20" setup_nodejs fetch_and_deploy_gh_release "mini-qr" "lyqht/mini-qr" "tarball" msg_info "Building MiniQR" cd /opt/mini-qr $STD npm install $STD npm run build msg_ok "Built MiniQR" msg_info "Configuring Caddy" cat </etc/caddy/Caddyfile :80 { root * /opt/mini-qr/dist file_server # Handle client-side routing try_files {path} /index.html # Cache static assets @assets { path /assets/* } header @assets Cache-Control "public, immutable, max-age=31536000" # Correct MIME types for JS modules @jsmodules { path *.js *.mjs } header @jsmodules Content-Type "application/javascript" } EOF systemctl enable -q --now caddy systemctl reload caddy msg_ok "Configured Caddy" motd_ssh customize cleanup_lxc ================================================ FILE: install/miniflux-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: omernaveedxyz # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://miniflux.app/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os PG_VERSION=17 setup_postgresql PG_DB_NAME="miniflux_db" PG_DB_USER="miniflux" PG_DB_GRANT_SUPERUSER="true" setup_postgresql_db fetch_and_deploy_gh_release "miniflux" "miniflux/v2" "binary" "latest" msg_info "Configuring Miniflux" ADMIN_NAME=admin ADMIN_PASS="$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | head -c13)" cat </etc/miniflux.conf # See https://miniflux.app/docs/configuration.html DATABASE_URL=user=$PG_DB_USER password=$PG_DB_PASS dbname=$PG_DB_NAME sslmode=disable CREATE_ADMIN=1 ADMIN_USERNAME=$ADMIN_NAME ADMIN_PASSWORD=$ADMIN_PASS LISTEN_ADDR=0.0.0.0:8080 EOF { echo "ADMIN_USERNAME: $ADMIN_NAME" echo "ADMIN_PASSWORD: $ADMIN_PASS" } >>~/miniflux.creds $STD miniflux -migrate -config-file /etc/miniflux.conf systemctl enable -q --now miniflux msg_ok "Configured Miniflux" motd_ssh customize cleanup_lxc ================================================ FILE: install/minio-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/minio/minio source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os FEATURE_RICH_VERSION="2025-04-22T22-12-26Z" echo echo "MinIO recently removed many management features from the Console UI." echo "The last feature-complete version is: $FEATURE_RICH_VERSION" echo "Latest versions require the paid edition for full UI functionality." echo echo "Choose which version to install:" echo " [N] Feature-rich community version ($FEATURE_RICH_VERSION) [Recommended]" echo " [Y] Latest version (may lack UI features)" echo read -p "Install latest MinIO version? [y/N]: " -n 1 -r echo if [[ $REPLY =~ ^[Yy]$ ]]; then USE_LATEST=true else USE_LATEST=false fi msg_info "Setting up MinIO" if [[ "$USE_LATEST" == "true" ]]; then RELEASE=$(curl -fsSL https://api.github.com/repos/minio/minio/releases/latest | grep '"tag_name"' | awk -F '"' '{print $4}') DOWNLOAD_URL="https://dl.min.io/server/minio/release/linux-arm64/minio" else RELEASE="$FEATURE_RICH_VERSION" DOWNLOAD_URL="https://dl.min.io/server/minio/release/linux-arm64/archive/minio.RELEASE.${FEATURE_RICH_VERSION}" fi curl -fsSL "$DOWNLOAD_URL" -o /usr/local/bin/minio chmod +x /usr/local/bin/minio useradd -r minio-user -s /sbin/nologin mkdir -p /home/minio-user chown minio-user:minio-user /home/minio-user mkdir -p /data chown minio-user:minio-user /data MINIO_ADMIN_USER="minioadmin" MINIO_ADMIN_PASSWORD="$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | cut -c1-13)" cat </etc/default/minio MINIO_ROOT_USER=${MINIO_ADMIN_USER} MINIO_ROOT_PASSWORD=${MINIO_ADMIN_PASSWORD} EOF { echo "" echo "MinIO Credentials" echo "MinIO Admin User: $MINIO_ADMIN_USER" echo "MinIO Admin Password: $MINIO_ADMIN_PASSWORD" } >>~/minio.creds echo "${RELEASE}" >/opt/${APPLICATION,,}_version.txt msg_ok "Setup MinIO" msg_info "Creating service" cat </etc/systemd/system/minio.service [Unit] Description=MinIO Documentation=https://docs.min.io Wants=network-online.target After=network-online.target [Service] User=minio-user Group=minio-user EnvironmentFile=-/etc/default/minio ExecStart=/usr/local/bin/minio server --console-address ":9001" /data Restart=always RestartSec=5 LimitNOFILE=65536 [Install] WantedBy=multi-user.target EOF systemctl enable -q --now minio msg_ok "Service created" motd_ssh customize cleanup_lxc ================================================ FILE: install/minthcm-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: MintHCM # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/minthcm/minthcm source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os PHP_VERSION="8.2" PHP_APACHE="YES" PHP_MODULE="mysql,redis" PHP_FPM="YES" setup_php setup_composer setup_mariadb $STD mariadb -u root -e "SET GLOBAL sql_mode='STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION'" fetch_and_deploy_gh_release "MintHCM" "minthcm/minthcm" "tarball" "latest" "/var/www/MintHCM" msg_info "Configuring MintHCM" mkdir -p /etc/php/${PHP_VERSION}/mods-available cp /var/www/MintHCM/docker/config/000-default.conf /etc/apache2/sites-available/000-default.conf cp /var/www/MintHCM/docker/config/php-minthcm.ini /etc/php/${PHP_VERSION}/mods-available/php-minthcm.ini mkdir -p "/etc/php/${PHP_VERSION}/cli/conf.d" "/etc/php/${PHP_VERSION}/apache2/conf.d" ln -s "/etc/php/${PHP_VERSION}/mods-available/php-minthcm.ini" "/etc/php/${PHP_VERSION}/cli/conf.d/20-minthcm.ini" ln -s "/etc/php/${PHP_VERSION}/mods-available/php-minthcm.ini" "/etc/php/${PHP_VERSION}/apache2/conf.d/20-minthcm.ini" chown -R www-data:www-data /var/www/MintHCM find /var/www/MintHCM -type d -exec chmod 755 {} \; find /var/www/MintHCM -type f -exec chmod 644 {} \; mkdir -p /var/www/script cp /var/www/MintHCM/docker/script/generate_config.php /var/www/script/generate_config.php cp /var/www/MintHCM/docker/.env /var/www/script/.env chown -R www-data:www-data /var/www/script $STD a2enmod rewrite $STD a2enmod headers $STD systemctl restart apache2 msg_ok "Configured MintHCM" msg_info "Setting up Elasticsearch" setup_deb822_repo \ "elasticsearch" \ "https://artifacts.elastic.co/GPG-KEY-elasticsearch" \ "https://artifacts.elastic.co/packages/7.x/apt" \ "stable" $STD apt install -y elasticsearch echo "-Xms2g" >>/etc/elasticsearch/jvm.options echo "-Xmx2g" >>/etc/elasticsearch/jvm.options $STD /usr/share/elasticsearch/bin/elasticsearch-plugin install ingest-attachment -b systemctl enable -q --now elasticsearch msg_ok "Set up Elasticsearch" msg_info "Configuring Database" DB_PASS=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | head -c13) $STD mariadb -u root -e "CREATE USER 'minthcm'@'localhost' IDENTIFIED BY '${DB_PASS}';" $STD mariadb -u root -e "GRANT ALL ON *.* TO 'minthcm'@'localhost'; FLUSH PRIVILEGES;" sed -i "s/^DB_HOST=.*/DB_HOST=localhost/" /var/www/script/.env sed -i "s/^DB_USER=.*/DB_USER=minthcm/" /var/www/script/.env sed -i "s/^DB_PASS=.*/DB_PASS=$DB_PASS/" /var/www/script/.env sed -i "s/^ELASTICSEARCH_HOST=.*/ELASTICSEARCH_HOST=localhost/" /var/www/script/.env msg_ok "Configured Database" msg_info "Generating configuration file" set -a source /var/www/script/.env set +a $STD php /var/www/script/generate_config.php msg_ok "Generated configuration file" msg_info "Installing MintHCM" cd /var/www/MintHCM $STD sudo -u www-data php MintCLI install /dev/null 2>&1\n" >/var/spool/cron/crontabs/www-data service cron start rm -f /var/www/MintHCM/configMint4 msg_ok "Installed MintHCM" motd_ssh customize cleanup_lxc ================================================ FILE: install/mongodb-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.mongodb.com/de-de source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os cpu_info=$(lscpu) if ! echo "$cpu_info" | grep -q 'asimdrdm\|asimdhf\|dotprod\|fp16'; then msg_error "This machine does not support ARMv8.2-A." exit fi read -p "${TAB3}Do you want to install MongoDB 8.0 instead of 7.0? [y/N]: " install_mongodb_8 if [[ "$install_mongodb_8" =~ ^[Yy]$ ]]; then MONGO_VERSION="8.0" setup_mongodb else MONGO_VERSION="7.0" setup_mongodb fi sed -i 's/bindIp: 127.0.0.1/bindIp: 0.0.0.0/' /etc/mongod.conf motd_ssh customize cleanup_lxc ================================================ FILE: install/monica-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: bvdberg01 # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.monicahq.com/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os PHP_VERSION="8.2" PHP_APACHE="YES" PHP_MODULE="mysqli,pdo-mysql" setup_php setup_composer setup_mariadb MARIADB_DB_NAME="monica" MARIADB_DB_USER="monica" setup_mariadb_db NODE_VERSION="22" NODE_MODULE="yarn@latest" setup_nodejs fetch_and_deploy_gh_release "monica" "monicahq/monica" "prebuild" "latest" "/opt/monica" "monica-v*.tar.bz2" msg_info "Configuring monica" cd /opt/monica cp /opt/monica/.env.example /opt/monica/.env HASH_SALT=$(openssl rand -base64 32) sed -i -e "s|^DB_USERNAME=.*|DB_USERNAME=${MARIADB_DB_USER}|" \ -e "s|^DB_PASSWORD=.*|DB_PASSWORD=${MARIADB_DB_PASS}|" \ -e "s|^HASH_SALT=.*|HASH_SALT=${HASH_SALT}|" \ /opt/monica/.env $STD composer install --no-dev -o --no-interaction $STD yarn config set ignore-engines true $STD yarn install $STD yarn run production $STD php artisan key:generate $STD php artisan setup:production --email=admin@community-scripts.org --password=community-scripts.org --force chown -R www-data:www-data /opt/monica chmod -R 775 /opt/monica/storage echo "* * * * * root php /opt/monica/artisan schedule:run >> /dev/null 2>&1" >>/etc/crontab msg_ok "Configured monica" msg_info "Creating Service" cat </etc/apache2/sites-available/monica.conf ServerName monica DocumentRoot /opt/monica/public Options Indexes FollowSymLinks AllowOverride All Require all granted ErrorLog /var/log/apache2/monica_error.log CustomLog /var/log/apache2/monica_access.log combined EOF $STD a2ensite monica $STD a2enmod rewrite $STD a2dissite 000-default.conf $STD systemctl reload apache2 msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/motioneye-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/motioneye-project/motioneye source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os setup_hwaccel msg_info "Installing Dependencies" $STD apt install -y git $STD apt install -y cifs-utils msg_ok "Installed Dependencies" msg_info "Setup Python3" $STD apt install -y \ python3 \ python3-dev \ python3-pip rm -rf /usr/lib/python3.*/EXTERNALLY-MANAGED msg_ok "Setup Python3" msg_info "Installing Motion" $STD apt install -y motion systemctl stop motion $STD systemctl disable motion msg_ok "Installed Motion" msg_info "Installing FFmpeg" $STD apt install -y ffmpeg v4l-utils msg_ok "Installed FFmpeg" msg_info "Installing MotionEye" $STD apt update $STD pip install git+https://github.com/motioneye-project/motioneye.git@dev mkdir -p /etc/motioneye chown -R root:root /etc/motioneye chmod -R 777 /etc/motioneye curl -fsSL "https://raw.githubusercontent.com/motioneye-project/motioneye/dev/motioneye/extra/motioneye.conf.sample" -o "/etc/motioneye/motioneye.conf" mkdir -p /var/lib/motioneye msg_ok "Installed MotionEye" msg_info "Creating Service" curl -fsSL "https://raw.githubusercontent.com/motioneye-project/motioneye/dev/motioneye/extra/motioneye.systemd" -o "/etc/systemd/system/motioneye.service" sed -i 's/^User=.*/User=root/' /etc/systemd/system/motioneye.service systemctl enable -q --now motioneye msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/mqtt-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://mosquitto.org/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Mosquitto MQTT Broker" source /etc/os-release $STD apt update $STD apt -y install mosquitto mosquitto-clients cat </etc/mosquitto/conf.d/default.conf allow_anonymous false persistence true password_file /etc/mosquitto/passwd listener 1883 EOF msg_ok "Installed Mosquitto MQTT Broker" motd_ssh customize cleanup_lxc ================================================ FILE: install/myip-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: Slaviša Arežina (tremor021) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://ipcheck.ing/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os NODE_VERSION="24" setup_nodejs fetch_and_deploy_gh_release "myip" "jason5ng32/MyIP" "tarball" msg_info "Configuring MyIP" cd /opt/myip cp .env.example .env $STD npm install $STD npm run build msg_ok "Configured MyIP" msg_info "Creating Service" cat </etc/systemd/system/myip.service [Unit] Description=MyIP Service After=network.target [Service] Type=simple User=root WorkingDirectory=/opt/myip ExecStart=/usr/bin/npm start EnvironmentFile=/opt/myip/.env Restart=on-failure RestartSec=5 [Install] WantedBy=multi-user.target EOF systemctl enable -q --now myip msg_ok "Service created" motd_ssh customize cleanup_lxc ================================================ FILE: install/mylar3-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: davalanche | Co-Author: Slaviša Arežina (tremor021) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/mylar3/mylar3 source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" cat </etc/apt/sources.list.d/non-free.sources Types: deb URIs: http://deb.debian.org/debian Suites: bookworm Components: non-free non-free-firmware EOF $STD apt update $STD apt install -y unrar msg_ok "Installed Dependencies" PYTHON_VERSION="3.12" setup_uv fetch_and_deploy_gh_release "mylar3" "mylar3/mylar3" "tarball" msg_info "Installing ${APPLICATION}" mkdir -p /opt/mylar3-data $STD uv venv --clear /opt/mylar3/.venv $STD /opt/mylar3/.venv/bin/python -m ensurepip --upgrade $STD /opt/mylar3/.venv/bin/python -m pip install --upgrade pip $STD /opt/mylar3/.venv/bin/python -m pip install --no-cache-dir -r /opt/mylar3/requirements.txt msg_ok "Installed ${APPLICATION}" msg_info "Creating Service" cat </etc/systemd/system/mylar3.service [Unit] Description=Mylar3 Service After=network-online.target [Service] ExecStart=/opt/mylar3/.venv/bin/python /opt/mylar3/Mylar.py --daemon --nolaunch --datadir=/opt/mylar3-data GuessMainPID=no Type=forking Restart=on-failure [Install] WantedBy=multi-user.target EOF systemctl enable -q --now mylar3 msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/myspeed-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: MickLesk (Canbiz) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/gnmyt/myspeed source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ build-essential \ ca-certificates \ python3-setuptools msg_ok "Installed Dependencies" NODE_VERSION="22" setup_nodejs fetch_and_deploy_gh_release "myspeed" "gnmyt/myspeed" "prebuild" "latest" "/opt/myspeed" "MySpeed-*.zip" msg_info "Configuring MySpeed" cd /opt/myspeed $STD npm install msg_ok "Installed MySpeed" msg_info "Creating Service" cat </etc/systemd/system/myspeed.service [Unit] Description=MySpeed After=network.target [Service] Type=simple ExecStart=/usr/bin/node server Restart=always User=root Environment=NODE_ENV=production WorkingDirectory=/opt/myspeed [Install] WantedBy=multi-user.target EOF systemctl enable -q --now myspeed msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/mysql-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck # Co-Author: MickLesk (Canbiz) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.mysql.com/products/community | https://www.phpmyadmin.net source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ lsb-release msg_ok "Installed Dependencies" RELEASE_REPO="mysql-8.0" RELEASE_AUTH="mysql_native_password" read -r -p "${TAB3}Would you like to install the MySQL 8.4 LTS release instead of MySQL 8.0 (bug fix track; EOL April-2026)? " prompt if [[ "${prompt,,}" =~ ^(y|yes)$ ]]; then RELEASE_REPO="mysql-8.4-lts" RELEASE_AUTH="caching_sha2_password" fi msg_info "Installing MySQL" curl -fsSL https://repo.mysql.com/RPM-GPG-KEY-mysql-2023 | gpg --dearmor -o /usr/share/keyrings/mysql.gpg if [ "$(lsb_release -si)" = "Debian" ]; then cat </etc/apt/sources.list.d/mysql.sources Types: deb URIs: http://repo.mysql.com/apt/debian Suites: $(lsb_release -sc) Components: ${RELEASE_REPO} Signed-By: /usr/share/keyrings/mysql.gpg EOF else cat </etc/apt/sources.list.d/mysql.sources Types: deb URIs: http://repo.mysql.com/apt/ubuntu Suites: $(lsb_release -sc) Components: ${RELEASE_REPO} Signed-By: /usr/share/keyrings/mysql.gpg EOF fi $STD apt update export DEBIAN_FRONTEND=noninteractive $STD apt install -y \ mysql-community-client \ mysql-community-server msg_ok "Installed MySQL" msg_info "Configure MySQL Server" ADMIN_PASS="$(openssl rand -base64 18 | cut -c1-13)" $STD mysql -uroot -p"$ADMIN_PASS" -e "ALTER USER 'root'@'localhost' IDENTIFIED WITH $RELEASE_AUTH BY '$ADMIN_PASS'; FLUSH PRIVILEGES;" echo "" >~/mysql.creds echo -e "MySQL user: root" >>~/mysql.creds echo -e "MySQL password: $ADMIN_PASS" >>~/mysql.creds msg_ok "MySQL Server configured" read -r -p "${TAB3}Would you like to add PhpMyAdmin? " prompt if [[ ${prompt,,} =~ ^(y|yes)$ ]]; then bash -c "$(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/tools/addon/phpmyadmin.sh)" fi msg_info "Start Service" systemctl enable -q --now mysql msg_ok "Service started" motd_ssh customize cleanup_lxc ================================================ FILE: install/n8n-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) | Co-Author: CrazyWolf13 # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://n8n.io/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ build-essential \ python3 \ python3-setuptools \ graphicsmagick msg_ok "Installed Dependencies" NODE_VERSION="24" setup_nodejs msg_info "Installing n8n (Patience)" $STD npm install -g n8n msg_ok "Installed n8n" msg_info "Creating Service" cat </opt/n8n.env N8N_SECURE_COOKIE=false N8N_PORT=5678 N8N_PROTOCOL=http N8N_HOST=${LOCAL_IP} EOF cat </etc/systemd/system/n8n.service [Unit] Description=n8n [Service] Type=simple EnvironmentFile=/opt/n8n.env ExecStart=n8n start [Install] WantedBy=multi-user.target EOF systemctl enable -q --now n8n msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/nagios-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: CanbiZ (MickLesk) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/NagiosEnterprises/nagioscore source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ autoconf \ automake \ build-essential \ bc \ dc \ gawk \ gettext \ gperf \ libgd-dev \ libmcrypt-dev \ libnet-snmp-perl \ libssl-dev \ snmp \ apache2 \ apache2-utils msg_ok "Installed Dependencies" PHP_APACHE="YES" setup_php fetch_and_deploy_gh_release "nagios" "NagiosEnterprises/nagioscore" "tarball" msg_info "Building Nagios Core" cd /opt/nagios $STD ./configure --with-httpd-conf=/etc/apache2/sites-enabled $STD make all $STD make install-groups-users usermod -a -G nagios www-data $STD make install $STD make install-daemoninit $STD make install-commandmode $STD make install-config $STD make install-webconf $STD a2enmod rewrite $STD a2enmod cgi msg_ok "Built Nagios Core" fetch_and_deploy_gh_release "nagios-plugins" "nagios-plugins/nagios-plugins" "tarball" msg_info "Building Nagios Plugins" cd /opt/nagios-plugins $STD ./tools/setup $STD ./configure $STD make $STD make install setcap cap_net_raw+p /bin/ping msg_ok "Built Nagios Plugins" msg_info "Configuring Web Authentication" $STD htpasswd -bc /usr/local/nagios/etc/htpasswd.users nagiosadmin nagiosadmin chown root:www-data /usr/local/nagios/etc/htpasswd.users chmod 640 /usr/local/nagios/etc/htpasswd.users msg_ok "Configured Web Authentication" msg_info "Starting Services" systemctl enable -q apache2 systemctl restart apache2 systemctl enable -q --now nagios msg_ok "Started Services" motd_ssh customize cleanup_lxc ================================================ FILE: install/nametag-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/mattogodoy/nametag source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os PG_VERSION="16" setup_postgresql PG_DB_NAME="nametag_db" PG_DB_USER="nametag" setup_postgresql_db NODE_VERSION="20" setup_nodejs fetch_and_deploy_gh_release "nametag" "mattogodoy/nametag" "tarball" "latest" "/opt/nametag" msg_info "Setting up Application" cd /opt/nametag $STD npm ci DATABASE_URL="postgresql://${PG_DB_USER}:${PG_DB_PASS}@127.0.0.1:5432/${PG_DB_NAME}" $STD npx prisma generate DATABASE_URL="postgresql://${PG_DB_USER}:${PG_DB_PASS}@127.0.0.1:5432/${PG_DB_NAME}" $STD npx prisma migrate deploy msg_ok "Set up Application" msg_info "Configuring Nametag" NEXTAUTH_SECRET=$(openssl rand -base64 32) CRON_SECRET=$(openssl rand -base64 16) mkdir -p /opt/nametag/data/photos cat </opt/nametag/.env DATABASE_URL=postgresql://${PG_DB_USER}:${PG_DB_PASS}@127.0.0.1:5432/${PG_DB_NAME} NEXTAUTH_URL=http://${LOCAL_IP}:3000 NEXTAUTH_SECRET=${NEXTAUTH_SECRET} CRON_SECRET=${CRON_SECRET} PHOTO_STORAGE_PATH=/opt/nametag/data/photos NODE_ENV=production EOF msg_ok "Configured Nametag" msg_info "Building Application" cd /opt/nametag set -a source /opt/nametag/.env set +a $STD npm run build cp -r /opt/nametag/.next/static /opt/nametag/.next/standalone/.next/static cp -r /opt/nametag/public /opt/nametag/.next/standalone/public msg_ok "Built Application" msg_info "Running Production Seed" cd /opt/nametag $STD npx esbuild prisma/seed.production.ts --platform=node --format=cjs --outfile=prisma/seed.production.js --bundle --external:@prisma/client --external:pg --minify $STD node prisma/seed.production.js msg_ok "Ran Production Seed" msg_info "Creating Service" cat </etc/systemd/system/nametag.service [Unit] Description=Nametag - Personal Relationships Manager After=network.target postgresql.service [Service] Type=simple WorkingDirectory=/opt/nametag EnvironmentFile=/opt/nametag/.env ExecStart=/usr/bin/node /opt/nametag/.next/standalone/server.js Restart=on-failure RestartSec=5 [Install] WantedBy=multi-user.target EOF systemctl enable -q --now nametag msg_ok "Created Service" msg_info "Setting up Cron Jobs" cat </etc/cron.d/nametag 0 8 * * * root curl -sf -H "Authorization: Bearer ${CRON_SECRET}" http://127.0.0.1:3000/api/cron/send-reminders >/dev/null 2>&1 0 3 * * * root curl -sf -H "Authorization: Bearer ${CRON_SECRET}" http://127.0.0.1:3000/api/cron/purge-deleted >/dev/null 2>&1 EOF chmod 644 /etc/cron.d/nametag msg_ok "Set up Cron Jobs" motd_ssh customize cleanup_lxc ================================================ FILE: install/navidrome-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/navidrome/navidrome source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies (Patience)" $STD apt install -y ffmpeg msg_ok "Installed Dependencies" fetch_and_deploy_gh_release "navidrome" "navidrome/navidrome" "binary" msg_info "Starting Navidrome" systemctl enable -q --now navidrome msg_ok "Started Navidrome" read -p "${TAB3}Do you want to install filebrowser addon? (y/n) " -n 1 -r if [[ $REPLY =~ ^[Yy]$ ]]; then bash -c "$(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/tools/addon/filebrowser.sh)" fi motd_ssh customize cleanup_lxc ================================================ FILE: install/neko-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: CanbiZ (MickLesk) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://neko.m1k1o.net/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ supervisor \ pulseaudio \ dbus-x11 \ xserver-xorg-video-dummy \ xdotool \ xclip \ libgtk-3-0 \ gstreamer1.0-plugins-base \ gstreamer1.0-plugins-good \ gstreamer1.0-plugins-bad \ gstreamer1.0-plugins-ugly \ gstreamer1.0-pulseaudio \ openbox \ firefox-esr \ fonts-noto-color-emoji \ fonts-wqy-zenhei msg_ok "Installed Dependencies" systemctl disable -q --now supervisor msg_info "Installing Build Dependencies" $STD apt install -y \ build-essential \ pkg-config \ libx11-dev \ libxrandr-dev \ libxtst-dev \ libgtk-3-dev \ libxcvt-dev \ libgstreamer1.0-dev \ libgstreamer-plugins-base1.0-dev msg_ok "Installed Build Dependencies" NODE_VERSION="22" setup_nodejs setup_go fetch_and_deploy_gh_release "neko" "m1k1o/neko" "tarball" msg_info "Building Client" cd /opt/neko/client $STD npm install $STD npm run build mkdir -p /var/www cp -r /opt/neko/client/dist/* /var/www/ msg_ok "Built Client" msg_info "Building Server" cd /opt/neko/server $STD ./build cp /opt/neko/server/bin/neko /usr/bin/neko mkdir -p /etc/neko/plugins cp -r /opt/neko/server/bin/plugins/* /etc/neko/plugins/ 2>/dev/null || true msg_ok "Built Server" msg_info "Setting up Runtime" useradd -m -s /bin/bash neko usermod -aG audio,video neko mkdir -p /etc/neko/supervisord /var/www /var/log/neko /tmp/.X11-unix /tmp/runtime-neko /home/neko/.config/pulse /home/neko/.local/share/xorg chmod 1777 /tmp/.X11-unix chmod 1777 /var/log/neko chmod 0700 /tmp/runtime-neko chown neko /tmp/.X11-unix /var/log/neko /tmp/runtime-neko chown -R neko:neko /home/neko cp /opt/neko/runtime/xorg.conf /etc/neko/xorg.conf # Remove the dummy_touchscreen InputDevice section (requires custom "neko" Xorg driver not available bare-metal) sed -i '/Section "InputDevice"/{N;/dummy_touchscreen/{:l;N;/EndSection/!bl;d}}' /etc/neko/xorg.conf sed -i '/dummy_touchscreen/d' /etc/neko/xorg.conf sed -i 's/InputDevice "dummy_mouse"/InputDevice "dummy_mouse" "CorePointer"/' /etc/neko/xorg.conf cp /opt/neko/runtime/default.pa /etc/pulse/default.pa cat </etc/neko/supervisord.conf [supervisord] nodaemon=true user=root pidfile=/var/run/supervisord.pid logfile=/dev/null logfile_maxbytes=0 loglevel=debug [include] files=/etc/neko/supervisord/*.conf [program:x-server] environment=HOME="/home/neko",USER="neko" command=/usr/bin/X :99.0 -config /etc/neko/xorg.conf -noreset -nolisten tcp autorestart=true priority=300 user=neko stdout_logfile=/var/log/neko/xorg.log stdout_logfile_maxbytes=100MB stdout_logfile_backups=10 redirect_stderr=true [program:pulseaudio] environment=HOME="/home/neko",USER="neko",DISPLAY=":99.0" command=/usr/bin/pulseaudio --log-level=error --disallow-module-loading --disallow-exit --exit-idle-time=-1 autorestart=true priority=300 user=neko stdout_logfile=/var/log/neko/pulseaudio.log stdout_logfile_maxbytes=100MB stdout_logfile_backups=10 redirect_stderr=true [program:neko] environment=HOME="/home/neko",USER="neko",DISPLAY=":99.0" command=/usr/bin/neko serve --server.static "/var/www" stopsignal=INT stopwaitsecs=3 autorestart=true priority=800 user=neko stdout_logfile=/var/log/neko/neko.log stdout_logfile_maxbytes=100MB stdout_logfile_backups=10 redirect_stderr=true [unix_http_server] file=/var/run/supervisor.sock chmod=0770 chown=root:neko [supervisorctl] serverurl=unix:///var/run/supervisor.sock [rpcinterface:supervisor] supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface EOF cat </etc/neko/supervisord/firefox.conf [program:firefox] environment=HOME="/home/neko",USER="neko",DISPLAY=":99.0" command=/usr/bin/firefox-esr --no-remote --display=:99.0 -width 1280 -height 720 stopsignal=INT autorestart=true priority=800 user=neko stdout_logfile=/var/log/neko/firefox.log stdout_logfile_maxbytes=100MB stdout_logfile_backups=10 redirect_stderr=true [program:openbox] environment=HOME="/home/neko",USER="neko",DISPLAY=":99.0" command=/usr/bin/openbox --config-file /etc/neko/openbox.xml autorestart=true priority=300 user=neko stdout_logfile=/var/log/neko/openbox.log stdout_logfile_maxbytes=100MB stdout_logfile_backups=10 redirect_stderr=true EOF cat <<'EOF' >/etc/neko/openbox.xml no true yes normal yes no yes no 200 no Smart

yes
1 1 0 EOF cat </etc/neko/neko.yaml server: bind: "0.0.0.0:8080" static: "/var/www" session: cookie: enabled: false webrtc: icelite: true nat1to1: - "${LOCAL_IP}" epr: "59000-59100" desktop: input: enabled: false member: provider: "multiuser" multiuser: admin_password: "admin" user_password: "neko" EOF msg_ok "Set up Runtime" msg_info "Creating Service" cat </etc/systemd/system/neko.service [Unit] Description=Neko Virtual Browser After=network.target [Service] Type=simple User=root Environment=USER=neko Environment=DISPLAY=:99.0 Environment=PULSE_SERVER=unix:/tmp/pulseaudio.socket Environment=XDG_RUNTIME_DIR=/tmp/runtime-neko Environment=NEKO_PLUGINS_ENABLED=true Environment=NEKO_PLUGINS_DIR=/etc/neko/plugins/ Environment=NEKO_CONFIG=/etc/neko/neko.yaml ExecStart=/usr/bin/supervisord -c /etc/neko/supervisord.conf -n Restart=on-failure RestartSec=5 [Install] WantedBy=multi-user.target EOF systemctl enable -q --now neko msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/neo4j-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck # Co-Author: havardthom # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://neo4j.com/product/neo4j-graph-database/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os JAVA_VERSION="21" setup_java msg_info "Installing Neo4j (patience)" curl -fsSL "https://debian.neo4j.com/neotechnology.gpg.key" | gpg --dearmor -o /etc/apt/keyrings/neotechnology.gpg echo 'deb [signed-by=/etc/apt/keyrings/neotechnology.gpg] https://debian.neo4j.com stable latest' >/etc/apt/sources.list.d/neo4j.list $STD apt update $STD apt install -y neo4j sed -i '/server.default_listen_address/s/^#//' /etc/neo4j/neo4j.conf systemctl enable -q --now neo4j msg_ok "Installed Neo4j" motd_ssh customize cleanup_lxc ================================================ FILE: install/netbird-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: TechHutTV # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://netbird.io/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing NetBird" setup_deb822_repo \ "netbird" \ "https://pkgs.netbird.io/debian/public.key" \ "https://pkgs.netbird.io/debian" \ "stable" $STD apt install -y netbird msg_ok "Installed NetBird" msg_info "Starting NetBird Service" systemctl enable -q --now netbird msg_ok "Started NetBird Service" echo "" echo "" echo -e "${BL}NetBird Deployment Type${CL}" echo "─────────────────────────────────────────" echo "Are you using NetBird Managed or Self-Hosted?" echo "" echo " 1) NetBird Managed (default) - Use NetBird's managed service" echo " 2) Self-Hosted - Use your own NetBird management server" echo "" read -r -p "${TAB3}Select deployment type [1]: " DEPLOYMENT_TYPE DEPLOYMENT_TYPE="${DEPLOYMENT_TYPE:-1}" NETBIRD_MGMT_URL="" case "$DEPLOYMENT_TYPE" in 1) msg_ok "Using NetBird Managed service" ;; 2) echo "" echo -e "${BL}Self-Hosted Configuration${CL}" echo "─────────────────────────────────────────" echo "Enter your NetBird management server URL." echo "Example: https://management.example.com" echo "" read -r -p "Management URL: " NETBIRD_MGMT_URL if [[ -z "$NETBIRD_MGMT_URL" ]]; then msg_warn "No management URL provided. Run 'netbird up --management-url ' to connect." else NETBIRD_MGMT_URL="${NETBIRD_MGMT_URL%/}" msg_ok "Management URL configured: ${NETBIRD_MGMT_URL}" fi ;; *) msg_warn "Invalid selection. Using NetBird Managed service." ;; esac echo "" echo "" echo -e "${BL}NetBird Connection Setup${CL}" echo "─────────────────────────────────────────" echo "Choose how to connect to your NetBird network:" echo "" if [[ "$DEPLOYMENT_TYPE" == "1" ]]; then echo " 1) Setup Key (default) - Use a pre-generated setup key" echo " 2) SSO Login - Authenticate via browser with your identity provider" echo " 3) Skip - Configure later with 'netbird up'" else echo " 1) Setup Key (default) - Use a pre-generated setup key" echo " 2) Skip - Configure later with 'netbird up'" fi echo "" read -r -p "Select authentication method [1]: " AUTH_METHOD AUTH_METHOD="${AUTH_METHOD:-1}" if [[ "$DEPLOYMENT_TYPE" == "1" ]]; then case "$AUTH_METHOD" in 1) echo "" echo "Enter your NetBird setup key from the NetBird dashboard." echo "" read -r -p "Setup key: " NETBIRD_SETUP_KEY echo "" if [[ -z "$NETBIRD_SETUP_KEY" ]]; then msg_warn "No setup key provided. Run 'netbird up -k ' to connect." else msg_info "Connecting to NetBird with setup key" if $STD netbird up -k "$NETBIRD_SETUP_KEY"; then msg_ok "Connected to NetBird" else msg_warn "Connection failed. Run 'netbird up -k ' to retry." fi fi ;; 2) echo "" echo -e "${BL}SSO Authentication${CL}" echo "─────────────────────────────────────────" echo "A login URL will appear below." echo "Copy the URL and open it in your browser to authenticate." echo "" msg_info "Starting SSO login" netbird login 2>&1 || true echo "" msg_info "Connecting to NetBird" if $STD netbird up; then msg_ok "Connected to NetBird" else msg_warn "Connection failed. Run 'netbird up' to retry." fi ;; 3) msg_ok "Skipped. Run 'netbird up' to connect." ;; *) msg_warn "Invalid selection. Run 'netbird up' to connect." ;; esac else case "$AUTH_METHOD" in 1) echo "" echo "Enter your NetBird setup key from the NetBird dashboard." echo "" read -r -p "Setup key: " NETBIRD_SETUP_KEY echo "" if [[ -z "$NETBIRD_SETUP_KEY" ]]; then if [[ -z "$NETBIRD_MGMT_URL" ]]; then msg_warn "No setup key provided. Run 'netbird up -k --management-url ' to connect." else msg_warn "No setup key provided. Run 'netbird up -k --management-url $NETBIRD_MGMT_URL' to connect." fi else if [[ -z "$NETBIRD_MGMT_URL" ]]; then msg_error "Management URL is required for self-hosted deployments. Please configure it first." else msg_info "Connecting to NetBird with setup key" if $STD netbird up -k "$NETBIRD_SETUP_KEY" --management-url "$NETBIRD_MGMT_URL"; then msg_ok "Connected to NetBird" else msg_warn "Connection failed. Run 'netbird up -k --management-url $NETBIRD_MGMT_URL' to retry." fi fi fi ;; 2) if [[ -z "$NETBIRD_MGMT_URL" ]]; then msg_ok "Skipped. Run 'netbird up --management-url ' to connect." else msg_ok "Skipped. Run 'netbird up --management-url $NETBIRD_MGMT_URL' to connect." fi ;; *) if [[ -z "$NETBIRD_MGMT_URL" ]]; then msg_warn "Invalid selection. Run 'netbird up --management-url ' to connect." else msg_warn "Invalid selection. Run 'netbird up --management-url $NETBIRD_MGMT_URL' to connect." fi ;; esac fi motd_ssh customize cleanup_lxc ================================================ FILE: install/netboot-xyz-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://netboot.xyz source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ nginx \ tftpd-hpa msg_ok "Installed Dependencies" fetch_and_deploy_gh_release "netboot-xyz" "netbootxyz/netboot.xyz" "prebuild" "latest" "/var/www/html" "menus.tar.gz" # x86_64 UEFI bootloaders USE_ORIGINAL_FILENAME=true fetch_and_deploy_gh_release "netboot-xyz-efi" "netbootxyz/netboot.xyz" "singlefile" "latest" "/var/www/html" "netboot.xyz.efi" USE_ORIGINAL_FILENAME=true fetch_and_deploy_gh_release "netboot-xyz-efi-dsk" "netbootxyz/netboot.xyz" "singlefile" "latest" "/var/www/html" "netboot.xyz.efi.dsk" USE_ORIGINAL_FILENAME=true fetch_and_deploy_gh_release "netboot-xyz-snp" "netbootxyz/netboot.xyz" "singlefile" "latest" "/var/www/html" "netboot.xyz-snp.efi" USE_ORIGINAL_FILENAME=true fetch_and_deploy_gh_release "netboot-xyz-snp-dsk" "netbootxyz/netboot.xyz" "singlefile" "latest" "/var/www/html" "netboot.xyz-snp.efi.dsk" USE_ORIGINAL_FILENAME=true fetch_and_deploy_gh_release "netboot-xyz-snponly" "netbootxyz/netboot.xyz" "singlefile" "latest" "/var/www/html" "netboot.xyz-snponly.efi" # x86_64 metal (code-signed) UEFI bootloaders USE_ORIGINAL_FILENAME=true fetch_and_deploy_gh_release "netboot-xyz-metal" "netbootxyz/netboot.xyz" "singlefile" "latest" "/var/www/html" "netboot.xyz-metal.efi" USE_ORIGINAL_FILENAME=true fetch_and_deploy_gh_release "netboot-xyz-metal-dsk" "netbootxyz/netboot.xyz" "singlefile" "latest" "/var/www/html" "netboot.xyz-metal.efi.dsk" USE_ORIGINAL_FILENAME=true fetch_and_deploy_gh_release "netboot-xyz-metal-snp" "netbootxyz/netboot.xyz" "singlefile" "latest" "/var/www/html" "netboot.xyz-metal-snp.efi" USE_ORIGINAL_FILENAME=true fetch_and_deploy_gh_release "netboot-xyz-metal-snp-dsk" "netbootxyz/netboot.xyz" "singlefile" "latest" "/var/www/html" "netboot.xyz-metal-snp.efi.dsk" USE_ORIGINAL_FILENAME=true fetch_and_deploy_gh_release "netboot-xyz-metal-snponly" "netbootxyz/netboot.xyz" "singlefile" "latest" "/var/www/html" "netboot.xyz-metal-snponly.efi" # x86_64 BIOS/Legacy bootloaders USE_ORIGINAL_FILENAME=true fetch_and_deploy_gh_release "netboot-xyz-kpxe" "netbootxyz/netboot.xyz" "singlefile" "latest" "/var/www/html" "netboot.xyz.kpxe" USE_ORIGINAL_FILENAME=true fetch_and_deploy_gh_release "netboot-xyz-undionly" "netbootxyz/netboot.xyz" "singlefile" "latest" "/var/www/html" "netboot.xyz-undionly.kpxe" USE_ORIGINAL_FILENAME=true fetch_and_deploy_gh_release "netboot-xyz-metal-kpxe" "netbootxyz/netboot.xyz" "singlefile" "latest" "/var/www/html" "netboot.xyz-metal.kpxe" USE_ORIGINAL_FILENAME=true fetch_and_deploy_gh_release "netboot-xyz-lkrn" "netbootxyz/netboot.xyz" "singlefile" "latest" "/var/www/html" "netboot.xyz.lkrn" USE_ORIGINAL_FILENAME=true fetch_and_deploy_gh_release "netboot-xyz-linux-bin" "netbootxyz/netboot.xyz" "singlefile" "latest" "/var/www/html" "netboot.xyz-linux.bin" USE_ORIGINAL_FILENAME=true fetch_and_deploy_gh_release "netboot-xyz-dsk" "netbootxyz/netboot.xyz" "singlefile" "latest" "/var/www/html" "netboot.xyz.dsk" USE_ORIGINAL_FILENAME=true fetch_and_deploy_gh_release "netboot-xyz-pdsk" "netbootxyz/netboot.xyz" "singlefile" "latest" "/var/www/html" "netboot.xyz.pdsk" # ARM64 bootloaders USE_ORIGINAL_FILENAME=true fetch_and_deploy_gh_release "netboot-xyz-arm64" "netbootxyz/netboot.xyz" "singlefile" "latest" "/var/www/html" "netboot.xyz-arm64.efi" USE_ORIGINAL_FILENAME=true fetch_and_deploy_gh_release "netboot-xyz-arm64-snp" "netbootxyz/netboot.xyz" "singlefile" "latest" "/var/www/html" "netboot.xyz-arm64-snp.efi" USE_ORIGINAL_FILENAME=true fetch_and_deploy_gh_release "netboot-xyz-arm64-snponly" "netbootxyz/netboot.xyz" "singlefile" "latest" "/var/www/html" "netboot.xyz-arm64-snponly.efi" USE_ORIGINAL_FILENAME=true fetch_and_deploy_gh_release "netboot-xyz-metal-arm64" "netbootxyz/netboot.xyz" "singlefile" "latest" "/var/www/html" "netboot.xyz-metal-arm64.efi" USE_ORIGINAL_FILENAME=true fetch_and_deploy_gh_release "netboot-xyz-metal-arm64-snp" "netbootxyz/netboot.xyz" "singlefile" "latest" "/var/www/html" "netboot.xyz-metal-arm64-snp.efi" USE_ORIGINAL_FILENAME=true fetch_and_deploy_gh_release "netboot-xyz-metal-arm64-snponly" "netbootxyz/netboot.xyz" "singlefile" "latest" "/var/www/html" "netboot.xyz-metal-arm64-snponly.efi" # ISO and IMG images (for virtual/physical media creation) USE_ORIGINAL_FILENAME=true fetch_and_deploy_gh_release "netboot-xyz-iso" "netbootxyz/netboot.xyz" "singlefile" "latest" "/var/www/html" "netboot.xyz.iso" USE_ORIGINAL_FILENAME=true fetch_and_deploy_gh_release "netboot-xyz-img" "netbootxyz/netboot.xyz" "singlefile" "latest" "/var/www/html" "netboot.xyz.img" USE_ORIGINAL_FILENAME=true fetch_and_deploy_gh_release "netboot-xyz-arm64-iso" "netbootxyz/netboot.xyz" "singlefile" "latest" "/var/www/html" "netboot.xyz-arm64.iso" USE_ORIGINAL_FILENAME=true fetch_and_deploy_gh_release "netboot-xyz-arm64-img" "netbootxyz/netboot.xyz" "singlefile" "latest" "/var/www/html" "netboot.xyz-arm64.img" USE_ORIGINAL_FILENAME=true fetch_and_deploy_gh_release "netboot-xyz-multiarch-iso" "netbootxyz/netboot.xyz" "singlefile" "latest" "/var/www/html" "netboot.xyz-multiarch.iso" USE_ORIGINAL_FILENAME=true fetch_and_deploy_gh_release "netboot-xyz-multiarch-img" "netbootxyz/netboot.xyz" "singlefile" "latest" "/var/www/html" "netboot.xyz-multiarch.img" # SHA256 checksums USE_ORIGINAL_FILENAME=true fetch_and_deploy_gh_release "netboot-xyz-checksums" "netbootxyz/netboot.xyz" "singlefile" "latest" "/var/www/html" "netboot.xyz-sha256-checksums.txt" msg_info "Configuring Webserver" rm -f /etc/nginx/sites-enabled/default cat <<'EOF' >/etc/nginx/sites-available/netboot-xyz server { listen 80 default_server; listen [::]:80 default_server; root /var/www/html; server_name _; location / { autoindex on; add_header Access-Control-Allow-Origin "*"; add_header Access-Control-Allow-Headers "Content-Type"; } # The index.html from menus.tar.gz links bootloaders under /ipxe/ — # serve them from the same root directory via alias location /ipxe/ { alias /var/www/html/; autoindex on; add_header Access-Control-Allow-Origin "*"; } } EOF ln -sf /etc/nginx/sites-available/netboot-xyz /etc/nginx/sites-enabled/netboot-xyz $STD systemctl reload nginx msg_ok "Configured Webserver" msg_info "Configuring TFTP Server" cat </etc/default/tftpd-hpa TFTP_USERNAME="tftp" TFTP_DIRECTORY="/var/www/html" TFTP_ADDRESS="0.0.0.0:69" TFTP_OPTIONS="--secure" EOF systemctl enable -q --now tftpd-hpa msg_ok "Configured TFTP Server" motd_ssh customize cleanup_lxc ================================================ FILE: install/netbox-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: bvdberg01 # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://netboxlabs.com/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ apache2 \ redis-server \ build-essential \ libxml2-dev \ libxslt1-dev \ libffi-dev \ libpq-dev \ libssl-dev \ zlib1g-dev msg_ok "Installed Dependencies" PG_VERSION="16" setup_postgresql PG_DB_NAME="netbox" PG_DB_USER="netbox" setup_postgresql_db msg_info "Installing Python" $STD apt install -y \ python3 \ python3-pip \ python3-venv \ python3-dev msg_ok "Installed Python" fetch_and_deploy_gh_release "netbox" "netbox-community/netbox" "tarball" msg_info "Configuring NetBox (Patience)" cd /opt/netbox mkdir -p /opt/netbox/netbox/media $STD adduser --system --group netbox chown -R netbox /opt/netbox/netbox mv /opt/netbox/netbox/netbox/configuration_example.py /opt/netbox/netbox/netbox/configuration.py SECRET_KEY=$(python3 /opt/netbox/netbox/generate_secret_key.py) ESCAPED_SECRET_KEY=$(printf '%s\n' "$SECRET_KEY" | sed 's/[&/\]/\\&/g') sed -i -e 's/ALLOWED_HOSTS = \[\]/ALLOWED_HOSTS = ["*"]/' \ -e "s|SECRET_KEY = ''|SECRET_KEY = '${ESCAPED_SECRET_KEY}'|" \ -e "/DATABASES = {/,/}/s/'USER': '[^']*'/'USER': '$PG_DB_USER'/" \ -e "/DATABASES = {/,/}/s/'PASSWORD': '[^']*'/'PASSWORD': '$PG_DB_PASS'/" /opt/netbox/netbox/netbox/configuration.py $STD /opt/netbox/upgrade.sh ln -s /opt/netbox/contrib/netbox-housekeeping.sh /etc/cron.daily/netbox-housekeeping mv /opt/netbox/contrib/apache.conf /etc/apache2/sites-available/netbox.conf $STD openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout /etc/ssl/private/netbox.key -out /etc/ssl/certs/netbox.crt -subj "/C=US/O=NetBox/OU=Certificate/CN=localhost" $STD a2enmod ssl proxy proxy_http headers rewrite $STD a2ensite netbox systemctl restart apache2 mv /opt/netbox/contrib/gunicorn.py /opt/netbox/gunicorn.py mv /opt/netbox/contrib/*.service /etc/systemd/system/ systemctl daemon-reload systemctl enable -q --now netbox netbox-rq echo -e "Netbox Secret: \e[32m$SECRET_KEY\e[0m" >>~/netbox.creds msg_ok "Configured NetBox" msg_info "Setting up Django Admin" DJANGO_USER=Admin DJANGO_PASS=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | cut -c1-13) source /opt/netbox/venv/bin/activate $STD python3 /opt/netbox/netbox/manage.py shell <>~/netbox.creds msg_ok "Setup Django Admin" motd_ssh customize cleanup_lxc ================================================ FILE: install/nextcloudpi-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://nextcloudpi.com/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_warn "WARNING: This script will run an external installer from a third-party source (https://nextcloudpi.com/)." msg_warn "The following code is NOT maintained or audited by our repository." msg_warn "If you have any doubts or concerns, please review the installer code before proceeding:" msg_custom "${TAB3}${GATEWAY}${BGN}${CL}" "\e[1;34m" "→ https://raw.githubusercontent.com/nextcloud/nextcloudpi/master/install.sh" echo read -r -p "${TAB3}Do you want to continue? [y/N]: " CONFIRM if [[ ! "$CONFIRM" =~ ^([yY][eE][sS]|[yY])$ ]]; then msg_error "Aborted by user. No changes have been made." exit 10 fi msg_info "Installing NextCloudPi (Patience)" $STD bash <(curl -fsSL https://raw.githubusercontent.com/nextcloud/nextcloudpi/master/install.sh) msg_ok "Installed NextCloudPi" motd_ssh customize cleanup_lxc ================================================ FILE: install/nextexplorer-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: vhsdream # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/nxzai/nextExplorer source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ ripgrep \ imagemagick \ ffmpeg \ libva-drm2 \ libva2 \ mesa-va-drivers \ vainfo msg_ok "Installed Dependencies" NODE_VERSION="24" setup_nodejs fetch_and_deploy_gh_release "nextExplorer" "nxzai/nextExplorer" "tarball" "latest" "/opt/nextExplorer" msg_info "Building nextExplorer" APP_DIR="/opt/nextExplorer/app" LOCAL_IP="$(hostname -I | awk '{print $1}')" mkdir -p "$APP_DIR" mkdir -p /etc/nextExplorer cd /opt/nextExplorer export NODE_ENV=production $STD npm ci --omit=dev --workspace backend mv node_modules "$APP_DIR" mv backend/{src,package.json} "$APP_DIR" unset NODE_ENV export NODE_ENV=development export NODE_OPTIONS="--max-old-space-size=2048" $STD npm ci --workspace frontend $STD npm run -w frontend build -- --sourcemap false unset NODE_ENV mv frontend/dist/ "$APP_DIR"/src/public msg_ok "Built nextExplorer" msg_info "Configuring nextExplorer" SECRET=$(openssl rand -hex 32) cat </etc/nextExplorer/.env NODE_ENV=production PORT=3000 VOLUME_ROOT=/mnt CONFIG_DIR=/etc/nextExplorer CACHE_DIR=/etc/nextExplorer/cache # USER_ROOT= PUBLIC_URL=${LOCAL_IP}:3000 # TRUST_PROXY= # CORS_ORIGINS= TERMINAL_ENABLED=false LOG_LEVEL=info DEBUG=false ENABLE_HTTP_LOGGING=false AUTH_ENABLED=true AUTH_MODE=both SESSION_SECRET="${SECRET}" # AUTH_MAX_FAILED= # AUTH_LOCK_MINUTES= # AUTH_USER_EMAIL= # AUTH_USER_PASSWORD= # OIDC_ENABLED= # OIDC_ISSUER= # OIDC_AUTHORIZATION_URL= # OIDC_TOKEN_URL= # OIDC_USERINFO_URL= # OIDC_CLIENT_ID= # OIDC_CLIENT_SECRET= # OIDC_CALLBACK_URL= # OIDC_LOGOUT_URL= # OIDC_SCOPES= # OIDC_AUTO_CREATE_USERS=true # SEARCH_DEEP= # SEARCH_RIPGREP= # SEARCH_MAX_FILESIZE= # ONLYOFFICE_URL= # ONLYOFFICE_SECRET= # ONLYOFFICE_LANG= # ONLYOFFICE_FORCE_SAVE= # ONLYOFFICE_FILE_EXTENSIONS= # COLLABORA_URL= # COLLABORA_DISCOVERY_URL= # COLLABORA_SECRET= # COLLABORA_LANG= # COLLABORA_FILE_EXTENSIONS= SHOW_VOLUME_USAGE=true # USER_DIR_ENABLED= # SKIP_HOME= # EDITOR_EXTENSIONS= # FFMPEG_PATH= # FFPROBE_PATH= ## Hardware acceleration # FFMPEG_HWACCEL=vaapi # FFMPEG_HWACCEL_DEVICE=/dev/dri/renderD128 # FFMPEG_HWACCEL_OUTPUT_FORMAT=nv12 FAVORITES_DEFAULT_ICON=outline.StarIcon SHARES_ENABLED=true # SHARES_TOKEN_LENGTH=10 # SHARES_MAX_PER_USER=100 # SHARES_DEFAULT_EXPIRY_DAYS=30 # SHARES_GUEST_SESSION_HOURS=24 # SHARES_ALLOW_PASSWORD=true # SHARES_ALLOW_ANONYMOUS=true EOF chmod 600 /etc/nextExplorer/.env $STD useradd -U -s /usr/sbin/nologin -m -d /home/explorer explorer chown -R explorer:explorer "$APP_DIR" /etc/nextExplorer sed -i "\|version|s|$(jq -cr '.version' ${APP_DIR}/package.json)|$(cat ~/.nextexplorer)|" "$APP_DIR"/package.json msg_ok "Configured nextExplorer" msg_info "Creating nextExplorer Service" cat </etc/systemd/system/nextexplorer.service [Unit] Description=nextExplorer Service After=network.target [Service] Type=simple User=explorer Group=explorer WorkingDirectory=/opt/nextExplorer/app EnvironmentFile=/etc/nextExplorer/.env ExecStart=/usr/bin/node ./src/server.js Restart=always RestartSec=5 StandardOutput=journal StandardError=journal [Install] WantedBy=multi-user.target EOF $STD systemctl enable -q --now nextexplorer msg_ok "Created nextExplorer Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/nextpvr-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (Canbiz) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://nextpvr.com/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os setup_hwaccel msg_info "Installing Dependencies (Patience)" $STD apt install -y \ mediainfo \ libmediainfo-dev \ libc6 \ libgdiplus \ acl \ dvb-tools \ libdvbv5-0 \ dtv-scan-tables \ libc6-dev \ ffmpeg msg_ok "Installed Dependencies" msg_info "Setup NextPVR (Patience)" cd /opt curl -fsSL "https://nextpvr.com/nextpvr-helper.deb" -o "/opt/nextpvr-helper.deb" $STD dpkg -i nextpvr-helper.deb rm -rf /opt/nextpvr-helper.deb msg_ok "Installed NextPVR" motd_ssh customize cleanup_lxc ================================================ FILE: install/nginx-ui-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://nginxui.com source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ nginx \ logrotate msg_ok "Installed Dependencies" fetch_and_deploy_gh_release "nginx-ui" "0xJacky/nginx-ui" "prebuild" "latest" "/opt/nginx-ui" "nginx-ui-linux-arm64.tar.gz" msg_info "Installing Nginx UI" cp /opt/nginx-ui/nginx-ui /usr/local/bin/nginx-ui chmod +x /usr/local/bin/nginx-ui rm -rf /opt/nginx-ui msg_ok "Installed Nginx UI" msg_info "Configuring Nginx UI" mkdir -p /usr/local/etc/nginx-ui cat </usr/local/etc/nginx-ui/app.ini [app] PageSize = 10 [server] Host = 0.0.0.0 Port = 9000 RunMode = release [cert] HTTPChallengePort = 9180 [terminal] StartCmd = login EOF msg_ok "Configured Nginx UI" msg_info "Creating Service" cat </etc/systemd/system/nginx-ui.service [Unit] Description=Another WebUI for Nginx Documentation=https://nginxui.com After=network.target nginx.service [Service] Type=simple ExecStart=/usr/local/bin/nginx-ui --config /usr/local/etc/nginx-ui/app.ini RuntimeDirectory=nginx-ui WorkingDirectory=/var/run/nginx-ui Restart=on-failure TimeoutStopSec=5 KillMode=mixed [Install] WantedBy=multi-user.target EOF systemctl daemon-reload msg_ok "Created Service" msg_info "Starting Service" systemctl enable -q --now nginx-ui rm -rf /etc/nginx/sites-enabled/default msg_ok "Started Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/nginxproxymanager-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: tteck (tteckster) | Co-Author: CrazyWolf13 # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://nginxproxymanager.com/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ apache2-utils \ logrotate \ build-essential \ git msg_ok "Installed Dependencies" msg_info "Installing Python Dependencies" $STD apt install -y python3 \ python3-dev \ python3-pip \ python3-venv \ python3-cffi \ python3-certbot \ python3-certbot-dns-cloudflare \ golang rm -rf /usr/lib/python3.*/EXTERNALLY-MANAGED $STD pip3 install certbot-dns-multi==4.15.0 $STD python3 -m venv /opt/certbot/ msg_ok "Installed Python Dependencies" msg_info "Setting up Certbot" $STD python3 -m venv /opt/certbot $STD /opt/certbot/bin/pip install --upgrade pip setuptools wheel $STD /opt/certbot/bin/pip install certbot certbot-dns-cloudflare ln -sf /opt/certbot/bin/certbot /usr/local/bin/certbot msg_ok "Set up Certbot" fetch_and_deploy_gh_release "openresty" "openresty/openresty" "prebuild" "latest" "/opt/openresty" "openresty-*.tar.gz" msg_info "Building OpenResty" cd /opt/openresty $STD ./configure \ --with-http_v2_module \ --with-http_realip_module \ --with-http_stub_status_module \ --with-http_ssl_module \ --with-http_sub_module \ --with-http_auth_request_module \ --with-pcre-jit \ --with-stream \ --with-stream_ssl_module $STD make -j"$(nproc)" $STD make install rm -rf /opt/openresty cat <<'EOF' >/lib/systemd/system/openresty.service [Unit] Description=The OpenResty Application Platform After=syslog.target network-online.target remote-fs.target nss-lookup.target Wants=network-online.target [Service] Type=simple ExecStartPre=-/bin/mkdir -p /tmp/nginx/body /run/nginx ExecStartPre=/usr/local/openresty/nginx/sbin/nginx -t ExecStart=/usr/local/openresty/nginx/sbin/nginx -g 'daemon off;' [Install] WantedBy=multi-user.target EOF msg_ok "Built OpenResty" NODE_VERSION="22" NODE_MODULE="yarn" setup_nodejs RELEASE=$(get_latest_github_release "NginxProxyManager/nginx-proxy-manager") fetch_and_deploy_gh_release "nginxproxymanager" "NginxProxyManager/nginx-proxy-manager" "tarball" "v${RELEASE}" msg_info "Setting up Environment" ln -sf /usr/bin/python3 /usr/bin/python ln -sf /usr/local/openresty/nginx/sbin/nginx /usr/sbin/nginx ln -sf /usr/local/openresty/nginx/ /etc/nginx sed -i "0,/\"version\": \"[^\"]*\"/s|\"version\": \"[^\"]*\"|\"version\": \"$RELEASE\"|" /opt/nginxproxymanager/backend/package.json sed -i "0,/\"version\": \"[^\"]*\"/s|\"version\": \"[^\"]*\"|\"version\": \"$RELEASE\"|" /opt/nginxproxymanager/frontend/package.json sed -i 's+^daemon+#daemon+g' /opt/nginxproxymanager/docker/rootfs/etc/nginx/nginx.conf NGINX_CONFS=$(find /opt/nginxproxymanager -type f -name "*.conf") for NGINX_CONF in $NGINX_CONFS; do sed -i 's+include conf.d+include /etc/nginx/conf.d+g' "$NGINX_CONF" done mkdir -p /var/www/html /etc/nginx/logs cp -r /opt/nginxproxymanager/docker/rootfs/var/www/html/* /var/www/html/ cp -r /opt/nginxproxymanager/docker/rootfs/etc/nginx/* /etc/nginx/ cp /opt/nginxproxymanager/docker/rootfs/etc/letsencrypt.ini /etc/letsencrypt.ini cp /opt/nginxproxymanager/docker/rootfs/etc/logrotate.d/nginx-proxy-manager /etc/logrotate.d/nginx-proxy-manager ln -sf /etc/nginx/nginx.conf /etc/nginx/conf/nginx.conf rm -f /etc/nginx/conf.d/dev.conf mkdir -p /tmp/nginx/body \ /run/nginx \ /data/nginx \ /data/custom_ssl \ /data/logs \ /data/access \ /data/nginx/default_host \ /data/nginx/default_www \ /data/nginx/proxy_host \ /data/nginx/redirection_host \ /data/nginx/stream \ /data/nginx/dead_host \ /data/nginx/temp \ /var/lib/nginx/cache/public \ /var/lib/nginx/cache/private \ /var/cache/nginx/proxy_temp chmod -R 777 /var/cache/nginx chown root /tmp/nginx echo resolver "$(awk 'BEGIN{ORS=" "} $1=="nameserver" {print ($2 ~ ":")? "["$2"]": $2}' /etc/resolv.conf);" >/etc/nginx/conf.d/include/resolvers.conf if [ ! -f /data/nginx/dummycert.pem ] || [ ! -f /data/nginx/dummykey.pem ]; then $STD openssl req -new -newkey rsa:2048 -days 3650 -nodes -x509 -subj "/O=Nginx Proxy Manager/OU=Dummy Certificate/CN=localhost" -keyout /data/nginx/dummykey.pem -out /data/nginx/dummycert.pem fi mkdir -p /app/frontend/images cp -r /opt/nginxproxymanager/backend/* /app msg_ok "Set up Environment" msg_info "Building Frontend" export NODE_OPTIONS="--max_old_space_size=2048 --openssl-legacy-provider" cd /opt/nginxproxymanager/frontend # Replace node-sass with sass in package.json before installation sed -E -i 's/"node-sass" *: *"([^"]*)"/"sass": "\1"/g' package.json $STD yarn install --network-timeout 600000 $STD yarn locale-compile $STD yarn build cp -r /opt/nginxproxymanager/frontend/dist/* /app/frontend cp -r /opt/nginxproxymanager/frontend/public/images/* /app/frontend/images msg_ok "Built Frontend" msg_info "Initializing Backend" rm -rf /app/config/default.json if [ ! -f /app/config/production.json ]; then cat <<'EOF' >/app/config/production.json { "database": { "engine": "knex-native", "knex": { "client": "better-sqlite3", "connection": { "filename": "/data/database.sqlite" }, "useNullAsDefault": true } } } EOF fi cd /app $STD yarn install --network-timeout 600000 msg_ok "Initialized Backend" msg_info "Creating Service" cat <<'EOF' >/lib/systemd/system/npm.service [Unit] Description=Nginx Proxy Manager After=network.target Wants=openresty.service [Service] Type=simple Environment=NODE_ENV=production ExecStartPre=-mkdir -p /tmp/nginx/body /data/letsencrypt-acme-challenge ExecStart=/usr/bin/node index.js --abort_on_uncaught_exception --max_old_space_size=250 WorkingDirectory=/app Restart=on-failure [Install] WantedBy=multi-user.target EOF msg_ok "Created Service" msg_info "Starting Services" sed -i 's/user npm/user root/g; s/^pid/#pid/g' /usr/local/openresty/nginx/conf/nginx.conf sed -r -i 's/^([[:space:]]*)su npm npm/\1#su npm npm/g;' /etc/logrotate.d/nginx-proxy-manager systemctl enable -q --now openresty systemctl enable -q --now npm msg_ok "Started Services" motd_ssh customize cleanup_lxc ================================================ FILE: install/nightscout-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: aendel # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/nightscout/cgm-remote-monitor source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ build-essential \ libssl-dev \ openssl msg_ok "Installed Dependencies" MONGO_VERSION="8.0" setup_mongodb NODE_VERSION="22" setup_nodejs fetch_and_deploy_gh_release "nightscout" "nightscout/cgm-remote-monitor" "tarball" msg_info "Installing Nightscout" $STD npm install --prefix /opt/nightscout msg_ok "Installed Nightscout" msg_info "Creating Service" useradd -s /bin/bash -m nightscout chown -R nightscout:nightscout /opt/nightscout API_SECRET=$(openssl rand -hex 16) cat </opt/nightscout/my.env MONGO_CONNECTION=mongodb://127.0.0.1:27017/nightscout BASE_URL=http://localhost:1337 API_SECRET=${API_SECRET} DISPLAY_UNITS=mg/dl ENABLE=careportal boluscalc food bwp cage sage iage iob cob basal ar2 rawbg pushover bgi pump openaps pvb linear custom INSECURE_USE_HTTP=true EOF chown nightscout:nightscout /opt/nightscout/my.env cat </etc/systemd/system/nightscout.service [Unit] Description=Nightscout CGM Service After=network.target mongodb.service [Service] Type=simple User=nightscout WorkingDirectory=/opt/nightscout EnvironmentFile=/opt/nightscout/my.env ExecStart=/usr/bin/npm start Restart=always [Install] WantedBy=multi-user.target EOF systemctl enable -q --now nightscout msg_ok "Created Service" { echo "Nightscout Credentials" echo "API_SECRET: ${API_SECRET}" } >> ~/nightscout.creds motd_ssh customize cleanup_lxc ================================================ FILE: install/nocodb-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.nocodb.com/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os fetch_and_deploy_gh_release "nocodb" "nocodb/nocodb" "singlefile" "0.301.1" "/opt/nocodb/" "Noco-linux-arm64" msg_info "Creating Service" cat </etc/systemd/system/nocodb.service [Unit] Description=nocodb [Service] Type=simple Restart=always User=root WorkingDirectory=/opt/nocodb ExecStart=/opt/nocodb/./nocodb [Install] WantedBy=multi-user.target EOF systemctl enable -q --now nocodb msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/node-red-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://nodered.org/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ git \ ca-certificates msg_ok "Installed Dependencies" NODE_VERSION="22" setup_nodejs msg_info "Installing Node-Red" $STD npm install -g --unsafe-perm node-red echo "journalctl -f -n 100 -u nodered -o cat" >/usr/bin/node-red-log chmod +x /usr/bin/node-red-log echo "systemctl stop nodered" >/usr/bin/node-red-stop chmod +x /usr/bin/node-red-stop echo "systemctl start nodered" >/usr/bin/node-red-start chmod +x /usr/bin/node-red-start echo "systemctl restart nodered" >/usr/bin/node-red-restart chmod +x /usr/bin/node-red-restart msg_ok "Installed Node-Red" msg_info "Creating Service" service_path="/etc/systemd/system/nodered.service" echo "[Unit] Description=Node-RED After=syslog.target network.target [Service] ExecStart=/usr/bin/node-red --max-old-space-size=128 -v Restart=on-failure KillSignal=SIGINT SyslogIdentifier=node-red StandardOutput=syslog WorkingDirectory=/root/ User=root Group=root [Install] WantedBy=multi-user.target" >$service_path systemctl enable -q --now nodered msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/nodebb-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2024 tteck # Author: MickLesk (Canbiz) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/NodeBB/NodeBB source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies (Patience)" $STD apt install -y \ build-essential \ redis-server \ expect \ ca-certificates msg_ok "Installed Dependencies" setup_mongodb NODE_VERSION="22" setup_nodejs msg_info "Configuring MongoDB" MONGO_ADMIN_USER="admin" MONGO_ADMIN_PWD="$(openssl rand -base64 18 | cut -c1-13)" NODEBB_USER="nodebb" NODEBB_PWD="$(openssl rand -base64 18 | cut -c1-13)" MONGO_CONNECTION_STRING="mongodb://${NODEBB_USER}:${NODEBB_PWD}@localhost:27017/nodebb" NODEBB_SECRET=$(uuidgen) { echo "NodeBB-Credentials" echo "Mongo Database User: $MONGO_ADMIN_USER" echo "Mongo Database Password: $MONGO_ADMIN_PWD" echo "NodeBB User: $NODEBB_USER" echo "NodeBB Password: $NODEBB_PWD" echo "NodeBB Secret: $NODEBB_SECRET" } >>~/nodebb.creds $STD mongosh <> /etc/mongod.conf' systemctl restart mongod msg_ok "MongoDB configured" fetch_and_deploy_gh_release "nodebb" "NodeBB/NodeBB" "tarball" msg_info "Configuring NodeBB" cd /opt/nodebb touch pidfile expect </dev/null 2>&1 log_file /dev/null set timeout -1 spawn ./nodebb setup expect "URL used to access this NodeBB" { send "http://localhost:4567\r" } expect "Please enter a NodeBB secret" { send "$NODEBB_SECRET\r" } expect "Would you like to submit anonymous plugin usage to nbbpm? (yes)" { send "no\r" } expect "Which database to use (mongo)" { send "mongo\r" } expect "Format: mongodb://*" { send "$MONGO_CONNECTION_STRING\r" } expect "Administrator username" { send "community-scripts\r" } expect "Administrator email address" { send "admin@community-scripts.org\r" } expect "Password" { send "community-scripts\r" } expect "Confirm Password" { send "community-scripts\r" } expect eof EOF msg_ok "Configured NodeBB" msg_info "Creating Services" cat </etc/systemd/system/nodebb.service [Unit] Description=NodeBB Documentation=https://docs.nodebb.org After=system.slice multi-user.target mongod.service [Service] Type=forking User=root WorkingDirectory=/opt/nodebb PIDFile=/opt/nodebb/pidfile ExecStart=/usr/bin/node /opt/nodebb/loader.js Restart=always [Install] WantedBy=multi-user.target EOF systemctl enable -q --now nodebb msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/nodecast-tv-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: luismco # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/technomancer702/nodecast-tv source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os fetch_and_deploy_gh_release "nodecast-tv" "technomancer702/nodecast-tv" "tarball" NODE_VERSION="20" setup_nodejs msg_info "Installing Dependencies" $STD apt install -y ffmpeg msg_ok "Installed Dependencies" msg_info "Installing Modules" cd /opt/nodecast-tv $STD npm install msg_ok "Installed Modules" msg_info "Creating Service" cat </etc/systemd/system/nodecast-tv.service [Unit] Description=nodecast-tv After=network.target Wants=network.target [Service] Type=simple WorkingDirectory=/opt/nodecast-tv ExecStart=/bin/npm run dev Restart=on-failure RestartSec=10 [Install] WantedBy=multi-user.target EOF systemctl enable -q --now nodecast-tv msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/notifiarr-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://notifiarr.com/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Setting up Notifiarr" $STD groupadd notifiarr $STD useradd -g notifiarr notifiarr setup_deb822_repo \ "notifiarr" \ "https://packagecloud.io/golift/pkgs/gpgkey" \ "https://packagecloud.io/golift/pkgs/ubuntu" \ "focal" $STD apt install -y notifiarr msg_ok "Setup Notifiarr" motd_ssh customize cleanup_lxc ================================================ FILE: install/npmplus-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/ZoeyVid/NPMplus source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apk add \ tzdata \ gawk \ yq msg_ok "Installed Dependencies" msg_info "Installing Docker & Compose" $STD apk add docker $STD rc-service docker start $STD rc-update add docker default get_latest_release() { curl -fsSL https://api.github.com/repos/$1/releases/latest | grep '"tag_name":' | cut -d'"' -f4 } DOCKER_COMPOSE_LATEST_VERSION=$(get_latest_release "docker/compose") DOCKER_CONFIG=${DOCKER_CONFIG:-$HOME/.docker} mkdir -p $DOCKER_CONFIG/cli-plugins curl -fsSL https://github.com/docker/compose/releases/download/$DOCKER_COMPOSE_LATEST_VERSION/docker-compose-linux-aarch64 -o ~/.docker/cli-plugins/docker-compose chmod +x $DOCKER_CONFIG/cli-plugins/docker-compose msg_ok "Installed Docker & Compose" msg_info "Fetching NPMplus" cd /opt curl -fsSL "https://raw.githubusercontent.com/ZoeyVid/NPMplus/refs/heads/develop/compose.yaml" -o compose.yaml msg_ok "Fetched NPMplus" attempts=0 while true; do read -r -p "${TAB3}Enter your TZ Identifier (e.g., Europe/Berlin): " TZ_INPUT if validate_tz "$TZ_INPUT"; then break fi msg_error "Invalid timezone! Please enter a valid TZ identifier." attempts=$((attempts + 1)) if [[ "$attempts" -ge 3 ]]; then msg_error "Maximum attempts reached. Exiting." exit 254 fi done read -r -p "${TAB3}Enter your ACME Email: " ACME_EMAIL_INPUT yq -i " .services.npmplus.environment |= (map(select(. != \"TZ=*\" and . != \"ACME_EMAIL=*\" and . != \"INITIAL_ADMIN_EMAIL=*\" and . != \"INITIAL_ADMIN_PASSWORD=*\")) + [\"TZ=$TZ_INPUT\", \"ACME_EMAIL=$ACME_EMAIL_INPUT\", \"INITIAL_ADMIN_EMAIL=admin@local.com\", \"INITIAL_ADMIN_PASSWORD=community-scripts.org\"]) " /opt/compose.yaml msg_info "Building and Starting NPMplus (Patience)" $STD docker compose up -d CONTAINER_ID="" for i in {1..60}; do CONTAINER_ID=$(docker ps --filter "name=npmplus" --format "{{.ID}}") if [[ -n "$CONTAINER_ID" ]]; then STATUS=$(docker inspect --format '{{.State.Health.Status}}' "$CONTAINER_ID" 2>/dev/null || echo "starting") if [[ "$STATUS" == "healthy" ]]; then msg_ok "NPMplus is running and healthy" break elif [[ "$STATUS" == "unhealthy" ]]; then msg_error "NPMplus container is unhealthy! Check logs." docker logs "$CONTAINER_ID" exit 150 fi fi sleep 2 [[ $i -eq 60 ]] && msg_error "NPMplus container did not become healthy within 120s." && docker logs "$CONTAINER_ID" && exit 150 done msg_ok "Builded and started NPMplus" motd_ssh customize ================================================ FILE: install/ntfy-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: CrazyWolf13 # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://ntfy.sh/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Setting up ntfy" setup_deb822_repo \ "ntfy" \ "https://archive.ntfy.sh/apt/keyring.gpg" \ "https://archive.ntfy.sh/apt/" \ "stable" $STD apt install -y ntfy systemctl enable -q --now ntfy msg_ok "Setup ntfy" motd_ssh customize cleanup_lxc ================================================ FILE: install/nxwitness-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://nxvms.com/download/releases/linux source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os setup_hwaccel msg_info "Installing Dependencies" $STD apt install -y \ make \ net-tools \ ffmpeg \ cifs-utils \ libtalloc2 \ libwbclient0 \ keyutils msg_ok "Installed Dependencies" msg_info "Setup Nx Witness" cd /tmp BASE_URL="https://updates.networkoptix.com/default/index.html" RELEASE=$(curl -fsSL "$BASE_URL" | grep -oP '(?<=)[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+(?=)' | head -n 1) DETAIL_PAGE=$(curl -fsSL "$BASE_URL#note_$RELEASE") DOWNLOAD_URL=$(echo "$DETAIL_PAGE" | grep -oP "https://updates.networkoptix.com/default/$RELEASE/arm/nxwitness-server-[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+-linux_arm64\.deb" | head -n 1) curl -fsSL "$DOWNLOAD_URL" -o ""nxwitness-server-$RELEASE-linux_arm64.deb"" export DEBIAN_FRONTEND=noninteractive $STD dpkg -i nxwitness-server-$RELEASE-linux_arm64.deb rm -f /tmp/nxwitness-server-$RELEASE-linux_arm64.deb echo "${RELEASE}" >/opt/${APPLICATION}_version.txt msg_ok "Setup Nx Witness" motd_ssh customize cleanup_lxc ================================================ FILE: install/nzbget-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck # Co-Author: havardthom # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://nzbget.com/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os setup_nonfree msg_info "Installing Dependencies" $STD apt install -y \ par2 \ unrar msg_ok "Installed Dependencies" msg_info "Installing NZBGet" setup_deb822_repo \ "nzbgetcom" \ "https://nzbgetcom.github.io/nzbgetcom.asc" \ "https://nzbgetcom.github.io/deb" \ "stable" $STD apt install -y nzbget sed -i "s|SevenZipCmd=7zz|SevenZipCmd=7z|g" /var/lib/nzbget/nzbget.conf systemctl restart nzbget msg_ok "Installed NZBGet" motd_ssh customize cleanup_lxc ================================================ FILE: install/oauth2-proxy-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: bvdberg01 # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/oauth2-proxy/oauth2-proxy/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os fetch_and_deploy_gh_release "oauth2-proxy" "oauth2-proxy/oauth2-proxy" "prebuild" "latest" "/opt/oauth2-proxy" "oauth2-proxy*linux-arm64.tar.gz" touch /opt/oauth2-proxy/config.toml msg_info "Creating Service" cat </etc/systemd/system/oauth2-proxy.service [Unit] Description=OAuth2-Proxy Service After=network.target [Service] Type=simple WorkingDirectory=/opt/oauth2-proxy ExecStart=/opt/oauth2-proxy/oauth2-proxy --config config.toml Restart=on-failure RestartSec=5 [Install] WantedBy=multi-user.target EOF systemctl enable -q --now oauth2-proxy msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/octoprint-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://octoprint.org/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y git \ libyaml-dev \ build-essential msg_ok "Installed Dependencies" msg_info "Setup Python3" $STD apt install -y \ python3 \ python3-dev \ python3-pip \ python3-venv \ python3-setuptools rm -rf /usr/lib/python3.*/EXTERNALLY-MANAGED msg_ok "Setup Python3" msg_info "Creating user octoprint" useradd -m -s /bin/bash -p $(openssl passwd -1 octoprint) octoprint usermod -aG sudo,tty,dialout octoprint chown -R octoprint:octoprint /opt echo "octoprint ALL=NOPASSWD: $(command -v systemctl) restart octoprint, $(command -v reboot), $(command -v poweroff)" >/etc/sudoers.d/octoprint msg_ok "Created user octoprint" msg_info "Installing OctoPrint" $STD sudo -u octoprint bash </etc/systemd/system/octoprint.service [Unit] Description=The snappy web interface for your 3D printer After=network-online.target Wants=network-online.target [Service] Environment="LC_ALL=C.UTF-8" Environment="LANG=C.UTF-8" Type=exec User=octoprint ExecStart=/opt/octoprint/bin/octoprint serve [Install] WantedBy=multi-user.target EOF systemctl enable -q --now octoprint msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/odoo-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/tteck/Proxmox/raw/main/LICENSE # Source: https://github.com/odoo/odoo source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y python3-lxml wkhtmltopdf curl -fsSL "http://archive.ubuntu.com/ubuntu/pool/universe/l/lxml-html-clean/python3-lxml-html-clean_0.1.1-1_all.deb" -o /opt/python3-lxml-html-clean.deb $STD dpkg -i /opt/python3-lxml-html-clean.deb msg_ok "Installed Dependencies" PG_VERSION="18" setup_postgresql RELEASE=$(curl -fsSL https://nightly.odoo.com/ | grep -oE 'href="[0-9]+\.[0-9]+/nightly"' | head -n1 | cut -d'"' -f2 | cut -d/ -f1) LATEST_VERSION=$(curl -fsSL "https://nightly.odoo.com/${RELEASE}/nightly/deb/" | grep -oP "odoo_${RELEASE}\.\d+_all\.deb" | sed -E "s/odoo_(${RELEASE}\.[0-9]+)_all\.deb/\1/" | sort -V | tail -n1) msg_info "Setup Odoo $RELEASE" curl -fsSL https://nightly.odoo.com/${RELEASE}/nightly/deb/odoo_${RELEASE}.latest_all.deb -o /opt/odoo.deb $STD apt install -y /opt/odoo.deb msg_ok "Setup Odoo $RELEASE" msg_info "Setup PostgreSQL Database" DB_NAME="odoo" DB_USER="odoo_usr" DB_PASS="$(openssl rand -base64 18 | cut -c1-13)" $STD sudo -u postgres psql -c "CREATE DATABASE $DB_NAME;" $STD sudo -u postgres psql -c "CREATE USER $DB_USER WITH PASSWORD '$DB_PASS';" $STD sudo -u postgres psql -c "GRANT ALL PRIVILEGES ON DATABASE $DB_NAME TO $DB_USER;" $STD sudo -u postgres psql -c "ALTER DATABASE $DB_NAME OWNER TO $DB_USER;" $STD sudo -u postgres psql -c "ALTER USER $DB_USER WITH SUPERUSER;" { echo "Odoo-Credentials" echo -e "Odoo Database User: $DB_USER" echo -e "Odoo Database Password: $DB_PASS" echo -e "Odoo Database Name: $DB_NAME" } >>~/odoo.creds msg_ok "Setup PostgreSQL" msg_info "Configuring Odoo" sed -i \ -e "s|^;*db_host *=.*|db_host = localhost|" \ -e "s|^;*db_port *=.*|db_port = 5432|" \ -e "s|^;*db_user *=.*|db_user = $DB_USER|" \ -e "s|^;*db_password *=.*|db_password = $DB_PASS|" \ /etc/odoo/odoo.conf $STD sudo -u odoo odoo -c /etc/odoo/odoo.conf -d odoo -i base --stop-after-init rm -f /opt/odoo.deb rm -f /opt/python3-lxml-html-clean.deb echo "${LATEST_VERSION}" >/opt/${APPLICATION}_version.txt msg_ok "Configured Odoo" msg_info "Restarting Odoo" systemctl restart odoo msg_ok "Restarted Odoo" motd_ssh customize cleanup_lxc ================================================ FILE: install/ollama-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: havardthom | Co-Author: MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://ollama.com/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ build-essential \ pkg-config \ zstd msg_ok "Installed Dependencies" msg_info "Installing Golang" set +o pipefail temp_file=$(mktemp) golang_tarball=$(curl -fsSL https://go.dev/dl/ | grep -oP 'go[\d\.]+\.linux-arm64\.tar\.gz' | head -n 1) curl -fsSL "https://golang.org/dl/${golang_tarball}" -o "$temp_file" tar -C /usr/local -xzf "$temp_file" ln -sf /usr/local/go/bin/go /usr/local/bin/go rm -f "$temp_file" set -o pipefail msg_ok "Installed Golang" setup_hwaccel msg_info "Installing Ollama (Patience)" RELEASE=$(curl -fsSL https://api.github.com/repos/ollama/ollama/releases/latest | grep "tag_name" | awk -F '"' '{print $4}') BINDIR="/usr/local/bin" mkdir -p $OLLAMA_INSTALL_DIR OLLAMA_URL="https://github.com/ollama/ollama/releases/download/${RELEASE}/ollama-linux-arm64.tar.zst" TMP_TAR="/tmp/ollama.tar.zst" echo -e "\n" if curl -fL# -C - -o "$TMP_TAR" "$OLLAMA_URL"; then if tar --zstd -xf "$TMP_TAR" -C "$OLLAMA_INSTALL_DIR"; then ln -sf "$OLLAMA_INSTALL_DIR/bin/ollama" "$BINDIR/ollama" echo "${RELEASE}" >/opt/Ollama_version.txt msg_ok "Installed Ollama ${RELEASE}" else msg_error "Extraction failed – archive corrupt or incomplete" exit 251 fi else msg_error "Download failed – $OLLAMA_URL not reachable" exit 250 fi msg_info "Creating ollama User and Group" if ! id ollama >/dev/null 2>&1; then useradd -r -s /usr/sbin/nologin -U -m -d /usr/share/ollama ollama fi $STD usermod -aG ollama $(id -u -n) msg_ok "Created ollama User and adjusted Groups" setup_hwaccel "ollama" msg_info "Creating Service" cat </etc/systemd/system/ollama.service [Unit] Description=Ollama Service After=network-online.target [Service] Type=exec ExecStart=/usr/local/bin/ollama serve Environment=HOME=$HOME Environment=OLLAMA_INTEL_GPU=true Environment=OLLAMA_HOST=0.0.0.0 Environment=OLLAMA_NUM_GPU=999 Environment=SYCL_CACHE_PERSISTENT=1 Environment=ZES_ENABLE_SYSMAN=1 Restart=always RestartSec=3 [Install] WantedBy=multi-user.target EOF systemctl enable -q --now ollama msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/omada-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.tp-link.com/us/support/download/omada-software-controller/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y jsvc msg_ok "Installed Dependencies" JAVA_VERSION="21" setup_java if lscpu | grep -q 'avx'; then MONGO_VERSION="8.0" setup_mongodb else msg_error "No AVX detected (CPU-Flag)! We have discontinued support for this. You are welcome to try it manually with a Debian LXC, but due to the many issues with Omada, we currently only support AVX CPUs." exit 10 fi if ! dpkg -l | grep -q 'libssl1.1'; then msg_info "Installing libssl (if needed)" curl -fsSL "https://security.debian.org/debian-security/pool/updates/main/o/openssl/libssl1.1_1.1.1w-0+deb11u4_arm64.deb" -o "/tmp/libssl.deb" $STD dpkg -i /tmp/libssl.deb rm -f /tmp/libssl.deb msg_ok "Installed libssl1.1" fi msg_info "Installing Omada Controller" OMADA_URL=$(curl -fsSL "https://support.omadanetworks.com/en/download/software/omada-controller/" | grep -o 'https://static\.tp-link\.com/upload/software/[^"]*linux_x64[^"]*\.deb' | head -n1) OMADA_PKG=$(basename "${OMADA_URL}") curl -fsSL "${OMADA_URL}" -o "${OMADA_PKG}" $STD dpkg -i "${OMADA_PKG}" rm -rf "${OMADA_PKG}" VERSION=$(sed -n 's/.*_v\([0-9.]*\)_.*_\([0-9]\{14\}\)\.deb$/\1-\2/p' <<<"${OMADA_PKG}") echo "${VERSION}" >$HOME/.omada msg_ok "Installed Omada Controller" motd_ssh customize cleanup_lxc ================================================ FILE: install/ombi-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://ombi.io/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os fetch_and_deploy_gh_release "ombi" "Ombi-app/Ombi" "prebuild" "latest" "/opt/ombi" "linux-arm64.tar.gz" msg_info "Creating Service" cat </etc/systemd/system/ombi.service [Unit] Description=Ombi After=syslog.target network-online.target [Service] ExecStart=/opt/ombi/./Ombi WorkingDirectory=/opt/ombi Restart=always [Install] WantedBy=multi-user.target EOF systemctl enable -q --now ombi msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/omv-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.openmediavault.org/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing OpenMediaVault (Patience)" curl -fsSL "https://packages.openmediavault.org/public/archive.key" | gpg --dearmor >"/etc/apt/trusted.gpg.d/openmediavault-archive-keyring.gpg" cat </etc/apt/sources.list.d/openmediavault.list deb [signed-by=/etc/apt/trusted.gpg.d/openmediavault-archive-keyring.gpg] http://packages.openmediavault.org/public sandworm main EOF export LANG=C.UTF-8 export DEBIAN_FRONTEND=noninteractive export APT_LISTCHANGES_FRONTEND=none $STD apt update apt -y --auto-remove --show-upgraded --allow-downgrades --allow-change-held-packages --no-install-recommends --option DPkg::Options::="--force-confdef" --option DPkg::Options::="--force-confold" install openmediavault-keyring openmediavault &>/dev/null omv-confdbadm populate &>/dev/null msg_ok "Installed OpenMediaVault" motd_ssh customize cleanup_lxc ================================================ FILE: install/onedev-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: kristocopani # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://onedev.io/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ git \ git-lfs msg_ok "Installed Dependencies" JAVA_VERSION="21" setup_java msg_info "Installing OneDev" RELEASE=$(curl -fsSL https://api.github.com/repos/theonedev/onedev/releases/latest | grep '"tag_name":' | cut -d'"' -f4) cd /opt curl -fsSL "https://code.onedev.io/onedev/server/~site/onedev-latest.tar.gz" -o onedev-latest.tar.gz tar -xzf onedev-latest.tar.gz mv /opt/onedev-latest /opt/onedev $STD /opt/onedev/bin/server.sh install systemctl start onedev rm -rf /opt/onedev-latest.tar.gz echo "${RELEASE}" >~/.onedev msg_ok "Installed OneDev" motd_ssh customize cleanup_lxc ================================================ FILE: install/onlyoffice-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ nginx \ rabbitmq-server \ ca-certificates msg_ok "Installed Dependencies" PG_VERSION="16" setup_postgresql msg_info "Setup Database" DB_NAME=onlyoffice DB_USER=onlyoffice_user DB_PASS=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | cut -c1-13) $STD sudo -u postgres psql -c "CREATE ROLE $DB_USER WITH LOGIN PASSWORD '$DB_PASS';" $STD sudo -u postgres psql -c "CREATE DATABASE $DB_NAME WITH OWNER $DB_USER ENCODING 'UTF8' TEMPLATE template0;" $STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET client_encoding TO 'utf8';" $STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET default_transaction_isolation TO 'read committed';" $STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET timezone TO 'UTC'" { echo "ONLYOFFICE-Credentials" echo "ONLYOFFICE Database User: $DB_USER" echo "ONLYOFFICE Database Password: $DB_PASS" echo "ONLYOFFICE Database Name: $DB_NAME" } >>~/onlyoffice.creds msg_ok "Set up Database" msg_info "Adding ONLYOFFICE GPG Key" GPG_TMP="/tmp/onlyoffice.gpg" KEY_URL="https://download.onlyoffice.com/GPG-KEY-ONLYOFFICE" TMP_KEY_CONTENT=$(mktemp) if curl -fsSL "$KEY_URL" -o "$TMP_KEY_CONTENT" && grep -q "BEGIN PGP PUBLIC KEY BLOCK" "$TMP_KEY_CONTENT"; then gpg --quiet --batch --yes --no-default-keyring --keyring "gnupg-ring:$GPG_TMP" --import "$TMP_KEY_CONTENT" >/dev/null 2>&1 chmod 644 "$GPG_TMP" chown root:root "$GPG_TMP" mv "$GPG_TMP" /usr/share/keyrings/onlyoffice.gpg cat </etc/apt/sources.list.d/onlyoffice.sources Types: deb URIs: https://download.onlyoffice.com/repo/debian Suites: squeeze Components: main Signed-By: /usr/share/keyrings/onlyoffice.gpg EOF $STD apt update msg_ok "GPG Key Added" else msg_error "Failed to download or verify GPG key from $KEY_URL" [[ -f "$TMP_KEY_CONTENT" ]] && rm -f "$TMP_KEY_CONTENT" exit 250 fi rm -f "$TMP_KEY_CONTENT" msg_info "Preconfiguring ONLYOFFICE Debconf Settings" RMQ_USER=onlyoffice_rmq RMQ_PASS=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | cut -c1-13) JWT_SECRET=$(openssl rand -hex 16) $STD rabbitmqctl add_user $RMQ_USER $RMQ_PASS $STD rabbitmqctl set_permissions -p / $RMQ_USER ".*" ".*" ".*" $STD rabbitmqctl set_user_tags $RMQ_USER administrator echo onlyoffice-documentserver onlyoffice/db-host string localhost | debconf-set-selections echo onlyoffice-documentserver onlyoffice/db-user string $DB_USER | debconf-set-selections echo onlyoffice-documentserver onlyoffice/db-pwd password $DB_PASS | debconf-set-selections echo onlyoffice-documentserver onlyoffice/db-name string $DB_NAME | debconf-set-selections echo onlyoffice-documentserver onlyoffice/rabbitmq-host string localhost | debconf-set-selections echo onlyoffice-documentserver onlyoffice/rabbitmq-user string $RMQ_USER | debconf-set-selections echo onlyoffice-documentserver onlyoffice/rabbitmq-pwd password $RMQ_PASS | debconf-set-selections echo onlyoffice-documentserver onlyoffice/jwt-enabled boolean true | debconf-set-selections echo onlyoffice-documentserver onlyoffice/jwt-secret password $JWT_SECRET | debconf-set-selections echo "RabbitMQ User: $RMQ_USER" >>~/onlyoffice.creds echo "RabbitMQ Password: $RMQ_PASS" >>~/onlyoffice.creds echo "JWT Secret: $JWT_SECRET" >>~/onlyoffice.creds { echo "" echo "ONLYOFFICE RabbitMQ Credentials" echo "User: $RMQ_USER" echo "Password: $RMQ_PASS" echo "Secret: $JWT_SECRET" } >>~/onlyoffice.creds msg_ok "Debconf Preconfiguration Done" msg_info "Installing ttf-mscorefonts-installer" echo ttf-mscorefonts-installer msttcorefonts/accepted-mscorefonts-eula select true | debconf-set-selections $STD apt install -y ttf-mscorefonts-installer msg_ok "Installed Microsoft Core Fonts" msg_info "Installing ONLYOFFICE Docs" $STD apt install -y onlyoffice-documentserver msg_ok "ONLYOFFICE Docs Installed" motd_ssh customize cleanup_lxc ================================================ FILE: install/open-archiver-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: Slaviša Arežina (tremor021) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://openarchiver.com/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing dependendencies" $STD apt install -y valkey msg_ok "Installed dependendencies" NODE_VERSION="22" NODE_MODULE="pnpm" setup_nodejs PG_VERSION="17" setup_postgresql PG_DB_NAME="openarchiver_db" PG_DB_USER="openarchiver" setup_postgresql_db setup_meilisearch fetch_and_deploy_gh_release "openarchiver" "LogicLabs-OU/OpenArchiver" "tarball" JWT_KEY="$(openssl rand -hex 32)" SECRET_KEY="$(openssl rand -hex 32)" msg_info "Setting up Open Archiver" mkdir -p /opt/openarchiver-data cd /opt/openarchiver cp .env.example .env sed -i "s|^NODE_ENV=.*|NODE_ENV=production|g" /opt/openarchiver/.env sed -i "s|^POSTGRES_DB=.*|POSTGRES_DB=$PG_DB_NAME|g" /opt/openarchiver/.env sed -i "s|^POSTGRES_USER=.*|POSTGRES_USER=$PG_DB_USER|g" /opt/openarchiver/.env sed -i "s|^POSTGRES_PASSWORD=.*|POSTGRES_PASSWORD=$PG_DB_PASS|g" /opt/openarchiver/.env sed -i "s|^DATABASE_URL=.*|DATABASE_URL=\"postgresql://$PG_DB_USER:$PG_DB_PASS@localhost:5432/$PG_DB_NAME\"|g" /opt/openarchiver/.env sed -i "s|^MEILI_HOST=.*|MEILI_HOST=http://localhost:7700|g" /opt/openarchiver/.env sed -i "s|^MEILI_MASTER_KEY=.*|MEILI_MASTER_KEY=$MEILISEARCH_MASTER_KEY|g" /opt/openarchiver/.env sed -i "s|^REDIS_HOST=.*|REDIS_HOST=localhost|g" /opt/openarchiver/.env sed -i "s|^REDIS_USER=.*|REDIS_USER=|g" /opt/openarchiver/.env sed -i "s|^REDIS_PASSWORD=.*|REDIS_PASSWORD=|g" /opt/openarchiver/.env sed -i "s|^STORAGE_LOCAL_ROOT_PATH=.*|STORAGE_LOCAL_ROOT_PATH=/opt/openarchiver-data|g" /opt/openarchiver/.env sed -i "s|^JWT_SECRET=.*|JWT_SECRET=$JWT_KEY|g" /opt/openarchiver/.env sed -i "s|^ENCRYPTION_KEY=.*|ENCRYPTION_KEY=$SECRET_KEY|g" /opt/openarchiver/.env sed -i "s|^TIKA_URL=.*|TIKA_URL=|g" /opt/openarchiver/.env sed -i "s|^ORIGIN=.*|ORIGIN=http://$LOCAL_IP:3000|g" /opt/openarchiver/.env $STD pnpm install --shamefully-hoist --frozen-lockfile --prod=false $STD pnpm run build:oss $STD pnpm db:migrate msg_ok "Setup Open Archiver" msg_info "Creating Service" cat </etc/systemd/system/openarchiver.service [Unit] Description=Open Archiver Service After=network-online.target [Service] Type=simple User=root EnvironmentFile=/opt/openarchiver/.env WorkingDirectory=/opt/openarchiver ExecStart=/usr/bin/pnpm docker-start:oss Restart=on-failure [Install] WantedBy=multi-user.target EOF systemctl enable -q --now openarchiver msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/opencloud-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: vhsdream # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://opencloud.eu source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os MAX_ATTEMPTS=3 servers=("opencloud" "collabora" "wopi") attempt=0 for server in "${servers[@]}"; do until ((attempt >= MAX_ATTEMPTS)); do attempt=$((attempt + 1)) read -rp "${TAB3}Enter the FQDN of your ${server^} server (ATTEMPT $attempt/$MAX_ATTEMPTS) (eg $server.domain.tld): " fqdn if [[ -z "$fqdn" ]]; then msg_warn "Domain cannot be empty!" elif [[ "$fqdn" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then msg_warn "IP address not allowed! Please use a FQDN" elif [[ "$fqdn" =~ ^[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(\.[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*\.[a-zA-Z]{2,}$ ]]; then export ${server^^}_FQDN="$fqdn" attempt=0 break else msg_warn "Invalid domain format!" fi done if ((attempt >= MAX_ATTEMPTS)); then msg_error "No more attempts - aborting script!" exit 254 fi done msg_info "Installing dependencies" $STD apt install -y inotify-tools msg_ok "Installed dependencies" msg_info "Installing Collabora Online" curl -fsSL https://collaboraoffice.com/downloads/gpg/collaboraonline-release-keyring.gpg -o /etc/apt/keyrings/collaboraonline-release-keyring.gpg cat </etc/apt/sources.list.d/colloboraonline.sources Types: deb URIs: https://www.collaboraoffice.com/repos/CollaboraOnline/CODE-deb Suites: ./ Signed-By: /etc/apt/keyrings/collaboraonline-release-keyring.gpg EOF $STD apt-get update $STD apt-get install -y coolwsd code-brand systemctl stop coolwsd mkdir -p /etc/systemd/system/coolwsd.service.d cat </etc/systemd/system/coolwsd.service.d/override.conf [Unit] Before=opencloud-wopi.service EOF systemctl daemon-reload COOLPASS="$(openssl rand -base64 36)" $STD sudo -u cool coolconfig set-admin-password --user=admin --password="$COOLPASS" echo "$COOLPASS" >~/.coolpass msg_ok "Installed Collabora Online" fetch_and_deploy_gh_release "opencloud" "opencloud-eu/opencloud" "singlefile" "v5.1.0" "/usr/bin" "opencloud-*-linux-arm64" msg_info "Configuring OpenCloud" DATA_DIR="/var/lib/opencloud" CONFIG_DIR="/etc/opencloud" ENV_FILE="${CONFIG_DIR}/opencloud.env" mkdir -p "$DATA_DIR" "$CONFIG_DIR"/web/assets/{apps,themes} curl -fsSL https://raw.githubusercontent.com/opencloud-eu/opencloud-compose/refs/heads/main/config/opencloud/csp.yaml -o "$CONFIG_DIR"/csp.yaml curl -fsSL https://raw.githubusercontent.com/opencloud-eu/opencloud-compose/refs/heads/main/config/opencloud/proxy.yaml -o "$CONFIG_DIR"/proxy.yaml.bak cat <"$ENV_FILE" OC_URL=https://${OPENCLOUD_FQDN} OC_INSECURE=false IDM_CREATE_DEMO_USERS=false OC_LOG_LEVEL=warning OC_CONFIG_DIR=${CONFIG_DIR} OC_BASE_DATA_PATH=${DATA_DIR} STORAGE_SYSTEM_OC_ROOT=${DATA_DIR}/storage/metadata ## Web WEB_ASSET_CORE_PATH=${CONFIG_DIR}/web/assets WEB_ASSET_APPS_PATH=${CONFIG_DIR}/web/assets/apps WEB_ASSET_THEMES_PATH=${CONFIG_DIR}/web/assets/themes # WEB_UI_THEME_PATH= ## Uncomment below to create & modify your web UI config # WEB_UI_CONFIG_FILE=${CONFIG_DIR}/web/config.json ## Frontend FRONTEND_DISABLE_RADICALE=true FRONTEND_GROUPWARE_ENABLED=false GRAPH_INCLUDE_OCM_SHAREES=true ## Proxy PROXY_TLS=false PROXY_CSP_CONFIG_FILE_LOCATION=${CONFIG_DIR}/csp.yaml ## Collaboration - requires VALID TLS COLLABORA_DOMAIN=${COLLABORA_FQDN} COLLABORATION_APP_NAME="CollaboraOnline" COLLABORATION_APP_PRODUCT="Collabora" COLLABORATION_APP_ADDR=https://${COLLABORA_FQDN} COLLABORATION_APP_INSECURE=false COLLABORATION_HTTP_ADDR=0.0.0.0:9300 COLLABORATION_WOPI_SRC=https://${WOPI_FQDN} COLLABORATION_JWT_SECRET= ## Notifications - Email settings # NOTIFICATIONS_SMTP_HOST= # NOTIFICATIONS_SMTP_PORT= # NOTIFICATIONS_SMTP_SENDER= # NOTIFICATIONS_SMTP_USERNAME= # NOTIFICATIONS_SMTP_PASSWORD= # NOTIFICATIONS_SMTP_AUTHENTICATION=login ## Encryption method. Possible values are 'starttls', 'ssltls' and 'none' # NOTIFICATIONS_SMTP_ENCRYPTION=starttls ## Allow insecure connections. Defaults to false. # NOTIFICATIONS_SMTP_INSECURE=false ## Start additional services at runtime ## Examples: notifications, antivirus etc. ## Do not uncomment unless configured above. # OC_ADD_RUN_SERVICES="notifications" ## OpenID - via web browser ## uncomment for OpenID in general # OC_EXCLUDE_RUN_SERVICES=idp # OC_OIDC_ISSUER= # IDP_DOMAIN= # PROXY_OIDC_ACCESS_TOKEN_VERIFY_METHOD=none # PROXY_OIDC_REWRITE_WELLKNOWN=true # PROXY_USER_OIDC_CLAIM=preferred_username # PROXY_USER_CS3_CLAIM=username ## automatically create accounts # PROXY_AUTOPROVISION_ACCOUNTS=true # WEB_OIDC_SCOPE=openid profile email groups # GRAPH_ASSIGN_DEFAULT_USER_ROLE=false # ## uncomment below if using PocketID # WEB_OIDC_CLIENT_ID= # WEB_OIDC_METADATA_URL=/.well-known/openid-configuration ## Full Text Search - Apache Tika ## Requires a separate install of Tika - see https://community-scripts.github.io/ProxmoxVE/scripts?id=apache-tika # SEARCH_EXTRACTOR_TYPE=tika # FRONTEND_FULL_TEXT_SEARCH_ENABLED=true # SEARCH_EXTRACTOR_TIKA_TIKA_URL= ## Uncomment below to enable PosixFS Collaborative Mode ## Increase inotify watch/instance limits on your PVE host: ### sysctl -w fs.inotify.max_user_watches=1048576 ### sysctl -w fs.inotify.max_user_instances=1024 # STORAGE_USERS_POSIX_ENABLE_COLLABORATION=true # STORAGE_USERS_POSIX_WATCH_TYPE=inotifywait # STORAGE_USERS_POSIX_WATCH_FS=true # STORAGE_USERS_POSIX_WATCH_PATH= ## User files location - experimental - use at your own risk! - ZFS, NFS v4.2+ supported - CIFS/SMB not supported # STORAGE_USERS_POSIX_ROOT= EOF cat </etc/systemd/system/opencloud.service [Unit] Description=OpenCloud server After=network-online.target [Service] Type=simple User=opencloud Group=opencloud EnvironmentFile=${ENV_FILE} ExecStart=/usr/bin/opencloud server Restart=always [Install] WantedBy=multi-user.target EOF cat </etc/systemd/system/opencloud-wopi.service [Unit] Description=OpenCloud WOPI Server Wants=coolwsd.service After=opencloud.service coolwsd.service [Service] Type=simple User=opencloud Group=opencloud EnvironmentFile=${ENV_FILE} ExecStartPre=/bin/sleep 10 ExecStart=/usr/bin/opencloud collaboration server Restart=always KillSignal=SIGKILL KillMode=mixed TimeoutStopSec=10 [Install] WantedBy=multi-user.target EOF $STD sudo -u cool coolconfig set ssl.enable false $STD sudo -u cool coolconfig set ssl.termination true $STD sudo -u cool coolconfig set ssl.ssl_verification true sed -i "s|-Policy\">|&frame-ancestors https://${OPENCLOUD_FQDN}|" /etc/coolwsd/coolwsd.xml useradd -r -M -s /usr/sbin/nologin opencloud chown -R opencloud:opencloud "$CONFIG_DIR" "$DATA_DIR" sudo -u opencloud opencloud init --config-path "$CONFIG_DIR" --insecure no OPENCLOUD_SECRET="$(sed -n '/jwt/p' "$CONFIG_DIR"/opencloud.yaml | awk '{print $2}')" sed -i "s/JWT_SECRET=/&${OPENCLOUD_SECRET//&/\\&}/" "$ENV_FILE" msg_ok "Configured OpenCloud" msg_info "Starting services" systemctl enable -q --now coolwsd opencloud sleep 5 systemctl enable -q --now opencloud-wopi msg_ok "Started services" motd_ssh customize cleanup_lxc ================================================ FILE: install/opengist-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: Jonathan (jd-apprentice) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://opengist.io/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y git msg_ok "Installed Dependencies" fetch_and_deploy_gh_release "opengist" "thomiceli/opengist" "prebuild" "latest" "/opt/opengist" "opengist*linux-arm64.tar.gz" mkdir -p /opt/opengist-data sed -i 's|opengist-home:.*|opengist-home: /opt/opengist-data|' /opt/opengist/config.yml msg_info "Creating Service" cat </etc/systemd/system/opengist.service [Unit] Description=Opengist server to manage your Gists After=network.target [Service] WorkingDirectory=/opt/opengist ExecStart=/opt/opengist/opengist --config /opt/opengist/config.yml Restart=always User=root [Install] WantedBy=multi-user.target EOF systemctl enable -q --now opengist msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/openhab-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.openhab.org/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os JAVA_VERSION="21" setup_java msg_info "Installing openHAB" setup_deb822_repo \ "openhab" \ "https://openhab.jfrog.io/artifactory/api/gpg/key/public" \ "https://openhab.jfrog.io/artifactory/openhab-linuxpkg" \ "stable" \ "main" $STD apt install -y openhab msg_ok "Installed openHAB" msg_info "Initializing openHAB directories" mkdir -p /var/lib/openhab/{tmp,etc,cache} mkdir -p /etc/openhab mkdir -p /var/log/openhab chown -R openhab:openhab /var/lib/openhab /etc/openhab /var/log/openhab msg_ok "Initialized openHAB directories" msg_info "Starting Service" systemctl daemon-reload systemctl enable -q --now openhab msg_ok "Started Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/openobserve-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://openobserve.ai/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing OpenObserve" mkdir -p /opt/openobserve/data RELEASE=$(get_latest_github_release "openobserve/openobserve") tar zxf <(curl -fsSL https://downloads.openobserve.ai/releases/openobserve/v$RELEASE/openobserve-v$RELEASE-linux-arm64.tar.gz) -C /opt/openobserve ROOT_PASS=$(openssl rand -base64 18 | cut -c1-13) cat </opt/openobserve/data/.env ZO_ROOT_USER_EMAIL = "admin@example.com" ZO_ROOT_USER_PASSWORD = "${ROOT_PASS}" ZO_DATA_DIR = "/opt/openobserve/data" ZO_HTTP_PORT = "5080" EOF echo "${RELEASE}" >>~/.openobserve msg_ok "Installed OpenObserve" msg_info "Creating Service" cat </etc/systemd/system/openobserve.service [Unit] Description=OpenObserve After=network.target [Service] Type=simple EnvironmentFile=/opt/openobserve/data/.env ExecStart=/opt/openobserve/openobserve ExecStop=killall -QUIT openobserve Restart=on-failure [Install] WantedBy=multi-user.target EOF systemctl enable -q --now openobserve msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/openproject-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: michelroegl-brunner # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/opf/openproject source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ apt-transport-https \ build-essential \ autoconf msg_ok "Installed Dependencies" PG_VERSION="17" setup_postgresql PG_DB_NAME="openproject" PG_DB_USER="openproject" setup_postgresql_db API_KEY=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | cut -c1-13) echo "OpenProject API Key: $API_KEY" >>~/openproject.creds fetch_and_deploy_gh_release "jemalloc" "jemalloc/jemalloc" "tarball" msg_info "Compiling jemalloc (Patience)" cd /opt/jemalloc $STD ./autogen.sh $STD make $STD make install msg_ok "Compiled jemalloc" setup_deb822_repo \ "openproject" \ "https://packages.openproject.com/srv/deb/opf/openproject/gpg-key.gpg" \ "https://packages.openproject.com/srv/deb/opf/openproject/stable/17/debian/" \ "12" msg_info "Installing OpenProject" $STD apt install -y openproject msg_ok "Installed OpenProject" msg_info "Configuring OpenProject" cat </etc/openproject/installer.dat openproject/edition default postgres/retry retry postgres/autoinstall reuse postgres/db_host 127.0.0.1 postgres/db_port 5432 postgres/db_username ${PG_DB_USER} postgres/db_password ${PG_DB_PASS} postgres/db_name ${PG_DB_NAME} server/autoinstall install server/variant apache2 server/hostname ${LOCAL_IP} server/server_path_prefix /openproject server/ssl no server/variant apache2 repositories/api-key ${API_KEY} repositories/svn-install skip repositories/git-install install repositories/git-path /var/db/openproject/git repositories/git-http-backend /usr/lib/git-core/git-http-backend/ memcached/autoinstall install openproject/admin_email admin@example.net openproject/default_language en EOF $STD sudo openproject configure systemctl stop openproject-web-1 if ! grep -qF 'Environment=LD_PRELOAD=/usr/local/lib/libjemalloc.so.2' /etc/systemd/system/openproject-web-1.service; then sed -i '/^\[Service\]/a Environment=LD_PRELOAD=/usr/local/lib/libjemalloc.so.2' /etc/systemd/system/openproject-web-1.service fi systemctl start openproject-web-1 msg_ok "Configured OpenProject" motd_ssh customize cleanup_lxc ================================================ FILE: install/openthread-br-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://openthread.io/guides/border-router source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ build-essential \ cmake \ ninja-build \ pkg-config \ git \ iproute2 \ libreadline-dev \ libncurses-dev \ rsyslog \ dbus \ libdbus-1-dev \ libjsoncpp-dev \ iptables \ ipset \ bind9 \ libnetfilter-queue1 \ libnetfilter-queue-dev \ libprotobuf-dev \ protobuf-compiler \ socat msg_ok "Installed Dependencies" setup_nodejs msg_info "Cloning OpenThread Border Router" # git clone is needed to fetch submodules, fetch_and_deploy_gh_release doesn't support this. We use --depth 1 to minimize the amount of data cloned, but it still may take a while. $STD git clone --depth 1 https://github.com/openthread/ot-br-posix /opt/ot-br-posix cd /opt/ot-br-posix $STD git submodule update --depth 1 --init --recursive msg_ok "Cloned OpenThread Border Router" msg_info "Building OpenThread Border Router (Patience)" mkdir -p build && cd build $STD cmake -GNinja \ -DBUILD_TESTING=OFF \ -DCMAKE_INSTALL_PREFIX=/usr \ -DOTBR_DBUS=ON \ -DOTBR_MDNS=openthread \ -DOTBR_REST=ON \ -DOTBR_WEB=ON \ -DOTBR_BORDER_ROUTING=ON \ -DOTBR_BACKBONE_ROUTER=ON \ -DOT_FIREWALL=ON \ -DOT_POSIX_NAT64_CIDR="192.168.255.0/24" \ .. $STD ninja $STD ninja install msg_ok "Built OpenThread Border Router" msg_info "Configuring Network" cat </etc/sysctl.d/99-otbr.conf net.ipv6.conf.all.forwarding=1 net.ipv4.ip_forward=1 EOF $STD sysctl -p /etc/sysctl.d/99-otbr.conf msg_ok "Configured Network" msg_info "Configuring Services" cat <<'EOF' >/etc/default/otbr-agent # USB example: # OTBR_AGENT_OPTS="-I wpan0 -B eth0 --vendor-name OpenThread --model-name BorderRouter --rest-listen-address 0.0.0.0 --rest-listen-port 8081 spinel+hdlc+uart:///dev/ttyACM0" # TCP via socat (for network-attached RCP like SLZB-06/SLZB-MR3): # OTBR_AGENT_OPTS="-I wpan0 -B eth0 --vendor-name OpenThread --model-name BorderRouter --rest-listen-address 0.0.0.0 --rest-listen-port 8081 spinel+hdlc+forkpty:///usr/bin/socat?forkpty-arg=-,rawer&forkpty-arg=tcp:IP:PORT trel://eth0" OTBR_AGENT_OPTS="-I wpan0 -B eth0 --vendor-name OpenThread --model-name BorderRouter --rest-listen-address 0.0.0.0 --rest-listen-port 8081 spinel+hdlc+uart:///dev/ttyACM0" EOF cat <<'EOF' >/etc/default/otbr-web OTBR_WEB_OPTS="-I wpan0 -a 0.0.0.0 -p 80" EOF systemctl enable -q dbus rsyslog otbr-agent otbr-web systemctl enable -q bind9 2>/dev/null || systemctl enable -q named 2>/dev/null || true systemctl start -q dbus rsyslog bind9 msg_ok "Configured Services" motd_ssh customize cleanup_lxc ================================================ FILE: install/openwebui-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck | Co-Author: havardthom | Co-Author: Slaviša Arežina (tremor021) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://openwebui.com/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ ffmpeg \ zstd \ build-essential \ libmariadb-dev msg_ok "Installed Dependencies" setup_hwaccel PYTHON_VERSION="3.12" setup_uv msg_info "Installing Open WebUI" $STD uv tool install --python 3.12 --constraint <(echo "numba>=0.60") open-webui[all] msg_ok "Installed Open WebUI" read -r -p "${TAB3}Would you like to add Ollama? " prompt if [[ ${prompt,,} =~ ^(y|yes)$ ]]; then msg_info "Setting up Intel® Repositories" mkdir -p /usr/share/keyrings curl -fsSL https://repositories.intel.com/gpu/intel-graphics.key | gpg --dearmor -o /usr/share/keyrings/intel-graphics.gpg 2>/dev/null || true cat </etc/apt/sources.list.d/intel-gpu.sources Types: deb URIs: https://repositories.intel.com/gpu/ubuntu Suites: jammy Components: client Architectures: arm64 i386 Signed-By: /usr/share/keyrings/intel-graphics.gpg EOF curl -fsSL https://apt.repos.intel.com/intel-gpg-keys/GPG-PUB-KEY-INTEL-SW-PRODUCTS.PUB | gpg --dearmor -o /usr/share/keyrings/oneapi-archive-keyring.gpg 2>/dev/null || true cat </etc/apt/sources.list.d/oneAPI.sources Types: deb URIs: https://apt.repos.intel.com/oneapi Suites: all Components: main Signed-By: /usr/share/keyrings/oneapi-archive-keyring.gpg EOF $STD apt update msg_ok "Set up Intel® Repositories" msg_info "Installing Intel® Level Zero" # Debian 13+ has newer Level Zero packages in system repos that conflict with Intel repo packages if is_debian && [[ "$(get_os_version_major)" -ge 13 ]]; then # Use system packages on Debian 13+ (avoid conflicts with libze1) $STD apt -y install libze1 libze-dev intel-level-zero-gpu 2>/dev/null || { msg_warn "Failed to install some Level Zero packages, continuing anyway" } else # Use Intel repository packages for older systems $STD apt -y install intel-level-zero-gpu level-zero level-zero-dev 2>/dev/null || { msg_warn "Failed to install Intel Level Zero packages, continuing anyway" } fi msg_ok "Installed Intel® Level Zero" msg_info "Installing Intel® oneAPI Base Toolkit (Patience)" $STD apt install -y --no-install-recommends intel-basekit-2024.1 2>/dev/null || true msg_ok "Installed Intel® oneAPI Base Toolkit" msg_info "Installing Ollama" OLLAMA_RELEASE=$(curl -fsSL https://api.github.com/repos/ollama/ollama/releases/latest | grep "tag_name" | awk -F '"' '{print $4}') curl -fsSLO -C - https://github.com/ollama/ollama/releases/download/${OLLAMA_RELEASE}/ollama-linux-arm64.tar.zst tar --zstd -C /usr -xf ollama-linux-arm64.tar.zst rm -rf ollama-linux-arm64.tar.zst cat </etc/systemd/system/ollama.service [Unit] Description=Ollama Service After=network-online.target [Service] Type=exec ExecStart=/usr/bin/ollama serve Environment=HOME=$HOME Environment=OLLAMA_HOST=0.0.0.0 Restart=always RestartSec=3 [Install] WantedBy=multi-user.target EOF systemctl enable -q --now ollama echo "ENABLE_OLLAMA_API=true" >/root/.env msg_ok "Installed Ollama" fi msg_info "Creating Service" cat </etc/systemd/system/open-webui.service [Unit] Description=Open WebUI Service After=network.target [Service] Type=simple EnvironmentFile=-/root/.env Environment=DATA_DIR=/root/.open-webui ExecStart=/root/.local/bin/open-webui serve WorkingDirectory=/root Restart=on-failure RestartSec=5 User=root [Install] WantedBy=multi-user.target EOF systemctl enable -q --now open-webui msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/openziti-controller-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: emoscardini # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/openziti/ziti source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing openziti" mkdir -p --mode=0755 /usr/share/keyrings curl -fsSL https://get.openziti.io/tun/package-repos.gpg | gpg --dearmor -o /usr/share/keyrings/openziti.gpg cat </etc/apt/sources.list.d/openziti.sources Types: deb URIs: https://packages.openziti.org/zitipax-openziti-deb-stable Suites: debian Components: main Signed-By: /usr/share/keyrings/openziti.gpg EOF $STD apt update $STD apt install -y openziti-controller openziti-console msg_ok "Installed openziti" read -r -p "${TAB3}Would you like to go through the auto configuration now? " prompt if [[ ${prompt,,} =~ ^(y|yes)$ ]]; then GEN_FQDN="controller.${LOCAL_IP}.sslip.io" read -r -p "${TAB3}Please enter the controller FQDN [${GEN_FQDN}]: " ZITI_CTRL_ADVERTISED_ADDRESS ZITI_CTRL_ADVERTISED_ADDRESS=${ZITI_CTRL_ADVERTISED_ADDRESS:-$GEN_FQDN} read -r -p "${TAB3}Please enter the controller port [1280]: " ZITI_CTRL_ADVERTISED_PORT ZITI_CTRL_ADVERTISED_PORT=${ZITI_CTRL_ADVERTISED_PORT:-1280} read -r -p "${TAB3}Please enter the controller admin user [admin]: " ZITI_USER ZITI_USER=${ZITI_USER:-admin} GEN_PWD=$(head -c128 /dev/urandom | LC_ALL=C tr -dc 'A-Za-z0-9!@#$%^*_+~' | cut -c 1-12) read -r -p "${TAB3}Please enter the controller admin password [${GEN_PWD}]:" ZITI_PWD ZITI_PWD=${ZITI_PWD:-$GEN_PWD} CONFIG_FILE="/opt/openziti/etc/controller/bootstrap.env" sed -i "s|^ZITI_CTRL_ADVERTISED_ADDRESS=.*|ZITI_CTRL_ADVERTISED_ADDRESS='${ZITI_CTRL_ADVERTISED_ADDRESS}'|" "$CONFIG_FILE" sed -i "s|^ZITI_CTRL_ADVERTISED_PORT=.*|ZITI_CTRL_ADVERTISED_PORT='${ZITI_CTRL_ADVERTISED_PORT}'|" "$CONFIG_FILE" sed -i "s|^ZITI_USER=.*|ZITI_USER='${ZITI_USER}'|" "$CONFIG_FILE" sed -i "s|^ZITI_PWD=.*|ZITI_PWD='${ZITI_PWD}'|" "$CONFIG_FILE" env VERBOSE=0 bash /opt/openziti/etc/controller/bootstrap.bash msg_ok "Configuration Completed" systemctl enable -q --now ziti-controller else systemctl enable -q ziti-controller msg_error "Configration not provided; Please run /opt/openziti/etc/controller/bootstrap.bash to configure the controller and restart the container" fi motd_ssh customize cleanup_lxc ================================================ FILE: install/openziti-tunnel-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: emoscardini # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/openziti/ziti source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing openziti" mkdir -p --mode=0755 /usr/share/keyrings curl -sSLf https://get.openziti.io/tun/package-repos.gpg | gpg --dearmor -o /usr/share/keyrings/openziti.gpg cat </etc/apt/sources.list.d/openziti.sources Types: deb URIs: https://packages.openziti.org/zitipax-openziti-deb-stable Suites: jammy Components: main Signed-By: /usr/share/keyrings/openziti.gpg EOF $STD apt update $STD apt install -y ziti-edge-tunnel sed -i '0,/^ExecStart/ { /^ExecStart/ { n; s|^ExecStart.*|ExecStart=/opt/openziti/bin/ziti-edge-tunnel run-host --verbose=${ZITI_VERBOSE} --identity-dir=${ZITI_IDENTITY_DIR}| } }' /usr/lib/systemd/system/ziti-edge-tunnel.service systemctl daemon-reload msg_ok "Installed openziti" read -r -p "${TAB3}Please paste an identity enrollment token(JTW)" prompt if [[ ${prompt} ]]; then msg_info "Adding identity" echo "${prompt}" >/opt/openziti/etc/identities/identity.jwt chown ziti:ziti /opt/openziti/etc/identities/identity.jwt systemctl enable -q --now ziti-edge-tunnel msg_ok "Service Started" else systemctl enable -q ziti-edge-tunnel msg_error "No identity provided; please place an identity file in /opt/openziti/etc/identities/ and restart the service" fi motd_ssh customize cleanup_lxc ================================================ FILE: install/ots-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: bvberg01 # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/Luzifer/ots source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ redis-server \ nginx msg_ok "Installed Dependencies" fetch_and_deploy_gh_release "ots" "Luzifer/ots" "prebuild" "latest" "/opt/ots" "ots_linux_arm64.tgz" create_self_signed_cert msg_info "Setup OTS" cat </opt/ots/.env LISTEN=127.0.0.1:3000 REDIS_URL=redis://127.0.0.1:6379 SECRET_EXPIRY=604800 STORAGE_TYPE=redis EOF msg_ok "Setup OTS" msg_info "Setting up nginx" cat </etc/nginx/sites-available/ots.conf server { listen 80; listen [::]:80; server_name ots; return 301 https://\$host\$request_uri; } server { listen 443 ssl; listen [::]:443 ssl; server_name ots; ssl_certificate /etc/ssl/ots/ots.crt; ssl_certificate_key /etc/ssl/ots/ots.key; location / { add_header X-Robots-Tag noindex; proxy_set_header Upgrade \$http_upgrade; proxy_set_header Connection "Upgrade"; proxy_set_header Host \$host; proxy_set_header X-Real-IP \$remote_addr; proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto \$scheme; client_max_body_size 64M; proxy_pass http://127.0.0.1:3000/; } } EOF ln -s /etc/nginx/sites-available/ots.conf /etc/nginx/sites-enabled/ rm -f /etc/nginx/sites-enabled/default $STD systemctl reload nginx msg_ok "Configured nginx" msg_info "Creating Services" cat </etc/systemd/system/ots.service [Unit] Description=One-Time-Secret Service After=network-online.target Requires=network-online.target [Service] EnvironmentFile=/opt/ots/.env ExecStart=/opt/ots/ots Restart=Always RestartSecs=5 [Install] WantedBy=multi-user.target EOF systemctl enable -q --now ots msg_ok "Created Services" motd_ssh customize cleanup_lxc ================================================ FILE: install/outline-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: Slaviša Arežina (tremor021) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/outline/outline source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ mkcert \ git \ redis msg_ok "Installed Dependencies" NODE_VERSION="24" setup_nodejs PG_VERSION="16" setup_postgresql PG_DB_NAME="outline" PG_DB_USER="outline" setup_postgresql_db fetch_and_deploy_gh_release "outline" "outline/outline" "tarball" msg_info "Configuring Outline (Patience)" SECRET_KEY="$(openssl rand -hex 32)" cd /opt/outline cp .env.sample .env export NODE_ENV=development sed -i 's/NODE_ENV=production/NODE_ENV=development/g' /opt/outline/.env sed -i "s/generate_a_new_key/${SECRET_KEY}/g" /opt/outline/.env sed -i "s/user:pass@postgres/${PG_DB_USER}:${PG_DB_PASS}@localhost/g" /opt/outline/.env sed -i 's/redis:6379/localhost:6379/g' /opt/outline/.env sed -i "5s#URL=#URL=http://${LOCAL_IP}#g" /opt/outline/.env sed -i 's/FORCE_HTTPS=true/FORCE_HTTPS=false/g' /opt/outline/.env export NODE_OPTIONS="--max-old-space-size=3584" export COREPACK_ENABLE_DOWNLOAD_PROMPT=0 $STD corepack enable $STD yarn install --immutable export NODE_ENV=production sed -i 's/NODE_ENV=development/NODE_ENV=production/g' /opt/outline/.env $STD yarn build msg_ok "Configured Outline" msg_info "Creating Service" cat </etc/systemd/system/outline.service [Unit] Description=Outline Service After=network.target [Service] Type=simple User=root WorkingDirectory=/opt/outline ExecStart=/usr/bin/yarn start Restart=always EnvironmentFile=/opt/outline/.env [Install] WantedBy=multi-user.target EOF systemctl enable -q --now outline msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/owncast-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://owncast.online/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os setup_hwaccel msg_info "Installing Dependencies (Patience)" $STD apt install -y ffmpeg msg_ok "Installed Dependencies" fetch_and_deploy_gh_release "owncast" "owncast/owncast" "prebuild" "latest" "/opt/owncast" "owncast*linux-arm64.zip" msg_info "Creating Service" cat </etc/systemd/system/owncast.service [Unit] Description=Owncast After=syslog.target network-online.target [Service] ExecStart=/opt/owncast/./owncast WorkingDirectory=/opt/owncast Restart=always [Install] WantedBy=multi-user.target EOF systemctl enable -q --now owncast msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/ownfoil-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: pajjski # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/a1ex4/ownfoil source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y git msg_ok "Installed Dependencies" setup_uv fetch_and_deploy_gh_release "ownfoil" "a1ex4/ownfoil" "tarball" msg_info "Setting up Ownfoil" cd /opt/ownfoil $STD uv venv .venv $STD source .venv/bin/activate $STD uv pip install -r requirements.txt msg_ok "Setup ownfoil" msg_info "Creating Service" cat </etc/systemd/system/ownfoil.service [Unit] Description=ownfoil Service After=network.target [Service] Type=simple User=root WorkingDirectory=/opt/ownfoil ExecStart=/opt/ownfoil/.venv/bin/python /opt/ownfoil/app/app.py Restart=on-failure RestartSec=5 [Install] WantedBy=multi-user.target EOF systemctl enable -q --now ownfoil msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/pairdrop-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://pairdrop.net/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os NODE_VERSION="22" setup_nodejs fetch_and_deploy_gh_release "pairdrop" "schlagmichdoch/PairDrop" "tarball" msg_info "Configuring PairDrop" cd /opt/pairdrop $STD npm install msg_ok "Installed PairDrop" msg_info "Creating Service" cat </etc/systemd/system/pairdrop.service [Unit] Description=PairDrop Service After=network.target [Service] ExecStart=npm start WorkingDirectory=/opt/pairdrop Restart=always [Install] WantedBy=multi-user.target EOF systemctl enable -q --now pairdrop msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/pangolin-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: Slaviša Arežina (tremor021) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://pangolin.net/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ build-essential \ python3 \ sqlite3 \ iptables msg_ok "Installed Dependencies" NODE_VERSION="24" setup_nodejs fetch_and_deploy_gh_release "pangolin" "fosrl/pangolin" "tarball" fetch_and_deploy_gh_release "gerbil" "fosrl/gerbil" "singlefile" "latest" "/usr/bin" "gerbil_linux_arm64" fetch_and_deploy_gh_release "traefik" "traefik/traefik" "prebuild" "latest" "/usr/bin" "traefik_v*_linux_arm64.tar.gz" read -rp "${TAB3}Enter your Pangolin URL (ex: https://pangolin.example.com): " pango_url read -rp "${TAB3}Enter your email address: " pango_email msg_info "Setup Pangolin" SECRET_KEY=$(openssl rand -base64 48 | tr -dc 'A-Za-z0-9' | head -c 32) BADGER_VERSION=$(get_latest_github_release "fosrl/badger" "false") cd /opt/pangolin mkdir -p /opt/pangolin/config/{traefik,db,letsencrypt,logs} $STD npm ci $STD npm run set:sqlite $STD npm run set:oss rm -rf server/private $STD npm run db:generate $STD npm run build $STD npm run build:cli cp -R .next/standalone ./ cat </usr/local/bin/pangctl #!/bin/sh cd /opt/pangolin ./dist/cli.mjs "$@" EOF chmod +x /usr/local/bin/pangctl ./dist/cli.mjs cp server/db/names.json ./dist/names.json cp server/db/ios_models.json ./dist/ios_models.json cp server/db/mac_models.json ./dist/mac_models.json mkdir -p /var/config cat </opt/pangolin/config/config.yml app: dashboard_url: "$pango_url" domains: domain1: base_domain: "$pango_url" cert_resolver: "letsencrypt" server: secret: "$SECRET_KEY" gerbil: base_endpoint: "${pango_url#https://}" flags: require_email_verification: false disable_signup_without_invite: false disable_user_create_org: false EOF cat </opt/pangolin/config/traefik/traefik_config.yml api: insecure: true dashboard: true providers: http: endpoint: "http://$LOCAL_IP:3001/api/v1/traefik-config" pollInterval: "5s" file: filename: "/opt/pangolin/config/traefik/dynamic_config.yml" experimental: plugins: badger: moduleName: "github.com/fosrl/badger" version: "$BADGER_VERSION" log: level: "INFO" format: "common" certificatesResolvers: letsencrypt: acme: httpChallenge: entryPoint: web email: $pango_email storage: "/opt/pangolin/config/letsencrypt/acme.json" caServer: "https://acme-v02.api.letsencrypt.org/directory" entryPoints: web: address: ":80" websecure: address: ":443" transport: respondingTimeouts: readTimeout: "30m" http: tls: certResolver: "letsencrypt" serversTransport: insecureSkipVerify: true ping: entryPoint: "web" EOF cat </opt/pangolin/config/traefik/dynamic_config.yml http: middlewares: redirect-to-https: redirectScheme: scheme: https routers: # HTTP to HTTPS redirect router main-app-router-redirect: rule: "Host(\`${pango_url#https://}\`)" service: next-service entryPoints: - web middlewares: - redirect-to-https # Next.js router (handles everything except API and WebSocket paths) next-router: rule: "Host(\`${pango_url#https://}\`) && !PathPrefix(\`/api/v1\`)" service: next-service entryPoints: - websecure tls: certResolver: letsencrypt # API router (handles /api/v1 paths) api-router: rule: "Host(\`${pango_url#https://}\`) && PathPrefix(\`/api/v1\`)" service: api-service entryPoints: - websecure tls: certResolver: letsencrypt # WebSocket router ws-router: rule: "Host(\`${pango_url#https://}\`)" service: api-service entryPoints: - websecure tls: certResolver: letsencrypt services: next-service: loadBalancer: servers: - url: "http://$LOCAL_IP:3002" api-service: loadBalancer: servers: - url: "http://$LOCAL_IP:3000" EOF $STD npm run db:push . /etc/os-release if [ "$VERSION_CODENAME" = "trixie" ]; then echo "net.ipv4.ip_forward=1" >>/etc/sysctl.d/sysctl.conf $STD sysctl -p /etc/sysctl.d/sysctl.conf else echo "net.ipv4.ip_forward=1" >>/etc/sysctl.conf $STD sysctl -p /etc/sysctl.conf fi msg_ok "Setup Pangolin" msg_info "Creating Services" cat </etc/systemd/system/pangolin.service [Unit] Description=Pangolin Service After=network.target [Service] Type=simple User=root Environment=NODE_ENV=production Environment=ENVIRONMENT=prod WorkingDirectory=/opt/pangolin ExecStartPre=/usr/bin/node dist/migrations.mjs ExecStart=/usr/bin/node --enable-source-maps dist/server.mjs Restart=always RestartSec=10 [Install] WantedBy=multi-user.target EOF systemctl enable -q --now pangolin cat </etc/systemd/system/gerbil.service [Unit] Description=Gerbil Service After=network.target Requires=pangolin.service [Service] Type=simple User=root ExecStart=/usr/bin/gerbil --reachableAt=http://$LOCAL_IP:3004 --generateAndSaveKeyTo=/var/config/key --remoteConfig=http://$LOCAL_IP:3001/api/v1/ Restart=always RestartSec=10 [Install] WantedBy=multi-user.target EOF systemctl enable -q --now gerbil cat <<'EOF' >/etc/systemd/system/traefik.service [Unit] Description=Traefik is an open-source Edge Router that makes publishing your services a fun and easy experience Wants=network-online.target After=network-online.target [Service] Type=notify ExecStart=/usr/bin/traefik --configFile=/opt/pangolin/config/traefik/traefik_config.yml Restart=on-failure ExecReload=/bin/kill -USR1 \$MAINPID [Install] WantedBy=multi-user.target EOF systemctl enable -q --now traefik msg_ok "Created Services" motd_ssh customize cleanup_lxc ================================================ FILE: install/paperless-ai-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/clusterzx/paperless-ai source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ build-essential msg_ok "Installed Dependencies" msg_info "Installing Python3" $STD apt install -y \ python3-pip \ python3-dev \ python3-venv mkdir -p ~/.config/pip cat >~/.config/pip/pip.conf </opt/paperless-ai/data/.env PAPERLESS_API_URL= PAPERLESS_API_TOKEN= PAPERLESS_USERNAME= AI_PROVIDER=openai OPENAI_API_KEY= OPENAI_MODEL=gpt-4o-mini OLLAMA_API_URL= OLLAMA_MODEL= SCAN_INTERVAL=*/10 * * * * SYSTEM_PROMPT="" PROCESS_PREDEFINED_DOCUMENTS=no TAGS= ADD_AI_PROCESSED_TAG=no AI_PROCESSED_TAG_NAME=ki-gen USE_PROMPT_TAGS=no PROMPT_TAGS= USE_EXISTING_DATA=no API_KEY= CUSTOM_API_KEY= CUSTOM_BASE_URL= CUSTOM_MODEL= RAG_SERVICE_URL=http://localhost:8000 RAG_SERVICE_ENABLED=true EOF msg_ok "Setup Paperless-AI" msg_info "Creating Service" cat </etc/systemd/system/paperless-ai.service [Unit] Description=PaperlessAI Service After=network.target paperless-rag.service Requires=paperless-rag.service [Service] WorkingDirectory=/opt/paperless-ai Environment="NODE_ENV=production" EnvironmentFile=/opt/paperless-ai/data/.env ExecStart=/usr/bin/node server.js Restart=always [Install] WantedBy=multi-user.target EOF cat </etc/systemd/system/paperless-rag.service [Unit] Description=PaperlessAI-RAG Service After=network.target [Service] WorkingDirectory=/opt/paperless-ai EnvironmentFile=/opt/paperless-ai/data/.env ExecStart=/opt/paperless-ai/venv/bin/python3 main.py --host 0.0.0.0 --port 8000 --initialize Restart=always [Install] WantedBy=multi-user.target EOF systemctl enable -q --now paperless-rag sleep 5 systemctl enable -q --now paperless-ai msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/paperless-gpt-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/icereed/paperless-gpt source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ gcc \ musl-dev \ mupdf \ libc6-dev \ musl-tools msg_ok "Installed Dependencies" NODE_VERSION="24" setup_nodejs setup_go fetch_and_deploy_gh_release "paperless-gpt" "icereed/paperless-gpt" "tarball" msg_info "Setup Paperless-GPT" cd /opt/paperless-gpt/web-app $STD npm install $STD npm run build cd /opt/paperless-gpt go mod download export CC=musl-gcc CGO_ENABLED=1 go build -tags musl -o /dev/null github.com/mattn/go-sqlite3 CGO_ENABLED=1 go build -tags musl -o paperless-gpt . msg_ok "Setup Paperless-GPT" mkdir -p /opt/paperless-gpt-data read -rp "${TAB3}Do you want to enter the Paperless local URL now? (y/n) " input_url if [[ $input_url =~ ^[Yy]$ ]]; then read -rp "${TAB3}Enter your Paperless-NGX instance URL (e.g., http://192.168.1.100:8000): " PAPERLESS_BASE_URL else PAPERLESS_BASE_URL="http://your_paperless_ngx_url" fi read -rp "${TAB3}Do you want to enter the Paperless API token now? (y/n) " input_token if [[ $input_token =~ ^[Yy]$ ]]; then read -rp "${TAB3}Enter your Paperless API token: " PAPERLESS_API_TOKEN else PAPERLESS_API_TOKEN="your_paperless_api_token" fi msg_info "Setup Environment" cat </opt/paperless-gpt-data/.env PAPERLESS_BASE_URL=$PAPERLESS_BASE_URL PAPERLESS_API_TOKEN=$PAPERLESS_API_TOKEN LLM_PROVIDER=openai LLM_MODEL=gpt-4o OPENAI_API_KEY=your_openai_api_key #VISION_LLM_PROVIDER=ollama #VISION_LLM_MODEL=minicpm-v LLM_LANGUAGE=English LOG_LEVEL=info LISTEN_INTERFACE=:8080 AUTO_TAG=paperless-gpt-auto MANUAL_TAG=paperless-gpt AUTO_OCR_TAG=paperless-gpt-ocr-auto OCR_LIMIT_PAGES=5 EOF msg_ok "Setup Environment" msg_info "Creating Service" cat </etc/systemd/system/paperless-gpt.service [Unit] Description=Paperless-GPT After=network.target [Service] Type=simple WorkingDirectory=/opt/paperless-gpt ExecStart=/opt/paperless-gpt/paperless-gpt Restart=always User=root EnvironmentFile=/opt/paperless-gpt-data/.env StandardOutput=append:/var/log/paperless-gpt.log StandardError=append:/var/log/paperless-gpt.log [Install] WantedBy=multi-user.target EOF systemctl enable -q --now paperless-gpt msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/paperless-ngx-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) | MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://docs.paperless-ngx.com/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies (Patience)" $STD apt install -y \ redis \ build-essential \ imagemagick \ fonts-liberation \ optipng \ libpq-dev \ libmagic-dev \ libzbar0t64 \ poppler-utils \ default-libmysqlclient-dev \ automake \ libtool \ pkg-config \ libtiff-dev \ libpng-dev \ libleptonica-dev \ unpaper \ icc-profiles-free \ qpdf \ libleptonica6 \ libxml2 \ pngquant \ zlib1g \ tesseract-ocr \ tesseract-ocr-eng \ ghostscript msg_ok "Installed Dependencies" PG_VERSION="16" setup_postgresql PG_DB_NAME="paperlessdb" PG_DB_USER="paperless" setup_postgresql_db PYTHON_VERSION="3.13" setup_uv fetch_and_deploy_gh_release "paperless" "paperless-ngx/paperless-ngx" "prebuild" "latest" "/opt/paperless" "paperless*tar.xz" msg_info "Setup Paperless-ngx" cd /opt/paperless rm -rf /opt/paperless/docker $STD uv sync --all-extras curl -fsSL "https://raw.githubusercontent.com/paperless-ngx/paperless-ngx/main/paperless.conf.example" -o /opt/paperless/paperless.conf mkdir -p /opt/paperless_data/{consume,data,media,trash} mkdir -p /opt/paperless/static SECRET_KEY="$(head /dev/urandom | tr -dc A-Za-z0-9 | head -c 32)" { echo "" echo "Paperless-ngx Secret Key: $SECRET_KEY" echo "Paperless-ngx WebUI User: admin" echo "Paperless-ngx WebUI Password: $PG_DB_PASS" } >>~/paperless-ngx.creds sed -i \ -e 's|#PAPERLESS_REDIS=redis://localhost:6379|PAPERLESS_REDIS=redis://localhost:6379|' \ -e "s|#PAPERLESS_CONSUMPTION_DIR=../consume|PAPERLESS_CONSUMPTION_DIR=/opt/paperless_data/consume|" \ -e "s|#PAPERLESS_DATA_DIR=../data|PAPERLESS_DATA_DIR=/opt/paperless_data/data|" \ -e "s|#PAPERLESS_MEDIA_ROOT=../media|PAPERLESS_MEDIA_ROOT=/opt/paperless_data/media|" \ -e "s|#PAPERLESS_EMPTY_TRASH_DIR=|PAPERLESS_EMPTY_TRASH_DIR=/opt/paperless_data/trash|" \ -e "s|#PAPERLESS_STATICDIR=../static|PAPERLESS_STATICDIR=/opt/paperless/static|" \ -e 's|#PAPERLESS_DBHOST=localhost|PAPERLESS_DBHOST=localhost|' \ -e 's|#PAPERLESS_DBPORT=5432|PAPERLESS_DBPORT=5432|' \ -e "s|#PAPERLESS_DBNAME=paperless|PAPERLESS_DBNAME=$PG_DB_NAME|" \ -e "s|#PAPERLESS_DBUSER=paperless|PAPERLESS_DBUSER=$PG_DB_USER|" \ -e "s|#PAPERLESS_DBPASS=paperless|PAPERLESS_DBPASS=$PG_DB_PASS|" \ -e "s|#PAPERLESS_SECRET_KEY=change-me|PAPERLESS_SECRET_KEY=$SECRET_KEY|" \ /opt/paperless/paperless.conf cd /opt/paperless/src set -a . /opt/paperless/paperless.conf set +a $STD uv run -- python manage.py migrate msg_ok "Setup Paperless-ngx" msg_info "Setting up admin Paperless-ngx User & Password" cat </etc/systemd/system/paperless-scheduler.service [Unit] Description=Paperless Celery beat Requires=redis.service [Service] WorkingDirectory=/opt/paperless/src ExecStart=uv run -- celery --app paperless beat --loglevel INFO [Install] WantedBy=multi-user.target EOF cat </etc/systemd/system/paperless-task-queue.service [Unit] Description=Paperless Celery Workers Requires=redis.service After=postgresql.service [Service] WorkingDirectory=/opt/paperless/src ExecStart=uv run -- celery --app paperless worker --loglevel INFO [Install] WantedBy=multi-user.target EOF cat </etc/systemd/system/paperless-consumer.service [Unit] Description=Paperless consumer Requires=redis.service [Service] WorkingDirectory=/opt/paperless/src ExecStartPre=/bin/sleep 2 ExecStart=uv run -- python manage.py document_consumer [Install] WantedBy=multi-user.target EOF cat </etc/systemd/system/paperless-webserver.service [Unit] Description=Paperless webserver After=network.target Wants=network.target Requires=redis.service [Service] WorkingDirectory=/opt/paperless/src ExecStart=uv run -- granian --interface asgi --ws "paperless.asgi:application" Environment=GRANIAN_HOST=:: Environment=GRANIAN_PORT=8000 Environment=GRANIAN_WORKERS=1 [Install] WantedBy=multi-user.target EOF systemctl enable -q --now paperless-webserver paperless-scheduler paperless-task-queue paperless-consumer msg_ok "Created Services" read -r -p "${TAB3}Would you like to add Adminer? " prompt if [[ "${prompt,,}" =~ ^(y|yes)$ ]]; then setup_adminer fi motd_ssh customize cleanup_lxc ================================================ FILE: install/papra-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/papra-hq/papra source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ build-essential \ tesseract-ocr \ tesseract-ocr-all msg_ok "Installed Dependencies" RELEASE=$(curl -fsSL https://api.github.com/repos/papra-hq/papra/releases | grep -oP '"tag_name":\s*"\K@papra/app@[^"]+' | head -n1) fetch_and_deploy_gh_release "papra" "papra-hq/papra" "tarball" "${RELEASE}" "/opt/papra" pnpm_version=$(grep -oP '"packageManager":\s*"pnpm@\K[^"]+' /opt/papra/package.json) NODE_VERSION="24" NODE_MODULE="pnpm@$pnpm_version" setup_nodejs msg_info "Installing Papra (Patience)" cd /opt/papra $STD pnpm install --frozen-lockfile $STD pnpm --filter "@papra/app-client..." run build $STD pnpm --filter "@papra/app-server..." run build ln -sf /opt/papra/apps/papra-client/dist /opt/papra/apps/papra-server/public msg_ok "Installed Papra" msg_info "Configuring Papra" mkdir -p /opt/papra_data/{db,documents,ingestion} [[ ! -f /opt/papra_data/.secret ]] && openssl rand -hex 32 >/opt/papra_data/.secret cat </opt/papra/apps/papra-server/.env NODE_ENV=production SERVER_SERVE_PUBLIC_DIR=true PORT=1221 DATABASE_URL=file:/opt/papra_data/db/db.sqlite DOCUMENT_STORAGE_FILESYSTEM_ROOT=/opt/papra_data/documents PAPRA_CONFIG_DIR=/opt/papra_data AUTH_SECRET=$(cat /opt/papra_data/.secret) BETTER_AUTH_SECRET=$(cat /opt/papra_data/.secret) BETTER_AUTH_TELEMETRY=0 CLIENT_BASE_URL=http://${LOCAL_IP}:1221 SERVER_BASE_URL=http://${LOCAL_IP}:1221 EMAILS_DRY_RUN=true INGESTION_FOLDER_IS_ENABLED=true INGESTION_FOLDER_ROOT_PATH=/opt/papra_data/ingestion EOF msg_ok "Configured Papra" msg_info "Creating Service" cat </etc/systemd/system/papra.service [Unit] Description=Papra Document Management After=network.target [Service] Type=simple WorkingDirectory=/opt/papra/apps/papra-server EnvironmentFile=/opt/papra/apps/papra-server/.env ExecStartPre=/usr/bin/pnpm run migrate:up ExecStart=/usr/bin/node dist/index.js [Install] WantedBy=multi-user.target EOF systemctl enable -q --now papra msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/part-db-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: bvdberg01 # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://docs.part-db.de/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os PG_VERSION="16" setup_postgresql PG_DB_NAME="partdb" PG_DB_USER="partdb" setup_postgresql_db PHP_VERSION="8.4" PHP_APACHE="YES" PHP_MODULE="xsl" PHP_POST_MAX_SIZE="100M" PHP_UPLOAD_MAX_FILESIZE="100M" setup_php setup_composer fetch_and_deploy_gh_release "partdb" "Part-DB/Part-DB-server" "prebuild" "latest" "/opt/partdb" "partdb_with_assets.zip" msg_info "Installing Part-DB" cd /opt/partdb/ cp .env .env.local sed -i "s|DATABASE_URL=\"sqlite:///%kernel.project_dir%/var/app.db\"|DATABASE_URL=\"postgresql://${PG_DB_USER}:${PG_DB_PASS}@127.0.0.1:5432/${PG_DB_NAME}?serverVersion=12.19&charset=utf8\"|" .env.local export COMPOSER_ALLOW_SUPERUSER=1 $STD composer install --no-dev -o --no-interaction $STD php bin/console cache:clear php bin/console doctrine:migrations:migrate -n >~/database-migration-output chown -R www-data:www-data /opt/partdb ADMIN_PASS=$(grep -oP 'The initial password for the "admin" user is: \K\w+' ~/database-migration-output) { echo "" echo "Part-DB Admin User: admin" echo "Part-DB Admin Password: $ADMIN_PASS" } >>~/partdb.creds rm -rf ~/database-migration-output msg_ok "Installed Part-DB" msg_info "Creating Service" cat </etc/apache2/sites-available/partdb.conf ServerName partdb DocumentRoot /opt/partdb/public Options FollowSymLinks AllowOverride All Require all granted ErrorLog /var/log/apache2/partdb_error.log CustomLog /var/log/apache2/partdb_access.log combined EOF $STD a2ensite partdb $STD a2enmod rewrite $STD a2dissite 000-default.conf $STD systemctl reload apache2 msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/passbolt-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: Slaviša Arežina (tremor021) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.passbolt.com/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing dependencies" $STD apt install -y \ apt-transport-https \ python3-certbot-nginx \ debconf-utils msg_ok "Installed dependencies" setup_mariadb MARIADB_DB_NAME="passboltdb" MARIADB_DB_USER="passbolt" setup_mariadb_db create_self_signed_cert setup_deb822_repo \ "passbolt" \ "https://keys.openpgp.org/pks/lookup?op=get&options=mr&search=0x3D1A0346C8E1802F774AEF21DE8B853FC155581D" \ "https://download.passbolt.com/ce/debian" \ "buster" \ "stable" msg_info "Setting up Passbolt (Patience)" export DEBIAN_FRONTEND=noninteractive echo passbolt-ce-server passbolt/mysql-configuration boolean true | debconf-set-selections echo passbolt-ce-server passbolt/mysql-passbolt-username string $MARIADB_DB_USER | debconf-set-selections echo passbolt-ce-server passbolt/mysql-passbolt-password password $MARIADB_DB_PASS | debconf-set-selections echo passbolt-ce-server passbolt/mysql-passbolt-password-repeat password $MARIADB_DB_PASS | debconf-set-selections echo passbolt-ce-server passbolt/mysql-passbolt-dbname string $MARIADB_DB_NAME | debconf-set-selections echo passbolt-ce-server passbolt/nginx-configuration boolean true | debconf-set-selections echo passbolt-ce-server passbolt/nginx-configuration-three-choices select manual | debconf-set-selections echo passbolt-ce-server passbolt/nginx-domain string $LOCAL_IP | debconf-set-selections echo passbolt-ce-server passbolt/nginx-certificate-file string /etc/ssl/passbolt/passbolt.crt | debconf-set-selections echo passbolt-ce-server passbolt/nginx-certificate-key-file string /etc/ssl/passbolt/passbolt.key | debconf-set-selections $STD apt install -y --no-install-recommends passbolt-ce-server sed -i 's/client_max_body_size[[:space:]]\+[0-9]\+M;/client_max_body_size 15M;/' /etc/nginx/sites-enabled/nginx-passbolt.conf systemctl reload nginx msg_ok "Setup Passbolt" motd_ssh customize cleanup_lxc ================================================ FILE: install/patchmon-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: vhsdream # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/PatcMmon/PatchMon source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y redis-server msg_ok "Installed Dependencies" PG_VERSION="17" setup_postgresql PG_DB_NAME="patchmon_db" PG_DB_USER="patchmon_usr" setup_postgresql_db RELEASE="v2.0.2" fetch_and_deploy_gh_release "PatchMon" "PatchMon/PatchMon" "singlefile" "latest" "/opt/patchmon" "patchmon-server-linux-amd64" mv /opt/patchmon/PatchMon /opt/patchmon/patchmon-server msg_info "Configuring PatchMon" cat </opt/patchmon/.env DATABASE_URL="postgresql://$PG_DB_USER:$PG_DB_PASS@localhost:5432/$PG_DB_NAME" JWT_SECRET="$(openssl rand -hex 64)" SESSION_SECRET="$(openssl rand -hex 64)" AI_ENCRYPTION_KEY="$(openssl rand -hex 64)" CORS_ORIGIN=http://${LOCAL_IP}:3000 PORT=3000 APP_ENV=production # Redis REDIS_HOST=localhost REDIS_PORT=6379 ## OIDC / SSO (when OIDC_ENABLED=true, issuer/client/secret/redirect required) # OIDC_ENABLED=false # OIDC_ISSUER_URL= # OIDC_CLIENT_ID= # OIDC_CLIENT_SECRET= # OIDC_REDIRECT_URI= # OIDC_SCOPES=openid email profile groups # OIDC_AUTO_CREATE_USERS=false # OIDC_DEFAULT_ROLE=user # OIDC_DISABLE_LOCAL_AUTH=false # OIDC_BUTTON_TEXT=Login with SSO # OIDC_SESSION_TTL=600 # OIDC_POST_LOGOUT_URI= # OIDC_SYNC_ROLES=false # OIDC_ADMIN_GROUP= # OIDC_SUPERADMIN_GROUP= # OIDC_HOST_MANAGER_GROUP= # OIDC_READONLY_GROUP= # OIDC_USER_GROUP= # OIDC_ENFORCE_HTTPS=true AGENT_BINARIES_DIR=/opt/patchmon/agents EOF msg_ok "Configured PatchMon" msg_info "Fetching PatchMon agent binaries" RELEASE=$(get_latest_github_release "PatchMon/PatchMon") mkdir -p /opt/patchmon/agents FILE_URL="https://github.com/PatchMon/PatchMon/releases/download/v${RELEASE}/patchmon-agent-" AGENT_NAME=( "linux-amd64" "linux-arm64" "linux-arm" "linux-386" "freebsd-amd64" "freebsd-arm64" "freebsd-arm" "freebsd-386" "windows-amd64.exe" "windows-arm64.exe" ) for arch in "${AGENT_NAME[@]}"; do curl_with_retry "${FILE_URL}${arch}" "/opt/patchmon/agents/patchmon-agent-${arch}" [[ "${arch}" != *.exe ]] && chmod 755 "/opt/patchmon/agents/patchmon-agent-${arch}" done msg_ok "Fetched PatchMon agent binaries" msg_info "Creating service" cat </etc/systemd/system/patchmon-server.service [Unit] Description=PatchMon Server After=network.target postgresql.service [Service] Type=simple WorkingDirectory=/opt/patchmon ExecStart=/opt/patchmon/patchmon-server Restart=always RestartSec=10 Environment=PATH=/usr/bin:/usr/local/bin EnvironmentFile=/opt/patchmon/.env NoNewPrivileges=true PrivateTmp=true ProtectSystem=strict ProtectHome=true ReadWritePaths=/opt/patchmon [Install] WantedBy=multi-user.target EOF systemctl enable -q --now patchmon-server msg_ok "Created and started service" motd_ssh customize cleanup_lxc ================================================ FILE: install/paymenter-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: Nícolas Pastorello (opastorello) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.paymenter.org source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ git \ nginx \ redis-server \ cron msg_ok "Installed Dependencies" setup_mariadb PHP_VERSION="8.3" PHP_FPM="YES" setup_php setup_composer fetch_and_deploy_gh_release "paymenter" "paymenter/paymenter" "prebuild" "latest" "/opt/paymenter" "paymenter.tar.gz" chmod -R 755 /opt/paymenter/storage/* /opt/paymenter/bootstrap/cache/ msg_info "Setting up database" DB_NAME=paymenter DB_USER=paymenter DB_PASS=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | head -c13) mariadb-tzinfo-to-sql /usr/share/zoneinfo | mariadb mysql $STD mariadb -u root -e "CREATE DATABASE $DB_NAME;" $STD mariadb -u root -e "CREATE USER '$DB_USER'@'localhost' IDENTIFIED BY '$DB_PASS';" $STD mariadb -u root -e "GRANT ALL PRIVILEGES ON $DB_NAME.* TO '$DB_USER'@'localhost' WITH GRANT OPTION;" { echo "Paymenter Database Credentials" echo "Database: $DB_NAME" echo "Username: $DB_USER" echo "Password: $DB_PASS" } >>~/paymenter_db.creds cd /opt/paymenter cp .env.example .env $STD composer install --no-dev --optimize-autoloader --no-interaction $STD php artisan key:generate --force $STD php artisan storage:link sed -i "s/^DB_DATABASE=.*/DB_DATABASE=${DB_NAME}/" .env sed -i "s/^DB_USERNAME=.*/DB_USERNAME=${DB_USER}/" .env sed -i "s/^DB_PASSWORD=.*/DB_PASSWORD=${DB_PASS}/" .env $STD php artisan migrate --force --seed msg_ok "Set up database" msg_info "Creating Admin User" $STD php artisan app:user:create paymenter admin admin@paymenter.org paymenter 1 -q msg_ok "Created Admin User" msg_info "Configuring Nginx" cat </etc/nginx/sites-available/paymenter.conf server { listen 80; listen [::]:80; server_name localhost; root /opt/paymenter/public; index index.php; location / { try_files \$uri \$uri/ /index.php?\$query_string; } location ~ \.php\$ { include snippets/fastcgi-php.conf; fastcgi_pass unix:/var/run/php/php8.3-fpm.sock; fastcgi_param SCRIPT_FILENAME \$document_root\$fastcgi_script_name; include fastcgi_params; } location ~ /\.ht { deny all; } } EOF ln -s /etc/nginx/sites-available/paymenter.conf /etc/nginx/sites-enabled/ rm -f /etc/nginx/sites-enabled/default $STD systemctl reload nginx chown -R www-data:www-data /opt/paymenter/* msg_ok "Configured Nginx" msg_info "Setting up Cronjob" echo "* * * * * php /opt/paymenter/artisan schedule:run >> /dev/null 2>&1" | crontab - msg_ok "Setup Cronjob" msg_info "Setting up Service" cat </etc/systemd/system/paymenter.service [Unit] Description=Paymenter Queue Worker [Service] User=www-data Group=www-data Restart=always ExecStart=/usr/bin/php /opt/paymenter/artisan queue:work StartLimitInterval=180 StartLimitBurst=30 RestartSec=5s [Install] WantedBy=multi-user.target EOF systemctl enable -q --now paymenter systemctl enable -q --now redis-server msg_ok "Setup Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/peanut-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # Co-Author: remz1337 # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/Brandawg93/PeaNUT/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing NUT" $STD apt install -y nut-client msg_ok "Installed NUT" NODE_VERSION="24" NODE_MODULE="pnpm" setup_nodejs fetch_and_deploy_gh_release "peanut" "Brandawg93/PeaNUT" "tarball" "latest" "/opt/peanut" msg_info "Setup Peanut" cd /opt/peanut $STD pnpm i $STD pnpm run build:local cp -r .next/static .next/standalone/.next/ mkdir -p /opt/peanut/.next/standalone/config mkdir -p /etc/peanut/ ln -sf .next/standalone/server.js server.js if [[ ! -f /etc/peanut/settings.yml ]]; then cat </etc/peanut/settings.yml NUT_SERVERS: [] EOF fi ln -sf /etc/peanut/settings.yml /opt/peanut/.next/standalone/config/settings.yml cat </etc/peanut/peanut.env NODE_ENV=production #WEB_HOST=0.0.0.0 #WEB_PORT=8080 #NUT_HOST=localhost #NUT_PORT=3493 # Disable auth entirely: #AUTH_DISABLED=true # Bootstrap initial account on first start (ignored afterwards): #WEB_USERNAME=admin #WEB_PASSWORD=changeme EOF chmod 600 /etc/peanut/peanut.env msg_ok "Setup Peanut" msg_info "Creating Service" cat </etc/systemd/system/peanut.service [Unit] Description=Peanut After=network.target [Service] SyslogIdentifier=peanut Restart=always RestartSec=5 Type=simple EnvironmentFile=/etc/peanut/peanut.env WorkingDirectory=/opt/peanut ExecStart=node /opt/peanut/entrypoint.mjs TimeoutStopSec=30 [Install] WantedBy=multi-user.target EOF systemctl enable -q --now peanut msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/pelican-panel-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: bvdberg01 # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/pelican-dev/panel source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ lsb-release \ cron msg_ok "Installed Dependencies" PHP_VERSION="8.4" PHP_APACHE="YES" PHP_FPM="YES" setup_php setup_composer setup_mariadb MARIADB_DB_NAME="panel" MARIADB_DB_USER="pelican" setup_mariadb_db fetch_and_deploy_gh_release "pelican-panel" "pelican-dev/panel" "prebuild" "latest" "/opt/pelican-panel" "panel.tar.gz" msg_info "Installing Pelican Panel" cd /opt/pelican-panel $STD composer install --no-dev --optimize-autoloader --no-interaction $STD php artisan p:environment:setup $STD php artisan p:environment:queue-service --no-interaction echo "* * * * * php /opt/pelican-panel/artisan schedule:run >> /dev/null 2>&1" | crontab -u www-data - chown -R www-data:www-data /opt/pelican-panel chmod -R 755 /opt/pelican-panel/storage /opt/pelican-panel/bootstrap/cache/ msg_ok "Installed Pelican Panel" msg_info "Creating Service" cat </etc/apache2/sites-available/pelican.conf ServerName pelican DocumentRoot /opt/pelican-panel/public AllowEncodedSlashes On php_value upload_max_filesize 100M php_value post_max_size 100M Options Indexes FollowSymLinks AllowOverride All Require all granted ErrorLog /var/log/apache2/pelican_error.log CustomLog /var/log/apache2/pelican_access.log combined EOF $STD a2ensite pelican $STD a2enmod rewrite $STD a2dissite 000-default.conf $STD systemctl reload apache2 msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/pelican-wings-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: bvdberg01 # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/pelican-dev/wings source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Docker" DOCKER_CONFIG_PATH='/etc/docker/daemon.json' mkdir -p "$(dirname $DOCKER_CONFIG_PATH)" echo -e '{\n "log-driver": "journald"\n}' >"$DOCKER_CONFIG_PATH" $STD sh <(curl -fsSL https://get.docker.com) systemctl enable -q --now docker msg_ok "Installed Docker" fetch_and_deploy_gh_release "wings" "pelican-dev/wings" "singlefile" "latest" "/usr/local/bin" "wings_linux_arm64" mkdir -p /etc/pelican /var/run/wings msg_info "Creating Service" cat </etc/systemd/system/wings.service [Unit] Description=Wings Daemon After=docker.service Requires=docker.service PartOf=docker.service [Service] User=root WorkingDirectory=/etc/pelican LimitNOFILE=4096 PIDFile=/var/run/wings/daemon.pid ExecStart=/usr/local/bin/wings Restart=on-failure StartLimitInterval=180 StartLimitBurst=30 RestartSec=5s [Install] WantedBy=multi-user.target EOF systemctl enable -q --now wings msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/photoprism-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.photoprism.app/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os setup_hwaccel msg_info "Installing Dependencies (Patience)" $STD apt install -y \ exiftool \ ffmpeg \ libheif1 \ libpng-dev \ libjpeg-dev \ libtiff-dev \ imagemagick \ darktable \ rawtherapee \ libvips42 \ lsb-release echo 'export PATH=/usr/local:$PATH' >>~/.bashrc echo '# Load PhotoPrism environment variables for CLI tools' >>~/.bashrc echo 'export $(grep -v "^#" /opt/photoprism/config/.env | xargs)' >>~/.bashrc export PATH=/usr/local:$PATH msg_ok "Installed Dependencies" fetch_and_deploy_gh_release "photoprism" "photoprism/photoprism" "prebuild" "latest" "/opt/photoprism" "*linux-arm64.tar.gz" msg_info "Installing PhotoPrism (Patience)" mkdir -p /opt/photoprism/{cache,config,photos,storage,temp} mkdir -p /opt/photoprism/photos/{originals,import} mkdir -p /opt/photoprism_backups LIBHEIF_URL=$(curl -fsSL "https://dl.photoprism.app/dist/libheif/" | grep -oP "libheif-bookworm-arm64-v[0-9\.]+\.tar\.gz" | sort -V | tail -n 1) curl -fsSL "https://dl.photoprism.app/dist/libheif/$LIBHEIF_URL" -o /tmp/libheif.tar.gz tar -xzf /tmp/libheif.tar.gz -C /usr/local ldconfig echo "${LIBHEIF_URL}" >~/.photoprism_libheif chmod -R 755 /opt/photoprism/photos/originals cat </opt/photoprism/config/.env # Authentication PHOTOPRISM_ADMIN_USER='admin' PHOTOPRISM_ADMIN_PASSWORD='changeme' PHOTOPRISM_AUTH_MODE='password' PHOTOPRISM_PUBLIC='false' # Network / HTTP PHOTOPRISM_HTTP_HOST='0.0.0.0' PHOTOPRISM_HTTP_PORT='2342' PHOTOPRISM_SITE_URL='http://localhost:2342/' PHOTOPRISM_DISABLE_TLS='true' PHOTOPRISM_DEFAULT_TLS='false' PHOTOPRISM_HTTP_COMPRESSION='gzip' # Features & AI PHOTOPRISM_DISABLE_TENSORFLOW='false' PHOTOPRISM_DISABLE_FACES='false' PHOTOPRISM_DISABLE_CLASSIFICATION='false' PHOTOPRISM_DISABLE_VECTORS='false' PHOTOPRISM_DETECT_NSFW='false' PHOTOPRISM_UPLOAD_NSFW='true' # Paths & Storage PHOTOPRISM_STORAGE_PATH='/opt/photoprism/storage' PHOTOPRISM_ORIGINALS_PATH='/opt/photoprism/photos/originals' PHOTOPRISM_IMPORT_PATH='/opt/photoprism/photos/import' PHOTOPRISM_BACKUP_PATH='/opt/photoprism_backups' # Database PHOTOPRISM_DATABASE_DRIVER='sqlite' # Behavior & Options PHOTOPRISM_AUTO_INDEX='300' PHOTOPRISM_AUTO_IMPORT='-1' PHOTOPRISM_DISABLE_WEBDAV='false' PHOTOPRISM_READONLY='false' PHOTOPRISM_DISABLE_SETTINGS='false' PHOTOPRISM_DISABLE_CHOWN='false' PHOTOPRISM_EXPERIMENTAL='false' PHOTOPRISM_INIT='https tensorflow' # Image Processing PHOTOPRISM_ORIGINALS_LIMIT='5000' PHOTOPRISM_JPEG_QUALITY='85' PHOTOPRISM_RAW_PRESETS='false' PHOTOPRISM_DISABLE_RAW='false' # Debug & Logging PHOTOPRISM_DEBUG='false' PHOTOPRISM_LOG_LEVEL='info' # Site Info PHOTOPRISM_SITE_CAPTION='https://community-scripts.org' PHOTOPRISM_SITE_DESCRIPTION='' PHOTOPRISM_SITE_AUTHOR='' EOF ln -sf /opt/photoprism/bin/photoprism /usr/local/bin/photoprism mkdir -p /etc/photoprism/ cat </etc/photoprism/defaults.yml ConfigPath: "~/.config/photoprism" StoragePath: "/opt/photoprism/storage" OriginalsPath: "/opt/photoprism/photos/originals" ImportPath: "/media" AdminUser: "admin" AdminPassword: "changeme" AuthMode: "password" DatabaseDriver: "sqlite" HttpHost: "0.0.0.0" HttpPort: 2342 HttpCompression: "gzip" DisableTLS: false DefaultTLS: true Experimental: false DisableWebDAV: false DisableSettings: false DisableTensorFlow: false DisableFaces: false DisableClassification: false DisableVectors: false DisableRaw: false RawPresets: false JpegQuality: 85 DetectNSFW: false UploadNSFW: true EOF msg_ok "Installed PhotoPrism" msg_info "Creating Service" cat </etc/systemd/system/photoprism.service [Unit] Description=PhotoPrism service After=network.target [Service] Type=forking User=root WorkingDirectory=/opt/photoprism EnvironmentFile=/opt/photoprism/config/.env ExecStart=/opt/photoprism/bin/photoprism up -d ExecStop=/opt/photoprism/bin/photoprism down [Install] WantedBy=multi-user.target EOF systemctl enable -q --now photoprism msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/pialert-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/leiweibau/Pi.Alert/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt -y install \ apt-utils \ avahi-utils \ lighttpd \ sqlite3 \ mmdb-bin \ arp-scan \ dnsutils \ net-tools \ nbtscan \ libwww-perl \ nmap \ aria2 \ wakeonlan \ fping \ zip \ libtext-csv-perl \ cron msg_ok "Installed Dependencies" msg_info "Installing PHP Dependencies" $STD apt -y install \ php \ php-cgi \ php-fpm \ php-curl \ php-xml \ php-sqlite3 $STD lighttpd-enable-mod fastcgi-php service lighttpd force-reload msg_ok "Installed PHP Dependencies" msg_info "Installing Python Dependencies" $STD apt -y install \ python3-pip \ python3-requests \ python3-tz \ python3-tzlocal \ python3-aiohttp \ python3-cryptography rm -rf /usr/lib/python3.*/EXTERNALLY-MANAGED $STD pip3 install mac-vendor-lookup $STD pip3 install fritzconnection $STD pip3 install cryptography $STD pip3 install pyunifi $STD pip3 install openwrt-luci-rpc $STD pip3 install asusrouter $STD pip3 install paho-mqtt msg_ok "Installed Python Dependencies" msg_info "Installing Pi.Alert" curl -fsSL https://github.com/leiweibau/Pi.Alert/raw/main/tar/pialert_latest.tar | tar xvf - -C /opt >/dev/null 2>&1 rm -rf /var/lib/ieee-data /var/www/html/index.html sed -i -e 's#^sudo cp -n /usr/share/ieee-data/.* /var/lib/ieee-data/#\# &#' -e '/^sudo mkdir -p 2_backup$/s/^/# /' -e '/^sudo cp \*.txt 2_backup$/s/^/# /' -e '/^sudo cp \*.csv 2_backup$/s/^/# /' /opt/pialert/back/update_vendors.sh mv /var/www/html/index.lighttpd.html /var/www/html/index.lighttpd.html.old ln -s /usr/share/ieee-data/ /var/lib/ ln -s /opt/pialert/install/index.html /var/www/html/index.html ln -s /opt/pialert/front /var/www/html/pialert chmod go+x /opt/pialert /opt/pialert/back/shoutrrr/arm64/shoutrrr chgrp -R www-data /opt/pialert/db /opt/pialert/front/reports /opt/pialert/config /opt/pialert/config/pialert.conf chmod -R 775 /opt/pialert/db /opt/pialert/db/temp /opt/pialert/config /opt/pialert/front/reports touch /opt/pialert/log/pialert.vendors.log /opt/pialert/log/pialert.IP.log /opt/pialert/log/pialert.1.log /opt/pialert/log/pialert.cleanup.log /opt/pialert/log/pialert.webservices.log src_dir="/opt/pialert/log" dest_dir="/opt/pialert/front/php/server" for file in pialert.vendors.log pialert.IP.log pialert.1.log pialert.cleanup.log pialert.webservices.log; do ln -s "$src_dir/$file" "$dest_dir/$file" done sed -i 's#PIALERT_PATH\s*=\s*'\''/home/pi/pialert'\''#PIALERT_PATH = '\''/opt/pialert'\''#' /opt/pialert/config/pialert.conf sed -i 's/$HOME/\/opt/g' /opt/pialert/install/pialert.cron crontab /opt/pialert/install/pialert.cron echo "python3 /opt/pialert/back/pialert.py 1" >/usr/bin/scan chmod +x /usr/bin/scan echo "/opt/pialert/back/pialert-cli set_permissions --lxc" >/usr/bin/permissions chmod +x /usr/bin/permissions echo "/opt/pialert/back/pialert-cli set_sudoers --lxc" >/usr/bin/sudoers chmod +x /usr/bin/sudoers msg_ok "Installed Pi.Alert" msg_info "Start Pi.Alert Scan (Patience)" $STD python3 /opt/pialert/back/pialert.py update_vendors $STD python3 /opt/pialert/back/pialert.py internet_IP $STD python3 /opt/pialert/back/pialert.py 1 msg_ok "Finished Pi.Alert Scan" motd_ssh customize cleanup_lxc ================================================ FILE: install/pihole-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://pi-hole.net/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_warn "WARNING: This script will run an external installer from a third-party source (https://pi-hole.net/)." msg_warn "The following code is NOT maintained or audited by our repository." msg_warn "If you have any doubts or concerns, please review the installer code before proceeding:" msg_custom "${TAB3}${GATEWAY}${BGN}${CL}" "\e[1;34m" "→ https://install.pi-hole.net" echo read -r -p "${TAB3}Do you want to continue? [y/N]: " CONFIRM if [[ ! "$CONFIRM" =~ ^([yY][eE][sS]|[yY])$ ]]; then msg_error "Aborted by user. No changes have been made." exit 10 fi msg_info "Installing Dependencies" $STD apt install -y ufw msg_ok "Installed Dependencies" msg_info "Installing Pi-hole" mkdir -p /etc/pihole touch /etc/pihole/pihole.toml $STD bash <(curl -fsSL https://install.pi-hole.net) --unattended sed -i -E ' /^\s*upstreams =/ s|=.*|= ["8.8.8.8", "8.8.4.4"]| /^\s*interface =/ s|=.*|= "eth0"| /^\s*queryLogging =/ s|=.*|= true| /^\s*size =/ s|=.*|= 10000| /^\s*active =/ s|=.*|= true| /^\s*listeningMode =/ s|=.*|= "LOCAL"| /^\s*port =/ s|=.*|= "80o,443os,[::]:80o,[::]:443os"| /^\s*pwhash =/ s|=.*|= ""| # DHCP Disable /^\s*\[dhcp\]/,/^\s*\[/{s/^\s*active = true/ active = false/} # NTP Disable /^\s*\[ntp.ipv4\]/,/^\s*\[/{s/^\s*active = true/ active = false/} /^\s*\[ntp.ipv6\]/,/^\s*\[/{s/^\s*active = true/ active = false/} /^\s*\[ntp.sync\]/,/^\s*\[/{s/^\s*active = true/ active = false/} /^\s*\[ntp.sync\]/,/^\s*\[/{s/^\s*interval = [0-9]+/ interval = 0/} /^\s*\[ntp.sync.rtc\]/,/^\s*\[/{s/^\s*set = true/ set = false/} # set domainNeeded und expandHosts /^\s*domainNeeded =/ s|=.*|= true| /^\s*expandHosts =/ s|=.*|= true| ' /etc/pihole/pihole.toml cat </etc/dnsmasq.d/01-pihole.conf server=8.8.8.8 server=8.8.4.4 EOF $STD pihole-FTL --config ntp.sync.interval 0 systemctl restart pihole-FTL.service msg_ok "Installed Pi-hole" read -r -p "${TAB3}Would you like to add Unbound? " prompt if [[ ${prompt,,} =~ ^(y|yes)$ ]]; then read -r -p "${TAB3}Unbound is configured as a recursive DNS server by default, would you like it to be configured as a forwarding DNS server (using DNS-over-TLS (DoT)) instead? " prompt msg_info "Installing Unbound" mkdir -p /etc/unbound/unbound.conf.d cat </etc/unbound/unbound.conf.d/pi-hole.conf server: verbosity: 0 interface: 127.0.0.1 port: 5335 do-ip6: no do-ip4: yes do-udp: yes do-tcp: yes num-threads: 1 hide-identity: yes hide-version: yes harden-glue: yes harden-dnssec-stripped: yes harden-referral-path: yes use-caps-for-id: no harden-algo-downgrade: no qname-minimisation: yes aggressive-nsec: yes rrset-roundrobin: yes cache-min-ttl: 300 cache-max-ttl: 14400 msg-cache-slabs: 8 rrset-cache-slabs: 8 infra-cache-slabs: 8 key-cache-slabs: 8 serve-expired: yes serve-expired-ttl: 3600 edns-buffer-size: 1232 prefetch: yes prefetch-key: yes target-fetch-policy: "3 2 1 1 1" unwanted-reply-threshold: 10000000 rrset-cache-size: 256m msg-cache-size: 128m so-rcvbuf: 1m private-address: 192.168.0.0/16 private-address: 169.254.0.0/16 private-address: 172.16.0.0/12 private-address: 10.0.0.0/8 private-address: fd00::/8 private-address: fe80::/10 EOF mkdir -p /etc/dnsmasq.d/ cat </etc/dnsmasq.d/99-edns.conf edns-packet-max=1232 EOF if [[ ${prompt,,} =~ ^(y|yes)$ ]]; then cat <>/etc/unbound/unbound.conf.d/pi-hole.conf tls-cert-bundle: "/etc/ssl/certs/ca-certificates.crt" forward-zone: name: "." forward-tls-upstream: yes forward-first: no forward-addr: 8.8.8.8@853#dns.google forward-addr: 8.8.4.4@853#dns.google forward-addr: 2001:4860:4860::8888@853#dns.google forward-addr: 2001:4860:4860::8844@853#dns.google #forward-addr: 1.1.1.1@853#cloudflare-dns.com #forward-addr: 1.0.0.1@853#cloudflare-dns.com #forward-addr: 2606:4700:4700::1111@853#cloudflare-dns.com #forward-addr: 2606:4700:4700::1001@853#cloudflare-dns.com #forward-addr: 9.9.9.9@853#dns.quad9.net #forward-addr: 149.112.112.112@853#dns.quad9.net #forward-addr: 2620:fe::fe@853#dns.quad9.net #forward-addr: 2620:fe::9@853#dns.quad9.net EOF fi $STD apt install -y unbound cat </etc/dnsmasq.d/01-pihole.conf server=127.0.0.1#5335 server=8.8.8.8 server=8.8.4.4 EOF sed -i -E '/^\s*upstreams\s*=\s*\[/,/^\s*\]/c\ upstreams = [\n "127.0.0.1#5335",\n "8.8.4.4"\n ]' /etc/pihole/pihole.toml systemctl restart unbound systemctl restart pihole-FTL.service msg_ok "Installed Unbound" fi motd_ssh customize cleanup_lxc ================================================ FILE: install/planka-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: Slaviša Arežina (tremor021) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/plankanban/planka source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing dependencies" $STD apt install -y \ unzip \ build-essential \ python3-venv msg_ok "Installed dependencies" NODE_VERSION="22" setup_nodejs PG_VERSION="16" setup_postgresql msg_info "Setting up PostgreSQL Database" DB_NAME=planka DB_USER=planka DB_PASS=$(openssl rand -base64 16 | tr -d '/+=') $STD sudo -u postgres psql -c "CREATE ROLE $DB_USER WITH LOGIN PASSWORD '$DB_PASS';" $STD sudo -u postgres psql -c "CREATE DATABASE $DB_NAME WITH OWNER $DB_USER ENCODING 'UTF8' TEMPLATE template0;" $STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET client_encoding TO 'utf8';" $STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET default_transaction_isolation TO 'read committed';" $STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET timezone TO 'UTC'" { echo "PLANKA DB Credentials" echo "PLANKA Database User: $DB_USER" echo "PLANKA Database Password: $DB_PASS" echo "PLANKA Database Name: $DB_NAME" } >>~/planka.creds msg_ok "Set up PostgreSQL Database" fetch_and_deploy_gh_release "planka" "plankanban/planka" "prebuild" "latest" "/opt/planka" "planka-prebuild.zip" msg_info "Configuring PLANKA" SECRET_KEY=$(openssl rand -hex 64) cd /opt/planka $STD npm install cp .env.sample .env sed -i "s#http://localhost:1337#http://$LOCAL_IP:1337#g" /opt/planka/.env sed -i "s#postgres@localhost#planka:$DB_PASS@localhost#g" /opt/planka/.env sed -i "s#notsecretkey#$SECRET_KEY#g" /opt/planka/.env mkdir -p /opt/planka/data/protected/{favicons,user-avatars,background-images} /opt/planka/data/private/attachments $STD npm run db:init msg_ok "Configured PLANKA" msg_info "Creating Admin User" ADMIN_EMAIL="admin@planka.local" ADMIN_PASSWORD="$(openssl rand -base64 12)" ADMIN_NAME="Administrator" ADMIN_USERNAME="admin" echo "" >>.env echo "# Temporary admin user creation settings" >>.env echo "DEFAULT_ADMIN_EMAIL=$ADMIN_EMAIL" >>.env echo "DEFAULT_ADMIN_PASSWORD=$ADMIN_PASSWORD" >>.env echo "DEFAULT_ADMIN_NAME=$ADMIN_NAME" >>.env echo "DEFAULT_ADMIN_USERNAME=$ADMIN_USERNAME" >>.env $STD npm run db:seed sed -i '/# Temporary admin user creation settings/,$d' .env { echo "" echo "PLANKA Admin Credentials" echo "Admin Email: $ADMIN_EMAIL" echo "Admin Password: $ADMIN_PASSWORD" echo "Admin Name: $ADMIN_NAME" echo "Admin Username: $ADMIN_USERNAME" } >>~/planka.creds msg_ok "Created Admin User" msg_info "Creating Service" cat </etc/systemd/system/planka.service [Unit] Description=planka Service After=network.target [Service] WorkingDirectory=/opt/planka ExecStart=/usr/bin/npm start --prod Restart=always [Install] WantedBy=multi-user.target EOF systemctl enable -q --now planka msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/plant-it-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://plant-it.org/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ redis \ nginx msg_ok "Installed Dependencies" setup_mariadb MARIADB_DB_NAME="plantit" MARIADB_DB_USER="plantit_usr" setup_mariadb_db JAVA_VERSION="21" setup_java USE_ORIGINAL_FILENAME="true" fetch_and_deploy_gh_release "plant-it" "MDeLuise/plant-it" "singlefile" "0.10.0" "/opt/plant-it/backend" "server.jar" fetch_and_deploy_gh_release "plant-it-front" "MDeLuise/plant-it" "prebuild" "0.10.0" "/opt/plant-it/frontend" "client.tar.gz" msg_info "Configured Plant-it" JWT_SECRET=$(openssl rand -base64 24 | tr -d '/+=') mkdir -p /opt/plant-it-data cat </opt/plant-it/backend/server.env MYSQL_HOST=localhost MYSQL_PORT=3306 MYSQL_USERNAME=$MARIADB_DB_USER MYSQL_PSW=$MARIADB_DB_PASS MYSQL_DATABASE=$MARIADB_DB_NAME MYSQL_ROOT_PASSWORD=$MARIADB_DB_PASS JWT_SECRET=$JWT_SECRET JWT_EXP=1 USERS_LIMIT=-1 UPLOAD_DIR=/opt/plant-it-data API_PORT=8080 FLORACODEX_KEY= LOG_LEVEL=DEBUG ALLOWED_ORIGINS=* CACHE_TYPE=redis CACHE_TTL=86400 CACHE_HOST=localhost CACHE_PORT=6379 EOF msg_ok "Configured Plant-it" msg_info "Creating Service" cat </etc/systemd/system/plant-it.service [Unit] Description=Plant-it Backend Service After=syslog.target network.target [Service] Type=simple WorkingDirectory=/opt/plant-it/backend EnvironmentFile=/opt/plant-it/backend/server.env ExecStart=/usr/bin/java -jar -Xmx2g server.jar TimeoutStopSec=20 KillMode=process Restart=on-failure [Install] WantedBy=multi-user.target EOF systemctl enable -q --now plant-it cat </etc/nginx/nginx.conf events { worker_connections 1024; } http { server { listen 3000; server_name localhost; root /opt/plant-it/frontend; index index.html; location / { try_files \$uri \$uri/ /index.html; } error_page 404 /404.html; location = /404.html { internal; } } } EOF systemctl restart nginx msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/plex-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.plex.tv/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Setting Up Plex Media Server Repository" setup_deb822_repo \ "plexmediaserver" \ "https://downloads.plex.tv/plex-keys/PlexSign.v2.key" \ "https://repo.plex.tv/deb/" \ "public" \ "main" msg_ok "Set Up Plex Media Server Repository" msg_info "Installing Plex Media Server" $STD apt install -y plexmediaserver msg_ok "Installed Plex Media Server" setup_hwaccel "plex" motd_ssh customize cleanup_lxc ================================================ FILE: install/pocketbase-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://pocketbase.io/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os fetch_and_deploy_gh_release "pocketbase" "pocketbase/pocketbase" "prebuild" "latest" "/opt/pocketbase" "pocketbase*linux_arm64.zip" msg_info "Configuring Pocketbase" mkdir -p /opt/pocketbase/{pb_public,pb_migrations,pb_hooks} msg_ok "Configured Pocketbase" msg_info "Creating service" cat </etc/systemd/system/pocketbase.service [Unit] Description = pocketbase [Service] Type = simple LimitNOFILE = 4096 Restart = always RestartSec = 5s StandardOutput = append:/opt/pocketbase/errors.log StandardError = append:/opt/pocketbase/errors.log ExecStart = /opt/pocketbase/pocketbase serve --http=0.0.0.0:8080 [Install] WantedBy = multi-user.target EOF systemctl enable -q --now pocketbase msg_ok "Service created" motd_ssh customize cleanup_lxc ================================================ FILE: install/pocketid-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: Snarkenfaugister # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/pocket-id/pocket-id source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os read -r -p "${TAB3}What public URL do you want to use (e.g. pocketid.mydomain.com)? " public_url fetch_and_deploy_gh_release "pocket-id" "pocket-id/pocket-id" "singlefile" "latest" "/opt/pocket-id/" "pocket-id-linux-arm64" msg_info "Configuring Pocket ID" ENCRYPTION_KEY=$(openssl rand -base64 32) cat </opt/pocket-id/.env APP_ENV=production APP_URL=https://${public_url} TRUST_PROXY=false # MAXMIND_LICENSE_KEY= PORT=1411 HOST=0.0.0.0 ENCRYPTION_KEY=${ENCRYPTION_KEY} EOF msg_ok "Configured Pocket ID" msg_info "Creating Service" cat </etc/systemd/system/pocketid.service [Unit] Description=Pocket ID Service After=network.target [Service] Type=simple User=root Group=root WorkingDirectory=/opt/pocket-id EnvironmentFile=/opt/pocket-id/.env ExecStart=/opt/pocket-id/pocket-id Restart=always RestartSec=10 [Install] WantedBy=multi-user.target EOF msg_ok "Created Service" msg_info "Starting Service" systemctl enable -q --now pocketid msg_ok "Started Services" motd_ssh customize cleanup_lxc ================================================ FILE: install/podman-homeassistant-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.home-assistant.io/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os PORTAINER_LATEST_VERSION=$(get_latest_github_release "portainer/portainer") PORTAINER_AGENT_LATEST_VERSION=$(get_latest_github_release "portainer/agent") if $STD mount | grep 'on / type zfs' >null && echo "ZFS"; then msg_info "Enabling ZFS support." mkdir -p /etc/containers cat <<'EOF' >/usr/local/bin/overlayzfsmount #!/bin/sh exec /bin/mount -t overlay overlay "$@" EOF chmod +x /usr/local/bin/overlayzfsmount cat <<'EOF' >/etc/containers/storage.conf [storage] driver = "overlay" runroot = "/run/containers/storage" graphroot = "/var/lib/containers/storage" [storage.options] pull_options = {enable_partial_images = "false", use_hard_links = "false", ostree_repos=""} mount_program = "/usr/local/bin/overlayzfsmount" [storage.options.overlay] mountopt = "nodev" EOF fi msg_info "Installing Podman" $STD apt install -y podman systemctl enable -q --now podman.socket echo -e 'unqualified-search-registries=["docker.io"]' >>/etc/containers/registries.conf msg_ok "Installed Podman" mkdir -p /etc/containers/systemd read -r -p "${TAB3}Would you like to add Portainer? " prompt if [[ ${prompt,,} =~ ^(y|yes)$ ]]; then msg_info "Installing Portainer $PORTAINER_LATEST_VERSION" podman volume create portainer_data >/dev/null cat </etc/containers/systemd/portainer.container [Unit] Description=Portainer Container After=network-online.target [Container] Image=docker.io/portainer/portainer-ce:latest ContainerName=portainer PublishPort=8000:8000 PublishPort=9443:9443 Volume=/run/podman/podman.sock:/var/run/docker.sock Volume=portainer_data:/data [Service] Restart=always [Install] WantedBy=default.target multi-user.target EOF systemctl daemon-reload $STD systemctl start portainer msg_ok "Installed Portainer $PORTAINER_LATEST_VERSION" else read -r -p "${TAB3}Would you like to add the Portainer Agent? " prompt if [[ ${prompt,,} =~ ^(y|yes)$ ]]; then msg_info "Installing Portainer agent $PORTAINER_AGENT_LATEST_VERSION" cat </etc/containers/systemd/portainer-agent.container [Unit] Description=Portainer Agent Container After=network-online.target [Container] Image=docker.io/portainer/agent:latest ContainerName=portainer_agent PublishPort=9001:9001 Volume=/run/podman/podman.sock:/var/run/docker.sock Volume=/var/lib/containers/storage/volumes:/var/lib/docker/volumes [Service] Restart=always [Install] WantedBy=default.target multi-user.target EOF systemctl daemon-reload $STD systemctl start portainer-agent msg_ok "Installed Portainer Agent $PORTAINER_AGENT_LATEST_VERSION" fi fi msg_info "Pulling Home Assistant Image" $STD podman pull docker.io/homeassistant/home-assistant:stable msg_ok "Pulled Home Assistant Image" msg_info "Installing Home Assistant" $STD podman volume create hass_config cat </etc/containers/systemd/homeassistant.container [Unit] Description=Home Assistant Container After=network-online.target [Container] Image=docker.io/homeassistant/home-assistant:stable ContainerName=homeassistant Volume=/dev:/dev Volume=hass_config:/config Volume=/etc/localtime:/etc/localtime:ro Volume=/etc/timezone:/etc/timezone:ro Network=host [Service] Restart=always TimeoutStartSec=300 [Install] WantedBy=default.target multi-user.target EOF systemctl daemon-reload $STD systemctl start homeassistant msg_ok "Installed Home Assistant" motd_ssh customize cleanup_lxc ================================================ FILE: install/podman-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://podman.io/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os PORTAINER_LATEST_VERSION=$(get_latest_github_release "portainer/portainer") PORTAINER_AGENT_LATEST_VERSION=$(get_latest_github_release "portainer/agent") if $STD mount | grep 'on / type zfs' >null && echo "ZFS"; then msg_info "Enabling ZFS support." mkdir -p /etc/containers cat <<'EOF' >/usr/local/bin/overlayzfsmount #!/bin/sh exec /bin/mount -t overlay overlay "$@" EOF chmod +x /usr/local/bin/overlayzfsmount cat <<'EOF' >/etc/containers/storage.conf [storage] driver = "overlay" runroot = "/run/containers/storage" graphroot = "/var/lib/containers/storage" [storage.options] pull_options = {enable_partial_images = "false", use_hard_links = "false", ostree_repos=""} mount_program = "/usr/local/bin/overlayzfsmount" [storage.options.overlay] mountopt = "nodev" EOF fi msg_info "Installing Podman" $STD apt install -y podman systemctl enable -q --now podman.socket echo -e 'unqualified-search-registries=["docker.io"]' >>/etc/containers/registries.conf msg_ok "Installed Podman" mkdir -p /etc/containers/systemd read -r -p "${TAB3}Would you like to add Portainer? " prompt if [[ ${prompt,,} =~ ^(y|yes)$ ]]; then msg_info "Installing Portainer $PORTAINER_LATEST_VERSION" podman volume create portainer_data >/dev/null cat </etc/containers/systemd/portainer.container [Unit] Description=Portainer Container After=network-online.target [Container] Image=docker.io/portainer/portainer-ce:latest ContainerName=portainer PublishPort=8000:8000 PublishPort=9443:9443 Volume=/run/podman/podman.sock:/var/run/docker.sock Volume=portainer_data:/data [Service] Restart=always [Install] WantedBy=default.target multi-user.target EOF systemctl daemon-reload $STD systemctl start portainer msg_ok "Installed Portainer $PORTAINER_LATEST_VERSION" else read -r -p "${TAB3}Would you like to add the Portainer Agent? " prompt if [[ ${prompt,,} =~ ^(y|yes)$ ]]; then msg_info "Installing Portainer agent $PORTAINER_AGENT_LATEST_VERSION" cat </etc/containers/systemd/portainer-agent.container [Unit] Description=Portainer Agent Container After=network-online.target [Container] Image=docker.io/portainer/agent:latest ContainerName=portainer_agent PublishPort=9001:9001 Volume=/run/podman/podman.sock:/var/run/docker.sock Volume=/var/lib/containers/storage/volumes:/var/lib/docker/volumes [Service] Restart=always [Install] WantedBy=default.target multi-user.target EOF systemctl daemon-reload $STD systemctl start portainer-agent msg_ok "Installed Portainer Agent $PORTAINER_AGENT_LATEST_VERSION" fi fi motd_ssh customize cleanup_lxc ================================================ FILE: install/postgresql-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.postgresql.org/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os read -r -p "${TAB3}Enter PostgreSQL version (15/16/17/18): " ver [[ $ver =~ ^(15|16|17|18)$ ]] || { echo "Invalid version" exit 64 } PG_VERSION=$ver setup_postgresql cat </etc/postgresql/$ver/main/pg_hba.conf # PostgreSQL Client Authentication Configuration File local all postgres peer # TYPE DATABASE USER ADDRESS METHOD # "local" is for Unix domain socket connections only local all all md5 # IPv4 local connections: host all all 127.0.0.1/32 scram-sha-256 host all all 0.0.0.0/24 md5 # IPv6 local connections: host all all ::1/128 scram-sha-256 host all all 0.0.0.0/0 md5 # Allow replication connections from localhost, by a user with the # replication privilege. local replication all peer host replication all 127.0.0.1/32 scram-sha-256 host replication all ::1/128 scram-sha-256 EOF cat </etc/postgresql/$ver/main/postgresql.conf # ----------------------------- # PostgreSQL configuration file # ----------------------------- #------------------------------------------------------------------------------ # FILE LOCATIONS #------------------------------------------------------------------------------ data_directory = '/var/lib/postgresql/$ver/main' hba_file = '/etc/postgresql/$ver/main/pg_hba.conf' ident_file = '/etc/postgresql/$ver/main/pg_ident.conf' external_pid_file = '/var/run/postgresql/$ver-main.pid' #------------------------------------------------------------------------------ # CONNECTIONS AND AUTHENTICATION #------------------------------------------------------------------------------ # - Connection Settings - listen_addresses = '*' port = 5432 max_connections = 100 unix_socket_directories = '/var/run/postgresql' # - SSL - ssl = on ssl_cert_file = '/etc/ssl/certs/ssl-cert-snakeoil.pem' ssl_key_file = '/etc/ssl/private/ssl-cert-snakeoil.key' #------------------------------------------------------------------------------ # RESOURCE USAGE (except WAL) #------------------------------------------------------------------------------ shared_buffers = 128MB dynamic_shared_memory_type = posix #------------------------------------------------------------------------------ # WRITE-AHEAD LOG #------------------------------------------------------------------------------ max_wal_size = 1GB min_wal_size = 80MB #------------------------------------------------------------------------------ # REPORTING AND LOGGING #------------------------------------------------------------------------------ # - What to Log - log_line_prefix = '%m [%p] %q%u@%d ' log_timezone = 'Etc/UTC' #------------------------------------------------------------------------------ # PROCESS TITLE #------------------------------------------------------------------------------ cluster_name = '$ver/main' #------------------------------------------------------------------------------ # CLIENT CONNECTION DEFAULTS #------------------------------------------------------------------------------ # - Locale and Formatting - datestyle = 'iso, mdy' timezone = 'Etc/UTC' lc_messages = 'C' lc_monetary = 'C' lc_numeric = 'C' lc_time = 'C' default_text_search_config = 'pg_catalog.english' #------------------------------------------------------------------------------ # CONFIG FILE INCLUDES #------------------------------------------------------------------------------ include_dir = 'conf.d' EOF systemctl restart postgresql msg_ok "Installed PostgreSQL" read -r -p "${TAB3}Would you like to add Adminer? " prompt if [[ "${prompt,,}" =~ ^(y|yes)$ ]]; then msg_info "Installing Adminer" $STD apt install -y adminer $STD a2enconf adminer systemctl reload apache2 msg_ok "Installed Adminer" fi motd_ssh customize cleanup_lxc ================================================ FILE: install/powerdns-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: Slaviša Arežina (tremor021) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.powerdns.com/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y sqlite3 msg_ok "Installed Dependencies" PHP_VERSION="8.3" PHP_APACHE="YES" PHP_FPM="YES" PHP_MODULE="gettext,tokenizer,sqlite3,ldap" setup_php setup_deb822_repo \ "pdns" \ "https://repo.powerdns.com/FD380FBB-pub.asc" \ "http://repo.powerdns.com/debian" \ "trixie-auth-50" cat </etc/apt/preferences.d/auth-50 Package: pdns-* Pin: origin repo.powerdns.com Pin-Priority: 600 EOF escape_sql() { printf '%s' "$1" | sed "s/'/''/g" } msg_info "Setting up PowerDNS" $STD apt install -y \ pdns-server \ pdns-backend-sqlite3 sed -i 's/^launch=$/# launch=/' /etc/powerdns/pdns.conf rm -f /etc/powerdns/pdns.d/bind.conf cat </etc/powerdns/pdns.d/gsqlite3.conf launch=gsqlite3 gsqlite3-database=/opt/poweradmin/powerdns.db EOF msg_ok "Setup PowerDNS" fetch_and_deploy_gh_release "poweradmin" "poweradmin/poweradmin" "tarball" msg_info "Setting up Poweradmin" sqlite3 /opt/poweradmin/powerdns.db /dev/null) sqlite3 /opt/poweradmin/powerdns.db "INSERT INTO users (username, password, fullname, email, description, perm_templ, active, use_ldap) \ VALUES ('$(escape_sql "${PA_ADMIN_USERNAME}")', '$(escape_sql "${PASSWORD_HASH}")', '$(escape_sql "${PA_ADMIN_FULLNAME}")', \ '$(escape_sql "${PA_ADMIN_EMAIL}")', 'System Administrator', 1, 1, 0);" cat <~/poweradmin.creds Admin Username: ${PA_ADMIN_USERNAME} Admin Password: ${PA_ADMIN_PASSWORD} EOF cat </opt/poweradmin/config/settings.php [ 'type' => 'sqlite', 'file' => '/opt/poweradmin/powerdns.db', ], /** * Security Settings */ 'security' => [ 'session_key' => '${PA_SESSION_KEY}', ], /** * Interface Settings */ 'interface' => [ 'language' => 'en_EN', ], /** * DNS Settings */ 'dns' => [ 'hostmaster' => 'localhost.lan', 'ns1' => '8.8.8.8', 'ns2' => '9.9.9.9', ] ]; EOF rm -rf /opt/poweradmin/install msg_ok "Setup Poweradmin" msg_info "Creating Service" rm /etc/apache2/sites-enabled/000-default.conf cat </etc/apache2/sites-enabled/poweradmin.conf ServerName localhost DocumentRoot /opt/poweradmin Options -Indexes +FollowSymLinks AllowOverride All Require all granted # For DDNS update functionality RewriteEngine On RewriteRule ^/update(.*)\$ /dynamic_update.php [L] RewriteRule ^/nic/update(.*)\$ /dynamic_update.php [L] EOF $STD a2enmod rewrite headers chown -R www-data:pdns /opt/poweradmin chmod 775 /opt/poweradmin chown pdns:pdns /opt/poweradmin/powerdns.db chmod 664 /opt/poweradmin/powerdns.db usermod -aG pdns www-data $STD systemctl restart pdns apache2 msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/privatebin-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: Nícolas Pastorello (opastorello) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://privatebin.info/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ nginx \ openssl msg_ok "Installed Dependencies" PHP_VERSION="8.2" PHP_FPM="YES" setup_php create_self_signed_cert fetch_and_deploy_gh_release "privatebin" "PrivateBin/PrivateBin" "tarball" msg_info "Configuring Environment" mkdir -p /opt/privatebin/data cp /opt/privatebin/cfg/conf.sample.php /opt/privatebin/cfg/conf.php sed -i "s|// 'traffic'|'traffic'|g" /opt/privatebin/cfg/conf.php chown -R www-data:www-data /opt/privatebin chmod -R 0755 /opt/privatebin/data msg_ok "Configured Environment" msg_info "Configuring PHP" sed -i 's/;cgi.fix_pathinfo=1/cgi.fix_pathinfo=0/' /etc/php/8.2/fpm/php.ini systemctl restart php8.2-fpm msg_ok "Configured PHP" msg_info "Configuring Universal Nginx" cat </etc/nginx/sites-available/privatebin.conf server { listen 80 default_server; listen [::]:80 default_server; return 301 https://\$host\$request_uri; } server { listen 443 ssl default_server; listen [::]:443 ssl default_server; ssl_certificate /etc/ssl/privatebin/privatebin.crt; ssl_certificate_key /etc/ssl/privatebin/privatebin.key; root /opt/privatebin; index index.php; location / { try_files \$uri \$uri/ /index.php\$is_args\$args; } location ~ \.php\$ { include snippets/fastcgi-php.conf; fastcgi_pass unix:/var/run/php/php8.2-fpm.sock; fastcgi_param SCRIPT_FILENAME \$document_root\$fastcgi_script_name; include fastcgi_params; } location ~ /\.ht { deny all; } add_header Strict-Transport-Security "max-age=63072000; includeSubdomains; preload"; add_header X-Content-Type-Options nosniff; add_header X-Frame-Options "SAMEORIGIN"; add_header X-XSS-Protection "1; mode=block"; } EOF ln -s /etc/nginx/sites-available/privatebin.conf /etc/nginx/sites-enabled/ rm -f /etc/nginx/sites-enabled/default systemctl reload nginx msg_ok "Nginx Configured" motd_ssh customize cleanup_lxc ================================================ FILE: install/profilarr-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: michelroegl-brunner # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/Dictionarry-Hub/profilarr source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ build-essential \ python3-dev \ libffi-dev \ libssl-dev \ git msg_ok "Installed Dependencies" PYTHON_VERSION="3.12" setup_uv NODE_VERSION="22" setup_nodejs msg_info "Creating directories" mkdir -p /opt/profilarr \ /config msg_ok "Created directories" fetch_and_deploy_gh_release "profilarr" "Dictionarry-Hub/profilarr" "tarball" msg_info "Installing Python Dependencies" cd /opt/profilarr/backend $STD uv venv /opt/profilarr/backend/.venv sed 's/==/>=/g' requirements.txt >requirements-relaxed.txt $STD uv pip install --python /opt/profilarr/backend/.venv/bin/python -r requirements-relaxed.txt rm -f requirements-relaxed.txt msg_ok "Installed Python Dependencies" msg_info "Building Frontend" cd /opt/profilarr/frontend $STD npm install $STD npm run build cp -r dist /opt/profilarr/backend/app/static msg_ok "Built Frontend" msg_info "Creating Service" cat </etc/systemd/system/profilarr.service [Unit] Description=Profilarr - Configuration Management Platform for Radarr/Sonarr After=network.target [Service] Type=simple User=root WorkingDirectory=/opt/profilarr/backend Environment="PATH=/opt/profilarr/backend/.venv/bin:/usr/local/bin:/usr/bin:/bin" Environment="PYTHONPATH=/opt/profilarr/backend" ExecStart=/opt/profilarr/backend/.venv/bin/gunicorn --bind 0.0.0.0:6868 --timeout 600 app.main:create_app() Restart=on-failure RestartSec=5 [Install] WantedBy=multi-user.target EOF systemctl enable -q --now profilarr msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/projectsend-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: bvdberg01 # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.projectsend.org/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os PHP_VERSION="8.4" PHP_APACHE="YES" setup_php setup_mariadb MARIADB_DB_NAME="projectsend" MARIADB_DB_USER="projectsend" setup_mariadb_db fetch_and_deploy_gh_release "projectsend" "projectsend/projectsend" "prebuild" "latest" "/opt/projectsend" "projectsend-r*.zip" msg_info "Installing ProjectSend" mv /opt/projectsend/includes/sys.config.sample.php /opt/projectsend/includes/sys.config.php chown -R www-data:www-data /opt/projectsend chmod -R 775 /opt/projectsend chmod 644 /opt/projectsend/includes/sys.config.php sed -i -e "s/\(define('DB_NAME', \).*/\1'$MARIADB_DB_NAME');/" \ -e "s/\(define('DB_USER', \).*/\1'$MARIADB_DB_USER');/" \ -e "s/\(define('DB_PASSWORD', \).*/\1'$MARIADB_DB_PASS');/" \ /opt/projectsend/includes/sys.config.php sed -i -e "s/^\(memory_limit = \).*/\1 256M/" \ -e "s/^\(post_max_size = \).*/\1 256M/" \ -e "s/^\(upload_max_filesize = \).*/\1 256M/" \ -e "s/^\(max_execution_time = \).*/\1 300/" \ /etc/php/8.4/apache2/php.ini msg_ok "Installed projectsend" msg_info "Creating Service" cat </etc/apache2/sites-available/projectsend.conf ServerName projectsend DocumentRoot /opt/projectsend Options FollowSymLinks AllowOverride All Require all granted ErrorLog /var/log/apache2/projectsend_error.log CustomLog /var/log/apache2/projectsend_access.log combined EOF $STD a2ensite projectsend $STD a2enmod rewrite $STD a2dissite 000-default.conf $STD systemctl reload apache2 msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/prometheus-alertmanager-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: Andy Grunwald (andygrunwald) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://prometheus.io/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os fetch_and_deploy_gh_release "alertmanager" "prometheus/alertmanager" "prebuild" "latest" "/usr/local/bin/" "alertmanager*linux-arm64.tar.gz" msg_info "Configuring Prometheus Alertmanager" mkdir -p /etc/alertmanager /var/lib/alertmanager mv /usr/local/bin/alertmanager.yml /etc/alertmanager/alertmanager.yml msg_ok "Configured Prometheus Alertmanager" msg_info "Creating Service" cat </etc/systemd/system/prometheus-alertmanager.service [Unit] Description=Prometheus Alertmanager Wants=network-online.target After=network-online.target [Service] User=root Restart=always Type=simple ExecStart=/usr/local/bin/alertmanager \ --config.file=/etc/alertmanager/alertmanager.yml \ --storage.path=/var/lib/alertmanager/ \ --web.listen-address=0.0.0.0:9093 ExecReload=/bin/kill -HUP \$MAINPID [Install] WantedBy=multi-user.target EOF systemctl enable -q --now prometheus-alertmanager msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/prometheus-blackbox-exporter-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: Marfnl # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/prometheus/blackbox_exporter source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os fetch_and_deploy_gh_release "blackbox-exporter" "prometheus/blackbox_exporter" "prebuild" "latest" "/opt/blackbox-exporter" "blackbox_exporter-*.linux-arm64.tar.gz" msg_info "Creating Service" cat </etc/systemd/system/blackbox-exporter.service [Unit] Description=Blackbox Exporter Service After=network.target [Service] Type=simple WorkingDirectory=/opt/blackbox-exporter ExecStart=/opt/blackbox-exporter/blackbox_exporter Restart=on-failure [Install] WantedBy=multi-user.target EOF systemctl enable -q --now blackbox-exporter msg_ok "Service Created" motd_ssh customize cleanup_lxc ================================================ FILE: install/prometheus-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://prometheus.io/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os fetch_and_deploy_gh_release "prometheus" "prometheus/prometheus" "prebuild" "latest" "/usr/local/bin" "*linux-arm64.tar.gz" msg_info "Installing Prometheus" mkdir -p /etc/prometheus mkdir -p /var/lib/prometheus mv /usr/local/bin/prometheus.yml /etc/prometheus/prometheus.yml msg_ok "Installed Prometheus" msg_info "Creating Service" cat <<'EOF' >/etc/systemd/system/prometheus.service [Unit] Description=Prometheus Wants=network-online.target After=network-online.target [Service] User=root Restart=always Type=simple ExecStart=/usr/local/bin/prometheus \ --config.file=/etc/prometheus/prometheus.yml \ --storage.tsdb.path=/var/lib/prometheus/ \ --web.listen-address=0.0.0.0:9090 [Install] WantedBy=multi-user.target EOF systemctl enable -q --now prometheus msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/prometheus-pve-exporter-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: Andy Grunwald (andygrunwald) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/prometheus-pve/prometheus-pve-exporter source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os PYTHON_VERSION="3.12" setup_uv msg_info "Installing Prometheus Proxmox VE Exporter" mkdir -p /opt/prometheus-pve-exporter cd /opt/prometheus-pve-exporter $STD uv venv --clear /opt/prometheus-pve-exporter/.venv $STD /opt/prometheus-pve-exporter/.venv/bin/python -m ensurepip --upgrade $STD /opt/prometheus-pve-exporter/.venv/bin/python -m pip install --upgrade pip $STD /opt/prometheus-pve-exporter/.venv/bin/python -m pip install prometheus-pve-exporter cat </opt/prometheus-pve-exporter/pve.yml default: user: prometheus@pve password: sEcr3T! verify_ssl: false EOF msg_ok "Installed Prometheus Proxmox VE Exporter" msg_info "Creating Service" cat </etc/systemd/system/prometheus-pve-exporter.service [Unit] Description=Prometheus Proxmox VE Exporter Documentation=https://github.com/znerol/prometheus-pve-exporter After=syslog.target network.target [Service] User=root Restart=always Type=simple ExecStart=/opt/prometheus-pve-exporter/.venv/bin/pve_exporter \ --config.file=/opt/prometheus-pve-exporter/pve.yml \ --web.listen-address=0.0.0.0:9221 ExecReload=/bin/kill -HUP \$MAINPID [Install] WantedBy=multi-user.target EOF systemctl enable -q --now prometheus-pve-exporter msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/protonmail-bridge-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: Stephen Chin (steveonjava) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/ProtonMail/proton-bridge source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y pass msg_ok "Installed Dependencies" msg_info "Creating Service User" useradd -r -m -d /home/protonbridge -s /usr/sbin/nologin protonbridge install -d -m 0750 -o protonbridge -g protonbridge /home/protonbridge msg_ok "Created Service User" fetch_and_deploy_gh_release "protonmail-bridge" "ProtonMail/proton-bridge" "binary" msg_info "Creating Services" cat </etc/systemd/system/protonmail-bridge.service [Unit] Description=Proton Mail Bridge (noninteractive) After=network-online.target Wants=network-online.target ConditionPathExists=/home/protonbridge/.protonmailbridge-initialized [Service] Type=simple User=protonbridge Group=protonbridge WorkingDirectory=/home/protonbridge Environment=HOME=/home/protonbridge ExecStart=/usr/bin/protonmail-bridge --noninteractive Restart=always RestartSec=3 NoNewPrivileges=yes PrivateTmp=yes ProtectSystem=full ProtectKernelTunables=yes ProtectKernelModules=yes ProtectControlGroups=yes [Install] WantedBy=multi-user.target EOF cat <<'EOF' >/etc/systemd/system/protonmail-bridge-imap.socket [Unit] Description=Proton Mail Bridge IMAP Socket (143) ConditionPathExists=/home/protonbridge/.protonmailbridge-initialized [Socket] ListenStream=143 Accept=no Service=protonmail-bridge-imap-proxy.service [Install] WantedBy=sockets.target EOF cat <<'EOF' >/etc/systemd/system/protonmail-bridge-imap-proxy.service [Unit] Description=Proton Mail Bridge IMAP Proxy (143 -> 127.0.0.1:1143) After=protonmail-bridge.service Requires=protonmail-bridge.service ConditionPathExists=/home/protonbridge/.protonmailbridge-initialized [Service] Type=simple Sockets=protonmail-bridge-imap.socket ExecStart=/usr/lib/systemd/systemd-socket-proxyd 127.0.0.1:1143 NoNewPrivileges=yes PrivateTmp=yes EOF cat <<'EOF' >/etc/systemd/system/protonmail-bridge-smtp.socket [Unit] Description=Proton Mail Bridge SMTP Socket (587) ConditionPathExists=/home/protonbridge/.protonmailbridge-initialized [Socket] ListenStream=587 Accept=no Service=protonmail-bridge-smtp-proxy.service [Install] WantedBy=sockets.target EOF cat <<'EOF' >/etc/systemd/system/protonmail-bridge-smtp-proxy.service [Unit] Description=Proton Mail Bridge SMTP Proxy (587 -> 127.0.0.1:1025) After=protonmail-bridge.service Requires=protonmail-bridge.service ConditionPathExists=/home/protonbridge/.protonmailbridge-initialized [Service] Type=simple Sockets=protonmail-bridge-smtp.socket ExecStart=/usr/lib/systemd/systemd-socket-proxyd 127.0.0.1:1025 NoNewPrivileges=yes PrivateTmp=yes EOF msg_ok "Created Services" msg_info "Creating Helper Commands" cat <<'EOF' >/usr/local/bin/protonmailbridge-configure #!/usr/bin/env bash set -euo pipefail BRIDGE_USER="protonbridge" BRIDGE_HOME="/home/${BRIDGE_USER}" GNUPG_HOME="${BRIDGE_HOME}/.gnupg" MARKER="${BRIDGE_HOME}/.protonmailbridge-initialized" FIRST_TIME=0 if [[ ! -f "${MARKER}" ]]; then FIRST_TIME=1 fi # Stop sockets/proxies/bridge daemon before configuration systemctl stop protonmail-bridge-imap.socket protonmail-bridge-smtp.socket systemctl stop protonmail-bridge-imap-proxy protonmail-bridge-smtp-proxy protonmail-bridge if [[ "${FIRST_TIME}" == "1" ]]; then echo "First-time setup: initializing pass keychain for ${BRIDGE_USER} (required by Proton Mail Bridge on Linux)." install -d -m 0700 -o "${BRIDGE_USER}" -g "${BRIDGE_USER}" "${GNUPG_HOME}" FPR="$(runuser -u "${BRIDGE_USER}" -- env HOME="${BRIDGE_HOME}" GNUPGHOME="${GNUPG_HOME}" \ gpg --list-secret-keys --with-colons 2>/dev/null | awk -F: '$1=="fpr"{print $10; exit}')" if [[ -z "${FPR}" ]]; then runuser -u "${BRIDGE_USER}" -- env HOME="${BRIDGE_HOME}" GNUPGHOME="${GNUPG_HOME}" \ gpg --batch --pinentry-mode loopback --passphrase '' \ --quick-gen-key 'ProtonMail Bridge' default default never FPR="$(runuser -u "${BRIDGE_USER}" -- env HOME="${BRIDGE_HOME}" GNUPGHOME="${GNUPG_HOME}" \ gpg --list-secret-keys --with-colons 2>/dev/null | awk -F: '$1=="fpr"{print $10; exit}')" fi if [[ -z "${FPR}" ]]; then echo "Failed to detect a GPG key fingerprint for ${BRIDGE_USER}." >&2 exit 1 fi runuser -u "${BRIDGE_USER}" -- env HOME="${BRIDGE_HOME}" GNUPGHOME="${GNUPG_HOME}" \ pass init "${FPR}" echo echo "To do initial configuration of the Proton Mail Bridge:" echo "Run: login" echo "Run: info" echo "Run: exit" echo else echo echo "Launching Proton Mail Bridge CLI for configuration." echo "External access is disabled until you exit." echo "Run: exit" echo fi runuser -u "${BRIDGE_USER}" -- env HOME="${BRIDGE_HOME}" \ protonmail-bridge -c if [[ "${FIRST_TIME}" == "1" ]]; then touch "${MARKER}" chown "${BRIDGE_USER}:${BRIDGE_USER}" "${MARKER}" chmod 0644 "${MARKER}" fi systemctl enable -q --now protonmail-bridge.service protonmail-bridge-imap.socket protonmail-bridge-smtp.socket if [[ "${FIRST_TIME}" == "1" ]]; then echo "Initialization complete. Services enabled and started." else echo "Configuration complete. Services enabled and started." fi EOF chmod +x /usr/local/bin/protonmailbridge-configure ln -sf /usr/local/bin/protonmailbridge-configure /usr/bin/protonmailbridge-configure msg_ok "Created Helper Commands" motd_ssh customize cleanup_lxc ================================================ FILE: install/prowlarr-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://prowlarr.com/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y sqlite3 libicu76 msg_ok "Installed Dependencies" fetch_and_deploy_gh_release "prowlarr" "Prowlarr/Prowlarr" "prebuild" "latest" "/opt/Prowlarr" "Prowlarr.master*linux-core-arm64.tar.gz" msg_info "Configuring Prowlarr" mkdir -p /var/lib/prowlarr/ chmod 775 /var/lib/prowlarr/ /opt/Prowlarr msg_ok "Configured Prowlarr" msg_info "Creating Service" cat </etc/systemd/system/prowlarr.service [Unit] Description=Prowlarr Daemon After=syslog.target network.target [Service] UMask=0002 Type=simple ExecStart=/opt/Prowlarr/Prowlarr -nobrowser -data=/var/lib/prowlarr/ TimeoutStopSec=20 KillMode=process Restart=on-failure [Install] WantedBy=multi-user.target EOF systemctl enable -q --now prowlarr msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/proxmox-backup-server-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.proxmox.com/en/proxmox-backup-server source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt-get install -y git $STD apt-get install -y dpkg-dev msg_info "Installing Proxmox Backup Server (Patience)" export DEBIAN_FRONTEND=noninteractive git clone https://github.com/wofferl/proxmox-backup-arm64 cd ./proxmox-backup-arm64 $STD ./build.sh install $STD apt install -y -f ./packages/* msg_ok "Installed Proxmox Backup Server" motd_ssh customize cleanup_lxc ================================================ FILE: install/proxmox-datacenter-manager-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: CrazyWolf13 # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: Proxmox Server Solution GmbH source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y rsyslog systemctl enable -q --now rsyslog msg_ok "Installed Dependencies" msg_info "Installing Proxmox Datacenter Manager" curl -fsSL https://enterprise.proxmox.com/debian/proxmox-archive-keyring-trixie.gpg -o /usr/share/keyrings/proxmox-archive-keyring.gpg setup_deb822_repo \ "pdm" \ "https://enterprise.proxmox.com/debian/proxmox-archive-keyring-trixie.gpg" \ "http://download.proxmox.com/debian/pdm" \ "trixie" \ "pdm-no-subscription" setup_deb822_repo \ "pdm-test" \ "https://enterprise.proxmox.com/debian/proxmox-archive-keyring-trixie.gpg" \ "http://download.proxmox.com/debian/pdm" \ "trixie" \ "pdm-test" \ "" \ "false" cat <<'EOF' > /etc/apt/preferences.d/99-pdm-unneeded-packages Package: proxmox-default-kernel proxmox-kernel-* pve-firmware Pin: release * Pin-Priority: -1 EOF DEBIAN_FRONTEND=noninteractive $STD apt -o Dpkg::Options::="--force-confdef" \ -o Dpkg::Options::="--force-confold" \ install -y proxmox-datacenter-manager \ proxmox-datacenter-manager-ui \ proxmox-mail-forward \ proxmox-offline-mirror-helper msg_ok "Installed Proxmox Datacenter Manager" motd_ssh customize cleanup_lxc ================================================ FILE: install/proxmox-mail-gateway-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: thost96 (thost96) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.proxmox.com/en/products/proxmox-mail-gateway source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Proxmox Mail Gateway" setup_deb822_repo \ "pmg" \ "https://enterprise.proxmox.com/debian/proxmox-release-trixie.gpg" \ "http://download.proxmox.com/debian/pmg" \ "trixie" \ "pmg-no-subscription" $STD apt install -y proxmox-mailgateway-container msg_ok "Installed Proxmox Mail Gateway" motd_ssh customize cleanup_lxc ================================================ FILE: install/ps5-mqtt-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: liecno # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/FunkeyFlo/ps5-mqtt/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ jq \ ca-certificates msg_ok "Installed Dependencies" NODE_VERSION="22" NODE_MODULE="playactor" setup_nodejs fetch_and_deploy_gh_release "ps5-mqtt" "FunkeyFlo/ps5-mqtt" "tarball" msg_info "Configuring PS5-MQTT" cd /opt/ps5-mqtt/ps5-mqtt/ $STD npm install $STD npm run build mkdir -p /opt/.config/ps5-mqtt/ mkdir -p /opt/.config/ps5-mqtt/playactor cat </opt/.config/ps5-mqtt/config.json { "mqtt": { "host": "", "port": "", "user": "", "pass": "", "discovery_topic": "homeassistant" }, "device_check_interval": 5000, "device_discovery_interval": 60000, "device_discovery_broadcast_address": "", "include_ps4_devices": false, "psn_accounts": [ { "username": "", "npsso":"" } ], "account_check_interval": 5000, "credentialsStoragePath": "/opt/.config/ps5-mqtt/credentials.json", "frontendPort": "8645" } EOF msg_ok "Configured PS5-MQTT" msg_info "Creating Service" cat </etc/systemd/system/ps5-mqtt.service [Unit] Description=PS5-MQTT Daemon After=syslog.target network.target [Service] WorkingDirectory=/opt/ps5-mqtt/ps5-mqtt Environment="CONFIG_PATH=/opt/.config/ps5-mqtt/config.json" Environment="DEBUG='@ha:ps5:*'" Restart=always RestartSec=5 Type=simple ExecStart=node server/dist/index.js KillMode=process SyslogIdentifier=ps5-mqtt [Install] WantedBy=multi-user.target EOF systemctl enable -q --now ps5-mqtt msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/pterodactyl-panel-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: bvdberg01 # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/pterodactyl/panel source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ lsb-release \ redis \ apache2 \ composer \ cron msg_ok "Installed Dependencies" setup_mariadb msg_info "Adding PHP Repository" $STD curl -sSLo /tmp/debsuryorg-archive-keyring.deb https://packages.sury.org/debsuryorg-archive-keyring.deb $STD dpkg -i /tmp/debsuryorg-archive-keyring.deb cat </etc/apt/sources.list.d/php.sources Types: deb URIs: https://packages.sury.org/php/ Suites: $(lsb_release -sc) Components: main Signed-By: /usr/share/keyrings/deb.sury.org-php.gpg EOF $STD apt update msg_ok "Added PHP Repository" msg_info "Installing PHP" $STD apt remove -y php8.2* $STD apt install -y \ php8.4 \ php8.4-{gd,mysql,mbstring,bcmath,xml,curl,zip,intl,fpm} \ libapache2-mod-php8.4 msg_ok "Installed PHP" msg_info "Setting up MariaDB" DB_NAME=panel DB_USER=pterodactyl DB_PASS=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | head -c13) $STD mariadb -u root -e "CREATE DATABASE $DB_NAME;" $STD mariadb -u root -e "CREATE USER '$DB_USER'@'localhost' IDENTIFIED BY '$DB_PASS';" $STD mariadb -u root -e "GRANT ALL ON $DB_NAME.* TO '$DB_USER'@'localhost'; FLUSH PRIVILEGES;" { echo "pterodactyl Panel-Credentials" echo "pterodactyl Panel Database User: $DB_USER" echo "pterodactyl Panel Database Password: $DB_PASS" echo "pterodactyl Panel Database Name: $DB_NAME" } >>~/pterodactyl-panel.creds msg_ok "Set up MariaDB" read -p "${TAB3}Provide an email address for admin login, this should be a valid email address: " ADMIN_EMAIL read -p "${TAB3}Enter your First Name: " NAME_FIRST read -p "${TAB3}Enter your Last Name: " NAME_LAST msg_info "Installing pterodactyl Panel" RELEASE=$(curl -fsSL https://api.github.com/repos/pterodactyl/panel/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4) }') mkdir /opt/pterodactyl-panel cd /opt/pterodactyl-panel curl -fsSL "https://github.com/pterodactyl/panel/releases/download/v${RELEASE}/panel.tar.gz" -o "panel.tar.gz" tar -xzf "panel.tar.gz" cp .env.example .env ADMIN_PASS=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | head -c13) $STD composer install --no-dev --optimize-autoloader --no-interaction $STD php artisan key:generate --force $STD php artisan p:environment:setup --no-interaction --author "$ADMIN_EMAIL" --url "http://$LOCAL_IP" $STD php artisan p:environment:database --no-interaction --database $DB_NAME --username $DB_USER --password "$DB_PASS" $STD php artisan migrate --seed --force --no-interaction $STD php artisan p:user:make --no-interaction --admin=1 --email "$ADMIN_EMAIL" --password "$ADMIN_PASS" --name-first "$NAME_FIRST" --name-last "$NAME_LAST" --username "admin" echo "* * * * * php /opt/pterodactyl-panel/artisan schedule:run >> /dev/null 2>&1" | crontab -u www-data - chown -R www-data:www-data /opt/pterodactyl-panel/* chmod -R 755 /opt/pterodactyl-panel/storage/* /opt/pterodactyl-panel/bootstrap/cache/ ln -s /opt/pterodactyl-panel /var/www/pterodactyl { echo "" echo "pterodactyl Admin Username: admin" echo "pterodactyl Admin Email: $ADMIN_EMAIL" echo "pterodactyl Admin Password: $ADMIN_PASS" } >>~/pterodactyl-panel.creds rm -rf "/opt/pterodactyl-panel/panel.tar.gz" rm -rf "/tmp/debsuryorg-archive-keyring.deb" echo "${RELEASE}" >/opt/"${APPLICATION}"_version.txt msg_ok "Installed pterodactyl Panel" msg_info "Creating Service" cat </etc/systemd/system/pteroq.service [Unit] Description=Pterodactyl Queue Worker After=redis-server.service [Service] User=www-data Group=www-data Restart=always ExecStart=/usr/bin/php /opt/pterodactyl-panel/artisan queue:work --queue=high,standard,low --sleep=3 --tries=3 StartLimitInterval=180 StartLimitBurst=30 RestartSec=5s [Install] WantedBy=multi-user.target EOF systemctl enable -q --now pteroq cat </etc/apache2/sites-available/pterodactyl.conf ServerName pterodactyl DocumentRoot /opt/pterodactyl-panel/public AllowEncodedSlashes On php_value upload_max_filesize 100M php_value post_max_size 100M Options Indexes FollowSymLinks AllowOverride All Require all granted ErrorLog /var/log/apache2/pterodactyl_error.log CustomLog /var/log/apache2/pterodactyl_access.log combined EOF $STD a2ensite pterodactyl $STD a2enmod rewrite $STD a2dissite 000-default.conf $STD systemctl reload apache2 msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/pterodactyl-wings-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: bvdberg01 # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/pterodactyl/wings source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Docker" DOCKER_CONFIG_PATH='/etc/docker/daemon.json' mkdir -p $(dirname $DOCKER_CONFIG_PATH) echo -e '{\n "log-driver": "journald"\n}' >/etc/docker/daemon.json $STD sh <(curl -fsSL https://get.docker.com) systemctl enable -q --now docker msg_ok "Installed Docker" fetch_and_deploy_gh_release "wings" "pterodactyl/wings" "singlefile" "latest" "/usr/local/bin" "wings_linux_arm64" mkdir -p /etc/pterodactyl msg_info "Creating Service" cat </etc/systemd/system/wings.service [Unit] Description=Pterodactyl Wings Daemon After=docker.service Requires=docker.service PartOf=docker.service [Service] User=root WorkingDirectory=/etc/pterodactyl LimitNOFILE=4096 PIDFile=/var/run/wings/daemon.pid ExecStart=/usr/local/bin/wings Restart=on-failure StartLimitInterval=180 StartLimitBurst=30 RestartSec=5s [Install] WantedBy=multi-user.target EOF systemctl enable -q --now wings msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/pulse-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: rcourtman & vhsdream # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/rcourtman/Pulse source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ diffutils \ polkitd msg_ok "Installed Dependencies" msg_info "Creating User" if useradd -r -m -d /opt/pulse-home -s /usr/sbin/nologin pulse; then msg_ok "Created User" else msg_error "User creation failed" exit 71 fi mkdir -p /etc/pulse fetch_and_deploy_gh_release "pulse" "rcourtman/Pulse" "prebuild" "latest" "/opt/pulse" "pulse-v*-linux-arm64.tar.gz" ln -sf /opt/pulse/bin/pulse /usr/local/bin/pulse chown -R pulse:pulse /etc/pulse /opt/pulse msg_ok "Installed Pulse" msg_info "Creating Service" cat </etc/systemd/system/pulse.service [Unit] Description=Pulse Monitoring Server After=network.target [Service] Type=simple User=pulse Group=pulse WorkingDirectory=/opt/pulse ExecStart=/opt/pulse/bin/pulse Restart=always RestartSec=3 StandardOutput=journal StandardError=journal Environment="PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" Environment="PULSE_DATA_DIR=/etc/pulse" [Install] WantedBy=multi-user.target EOF systemctl enable -q --now pulse msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/pve-scripts-local-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: michelroegl-brunner # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ build-essential \ sshpass \ rsync \ expect msg_ok "Dependencies installed." NODE_VERSION="24" setup_nodejs fetch_and_deploy_gh_release "ProxmoxVE-Local" "community-scripts/ProxmoxVE-Local" "tarball" msg_info "Installing PVE Scripts local" cd /opt/ProxmoxVE-Local $STD npm install cp .env.example .env mkdir -p data chmod 755 data $STD npx prisma generate $STD npx prisma migrate deploy $STD npm run build msg_ok "Installed PVE Scripts local" msg_info "Creating Service" cat </etc/systemd/system/pvescriptslocal.service [Unit] Description=PVEScriptslocal Service After=network.target [Service] WorkingDirectory=/opt/ProxmoxVE-Local ExecStart=/usr/bin/npm start Restart=always RestartSec=10 Environment=NODE_ENV=production User=root [Install] WantedBy=multi-user.target EOF systemctl enable -q --now pvescriptslocal msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/qbittorrent-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: tteck (tteckster) | Co-Author: Slaviša Arežina (tremor021) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.qbittorrent.org/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os fetch_and_deploy_gh_release "qbittorrent" "userdocs/qbittorrent-nox-static" "singlefile" "latest" "/opt/qbittorrent" "aarch64-qbittorrent-nox" msg_info "Setup qBittorrent-nox" mv /opt/qbittorrent/qbittorrent /opt/qbittorrent/qbittorrent-nox mkdir -p ~/.config/qBittorrent/ cat <~/.config/qBittorrent/qBittorrent.conf [LegalNotice] Accepted=true [Preferences] WebUI\Password_PBKDF2="@ByteArray(amjeuVrF3xRbgzqWQmes5A==:XK3/Ra9jUmqUc4RwzCtrhrkQIcYczBl90DJw2rT8DFVTss4nxpoRhvyxhCf87ahVE3SzD8K9lyPdpyUCfmVsUg==)" WebUI\Port=8090 WebUI\UseUPnP=false WebUI\Username=admin [Network] PortForwardingEnabled=false EOF msg_ok "Setup qBittorrent-nox" msg_info "Creating Service" cat </etc/systemd/system/qbittorrent-nox.service [Unit] Description=qBittorrent client After=network.target [Service] Type=simple User=root ExecStart=/opt/qbittorrent/qbittorrent-nox Restart=always [Install] WantedBy=multi-user.target EOF systemctl enable -q --now qbittorrent-nox msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/qdrant-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/qdrant/qdrant source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os fetch_and_deploy_gh_release "qdrant" "qdrant/qdrant" "prebuild" "latest" "/opt/qdrant" "qdrant-aarch64-unknown-linux-musl.tar.gz" $STD mv /opt/qdrant/qdrant /usr/bin/qdrant $STD rm -rf /opt/qdrant msg_info "Creating Qdrant Configuration" mkdir -p /etc/qdrant mkdir -p /var/lib/qdrant/{storage,snapshots} chown -R root:root /var/lib/qdrant chmod -R 755 /var/lib/qdrant cat </etc/qdrant/config.yaml log_level: INFO storage: storage_path: /var/lib/qdrant/storage snapshots_path: /var/lib/qdrant/snapshots service: host: 0.0.0.0 http_port: 6333 grpc_port: 6334 enable_cors: true EOF msg_ok "Created Qdrant Configuration" msg_info "Creating Qdrant Service" cat </etc/systemd/system/qdrant.service [Unit] Description=Qdrant Vector Search Engine After=network-online.target Wants=network-online.target [Service] Type=simple ExecStart=/usr/bin/qdrant --config-path /etc/qdrant/config.yaml WorkingDirectory=/var/lib/qdrant Restart=on-failure RestartSec=5 User=root LimitNOFILE=1048576 [Install] WantedBy=multi-user.target EOF systemctl enable -q --now qdrant msg_ok "Created Qdrant Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/qui-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (Canbiz) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/autobrr/qui source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os fetch_and_deploy_gh_release "qui" "autobrr/qui" "prebuild" "latest" "/usr/local/bin" "qui_*_linux_aarch64.tar.gz" chmod +x /usr/local/bin/qui ln -sf /usr/local/bin/qui /usr/bin/qui ln -sf /usr/local/bin/qui /opt/qui msg_info "Creating Qui Service" cat </etc/systemd/system/qui.service [Unit] Description=Qui - qBittorrent Web UI After=network-online.target Wants=network-online.target [Service] Type=simple ExecStart=/usr/local/bin/qui serve Restart=on-failure RestartSec=5s [Install] WantedBy=multi-user.target EOF systemctl enable -q --now qui msg_ok "Created Qui Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/rabbitmq-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck # Co-Author: MickLesk (Canbiz) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.rabbitmq.com/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ lsb-release \ apt-transport-https \ make \ software-properties-common msg_ok "Installed Dependencies" msg_info "Adding RabbitMQ signing key" # primary RabbitMQ signing key curl -1sLf "https://github.com/rabbitmq/signing-keys/releases/download/3.0/rabbitmq-release-signing-key.asc" | sudo gpg --dearmor | sudo tee /usr/share/keyrings/com.github.rabbitmq.signing.gpg > /dev/null # Launchpad PPA signing key for apt curl -1sLf "https://keyserver.ubuntu.com/pks/lookup?op=get&search=0xf77f1eda57ebb1cc" | sudo gpg --dearmor | sudo tee /usr/share/keyrings/net.launchpad.ppa.rabbitmq.erlang.gpg > /dev/null msg_ok "Signing keys added" msg_info "Adding RabbitMQ repository" cat </etc/apt/sources.list.d/rabbitmq.list deb [arch=arm64 signed-by=/usr/share/keyrings/net.launchpad.ppa.rabbitmq.erlang.gpg] http://ppa.launchpad.net/rabbitmq/rabbitmq-erlang/ubuntu noble main deb-src [signed-by=/usr/share/keyrings/net.launchpad.ppa.rabbitmq.erlang.gpg] http://ppa.launchpad.net/rabbitmq/rabbitmq-erlang/ubuntu noble main EOF $STD add-apt-repository -y ppa:rabbitmq/rabbitmq-erlang msg_ok "RabbitMQ repository added" msg_info "Updating package list" $STD apt update -y msg_ok "Package list updated" msg_info "Installing Erlang & RabbitMQ server" $STD apt install -y erlang-base \ erlang-asn1 erlang-crypto erlang-eldap erlang-ftp erlang-inets \ erlang-mnesia erlang-os-mon erlang-parsetools erlang-public-key \ erlang-runtime-tools erlang-snmp erlang-ssl \ erlang-syntax-tools erlang-tftp erlang-tools erlang-xmerl \ rabbitmq-server msg_ok "RabbitMQ server installed" msg_info "Starting RabbitMQ service" systemctl enable -q --now rabbitmq-server msg_ok "Started Service" msg_info "Enabling RabbitMQ Management Plugin" $STD rabbitmq-plugins enable rabbitmq_management $STD rabbitmqctl enable_feature_flag all msg_ok "Enabled RabbitMQ Management Plugin" msg_info "Creating User" $STD rabbitmqctl add_user proxmox proxmox $STD rabbitmqctl set_user_tags proxmox administrator $STD rabbitmqctl set_permissions -p / proxmox ".*" ".*" ".*" msg_ok "Created User" motd_ssh customize cleanup_lxc ================================================ FILE: install/radarr-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://radarr.video/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y sqlite3 libicu76 msg_ok "Installed Dependencies" fetch_and_deploy_gh_release "Radarr" "Radarr/Radarr" "prebuild" "latest" "/opt/Radarr" "Radarr.master*linux-core-arm64.tar.gz" msg_info "Configuring Radarr" mkdir -p /var/lib/radarr/ chmod 775 /var/lib/radarr/ /opt/Radarr/ msg_ok "Configured Radarr" msg_info "Creating Service" cat </etc/systemd/system/radarr.service [Unit] Description=Radarr Daemon After=syslog.target network.target [Service] UMask=0002 Type=simple ExecStart=/opt/Radarr/Radarr -nobrowser -data=/var/lib/radarr/ TimeoutStopSec=20 KillMode=process Restart=on-failure [Install] WantedBy=multi-user.target EOF systemctl enable -q --now radarr msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/radicale-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: Slaviša Arežina (tremor021) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://radicale.org/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y apache2-utils msg_ok "Installed Dependencies" PYTHON_VERSION="3.13" setup_uv fetch_and_deploy_gh_release "Radicale" "Kozea/Radicale" "tarball" "latest" "/opt/radicale" msg_info "Setting up Radicale" cd /opt/radicale RNDPASS=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | head -c13) $STD htpasswd -c -b -5 /opt/radicale/users admin "$RNDPASS" { echo "Radicale Credentials" echo "Admin User: admin" echo "Admin Password: $RNDPASS" } >>~/radicale.creds mkdir -p /etc/radicale cat </etc/radicale/config [server] hosts = 0.0.0.0:5232 [auth] type = htpasswd htpasswd_filename = /opt/radicale/users htpasswd_encryption = sha512 [storage] type = multifilesystem filesystem_folder = /var/lib/radicale/collections [web] type = internal EOF msg_ok "Set up Radicale" msg_info "Creating Service" cat </etc/systemd/system/radicale.service [Unit] Description=A simple CalDAV (calendar) and CardDAV (contact) server After=network.target Requires=network.target [Service] WorkingDirectory=/opt/radicale ExecStart=/usr/local/bin/uv run -m radicale --config /etc/radicale/config Restart=on-failure # User=radicale # Deny other users access to the calendar data # UMask=0027 [Install] WantedBy=multi-user.target EOF systemctl enable -q --now radicale msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/rclone-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: Slaviša Arežina (tremor021) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/rclone/rclone source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y apache2-utils fuse3 msg_ok "Installed Dependencies" fetch_and_deploy_gh_release "rclone" "rclone/rclone" "prebuild" "latest" "/opt/rclone" "rclone*linux-arm64.zip" msg_info "Installing rclone" cd /opt/rclone RCLONE_PASSWORD=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | head -c13) $STD htpasswd -cb -B /opt/login.pwd admin "$RCLONE_PASSWORD" { echo "rclone-Credentials" echo "rclone User Name: admin" echo "rclone Password: $RCLONE_PASSWORD" } >>~/rclone.creds msg_ok "Installed rclone" msg_info "Creating Service" cat </etc/systemd/system/rclone-web.service [Unit] Description=Rclone Web GUI After=network-online.target [Service] Type=simple User=root WorkingDirectory=/opt/rclone ExecStart=/opt/rclone/rclone rcd --rc-web-gui --rc-web-gui-no-open-browser --rc-addr :3000 --rc-htpasswd /opt/login.pwd Restart=on-failure [Install] WantedBy=multi-user.target EOF systemctl enable -q --now rclone-web msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/rdtclient-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/rogerfar/rdt-client source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt-get install -y unzip msg_ok "Installed Dependencies" msg_info "Installing ASP.NET Core Runtime" $STD apt-get install -y libc6 $STD apt-get install -y libgcc1 $STD apt-get install -y libgssapi-krb5-2 $STD apt-get install -y libicu72 $STD apt-get install -y liblttng-ust1 $STD apt-get install -y libssl3 $STD apt-get install -y libstdc++6 $STD apt-get install -y zlib1g curl -SL -o dotnet.tar.gz https://download.visualstudio.microsoft.com/download/pr/6f79d99b-dc38-4c44-a549-32329419bb9f/a411ec38fb374e3a4676647b236ba021/dotnet-sdk-9.0.100-linux-arm64.tar.gz mkdir -p /usr/share/dotnet $STD tar -zxf dotnet.tar.gz -C /usr/share/dotnet $STD ln -s /usr/share/dotnet/dotnet /usr/bin/dotnet msg_ok "Installed ASP.NET Core Runtime" fetch_and_deploy_gh_release "rdt-client" "rogerfar/rdt-client" "prebuild" "latest" "/opt/rdtc" "RealDebridClient.zip" msg_info "Setting up rdtclient" cd /opt/rdtc mkdir -p data/{db,downloads} sed -i 's#/data/db/#/opt/rdtc&#g' /opt/rdtc/appsettings.json msg_ok "Configured rdtclient" msg_info "Creating Service" cat </etc/systemd/system/rdtc.service [Unit] Description=RdtClient Service [Service] WorkingDirectory=/opt/rdtc ExecStart=/usr/bin/dotnet RdtClient.Web.dll SyslogIdentifier=RdtClient User=root [Install] WantedBy=multi-user.target EOF systemctl enable -q --now rdtc msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/reactive-resume-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: vhsdream # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://rxresume.org source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os PG_VERSION="16" setup_postgresql PG_DB_NAME="reactive_resume" PG_DB_USER="reactive_resume" setup_postgresql_db NODE_VERSION="24" setup_nodejs msg_info "Installing Dependencies" cd /tmp curl -fsSL https://dl.min.io/server/minio/release/linux-arm64/minio.deb -o minio.deb $STD dpkg -i minio.deb msg_ok "Installed Dependencies" fetch_and_deploy_gh_release "reactive-resume" "amruthpillai/reactive-resume" "tarball" msg_info "Building Reactive Resume (Patience)" cd /opt/reactive-resume export COREPACK_ENABLE_DOWNLOAD_PROMPT=0 corepack enable corepack prepare --activate export NODE_ENV="production" export CI="true" $STD pnpm install --frozen-lockfile $STD pnpm run build mkdir -p /opt/reactive-resume/data msg_ok "Built Reactive Resume" msg_info "Installing Browserless (Patience)" cd /tmp curl -fsSL https://github.com/browserless/browserless/archive/refs/tags/v"$TAG".zip -o v"$TAG".zip $STD unzip v"$TAG".zip mv browserless-"$TAG" /opt/browserless cd /opt/browserless $STD npm install rm -rf src/routes/{chrome,edge,firefox,webkit} $STD npm install typescript -g $STD node_modules/playwright-core/cli.js install --with-deps chromium $STD npm install typescript --save-dev $STD npm install esbuild --save-dev $STD npm run build $STD npm run build:function $STD npm prune production msg_ok "Installed Browserless" AUTH_SECRET=$(openssl rand -hex 32) cat </opt/reactive-resume/.env # Reactive Resume v5 Configuration NODE_ENV=production PORT=3000 # Public URL (change to your FQDN when using a reverse proxy) APP_URL=http://${LOCAL_IP}:3000 # Database DATABASE_URL=postgresql://${PG_DB_USER}:${PG_DB_PASS}@localhost:5432/${PG_DB_NAME} # Authentication Secret (do not change after initial setup) AUTH_SECRET=${AUTH_SECRET} # Printer (headless Chromium for PDF generation) PRINTER_ENDPOINT=http://localhost:9222 # Storage: uses local filesystem (/opt/reactive-resume/data) when S3 is not configured # S3_ACCESS_KEY_ID= # S3_SECRET_ACCESS_KEY= # S3_REGION=us-east-1 # S3_ENDPOINT= # S3_BUCKET= # S3_FORCE_PATH_STYLE=false # Email (optional, logs to console if not configured) # SMTP_HOST= # SMTP_PORT=465 # SMTP_USER= # SMTP_PASS= # SMTP_FROM=Reactive Resume # OAuth (optional) # GITHUB_CLIENT_ID= # GITHUB_CLIENT_SECRET= # GOOGLE_CLIENT_ID= # GOOGLE_CLIENT_SECRET= # Feature Flags # FLAG_DISABLE_SIGNUPS=false # FLAG_DISABLE_EMAIL_AUTH=false EOF msg_ok "Configured Reactive Resume" msg_info "Creating Services" cat </etc/systemd/system/chromium-printer.service [Unit] Description=Headless Chromium for Reactive Resume PDF generation After=network.target [Service] Type=simple ExecStart=/usr/bin/chromium --headless --disable-gpu --no-sandbox --disable-dev-shm-usage --remote-debugging-address=127.0.0.1 --remote-debugging-port=9222 Restart=always RestartSec=5 [Install] WantedBy=multi-user.target EOF cat </etc/systemd/system/reactive-resume.service [Unit] Description=Reactive Resume After=network.target postgresql.service chromium-printer.service Wants=postgresql.service chromium-printer.service [Service] WorkingDirectory=/opt/reactive-resume EnvironmentFile=/opt/reactive-resume/.env ExecStart=/usr/bin/node .output/server/index.mjs Restart=always RestartSec=5 [Install] WantedBy=multi-user.target EOF systemctl daemon-reload systemctl enable -q --now chromium-printer.service reactive-resume.service msg_ok "Created Services" motd_ssh customize cleanup_lxc ================================================ FILE: install/readarr-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://readarr.com/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y sqlite3 libicu76 msg_ok "Installed Dependencies" msg_info "Installing Readarr" mkdir -p /var/lib/readarr/ chmod 775 /var/lib/readarr/ cd /var/lib/readarr/ $STD curl -fsSL 'https://readarr.servarr.com/v1/update/develop/updatefile?os=linux&runtime=netcore&arch=arm64' -o readarr.tar.gz $STD tar -xvzf readarr.tar.gz mv Readarr /opt chmod 775 /opt/Readarr rm -rf Readarr.develop.*.tar.gz msg_ok "Installed Readarr" msg_info "Creating Service" cat </etc/systemd/system/readarr.service [Unit] Description=Readarr Daemon After=syslog.target network.target [Service] UMask=0002 Type=simple ExecStart=/opt/Readarr/Readarr -nobrowser -data=/var/lib/readarr/ TimeoutStopSec=20 KillMode=process Restart=on-failure [Install] WantedBy=multi-user.target EOF systemctl -q daemon-reload systemctl enable --now -q readarr msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/readeck-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://readeck.org/en/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os fetch_and_deploy_codeberg_release "readeck" "readeck/readeck" "singlefile" "latest" "/opt/readeck" "readeck-*-linux-arm64" msg_info "Creating Service" cat </etc/systemd/system/readeck.service [Unit] Description=Readeck Service After=network.target [Service] Environment=READECK_SERVER_HOST=0.0.0.0 Environment=READECK_SERVER_PORT=8000 ExecStart=/opt/readeck/./readeck serve WorkingDirectory=/opt/readeck Restart=always [Install] WantedBy=multi-user.target EOF systemctl enable -q --now readeck msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/recyclarr-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: MrYadro # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://recyclarr.dev/wiki/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y git libicu76 msg_ok "Installed Dependencies" fetch_and_deploy_gh_release "recyclarr" "recyclarr/recyclarr" "prebuild" "latest" "/usr/local/bin" "recyclarr-linux-arm64.tar.xz" msg_info "Configuring Recyclarr" mkdir -p /root/.config/recyclarr/{configs,includes} $STD recyclarr config create msg_ok "Configured Recyclarr" msg_info "Setting up Daily Sync Cron" cat </etc/cron.d/recyclarr # Run recyclarr sync daily @daily root /usr/local/bin/recyclarr sync >> /root/.config/recyclarr/sync.log 2>&1 EOF chmod 644 /etc/cron.d/recyclarr msg_ok "Setup Daily Sync Cron" motd_ssh customize cleanup_lxc ================================================ FILE: install/redis-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://redis.io/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y apt-transport-https msg_ok "Installed Dependencies" msg_info "Setting up Redis Repository" setup_deb822_repo \ "redis" \ "https://packages.redis.io/gpg" \ "https://packages.redis.io/deb" \ "trixie" msg_ok "Setup Redis Repository" msg_info "Setting up Redis" $STD apt install -y redis sed -i 's/^bind .*/bind 0.0.0.0/' /etc/redis/redis.conf systemctl enable -q --now redis-server msg_ok "Setup Redis" motd_ssh customize cleanup_lxc ================================================ FILE: install/reitti-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/dedicatedcode/reitti source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ redis-server \ libpq-dev \ zstd \ nginx msg_ok "Installed Dependencies" JAVA_VERSION="25" setup_java PG_VERSION="17" PG_MODULES="postgis" setup_postgresql PG_DB_NAME="reitti_db" PG_DB_USER="reitti" PG_DB_EXTENSIONS="postgis" setup_postgresql_db USE_ORIGINAL_FILENAME="true" fetch_and_deploy_gh_release "reitti" "dedicatedcode/reitti" "singlefile" "latest" "/opt/reitti" "reitti-app.jar" mv /opt/reitti/reitti-*.jar /opt/reitti/reitti.jar msg_info "Installing Nginx Tile Cache" mkdir -p /var/cache/nginx/tiles cat </etc/nginx/nginx.conf user www-data; events { worker_connections 1024; } http { proxy_cache_path /var/cache/nginx/tiles levels=1:2 keys_zone=tiles:10m max_size=1g inactive=30d use_temp_path=off; server { listen 80; location / { proxy_pass https://tile.openstreetmap.org/; proxy_set_header Host tile.openstreetmap.org; proxy_set_header User-Agent "Reitti/1.0"; proxy_cache tiles; proxy_cache_valid 200 30d; proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504; } } } EOF chown -R www-data:www-data /var/cache/nginx chmod -R 750 /var/cache/nginx systemctl restart nginx msg_ok "Installed Nginx Tile Cache" msg_info "Creating Reitti Configuration-File" mkdir -p /opt/reitti/data cat </opt/reitti/application.properties # Server configuration server.port=8080 server.servlet.context-path=/ server.forward-headers-strategy=framework server.compression.enabled=true server.compression.min-response-size=1024 server.compression.mime-types=text/plain,application/json # Logging configuration logging.level.root=INFO logging.level.org.hibernate.engine.jdbc.spi.SqlExceptionHelper=FATAL logging.level.com.dedicatedcode.reitti=INFO # Internationalization spring.messages.basename=messages spring.messages.encoding=UTF-8 spring.messages.cache-duration=3600 spring.messages.fallback-to-system-locale=false # PostgreSQL configuration spring.datasource.url=jdbc:postgresql://127.0.0.1:5432/$PG_DB_NAME spring.datasource.username=$PG_DB_USER spring.datasource.password=$PG_DB_PASS spring.datasource.hikari.maximum-pool-size=20 # Redis configuration spring.data.redis.host=127.0.0.1 spring.data.redis.port=6379 spring.data.redis.username= spring.data.redis.password= spring.data.redis.database=0 spring.cache.redis.key-prefix= spring.cache.cache-names=processed-visits,significant-places,users,magic-links,configurations,transport-mode-configs,avatarThumbnails,avatarData,user-settings spring.cache.redis.time-to-live=1d # Upload configuration spring.servlet.multipart.max-file-size=5GB spring.servlet.multipart.max-request-size=5GB server.tomcat.max-part-count=100 # Rqueue configuration rqueue.web.enable=false rqueue.job.enabled=false rqueue.message.durability.in-terminal-state=0 rqueue.key.prefix=\${spring.cache.redis.key-prefix} rqueue.message.converter.provider.class=com.dedicatedcode.reitti.config.RQueueCustomMessageConverter # Application-specific settings reitti.server.advertise-uri= reitti.security.local-login.disable=false # OIDC / Security Settings reitti.security.oidc.enabled=false reitti.security.oidc.registration.enabled=false reitti.import.batch-size=10000 reitti.import.processing-idle-start-time=10 reitti.geo-point-filter.max-speed-kmh=1000 reitti.geo-point-filter.max-accuracy-meters=100 reitti.geo-point-filter.history-lookback-hours=24 reitti.geo-point-filter.window-size=50 reitti.process-data.schedule=0 */10 * * * * reitti.process-data.refresh-views.schedule=0 0 4 * * * reitti.imports.schedule=0 5/10 * * * * reitti.imports.owntracks-recorder.schedule=\${reitti.imports.schedule} # Geocoding service configuration reitti.geocoding.max-errors=10 reitti.geocoding.photon.base-url= # Tiles Configuration reitti.ui.tiles.cache.url=http://127.0.0.1 reitti.ui.tiles.default.service=https://tile.openstreetmap.org/{z}/{x}/{y}.png reitti.ui.tiles.default.attribution=© OpenStreetMap contributors # Data management configuration reitti.data-management.enabled=false reitti.data-management.preview-cleanup.cron=0 0 4 * * * reitti.storage.path=data/ reitti.storage.cleanup.cron=0 0 4 * * * # Location data density normalization reitti.location.density.target-points-per-minute=4 # Logging buffer reitti.logging.buffer-size=1000 reitti.logging.max-buffer-size=10000 spring.config.import=optional:oidc.properties EOF msg_ok "Created Configuration-File for Reitti" msg_info "Creating Services" cat </etc/systemd/system/reitti.service [Unit] Description=Reitti After=network.target postgresql.service redis-server.service Wants=postgresql.service redis-server.service [Service] Type=simple WorkingDirectory=/opt/reitti/ ExecStart=/usr/bin/java --enable-native-access=ALL-UNNAMED -jar -Xmx2g reitti.jar TimeoutStopSec=20 KillMode=process Restart=on-failure [Install] WantedBy=multi-user.target EOF systemctl enable -q --now reitti msg_ok "Created Services" motd_ssh customize cleanup_lxc ================================================ FILE: install/resiliosync-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: David Bennett (dbinit) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.resilio.com/sync source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Setting up Resilio Sync Repository" setup_deb822_repo \ "resilio" \ "https://linux-packages.resilio.com/resilio-sync/key.asc" \ "http://linux-packages.resilio.com/resilio-sync/deb" \ "resilio-sync" \ "non-free" msg_ok "Setup Resilio Sync Repository" msg_info "Installing Resilio Sync" $STD apt install -y resilio-sync sed -i "s/127.0.0.1:8888/0.0.0.0:8888/g" /etc/resilio-sync/config.json systemctl enable -q resilio-sync systemctl restart resilio-sync msg_ok "Installed Resilio Sync" motd_ssh customize cleanup_lxc ================================================ FILE: install/revealjs-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: Slaviša Arežina (tremor021) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/hakimel/reveal.js source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os NODE_VERSION="22" setup_nodejs fetch_and_deploy_gh_release "revealjs" "hakimel/reveal.js" "tarball" msg_info "Configuring ${APPLICATION}" cd /opt/revealjs $STD npm install sed -i 's/"vite"/"vite --host"/g' package.json msg_ok "Setup ${APPLICATION}" msg_info "Creating Service" cat </etc/systemd/system/revealjs.service [Unit] Description=Reveal.js Service After=network.target [Service] Type=simple User=root WorkingDirectory=/opt/revealjs ExecStart=/usr/bin/npm start Restart=always [Install] WantedBy=multi-user.target EOF systemctl enable -q --now revealjs msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/romm-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) | DevelopmentCats | AlphaLawless # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://romm.app source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ acl \ git \ build-essential \ libssl-dev \ libffi-dev \ libmagic-dev \ python3-dev \ python3-pip \ python3-venv \ libmariadb3 \ libmariadb-dev \ libpq-dev \ libbz2-dev \ libreadline-dev \ libsqlite3-dev \ zlib1g-dev \ liblzma-dev \ libncurses5-dev \ libncursesw5-dev \ redis-server \ redis-tools \ p7zip-full \ tzdata \ nginx msg_ok "Installed Dependencies" PYTHON_VERSION="3.13" setup_uv NODE_VERSION="24" setup_nodejs setup_mariadb MARIADB_DB_NAME="romm" MARIADB_DB_USER="romm" setup_mariadb_db msg_info "Creating directories" mkdir -p /opt/romm \ /var/lib/romm/config \ /var/lib/romm/resources \ /var/lib/romm/assets/{saves,states,screenshots} \ /var/lib/romm/library/roms \ /var/lib/romm/library/bios msg_ok "Created directories" msg_info "Creating configuration file" cat <<'EOF' >/var/lib/romm/config/config.yml # RomM Configuration File # Documentation: https://docs.romm.app/latest/Getting-Started/Configuration-File/ # Only uncomment the lines you want to use/modify # exclude: # platforms: # - excluded_folder_a # roms: # single_file: # extensions: # - xml # - txt # names: # - '._*' # - '*.nfo' # multi_file: # names: # - downloaded_media # - media # system: # platforms: # gc: ngc # ps1: psx # The folder name where your roms are located (relative to library path) # filesystem: # roms_folder: 'roms' # scan: # priority: # metadata: # - "igdb" # - "moby" # - "ss" # - "ra" # artwork: # - "igdb" # - "moby" # - "ss" # region: # - "us" # - "eu" # - "jp" # language: # - "en" # media: # - box2d # - box3d # - screenshot # - manual # emulatorjs: # debug: false # cache_limit: null EOF chmod 644 /var/lib/romm/config/config.yml msg_ok "Created configuration file" fetch_and_deploy_gh_release "RAHasher" "RetroAchievements/RALibretro" "prebuild" "latest" "/opt/RALibretro" "RAHasher-x64-Linux-*.zip" cp /opt/RALibretro/RAHasher /usr/bin/RAHasher chmod +x /usr/bin/RAHasher fetch_and_deploy_gh_release "romm" "rommapp/romm" "tarball" msg_info "Creating environment file" sed -i 's/^supervised no/supervised systemd/' /etc/redis/redis.conf systemctl restart redis-server systemctl enable -q --now redis-server AUTH_SECRET_KEY=$(openssl rand -hex 32) cat </opt/romm/.env ROMM_BASE_PATH=/var/lib/romm ROMM_CONFIG_PATH=/var/lib/romm/config/config.yml WEB_CONCURRENCY=4 DB_HOST=127.0.0.1 DB_PORT=3306 DB_NAME=$MARIADB_DB_NAME DB_USER=$MARIADB_DB_USER DB_PASSWD=$MARIADB_DB_PASS REDIS_HOST=127.0.0.1 REDIS_PORT=6379 ROMM_AUTH_SECRET_KEY=$AUTH_SECRET_KEY DISABLE_DOWNLOAD_ENDPOINT_AUTH=false DISABLE_CSRF_PROTECTION=false ENABLE_RESCAN_ON_FILESYSTEM_CHANGE=true RESCAN_ON_FILESYSTEM_CHANGE_DELAY=5 ENABLE_SCHEDULED_RESCAN=true SCHEDULED_RESCAN_CRON=0 3 * * * ENABLE_SCHEDULED_UPDATE_SWITCH_TITLEDB=true SCHEDULED_UPDATE_SWITCH_TITLEDB_CRON=0 4 * * * LOGLEVEL=INFO EOF chmod 600 /opt/romm/.env msg_ok "Created environment file" msg_info "Setting up RomM Backend" cd /opt/romm export UV_CONCURRENT_DOWNLOADS=1 $STD uv sync --all-extras cd /opt/romm/backend $STD uv run alembic upgrade head msg_ok "Set up RomM Backend" msg_info "Setting up RomM Frontend" cd /opt/romm/frontend $STD npm install $STD npm run build cp -rf /opt/romm/frontend/assets/* /opt/romm/frontend/dist/assets/ mkdir -p /opt/romm/frontend/dist/assets/romm ROMM_BASE=$(grep '^ROMM_BASE_PATH=' /opt/romm/.env | cut -d'=' -f2) ROMM_BASE=${ROMM_BASE:-/var/lib/romm} ln -sfn "$ROMM_BASE"/resources /opt/romm/frontend/dist/assets/romm/resources ln -sfn "$ROMM_BASE"/assets /opt/romm/frontend/dist/assets/romm/assets msg_ok "Set up RomM Frontend" msg_info "Configuring Nginx" cat <<'EOF' >/etc/nginx/sites-available/romm upstream romm_backend { server 127.0.0.1:5000; } map $http_upgrade $connection_upgrade { default upgrade; '' close; } server { listen 80; server_name _; root /opt/romm/frontend/dist; client_max_body_size 0; # Frontend SPA location / { try_files $uri $uri/ /index.html; } # Static assets location /assets { alias /opt/romm/frontend/dist/assets; try_files $uri $uri/ =404; expires 1y; add_header Cache-Control "public, immutable"; } # EmulatorJS player - requires COOP/COEP headers for SharedArrayBuffer location ~ ^/rom/.*/ejs$ { add_header Cross-Origin-Embedder-Policy "require-corp"; add_header Cross-Origin-Opener-Policy "same-origin"; try_files $uri /index.html; } # Backend API location /api { proxy_pass http://romm_backend; proxy_buffering off; proxy_request_buffering off; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } # WebSocket and Netplay location ~ ^/(ws|netplay) { proxy_pass http://romm_backend; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $connection_upgrade; proxy_set_header Host $host; proxy_read_timeout 86400; } # OpenAPI docs location = /openapi.json { proxy_pass http://romm_backend; } # Internal library file serving location /library/ { internal; alias /var/lib/romm/library/; } } EOF sed -i "s|alias /var/lib/romm/library/;|alias ${ROMM_BASE}/library/;|" /etc/nginx/sites-available/romm rm -f /etc/nginx/sites-enabled/default ln -sf /etc/nginx/sites-available/romm /etc/nginx/sites-enabled/romm systemctl restart nginx systemctl enable -q --now nginx msg_ok "Configured Nginx" msg_info "Creating Services" cat </etc/systemd/system/romm-backend.service [Unit] Description=RomM Backend After=network.target mariadb.service redis-server.service Requires=mariadb.service redis-server.service [Service] Type=simple WorkingDirectory=/opt/romm/backend EnvironmentFile=/opt/romm/.env Environment="PYTHONPATH=/opt/romm" ExecStart=/opt/romm/.venv/bin/python main.py Restart=on-failure RestartSec=5 [Install] WantedBy=multi-user.target EOF cat </etc/systemd/system/romm-worker.service [Unit] Description=RomM RQ Worker After=network.target mariadb.service redis-server.service romm-backend.service Requires=mariadb.service redis-server.service [Service] Type=simple WorkingDirectory=/opt/romm/backend EnvironmentFile=/opt/romm/.env Environment="PYTHONPATH=/opt/romm/backend" ExecStart=/opt/romm/.venv/bin/rq worker --path /opt/romm/backend --url redis://127.0.0.1:6379/0 high default low Restart=on-failure RestartSec=5 [Install] WantedBy=multi-user.target EOF cat </etc/systemd/system/romm-scheduler.service [Unit] Description=RomM RQ Scheduler After=network.target mariadb.service redis-server.service romm-backend.service Requires=mariadb.service redis-server.service [Service] Type=simple WorkingDirectory=/opt/romm/backend EnvironmentFile=/opt/romm/.env Environment="PYTHONPATH=/opt/romm/backend" Environment="RQ_REDIS_HOST=127.0.0.1" Environment="RQ_REDIS_PORT=6379" ExecStart=/opt/romm/.venv/bin/rqscheduler --path /opt/romm/backend Restart=on-failure RestartSec=5 [Install] WantedBy=multi-user.target EOF cat </etc/systemd/system/romm-watcher.service [Unit] Description=RomM Filesystem Watcher After=network.target romm-backend.service Requires=romm-backend.service [Service] Type=simple WorkingDirectory=/opt/romm/backend EnvironmentFile=/opt/romm/.env Environment="PYTHONPATH=/opt/romm/backend" ExecStart=/opt/romm/.venv/bin/watchfiles --target-type command '/opt/romm/.venv/bin/python watcher.py' /var/lib/romm/library Restart=on-failure RestartSec=5 [Install] WantedBy=multi-user.target EOF systemctl enable -q --now romm-backend romm-worker romm-scheduler romm-watcher msg_ok "Created Services" motd_ssh customize cleanup_lxc ================================================ FILE: install/rustdeskserver-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: Slaviša Arežina (tremor021) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/rustdesk/rustdesk-server source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os fetch_and_deploy_gh_release "rustdesk-hbbr" "rustdesk/rustdesk-server" "binary" "latest" "/opt/rustdesk" "rustdesk-server-hbbr*arm64.deb" fetch_and_deploy_gh_release "rustdesk-hbbs" "rustdesk/rustdesk-server" "binary" "latest" "/opt/rustdesk" "rustdesk-server-hbbs*arm64.deb" fetch_and_deploy_gh_release "rustdesk-utils" "rustdesk/rustdesk-server" "binary" "latest" "/opt/rustdesk" "rustdesk-server-utils*arm64.deb" fetch_and_deploy_gh_release "rustdesk-api" "lejianwen/rustdesk-api" "binary" "latest" "/opt/rustdesk" "rustdesk-api-server*arm64.deb" motd_ssh customize cleanup_lxc ================================================ FILE: install/rustypaste-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: GoldenSpringness | MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/orhun/rustypaste source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os fetch_and_deploy_gh_release "rustypaste" "orhun/rustypaste" "prebuild" "latest" "/opt/rustypaste" "*aarch64-unknown-linux-gnu.tar.gz" fetch_and_deploy_gh_release "rustypaste-cli" "orhun/rustypaste-cli" "prebuild" "latest" "/usr/local/bin" "*aarch64-unknown-linux-gnu.tar.gz" msg_info "Setting up RustyPaste" cd /opt/rustypaste sed -i 's|^address = ".*"|address = "0.0.0.0:8000"|' config.toml msg_ok "Set up RustyPaste" msg_info "Creating Service" cat </etc/systemd/system/rustypaste.service [Unit] Description=rustypaste Service After=network.target [Service] WorkingDirectory=/opt/rustypaste ExecStart=/opt/rustypaste/rustypaste Restart=always [Install] WantedBy=multi-user.target EOF systemctl enable -q --now rustypaste msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/sabnzbd-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) | Co-Author: MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://sabnzbd.org/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ par2 \ p7zip-full msg_ok "Installed Dependencies" PYTHON_VERSION="3.13" setup_uv msg_info "Setup Unrar" cat </etc/apt/sources.list.d/non-free.sources Types: deb URIs: http://deb.debian.org/debian/ Suites: trixie Components: non-free Signed-By: /usr/share/keyrings/debian-archive-keyring.gpg EOF $STD apt update $STD apt install -y unrar msg_ok "Setup Unrar" fetch_and_deploy_gh_release "sabnzbd-org" "sabnzbd/sabnzbd" "prebuild" "latest" "/opt/sabnzbd" "SABnzbd-*-src.tar.gz" msg_info "Installing SABnzbd" $STD uv venv --clear /opt/sabnzbd/venv $STD uv pip install -r /opt/sabnzbd/requirements.txt --python=/opt/sabnzbd/venv/bin/python msg_ok "Installed SABnzbd" read -r -p "Would you like to install par2cmdline-turbo? " prompt if [[ "${prompt,,}" =~ ^(y|yes)$ ]]; then mv /usr/bin/par2 /usr/bin/par2.old fetch_and_deploy_gh_release "par2cmdline-turbo" "animetosho/par2cmdline-turbo" "prebuild" "latest" "/usr/bin/" "*-linux-arm64.zip" fi msg_info "Creating Service" cat </etc/systemd/system/sabnzbd.service [Unit] Description=SABnzbd After=network.target [Service] WorkingDirectory=/opt/sabnzbd ExecStart=/opt/sabnzbd/venv/bin/python SABnzbd.py -s 0.0.0.0:7777 Restart=always User=root [Install] WantedBy=multi-user.target EOF systemctl enable -q --now sabnzbd msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/salt-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: bvdberg01 # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/saltstack/salt source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Setting up Salt Repo" setup_deb822_repo \ "salt" \ "https://packages.broadcom.com/artifactory/api/security/keypair/SaltProjectKey/public" \ "https://packages.broadcom.com/artifactory/saltproject-deb" \ "stable" msg_ok "Setup Salt Repo" msg_info "Installing Salt" RELEASE=$(get_latest_github_release "saltstack/salt") cat </etc/apt/preferences.d/salt-pin-1001 Package: salt-* Pin: version ${RELEASE} Pin-Priority: 1001 EOF $STD apt install -y salt-master echo "${RELEASE}" >/~.salt msg_ok "Installed Salt" motd_ssh customize cleanup_lxc ================================================ FILE: install/scanopy-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: vhsdream # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/scanopy/scanopy source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ build-essential \ libssl-dev \ pkg-config msg_ok "Installed Dependencies" PG_VERSION=17 setup_postgresql NODE_VERSION="24" setup_nodejs PG_DB_NAME="scanopy_db" PG_DB_USER="scanopy" PG_DB_GRANT_SUPERUSER="true" setup_postgresql_db fetch_and_deploy_gh_release "Scanopy" "scanopy/scanopy" "tarball" "latest" "/opt/scanopy" TOOLCHAIN="$(grep "channel" /opt/scanopy/backend/rust-toolchain.toml | awk -F\" '{print $2}')" RUST_TOOLCHAIN=$TOOLCHAIN setup_rust msg_info "Building Scanopy Server (patience)" cd /opt/scanopy/backend $STD cargo build --release --bin server --bin generate-fixtures $STD ./target/release/generate-fixtures --output-dir /opt/scanopy/ui/src/lib/data mv ./target/release/server /usr/bin/scanopy-server msg_ok "Built Scanopy Server" msg_info "Creating frontend UI" export PUBLIC_SERVER_HOSTNAME=default export PUBLIC_SERVER_PORT="" cd /opt/scanopy/ui $STD npm ci --no-fund --no-audit $STD npm run build msg_ok "Created frontend UI" msg_info "Configuring server for first-run" cat </opt/scanopy/.env ### - SERVER SCANOPY_DATABASE_URL=postgresql://$PG_DB_USER:$PG_DB_PASS@localhost:5432/$PG_DB_NAME SCANOPY_WEB_EXTERNAL_PATH="/opt/scanopy/ui/build" SCANOPY_PUBLIC_URL=http://${LOCAL_IP}:60072 SCANOPY_SERVER_PORT=60072 SCANOPY_LOG_LEVEL=info SCANOPY_INTEGRATED_DAEMON_URL=http://127.0.0.1:60073 ## - uncomment to disable signups # SCANOPY_DISABLE_REGISTRATION=true ## - uncomment when using TLS # SCANOPY_USE_SECURE_SESSION_COOKIES=true ## - see https://github.com/imbolc/axum-client-ip?tab=readme-ov-file#configurable-vs-specific-extractors ## - before uncommenting the below # SCANOPY_CLIENT_IP_SOURCE= ### - SMTP (password reset and notifications - optional) # SCANOPY_SMTP_RELAY=smtp.gmail.com:587 # SCANOPY_SMTP_USERNAME=your-email@gmail.com # SCANOPY_SMTP_PASSWORD=your-app-password # SCANOPY_SMTP_EMAIL=scanopy@yourdomain.tld ### - INTEGRATED DAEMON SCANOPY_SERVER_URL=http://127.0.0.1:60072 SCANOPY_BIND_ADDRESS=0.0.0.0 SCANOPY_NAME="scanopy-daemon" SCANOPY_HEARTBEAT_INTERVAL=30 ### - see https://github.com/scanopy/scanopy/blob/main/docs/CONFIGURATION.md for more options EOF cat </etc/systemd/system/scanopy-server.service [Unit] Description=Scanopy Network Discovery Server After=network.target postgresql.service [Service] Type=simple WorkingDirectory=/opt/scanopy/backend EnvironmentFile=/opt/scanopy/.env ExecStart=/usr/bin/scanopy-server Restart=always RestartSec=10 StandardOutput=journal StandardError=journal [Install] WantedBy=multi-user.target EOF systemctl enable -q --now scanopy-server # Creating short script to configure scanopy-daemon # cat <~/configure_daemon.sh # #!/usr/bin/env bash # # echo "Auto-configuring integrated daemon..." # # NETWORK_ID="\$(sudo -u postgres psql -1 -t -d "${PG_DB_NAME}" -c 'SELECT id FROM networks;')" # API_KEY="\$(sudo -u postgres psql -1 -t -d "${PG_DB_NAME}" -c 'SELECT key FROM api_keys;')" # # cat </etc/systemd/system/scanopy-daemon.service # [Unit] # Description=Scanopy Network Discovery Daemon # After=network-online.target # Wants=network-online.target # # [Service] # Type=simple # User=root # ExecStart=/usr/bin/scanopy-daemon --server-url http://127.0.0.1:60072 --network-id \${NETWORK_ID} --daemon-api-key \${API_KEY} --mode push # Restart=always # RestartSec=10 # StandardOutput=journal # StandardError=journal # # [Install] # WantedBy=multi-user.target # END # # systemctl enable -q --now scanopy-daemon # echo "Scanopy daemon configured and running" # # EOF # chmod +x ~/configure_daemon.sh msg_ok "Scanopy server running - please create an account, daemon API key and daemon in the Scanopy UI." motd_ssh customize cleanup_lxc ================================================ FILE: install/scraparr-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: JasonGreenC # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/thecfu/scraparr source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y libicu76 msg_ok "Installed Dependencies" PYTHON_VERSION="3.13" setup_uv fetch_and_deploy_gh_release "scrappar" "thecfu/scraparr" "tarball" "latest" "/opt/scraparr" msg_info "Installing Scraparr" cd /opt/scraparr $STD uv venv --clear /opt/scraparr/.venv $STD /opt/scraparr/.venv/bin/python -m ensurepip --upgrade $STD /opt/scraparr/.venv/bin/python -m pip install --upgrade pip $STD /opt/scraparr/.venv/bin/python -m pip install -r /opt/scraparr/src/scraparr/requirements.txt chmod -R 755 /opt/scraparr mkdir -p /scraparr/config mv /opt/scraparr/config.yaml /scraparr/config/config.yaml chmod -R 755 /scraparr msg_ok "Installed Scraparr" msg_info "Creating Service" cat </etc/systemd/system/scraparr.service [Unit] Description=Scraparr Wants=network-online.target After=network.target [Service] Type=simple WorkingDirectory=/opt/scraparr/src ExecStart=/opt/scraparr/.venv/bin/python -m scraparr.scraparr Restart=always [Install] WantedBy=multi-user.target EOF systemctl enable -q --now scraparr msg_ok "Configured Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/scrypted-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2024 tteck # Author: tteck (tteckster) # License: MIT # https://github.com/tteck/Proxmox/raw/main/LICENSE source /dev/stdin <<< "$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies (Patience)" $STD apt-get -y install software-properties-common apt-utils $STD apt-get -y update $STD apt-get -y upgrade $STD apt-get install -y avahi-daemon $STD apt-get -y install \ build-essential \ gcc \ gir1.2-gtk-3.0 \ libcairo2-dev \ libgirepository1.0-dev \ libglib2.0-dev \ libjpeg-dev \ libgif-dev \ libopenjp2-7 \ libpango1.0-dev \ librsvg2-dev \ pkg-config \ gpg msg_ok "Installed Dependencies" msg_info "Setting Up Hardware Acceleration" $STD apt-get -y install {va-driver-all,ocl-icd-libopencl1,vainfo} if [[ "$CTTYPE" == "0" ]]; then chgrp video /dev/dri chmod 755 /dev/dri chmod 660 /dev/dri/* $STD adduser $(id -u -n) video $STD adduser $(id -u -n) render fi msg_ok "Set Up Hardware Acceleration" msg_info "Installing GStreamer (Patience)" $STD apt-get -y install \ gstreamer1.0-tools \ libgstreamer1.0-dev \ libgstreamer-plugins-base1.0-dev \ libgstreamer-plugins-bad1.0-dev \ gstreamer1.0-plugins-base \ gstreamer1.0-plugins-good \ gstreamer1.0-plugins-bad \ gstreamer1.0-plugins-ugly \ gstreamer1.0-libav \ gstreamer1.0-alsa msg_ok "Installed GStreamer" msg_info "Setting up Node.js Repository" mkdir -p /etc/apt/keyrings curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_20.x nodistro main" >/etc/apt/sources.list.d/nodesource.list msg_ok "Set up Node.js Repository" msg_info "Installing Node.js" $STD apt-get update $STD apt-get install -y nodejs msg_ok "Installed Node.js" msg_info "Updating Python3" $STD apt-get install -y \ python3 \ python3-dev \ python3-pip rm -rf /usr/lib/python3.*/EXTERNALLY-MANAGED msg_ok "Updated Python3" msg_info "Installing Python3 Dependencies" $STD apt-get -y install \ python3-gi \ python3-gst-1.0 \ python3-matplotlib \ python3-numpy \ python3-opencv \ python3-pil \ python3-setuptools \ python3-skimage \ python3-wheel $STD python3 -m pip install --upgrade pip $STD python3 -m pip install aiofiles debugpy typing_extensions typing msg_ok "Installed Python3 Dependencies" msg_info "Installing Scrypted" $STD npx -y scrypted@latest install-server if [[ "$CTTYPE" == "0" ]]; then sed -i -e 's/^sgx:x:104:$/render:x:104:root/' -e 's/^render:x:106:root$/sgx:x:106:/' /etc/group else sed -i -e 's/^sgx:x:104:$/render:x:104:/' -e 's/^render:x:106:$/sgx:x:106:/' /etc/group fi msg_ok "Installed Scrypted" msg_info "Creating Service" cat </etc/systemd/system/scrypted.service [Unit] Description=Scrypted service After=network.target [Service] User=root Group=root Type=simple ExecStart=/usr/bin/npx -y scrypted serve Restart=on-failure RestartSec=3 [Install] WantedBy=multi-user.target EOF systemctl enable -q --now scrypted.service msg_ok "Created Service" motd_ssh customize msg_info "Cleaning up" $STD apt-get -y autoremove $STD apt-get -y autoclean msg_ok "Cleaned" ================================================ FILE: install/searxng-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (Canbiz) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/searxng/searxng source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing SearXNG dependencies" cat </etc/apt/sources.list.d/backports.sources Types: deb URIs: http://deb.debian.org/debian Suites: trixie-backports Components: main EOF $STD apt update $STD apt install -y \ python3-dev python3-babel python3-venv python-is-python3 \ uwsgi uwsgi-plugin-python3 \ git build-essential libxslt-dev zlib1g-dev libffi-dev libssl-dev sudo valkey msg_ok "Installed dependencies" msg_info "Creating user and preparing directories" useradd --system --shell /bin/bash --home-dir "/usr/local/searxng" --comment 'Privacy-respecting metasearch engine' searxng || true mkdir -p /usr/local/searxng chown -R searxng:searxng /usr/local/searxng msg_ok "User and directories ready" msg_info "Cloning SearXNG source" $STD sudo -H -u searxng git clone https://github.com/searxng/searxng /usr/local/searxng/searxng-src msg_ok "Cloned SearXNG" msg_info "Creating Python virtual environment" sudo -H -u searxng bash -c ' python3 -m venv /usr/local/searxng/searx-pyenv && . /usr/local/searxng/searx-pyenv/bin/activate && pip install -U pip setuptools wheel pyyaml lxml msgspec typing_extensions && pip install --use-pep517 --no-build-isolation -e /usr/local/searxng/searxng-src ' msg_ok "Python environment ready" msg_info "Configuring SearXNG settings" mkdir -p /etc/searxng SECRET_KEY=$(openssl rand -hex 32) cat </etc/searxng/settings.yml # SearXNG settings use_default_settings: true general: debug: false instance_name: "SearXNG" privacypolicy_url: false contact_url: false server: bind_address: "0.0.0.0" port: 8888 secret_key: "${SECRET_KEY}" limiter: false image_proxy: true valkey: url: "valkey://localhost:6379/0" ui: static_use_hash: true enabled_plugins: - 'Hash plugin' - 'Self Information' - 'Tracker URL remover' - 'Ahmia blacklist' search: safe_search: 2 autocomplete: 'google' engines: - name: google engine: google shortcut: gg use_mobile_ui: false - name: duckduckgo engine: duckduckgo shortcut: ddg display_error_messages: true EOF chown searxng:searxng /etc/searxng/settings.yml chmod 640 /etc/searxng/settings.yml msg_ok "Configured settings" msg_info "Set up web services" cat </etc/systemd/system/searxng.service [Unit] Description=SearXNG service After=network.target valkey-server.service Wants=valkey-server.service [Service] Type=simple User=searxng Group=searxng Environment="SEARXNG_SETTINGS_PATH=/etc/searxng/settings.yml" ExecStart=/usr/local/searxng/searx-pyenv/bin/python -m searx.webapp WorkingDirectory=/usr/local/searxng/searxng-src Restart=always [Install] WantedBy=multi-user.target EOF systemctl enable -q --now searxng msg_ok "Created Services" motd_ssh customize cleanup_lxc ================================================ FILE: install/seaweedfs-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/seaweedfs/seaweedfs source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y fuse3 msg_ok "Installed Dependencies" fetch_and_deploy_gh_release "seaweedfs" "seaweedfs/seaweedfs" "prebuild" "latest" "/opt/seaweedfs" "linux_amd64.tar.gz" msg_info "Setting up SeaweedFS" mkdir -p /opt/seaweedfs-data ln -sf /opt/seaweedfs/weed /usr/local/bin/weed msg_ok "Set up SeaweedFS" msg_info "Creating Service" cat </etc/systemd/system/seaweedfs.service [Unit] Description=SeaweedFS Server After=network.target [Service] Type=simple User=root WorkingDirectory=/opt/seaweedfs ExecStart=/opt/seaweedfs/weed server -dir=/opt/seaweedfs-data -master.port=9333 -volume.port=8080 -filer -s3 Restart=on-failure RestartSec=5 LimitNOFILE=65536 [Install] WantedBy=multi-user.target EOF systemctl enable -q --now seaweedfs msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/seelf-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: Slaviša Arežina (tremor021) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/YuukanOO/seelf source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ make \ gcc msg_ok "Installed Dependencies" setup_go NODE_VERSION="22" setup_nodejs fetch_and_deploy_gh_release "seelf" "YuukanOO/seelf" "tarball" msg_info "Setting up seelf. Patience" cd /opt/seelf $STD make build PASS=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | head -c13) mkdir -p /opt/seelf/data { echo "ADMIN_EMAIL=admin@example.com" echo "ADMIN_PASSWORD=$PASS" } | tee .env ~/seelf.creds >/dev/null SEELF_ADMIN_EMAIL=admin@example.com SEELF_ADMIN_PASSWORD=$PASS ./seelf serve &>/dev/null & sleep 5 kill $! msg_ok "Done setting up seelf" msg_info "Creating Service" cat </etc/systemd/system/seelf.service [Unit] Description=seelf Service After=network.target [Service] Type=simple User=root Group=root EnvironmentFile=/opt/seelf/.env Environment=DATA_PATH=/opt/seelf/data WorkingDirectory=/opt/seelf ExecStart=/opt/seelf/./seelf -c data/conf.yml serve Restart=always [Install] WantedBy=multi-user.target EOF systemctl enable -q --now seelf msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/seerr-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: CrazyWolf13 # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://docs.seerr.dev/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ build-essential \ python3-setuptools msg_ok "Installed Dependencies" fetch_and_deploy_gh_release "seerr" "seerr-team/seerr" "tarball" pnpm_desired=$(grep -Po '"pnpm":\s*"\K[^"]+' /opt/seerr/package.json) NODE_VERSION="22" NODE_MODULE="pnpm@$pnpm_desired" setup_nodejs msg_info "Installing Seerr (Patience)" export CYPRESS_INSTALL_BINARY=0 cd /opt/seerr $STD pnpm install --frozen-lockfile export NODE_OPTIONS="--max-old-space-size=3072" $STD pnpm build mkdir -p /etc/seerr/ cat </etc/seerr/seerr.conf ## Seerr's default port is 5055, if you want to use both, change this. ## specify on which port to listen PORT=5055 ## specify on which interface to listen, by default seerr listens on all interfaces HOST=0.0.0.0 ## Uncomment if you want to force Node.js to resolve IPv4 before IPv6 (advanced users only) # FORCE_IPV4_FIRST=true EOF msg_ok "Installed Seerr" msg_info "Creating Service" cat </etc/systemd/system/seerr.service [Unit] Description=Seerr Service Wants=network-online.target After=network-online.target [Service] EnvironmentFile=/etc/seerr/seerr.conf Environment=NODE_ENV=production Type=exec Restart=on-failure WorkingDirectory=/opt/seerr ExecStart=/usr/bin/node dist/index.js [Install] WantedBy=multi-user.target EOF systemctl enable -q --now seerr msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/semaphore-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: kristocopani # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://semaphoreui.com/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ git \ ansible msg_ok "Installed Dependencies" fetch_and_deploy_gh_release "semaphore" "semaphoreui/semaphore" "binary" "latest" "/opt/semaphore" "semaphore_*_linux_arm64.deb" msg_info "Configuring Semaphore" mkdir -p /opt/semaphore cd /opt/semaphore SEM_HASH=$(openssl rand -base64 32) SEM_ENCRYPTION=$(openssl rand -base64 32) SEM_KEY=$(openssl rand -base64 32) SEM_PW=$(openssl rand -base64 12) cat </opt/semaphore/config.json { "sqlite": { "host": "/opt/semaphore/database.sqlite" }, "dialect": "sqlite", "tmp_path": "/opt/semaphore/tmp", "cookie_hash": "${SEM_HASH}", "cookie_encryption": "${SEM_ENCRYPTION}", "access_key_encryption": "${SEM_KEY}" } EOF $STD semaphore user add --admin --login admin --email admin@community-scripts.org --name Administrator --password "${SEM_PW}" --config /opt/semaphore/config.json echo "${SEM_PW}" >~/semaphore.creds msg_ok "Setup Semaphore" msg_info "Creating Service" cat </etc/systemd/system/semaphore.service [Unit] Description=Semaphore UI Documentation=https://docs.semaphoreui.com/ Wants=network-online.target After=network-online.target [Service] ExecStart=/usr/bin/semaphore server --config /opt/semaphore/config.json Restart=always RestartSec=10s [Install] WantedBy=multi-user.target EOF systemctl enable -q --now semaphore msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/sftpgo-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://sftpgo.com/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y sqlite3 msg_ok "Installed Dependencies" setup_deb822_repo \ "sftpgo" \ "https://ftp.osuosl.org/pub/sftpgo/apt/gpg.key" \ "https://ftp.osuosl.org/pub/sftpgo/apt" \ "trixie" msg_info "Installing SFTPGo" $STD apt install -y sftpgo msg_ok "Installed SFTPGo" motd_ssh customize cleanup_lxc ================================================ FILE: install/shelfmark-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: vhsdream # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/calibrain/shelfmark source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y unrar-free ln -sf /usr/bin/unrar-free /usr/bin/unrar msg_ok "Installed Dependencies" mkdir -p /etc/shelfmark cat </etc/shelfmark/.env DOCKERMODE=false URL_BASE="" CONFIG_DIR=/etc/shelfmark TMP_DIR=/tmp/shelfmark ENABLE_LOGGING=true FLASK_HOST=0.0.0.0 FLASK_PORT=8084 # SESSION_COOKIES_SECURE=true # CWA_DB_PATH= USE_CF_BYPASS=true USING_EXTERNAL_BYPASSER=false # EXT_BYPASSER_URL= # EXT_BYPASSER_PATH=/v1 EOF echo "" echo "" echo -e "${BL}Shelfmark Deployment Type${CL}" echo "─────────────────────────────────────────" echo "Please choose your deployment type:" echo "" echo " 1) Use Shelfmark's internal captcha bypasser (default)" echo " 2) Install FlareSolverr in this LXC" echo " 3) Use an existing Flaresolverr/Byparr LXC" echo " 4) Disable captcha bypassing altogether (not recommended)" echo "" read -r -p "${TAB3}Select deployment type [1]: " DEPLOYMENT_TYPE DEPLOYMENT_TYPE="${DEPLOYMENT_TYPE:-1}" case "$DEPLOYMENT_TYPE" in 1) msg_ok "Using Shelfmark's internal captcha bypasser" ;; 2) msg_ok "Proceeding with FlareSolverr installation" ;; 3) echo "" echo -e "${BL}Use an existing FlareSolverr/Byparr LXC${CL}" echo "─────────────────────────────────────────" echo "Enter the URL/IP address with port of your Flaresolverr/Byparr instance" echo "Example: http://flaresoverr.homelab.lan:8191 or" echo "http://192.168.10.99:8191" echo "" read -r -p "FlareSolverr/Byparr URL: " BYPASSER_URL if [[ -z "$BYPASSER_URL" ]]; then msg_warn "No Flaresolverr/Byparr URL provided. Falling back to Shelfmark's internal bypasser." else BYPASSER_URL="${BYPASSER_URL%/}" msg_ok "FlareSolverr/Byparr URL: ${BYPASSER_URL}" fi ;; 4) msg_warn "Disabling captcha bypass. This may cause the majority of searches and downloads to fail." ;; *) msg_warn "Invalid selection. Reverting to default (internal bypasser)!" ;; esac if [[ "$DEPLOYMENT_TYPE" == "2" ]]; then fetch_and_deploy_gh_release "flaresolverr" "FlareSolverr/FlareSolverr" "prebuild" "latest" "/opt/flaresolverr" "flaresolverr_linux_x64.tar.gz" msg_info "Installing FlareSolverr (patience)" $STD apt install -y xvfb setup_deb822_repo \ "google-chrome" \ "https://dl.google.com/linux/linux_signing_key.pub" \ "https://dl.google.com/linux/chrome/deb/" \ "stable" $STD apt install -y google-chrome-stable rm /etc/apt/sources.list.d/google-chrome.list sed -i -e '/BYPASSER=/s/false/true/' \ -e 's/^# EXT_/EXT_/' \ -e "s|_URL=.*|_URL=http://localhost:8191|" /etc/shelfmark/.env msg_ok "Installed FlareSolverr" elif [[ "$DEPLOYMENT_TYPE" == "3" ]]; then sed -i -e '/BYPASSER=/s/false/true/' \ -e 's/^# EXT_/EXT_/' \ -e "s|_URL=.*|_URL=${BYPASSER_URL}|" /etc/shelfmark/.env elif [[ "$DEPLOYMENT_TYPE" == "4" ]]; then sed -i '/_BYPASS=/s/true/false/' /etc/shelfmark/.env else DEPLOYMENT_TYPE="1" msg_info "Installing internal bypasser dependencies" $STD apt install -y --no-install-recommends \ xvfb \ ffmpeg \ chromium-common \ chromium \ python3-tk msg_ok "Installed internal bypasser dependencies" fi NODE_VERSION="24" setup_nodejs PYTHON_VERSION="3.14" setup_uv fetch_and_deploy_gh_release "shelfmark" "calibrain/shelfmark" "tarball" "latest" "/opt/shelfmark" RELEASE_VERSION=$(cat "$HOME/.shelfmark") msg_info "Building Shelfmark frontend" cd /opt/shelfmark/src/frontend echo "RELEASE_VERSION=${RELEASE_VERSION}" >>/etc/shelfmark/.env $STD npm ci $STD npm run build mv /opt/shelfmark/src/frontend/dist /opt/shelfmark/frontend-dist msg_ok "Built Shelfmark frontend" msg_info "Configuring Shelfmark" export VIRTUAL_ENV=/opt/shelfmark/venv cd /opt/shelfmark $STD uv venv --clear ./venv $STD source ./venv/bin/activate if [[ "$DEPLOYMENT_TYPE" == "1" ]]; then $STD uv sync --active --locked --no-default-groups --extra browser else $STD uv sync --active --locked --no-default-groups fi mkdir -p {/var/log/shelfmark,/tmp/shelfmark} msg_ok "Configured Shelfmark" msg_info "Creating Services and start script" cat </etc/systemd/system/shelfmark.service [Unit] Description=Shelfmark server After=network.target [Service] Type=simple WorkingDirectory=/opt/shelfmark EnvironmentFile=/etc/shelfmark/.env ExecStart=/usr/bin/bash /opt/shelfmark/start.sh Restart=always RestartSec=10 KillMode=mixed [Install] WantedBy=multi-user.target EOF if [[ "$DEPLOYMENT_TYPE" == "1" ]]; then cat </etc/systemd/system/chromium.service [Unit] Description=Chromium Headless Browser After=network.target [Service] User=root ExecStart=/usr/bin/chromium --headless --no-sandbox --disable-gpu --disable-dev-shm-usage --remote-debugging-address=127.0.0.1 --remote-debugging-port=9222 --hide-scrollbars Restart=always [Install] WantedBy=multi-user.target EOF systemctl enable -q --now chromium fi if [[ "$DEPLOYMENT_TYPE" == "2" ]]; then cat </etc/systemd/system/flaresolverr.service [Unit] Description=FlareSolverr After=network.target [Service] SyslogIdentifier=flaresolverr Restart=always RestartSec=5 Type=simple Environment="LOG_LEVEL=info" Environment="CAPTCHA_SOLVER=none" WorkingDirectory=/opt/flaresolverr ExecStart=/opt/flaresolverr/flaresolverr TimeoutStopSec=30 [Install] WantedBy=multi-user.target EOF systemctl enable -q --now flaresolverr fi cat </opt/shelfmark/start.sh #!/usr/bin/env bash source /opt/shelfmark/venv/bin/activate set -a source /etc/shelfmark/.env set +a gunicorn --worker-class geventwebsocket.gunicorn.workers.GeventWebSocketWorker --workers 1 -t 300 -b 0.0.0.0:8084 shelfmark.main:app EOF chmod +x /opt/shelfmark/start.sh systemctl enable -q --now shelfmark msg_ok "Created Services and start script" motd_ssh customize cleanup_lxc ================================================ FILE: install/shinobi-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://shinobi.video/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os setup_hwaccel msg_info "Installing Dependencies" $STD apt-get install -y make zip net-tools git $STD apt-get install -y gcc g++ cmake $STD apt-get install -y ca-certificates msg_ok "Installed Dependencies" NODE_VERSION="22" setup_nodejs setup_mariadb msg_info "Installing FFMPEG" $STD apt-get install -y ffmpeg msg_ok "Installed FFMPEG" msg_info "Cloning Shinobi" cd /opt $STD git clone https://gitlab.com/Shinobi-Systems/Shinobi.git -b master Shinobi cd Shinobi gitVersionNumber=$(git rev-parse HEAD) theDateRightNow=$(date) touch version.json chmod 644 version.json echo '{"Product" : "'"Shinobi"'" , "Branch" : "'"master"'" , "Version" : "'"$gitVersionNumber"'" , "Date" : "'"$theDateRightNow"'" , "Repository" : "'"https://gitlab.com/Shinobi-Systems/Shinobi.git"'"}' >version.json msg_ok "Cloned Shinobi" msg_info "Installing Database" sqluser="root" sqlpass="root" echo "mariadb-server mariadb-server/root_password password $sqlpass" | debconf-set-selections echo "mariadb-server mariadb-server/root_password_again password $sqlpass" | debconf-set-selections service mysql start $STD mariadb -u "$sqluser" -p"$sqlpass" -e "source sql/user.sql" || true msg_ok "Installed Database" msg_info "Installing Shinobi" cp conf.sample.json conf.json cronKey=$(head -c 1024 /dev/null $STD pm2 start camera.js $STD pm2 start cron.js $STD pm2 startup $STD pm2 save $STD pm2 list msg_ok "Installed Shinobi" motd_ssh customize cleanup_lxc ================================================ FILE: install/shlink-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://shlink.io/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os PHP_VERSION="8.5" setup_php setup_mariadb MARIADB_DB_NAME="shlink" MARIADB_DB_USER="shlink" setup_mariadb_db fetch_and_deploy_gh_release "shlink" "shlinkio/shlink" "prebuild" "latest" "/opt/shlink" "shlink*_php8.5_dist.zip" msg_info "Setting up Application" cd /opt/shlink $STD php ./vendor/bin/rr get --no-interaction --location bin/ chmod +x bin/rr mkdir -p data/cache data/locks data/log data/proxies data/temp-geolite chmod -R 775 data cat </opt/shlink/.env DEFAULT_DOMAIN=${LOCAL_IP}:8080 IS_HTTPS_ENABLED=false DB_DRIVER=maria DB_NAME=${MARIADB_DB_NAME} DB_USER=${MARIADB_DB_USER} DB_PASSWORD=${MARIADB_DB_PASS} DB_HOST=127.0.0.1 DB_PORT=3306 EOF set -a source /opt/shlink/.env set +a $STD php vendor/bin/shlink-installer init --no-interaction --clear-db-cache --skip-download-geolite API_OUTPUT=$(php bin/cli api-key:generate --name=default 2>&1) INITIAL_API_KEY=$(echo "$API_OUTPUT" | sed -n 's/.*Generated API key: "\([^"]*\)".*/\1/p') if [[ -n "$INITIAL_API_KEY" ]]; then echo "INITIAL_API_KEY=${INITIAL_API_KEY}" >>/opt/shlink/.env fi msg_ok "Set up Application" if prompt_confirm "Install Shlink Web Client?" "y" 60; then msg_info "Installing Dependencies" $STD apt install -y nginx msg_ok "Installed Dependencies" fetch_and_deploy_gh_release "shlink-web-client" "shlinkio/shlink-web-client" "prebuild" "latest" "/opt/shlink-web-client" "shlink-web-client_*_dist.zip" msg_info "Setting up Web Client" cat </opt/shlink-web-client/servers.json [ { "name": "Shlink", "url": "http://${LOCAL_IP}:8080", "apiKey": "${INITIAL_API_KEY}" } ] EOF cat <<'EOF' >/etc/nginx/sites-available/shlink-web-client server { listen 3000 default_server; charset utf-8; root /opt/shlink-web-client; index index.html; location ~* \.(?:manifest|appcache|html?|xml|json)$ { expires -1; } location ~* \.(?:jpg|jpeg|gif|png|ico|cur|gz|svg|svgz|mp4|ogg|ogv|webm|htc)$ { expires 1M; add_header Cache-Control "public"; } location ~* \.(?:css|js)$ { expires 1y; add_header Cache-Control "public"; } location = /servers.json { try_files /servers.json /conf.d/servers.json; } location / { try_files $uri $uri/ /index.html$is_args$args; } } EOF ln -sf /etc/nginx/sites-available/shlink-web-client /etc/nginx/sites-enabled/shlink-web-client rm -f /etc/nginx/sites-enabled/default systemctl enable -q nginx $STD systemctl restart nginx msg_ok "Set up Web Client" fi msg_info "Creating Service" cat </etc/systemd/system/shlink.service [Unit] Description=Shlink URL Shortener After=network.target mariadb.service [Service] Type=simple User=root WorkingDirectory=/opt/shlink EnvironmentFile=/opt/shlink/.env ExecStart=/opt/shlink/bin/rr serve -c config/roadrunner/.rr.yml Restart=on-failure RestartSec=5 [Install] WantedBy=multi-user.target EOF systemctl enable -q --now shlink msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/signoz-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: Slaviša Arežina (tremor021) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://signoz.io/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ apt-transport-https \ ca-certificates msg_ok "Installed Dependencies" JAVA_VERSION="21" setup_java msg_info "Setting up ClickHouse" curl -fsSL "https://packages.clickhouse.com/rpm/lts/repodata/repomd.xml.key" | gpg --dearmor -o /usr/share/keyrings/clickhouse-keyring.gpg cat </etc/apt/sources.list.d/clickhouse.sources Types: deb URIs: https://packages.clickhouse.com/deb Suites: stable Components: main Architectures: arm64 Signed-By: /usr/share/keyrings/clickhouse-keyring.gpg EOF $STD apt update export DEBIAN_FRONTEND=noninteractive $STD apt install -y clickhouse-server clickhouse-client msg_ok "Setup ClickHouse" msg_info "Setting up Zookeeper" ZOOURL=$(curl -fsSL https://dlcdn.apache.org/zookeeper/current/ | grep -o 'apache-zookeeper-[0-9.]\+-bin\.tar\.gz' | head -n1) curl -fsSL "https://dlcdn.apache.org/zookeeper/current/$ZOOURL" -o ~/zookeeper.tar.gz tar -xzf "$HOME/zookeeper.tar.gz" -C "$HOME" mkdir -p /opt/zookeeper mkdir -p /var/lib/zookeeper mkdir -p /var/log/zookeeper cp -r ~/apache-zookeeper-*-bin/* /opt/zookeeper cat </opt/zookeeper/conf/zoo.cfg tickTime=2000 dataDir=/var/lib/zookeeper clientPort=2181 admin.serverPort=3181 EOF cat </opt/zookeeper/conf/zoo.env ZOO_LOG_DIR=/var/log/zookeeper EOF cat </etc/systemd/system/zookeeper.service [Unit] Description=Zookeeper Documentation=http://zookeeper.apache.org [Service] EnvironmentFile=/opt/zookeeper/conf/zoo.env Type=forking WorkingDirectory=/opt/zookeeper ExecStart=/opt/zookeeper/bin/zkServer.sh start /opt/zookeeper/conf/zoo.cfg ExecStop=/opt/zookeeper/bin/zkServer.sh stop /opt/zookeeper/conf/zoo.cfg ExecReload=/opt/zookeeper/bin/zkServer.sh restart /opt/zookeeper/conf/zoo.cfg TimeoutSec=30 Restart=on-failure [Install] WantedBy=multi-user.target EOF systemctl enable -q --now zookeeper msg_ok "Setup Zookeeper" msg_info "Configuring ClickHouse" cat </etc/clickhouse-server/config.d/cluster.xml /clickhouse/task_queue/ddl 127.0.0.1 9000 127.0.0.1 2181 01 01 EOF systemctl enable -q --now clickhouse-server msg_ok "Configured ClickHouse" fetch_and_deploy_gh_release "signoz-schema-migrator" "SigNoz/signoz-otel-collector" "prebuild" "latest" "/opt/signoz-schema-migrator" "signoz-schema-migrator_linux_arm64.tar.gz" msg_info "Running ClickHouse migrations" cd /opt/signoz-schema-migrator/bin $STD ./signoz-schema-migrator sync --dsn="tcp://localhost:9000?password=" --replication=true --up= $STD ./signoz-schema-migrator async --dsn="tcp://localhost:9000?password=" --replication=true --up= msg_ok "ClickHouse Migrations Completed" fetch_and_deploy_gh_release "signoz" "SigNoz/signoz" "prebuild" "latest" "/opt/signoz" "signoz-community_linux_arm64.tar.gz" msg_info "Setting up SigNoz" mkdir -p /var/lib/signoz cat </opt/signoz/conf/systemd.env SIGNOZ_INSTRUMENTATION_LOGS_LEVEL=info INVITE_EMAIL_TEMPLATE=/opt/signoz/templates/invitation_email_template.html SIGNOZ_SQLSTORE_SQLITE_PATH=/var/lib/signoz/signoz.db SIGNOZ_WEB_ENABLED=true SIGNOZ_WEB_DIRECTORY=/opt/signoz/web SIGNOZ_JWT_SECRET=secret SIGNOZ_ALERTMANAGER_PROVIDER=signoz SIGNOZ_TELEMETRYSTORE_PROVIDER=clickhouse SIGNOZ_TELEMETRYSTORE_CLICKHOUSE_DSN=tcp://localhost:9000?password= DOT_METRICS_ENABLED=true EOF cat </etc/systemd/system/signoz.service [Unit] Description=SigNoz Documentation=https://signoz.io/docs After=clickhouse-server.service [Service] Type=simple KillMode=mixed Restart=on-failure WorkingDirectory=/opt/signoz EnvironmentFile=/opt/signoz/conf/systemd.env ExecStart=/opt/signoz/bin/signoz server [Install] WantedBy=multi-user.target EOF systemctl enable -q --now signoz msg_ok "Setup Signoz" fetch_and_deploy_gh_release "signoz-otel-collector" "SigNoz/signoz-otel-collector" "prebuild" "latest" "/opt/signoz-otel-collector" "signoz-otel-collector_linux_arm64.tar.gz" msg_info "Setting up SigNoz OTel Collector" mkdir -p /var/lib/signoz-otel-collector cat </opt/signoz-otel-collector/conf/config.yaml receivers: otlp: protocols: grpc: endpoint: 0.0.0.0:4317 max_recv_msg_size_mib: 16 http: endpoint: 0.0.0.0:4318 jaeger: protocols: grpc: endpoint: 0.0.0.0:14250 thrift_http: endpoint: 0.0.0.0:14268 httplogreceiver/heroku: endpoint: 0.0.0.0:8081 source: heroku httplogreceiver/json: endpoint: 0.0.0.0:8082 source: json processors: batch: send_batch_size: 50000 timeout: 1s signozspanmetrics/delta: metrics_exporter: signozclickhousemetrics latency_histogram_buckets: [100us, 1ms, 2ms, 6ms, 10ms, 50ms, 100ms, 250ms, 500ms, 1000ms, 1400ms, 2000ms, 5s, 10s, 20s, 40s, 60s] dimensions_cache_size: 100000 dimensions: - name: service.namespace default: default - name: deployment.environment default: default - name: signoz.collector.id aggregation_temporality: AGGREGATION_TEMPORALITY_DELTA extensions: health_check: endpoint: 0.0.0.0:13133 zpages: endpoint: localhost:55679 pprof: endpoint: localhost:1777 exporters: clickhousetraces: datasource: tcp://localhost:9000/signoz_traces?password= use_new_schema: true signozclickhousemetrics: dsn: tcp://localhost:9000/signoz_metrics?password= timeout: 45s clickhouselogsexporter: dsn: tcp://localhost:9000/signoz_logs?password= timeout: 10s use_new_schema: true metadataexporter: dsn: tcp://localhost:9000/signoz_metadata?password= timeout: 10s tenant_id: default cache: provider: in_memory service: telemetry: logs: encoding: json extensions: [health_check, zpages, pprof] pipelines: traces: receivers: [otlp, jaeger] processors: [signozspanmetrics/delta, batch] exporters: [clickhousetraces, metadataexporter] metrics: receivers: [otlp] processors: [batch] exporters: [metadataexporter, signozclickhousemetrics] logs: receivers: [otlp, httplogreceiver/heroku, httplogreceiver/json] processors: [batch] exporters: [clickhouselogsexporter, metadataexporter] EOF cat </opt/signoz-otel-collector/conf/opamp.yaml server_endpoint: ws://127.0.0.1:4320/v1/opamp EOF cat </etc/systemd/system/signoz-otel-collector.service [Unit] Description=SigNoz OTel Collector Documentation=https://signoz.io/docs After=clickhouse-server.service [Service] Type=simple KillMode=mixed Restart=on-failure WorkingDirectory=/opt/signoz-otel-collector ExecStart=/opt/signoz-otel-collector/bin/signoz-otel-collector --config=/opt/signoz-otel-collector/conf/config.yaml --manager-config=/opt/signoz-otel-collector/conf/opamp.yaml --copy-path=/var/lib/signoz-otel-collector/config.yaml [Install] WantedBy=multi-user.target EOF systemctl enable -q --now signoz-otel-collector rm -rf ~/zookeeper.tar.gz rm -rf ~/apache-zookeeper-*-bin msg_ok "Setup Signoz OTel Collector" motd_ssh customize cleanup_lxc ================================================ FILE: install/silverbullet-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: Dominik Siebel (dsiebel) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://silverbullet.md source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os fetch_and_deploy_gh_release "silverbullet" "silverbulletmd/silverbullet" "prebuild" "latest" "/opt/silverbullet/bin" "silverbullet-server-linux-aarch64.zip" mkdir -p /opt/silverbullet/space msg_info "Creating Service" cat </etc/systemd/system/silverbullet.service [Unit] Description=Silverbullet Daemon After=syslog.target network.target [Service] User=root Type=simple ExecStart=/opt/silverbullet/bin/silverbullet --hostname 0.0.0.0 --port 3000 /opt/silverbullet/space WorkingDirectory=/opt/silverbullet Restart=on-failure [Install] WantedBy=multi-user.target EOF systemctl enable --now -q silverbullet msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/slskd-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: vhsdream # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/slskd/slskd/, https://github.com/mrusse/soularr source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os fetch_and_deploy_gh_release "Slskd" "slskd/slskd" "prebuild" "latest" "/opt/slskd" "slskd-*-linux-arm64.zip" msg_info "Configuring Slskd" JWT_KEY=$(openssl rand -base64 44) SLSKD_API_KEY=$(openssl rand -base64 44) cp /opt/slskd/config/slskd.example.yml /opt/slskd/config/slskd.yml sed -i \ -e '/web:/,/cidr/s/^# //' \ -e '/https:/,/port: 5031/s/false/true/' \ -e '/port: 5030/,/socket/s/,.*$//' \ -e '/content_path:/,/authentication/s/false/true/' \ -e "\|api_keys|,\|cidr|s|/opt/soularr/run.sh #!/usr/bin/env bash if ps aux | grep "[s]oularr.py" >/dev/null; then echo "Soularr is already running. Exiting..." >&2 exit 1 fi # Remove stale lock file from previous ungraceful exit rm -f "/opt/soularr/.soularr.lock" source /opt/soularr/venv/bin/activate uv run python3 -u /opt/soularr/soularr.py --config-dir /opt/soularr 2>&1 EOF chmod +x /opt/soularr/run.sh deactivate msg_ok "Installed Soularr" fi msg_info "Creating Service" cat </etc/systemd/system/slskd.service [Unit] Description=Slskd Service After=network.target Wants=network.target [Service] WorkingDirectory=/opt/slskd ExecStart=/opt/slskd/slskd --config /opt/slskd/config/slskd.yml Restart=always [Install] WantedBy=multi-user.target EOF if [[ -d /opt/soularr ]]; then cat </etc/systemd/system/soularr.timer [Unit] Description=Soularr service timer RefuseManualStart=no RefuseManualStop=no [Timer] Persistent=true # run every 10 minutes OnCalendar=*-*-* *:0/10:00 Unit=soularr.service [Install] WantedBy=timers.target EOF cat </etc/systemd/system/soularr.service [Unit] Description=Soularr service After=network.target slskd.service [Service] Type=simple WorkingDirectory=/opt/soularr ExecStart=/bin/bash -c /opt/soularr/run.sh [Install] WantedBy=multi-user.target EOF msg_warn "Add your Lidarr API key to Soularr in '/opt/soularr/config.ini', then run 'systemctl enable --now soularr.timer'" fi systemctl enable -q --now slskd msg_ok "Created Services" motd_ssh customize cleanup_lxc ================================================ FILE: install/smokeping-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://oss.oetiker.ch/smokeping/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing SmokePing" $STD apt install -y smokeping cat </etc/smokeping/config.d/Targets *** Targets *** probe = FPing menu = Top title = Network Latency Grapher remark = Welcome to SmokePing. + Local menu = Local title = Local Network (ICMP) ++ LocalMachine menu = Local Machine title = This host host = localhost + DNS menu = DNS latency title = DNS latency (ICMP) ++ Google title = Google host = 8.8.8.8 ++ Cloudflare title = Cloudflare host = 1.1.1.1 ++ Quad9 title = Quad9 host = 9.9.9.9 ++ OpenDNS title = OpenDNS host = 208.67.222.222 + HTTP menu = HTTP latency title = HTTP latency (ICMP) ++ Github host = github.com ++ Discord host = discord.com ++ Google host = google.com ++ Cloudflare host = cloudflare.com ++ Amazon host = amazon.com ++ Netflix host = netflix.com EOF systemctl restart smokeping msg_ok "Installed SmokePing" motd_ssh customize cleanup_lxc ================================================ FILE: install/snipeit-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: Michel Roegl-Brunner (michelroegl-brunner) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://snipeitapp.com/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ git \ nginx msg_ok "Installed Dependencies" PHP_VERSION="8.3" PHP_FPM="YES" PHP_MODULE="ldap,soap,xsl" setup_php setup_composer fetch_and_deploy_gh_release "snipe-it" "grokability/snipe-it" "tarball" setup_mariadb MARIADB_DB_NAME="snipeit_db" MARIADB_DB_USER="snipeit" setup_mariadb_db msg_info "Configuring Snipe-IT" cd /opt/snipe-it cp .env.example .env sed -i -e "s|^APP_URL=.*|APP_URL=http://$LOCAL_IP|" \ -e "s|^DB_DATABASE=.*|DB_DATABASE=$MARIADB_DB_NAME|" \ -e "s|^DB_USERNAME=.*|DB_USERNAME=$MARIADB_DB_USER|" \ -e "s|^DB_PASSWORD=.*|DB_PASSWORD=$MARIADB_DB_PASS|" .env chown -R www-data: /opt/snipe-it chmod -R 755 /opt/snipe-it export COMPOSER_ALLOW_SUPERUSER=1 $STD composer install --no-dev --optimize-autoloader --no-interaction $STD php artisan key:generate --force msg_ok "Configured Snipe-IT" msg_info "Creating Service" cat </etc/nginx/conf.d/snipeit.conf server { listen 80; root /opt/snipe-it/public; server_name $LOCAL_IP; client_max_body_size 100M; index index.php; location / { try_files \$uri \$uri/ /index.php?\$query_string; } location ~ \.php\$ { include fastcgi.conf; include snippets/fastcgi-php.conf; fastcgi_pass unix:/run/php/php8.3-fpm.sock; fastcgi_split_path_info ^(.+\.php)(/.+)\$; fastcgi_param SCRIPT_FILENAME \$document_root\$fastcgi_script_name; include fastcgi_params; } } EOF systemctl reload nginx msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/snowshare-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: TuroYT # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/TuroYT/snowshare source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os NODE_VERSION="24" setup_nodejs PG_VERSION="17" setup_postgresql PG_DB_USER="snowshare" PG_DB_NAME="snowshare" setup_postgresql_db fetch_and_deploy_gh_release "snowshare" "TuroYT/snowshare" "tarball" msg_info "Installing SnowShare" cd /opt/snowshare $STD npm ci cat </opt/snowshare.env DATABASE_URL="postgresql://$PG_DB_USER:$PG_DB_PASS@localhost:5432/$PG_DB_NAME" NEXTAUTH_URL="http://localhost:3000" NEXTAUTH_SECRET="$(openssl rand -base64 32)" ALLOW_SIGNUP=true NODE_ENV=production EOF set -a source /opt/snowshare.env set +a $STD npx prisma generate $STD npx prisma migrate deploy $STD npm run build cat </etc/systemd/system/snowshare.service [Unit] Description=SnowShare - Modern File Sharing Platform After=network.target postgresql.service Requires=postgresql.service [Service] Type=simple WorkingDirectory=/opt/snowshare EnvironmentFile=/opt/snowshare.env ExecStart=/usr/bin/npm start Restart=on-failure RestartSec=10 [Install] WantedBy=multi-user.target EOF systemctl enable -q --now snowshare msg_ok "Installed SnowShare" msg_info "Setting up Cleanup Cron Job" cat </etc/cron.d/snowshare-cleanup 0 2 * * * root cd /opt/snowshare && /usr/bin/npm run cleanup:expired >> /var/log/snowshare-cleanup.log 2>&1 EOF msg_ok "Set up Cleanup Cron Job" motd_ssh customize cleanup_lxc ================================================ FILE: install/solidtime-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://www.solidtime.io/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y caddy msg_ok "Installed Dependencies" PHP_VERSION="8.3" PHP_FPM="YES" PHP_MODULES="bcmath,gd,intl,xml,zip,pdo_pgsql,redis,mbstring,curl" setup_php setup_composer NODE_VERSION="22" setup_nodejs PG_VERSION="16" setup_postgresql PG_DB_NAME="solidtime" PG_DB_USER="solidtime" setup_postgresql_db fetch_and_deploy_gh_release "solidtime" "solidtime-io/solidtime" "tarball" msg_info "Setting up SolidTime" cd /opt/solidtime cp .env.example .env sed -i "s|^APP_ENV=.*|APP_ENV=production|" .env sed -i "s|^APP_DEBUG=.*|APP_DEBUG=false|" .env sed -i "s|^APP_URL=.*|APP_URL=http://${LOCAL_IP}|" .env sed -i "s|^APP_ENABLE_REGISTRATION=.*|APP_ENABLE_REGISTRATION=true|" .env sed -i "s|^DB_CONNECTION=.*|DB_CONNECTION=pgsql|" .env sed -i "s|^DB_HOST=.*|DB_HOST=127.0.0.1|" .env sed -i "s|^DB_PORT=.*|DB_PORT=5432|" .env sed -i "s|^DB_DATABASE=.*|DB_DATABASE=${PG_DB_NAME}|" .env sed -i "s|^DB_USERNAME=.*|DB_USERNAME=${PG_DB_USER}|" .env sed -i "s|^DB_PASSWORD=.*|DB_PASSWORD=${PG_DB_PASS}|" .env sed -i "s|^FILESYSTEM_DISK=.*|FILESYSTEM_DISK=local|" .env sed -i "s|^PUBLIC_FILESYSTEM_DISK=.*|PUBLIC_FILESYSTEM_DISK=public|" .env sed -i "s|^MAIL_MAILER=.*|MAIL_MAILER=log|" .env sed -i "s|^SESSION_SECURE_COOKIE=.*|SESSION_SECURE_COOKIE=false|" .env grep -q "^SESSION_SECURE_COOKIE=" .env || echo "SESSION_SECURE_COOKIE=false" >>.env sed -i "s|^APP_FORCE_HTTPS=.*|APP_FORCE_HTTPS=false|" .env grep -q "^APP_FORCE_HTTPS=" .env || echo "APP_FORCE_HTTPS=false" >>.env $STD composer install --no-dev --optimize-autoloader php artisan self-host:generate-keys >/tmp/solidtime.keys 2>/dev/null while IFS= read -r line; do KEY="${line%%=*}" [[ -z "$KEY" || "${KEY:0:1}" == "#" ]] && continue sed -i "/^${KEY}=/d" .env echo "$line" >>.env done /etc/caddy/Caddyfile :80 { root * /opt/solidtime/public php_fastcgi unix//run/php/php${PHP_VER}-fpm.sock file_server encode gzip } EOF usermod -aG www-data caddy systemctl enable -q --now php${PHP_VER}-fpm systemctl restart caddy msg_ok "Configured Caddy" motd_ssh customize cleanup_lxc ================================================ FILE: install/sonarqube-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: prop4n # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://docs.sonarsource.com/sonarqube-server source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os JAVA_VERSION="21" setup_java PG_VERSION="17" setup_postgresql PG_DB_NAME="sonarqube" PG_DB_USER="sonarqube" setup_postgresql_db msg_info "Setting up SonarQube" temp_file=$(mktemp) RELEASE=$(get_latest_github_release "SonarSource/sonarqube") curl -fsSL "https://binaries.sonarsource.com/Distribution/sonarqube/sonarqube-${RELEASE}.zip" -o $temp_file unzip -q "$temp_file" -d /opt rm -f "$temp_file" mv /opt/sonarqube-* /opt/sonarqube $STD useradd -r -m -U -d /opt/sonarqube -s /bin/bash sonarqube chown -R sonarqube:sonarqube /opt/sonarqube chmod -R 755 /opt/sonarqube mkdir -p /opt/sonarqube/conf cat </opt/sonarqube/conf/sonar.properties sonar.jdbc.username=${PG_DB_USER} sonar.jdbc.password=${PG_DB_PASS} sonar.jdbc.url=jdbc:postgresql://localhost/${PG_DB_NAME} sonar.web.host=0.0.0.0 sonar.web.port=9000 EOF chmod +x /opt/sonarqube/bin/linux-aarch64/sonar.sh echo ${RELEASE} >>~/.sonarqube msg_ok "Configured SonarQube" msg_info "Creating Service" cat </etc/systemd/system/sonarqube.service [Unit] Description=SonarQube service After=postgresql.service [Service] Type=forking ExecStart=/opt/sonarqube/bin/linux-aarch64/sonar.sh start ExecStop=/opt/sonarqube/bin/linux-aarch64/sonar.sh stop User=sonarqube Group=sonarqube Restart=on-failure LimitNOFILE=131072 LimitNPROC=8192 [Install] WantedBy=multi-user.target EOF systemctl enable -q --now sonarqube msg_ok "Service Created" motd_ssh customize cleanup_lxc ================================================ FILE: install/sonarr-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://sonarr.tv/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y sqlite3 libicu76 msg_ok "Installed Dependencies" fetch_and_deploy_gh_release "Sonarr" "Sonarr/Sonarr" "prebuild" "latest" "/opt/Sonarr" "Sonarr.main.*.linux-arm64.tar.gz" mkdir -p /var/lib/sonarr/ chmod 775 /var/lib/sonarr/ msg_info "Creating Service" cat </etc/systemd/system/sonarr.service [Unit] Description=Sonarr Daemon After=syslog.target network.target [Service] Type=simple ExecStart=/opt/Sonarr/Sonarr -nobrowser -data=/var/lib/sonarr/ TimeoutStopSec=20 KillMode=process Restart=on-failure [Install] WantedBy=multi-user.target EOF systemctl enable -q --now sonarr msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/sonobarr-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: GoldenSpringness # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/Dodelidoo-Labs/sonobarr source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os fetch_and_deploy_gh_release "sonobarr" "Dodelidoo-Labs/sonobarr" "tarball" PYTHON_VERSION="3.12" setup_uv msg_info "Setting up sonobarr" $STD uv venv -c /opt/sonobarr/venv source /opt/sonobarr/venv/bin/activate $STD uv pip install --no-cache-dir -r /opt/sonobarr/requirements.txt mkdir -p /etc/sonobarr mv /opt/sonobarr/.sample-env /etc/sonobarr/.env sed -i "s/^secret_key=.*/secret_key=$(openssl rand -hex 16)/" /etc/sonobarr/.env sed -i "s/^sonobarr_superadmin_password=.*/sonobarr_superadmin_password=$(openssl rand -hex 16)/" /etc/sonobarr/.env echo "release_version=$(cat ~/.sonobarr)" >>/etc/sonobarr/.env echo "sonobarr_config_dir=/etc/sonobarr" >>/etc/sonobarr.env msg_ok "Set up sonobarr" msg_info "Creating Service" cat </etc/systemd/system/sonobarr.service [Unit] Description=sonobarr Service After=network.target [Service] WorkingDirectory=/opt/sonobarr/src EnvironmentFile=/etc/sonobarr/.env Environment="PATH=/opt/sonobarr/venv/bin" ExecStart=/bin/bash -c 'gunicorn Sonobarr:app -c ../gunicorn_config.py' Restart=always [Install] WantedBy=multi-user.target EOF systemctl enable -q --now sonobarr msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/soulsync-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/Nezreka/SoulSync source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ gcc \ libffi-dev \ libssl-dev \ libchromaprint-tools \ ffmpeg msg_ok "Installed Dependencies" UV_PYTHON="3.11" setup_uv fetch_and_deploy_gh_release "soulsync" "Nezreka/SoulSync" "tarball" msg_info "Setting up Application" cd /opt/soulsync $STD uv venv /opt/soulsync/.venv --python 3.11 $STD uv pip install -r requirements.txt --python /opt/soulsync/.venv/bin/python mkdir -p /opt/soulsync/{config,data,logs} msg_ok "Set up Application" msg_info "Creating Service" cat </etc/systemd/system/soulsync.service [Unit] Description=SoulSync Music Discovery After=network.target [Service] Type=simple User=root WorkingDirectory=/opt/soulsync ExecStart=/opt/soulsync/.venv/bin/python web_server.py Environment=PYTHONPATH=/opt/soulsync PYTHONUNBUFFERED=1 DATABASE_PATH=/opt/soulsync/data/music_library.db Restart=on-failure RestartSec=5 [Install] WantedBy=multi-user.target EOF systemctl enable -q --now soulsync msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/sparkyfitness-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: Tom Frenzel (tomfrenzel) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/CodeWithCJ/SparkyFitness source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y nginx msg_ok "Installed Dependencies" PG_VERSION="18" setup_postgresql PG_DB_NAME="sparkyfitness" PG_DB_USER="sparky" PG_DB_GRANT_SUPERUSER="true" setup_postgresql_db fetch_and_deploy_gh_release sparkyfitness "CodeWithCJ/SparkyFitness" "tarball" "latest" PNPM_VERSION="$(jq -r '.packageManager | split("@")[1]' /opt/sparkyfitness/package.json)" NODE_VERSION="25" NODE_MODULE="pnpm@${PNPM_VERSION}" setup_nodejs msg_info "Configuring Sparky Fitness" mkdir -p "/etc/sparkyfitness" "/var/lib/sparkyfitness/uploads" "/var/lib/sparkyfitness/backup" "/var/www/sparkyfitness" cp "/opt/sparkyfitness/docker/.env.example" "/etc/sparkyfitness/.env" sed \ -i \ -e "s|^#\?SPARKY_FITNESS_DB_HOST=.*|SPARKY_FITNESS_DB_HOST=localhost|" \ -e "s|^#\?SPARKY_FITNESS_DB_PORT=.*|SPARKY_FITNESS_DB_PORT=5432|" \ -e "s|^SPARKY_FITNESS_DB_NAME=.*|SPARKY_FITNESS_DB_NAME=sparkyfitness|" \ -e "s|^SPARKY_FITNESS_DB_USER=.*|SPARKY_FITNESS_DB_USER=sparky|" \ -e "s|^SPARKY_FITNESS_DB_PASSWORD=.*|SPARKY_FITNESS_DB_PASSWORD=${PG_DB_PASS}|" \ -e "s|^SPARKY_FITNESS_APP_DB_USER=.*|SPARKY_FITNESS_APP_DB_USER=sparky_app|" \ -e "s|^SPARKY_FITNESS_APP_DB_PASSWORD=.*|SPARKY_FITNESS_APP_DB_PASSWORD=$(openssl rand -base64 24 | tr -dc 'a-zA-Z0-9' | head -c20)|" \ -e "s|^SPARKY_FITNESS_SERVER_HOST=.*|SPARKY_FITNESS_SERVER_HOST=localhost|" \ -e "s|^SPARKY_FITNESS_SERVER_PORT=.*|SPARKY_FITNESS_SERVER_PORT=3010|" \ -e "s|^SPARKY_FITNESS_FRONTEND_URL=.*|SPARKY_FITNESS_FRONTEND_URL=http://${LOCAL_IP}:80|" \ -e "s|^GARMIN_MICROSERVICE_URL=.*|GARMIN_MICROSERVICE_URL=http://${LOCAL_IP}:8000|" \ -e "s|^SPARKY_FITNESS_API_ENCRYPTION_KEY=.*|SPARKY_FITNESS_API_ENCRYPTION_KEY=$(openssl rand -hex 32)|" \ -e "s|^BETTER_AUTH_SECRET=.*|BETTER_AUTH_SECRET=$(openssl rand -hex 32)|" \ "/etc/sparkyfitness/.env" msg_ok "Configured Sparky Fitness" msg_info "Building Backend" cd /opt/sparkyfitness/SparkyFitnessServer $STD pnpm install msg_ok "Built Backend" msg_info "Building Frontend (Patience)" cd /opt/sparkyfitness $STD pnpm install cd /opt/sparkyfitness/SparkyFitnessFrontend $STD pnpm run build cp -a /opt/sparkyfitness/SparkyFitnessFrontend/dist/. /var/www/sparkyfitness/ msg_ok "Built Frontend" msg_info "Creating SparkyFitness Service" cat </etc/systemd/system/sparkyfitness-server.service [Unit] Description=SparkyFitness Backend Service After=network.target postgresql.service Requires=postgresql.service [Service] Type=simple WorkingDirectory=/opt/sparkyfitness/SparkyFitnessServer EnvironmentFile=/etc/sparkyfitness/.env ExecStart=/opt/sparkyfitness/SparkyFitnessServer/node_modules/.bin/tsx SparkyFitnessServer.js Restart=always RestartSec=5 [Install] WantedBy=multi-user.target EOF systemctl enable -q --now sparkyfitness-server msg_ok "Created SparkyFitness Service" msg_info "Configuring Nginx" sed \ -e 's|${SPARKY_FITNESS_SERVER_HOST}|127.0.0.1|g' \ -e 's|${SPARKY_FITNESS_SERVER_PORT}|3010|g' \ -e 's|root /usr/share/nginx/html;|root /var/www/sparkyfitness;|g' \ -e 's|server_name localhost;|server_name _;|g' \ "/opt/sparkyfitness/docker/nginx.conf" >/etc/nginx/sites-available/sparkyfitness ln -sf /etc/nginx/sites-available/sparkyfitness /etc/nginx/sites-enabled/sparkyfitness rm -f /etc/nginx/sites-enabled/default $STD nginx -t $STD systemctl enable -q --now nginx $STD systemctl reload nginx msg_ok "Configured Nginx" motd_ssh customize cleanup_lxc ================================================ FILE: install/speedtest-tracker-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: AlphaLawless # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/alexjustesen/speedtest-tracker source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ nginx \ sqlite3 setcap cap_net_raw+ep /bin/ping msg_ok "Installed Dependencies" PHP_VERSION="8.4" PHP_FPM="YES" setup_php setup_composer NODE_VERSION="22" setup_nodejs fetch_and_deploy_gh_release "speedtest-tracker" "alexjustesen/speedtest-tracker" "tarball" msg_info "Installing Speedtest CLI" setup_deb822_repo \ "speedtest-cli" \ "https://packagecloud.io/ookla/speedtest-cli/gpgkey" \ "https://packagecloud.io/ookla/speedtest-cli/debian" \ "$(get_os_info codename)" \ "main" $STD apt install -y speedtest msg_ok "Installed Speedtest CLI" msg_info "Configuring PHP-FPM runtime directory" mkdir -p /etc/systemd/system/php8.4-fpm.service.d/ cat </etc/systemd/system/php8.4-fpm.service.d/override.conf [Service] RuntimeDirectory=php RuntimeDirectoryMode=0755 EOF msg_ok "Configured PHP-FPM runtime directory" msg_info "Setting up Speedtest Tracker" cd /opt/speedtest-tracker APP_KEY=$(php -r "echo bin2hex(random_bytes(16));") TIMEZONE=$(timedatectl | grep "Time zone" | awk '{print $3}') cat </opt/speedtest-tracker/.env APP_NAME="Speedtest Tracker" APP_ENV=production APP_TIMEZONE=${TIMEZONE} APP_KEY=base64:$(echo -n $APP_KEY | base64) APP_DEBUG=false APP_URL=http://${LOCAL_IP} LOG_CHANNEL=stack LOG_LEVEL=debug DB_CONNECTION=sqlite DB_DATABASE=/opt/speedtest-tracker/database/database.sqlite BROADCAST_DRIVER=log CACHE_DRIVER=file FILESYSTEM_DISK=local QUEUE_CONNECTION=sync SESSION_DRIVER=file SESSION_LIFETIME=120 SPEEDTEST_SCHEDULE="0 */6 * * *" SPEEDTEST_SERVERS= SPEEDTEST_EXTERNAL_IP_URL=https://ip.me SPEEDTEST_INTERNET_CHECK_HOSTNAME=1.1.1.1 PRUNE_RESULTS_OLDER_THAN=0 DISPLAY_TIMEZONE=${TIMEZONE} EOF mkdir -p /opt/speedtest-tracker/database touch /opt/speedtest-tracker/database/database.sqlite export COMPOSER_ALLOW_SUPERUSER=1 $STD composer install --optimize-autoloader --no-dev $STD npm ci $STD npm run build $STD php artisan key:generate --force $STD php artisan migrate --force --seed $STD php artisan config:clear $STD php artisan cache:clear $STD php artisan view:clear chown -R www-data:www-data /opt/speedtest-tracker chmod -R 755 /opt/speedtest-tracker/storage chmod -R 755 /opt/speedtest-tracker/bootstrap/cache msg_ok "Set up Speedtest Tracker" msg_info "Creating Service" cat </etc/systemd/system/speedtest-tracker.service [Unit] Description=Speedtest Tracker Queue Worker After=network.target [Service] Type=simple User=www-data Group=www-data Restart=always ExecStart=/usr/bin/php /opt/speedtest-tracker/artisan queue:work --sleep=3 --tries=3 --max-time=3600 WorkingDirectory=/opt/speedtest-tracker [Install] WantedBy=multi-user.target EOF systemctl enable -q --now speedtest-tracker msg_ok "Created Service" msg_info "Setting up Scheduler" cat </etc/cron.d/speedtest-tracker * * * * * www-data cd /opt/speedtest-tracker && php artisan schedule:run >> /dev/null 2>&1 EOF msg_ok "Set up Scheduler" msg_info "Configuring Nginx" cat </etc/nginx/sites-available/speedtest-tracker server { listen 80; server_name _; root /opt/speedtest-tracker/public; add_header X-Frame-Options "SAMEORIGIN"; add_header X-Content-Type-Options "nosniff"; index index.php; charset utf-8; location / { try_files \$uri \$uri/ /index.php?\$query_string; } location = /favicon.ico { access_log off; log_not_found off; } location = /robots.txt { access_log off; log_not_found off; } error_page 404 /index.php; location ~ \.php$ { fastcgi_pass unix:/var/run/php/php8.4-fpm.sock; fastcgi_param SCRIPT_FILENAME \$realpath_root\$fastcgi_script_name; include fastcgi_params; } location ~ /\.(?!well-known).* { deny all; } } EOF ln -sf /etc/nginx/sites-available/speedtest-tracker /etc/nginx/sites-enabled/ rm -f /etc/nginx/sites-enabled/default systemctl reload nginx msg_ok "Configured Nginx" motd_ssh customize cleanup_lxc ================================================ FILE: install/split-pro-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: johanngrobe # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/oss-apps/split-pro source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os NODE_VERSION="22" NODE_MODULE="pnpm" setup_nodejs PG_VERSION="17" PG_MODULES="cron" setup_postgresql msg_info "Installing Dependencies" $STD apt install -y openssl msg_ok "Installed Dependencies" PG_DB_NAME="splitpro" PG_DB_USER="splitpro" PG_DB_EXTENSIONS="pg_cron" setup_postgresql_db fetch_and_deploy_gh_release "split-pro" "oss-apps/split-pro" "tarball" msg_info "Installing Dependencies" cd /opt/split-pro $STD pnpm install --frozen-lockfile msg_ok "Installed Dependencies" msg_info "Building Split Pro" cd /opt/split-pro mkdir -p /opt/split-pro_data/uploads ln -sf /opt/split-pro_data/uploads /opt/split-pro/uploads NEXTAUTH_SECRET=$(openssl rand -base64 32) cp .env.example .env sed -i "s|^DATABASE_URL=.*|DATABASE_URL=\"postgresql://${PG_DB_USER}:${PG_DB_PASS}@localhost:5432/${PG_DB_NAME}\"|" .env sed -i "s|^NEXTAUTH_SECRET=.*|NEXTAUTH_SECRET=\"${NEXTAUTH_SECRET}\"|" .env sed -i "s|^NEXTAUTH_URL=.*|NEXTAUTH_URL=\"http://${LOCAL_IP}:3000\"|" .env sed -i "s|^NEXTAUTH_URL_INTERNAL=.*|NEXTAUTH_URL_INTERNAL=\"http://localhost:3000\"|" .env sed -i "/^POSTGRES_CONTAINER_NAME=/d" .env sed -i "/^POSTGRES_USER=/d" .env sed -i "/^POSTGRES_PASSWORD=/d" .env sed -i "/^POSTGRES_DB=/d" .env sed -i "/^POSTGRES_PORT=/d" .env $STD pnpm build $STD pnpm exec prisma migrate deploy msg_ok "Built Split Pro" msg_info "Creating Service" cat </etc/systemd/system/split-pro.service [Unit] Description=Split Pro After=network.target postgresql.service Requires=postgresql.service [Service] Type=simple User=root WorkingDirectory=/opt/split-pro EnvironmentFile=/opt/split-pro/.env ExecStart=/usr/bin/pnpm start Restart=on-failure RestartSec=5 [Install] WantedBy=multi-user.target EOF systemctl enable -q --now split-pro msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/splunk-enterprise-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: rcastley # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.splunk.com/en_us/download.html source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os echo -e "${TAB3}┌─────────────────────────────────────────────────────────────────────────┐" echo -e "${TAB3}│ SPLUNK GENERAL TERMS │" echo -e "${TAB3}└─────────────────────────────────────────────────────────────────────────┘" echo "" echo -e "${TAB3}Before proceeding with the Splunk Enterprise installation, you must" echo -e "${TAB3}review and accept the Splunk General Terms." echo "" echo -e "${TAB3}Please review the terms at:" echo -e "${TAB3}${GATEWAY}${BGN}https://www.splunk.com/en_us/legal/splunk-general-terms.html${CL}" echo "" while true; do echo -e "${TAB3}Do you accept the Splunk General Terms? (y/N): \c" read -r response case $response in [Yy]|[Yy][Ee][Ss]) msg_ok "Terms accepted. Proceeding with installation..." break ;; [Nn]|[Nn][Oo]|"") msg_error "Terms not accepted. Installation cannot proceed." msg_error "Please review the terms and run the script again if you wish to proceed." exit 254 ;; *) msg_error "Invalid response. Please enter 'y' for yes or 'n' for no." ;; esac done msg_info "Setup Splunk Enterprise" DOWNLOAD_URL=$(curl -s "https://www.splunk.com/en_us/download/splunk-enterprise.html" | grep -o 'data-link="[^"]*' | sed 's/data-link="//' | grep "https.*products/splunk/releases" | grep "linux-amd64\.tgz$") RELEASE=$(echo "$DOWNLOAD_URL" | sed 's|.*/releases/\([^/]*\)/.*|\1|') $STD curl -fsSL -o "splunk-enterprise.tgz" "$DOWNLOAD_URL" || { msg_error "Failed to download Splunk Enterprise from the provided link." exit 250 } $STD tar -xzf "splunk-enterprise.tgz" -C /opt rm -f "splunk-enterprise.tgz" addgroup --system splunk adduser --system --home /opt/splunk --shell /bin/bash --ingroup splunk --no-create-home splunk chown -R splunk:splunk /opt/splunk msg_ok "Setup Splunk Enterprise v${RELEASE}" msg_info "Creating Splunk admin user" ADMIN_USER="admin" ADMIN_PASS=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | head -c13) { echo "Splunk-Credentials" echo "Username: $ADMIN_USER" echo "Password: $ADMIN_PASS" } >> ~/splunk.creds cat << EOF > "/opt/splunk/etc/system/local/user-seed.conf" [user_info] USERNAME = $ADMIN_USER PASSWORD = $ADMIN_PASS EOF msg_ok "Created Splunk admin user" msg_info "Starting Service" $STD sudo -u splunk /opt/splunk/bin/splunk start --accept-license --answer-yes --no-prompt $STD /opt/splunk/bin/splunk enable boot-start -user splunk msg_ok "Started Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/spoolman-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck # Co-Author: MickLesk (Canbiz) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/Donkie/Spoolman source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ build-essential \ libpq-dev \ libffi-dev msg_ok "Installed Dependencies" fetch_and_deploy_gh_release "spoolman" "Donkie/Spoolman" "prebuild" "latest" "/opt/spoolman" "spoolman.zip" PYTHON_VERSION="3.14" setup_uv msg_info "Setting up Spoolman" cd /opt/spoolman $STD uv sync --locked --no-install-project $STD uv sync --locked cp .env.example .env msg_ok "Setup Spoolman" msg_info "Creating Service" cat </etc/systemd/system/spoolman.service [Unit] Description=Spoolman After=network.target [Service] Type=simple WorkingDirectory=/opt/spoolman EnvironmentFile=/opt/spoolman/.env ExecStart=/usr/bin/bash /opt/spoolman/scripts/start.sh Restart=always User=root [Install] WantedBy=multi-user.target EOF systemctl enable -q --now spoolman msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/sportarr-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: Slaviša Arežina (tremor021) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/Sportarr/Sportarr source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os setup_hwaccel msg_info "Installing Dependencies" $STD apt install -y \ ffmpeg \ gosu \ sqlite3 msg_ok "Installed Dependencies" fetch_and_deploy_gh_release "sportarr" "Sportarr/Sportarr" "prebuild" "latest" "/opt/sportarr" "Sportarr-linux-arm64-*.tar.gz" msg_info "Creating Service" cat </opt/sportarr/.env Sportarr__DataPath="/opt/sportarr-data/config" ASPNETCORE_URLS="http://*:1867" ASPNETCORE_ENVIRONMENT="Production" DOTNET_CLI_TELEMETRY_OPTOUT=1 DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=false LIBVA_DRIVER_NAME=iHD EOF cat </etc/systemd/system/sportarr.service [Unit] Description=Sportarr Service After=network.target [Service] EnvironmentFile=/opt/sportarr/.env WorkingDirectory=/opt/sportarr ExecStart=/opt/sportarr/Sportarr Restart=always [Install] WantedBy=multi-user.target EOF systemctl enable -q --now sportarr msg_info "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/sqlserver2022-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: Kristian Skov # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.microsoft.com/en-us/sql-server/sql-server-2022 source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y coreutils msg_ok "Installed Dependencies" msg_info "Setting up SQL Server 2022 Repository" setup_deb822_repo \ "mssql-server-2022" \ "https://packages.microsoft.com/keys/microsoft.asc" \ "https://packages.microsoft.com/ubuntu/22.04/mssql-server-2022" \ "jammy" \ "main" msg_ok "Repository configured" msg_info "Installing SQL Server 2022" $STD apt install -y mssql-server msg_ok "Installed SQL Server 2022" msg_info "Installing SQL Server Tools" export DEBIAN_FRONTEND=noninteractive export ACCEPT_EULA=Y setup_deb822_repo \ "mssql-release" \ "https://packages.microsoft.com/keys/microsoft.asc" \ "https://packages.microsoft.com/ubuntu/22.04/prod" \ "jammy" \ "main" $STD apt-get install -y \ mssql-tools18 \ unixodbc-dev echo 'export PATH="$PATH:/opt/mssql-tools18/bin"' >>~/.bash_profile source ~/.bash_profile msg_ok "Installed SQL Server Tools" read -r -p "${TAB3}Do you want to run the SQL server setup now? (Later is also possible) " prompt if [[ "${prompt,,}" =~ ^(y|yes)$ ]]; then /opt/mssql/bin/mssql-conf setup else msg_ok "Skipping SQL Server setup. You can run it later with '/opt/mssql/bin/mssql-conf setup'." fi msg_info "Start Service" systemctl enable -q --now mssql-server msg_ok "Service started" msg_info "Cleaning up" rm -f /etc/profile.d/debuginfod.sh rm -f /etc/profile.d/debuginfod.csh msg_ok "Cleaned up" motd_ssh customize cleanup_lxc ================================================ FILE: install/sqlserver2025-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.microsoft.com/en-us/sql-server/sql-server-2025 source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y coreutils msg_ok "Installed Dependencies" msg_info "Setting up SQL Server 2025 Repository" setup_deb822_repo \ "mssql-server-2025" \ "https://packages.microsoft.com/keys/microsoft.asc" \ "https://packages.microsoft.com/ubuntu/24.04/mssql-server-2025" \ "noble" \ "main" msg_ok "Repository configured" msg_info "Installing SQL Server 2025" $STD apt install -y mssql-server msg_ok "Installed SQL Server 2025" msg_info "Installing SQL Server Tools" export DEBIAN_FRONTEND=noninteractive export ACCEPT_EULA=Y setup_deb822_repo \ "mssql-release" \ "https://packages.microsoft.com/keys/microsoft.asc" \ "https://packages.microsoft.com/ubuntu/24.04/prod" \ "noble" \ "main" $STD apt-get install -y \ mssql-tools18 \ unixodbc-dev echo 'export PATH="$PATH:/opt/mssql-tools18/bin"' >>~/.bash_profile source ~/.bash_profile msg_ok "Installed SQL Server Tools" read -r -p "${TAB3}Do you want to run the SQL Server setup now? (Later is also possible) " prompt if [[ "${prompt,,}" =~ ^(y|yes)$ ]]; then /opt/mssql/bin/mssql-conf setup else msg_ok "Skipping SQL Server setup. You can run it later with '/opt/mssql/bin/mssql-conf setup'." fi msg_info "Starting SQL Server Service" systemctl enable -q --now mssql-server msg_ok "Service started" msg_info "Cleaning up" rm -f /etc/profile.d/debuginfod.sh /etc/profile.d/debuginfod.csh msg_ok "Cleaned up" motd_ssh customize cleanup_lxc ================================================ FILE: install/step-ca-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: Joerg Heinemann (heinemannj) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/smallstep/certificates source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os setup_deb822_repo \ "smallstep" \ "https://packages.smallstep.com/keys/apt/repo-signing-key.gpg" \ "https://packages.smallstep.com/stable/debian" \ "debs" \ "main" msg_info "Installing step-ca and step-cli" $STD apt install -y step-ca step-cli STEPPATH="/etc/step-ca" STEPHOME="/etc/step" export STEPPATH=$STEPPATH echo "export STEPPATH=${STEPPATH}" >> /etc/profile export STEPHOME=$STEPHOME echo "export STEPHOME=${STEPHOME}" >> /etc/profile mkdir -p "$STEPHOME" # Patch for making $STD happy (/usr/bin/step is a symlink to /usr/bin/step-cli) STEPBIN="$(which step)" rm -f "$STEPBIN" cp -f "$(which step-cli)" "$STEPBIN" # Low port-binding capabilities (ports < 1024) # - Default step-ca listener port: 443 setcap CAP_NET_BIND_SERVICE=+eip "$(which step-ca)" # Service User used by systemd step-ca.service $STD useradd --user-group --system --home "$(step path)" --shell /bin/false step msg_ok "Installed step-ca and step-cli" DomainName="$(hostname -d)" PKIName="$(prompt_input "Enter PKIName" "MyHomePKI" 30)" PKICountry="$(prompt_input "Enter PKICountry" "DE" 30)" PKIOrganizationalUnit="$(prompt_input "Enter PKIOrganizationalUnit" "MyHomeLab" 30)" PKIProvisioner="$(prompt_input "Enter PKIProvisioner" "pki@$DomainName" 30)" AcmeProvisioner="$(prompt_input "Enter AcmeProvisioner" "acme@$DomainName" 30)" X509MinDur="$(prompt_input "Enter X509MinDur" "48h" 30)" X509MaxDur="$(prompt_input "Enter X509MaxDur" "87600h" 30)" X509DefaultDur="$(prompt_input "Enter X509DefaultDur" "168h" 30)" msg_info "Initializing step-ca" # Initialize step-ca DeploymentType="standalone" FQDN="$(hostname -f)" IP="${LOCAL_IP}" LISTENER=":443" LISTENER_INSECURE=":80" # Set different signing CA and Provisioner Passwords EncryptionPwdDir="$(step path)/encryption" PwdFile="$EncryptionPwdDir/ca.pwd" ProvisionerPwdFile="$EncryptionPwdDir/provisioner.pwd" mkdir -p "$EncryptionPwdDir" gpg -q --gen-random --armor 2 32 >"$PwdFile" gpg -q --gen-random --armor 2 32 >"$ProvisionerPwdFile" # Used by systemd step-ca.service ln -s "$PwdFile" "$(step path)/password.txt" # Usage of: # - SSH feature of step-ca # - BadgerDB (badgerv2) => Default DB backend of step-ca # - badgerFileLoadingMode: FileIO (instead of MemoryMap) for LXC with low RAM $STD step ca init \ --deployment-type="$DeploymentType" \ --ssh \ --name="$PKIName" \ --dns="$FQDN" \ --dns="$IP" \ --address="$LISTENER" \ --provisioner="$PKIProvisioner" \ --password-file="$PwdFile" \ --provisioner-password-file="$ProvisionerPwdFile" # Define enhanced x509 CA and Certificate Templates mkdir -p "$(step path)/templates/ca" mkdir -p "$(step path)/templates/x509" CARootTemplate="$(step path)/templates/ca/root.tpl" CAIntermediateTemplate="$(step path)/templates/ca/intermediate.tpl" X509LeafTemplate="$(step path)/templates/x509/leaf.tpl" X509LeafTemplateData="$(step path)/templates/x509/leaf_data.tpl" cat <<'EOF' >"$CARootTemplate" { "subject": { "country": {{ toJson .Insecure.User.country }}, "organization": {{ toJson .Insecure.User.organization }}, "organizationalUnit": {{ toJson .Insecure.User.organizationalUnit }}, "commonName": {{ toJson .Subject.CommonName }} }, "issuer": {{ toJson .Subject }}, "keyUsage": ["certSign", "crlSign"], "basicConstraints": { "isCA": true, "maxPathLen": 1 }, "issuingCertificateURL": [{{ toJson .Insecure.User.issuingCertificateURL }}], "crlDistributionPoints": [{{ toJson .Insecure.User.crlDistributionPoints }}] } EOF cat <<'EOF' >"$CAIntermediateTemplate" { "subject": { "country": {{ toJson .Insecure.User.country }}, "organization": {{ toJson .Insecure.User.organization }}, "organizationalUnit": {{ toJson .Insecure.User.organizationalUnit }}, "commonName": {{ toJson .Subject.CommonName }} }, "keyUsage": ["certSign", "crlSign"], "basicConstraints": { "isCA": true, "maxPathLen": 0 }, "issuingCertificateURL": [{{ toJson .Insecure.User.issuingCertificateURL }}], "crlDistributionPoints": [{{ toJson .Insecure.User.crlDistributionPoints }}] } EOF cat <<'EOF' >"$X509LeafTemplate" { "subject": { {{- if .Insecure.User.Country }} "country": {{ toJson .Insecure.User.country }}, {{- else }} "country": {{ toJson .country }}, {{- end }} {{- if .Insecure.User.organization }} "organization": {{ toJson .Insecure.User.organization }}, {{- else }} "organization": {{ toJson .organization }}, {{- end }} {{- if .Insecure.User.organizationalUnit }} "organizationalUnit": {{ toJson .Insecure.User.organizationalUnit }}, {{- else }} "organizationalUnit": {{ toJson .organizationalUnit }}, {{- end }} "commonName": {{ toJson .Subject.CommonName }} }, "sans": {{ toJson .SANs }}, {{- if typeIs "*rsa.PublicKey" .Insecure.CR.PublicKey }} "keyUsage": ["keyEncipherment", "digitalSignature"], {{- else }} "keyUsage": ["digitalSignature"], {{- end }} "extKeyUsage": ["serverAuth", "clientAuth"], {{- if .Insecure.User.issuingCertificateURL }} "issuingCertificateURL": [{{ toJson .Insecure.User.issuingCertificateURL }}], {{- else }} "issuingCertificateURL": [{{ toJson .issuingCertificateURL }}], {{- end }} {{- if .Insecure.User.crlDistributionPoints }} "crlDistributionPoints": [{{ toJson .Insecure.User.crlDistributionPoints }}] {{- else }} "crlDistributionPoints": [{{ toJson .crlDistributionPoints }}] {{- end }} } EOF cat <"$X509LeafTemplateData" { "country": "${PKICountry}", "organization": "${PKIName}", "organizationalUnit": "${PKIOrganizationalUnit}", "issuingCertificateURL": ["https://${FQDN}${LISTENER}/intermediates.pem"], "crlDistributionPoints": ["https://${FQDN}${LISTENER}/crl"] } EOF # Configure CA Provisioners, DB and CRL settings $STD step ca provisioner add "$AcmeProvisioner" \ --type ACME \ --admin-name "$AcmeProvisioner" $STD step ca provisioner update "$PKIProvisioner" \ --x509-min-dur="$X509MinDur" \ --x509-max-dur="$X509MaxDur" \ --x509-default-dur="$X509DefaultDur" \ --x509-template="$X509LeafTemplate" \ --x509-template-data="$X509LeafTemplateData" \ --allow-renewal-after-expiry $STD step ca provisioner update "$AcmeProvisioner" \ --x509-min-dur="$X509MinDur" \ --x509-max-dur="$X509MaxDur" \ --x509-default-dur="$X509DefaultDur" \ --x509-template="$X509LeafTemplate" \ --x509-template-data="$X509LeafTemplateData" \ --allow-renewal-after-expiry CAConfig="$(step path)/config/ca.json" jq --arg a "${PKICountry}" '.country = $a' "${CAConfig}" > "${CAConfig}_tmp" && mv "${CAConfig}_tmp" "${CAConfig}" jq --arg a "${PKIName}" '.organization = $a' "${CAConfig}" > "${CAConfig}_tmp" && mv "${CAConfig}_tmp" "${CAConfig}" jq --arg a "${PKIOrganizationalUnit}" '.organizationalUnit = $a' "${CAConfig}" > "${CAConfig}_tmp" && mv "${CAConfig}_tmp" "${CAConfig}" jq --arg a "${PKIName} Online CA" '.commonName = $a' "${CAConfig}" > "${CAConfig}_tmp" && mv "${CAConfig}_tmp" "${CAConfig}" jq '.db.badgerFileLoadingMode = "FileIO"' "${CAConfig}" > "${CAConfig}_tmp" && mv "${CAConfig}_tmp" "${CAConfig}" jq '.crl.enabled = true' "${CAConfig}" > "${CAConfig}_tmp" && mv "${CAConfig}_tmp" "${CAConfig}" jq '.crl.generateOnRevoke = true' "${CAConfig}" > "${CAConfig}_tmp" && mv "${CAConfig}_tmp" "${CAConfig}" jq '.crl.cacheDuration = "24h0m0s"' "${CAConfig}" > "${CAConfig}_tmp" && mv "${CAConfig}_tmp" "${CAConfig}" jq '.crl.renewPeriod = "16h0m0s"' "${CAConfig}" > "${CAConfig}_tmp" && mv "${CAConfig}_tmp" "${CAConfig}" jq --arg a "https://${FQDN}${LISTENER}/crl" '.crl.idpURL = $a' "${CAConfig}" > "${CAConfig}_tmp" && mv "${CAConfig}_tmp" "${CAConfig}" jq --arg a "$LISTENER_INSECURE" '.insecureAddress = $a' "${CAConfig}" > "${CAConfig}_tmp" && mv "${CAConfig}_tmp" "${CAConfig}" # Generate Root CA Certificate and Key # - Validity: 219168h (~25 Years) # - maxPathLen: 1 (Root -> Intermediate -> Leaf) => Only one Intermediate CA allowed below Root CA # - Active revocation on Intermediate CA and Leaf Certificates by the usage of build-in Certificate Revocation List (CRL) FLAGS=(--force --template="${CARootTemplate}" --not-after="219168h" --password-file="${PwdFile}" --set country="${PKICountry}" --set organization="${PKIName}" --set organizationalUnit="${PKIOrganizationalUnit}" --set issuingCertificateURL="https://${FQDN}${LISTENER}/roots.pem" --set crlDistributionPoints="https://${FQDN}${LISTENER}/crl") $STD step certificate create "${PKIName} Root CA" \ "$(step path)/certs/root_ca.crt" \ "$(step path)/secrets/root_ca_key" \ "${FLAGS[@]}" # Generate Intermediate CA Certificate Bundle and Key # - Validity: 175368h (~20 Years) # - maxPathLen: 0 (Root -> Intermediate -> Leaf) => Intermediate CA is only allowed to issue Leaf Certificates # - Active revocation on Leaf Certificates by the usage of build-in Certificate Revocation List (CRL) # - Bundle: Certificate Chain (including Root CA Certificate) FLAGS=(--force --template="${CAIntermediateTemplate}" --ca="$(step path)/certs/root_ca.crt" --ca-key="$(step path)/secrets/root_ca_key" --not-after="175368h" --ca-password-file="${PwdFile}" --password-file="${PwdFile}" --bundle --set country="${PKICountry}" --set organization="${PKIName}" --set organizationalUnit="${PKIOrganizationalUnit}" --set issuingCertificateURL="https://${FQDN}${LISTENER}/roots.pem" --set crlDistributionPoints="https://${FQDN}${LISTENER}/crl") $STD step certificate create "${PKIName} Intermediate CA" \ "$(step path)/certs/intermediate_ca.crt" \ "$(step path)/secrets/intermediate_ca_key" \ "${FLAGS[@]}" # Install Root CA Certificate to System Trust Store $STD step certificate install --all "$(step path)/certs/root_ca.crt" $STD update-ca-certificates chown -R step:step "$(step path)" chmod -R 700 "$(step path)" msg_ok "Initialized step-ca" msg_info "Start step-ca as a Daemon" # https://smallstep.com/docs/step-ca/certificate-authority-server-production/#running-step-ca-as-a-daemon cat <<'EOF' >/etc/systemd/system/step-ca.service [Unit] Description=step-ca service Documentation=https://smallstep.com/docs/step-ca Documentation=https://smallstep.com/docs/step-ca/certificate-authority-server-production After=network-online.target Wants=network-online.target StartLimitIntervalSec=30 StartLimitBurst=3 ConditionFileNotEmpty=/etc/step-ca/config/ca.json ConditionFileNotEmpty=/etc/step-ca/password.txt [Service] Type=simple User=step Group=step Environment=STEPPATH=/etc/step-ca WorkingDirectory=/etc/step-ca ExecStart=/usr/bin/step-ca config/ca.json --password-file password.txt ExecReload=/bin/kill -USR1 $MAINPID Restart=on-failure RestartSec=5 TimeoutStopSec=30 StartLimitAction=reboot ; Process capabilities & privileges AmbientCapabilities=CAP_NET_BIND_SERVICE CapabilityBoundingSet=CAP_NET_BIND_SERVICE SecureBits=keep-caps NoNewPrivileges=yes ; Sandboxing SystemCallArchitectures=native SystemCallFilter=@system-service SystemCallFilter=~@resources @privileged RestrictNamespaces=yes LockPersonality=yes MemoryDenyWriteExecute=yes RestrictRealtime=yes RestrictSUIDSGID=yes PrivateMounts=yes ProtectControlGroups=yes ProtectKernelModules=yes ProtectKernelTunables=yes ProtectSystem=strict ProtectHome=yes ReadWritePaths=/etc/step-ca/db ; Read only paths ReadOnlyPaths=/etc/step-ca [Install] WantedBy=multi-user.target EOF $STD systemctl enable -q --now step-ca msg_ok "Started step-ca as a Daemon" fetch_and_deploy_gh_release "step-badger" "lukasz-lobocki/step-badger" "prebuild" "latest" "/opt/step-badger" "step-badger_Linux_x86_64.tar.gz" ln -s /opt/step-badger/step-badger /usr/local/bin/step-badger motd_ssh customize cleanup_lxc ================================================ FILE: install/stirling-pdf-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.stirlingpdf.com/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies (Patience)" $STD apt install -y \ automake \ autoconf \ libtool \ libleptonica-dev \ pkg-config \ zlib1g-dev \ make \ g++ \ unpaper \ fonts-urw-base35 \ qpdf \ poppler-utils \ jbig2 msg_ok "Installed Dependencies" PYTHON_VERSION="3.12" setup_uv JAVA_VERSION="25" setup_java read -r -p "${TAB3}Do you want to use Stirling-PDF with Login? (no/n = without Login) [Y/n] " response response=${response,,} # Convert to lowercase login_mode="false" if [[ "$response" == "y" || "$response" == "yes" || -z "$response" ]]; then USE_ORIGINAL_FILENAME=true fetch_and_deploy_gh_release "stirling-pdf" "Stirling-Tools/Stirling-PDF" "singlefile" "latest" "/opt/Stirling-PDF" "Stirling-PDF-with-login.jar" mv /opt/Stirling-PDF/Stirling-PDF-with-login.jar /opt/Stirling-PDF/Stirling-PDF.jar touch ~/.Stirling-PDF-login login_mode="true" else USE_ORIGINAL_FILENAME=true fetch_and_deploy_gh_release "stirling-pdf" "Stirling-Tools/Stirling-PDF" "singlefile" "latest" "/opt/Stirling-PDF" "Stirling-PDF.jar" fi msg_info "Installing LibreOffice Components" $STD apt install -y \ libreoffice-writer \ libreoffice-calc \ libreoffice-impress \ libreoffice-core \ libreoffice-common \ libreoffice-base-core \ libreoffice-script-provider-python \ libreoffice-java-common \ pngquant \ weasyprint msg_ok "Installed LibreOffice Components" msg_info "Installing Python Dependencies" mkdir -p /tmp/stirling-pdf $STD uv venv --clear /opt/.venv export PATH="/opt/.venv/bin:$PATH" source /opt/.venv/bin/activate $STD uv pip install --upgrade pip $STD uv pip install \ opencv-python-headless \ ocrmypdf \ pillow \ pdf2image $STD apt install -y python3-uno python3-pip $STD pip3 install --break-system-packages --timeout=120 unoserver ln -sf /opt/.venv/bin/python3 /usr/local/bin/python3 ln -sf /opt/.venv/bin/pip /usr/local/bin/pip msg_ok "Installed Python Dependencies" msg_info "Installing Language Packs (Patience)" $STD apt install -y 'tesseract-ocr-*' msg_ok "Installed Language Packs" msg_info "Creating Environment Variables" cat </opt/Stirling-PDF/.env # Java tuning JAVA_BASE_OPTS="-XX:+UnlockExperimentalVMOptions -XX:MaxRAMPercentage=75 -XX:InitiatingHeapOccupancyPercent=20 -XX:+G1PeriodicGCInvokesConcurrent -XX:G1PeriodicGCInterval=10000 -XX:+UseStringDeduplication -XX:G1PeriodicGCSystemLoadThreshold=70" JAVA_CUSTOM_OPTS="" # LibreOffice PATH=/opt/.venv/bin:/usr/lib/libreoffice/program:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin UNO_PATH=/usr/lib/libreoffice/program URE_BOOTSTRAP=file:///usr/lib/libreoffice/program/fundamentalrc PYTHONPATH=/usr/lib/libreoffice/program:/opt/.venv/lib/python3.12/site-packages LD_LIBRARY_PATH=/usr/lib/libreoffice/program STIRLING_TEMPFILES_DIRECTORY=/tmp/stirling-pdf TMPDIR=/tmp/stirling-pdf TEMP=/tmp/stirling-pdf TMP=/tmp/stirling-pdf # Paths PATH=/opt/.venv/bin:/usr/lib/libreoffice/program:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin EOF if [[ "$login_mode" == "true" ]]; then cat <>/opt/Stirling-PDF/.env # activate Login DISABLE_ADDITIONAL_FEATURES=false SECURITY_ENABLELOGIN=true # login credentials SECURITY_INITIALLOGIN_USERNAME=admin SECURITY_INITIALLOGIN_PASSWORD=stirling EOF fi msg_ok "Created Environment Variables" msg_info "Refreshing Font Cache" $STD fc-cache -fv msg_ok "Font Cache Updated" msg_info "Creating Service" cat </etc/systemd/system/libreoffice-listener.service [Unit] Description=LibreOffice Headless Listener Service After=network.target [Service] Type=simple User=root Group=root ExecStart=/usr/lib/libreoffice/program/soffice --headless --invisible --nodefault --nofirststartwizard --nolockcheck --nologo --accept="socket,host=127.0.0.1,port=2002;urp;StarOffice.ComponentContext" Restart=always [Install] WantedBy=multi-user.target EOF cat </etc/systemd/system/stirlingpdf.service [Unit] Description=Stirling-PDF service After=syslog.target network.target libreoffice-listener.service Requires=libreoffice-listener.service [Service] SuccessExitStatus=143 Type=simple User=root Group=root EnvironmentFile=/opt/Stirling-PDF/.env WorkingDirectory=/opt/Stirling-PDF ExecStart=/usr/bin/java -jar Stirling-PDF.jar ExecStop=/bin/kill -15 %n Restart=always RestartSec=10 [Install] WantedBy=multi-user.target EOF cat </etc/systemd/system/unoserver.service [Unit] Description=UnoServer RPC Interface After=libreoffice-listener.service Requires=libreoffice-listener.service [Service] Type=simple ExecStart=/usr/local/bin/unoserver --port 2003 --interface 127.0.0.1 Restart=always EnvironmentFile=/opt/Stirling-PDF/.env [Install] WantedBy=multi-user.target EOF systemctl enable -q --now libreoffice-listener systemctl enable -q --now stirlingpdf systemctl enable -q --now unoserver msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/storybook-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/storybookjs/storybook source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os NODE_VERSION="24" NODE_MODULE="pnpm" setup_nodejs msg_info "Preparing Storybook" mkdir -p /opt/storybook cd /opt/storybook msg_ok "Important: Interactive configuration will start now." npx -y storybook@latest init --yes --no-dev PROJECT_PATH=$(find /opt/storybook -maxdepth 2 -name ".storybook" -type d 2>/dev/null | head -n1 | xargs dirname) if [[ -z "$PROJECT_PATH" ]]; then PROJECT_PATH="/opt/storybook" fi cd "$PROJECT_PATH" echo "$PROJECT_PATH" >/opt/storybook/.projectpath msg_info "Creating Service" cat </etc/systemd/system/storybook.service [Unit] Description=Storybook Dev Server After=network.target [Service] Type=simple User=root WorkingDirectory=${PROJECT_PATH} ExecStart=/usr/bin/npx storybook dev --host 0.0.0.0 --port 6006 --no-open Restart=on-failure RestartSec=5 [Install] WantedBy=multi-user.target EOF systemctl enable -q --now storybook msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/storyteller-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://gitlab.com/storyteller-platform/storyteller source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ build-essential \ git \ pkg-config \ libsqlite3-dev \ sqlite3 \ python3-setuptools \ ffmpeg msg_ok "Installed Dependencies" NODE_VERSION="22" NODE_MODULE="yarn" setup_nodejs fetch_and_deploy_gh_release "readium" "readium/cli" "prebuild" "latest" "/opt/readium" "readium_linux_x86_64.tar.gz" ln -sf /opt/readium/readium /usr/local/bin/readium fetch_and_deploy_gl_release "storyteller" "storyteller-platform/storyteller" "tarball" "latest" "/opt/storyteller" msg_info "Setting up Storyteller" cd /opt/storyteller $STD yarn install --network-timeout 600000 $STD gcc -g -fPIC -rdynamic -shared web/sqlite/uuid.c -o web/sqlite/uuid.c.so STORYTELLER_SECRET_KEY=$(openssl rand -base64 32) cat </opt/storyteller/.env STORYTELLER_SECRET_KEY=${STORYTELLER_SECRET_KEY} STORYTELLER_DATA_DIR=/opt/storyteller/data PORT=8001 HOSTNAME=0.0.0.0 READIUM_PORT=9000 NODE_ENV=production NEXT_TELEMETRY_DISABLED=1 EOF mkdir -p /opt/storyteller/data { echo "Storyteller Credentials" echo "=======================" echo "Secret Key: ${STORYTELLER_SECRET_KEY}" } >~/storyteller.creds msg_ok "Set up Storyteller" msg_info "Building Storyteller" cd /opt/storyteller export CI=1 export NODE_ENV=production export NEXT_TELEMETRY_DISABLED=1 export SQLITE_NATIVE_BINDING=/opt/storyteller/node_modules/better-sqlite3/build/Release/better_sqlite3.node $STD yarn workspaces foreach -Rpt --from @storyteller-platform/web --exclude @storyteller-platform/eslint run build mkdir -p /opt/storyteller/web/.next/standalone/web/.next/static cp -rT /opt/storyteller/web/.next/static /opt/storyteller/web/.next/standalone/web/.next/static if [[ -d /opt/storyteller/web/public ]]; then mkdir -p /opt/storyteller/web/.next/standalone/web/public cp -rT /opt/storyteller/web/public /opt/storyteller/web/.next/standalone/web/public fi mkdir -p /opt/storyteller/web/.next/standalone/web/migrations cp -rT /opt/storyteller/web/migrations /opt/storyteller/web/.next/standalone/web/migrations mkdir -p /opt/storyteller/web/.next/standalone/web/sqlite cp -rT /opt/storyteller/web/sqlite /opt/storyteller/web/.next/standalone/web/sqlite ln -sf /opt/storyteller/.env /opt/storyteller/web/.next/standalone/web/.env msg_ok "Built Storyteller" msg_info "Creating Service" cat </etc/systemd/system/storyteller.service [Unit] Description=Storyteller After=network.target [Service] Type=simple User=root WorkingDirectory=/opt/storyteller/web/.next/standalone/web EnvironmentFile=/opt/storyteller/.env ExecStart=/usr/bin/node --enable-source-maps server.js Restart=on-failure RestartSec=5 [Install] WantedBy=multi-user.target EOF systemctl enable -q --now storyteller msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/strapi-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: pespinel # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://strapi.io/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ build-essential \ python3 \ python3-setuptools \ libvips42 msg_ok "Installed Dependencies" NODE_VERSION="24" setup_nodejs msg_info "Installing Strapi (Patience)" mkdir -p /opt/strapi cd /opt/strapi $STD npx --yes create-strapi-app@latest . --quickstart --no-run --skip-cloud msg_ok "Installed Strapi" msg_info "Building Strapi" cd /opt/strapi export NODE_OPTIONS="--max-old-space-size=3072" $STD npm run build msg_ok "Built Strapi" msg_info "Creating Service" cat </opt/strapi/.env HOST=0.0.0.0 PORT=1337 APP_KEYS=$(openssl rand -base64 32) API_TOKEN_SALT=$(openssl rand -base64 32) ADMIN_JWT_SECRET=$(openssl rand -base64 32) TRANSFER_TOKEN_SALT=$(openssl rand -base64 32) JWT_SECRET=$(openssl rand -base64 32) EOF cat </etc/systemd/system/strapi.service [Unit] Description=Strapi CMS After=network.target [Service] Type=simple WorkingDirectory=/opt/strapi EnvironmentFile=/opt/strapi/.env ExecStart=/usr/bin/npm run start Restart=on-failure Environment=NODE_ENV=production [Install] WantedBy=multi-user.target EOF systemctl enable -q --now strapi msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/streamlink-webui-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: CrazyWolf13 # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/CrazyWolf13/streamlink-webui # Import Functions und Setup source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os NODE_VERSION="22" NODE_MODULE="npm@latest,yarn@latest" setup_nodejs setup_uv fetch_and_deploy_gh_release "streamlink-webui" "CrazyWolf13/streamlink-webui" "tarball" msg_info "Setup ${APPLICATION}" mkdir -p "/opt/${APPLICATION}-download" $STD uv venv --clear /opt/"${APPLICATION}"/backend/src/.venv source /opt/"${APPLICATION}"/backend/src/.venv/bin/activate $STD uv pip install -r /opt/streamlink-webui/backend/src/requirements.txt --python=/opt/"${APPLICATION}"/backend/src/.venv cd /opt/"${APPLICATION}"/frontend/src $STD yarn install $STD yarn build chmod +x /opt/"${APPLICATION}"/start.sh msg_ok "Setup ${APPLICATION}" msg_info "Creating Service" cat <<'EOF' >/opt/"${APPLICATION}".env CLIENT_ID='your_client_id' CLIENT_SECRET='your_client_secret' DOWNLOAD_PATH='/opt/streamlink-webui-download' # BASE_URL='https://sub.domain.com' \ # REVERSE_PROXY=True \ EOF cat </etc/systemd/system/"${APPLICATION}".service [Unit] Description=${APPLICATION} Service After=network.target [Service] EnvironmentFile=/opt/${APPLICATION}.env WorkingDirectory=/opt/${APPLICATION}/backend/src ExecStart=/bin/bash -c 'source /opt/${APPLICATION}/backend/src/.venv/bin/activate && exec /opt/${APPLICATION}/start.sh' Restart=always [Install] WantedBy=multi-user.target EOF systemctl enable -q --now "${APPLICATION}" msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/stylus-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: luismco # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/mmastrac/stylus source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os fetch_and_deploy_gh_release "stylus" "mmastrac/stylus" "singlefile" "latest" "/usr/bin/" "*_linux_arm64" msg_info "Configuring Stylus" $STD stylus init /opt/stylus/ msg_ok "Configured Stylus" msg_info "Creating service" cat </etc/systemd/system/stylus.service [Unit] Description=Stylus Service After=network.target [Service] Type=simple ExecStart=stylus run /opt/stylus/ Restart=on-failure RestartSec=5 [Install] WantedBy=multi-user.target EOF systemctl enable -q --now stylus msg_ok "Created service" motd_ssh customize cleanup_lxc ================================================ FILE: install/sure-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: vhsdream # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://sure.am source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ build-essential \ redis-server \ pkg-config \ libpq-dev \ libvips msg_ok "Installed Dependencies" fetch_and_deploy_gh_release "Sure" "we-promise/sure" "tarball" "latest" "/opt/sure" PG_VERSION="$(sed -n '/postgres:/s/[^[:digit:]]*//p' /opt/sure/compose.example.yml)" setup_postgresql PG_DB_NAME=sure_production PG_DB_USER=sure_user setup_postgresql_db RUBY_VERSION="$(cat /opt/sure/.ruby-version)" RUBY_INSTALL_RAILS=false setup_ruby msg_info "Building Sure" cd /opt/sure export RAILS_ENV=production export BUNDLE_DEPLOYMENT=1 export BUNDLE_WITHOUT=development $STD ./bin/bundle install $STD ./bin/bundle exec bootsnap precompile --gemfile -j 0 $STD ./bin/bundle exec bootsnap precompile -j 0 app/ lib/ export SECRET_KEY_BASE_DUMMY=1 && $STD ./bin/rails assets:precompile unset SECRET_KEY_BASE_DUMMY msg_ok "Built Sure" msg_info "Configuring Sure" KEY="$(openssl rand -hex 64)" mkdir -p /etc/sure mv /opt/sure/.env.example /etc/sure/.env sed -i -e "/^SECRET_KEY_BASE=/s/secret-value/${KEY}/" \ -e 's/_KEY_BASE=.*$/&\n\nRAILS_FORCE_SSL=false \ \ # Change to true when using a reverse proxy \ RAILS_ASSUME_SSL=false/' \ -e "/POSTGRES_PASSWORD=/s/postgres/${PG_DB_PASS}/" \ -e "/POSTGRES_USER=/s/postgres/${PG_DB_USER}\\ POSTGRES_DB=${PG_DB_NAME}/" \ -e "s|^APP_DOMAIN=|&${LOCAL_IP}|" /etc/sure/.env msg_ok "Configured Sure" msg_info "Creating Services" cat </etc/systemd/system/sure.service [Unit] Description=Sure Service After=network.target [Service] Type=simple WorkingDirectory=/opt/sure Environment=RAILS_ENV=production Environment=BUNDLE_DEPLOYMENT=1 Environment=BUNDLE_WITHOUT=development Environment=PATH=/root/.rbenv/shims:/root/.rbenv/bin:/usr/bin:\$PATH EnvironmentFile=/etc/sure/.env ExecStartPre=/opt/sure/bin/rails db:prepare ExecStart=/opt/sure/bin/rails server Restart=always RestartSec=5 StandardOutput=journal StandardError=journal [Install] WantedBy=multi-user.target EOF cat </etc/systemd/system/sure-worker.service [Unit] Description=Sure Background Worker (Sidekiq) After=network.target redis-server.service [Service] Type=simple WorkingDirectory=/opt/sure Environment=RAILS_ENV=production Environment=BUNDLE_DEPLOYMENT=1 Environment=BUNDLE_WITHOUT=development Environment=PATH=/root/.rbenv/shims:/root/.rbenv/bin:/usr/bin:/usr/local/bin:/sbin:/bin EnvironmentFile=/etc/sure/.env ExecStart=/opt/sure/bin/bundle exec sidekiq -e production Restart=always RestartSec=5 StandardOutput=journal StandardError=journal [Install] WantedBy=multi-user.target EOF $STD systemctl enable -q --now sure sure-worker msg_ok "Created Services" motd_ssh customize cleanup_lxc ================================================ FILE: install/swizzin-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: EEJoshua # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://swizzin.ltd/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_warn "WARNING: This script will run an external installer from a third-party source (https://swizzin.ltd/)." msg_warn "The following code is NOT maintained or audited by our repository." msg_warn "If you have any doubts or concerns, please review the installer code before proceeding:" msg_custom "${TAB3}${GATEWAY}${BGN}${CL}" "\e[1;34m" "→ https://s5n.sh" echo read -r -p "${TAB3}Do you want to continue? [y/N]: " CONFIRM if [[ ! "$CONFIRM" =~ ^([yY][eE][sS]|[yY])$ ]]; then msg_error "Aborted by user. No changes have been made." exit 10 fi bash <(curl -fsSL https://s5n.sh) motd_ssh customize cleanup_lxc ================================================ FILE: install/syncthing-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://syncthing.net/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os setup_deb822_repo \ "syncthing" \ "https://syncthing.net/release-key.gpg" \ "https://apt.syncthing.net/" \ "syncthing" \ "stable-v2" msg_info "Setting up Syncthing" $STD apt install -y syncthing systemctl enable -q --now syncthing@root sleep 5 sed -i "{s/127.0.0.1:8384/0.0.0.0:8384/g}" /root/.local/state/syncthing/config.xml systemctl restart syncthing@root msg_ok "Setup Syncthing" motd_ssh customize cleanup_lxc ================================================ FILE: install/tandoor-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (Canbiz) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://tandoor.dev/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies (Patience)" $STD apt install -y \ build-essential \ python3 \ libpq-dev \ libmagic-dev \ libzbar0 \ nginx \ libsasl2-dev \ libldap2-dev \ libssl-dev \ pkg-config \ libxmlsec1-dev \ libxml2-dev \ libxmlsec1-openssl msg_ok "Installed Dependencies" NODE_VERSION="22" NODE_MODULE="yarn" setup_nodejs fetch_and_deploy_gh_release "tandoor" "TandoorRecipes/recipes" "tarball" "latest" "/opt/tandoor" PG_VERSION="17" setup_postgresql PYTHON_VERSION="3.13" setup_uv PG_DB_USER="tandoor" PG_DB_NAME="db_recipes" PG_DB_EXTENSIONS="unaccent,pg_trgm" setup_postgresql_db SECRET_KEY=$(openssl rand -base64 45 | sed 's/\//\\\//g') msg_info "Setup Tandoor" mkdir -p /opt/tandoor/{config,api,mediafiles,staticfiles} cd /opt/tandoor $STD uv venv --clear .venv --python=python3 $STD uv pip install -r requirements.txt --python .venv/bin/python cd /opt/tandoor/vue3 $STD yarn install $STD yarn build cat </opt/tandoor/.env SECRET_KEY=$SECRET_KEY ALLOWED_HOSTS=$LOCAL_IP TZ=Europe/Berlin DB_ENGINE=django.db.backends.postgresql POSTGRES_HOST=localhost POSTGRES_DB=$PG_DB_NAME POSTGRES_PORT=5432 POSTGRES_USER=$PG_DB_USER POSTGRES_PASSWORD=$PG_DB_PASS STATIC_URL=/staticfiles/ MEDIA_URL=/media/ EOF TANDOOR_VERSION=$(get_latest_github_release "TandoorRecipes/recipes") cat </opt/tandoor/cookbook/version_info.py TANDOOR_VERSION = "$TANDOOR_VERSION" TANDOOR_REF = "bare-metal" VERSION_INFO = [] EOF cd /opt/tandoor $STD /opt/tandoor/.venv/bin/python manage.py migrate $STD /opt/tandoor/.venv/bin/python manage.py collectstatic --no-input msg_ok "Installed Tandoor" msg_info "Creating Services" cat </etc/systemd/system/tandoor.service [Unit] Description=gunicorn daemon for tandoor After=network.target [Service] Type=simple Restart=always RestartSec=3 WorkingDirectory=/opt/tandoor EnvironmentFile=/opt/tandoor/.env ExecStart=/opt/tandoor/.venv/bin/gunicorn --error-logfile /tmp/gunicorn_err.log --log-level debug --capture-output --bind unix:/opt/tandoor/tandoor.sock recipes.wsgi:application [Install] WantedBy=multi-user.target EOF cat <<'EOF' >/etc/nginx/conf.d/tandoor.conf server { listen 8002; access_log /var/log/nginx/access.log; error_log /var/log/nginx/error.log; client_max_body_size 128M; # serve media files location /static/ { alias /opt/tandoor/staticfiles/; } location /media/ { alias /opt/tandoor/mediafiles/; } location / { proxy_set_header Host $http_host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto; proxy_pass http://unix:/opt/tandoor/tandoor.sock; } } EOF systemctl reload nginx systemctl enable -q --now tandoor msg_ok "Created Services" motd_ssh customize cleanup_lxc ================================================ FILE: install/tasmoadmin-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/TasmoAdmin/TasmoAdmin source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y git msg_ok "Installed Dependencies" PHP_VERSION="8.4" PHP_APACHE="YES" setup_php fetch_and_deploy_gh_release "tasmoadmin" "TasmoAdmin/TasmoAdmin" "prebuild" "latest" "/var/www/tasmoadmin" "tasmoadmin_v*.tar.gz" msg_info "Configuring TasmoAdmin" rm -rf /etc/php/8.4/apache2/conf.d/10-opcache.ini chown -R www-data:www-data /var/www/tasmoadmin chmod 775 /var/www/tasmoadmin/tmp /var/www/tasmoadmin/data cat </etc/apache2/sites-available/tasmoadmin.conf ServerName tasmoadmin ServerAdmin webmaster@localhost DocumentRoot /var/www/tasmoadmin AllowOverride All Order allow,deny allow from all ErrorLog /var/log/apache2/error.log LogLevel warn CustomLog /var/log/apache2/access.log combined ServerSignature On EOF sed -i '6iListen 9999' /etc/apache2/ports.conf $STD a2ensite tasmoadmin $STD a2enmod rewrite systemctl reload apache2 systemctl restart apache2 msg_ok "Configured TasmoAdmin" motd_ssh customize cleanup_lxc ================================================ FILE: install/tasmocompiler-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: Slaviša Arežina (tremor021) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/benzino77/tasmocompiler source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies. Patience" $STD apt-get install -y \ git \ python3-venv msg_ok "Installed Dependencies" NODE_VERSION="22" NODE_MODULE="yarn@latest" setup_nodejs msg_info "Setup Platformio" curl -fsSL -o get-platformio.py https://raw.githubusercontent.com/platformio/platformio-core-installer/master/get-platformio.py $STD python3 get-platformio.py msg_ok "Setup Platformio" msg_info "Setup TasmoCompiler" mkdir /tmp/Tasmota RELEASE=$(curl -fsSL https://api.github.com/repos/benzino77/tasmocompiler/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4) }') curl -fsSL "https://github.com/benzino77/tasmocompiler/archive/refs/tags/v${RELEASE}.tar.gz" -o "/tmp/v${RELEASE}.tar.gz" cd /tmp tar xzf /tmp/v${RELEASE}.tar.gz mv tasmocompiler-${RELEASE}/ /opt/tasmocompiler/ cd /opt/tasmocompiler $STD yarn install export NODE_OPTIONS=--openssl-legacy-provider $STD npm i $STD yarn build mkdir -p /usr/local/bin ln -s ~/.platformio/penv/bin/platformio /usr/local/bin/platformio ln -s ~/.platformio/penv/bin/pio /usr/local/bin/pio ln -s ~/.platformio/penv/bin/piodebuggdb /usr/local/bin/piodebuggdb rm -f /tmp/v${RELEASE}.tar.gz echo "${RELEASE}" >"/opt/tasmocompiler_version.txt" msg_ok "Setup TasmoCompiler" msg_info "Creating Service" cat </etc/systemd/system/tasmocompiler.service [Unit] Description=TasmoCompiler Service After=multi-user.target [Service] Type=simple User=root ExecStart=/usr/bin/node /opt/tasmocompiler/server/app.js [Install] WantedBy=multi-user.target EOF systemctl enable -q --now tasmocompiler msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/tautulli-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://tautulli.com/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y git msg_ok "Installed Dependencies" PYTHON_VERSION="3.13" setup_uv fetch_and_deploy_gh_release "Tautulli" "Tautulli/Tautulli" "tarball" msg_info "Installing Tautulli" cd /opt/Tautulli TAUTULLI_VERSION=$(get_latest_github_release "Tautulli/Tautulli" "false") echo "${TAUTULLI_VERSION}" >/opt/Tautulli/version.txt echo "master" >/opt/Tautulli/branch.txt $STD uv venv --clear $STD source /opt/Tautulli/.venv/bin/activate $STD uv pip install -r requirements.txt $STD uv pip install pyopenssl $STD uv pip install "setuptools<81" msg_ok "Installed Tautulli" msg_info "Creating Service" cat </etc/systemd/system/tautulli.service [Unit] Description=Tautulli After=syslog.target network.target [Service] WorkingDirectory=/opt/Tautulli/ Restart=on-failure RestartSec=5 Type=simple ExecStart=/opt/Tautulli/.venv/bin/python3 /opt/Tautulli/Tautulli.py KillSignal=SIGINT TimeoutStopSec=20 SyslogIdentifier=tautulli [Install] WantedBy=multi-user.target EOF systemctl enable -q --now tautulli msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/tdarr-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://home.tdarr.io/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y handbrake-cli msg_ok "Installed Dependencies" msg_info "Installing Tdarr" mkdir -p /opt/tdarr cd /opt/tdarr RELEASE=$(curl -fsSL https://f000.backblazeb2.com/file/tdarrs/versions.json | grep -oP '(?<="Tdarr_Updater": ")[^"]+' | grep linux_arm64 | head -n 1) curl -fsSL "$RELEASE" -o Tdarr_Updater.zip $STD unzip Tdarr_Updater.zip chmod +x Tdarr_Updater $STD ./Tdarr_Updater rm -rf /opt/tdarr/Tdarr_Updater.zip [[ -f /opt/tdarr/Tdarr_Server/Tdarr_Server ]] || { msg_error "Tdarr_Updater failed — tdarr.io may be blocked by local DNS" exit 250 } msg_ok "Installed Tdarr" setup_hwaccel msg_info "Creating Service" cat </etc/systemd/system/tdarr-server.service [Unit] Description=Tdarr Server Daemon After=network.target # Enable if using ZFS, edit and enable if other FS mounting is required to access directory #Requires=zfs-mount.service [Service] User=root Group=root Type=simple WorkingDirectory=/opt/tdarr/Tdarr_Server ExecStartPre=/opt/tdarr/Tdarr_Updater ExecStart=/opt/tdarr/Tdarr_Server/Tdarr_Server TimeoutStopSec=20 KillMode=process Restart=on-failure [Install] WantedBy=multi-user.target EOF cat </etc/systemd/system/tdarr-node.service [Unit] Description=Tdarr Node Daemon After=network.target Requires=tdarr-server.service [Service] User=root Group=root Type=simple WorkingDirectory=/opt/tdarr/Tdarr_Node ExecStart=/opt/tdarr/Tdarr_Node/Tdarr_Node TimeoutStopSec=20 KillMode=process Restart=on-failure [Install] WantedBy=multi-user.target EOF systemctl enable --now -q tdarr-server tdarr-node msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/teable-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/teableio/teable source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ build-essential \ python3 \ git msg_ok "Installed Dependencies" NODE_VERSION="24" NODE_MODULE="pnpm" setup_nodejs PG_VERSION="16" setup_postgresql PG_DB_NAME="teable" PG_DB_USER="teable" setup_postgresql_db fetch_and_deploy_gh_release "teable" "teableio/teable" "tarball" msg_info "Setting up Teable" cd /opt/teable TEABLE_VERSION=$(cat ~/.teable) echo "NEXT_PUBLIC_BUILD_VERSION=\"${TEABLE_VERSION}\"" >>apps/nextjs-app/.env export HUSKY=0 export NODE_OPTIONS="--max-old-space-size=8192" $STD pnpm install --frozen-lockfile $STD pnpm -F @teable/db-main-prisma prisma-generate --schema ./prisma/postgres/schema.prisma msg_ok "Set up Teable" msg_info "Building Teable" NODE_ENV=production NEXT_BUILD_ENV_TYPECHECK=false \ $STD pnpm -r --filter '!playground' run build msg_ok "Built Teable" msg_info "Running Database Migrations" PRISMA_DATABASE_URL="postgresql://teable:${PG_DB_PASS}@localhost:5432/teable?schema=public" \ $STD pnpm -F @teable/db-main-prisma prisma-migrate deploy --schema ./prisma/postgres/schema.prisma msg_ok "Ran Database Migrations" msg_info "Configuring Teable" mkdir -p /opt/teable/.assets /opt/teable/.temporary SECRET_KEY=$(openssl rand -base64 32) cat </opt/teable/.env PRISMA_DATABASE_URL=postgresql://teable:${PG_DB_PASS}@localhost:5432/teable?schema=public&statement_cache_size=1 PUBLIC_ORIGIN=http://${LOCAL_IP}:3000 SECRET_KEY=${SECRET_KEY} PORT=3000 NODE_ENV=production NEXT_TELEMETRY_DISABLED=1 BACKEND_CACHE_PROVIDER=sqlite BACKEND_CACHE_SQLITE_URI=sqlite:///opt/teable/.assets/.cache.db NEXTJS_DIR=apps/nextjs-app EOF ln -sf /opt/teable /app rm -rf /opt/teable/static if [ -d "/opt/teable/apps/nestjs-backend/static/static" ]; then ln -sf /opt/teable/apps/nestjs-backend/static/static /opt/teable/static else ln -sf /opt/teable/apps/nestjs-backend/static /opt/teable/static fi msg_ok "Configured Teable" msg_info "Creating Service" cat </etc/systemd/system/teable.service [Unit] Description=Teable After=network.target postgresql.service [Service] Type=simple WorkingDirectory=/opt/teable EnvironmentFile=/opt/teable/.env ExecStart=/usr/bin/node apps/nestjs-backend/dist/index.js Restart=on-failure RestartSec=5 [Install] WantedBy=multi-user.target EOF systemctl enable -q --now teable msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/teamspeak-server-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: tremor021 (Slaviša Arežina) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://teamspeak.com/en/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os RELEASE=$(curl -fsSL https://teamspeak.com/en/downloads/#server | grep -oP 'teamspeak3-server_linux_arm64-\K[0-9]+\.[0-9]+\.[0-9]+' | head -1) msg_info "Setting up Teamspeak Server" curl -fsSL "https://files.teamspeak-services.com/releases/server/${RELEASE}/teamspeak3-server_linux_arm64-${RELEASE}.tar.bz2" -o ts3server.tar.bz2 tar -xf ./ts3server.tar.bz2 mv teamspeak3-server_linux_arm64/ /opt/teamspeak-server/ touch /opt/teamspeak-server/.ts3server_license_accepted rm -f ~/ts3server.tar.bz* echo "${RELEASE}" >~/.teamspeak-server msg_ok "Setup Teamspeak Server" msg_info "Creating service" cat </etc/systemd/system/teamspeak-server.service [Unit] Description=TeamSpeak3 Server Wants=network-online.target After=network.target [Service] WorkingDirectory=/opt/teamspeak-server User=root Type=forking ExecStart=/opt/teamspeak-server/ts3server_startscript.sh start ExecStop=/opt/teamspeak-server/ts3server_startscript.sh stop ExecReload=/opt/teamspeak-server/ts3server_startscript.sh restart Restart=always RestartSec=15 [Install] WantedBy=multi-user.target EOF systemctl enable -q --now teamspeak-server msg_ok "Created service" motd_ssh customize cleanup_lxc ================================================ FILE: install/technitiumdns-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://technitium.com/dns/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing ASP.NET Core Runtime" curl -SL -o dotnet.tar.gz https://builds.dotnet.microsoft.com/dotnet/Sdk/9.0.306/dotnet-sdk-9.0.306-linux-arm64.tar.gz curl -SL -o aspnet.tar.gz https://builds.dotnet.microsoft.com/dotnet/aspnetcore/Runtime/9.0.10/aspnetcore-runtime-9.0.10-linux-arm64.tar.gz $STD mkdir -p /usr/share/dotnet $STD tar -zxf dotnet.tar.gz -C /usr/share/dotnet $STD tar -zxf aspnet.tar.gz -C /usr/share/dotnet ln -s /usr/share/dotnet/dotnet /usr/bin/dotnet msg_ok "Installed ASP.NET Core Runtime" RELEASE=$(curl -fsSL https://technitium.com/dns/ | grep -oP 'Version \K[\d.]+') fetch_and_deploy_from_url "https://download.technitium.com/dns/DnsServerPortable.tar.gz" /opt/technitium/dns echo "${RELEASE}" >~/.technitium msg_info "Creating service" mkdir -p /etc/dns /var/log/technitium/dns sed -i '/^User=/d;/^Group=/d' /opt/technitium/dns/systemd.service cp /opt/technitium/dns/systemd.service /etc/systemd/system/technitium.service systemctl enable -q --now technitium msg_ok "Service created" motd_ssh customize cleanup_lxc ================================================ FILE: install/teddycloud-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: Dominik Siebel (dsiebel) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/toniebox-reverse-engineering/teddycloud source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ libubsan1 \ ffmpeg \ curl \ ca-certificates \ wget \ openssh-server msg_ok "Installed Dependencies" fetch_and_deploy_gh_release "teddycloud" "toniebox-reverse-engineering/teddycloud" "prebuild" "latest" "/opt/teddycloud" "teddycloud.arm64.release*.zip" msg_info "Creating Service" cat </etc/systemd/system/teddycloud.service [Unit] Description=TeddyCloud Server After=network.target [Service] User=root Type=simple ExecStart=/opt/teddycloud/teddycloud WorkingDirectory=/opt/teddycloud Restart=on-failure [Install] WantedBy=multi-user.target EOF systemctl enable --now -q teddycloud msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/telegraf-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: CrazyWolf13 # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/influxdata/telegraf source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Setting up Telegraf repository" setup_deb822_repo \ "telegraf" \ "https://repos.influxdata.com/influxdata-archive.key" \ "https://repos.influxdata.com/debian" \ "stable" msg_ok "Setup Telegraf Repository" msg_info "Setting up Telegraf" $STD apt install -y telegraf msg_ok "Setup Telegraf" motd_ssh customize cleanup_lxc ================================================ FILE: install/teleport-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: Slaviša Arežina (tremor021) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://goteleport.com/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os setup_deb822_repo \ "teleport" \ "https://deb.releases.teleport.dev/teleport-pubkey.asc" \ "https://apt.releases.teleport.dev/debian" \ "trixie" \ "stable/v18" msg_info "Configuring Teleport" $STD apt install -y teleport $STD teleport configure -o /etc/teleport.yaml systemctl enable -q --now teleport sleep 10 tctl users add teleport-admin --roles=editor,access --logins=root >~/teleportadmin.txt sed -i "s|https://[^:]*:3080|https://${LOCAL_IP}:3080|g" ~/teleportadmin.txt msg_ok "Configured Teleport" motd_ssh customize cleanup_lxc ================================================ FILE: install/termix-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/Termix-SSH/Termix source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ build-essential \ python3 \ nginx \ openssl \ gettext-base \ libcairo2-dev \ libjpeg62-turbo-dev \ libpng-dev \ libtool-bin \ uuid-dev \ libvncserver-dev \ freerdp3-dev \ libssh2-1-dev \ libtelnet-dev \ libwebsockets-dev \ libpulse-dev \ libvorbis-dev \ libwebp-dev \ libssl-dev \ libpango1.0-dev \ libswscale-dev \ libavcodec-dev \ libavutil-dev \ libavformat-dev msg_ok "Installed Dependencies" msg_info "Building Guacamole Server (guacd)" fetch_and_deploy_gh_tag "guacd" "apache/guacamole-server" "latest" "/opt/guacamole-server" cd /opt/guacamole-server export CPPFLAGS="-Wno-error=deprecated-declarations" $STD autoreconf -fi $STD ./configure --with-init-dir=/etc/init.d --enable-allow-freerdp-snapshots $STD make $STD make install $STD ldconfig cd /opt rm -rf /opt/guacamole-server msg_ok "Built Guacamole Server (guacd)" NODE_VERSION="22" setup_nodejs fetch_and_deploy_gh_release "termix" "Termix-SSH/Termix" "tarball" msg_info "Building Frontend" cd /opt/termix export COREPACK_ENABLE_DOWNLOAD_PROMPT=0 find public/fonts -name "*.ttf" ! -name "*Regular.ttf" ! -name "*Bold.ttf" ! -name "*Italic.ttf" -delete 2>/dev/null || true $STD npm install --ignore-scripts --force $STD npm cache clean --force $STD npm run build msg_ok "Built Frontend" msg_info "Building Backend" $STD npm rebuild better-sqlite3 --force $STD npm run build:backend msg_ok "Built Backend" msg_info "Setting up Node Dependencies" cd /opt/termix $STD npm ci --only=production --ignore-scripts --force $STD npm rebuild better-sqlite3 bcryptjs --force $STD npm cache clean --force msg_ok "Set up Node Dependencies" msg_info "Setting up Directories" mkdir -p /opt/termix/data \ /opt/termix/uploads \ /opt/termix/html \ /opt/termix/nginx \ /opt/termix/nginx/logs \ /opt/termix/nginx/cache \ /opt/termix/nginx/client_body cp -r /opt/termix/dist/* /opt/termix/html/ 2>/dev/null || true cp -r /opt/termix/src/locales /opt/termix/html/locales 2>/dev/null || true cp -r /opt/termix/public/fonts /opt/termix/html/fonts 2>/dev/null || true msg_ok "Set up Directories" msg_info "Configuring Nginx" curl -fsSL "https://raw.githubusercontent.com/Termix-SSH/Termix/main/docker/nginx.conf" -o /etc/nginx/nginx.conf sed -i '/^master_process/d' /etc/nginx/nginx.conf sed -i '/^pid \/app\/nginx/d' /etc/nginx/nginx.conf sed -i 's|/app/html|/opt/termix/html|g' /etc/nginx/nginx.conf sed -i 's|/app/nginx|/opt/termix/nginx|g' /etc/nginx/nginx.conf sed -i 's|listen ${PORT};|listen 80;|g' /etc/nginx/nginx.conf mkdir -p /tmp/nginx echo "d /tmp/nginx 0755 nobody nobody -" > /etc/tmpfiles.d/nginx-termix.conf mkdir -p /etc/systemd/system/nginx.service.d/ cat > /etc/systemd/system/nginx.service.d/pidfile.conf << EOF [Service] PIDFile=/tmp/nginx/nginx.pid EOF systemctl daemon-reload rm -f /etc/nginx/sites-enabled/default nginx -t systemctl reload nginx msg_ok "Configured Nginx" msg_info "Creating Service" mkdir -p /etc/guacamole cat </etc/guacamole/guacd.conf [server] bind_host = 127.0.0.1 bind_port = 4822 EOF cat </opt/termix/.env NODE_ENV=production DATA_DIR=/opt/termix/data GUACD_HOST=127.0.0.1 GUACD_PORT=4822 EOF cat </etc/systemd/system/guacd.service [Unit] Description=Guacamole Proxy Daemon (guacd) After=network.target [Service] Type=simple ExecStart=/usr/local/sbin/guacd -f -b 127.0.0.1 -l 4822 Restart=on-failure RestartSec=5 [Install] WantedBy=multi-user.target EOF cat </etc/systemd/system/termix.service [Unit] Description=Termix Backend After=network.target guacd.service Wants=guacd.service [Service] Type=simple User=root WorkingDirectory=/opt/termix EnvironmentFile=/opt/termix/.env ExecStart=/usr/bin/node /opt/termix/dist/backend/backend/starter.js Restart=on-failure RestartSec=5 [Install] WantedBy=multi-user.target EOF systemctl enable -q --now guacd termix msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/the-lounge-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: kristocopani # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://thelounge.chat/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os fetch_and_deploy_gh_release "thelounge" "thelounge/thelounge-deb" "binary" systemctl enable -q --now thelounge motd_ssh customize cleanup_lxc ================================================ FILE: install/thingsboard-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/thingsboard/thingsboard source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ libharfbuzz0b \ fontconfig \ fonts-dejavu-core msg_ok "Installed Dependencies" JAVA_VERSION="17" setup_java PG_VERSION="16" setup_postgresql PG_DB_NAME="thingsboard_db" PG_DB_USER="thingsboard" setup_postgresql_db fetch_and_deploy_gh_release "thingsboard" "thingsboard/thingsboard" "binary" "latest" "/tmp" "thingsboard-*.deb" msg_info "Configuring ThingsBoard" cat </etc/thingsboard/conf/thingsboard.conf # DB Configuration export DATABASE_TS_TYPE=sql export SPRING_DATASOURCE_URL=jdbc:postgresql://localhost:5432/${PG_DB_NAME} export SPRING_DATASOURCE_USERNAME=${PG_DB_USER} export SPRING_DATASOURCE_PASSWORD=${PG_DB_PASS} # Specify partitioning size for timestamp key-value storage. Allowed values: DAYS, MONTHS, YEARS, INDEFINITE. export SQL_POSTGRES_TS_KV_PARTITIONING=MONTHS EOF systemctl daemon-reload msg_ok "Configured ThingsBoard" msg_info "Running ThingsBoard Installation Script" $STD /usr/share/thingsboard/bin/install/install.sh --loadDemo msg_ok "Ran Installation Script" msg_info "Starting ThingsBoard Service" systemctl enable -q --now thingsboard msg_ok "Started ThingsBoard Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/threadfin-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/Threadfin/Threadfin source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os setup_hwaccel msg_info "Installing Dependencies" $STD apt install -y \ ffmpeg \ vlc msg_ok "Installed Dependencies" fetch_and_deploy_gh_release "threadfin" "threadfin/threadfin" "singlefile" "latest" "/opt/threadfin" "Threadfin_linux_arm64" mv /root/.threadfin /root/.threadfin_version mkdir -p /root/.threadfin msg_info "Creating Service" cat </etc/systemd/system/threadfin.service [Unit] Description=Threadfin: M3U Proxy for Plex DVR and Emby/Jellyfin Live TV After=syslog.target network.target [Service] Type=simple WorkingDirectory=/opt/threadfin ExecStart=/opt/threadfin/threadfin TimeoutStopSec=20 KillMode=process Restart=on-failure [Install] WantedBy=multi-user.target EOF systemctl enable -q --now threadfin msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/tianji-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck # Co-Author: MickLesk (Canbiz) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/msgbyte/tianji source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ python3 \ cmake \ build-essential \ git msg_ok "Installed Dependencies" NODE_VERSION="22" NODE_MODULE="pnpm@$(curl -s https://raw.githubusercontent.com/msgbyte/tianji/master/package.json | jq -r '.packageManager | split("@")[1]')" setup_nodejs PG_VERSION="17" setup_postgresql PG_DB_NAME="tianji_db" PG_DB_USER="tianji" setup_postgresql_db PYTHON_VERSION="3.13" setup_uv fetch_and_deploy_gh_release "tianji" "msgbyte/tianji" "tarball" TIANJI_SECRET=$(openssl rand -base64 256 | tr -dc 'A-Za-z' | head -c 64) echo "Tianji Secret: $TIANJI_SECRET" >>~/tianji.creds msg_info "Setting up Tianji" cd /opt/tianji $STD pnpm install --filter @tianji/client... --config.dedupe-peer-dependents=false --frozen-lockfile $STD pnpm build:static $STD pnpm install --filter @tianji/server... --config.dedupe-peer-dependents=false mkdir -p ./src/server/public cp -r ./geo ./src/server/public $STD pnpm build:server cat </opt/tianji/src/server/.env DATABASE_URL="postgresql://$PG_DB_USER:$PG_DB_PASS@localhost:5432/$PG_DB_NAME?schema=public" OPENAI_API_KEY="" JWT_SECRET="$TIANJI_SECRET" EOF cd /opt/tianji/src/server $STD pnpm db:migrate:apply rm -rf /opt/tianji/src/client rm -rf /opt/tianji/website rm -rf /opt/tianji/reporter msg_ok "Setup Tianji" msg_info "Setup AppRise" rm -rf /usr/lib/python3.*/EXTERNALLY-MANAGED $STD uv pip install apprise cryptography --system msg_ok "Setup AppRise" msg_info "Creating Service" cat </etc/systemd/system/tianji.service [Unit] Description=Tianji Server After=network.target [Service] ExecStart=/usr/bin/node /opt/tianji/src/server/dist/src/server/main.js WorkingDirectory=/opt/tianji/src/server Restart=always RestartSec=10 Environment=NODE_ENV=production [Install] WantedBy=multi-user.target EOF systemctl enable -q --now tianji msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/tinyauth-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/steveiliop56/tinyauth source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ openssl \ apache2-utils msg_ok "Installed Dependencies" fetch_and_deploy_gh_release "tinyauth" "steveiliop56/tinyauth" "singlefile" "latest" "/opt/tinyauth" "tinyauth-amd64" msg_info "Setting up Tinyauth" PASS=$(openssl rand -base64 8 | tr -dc 'a-zA-Z0-9' | head -c 8) USER=$(htpasswd -Bbn "tinyauth" "${PASS}") cat </opt/tinyauth/credentials.txt Tinyauth Credentials Username: tinyauth Password: ${PASS} EOF msg_ok "Set up Tinyauth" read -r -p "${TAB3}Enter your Tinyauth subdomain (e.g. https://tinyauth.example.com): " app_url msg_info "Creating Service" cat </opt/tinyauth/.env TINYAUTH_DATABASE_PATH=/opt/tinyauth/database.db TINYAUTH_AUTH_USERS='${USER}' TINYAUTH_APPURL=${app_url} EOF cat </etc/systemd/system/tinyauth.service [Unit] Description=Tinyauth Service After=network.target [Service] Type=simple EnvironmentFile=/opt/tinyauth/.env ExecStart=/opt/tinyauth/tinyauth WorkingDirectory=/opt/tinyauth Restart=on-failure [Install] WantedBy=multi-user.target EOF systemctl enable -q --now tinyauth msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/traccar-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.traccar.org/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os fetch_and_deploy_gh_release "traccar" "traccar/traccar" "prebuild" "latest" "/opt/traccar" "traccar-linux-arm*.zip" msg_info "Configuring Traccar" cd /opt/traccar $STD ./traccar.run [ -f README.txt ] || [ -f traccar.run ] && rm -f README.txt traccar.run msg_ok "Configured Traccar" msg_info "Starting service" systemctl enable -q --now traccar msg_ok "Service started" motd_ssh customize cleanup_lxc ================================================ FILE: install/tracearr-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2025 community-scripts ORG # Author: durzo # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/connorgallopo/Tracearr source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y redis-server msg_ok "Installed Dependencies" NODE_VERSION="24" setup_nodejs PG_VERSION="18" setup_postgresql msg_info "Installing pnpm" PNPM_VERSION="$(curl -fsSL "https://raw.githubusercontent.com/connorgallopo/Tracearr/refs/heads/main/package.json" | jq -r '.packageManager | split("@")[1]' | cut -d'+' -f1)" export COREPACK_ENABLE_DOWNLOAD_PROMPT=0 $STD corepack enable pnpm $STD corepack prepare pnpm@${PNPM_VERSION} --activate msg_ok "Installed pnpm" msg_info "Installing TimescaleDB" setup_deb822_repo \ "timescaledb" \ "https://packagecloud.io/timescale/timescaledb/gpgkey" \ "https://packagecloud.io/timescale/timescaledb/debian" \ "$(get_os_info codename)" $STD apt install -y \ timescaledb-2-postgresql-18 \ timescaledb-tools \ timescaledb-toolkit-postgresql-18 total_ram_kb=$(grep MemTotal /proc/meminfo | awk '{print $2}') ram_for_tsdb=$((total_ram_kb / 1024 / 2)) $STD timescaledb-tune -yes -memory "$ram_for_tsdb"MB $STD systemctl restart postgresql msg_ok "Installed TimescaleDB" PG_DB_NAME="tracearr_db" PG_DB_USER="tracearr" PG_DB_EXTENSIONS="timescaledb,timescaledb_toolkit" PG_DB_GRANT_SUPERUSER="true" setup_postgresql_db msg_info "Installing tailscale" setup_deb822_repo \ "tailscale" \ "https://pkgs.tailscale.com/stable/$(get_os_info id)/$(get_os_info codename).noarmor.gpg" \ "https://pkgs.tailscale.com/stable/$(get_os_info id)/" \ "$(get_os_info codename)" $STD apt install -y tailscale # Tracearr runs tailscaled in user mode, disable the service. $STD systemctl disable --now tailscaled $STD systemctl stop tailscaled msg_ok "Installed tailscale" fetch_and_deploy_gh_release "tracearr" "connorgallopo/Tracearr" "tarball" "latest" "/opt/tracearr.build" msg_info "Building Tracearr" export TZ=$(cat /etc/timezone) export NODE_OPTIONS="--max-old-space-size=4096" cd /opt/tracearr.build $STD pnpm install --frozen-lockfile --force $STD pnpm turbo telemetry disable $STD pnpm turbo run build --no-daemon --filter=@tracearr/shared --filter=@tracearr/server --filter=@tracearr/web mkdir -p /opt/tracearr/{packages/shared,apps/server,apps/web,apps/server/src/db} cp -rf package.json /opt/tracearr/ cp -rf pnpm-workspace.yaml /opt/tracearr/ cp -rf pnpm-lock.yaml /opt/tracearr/ cp -rf apps/server/package.json /opt/tracearr/apps/server/ cp -rf apps/server/dist /opt/tracearr/apps/server/dist cp -rf apps/server/scripts /opt/tracearr/apps/server/scripts cp -rf apps/web/dist /opt/tracearr/apps/web/dist cp -rf packages/shared/package.json /opt/tracearr/packages/shared/ cp -rf packages/shared/dist /opt/tracearr/packages/shared/dist cp -rf apps/server/src/db/migrations /opt/tracearr/apps/server/src/db/migrations cp -rf data /opt/tracearr/data mkdir -p /opt/tracearr/data/image-cache rm -rf /opt/tracearr.build cd /opt/tracearr $STD pnpm install --prod --frozen-lockfile --ignore-scripts msg_ok "Built Tracearr" msg_info "Configuring Tracearr" $STD useradd -r -s /bin/false -U tracearr $STD chown -R tracearr:tracearr /opt/tracearr install -d -m 750 -o tracearr -g tracearr /data/tracearr install -d -m 750 -o tracearr -g tracearr /data/backup export JWT_SECRET=$(openssl rand -hex 32) export COOKIE_SECRET=$(openssl rand -hex 32) cat </data/tracearr/.env DATABASE_URL=postgresql://${PG_DB_USER}:${PG_DB_PASS}@127.0.0.1:5432/${PG_DB_NAME} REDIS_URL=redis://127.0.0.1:6379 PORT=3000 HOST=0.0.0.0 NODE_ENV=production TZ=${TZ} LOG_LEVEL=info JWT_SECRET=$JWT_SECRET COOKIE_SECRET=$COOKIE_SECRET APP_VERSION=$(cat /root/.tracearr) #CORS_ORIGIN=http://localhost:5173 EOF chmod 600 /data/tracearr/.env chown -R tracearr:tracearr /data/tracearr msg_ok "Configured Tracearr" msg_info "Creating Services" cat </data/tracearr/prestart.sh #!/usr/bin/env bash # ============================================================================= # Tune PostgreSQL for available resources (runs every startup) # ============================================================================= # timescaledb-tune automatically optimizes PostgreSQL settings based on # available RAM and CPU. Safe to run repeatedly - recalculates if resources change. if command -v timescaledb-tune &> /dev/null; then total_ram_kb=\$(grep MemTotal /proc/meminfo | awk '{print \$2}') ram_for_tsdb=\$((total_ram_kb / 1024 / 2)) timescaledb-tune -yes -memory "\$ram_for_tsdb"MB --quiet 2>/dev/null \ || echo "Warning: timescaledb-tune failed (non-fatal)" fi # ============================================================================= # Ensure required PostgreSQL settings for Tracearr # ============================================================================= pg_config_file="/etc/postgresql/18/main/postgresql.conf" if [ -f \$pg_config_file ]; then # Ensure max_tuples_decompressed_per_dml_transaction is set if grep -q "^timescaledb\.max_tuples_decompressed_per_dml_transaction" \$pg_config_file; then # Setting exists (uncommented) - update if not 0 current_value=\$(grep "^timescaledb\.max_tuples_decompressed_per_dml_transaction" \$pg_config_file | grep -oE '[0-9]+' | head -1) if [ -n "\$current_value" ] && [ "\$current_value" -ne 0 ]; then sed -i "s/^timescaledb\.max_tuples_decompressed_per_dml_transaction.*/timescaledb.max_tuples_decompressed_per_dml_transaction = 0/" \$pg_config_file fi elif ! grep -q "^timescaledb\.max_tuples_decompressed_per_dml_transaction" \$pg_config_file; then echo "" >> \$pg_config_file echo "# Allow unlimited tuple decompression for migrations on compressed hypertables" >> \$pg_config_file echo "timescaledb.max_tuples_decompressed_per_dml_transaction = 0" >> \$pg_config_file fi # Ensure max_locks_per_transaction is set (for existing databases) if grep -q "^max_locks_per_transaction" \$pg_config_file; then # Setting exists (uncommented) - update if below 4096 current_value=\$(grep "^max_locks_per_transaction" \$pg_config_file | grep -oE '[0-9]+' | head -1) if [ -n "\$current_value" ] && [ "\$current_value" -lt 4096 ]; then sed -i "s/^max_locks_per_transaction.*/max_locks_per_transaction = 4096/" \$pg_config_file fi elif ! grep -q "^max_locks_per_transaction" \$pg_config_file; then echo "" >> \$pg_config_file echo "# Increase lock table size for TimescaleDB hypertables with many chunks" >> \$pg_config_file echo "max_locks_per_transaction = 4096" >> \$pg_config_file fi fi systemctl restart postgresql sudo -u postgres psql -c "ALTER USER tracearr WITH SUPERUSER;" EOF chmod +x /data/tracearr/prestart.sh cat </lib/systemd/system/tracearr.service [Unit] Description=Tracearr Web Server After=network.target postgresql.service redis-server.service [Service] Type=simple KillMode=control-group EnvironmentFile=/data/tracearr/.env WorkingDirectory=/opt/tracearr ExecStartPre=+/data/tracearr/prestart.sh ExecStart=node /opt/tracearr/apps/server/dist/index.js Restart=on-failure RestartSec=10 User=tracearr [Install] WantedBy=multi-user.target EOF systemctl enable -q --now postgresql redis-server tracearr msg_ok "Created Services" motd_ssh customize cleanup_lxc ================================================ FILE: install/tracktor-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: CrazyWolf13 # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://tracktor.bytedge.in source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os NODE_VERSION="24" setup_nodejs fetch_and_deploy_gh_release "tracktor" "javedh-dev/tracktor" "tarball" "latest" "/opt/tracktor" msg_info "Configuring Tracktor" cd /opt/tracktor $STD npm install $STD npm run build mkdir -p /opt/tracktor-data/{uploads,logs} cat </opt/tracktor.env NODE_ENV=production # Set this to the path of the database file. Default - ./tracktor.db DB_PATH=/opt/tracktor-data/tracktor.db # Set this to the path of the uploads directory. Default - ./uploads UPLOADS_DIR="/opt/tracktor-data/uploads" # Set this to the path of the logs directory. Default - ./logs LOG_DIR="/opt/tracktor-data/logs" # Hostname to bind the server to. Default - 0.0.0.0 #HOST="0.0.0.0" # Port to bind the server to. Default - 3000 #PORT=3000 # Set this to remove upload size limitations. Default - 512 Kb BODY_SIZE_LIMIT=Infinity # Enable request logging. Default - true #LOG_REQUESTS=true # Set the logging level. Options - error, warn, info, verbose, debug, silly. Default - info #LOG_LEVEL="info" # Enable demo mode. Default - false #TRACKTOR_DEMO_MODE=false # Force reseeding of data on every startup. Default - false #FORCE_DATA_SEED=false EOF msg_ok "Configured Tracktor" msg_info "Creating service" cat </etc/systemd/system/tracktor.service [Unit] Description=Tracktor Service After=network.target [Service] Type=simple WorkingDirectory=/opt/tracktor EnvironmentFile=/opt/tracktor.env ExecStart=/usr/bin/npm start [Install] WantedBy=multi-user.target EOF systemctl enable -q --now tracktor msg_ok "Created service" motd_ssh customize cleanup_lxc ================================================ FILE: install/traefik-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://traefik.io/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y apt-transport-https msg_ok "Installed Dependencies" fetch_and_deploy_gh_release "traefik" "traefik/traefik" "prebuild" "latest" "/usr/bin" "traefik_v*_linux_arm64.tar.gz" mkdir -p /etc/traefik/{conf.d,ssl} msg_info "Creating Traefik configuration" cat </etc/traefik/traefik.yaml providers: file: directory: /etc/traefik/conf.d/ entryPoints: web: address: ':80' http: redirections: entryPoint: to: websecure scheme: https websecure: address: ':443' http: tls: certResolver: letsencrypt traefik: address: ':8080' certificatesResolvers: letsencrypt: acme: email: "foo@bar.com" storage: /etc/traefik/ssl/acme.json tlsChallenge: {} api: dashboard: true insecure: true log: filePath: /var/log/traefik/traefik.log format: json level: INFO accessLog: filePath: /var/log/traefik/traefik-access.log format: json filters: statusCodes: - "200" - "400-599" retryAttempts: true minDuration: "10ms" bufferingSize: 0 fields: headers: defaultMode: drop names: User-Agent: keep EOF msg_ok "Created Traefik configuration" msg_info "Creating Service" cat <<'EOF' >/etc/systemd/system/traefik.service [Unit] Description=Traefik is an open-source Edge Router that makes publishing your services a fun and easy experience [Service] Type=notify ExecStart=/usr/bin/traefik --configFile=/etc/traefik/traefik.yaml Restart=on-failure ExecReload=/bin/kill -USR1 \$MAINPID [Install] WantedBy=multi-user.target EOF systemctl enable -q --now traefik msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/transmission-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://transmissionbt.com/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Transmission" $STD apt install -y transmission-daemon systemctl stop transmission-daemon sed -i '{s/"rpc-whitelist-enabled": true/"rpc-whitelist-enabled": false/g; s/"rpc-host-whitelist-enabled": true,/"rpc-host-whitelist-enabled": false,/g}' /etc/transmission-daemon/settings.json systemctl start transmission-daemon msg_ok "Installed Transmission" motd_ssh customize cleanup_lxc ================================================ FILE: install/transmute-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/transmute-app/transmute source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os UV_PYTHON="3.13" setup_uv NODE_VERSION="25" setup_nodejs setup_ffmpeg setup_gs msg_info "Installing Dependencies" $STD apt install -y \ inkscape \ tesseract-ocr \ libreoffice-impress \ libreoffice-common \ libmagic1 \ xvfb \ libsm6 \ libxext6 \ libpango-1.0-0 \ libopengl0 \ libpangocairo-1.0-0 \ libgdk-pixbuf-2.0-0 \ libffi-dev \ libcairo2 \ librsvg2-bin \ unrar-free \ python3-numpy \ python3-lxml \ python3-tinycss2 \ python3-cssselect msg_ok "Installed Dependencies" fetch_and_deploy_gh_release "pandoc" "jgm/pandoc" "binary" "latest" "" "pandoc-*-amd64.deb" fetch_and_deploy_gh_release "calibre" "kovidgoyal/calibre" "prebuild" "latest" "/opt/calibre" "calibre-*-x86_64.txz" ln -sf /opt/calibre/ebook-convert /usr/bin/ebook-convert ln -sf /usr/local/bin/ffmpeg /usr/bin/ffmpeg fetch_and_deploy_gh_release "drawio" "jgraph/drawio-desktop" "binary" "latest" "" "drawio-amd64-*.deb" fetch_and_deploy_gh_release "transmute" "transmute-app/transmute" "tarball" msg_info "Setting up Python Backend" cd /opt/transmute $STD uv venv --clear /opt/transmute/.venv $STD uv pip install --python /opt/transmute/.venv/bin/python -r requirements.txt ln -sf /opt/transmute/.venv/bin/weasyprint /usr/bin/weasyprint msg_ok "Set up Python Backend" msg_info "Configuring Transmute" SECRET_KEY=$(openssl rand -hex 64) cat </opt/transmute/backend/.env AUTH_SECRET_KEY=${SECRET_KEY} HOST=0.0.0.0 PORT=3313 DATA_DIR=/opt/transmute/data WEB_DIR=/opt/transmute/frontend/dist QT_QPA_PLATFORM=offscreen EOF mkdir -p /opt/transmute/data msg_ok "Configured Transmute" msg_info "Building Frontend" cd /opt/transmute/frontend $STD npm ci $STD npm run build msg_ok "Built Frontend" msg_info "Creating Service" cat </etc/systemd/system/transmute.service [Unit] Description=Transmute File Converter After=network.target [Service] Type=simple WorkingDirectory=/opt/transmute EnvironmentFile=/opt/transmute/backend/.env ExecStart=/usr/bin/xvfb-run -a -s "-screen 0 1024x768x24 -nolisten tcp" /opt/transmute/.venv/bin/python backend/main.py Restart=on-failure RestartSec=5 [Install] WantedBy=multi-user.target EOF systemctl enable -q --now transmute msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/trek-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/mauriceboe/TREK source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y build-essential msg_ok "Installed Dependencies" NODE_VERSION="22" setup_nodejs fetch_and_deploy_gh_release "trek" "mauriceboe/TREK" "tarball" msg_info "Building Client" cd /opt/trek/client $STD npm ci $STD npm run build msg_ok "Built Client" msg_info "Setting up Server" cd /opt/trek/server $STD npm ci mkdir -p /opt/trek/server/public cp -r /opt/trek/client/dist/* /opt/trek/server/public/ cp -r /opt/trek/client/public/fonts /opt/trek/server/public/fonts 2>/dev/null || true mkdir -p /opt/trek/{data/logs,uploads/{files,covers,avatars,photos}} rm -rf /opt/trek/server/data /opt/trek/server/uploads ln -s /opt/trek/data /opt/trek/server/data ln -s /opt/trek/uploads /opt/trek/server/uploads ENCRYPTION_KEY=$(openssl rand -hex 32) ADMIN_EMAIL="admin@trek.local" ADMIN_PASSWORD=$(openssl rand -base64 18 | tr -dc 'A-Za-z0-9' | head -c 16) cat </opt/trek/server/.env NODE_ENV=production PORT=3000 ENCRYPTION_KEY=${ENCRYPTION_KEY} ADMIN_EMAIL=${ADMIN_EMAIL} ADMIN_PASSWORD=${ADMIN_PASSWORD} COOKIE_SECURE=false FORCE_HTTPS=false LOG_LEVEL=info TZ=UTC EOF chmod 600 /opt/trek/server/.env msg_ok "Set up Server" msg_info "Creating Service" cat </etc/systemd/system/trek.service [Unit] Description=TREK Travel Planner After=network.target [Service] Type=simple User=root WorkingDirectory=/opt/trek/server EnvironmentFile=/opt/trek/server/.env ExecStart=/usr/bin/node --import tsx src/index.ts Restart=on-failure RestartSec=5 [Install] WantedBy=multi-user.target EOF systemctl enable -q --now trek msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/trilium-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/TriliumNext/Trilium source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os fetch_and_deploy_gh_release "Trilium" "TriliumNext/Trilium" "prebuild" "latest" "/opt/trilium" "TriliumNotes-Server-*linux-arm64.tar.xz" msg_info "Creating Service" cat </etc/systemd/system/trilium.service [Unit] Description=Trilium Daemon After=syslog.target network.target [Service] User=root Type=simple ExecStart=/opt/trilium/trilium.sh WorkingDirectory=/opt/trilium/ TimeoutStopSec=20 Restart=always [Install] WantedBy=multi-user.target EOF systemctl enable --now -q trilium msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/trip-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: CrazyWolf13 # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/itskovacs/TRIP source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ build-essential msg_ok "Installed Dependencies" NODE_VERSION="22" setup_nodejs PYTHON_VERSION="3.12" setup_uv fetch_and_deploy_gh_release "trip" "itskovacs/TRIP" "tarball" msg_info "Building Frontend" cd /opt/trip/src $STD npm install $STD npm run build msg_ok "Built Frontend" msg_info "Setting up Backend" cd /opt/trip/backend $STD uv venv --clear /opt/trip/.venv $STD uv pip install --python /opt/trip/.venv/bin/python -r trip/requirements.txt msg_ok "Set up Backend" msg_info "Configuring Application" mkdir -p /opt/trip/frontend cp -r /opt/trip/src/dist/trip/browser/* /opt/trip/frontend/ mkdir -p /opt/trip_storage/{attachments,backups,assets} cat </opt/trip.env # TRIP Configuration # https://itskovacs.github.io/trip/docs/getting-started/configuration/ ATTACHMENTS_FOLDER=/opt/trip_storage/attachments BACKUPS_FOLDER=/opt/trip_storage/backups ASSETS_FOLDER=/opt/trip_storage/assets FRONTEND_FOLDER=/opt/trip/frontend SQLITE_FILE=/opt/trip_storage/trip.sqlite EOF msg_ok "Configured Application" msg_info "Creating Service" cat </etc/systemd/system/trip.service [Unit] Description=TRIP - Minimalist POI Map Tracker and Trip Planner After=network.target [Service] Type=simple WorkingDirectory=/opt/trip/backend EnvironmentFile=/opt/trip.env ExecStart=/opt/trip/.venv/bin/fastapi run /opt/trip/backend/trip/main.py --host 0.0.0.0 --port 8000 Restart=on-failure RestartSec=5 [Install] WantedBy=multi-user.target EOF systemctl enable -q --now trip msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/tubearchivist-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/tubearchivist/tubearchivist source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ build-essential \ git \ nginx \ redis-server \ atomicparsley \ python3-dev \ libldap2-dev \ libsasl2-dev \ libssl-dev \ sqlite3 \ ffmpeg msg_ok "Installed Dependencies" UV_PYTHON="3.13" setup_uv NODE_VERSION="24" setup_nodejs fetch_and_deploy_gh_release "deno" "denoland/deno" "prebuild" "latest" "/usr/local/bin" "deno-x86_64-unknown-linux-gnu.zip" msg_info "Installing ElasticSearch" setup_deb822_repo \ "elastic-8.x" \ "https://artifacts.elastic.co/GPG-KEY-elasticsearch" \ "https://artifacts.elastic.co/packages/8.x/apt" \ "stable" \ "main" ES_JAVA_OPTS="-Xms1g -Xmx1g" $STD apt install -y elasticsearch msg_ok "Installed ElasticSearch" msg_info "Configuring ElasticSearch" cat </etc/elasticsearch/elasticsearch.yml cluster.name: tubearchivist path.data: /var/lib/elasticsearch path.logs: /var/log/elasticsearch path.repo: ["/var/lib/elasticsearch/snapshot"] network.host: 127.0.0.1 xpack.security.enabled: false xpack.security.transport.ssl.enabled: false xpack.security.http.ssl.enabled: false EOF mkdir -p /var/lib/elasticsearch/snapshot chown -R elasticsearch:elasticsearch /var/lib/elasticsearch/snapshot cat </etc/elasticsearch/jvm.options.d/heap.options -Xms1g -Xmx1g EOF sysctl -w vm.max_map_count=262144 2>/dev/null || true cat </etc/sysctl.d/99-elasticsearch.conf vm.max_map_count=262144 EOF systemctl enable -q --now elasticsearch msg_ok "Configured ElasticSearch" fetch_and_deploy_gh_release "tubearchivist" "tubearchivist/tubearchivist" "tarball" msg_info "Building Frontend" cd /opt/tubearchivist/frontend $STD npm install $STD npm run build:deploy mkdir -p /opt/tubearchivist/backend/static cp -r /opt/tubearchivist/frontend/dist/* /opt/tubearchivist/backend/static/ msg_ok "Built Frontend" msg_info "Setting up Tube Archivist" cp /opt/tubearchivist/docker_assets/backend_start.py /opt/tubearchivist/backend/ $STD uv venv /opt/tubearchivist/.venv $STD uv pip install --python /opt/tubearchivist/.venv/bin/python -r /opt/tubearchivist/backend/requirements.txt if [[ -f /opt/tubearchivist/backend/requirements.plugins.txt ]]; then mkdir -p /opt/yt_plugins/bgutil $STD uv pip install --python /opt/tubearchivist/.venv/bin/python --target /opt/yt_plugins/bgutil -r /opt/tubearchivist/backend/requirements.plugins.txt fi TA_PASSWORD=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | head -c13) ES_PASSWORD=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | head -c13) mkdir -p /opt/tubearchivist/{cache,media} ln -sf /opt/tubearchivist/cache /cache ln -sf /opt/tubearchivist/media /youtube cat </opt/tubearchivist/.env TA_HOST=http://${LOCAL_IP}:8000 TA_USERNAME=admin TA_PASSWORD=${TA_PASSWORD} TA_BACKEND_PORT=8080 TA_APP_DIR=/opt/tubearchivist/backend TA_CACHE_DIR=/cache TA_MEDIA_DIR=/youtube ES_SNAPSHOT_DIR=/var/lib/elasticsearch/snapshot ELASTIC_PASSWORD=${ES_PASSWORD} REDIS_CON=redis://localhost:6379 ES_URL=http://localhost:9200 TZ=UTC PYTHONUNBUFFERED=1 YTDLP_PLUGIN_DIRS=/opt/yt_plugins EOF { echo "Tube Archivist Credentials" echo "==========================" echo "Username: admin" echo "Password: ${TA_PASSWORD}" echo "Elasticsearch Password: ${ES_PASSWORD}" } >~/tubearchivist.creds systemctl enable -q --now redis-server msg_ok "Set up Tube Archivist" msg_info "Configuring Nginx" sed -i 's/^user www-data;$/user root;/' /etc/nginx/nginx.conf cat <<'EOF' >/etc/nginx/sites-available/default server { listen 8000; location = /_auth { internal; proxy_pass http://localhost:8080/api/ping/; proxy_pass_request_body off; proxy_set_header Content-Length ""; proxy_set_header Host $http_host; proxy_set_header Cookie $http_cookie; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } location /cache/videos/ { auth_request /_auth; alias /cache/videos/; } location /cache/channels/ { auth_request /_auth; alias /cache/channels/; } location /cache/playlists/ { auth_request /_auth; alias /cache/playlists/; } location /media/ { auth_request /_auth; alias /youtube/; types { text/vtt vtt; } } location /youtube/ { auth_request /_auth; alias /youtube/; types { video/mp4 mp4; } } location /api { include proxy_params; proxy_pass http://localhost:8080; } location /admin { include proxy_params; proxy_pass http://localhost:8080; } location /static/ { alias /opt/tubearchivist/backend/staticfiles/; } root /opt/tubearchivist/backend/static; index index.html; location ~* ^/(?!static/|cache/).*\.(?:css|js|png|jpg|jpeg|gif|ico|svg|woff2?)$ { try_files $uri $uri/ /index.html =404; } location = /index.html { add_header Cache-Control "no-store, no-cache, must-revalidate"; add_header Pragma "no-cache"; expires 0; } location / { add_header Cache-Control "no-store, no-cache, must-revalidate"; add_header Pragma "no-cache"; expires 0; try_files $uri $uri/ /index.html =404; } } EOF systemctl enable -q nginx systemctl restart nginx msg_ok "Configured Nginx" msg_info "Creating Services" cat <<'RUNEOF' >/opt/tubearchivist/backend/run.sh #!/bin/bash set -e cd /opt/tubearchivist/backend set -a source /opt/tubearchivist/.env set +a PYTHON=/opt/tubearchivist/.venv/bin/python echo "Waiting for ElasticSearch..." for i in $(seq 1 30); do if curl -sf http://localhost:9200/_cluster/health >/dev/null 2>&1; then break fi sleep 2 done $PYTHON manage.py migrate $PYTHON manage.py collectstatic --noinput -c $PYTHON manage.py ta_envcheck $PYTHON manage.py ta_connection $PYTHON manage.py ta_startup exec $PYTHON backend_start.py RUNEOF chmod +x /opt/tubearchivist/backend/run.sh ln -sf /opt/tubearchivist/.env /opt/tubearchivist/backend/.env cat </etc/systemd/system/tubearchivist.service [Unit] Description=Tube Archivist Backend After=network.target elasticsearch.service redis-server.service [Service] Type=simple User=root WorkingDirectory=/opt/tubearchivist/backend EnvironmentFile=/opt/tubearchivist/.env Environment=PATH=/opt/tubearchivist/.venv/bin:/usr/local/bin:/usr/bin:/bin ExecStart=/opt/tubearchivist/backend/run.sh Restart=on-failure RestartSec=10 [Install] WantedBy=multi-user.target EOF cat </etc/systemd/system/tubearchivist-celery.service [Unit] Description=Tube Archivist Celery Worker After=tubearchivist.service redis-server.service elasticsearch.service [Service] Type=simple User=root WorkingDirectory=/opt/tubearchivist/backend EnvironmentFile=/opt/tubearchivist/.env Environment=PATH=/opt/tubearchivist/.venv/bin:/usr/local/bin:/usr/bin:/bin ExecStart=/opt/tubearchivist/.venv/bin/celery -A task worker --loglevel=error --concurrency=4 --max-tasks-per-child=5 --max-memory-per-child=150000 Restart=on-failure RestartSec=5 [Install] WantedBy=multi-user.target EOF cat </etc/systemd/system/tubearchivist-beat.service [Unit] Description=Tube Archivist Celery Beat After=tubearchivist.service redis-server.service [Service] Type=simple User=root WorkingDirectory=/opt/tubearchivist/backend EnvironmentFile=/opt/tubearchivist/.env Environment=PATH=/opt/tubearchivist/.venv/bin:/usr/local/bin:/usr/bin:/bin ExecStartPre=/bin/bash -c 'for i in \$(seq 1 60); do sqlite3 /cache/db.sqlite3 "SELECT 1 FROM django_celery_beat_crontabschedule LIMIT 1" 2>/dev/null && exit 0; sleep 2; done; exit 1' ExecStart=/opt/tubearchivist/.venv/bin/celery -A task beat --loglevel=error --scheduler django_celery_beat.schedulers:DatabaseScheduler Restart=always RestartSec=5 RuntimeMaxSec=3600 [Install] WantedBy=multi-user.target EOF systemctl enable -q --now tubearchivist tubearchivist-celery tubearchivist-beat msg_ok "Created Services" motd_ssh customize cleanup_lxc ================================================ FILE: install/tududi-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: vhsdream # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://tududi.com/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ sqlite3 \ yq msg_ok "Installed Dependencies" NODE_VERSION="22" setup_nodejs fetch_and_deploy_gh_release "tududi" "chrisvel/tududi" "tarball" "latest" "/opt/tududi" msg_info "Configuring Tududi" cd /opt/tududi $STD npm install export NODE_ENV=production $STD npm run frontend:build mv ./dist ./backend msg_ok "Configured Tududi" msg_info "Creating env and database" DB_LOCATION="/opt/tududi-db" UPLOAD_DIR="/opt/tududi-uploads" mkdir -p {"$DB_LOCATION","$UPLOAD_DIR"} SECRET="$(openssl rand -hex 64)" sed -e '/^NODE_ENV=/s/=.*$/=production/' \ -e 's/^TUDUDI_USER/# TUDUDI_USER/g' \ -e "/_SECRET=/s/=.*$/=${SECRET}/" \ -e '/^# DB_FILE=/s/^# //' \ -e "s|^DB_FILE=.*|DB_FILE=${DB_LOCATION}/production.sqlite3|" \ -e "/^# TUDUDI_ALLOWED/s/^# //; \ \|_ORIGINS=|s|=.*$|=|" \ -e "/^# TUDUDI_UPLOAD/s/^# //; \ \|UPLOAD_PATH=|s|=.*$|=${UPLOAD_DIR}|" \ /opt/tududi/backend/.env.example >/opt/tududi/backend/.env export DB_FILE="${DB_LOCATION}/production.sqlite3" $STD npm run db:init msg_ok "Created env and database" msg_info "Creating service" cat </etc/systemd/system/tududi.service [Unit] Description=Tududi Service After=network.target [Service] Type=simple WorkingDirectory=/opt/tududi/backend EnvironmentFile=/opt/tududi/backend/.env ExecStart=/usr/bin/bash /opt/tududi/backend/cmd/start.sh [Install] WantedBy=multi-user.target EOF systemctl enable -q --now tududi msg_ok "Created service" motd_ssh customize cleanup_lxc ================================================ FILE: install/tunarr-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: chrisbenincasa # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://tunarr.com/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os setup_hwaccel fetch_and_deploy_gh_release "tunarr" "chrisbenincasa/tunarr" "prebuild" "latest" "/opt/tunarr" "*linux-arm64.tar.gz" cd /opt/tunarr mv tunarr* tunarr fetch_and_deploy_gh_release "ersatztv-ffmpeg" "ErsatzTV/ErsatzTV-ffmpeg" "prebuild" "latest" "/opt/ErsatzTV-ffmpeg" "*-linuarm64-gpl-7.1.tar.xz" msg_info "Set ErsatzTV-ffmpeg links" chmod +x /opt/ErsatzTV-ffmpeg/bin/* ln -sf /opt/ErsatzTV-ffmpeg/bin/ffmpeg /usr/bin/ffmpeg ln -sf /opt/ErsatzTV-ffmpeg/bin/ffplay /usr/bin/ffplay ln -sf /opt/ErsatzTV-ffmpeg/bin/ffprobe /usr/bin/ffprobe msg_ok "ffmpeg links set" msg_info "Creating Service" cat </etc/systemd/system/tunarr.service [Unit] Description=Tunarr Service After=multi-user.target [Service] Type=simple User=root WorkingDirectory=/opt/tunarr ExecStart=/opt/tunarr/tunarr Restart=always RestartSec=30 [Install] WantedBy=multi-user.target EOF systemctl enable -q --now tunarr msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/twingate-connector-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ), twingate-andrewb # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.twingate.com/docs/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os install -d -m 0700 /etc/twingate access_token="" refresh_token="" network="" while [[ -z "$access_token" ]]; do read -rp "${TAB3}Please enter your access token: " access_token done while [[ -z "$refresh_token" ]]; do read -rp "${TAB3}Please enter your refresh token: " refresh_token done while [[ -z "$network" ]]; do read -rp "${TAB3}Please enter your network name: " network done msg_info "Setup Twingate Repository" curl -fsSL "https://packages.twingate.com/apt/gpg.key" | gpg --dearmor -o /usr/share/keyrings/twingate-connector-keyring.gpg echo "deb [signed-by=/usr/share/keyrings/twingate-connector-keyring.gpg] https://packages.twingate.com/apt/ /" >/etc/apt/sources.list.d/twingate.list $STD apt-get update msg_ok "Setup Twingate Repository" msg_info "Setup Twingate Connector" $STD apt-get install -y twingate-connector msg_ok "Setup Twingate Connector" msg_info "Configure Twingate-Connector" { echo "TWINGATE_NETWORK=${network}" echo "TWINGATE_ACCESS_TOKEN=${access_token}" echo "TWINGATE_REFRESH_TOKEN=${refresh_token}" echo "TWINGATE_LABEL_HOSTNAME=$(hostname)" echo "TWINGATE_LABEL_DEPLOYED_BY=proxmox" } >/etc/twingate/connector.conf chmod 600 /etc/twingate/connector.conf msg_ok "Configured Twingate-Connector" msg_info "Starting Service" systemctl enable -q --now twingate-connector msg_ok "Service started" motd_ssh customize cleanup_lxc ================================================ FILE: install/ubuntu-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://ubuntu.com/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os motd_ssh customize cleanup_lxc ================================================ FILE: install/uhf-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: zackwithak13 # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.uhfapp.com/server source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os setup_hwaccel msg_info "Installing Dependencies" setup_ffmpeg msg_ok "Installed Dependencies" msg_info "Setting Up UHF Server Environment" mkdir -p /etc/uhf-server mkdir -p /var/lib/uhf-server/data mkdir -p /var/lib/uhf-server/recordings cat </etc/uhf-server/.env API_HOST=0.0.0.0 API_PORT=7568 RECORDINGS_DIR=/var/lib/uhf-server/recordings DB_PATH=/var/lib/uhf-server/data/db.json LOG_LEVEL=INFO EOF msg_ok "Set Up UHF Server Environment" fetch_and_deploy_gh_release "comskip" "swapplications/comskip" "prebuild" "latest" "/opt/comskip" "comskip-arm64-*.zip" fetch_and_deploy_gh_release "uhf-server" "swapplications/uhf-server-dist" "prebuild" "latest" "/opt/uhf-server" "UHF.Server-linux-arm64-*.zip" msg_info "Creating Service" cat </etc/systemd/system/uhf-server.service [Unit] Description=UHF Server service After=syslog.target network-online.target [Service] Type=simple WorkingDirectory=/opt/uhf-server EnvironmentFile=/etc/uhf-server/.env ExecStart=/opt/uhf-server/uhf-server [Install] WantedBy=multi-user.target EOF systemctl enable -q --now uhf-server msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/umami-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://umami.is/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os NODE_VERSION="22" NODE_MODULE="pnpm@latest" setup_nodejs PG_VERSION="17" setup_postgresql PG_DB_NAME="umamidb" PG_DB_USER="umami" setup_postgresql_db fetch_and_deploy_gh_release "umami" "umami-software/umami" "tarball" msg_info "Configuring Umami" cd /opt/umami $STD pnpm install echo -e "DATABASE_URL=postgresql://$PG_DB_USER:$PG_DB_PASS@localhost:5432/$PG_DB_NAME" >>/opt/umami/.env $STD pnpm run build msg_ok "Configured Umami" msg_info "Creating Service" cat </etc/systemd/system/umami.service [Unit] Description=umami [Service] Type=simple Restart=always User=root WorkingDirectory=/opt/umami ExecStart=/usr/bin/pnpm run start [Install] WantedBy=multi-user.target EOF systemctl enable -q --now umami msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/umlautadaptarr-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: elvito # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/PCJones/UmlautAdaptarr source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" setup_deb822_repo \ "microsoft" \ "https://packages.microsoft.com/keys/microsoft.asc" \ "https://packages.microsoft.com/debian/12/prod/" \ "bookworm" \ "main" $STD apt install -y \ dotnet-sdk-8.0 \ aspnetcore-runtime-8.0 msg_ok "Installed Dependencies" fetch_and_deploy_gh_release "UmlautAdaptarr" "PCJones/Umlautadaptarr" "prebuild" "latest" "/opt/UmlautAdaptarr" "linux-x64.zip" msg_info "Creating Service" cat </etc/systemd/system/umlautadaptarr.service [Unit] Description=UmlautAdaptarr Service After=network.target [Service] WorkingDirectory=/opt/UmlautAdaptarr ExecStart=/usr/bin/dotnet /opt/UmlautAdaptarr/UmlautAdaptarr.dll --urls=http://0.0.0.0:5005 Restart=always User=root Group=root Environment=ASPNETCORE_ENVIRONMENT=Production [Install] WantedBy=multi-user.target EOF systemctl enable -q --now umlautadaptarr msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/unbound-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: wimb0 # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/NLnetLabs/unbound source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt-get install -y logrotate msg_ok "Installed Dependencies" msg_info "Installing Unbound" mkdir -p /etc/unbound/unbound.conf.d cat </etc/unbound/unbound.conf.d/unbound.conf server: interface: 0.0.0.0 port: 5335 do-ip6: no hide-identity: yes hide-version: yes harden-referral-path: yes cache-min-ttl: 300 cache-max-ttl: 14400 serve-expired: yes serve-expired-ttl: 3600 prefetch: yes prefetch-key: yes target-fetch-policy: "3 2 1 1 1" unwanted-reply-threshold: 10000000 rrset-cache-size: 256m msg-cache-size: 128m so-rcvbuf: 1m private-address: 192.168.0.0/16 private-address: 169.254.0.0/16 private-address: 172.16.0.0/12 private-address: 10.0.0.0/8 private-address: fd00::/8 private-address: fe80::/10 access-control: 192.168.0.0/16 allow access-control: 172.16.0.0/12 allow access-control: 10.0.0.0/8 allow access-control: 127.0.0.1/32 allow chroot: "" logfile: /var/log/unbound.log EOF $STD apt install -y \ unbound \ unbound-host touch /var/log/unbound.log chown unbound:unbound /var/log/unbound.log sleep 5 systemctl restart unbound msg_ok "Installed Unbound" msg_info "Configuring Logrotate" cat </etc/logrotate.d/unbound /var/log/unbound.log { daily rotate 7 missingok notifempty compress delaycompress sharedscripts create 644 postrotate /usr/sbin/unbound-control log_reopen endscript } EOF systemctl restart logrotate msg_ok "Configured Logrotate" motd_ssh customize cleanup_lxc ================================================ FILE: install/unifi-os-server-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://ui.com/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os if [[ "${CTTYPE:-1}" != "0" ]]; then msg_error "UniFi OS Server requires a privileged LXC container." msg_error "Recreate the container with unprivileged=0." exit 10 fi if [[ ! -e /dev/net/tun ]]; then msg_error "Missing /dev/net/tun in container." msg_error "Enable TUN/TAP (var_tun=yes) or add /dev/net/tun passthrough." exit 236 fi msg_info "Installing dependencies" $STD apt install -y \ podman \ uidmap \ slirp4netns msg_ok "Installed dependencies" msg_info "Installing sysctl wrapper (ignore non-critical errors)" cat <<'EOF' >/usr/local/sbin/sysctl #!/bin/sh /usr/sbin/sysctl "$@" || true exit 0 EOF chmod +x /usr/local/sbin/sysctl export PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" msg_ok "Sysctl wrapper installed" msg_info "Fetching latest UniFi OS Server" API_URL="https://fw-update.ui.com/api/firmware-latest" TEMP_JSON="$(mktemp)" if ! curl -fsSL "$API_URL" -o "$TEMP_JSON"; then rm -f "$TEMP_JSON" msg_error "Failed to fetch data from Ubiquiti API" exit 250 fi LATEST=$(jq -r ' ._embedded.firmware | map(select(.product == "unifi-os-server")) | map(select(.platform == "linux-arm64")) | sort_by(.version_major, .version_minor, .version_patch) | last ' "$TEMP_JSON") UOS_VERSION=$(echo "$LATEST" | jq -r '.version' | sed 's/^v//') UOS_URL=$(echo "$LATEST" | jq -r '._links.data.href') rm -f "$TEMP_JSON" if [[ -z "$UOS_URL" || -z "$UOS_VERSION" || "$UOS_URL" == "null" ]]; then msg_error "Failed to parse UniFi OS Server version or download URL" exit 250 fi msg_ok "Found UniFi OS Server ${UOS_VERSION}" msg_info "Downloading UniFi OS Server installer" mkdir -p /usr/local/sbin curl -fsSL "$UOS_URL" -o /usr/local/sbin/unifi-os-server.bin chmod +x /usr/local/sbin/unifi-os-server.bin msg_ok "Downloaded UniFi OS Server installer" msg_info "Installing UniFi OS Server (this takes a few minutes)" $STD /usr/local/sbin/unifi-os-server.bin <<<"y" msg_ok "UniFi OS Server installed" motd_ssh customize cleanup_lxc ================================================ FILE: install/unmanic-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://docs.unmanic.app/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies (Patience)" $STD apt install -y \ ffmpeg \ python3-pip rm -rf /usr/lib/python3.*/EXTERNALLY-MANAGED msg_ok "Installed Dependencies" setup_hwaccel msg_info "Installing Unmanic" $STD pip3 install unmanic msg_ok "Installed Unmanic" msg_info "Creating Service" cat </etc/systemd/system/unmanic.service [Unit] Description=Unmanic - Library Optimiser After=network-online.target StartLimitInterval=200 StartLimitBurst=3 [Service] Type=simple ExecStart=/usr/local/bin/unmanic Restart=always RestartSec=30 [Install] WantedBy=multi-user.target EOF systemctl enable --now -q unmanic.service msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/upgopher-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: Eduardo González (wanetty) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/wanetty/upgopher source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os fetch_and_deploy_gh_release "upgopher" "wanetty/upgopher" "prebuild" "latest" "/opt/upgopher" "upgopher_*_linux_arm64.tar.gz" msg_info "Installing Upgopher" chmod +x /opt/upgopher/upgopher mkdir -p /opt/upgopher/uploads msg_ok "Installed Upgopher" msg_info "Creating Service" cat </etc/systemd/system/upgopher.service [Unit] Description=Upgopher File Server Documentation=https://github.com/wanetty/upgopher After=network.target [Service] Type=simple User=root WorkingDirectory=/opt/upgopher ExecStart=/opt/upgopher/upgopher -port 9090 -dir /opt/upgopher/uploads Restart=always RestartSec=5 [Install] WantedBy=multi-user.target EOF systemctl enable -q --now upgopher msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/upsnap-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: Slaviša Arežina (tremor021) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/seriousm4x/UpSnap source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ nmap \ samba \ samba-common-bin \ openssh-client \ openssh-server \ sshpass msg_ok "Installed Dependencies" fetch_and_deploy_gh_release "upsnap" "seriousm4x/UpSnap" "prebuild" "latest" "/opt/upsnap" "UpSnap_*_linux_arm64.zip" setcap 'cap_net_raw=+ep' /opt/upsnap/upsnap msg_info "Creating Service" cat </etc/systemd/system/upsnap.service [Unit] Description=UpSnap Service Documentation=https://github.com/seriousm4x/UpSnap/wiki After=network.target [Service] Type=simple User=root Restart=on-failure WorkingDirectory=/opt/upsnap ExecStart=/opt/upsnap/upsnap serve --http=0.0.0.0:8090 [Install] WantedBy=multi-user.target EOF systemctl enable -q --now upsnap msg_ok "Service Created" motd_ssh customize cleanup_lxc ================================================ FILE: install/uptimekuma-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://uptime.kuma.pet/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing dependencies" $STD apt install -y chromium msg_ok "Installed dependencies" NODE_VERSION="22" setup_nodejs fetch_and_deploy_gh_release "uptime-kuma" "louislam/uptime-kuma" "tarball" msg_info "Installing Uptime Kuma" cd /opt/uptime-kuma $STD npm ci --omit dev $STD npm run download-dist msg_ok "Installed Uptime Kuma" msg_info "Creating Service" ln -s /usr/bin/chromium /opt/uptime-kuma/chromium cat </etc/systemd/system/uptime-kuma.service [Unit] Description=uptime-kuma [Service] Type=simple Restart=always User=root WorkingDirectory=/opt/uptime-kuma ExecStart=/usr/bin/npm start [Install] WantedBy=multi-user.target EOF systemctl enable -q --now uptime-kuma msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/urbackupserver-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: Kristian Skov # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.urbackup.org/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y debconf-utils msg_ok "Installed Dependencies" setup_deb822_repo \ "urbackup" \ "https://download.opensuse.org/repositories/home:uroni/Debian_13/Release.key" \ "http://download.opensuse.org/repositories/home:/uroni/Debian_13/" \ "./" \ "" msg_info "Setting up UrBackup Server" mkdir -p /opt/urbackup/backups echo "urbackup-server urbackup/backuppath string /opt/urbackup/backups" | debconf-set-selections $STD apt install -y urbackup-server msg_ok "Setup UrBackup Server" motd_ssh customize cleanup_lxc ================================================ FILE: install/valkey-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: pshankinclarke (lazarillo) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://valkey.io/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Valkey" $STD apt update $STD apt install -y valkey openssl sed -i 's/^bind .*/bind 0.0.0.0/' /etc/valkey/valkey.conf PASS="$(openssl rand -base64 48 | tr -dc 'a-zA-Z0-9' | head -c32)" echo "requirepass $PASS" >> /etc/valkey/valkey.conf echo "$PASS" >~/valkey.creds chmod 600 ~/valkey.creds MEMTOTAL_MB=$(free -m | grep ^Mem: | awk '{print $2}') # reserve 25% of a node type's maxmemory value for system use MAXMEMORY_MB=$((MEMTOTAL_MB * 75 / 100)) echo "" >> /etc/valkey/valkey.conf echo "# Memory-optimized settings for small-scale deployments" >> /etc/valkey/valkey.conf echo "maxmemory ${MAXMEMORY_MB}mb" >> /etc/valkey/valkey.conf echo "maxmemory-policy allkeys-lru" >> /etc/valkey/valkey.conf echo "maxmemory-samples 10" >> /etc/valkey/valkey.conf msg_ok "Installed Valkey" echo read -r -p "${TAB3}Enable TLS for Valkey (Sentinel mode does not supported)? [y/N]: " prompt if [[ ${prompt,,} =~ ^(y|yes)$ ]]; then read -r -p "${TAB3}Use TLS-only mode (disable TCP port 6379)? [y/N]: " tls_only msg_info "Configuring TLS for Valkey..." create_self_signed_cert "Valkey" TLS_DIR="/etc/ssl/valkey" TLS_CERT="$TLS_DIR/valkey.crt" TLS_KEY="$TLS_DIR/valkey.key" chown valkey:valkey "$TLS_CERT" "$TLS_KEY" if [[ ${tls_only,,} =~ ^(y|yes)$ ]]; then { echo "" echo "# TLS configuration generated by Proxmox VE Valkey helper-script" echo "port 0" echo "tls-port 6379" echo "tls-cert-file $TLS_DIR/valkey.crt" echo "tls-key-file $TLS_DIR/valkey.key" echo "tls-auth-clients no" } >> /etc/valkey/valkey.conf msg_ok "Enabled TLS-only mode on port 6379" else { echo "" echo "# TLS configuration generated by Proxmox VE Valkey helper-script" echo "tls-port 6380" echo "tls-cert-file $TLS_DIR/valkey.crt" echo "tls-key-file $TLS_DIR/valkey.key" echo "tls-auth-clients no" } >> /etc/valkey/valkey.conf msg_ok "Enabled TLS on port 6380 and TCP on 6379" fi fi systemctl enable -q --now valkey-server systemctl restart valkey-server motd_ssh customize cleanup_lxc ================================================ FILE: install/vaultwarden-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/dani-garcia/vaultwarden source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ build-essential \ pkgconf \ libssl-dev \ libmariadb-dev-compat \ libpq-dev \ argon2 \ ssl-cert msg_ok "Installed Dependencies" setup_rust fetch_and_deploy_gh_release "vaultwarden" "dani-garcia/vaultwarden" "tarball" "latest" "/tmp/vaultwarden-src" msg_info "Building Vaultwarden (Patience)" cd /tmp/vaultwarden-src VW_VERSION=$(get_latest_github_release "dani-garcia/vaultwarden") export VW_VERSION $STD cargo build --features "sqlite,mysql,postgresql" --release msg_ok "Built Vaultwarden" msg_info "Setting up Vaultwarden" $STD addgroup --system vaultwarden $STD adduser --system --home /opt/vaultwarden --shell /usr/sbin/nologin --no-create-home --gecos 'vaultwarden' --ingroup vaultwarden --disabled-login --disabled-password vaultwarden mkdir -p /opt/vaultwarden/{bin,data,web-vault} cp target/release/vaultwarden /opt/vaultwarden/bin/ cd ~ && rm -rf /tmp/vaultwarden-src msg_ok "Set up Vaultwarden" fetch_and_deploy_gh_release "vaultwarden_webvault" "dani-garcia/bw_web_builds" "prebuild" "latest" "/opt/vaultwarden/web-vault" "bw_web_*.tar.gz" msg_info "Configuring Vaultwarden" cat </opt/vaultwarden/.env ADMIN_TOKEN='' ROCKET_ADDRESS=0.0.0.0 ROCKET_TLS='{certs="/opt/vaultwarden/ssl-cert-snakeoil.pem",key="/opt/vaultwarden/ssl-cert-snakeoil.key"}' DATA_FOLDER=/opt/vaultwarden/data DATABASE_MAX_CONNS=10 WEB_VAULT_FOLDER=/opt/vaultwarden/web-vault WEB_VAULT_ENABLED=true EOF mv /etc/ssl/certs/ssl-cert-snakeoil.pem /opt/vaultwarden/ mv /etc/ssl/private/ssl-cert-snakeoil.key /opt/vaultwarden/ chown -R vaultwarden:vaultwarden /opt/vaultwarden/ chown root:root /opt/vaultwarden/bin/vaultwarden chmod +x /opt/vaultwarden/bin/vaultwarden chown -R root:root /opt/vaultwarden/web-vault/ chmod +r /opt/vaultwarden/.env msg_ok "Configured Vaultwarden" msg_info "Creating Service" cat </etc/systemd/system/vaultwarden.service [Unit] Description=Bitwarden Server (Powered by Vaultwarden) Documentation=https://github.com/dani-garcia/vaultwarden After=network.target [Service] User=vaultwarden Group=vaultwarden EnvironmentFile=-/opt/vaultwarden/.env ExecStart=/opt/vaultwarden/bin/vaultwarden LimitNOFILE=65535 LimitNPROC=4096 PrivateTmp=true PrivateDevices=true ProtectHome=true ProtectSystem=strict DevicePolicy=closed ProtectControlGroups=yes ProtectKernelModules=yes ProtectKernelTunables=yes RestrictNamespaces=yes RestrictRealtime=yes MemoryDenyWriteExecute=yes LockPersonality=yes WorkingDirectory=/opt/vaultwarden ReadWriteDirectories=/opt/vaultwarden/data AmbientCapabilities=CAP_NET_BIND_SERVICE [Install] WantedBy=multi-user.target EOF systemctl enable -q --now vaultwarden msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/verdaccio-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: BrynnJKnight # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://verdaccio.org/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y build-essential msg_ok "Installed Dependencies" NODE_VERSION="24" NODE_MODULE="verdaccio" setup_nodejs msg_info "Configuring Verdaccio" mkdir -p /opt/verdaccio/config mkdir -p /opt/verdaccio/storage cat </opt/verdaccio/config/config.yaml # Verdaccio configuration storage: /opt/verdaccio/storage auth: htpasswd: file: /opt/verdaccio/storage/htpasswd max_users: 1000 uplinks: npmjs: url: https://registry.npmjs.org/ packages: '@*/*': access: \$all publish: \$authenticated proxy: npmjs '**': access: \$all publish: \$authenticated proxy: npmjs middlewares: audit: enabled: true logs: - {type: stdout, format: pretty, level: http} listen: - 0.0.0.0:4873 web: enable: true title: Verdaccio gravatar: true sort_packages: asc login: true EOF chown -R root:root /opt/verdaccio chmod -R 755 /opt/verdaccio msg_ok "Configured Verdaccio" msg_info "Creating Service" cat </etc/systemd/system/verdaccio.service [Unit] Description=Verdaccio lightweight private npm proxy registry After=network.target [Service] Type=simple ExecStart=/usr/bin/verdaccio --config /opt/verdaccio/config/config.yaml Restart=on-failure StandardOutput=journal StandardError=journal SyslogIdentifier=verdaccio KillMode=control-group [Install] WantedBy=multi-user.target EOF systemctl enable -q --now verdaccio msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/versitygw-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/versity/versitygw source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os fetch_and_deploy_gh_release "versitygw" "versity/versitygw" "binary" WEBUI_CONF="" read -rp "Would you like to enable the VersityGW WebGUI (Beta)? (y/N): " webui_prompt if [[ "${webui_prompt,,}" =~ ^(y|yes)$ ]]; then WEBUI_CONF="\nVGW_WEBUI_PORT=:7071\nVGW_WEBUI_NO_TLS=true" msg_ok "WebGUI will be enabled on port 7071" fi msg_info "Configuring VersityGW" mkdir -p /opt/versitygw-data ACCESS_KEY=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | cut -c1-20) SECRET_KEY=$(openssl rand -base64 36 | tr -dc 'a-zA-Z0-9' | cut -c1-40) cat </etc/versitygw.d/gateway.conf VGW_BACKEND=posix VGW_BACKEND_ARG=/opt/versitygw-data VGW_PORT=:7070 ROOT_ACCESS_KEY_ID=${ACCESS_KEY} ROOT_SECRET_ACCESS_KEY=${SECRET_KEY} EOF if [[ -n "$WEBUI_CONF" ]]; then echo -e "$WEBUI_CONF" >>/etc/versitygw.d/gateway.conf fi msg_ok "Configured VersityGW" msg_info "Enabling Service" systemctl enable -q --now versitygw@gateway msg_ok "Enabled Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/victoriametrics-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: Slaviša Arežina (tremor021) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/VictoriaMetrics/VictoriaMetrics source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Getting latest version of VictoriaMetrics" victoriametrics_filename=$(curl -fsSL "https://api.github.com/repos/VictoriaMetrics/VictoriaMetrics/releases/latest" | jq -r '.assets[].name' | grep -E '^victoria-metrics-linux-arm64-v[0-9.]+\.tar\.gz$') vmutils_filename=$(curl -fsSL "https://api.github.com/repos/VictoriaMetrics/VictoriaMetrics/releases/latest" | jq -r '.assets[].name' | grep -E '^vmutils-linux-arm64-v[0-9.]+\.tar\.gz$') msg_ok "Got latest version of VictoriaMetrics" victoriametrics_release=$(curl -fsSL "https://api.github.com/repos/VictoriaMetrics/VictoriaMetrics/releases" | jq -r '.[] | select(.assets[].name | match("^victoria-metrics-linux-amd64-v[0-9.]+.tar.gz$")) | .tag_name' | head -n 1) victoriametrics_filename="victoria-metrics-linux-amd64-${victoriametrics_release}.tar.gz" vmutils_filename="vmutils-linux-amd64-${victoriametrics_release}.tar.gz" msg_ok "Got version $victoriametrics_release of VictoriaMetrics" fetch_and_deploy_gh_release "victoriametrics" "VictoriaMetrics/VictoriaMetrics" "prebuild" "$victoriametrics_release" "/opt/victoriametrics" "$victoriametrics_filename" fetch_and_deploy_gh_release "vmutils" "VictoriaMetrics/VictoriaMetrics" "prebuild" "$victoriametrics_release" "/opt/victoriametrics" "$vmutils_filename" read -r -p "${TAB3}Would you like to add VictoriaLogs? " prompt if [[ ${prompt,,} =~ ^(y|yes)$ ]]; then vmlogs_filename=$(curl -fsSL "https://api.github.com/repos/VictoriaMetrics/VictoriaLogs/releases/latest" | jq -r '.assets[].name' | grep -E '^victoria-logs-linux-arm64-v[0-9.]+\.tar\.gz$') vlutils_filename=$(curl -fsSL "https://api.github.com/repos/VictoriaMetrics/VictoriaLogs/releases/latest" | jq -r '.assets[].name' | grep -E '^vlutils-linux-arm64-v[0-9.]+\.tar\.gz$') fetch_and_deploy_gh_release "victorialogs" "VictoriaMetrics/VictoriaLogs" "prebuild" "latest" "/opt/victoriametrics" "$vmlogs_filename" fetch_and_deploy_gh_release "vlutils" "VictoriaMetrics/VictoriaLogs" "prebuild" "latest" "/opt/victoriametrics" "$vlutils_filename" fi msg_info "Setup VictoriaMetrics" mkdir -p /opt/victoriametrics/data chmod +x /opt/victoriametrics/* msg_ok "Setup VictoriaMetrics" msg_info "Creating Service" cat </etc/systemd/system/victoriametrics.service [Unit] Description=VictoriaMetrics Service [Service] Type=simple Restart=always User=root WorkingDirectory=/opt/victoriametrics ExecStart=/opt/victoriametrics/victoria-metrics-prod --storageDataPath="/opt/victoriametrics/data" [Install] WantedBy=multi-user.target EOF systemctl enable -q --now victoriametrics if [[ ${prompt,,} =~ ^(y|yes)$ ]]; then cat </etc/systemd/system/victoriametrics-logs.service [Unit] Description=VictoriaMetrics Service [Service] Type=simple Restart=always User=root WorkingDirectory=/opt/victoriametrics ExecStart=/opt/victoriametrics/victoria-logs-prod [Install] WantedBy=multi-user.target EOF systemctl enable -q --now victoriametrics-logs fi msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/vikunja-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (Canbiz) | Co-Author: CrazyWolf13 # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://vikunja.io/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os fetch_and_deploy_gh_release "vikunja" "go-vikunja/vikunja" "binary" msg_info "Setting up Vikunja" sed -i 's|^# \(service:\)|\1|' /etc/vikunja/config.yml sed -i "s|^ # \(publicurl: \).*| \1\"http://$LOCAL_IP\"|" /etc/vikunja/config.yml sed -i "0,/^ # \(timezone: \).*/s|| \1${tz}|" /etc/vikunja/config.yml systemctl enable -q --now vikunja msg_ok "Set up Vikunja" motd_ssh customize cleanup_lxc ================================================ FILE: install/wallabag-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://wallabag.org/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ nginx \ redis-server \ imagemagick msg_ok "Installed Dependencies" setup_mariadb MARIADB_DB_NAME="wallabag" MARIADB_DB_USER="wallabag" setup_mariadb_db PHP_VERSION="8.3" PHP_FPM="YES" PHP_MODULE="tidy" setup_php setup_composer NODE_VERSION="22" setup_nodejs fetch_and_deploy_gh_release "wallabag" "wallabag/wallabag" "prebuild" "latest" "/opt/wallabag" "wallabag-*.tar.gz" msg_info "Configuring Wallabag" cd /opt/wallabag SECRET_KEY="$(openssl rand -base64 32 | tr -dc 'a-zA-Z0-9' | cut -c1-32)" cat </opt/wallabag/app/config/parameters.yml parameters: database_driver: pdo_mysql database_host: 127.0.0.1 database_port: 3306 database_name: ${MARIADB_DB_NAME} database_user: ${MARIADB_DB_USER} database_password: ${MARIADB_DB_PASS} database_path: null database_table_prefix: wallabag_ database_socket: null database_charset: utf8mb4 domain_name: http://${LOCAL_IP}:8000 server_name: Wallabag mailer_dsn: null locale: en secret: ${SECRET_KEY} twofactor_auth: false twofactor_sender: no-reply@wallabag.org fosuser_registration: true fosuser_confirmation: false fos_oauth_server_access_token_lifetime: 3600 fos_oauth_server_refresh_token_lifetime: 1209600 from_email: no-reply@wallabag.org rss_limit: 50 rabbitmq_host: localhost rabbitmq_port: 5672 rabbitmq_user: guest rabbitmq_password: guest rabbitmq_prefetch_count: 10 redis_scheme: tcp redis_host: localhost redis_port: 6379 redis_path: null redis_password: null sentry_dsn: null EOF chown -R www-data:www-data /opt/wallabag msg_ok "Configured Wallabag" msg_info "Installing Wallabag (Patience)" export COMPOSER_ALLOW_SUPERUSER=1 export SYMFONY_ENV=prod cd /opt/wallabag $STD php bin/console wallabag:install --env=prod --no-interaction $STD php bin/console cache:clear --env=prod chown -R www-data:www-data /opt/wallabag chmod -R 755 /opt/wallabag/{var,web/assets} msg_ok "Installed Wallabag" msg_info "Configuring Nginx" cat <<'EOF' >/etc/nginx/sites-available/wallabag server { listen 8000; server_name _; root /opt/wallabag/web; add_header X-Frame-Options "SAMEORIGIN"; add_header X-Content-Type-Options "nosniff"; index app.php; charset utf-8; location / { try_files $uri /app.php$is_args$args; } location ~ ^/app\.php(/|$) { fastcgi_pass unix:/run/php/php8.3-fpm.sock; fastcgi_split_path_info ^(.+\.php)(/.*)$; include fastcgi_params; fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name; fastcgi_param DOCUMENT_ROOT $realpath_root; internal; } location ~ \.php$ { return 404; } location ~ /\.(?!well-known).* { deny all; } error_log /var/log/nginx/wallabag_error.log; access_log /var/log/nginx/wallabag_access.log; } EOF ln -sf /etc/nginx/sites-available/wallabag /etc/nginx/sites-enabled/ rm -f /etc/nginx/sites-enabled/default $STD systemctl reload nginx msg_ok "Configured Nginx" msg_info "Enabling Services" systemctl enable -q --now redis-server systemctl enable -q --now php8.3-fpm systemctl enable -q --now nginx msg_ok "Enabled Services" motd_ssh customize cleanup_lxc ================================================ FILE: install/wallos-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck # Co-Author: MickLesk (Canbiz) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/ellite/wallos source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os PHP_VERSION="8.4" PHP_APACHE="YES" setup_php fetch_and_deploy_gh_release "wallos" "ellite/Wallos" "tarball" msg_info "Installing Wallos (Patience)" cd /opt/wallos mv /opt/wallos/db/wallos.empty.db /opt/wallos/db/wallos.db chown -R www-data:www-data /opt/wallos chmod -R 755 /opt/wallos cat </etc/apache2/sites-available/wallos.conf ServerAdmin webmaster@localhost DocumentRoot /opt/wallos Options Indexes FollowSymLinks AllowOverride All Require all granted ErrorLog \${APACHE_LOG_DIR}/wallos_error.log CustomLog \${APACHE_LOG_DIR}/wallos_access.log combined EOF $STD a2ensite wallos.conf $STD a2dissite 000-default.conf $STD systemctl reload apache2 $STD curl http://localhost/endpoints/db/migrate.php msg_ok "Installed Wallos" msg_info "Setting up Crontabs" $STD apt-get -y install cron mkdir -p /var/log/cron cat </opt/wallos.cron 0 1 * * * php /opt/wallos/endpoints/cronjobs/updatenextpayment.php >> /var/log/cron/updatenextpayment.log 2>&1 0 2 * * * php /opt/wallos/endpoints/cronjobs/updateexchange.php >> /var/log/cron/updateexchange.log 2>&1 0 8 * * * php /opt/wallos/endpoints/cronjobs/sendcancellationnotifications.php >> /var/log/cron/sendcancellationnotifications.log 2>&1 0 9 * * * php /opt/wallos/endpoints/cronjobs/sendnotifications.php >> /var/log/cron/sendnotifications.log 2>&1 */2 * * * * php /opt/wallos/endpoints/cronjobs/sendverificationemails.php >> /var/log/cron/sendverificationemail.log 2>&1 */2 * * * * php /opt/wallos/endpoints/cronjobs/sendresetpasswordemails.php >> /var/log/cron/sendresetpasswordemails.log 2>&1 0 */6 * * * php /opt/wallos/endpoints/cronjobs/checkforupdates.php >> /var/log/cron/checkforupdates.log 2>&1 30 1 * * 1 php /opt/wallos/endpoints/cronjobs/storetotalyearlycost.php >> /var/log/cron/storetotalyearlycost.log 2>&1 EOF crontab /opt/wallos.cron msg_ok "Crontabs setup" motd_ssh customize cleanup_lxc ================================================ FILE: install/wanderer-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: rrole # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://wanderer.to source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os setup_go NODE_VERSION="22" setup_nodejs fetch_and_deploy_gh_release "meilisearch" "meilisearch/meilisearch" "singlefile" "latest" "/usr/bin" "meilisearch-linux-aarch64" mkdir -p /opt/wanderer/{source,data/pb_data,data/meili_data} fetch_and_deploy_gh_release "wanderer" "open-wanderer/wanderer" "tarball" "latest" "/opt/wanderer/source" msg_info "Installing wanderer (patience)" cd /opt/wanderer/source/db $STD go mod tidy $STD go build cd /opt/wanderer/source/web $STD npm ci -s vitest $STD npm ci --omit=dev $STD npm run build msg_ok "Installed wanderer" msg_info "Creating Service" MEILI_KEY=$(openssl rand -hex 32) POCKETBASE_KEY=$(openssl rand -hex 16) cat </opt/wanderer/.env ORIGIN=http://${LOCAL_IP}:3000 MEILI_HTTP_ADDR=${LOCAL_IP}:7700 MEILI_URL=http://${LOCAL_IP}:7700 MEILI_MASTER_KEY=${MEILI_KEY} PB_URL=${LOCAL_IP}:8090 PUBLIC_POCKETBASE_URL=http://${LOCAL_IP}:8090 PUBLIC_VALHALLA_URL=https://valhalla1.openstreetmap.de POCKETBASE_ENCRYPTION_KEY=${POCKETBASE_KEY} PB_DB_LOCATION=/opt/wanderer/data/pb_data MEILI_DB_PATH=/opt/wanderer/data/meili_data EOF cat </opt/wanderer/start.sh #!/usr/bin/env bash trap "kill 0" EXIT cd /opt/wanderer/source/search && meilisearch --experimental-dumpless-upgrade --master-key \$MEILI_MASTER_KEY & sleep 1 cd /opt/wanderer/source/db && ./pocketbase serve --http=\$PB_URL --dir=\$PB_DB_LOCATION & cd /opt/wanderer/source/web && node build & wait -n EOF chmod +x /opt/wanderer/start.sh cat <<'EOF' >/usr/local/bin/wanderer-pb #!/usr/bin/env bash set -a source /opt/wanderer/.env set +a cd /opt/wanderer/source/db exec ./pocketbase "$@" --dir="$PB_DB_LOCATION" EOF chmod +x /usr/local/bin/wanderer-pb cat </etc/systemd/system/wanderer-web.service [Unit] Description=wanderer After=network.target StartLimitIntervalSec=10 StartLimitBurst=5 [Service] Type=simple EnvironmentFile=/opt/wanderer/.env ExecStart=/usr/bin/bash /opt/wanderer/start.sh Restart=always RestartSec=1 [Install] WantedBy=multi-user.target EOF sleep 1 systemctl enable -q --now wanderer-web msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/warracker-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: bvdberg01 # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/sassanix/Warracker/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ build-essential \ libpq-dev \ nginx msg_ok "Installed Dependencies" PYTHON_VERSION="3.12" setup_uv PG_VERSION="17" setup_postgresql msg_info "Setup PostgreSQL" DB_NAME="warranty_db" DB_USER="warranty_user" DB_PASS=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | cut -c1-13) DB_ADMIN_USER="warracker_admin" DB_ADMIN_PASS=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | cut -c1-13) $STD sudo -u postgres psql -c "CREATE USER $DB_USER WITH PASSWORD '$DB_PASS';" $STD sudo -u postgres psql -c "CREATE USER $DB_ADMIN_USER WITH PASSWORD '$DB_ADMIN_PASS' SUPERUSER;" $STD sudo -u postgres psql -c "CREATE DATABASE $DB_NAME OWNER $DB_ADMIN_USER;" $STD sudo -u postgres psql -d "$DB_NAME" -c "GRANT USAGE ON SCHEMA public TO $DB_USER;" $STD sudo -u postgres psql -d "$DB_NAME" -c "GRANT CREATE ON SCHEMA public TO $DB_USER;" $STD sudo -u postgres psql -d "$DB_NAME" -c "ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO $DB_USER;" { echo "Application Credentials" echo "DB_NAME: $DB_NAME" echo "DB_USER: $DB_USER" echo "DB_PASS: $DB_PASS" echo "DB_ADMIN_USER: $DB_ADMIN_USER" echo "DB_ADMIN_PASS: $DB_ADMIN_PASS" } >>~/warracker.creds msg_ok "Setup PostgreSQL" fetch_and_deploy_gh_release "warracker" "sassanix/Warracker" "tarball" "latest" "/opt/warracker" msg_info "Installing Warracker" cd /opt/warracker/backend $STD uv venv --clear .venv $STD source .venv/bin/activate $STD uv pip install -r requirements.txt mv /opt/warracker/env.example /opt/.env sed -i \ -e "s/your_secure_database_password/$DB_PASS/" \ -e "s/your_secure_admin_password/$DB_ADMIN_PASS/" \ -e "s|^# DB_PORT=5432$|DB_HOST=127.0.0.1|" \ -e "s|your_very_secure_flask_secret_key_change_this_in_production|$(openssl rand -base64 32 | tr -d '\n')|" \ /opt/.env mkdir -p /data/uploads msg_ok "Installed Warracker" msg_info "Configuring Nginx" mv /opt/warracker/nginx.conf /etc/nginx/sites-available/warracker.conf sed -i \ -e "s|alias /var/www/html/locales/;|alias /opt/warracker/locales/;|" \ -e "s|/var/www/html|/opt/warracker/frontend|g" \ -e "s/client_max_body_size __NGINX_MAX_BODY_SIZE_CONFIG_VALUE__/client_max_body_size 32M/" \ /etc/nginx/sites-available/warracker.conf ln -s /etc/nginx/sites-available/warracker.conf /etc/nginx/sites-enabled/warracker.conf rm /etc/nginx/sites-enabled/default systemctl restart nginx msg_ok "Configured Nginx" msg_info "Creating systemd services" cat </etc/systemd/system/warrackermigration.service [Unit] Description=Warracker Migration Service After=network.target [Service] Type=oneshot WorkingDirectory=/opt/warracker/backend/migrations EnvironmentFile=/opt/.env ExecStart=/opt/warracker/backend/.venv/bin/python apply_migrations.py [Install] WantedBy=multi-user.target EOF cat </etc/systemd/system/warracker.service [Unit] Description=Warracker Service After=network.target warrackermigration.service Requires=warrackermigration.service [Service] WorkingDirectory=/opt/warracker EnvironmentFile=/opt/.env ExecStart=/opt/warracker/backend/.venv/bin/gunicorn --config /opt/warracker/backend/gunicorn_config.py backend:create_app() --bind 127.0.0.1:5000 Restart=always [Install] WantedBy=multi-user.target EOF systemctl enable -q --now warracker msg_ok "Started Warracker Services" motd_ssh customize cleanup_lxc ================================================ FILE: install/wastebin-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (Canbiz) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/matze/wastebin source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing dependencies" $STD apt install -y zstd msg_ok "Installed dependencies" msg_info "Installing Wastebin" temp_file=$(mktemp) RELEASE=$(curl -fsSL https://api.github.com/repos/matze/wastebin/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }') curl -fsSL "https://github.com/matze/wastebin/releases/download/${RELEASE}/wastebin_${RELEASE}_aarch64-unknown-linux-musl.tar.zst" -o "$temp_file" tar -xf "$temp_file" mkdir -p /opt/wastebin mv wastebin* /opt/wastebin/ chmod +x /opt/wastebin/wastebin chmod +x /opt/wastebin/wastebin-ctl mkdir -p /opt/wastebin-data cat </opt/wastebin-data/.env WASTEBIN_DATABASE_PATH=/opt/wastebin-data/wastebin.db WASTEBIN_CACHE_SIZE=1024 WASTEBIN_HTTP_TIMEOUT=30 WASTEBIN_SIGNING_KEY=$(openssl rand -hex 32) WASTEBIN_PASTE_EXPIRATIONS=0,600,3600=d,86400,604800,2419200,29030400 EOF rm -f "$temp_file" echo "${RELEASE}" >"/opt/${APPLICATION}_version.txt" msg_ok "Installed Wastebin" msg_info "Creating Service" cat </etc/systemd/system/wastebin.service [Unit] Description=Start Wastebin Service After=network.target [Service] User=root WorkingDirectory=/opt/wastebin ExecStart=/root/.cargo/bin/cargo run --release --quiet [Install] WantedBy=multi-user.target EOF systemctl enable -q --now wastebin msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/watcharr-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: Slaviša Arežina (tremor021) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/sbondCo/Watcharr source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y gcc msg_ok "Installed Dependencies" setup_go NODE_VERSION="22" setup_nodejs fetch_and_deploy_gh_release "watcharr" "sbondCo/Watcharr" "tarball" msg_info "Setup Watcharr" cd /opt/watcharr $STD npm i $STD npm run build mv ./build ./server/ui cd server export CGO_ENABLED=1 GOOS=linux $STD go mod download $STD go build -o ./watcharr msg_ok "Setup Watcharr" msg_info "Creating Service" cat </etc/systemd/system/watcharr.service [Unit] Description=Watcharr Service After=network.target [Service] WorkingDirectory=/opt/watcharr/server ExecStart=/opt/watcharr/server/watcharr Restart=always User=root [Install] WantedBy=multi-user.target EOF systemctl enable -q --now watcharr msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/watchyourlan-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/aceberg/WatchYourLAN source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ arp-scan \ ieee-data \ libwww-perl msg_ok "Installed Dependencies" fetch_and_deploy_gh_release "watchyourlan" "aceberg/WatchYourLAN" "binary" msg_info "Configuring WatchYourLAN" mkdir /data cat </data/config.yaml arp_timeout: "500" auth: false auth_expire: 7d auth_password: "" auth_user: "" color: dark dbpath: /data/db.sqlite guiip: 0.0.0.0 guiport: "8840" history_days: "30" iface: eth0 ignoreip: "no" loglevel: verbose shoutrrr_url: "" theme: solar timeout: 60 EOF msg_ok "Configured WatchYourLAN" msg_info "Creating Service" sed -i 's|/etc/watchyourlan/config.yaml|/data/config.yaml|' /lib/systemd/system/watchyourlan.service systemctl enable -q --now watchyourlan msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/wavelog-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: Don Locke (DonLocke) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/wavelog/wavelog source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os PHP_VERSION="8.4" PHP_APACHE="YES" PHP_MAX_EXECUTION_TIME="600" setup_php setup_mariadb MARIADB_DB_NAME="wavelog" MARIADB_DB_USER="waveloguser" setup_mariadb_db fetch_and_deploy_gh_release "wavelog" "wavelog/wavelog" "tarball" msg_info "Configuring Wavelog" chown -R www-data:www-data /opt/wavelog/ find /opt/wavelog/ -type d -exec chmod 755 {} \; find /opt/wavelog/ -type f -exec chmod 664 {} \; msg_ok "Configured Wavelog" msg_info "Creating Service" cat </etc/apache2/sites-available/wavelog.conf ServerAdmin webmaster@localhost DocumentRoot /opt/wavelog Options Indexes FollowSymLinks AllowOverride All Require all granted ErrorLog /var/log/apache2/error.log CustomLog /var/log/apache2/access.log combined EOF $STD a2ensite wavelog.conf $STD a2dissite 000-default.conf $STD systemctl reload apache2 msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/wazuh-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2024 community-scripts ORG # Author: Omar Minaya # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://wazuh.com/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os RELEASE=$(curl -fsSL https://api.github.com/repos/wazuh/wazuh/releases/latest | grep '"tag_name"' | awk -F '"' '{print substr($4, 2, length($2)-4)}') msg_warn "WARNING: This script will run an external installer from a third-party source (https://wazuh.com/)." msg_warn "The following code is NOT maintained or audited by our repository." msg_warn "If you have any doubts or concerns, please review the installer code before proceeding:" msg_custom "${TAB3}${GATEWAY}${BGN}${CL}" "\e[1;34m" "→ https://packages.wazuh.com/$RELEASE/wazuh-install.sh " echo read -r -p "${TAB3}Do you want to continue? [y/N]: " CONFIRM if [[ ! "$CONFIRM" =~ ^([yY][eE][sS]|[yY])$ ]]; then msg_error "Aborted by user. No changes have been made." exit 10 fi msg_info "Setup Wazuh" curl -fsSL https://packages.wazuh.com/$RELEASE/wazuh-install.sh -o wazuh-install.sh chmod +x wazuh-install.sh if [ "$STD" = "silent" ]; then bash wazuh-install.sh -a >>~/wazuh-install.output else bash wazuh-install.sh -a | tee -a ~/wazuh-install.output fi cat ~/wazuh-install.output | grep -E "User|Password" | awk '{$1=$1};1' | sed '1i wazuh-credentials' >~/wazuh.creds rm -f wazuh-*.sh rm -f ~/wazuh-install.output msg_ok "Setup Wazuh" # Fix LXC container false positives in rootcheck # When running Wazuh in an LXC container, /dev/.lxc/* paths trigger false alerts if [ -d /dev/.lxc ]; then msg_info "Adding LXC rootcheck exclusion" sed -i '/<\/rootcheck>/i \ /dev/.lxc' /var/ossec/etc/ossec.conf msg_ok "Added LXC rootcheck exclusion" fi motd_ssh customize cleanup_lxc ================================================ FILE: install/wealthfolio-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: CrazyWolf13 # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://wealthfolio.app/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ pkg-config \ libssl-dev \ build-essential \ libsqlite3-dev \ argon2 msg_ok "Installed Dependencies" setup_rust NODE_VERSION="24" NODE_MODULE="pnpm" setup_nodejs fetch_and_deploy_gh_release "wealthfolio" "afadil/wealthfolio" "tarball" msg_info "Building Frontend (patience)" cd /opt/wealthfolio export BUILD_TARGET=web $STD pnpm install --frozen-lockfile $STD pnpm --filter frontend... build msg_ok "Built Frontend" msg_info "Building Backend (patience)" source ~/.cargo/env $STD cargo build --release --manifest-path apps/server/Cargo.toml cp /opt/wealthfolio/target/release/wealthfolio-server /usr/local/bin/wealthfolio-server chmod +x /usr/local/bin/wealthfolio-server msg_ok "Built Backend" msg_info "Configuring Wealthfolio" mkdir -p /opt/wealthfolio_data SECRET_KEY=$(openssl rand -base64 32) WF_PASSWORD=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | cut -c1-16) WF_PASSWORD_HASH=$(echo -n "$WF_PASSWORD" | argon2 "$(openssl rand -base64 16)" -id -e) cat </opt/wealthfolio/.env WF_LISTEN_ADDR=0.0.0.0:8080 WF_DB_PATH=/opt/wealthfolio_data/wealthfolio.db WF_SECRET_KEY=${SECRET_KEY} WF_AUTH_PASSWORD_HASH=${WF_PASSWORD_HASH} WF_STATIC_DIR=/opt/wealthfolio/dist WF_REQUEST_TIMEOUT_MS=30000 WF_CORS_ALLOW_ORIGINS=http://${LOCAL_IP}:8080 EOF echo "WF_PASSWORD=${WF_PASSWORD}" >~/wealthfolio.creds msg_ok "Configured Wealthfolio" msg_info "Cleaning Up" rm -rf /opt/wealthfolio/target rm -rf /root/.cargo/registry rm -rf /opt/wealthfolio/node_modules msg_ok "Cleaned Up" msg_info "Creating Service" cat </etc/systemd/system/wealthfolio.service [Unit] Description=Wealthfolio Investment Tracker After=network.target [Service] Type=simple User=root WorkingDirectory=/opt/wealthfolio EnvironmentFile=/opt/wealthfolio/.env ExecStart=/usr/local/bin/wealthfolio-server Restart=on-failure RestartSec=5 [Install] WantedBy=multi-user.target EOF systemctl enable -q --now wealthfolio msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/web-check-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: CrazyWolf13 # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/lissy93/web-check source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" export DEBIAN_FRONTEND=noninteractive $STD apt -y install --no-install-recommends \ git \ traceroute \ build-essential \ xvfb \ dbus \ xorg \ gtk2-engines-pixbuf \ dbus-x11 \ xfonts-base \ xfonts-100dpi \ xfonts-75dpi \ xfonts-scalable \ imagemagick \ x11-apps msg_ok "Installed Dependencies" NODE_VERSION="22" NODE_MODULE="yarn" setup_nodejs msg_info "Setup Python3" $STD apt install -y python3 rm -rf /usr/lib/python3.*/EXTERNALLY-MANAGED msg_ok "Setup Python3" msg_info "Installing Chromium" curl -fsSL https://dl-ssl.google.com/linux/linux_signing_key.pub | gpg --dearmor -o /usr/share/keyrings/google-chrome-keyring.gpg cat </dev/null Types: deb URIs: http://dl.google.com/linux/chrome/deb/ Suites: stable Components: main Architectures: arm64 Signed-By: /usr/share/keyrings/google-chrome-keyring.gpg EOF $STD apt update $STD apt -y install \ chromium \ libxss1 \ lsb-release msg_ok "Installed Chromium" msg_info "Setting up Chromium" /usr/bin/chromium --no-sandbox --version >/etc/chromium-version chmod 755 /usr/bin/chromium msg_ok "Setup Chromium" fetch_and_deploy_gh_release "web-check" "Lissy93/web-check" "tarball" msg_info "Installing Web-Check (Patience)" cd /opt/web-check cat <<'EOF' >/opt/web-check/.env CHROME_PATH=/usr/bin/chromium PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromium PUPPETEER_SKIP_DOWNLOAD='true' HEADLESS=true GOOGLE_CLOUD_API_KEY='' REACT_APP_SHODAN_API_KEY='' REACT_APP_WHO_API_KEY='' SECURITY_TRAILS_API_KEY='' CLOUDMERSIVE_API_KEY='' TRANCO_USERNAME='' TRANCO_API_KEY='' URL_SCAN_API_KEY='' BUILT_WITH_API_KEY='' TORRENT_IP_API_KEY='' PORT='3000' DISABLE_GUI='false' API_TIMEOUT_LIMIT='10000' API_CORS_ORIGIN='*' API_ENABLE_RATE_LIMIT='false' REACT_APP_API_ENDPOINT='/api' ENABLE_ANALYTICS='false' EOF $STD yarn install --frozen-lockfile --network-timeout 100000 msg_ok "Installed Web-Check" msg_info "Building Web-Check" $STD yarn build --production msg_ok "Built Web-Check" msg_info "Creating Service" cat <<'EOF' >/opt/run_web-check.sh #!/bin/bash SCREEN_RESOLUTION="1280x1024x24" if ! systemctl is-active --quiet dbus; then echo "Warning: dbus service is not running. Some features may not work properly." fi [[ -z "${DISPLAY}" ]] && export DISPLAY=":99" Xvfb "${DISPLAY}" -screen 0 "${SCREEN_RESOLUTION}" & XVFB_PID=$! sleep 2 cd /opt/web-check exec yarn start EOF chmod +x /opt/run_web-check.sh cat <<'EOF' >/etc/systemd/system/web-check.service [Unit] Description=Web Check Service After=network.target [Service] Type=simple User=root Group=root WorkingDirectory=/opt/web-check EnvironmentFile=/opt/web-check/.env ExecStartPre=/bin/bash -c "service dbus start || true" ExecStartPre=/bin/bash -c "if ! pgrep -f 'Xvfb.*:99' > /dev/null; then Xvfb :99 -screen 0 1280x1024x24 & fi" ExecStart=/opt/run_web-check.sh Restart=on-failure Environment=DISPLAY=:99 [Install] WantedBy=multi-user.target EOF systemctl enable -q --now web-check msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/wger-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: Slaviša Arežina (tremor021) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/wger-project/wger source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ build-essential \ nginx \ redis-server \ libpq-dev msg_ok "Installed Dependencies" NODE_VERSION="22" NODE_MODULE="sass" setup_nodejs setup_uv PG_VERSION="16" setup_postgresql PG_DB_NAME="wger" PG_DB_USER="wger" setup_postgresql_db fetch_and_deploy_gh_release "wger" "wger-project/wger" "tarball" msg_info "Setting up wger" mkdir -p /opt/wger/{static,media} chmod o+w /opt/wger/media cd /opt/wger $STD corepack enable $STD npm install $STD npm run build:css:sass $STD uv venv $STD uv pip install . --group docker SECRET_KEY=$(openssl rand -base64 40) cat </opt/wger/.env DJANGO_SETTINGS_MODULE=settings.main PYTHONPATH=/opt/wger DJANGO_DB_ENGINE=django.db.backends.postgresql DJANGO_DB_DATABASE=${PG_DB_NAME} DJANGO_DB_USER=${PG_DB_USER} DJANGO_DB_PASSWORD=${PG_DB_PASS} DJANGO_DB_HOST=localhost DJANGO_DB_PORT=5432 DATABASE_URL=postgresql://${PG_DB_USER}:${PG_DB_PASS}@localhost:5432/${PG_DB_NAME} DJANGO_MEDIA_ROOT=/opt/wger/media DJANGO_STATIC_ROOT=/opt/wger/static DJANGO_STATIC_URL=/static/ ALLOWED_HOSTS=${LOCAL_IP},localhost,127.0.0.1 CSRF_TRUSTED_ORIGINS=http://${LOCAL_IP}:3000 USE_X_FORWARDED_HOST=True SECURE_PROXY_SSL_HEADER=HTTP_X_FORWARDED_PROTO,http DJANGO_CACHE_BACKEND=django_redis.cache.RedisCache DJANGO_CACHE_LOCATION=redis://127.0.0.1:6379/1 DJANGO_CACHE_TIMEOUT=300 DJANGO_CACHE_CLIENT_CLASS=django_redis.client.DefaultClient AXES_CACHE_ALIAS=default USE_CELERY=True CELERY_BROKER=redis://127.0.0.1:6379/2 CELERY_BACKEND=redis://127.0.0.1:6379/2 SITE_URL=http://${LOCAL_IP}:3000 SECRET_KEY=${SECRET_KEY} EOF set -a && source /opt/wger/.env && set +a $STD uv run wger bootstrap $STD uv run python manage.py collectstatic --no-input cat </etc/systemd/system/wger.service [Unit] Description=wger Gunicorn After=network.target [Service] User=root WorkingDirectory=/opt/wger EnvironmentFile=/opt/wger/.env ExecStart=/opt/wger/.venv/bin/gunicorn \ --bind 127.0.0.1:8000 \ --workers 3 \ --threads 2 \ --timeout 120 \ wger.wsgi:application Restart=always [Install] WantedBy=multi-user.target EOF cat </etc/systemd/system/celery.service [Unit] Description=wger Celery Worker After=network.target redis-server.service Requires=redis-server.service [Service] WorkingDirectory=/opt/wger EnvironmentFile=/opt/wger/.env ExecStart=/opt/wger/.venv/bin/celery -A wger worker -l info Restart=always [Install] WantedBy=multi-user.target EOF mkdir -p /var/lib/wger/celery chmod 700 /var/lib/wger/celery cat </etc/systemd/system/celery-beat.service [Unit] Description=wger Celery Beat After=network.target redis-server.service Requires=redis-server.service [Service] WorkingDirectory=/opt/wger EnvironmentFile=/opt/wger/.env ExecStart=/opt/wger/.venv/bin/celery -A wger beat -l info \ --schedule /var/lib/wger/celery/celerybeat-schedule Restart=always [Install] WantedBy=multi-user.target EOF cat <<'EOF' >/etc/nginx/sites-available/wger server { listen 3000; server_name _; client_max_body_size 20M; location /static/ { alias /opt/wger/static/; expires 30d; } location /media/ { alias /opt/wger/media/; } location / { proxy_pass http://127.0.0.1:8000; proxy_set_header Host $http_host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_redirect off; } } EOF $STD rm -f /etc/nginx/sites-enabled/default $STD ln -sf /etc/nginx/sites-available/wger /etc/nginx/sites-enabled/wger systemctl enable -q --now redis-server nginx wger celery celery-beat systemctl restart nginx msg_ok "Created Config and Services" motd_ssh customize cleanup_lxc ================================================ FILE: install/whisparr-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/Whisparr/Whisparr source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y sqlite3 libicu76 msg_ok "Installed Dependencies" fetch_and_deploy_from_url "https://whisparr.servarr.com/v1/update/nightly/updatefile?os=linux&runtime=netcore&arch=arm64" /opt/Whisparr msg_info "Configuring Whisparr" mkdir -p /var/lib/whisparr/ chmod 775 /var/lib/whisparr/ chmod 775 /opt/Whisparr msg_ok "Configured Whisparr" msg_info "Creating Service" cat </etc/systemd/system/whisparr.service [Unit] Description=whisparr Daemon After=syslog.target network.target [Service] UMask=0002 Type=simple ExecStart=/opt/Whisparr/Whisparr -nobrowser -data=/var/lib/whisparr/ TimeoutStopSec=20 KillMode=process Restart=on-failure [Install] WantedBy=multi-user.target EOF systemctl enable -q --now whisparr msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/whodb-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://whodb.com/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os fetch_and_deploy_gh_release "whodb" "clidey/whodb" "singlefile" "latest" "/opt/whodb" "whodb-*-linux-amd64" msg_info "Creating Service" cat </etc/systemd/system/whodb.service [Unit] Description=WhoDB Database Management After=network.target [Service] Type=simple User=root WorkingDirectory=/opt/whodb ExecStart=/opt/whodb/whodb Restart=on-failure RestartSec=5 [Install] WantedBy=multi-user.target EOF systemctl enable -q --now whodb msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/wikijs-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://js.wiki/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y git msg_ok "Installed Dependencies" NODE_VERSION="22" NODE_MODULE="yarn,node-gyp" setup_nodejs PG_VERSION="17" setup_postgresql PG_DB_NAME="wiki" PG_DB_USER="wikijs_user" PG_DB_EXTENSIONS="pg_trgm" setup_postgresql_db fetch_and_deploy_gh_release "wikijs" "requarks/wiki" "prebuild" "latest" "/opt/wikijs" "wiki-js.tar.gz" msg_info "Configuring Wiki.js" mv /opt/wikijs/config.sample.yml /opt/wikijs/config.yml sed -i -E 's|^( *user: ).*|\1'"$PG_DB_USER"'|' /opt/wikijs/config.yml sed -i -E 's|^( *pass: ).*|\1'"$PG_DB_PASS"'|' /opt/wikijs/config.yml msg_ok "Configured Wiki.js" msg_info "Creating Service" cat </etc/systemd/system/wikijs.service [Unit] Description=Wiki.js After=network.target [Service] Type=simple ExecStart=/usr/bin/node server Restart=always User=root Environment=NODE_ENV=production WorkingDirectory=/opt/wikijs [Install] WantedBy=multi-user.target EOF systemctl enable -q --now wikijs msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/wireguard-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.wireguard.com/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y git msg_ok "Installed Dependencies" msg_info "Installing WireGuard" $STD apt install -y wireguard wireguard-tools net-tools iptables DEBIAN_FRONTEND=noninteractive apt -o Dpkg::Options::="--force-confnew" install -y iptables-persistent &>/dev/null $STD netfilter-persistent reload msg_ok "Installed WireGuard" read -r -p "${TAB3}Would you like to add WGDashboard? " prompt if [[ "${prompt,,}" =~ ^(y|yes)$ ]]; then git clone -q https://github.com/WGDashboard/WGDashboard.git /etc/wgdashboard msg_info "Installing WGDashboard" cd /etc/wgdashboard/src chmod u+x wgd.sh $STD ./wgd.sh install . /etc/os-release if [ "$VERSION_CODENAME" = "trixie" ]; then echo "net.ipv4.ip_forward=1" >>/etc/sysctl.d/sysctl.conf $STD sysctl -p /etc/sysctl.d/sysctl.conf else echo "net.ipv4.ip_forward=1" >>/etc/sysctl.conf $STD sysctl -p /etc/sysctl.conf fi msg_ok "Installed WGDashboard" msg_info "Create Example Config for WGDashboard" private_key=$(wg genkey) cat </etc/wireguard/wg0.conf [Interface] PrivateKey = ${private_key} Address = 10.0.0.1/24 SaveConfig = true PostUp = iptables -A FORWARD -i wg0 -j ACCEPT; iptables -A FORWARD -o wg0 -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE; PostDown = iptables -D FORWARD -i wg0 -j ACCEPT; iptables -D FORWARD -o wg0 -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE; ListenPort = 51820 EOF msg_ok "Created Example Config for WGDashboard" msg_info "Creating Service" cat </etc/systemd/system/wg-dashboard.service [Unit] After=syslog.target network-online.target Wants=wg-quick.target ConditionPathIsDirectory=/etc/wireguard [Service] Type=forking PIDFile=/etc/wgdashboard/src/gunicorn.pid WorkingDirectory=/etc/wgdashboard/src ExecStart=/etc/wgdashboard/src/wgd.sh start ExecStop=/etc/wgdashboard/src/wgd.sh stop ExecReload=/etc/wgdashboard/src/wgd.sh restart TimeoutSec=120 PrivateTmp=yes Restart=always [Install] WantedBy=multi-user.target EOF systemctl enable -q --now wg-dashboard msg_ok "Created Service" fi motd_ssh customize cleanup_lxc ================================================ FILE: install/wishlist-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: Dunky13 # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/cmintey/wishlist source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing dependencies" $STD apt install -y \ build-essential \ openssl \ caddy msg_ok "Installed dependencies" NODE_VERSION="24" NODE_MODULE="pnpm@10" setup_nodejs fetch_and_deploy_gh_release "wishlist" "cmintey/wishlist" "tarball" LATEST_APP_VERSION=$(get_latest_github_release "cmintey/wishlist") msg_info "Installing Wishlist" cd /opt/wishlist cp .env.example .env sed -i "s|^ORIGIN=.*|ORIGIN=http://${LOCAL_IP}:3280|" /opt/wishlist/.env echo "" >>/opt/wishlist/.env echo "NODE_ENV=production" >>/opt/wishlist/.env $STD pnpm install --frozen-lockfile $STD pnpm svelte-kit sync $STD pnpm prisma generate sed -i 's|/usr/src/app/|/opt/wishlist/|g' $(grep -rl '/usr/src/app/' /opt/wishlist) export VERSION="v${LATEST_APP_VERSION}" export SHA="v${LATEST_APP_VERSION}" $STD pnpm run build $STD pnpm prune --prod chmod +x /opt/wishlist/entrypoint.sh mkdir -p /opt/wishlist/uploads mkdir -p /opt/wishlist/data msg_ok "Installed Wishlist" msg_info "Creating Service" cat </etc/systemd/system/wishlist.service [Unit] Description=Wishlist Service After=network.target [Service] WorkingDirectory=/opt/wishlist EnvironmentFile=/opt/wishlist/.env ExecStart=/usr/bin/env sh -c './entrypoint.sh' Restart=on-failure [Install] WantedBy=multi-user.target EOF systemctl enable -q --now wishlist msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/wizarr-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: vhsdream # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/wizarrrr/wizarr source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y sqlite3 msg_ok "Installed Dependencies" setup_uv NODE_VERSION="22" setup_nodejs fetch_and_deploy_gh_release "wizarr" "wizarrrr/wizarr" "tarball" msg_info "Configure Wizarr" cd /opt/wizarr $STD /usr/local/bin/uv sync --frozen $STD /usr/local/bin/uv run --frozen pybabel compile -d app/translations $STD npm --prefix app/static install $STD npm --prefix app/static run build:css mkdir -p ./.cache cat </opt/wizarr/.env FLASK_ENV=production GUNICORN_WORKERS=4 APP_URL=http://${LOCAL_IP} DISABLE_BUILTIN_AUTH=false LOG_LEVEL=INFO APP_VERSION=v$(get_latest_github_release "wizarrrr/wizarr") EOF cat </opt/wizarr/start.sh #!/usr/bin/env bash uv run --frozen gunicorn \ --config gunicorn.conf.py \ --preload \ --bind 0.0.0.0:5690 \ --umask 007 \ run:app EOF chmod u+x /opt/wizarr/start.sh msg_ok "Configure Wizarr" msg_info "Creating Service" cat </etc/systemd/system/wizarr.service [Unit] Description=Wizarr Service After=network.target [Service] Type=simple WorkingDirectory=/opt/wizarr EnvironmentFile=/opt/wizarr/.env ExecStart=/opt/wizarr/start.sh Restart=on-abnormal [Install] WantedBy=multi-user.target EOF msg_ok "Created Service" msg_info "Running DB upgrade" export FLASK_SKIP_SCHEDULER=true $STD /usr/local/bin/uv run --frozen flask db upgrade msg_ok "DB upgrade complete" systemctl enable -q --now wizarr motd_ssh customize cleanup_lxc ================================================ FILE: install/wordpress-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (Canbiz) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://wordpress.org/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os PHP_VERSION="8.4" PHP_FPM="YES" PHP_APACHE="YES" PHP_MODULE="snmp,imap" setup_php setup_mariadb MARIADB_DB_NAME="wordpress_db" MARIADB_DB_USER="wordpress" setup_mariadb_db fetch_and_deploy_from_url "https://wordpress.org/latest.zip" /var/www/html/wordpress msg_info "Installing Wordpress (Patience)" chown -R www-data:www-data /var/www/html/wordpress cd /var/www/html/wordpress find . -type d -exec chmod 755 {} \; find . -type f -exec chmod 644 {} \; mv wp-config-sample.php wp-config.php sed -i -e "s|^define( 'DB_NAME', '.*' );|define( 'DB_NAME', '$MARIADB_DB_NAME' );|" \ -e "s|^define( 'DB_USER', '.*' );|define( 'DB_USER', '$MARIADB_DB_USER' );|" \ -e "s|^define( 'DB_PASSWORD', '.*' );|define( 'DB_PASSWORD', '$MARIADB_DB_PASS' );|" \ /var/www/html/wordpress/wp-config.php msg_ok "Installed Wordpress" msg_info "Setup Services" cat </etc/apache2/sites-available/wordpress.conf ServerName yourdomain.com DocumentRoot /var/www/html/wordpress AllowOverride All ErrorLog \${APACHE_LOG_DIR}/error.log CustomLog \${APACHE_LOG_DIR}/access.log combined EOF $STD a2ensite wordpress.conf $STD a2dissite 000-default.conf systemctl reload apache2 msg_ok "Created Services" motd_ssh customize cleanup_lxc ================================================ FILE: install/writefreely-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: StellaeAlis # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/writefreely/writefreely source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y crudini msg_ok "Installed Dependencies" setup_mariadb MARIADB_DB_NAME="writefreely" MARIADB_DB_USER="writefreely" setup_mariadb_db fetch_and_deploy_gh_release "writefreely" "writefreely/writefreely" "prebuild" "latest" "/opt/writefreely" "writefreely_*_linux_arm64.tar.gz" msg_info "Setting up WriteFreely" cd /opt/writefreely $STD ./writefreely config generate $STD ./writefreely keys generate msg_ok "Setup WriteFreely" msg_info "Configuring WriteFreely" $STD crudini --set config.ini server port 80 $STD crudini --set config.ini server bind $LOCAL_IP $STD crudini --set config.ini database username $MARIADB_DB_USER $STD crudini --set config.ini database password $MARIADB_DB_PASS $STD crudini --set config.ini database database $MARIADB_DB_NAME $STD crudini --set config.ini app host http://$LOCAL_IP:80 $STD ./writefreely db init ln -s /opt/writefreely/writefreely /usr/local/bin/writefreely msg_ok "Configured WriteFreely" msg_info "Creating Service" cat </etc/systemd/system/writefreely.service [Unit] Description=WriteFreely Service After=syslog.target network.target [Service] Type=simple User=root WorkingDirectory=/opt/writefreely ExecStart=/opt/writefreely/writefreely Restart=on-failure RestartSec=5 [Install] WantedBy=multi-user.target EOF systemctl enable -q --now writefreely msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/yamtrack-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/FuzzyGrim/Yamtrack source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ nginx \ redis-server msg_ok "Installed Dependencies" PG_VERSION="16" setup_postgresql PG_DB_NAME="yamtrack" PG_DB_USER="yamtrack" setup_postgresql_db PYTHON_VERSION="3.12" setup_uv fetch_and_deploy_gh_release "yamtrack" "FuzzyGrim/Yamtrack" "tarball" msg_info "Installing Python Dependencies" cd /opt/yamtrack $STD uv venv .venv $STD uv pip install --no-cache-dir -r requirements.txt msg_ok "Installed Python Dependencies" msg_info "Configuring Yamtrack" SECRET=$(openssl rand -hex 32) cat </opt/yamtrack/src/.env SECRET=${SECRET} DB_HOST=localhost DB_NAME=${PG_DB_NAME} DB_USER=${PG_DB_USER} DB_PASSWORD=${PG_DB_PASS} DB_PORT=5432 REDIS_URL=redis://localhost:6379 URLS=http://${LOCAL_IP}:8000 EOF cd /opt/yamtrack/src $STD /opt/yamtrack/.venv/bin/python manage.py migrate $STD /opt/yamtrack/.venv/bin/python manage.py collectstatic --noinput msg_ok "Configured Yamtrack" msg_info "Configuring Nginx" rm -f /etc/nginx/sites-enabled/default /etc/nginx/sites-available/default cp /opt/yamtrack/nginx.conf /etc/nginx/nginx.conf sed -i 's|user abc;|user www-data;|' /etc/nginx/nginx.conf sed -i 's|pid /tmp/nginx.pid;|pid /run/nginx.pid;|' /etc/nginx/nginx.conf sed -i 's|/yamtrack/staticfiles/|/opt/yamtrack/src/staticfiles/|' /etc/nginx/nginx.conf sed -i 's|error_log /dev/stderr|error_log /var/log/nginx/error.log|' /etc/nginx/nginx.conf sed -i 's|access_log /dev/stdout|access_log /var/log/nginx/access.log|' /etc/nginx/nginx.conf $STD nginx -t systemctl enable -q nginx $STD systemctl restart nginx msg_ok "Configured Nginx" msg_info "Creating Services" cat </etc/systemd/system/yamtrack.service [Unit] Description=Yamtrack Gunicorn After=network.target postgresql.service redis-server.service Requires=postgresql.service redis-server.service [Service] Type=simple WorkingDirectory=/opt/yamtrack/src ExecStart=/opt/yamtrack/.venv/bin/gunicorn config.wsgi:application -b 127.0.0.1:8001 -w 2 --timeout 120 Restart=on-failure RestartSec=5 [Install] WantedBy=multi-user.target EOF cat </etc/systemd/system/yamtrack-celery.service [Unit] Description=Yamtrack Celery Worker After=network.target postgresql.service redis-server.service yamtrack.service Requires=postgresql.service redis-server.service [Service] Type=simple WorkingDirectory=/opt/yamtrack/src ExecStart=/opt/yamtrack/.venv/bin/celery -A config worker --beat --scheduler django --loglevel INFO Restart=on-failure RestartSec=5 [Install] WantedBy=multi-user.target EOF systemctl enable -q --now redis-server yamtrack yamtrack-celery msg_ok "Created Services" motd_ssh customize cleanup_lxc ================================================ FILE: install/yourls-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://yourls.org/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y nginx msg_ok "Installed Dependencies" setup_mariadb MARIADB_DB_NAME="yourls" MARIADB_DB_USER="yourls" setup_mariadb_db PHP_VERSION="8.3" PHP_FPM="YES" PHP_MODULE="mysql,mbstring,gd,xml,curl" setup_php fetch_and_deploy_gh_release "yourls" "YOURLS/YOURLS" "tarball" msg_info "Configuring YOURLS" COOKIEKEY=$(openssl rand -hex 24) YOURLS_PASS=$(openssl rand -base64 12 | tr -dc 'a-zA-Z0-9' | cut -c1-16) cat </opt/yourls/user/config.php '${YOURLS_PASS}', ]; define( 'YOURLS_URL_CONVERT', 36 ); define( 'YOURLS_DEBUG', false ); EOF chown -R www-data:www-data /opt/yourls msg_ok "Configured YOURLS" msg_info "Configuring Nginx" cat </etc/nginx/sites-available/yourls server { listen 80 default_server; server_name _; root /opt/yourls; index index.php; location / { try_files \$uri \$uri/ /yourls-loader.php\$is_args\$args; } location ~ \.php\$ { try_files \$uri =404; fastcgi_split_path_info ^(.+\.php)(/.+)\$; fastcgi_pass unix:/run/php/php8.3-fpm.sock; fastcgi_index index.php; include fastcgi_params; fastcgi_param SCRIPT_FILENAME \$document_root\$fastcgi_script_name; fastcgi_param PATH_INFO \$fastcgi_path_info; } location ~* \.(jpg|jpeg|gif|css|png|js|ico|woff|woff2)\$ { access_log off; expires max; } location ~ /\.ht { deny all; } } EOF ln -sf /etc/nginx/sites-available/yourls /etc/nginx/sites-enabled/yourls rm -f /etc/nginx/sites-enabled/default $STD nginx -t systemctl enable -q --now nginx systemctl reload nginx msg_ok "Configured Nginx" motd_ssh customize cleanup_lxc ================================================ FILE: install/yt-dlp-webui-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: CrazyWolf13 # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/marcopiovanello/yt-dlp-web-ui source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y ffmpeg msg_ok "Installed Dependencies" fetch_and_deploy_gh_release "yt-dlp-webui" "marcopiovanello/yt-dlp-web-ui" "singlefile" "latest" "/usr/local/bin" "yt-dlp-webui_linux-arm64" fetch_and_deploy_gh_release "yt-dlp" "yt-dlp/yt-dlp" "singlefile" "latest" "/usr/local/bin" "yt-dlp" msg_info "Setting up YT-DLP-WEBUI" mkdir -p /opt/yt-dlp-webui mkdir /downloads RPC_PASSWORD=$(openssl rand -base64 16) { echo "yt-dlp-webui-Credentials" echo "Username: admin" echo "Password: ${RPC_PASSWORD}" } >>~/yt-dlp-webui.creds cat </opt/yt-dlp-webui/config.conf # Host where server will listen at (default: "0.0.0.0") #host: 0.0.0.0 # Port where server will listen at (default: 3033) port: 3033 # Directory where downloaded files will be stored (default: ".") downloadPath: /downloads # [optional] Enable RPC authentication (requires username and password) require_auth: true username: admin password: ${RPC_PASSWORD} # [optional] The download queue size (default: logical cpu cores) queue_size: 4 # min. 2 # [optional] Full path to the yt-dlp (default: "yt-dlp") downloaderPath: /usr/local/bin/yt-dlp # [optional] Enable file based logging with rotation (default: false) #enable_file_logging: false # [optional] Directory where the log file will be stored (default: ".") #log_path: . # [optional] Directory where the session database file will be stored (default: ".") #session_file_path: . # [optional] Path where the sqlite database will be created/opened (default: "./local.db") #local_database_path # [optional] Path where a custom frontend will be loaded (instead of the embedded one) #frontend_path: ./web/solid-frontend EOF msg_ok "Set up YT-DLP-WEBUI" msg_info "Creating Service" cat </etc/systemd/system/yt-dlp-webui.service [Unit] Description=yt-dlp-webui service file After=network.target [Service] ExecStart=/usr/local/bin/yt-dlp-webui --conf /opt/yt-dlp-webui/config.conf [Install] WantedBy=multi-user.target EOF systemctl enable -q --now yt-dlp-webui msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/yubal-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: Crazywolf13 # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/guillevc/yubal source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ build-essential \ libssl-dev \ libffi-dev \ python3-dev \ ffmpeg \ git msg_ok "Installed Dependencies" msg_info "Installing Bun" export BUN_INSTALL=/opt/bun curl -fsSL https://bun.sh/install | $STD bash ln -sf /opt/bun/bin/bun /usr/local/bin/bun ln -sf /opt/bun/bin/bunx /usr/local/bin/bunx msg_ok "Installed Bun" UV_VERSION="0.7.19" PYTHON_VERSION="3.12" setup_uv msg_info "Installing Deno" $STD sh -c "curl -fsSL https://deno.land/install.sh | DENO_INSTALL=/usr/local sh -s -- -y" msg_ok "Installed Deno" msg_info "Creating directories" mkdir -p /opt/yubal \ /opt/yubal_data \ /opt/yubal_config msg_ok "Created directories" fetch_and_deploy_gh_release "yubal" "guillevc/yubal" "tarball" "latest" "/opt/yubal" msg_info "Building Frontend" cd /opt/yubal/web $STD bun install --frozen-lockfile VERSION=$(get_latest_github_release "guillevc/yubal") VITE_VERSION=$VERSION VITE_COMMIT_SHA=$VERSION VITE_IS_RELEASE=true $STD bun run build msg_ok "Built Frontend" msg_info "Installing Python Dependencies" cd /opt/yubal export UV_CONCURRENT_DOWNLOADS=1 $STD uv sync --package yubal-api --no-dev --frozen msg_ok "Installed Python Dependencies" msg_info "Creating Service" cat </opt/yubal.env YUBAL_HOST=0.0.0.0 YUBAL_PORT=8000 YUBAL_DATA=/opt/yubal_data YUBAL_CONFIG=/opt/yubal_config YUBAL_ROOT=/opt/yubal PYTHONUNBUFFERED=1 EOF cat </etc/systemd/system/yubal.service [Unit] Description=Yubal After=network.target [Service] Type=simple User=root WorkingDirectory=/opt/yubal EnvironmentFile=/opt/yubal.env Environment="PATH=/opt/yubal/.venv/bin:/usr/local/bin:/usr/bin:/bin" ExecStart=/opt/yubal/.venv/bin/python -m yubal_api Restart=always RestartSec=5 [Install] WantedBy=multi-user.target EOF systemctl enable -q --now yubal msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/yunohost-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://yunohost.org/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt-get install -y \ apt-transport-https \ lsb-release \ ca-certificates msg_ok "Installed Dependencies" msg_warn "WARNING: This script will run an external installer from a third-party source (https://yunohost.org/)." msg_warn "The following code is NOT maintained or audited by our repository." msg_warn "If you have any doubts or concerns, please review the installer code before proceeding:" msg_custom "${TAB3}${GATEWAY}${BGN}${CL}" "\e[1;34m" "→ https://install.yunohost.org" echo read -r -p "${TAB3}Do you want to continue? [y/N]: " CONFIRM if [[ ! "$CONFIRM" =~ ^([yY][eE][sS]|[yY])$ ]]; then msg_error "Aborted by user. No changes have been made." exit 10 fi msg_info "Installing YunoHost (Patience)" touch /etc/.pve-ignore.resolv.conf curl -fsSLo /usr/share/keyrings/deb.sury.org-php.gpg https://packages.sury.org/php/apt.gpg $STD bash <(curl -fsSL https://install.yunohost.org) -a msg_ok "Installed YunoHost" motd_ssh customize cleanup_lxc ================================================ FILE: install/zabbix-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.zabbix.com/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os PG_VERSION="17" setup_postgresql PG_DB_NAME="zabbixdb" PG_DB_USER="zabbix" setup_postgresql_db read -rp "Choose Zabbix version [1] 7.0 LTS [2] 7.4 (Latest Stable) [3] Latest available (default: 2): " ZABBIX_CHOICE ZABBIX_CHOICE=${ZABBIX_CHOICE:-2} case "$ZABBIX_CHOICE" in 1) ZABBIX_VERSION="7.0" ;; 2) ZABBIX_VERSION="7.4" ;; 3) ZABBIX_VERSION=$(curl -fsSL https://repo.zabbix.com/zabbix/ | grep -oP '(?<=href=")[0-9]+\.[0-9]+(?=/")' | sort -V | tail -n1) ;; *) ZABBIX_VERSION="7.4" echo "Invalid choice. Defaulting to 7.4." ;; esac msg_info "Installing Zabbix $ZABBIX_VERSION" cd /tmp if [[ "$ZABBIX_VERSION" == "7.0" ]]; then ZABBIX_DEB_URL="https://repo.zabbix.com/zabbix/${ZABBIX_VERSION}/debian/pool/main/z/zabbix-release/zabbix-release_latest_${ZABBIX_VERSION}+debian13_all.deb" ZABBIX_DEB_FILE="zabbix-release_latest_${ZABBIX_VERSION}+debian13_all.deb" else ZABBIX_DEB_URL="https://repo.zabbix.com/zabbix/${ZABBIX_VERSION}/release/debian/pool/main/z/zabbix-release/zabbix-release_latest+debian13_all.deb" ZABBIX_DEB_FILE="zabbix-release_latest+debian13_all.deb" fi curl -fsSL "$ZABBIX_DEB_URL" -o /tmp/"$ZABBIX_DEB_FILE" $STD dpkg -i /tmp/"$ZABBIX_DEB_FILE" $STD apt update $STD apt install -y zabbix-server-pgsql zabbix-frontend-php php8.4-pgsql zabbix-apache-conf zabbix-sql-scripts if [[ "$ZABBIX_VERSION" == "7.0" ]]; then ZABBIX_SQL="/usr/share/zabbix-sql-scripts/postgresql/server.sql.gz" else ZABBIX_SQL="/usr/share/zabbix/sql-scripts/postgresql/server.sql.gz" fi zcat "$ZABBIX_SQL" | sudo -u "$PG_DB_USER" psql "$PG_DB_NAME" &>/dev/null sed -i "s/^DBName=.*/DBName=$PG_DB_NAME/" /etc/zabbix/zabbix_server.conf sed -i "s/^DBUser=.*/DBUser=$PG_DB_USER/" /etc/zabbix/zabbix_server.conf sed -i "s/^# DBPassword=.*/DBPassword=$PG_DB_PASS/" /etc/zabbix/zabbix_server.conf msg_ok "Installed Zabbix $ZABBIX_VERSION" while true; do read -rp "Which agent do you want to install? [1=agent (classic), 2=agent2 (modern), default=1]: " AGENT_CHOICE case "$AGENT_CHOICE" in 2) AGENT_PKG="zabbix-agent2" break ;; "" | 1) AGENT_PKG="zabbix-agent" break ;; *) echo "Invalid choice. Please enter 1 or 2." ;; esac done msg_ok "Selected $AGENT_PKG" if [ "$AGENT_PKG" = "zabbix-agent2" ]; then echo "Choose plugins for Zabbix Agent2:" echo "1) PostgreSQL only (default, recommended)" echo "2) All plugins (may cause issues)" read -rp "Choose option [1-2]: " PLUGIN_CHOICE case "$PLUGIN_CHOICE" in 2) $STD apt install -y zabbix-agent2 zabbix-agent2-plugin-* ;; *) $STD apt install -y zabbix-agent2 zabbix-agent2-plugin-postgresql ;; esac if [ -f /etc/zabbix/zabbix_agent2.d/plugins.d/nvidia.conf ]; then sed -i 's|^Plugins.NVIDIA.System.Path=.*|# Plugins.NVIDIA.System.Path=/usr/libexec/zabbix/zabbix-agent2-plugin-nvidia-gpu|' \ /etc/zabbix/zabbix_agent2.d/plugins.d/nvidia.conf fi else $STD apt install -y zabbix-agent fi msg_info "Configuring Fping" if command -v fping >/dev/null 2>&1; then FPING_PATH=$(command -v fping) sed -i "s|^#\?FpingLocation=.*|FpingLocation=$FPING_PATH|" /etc/zabbix/zabbix_server.conf fi if command -v fping6 >/dev/null 2>&1; then FPING6_PATH=$(command -v fping6) sed -i "s|^#\?Fping6Location=.*|Fping6Location=$FPING6_PATH|" /etc/zabbix/zabbix_server.conf fi msg_ok "Configured Fping" msg_info "Starting Services" if [ "$AGENT_PKG" = "zabbix-agent2" ]; then AGENT_SERVICE="zabbix-agent2" else AGENT_SERVICE="zabbix-agent" fi systemctl restart zabbix-server apache2 systemctl enable -q --now zabbix-server $AGENT_SERVICE apache2 rm -rf /tmp/zabbix-release_*.deb msg_ok "Started Services" motd_ssh customize cleanup_lxc ================================================ FILE: install/zammad-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: Michel Roegl-Brunner (michelroegl-brunner) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://zammad.com source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ git \ nginx \ apt-transport-https msg_ok "Installed Dependencies" msg_info "Setting up Elasticsearch" setup_deb822_repo \ "elasticsearch" \ "https://artifacts.elastic.co/GPG-KEY-elasticsearch" \ "https://artifacts.elastic.co/packages/7.x/apt" \ "stable" \ "main" $STD apt install -y elasticsearch sed -i 's/^#\{0,2\} *-Xms[0-9]*g.*/-Xms2g/' /etc/elasticsearch/jvm.options sed -i 's/^#\{0,2\} *-Xmx[0-9]*g.*/-Xmx2g/' /etc/elasticsearch/jvm.options cat <>/etc/elasticsearch/elasticsearch.yml discovery.type: single-node xpack.security.enabled: false bootstrap.memory_lock: false EOF $STD /usr/share/elasticsearch/bin/elasticsearch-plugin install ingest-attachment -b systemctl daemon-reload systemctl enable -q elasticsearch systemctl restart -q elasticsearch for i in $(seq 1 30); do if curl -s http://localhost:9200 >/dev/null 2>&1; then break fi sleep 2 done msg_ok "Setup Elasticsearch" msg_info "Installing Zammad" setup_deb822_repo \ "zammad" \ "https://dl.packager.io/srv/zammad/zammad/key" \ "https://dl.packager.io/srv/deb/zammad/zammad/stable/debian" \ "$(get_os_info version_id)" \ "main" $STD apt install -y zammad $STD zammad run rails r "Setting.set('es_url', 'http://localhost:9200')" $STD zammad run rake zammad:searchindex:rebuild msg_ok "Installed Zammad" msg_info "Setup Services" cp /opt/zammad/contrib/nginx/zammad.conf /etc/nginx/sites-available/zammad.conf sed -i "s/server_name localhost;/server_name $LOCAL_IP;/g" /etc/nginx/sites-available/zammad.conf ln -sf /etc/nginx/sites-available/zammad.conf /etc/nginx/sites-enabled/ rm -f /etc/nginx/sites-enabled/default $STD systemctl reload nginx msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/zerobyte-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: community-scripts # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/nicotsx/zerobyte source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" echo "davfs2 davfs2/suid_file boolean false" | debconf-set-selections $STD apt-get install -y \ bzip2 \ fuse3 \ git \ sshfs \ davfs2 \ openssh-client msg_ok "Installed Dependencies" fetch_and_deploy_gh_release "restic" "restic/restic" "singlefile" "latest" "/usr/local/bin" "restic_*_linux_amd64.bz2" mv /usr/local/bin/restic /usr/local/bin/restic.bz2 bzip2 -d /usr/local/bin/restic.bz2 chmod +x /usr/local/bin/restic fetch_and_deploy_gh_release "rclone" "rclone/rclone" "prebuild" "latest" "/opt/rclone" "rclone-*-linux-amd64.zip" ln -sf /opt/rclone/rclone /usr/local/bin/rclone fetch_and_deploy_gh_release "shoutrrr" "nicholas-fedor/shoutrrr" "prebuild" "latest" "/opt/shoutrrr" "shoutrrr_linux_amd64_*.tar.gz" ln -sf /opt/shoutrrr/shoutrrr /usr/local/bin/shoutrrr msg_info "Installing Bun" export BUN_INSTALL="/root/.bun" curl -fsSL https://bun.sh/install | $STD bash ln -sf /root/.bun/bin/bun /usr/local/bin/bun ln -sf /root/.bun/bin/bunx /usr/local/bin/bunx msg_ok "Installed Bun" NODE_VERSION="24" setup_nodejs fetch_and_deploy_gh_release "zerobyte" "nicotsx/zerobyte" "tarball" msg_info "Building Zerobyte (Patience)" cd /opt/zerobyte export VITE_RESTIC_VERSION=$(cat ~/.restic) export VITE_RCLONE_VERSION=$(cat ~/.rclone) export VITE_SHOUTRRR_VERSION=$(cat ~/.shoutrrr) export NODE_OPTIONS="--max-old-space-size=3072" $STD bun install $STD node ./node_modules/vite/bin/vite.js build msg_ok "Built Zerobyte" msg_info "Configuring Zerobyte" mkdir -p /var/lib/zerobyte/{data,restic/cache,repositories,volumes} APP_SECRET=$(openssl rand -hex 32) cat </opt/zerobyte/.env BASE_URL=http://${LOCAL_IP}:4096 APP_SECRET=${APP_SECRET} PORT=4096 ZEROBYTE_DATABASE_URL=/var/lib/zerobyte/data/zerobyte.db RESTIC_CACHE_DIR=/var/lib/zerobyte/restic/cache ZEROBYTE_REPOSITORIES_DIR=/var/lib/zerobyte/repositories ZEROBYTE_VOLUMES_DIR=/var/lib/zerobyte/volumes MIGRATIONS_PATH=/opt/zerobyte/app/drizzle NODE_ENV=production EOF msg_ok "Configured Zerobyte" msg_info "Creating Service" cat </etc/systemd/system/zerobyte.service [Unit] Description=Zerobyte Backup Automation After=network.target [Service] Type=simple User=root WorkingDirectory=/opt/zerobyte EnvironmentFile=/opt/zerobyte/.env ExecStart=/usr/local/bin/bun .output/server/index.mjs Restart=on-failure RestartSec=5 [Install] WantedBy=multi-user.target EOF systemctl enable -q --now zerobyte msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/zerotier-one-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: tremor021 # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.zerotier.com/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_warn "WARNING: This script will run an external installer from a third-party source (https://install.zerotier.com)." msg_warn "The following code is NOT maintained or audited by our repository." msg_warn "If you have any doubts or concerns, please review the installer code before proceeding:" msg_custom "${TAB3}${GATEWAY}${BGN}${CL}" "\e[1;34m" "→ https://install.zerotier.com" echo read -r -p "${TAB3}Do you want to continue? [y/N]: " CONFIRM if [[ ! $CONFIRM =~ ^([yY][eE][sS]|[yY])$ ]]; then msg_error "Aborted by user. No changes have been made." exit 10 fi msg_info "Setting up Zerotier-One" curl -fsSL https://raw.githubusercontent.com/zerotier/ZeroTierOne/main/doc/contact%40zerotier.com.gpg | gpg --import >/dev/null 2>&1 curl -fsSL https://install.zerotier.com -o /tmp/zerotier-install.sh if gpg --verify /tmp/zerotier-install.sh >/dev/null 2>&1; then $STD bash /tmp/zerotier-install.sh else msg_warn "Could not verify signature of Zerotier-One install script. Exiting..." exit 250 fi msg_ok "Setup Zerotier-One" msg_info "Setting up UI" curl -O https://s3-us-west-1.amazonaws.com/key-networks/deb/ztncui/1/x86_64/ztncui_0.8.14_arm64.deb dpkg -i ztncui_0.8.14_arm64.deb sh -c "echo ZT_TOKEN=$(cat /var/lib/zerotier-one/authtoken.secret) > /opt/key-networks/ztncui/.env" echo HTTPS_PORT=3443 >>/opt/key-networks/ztncui/.env echo NODE_ENV=production >>/opt/key-networks/ztncui/.env chmod 400 /opt/key-networks/ztncui/.env chown ztncui:ztncui /opt/key-networks/ztncui/.env systemctl restart ztncui msg_ok "Setup UI." motd_ssh customize cleanup_lxc ================================================ FILE: install/zigbee2mqtt-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.zigbee2mqtt.io/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y \ git \ build-essential msg_ok "Installed Dependencies" NODE_VERSION="24" NODE_MODULE="pnpm@$(curl -fsSL https://raw.githubusercontent.com/Koenkk/zigbee2mqtt/master/package.json | jq -r '.packageManager | split("@")[1]')" setup_nodejs fetch_and_deploy_gh_release "Zigbee2MQTT" "Koenkk/zigbee2mqtt" "tarball" "latest" "/opt/zigbee2mqtt" msg_info "Setting up Zigbee2MQTT" mv /opt/zigbee2mqtt/data/configuration.example.yaml /opt/zigbee2mqtt/data/configuration.yaml cd /opt/zigbee2mqtt echo "packageImportMethod: hardlink" >>./pnpm-workspace.yaml $STD pnpm install --no-frozen-lockfile $STD pnpm build msg_ok "Setup Zigbee2MQTT" msg_info "Creating Service" cat </etc/systemd/system/zigbee2mqtt.service [Unit] Description=zigbee2mqtt After=network.target [Service] Environment=NODE_ENV=production ExecStart=/usr/bin/pnpm start WorkingDirectory=/opt/zigbee2mqtt StandardOutput=inherit StandardError=inherit Restart=always User=root [Install] WantedBy=multi-user.target EOF systemctl enable -q --now zigbee2mqtt msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/zipline-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck # Co-Author: MickLesk (Canbiz) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/diced/zipline source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os NODE_VERSION="22" NODE_MODULE="pnpm" setup_nodejs PG_VERSION="17" setup_postgresql PG_DB_NAME="ziplinedb" PG_DB_USER="zipline" setup_postgresql_db fetch_and_deploy_gh_release "zipline" "diced/zipline" "tarball" SECRET_KEY="$(openssl rand -base64 42 | tr -dc 'a-zA-Z0-9')" echo "Zipline Secret Key: ${SECRET_KEY}" >>~/zipline.creds msg_info "Installing Zipline (Patience)" cd /opt/zipline cat </opt/zipline/.env DATABASE_URL=postgres://$PG_DB_USER:$PG_DB_PASS@localhost:5432/$PG_DB_NAME CORE_SECRET=$SECRET_KEY CORE_HOSTNAME=0.0.0.0 CORE_PORT=3000 CORE_RETURN_HTTPS=false DATASOURCE_TYPE=local DATASOURCE_LOCAL_DIRECTORY=/opt/zipline-uploads EOF mkdir -p /opt/zipline-uploads $STD pnpm install $STD pnpm build msg_ok "Installed Zipline" msg_info "Creating Service" cat </etc/systemd/system/zipline.service [Unit] Description=Zipline Service After=network.target [Service] WorkingDirectory=/opt/zipline ExecStart=/usr/bin/pnpm start Restart=on-failure [Install] WantedBy=multi-user.target EOF systemctl enable -q --now zipline msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/zitadel-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: dave-yap # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://zitadel.com/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies (Patience)" $STD apt install -y ca-certificates lsof msg_ok "Installed Dependecies" PG_VERSION="17" setup_postgresql msg_info "Installing Postgresql" DB_NAME="zitadel" DB_USER="zitadel" DB_PASS=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | cut -c1-13) DB_ADMIN_USER="root" DB_ADMIN_PASS=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | cut -c1-13) systemctl start postgresql $STD sudo -u postgres psql -c "CREATE USER $DB_USER WITH PASSWORD '$DB_PASS';" $STD sudo -u postgres psql -c "CREATE USER $DB_ADMIN_USER WITH PASSWORD '$DB_ADMIN_PASS' SUPERUSER;" $STD sudo -u postgres psql -c "CREATE DATABASE $DB_NAME OWNER $DB_ADMIN_USER;" { echo "Application Credentials" echo "DB_NAME: $DB_NAME" echo "DB_USER: $DB_USER" echo "DB_PASS: $DB_PASS" echo "DB_ADMIN_USER: $DB_ADMIN_USER" echo "DB_ADMIN_PASS: $DB_ADMIN_PASS" } >>~/zitadel.creds msg_ok "Installed PostgreSQL" fetch_and_deploy_gh_release "zitadel" "zitadel/zitadel" "prebuild" "latest" "/usr/local/bin" "zitadel-linux-arm64.tar.gz" msg_info "Setting up Zitadel Environments" mkdir -p /opt/zitadel echo "/opt/zitadel/config.yaml" >"/opt/zitadel/.config" head -c 32 < <(openssl rand -base64 48 | tr -dc 'a-zA-Z0-9') >"/opt/zitadel/.masterkey" { echo "Config location: $(cat "/opt/zitadel/.config")" echo "Masterkey: $(cat "/opt/zitadel/.masterkey")" } >>~/zitadel.creds cat </opt/zitadel/config.yaml Port: 8080 ExternalPort: 8080 ExternalDomain: localhost ExternalSecure: false TLS: Enabled: false KeyPath: "" Key: "" CertPath: "" Cert: "" Database: postgres: Host: localhost Port: 5432 Database: ${DB_NAME} User: Username: ${DB_USER} Password: ${DB_PASS} SSL: Mode: disable RootCert: "" Cert: "" Key: "" Admin: Username: ${DB_ADMIN_USER} Password: ${DB_ADMIN_PASS} SSL: Mode: disable RootCert: "" Cert: "" Key: "" DefaultInstance: Features: LoginV2: Required: false EOF msg_ok "Installed Zitadel Enviroments" msg_info "Creating Services" cat </etc/systemd/system/zitadel.service [Unit] Description=ZITADEL Identiy Server After=network.target postgresql.service Wants=postgresql.service [Service] Type=simple User=zitadel Group=zitadel ExecStart=/usr/local/bin/zitadel start --masterkeyFile "/opt/zitadel/.masterkey" --config "/opt/zitadel/config.yaml" Restart=always RestartSec=5 TimeoutStartSec=0 # Security Hardening options ProtectSystem=full ProtectHome=true PrivateTmp=true NoNewPrivileges=true [Install] WantedBy=multi-user.target EOF systemctl enable -q --now zitadel msg_ok "Created Services" msg_info "Zitadel initial setup" zitadel start-from-init --masterkeyFile /opt/zitadel/.masterkey --config /opt/zitadel/config.yaml &>/dev/null & sleep 60 kill $(lsof -i | awk '/zitadel/ {print $2}' | head -n1) useradd zitadel msg_ok "Zitadel initialized" msg_info "Set ExternalDomain to current IP and restart Zitadel" IP=$(ip a s dev eth0 | awk '/inet / {print $2}' | cut -d/ -f1) sed -i "0,/localhost/s/localhost/${IP}/" /opt/zitadel/config.yaml systemctl stop -q zitadel $STD zitadel setup --masterkeyFile /opt/zitadel/.masterkey --config /opt/zitadel/config.yaml systemctl restart -q zitadel msg_ok "Zitadel restarted with ExternalDomain set to current IP" msg_info "Create zitadel-rerun.sh" cat <~/zitadel-rerun.sh systemctl stop zitadel timeout --kill-after=5s 15s zitadel setup --masterkeyFile /opt/zitadel/.masterkey --config /opt/zitadel/config.yaml systemctl restart zitadel EOF msg_ok "Bash script for rerunning Zitadel after changing Zitadel config.yaml" motd_ssh customize cleanup_lxc ================================================ FILE: install/zoraxy-install.sh ================================================ f#!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://zoraxy.aroz.org/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os fetch_and_deploy_gh_release "zoraxy" "tobychui/zoraxy" "singlefile" "latest" "/opt/zoraxy" "zoraxy_linux_arm64" ln -s /opt/zoraxy/zoraxy /usr/local/bin/zoraxy msg_info "Creating Service" cat </etc/systemd/system/zoraxy.service [Unit] Description=General purpose request proxy and forwarding tool After=syslog.target network-online.target [Service] ExecStart=/opt/zoraxy/./zoraxy WorkingDirectory=/opt/zoraxy/ Restart=always [Install] WantedBy=multi-user.target EOF systemctl enable -q --now zoraxy msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/zot-registry-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://zotregistry.dev/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt install -y apache2-utils msg_ok "Installed Dependencies" fetch_and_deploy_gh_release "zot" "project-zot/zot" "singlefile" "latest" "/usr/bin" "zot-linux-arm64" msg_info "Configuring Zot Registry" mkdir -p /etc/zot curl -fsSL https://raw.githubusercontent.com/project-zot/zot/refs/heads/main/examples/config-ui.json -o /etc/zot/config.json ZOTPASSWORD=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | head -c13) $STD htpasswd -b -B -c /etc/zot/htpasswd admin "$ZOTPASSWORD" { echo "Zot-Credentials" echo "Zot User: admin" echo "Zot Password: $ZOTPASSWORD" } >>~/zot.creds msg_ok "Configured Zot Registry" msg_info "Setup Service" cat </etc/systemd/system/zot.service [Unit] Description=OCI Distribution Registry Documentation=https://zotregistry.dev/ After=network.target auditd.service local-fs.target [Service] Type=simple ExecStart=/usr/bin/zot serve /etc/zot/config.json Restart=on-failure User=root LimitNOFILE=500000 MemoryHigh=2G MemoryMax=4G [Install] WantedBy=multi-user.target EOF systemctl enable -q --now zot msg_ok "Setup Service" motd_ssh customize cleanup_lxc ================================================ FILE: install/zwave-js-ui-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://zwave-js.github.io/zwave-js-ui/#/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os fetch_and_deploy_gh_release "zwave-js-ui" "zwave-js/zwave-js-ui" "prebuild" "latest" "/opt/zwave-js-ui" "zwave-js-ui*-linux.zip" msg_info "Configuring Z-Wave JS UI" mkdir -p /opt/zwave_store cat </opt/.env ZWAVEJS_EXTERNAL_CONFIG=/opt/zwave_store/.config-db STORE_DIR=/opt/zwave_store EOF msg_ok "Configured Z-Wave JS UI" msg_info "Creating Service" cat </etc/systemd/system/zwave-js-ui.service [Unit] Description=zwave-js-ui Wants=network-online.target After=network-online.target [Service] User=root WorkingDirectory=/opt/zwave-js-ui ExecStart=/opt/zwave-js-ui/zwave-js-ui EnvironmentFile=/opt/.env [Install] WantedBy=multi-user.target EOF systemctl enable -q --now zwave-js-ui msg_ok "Created Service" motd_ssh customize cleanup_lxc ================================================ FILE: misc/add-beszel-agent-lxc.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2025 tteck # Author: tteck (tteckster) # License: MIT # https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE function header_info { clear cat <<"EOF" ____ __ ___ __ / __ )___ _________ ___ / / / | ____ ____ ____ / /_ / __ / _ \/ ___/_ / / _ \/ /_____/ /| |/ __ `/ _ \/ __ \/ __/ / /_/ / __(__ ) / /_/ __/ /_____/ ___ / /_/ / __/ / / / /_ /_____/\___/____/ /___/\___/_/ /_/ |_\__, /\___/_/ /_/\__/ /____/ EOF } header_info set -e while true; do read -p "This will add Beszel Agent to an existing LXC Container ONLY. Proceed(y/n)?" yn case $yn in [Yy]*) break ;; [Nn]*) exit ;; *) echo "Please answer yes or no." ;; esac done header_info echo "Loading..." function msg() { local TEXT="$1" echo -e "$TEXT" } NODE=$(hostname) MSG_MAX_LENGTH=0 while read -r line; do TAG=$(echo "$line" | awk '{print $1}') ITEM=$(echo "$line" | awk '{print substr($0,36)}') OFFSET=2 if [[ $((${#ITEM} + $OFFSET)) -gt ${MSG_MAX_LENGTH:-} ]]; then MSG_MAX_LENGTH=$((${#ITEM} + $OFFSET)) fi CTID_MENU+=("$TAG" "$ITEM " "OFF") done < <(pct list | awk 'NR>1') while [ -z "${CTID:+x}" ]; do CTID=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "Containers on $NODE" --radiolist \ "\nSelect a container to add Beszel Agent to:\n" \ 16 $(($MSG_MAX_LENGTH + 23)) 6 \ "${CTID_MENU[@]}" 3>&1 1>&2 2>&3) || exit done CTID_CONFIG_PATH=/etc/pve/lxc/${CTID}.conf header_info msg "Installing Beszel Agent..." pct exec "$CTID" -- bash -c 'curl -sL https://raw.githubusercontent.com/henrygd/beszel/main/supplemental/scripts/install-agent.sh -o install-beszel-agent.sh && chmod +x install-beszel-agent.sh && ./install-beszel-agent.sh && rm -f ./install-beszel-agent.sh &>/dev/null' || exit msg "\e[1;32m ✔ Installed Beszel Agent\e[0m" ================================================ FILE: misc/alpine-install.func ================================================ # Copyright (c) 2021-2026 community-scripts ORG # Author: tteck (tteckster) # License: MIT # https://github.com/asylumexp/Proxmox/raw/main/LICENSE if ! command -v curl >/dev/null 2>&1; then apk update && apk add curl >/dev/null 2>&1 fi source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/core.func) source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/error_handler.func) load_functions catch_errors # Persist diagnostics setting inside container (exported from build.func) # so addon scripts running later can find the user's choice if [[ ! -f /usr/local/community-scripts/diagnostics ]]; then mkdir -p /usr/local/community-scripts echo "DIAGNOSTICS=${DIAGNOSTICS:-no}" >/usr/local/community-scripts/diagnostics fi # Get LXC IP address (must be called INSIDE container, after network is up) get_lxc_ip # ------------------------------------------------------------------------------ # post_progress_to_api() # # - Lightweight progress ping from inside the container # - Updates the existing telemetry record status # - Arguments: # * $1: status (optional, default: "configuring") # - Signals that the installation is actively progressing (not stuck) # - Fire-and-forget: never blocks or fails the script # - Only executes if DIAGNOSTICS=yes and RANDOM_UUID is set # ------------------------------------------------------------------------------ post_progress_to_api() { command -v curl &>/dev/null || return 0 [[ "${DIAGNOSTICS:-no}" == "no" ]] && return 0 [[ -z "${RANDOM_UUID:-}" ]] && return 0 local progress_status="${1:-configuring}" curl -fsS -m 5 -X POST "https://telemetry.community-scripts.org/telemetry" \ -H "Content-Type: application/json" \ -d "{\"random_id\":\"${RANDOM_UUID}\",\"execution_id\":\"${EXECUTION_ID:-${RANDOM_UUID}}\",\"type\":\"lxc\",\"nsapp\":\"${app:-unknown}\",\"status\":\"${progress_status}\"}" &>/dev/null || true } # This function enables IPv6 if it's not disabled and sets verbose mode verb_ip6() { set_std_mode # Set STD mode based on VERBOSE if [ "${IPV6_METHOD:-}" = "disable" ]; then msg_info "Disabling IPv6 (this may affect some services)" $STD sysctl -w net.ipv6.conf.all.disable_ipv6=1 $STD sysctl -w net.ipv6.conf.default.disable_ipv6=1 $STD sysctl -w net.ipv6.conf.lo.disable_ipv6=1 mkdir -p /etc/sysctl.d $STD tee /etc/sysctl.d/99-disable-ipv6.conf >/dev/null <&2 -en "${CROSS}${RD} No Network! " sleep $RETRY_EVERY i=$((i - 1)) done if [ "$(ip addr show | grep 'inet ' | grep -v '127.0.0.1' | awk '{print $2}' | cut -d'/' -f1)" = "" ]; then echo 1>&2 -e "\n${CROSS}${RD} No Network After $RETRY_NUM Tries${CL}" echo -e "${NETWORK}Check Network Settings" exit 121 fi msg_ok "Set up Container OS" msg_ok "Network Connected: ${BL}$(ip addr show | grep 'inet ' | awk '{print $2}' | cut -d'/' -f1 | tail -n1)${CL}" post_progress_to_api } # This function checks the network connection by pinging a known IP address and prompts the user to continue if the internet is not connected network_check() { set +e trap - ERR ipv4_connected=false # Check IPv4 connectivity to Cloudflare, Google & Quad9 DNS servers if ping -c 1 -W 1 1.1.1.1 &>/dev/null || ping -c 1 -W 1 8.8.8.8 &>/dev/null || ping -c 1 -W 1 9.9.9.9 &>/dev/null; then msg_ok "IPv4 Internet Connected" ipv4_connected=true else msg_error "IPv4 Internet Not Connected" fi if [[ $ipv4_connected == false ]]; then read -r -p "No Internet detected, would you like to continue anyway? " prompt if [[ "${prompt,,}" =~ ^(y|yes)$ ]]; then echo -e "${INFO}${RD}Expect Issues Without Internet${CL}" else echo -e "${NETWORK}Check Network Settings" exit 122 fi fi # DNS resolution checks for GitHub-related domains GIT_HOSTS=("github.com" "raw.githubusercontent.com" "api.github.com" "git.community-scripts.org") GIT_STATUS="Git DNS:" DNS_FAILED=false for HOST in "${GIT_HOSTS[@]}"; do RESOLVEDIP=$(getent hosts "$HOST" | awk '{ print $1 }' | grep -E '(^([0-9]{1,3}\.){3}[0-9]{1,3}$)|(^[a-fA-F0-9:]+$)' | head -n1) if [[ -z "$RESOLVEDIP" ]]; then GIT_STATUS+="$HOST:($DNSFAIL)" DNS_FAILED=true else GIT_STATUS+=" $HOST:($DNSOK)" fi done if [[ "$DNS_FAILED" == true ]]; then fatal "$GIT_STATUS" else msg_ok "$GIT_STATUS" fi set -e trap 'error_handler $LINENO "$BASH_COMMAND"' ERR } # This function updates the Container OS by running apk upgrade with mirror fallback update_os() { msg_info "Updating Container OS" if ! $STD apk -U upgrade; then msg_warn "apk update failed (dl-cdn.alpinelinux.org), trying alternate mirrors..." local alpine_mirrors="mirror.init7.net ftp.halifax.rwth-aachen.de mirrors.edge.kernel.org alpine.mirror.wearetriple.com mirror.leaseweb.com uk.alpinelinux.org dl-2.alpinelinux.org dl-4.alpinelinux.org" local apk_ok=false for m in $(printf '%s\n' $alpine_mirrors | shuf); do if timeout 2 bash -c "echo >/dev/tcp/$m/80" 2>/dev/null; then msg_custom "${INFO}" "${YW}" "Attempting mirror: ${m}" cat </etc/apk/repositories http://$m/alpine/latest-stable/main http://$m/alpine/latest-stable/community EOF if $STD apk -U upgrade; then msg_ok "CDN set to ${m}: tests passed" apk_ok=true break else msg_warn "Mirror ${m} failed" fi fi done if [[ "$apk_ok" != true ]]; then msg_error "All Alpine mirrors failed. Check network or try again later." exit 1 fi fi local tools_content tools_content=$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/tools.func) || { msg_error "Failed to download tools.func" exit 115 } source /dev/stdin <<<"$tools_content" if ! declare -f fetch_and_deploy_gh_release >/dev/null 2>&1; then msg_error "tools.func loaded but incomplete — missing expected functions" exit 115 fi msg_ok "Updated Container OS" post_progress_to_api } # This function modifies the message of the day (motd) and SSH settings motd_ssh() { echo "export TERM='xterm-256color'" >>/root/.bashrc PROFILE_FILE="/etc/profile.d/00_lxc-details.sh" echo "echo -e \"\"" >"$PROFILE_FILE" echo -e "echo -e \"${BOLD}${APPLICATION} LXC Container${CL}"\" >>"$PROFILE_FILE" echo -e "echo -e \"${TAB}${GATEWAY}${YW} Provided by: ${GN}community-scripts & pimox-scripts ${YW}| GitHub: ${GN}https://github.com/asylumexp/ProxmoxVE${CL}\"" >>"$PROFILE_FILE" echo "echo \"\"" >>"$PROFILE_FILE" echo -e "echo -e \"${TAB}${OS}${YW} OS: ${GN}\$(grep ^NAME /etc/os-release | cut -d= -f2 | tr -d '\"') - Version: \$(grep ^VERSION_ID /etc/os-release | cut -d= -f2 | tr -d '\"')${CL}\"" >>"$PROFILE_FILE" echo -e "echo -e \"${TAB}${HOSTNAME}${YW} Hostname: ${GN}\$(hostname)${CL}\"" >>"$PROFILE_FILE" echo -e "echo -e \"${TAB}${INFO}${YW} IP Address: ${GN}\$(ip -4 addr show eth0 | awk '/inet / {print \$2}' | cut -d/ -f1 | head -n 1)${CL}\"" >>"$PROFILE_FILE" # Configure SSH if enabled if [[ "${SSH_ROOT}" == "yes" ]]; then # Enable sshd service $STD rc-update add sshd # Allow root login via SSH sed -i "s/#PermitRootLogin prohibit-password/PermitRootLogin yes/g" /etc/ssh/sshd_config # Start the sshd service $STD /etc/init.d/sshd start fi post_progress_to_api } # Validate Timezone for some LXC's validate_tz() { [[ -f "/usr/share/zoneinfo/$1" ]] } # This function customizes the container and enables passwordless login for the root user customize() { if [[ "$PASSWORD" == "" ]]; then msg_info "Customizing Container" passwd -d root >/dev/null 2>&1 # Ensure agetty is available apk add --no-cache --force-broken-world util-linux >/dev/null 2>&1 # Create persistent autologin boot script mkdir -p /etc/local.d cat <<'EOF' >/etc/local.d/autologin.start #!/bin/sh sed -i 's|^tty1::respawn:.*|tty1::respawn:/sbin/agetty --autologin root --noclear tty1 38400 linux|' /etc/inittab kill -HUP 1 EOF touch /root/.hushlogin chmod +x /etc/local.d/autologin.start rc-update add local >/dev/null 2>&1 # Apply autologin immediately for current session /etc/local.d/autologin.start msg_ok "Customized Container" fi echo "bash -c \"\$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/${app}.sh)\"" >/usr/bin/update chmod +x /usr/bin/update post_progress_to_api } ================================================ FILE: misc/alpine-tools.func ================================================ #!/bin/ash # shellcheck shell=ash # Expects existing msg_* functions and optional $STD from the framework. # ------------------------------ # helpers # ------------------------------ lower() { printf '%s' "$1" | tr '[:upper:]' '[:lower:]'; } has() { command -v "$1" >/dev/null 2>&1; } need_tool() { # usage: need_tool curl jq unzip ... # setup missing tools via apk local missing=0 t for t in "$@"; do if ! has "$t"; then missing=1; fi done if [ "$missing" -eq 1 ]; then msg_info "Installing tools: $*" apk add --no-cache "$@" >/dev/null 2>&1 || { msg_error "apk add failed for: $*" return 100 } msg_ok "Tools ready: $*" fi } net_resolves() { # better handling for missing getent on Alpine # usage: net_resolves api.github.com local host="$1" ping -c1 -W1 "$host" >/dev/null 2>&1 || nslookup "$host" >/dev/null 2>&1 } ensure_usr_local_bin_persist() { # Login shells: /etc/profile.d/ local PROFILE_FILE="/etc/profile.d/10-localbin.sh" if [ ! -f "$PROFILE_FILE" ]; then echo 'case ":$PATH:" in *:/usr/local/bin:*) ;; *) export PATH="/usr/local/bin:$PATH";; esac' >"$PROFILE_FILE" chmod +x "$PROFILE_FILE" fi # Non-login shells (pct enter): /root/.profile and /root/.bashrc for rc_file in /root/.profile /root/.bashrc; do if [ -f "$rc_file" ] && ! grep -q '/usr/local/bin' "$rc_file"; then echo 'export PATH="/usr/local/bin:$PATH"' >>"$rc_file" fi done } download_with_progress() { # $1 url, $2 dest local url="$1" out="$2" cl need_tool curl pv || return 127 cl=$(curl -fsSLI "$url" 2>/dev/null | awk 'tolower($0) ~ /^content-length:/ {print $2}' | tr -d '\r') if [ -n "$cl" ]; then curl -fsSL "$url" | pv -s "$cl" >"$out" || { msg_error "Download failed: $url" return 250 } else curl -fL# -o "$out" "$url" || { msg_error "Download failed: $url" return 250 } fi } # ------------------------------ # GitHub: check Release # ------------------------------ check_for_gh_release() { # app, repo, [pinned] local app="$1" source="$2" pinned="${3:-}" local app_lc app_lc="$(lower "$app" | tr -d ' ')" local current_file="$HOME/.${app_lc}" local current="" release tag msg_info "Check for update: $app" net_resolves api.github.com || { msg_error "DNS/network error: api.github.com" return 6 } need_tool curl jq || return 127 tag=$(curl -fsSL "https://api.github.com/repos/${source}/releases/latest" | jq -r '.tag_name // empty') [ -z "$tag" ] && { msg_error "Unable to fetch latest tag for $app" return 22 } release="${tag#v}" [ -f "$current_file" ] && current="$(cat "$current_file")" if [ -n "$pinned" ]; then if [ "$pinned" = "$release" ]; then msg_ok "$app pinned to v$pinned (no update)" return 1 fi if [ "$current" = "$pinned" ]; then msg_ok "$app pinned v$pinned installed (upstream v$release)" return 1 fi msg_info "$app pinned v$pinned (upstream v$release) → update/downgrade" CHECK_UPDATE_RELEASE="$pinned" return 0 fi if [ "$release" != "$current" ] || [ ! -f "$current_file" ]; then CHECK_UPDATE_RELEASE="$release" msg_info "New release available: v$release (current: v${current:-none})" return 0 fi msg_ok "$app is up to date (v$release)" return 1 } # ------------------------------ # GitHub: get Release & deploy (Alpine) # modes: tarball | prebuild | singlefile # ------------------------------ fetch_and_deploy_gh() { # $1 app, $2 repo, [$3 mode], [$4 version], [$5 target], [$6 asset_pattern local app="$1" repo="$2" mode="${3:-tarball}" version="${4:-latest}" target="${5:-/opt/$1}" pattern="${6:-}" local app_lc app_lc="$(lower "$app" | tr -d ' ')" local vfile="$HOME/.${app_lc}" local json url filename tmpd unpack net_resolves api.github.com || { msg_error "DNS/network error" return 6 } need_tool curl jq tar || return 127 [ "$mode" = "prebuild" ] || [ "$mode" = "singlefile" ] && need_tool unzip >/dev/null 2>&1 || true tmpd="$(mktemp -d)" || return 252 mkdir -p "$target" # Release JSON if [ "$version" = "latest" ]; then json="$(curl -fsSL "https://api.github.com/repos/$repo/releases/latest")" || { msg_error "GitHub API failed" rm -rf "$tmpd" return 22 } else json="$(curl -fsSL "https://api.github.com/repos/$repo/releases/tags/$version")" || { msg_error "GitHub API failed" rm -rf "$tmpd" return 22 } fi # correct Version version="$(printf '%s' "$json" | jq -r '.tag_name // empty')" version="${version#v}" [ -z "$version" ] && { msg_error "No tag in release json" rm -rf "$tmpd" return 65 } case "$mode" in tarball | source) url="$(printf '%s' "$json" | jq -r '.tarball_url // empty')" [ -z "$url" ] && url="https://github.com/$repo/archive/refs/tags/v$version.tar.gz" filename="${app_lc}-${version}.tar.gz" download_with_progress "$url" "$tmpd/$filename" || { rm -rf "$tmpd" return 250 } tar -xzf "$tmpd/$filename" -C "$tmpd" || { msg_error "tar extract failed" rm -rf "$tmpd" return 251 } unpack="$(find "$tmpd" -mindepth 1 -maxdepth 1 -type d | head -n1)" # copy content of unpack to target (cd "$unpack" && tar -cf - .) | (cd "$target" && tar -xf -) || { msg_error "copy failed" rm -rf "$tmpd" return 252 } ;; prebuild) [ -n "$pattern" ] || { msg_error "prebuild requires asset pattern" rm -rf "$tmpd" return 65 } url="$(printf '%s' "$json" | jq -r '.assets[].browser_download_url' | awk -v p="$pattern" ' BEGIN{IGNORECASE=1} $0 ~ p {print; exit} ')" [ -z "$url" ] && { msg_error "asset not found for pattern: $pattern" rm -rf "$tmpd" return 250 } filename="${url##*/}" download_with_progress "$url" "$tmpd/$filename" || { rm -rf "$tmpd" return 250 } # unpack archive (Zip or tarball) case "$filename" in *.zip) need_tool unzip || { rm -rf "$tmpd" return 127 } mkdir -p "$tmpd/unp" unzip -q "$tmpd/$filename" -d "$tmpd/unp" ;; *.tar.gz | *.tgz | *.tar.xz | *.tar.zst | *.tar.bz2) mkdir -p "$tmpd/unp" tar -xf "$tmpd/$filename" -C "$tmpd/unp" ;; *) msg_error "unsupported archive: $filename" rm -rf "$tmpd" return 251 ;; esac # top-level folder strippen if [ "$(find "$tmpd/unp" -mindepth 1 -maxdepth 1 -type d | wc -l)" -eq 1 ] && [ -z "$(find "$tmpd/unp" -mindepth 1 -maxdepth 1 -type f | head -n1)" ]; then unpack="$(find "$tmpd/unp" -mindepth 1 -maxdepth 1 -type d)" (cd "$unpack" && tar -cf - .) | (cd "$target" && tar -xf -) || { msg_error "copy failed" rm -rf "$tmpd" return 252 } else (cd "$tmpd/unp" && tar -cf - .) | (cd "$target" && tar -xf -) || { msg_error "copy failed" rm -rf "$tmpd" return 252 } fi ;; singlefile) [ -n "$pattern" ] || { msg_error "singlefile requires asset pattern" rm -rf "$tmpd" return 65 } url="$(printf '%s' "$json" | jq -r '.assets[].browser_download_url' | awk -v p="$pattern" ' BEGIN{IGNORECASE=1} $0 ~ p {print; exit} ')" [ -z "$url" ] && { msg_error "asset not found for pattern: $pattern" rm -rf "$tmpd" return 250 } filename="${url##*/}" download_with_progress "$url" "$target/$app" || { rm -rf "$tmpd" return 250 } chmod +x "$target/$app" ;; *) msg_error "Unknown mode: $mode" rm -rf "$tmpd" return 65 ;; esac echo "$version" >"$vfile" ensure_usr_local_bin_persist rm -rf "$tmpd" msg_ok "Deployed $app ($version) → $target" } # ------------------------------ # yq (mikefarah) – Alpine # ------------------------------ setup_yq() { # prefer apk, unless FORCE_GH=1 if [ "${FORCE_GH:-0}" != "1" ] && apk info -e yq >/dev/null 2>&1; then msg_info "Updating yq via apk" apk add --no-cache --upgrade yq >/dev/null 2>&1 || true msg_ok "yq ready ($(yq --version 2>/dev/null))" return 0 fi need_tool curl || return 127 local arch bin url tmp case "$(uname -m)" in x86_64) arch="amd64" ;; aarch64) arch="arm64" ;; *) msg_error "Unsupported arch for yq: $(uname -m)" return 238 ;; esac url="https://github.com/mikefarah/yq/releases/latest/download/yq_linux_${arch}" tmp="$(mktemp)" download_with_progress "$url" "$tmp" || return 250 /usr/bin/install -m 0755 "$tmp" /usr/local/bin/yq rm -f "$tmp" msg_ok "Setup yq ($(yq --version 2>/dev/null))" } # ------------------------------ # Adminer – Alpine # ------------------------------ setup_adminer() { need_tool curl || return 127 msg_info "Setup Adminer (Alpine)" mkdir -p /var/www/localhost/htdocs/adminer curl -fsSL https://github.com/vrana/adminer/releases/latest/download/adminer.php \ -o /var/www/localhost/htdocs/adminer/index.php || { msg_error "Adminer download failed" return 250 } msg_ok "Adminer at /adminer (served by your webserver)" } # ------------------------------ # uv – Alpine (musl tarball) # optional: PYTHON_VERSION="3.12" # ------------------------------ setup_uv() { need_tool curl tar || return 127 local UV_BIN="/usr/local/bin/uv" local arch tarball url tmpd ver installed case "$(uname -m)" in x86_64) arch="x86_64-unknown-linux-musl" ;; aarch64) arch="aarch64-unknown-linux-musl" ;; *) msg_error "Unsupported arch for uv: $(uname -m)" return 238 ;; esac ver="$(curl -fsSL https://api.github.com/repos/astral-sh/uv/releases/latest | jq -r '.tag_name' 2>/dev/null)" ver="${ver#v}" [ -z "$ver" ] && { msg_error "uv: cannot determine latest version" return 250 } if has "$UV_BIN"; then installed="$($UV_BIN -V 2>/dev/null | awk '{print $2}')" [ "$installed" = "$ver" ] && { msg_ok "uv $ver already installed" return 0 } msg_info "Updating uv $installed → $ver" else msg_info "Setup uv $ver" fi tmpd="$(mktemp -d)" || return 252 tarball="uv-${arch}.tar.gz" url="https://github.com/astral-sh/uv/releases/download/v${ver}/${tarball}" download_with_progress "$url" "$tmpd/uv.tar.gz" || { rm -rf "$tmpd" return 250 } tar -xzf "$tmpd/uv.tar.gz" -C "$tmpd" || { msg_error "uv: extract failed" rm -rf "$tmpd" return 251 } # tar contains ./uv if [ -x "$tmpd/uv" ]; then /usr/bin/install -m 0755 "$tmpd/uv" "$UV_BIN" else # fallback: in subfolder /usr/bin/install -m 0755 "$tmpd"/*/uv "$UV_BIN" 2>/dev/null || { msg_error "uv binary not found in tar" rm -rf "$tmpd" return 252 } fi rm -rf "$tmpd" ensure_usr_local_bin_persist msg_ok "Setup uv $ver" if [ -n "${PYTHON_VERSION:-}" ]; then local match match="$(uv python list --only-downloads 2>/dev/null | awk -v maj="$PYTHON_VERSION" ' $0 ~ "^cpython-"maj"\\." { print $0 }' | awk -F- '{print $2}' | sort -V | tail -n1)" [ -z "$match" ] && { msg_error "No matching Python for $PYTHON_VERSION" return 250 } if ! uv python list | grep -q "cpython-${match}-linux"; then msg_info "Installing Python $match via uv" uv python install "$match" || { msg_error "uv python install failed" return 150 } msg_ok "Python $match installed (uv)" fi fi } # ------------------------------ # Java – Alpine (OpenJDK) # JAVA_VERSION: 17|21 (Default 21) # ------------------------------ setup_java() { local JAVA_VERSION="${JAVA_VERSION:-21}" pkg case "$JAVA_VERSION" in 17) pkg="openjdk17-jdk" ;; 21 | *) pkg="openjdk21-jdk" ;; esac msg_info "Setup Java (OpenJDK $JAVA_VERSION)" apk add --no-cache "$pkg" >/dev/null 2>&1 || { msg_error "apk add $pkg failed" return 100 } # set JAVA_HOME local prof="/etc/profile.d/20-java.sh" if [ ! -f "$prof" ]; then echo 'export JAVA_HOME=$(dirname $(dirname $(readlink -f $(command -v java))))' >"$prof" echo 'case ":$PATH:" in *:$JAVA_HOME/bin:*) ;; *) export PATH="$JAVA_HOME/bin:$PATH";; esac' >>"$prof" chmod +x "$prof" fi msg_ok "Java ready: $(java -version 2>&1 | head -n1)" } # ------------------------------ # Go – Alpine (apk prefers, else tarball) # ------------------------------ setup_go() { if [ -z "${GO_VERSION:-}" ]; then msg_info "Setup Go (apk)" apk add --no-cache go >/dev/null 2>&1 || { msg_error "apk add go failed" return 100 } msg_ok "Go ready: $(go version 2>/dev/null)" return 0 fi need_tool curl tar || return 127 local ARCH TARBALL URL TMP case "$(uname -m)" in x86_64) ARCH="amd64" ;; aarch64) ARCH="arm64" ;; *) msg_error "Unsupported arch for Go: $(uname -m)" return 238 ;; esac TARBALL="go${GO_VERSION}.linux-${ARCH}.tar.gz" URL="https://go.dev/dl/${TARBALL}" msg_info "Setup Go $GO_VERSION (tarball)" TMP="$(mktemp)" download_with_progress "$URL" "$TMP" || return 250 rm -rf /usr/local/go tar -C /usr/local -xzf "$TMP" || { msg_error "extract go failed" rm -f "$TMP" return 251 } rm -f "$TMP" ln -sf /usr/local/go/bin/go /usr/local/bin/go ln -sf /usr/local/go/bin/gofmt /usr/local/bin/gofmt ensure_usr_local_bin_persist msg_ok "Go ready: $(go version 2>/dev/null)" } # ------------------------------ # Composer – Alpine # uses php83-cli + openssl + phar # ------------------------------ setup_composer() { local COMPOSER_BIN="/usr/local/bin/composer" if ! has php; then # prefers php83 msg_info "Installing PHP CLI for Composer" apk add --no-cache php83-cli php83-openssl php83-phar php83-iconv >/dev/null 2>&1 || { # Fallback to generic php if 83 not available apk add --no-cache php-cli php-openssl php-phar php-iconv >/dev/null 2>&1 || { msg_error "Failed to install php-cli for composer" return 100 } } msg_ok "PHP CLI ready: $(php -v | head -n1)" fi if [ -x "$COMPOSER_BIN" ]; then msg_info "Updating Composer" else msg_info "Setup Composer" fi need_tool curl || return 127 curl -fsSL https://getcomposer.org/installer -o /tmp/composer-setup.php || { msg_error "composer installer download failed" return 250 } php /tmp/composer-setup.php --install-dir=/usr/local/bin --filename=composer >/dev/null 2>&1 || { msg_error "composer install failed" return 150 } rm -f /tmp/composer-setup.php ensure_usr_local_bin_persist msg_ok "Composer ready: $(composer --version 2>/dev/null)" } ================================================ FILE: misc/api.func ================================================ # Copyright (c) 2021-2026 community-scripts ORG # Author: michelroegl-brunner | MickLesk # License: MIT | https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/LICENSE # ============================================================================== # API.FUNC - TELEMETRY & DIAGNOSTICS API # ============================================================================== # # Provides functions for sending anonymous telemetry data via the community # telemetry ingest service at telemetry.community-scripts.org. # # Features: # - Container/VM creation statistics # - Installation success/failure tracking # - Error code mapping and reporting # - Privacy-respecting anonymous telemetry # # Usage: # source <(curl -fsSL .../api.func) # post_to_api # Report LXC container creation # post_to_api_vm # Report VM creation # post_update_to_api # Report installation status # # Privacy: # - Only anonymous statistics (no personal data) # - User can opt-out via DIAGNOSTICS=no # - Random UUID for session tracking only # - Data retention: 30 days # # ============================================================================== # ============================================================================== # Telemetry Configuration # ============================================================================== TELEMETRY_URL="https://telemetry.community-scripts.org/telemetry" # Timeout for telemetry requests (seconds) # Progress pings (validation/configuring) use the short timeout TELEMETRY_TIMEOUT=5 # Final status updates (success/failed) use the longer timeout # PocketBase may need more time under load (FindRecord + UpdateRecord) STATUS_TIMEOUT=10 # ============================================================================== # SECTION 0: REPOSITORY SOURCE DETECTION # ============================================================================== # ------------------------------------------------------------------------------ # detect_repo_source() # # - Dynamically detects which GitHub/Gitea repo the scripts were loaded from # - Inspects /proc/$$/cmdline and $0 to find the source URL # - Maps detected repo to one of three canonical values: # * "ProxmoxVE" — official community-scripts/ProxmoxVE (production) # * "ProxmoxVED" — official community-scripts/ProxmoxVED (development) # * "external" — any fork or unknown source # - Fallback: "ProxmoxVED" (CI sed transforms ProxmoxVED → ProxmoxVE on promotion) # - Sets and exports REPO_SOURCE global variable # - Skips detection if REPO_SOURCE is already set (e.g., by environment) # ------------------------------------------------------------------------------ detect_repo_source() { # Allow explicit override via environment [[ -n "${REPO_SOURCE:-}" ]] && return 0 local content="" owner_repo="" # Method 1: Read from /proc/$$/cmdline # When invoked via: bash -c "$(curl -fsSL https://.../ct/app.sh)" # the full CT/VM script content is in /proc/$$/cmdline (same PID through source chain) if [[ -r /proc/$$/cmdline ]]; then content=$(tr '\0' ' ' /dev/null) || true fi # Method 2: Read from the original script file (bash ct/app.sh / bash vm/app.sh) if [[ -z "$content" ]] || ! echo "$content" | grep -qE 'githubusercontent\.com|community-scripts\.org' 2>/dev/null; then if [[ -f "$0" ]] && [[ "$0" != *bash* ]]; then content=$(head -10 "$0" 2>/dev/null) || true fi fi # Extract owner/repo from URL patterns found in the script content if [[ -n "$content" ]]; then # GitHub raw URL: raw.githubusercontent.com/OWNER/REPO/... owner_repo=$(echo "$content" | grep -oE 'raw\.githubusercontent\.com/[^/]+/[^/]+' | head -1 | sed 's|raw\.githubusercontent\.com/||') || true # Gitea URL: git.community-scripts.org/OWNER/REPO/... if [[ -z "$owner_repo" ]]; then owner_repo=$(echo "$content" | grep -oE 'git\.community-scripts\.org/[^/]+/[^/]+' | head -1 | sed 's|git\.community-scripts\.org/||') || true fi fi # Map detected owner/repo to canonical repo_source value case "$owner_repo" in community-scripts/ProxmoxVE) REPO_SOURCE="ProxmoxVE" ;; community-scripts/ProxmoxVED) REPO_SOURCE="ProxmoxVED" ;; "") # No URL detected — use hardcoded fallback # This value must match the repo: ProxmoxVE for production, ProxmoxVED for dev REPO_SOURCE="ProxmoxVE" ;; *) # Fork or unknown repo REPO_SOURCE="external" ;; esac export REPO_SOURCE } # Run detection immediately when api.func is sourced detect_repo_source # ============================================================================== # SECTION 1: ERROR CODE DESCRIPTIONS # ============================================================================== # ------------------------------------------------------------------------------ # explain_exit_code() # # - Maps numeric exit codes to human-readable error descriptions # - Canonical source of truth for ALL exit code mappings # - Used by both api.func (telemetry) and error_handler.func (error display) # - Supports: # * Generic/Shell errors (1-3, 10, 124-132, 134, 137, 139, 141, 143-146) # * curl/wget errors (4-8, 16, 18, 22-28, 30, 32-36, 39, 44-48, 51-52, 55-57, 59, 61, 63, 75, 78-79, 92, 95) # * Package manager errors (APT, DPKG: 100-102, 255) # * Script Validation & Setup (103-123) # * BSD sysexits (64-78) # * Systemd/Service errors (150-154) # * Python/pip/uv errors (160-162) # * PostgreSQL errors (170-173) # * MySQL/MariaDB errors (180-183) # * MongoDB errors (190-193) # * Proxmox custom codes (200-231) # * Tools & Addon Scripts (232-238) # * Node.js/npm errors (239, 243, 245-249) # * Application Install/Update errors (250-254) # - Returns description string for given exit code # ------------------------------------------------------------------------------ explain_exit_code() { local code="$1" case "$code" in # --- Generic / Shell --- 1) echo "General error / Operation not permitted" ;; 2) echo "Misuse of shell builtins (e.g. syntax error)" ;; 3) echo "General syntax or argument error" ;; 10) echo "Docker / privileged mode required (unsupported environment)" ;; # --- curl / wget errors (commonly seen in downloads) --- 4) echo "curl: Feature not supported or protocol error" ;; 5) echo "curl: Could not resolve proxy" ;; 6) echo "curl: DNS resolution failed (could not resolve host)" ;; 7) echo "curl: Failed to connect (network unreachable / host down)" ;; 8) echo "curl: Server reply error (FTP/SFTP or apk untrusted key)" ;; 16) echo "curl: HTTP/2 framing layer error" ;; 18) echo "curl: Partial file (transfer not completed)" ;; 22) echo "curl: HTTP error returned (404, 429, 500+)" ;; 23) echo "curl: Write error (disk full or permissions)" ;; 24) echo "curl: Write to local file failed" ;; 25) echo "curl: Upload failed" ;; 26) echo "curl: Read error on local file (I/O)" ;; 27) echo "curl: Out of memory (memory allocation failed)" ;; 28) echo "curl: Operation timeout (network slow or server not responding)" ;; 30) echo "curl: FTP port command failed" ;; 32) echo "curl: FTP SIZE command failed" ;; 33) echo "curl: HTTP range error" ;; 34) echo "curl: HTTP post error" ;; 35) echo "curl: SSL/TLS handshake failed (certificate error)" ;; 36) echo "curl: FTP bad download resume" ;; 39) echo "curl: LDAP search failed" ;; 44) echo "curl: Internal error (bad function call order)" ;; 45) echo "curl: Interface error (failed to bind to specified interface)" ;; 46) echo "curl: Bad password entered" ;; 47) echo "curl: Too many redirects" ;; 48) echo "curl: Unknown command line option specified" ;; 51) echo "curl: SSL peer certificate or SSH host key verification failed" ;; 52) echo "curl: Empty reply from server (got nothing)" ;; 55) echo "curl: Failed sending network data" ;; 56) echo "curl: Receive error (connection reset by peer)" ;; 57) echo "curl: Unrecoverable poll/select error (system I/O failure)" ;; 59) echo "curl: Couldn't use specified SSL cipher" ;; 61) echo "curl: Bad/unrecognized transfer encoding" ;; 63) echo "curl: Maximum file size exceeded" ;; 75) echo "Temporary failure (retry later)" ;; 78) echo "curl: Remote file not found (404 on FTP/file)" ;; 79) echo "curl: SSH session error (key exchange/auth failed)" ;; 92) echo "curl: HTTP/2 stream error (protocol violation)" ;; 95) echo "curl: HTTP/3 layer error" ;; # --- Package manager / APT / DPKG --- 100) echo "APT: Package manager error (broken packages / dependency problems)" ;; 101) echo "APT: Configuration error (bad sources.list, malformed config)" ;; 102) echo "APT: Lock held by another process (dpkg/apt still running)" ;; # --- Script Validation & Setup (103-123) --- 103) echo "Validation: Shell is not Bash" ;; 104) echo "Validation: Not running as root (or invoked via sudo)" ;; 105) echo "Validation: Proxmox VE version not supported" ;; 106) echo "Validation: Architecture not supported (ARM / PiMox)" ;; 107) echo "Validation: Kernel key parameters unreadable" ;; 108) echo "Validation: Kernel key limits exceeded" ;; 109) echo "Proxmox: No available container ID after max attempts" ;; 110) echo "Proxmox: Failed to apply default.vars" ;; 111) echo "Proxmox: App defaults file not available" ;; 112) echo "Proxmox: Invalid install menu option" ;; 113) echo "LXC: Under-provisioned — user aborted update" ;; 114) echo "LXC: Storage too low — user aborted update" ;; 115) echo "Download: install.func download failed or incomplete" ;; 116) echo "Proxmox: Default bridge vmbr0 not found" ;; 117) echo "LXC: Container did not reach running state" ;; 118) echo "LXC: No IP assigned to container after timeout" ;; 119) echo "Proxmox: No valid storage for rootdir content" ;; 120) echo "Proxmox: No valid storage for vztmpl content" ;; 121) echo "LXC: Container network not ready (no IP after retries)" ;; 122) echo "LXC: No internet connectivity — user declined to continue" ;; 123) echo "LXC: Local IP detection failed" ;; # --- BSD sysexits.h (64-78) --- 64) echo "Usage error (wrong arguments)" ;; 65) echo "Data format error (bad input data)" ;; 66) echo "Input file not found (cannot open input)" ;; 67) echo "User not found (addressee unknown)" ;; 68) echo "Host not found (hostname unknown)" ;; 69) echo "Service unavailable" ;; 70) echo "Internal software error" ;; 71) echo "System error (OS-level failure)" ;; 72) echo "Critical OS file missing" ;; 73) echo "Cannot create output file" ;; 74) echo "I/O error" ;; 76) echo "Remote protocol error" ;; 77) echo "Permission denied" ;; # --- Common shell/system errors --- 124) echo "Command timed out (timeout command)" ;; 125) echo "Command failed to start (Docker daemon or execution error)" ;; 126) echo "Command invoked cannot execute (permission problem?)" ;; 127) echo "Command not found" ;; 128) echo "Invalid argument to exit" ;; 129) echo "Killed by SIGHUP (terminal closed / hangup)" ;; 130) echo "Aborted by user (SIGINT)" ;; 131) echo "Killed by SIGQUIT (core dumped)" ;; 132) echo "Killed by SIGILL (illegal CPU instruction)" ;; 134) echo "Process aborted (SIGABRT - possibly Node.js heap overflow)" ;; 137) echo "Killed (SIGKILL / Out of memory?)" ;; 139) echo "Segmentation fault (core dumped)" ;; 141) echo "Broken pipe (SIGPIPE - output closed prematurely)" ;; 143) echo "Terminated (SIGTERM)" ;; 144) echo "Killed by signal 16 (SIGUSR1 / SIGSTKFLT)" ;; 146) echo "Killed by signal 18 (SIGTSTP)" ;; # --- Systemd / Service errors (150-154) --- 150) echo "Systemd: Service failed to start" ;; 151) echo "Systemd: Service unit not found" ;; 152) echo "Permission denied (EACCES)" ;; 153) echo "Build/compile failed (make/gcc/cmake)" ;; 154) echo "Node.js: Native addon build failed (node-gyp)" ;; # --- Python / pip / uv (160-162) --- 160) echo "Python: Virtualenv / uv environment missing or broken" ;; 161) echo "Python: Dependency resolution failed" ;; 162) echo "Python: Installation aborted (permissions or EXTERNALLY-MANAGED)" ;; # --- PostgreSQL (170-173) --- 170) echo "PostgreSQL: Connection failed (server not running / wrong socket)" ;; 171) echo "PostgreSQL: Authentication failed (bad user/password)" ;; 172) echo "PostgreSQL: Database does not exist" ;; 173) echo "PostgreSQL: Fatal error in query / syntax" ;; # --- MySQL / MariaDB (180-183) --- 180) echo "MySQL/MariaDB: Connection failed (server not running / wrong socket)" ;; 181) echo "MySQL/MariaDB: Authentication failed (bad user/password)" ;; 182) echo "MySQL/MariaDB: Database does not exist" ;; 183) echo "MySQL/MariaDB: Fatal error in query / syntax" ;; # --- MongoDB (190-193) --- 190) echo "MongoDB: Connection failed (server not running)" ;; 191) echo "MongoDB: Authentication failed (bad user/password)" ;; 192) echo "MongoDB: Database not found" ;; 193) echo "MongoDB: Fatal query error" ;; # --- Proxmox Custom Codes (200-231) --- 200) echo "Proxmox: Failed to create lock file" ;; 203) echo "Proxmox: Missing CTID variable" ;; 204) echo "Proxmox: Missing PCT_OSTYPE variable" ;; 205) echo "Proxmox: Invalid CTID (<100)" ;; 206) echo "Proxmox: CTID already in use" ;; 207) echo "Proxmox: Password contains unescaped special characters" ;; 208) echo "Proxmox: Invalid configuration (DNS/MAC/Network format)" ;; 209) echo "Proxmox: Container creation failed" ;; 210) echo "Proxmox: Cluster not quorate" ;; 211) echo "Proxmox: Timeout waiting for template lock" ;; 212) echo "Proxmox: Storage type 'iscsidirect' does not support containers (VMs only)" ;; 213) echo "Proxmox: Storage type does not support 'rootdir' content" ;; 214) echo "Proxmox: Not enough storage space" ;; 215) echo "Proxmox: Container created but not listed (ghost state)" ;; 216) echo "Proxmox: RootFS entry missing in config" ;; 217) echo "Proxmox: Storage not accessible" ;; 218) echo "Proxmox: Template file corrupted or incomplete" ;; 219) echo "Proxmox: CephFS does not support containers - use RBD" ;; 220) echo "Proxmox: Unable to resolve template path" ;; 221) echo "Proxmox: Template file not readable" ;; 222) echo "Proxmox: Template download failed" ;; 223) echo "Proxmox: Template not available after download" ;; 224) echo "Proxmox: PBS storage is for backups only" ;; 225) echo "Proxmox: No template available for OS/Version" ;; 226) echo "Proxmox: VM disk import or post-creation setup failed" ;; 231) echo "Proxmox: LXC stack upgrade failed" ;; # --- Tools & Addon Scripts (232-238) --- 232) echo "Tools: Wrong execution environment (run on PVE host, not inside LXC)" ;; 233) echo "Tools: Application not installed (update prerequisite missing)" ;; 234) echo "Tools: No LXC containers found or available" ;; 235) echo "Tools: Backup or restore operation failed" ;; 236) echo "Tools: Required hardware not detected" ;; 237) echo "Tools: Dependency package installation failed" ;; 238) echo "Tools: OS or distribution not supported for this addon" ;; # --- Node.js / npm / pnpm / yarn (239-249) --- 239) echo "npm/Node.js: Unexpected runtime error or dependency failure" ;; 243) echo "Node.js: Out of memory (JavaScript heap out of memory)" ;; 245) echo "Node.js: Invalid command-line option" ;; 246) echo "Node.js: Internal JavaScript Parse Error" ;; 247) echo "Node.js: Fatal internal error" ;; 248) echo "Node.js: Invalid C++ addon / N-API failure" ;; 249) echo "npm/pnpm/yarn: Unknown fatal error" ;; # --- Application Install/Update Errors (250-254) --- 250) echo "App: Download failed or version not determined" ;; 251) echo "App: File extraction failed (corrupt or incomplete archive)" ;; 252) echo "App: Required file or resource not found" ;; 253) echo "App: Data migration required — update aborted" ;; 254) echo "App: User declined prompt or input timed out" ;; # --- DPKG --- 255) echo "DPKG: Fatal internal error" ;; # --- Default --- *) echo "Unknown error" ;; esac } # ------------------------------------------------------------------------------ # json_escape() # # - Escapes a string for safe JSON embedding # - Strips ANSI escape sequences and non-printable control characters # - Handles backslashes, quotes, newlines, tabs, and carriage returns # - Uses jq when available (guaranteed correct), falls back to awk # ------------------------------------------------------------------------------ json_escape() { local input # Pipeline: strip ANSI → remove control chars → escape for JSON input=$(printf '%s' "$1" | sed 's/\x1b\[[0-9;]*[a-zA-Z]//g' | tr -d '\000-\010\013\014\016-\037\177\r') # Prefer jq: guaranteed correct JSON string encoding (handles all edge cases) if command -v jq &>/dev/null; then # jq -Rs reads raw stdin as string, outputs JSON-encoded string with quotes. # We strip the surrounding quotes since the heredoc adds them. printf '%s' "$input" | jq -Rs '.' | sed 's/^"//;s/"$//' return fi # Fallback: character-by-character processing with awk (avoids gsub replacement pitfalls) printf '%s' "$input" | awk ' BEGIN { ORS="" } { if (NR > 1) printf "%s", "\\n" for (i = 1; i <= length($0); i++) { c = substr($0, i, 1) if (c == "\\") printf "%s", "\\\\" else if (c == "\"") printf "%s", "\\\"" else if (c == "\t") printf "%s", "\\t" else printf "%s", c } }' } # ------------------------------------------------------------------------------ # get_error_text() # # - Returns last 20 lines of the active log (INSTALL_LOG or BUILD_LOG) # - Falls back to combined log or BUILD_LOG if primary is not accessible # - Handles container paths that don't exist on the host # ------------------------------------------------------------------------------ get_error_text() { local logfile="" if declare -f get_active_logfile >/dev/null 2>&1; then logfile=$(get_active_logfile) elif [[ -n "${INSTALL_LOG:-}" ]]; then logfile="$INSTALL_LOG" elif [[ -n "${BUILD_LOG:-}" ]]; then logfile="$BUILD_LOG" fi # If logfile is inside container (e.g. /root/.install-*), try the host copy if [[ -n "$logfile" && ! -s "$logfile" ]]; then # Try combined log: /tmp/--.log if [[ -n "${CTID:-}" && -n "${SESSION_ID:-}" ]]; then local combined_log="/tmp/${NSAPP:-lxc}-${CTID}-${SESSION_ID}.log" if [[ -s "$combined_log" ]]; then logfile="$combined_log" fi fi fi # Also try BUILD_LOG as fallback if primary log is empty/missing if [[ -z "$logfile" || ! -s "$logfile" ]] && [[ -n "${BUILD_LOG:-}" && -s "${BUILD_LOG}" ]]; then logfile="$BUILD_LOG" fi # Try SILENT_LOGFILE as last resort (captures $STD command output) if [[ -z "$logfile" || ! -s "$logfile" ]] && [[ -n "${SILENT_LOGFILE:-}" && -s "${SILENT_LOGFILE}" ]]; then logfile="$SILENT_LOGFILE" fi if [[ -n "$logfile" && -s "$logfile" ]]; then tail -n 20 "$logfile" 2>/dev/null | sed 's/\r$//' | sed 's/\x1b\[[0-9;]*[a-zA-Z]//g' fi } # ------------------------------------------------------------------------------ # get_full_log() # # - Returns the FULL installation log (build + install combined) # - Calls ensure_log_on_host() to pull container log if needed # - Strips ANSI escape codes and carriage returns # - Truncates to max_bytes (default: 120KB) to stay within API limits # - Used for the error telemetry field (full trace instead of 20 lines) # ------------------------------------------------------------------------------ get_full_log() { local max_bytes="${1:-122880}" # 120KB default local logfile="" # Ensure logs are available on host (pulls from container if needed) if declare -f ensure_log_on_host >/dev/null 2>&1; then ensure_log_on_host fi # Try combined log first (most complete) if [[ -n "${CTID:-}" && -n "${SESSION_ID:-}" ]]; then local combined_log="/tmp/${NSAPP:-lxc}-${CTID}-${SESSION_ID}.log" if [[ -s "$combined_log" ]]; then logfile="$combined_log" fi fi # Fall back to INSTALL_LOG if [[ -z "$logfile" || ! -s "$logfile" ]]; then if [[ -n "${INSTALL_LOG:-}" && -s "${INSTALL_LOG}" ]]; then logfile="$INSTALL_LOG" fi fi # Fall back to BUILD_LOG if [[ -z "$logfile" || ! -s "$logfile" ]]; then if [[ -n "${BUILD_LOG:-}" && -s "${BUILD_LOG}" ]]; then logfile="$BUILD_LOG" fi fi # Fall back to SILENT_LOGFILE (captures $STD command output) if [[ -z "$logfile" || ! -s "$logfile" ]]; then if [[ -n "${SILENT_LOGFILE:-}" && -s "${SILENT_LOGFILE}" ]]; then logfile="$SILENT_LOGFILE" fi fi if [[ -n "$logfile" && -s "$logfile" ]]; then # Strip ANSI codes, carriage returns, and anonymize IP addresses (GDPR) sed 's/\r$//' "$logfile" 2>/dev/null | sed 's/\x1b\[[0-9;]*[a-zA-Z]//g' | sed -E 's/([0-9]{1,3}\.)[0-9]{1,3}\.[0-9]{1,3}/\1x.x/g' | head -c "$max_bytes" fi } # ------------------------------------------------------------------------------ # build_error_string() # # - Builds a structured error string for telemetry reporting # - Format: "exit_code= | \n---\n" # - If no log lines available, returns just the explanation # - Arguments: # * $1: exit_code (numeric) # * $2: log_text (optional, output from get_error_text) # - Returns structured error string via stdout # ------------------------------------------------------------------------------ build_error_string() { local exit_code="${1:-1}" local log_text="${2:-}" local explanation explanation=$(explain_exit_code "$exit_code") if [[ -n "$log_text" ]]; then # Structured format: header + separator + log lines printf 'exit_code=%s | %s\n---\n%s' "$exit_code" "$explanation" "$log_text" else # No log available - just the explanation with exit code printf 'exit_code=%s | %s' "$exit_code" "$explanation" fi } # ============================================================================== # SECTION 2: TELEMETRY FUNCTIONS # ============================================================================== # ------------------------------------------------------------------------------ # detect_gpu() # # - Detects GPU vendor, model, and passthrough type # - Sets GPU_VENDOR, GPU_MODEL, and GPU_PASSTHROUGH globals # - Used for GPU analytics # ------------------------------------------------------------------------------ detect_gpu() { GPU_VENDOR="unknown" GPU_MODEL="" GPU_PASSTHROUGH="unknown" local gpu_line gpu_line=$(lspci 2>/dev/null | grep -iE "VGA|3D|Display" | head -1 || true) if [[ -n "$gpu_line" ]]; then # Extract model: everything after the colon, clean up GPU_MODEL=$(echo "$gpu_line" | sed 's/.*: //' | sed 's/ (rev .*)$//' | cut -c1-64) # Detect vendor and passthrough type if echo "$gpu_line" | grep -qi "Intel"; then GPU_VENDOR="intel" GPU_PASSTHROUGH="igpu" elif echo "$gpu_line" | grep -qi "AMD\|ATI"; then GPU_VENDOR="amd" if echo "$gpu_line" | grep -qi "Radeon RX\|Radeon Pro"; then GPU_PASSTHROUGH="dgpu" else GPU_PASSTHROUGH="igpu" fi elif echo "$gpu_line" | grep -qi "NVIDIA"; then GPU_VENDOR="nvidia" GPU_PASSTHROUGH="dgpu" fi fi export GPU_VENDOR GPU_MODEL GPU_PASSTHROUGH } # ------------------------------------------------------------------------------ # detect_cpu() # # - Detects CPU vendor and model # - Sets CPU_VENDOR (intel/amd/arm/unknown) and CPU_MODEL globals # - Used for CPU analytics # ------------------------------------------------------------------------------ detect_cpu() { CPU_VENDOR="unknown" CPU_MODEL="" if [[ -f /proc/cpuinfo ]]; then local vendor_id vendor_id=$(grep -m1 "vendor_id" /proc/cpuinfo 2>/dev/null | cut -d: -f2 | tr -d ' ' || true) case "$vendor_id" in GenuineIntel) CPU_VENDOR="intel" ;; AuthenticAMD) CPU_VENDOR="amd" ;; *) # ARM doesn't have vendor_id, check for CPU implementer if grep -qi "CPU implementer" /proc/cpuinfo 2>/dev/null; then CPU_VENDOR="arm" fi ;; esac # Extract model name and clean it up CPU_MODEL=$(grep -m1 "model name" /proc/cpuinfo 2>/dev/null | cut -d: -f2 | sed 's/^ *//' | sed 's/(R)//g' | sed 's/(TM)//g' | sed 's/ */ /g' | cut -c1-64 || true) fi export CPU_VENDOR CPU_MODEL } # ------------------------------------------------------------------------------ # detect_ram() # # - Detects RAM speed using dmidecode # - Sets RAM_SPEED global (e.g., "4800" for DDR5-4800) # - Requires root access for dmidecode # - Returns empty if not available or if speed is "Unknown" (nested VMs) # ------------------------------------------------------------------------------ detect_ram() { RAM_SPEED="" if command -v dmidecode &>/dev/null; then # Get configured memory speed (actual running speed) # Use || true to handle "Unknown" values in nested VMs (no numeric match) RAM_SPEED=$(dmidecode -t memory 2>/dev/null | grep -m1 "Configured Memory Speed:" | grep -oE "[0-9]+" | head -1) || true # Fallback to Speed: if Configured not available if [[ -z "$RAM_SPEED" ]]; then RAM_SPEED=$(dmidecode -t memory 2>/dev/null | grep -m1 "Speed:" | grep -oE "[0-9]+" | head -1) || true fi fi export RAM_SPEED } # ------------------------------------------------------------------------------ # post_to_api() # # - Sends LXC container creation statistics to telemetry ingest service # - Only executes if: # * curl is available # * DIAGNOSTICS=yes # * RANDOM_UUID is set # - Payload includes: # * Container type, disk size, CPU cores, RAM # * OS type and version # * Application name (NSAPP) # * Installation method # * PVE version # * Status: "installing" # * Random UUID for session tracking # - Anonymous telemetry (no personal data) # - Never blocks or fails script execution # ------------------------------------------------------------------------------ post_to_api() { # Prevent duplicate submissions (post_to_api is called from multiple places) [[ "${POST_TO_API_DONE:-}" == "true" ]] && return 0 # Silent fail - telemetry should never break scripts command -v curl &>/dev/null || { [[ "${DEV_MODE:-}" == "true" ]] && echo "[DEBUG] curl not found, skipping" >&2 return 0 } [[ "${DIAGNOSTICS:-no}" == "no" ]] && { [[ "${DEV_MODE:-}" == "true" ]] && echo "[DEBUG] DIAGNOSTICS=no, skipping" >&2 return 0 } [[ -z "${RANDOM_UUID:-}" ]] && { [[ "${DEV_MODE:-}" == "true" ]] && echo "[DEBUG] RANDOM_UUID empty, skipping" >&2 return 0 } [[ "${DEV_MODE:-}" == "true" ]] && echo "[DEBUG] post_to_api() DIAGNOSTICS=$DIAGNOSTICS RANDOM_UUID=$RANDOM_UUID NSAPP=$NSAPP" >&2 # Set type for later status updates (preserve if already set, e.g. turnkey) TELEMETRY_TYPE="${TELEMETRY_TYPE:-lxc}" local pve_version="" if command -v pveversion &>/dev/null; then pve_version=$(pveversion 2>/dev/null | awk -F'[/ ]' '{print $2}') || true fi # Detect GPU if not already set if [[ -z "${GPU_VENDOR:-}" ]]; then detect_gpu fi local gpu_vendor="${GPU_VENDOR:-unknown}" local gpu_model gpu_model=$(json_escape "${GPU_MODEL:-}") local gpu_passthrough="${GPU_PASSTHROUGH:-unknown}" # Detect CPU if not already set if [[ -z "${CPU_VENDOR:-}" ]]; then detect_cpu fi local cpu_vendor="${CPU_VENDOR:-unknown}" local cpu_model cpu_model=$(json_escape "${CPU_MODEL:-}") # Detect RAM if not already set if [[ -z "${RAM_SPEED:-}" ]]; then detect_ram fi local ram_speed="${RAM_SPEED:-}" local JSON_PAYLOAD JSON_PAYLOAD=$( cat <&2 [[ "${DEV_MODE:-}" == "true" ]] && echo "[DEBUG] Payload: $JSON_PAYLOAD" >&2 # Send initial "installing" record with retry. # This record MUST exist for all subsequent updates to succeed. local http_code="" attempt local _post_success=false for attempt in 1 2 3; do if [[ "${DEV_MODE:-}" == "true" ]]; then http_code=$(curl -sS -w "%{http_code}" -m "${TELEMETRY_TIMEOUT}" -X POST "${TELEMETRY_URL}" \ -H "Content-Type: application/json" \ -d "$JSON_PAYLOAD" -o /dev/stderr 2>&1) || http_code="000" echo "[DEBUG] post_to_api attempt $attempt HTTP=$http_code" >&2 else http_code=$(curl -sS -w "%{http_code}" -m "${TELEMETRY_TIMEOUT}" -X POST "${TELEMETRY_URL}" \ -H "Content-Type: application/json" \ -d "$JSON_PAYLOAD" -o /dev/null 2>/dev/null) || http_code="000" fi if [[ "$http_code" =~ ^2[0-9]{2}$ ]]; then _post_success=true break fi [[ "$attempt" -lt 3 ]] && sleep 1 done # Only mark done if at least one attempt succeeded. # If all 3 failed, POST_TO_API_DONE stays false so post_update_to_api # and on_exit() know the initial record was never created. # The server has fallback logic to create a new record on status updates, # so subsequent calls can still succeed even without the initial record. POST_TO_API_DONE=${_post_success} } # ------------------------------------------------------------------------------ # post_to_api_vm() # # - Sends VM creation statistics to telemetry ingest service # - Reads DIAGNOSTICS from /usr/local/community-scripts/diagnostics file # - Payload differences from LXC: # * ct_type=2 (VM instead of LXC) # * type="vm" # * Disk size without 'G' suffix # - Includes hardware detection: CPU, GPU, RAM speed # - Only executes if DIAGNOSTICS=yes and RANDOM_UUID is set # - Never blocks or fails script execution # ------------------------------------------------------------------------------ post_to_api_vm() { # Read diagnostics setting from file if [[ -f /usr/local/community-scripts/diagnostics ]]; then DIAGNOSTICS=$(grep -i "^DIAGNOSTICS=" /usr/local/community-scripts/diagnostics 2>/dev/null | awk -F'=' '{print $2}') || true fi # Silent fail - telemetry should never break scripts command -v curl &>/dev/null || return 0 [[ "${DIAGNOSTICS:-no}" == "no" ]] && return 0 [[ -z "${RANDOM_UUID:-}" ]] && return 0 # Set type for later status updates TELEMETRY_TYPE="vm" local pve_version="" if command -v pveversion &>/dev/null; then pve_version=$(pveversion 2>/dev/null | awk -F'[/ ]' '{print $2}') || true fi # Detect GPU if not already set if [[ -z "${GPU_VENDOR:-}" ]]; then detect_gpu fi local gpu_vendor="${GPU_VENDOR:-unknown}" local gpu_model gpu_model=$(json_escape "${GPU_MODEL:-}") local gpu_passthrough="${GPU_PASSTHROUGH:-unknown}" # Detect CPU if not already set if [[ -z "${CPU_VENDOR:-}" ]]; then detect_cpu fi local cpu_vendor="${CPU_VENDOR:-unknown}" local cpu_model cpu_model=$(json_escape "${CPU_MODEL:-}") # Detect RAM if not already set if [[ -z "${RAM_SPEED:-}" ]]; then detect_ram fi local ram_speed="${RAM_SPEED:-}" # Remove 'G' suffix from disk size local DISK_SIZE_API="${DISK_SIZE%G}" local JSON_PAYLOAD JSON_PAYLOAD=$( cat </dev/null) || http_code="000" if [[ "$http_code" =~ ^2[0-9]{2}$ ]]; then _post_success=true break fi [[ "$attempt" -lt 3 ]] && sleep 1 done POST_TO_API_DONE=${_post_success} } # ------------------------------------------------------------------------------ # post_progress_to_api() # # - Lightweight progress ping from host or container # - Updates the existing telemetry record status # - Arguments: # * $1: status (optional, default: "configuring") # Valid values: "validation", "configuring" # - Signals that the installation is actively progressing (not stuck) # - Fire-and-forget: never blocks or fails the script # - Only executes if DIAGNOSTICS=yes and RANDOM_UUID is set # - Can be called multiple times safely # ------------------------------------------------------------------------------ post_progress_to_api() { command -v curl &>/dev/null || return 0 [[ "${DIAGNOSTICS:-no}" == "no" ]] && return 0 [[ -z "${RANDOM_UUID:-}" ]] && return 0 local progress_status="${1:-configuring}" local app_name="${NSAPP:-${app:-unknown}}" local telemetry_type="${TELEMETRY_TYPE:-lxc}" curl -fsS -m 5 -X POST "${TELEMETRY_URL:-https://telemetry.community-scripts.org/telemetry}" \ -H "Content-Type: application/json" \ -d "{\"random_id\":\"${RANDOM_UUID}\",\"execution_id\":\"${EXECUTION_ID:-${RANDOM_UUID}}\",\"type\":\"${telemetry_type}\",\"nsapp\":\"${app_name}\",\"status\":\"${progress_status}\"}" &>/dev/null || true } # ------------------------------------------------------------------------------ # post_update_to_api() # # - Reports installation completion status to telemetry ingest service # - Prevents duplicate submissions via POST_UPDATE_DONE flag # - Arguments: # * $1: status ("done" or "failed") # * $2: exit_code (numeric, default: 1 for failed, 0 for done) # - Payload includes: # * Final status (mapped: "done"→"success", "failed"→"failed") # * Error description via explain_exit_code() # * Numeric exit code # - Only executes once per session # - Never blocks or fails script execution # ------------------------------------------------------------------------------ post_update_to_api() { # Silent fail - telemetry should never break scripts command -v curl &>/dev/null || return 0 # Support "force" mode (3rd arg) to bypass duplicate check for retries after cleanup local force="${3:-}" POST_UPDATE_DONE=${POST_UPDATE_DONE:-false} if [[ "$POST_UPDATE_DONE" == "true" && "$force" != "force" ]]; then return 0 fi [[ "${DIAGNOSTICS:-no}" == "no" ]] && return 0 [[ -z "${RANDOM_UUID:-}" ]] && return 0 local status="${1:-failed}" local raw_exit_code="${2:-1}" local exit_code=0 error="" pb_status error_category="" # Get GPU info (if detected) local gpu_vendor="${GPU_VENDOR:-unknown}" local gpu_model gpu_model=$(json_escape "${GPU_MODEL:-}") local gpu_passthrough="${GPU_PASSTHROUGH:-unknown}" # Get CPU info (if detected) local cpu_vendor="${CPU_VENDOR:-unknown}" local cpu_model cpu_model=$(json_escape "${CPU_MODEL:-}") # Get RAM info (if detected) local ram_speed="${RAM_SPEED:-}" # Map status to telemetry values: installing, success, failed, unknown case "$status" in done | success) pb_status="success" exit_code=0 error="" error_category="" ;; failed) pb_status="failed" ;; *) pb_status="unknown" ;; esac # For failed/unknown status, resolve exit code and error description local short_error="" medium_error="" if [[ "$pb_status" == "failed" ]] || [[ "$pb_status" == "unknown" ]]; then if [[ "$raw_exit_code" =~ ^[0-9]+$ ]]; then exit_code="$raw_exit_code" else exit_code=1 fi # Get full installation log for error field local log_text="" log_text=$(get_full_log 122880) || true # 120KB max if [[ -z "$log_text" ]]; then # Fallback to last 20 lines log_text=$(get_error_text) fi local full_error full_error=$(build_error_string "$exit_code" "$log_text") error=$(json_escape "$full_error") short_error=$(json_escape "$(explain_exit_code "$exit_code")") error_category=$(categorize_error "$exit_code") [[ -z "$error" ]] && error="Unknown error" # Build medium error for attempt 2: explanation + last 100 log lines (≤16KB) # This is the critical middle ground between full 120KB log and generic-only description local medium_log="" medium_log=$(get_full_log 16384) || true # 16KB max if [[ -z "$medium_log" ]]; then medium_log=$(get_error_text) || true fi local medium_full medium_full=$(build_error_string "$exit_code" "$medium_log") medium_error=$(json_escape "$medium_full") [[ -z "$medium_error" ]] && medium_error="$short_error" fi # Calculate duration if timer was started local duration=0 if [[ -n "${INSTALL_START_TIME:-}" ]]; then duration=$(($(date +%s) - INSTALL_START_TIME)) fi # Get PVE version local pve_version="" if command -v pveversion &>/dev/null; then pve_version=$(pveversion 2>/dev/null | awk -F'[/ ]' '{print $2}') || true fi local http_code="" # Strip 'G' suffix from disk size (VMs set DISK_SIZE=32G) local DISK_SIZE_API="${DISK_SIZE:-0}" DISK_SIZE_API="${DISK_SIZE_API%G}" [[ ! "$DISK_SIZE_API" =~ ^[0-9]+$ ]] && DISK_SIZE_API=0 # ── Attempt 1: Full payload with complete error text (includes full log) ── local JSON_PAYLOAD JSON_PAYLOAD=$( cat </dev/null) || http_code="000" if [[ "$http_code" =~ ^2[0-9]{2}$ ]]; then POST_UPDATE_DONE=true return 0 fi # ── Attempt 2: Medium error text (truncated log ≤16KB instead of full 120KB) ── sleep 1 local RETRY_PAYLOAD RETRY_PAYLOAD=$( cat </dev/null) || http_code="000" if [[ "$http_code" =~ ^2[0-9]{2}$ ]]; then POST_UPDATE_DONE=true return 0 fi # ── Attempt 3: Minimal payload with medium error (bare minimum to set status) ── sleep 2 local MINIMAL_PAYLOAD MINIMAL_PAYLOAD=$( cat </dev/null) || http_code="000" if [[ "$http_code" =~ ^2[0-9]{2}$ ]]; then POST_UPDATE_DONE=true return 0 fi # All 3 attempts failed — do NOT set POST_UPDATE_DONE=true. # This allows the EXIT trap (on_exit in error_handler.func) to retry. # No infinite loop risk: EXIT trap fires exactly once. } # ============================================================================== # SECTION 3: EXTENDED TELEMETRY FUNCTIONS # ============================================================================== # ------------------------------------------------------------------------------ # categorize_error() # # - Maps exit codes to error categories for better analytics # - Categories: network, storage, dependency, permission, timeout, config, resource, unknown # - Used to group errors in dashboard # ------------------------------------------------------------------------------ categorize_error() { # Allow build.func to override category based on log analysis (exit code 1 subclassification) if [[ -n "${ERROR_CATEGORY_OVERRIDE:-}" ]]; then echo "$ERROR_CATEGORY_OVERRIDE" return fi local code="$1" case "$code" in # Network errors (curl/wget) 6 | 7 | 22 | 35) echo "network" ;; # Docker / Privileged mode required 10) echo "config" ;; # Timeout errors 28 | 124 | 211) echo "timeout" ;; # Storage errors (Proxmox storage) 214 | 217 | 219 | 224) echo "storage" ;; # Dependency/Package errors (APT, DPKG, pip, commands) 100 | 101 | 102 | 127 | 160 | 161 | 162 | 255) echo "dependency" ;; # Permission errors 126 | 152) echo "permission" ;; # Configuration errors (Proxmox config, invalid args) 128 | 203 | 204 | 205 | 206 | 207 | 208) echo "config" ;; # Proxmox container/template errors 200 | 209 | 210 | 212 | 213 | 215 | 216 | 218 | 220 | 221 | 222 | 223 | 225 | 231) echo "proxmox" ;; # Service/Systemd errors 150 | 151 | 153 | 154) echo "service" ;; # Database errors (PostgreSQL, MySQL, MongoDB) 170 | 171 | 172 | 173 | 180 | 181 | 182 | 183 | 190 | 191 | 192 | 193) echo "database" ;; # Node.js / JavaScript runtime errors 243 | 245 | 246 | 247 | 248 | 249) echo "runtime" ;; # Python environment errors # (already covered: 160-162 under dependency) # Aborted by user (SIGHUP=terminal closed, SIGINT=Ctrl+C, SIGTERM=killed) 129 | 130 | 143) echo "user_aborted" ;; # Resource errors (OOM, SIGKILL, SIGABRT) 134 | 137) echo "resource" ;; # Signal/Process errors (SIGPIPE, SIGSEGV) 139 | 141) echo "signal" ;; # Shell errors (general error, syntax error) 1 | 2) echo "shell" ;; # Default - truly unknown *) echo "unknown" ;; esac } # ------------------------------------------------------------------------------ # start_install_timer() # # - Captures start time for installation duration tracking # - Call at the beginning of installation # - Sets INSTALL_START_TIME global variable # ------------------------------------------------------------------------------ start_install_timer() { INSTALL_START_TIME=$(date +%s) export INSTALL_START_TIME } # ------------------------------------------------------------------------------ # get_install_duration() # # - Returns elapsed seconds since start_install_timer() was called # - Returns 0 if timer was not started # ------------------------------------------------------------------------------ get_install_duration() { if [[ -z "${INSTALL_START_TIME:-}" ]]; then echo "0" return fi local now=$(date +%s) echo $((now - INSTALL_START_TIME)) } # ------------------------------------------------------------------------------ # _telemetry_report_exit() # # - Internal handler called by EXIT trap set in init_tool_telemetry() # - Determines success/failure from exit code and reports via appropriate API # - Arguments: # * $1: exit_code from the script # ------------------------------------------------------------------------------ _telemetry_report_exit() { local ec="${1:-0}" local status="success" [[ "$ec" -ne 0 ]] && status="failed" # Lazy name resolution: use explicit name, fall back to $APP, then "unknown" local name="${TELEMETRY_TOOL_NAME:-${APP:-unknown}}" if [[ "${TELEMETRY_TOOL_TYPE:-pve}" == "addon" ]]; then post_addon_to_api "$name" "$status" "$ec" else post_tool_to_api "$name" "$status" "$ec" fi } # ------------------------------------------------------------------------------ # init_tool_telemetry() # # - One-line telemetry setup for tools/addon scripts # - Reads DIAGNOSTICS from /usr/local/community-scripts/diagnostics # (persisted on PVE host during first build, and inside containers by install.func) # - Starts install timer for duration tracking # - Sets EXIT trap to automatically report success/failure on script exit # - Arguments: # * $1: tool_name (optional, falls back to $APP at exit time) # * $2: type ("pve" for PVE host scripts, "addon" for container addons) # - Usage: # source <(curl -fsSL .../misc/api.func) 2>/dev/null || true # init_tool_telemetry "post-pve-install" "pve" # init_tool_telemetry "" "addon" # uses $APP at exit time # ------------------------------------------------------------------------------ init_tool_telemetry() { local name="${1:-}" local type="${2:-pve}" [[ -n "$name" ]] && TELEMETRY_TOOL_NAME="$name" TELEMETRY_TOOL_TYPE="$type" # Read diagnostics opt-in/opt-out if [[ -f /usr/local/community-scripts/diagnostics ]]; then DIAGNOSTICS=$(grep -i "^DIAGNOSTICS=" /usr/local/community-scripts/diagnostics 2>/dev/null | awk -F'=' '{print $2}') || true fi start_install_timer # EXIT trap: automatically report telemetry when script ends trap '_telemetry_report_exit "$?"' EXIT } # ------------------------------------------------------------------------------ # post_tool_to_api() # # - Reports tool usage to telemetry # - Arguments: # * $1: tool_name (e.g., "microcode", "lxc-update", "post-pve-install") # * $2: status ("success" or "failed") # * $3: exit_code (optional, default: 0 for success, 1 for failed) # - For PVE host tools, not container installations # ------------------------------------------------------------------------------ post_tool_to_api() { command -v curl &>/dev/null || return 0 [[ "${DIAGNOSTICS:-no}" == "no" ]] && return 0 local tool_name="${1:-unknown}" local status="${2:-success}" local exit_code="${3:-0}" local error="" error_category="" local uuid duration # Generate UUID for this tool execution uuid=$(cat /proc/sys/kernel/random/uuid 2>/dev/null || uuidgen 2>/dev/null || echo "tool-$(date +%s)") duration=$(get_install_duration) # Map status [[ "$status" == "done" ]] && status="success" if [[ "$status" == "failed" ]]; then [[ ! "$exit_code" =~ ^[0-9]+$ ]] && exit_code=1 local error_text="" error_text=$(get_error_text) local full_error full_error=$(build_error_string "$exit_code" "$error_text") error=$(json_escape "$full_error") error_category=$(categorize_error "$exit_code") fi local pve_version="" if command -v pveversion &>/dev/null; then pve_version=$(pveversion 2>/dev/null | awk -F'[/ ]' '{print $2}') || true fi local JSON_PAYLOAD JSON_PAYLOAD=$( cat </dev/null || true } # ------------------------------------------------------------------------------ # post_addon_to_api() # # - Reports addon installation to telemetry # - Arguments: # * $1: addon_name (e.g., "filebrowser", "netdata") # * $2: status ("success" or "failed") # * $3: exit_code (optional) # - For addons installed inside containers # ------------------------------------------------------------------------------ post_addon_to_api() { command -v curl &>/dev/null || return 0 [[ "${DIAGNOSTICS:-no}" == "no" ]] && return 0 local addon_name="${1:-unknown}" local status="${2:-success}" local exit_code="${3:-0}" local error="" error_category="" local uuid duration # Generate UUID for this addon installation uuid=$(cat /proc/sys/kernel/random/uuid 2>/dev/null || uuidgen 2>/dev/null || echo "addon-$(date +%s)") duration=$(get_install_duration) # Map status [[ "$status" == "done" ]] && status="success" if [[ "$status" == "failed" ]]; then [[ ! "$exit_code" =~ ^[0-9]+$ ]] && exit_code=1 local error_text="" error_text=$(get_error_text) local full_error full_error=$(build_error_string "$exit_code" "$error_text") error=$(json_escape "$full_error") error_category=$(categorize_error "$exit_code") fi # Detect OS info local os_type="" os_version="" if [[ -f /etc/os-release ]]; then os_type=$(grep "^ID=" /etc/os-release | cut -d= -f2 | tr -d '"' || true) os_version=$(grep "^VERSION_ID=" /etc/os-release | cut -d= -f2 | tr -d '"' || true) fi local JSON_PAYLOAD JSON_PAYLOAD=$( cat </dev/null || true } # ------------------------------------------------------------------------------ # post_update_to_api_extended() # # - Extended version of post_update_to_api with duration, GPU, and error category # - Same arguments as post_update_to_api: # * $1: status ("done" or "failed") # * $2: exit_code (numeric) # - Automatically includes: # * Install duration (if start_install_timer was called) # * Error category (for failed status) # * GPU info (if detect_gpu was called) # ------------------------------------------------------------------------------ post_update_to_api_extended() { # Silent fail - telemetry should never break scripts command -v curl &>/dev/null || return 0 # Prevent duplicate submissions POST_UPDATE_DONE=${POST_UPDATE_DONE:-false} [[ "$POST_UPDATE_DONE" == "true" ]] && return 0 [[ "${DIAGNOSTICS:-no}" == "no" ]] && return 0 [[ -z "${RANDOM_UUID:-}" ]] && return 0 local status="${1:-failed}" local raw_exit_code="${2:-1}" local exit_code=0 error="" pb_status error_category="" local duration gpu_vendor gpu_passthrough # Get duration duration=$(get_install_duration) # Get GPU info (if detected) gpu_vendor="${GPU_VENDOR:-}" gpu_passthrough="${GPU_PASSTHROUGH:-}" # Map status to telemetry values case "$status" in done | success) pb_status="success" exit_code=0 error="" error_category="" ;; failed) pb_status="failed" ;; *) pb_status="unknown" ;; esac # For failed/unknown status, resolve exit code and error description if [[ "$pb_status" == "failed" ]] || [[ "$pb_status" == "unknown" ]]; then if [[ "$raw_exit_code" =~ ^[0-9]+$ ]]; then exit_code="$raw_exit_code" else exit_code=1 fi local error_text="" error_text=$(get_error_text) local full_error full_error=$(build_error_string "$exit_code" "$error_text") error=$(json_escape "$full_error") error_category=$(categorize_error "$exit_code") [[ -z "$error" ]] && error="Unknown error" fi local JSON_PAYLOAD JSON_PAYLOAD=$( cat </dev/null) || http_code="000" if [[ "$http_code" =~ ^2[0-9]{2}$ ]]; then POST_UPDATE_DONE=true return 0 fi # Retry with minimal payload sleep 1 http_code=$(curl -sS -w "%{http_code}" -m "${STATUS_TIMEOUT}" -X POST "${TELEMETRY_URL}" \ -H "Content-Type: application/json" \ -d "{\"random_id\":\"${RANDOM_UUID}\",\"execution_id\":\"${EXECUTION_ID:-${RANDOM_UUID}}\",\"type\":\"${TELEMETRY_TYPE:-lxc}\",\"nsapp\":\"${NSAPP:-unknown}\",\"status\":\"${pb_status}\",\"exit_code\":${exit_code},\"install_duration\":${duration:-0}}" \ -o /dev/null 2>/dev/null) || http_code="000" if [[ "$http_code" =~ ^2[0-9]{2}$ ]]; then POST_UPDATE_DONE=true return 0 fi # Do NOT set POST_UPDATE_DONE=true — let EXIT trap retry } ================================================ FILE: misc/auto-build.func ================================================ # Copyright (c) 2021-2025 tteck # Author: tteck (tteckster) # License: MIT # https://github.com/asylumexp/Proxmox/raw/main/LICENSE # AUTOMATED TESTING VERSION - Non-interactive build function variables() { NSAPP=$(echo "${APP,,}" | tr -d ' ') var_install="${NSAPP}-install" INTEGER='^[0-9]+([.][0-9]+)?$' PVEHOST_NAME=$(hostname) DIAGNOSTICS="no" # Disable diagnostics for automated testing METHOD="default" RANDOM_UUID="$(cat /proc/sys/kernel/random/uuid)" CT_TYPE=${var_unprivileged:-$CT_TYPE} } # For automated testing, we use the online versions # This ensures we're testing with the actual deployed code source <(curl -s https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/api.func) if command -v curl >/dev/null 2>&1; then source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/core.func) load_functions elif command -v wget >/dev/null 2>&1; then source <(wget -qO- https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/core.func) load_functions fi catch_errors() { set -Eeo pipefail trap 'error_handler $LINENO "$BASH_COMMAND"' ERR } error_handler() { source /dev/stdin <<<$(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/api.func) printf "\e[?25h" local exit_code="$?" local line_number="$1" local command="$2" local error_message="${RD}[ERROR]${CL} in line ${RD}$line_number${CL}: exit code ${RD}$exit_code${CL}: while executing command ${YW}$command${CL}" post_update_to_api "failed" "${command}" echo -e "\n$error_message\n" } shell_check() { if [[ "$(ps -p $$ -o comm=)" != "bash" ]]; then clear msg_error "Your default shell is not bash. Please report this to our github issues or discord." echo -e "\nExiting..." sleep 2 exit fi } root_check() { if [[ "$(id -u)" -ne 0 || $(ps -o comm= -p $PPID) == "sudo" ]]; then clear msg_error "Please run this script as root." echo -e "\nExiting..." sleep 2 exit fi } pve_check() { local PVE_VER PVE_VER="$(pveversion | awk -F'/' '{print $2}' | awk -F'-' '{print $1}')" if [[ "$PVE_VER" =~ ^8\.([0-9]+) ]]; then local MINOR="${BASH_REMATCH[1]}" if ((MINOR < 0 || MINOR > 9)); then msg_error "This version of Proxmox VE is not supported." msg_error "Supported: Proxmox VE version 8.0 – 8.9" exit 1 fi return 0 fi if [[ "$PVE_VER" =~ ^9\.([0-9]+) ]]; then local MINOR="${BASH_REMATCH[1]}" if ((MINOR != 0)); then msg_error "This version of Proxmox VE is not yet supported." msg_error "Supported: Proxmox VE version 9.0" exit 1 fi return 0 fi msg_error "This version of Proxmox VE is not supported." msg_error "Supported versions: Proxmox VE 8.0 – 8.x or 9.0" exit 1 } maxkeys_check() { per_user_maxkeys=$(cat /proc/sys/kernel/keys/maxkeys 2>/dev/null || echo 0) per_user_maxbytes=$(cat /proc/sys/kernel/keys/maxbytes 2>/dev/null || echo 0) if [[ "$per_user_maxkeys" -eq 0 || "$per_user_maxbytes" -eq 0 ]]; then echo -e "${CROSS}${RD} Error: Unable to read kernel parameters. Ensure proper permissions.${CL}" exit 1 fi used_lxc_keys=$(awk '/100000:/ {print $2}' /proc/key-users 2>/dev/null || echo 0) used_lxc_bytes=$(awk '/100000:/ {split($5, a, "/"); print a[1]}' /proc/key-users 2>/dev/null || echo 0) threshold_keys=$((per_user_maxkeys - 100)) threshold_bytes=$((per_user_maxbytes - 1000)) new_limit_keys=$((per_user_maxkeys * 2)) new_limit_bytes=$((per_user_maxbytes * 2)) failure=0 if [[ "$used_lxc_keys" -gt "$threshold_keys" ]]; then echo -e "${CROSS}${RD} Warning: Key usage is near the limit (${used_lxc_keys}/${per_user_maxkeys}).${CL}" echo -e "${INFO} Suggested action: Set ${GN}kernel.keys.maxkeys=${new_limit_keys}${CL} in ${BOLD}/etc/sysctl.d/98-community-scripts.conf${CL}." failure=1 fi if [[ "$used_lxc_bytes" -gt "$threshold_bytes" ]]; then echo -e "${CROSS}${RD} Warning: Key byte usage is near the limit (${used_lxc_bytes}/${per_user_maxbytes}).${CL}" echo -e "${INFO} Suggested action: Set ${GN}kernel.keys.maxbytes=${new_limit_bytes}${CL} in ${BOLD}/etc/sysctl.d/98-community-scripts.conf${CL}." failure=1 fi if [[ "$failure" -eq 1 ]]; then echo -e "${INFO} To apply changes, run: ${BOLD}service procps force-reload${CL}" exit 1 fi echo -e "${CM}${GN} All kernel key limits are within safe thresholds.${CL}" } arch_check() { if [ "$(dpkg --print-architecture)" != "arm64" ]; then echo -e "\n ${INFO}${YWB}This script will not work on non arm64 systems! \n" echo -e "\n ${YWB}Visit https://github.com/community-scripts/ProxmoxVE for AMD64 support. \n" echo -e "Exiting..." sleep 2 exit fi } get_current_ip() { if [ -f /etc/os-release ]; then if grep -qE 'ID=debian|ID=ubuntu' /etc/os-release; then CURRENT_IP=$(hostname -I | awk '{print $1}') elif grep -q 'ID=alpine' /etc/os-release; then CURRENT_IP=$(ip -4 addr show eth0 | awk '/inet / {print $2}' | cut -d/ -f1 | head -n 1) else CURRENT_IP="Unknown" fi fi echo "$CURRENT_IP" } update_motd_ip() { MOTD_FILE="/etc/motd" if [ -f "$MOTD_FILE" ]; then sed -i '/IP Address:/d' "$MOTD_FILE" IP=$(get_current_ip) echo -e "${TAB}${NETWORK}${YW} IP Address: ${GN}${IP}${CL}" >>"$MOTD_FILE" fi } # AUTOMATED: Skip SSH check ssh_check() { return 0 } base_settings() { CT_TYPE=${var_unprivileged:-"1"} DISK_SIZE=${var_disk:-"4"} CORE_COUNT=${var_cpu:-"1"} RAM_SIZE=${var_ram:-"1024"} VERBOSE="yes" # Force verbose for automated testing PW=${var_pw:-""} CT_ID=${var_ctid:-$NEXTID} HN=${var_hostname:-$NSAPP} BRG=${var_brg:-"vmbr0"} NET=${var_net:-"dhcp"} IPV6_METHOD=${var_ipv6_method:-"none"} IPV6_STATIC=${var_ipv6_static:-""} GATE=${var_gateway:-""} APT_CACHER=${var_apt_cacher:-""} APT_CACHER_IP=${var_apt_cacher_ip:-""} MTU=${var_mtu:-""} SD=${var_storage:-""} NS=${var_ns:-""} MAC=${var_mac:-""} VLAN=${var_vlan:-""} SSH=${var_ssh:-"no"} SSH_AUTHORIZED_KEY=${var_ssh_authorized_key:-""} UDHCPC_FIX=${var_udhcpc_fix:-""} TAGS="community-script;auto-test;${var_tags:-}" ENABLE_FUSE=${var_fuse:-"no"} ENABLE_TUN=${var_tun:-"no"} if [ -z "$var_os" ]; then var_os="debian" fi if [ -z "$var_version" ]; then var_version="12" fi } echo_default() { CT_TYPE_DESC="Unprivileged" if [ "$CT_TYPE" -eq 0 ]; then CT_TYPE_DESC="Privileged" fi echo -e "${CONTAINERID}${BOLD}${DGN}Container ID: ${BGN}${CT_ID}${CL}" echo -e "${OS}${BOLD}${DGN}Operating System: ${BGN}$var_os ($var_version)${CL}" echo -e "${CONTAINERTYPE}${BOLD}${DGN}Container Type: ${BGN}$CT_TYPE_DESC${CL}" echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}${DISK_SIZE} GB${CL}" echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}${CORE_COUNT}${CL}" echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}${RAM_SIZE} MiB${CL}" echo -e "${SEARCH}${BOLD}${DGN}Verbose Mode: ${BGN}Enabled (Auto-Test)${CL}" echo -e "${CREATING}${BOLD}${BL}Creating a ${APP} LXC using automated test settings${CL}" echo -e " " } exit_script() { clear echo -e "\n${CROSS}${RD}User exited script${CL}\n" exit } # AUTOMATED: Skip diagnostics check diagnostics_check() { DIAGNOSTICS="no" } # AUTOMATED: Non-interactive install install_script() { pve_check shell_check root_check arch_check maxkeys_check diagnostics_check if systemctl is-active -q ping-instances.service; then systemctl -q stop ping-instances.service fi NEXTID=$(pvesh get /cluster/nextid) timezone=$( (cat /etc/timezone 2>/dev/null || readlink -f /etc/localtime | sed 's|.*/zoneinfo/||') ) # Use default settings automatically header_info echo -e "${DEFAULT}${BOLD}${BL}Using Automated Test Settings on node $PVEHOST_NAME${CL}" VERBOSE="yes" METHOD="default" base_settings "$VERBOSE" echo_default } check_container_resources() { current_ram=$(free -m | awk 'NR==2{print $2}') current_cpu=$(nproc) if [[ "$current_ram" -lt "$var_ram" ]] || [[ "$current_cpu" -lt "$var_cpu" ]]; then echo -e "\n${INFO}${HOLD} ${GN}Required: ${var_cpu} CPU, ${var_ram}MB RAM ${CL}| ${RD}Current: ${current_cpu} CPU, ${current_ram}MB RAM${CL}" echo -e "${YWB}Container may be under-provisioned for ${APP} LXC.${CL}\n" fi } check_container_storage() { total_size=$(df /boot --output=size | tail -n 1) local used_size=$(df /boot --output=used | tail -n 1) usage=$((100 * used_size / total_size)) if ((usage > 80)); then echo -e "${INFO}${HOLD} ${YWB}Warning: Storage is dangerously low (${usage}%).${CL}" fi } start() { source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/tools.func) if ! command -v pveversion >/dev/null 2>&1; then if [ -f /etc/debian_version ] || [ -f /etc/lsb-release ]; then apt-get install -y whiptail &>/dev/null elif [ -f /etc/alpine-release ]; then apk add --no-cache whiptail &>/dev/null fi fi if command -v pveversion >/dev/null 2>&1; then install_script else echo -e "${RD}Not running on Proxmox VE host${CL}" exit 1 fi } build_container() { NET_STRING="-net0 name=eth0,bridge=$BRG$MAC,ip=$NET$GATE$VLAN$MTU" case "$IPV6_METHOD" in auto) NET_STRING="$NET_STRING,ip6=auto" ;; dhcp) NET_STRING="$NET_STRING,ip6=dhcp" ;; static) NET_STRING="$NET_STRING,ip6=$IPV6_ADDR" [ -n "$IPV6_GATE" ] && NET_STRING="$NET_STRING,gw6=$IPV6_GATE" ;; none) ;; esac if [ "$CT_TYPE" == "1" ]; then FEATURES="keyctl=1,nesting=1" else FEATURES="nesting=1" fi if [ "$ENABLE_FUSE" == "yes" ]; then FEATURES="$FEATURES,fuse=1" fi if [[ $DIAGNOSTICS == "yes" ]]; then post_to_api fi TEMP_DIR=$(mktemp -d) pushd "$TEMP_DIR" >/dev/null if [ "$var_os" == "alpine" ]; then export FUNCTIONS_FILE_PATH="$(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/alpine-install.func)" else export FUNCTIONS_FILE_PATH="$(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/install.func)" fi export DIAGNOSTICS="$DIAGNOSTICS" export RANDOM_UUID="$RANDOM_UUID" export CACHER="$APT_CACHER" export CACHER_IP="$APT_CACHER_IP" export tz="$timezone" export APPLICATION="$APP" export app="$NSAPP" export PASSWORD="$PW" export VERBOSE="$VERBOSE" export SSH_ROOT="${SSH}" export SSH_AUTHORIZED_KEY export CTID="$CT_ID" export CTTYPE="$CT_TYPE" export ENABLE_FUSE="$ENABLE_FUSE" export ENABLE_TUN="$ENABLE_TUN" export PCT_OSTYPE="$var_os" export PCT_OSVERSION="$var_version" export PCT_DISK_SIZE="$DISK_SIZE" export PCT_OPTIONS=" -features $FEATURES -hostname $HN -tags $TAGS $SD $NS $NET_STRING -onboot 1 -cores $CORE_COUNT -memory $RAM_SIZE -unprivileged $CT_TYPE $PW " bash -c "$(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/create_lxc.sh)" $? LXC_CONFIG="/etc/pve/lxc/${CTID}.conf" if [ "$CT_TYPE" == "0" ]; then cat <>"$LXC_CONFIG" # USB passthrough lxc.cgroup2.devices.allow: a lxc.cap.drop: lxc.cgroup2.devices.allow: c 188:* rwm lxc.cgroup2.devices.allow: c 189:* rwm lxc.mount.entry: /dev/serial/by-id dev/serial/by-id none bind,optional,create=dir lxc.mount.entry: /dev/ttyUSB0 dev/ttyUSB0 none bind,optional,create=file lxc.mount.entry: /dev/ttyUSB1 dev/ttyUSB1 none bind,optional,create=file lxc.mount.entry: /dev/ttyACM0 dev/ttyACM0 none bind,optional,create=file lxc.mount.entry: /dev/ttyACM1 dev/ttyACM1 none bind,optional,create=file EOF fi # Skip VAAPI interactive prompts in automated mode if [ "$ENABLE_TUN" == "yes" ]; then cat <>"$LXC_CONFIG" lxc.cgroup2.devices.allow: c 10:200 rwm lxc.mount.entry: /dev/net/tun dev/net/tun none bind,create=file EOF fi msg_info "Starting LXC Container" pct start "$CTID" for i in {1..10}; do if pct status "$CTID" | grep -q "status: running"; then msg_ok "Started LXC Container" break fi sleep 1 if [ "$i" -eq 10 ]; then msg_error "LXC Container did not reach running state" exit 1 fi done if [ "$var_os" != "alpine" ]; then msg_info "Waiting for network in LXC container" for i in {1..10}; do if pct exec "$CTID" -- ping -c1 -W1 deb.debian.org >/dev/null 2>&1; then msg_ok "Network in LXC is reachable (ping)" break fi if [ "$i" -lt 10 ]; then msg_warn "No network in LXC yet (try $i/10) – waiting..." sleep 3 else msg_warn "Ping failed 10 times. Trying HTTP connectivity check (wget) as fallback..." if pct exec "$CTID" -- wget -q --spider http://deb.debian.org; then msg_ok "Network in LXC is reachable (wget fallback)" else msg_error "No network in LXC after all checks - using fallback DNS" pct set "$CTID" --nameserver 1.1.1.1 pct set "$CTID" --nameserver 8.8.8.8 fi break fi done fi msg_info "Customizing LXC Container" : "${tz:=Etc/UTC}" if [ "$var_os" == "alpine" ]; then sleep 3 pct exec "$CTID" -- /bin/sh -c 'cat </etc/apk/repositories http://dl-cdn.alpinelinux.org/alpine/latest-stable/main http://dl-cdn.alpinelinux.org/alpine/latest-stable/community EOF' pct exec "$CTID" -- ash -c "apk add bash newt curl openssh nano mc ncurses jq >/dev/null" else sleep 3 pct exec "$CTID" -- bash -c "sed -i '/$LANG/ s/^# //' /etc/locale.gen" pct exec "$CTID" -- bash -c "locale_line=\$(grep -v '^#' /etc/locale.gen | grep -E '^[a-zA-Z]' | awk '{print \$1}' | head -n 1) && \ echo LANG=\$locale_line >/etc/default/locale && \ locale-gen >/dev/null && \ export LANG=\$locale_line" if [[ -z "${tz:-}" ]]; then tz=$(timedatectl show --property=Timezone --value 2>/dev/null || echo "Etc/UTC") fi if pct exec "$CTID" -- test -e "/usr/share/zoneinfo/$tz"; then pct exec "$CTID" -- bash -c "tz='$tz'; echo \"\$tz\" >/etc/timezone && ln -sf \"/usr/share/zoneinfo/\$tz\" /etc/localtime" else msg_warn "Skipping timezone setup – zone '$tz' not found in container" fi pct exec "$CTID" -- bash -c "apt-get update >/dev/null && apt-get install -y sudo curl mc gnupg2 jq >/dev/null" fi msg_ok "Customized LXC Container" lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/install/"$var_install".sh)" } description() { IP=$(pct exec "$CTID" ip a s dev eth0 | awk '/inet / {print $2}' | cut -d/ -f1) DESCRIPTION=$( cat < Logo

${APP} LXC (Auto-Test)

spend Coffee

GitHub EOF ) pct set "$CTID" -description "$DESCRIPTION" if [[ -f /etc/systemd/system/ping-instances.service ]]; then systemctl start ping-instances.service fi post_update_to_api "done" "none" } api_exit_script() { exit_code=$? if [ $exit_code -ne 0 ]; then case $exit_code in 100) post_update_to_api "failed" "100: Unexpected error in create_lxc.sh" ;; 101) post_update_to_api "failed" "101: No network connection detected in create_lxc.sh" ;; 200) post_update_to_api "failed" "200: LXC creation failed in create_lxc.sh" ;; 203) post_update_to_api "failed" "203: CTID not set in create_lxc.sh" ;; 204) post_update_to_api "failed" "204: PCT_OSTYPE not set in create_lxc.sh" ;; 205) post_update_to_api "failed" "205: CTID cannot be less than 100 in create_lxc.sh" ;; 206) post_update_to_api "failed" "206: CTID already in use in create_lxc.sh" ;; 207) post_update_to_api "failed" "207: Template not found in create_lxc.sh" ;; 208) post_update_to_api "failed" "208: Error downloading template in create_lxc.sh" ;; 209) post_update_to_api "failed" "209: Container creation failed in create_lxc.sh" ;; *) post_update_to_api "failed" "Unknown error, exit code: $exit_code" ;; esac fi } if command -v pveversion >/dev/null 2>&1; then trap 'api_exit_script' EXIT fi trap 'post_update_to_api "failed" "$BASH_COMMAND"' ERR trap 'post_update_to_api "failed" "INTERRUPTED"' SIGINT trap 'post_update_to_api "failed" "TERMINATED"' SIGTERM ================================================ FILE: misc/build.func ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: tteck (tteckster) | MickLesk | michelroegl-brunner # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/branch/main/LICENSE # ============================================================================== # BUILD.FUNC - LXC CONTAINER BUILD & CONFIGURATION # ============================================================================== # # This file provides the main build functions for creating and configuring # LXC containers in Proxmox VE. It handles: # # - Variable initialization and defaults # - Container creation and resource allocation # - Storage selection and management # - Advanced configuration and customization # - User interaction menus and prompts # # Usage: # - Sourced automatically by CT creation scripts # - Requires core.func and error_handler.func to be loaded first # # ============================================================================== # ============================================================================== # SECTION 1: INITIALIZATION & CORE VARIABLES # ============================================================================== # ------------------------------------------------------------------------------ # variables() # # - Initializes core variables for container creation # - Normalizes application name (NSAPP = lowercase, no spaces) # - Builds installer filename (var_install) # - Defines regex patterns for validation # - Fetches Proxmox hostname and version # - Generates unique session ID for tracking and logging # - Captures app-declared resource defaults (CPU, RAM, Disk) # ------------------------------------------------------------------------------ variables() { NSAPP=$(echo "${APP,,}" | tr -d ' ') # This function sets the NSAPP variable by converting the value of the APP variable to lowercase and removing any spaces. var_install="${NSAPP}-install" # sets the var_install variable by appending "-install" to the value of NSAPP. INTEGER='^[0-9]+([.][0-9]+)?$' # it defines the INTEGER regular expression pattern. PVEHOST_NAME=$(hostname) # gets the Proxmox Hostname and sets it to Uppercase DIAGNOSTICS="no" # Safe default: no telemetry until user consents via diagnostics_check() METHOD="default" # sets the METHOD variable to "default", used for the API call. RANDOM_UUID="$(cat /proc/sys/kernel/random/uuid)" # generates a random UUID and sets it to the RANDOM_UUID variable. EXECUTION_ID="${RANDOM_UUID}" # Unique execution ID for telemetry record identification (unique-indexed in PocketBase) SESSION_ID="${RANDOM_UUID:0:8}" # Short session ID (first 8 chars of UUID) for log files BUILD_LOG="/tmp/create-lxc-${SESSION_ID}.log" # Host-side container creation log # NOTE: combined_log is constructed locally in build_container() and ensure_log_on_host() # as "/tmp/${NSAPP}-${CTID}-${SESSION_ID}.log" (requires CTID, not available here) CTTYPE="${CTTYPE:-${CT_TYPE:-1}}" # Parse dev_mode early parse_dev_mode # Setup persistent log directory if logs mode active if [[ "${DEV_MODE_LOGS:-false}" == "true" ]]; then mkdir -p /var/log/community-scripts BUILD_LOG="/var/log/community-scripts/create-lxc-${SESSION_ID}-$(date +%Y%m%d_%H%M%S).log" fi # Get Proxmox VE version and kernel version if command -v pveversion >/dev/null 2>&1; then PVEVERSION="$(pveversion | awk -F'/' '{print $2}' | awk -F'-' '{print $1}')" else PVEVERSION="N/A" fi KERNEL_VERSION=$(uname -r) # Capture app-declared defaults (for precedence logic) # These values are set by the app script BEFORE default.vars is loaded # If app declares higher values than default.vars, app values take precedence if [[ -n "${var_cpu:-}" && "${var_cpu}" =~ ^[0-9]+$ ]]; then export APP_DEFAULT_CPU="${var_cpu}" fi if [[ -n "${var_ram:-}" && "${var_ram}" =~ ^[0-9]+$ ]]; then export APP_DEFAULT_RAM="${var_ram}" fi if [[ -n "${var_disk:-}" && "${var_disk}" =~ ^[0-9]+$ ]]; then export APP_DEFAULT_DISK="${var_disk}" fi } source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/api.func) if command -v curl >/dev/null 2>&1; then source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/core.func) source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/error_handler.func) load_functions catch_errors elif command -v wget >/dev/null 2>&1; then source <(wget -qO- https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/core.func) source <(wget -qO- https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/error_handler.func) load_functions catch_errors fi # ============================================================================== # SECTION 2: PRE-FLIGHT CHECKS & SYSTEM VALIDATION # ============================================================================== # ------------------------------------------------------------------------------ # maxkeys_check() # # - Reads kernel keyring limits (maxkeys, maxbytes) # - Checks current usage for LXC user (UID 100000) # - Warns if usage is close to limits and suggests sysctl tuning # - Exits if thresholds are exceeded # - https://cleveruptime.com/docs/files/proc-key-users | https://docs.kernel.org/security/keys/core.html # ------------------------------------------------------------------------------ maxkeys_check() { # Read kernel parameters per_user_maxkeys=$(cat /proc/sys/kernel/keys/maxkeys 2>/dev/null || echo 0) per_user_maxbytes=$(cat /proc/sys/kernel/keys/maxbytes 2>/dev/null || echo 0) # Exit if kernel parameters are unavailable if [[ "$per_user_maxkeys" -eq 0 || "$per_user_maxbytes" -eq 0 ]]; then msg_error "Unable to read kernel key parameters. Ensure proper permissions." exit 107 fi # Fetch key usage for user ID 100000 (typical for containers) used_lxc_keys=$(awk '/100000:/ {print $2}' /proc/key-users 2>/dev/null || echo 0) used_lxc_bytes=$(awk '/100000:/ {split($5, a, "/"); print a[1]}' /proc/key-users 2>/dev/null || echo 0) # Calculate thresholds and suggested new limits threshold_keys=$((per_user_maxkeys - 100)) threshold_bytes=$((per_user_maxbytes - 1000)) new_limit_keys=$((per_user_maxkeys * 2)) new_limit_bytes=$((per_user_maxbytes * 2)) # Check if key or byte usage is near limits failure=0 if [[ "$used_lxc_keys" -gt "$threshold_keys" ]]; then msg_warn "Key usage is near the limit (${used_lxc_keys}/${per_user_maxkeys})" echo -e "${INFO} Suggested action: Set ${GN}kernel.keys.maxkeys=${new_limit_keys}${CL} in ${BOLD}/etc/sysctl.d/98-community-scripts.conf${CL}." failure=1 fi if [[ "$used_lxc_bytes" -gt "$threshold_bytes" ]]; then msg_warn "Key byte usage is near the limit (${used_lxc_bytes}/${per_user_maxbytes})" echo -e "${INFO} Suggested action: Set ${GN}kernel.keys.maxbytes=${new_limit_bytes}${CL} in ${BOLD}/etc/sysctl.d/98-community-scripts.conf${CL}." failure=1 fi # Provide next steps if issues are detected if [[ "$failure" -eq 1 ]]; then msg_error "Kernel key limits exceeded - see suggestions above" exit 108 fi # Silent success - only show errors if they exist } # ============================================================================== # SECTION 3: CONTAINER SETUP UTILITIES # ============================================================================== # ------------------------------------------------------------------------------ # get_current_ip() # # - Returns current container IP depending on OS type # - Debian/Ubuntu: uses `hostname -I` # - Alpine: parses eth0 via `ip -4 addr` or `ip -6 addr` # - Supports IPv6-only environments as fallback # - Returns "Unknown" if OS type cannot be determined # ------------------------------------------------------------------------------ get_current_ip() { CURRENT_IP="" if [ -f /etc/os-release ]; then # Check for Debian/Ubuntu (uses hostname -I) if grep -qE 'ID=debian|ID=ubuntu' /etc/os-release; then # Try IPv4 first CURRENT_IP=$(hostname -I 2>/dev/null | tr ' ' '\n' | grep -E '^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$' | head -n1 || true) # Fallback to IPv6 if no IPv4 if [[ -z "$CURRENT_IP" ]]; then CURRENT_IP=$(hostname -I 2>/dev/null | tr ' ' '\n' | grep -E ':' | head -n1 || true) fi # Check for Alpine (uses ip command) elif grep -q 'ID=alpine' /etc/os-release; then # Try IPv4 first CURRENT_IP=$(ip -4 addr show eth0 2>/dev/null | awk '/inet / {print $2}' | cut -d/ -f1 | head -n 1) # Fallback to IPv6 if no IPv4 if [[ -z "$CURRENT_IP" ]]; then CURRENT_IP=$(ip -6 addr show eth0 scope global 2>/dev/null | awk '/inet6 / {print $2}' | cut -d/ -f1 | head -n 1) fi else CURRENT_IP="Unknown" fi fi echo "$CURRENT_IP" } # ------------------------------------------------------------------------------ # update_motd_ip() # # - Updates /etc/motd with current container IP # - Removes old IP entries to avoid duplicates # - Regenerates /etc/profile.d/00_lxc-details.sh with dynamic OS/IP info # ------------------------------------------------------------------------------ update_motd_ip() { MOTD_FILE="/etc/motd" PROFILE_FILE="/etc/profile.d/00_lxc-details.sh" if [ -f "$MOTD_FILE" ]; then # Remove existing IP Address lines to prevent duplication sed -i '/IP Address:/d' "$MOTD_FILE" IP=$(get_current_ip) # Add the new IP address echo -e "${TAB}${NETWORK}${YW} IP Address: ${GN}${IP}${CL}" >>"$MOTD_FILE" fi # Update dynamic LXC details profile if values changed (e.g., after OS upgrade) # Only update if file exists and is from community-scripts if [ -f "$PROFILE_FILE" ] && grep -q "community-scripts" "$PROFILE_FILE" 2>/dev/null; then # Get current values local current_os="$(grep ^NAME /etc/os-release | cut -d= -f2 | tr -d '"') - Version: $(grep ^VERSION_ID /etc/os-release | cut -d= -f2 | tr -d '"')" local current_hostname="$(hostname)" local current_ip="$(hostname -I | awk '{print $1}')" # Escape sed special chars in replacement strings (& \ |) current_os="${current_os//\\/\\\\}" current_os="${current_os//&/\\&}" current_hostname="${current_hostname//\\/\\\\}" current_hostname="${current_hostname//&/\\&}" current_ip="${current_ip//\\/\\\\}" current_ip="${current_ip//&/\\&}" # Update only if values actually changed if ! grep -q "OS:.*$current_os" "$PROFILE_FILE" 2>/dev/null; then sed -i "s|OS:.*|OS: \${GN}$current_os\${CL}\\\"|" "$PROFILE_FILE" fi if ! grep -q "Hostname:.*$current_hostname" "$PROFILE_FILE" 2>/dev/null; then sed -i "s|Hostname:.*|Hostname: \${GN}$current_hostname\${CL}\\\"|" "$PROFILE_FILE" fi if ! grep -q "IP Address:.*$current_ip" "$PROFILE_FILE" 2>/dev/null; then sed -i "s|IP Address:.*|IP Address: \${GN}$current_ip\${CL}\\\"|" "$PROFILE_FILE" fi fi } # ------------------------------------------------------------------------------ # install_ssh_keys_into_ct() # # - Installs SSH keys into container root account if SSH is enabled # - Uses pct push or direct input to authorized_keys # - Supports both SSH_KEYS_FILE (from advanced settings) and SSH_AUTHORIZED_KEY (from user defaults) # - Falls back to warning if no keys provided # ------------------------------------------------------------------------------ install_ssh_keys_into_ct() { [[ "${SSH:-no}" != "yes" ]] && return 0 # Ensure SSH_KEYS_FILE is defined (may not be set if advanced_settings was skipped) : "${SSH_KEYS_FILE:=}" # If SSH_KEYS_FILE doesn't exist but SSH_AUTHORIZED_KEY is set (from user defaults), # create a temporary SSH_KEYS_FILE with the key if [[ -z "$SSH_KEYS_FILE" || ! -s "$SSH_KEYS_FILE" ]] && [[ -n "${SSH_AUTHORIZED_KEY:-}" ]]; then SSH_KEYS_FILE="$(mktemp)" printf '%s\n' "$SSH_AUTHORIZED_KEY" >"$SSH_KEYS_FILE" fi if [[ -n "$SSH_KEYS_FILE" && -s "$SSH_KEYS_FILE" ]]; then msg_info "Installing selected SSH keys into CT ${CTID}" pct exec "$CTID" -- sh -c 'mkdir -p /root/.ssh && chmod 700 /root/.ssh' || { msg_error "prepare /root/.ssh failed" return 252 } pct push "$CTID" "$SSH_KEYS_FILE" /root/.ssh/authorized_keys >/dev/null 2>&1 || pct exec "$CTID" -- sh -c "cat > /root/.ssh/authorized_keys" <"$SSH_KEYS_FILE" || { msg_error "write authorized_keys failed" return 252 } pct exec "$CTID" -- sh -c 'chmod 600 /root/.ssh/authorized_keys' || true msg_ok "Installed SSH keys into CT ${CTID}" return 0 fi # Fallback msg_warn "No SSH keys to install (skipping)." return 0 } # ------------------------------------------------------------------------------ # validate_container_id() # # - Validates if a container ID is available for use (CLUSTER-WIDE) # - Checks cluster resources via pvesh for VMs/CTs on ALL nodes # - Falls back to local config file check if pvesh unavailable # - Checks if ID is used in LVM logical volumes # - Returns 0 if ID is available, 1 if already in use # ------------------------------------------------------------------------------ validate_container_id() { local ctid="$1" # Check if ID is numeric if ! [[ "$ctid" =~ ^[0-9]+$ ]]; then return 1 fi # CLUSTER-WIDE CHECK: Query all VMs/CTs across all nodes # This catches IDs used on other nodes in the cluster # NOTE: Works on single-node too - Proxmox always has internal cluster structure # Falls back gracefully if pvesh unavailable or returns empty if command -v pvesh &>/dev/null; then local cluster_ids cluster_ids=$(pvesh get /cluster/resources --type vm --output-format json 2>/dev/null | grep -oP '"vmid":\s*\K[0-9]+' 2>/dev/null || true) if [[ -n "$cluster_ids" ]] && echo "$cluster_ids" | grep -qw "$ctid"; then return 1 fi fi # LOCAL FALLBACK: Check if config file exists for VM or LXC # This handles edge cases where pvesh might not return all info if [[ -f "/etc/pve/qemu-server/${ctid}.conf" ]] || [[ -f "/etc/pve/lxc/${ctid}.conf" ]]; then return 1 fi # Check ALL nodes in cluster for config files (handles pmxcfs sync delays) # NOTE: On single-node, /etc/pve/nodes/ contains just the one node - still works if [[ -d "/etc/pve/nodes" ]]; then for node_dir in /etc/pve/nodes/*/; do if [[ -f "${node_dir}qemu-server/${ctid}.conf" ]] || [[ -f "${node_dir}lxc/${ctid}.conf" ]]; then return 1 fi done fi # Check if ID is used in LVM logical volumes if lvs --noheadings -o lv_name 2>/dev/null | grep -qE "(^|[-_])${ctid}($|[-_])"; then return 1 fi return 0 } # ------------------------------------------------------------------------------ # get_valid_container_id() # # - Returns a valid, unused container ID (CLUSTER-AWARE) # - Uses pvesh /cluster/nextid as starting point (already cluster-aware) # - If provided ID is valid, returns it # - Otherwise increments until a free one is found across entire cluster # - Calls validate_container_id() to check availability # ------------------------------------------------------------------------------ get_valid_container_id() { local suggested_id="${1:-$(pvesh get /cluster/nextid 2>/dev/null || echo 100)}" # Ensure we have a valid starting ID if ! [[ "$suggested_id" =~ ^[0-9]+$ ]]; then suggested_id=$(pvesh get /cluster/nextid 2>/dev/null || echo 100) fi local max_attempts=1000 local attempts=0 while ! validate_container_id "$suggested_id"; do suggested_id=$((suggested_id + 1)) attempts=$((attempts + 1)) if [[ $attempts -ge $max_attempts ]]; then msg_error "Could not find available container ID after $max_attempts attempts" exit 109 fi done echo "$suggested_id" } # ------------------------------------------------------------------------------ # validate_hostname() # # - Validates hostname/FQDN according to RFC 1123/952 # - Checks total length (max 253 characters for FQDN) # - Validates each label (max 63 chars, alphanumeric + hyphens) # - Returns 0 if valid, 1 if invalid # ------------------------------------------------------------------------------ validate_hostname() { local hostname="$1" # Check total length (max 253 for FQDN) if [[ ${#hostname} -gt 253 ]] || [[ -z "$hostname" ]]; then return 1 fi # Split by dots and validate each label local IFS='.' read -ra labels <<<"$hostname" for label in "${labels[@]}"; do # Each label: 1-63 chars, alphanumeric, hyphens allowed (not at start/end) if [[ -z "$label" ]] || [[ ${#label} -gt 63 ]]; then return 1 fi if [[ ! "$label" =~ ^[a-z0-9]([a-z0-9-]*[a-z0-9])?$ ]] && [[ ! "$label" =~ ^[a-z0-9]$ ]]; then return 1 fi done return 0 } # ------------------------------------------------------------------------------ # validate_mac_address() # # - Validates MAC address format (XX:XX:XX:XX:XX:XX) # - Empty value is allowed (auto-generated) # - Returns 0 if valid, 1 if invalid # ------------------------------------------------------------------------------ validate_mac_address() { local mac="$1" [[ -z "$mac" ]] && return 0 if [[ ! "$mac" =~ ^([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}$ ]]; then return 1 fi return 0 } # ------------------------------------------------------------------------------ # validate_vlan_tag() # # - Validates VLAN tag (1-4094) # - Empty value is allowed (no VLAN) # - Returns 0 if valid, 1 if invalid # ------------------------------------------------------------------------------ validate_vlan_tag() { local vlan="$1" [[ -z "$vlan" ]] && return 0 if ! [[ "$vlan" =~ ^[0-9]+$ ]] || ((vlan < 1 || vlan > 4094)); then return 1 fi return 0 } # ------------------------------------------------------------------------------ # validate_mtu() # # - Validates MTU size (576-65535, common values: 1500, 9000) # - Empty value is allowed (default 1500) # - Returns 0 if valid, 1 if invalid # ------------------------------------------------------------------------------ validate_mtu() { local mtu="$1" [[ -z "$mtu" ]] && return 0 if ! [[ "$mtu" =~ ^[0-9]+$ ]] || ((mtu < 576 || mtu > 65535)); then return 1 fi return 0 } # ------------------------------------------------------------------------------ # validate_ipv6_address() # # - Validates IPv6 address with optional CIDR notation # - Supports compressed (::) and full notation # - Empty value is allowed # - Returns 0 if valid, 1 if invalid # ------------------------------------------------------------------------------ validate_ipv6_address() { local ipv6="$1" [[ -z "$ipv6" ]] && return 0 # Extract address and CIDR local addr="${ipv6%%/*}" local cidr="${ipv6##*/}" # Validate CIDR if present (1-128) if [[ "$ipv6" == */* ]]; then if ! [[ "$cidr" =~ ^[0-9]+$ ]] || ((cidr < 1 || cidr > 128)); then return 1 fi fi # Basic IPv6 validation - check for valid characters and structure # Must contain only hex digits and colons if [[ ! "$addr" =~ ^[0-9a-fA-F:]+$ ]]; then return 1 fi # Must contain at least one colon if [[ ! "$addr" == *:* ]]; then return 1 fi # Check for valid double-colon usage (only one :: allowed) if [[ "$addr" == *::*::* ]]; then return 1 fi # Check that no segment exceeds 4 hex chars local IFS=':' local -a segments read -ra segments <<<"$addr" for seg in "${segments[@]}"; do if [[ ${#seg} -gt 4 ]]; then return 1 fi done return 0 } # ------------------------------------------------------------------------------ # validate_bridge() # # - Validates that network bridge exists and is active # - Returns 0 if valid, 1 if invalid # ------------------------------------------------------------------------------ validate_bridge() { local bridge="$1" [[ -z "$bridge" ]] && return 1 # Check if bridge interface exists if ! ip link show dev "$bridge" &>/dev/null; then return 1 fi return 0 } # ------------------------------------------------------------------------------ # validate_gateway_in_subnet() # # - Validates that gateway IP is in the same subnet as static IP # - Arguments: static_ip (with CIDR), gateway_ip # - Returns 0 if valid, 1 if invalid # ------------------------------------------------------------------------------ validate_gateway_in_subnet() { local static_ip="$1" local gateway="$2" [[ -z "$static_ip" || -z "$gateway" ]] && return 0 # Extract IP and CIDR local ip="${static_ip%%/*}" local cidr="${static_ip##*/}" # /31 and /32 are valid point-to-point / zero-trust DMZ configurations # where the gateway is technically outside the subnet — skip validation ((cidr >= 31)) && return 0 # Convert CIDR to netmask bits local mask=$((0xFFFFFFFF << (32 - cidr) & 0xFFFFFFFF)) # Convert IPs to integers local IFS='.' read -r i1 i2 i3 i4 <<<"$ip" read -r g1 g2 g3 g4 <<<"$gateway" local ip_int=$(((i1 << 24) + (i2 << 16) + (i3 << 8) + i4)) local gw_int=$(((g1 << 24) + (g2 << 16) + (g3 << 8) + g4)) # Check if both are in same network if (((ip_int & mask) != (gw_int & mask))); then return 1 fi return 0 } # ------------------------------------------------------------------------------ # validate_ip_address() # # - Validates IPv4 address with CIDR notation # - Checks each octet is 0-255 # - Checks CIDR is 1-32 # - Returns 0 if valid, 1 if invalid # ------------------------------------------------------------------------------ validate_ip_address() { local ip="$1" [[ -z "$ip" ]] && return 1 # Check format with CIDR if [[ ! "$ip" =~ ^([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})/([0-9]{1,2})$ ]]; then return 1 fi local o1="${BASH_REMATCH[1]}" local o2="${BASH_REMATCH[2]}" local o3="${BASH_REMATCH[3]}" local o4="${BASH_REMATCH[4]}" local cidr="${BASH_REMATCH[5]}" # Validate octets (0-255) for octet in "$o1" "$o2" "$o3" "$o4"; do if ((octet > 255)); then return 1 fi done # Validate CIDR (1-32) if ((cidr < 1 || cidr > 32)); then return 1 fi return 0 } # ------------------------------------------------------------------------------ # validate_gateway_ip() # # - Validates gateway IPv4 address (without CIDR) # - Checks each octet is 0-255 # - Returns 0 if valid, 1 if invalid # ------------------------------------------------------------------------------ validate_gateway_ip() { local ip="$1" [[ -z "$ip" ]] && return 0 # Check format without CIDR if [[ ! "$ip" =~ ^([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})$ ]]; then return 1 fi local o1="${BASH_REMATCH[1]}" local o2="${BASH_REMATCH[2]}" local o3="${BASH_REMATCH[3]}" local o4="${BASH_REMATCH[4]}" # Validate octets (0-255) for octet in "$o1" "$o2" "$o3" "$o4"; do if ((octet > 255)); then return 1 fi done return 0 } # ------------------------------------------------------------------------------ # validate_timezone() # # - Validates timezone string against system zoneinfo # - Empty value or "host" is allowed # - Returns 0 if valid, 1 if invalid # ------------------------------------------------------------------------------ validate_timezone() { local tz="$1" [[ -z "$tz" || "$tz" == "host" ]] && return 0 # Check if timezone file exists if [[ ! -f "/usr/share/zoneinfo/$tz" ]]; then return 1 fi return 0 } # ------------------------------------------------------------------------------ # validate_tags() # # - Validates Proxmox tags format # - Only alphanumeric, hyphens, underscores, and semicolons allowed # - Empty value is allowed # - Returns 0 if valid, 1 if invalid # ------------------------------------------------------------------------------ validate_tags() { local tags="$1" [[ -z "$tags" ]] && return 0 # Tags can only contain alphanumeric, -, _, and ; (separator) if [[ ! "$tags" =~ ^[a-zA-Z0-9_\;-]+$ ]]; then return 1 fi return 0 } # ------------------------------------------------------------------------------ # find_host_ssh_keys() # # - Scans system for available SSH keys # - Supports defaults (~/.ssh, /etc/ssh/authorized_keys) # - Returns list of files containing valid SSH public keys # - Sets FOUND_HOST_KEY_COUNT to number of keys found # ------------------------------------------------------------------------------ find_host_ssh_keys() { local re='(ssh-(rsa|ed25519)|ecdsa-sha2-nistp256|sk-(ssh-ed25519|ecdsa-sha2-nistp256))' local -a files=() cand=() local g="${var_ssh_import_glob:-}" local total=0 f base c shopt -s nullglob if [[ -n "$g" ]]; then for pat in $g; do cand+=($pat); done else cand+=(/root/.ssh/authorized_keys /root/.ssh/authorized_keys2) cand+=(/root/.ssh/*.pub) cand+=(/etc/ssh/authorized_keys /etc/ssh/authorized_keys.d/*) fi shopt -u nullglob for f in "${cand[@]}"; do [[ -f "$f" && -r "$f" ]] || continue base="$(basename -- "$f")" case "$base" in known_hosts | known_hosts.* | config) continue ;; id_*) [[ "$f" != *.pub ]] && continue ;; esac # CRLF safe check for host keys c=$(tr -d '\r' <"$f" | awk ' /^[[:space:]]*#/ {next} /^[[:space:]]*$/ {next} {print} ' | grep -E -c "$re" || true) if ((c > 0)); then files+=("$f") total=$((total + c)) fi done # Fallback to /root/.ssh/authorized_keys if ((${#files[@]} == 0)) && [[ -r /root/.ssh/authorized_keys ]]; then if grep -E -q "$re" /root/.ssh/authorized_keys; then files+=(/root/.ssh/authorized_keys) total=$((total + $(grep -E -c "$re" /root/.ssh/authorized_keys || echo 0))) fi fi FOUND_HOST_KEY_COUNT="$total" ( IFS=: echo "${files[*]}" ) } # ============================================================================== # SECTION 3B: IP RANGE SCANNING # ============================================================================== # ------------------------------------------------------------------------------ # ip_to_int() / int_to_ip() # # - Converts IP address to integer and vice versa for range iteration # ------------------------------------------------------------------------------ ip_to_int() { local IFS=. read -r i1 i2 i3 i4 <<<"$1" echo $(((i1 << 24) + (i2 << 16) + (i3 << 8) + i4)) } int_to_ip() { local ip=$1 echo "$(((ip >> 24) & 0xFF)).$(((ip >> 16) & 0xFF)).$(((ip >> 8) & 0xFF)).$((ip & 0xFF))" } # ------------------------------------------------------------------------------ # resolve_ip_from_range() # # - Takes an IP range in format "10.0.0.1/24-10.0.0.10/24" # - Pings each IP in the range to find the first available one # - Returns the first free IP with CIDR notation # - Sets NET_RESOLVED to the resolved IP or empty on failure # ------------------------------------------------------------------------------ resolve_ip_from_range() { local range="$1" local ip_cidr_regex='^([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})/([0-9]{1,2})$' local ip_start ip_end # Parse range: "10.0.0.1/24-10.0.0.10/24" ip_start="${range%%-*}" ip_end="${range##*-}" if [[ ! "$ip_start" =~ $ip_cidr_regex ]] || [[ ! "$ip_end" =~ $ip_cidr_regex ]]; then NET_RESOLVED="" return 1 fi local ip1="${ip_start%%/*}" local ip2="${ip_end%%/*}" local cidr="${ip_start##*/}" local start_int=$(ip_to_int "$ip1") local end_int=$(ip_to_int "$ip2") for ((ip_int = start_int; ip_int <= end_int; ip_int++)); do local ip=$(int_to_ip $ip_int) msg_info "Checking IP: $ip" if ! ping -c 1 -W 1 "$ip" >/dev/null 2>&1; then NET_RESOLVED="$ip/$cidr" msg_ok "Found free IP: ${BGN}$NET_RESOLVED${CL}" return 0 fi done NET_RESOLVED="" msg_error "No free IP found in range $range" return 1 } # ------------------------------------------------------------------------------ # is_ip_range() # # - Checks if a string is an IP range (contains - and looks like IP/CIDR) # - Returns 0 if it's a range, 1 otherwise # ------------------------------------------------------------------------------ is_ip_range() { local value="$1" local ip_start ip_end if [[ "$value" == *-* ]] && [[ "$value" != "dhcp" ]]; then local ip_cidr_regex='^([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})/([0-9]{1,2})$' ip_start="${value%%-*}" ip_end="${value##*-}" if [[ "$ip_start" =~ $ip_cidr_regex ]] && [[ "$ip_end" =~ $ip_cidr_regex ]]; then return 0 fi fi return 1 } # ============================================================================== # SECTION 4: STORAGE & RESOURCE MANAGEMENT # ============================================================================== # ------------------------------------------------------------------------------ # _write_storage_to_vars() # # - Writes storage selection to vars file # - Removes old entries (commented and uncommented) to avoid duplicates # - Arguments: vars_file, key (var_container_storage/var_template_storage), value # ------------------------------------------------------------------------------ _write_storage_to_vars() { # $1 = vars_file, $2 = key (var_container_storage / var_template_storage), $3 = value local vf="$1" key="$2" val="$3" # remove uncommented and commented versions to avoid duplicates sed -i "/^[#[:space:]]*${key}=/d" "$vf" echo "${key}=${val}" >>"$vf" } choose_and_set_storage_for_file() { # $1 = vars_file, $2 = class ('container'|'template') local vf="$1" class="$2" key="" current="" case "$class" in container) key="var_container_storage" ;; template) key="var_template_storage" ;; *) msg_error "Unknown storage class: $class" return 65 ;; esac current=$(awk -F= -v k="^${key}=" '$0 ~ k {print $2; exit}' "$vf") # If only one storage exists for the content type, auto-pick. Else always ask (your wish #4). local content="rootdir" [[ "$class" == "template" ]] && content="vztmpl" local count count=$(pvesm status -content "$content" | awk 'NR>1{print $1}' | wc -l) if [[ "$count" -eq 1 ]]; then STORAGE_RESULT=$(pvesm status -content "$content" | awk 'NR>1{print $1; exit}') STORAGE_INFO="" # Validate storage space for auto-picked container storage if [[ "$class" == "container" && -n "${DISK_SIZE:-}" ]]; then validate_storage_space "$STORAGE_RESULT" "$DISK_SIZE" "yes" # Continue even if validation fails - user was warned fi else # If the current value is preselectable, we could show it, but per your requirement we always offer selection select_storage "$class" || return 150 fi _write_storage_to_vars "$vf" "$key" "$STORAGE_RESULT" # Keep environment in sync for later steps (e.g. app-default save) if [[ "$class" == "container" ]]; then export var_container_storage="$STORAGE_RESULT" export CONTAINER_STORAGE="$STORAGE_RESULT" else export var_template_storage="$STORAGE_RESULT" export TEMPLATE_STORAGE="$STORAGE_RESULT" fi # Silent operation - no output message } # ============================================================================== # SECTION 5: CONFIGURATION & DEFAULTS MANAGEMENT # ============================================================================== # ------------------------------------------------------------------------------ # base_settings() # # - Defines all base/default variables for container creation # - Reads from environment variables (var_*) # - Provides fallback defaults for OS type/version # - App-specific values take precedence when they are HIGHER (for CPU, RAM, DISK) # - Sets up container type, resources, network, SSH, features, and tags # ------------------------------------------------------------------------------ base_settings() { # Default Settings CT_TYPE=${var_unprivileged:-"1"} # Resource allocation: App defaults take precedence if HIGHER # Compare app-declared values (saved in APP_DEFAULT_*) with current var_* values local final_disk="${var_disk:-4}" local final_cpu="${var_cpu:-1}" local final_ram="${var_ram:-1024}" # If app declared higher values, use those instead if [[ -n "${APP_DEFAULT_DISK:-}" && "${APP_DEFAULT_DISK}" =~ ^[0-9]+$ ]]; then if [[ "${APP_DEFAULT_DISK}" -gt "${final_disk}" ]]; then final_disk="${APP_DEFAULT_DISK}" fi fi if [[ -n "${APP_DEFAULT_CPU:-}" && "${APP_DEFAULT_CPU}" =~ ^[0-9]+$ ]]; then if [[ "${APP_DEFAULT_CPU}" -gt "${final_cpu}" ]]; then final_cpu="${APP_DEFAULT_CPU}" fi fi if [[ -n "${APP_DEFAULT_RAM:-}" && "${APP_DEFAULT_RAM}" =~ ^[0-9]+$ ]]; then if [[ "${APP_DEFAULT_RAM}" -gt "${final_ram}" ]]; then final_ram="${APP_DEFAULT_RAM}" fi fi DISK_SIZE="${final_disk}" CORE_COUNT="${final_cpu}" RAM_SIZE="${final_ram}" VERBOSE=${var_verbose:-"${1:-no}"} PW="" if [[ -n "${var_pw:-}" ]]; then local _pw_raw="${var_pw}" case "$_pw_raw" in --password\ *) _pw_raw="${_pw_raw#--password }" ;; -password\ *) _pw_raw="${_pw_raw#-password }" ;; esac while [[ "$_pw_raw" == -* ]]; do _pw_raw="${_pw_raw#-}" done if [[ -z "$_pw_raw" ]]; then msg_warn "Password was only dashes after cleanup; leaving empty." else PW="--password $_pw_raw" fi fi # Validate and set Container ID local requested_id="${var_ctid:-$NEXTID}" if ! validate_container_id "$requested_id"; then # Only show warning if user manually specified an ID (not auto-assigned) if [[ -n "${var_ctid:-}" ]]; then msg_warn "Container ID $requested_id is already in use. Using next available ID: $(get_valid_container_id "$requested_id")" fi requested_id=$(get_valid_container_id "$requested_id") fi CT_ID="$requested_id" # Validate and set Hostname/FQDN local requested_hostname="${var_hostname:-$NSAPP}" requested_hostname=$(echo "${requested_hostname,,}" | tr -d ' ') if ! validate_hostname "$requested_hostname"; then if [[ -n "${var_hostname:-}" ]]; then msg_warn "Invalid hostname '$requested_hostname'. Using default: $NSAPP" fi requested_hostname="$NSAPP" fi HN="$requested_hostname" BRG=${var_brg:-"vmbr0"} NET=${var_net:-"dhcp"} # Resolve IP range if NET contains a range (e.g., 192.168.1.100/24-192.168.1.200/24) if is_ip_range "$NET"; then msg_info "Scanning IP range: $NET" if resolve_ip_from_range "$NET"; then NET="$NET_RESOLVED" else msg_error "Could not find free IP in range. Falling back to DHCP." NET="dhcp" fi fi IPV6_METHOD=${var_ipv6_method:-"none"} GATE=${var_gateway:-""} APT_CACHER=${var_apt_cacher:-""} APT_CACHER_IP=${var_apt_cacher_ip:-""} # Runtime check: Verify APT cacher is reachable if configured if [[ -n "$APT_CACHER_IP" && "$APT_CACHER" == "yes" ]]; then local _check_host _check_port _check_url _check_host=$(echo "$APT_CACHER_IP" | sed -e 's|https\?://||' -e 's|/.*||' | cut -d: -f1) _check_port=$(echo "$APT_CACHER_IP" | sed -e 's|https\?://||' -e 's|/.*||' | cut -s -d: -f2) if [[ "$APT_CACHER_IP" =~ ^https?:// ]]; then _check_url="$APT_CACHER_IP" _check_port="${_check_port:-80}" else _check_port="${_check_port:-3142}" _check_url="http://${APT_CACHER_IP}:${_check_port}" fi if ! curl -s --connect-timeout 2 "${_check_url}" >/dev/null 2>&1; then msg_warn "APT Cacher configured but not reachable at ${_check_url}" msg_custom "⚠️" "${YW}" "Disabling APT Cacher for this installation" APT_CACHER="" APT_CACHER_IP="" else msg_ok "APT Cacher verified at ${_check_url}" fi fi MTU=${var_mtu:-""} _sd_val="${var_searchdomain:-""}" [[ -n "$_sd_val" ]] && SD="-searchdomain=$_sd_val" || SD="" _ns_val="${var_ns:-""}" [[ -n "$_ns_val" ]] && NS="-nameserver=$_ns_val" || NS="" MAC=${var_mac:-""} VLAN=${var_vlan:-""} SSH=${var_ssh:-"no"} SSH_AUTHORIZED_KEY=${var_ssh_authorized_key:-""} # Build TAGS: ensure community-script prefix, use semicolons (pct format), no duplicates if [[ "${var_tags:-}" == *community-script* ]]; then TAGS="${var_tags:-community-script}" else TAGS="community-script${var_tags:+;${var_tags}}" fi ENABLE_FUSE=${var_fuse:-"${1:-no}"} ENABLE_TUN=${var_tun:-"${1:-no}"} # Additional settings that may be skipped if advanced_settings is not run (e.g., App Defaults) ENABLE_GPU=${var_gpu:-"no"} ENABLE_NESTING=${var_nesting:-"1"} ENABLE_KEYCTL=${var_keyctl:-"0"} ENABLE_MKNOD=${var_mknod:-"0"} ALLOW_MOUNT_FS=${var_mount_fs:-""} PROTECT_CT=${var_protection:-"no"} CT_TIMEZONE=${var_timezone:-"$timezone"} [[ "${CT_TIMEZONE:-}" == Etc/* ]] && CT_TIMEZONE="host" # pct doesn't accept Etc/* zones # Since these 2 are only defined outside of default_settings function, we add a temporary fallback. TODO: To align everything, we should add these as constant variables (e.g. OSTYPE and OSVERSION), but that would currently require updating the default_settings function for all existing scripts if [ -z "$var_os" ]; then var_os="debian" fi if [ -z "$var_version" ]; then var_version="12" fi } # ------------------------------------------------------------------------------ # load_vars_file() # # - Safe parser for KEY=VALUE lines from vars files # - Used by default_var_settings and app defaults loading # - Only loads whitelisted var_* keys # - Optional force parameter to override existing values (for app defaults) # ------------------------------------------------------------------------------ load_vars_file() { local file="$1" local force="${2:-no}" # If "yes", override existing variables [ -f "$file" ] || return 0 msg_info "Loading defaults from ${file}" # Allowed var_* keys local VAR_WHITELIST=( var_apt_cacher var_apt_cacher_ip var_brg var_cpu var_disk var_fuse var_github_token var_gpu var_keyctl var_gateway var_hostname var_ipv6_method var_mac var_mknod var_mount_fs var_mtu var_net var_nesting var_ns var_os var_protection var_pw var_ram var_tags var_timezone var_tun var_unprivileged var_verbose var_version var_vlan var_ssh var_ssh_authorized_key var_container_storage var_template_storage var_searchdomain var_post_install ) # Whitelist check helper _is_whitelisted() { local k="$1" w for w in "${VAR_WHITELIST[@]}"; do [ "$k" = "$w" ] && return 0; done return 1 } local line key val while IFS= read -r line || [ -n "$line" ]; do line="${line#"${line%%[![:space:]]*}"}" line="${line%"${line##*[![:space:]]}"}" [[ -z "$line" || "$line" == \#* ]] && continue if [[ "$line" =~ ^([A-Za-z_][A-Za-z0-9_]*)=(.*)$ ]]; then local var_key="${BASH_REMATCH[1]}" local var_val="${BASH_REMATCH[2]}" [[ "$var_key" != var_* ]] && continue _is_whitelisted "$var_key" || continue # Strip inline comments (anything after unquoted #) # Only strip if not inside quotes if [[ ! "$var_val" =~ ^[\"\'] ]]; then var_val="${var_val%%#*}" fi # Strip quotes if [[ "$var_val" =~ ^\"(.*)\"$ ]]; then var_val="${BASH_REMATCH[1]}" elif [[ "$var_val" =~ ^\'(.*)\'$ ]]; then var_val="${BASH_REMATCH[1]}" fi # Trim trailing whitespace var_val="${var_val%"${var_val##*[![:space:]]}"}" # Validate values before setting (skip empty values - they use defaults) if [[ -n "$var_val" ]]; then case "$var_key" in var_mac) if ! validate_mac_address "$var_val"; then msg_warn "Invalid MAC address '$var_val' in $file, ignoring" continue fi ;; var_vlan) if ! validate_vlan_tag "$var_val"; then msg_warn "Invalid VLAN tag '$var_val' in $file (must be 1-4094), ignoring" continue fi ;; var_mtu) if ! validate_mtu "$var_val"; then msg_warn "Invalid MTU '$var_val' in $file (must be 576-65535), ignoring" continue fi ;; var_tags) if ! validate_tags "$var_val"; then msg_warn "Invalid tags '$var_val' in $file (alphanumeric, -, _, ; only), ignoring" continue fi ;; var_timezone) if ! validate_timezone "$var_val"; then msg_warn "Invalid timezone '$var_val' in $file, ignoring" continue fi ;; var_brg) if ! validate_bridge "$var_val"; then msg_warn "Bridge '$var_val' not found in $file, ignoring" continue fi ;; var_gateway) if ! validate_gateway_ip "$var_val"; then msg_warn "Invalid gateway IP '$var_val' in $file, ignoring" continue fi ;; var_hostname) if ! validate_hostname "$var_val"; then msg_warn "Invalid hostname '$var_val' in $file, ignoring" continue fi ;; var_cpu) if ! [[ "$var_val" =~ ^[0-9]+$ ]] || ((var_val < 1 || var_val > 128)); then msg_warn "Invalid CPU count '$var_val' in $file (must be 1-128), ignoring" continue fi ;; var_ram) if ! [[ "$var_val" =~ ^[0-9]+$ ]] || ((var_val < 256)); then msg_warn "Invalid RAM '$var_val' in $file (must be >= 256 MiB), ignoring" continue fi ;; var_disk) if ! [[ "$var_val" =~ ^[0-9]+$ ]] || ((var_val < 1)); then msg_warn "Invalid disk size '$var_val' in $file (must be >= 1 GB), ignoring" continue fi ;; var_unprivileged) if [[ "$var_val" != "0" && "$var_val" != "1" ]]; then msg_warn "Invalid unprivileged value '$var_val' in $file (must be 0 or 1), ignoring" continue fi ;; var_nesting) if [[ "$var_val" != "0" && "$var_val" != "1" ]]; then msg_warn "Invalid nesting value '$var_val' in $file (must be 0 or 1), ignoring" continue fi # Warn about potential issues with systemd-based OS when nesting is disabled via vars file if [[ "$var_val" == "0" && "${var_os:-debian}" != "alpine" ]]; then msg_warn "Nesting disabled in $file - modern systemd-based distributions may require nesting for proper operation" fi ;; var_keyctl) if [[ "$var_val" != "0" && "$var_val" != "1" ]]; then msg_warn "Invalid keyctl value '$var_val' in $file (must be 0 or 1), ignoring" continue fi ;; var_net) # var_net can be: dhcp, static IP/CIDR, or IP range if [[ "$var_val" != "dhcp" ]]; then if is_ip_range "$var_val"; then : # IP range is valid, will be resolved at runtime elif ! validate_ip_address "$var_val"; then msg_warn "Invalid network '$var_val' in $file (must be dhcp or IP/CIDR), ignoring" continue fi fi ;; var_fuse | var_tun | var_gpu | var_ssh | var_verbose | var_protection) if [[ "$var_val" != "yes" && "$var_val" != "no" ]]; then msg_warn "Invalid boolean '$var_val' for $var_key in $file (must be yes/no), ignoring" continue fi ;; var_mknod) if [[ "$var_val" != "0" && "$var_val" != "1" ]]; then msg_warn "Invalid mknod value '$var_val' in $file (must be 0 or 1), ignoring" continue fi ;; var_mount_fs) # Normalize: strip spaces, trailing commas var_val="${var_val// /}" var_val="${var_val%%,}" var_val="${var_val##,}" if [[ -n "$var_val" ]] && [[ ! "$var_val" =~ ^[a-zA-Z0-9]+(,[a-zA-Z0-9]+)*$ ]]; then msg_warn "Invalid mount_fs value '$var_val' in $file (comma-separated fs names only, e.g. nfs,cifs), ignoring" continue fi ;; var_ipv6_method) if [[ "$var_val" != "auto" && "$var_val" != "dhcp" && "$var_val" != "static" && "$var_val" != "none" ]]; then msg_warn "Invalid IPv6 method '$var_val' in $file (must be auto/dhcp/static/none), ignoring" continue fi ;; var_apt_cacher_ip) # Allow: plain IP/hostname, http://host, https://host:port if [[ -n "$var_val" ]] && ! [[ "$var_val" =~ ^(https?://)?[a-zA-Z0-9._-]+(:[0-9]+)?(/.*)?$ ]]; then msg_warn "Invalid APT Cacher address '$var_val' in $file, ignoring" continue fi ;; var_container_storage | var_template_storage) # Validate that the storage exists and is active on the current node local _storage_status _storage_status=$(pvesm status 2>/dev/null | awk -v s="$var_val" '$1 == s { print $3 }') if [[ -z "$_storage_status" ]]; then msg_warn "Storage '$var_val' from $file not found on this node, ignoring" continue elif [[ "$_storage_status" == "disabled" ]]; then msg_warn "Storage '$var_val' from $file is disabled on this node, ignoring" continue fi ;; esac fi # Set variable: force mode overrides existing, otherwise only set if empty if [[ "$force" == "yes" ]]; then export "${var_key}=${var_val}" else [[ -z "${!var_key+x}" ]] && export "${var_key}=${var_val}" fi fi done <"$file" msg_ok "Loaded ${file}" } # ------------------------------------------------------------------------------ # default_var_settings # # - Ensures /usr/local/community-scripts/default.vars exists (creates if missing) # - Loads var_* values from default.vars (safe parser, no source/eval) # - Precedence: ENV var_* > default.vars > built-in defaults # - Maps var_verbose → VERBOSE # - Calls base_settings "$VERBOSE" and echo_default # ------------------------------------------------------------------------------ default_var_settings() { # Allowed var_* keys (alphabetically sorted) # Note: Removed var_ctid (can only exist once), var_ipv6_static (static IPs are unique) local VAR_WHITELIST=( var_apt_cacher var_apt_cacher_ip var_brg var_cpu var_disk var_fuse var_github_token var_gpu var_keyctl var_gateway var_hostname var_ipv6_method var_mac var_mknod var_mount_fs var_mtu var_net var_nesting var_ns var_os var_protection var_pw var_ram var_tags var_timezone var_tun var_unprivileged var_verbose var_version var_vlan var_ssh var_ssh_authorized_key var_container_storage var_template_storage var_post_install ) # Snapshot: environment variables (highest precedence) declare -A _HARD_ENV=() local _k for _k in "${VAR_WHITELIST[@]}"; do if printenv "$_k" >/dev/null 2>&1; then _HARD_ENV["$_k"]=1; fi done # Find default.vars location local _find_default_vars _find_default_vars() { local f for f in \ /usr/local/community-scripts/default.vars \ "$HOME/.config/community-scripts/default.vars" \ "./default.vars"; do [ -f "$f" ] && { echo "$f" return 0 } done return 252 } # Allow override of storages via env (for non-interactive use cases) [ -n "${var_template_storage:-}" ] && TEMPLATE_STORAGE="$var_template_storage" [ -n "${var_container_storage:-}" ] && CONTAINER_STORAGE="$var_container_storage" # Create once, with storages already selected, no var_ctid/var_hostname lines local _ensure_default_vars _ensure_default_vars() { _find_default_vars >/dev/null 2>&1 && return 0 local canonical="/usr/local/community-scripts/default.vars" # Silent creation - no msg_info output mkdir -p /usr/local/community-scripts # Pick storages before writing the file (always ask unless only one) # Create a minimal temp file to write into : >"$canonical" # Base content (no var_ctid / var_hostname here) cat >"$canonical" <<'EOF' # Community-Scripts defaults (var_* only). Lines starting with # are comments. # Precedence: ENV var_* > default.vars > built-ins. # Keep keys alphabetically sorted. # Container type var_unprivileged=1 # Resources var_cpu=1 var_disk=4 var_ram=1024 # Network var_brg=vmbr0 var_net=dhcp var_ipv6_method=none # var_gateway= # var_vlan= # var_mtu= # var_mac= # var_ns= # SSH var_ssh=no # var_ssh_authorized_key= # APT cacher (optional - IP or URL) # var_apt_cacher=yes # var_apt_cacher_ip=192.168.1.10 # var_apt_cacher_ip=http://proxy.local # var_apt_cacher_ip=https://proxy.local:443 # Features/Tags/verbosity var_fuse=no var_tun=no # Advanced Settings (Proxmox-official features) var_nesting=1 # Allow nesting (required for Docker/LXC in CT) var_keyctl=0 # Allow keyctl() - needed for Docker (systemd-networkd workaround) var_mknod=0 # Allow device node creation (requires kernel 5.3+, experimental) var_mount_fs= # Allow specific filesystems: nfs,fuse,ext4,etc (leave empty for defaults) var_protection=no # Prevent accidental deletion of container var_timezone= # Container timezone (e.g. Europe/Berlin, leave empty for host timezone) var_tags=community-script var_verbose=no # Security (root PW) – empty => autologin # var_pw= # GitHub Personal Access Token (optional – avoids API rate limits during installs) # Create at https://github.com/settings/tokens – read-only public access is sufficient # var_github_token=ghp_your_token_here # Optional post-install script (host-side path to a *.sh on the Proxmox host) # Runs ON THE HOST after the container is fully provisioned. # Available env vars: APP, NSAPP, CTID, IP, HN, STORAGE, BRG # var_post_install=/opt/post-install/myhook.sh EOF # Now choose storages (always prompt unless just one exists) choose_and_set_storage_for_file "$canonical" template choose_and_set_storage_for_file "$canonical" container chmod 0644 "$canonical" # Silent creation - no output message } # Whitelist check local _is_whitelisted_key _is_whitelisted_key() { local k="$1" local w for w in "${VAR_WHITELIST[@]}"; do [ "$k" = "$w" ] && return 0; done return 1 } # 1) Ensure file exists _ensure_default_vars # 2) Load file local dv dv="$(_find_default_vars)" || { msg_error "default.vars not found after ensure step" return 252 } load_vars_file "$dv" # 3) Map var_verbose → VERBOSE if [[ -n "${var_verbose:-}" ]]; then case "${var_verbose,,}" in 1 | yes | true | on) VERBOSE="yes" ;; 0 | no | false | off) VERBOSE="no" ;; *) VERBOSE="${var_verbose}" ;; esac else VERBOSE="no" fi # 4) Map var_github_token → GITHUB_TOKEN (only if not already set in environment) if [[ -z "${GITHUB_TOKEN:-}" && -n "${var_github_token:-}" ]]; then export GITHUB_TOKEN="${var_github_token}" fi # 4) Apply base settings and show summary METHOD="mydefaults-global" base_settings "$VERBOSE" header_info echo -e "${DEFAULT}${BOLD}${BL}Using User Defaults (default.vars) on node $PVEHOST_NAME${CL}" echo_default } # ------------------------------------------------------------------------------ # get_app_defaults_path() # # - Returns full path for app-specific defaults file # - Example: /usr/local/community-scripts/defaults/.vars # ------------------------------------------------------------------------------ get_app_defaults_path() { local n="${NSAPP:-${APP,,}}" echo "/usr/local/community-scripts/defaults/${n}.vars" } # ------------------------------------------------------------------------------ # maybe_offer_save_app_defaults # # - Called after advanced_settings returned with fully chosen values. # - If no .vars exists, offers to persist current advanced settings # into /usr/local/community-scripts/defaults/.vars # - Only writes whitelisted var_* keys. # - Extracts raw values from flags like ",gw=..." ",mtu=..." etc. # ------------------------------------------------------------------------------ if ! declare -p VAR_WHITELIST >/dev/null 2>&1; then # Note: Removed var_ctid (can only exist once), var_ipv6_static (static IPs are unique) declare -ag VAR_WHITELIST=( var_apt_cacher var_apt_cacher_ip var_brg var_cpu var_disk var_fuse var_github_token var_gpu var_keyctl var_gateway var_hostname var_ipv6_method var_mac var_mknod var_mount_fs var_mtu var_net var_nesting var_ns var_os var_protection var_pw var_ram var_tags var_timezone var_tun var_unprivileged var_verbose var_version var_vlan var_ssh var_ssh_authorized_key var_container_storage var_template_storage var_searchdomain var_post_install ) fi # Global whitelist check function (used by _load_vars_file_to_map and others) _is_whitelisted_key() { local k="$1" local w for w in "${VAR_WHITELIST[@]}"; do [ "$k" = "$w" ] && return 0; done return 1 } _sanitize_value() { # Disallow Command-Substitution / Shell-Meta case "$1" in *'$('* | *'`'* | *';'* | *'&'* | *'<('*) echo "" return 0 ;; esac echo "$1" } # Map-Parser: read var_* from file into _VARS_IN associative array # Note: Main _load_vars_file() with full validation is defined in default_var_settings section # This simplified version is used specifically for diff operations via _VARS_IN array declare -A _VARS_IN _load_vars_file_to_map() { local file="$1" [ -f "$file" ] || return 0 _VARS_IN=() # Clear array local line key val while IFS= read -r line || [ -n "$line" ]; do line="${line#"${line%%[![:space:]]*}"}" line="${line%"${line##*[![:space:]]}"}" [ -z "$line" ] && continue case "$line" in \#*) continue ;; esac key=$(printf "%s" "$line" | cut -d= -f1) val=$(printf "%s" "$line" | cut -d= -f2-) case "$key" in var_*) if _is_whitelisted_key "$key"; then _VARS_IN["$key"]="$val" fi ;; esac done <"$file" } # Diff function for two var_* files -> produces human-readable diff list for $1 (old) vs $2 (new) _build_vars_diff() { local oldf="$1" newf="$2" local k local -A OLD=() NEW=() _load_vars_file_to_map "$oldf" for k in "${!_VARS_IN[@]}"; do OLD["$k"]="${_VARS_IN[$k]}"; done _load_vars_file_to_map "$newf" for k in "${!_VARS_IN[@]}"; do NEW["$k"]="${_VARS_IN[$k]}"; done local out out+="# Diff for ${APP} (${NSAPP})\n" out+="# Old: ${oldf}\n# New: ${newf}\n\n" local found_change=0 # Changed & Removed for k in "${!OLD[@]}"; do if [[ -v NEW["$k"] ]]; then if [[ "${OLD[$k]}" != "${NEW[$k]}" ]]; then out+="~ ${k}\n - old: ${OLD[$k]}\n + new: ${NEW[$k]}\n" found_change=1 fi else out+="- ${k}\n - old: ${OLD[$k]}\n" found_change=1 fi done # Added for k in "${!NEW[@]}"; do if [[ ! -v OLD["$k"] ]]; then out+="+ ${k}\n + new: ${NEW[$k]}\n" found_change=1 fi done if [[ $found_change -eq 0 ]]; then out+="(No differences)\n" fi printf "%b" "$out" } # Build a temporary .vars file from current advanced settings _build_current_app_vars_tmp() { tmpf="$(mktemp /tmp/${NSAPP:-app}.vars.new.XXXXXX)" # NET/GW _net="${NET:-}" _gate="" case "${GATE:-}" in ,gw=*) _gate=$(echo "$GATE" | sed 's/^,gw=//') ;; esac # IPv6 _ipv6_method="${IPV6_METHOD:-auto}" _ipv6_static="" _ipv6_gateway="" if [ "$_ipv6_method" = "static" ]; then _ipv6_static="${IPV6_ADDR:-}" _ipv6_gateway="${IPV6_GATE:-}" fi # MTU/VLAN/MAC _mtu="" _vlan="" _mac="" case "${MTU:-}" in ,mtu=*) _mtu=$(echo "$MTU" | sed 's/^,mtu=//') ;; esac case "${VLAN:-}" in ,tag=*) _vlan=$(echo "$VLAN" | sed 's/^,tag=//') ;; esac case "${MAC:-}" in ,hwaddr=*) _mac=$(echo "$MAC" | sed 's/^,hwaddr=//') ;; esac # DNS / Searchdomain _ns="" _searchdomain="" case "${NS:-}" in -nameserver=*) _ns=$(echo "$NS" | sed 's/^-nameserver=//') ;; esac case "${SD:-}" in -searchdomain=*) _searchdomain=$(echo "$SD" | sed 's/^-searchdomain=//') ;; esac # SSH / APT / Features _ssh="${SSH:-no}" _ssh_auth="${SSH_AUTHORIZED_KEY:-}" _apt_cacher="${APT_CACHER:-}" _apt_cacher_ip="${APT_CACHER_IP:-}" _fuse="${ENABLE_FUSE:-no}" _tun="${ENABLE_TUN:-no}" _gpu="${ENABLE_GPU:-no}" _nesting="${ENABLE_NESTING:-1}" _keyctl="${ENABLE_KEYCTL:-0}" _mknod="${ENABLE_MKNOD:-0}" _mount_fs="${ALLOW_MOUNT_FS:-}" _protect="${PROTECT_CT:-no}" _timezone="${CT_TIMEZONE:-}" _tags="${TAGS:-}" _verbose="${VERBOSE:-no}" # Type / Resources / Identity _unpriv="${CT_TYPE:-1}" _cpu="${CORE_COUNT:-1}" _ram="${RAM_SIZE:-1024}" _disk="${DISK_SIZE:-4}" _hostname="${HN:-$NSAPP}" # Storage _tpl_storage="${TEMPLATE_STORAGE:-${var_template_storage:-}}" _ct_storage="${CONTAINER_STORAGE:-${var_container_storage:-}}" { echo "# App-specific defaults for ${APP} (${NSAPP})" echo "# Generated on $(date -u '+%Y-%m-%dT%H:%M:%SZ')" echo echo "var_os=$(_sanitize_value "${var_os:-}")" echo "var_version=$(_sanitize_value "${var_version:-}")" echo "var_unprivileged=$(_sanitize_value "$_unpriv")" echo "var_cpu=$(_sanitize_value "$_cpu")" echo "var_ram=$(_sanitize_value "$_ram")" echo "var_disk=$(_sanitize_value "$_disk")" [ -n "${BRG:-}" ] && echo "var_brg=$(_sanitize_value "$BRG")" [ -n "$_net" ] && echo "var_net=$(_sanitize_value "$_net")" [ -n "$_gate" ] && echo "var_gateway=$(_sanitize_value "$_gate")" [ -n "$_mtu" ] && echo "var_mtu=$(_sanitize_value "$_mtu")" [ -n "$_vlan" ] && echo "var_vlan=$(_sanitize_value "$_vlan")" [ -n "$_mac" ] && echo "var_mac=$(_sanitize_value "$_mac")" [ -n "$_ns" ] && echo "var_ns=$(_sanitize_value "$_ns")" [ -n "$_ipv6_method" ] && echo "var_ipv6_method=$(_sanitize_value "$_ipv6_method")" # var_ipv6_static removed - static IPs are unique, can't be default [ -n "$_ssh" ] && echo "var_ssh=$(_sanitize_value "$_ssh")" [ -n "$_ssh_auth" ] && echo "var_ssh_authorized_key=$(_sanitize_value "$_ssh_auth")" [ -n "$_apt_cacher" ] && echo "var_apt_cacher=$(_sanitize_value "$_apt_cacher")" [ -n "$_apt_cacher_ip" ] && echo "var_apt_cacher_ip=$(_sanitize_value "$_apt_cacher_ip")" [ -n "$_fuse" ] && echo "var_fuse=$(_sanitize_value "$_fuse")" [ -n "$_tun" ] && echo "var_tun=$(_sanitize_value "$_tun")" [ -n "$_gpu" ] && echo "var_gpu=$(_sanitize_value "$_gpu")" [ -n "$_nesting" ] && echo "var_nesting=$(_sanitize_value "$_nesting")" [ -n "$_keyctl" ] && echo "var_keyctl=$(_sanitize_value "$_keyctl")" [ -n "$_mknod" ] && echo "var_mknod=$(_sanitize_value "$_mknod")" [ -n "$_mount_fs" ] && echo "var_mount_fs=$(_sanitize_value "$_mount_fs")" [ -n "$_protect" ] && echo "var_protection=$(_sanitize_value "$_protect")" [ -n "$_timezone" ] && echo "var_timezone=$(_sanitize_value "$_timezone")" [ -n "$_tags" ] && echo "var_tags=$(_sanitize_value "$_tags")" [ -n "$_verbose" ] && echo "var_verbose=$(_sanitize_value "$_verbose")" [ -n "$_hostname" ] && echo "var_hostname=$(_sanitize_value "$_hostname")" [ -n "$_searchdomain" ] && echo "var_searchdomain=$(_sanitize_value "$_searchdomain")" [ -n "$_tpl_storage" ] && echo "var_template_storage=$(_sanitize_value "$_tpl_storage")" [ -n "$_ct_storage" ] && echo "var_container_storage=$(_sanitize_value "$_ct_storage")" [ -n "${var_post_install:-}" ] && echo "var_post_install=$(_sanitize_value "${var_post_install}")" } >"$tmpf" echo "$tmpf" } # ------------------------------------------------------------------------------ # maybe_offer_save_app_defaults() # # - Called after advanced_settings() # - Offers to save current values as app defaults if not existing # - If file exists: shows diff and allows Update, Keep, View Diff, or Cancel # ------------------------------------------------------------------------------ maybe_offer_save_app_defaults() { local app_vars_path app_vars_path="$(get_app_defaults_path)" # always build from current settings local new_tmp diff_tmp new_tmp="$(_build_current_app_vars_tmp)" diff_tmp="$(mktemp -p /tmp "${NSAPP:-app}.vars.diff.XXXXXX")" # 1) if no file → offer to create if [[ ! -f "$app_vars_path" ]]; then if whiptail --backtitle "Proxmox VE Helper Scripts" \ --yesno "Save these advanced settings as defaults for ${APP}?\n\nThis will create:\n${app_vars_path}" 12 72; then mkdir -p "$(dirname "$app_vars_path")" /usr/bin/install -m 0644 "$new_tmp" "$app_vars_path" msg_ok "Saved app defaults: ${app_vars_path}" fi rm -f "$new_tmp" "$diff_tmp" return 0 fi # 2) if file exists → build diff _build_vars_diff "$app_vars_path" "$new_tmp" >"$diff_tmp" # if no differences → do nothing if grep -q "^(No differences)$" "$diff_tmp"; then rm -f "$new_tmp" "$diff_tmp" return 0 fi # 3) if file exists → show menu with default selection "Update Defaults" local app_vars_file app_vars_file="$(basename "$app_vars_path")" while true; do local sel sel="$(whiptail --backtitle "Proxmox VE Helper Scripts" \ --title "APP DEFAULTS – ${APP}" \ --menu "Differences detected. What do you want to do?" 20 78 10 \ "Update Defaults" "Write new values to ${app_vars_file}" \ "Keep Current" "Keep existing defaults (no changes)" \ "View Diff" "Show a detailed diff" \ "Cancel" "Abort without changes" \ --default-item "Update Defaults" \ 3>&1 1>&2 2>&3)" || { sel="Cancel"; } case "$sel" in "Update Defaults") /usr/bin/install -m 0644 "$new_tmp" "$app_vars_path" msg_ok "Updated app defaults: ${app_vars_path}" break ;; "Keep Current") msg_custom "ℹ️" "${BL}" "Keeping current app defaults: ${app_vars_path}" break ;; "View Diff") whiptail --backtitle "Proxmox VE Helper Scripts" \ --title "Diff – ${APP}" \ --scrolltext --textbox "$diff_tmp" 25 100 ;; "Cancel" | *) msg_custom "🚫" "${YW}" "Canceled. No changes to app defaults." break ;; esac done rm -f "$new_tmp" "$diff_tmp" } ensure_storage_selection_for_vars_file() { local vf="$1" # Read stored values (if any) local tpl ct tpl=$(grep -E '^var_template_storage=' "$vf" | cut -d= -f2- || true) ct=$(grep -E '^var_container_storage=' "$vf" | cut -d= -f2- || true) if [[ -n "$tpl" && -n "$ct" ]]; then TEMPLATE_STORAGE="$tpl" CONTAINER_STORAGE="$ct" # Validate storage space for loaded container storage if [[ -n "${DISK_SIZE:-}" ]]; then validate_storage_space "$ct" "$DISK_SIZE" "yes" # Continue even if validation fails - user was warned fi return 0 fi choose_and_set_storage_for_file "$vf" template choose_and_set_storage_for_file "$vf" container # Silent operation - no output message } ensure_global_default_vars_file() { local vars_path="/usr/local/community-scripts/default.vars" if [[ ! -f "$vars_path" ]]; then mkdir -p "$(dirname "$vars_path")" touch "$vars_path" fi echo "$vars_path" } # ============================================================================== # SECTION 6: ADVANCED INTERACTIVE CONFIGURATION # ============================================================================== # ------------------------------------------------------------------------------ # advanced_settings() # # - Interactive wizard-style configuration with BACK navigation # - State-machine approach: each step can go forward or backward # - Cancel at Step 1 = Exit Script, Cancel at other steps = Go Back # - Allows user to customize all container settings # ------------------------------------------------------------------------------ advanced_settings() { # Enter alternate screen buffer to prevent flicker between dialogs tput smcup 2>/dev/null || true trap 'tput rmcup 2>/dev/null || true' RETURN # Initialize defaults # Build TAGS: ensure community-script prefix, use semicolons (pct format), no duplicates if [[ "${var_tags:-}" == *community-script* ]]; then TAGS="${var_tags:-community-script}" else TAGS="community-script${var_tags:+;${var_tags}}" fi local STEP=1 local MAX_STEP=29 # Store values for back navigation - inherit from var_* app defaults local _ct_type="${var_unprivileged:-1}" local _pw="" local _pw_display="Automatic Login" local _ct_id="$NEXTID" local _hostname="$NSAPP" local _disk_size="${var_disk:-4}" local _core_count="${var_cpu:-1}" local _ram_size="${var_ram:-1024}" local _bridge="${var_brg:-vmbr0}" local _net="${var_net:-dhcp}" local _gate="${var_gateway:-}" local _ipv6_method="${var_ipv6_method:-auto}" local _ipv6_addr="" local _ipv6_gate="" local _apt_cacher="${var_apt_cacher:-no}" local _apt_cacher_ip="${var_apt_cacher_ip:-}" local _mtu="${var_mtu:-}" local _sd="${var_searchdomain:-}" local _ns="${var_ns:-}" local _mac="${var_mac:-}" local _vlan="${var_vlan:-}" local _tags="$TAGS" local _enable_fuse="${var_fuse:-no}" local _enable_tun="${var_tun:-no}" local _enable_gpu="${var_gpu:-no}" local _enable_nesting="${var_nesting:-1}" local _verbose="${var_verbose:-no}" local _enable_keyctl="${var_keyctl:-0}" local _enable_mknod="${var_mknod:-0}" local _mount_fs="${var_mount_fs:-}" local _protect_ct="${var_protection:-no}" local _post_install="${var_post_install:-}" # Detect host timezone for default (if not set via var_timezone) local _host_timezone="" if command -v timedatectl >/dev/null 2>&1; then _host_timezone=$(timedatectl show --value --property=Timezone 2>/dev/null || echo "") elif [ -f /etc/timezone ]; then _host_timezone=$(cat /etc/timezone 2>/dev/null || echo "") fi # Map Etc/* timezones to "host" (pct doesn't accept Etc/* zones) [[ "${_host_timezone:-}" == Etc/* ]] && _host_timezone="host" local _ct_timezone="${var_timezone:-$_host_timezone}" [[ "${_ct_timezone:-}" == Etc/* ]] && _ct_timezone="host" # Helper to show current progress show_progress() { local current=$1 local total=$MAX_STEP echo -e "\n${INFO}${BOLD}${DGN}Step $current of $total${CL}" } # Detect available bridges (do this once) local BRIDGES="" local BRIDGE_MENU_OPTIONS=() _detect_bridges() { IFACE_FILEPATH_LIST="/etc/network/interfaces"$'\n'$(find "/etc/network/interfaces.d/" -type f 2>/dev/null) BRIDGES="" local OLD_IFS=$IFS IFS=$'\n' for iface_filepath in ${IFACE_FILEPATH_LIST}; do local iface_indexes_tmpfile=$(mktemp -q -u '.iface-XXXX') (grep -Pn '^\s*iface' "${iface_filepath}" 2>/dev/null | cut -d':' -f1 && wc -l "${iface_filepath}" 2>/dev/null | cut -d' ' -f1) | awk 'FNR==1 {line=$0; next} {print line":"$0-1; line=$0}' >"${iface_indexes_tmpfile}" 2>/dev/null || true if [ -f "${iface_indexes_tmpfile}" ]; then while read -r pair; do local start=$(echo "${pair}" | cut -d':' -f1) local end=$(echo "${pair}" | cut -d':' -f2) if awk "NR >= ${start} && NR <= ${end}" "${iface_filepath}" 2>/dev/null | grep -qP '^\s*(bridge[-_](ports|stp|fd|vlan-aware|vids)|ovs_type\s+OVSBridge)\b'; then local iface_name=$(sed "${start}q;d" "${iface_filepath}" | awk '{print $2}') BRIDGES="${iface_name}"$'\n'"${BRIDGES}" fi done <"${iface_indexes_tmpfile}" rm -f "${iface_indexes_tmpfile}" fi done IFS=$OLD_IFS BRIDGES=$(echo "$BRIDGES" | grep -v '^\s*$' | sort | uniq) # Build bridge menu BRIDGE_MENU_OPTIONS=() if [[ -n "$BRIDGES" ]]; then while IFS= read -r bridge; do if [[ -n "$bridge" ]]; then local description=$(grep -A 10 "iface $bridge" /etc/network/interfaces 2>/dev/null | grep '^#' | head -n1 | sed 's/^#\s*//;s/^[- ]*//' || true) BRIDGE_MENU_OPTIONS+=("$bridge" "${description:- }") fi done <<<"$BRIDGES" fi } _detect_bridges # Main wizard loop while [ $STEP -le $MAX_STEP ]; do case $STEP in # ═══════════════════════════════════════════════════════════════════════════ # STEP 1: Container Type # ═══════════════════════════════════════════════════════════════════════════ 1) local default_on="ON" local default_off="OFF" [[ "$_ct_type" == "0" ]] && { default_on="OFF" default_off="ON" } if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ --title "CONTAINER TYPE" \ --ok-button "Next" --cancel-button "Exit" \ --radiolist "\nChoose container type:\n\nUse SPACE to select, ENTER to confirm." 14 58 2 \ "1" "Unprivileged (recommended)" $default_on \ "0" "Privileged" $default_off \ 3>&1 1>&2 2>&3); then [[ -n "$result" ]] && _ct_type="$result" ((STEP++)) else exit_script fi ;; # ═══════════════════════════════════════════════════════════════════════════ # STEP 2: Root Password # ════════════════════════════════════════���═══════════════════════════════���══ 2) if PW1=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ --title "ROOT PASSWORD" \ --ok-button "Next" --cancel-button "Back" \ --passwordbox "\nSet Root Password (needed for root ssh access)\n\nLeave blank for automatic login (no password)" 12 58 \ 3>&1 1>&2 2>&3); then if [[ -z "$PW1" ]]; then _pw="" _pw_display="Automatic Login" ((STEP++)) elif [[ "$PW1" == *" "* ]]; then whiptail --msgbox "Password cannot contain spaces." 8 58 else local _pw1_clean="$PW1" while [[ "$_pw1_clean" == -* ]]; do _pw1_clean="${_pw1_clean#-}" done if [[ -z "$_pw1_clean" ]]; then whiptail --msgbox "Password cannot be only '-' characters." 8 58 continue elif ((${#_pw1_clean} < 5)); then whiptail --msgbox "Password must be at least 5 characters (after removing leading '-')." 8 70 continue fi # Verify password if PW2=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ --title "PASSWORD VERIFICATION" \ --ok-button "Confirm" --cancel-button "Back" \ --passwordbox "\nVerify Root Password" 10 58 \ 3>&1 1>&2 2>&3); then local _pw2_clean="$PW2" while [[ "$_pw2_clean" == -* ]]; do _pw2_clean="${_pw2_clean#-}" done if [[ "$_pw1_clean" == "$_pw2_clean" ]]; then _pw="--password $_pw1_clean" _pw_display="********" ((STEP++)) else whiptail --msgbox "Passwords do not match. Please try again." 8 58 fi else ((STEP--)) fi fi else ((STEP--)) fi ;; # ═══════════════════════════════════════════════════════════════════════════ # STEP 3: Container ID # ═══════════════════════════════════════════════════════════════════════════ 3) if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ --title "CONTAINER ID" \ --ok-button "Next" --cancel-button "Back" \ --inputbox "\nSet Container ID" 10 58 "$_ct_id" \ 3>&1 1>&2 2>&3); then local input_id="${result:-$NEXTID}" # Validate that ID is numeric if ! [[ "$input_id" =~ ^[0-9]+$ ]]; then whiptail --backtitle "Proxmox VE Helper Scripts" --title "Invalid ID" --msgbox "Container ID must be numeric." 8 58 continue fi # Check if ID is already in use if ! validate_container_id "$input_id"; then if whiptail --backtitle "Proxmox VE Helper Scripts" --title "ID Already In Use" \ --yesno "Container/VM ID $input_id is already in use.\n\nWould you like to use the next available ID ($(get_valid_container_id "$input_id"))?" 10 58; then _ct_id=$(get_valid_container_id "$input_id") else continue fi else _ct_id="$input_id" fi ((STEP++)) else ((STEP--)) fi ;; # ═══════════════════════════════════════════════════════════════════════════ # STEP 4: Hostname # ═══════════════════════════════════════════════════════════════════════════ 4) if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ --title "HOSTNAME" \ --ok-button "Next" --cancel-button "Back" \ --inputbox "\nSet Hostname (or FQDN, e.g. host.example.com)" 10 58 "$_hostname" \ 3>&1 1>&2 2>&3); then local hn_test="${result:-$NSAPP}" hn_test=$(echo "${hn_test,,}" | tr -d ' ') if validate_hostname "$hn_test"; then _hostname="$hn_test" ((STEP++)) else whiptail --msgbox "Invalid hostname: '$hn_test'\n\nRules:\n- Only lowercase letters, digits, dots and hyphens\n- Labels separated by dots (max 63 chars each)\n- No leading/trailing hyphens or dots\n- No consecutive dots\n- Total max 253 characters" 14 60 fi else ((STEP--)) fi ;; # ═══════════════════════════════════════════════════════════════════════════ # STEP 5: Disk Size # ═══════════════════════════════════════════════════════════════════════════ 5) if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ --title "DISK SIZE" \ --ok-button "Next" --cancel-button "Back" \ --inputbox "\nSet Disk Size in GB" 10 58 "$_disk_size" \ 3>&1 1>&2 2>&3); then local disk_test="${result:-$var_disk}" if [[ "$disk_test" =~ ^[1-9][0-9]*$ ]]; then _disk_size="$disk_test" ((STEP++)) else whiptail --msgbox "Disk size must be a positive integer!" 8 58 fi else ((STEP--)) fi ;; # ═══════════════════════════════════════════════════════════════════════════ # STEP 6: CPU Cores # ═══════════════════════════════════════════════════════════════════════════ 6) if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ --title "CPU CORES" \ --ok-button "Next" --cancel-button "Back" \ --inputbox "\nAllocate CPU Cores" 10 58 "$_core_count" \ 3>&1 1>&2 2>&3); then local cpu_test="${result:-$var_cpu}" if [[ "$cpu_test" =~ ^[1-9][0-9]*$ ]]; then _core_count="$cpu_test" ((STEP++)) else whiptail --msgbox "CPU core count must be a positive integer!" 8 58 fi else ((STEP--)) fi ;; # ═══════════════════════════════════════════════════════════════════════════ # STEP 7: RAM Size # ═══════════════════════════════════════════════════════════════════════════ 7) if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ --title "RAM SIZE" \ --ok-button "Next" --cancel-button "Back" \ --inputbox "\nAllocate RAM in MiB" 10 58 "$_ram_size" \ 3>&1 1>&2 2>&3); then local ram_test="${result:-$var_ram}" if [[ "$ram_test" =~ ^[1-9][0-9]*$ ]]; then _ram_size="$ram_test" ((STEP++)) else whiptail --msgbox "RAM size must be a positive integer!" 8 58 fi else ((STEP--)) fi ;; # ═══════════════════════════════════════════════════════════════════════════ # STEP 8: Network Bridge # ═══════════════════════════════════════════════════════════════════════════ 8) if [[ ${#BRIDGE_MENU_OPTIONS[@]} -eq 0 ]]; then # Validate default bridge exists if validate_bridge "vmbr0"; then _bridge="vmbr0" ((STEP++)) else whiptail --msgbox "Default bridge 'vmbr0' not found!\n\nPlease configure a network bridge in Proxmox first." 10 58 msg_error "Default bridge 'vmbr0' not found" exit 116 fi else if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ --title "NETWORK BRIDGE" \ --ok-button "Next" --cancel-button "Back" \ --menu "\nSelect network bridge:" 16 58 6 \ "${BRIDGE_MENU_OPTIONS[@]}" \ 3>&1 1>&2 2>&3); then local bridge_test="${result:-vmbr0}" # Skip separator entries (e.g., __other__) - re-display menu if [[ "$bridge_test" == "__other__" || "$bridge_test" == -* ]]; then continue fi if validate_bridge "$bridge_test"; then _bridge="$bridge_test" ((STEP++)) else whiptail --msgbox "Bridge '$bridge_test' is not available or not active." 8 58 fi else ((STEP--)) fi fi ;; # ═══════════════════════════════════════════════════════════════════════════ # STEP 9: IPv4 Configuration # ═══════════════════════════════════════════════════════════════════════════ 9) if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ --title "IPv4 CONFIGURATION" \ --ok-button "Next" --cancel-button "Back" \ --menu "\nSelect IPv4 Address Assignment:" 16 65 3 \ "dhcp" "Automatic (DHCP, recommended)" \ "static" "Static (manual entry)" \ "range" "IP Range Scan (find first free IP)" \ 3>&1 1>&2 2>&3); then if [[ "$result" == "static" ]]; then # Get static IP local static_ip if static_ip=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ --title "STATIC IPv4 ADDRESS" \ --ok-button "Next" --cancel-button "Back" \ --inputbox "\nEnter Static IPv4 CIDR Address\n(e.g. 192.168.1.100/24)" 12 58 "" \ 3>&1 1>&2 2>&3); then if validate_ip_address "$static_ip"; then # Get gateway local gateway_ip if gateway_ip=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ --title "GATEWAY IP" \ --ok-button "Next" --cancel-button "Back" \ --inputbox "\nEnter Gateway IP address" 10 58 "" \ 3>&1 1>&2 2>&3); then if validate_gateway_ip "$gateway_ip"; then # Validate gateway is in same subnet if validate_gateway_in_subnet "$static_ip" "$gateway_ip"; then _net="$static_ip" _gate=",gw=$gateway_ip" ((STEP++)) else whiptail --msgbox "Gateway is not in the same subnet as the static IP.\n\nStatic IP: $static_ip\nGateway: $gateway_ip" 10 58 fi else whiptail --msgbox "Invalid Gateway IP format.\n\nEach octet must be 0-255.\nExample: 192.168.1.1" 10 58 fi fi else whiptail --msgbox "Invalid IPv4 CIDR format.\n\nEach octet must be 0-255.\nCIDR must be 1-32.\nExample: 192.168.1.100/24" 12 58 fi fi elif [[ "$result" == "range" ]]; then # IP Range Scan local ip_range if ip_range=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ --title "IP RANGE SCAN" \ --ok-button "Scan" --cancel-button "Back" \ --inputbox "\nEnter IP range to scan for free address\n(e.g. 192.168.1.100/24-192.168.1.200/24)" 12 65 "" \ 3>&1 1>&2 2>&3); then if is_ip_range "$ip_range"; then # Exit whiptail screen temporarily to show scan progress clear header_info echo -e "${INFO}${BOLD}${DGN}Scanning IP range for free address...${CL}\n" if resolve_ip_from_range "$ip_range"; then # Get gateway local gateway_ip if gateway_ip=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ --title "GATEWAY IP" \ --ok-button "Next" --cancel-button "Back" \ --inputbox "\nFound free IP: $NET_RESOLVED\n\nEnter Gateway IP address" 12 58 "" \ 3>&1 1>&2 2>&3); then if validate_gateway_ip "$gateway_ip"; then # Validate gateway is in same subnet if validate_gateway_in_subnet "$NET_RESOLVED" "$gateway_ip"; then _net="$NET_RESOLVED" _gate=",gw=$gateway_ip" ((STEP++)) else whiptail --msgbox "Gateway is not in the same subnet as the IP.\n\nIP: $NET_RESOLVED\nGateway: $gateway_ip" 10 58 fi else whiptail --msgbox "Invalid Gateway IP format.\n\nEach octet must be 0-255.\nExample: 192.168.1.1" 10 58 fi fi else whiptail --msgbox "No free IP found in the specified range.\nAll IPs responded to ping." 10 58 fi else whiptail --msgbox "Invalid IP range format.\n\nExample: 192.168.1.100/24-192.168.1.200/24" 10 58 fi fi else _net="dhcp" _gate="" ((STEP++)) fi else ((STEP--)) fi ;; # ═══════════════════════════════════════════════════════════════════════════ # STEP 10: IPv6 Configuration # ═══════════════════════════════════════════════════════════════════════════ 10) if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ --title "IPv6 CONFIGURATION" \ --ok-button "Next" --cancel-button "Back" \ --menu "\nSelect IPv6 Address Management:" 16 70 5 \ "auto" "SLAAC/AUTO (recommended) - Dynamic IPv6 from network" \ "dhcp" "DHCPv6 - DHCP-assigned IPv6 address" \ "static" "Static - Manual IPv6 address configuration" \ "none" "None - No IPv6 assignment (most containers)" \ "disable" "Fully Disabled - (breaks some services)" \ 3>&1 1>&2 2>&3); then _ipv6_method="$result" case "$result" in static) local ipv6_addr if ipv6_addr=$(whiptail --backtitle "Proxmox VE Helper Scripts" \ --title "STATIC IPv6 ADDRESS" \ --inputbox "\nEnter IPv6 CIDR address\n(e.g. 2001:db8::1/64)" 12 58 "" \ 3>&1 1>&2 2>&3); then if validate_ipv6_address "$ipv6_addr"; then _ipv6_addr="$ipv6_addr" # Optional gateway - loop until valid or empty local ipv6_gw_valid=false while [[ "$ipv6_gw_valid" == "false" ]]; do local ipv6_gw ipv6_gw=$(whiptail --backtitle "Proxmox VE Helper Scripts" \ --title "IPv6 GATEWAY" \ --inputbox "\nEnter IPv6 gateway (optional, leave blank for none)" 10 58 "" \ 3>&1 1>&2 2>&3) || true # Validate gateway if provided if [[ -n "$ipv6_gw" ]]; then if validate_ipv6_address "$ipv6_gw"; then _ipv6_gate="$ipv6_gw" ipv6_gw_valid=true ((STEP++)) else whiptail --msgbox "Invalid IPv6 gateway format.\n\nExample: 2001:db8::1" 8 58 fi else _ipv6_gate="" ipv6_gw_valid=true ((STEP++)) fi done else whiptail --msgbox "Invalid IPv6 CIDR format.\n\nExample: 2001:db8::1/64\nCIDR must be 1-128." 10 58 fi fi ;; dhcp) _ipv6_addr="dhcp" _ipv6_gate="" ((STEP++)) ;; none) _ipv6_addr="none" _ipv6_gate="" ((STEP++)) ;; *) _ipv6_addr="" _ipv6_gate="" ((STEP++)) ;; esac else ((STEP--)) fi ;; # ═══════════════════════════════════════════════════════════════════════════ # STEP 11: MTU Size # ═══════════════════════════════════════════════════════════════════════════ 11) if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ --title "MTU SIZE" \ --ok-button "Next" --cancel-button "Back" \ --inputbox "\nSet Interface MTU Size\n(leave blank for default 1500, common values: 1500, 9000)" 12 62 "" \ 3>&1 1>&2 2>&3); then if validate_mtu "$result"; then _mtu="$result" ((STEP++)) else whiptail --msgbox "Invalid MTU size.\n\nMTU must be between 576 and 65535.\nCommon values: 1500 (default), 9000 (jumbo frames)" 10 58 fi else ((STEP--)) fi ;; # ═══════════════════════════════════════════════════════════════════════════ # STEP 12: DNS Search Domain # ═══════════════════════════════════════════════════════════════════════════ 12) if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ --title "DNS SEARCH DOMAIN" \ --ok-button "Next" --cancel-button "Back" \ --inputbox "\nSet DNS Search Domain\n(leave blank to use host setting)" 12 58 "" \ 3>&1 1>&2 2>&3); then _sd="$result" ((STEP++)) else ((STEP--)) fi ;; # ═══════════════════════════════════════════════════════════════════════════ # STEP 13: DNS Server # ═══════════════════════════════════════════════════════════════════════════ 13) if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ --title "DNS SERVER" \ --ok-button "Next" --cancel-button "Back" \ --inputbox "\nSet DNS Server IP\n(leave blank to use host setting)" 12 58 "" \ 3>&1 1>&2 2>&3); then _ns="$result" ((STEP++)) else ((STEP--)) fi ;; # ═══════════════════════════════════════════════════════════════════════════ # STEP 14: MAC Address # ═══════════════════════════════════════════════════════════════════════════ 14) if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ --title "MAC ADDRESS" \ --ok-button "Next" --cancel-button "Back" \ --inputbox "\nSet MAC Address\n(leave blank for auto-generated, format: XX:XX:XX:XX:XX:XX)" 12 62 "" \ 3>&1 1>&2 2>&3); then if validate_mac_address "$result"; then _mac="$result" ((STEP++)) else whiptail --msgbox "Invalid MAC address format.\n\nRequired format: XX:XX:XX:XX:XX:XX\nExample: 02:00:00:00:00:01" 10 58 fi else ((STEP--)) fi ;; # ═══════════════════════════════════════════════════════════════════════════ # STEP 15: VLAN Tag # ═══════════════════════════════════════════════════════════════════════════ 15) if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ --title "VLAN TAG" \ --ok-button "Next" --cancel-button "Back" \ --inputbox "\nSet VLAN Tag (1-4094)\n(leave blank for no VLAN)" 12 58 "" \ 3>&1 1>&2 2>&3); then if validate_vlan_tag "$result"; then _vlan="$result" ((STEP++)) else whiptail --msgbox "Invalid VLAN tag.\n\nVLAN must be a number between 1 and 4094." 8 58 fi else ((STEP--)) fi ;; # ═══════════════════════════════════════════════════════════════════════════ # STEP 16: Tags # ═══════════════════════════════════════════════════════════════════════════ 16) if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ --title "CONTAINER TAGS" \ --ok-button "Next" --cancel-button "Back" \ --inputbox "\nSet Custom Tags (semicolon-separated)\n(alphanumeric, hyphens, underscores only)" 12 58 "$_tags" \ 3>&1 1>&2 2>&3); then local tags_test="${result:-}" tags_test=$(echo "$tags_test" | tr -d '[:space:]') if validate_tags "$tags_test"; then _tags="$tags_test" ((STEP++)) else whiptail --msgbox "Invalid tag format.\n\nTags can only contain:\n- Letters (a-z, A-Z)\n- Numbers (0-9)\n- Hyphens (-)\n- Underscores (_)\n- Semicolons (;) as separator" 14 58 fi else ((STEP--)) fi ;; # ═══════════════════════════════════════════════════════════════════════════ # STEP 17: SSH Settings # ═══════════════════════════════════════════════════════════════════════════ 17) configure_ssh_settings "Step $STEP/$MAX_STEP" # configure_ssh_settings handles its own flow, always advance ((STEP++)) ;; # ═══════════════════════════════════════════════════════════════════════════ # STEP 18: FUSE Support # ═══════════════════════════════════════════════════════════════════════════ 18) local fuse_default_flag="--defaultno" [[ "$_enable_fuse" == "yes" || "$_enable_fuse" == "1" ]] && fuse_default_flag="" if whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ --title "FUSE SUPPORT" \ --ok-button "Next" --cancel-button "Back" \ $fuse_default_flag \ --yesno "\nEnable FUSE support?\n\nRequired for: rclone, mergerfs, AppImage, etc.\n\n(App default: ${var_fuse:-no})" 14 58; then _enable_fuse="yes" else if [ $? -eq 1 ]; then _enable_fuse="no" else ((STEP--)) continue fi fi ((STEP++)) ;; # ═══════════════════════════════════════════════════════════════════════════ # STEP 19: TUN/TAP Support # ═══════════════════════════════════════════════════════════════════════════ 19) local tun_default_flag="--defaultno" [[ "$_enable_tun" == "yes" || "$_enable_tun" == "1" ]] && tun_default_flag="" if whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ --title "TUN/TAP SUPPORT" \ --ok-button "Next" --cancel-button "Back" \ $tun_default_flag \ --yesno "\nEnable TUN/TAP device support?\n\nRequired for: VPN apps (WireGuard, OpenVPN, Tailscale),\nnetwork tunneling, and containerized networking.\n\n(App default: ${var_tun:-no})" 14 62; then _enable_tun="yes" else if [ $? -eq 1 ]; then _enable_tun="no" else ((STEP--)) continue fi fi ((STEP++)) ;; # ═══════════════════════════════════════════════════════════════════════════ # STEP 20: Nesting Support # ═══════════════════════════════════════════════════════════════════════════ 20) local nesting_default_flag="" [[ "$_enable_nesting" == "0" || "$_enable_nesting" == "no" ]] && nesting_default_flag="--defaultno" if whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ --title "NESTING SUPPORT" \ --ok-button "Next" --cancel-button "Back" \ $nesting_default_flag \ --yesno "\nEnable Nesting?\n\nRequired for: Docker, LXC inside LXC, Podman,\nand other containerization tools.\n\n(App default: ${var_nesting:-1})" 14 58; then _enable_nesting="1" else if [ $? -eq 1 ]; then _enable_nesting="0" # Warn about potential issues with systemd-based OS when nesting is disabled if [[ "$var_os" != "alpine" ]]; then whiptail --backtitle "Proxmox VE Helper Scripts" \ --title "⚠️ NESTING WARNING" \ --msgbox "Modern systemd-based distributions (Debian 13+, Ubuntu 24.04+, etc.) may require nesting to be enabled for proper operation.\n\nWithout nesting, the container may start in a degraded state with failing services (error 243/CREDENTIALS).\n\nIf you experience issues, enable nesting in the container options." 14 68 fi else ((STEP--)) continue fi fi ((STEP++)) ;; # ═══════════════════════════════════════════════════════════════════════════ # STEP 21: GPU Passthrough # ═══════════════════════════════════════════════════════════════════════════ 21) local gpu_default_flag="--defaultno" [[ "$_enable_gpu" == "yes" ]] && gpu_default_flag="" if whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ --title "GPU PASSTHROUGH" \ --ok-button "Next" --cancel-button "Back" \ $gpu_default_flag \ --yesno "\nEnable GPU Passthrough?\n\nAutomatically detects and passes through available GPUs\n(Intel/AMD/NVIDIA) for hardware acceleration.\n\nRecommended for: Media servers, AI/ML, Transcoding\n\n(App default: ${var_gpu:-no})" 16 62; then _enable_gpu="yes" else if [ $? -eq 1 ]; then _enable_gpu="no" else ((STEP--)) continue fi fi ((STEP++)) ;; # ═══════════════════════════════════════════════════════════════════════════ # STEP 22: Keyctl Support (Docker/systemd) # ═══════════════════════════════════════════════════════════════════════════ 22) # Keyctl is always required for unprivileged containers — skip dialog if [[ "$_ct_type" == "1" ]]; then _enable_keyctl="1" ((STEP++)) continue fi local keyctl_default_flag="--defaultno" [[ "$_enable_keyctl" == "1" ]] && keyctl_default_flag="" if whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ --title "KEYCTL SUPPORT" \ --ok-button "Next" --cancel-button "Back" \ $keyctl_default_flag \ --yesno "\nEnable Keyctl support?\n\nRequired for: Docker containers, systemd-networkd,\nand kernel keyring operations.\n\n(App default: ${var_keyctl:-0})" 14 62; then _enable_keyctl="1" else if [ $? -eq 1 ]; then _enable_keyctl="0" else ((STEP--)) continue fi fi ((STEP++)) ;; # ═══════════════════════════════════════════════════════════════════════════ # STEP 23: APT Cacher Proxy # ═══════════════════════════════════════════════════════════════════════════ 23) local apt_cacher_default_flag="--defaultno" [[ "$_apt_cacher" == "yes" ]] && apt_cacher_default_flag="" if whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ --title "APT CACHER PROXY" \ --ok-button "Next" --cancel-button "Back" \ $apt_cacher_default_flag \ --yesno "\nUse APT Cacher-NG proxy?\n\nSpeeds up package downloads by caching them locally.\nRequires apt-cacher-ng running on your network.\n\n(App default: ${var_apt_cacher:-no})" 14 62; then _apt_cacher="yes" # Ask for IP if enabled if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ --title "APT CACHER IP" \ --inputbox "\nEnter APT Cacher-NG IP or URL:\n(e.g. 192.168.1.10, http://host, https://host:443)" 12 62 "$_apt_cacher_ip" \ 3>&1 1>&2 2>&3); then _apt_cacher_ip="$result" fi else if [ $? -eq 1 ]; then _apt_cacher="no" _apt_cacher_ip="" else ((STEP--)) continue fi fi ((STEP++)) ;; # ═══════════════════════════════════════════════════════════════════════════ # STEP 24: Container Timezone # ═══════════════════════════════════════════════════════════════════════════ 24) local tz_hint="$_ct_timezone" [[ -z "$tz_hint" ]] && tz_hint="(empty - will use host timezone)" if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ --title "CONTAINER TIMEZONE" \ --ok-button "Next" --cancel-button "Back" \ --inputbox "\nSet container timezone.\n\nExamples: Europe/Berlin, America/New_York, Asia/Tokyo\n\nHost timezone: ${_host_timezone:-unknown}\n\nLeave empty to inherit from host." 16 62 "$_ct_timezone" \ 3>&1 1>&2 2>&3); then local tz_test="$result" [[ "${tz_test:-}" == Etc/* ]] && tz_test="host" # pct doesn't accept Etc/* zones if validate_timezone "$tz_test"; then _ct_timezone="$tz_test" ((STEP++)) else whiptail --msgbox "Invalid timezone: '$result'\n\nTimezone must exist in /usr/share/zoneinfo/\n\nExamples:\n- Europe/Berlin\n- America/New_York\n- Asia/Tokyo\n- UTC" 14 58 fi else ((STEP--)) fi ;; # ═══════════════════════════════════════════════════════════════════════════ # STEP 25: Container Protection # ═══════════════════════════════════════════════════════════════════════════ 25) local protect_default_flag="--defaultno" [[ "$_protect_ct" == "yes" || "$_protect_ct" == "1" ]] && protect_default_flag="" if whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ --title "CONTAINER PROTECTION" \ --ok-button "Next" --cancel-button "Back" \ $protect_default_flag \ --yesno "\nEnable Container Protection?\n\nPrevents accidental deletion of this container.\nYou must disable protection before removing.\n\n(App default: ${var_protection:-no})" 14 62; then _protect_ct="yes" else if [ $? -eq 1 ]; then _protect_ct="no" else ((STEP--)) continue fi fi ((STEP++)) ;; # ═══════════════════════════════════════════════════════════════════════════ # STEP 26: Device Node Creation (mknod) # ═══════════════════════════════════════════════════════════════════════════ 26) local mknod_default_flag="--defaultno" [[ "$_enable_mknod" == "1" ]] && mknod_default_flag="" if whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ --title "DEVICE NODE CREATION" \ --ok-button "Next" --cancel-button "Back" \ $mknod_default_flag \ --yesno "\nAllow device node creation (mknod)?\n\nRequired for: Creating device files inside container.\nExperimental feature (requires kernel 5.3+).\n\n(App default: ${var_mknod:-0})" 14 62; then _enable_mknod="1" else if [ $? -eq 1 ]; then _enable_mknod="0" else ((STEP--)) continue fi fi ((STEP++)) ;; # ═══════════════════════════════════════════════════════════════════════════ # STEP 27: Mount Filesystems # ═══════════════════════════════════════════════════════════════════════════ 27) local mount_hint="" [[ -n "$_mount_fs" ]] && mount_hint="$_mount_fs" || mount_hint="(none)" if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ --title "MOUNT FILESYSTEMS" \ --ok-button "Next" --cancel-button "Back" \ --inputbox "\nAllow specific filesystem mounts.\n\nComma-separated list: nfs, cifs, fuse, ext4, etc.\nLeave empty for defaults (none).\n\nCurrent: $mount_hint" 14 62 "$_mount_fs" \ 3>&1 1>&2 2>&3); then # Normalize: strip spaces and trailing/leading commas result="${result// /}" result="${result%%,}" result="${result##,}" _mount_fs="$result" ((STEP++)) else ((STEP--)) fi ;; # ═══════════════════════════════════════════════════════════════════════════ # STEP 28: Optional host-side post-install hook (path on the Proxmox HOST) # ═══════════════════════════════════════════════════════════════════════════ 28) local _hook_prompt="Optional: absolute path to a *.sh file ON THE PROXMOX HOST. It runs as root on the HOST (NOT in the LXC) after the container is fully provisioned and started. Available env vars: APP, NSAPP, CTID, IP, HN, STORAGE, BRG. Leave empty to skip." while true; do if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ --title "POST-INSTALL HOOK (HOST)" \ --ok-button "Next" --cancel-button "Back" \ --inputbox "$_hook_prompt" 16 70 "${_post_install}" \ 3>&1 1>&2 2>&3); then # Normalize: strip surrounding whitespace result="$(printf '%s' "$result" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')" if [[ -z "$result" ]]; then _post_install="" ((STEP++)) break fi # Reject obvious shell-meta sneaking through if [[ "$result" == *';'* || "$result" == *'$('* || "$result" == *'`'* || "$result" == *'&&'* || "$result" == *'||'* ]]; then whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID PATH" \ --msgbox "Path contains shell metacharacters. Please provide a plain absolute file path." 10 70 continue fi if [[ "$result" != /* ]]; then whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID PATH" \ --msgbox "Path must be absolute (start with /).\n\nGot: $result" 10 70 continue fi if [[ ! -f "$result" ]]; then if ! whiptail --backtitle "Proxmox VE Helper Scripts" --title "FILE NOT FOUND" \ --yesno "File does not exist on host:\n\n$result\n\nKeep this path anyway?" 12 70; then continue fi fi _post_install="$result" ((STEP++)) break else ((STEP--)) break fi done ;; # ═══════════════════════════════════════════════════════════════════════════ # STEP 29: Verbose Mode & Confirmation # ═══════════════════════════════════════════════════════════════════════════ 29) local verbose_default_flag="--defaultno" [[ "$_verbose" == "yes" ]] && verbose_default_flag="" if whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ --title "VERBOSE MODE" \ $verbose_default_flag \ --yesno "\nEnable Verbose Mode?\n\nShows detailed output during installation." 12 58; then _verbose="yes" else _verbose="no" fi # Build summary local ct_type_desc="Unprivileged" [[ "$_ct_type" == "0" ]] && ct_type_desc="Privileged" local nesting_desc="Disabled" [[ "$_enable_nesting" == "1" ]] && nesting_desc="Enabled" local keyctl_desc="Disabled" [[ "$_enable_keyctl" == "1" ]] && keyctl_desc="Enabled" local protect_desc="No" [[ "$_protect_ct" == "yes" || "$_protect_ct" == "1" ]] && protect_desc="Yes" local tz_display="${_ct_timezone:-Host TZ}" local apt_display="${_apt_cacher:-no}" [[ "$_apt_cacher" == "yes" && -n "$_apt_cacher_ip" ]] && apt_display="$_apt_cacher_ip" local post_install_display="${_post_install:-(none)}" local post_install_warn="" [[ -n "$_post_install" ]] && post_install_warn=" ⚠ Hook runs as root on Proxmox HOST (not in LXC)" local summary="Container Type: $ct_type_desc Container ID: $_ct_id Hostname: $_hostname Resources: Disk: ${_disk_size} GB CPU: $_core_count cores RAM: $_ram_size MiB Network: Bridge: $_bridge IPv4: $_net IPv6: $_ipv6_method Features: FUSE: $_enable_fuse | TUN: $_enable_tun Nesting: $nesting_desc | Keyctl: $keyctl_desc Mknod: $([ "$_enable_mknod" == "1" ] && echo Enabled || echo Disabled) | Mount FS: ${_mount_fs:-(none)} GPU: $_enable_gpu | Protection: $protect_desc Advanced: Timezone: $tz_display APT Cacher: $apt_display Verbose: $_verbose Post-Install Script: ${post_install_display}${post_install_warn}" if whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ --title "CONFIRM SETTINGS" \ --ok-button "Create LXC" --cancel-button "Back" \ --yesno "$summary\n\nCreate ${APP} LXC with these settings?" 32 62; then ((STEP++)) else ((STEP--)) fi ;; esac done # ═══════════════════════════════════════════════════════════════════════════ # Apply all collected values to global variables # ═══════════════════════════════════════════════════════════════════════════ CT_TYPE="$_ct_type" PW="$_pw" CT_ID="$_ct_id" HN="$_hostname" DISK_SIZE="$_disk_size" CORE_COUNT="$_core_count" RAM_SIZE="$_ram_size" BRG="$_bridge" NET="$_net" GATE="$_gate" IPV6_METHOD="$_ipv6_method" IPV6_ADDR="$_ipv6_addr" IPV6_GATE="$_ipv6_gate" TAGS="$_tags" ENABLE_FUSE="$_enable_fuse" ENABLE_TUN="$_enable_tun" ENABLE_GPU="$_enable_gpu" ENABLE_NESTING="$_enable_nesting" ENABLE_KEYCTL="$_enable_keyctl" ENABLE_MKNOD="$_enable_mknod" ALLOW_MOUNT_FS="$_mount_fs" PROTECT_CT="$_protect_ct" CT_TIMEZONE="$_ct_timezone" APT_CACHER="$_apt_cacher" APT_CACHER_IP="$_apt_cacher_ip" VERBOSE="$_verbose" var_post_install="$_post_install" # Update var_* based on user choice (for functions that check these) var_gpu="$_enable_gpu" var_fuse="$_enable_fuse" var_tun="$_enable_tun" var_nesting="$_enable_nesting" var_keyctl="$_enable_keyctl" var_mknod="$_enable_mknod" var_mount_fs="$_mount_fs" var_protection="$_protect_ct" var_timezone="$_ct_timezone" var_apt_cacher="$_apt_cacher" var_apt_cacher_ip="$_apt_cacher_ip" # Format optional values [[ -n "$_mtu" ]] && MTU=",mtu=$_mtu" || MTU="" [[ -n "$_sd" ]] && SD="-searchdomain=$_sd" || SD="" [[ -n "$_ns" ]] && NS="-nameserver=$_ns" || NS="" [[ -n "$_mac" ]] && MAC=",hwaddr=$_mac" || MAC="" [[ -n "$_vlan" ]] && VLAN=",tag=$_vlan" || VLAN="" export SSH_KEYS_FILE # Exit alternate screen buffer before showing summary (so output remains visible) tput rmcup 2>/dev/null || true trap - RETURN # Display final summary echo -e "\n${INFO}${BOLD}${DGN}PVE Version ${PVEVERSION} (Kernel: ${KERNEL_VERSION})${CL}" echo -e "${OS}${BOLD}${DGN}Operating System: ${BGN}$var_os${CL}" echo -e "${OSVERSION}${BOLD}${DGN}Version: ${BGN}$var_version${CL}" echo -e "${CONTAINERTYPE}${BOLD}${DGN}Container Type: ${BGN}$([ "$CT_TYPE" == "1" ] && echo "Unprivileged" || echo "Privileged")${CL}" echo -e "${CONTAINERID}${BOLD}${DGN}Container ID: ${BGN}$CT_ID${CL}" echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}" echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}${DISK_SIZE} GB${CL}" echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}$CORE_COUNT${CL}" echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}${RAM_SIZE} MiB${CL}" echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}" echo -e "${NETWORK}${BOLD}${DGN}IPv4: ${BGN}$NET${CL}" echo -e "${NETWORK}${BOLD}${DGN}IPv6: ${BGN}$IPV6_METHOD${CL}" echo -e "${FUSE}${BOLD}${DGN}FUSE Support: ${BGN}${ENABLE_FUSE:-no}${CL}" [[ "${ENABLE_TUN:-no}" == "yes" ]] && echo -e "${NETWORK}${BOLD}${DGN}TUN/TAP Support: ${BGN}$ENABLE_TUN${CL}" echo -e "${CONTAINERTYPE}${BOLD}${DGN}Nesting: ${BGN}$([ "${ENABLE_NESTING:-1}" == "1" ] && echo "Enabled" || echo "Disabled")${CL}" [[ "${ENABLE_KEYCTL:-0}" == "1" ]] && echo -e "${CONTAINERTYPE}${BOLD}${DGN}Keyctl: ${BGN}Enabled${CL}" echo -e "${GPU}${BOLD}${DGN}GPU Passthrough: ${BGN}${ENABLE_GPU:-no}${CL}" [[ "${ENABLE_MKNOD:-0}" == "1" ]] && echo -e "${CONTAINERTYPE}${BOLD}${DGN}Mknod: ${BGN}Enabled${CL}" [[ -n "${ALLOW_MOUNT_FS:-}" ]] && echo -e "${CONTAINERTYPE}${BOLD}${DGN}Mount FS: ${BGN}${ALLOW_MOUNT_FS}${CL}" [[ "${PROTECT_CT:-no}" == "yes" || "${PROTECT_CT:-no}" == "1" ]] && echo -e "${CONTAINERTYPE}${BOLD}${DGN}Protection: ${BGN}Enabled${CL}" [[ -n "${CT_TIMEZONE:-}" ]] && echo -e "${INFO}${BOLD}${DGN}Timezone: ${BGN}$CT_TIMEZONE${CL}" [[ "$APT_CACHER" == "yes" ]] && echo -e "${INFO}${BOLD}${DGN}APT Cacher: ${BGN}$APT_CACHER_IP${CL}" echo -e "${SEARCH}${BOLD}${DGN}Verbose Mode: ${BGN}$VERBOSE${CL}" echo -e "${CREATING}${BOLD}${RD}Creating an LXC of ${APP} using the above advanced settings${CL}" # Log settings to file log_section "CONTAINER SETTINGS (ADVANCED) - ${APP}" log_msg "Application: ${APP}" log_msg "PVE Version: ${PVEVERSION} (Kernel: ${KERNEL_VERSION})" log_msg "Operating System: $var_os ($var_version)" log_msg "Container Type: $([ "$CT_TYPE" == "1" ] && echo "Unprivileged" || echo "Privileged")" log_msg "Container ID: $CT_ID" log_msg "Hostname: $HN" log_msg "Disk Size: ${DISK_SIZE} GB" log_msg "CPU Cores: $CORE_COUNT" log_msg "RAM Size: ${RAM_SIZE} MiB" log_msg "Bridge: $BRG" log_msg "IPv4: $NET" log_msg "IPv6: $IPV6_METHOD" log_msg "FUSE Support: ${ENABLE_FUSE:-no}" log_msg "Nesting: $([ "${ENABLE_NESTING:-1}" == "1" ] && echo "Enabled" || echo "Disabled")" log_msg "Mknod: $([ "${ENABLE_MKNOD:-0}" == "1" ] && echo "Enabled" || echo "Disabled")" [[ -n "${ALLOW_MOUNT_FS:-}" ]] && log_msg "Mount FS: ${ALLOW_MOUNT_FS}" log_msg "GPU Passthrough: ${ENABLE_GPU:-no}" log_msg "Verbose Mode: $VERBOSE" log_msg "Session ID: ${SESSION_ID}" } # ============================================================================== # SECTION 7: USER INTERFACE & DIAGNOSTICS # ============================================================================== # ------------------------------------------------------------------------------ # diagnostics_check() # # - Ensures diagnostics config file exists at /usr/local/community-scripts/diagnostics # - Asks user whether to send anonymous diagnostic data (first run only) # - Saves DIAGNOSTICS=yes/no in the config file # - Reads current diagnostics setting from existing file # - Sets global DIAGNOSTICS variable for API telemetry opt-in/out # ------------------------------------------------------------------------------ diagnostics_check() { local config_dir="/usr/local/community-scripts" local config_file="${config_dir}/diagnostics" mkdir -p "$config_dir" if [[ -f "$config_file" ]]; then DIAGNOSTICS=$(awk -F '=' '/^DIAGNOSTICS/ {print $2}' "$config_file") || true DIAGNOSTICS="${DIAGNOSTICS:-no}" return fi local result result=$(whiptail --backtitle "Proxmox VE Helper Scripts" \ --title "TELEMETRY & DIAGNOSTICS" \ --ok-button "Confirm" --cancel-button "Exit" \ --radiolist "\nHelp improve Community-Scripts by sharing anonymous data.\n\nWhat we collect:\n - Container resources (CPU, RAM, disk), OS & PVE version\n - Application name, install method and status\n\nWhat we DON'T collect:\n - No IP addresses, hostnames, or personal data\n\nYou can change this anytime in the Settings menu.\nPrivacy: https://github.com/community-scripts/telemetry-service/blob/main/docs/PRIVACY.md\n\nUse SPACE to select, ENTER to confirm." 22 76 2 \ "yes" "Yes, share anonymous data" OFF \ "no" "No, opt out" OFF \ 3>&1 1>&2 2>&3) || result="no" DIAGNOSTICS="${result:-no}" cat <"$config_file" DIAGNOSTICS=${DIAGNOSTICS} # Community-Scripts Telemetry Configuration # https://telemetry.community-scripts.org # # This file stores your telemetry preference. # Set DIAGNOSTICS=yes to share anonymous installation data. # Set DIAGNOSTICS=no to disable telemetry. # # You can also change this via the Settings menu during installation. # # Data collected (when enabled): # disk_size, core_count, ram_size, os_type, os_version, # nsapp, method, pve_version, status, exit_code # # No personal data (IPs, hostnames, passwords) is ever collected. # Privacy: https://github.com/community-scripts/telemetry-service/blob/main/docs/PRIVACY.md EOF } diagnostics_menu() { local current="${DIAGNOSTICS:-no}" local status_text="DISABLED" [[ "$current" == "yes" ]] && status_text="ENABLED" local dialog_text=( "Telemetry is currently: ${status_text}\n\n" "Anonymous data helps us improve scripts and track issues.\n" "No personal data is ever collected.\n\n" "More info: https://telemetry.community-scripts.org\n\n" "Do you want to ${current:+change this setting}?" ) if [[ "$current" == "yes" ]]; then if whiptail --backtitle "Proxmox VE Helper Scripts" \ --title "TELEMETRY SETTINGS" \ --yesno "${dialog_text[*]}" 14 64 \ --yes-button "Disable" --no-button "Keep enabled"; then DIAGNOSTICS="no" sed -i 's/^DIAGNOSTICS=.*/DIAGNOSTICS=no/' /usr/local/community-scripts/diagnostics whiptail --msgbox "Telemetry disabled.\n\nNote: Existing containers keep their current setting.\nNew containers will inherit this choice." 10 58 fi else if whiptail --backtitle "Proxmox VE Helper Scripts" \ --title "TELEMETRY SETTINGS" \ --yesno "${dialog_text[*]}" 14 64 \ --yes-button "Enable" --no-button "Keep disabled"; then DIAGNOSTICS="yes" sed -i 's/^DIAGNOSTICS=.*/DIAGNOSTICS=yes/' /usr/local/community-scripts/diagnostics whiptail --msgbox "Telemetry enabled.\n\nNote: Existing containers keep their current setting.\nNew containers will inherit this choice." 10 58 fi fi } # ------------------------------------------------------------------------------ # echo_default() # # - Prints summary of default values (ID, OS, type, disk, RAM, CPU, etc.) # - Uses icons and formatting for readability # - Convert CT_TYPE to description # - Also logs settings to log file for debugging # ------------------------------------------------------------------------------ echo_default() { CT_TYPE_DESC="Unprivileged" if [ "$CT_TYPE" -eq 0 ]; then CT_TYPE_DESC="Privileged" fi echo -e "${INFO}${BOLD}${DGN}PVE Version ${PVEVERSION} (Kernel: ${KERNEL_VERSION})${CL}" echo -e "${CONTAINERID}${BOLD}${DGN}Container ID: ${BGN}${CT_ID}${CL}" echo -e "${OS}${BOLD}${DGN}Operating System: ${BGN}$var_os ($var_version)${CL}" echo -e "${CONTAINERTYPE}${BOLD}${DGN}Container Type: ${BGN}$CT_TYPE_DESC${CL}" echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}${DISK_SIZE} GB${CL}" echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}${CORE_COUNT}${CL}" echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}${RAM_SIZE} MiB${CL}" if [[ -n "${var_gpu:-}" && "${var_gpu}" == "yes" ]]; then echo -e "${GPU}${BOLD}${DGN}GPU Passthrough: ${BGN}Enabled${CL}" fi if [ "$VERBOSE" == "yes" ]; then echo -e "${SEARCH}${BOLD}${DGN}Verbose Mode: ${BGN}Enabled${CL}" fi echo -e "${CREATING}${BOLD}${BL}Creating a ${APP} LXC using the above default settings${CL}" echo -e " " # Log settings to file log_section "CONTAINER SETTINGS - ${APP}" log_msg "Application: ${APP}" log_msg "PVE Version: ${PVEVERSION} (Kernel: ${KERNEL_VERSION})" log_msg "Container ID: ${CT_ID}" log_msg "Operating System: $var_os ($var_version)" log_msg "Container Type: $CT_TYPE_DESC" log_msg "Disk Size: ${DISK_SIZE} GB" log_msg "CPU Cores: ${CORE_COUNT}" log_msg "RAM Size: ${RAM_SIZE} MiB" [[ -n "${var_gpu:-}" && "${var_gpu}" == "yes" ]] && log_msg "GPU Passthrough: Enabled" [[ "$VERBOSE" == "yes" ]] && log_msg "Verbose Mode: Enabled" log_msg "Session ID: ${SESSION_ID}" } # ------------------------------------------------------------------------------ # install_script() # # - Main entrypoint for installation mode # - Runs safety checks (pve_check, root_check, maxkeys_check, diagnostics_check) # - Builds interactive menu (Default, Verbose, Advanced, My Defaults, App Defaults, Diagnostics, Storage, Exit) # - Applies chosen settings and triggers container build # ------------------------------------------------------------------------------ install_script() { pve_check shell_check root_check arch_check ssh_check maxkeys_check diagnostics_check if systemctl is-active -q ping-instances.service; then systemctl -q stop ping-instances.service fi NEXTID=$(pvesh get /cluster/nextid) # Get timezone using timedatectl (Debian 13+ compatible) # Fallback to /etc/timezone for older systems if command -v timedatectl >/dev/null 2>&1; then timezone=$(timedatectl show --value --property=Timezone 2>/dev/null || echo "UTC") elif [ -f /etc/timezone ]; then timezone=$(cat /etc/timezone) else timezone="UTC" fi [[ "${timezone:-}" == Etc/* ]] && timezone="host" # pct doesn't accept Etc/* zones # Show APP Header header_info # --- Support CLI argument as direct preset (default, advanced, …) --- CHOICE="${mode:-${1:-}}" # If no CLI argument → show whiptail menu # Build menu dynamically based on available options local appdefaults_option="" local settings_option="" local menu_items=( "1" "Default Install" "2" "Advanced Install" "3" "User Defaults" ) if [ -f "$(get_app_defaults_path)" ]; then appdefaults_option="4" menu_items+=("4" "App Defaults for ${APP}") settings_option="5" menu_items+=("5" "Settings") else settings_option="4" menu_items+=("4" "Settings") fi APPDEFAULTS_OPTION="$appdefaults_option" SETTINGS_OPTION="$settings_option" # Main menu loop - allows returning from Settings while true; do if [ -z "$CHOICE" ]; then TMP_CHOICE=$(whiptail \ --backtitle "Proxmox VE Helper Scripts" \ --title "Community-Scripts Options" \ --ok-button "Select" --cancel-button "Exit Script" \ --notags \ --menu "\nChoose an option:\n Use TAB or Arrow keys to navigate, ENTER to select.\n" \ 20 60 9 \ "${menu_items[@]}" \ --default-item "1" \ 3>&1 1>&2 2>&3) || exit_script CHOICE="$TMP_CHOICE" fi # --- Main case --- local defaults_target="" local run_maybe_offer="no" case "$CHOICE" in 1 | default | DEFAULT) header_info echo -e "${DEFAULT}${BOLD}${BL}Using Default Settings on node $PVEHOST_NAME${CL}" VERBOSE="no" METHOD="default" base_settings "$VERBOSE" echo_default defaults_target="$(ensure_global_default_vars_file)" break ;; 2 | advanced | ADVANCED) header_info echo -e "${ADVANCED}${BOLD}${RD}Using Advanced Install on node $PVEHOST_NAME${CL}" METHOD="advanced" base_settings advanced_settings defaults_target="$(ensure_global_default_vars_file)" run_maybe_offer="yes" break ;; 3 | mydefaults | MYDEFAULTS | userdefaults | USERDEFAULTS) default_var_settings || { msg_error "Failed to apply default.vars" exit 110 } defaults_target="/usr/local/community-scripts/default.vars" break ;; "$APPDEFAULTS_OPTION" | appdefaults | APPDEFAULTS) if [ -f "$(get_app_defaults_path)" ]; then header_info echo -e "${DEFAULT}${BOLD}${BL}Using App Defaults for ${APP} on node $PVEHOST_NAME${CL}" METHOD="appdefaults" load_vars_file "$(get_app_defaults_path)" "yes" # Force override script defaults base_settings echo_default defaults_target="$(get_app_defaults_path)" break else msg_error "No App Defaults available for ${APP}" exit 111 fi ;; "$SETTINGS_OPTION" | settings | SETTINGS) settings_menu # After settings menu, show main menu again header_info CHOICE="" ;; generated | GENERATED) header_info echo -e "${DEFAULT}${BOLD}${BL}Using Generated Settings on node $PVEHOST_NAME${CL}" VERBOSE="no" METHOD="generated" base_settings "$VERBOSE" echo_default break ;; *) msg_error "Invalid option: $CHOICE" exit 112 ;; esac done if [[ -n "$defaults_target" ]]; then ensure_storage_selection_for_vars_file "$defaults_target" fi if [[ "$run_maybe_offer" == "yes" ]]; then maybe_offer_save_app_defaults fi } edit_default_storage() { local vf="/usr/local/community-scripts/default.vars" # Ensure file exists if [[ ! -f "$vf" ]]; then mkdir -p "$(dirname "$vf")" touch "$vf" fi # Let ensure_storage_selection_for_vars_file handle everything ensure_storage_selection_for_vars_file "$vf" } settings_menu() { while true; do local settings_items=( "1" "Manage API-Diagnostic Setting" "2" "Edit Default.vars" ) if [ -f "$(get_app_defaults_path)" ]; then settings_items+=("3" "Edit App.vars for ${APP}") settings_items+=("4" "Back to Main Menu") else settings_items+=("3" "Back to Main Menu") fi local choice choice=$(whiptail --backtitle "Proxmox VE Helper Scripts" \ --title "Community-Scripts SETTINGS Menu" \ --ok-button "Select" --cancel-button "Exit Script" \ --menu "\n\nChoose a settings option:\n\nUse Arrow keys to navigate, ENTER to select, TAB for buttons." 20 60 9 \ "${settings_items[@]}" \ 3>&1 1>&2 2>&3) || exit_script case "$choice" in 1) diagnostics_menu ;; 2) ${EDITOR:-nano} /usr/local/community-scripts/default.vars ;; 3) if [ -f "$(get_app_defaults_path)" ]; then ${EDITOR:-nano} "$(get_app_defaults_path)" else # Back was selected (no app.vars available) return fi ;; 4) # Back to main menu return ;; esac done } # ------------------------------------------------------------------------------ # check_container_resources() # # - Compares host RAM/CPU with required values # - Warns if under-provisioned and asks user to continue or abort # ------------------------------------------------------------------------------ check_container_resources() { current_ram=$(free -m | awk 'NR==2{print $2}') current_cpu=$(nproc) if [[ "$current_ram" -lt "$var_ram" ]] || [[ "$current_cpu" -lt "$var_cpu" ]]; then msg_warn "Under-provisioned: Required ${var_cpu} CPU/${var_ram}MB RAM, Current ${current_cpu} CPU/${current_ram}MB RAM" echo -e "${YWB}Please ensure that the ${APP} LXC is configured with at least ${var_cpu} vCPU and ${var_ram} MB RAM for the build process.${CL}\n" if is_unattended; then msg_error "Aborted: under-provisioned LXC in unattended mode (${current_cpu} CPU/${current_ram}MB RAM < ${var_cpu} CPU/${var_ram}MB RAM)" exit 113 fi echo -ne "${INFO}${HOLD} May cause data loss! ${INFO} Continue update with under-provisioned LXC? " read -r prompt 80% and asks user confirmation before proceeding # ------------------------------------------------------------------------------ check_container_storage() { total_size=$(df /boot --output=size | tail -n 1) local used_size=$(df /boot --output=used | tail -n 1) usage=$((100 * used_size / total_size)) if ((usage > 80)); then msg_warn "Storage is dangerously low (${usage}% used on /boot)" if is_unattended; then msg_error "Aborted: storage too low in unattended mode (${usage}% used on /boot)" exit 114 fi echo -ne "Continue anyway? " read -r prompt 0) { print substr($0, RSTART) } } ' } # ------------------------------------------------------------------------------ # ssh_build_choices_from_files() # # - Builds interactive whiptail checklist of available SSH keys # - Generates fingerprint, type and comment for each key # ------------------------------------------------------------------------------ ssh_build_choices_from_files() { local -a files=("$@") CHOICES=() COUNT=0 MAPFILE="$(mktemp)" local id key typ fp cmt base ln=0 for f in "${files[@]}"; do [[ -f "$f" && -r "$f" ]] || continue base="$(basename -- "$f")" case "$base" in known_hosts | known_hosts.* | config) continue ;; id_*) [[ "$f" != *.pub ]] && continue ;; esac # map every key in file while IFS= read -r key; do [[ -n "$key" ]] || continue typ="" fp="" cmt="" # Only the pure key part (without options) is already included in ‘key’. read -r _typ _b64 _cmt <<<"$key" typ="${_typ:-key}" cmt="${_cmt:-}" # Fingerprint via ssh-keygen (if available) if command -v ssh-keygen >/dev/null 2>&1; then fp="$(printf '%s\n' "$key" | ssh-keygen -lf - 2>/dev/null | awk '{print $2}')" fi # Label shorten [[ ${#cmt} -gt 40 ]] && cmt="${cmt:0:37}..." ln=$((ln + 1)) COUNT=$((COUNT + 1)) id="K${COUNT}" echo "${id}|${key}" >>"$MAPFILE" CHOICES+=("$id" "[$typ] ${fp:+$fp }${cmt:+$cmt }— ${base}" "OFF") done < <(ssh_extract_keys_from_file "$f") done } # ------------------------------------------------------------------------------ # ssh_discover_default_files() # # - Scans standard paths for SSH keys # - Includes ~/.ssh/*.pub, /etc/ssh/authorized_keys, etc. # ------------------------------------------------------------------------------ ssh_discover_default_files() { local -a cand=() shopt -s nullglob cand+=(/root/.ssh/authorized_keys /root/.ssh/authorized_keys2) cand+=(/root/.ssh/*.pub) cand+=(/etc/ssh/authorized_keys /etc/ssh/authorized_keys.d/*) shopt -u nullglob printf '%s\0' "${cand[@]}" } configure_ssh_settings() { local step_info="${1:-}" local backtitle="Proxmox VE Helper Scripts" [[ -n "$step_info" ]] && backtitle="Proxmox VE Helper Scripts [${step_info}]" SSH_KEYS_FILE="$(mktemp)" : >"$SSH_KEYS_FILE" IFS=$'\0' read -r -d '' -a _def_files < <(ssh_discover_default_files && printf '\0') ssh_build_choices_from_files "${_def_files[@]}" local default_key_count="$COUNT" local ssh_key_mode if [[ "$default_key_count" -gt 0 ]]; then ssh_key_mode=$(whiptail --backtitle "$backtitle" --title "SSH KEY SOURCE" --menu \ "Provision SSH keys for root:" 14 72 4 \ "found" "Select from detected keys (${default_key_count})" \ "manual" "Paste a single public key" \ "folder" "Scan another folder (path or glob)" \ "none" "No keys" 3>&1 1>&2 2>&3) || exit_script else ssh_key_mode=$(whiptail --backtitle "$backtitle" --title "SSH KEY SOURCE" --menu \ "No host keys detected; choose manual/none:" 12 72 2 \ "manual" "Paste a single public key" \ "none" "No keys" 3>&1 1>&2 2>&3) || exit_script fi case "$ssh_key_mode" in found) local selection selection=$(whiptail --backtitle "$backtitle" --title "SELECT HOST KEYS" \ --checklist "Select one or more keys to import:" 20 140 10 "${CHOICES[@]}" 3>&1 1>&2 2>&3) || exit_script for tag in $selection; do tag="${tag%\"}" tag="${tag#\"}" local line line=$(grep -E "^${tag}\|" "$MAPFILE" | head -n1 | cut -d'|' -f2- || true) [[ -n "$line" ]] && printf '%s\n' "$line" >>"$SSH_KEYS_FILE" done ;; manual) SSH_AUTHORIZED_KEY="$(whiptail --backtitle "$backtitle" \ --inputbox "Paste one SSH public key line (ssh-ed25519/ssh-rsa/...)" 10 72 --title "SSH Public Key" 3>&1 1>&2 2>&3)" [[ -n "$SSH_AUTHORIZED_KEY" ]] && printf '%s\n' "$SSH_AUTHORIZED_KEY" >>"$SSH_KEYS_FILE" ;; folder) local glob_path glob_path=$(whiptail --backtitle "$backtitle" \ --inputbox "Enter a folder or glob to scan (e.g. /root/.ssh/*.pub)" 10 72 --title "Scan Folder/Glob" 3>&1 1>&2 2>&3) if [[ -n "$glob_path" ]]; then shopt -s nullglob read -r -a _scan_files <<<"$glob_path" shopt -u nullglob if [[ "${#_scan_files[@]}" -gt 0 ]]; then ssh_build_choices_from_files "${_scan_files[@]}" if [[ "$COUNT" -gt 0 ]]; then local folder_selection folder_selection=$(whiptail --backtitle "$backtitle" --title "SELECT FOLDER KEYS" \ --checklist "Select key(s) to import:" 20 78 10 "${CHOICES[@]}" 3>&1 1>&2 2>&3) || exit_script for tag in $folder_selection; do tag="${tag%\"}" tag="${tag#\"}" local line line=$(grep -E "^${tag}\|" "$MAPFILE" | head -n1 | cut -d'|' -f2- || true) [[ -n "$line" ]] && printf '%s\n' "$line" >>"$SSH_KEYS_FILE" done else whiptail --backtitle "$backtitle" --msgbox "No keys found in: $glob_path" 8 60 fi else whiptail --backtitle "$backtitle" --msgbox "Path/glob returned no files." 8 60 fi fi ;; none) : ;; esac if [[ -s "$SSH_KEYS_FILE" ]]; then sort -u -o "$SSH_KEYS_FILE" "$SSH_KEYS_FILE" printf '\n' >>"$SSH_KEYS_FILE" fi # Always show SSH access dialog - user should be able to enable SSH even without keys if (whiptail --backtitle "$backtitle" --defaultno --title "SSH ACCESS" --yesno "Enable root SSH access?" 10 58); then SSH="yes" else SSH="no" fi } # ------------------------------------------------------------------------------ # msg_menu() # # - Displays a numbered menu for update_script() functions # - In silent mode (PHS_SILENT=1): auto-selects the default option # - In interactive mode: shows menu via read with 10s timeout + default fallback # - Usage: CHOICE=$(msg_menu "Title" "tag1" "Description 1" "tag2" "Desc 2" ...) # - The first item is always the default # - Returns the selected tag to stdout # - If no valid selection or timeout, returns the default (first) tag # ------------------------------------------------------------------------------ msg_menu() { local title="$1" shift # Parse items into parallel arrays: tags[] and descriptions[] local -a tags=() local -a descs=() while [[ $# -ge 2 ]]; do tags+=("$1") descs+=("$2") shift 2 done local default_tag="${tags[0]}" local count=${#tags[@]} # Silent mode: return default immediately if [[ -n "${PHS_SILENT+x}" ]] && [[ "${PHS_SILENT}" == "1" ]]; then echo "$default_tag" return 0 fi # Display menu to /dev/tty so it doesn't get captured by command substitution { echo "" msg_custom "📋" "${BL}" "${title}" echo "" for i in "${!tags[@]}"; do local marker=" " [[ $i -eq 0 ]] && marker="* " printf "${TAB3}${marker}%s) %s\n" "${tags[$i]}" "${descs[$i]}" done echo "" } >/dev/tty local selection="" read -r -t 10 -p "${TAB3}Select [default=${default_tag}, timeout 10s]: " selection /dev/tty || true # Validate selection if [[ -n "$selection" ]]; then for tag in "${tags[@]}"; do if [[ "$selection" == "$tag" ]]; then echo "$selection" return 0 fi done msg_warn "Invalid selection '${selection}' - using default: ${default_tag}" fi echo "$default_tag" return 0 } # ------------------------------------------------------------------------------ # run_addon_updates() # # - Scans /usr/local/bin/update_* for addon update scripts installed alongside # the main application (e.g. by tools/addon/*.sh) # - For each found addon, prompts the user (60s timeout, default no) whether # it should be updated as well # - Skipped entirely when PHS_SILENT=1 to keep unattended updates predictable # ------------------------------------------------------------------------------ run_addon_updates() { shopt -s nullglob local addons=(/usr/local/bin/update_*) shopt -u nullglob ((${#addons[@]} == 0)) && return 0 if [[ "${PHS_SILENT:-0}" == "1" ]]; then msg_info "Detected ${#addons[@]} addon update script(s) - skipping (PHS_SILENT)" return 0 fi echo echo -e "${INFO}${YW} Detected installed addon update script(s):${CL}" local a name for a in "${addons[@]}"; do echo -e "${TAB}- ${a##*/update_}" done echo local ans for a in "${addons[@]}"; do name="${a##*/update_}" printf 'Do you also want to update addon "%s"? (y/N) [60s]: ' "$name" ans="" if read -r -t 60 ans; then :; else echo; fi case "${ans,,}" in y | yes) bash "$a" || msg_warn "Addon update for $name failed (rc=$?)" ;; *) msg_info "Skipped addon: $name" ;; esac done } # ------------------------------------------------------------------------------ # start() # # - Entry point of script # - On Proxmox host: calls install_script # - In silent mode: runs update_script with automatic cleanup # - Otherwise: shows update/setting menu and runs update_script with cleanup # ------------------------------------------------------------------------------ start() { source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/tools.func) if command -v pveversion >/dev/null 2>&1; then install_script || return 0 return 0 elif [ ! -z ${PHS_SILENT+x} ] && [[ "${PHS_SILENT}" == "1" ]]; then VERBOSE="no" set_std_mode ensure_profile_loaded get_lxc_ip update_script run_addon_updates update_motd_ip cleanup_lxc else CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "${APP} LXC Update/Setting" --menu \ "Support/Update functions for ${APP} LXC. Choose an option:" \ 12 60 3 \ "1" "YES (Silent Mode)" \ "2" "YES (Verbose Mode)" \ "3" "NO (Cancel Update)" --nocancel --default-item "1" 3>&1 1>&2 2>&3) case "$CHOICE" in 1) VERBOSE="no" set_std_mode ;; 2) VERBOSE="yes" set_std_mode ;; 3) clear exit_script exit 0 ;; esac ensure_profile_loaded get_lxc_ip update_script run_addon_updates update_motd_ip cleanup_lxc fi } # ============================================================================== # SECTION 8: CONTAINER CREATION & DEPLOYMENT # ============================================================================== # ------------------------------------------------------------------------------ # build_container() # # - Main function for creating and configuring LXC container # - Builds network configuration string (IP, gateway, VLAN, MTU, MAC, IPv6) # - Creates container via pct create with all specified settings # - Applies features: FUSE, TUN, keyctl, VAAPI passthrough # - Starts container and waits for network connectivity # - Installs base packages (curl, sudo, etc.) # - Injects SSH keys if configured # - Executes -install.sh inside container # - Posts installation telemetry to API if diagnostics enabled # ------------------------------------------------------------------------------ build_container() { # if [ "$VERBOSE" == "yes" ]; then set -x; fi NET_STRING="-net0 name=eth0,bridge=${BRG:-vmbr0}" # MAC if [[ -n "$MAC" ]]; then case "$MAC" in ,hwaddr=*) NET_STRING+="$MAC" ;; *) NET_STRING+=",hwaddr=$MAC" ;; esac fi # IP (always required, default dhcp) NET_STRING+=",ip=${NET:-dhcp}" # Gateway if [[ -n "$GATE" ]]; then case "$GATE" in ,gw=) ;; ,gw=*) NET_STRING+="$GATE" ;; *) NET_STRING+=",gw=$GATE" ;; esac fi # VLAN if [[ -n "$VLAN" ]]; then case "$VLAN" in ,tag=*) NET_STRING+="$VLAN" ;; *) NET_STRING+=",tag=$VLAN" ;; esac fi # MTU if [[ -n "$MTU" ]]; then case "$MTU" in ,mtu=*) NET_STRING+="$MTU" ;; *) NET_STRING+=",mtu=$MTU" ;; esac fi # IPv6 Handling case "$IPV6_METHOD" in auto) NET_STRING="$NET_STRING,ip6=auto" ;; dhcp) NET_STRING="$NET_STRING,ip6=dhcp" ;; static) if [[ -n "$IPV6_ADDR" ]]; then NET_STRING="$NET_STRING,ip6=$IPV6_ADDR" [ -n "$IPV6_GATE" ] && NET_STRING="$NET_STRING,gw6=$IPV6_GATE" fi ;; none) ;; esac # Build FEATURES string based on container type and user choices FEATURES="" # Nesting support (user configurable, default enabled) if [ "${ENABLE_NESTING:-1}" == "1" ]; then FEATURES="nesting=1" fi # Keyctl for unprivileged containers (needed for Docker) if [ "$CT_TYPE" == "1" ]; then [ -n "$FEATURES" ] && FEATURES="$FEATURES," FEATURES="${FEATURES}keyctl=1" fi if [ "$ENABLE_FUSE" == "yes" ]; then [ -n "$FEATURES" ] && FEATURES="$FEATURES," FEATURES="${FEATURES}fuse=1" fi # Mknod support (user configurable via advanced settings) if [ "${ENABLE_MKNOD:-0}" == "1" ]; then [ -n "$FEATURES" ] && FEATURES="$FEATURES," FEATURES="${FEATURES}mknod=1" fi # Mount filesystem types (user configurable via advanced settings) if [ -n "${ALLOW_MOUNT_FS:-}" ]; then # Sanitize: strip spaces, trailing/leading commas, then convert commas to semicolons local _mount_clean="${ALLOW_MOUNT_FS// /}" _mount_clean="${_mount_clean%%,}" _mount_clean="${_mount_clean##,}" _mount_clean="${_mount_clean%%;}" _mount_clean="${_mount_clean//,/;}" if [ -n "$_mount_clean" ]; then [ -n "$FEATURES" ] && FEATURES="$FEATURES," FEATURES="${FEATURES}mount=${_mount_clean}" fi fi # Build PCT_OPTIONS as string for export local _func_url if [ "$var_os" == "alpine" ]; then _func_url="https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/alpine-install.func" else _func_url="https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/install.func" fi export FUNCTIONS_FILE_PATH="$(curl -fsSL "$_func_url")" if [[ -z "$FUNCTIONS_FILE_PATH" || ${#FUNCTIONS_FILE_PATH} -lt 100 ]]; then msg_error "Failed to download install functions from: $_func_url" exit 115 fi # Core exports for install.func export DIAGNOSTICS="$DIAGNOSTICS" export RANDOM_UUID="$RANDOM_UUID" export EXECUTION_ID="$EXECUTION_ID" export SESSION_ID="$SESSION_ID" export CACHER="$APT_CACHER" export CACHER_IP="$APT_CACHER_IP" export tz="$timezone" export APPLICATION="$APP" export app="$NSAPP" export PASSWORD="$PW" export VERBOSE="$VERBOSE" export SSH_ROOT="${SSH}" export SSH_AUTHORIZED_KEY export CTID="$CT_ID" export CTTYPE="$CT_TYPE" export ENABLE_FUSE="$ENABLE_FUSE" export ENABLE_TUN="$ENABLE_TUN" export PCT_OSTYPE="$var_os" export PCT_OSVERSION="$var_version" export PCT_DISK_SIZE="$DISK_SIZE" export IPV6_METHOD="$IPV6_METHOD" export ENABLE_GPU="$ENABLE_GPU" # DEV_MODE exports (optional, for debugging) export BUILD_LOG="$BUILD_LOG" export INSTALL_LOG="/root/.install-${SESSION_ID}.log" # Keep host-side logging on BUILD_LOG (not exported — invisible to container) # Without this, get_active_logfile() would return INSTALL_LOG (a container path) # and all host msg_info/msg_ok/msg_error would write to /root/.install-SESSION.log # on the HOST instead of BUILD_LOG, causing incomplete telemetry logs. _HOST_LOGFILE="$BUILD_LOG" export dev_mode="${dev_mode:-}" export DEV_MODE_MOTD="${DEV_MODE_MOTD:-false}" export DEV_MODE_KEEP="${DEV_MODE_KEEP:-false}" export DEV_MODE_TRACE="${DEV_MODE_TRACE:-false}" export DEV_MODE_PAUSE="${DEV_MODE_PAUSE:-false}" export DEV_MODE_BREAKPOINT="${DEV_MODE_BREAKPOINT:-false}" export DEV_MODE_LOGS="${DEV_MODE_LOGS:-false}" export DEV_MODE_DRYRUN="${DEV_MODE_DRYRUN:-false}" # Build PCT_OPTIONS as multi-line string PCT_OPTIONS_STRING=" -hostname $HN" # Only add -tags if TAGS is not empty if [ -n "$TAGS" ]; then PCT_OPTIONS_STRING="$PCT_OPTIONS_STRING -tags $TAGS" fi # Only add -features if FEATURES is not empty if [ -n "$FEATURES" ]; then PCT_OPTIONS_STRING=" -features $FEATURES $PCT_OPTIONS_STRING" fi # Add searchdomain if specified if [ -n "$SD" ]; then PCT_OPTIONS_STRING="$PCT_OPTIONS_STRING $SD" fi # Add nameserver if specified if [ -n "$NS" ]; then PCT_OPTIONS_STRING="$PCT_OPTIONS_STRING $NS" fi # Network configuration PCT_OPTIONS_STRING="$PCT_OPTIONS_STRING $NET_STRING -onboot 1 -cores $CORE_COUNT -memory $RAM_SIZE -unprivileged $CT_TYPE" # Protection flag (if var_protection was set) if [ "${PROTECT_CT:-}" == "1" ] || [ "${PROTECT_CT:-}" == "yes" ]; then PCT_OPTIONS_STRING="$PCT_OPTIONS_STRING -protection 1" fi # Timezone (map Etc/* to "host" as pct doesn't accept them) if [ -n "${CT_TIMEZONE:-}" ]; then local _pct_timezone="$CT_TIMEZONE" [[ "$_pct_timezone" == Etc/* ]] && _pct_timezone="host" PCT_OPTIONS_STRING="$PCT_OPTIONS_STRING -timezone $_pct_timezone" fi # Password (already formatted) if [ -n "$PW" ]; then PCT_OPTIONS_STRING="$PCT_OPTIONS_STRING $PW" fi # Export as string (this works, unlike arrays!) export PCT_OPTIONS="$PCT_OPTIONS_STRING" export TEMPLATE_STORAGE="${var_template_storage:-}" export CONTAINER_STORAGE="${var_container_storage:-}" # Validate storage space only if CONTAINER_STORAGE is already set # (Storage selection happens in create_lxc_container for some modes) if [[ -n "$CONTAINER_STORAGE" ]]; then msg_info "Validating storage space" if ! validate_storage_space "$CONTAINER_STORAGE" "$DISK_SIZE" "no"; then local free_space free_space=$(pvesm status 2>/dev/null | awk -v s="$CONTAINER_STORAGE" '$1 == s { print $6 }') local free_fmt free_fmt=$(numfmt --to=iec --from-unit=1024 --suffix=B --format %.1f "$free_space" 2>/dev/null || echo "${free_space}KB") msg_error "Not enough space on '$CONTAINER_STORAGE'. Required: ${DISK_SIZE}GB, Available: ${free_fmt}" exit 214 fi msg_ok "Storage space validated" fi create_lxc_container || exit $? # Transition to 'configuring' — container created, now setting up OS/userland post_progress_to_api "configuring" LXC_CONFIG="/etc/pve/lxc/${CTID}.conf" # ============================================================================ # GPU/USB PASSTHROUGH CONFIGURATION # ============================================================================ # Check if GPU passthrough is enabled # Returns true only if var_gpu is explicitly set to "yes" # Can be set via: # - Environment variable: var_gpu=yes bash -c "..." # - CT script default: var_gpu="${var_gpu:-no}" # - Advanced settings wizard # - App defaults file: /usr/local/community-scripts/defaults/.vars is_gpu_app() { [[ "${var_gpu:-no}" == "yes" ]] && return 0 return 1 } # Detect all available GPU devices detect_gpu_devices() { INTEL_DEVICES=() AMD_DEVICES=() NVIDIA_DEVICES=() # Store PCI info to avoid multiple calls # grep returns exit 1 when no match — use || true to prevent ERR trap local pci_vga_info pci_vga_info=$(lspci -nn 2>/dev/null | grep -E "VGA|Display|3D" || true) # No GPU-related PCI devices at all? Skip silently. if [[ -z "$pci_vga_info" ]]; then msg_debug "No VGA/Display/3D PCI devices found" return 0 fi # Check for Intel GPU - look for Intel vendor ID [8086] if grep -q "\[8086:" <<<"$pci_vga_info"; then msg_custom "🎮" "${BL}" "Detected Intel GPU" if [[ -d /dev/dri ]]; then for d in /dev/dri/renderD* /dev/dri/card*; do [[ -e "$d" ]] && INTEL_DEVICES+=("$d") done fi fi # Check for AMD GPU - look for AMD vendor IDs [1002] (AMD/ATI) or [1022] (AMD) if grep -qE "\[1002:|\[1022:" <<<"$pci_vga_info"; then msg_custom "🎮" "${RD}" "Detected AMD GPU" if [[ -d /dev/dri ]]; then # Only add if not already claimed by Intel if [[ ${#INTEL_DEVICES[@]} -eq 0 ]]; then for d in /dev/dri/renderD* /dev/dri/card*; do [[ -e "$d" ]] && AMD_DEVICES+=("$d") done fi fi fi # Check for NVIDIA GPU - look for NVIDIA vendor ID [10de] if grep -q "\[10de:" <<<"$pci_vga_info"; then msg_custom "🎮" "${GN}" "Detected NVIDIA GPU" # Simple passthrough - just bind /dev/nvidia* devices if they exist # Only include character devices (-c), skip directories like /dev/nvidia-caps for d in /dev/nvidia*; do [[ -c "$d" ]] && NVIDIA_DEVICES+=("$d") done # Also check for devices inside /dev/nvidia-caps/ directory if [[ -d /dev/nvidia-caps ]]; then for d in /dev/nvidia-caps/*; do [[ -c "$d" ]] && NVIDIA_DEVICES+=("$d") done fi if [[ ${#NVIDIA_DEVICES[@]} -gt 0 ]]; then msg_custom "🎮" "${GN}" "Found ${#NVIDIA_DEVICES[@]} NVIDIA device(s) for passthrough" else msg_warn "NVIDIA GPU detected via PCI but no /dev/nvidia* devices found" msg_custom "ℹ️" "${YW}" "Skipping NVIDIA passthrough (host drivers may not be loaded)" fi fi # Debug output msg_debug "Intel devices: ${INTEL_DEVICES[*]}" msg_debug "AMD devices: ${AMD_DEVICES[*]}" msg_debug "NVIDIA devices: ${NVIDIA_DEVICES[*]}" } # Configure USB passthrough for privileged containers configure_usb_passthrough() { if [[ "$CT_TYPE" != "0" ]]; then return 0 fi msg_info "Configuring automatic USB passthrough (privileged container)" cat <>"$LXC_CONFIG" # Automatic USB passthrough (privileged container) lxc.cgroup2.devices.allow: a lxc.cap.drop: lxc.cgroup2.devices.allow: c 188:* rwm lxc.cgroup2.devices.allow: c 189:* rwm lxc.mount.entry: /dev/serial/by-id dev/serial/by-id none bind,optional,create=dir lxc.mount.entry: /dev/ttyUSB0 dev/ttyUSB0 none bind,optional,create=file lxc.mount.entry: /dev/ttyUSB1 dev/ttyUSB1 none bind,optional,create=file lxc.mount.entry: /dev/ttyACM0 dev/ttyACM0 none bind,optional,create=file lxc.mount.entry: /dev/ttyACM1 dev/ttyACM1 none bind,optional,create=file EOF msg_ok "USB passthrough configured" } # Configure GPU passthrough configure_gpu_passthrough() { # Skip if: # GPU passthrough is enabled when var_gpu="yes": # - Set via environment variable: var_gpu=yes bash -c "..." # - Set in CT script: var_gpu="${var_gpu:-no}" # - Enabled in advanced_settings wizard # - Configured in app defaults file if ! is_gpu_app "$APP"; then return 0 fi detect_gpu_devices # Count available GPU types local gpu_count=0 local available_gpus=() if [[ ${#INTEL_DEVICES[@]} -gt 0 ]]; then available_gpus+=("INTEL") gpu_count=$((gpu_count + 1)) fi if [[ ${#AMD_DEVICES[@]} -gt 0 ]]; then available_gpus+=("AMD") gpu_count=$((gpu_count + 1)) fi if [[ ${#NVIDIA_DEVICES[@]} -gt 0 ]]; then available_gpus+=("NVIDIA") gpu_count=$((gpu_count + 1)) fi if [[ $gpu_count -eq 0 ]]; then msg_custom "ℹ️" "${YW}" "No GPU devices found for passthrough" return 0 fi local selected_gpu="" if [[ $gpu_count -eq 1 ]]; then # Automatic selection for single GPU selected_gpu="${available_gpus[0]}" msg_ok "Automatically configuring ${selected_gpu} GPU passthrough" else # Multiple GPUs - ask user echo -e "\n${INFO} Multiple GPU types detected:" for gpu in "${available_gpus[@]}"; do echo " - $gpu" done read -rp "Which GPU type to passthrough? (${available_gpus[*]}): " selected_gpu >"$LXC_CONFIG" dev_index=$((dev_index + 1)) done export GPU_TYPE="$selected_gpu" msg_ok "${selected_gpu} GPU passthrough configured (${#devices[@]} devices)" ;; NVIDIA) if [[ ${#NVIDIA_DEVICES[@]} -eq 0 ]]; then msg_warn "No NVIDIA devices available for passthrough" return 0 fi # Use pct set for NVIDIA devices local dev_index=0 for dev in "${NVIDIA_DEVICES[@]}"; do echo "dev${dev_index}: ${dev},gid=44" >>"$LXC_CONFIG" dev_index=$((dev_index + 1)) done export GPU_TYPE="NVIDIA" msg_ok "NVIDIA GPU passthrough configured (${#NVIDIA_DEVICES[@]} devices) - install drivers in container if needed" ;; esac } # Additional device passthrough configure_additional_devices() { # TUN device passthrough if [ "$ENABLE_TUN" == "yes" ]; then cat <>"$LXC_CONFIG" lxc.cgroup2.devices.allow: c 10:200 rwm lxc.mount.entry: /dev/net/tun dev/net/tun none bind,create=file EOF fi # Coral TPU passthrough if [[ -e /dev/apex_0 ]]; then msg_custom "🔌" "${BL}" "Detected Coral TPU - configuring passthrough" echo "lxc.mount.entry: /dev/apex_0 dev/apex_0 none bind,optional,create=file" >>"$LXC_CONFIG" fi } # Execute pre-start configurations configure_usb_passthrough configure_gpu_passthrough configure_additional_devices # Increase disk size for AMD ROCm runtime (~4GB extra needed) if [[ "${GPU_TYPE:-}" == "AMD" ]]; then local rocm_extra=4 local new_disk_size=$((PCT_DISK_SIZE + rocm_extra)) if pct resize "$CTID" rootfs "${new_disk_size}G" >/dev/null 2>&1; then msg_ok "Disk resized ${PCT_DISK_SIZE}GB → ${new_disk_size}GB for ROCm" else msg_warn "Failed to resize disk for ROCm — installation may fail if space is insufficient" fi fi # ============================================================================ # START CONTAINER AND INSTALL USERLAND # ============================================================================ msg_info "Starting LXC Container" pct start "$CTID" # Wait for container to be running for i in {1..10}; do if pct status "$CTID" | grep -q "status: running"; then msg_ok "Started LXC Container" break fi sleep 1 if [ "$i" -eq 10 ]; then local ct_status ct_status=$(pct status "$CTID" 2>/dev/null || echo "unknown") msg_error "LXC Container did not reach running state (status: ${ct_status})" exit 117 fi done # Wait for network (skip for Alpine initially) if [ "$var_os" != "alpine" ]; then msg_info "Waiting for network in LXC container" # Wait for IP assignment (IPv4 or IPv6) local ip_in_lxc="" for i in {1..60}; do # Try IPv4 first ip_in_lxc=$(pct exec "$CTID" -- ip -4 addr show dev eth0 2>/dev/null | awk '/inet / {print $2}' | cut -d/ -f1) # Fallback to IPv6 if IPv4 not available if [ -z "$ip_in_lxc" ]; then ip_in_lxc=$(pct exec "$CTID" -- ip -6 addr show dev eth0 scope global 2>/dev/null | awk '/inet6 / {print $2}' | cut -d/ -f1 | head -n1) fi [ -n "$ip_in_lxc" ] && break # Progressive backoff: 1s for first 20, 2s for next 20, 3s for last 20 if [ "$i" -le 20 ]; then sleep 1 elif [ "$i" -le 40 ]; then sleep 2 else sleep 3 fi done if [ -z "$ip_in_lxc" ]; then msg_error "No IP assigned to CT $CTID after 60 attempts" msg_custom "🔧" "${YW}" "Troubleshooting:" echo " • Verify bridge ${BRG} exists and has connectivity" echo " • Check if DHCP server is reachable (if using DHCP)" echo " • Verify static IP configuration (if using static IP)" echo " • Check Proxmox firewall rules" echo " • If using Tailscale: Disable MagicDNS temporarily" exit 118 fi # Verify basic connectivity (ping test) local ping_success=false for retry in {1..3}; do if pct exec "$CTID" -- ping -c 1 -W 2 1.1.1.1 &>/dev/null || pct exec "$CTID" -- ping -c 1 -W 2 8.8.8.8 &>/dev/null || pct exec "$CTID" -- ping6 -c 1 -W 2 2606:4700:4700::1111 &>/dev/null; then ping_success=true break fi sleep 2 done if [ "$ping_success" = false ]; then msg_warn "Network configured (IP: $ip_in_lxc) but connectivity test failed - installation will continue" else msg_ok "Network in LXC is reachable (ping)" fi fi # Function to get correct GID inside container get_container_gid() { local group="$1" local gid=$(pct exec "$CTID" -- getent group "$group" 2>/dev/null | cut -d: -f3) echo "${gid:-44}" # Default to 44 if not found } fix_gpu_gids # Fix Debian 13 LXC template bug where / is owned by nobody:nogroup # This must be done from the host as unprivileged containers cannot chown / local rootfs rootfs=$(pct config "$CTID" | grep -E '^rootfs:' | sed 's/rootfs: //' | cut -d',' -f1 || true) if [[ -n "$rootfs" ]]; then local mount_point="/var/lib/lxc/${CTID}/rootfs" if [[ -d "$mount_point" ]] && [[ "$(stat -c '%U' "$mount_point")" != "root" ]]; then chown root:root "$mount_point" 2>/dev/null || true fi fi # Continue with standard container setup msg_info "Customizing LXC Container" # # Install GPU userland if configured # if [[ "${ENABLE_VAAPI:-0}" == "1" ]]; then # install_gpu_userland "VAAPI" # fi # if [[ "${ENABLE_NVIDIA:-0}" == "1" ]]; then # install_gpu_userland "NVIDIA" # fi # Disable error trap for entire customization & install phase. # All errors are handled explicitly — recovery menu shown on failure. # Without this, customization errors (e.g. container stopped during base package # install) would trigger error_handler() with a simple "Remove broken container?" # prompt instead of the full recovery menu with retry/repair options. set +Eeuo pipefail trap - ERR local install_exit_code=0 # Continue with standard container setup if [ "$var_os" == "alpine" ]; then sleep 3 pct exec "$CTID" -- /bin/sh -c 'cat </etc/apk/repositories https://dl-cdn.alpinelinux.org/alpine/latest-stable/main https://dl-cdn.alpinelinux.org/alpine/latest-stable/community EOF' pct exec "$CTID" -- ash -c "apk add bash newt curl openssh nano mc ncurses jq" >>"$BUILD_LOG" 2>&1 || { msg_warn "apk install failed (dl-cdn.alpinelinux.org), trying alternate mirrors..." local alpine_exit=0 pct exec "$CTID" -- ash -c ' ALPINE_MIRRORS="mirror.init7.net ftp.halifax.rwth-aachen.de mirrors.edge.kernel.org alpine.mirror.wearetriple.com mirror.leaseweb.com uk.alpinelinux.org dl-2.alpinelinux.org dl-4.alpinelinux.org" for m in $(printf "%s\n" $ALPINE_MIRRORS | shuf); do if wget -q --spider --timeout=2 "http://$m/alpine/latest-stable/main/" 2>/dev/null; then echo " Attempting mirror: $m" cat </etc/apk/repositories http://$m/alpine/latest-stable/main http://$m/alpine/latest-stable/community EOF if apk update >/dev/null 2>&1 && apk add bash newt curl openssh nano mc ncurses jq >/dev/null 2>&1; then echo " CDN set to $m: tests passed" exit 0 else echo " Mirror $m failed" fi fi done exit 2 ' && alpine_exit=0 || alpine_exit=$? if [[ $alpine_exit -ne 0 ]]; then msg_error "Failed to install base packages in Alpine container" install_exit_code=1 fi } else sleep 3 LANG=${LANG:-en_US.UTF-8} local LANG_ESC="${LANG//./\\.}" LANG_ESC="${LANG_ESC//|/\\|}" pct exec "$CTID" -- bash -c "sed -i \"/$LANG_ESC/ s/^# //\" /etc/locale.gen" pct exec "$CTID" -- bash -c "locale_line=\$(grep -v '^#' /etc/locale.gen | grep -E '^[a-zA-Z]' | awk '{print \$1}' | head -n 1) && \ echo LANG=\$locale_line >/etc/default/locale && \ locale-gen >/dev/null && \ export LANG=\$locale_line" if [[ -z "${tz:-}" ]]; then tz=$(timedatectl show --property=Timezone --value 2>/dev/null || echo "UTC") fi [[ "${tz:-}" == Etc/* ]] && tz="UTC" # Normalize Etc/* to UTC for container setup if pct exec "$CTID" -- test -e "/usr/share/zoneinfo/$tz"; then # Set timezone using symlink (Debian 13+ compatible) # Create /etc/timezone for backwards compatibility with older scripts pct exec "$CTID" -- bash -c "tz='$tz'; ln -sf \"/usr/share/zoneinfo/\$tz\" /etc/localtime && echo \"\$tz\" >/etc/timezone || true" else msg_warn "Skipping timezone setup – zone '$tz' not found in container" fi # Detect broken DNS resolver (e.g. Tailscale MagicDNS) and inject public DNS if ! pct exec "$CTID" -- bash -c "getent hosts deb.debian.org >/dev/null 2>&1 && getent hosts archive.ubuntu.com >/dev/null 2>&1"; then msg_warn "APT repository DNS resolution failed in container, injecting public DNS servers" pct exec "$CTID" -- bash -c "echo -e 'nameserver 8.8.8.8\nnameserver 1.1.1.1' >/etc/resolv.conf" fi pct exec "$CTID" -- bash -c "apt-get update 2>&1 && apt-get install -y sudo curl mc gnupg2 jq 2>&1" >>"$BUILD_LOG" 2>&1 || { local failed_mirror failed_mirror=$(pct exec "$CTID" -- bash -c "grep -m1 -oP '(?<=URIs: https?://)[^/]+' /etc/apt/sources.list.d/debian.sources 2>/dev/null || grep -m1 -oP '(?<=deb https?://)[^/]+' /etc/apt/sources.list 2>/dev/null" 2>/dev/null || echo "unknown") msg_warn "apt-get update failed (${failed_mirror}), trying alternate mirrors..." local mirror_exit=0 pct exec "$CTID" -- bash -c ' APT_BASE="sudo curl mc gnupg2 jq" DISTRO=$(. /etc/os-release 2>/dev/null && echo "$ID" || echo "debian") if [ "$DISTRO" = "ubuntu" ]; then EU_MIRRORS="de.archive.ubuntu.com fr.archive.ubuntu.com se.archive.ubuntu.com nl.archive.ubuntu.com it.archive.ubuntu.com ch.archive.ubuntu.com mirrors.xtom.de" US_MIRRORS="us.archive.ubuntu.com archive.ubuntu.com mirrors.edge.kernel.org mirror.csclub.uwaterloo.ca mirrors.ocf.berkeley.edu mirror.math.princeton.edu" AP_MIRRORS="au.archive.ubuntu.com jp.archive.ubuntu.com kr.archive.ubuntu.com tw.archive.ubuntu.com mirror.aarnet.edu.au" else EU_MIRRORS="ftp.de.debian.org ftp.fr.debian.org ftp.nl.debian.org ftp.uk.debian.org ftp.ch.debian.org ftp.se.debian.org ftp.it.debian.org ftp.fau.de ftp.halifax.rwth-aachen.de debian.mirror.lrz.de mirror.init7.net debian.ethz.ch mirrors.dotsrc.org debian.mirrors.ovh.net" US_MIRRORS="ftp.us.debian.org ftp.ca.debian.org debian.csail.mit.edu mirrors.ocf.berkeley.edu mirrors.wikimedia.org debian.osuosl.org mirror.cogentco.com" AP_MIRRORS="ftp.au.debian.org ftp.jp.debian.org ftp.tw.debian.org ftp.kr.debian.org ftp.hk.debian.org ftp.sg.debian.org mirror.aarnet.edu.au mirror.nitc.ac.in" fi TZ=$(cat /etc/timezone 2>/dev/null || echo "UTC") case "$TZ" in Europe/*|Arctic/*) REGIONAL="$EU_MIRRORS"; OTHERS="$US_MIRRORS $AP_MIRRORS" ;; America/*) REGIONAL="$US_MIRRORS"; OTHERS="$EU_MIRRORS $AP_MIRRORS" ;; Asia/*|Australia/*|Pacific/*) REGIONAL="$AP_MIRRORS"; OTHERS="$EU_MIRRORS $US_MIRRORS" ;; *) REGIONAL=""; OTHERS="$EU_MIRRORS $US_MIRRORS $AP_MIRRORS" ;; esac echo "Acquire::By-Hash \"no\";" >/etc/apt/apt.conf.d/99no-by-hash try_mirrors() { for src in /etc/apt/sources.list.d/debian.sources /etc/apt/sources.list; do [ -f "$src" ] && sed -i "s|URIs: http[s]*://[^/]*/|URIs: http://${1}/|g; s|deb http[s]*://[^/]*/|deb http://${1}/|g" "$src" done rm -rf /var/lib/apt/lists/* APT_OUT=$(apt-get update 2>&1) APT_RC=$? if echo "$APT_OUT" | grep -qi "hashsum\|hash sum"; then echo " Mirror $1 failed (hash mismatch)" return 1 elif echo "$APT_OUT" | grep -qi "SSL\|certificate"; then echo " Mirror $1 failed (SSL/certificate error)" return 1 elif [ $APT_RC -ne 0 ]; then echo " Mirror $1 failed (apt-get update error)" return 1 elif apt-get install -y $APT_BASE >/dev/null 2>&1; then echo " CDN set to $1: tests passed" return 0 else echo " Mirror $1 failed (package install error)" return 1 fi } scan_reachable() { local result="" for m in $1; do if timeout 2 bash -c "echo >/dev/tcp/$m/80" 2>/dev/null; then result="$result $m" fi done echo "$result" | xargs } # Phase 1: Scan global mirrors first (independent of local CDN issues) OTHERS_OK=$(scan_reachable "$OTHERS") OTHERS_PICK=$(printf "%s\n" $OTHERS_OK | shuf | head -3 | xargs) for mirror in $OTHERS_PICK; do echo " Attempting mirror: $mirror" try_mirrors "$mirror" && exit 0 done # Phase 2: Try primary mirror if [ "$DISTRO" = "ubuntu" ]; then PRIMARY="archive.ubuntu.com" else PRIMARY="ftp.debian.org" fi if timeout 2 bash -c "echo >/dev/tcp/$PRIMARY/80" 2>/dev/null; then echo " Attempting mirror: $PRIMARY" try_mirrors "$PRIMARY" && exit 0 fi # Phase 3: Fall back to regional mirrors REGIONAL_OK=$(scan_reachable "$REGIONAL") REGIONAL_PICK=$(printf "%s\n" $REGIONAL_OK | shuf | head -3 | xargs) for mirror in $REGIONAL_PICK; do echo " Attempting mirror: $mirror" try_mirrors "$mirror" && exit 0 done exit 2 ' && mirror_exit=0 || mirror_exit=$? if [[ $mirror_exit -eq 2 ]]; then msg_warn "Multiple mirrors failed (possible CDN synchronization issue)." if [[ "$var_os" == "ubuntu" ]]; then msg_warn "Find Ubuntu mirrors at: https://launchpad.net/ubuntu/+archivemirrors" else msg_warn "Find Debian mirrors at: https://www.debian.org/mirror/list" fi local custom_mirror="" while true; do read -rp " Enter a mirror hostname (or 'skip' to abort): " custom_mirror /dev/null 2>&1 && apt-get install -y sudo curl mc gnupg2 jq >/dev/null 2>&1 " && break msg_warn "Mirror '${custom_mirror}' also failed. Try another or type 'skip'." done if [[ "$custom_mirror" == "skip" ]]; then msg_error "apt-get base packages installation failed" install_exit_code=1 fi elif [[ $mirror_exit -ne 0 ]]; then msg_error "apt-get base packages installation failed" install_exit_code=1 fi } fi # Only continue with installation if customization succeeded if [[ $install_exit_code -eq 0 ]]; then msg_ok "Customized LXC Container" # Optional DNS override for retry scenarios (inside LXC, never on host) if [[ "${DNS_RETRY_OVERRIDE:-false}" == "true" ]]; then msg_info "Applying DNS retry override in LXC (8.8.8.8, 1.1.1.1)" pct exec "$CTID" -- bash -c "printf 'nameserver 8.8.8.8\nnameserver 1.1.1.1\n' >/etc/resolv.conf" >/dev/null 2>&1 || true msg_ok "DNS override applied in LXC" fi # Install SSH keys install_ssh_keys_into_ct # Start timer for duration tracking start_install_timer # Run application installer # Error handling already disabled above (before customization phase) # Signal handlers use this flag to stop the container on abort (SIGHUP/SIGINT/SIGTERM) # Without this, SSH disconnects leave the container running as an orphan process # that sends "configuring" status AFTER the host already reported "failed" export CONTAINER_INSTALLING=true lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/install/${var_install}.sh)" local lxc_exit=$? unset CONTAINER_INSTALLING # Keep error handling DISABLED during failure detection and recovery # Re-enabling it here would cause any pct exec/pull failure to trigger # error_handler() on the host, bypassing the recovery menu entirely # Check for error flag file in container (more reliable than lxc-attach exit code) if [[ -n "${SESSION_ID:-}" ]]; then local error_flag="/root/.install-${SESSION_ID}.failed" if pct exec "$CTID" -- test -f "$error_flag" 2>/dev/null; then install_exit_code=$(pct exec "$CTID" -- cat "$error_flag" 2>/dev/null || echo "1") pct exec "$CTID" -- rm -f "$error_flag" 2>/dev/null || true fi fi # Fallback to lxc-attach exit code if no flag file if [[ $install_exit_code -eq 0 && ${lxc_exit:-0} -ne 0 ]]; then install_exit_code=${lxc_exit:-0} fi fi # end: if [[ $install_exit_code -eq 0 ]] (customization succeeded) # Installation or customization failed? if [[ $install_exit_code -ne 0 ]]; then # Prevent job-control signals from suspending the script during recovery. # In non-interactive shells (bash -c), background processes (spinner) can # trigger terminal-related signals that stop the entire process group. # TSTP = Ctrl+Z, TTIN = bg read from tty, TTOU = bg write to tty (tostop) trap '' TSTP TTIN TTOU msg_error "Installation failed in container ${CTID} (exit code: ${install_exit_code})" # Copy install log from container BEFORE API call so get_error_text() can read it local build_log_copied=false local install_log_copied=false local combined_log="/tmp/${NSAPP:-lxc}-${CTID}-${SESSION_ID}.log" if [[ -n "$CTID" && -n "${SESSION_ID:-}" ]]; then # Create combined log with header { echo "================================================================================" echo "COMBINED INSTALLATION LOG - ${APP:-LXC}" echo "Container ID: ${CTID}" echo "Session ID: ${SESSION_ID}" echo "Timestamp: $(date '+%Y-%m-%d %H:%M:%S')" echo "================================================================================" echo "" } >"$combined_log" # Append BUILD_LOG (host-side creation log) if it exists if [[ -f "${BUILD_LOG}" ]]; then { echo "================================================================================" echo "PHASE 1: CONTAINER CREATION (Host)" echo "================================================================================" cat "${BUILD_LOG}" echo "" } >>"$combined_log" build_log_copied=true fi # Copy and append INSTALL_LOG from container local temp_install_log="/tmp/.install-temp-${SESSION_ID}.log" if pct pull "$CTID" "/root/.install-${SESSION_ID}.log" "$temp_install_log" 2>/dev/null; then { echo "================================================================================" echo "PHASE 2: APPLICATION INSTALLATION (Container)" echo "================================================================================" cat "$temp_install_log" echo "" } >>"$combined_log" rm -f "$temp_install_log" install_log_copied=true # Point INSTALL_LOG to combined log so get_full_log() finds it INSTALL_LOG="$combined_log" fi fi # Defense-in-depth: Ensure error handling stays disabled during recovery. # Some functions (e.g. silent/$STD) unconditionally re-enable set -Eeuo pipefail # and trap 'error_handler' ERR. If any code path above called such a function, # the grep/sed pipelines below would trigger error_handler on non-match (exit 1). set +Eeuo pipefail trap - ERR # --- Exit code 1 subclassification: analyze logs BEFORE telemetry call --- # Exit code 1 is generic ("General error"). Analyze logs to determine the # real error category so telemetry gets a useful classification instead of "shell". local is_oom=false local is_network_issue=false local is_apt_issue=false local is_cmd_not_found=false local is_disk_full=false if [[ $install_exit_code -eq 1 && -f "$combined_log" ]]; then if grep -qiE 'E: Unable to|E: Package|E: Failed to fetch|dpkg.*error|broken packages|unmet dependencies|dpkg --configure -a' "$combined_log"; then is_apt_issue=true fi if grep -qiE 'Cannot allocate memory|Out of memory|oom-killer|Killed process|JavaScript heap' "$combined_log"; then is_oom=true fi if grep -qiE 'Could not resolve|DNS|Connection refused|Network is unreachable|No route to host|Temporary failure resolving|Failed to fetch' "$combined_log"; then is_network_issue=true fi if grep -qiE ': command not found|No such file or directory.*/s?bin/' "$combined_log"; then is_cmd_not_found=true fi if grep -qiE 'ENOSPC|no space left on device|Disk quota exceeded|errno -28' "$combined_log"; then is_disk_full=true fi fi # Set override for categorize_error() so telemetry gets the real category if [[ "$is_apt_issue" == true ]]; then export ERROR_CATEGORY_OVERRIDE="dependency" elif [[ "$is_oom" == true ]]; then export ERROR_CATEGORY_OVERRIDE="resource" elif [[ "$is_network_issue" == true ]]; then export ERROR_CATEGORY_OVERRIDE="network" elif [[ "$is_disk_full" == true ]]; then export ERROR_CATEGORY_OVERRIDE="storage" elif [[ "$is_cmd_not_found" == true ]]; then export ERROR_CATEGORY_OVERRIDE="dependency" fi # Report failure to telemetry API (now with log available on host) # NOTE: Do NOT use msg_info/spinner here — the background spinner process # causes SIGTSTP in non-interactive shells (bash -c "$(curl ...)"), which # stops the entire process group and prevents the recovery dialog from appearing. $STD echo -e "${TAB}⏳ Reporting failure to telemetry..." post_update_to_api "failed" "$install_exit_code" $STD echo -e "${TAB}${CM:-✔} Failure reported" # Show combined log location if [[ -n "$CTID" && -n "${SESSION_ID:-}" ]]; then msg_custom "📋" "${YW}" "Installation log: ${combined_log}" fi # Dev mode: Keep container or open breakpoint shell if [[ "${DEV_MODE_KEEP:-false}" == "true" ]]; then msg_dev "Keep mode active - container ${CTID} preserved" return 0 elif [[ "${DEV_MODE_BREAKPOINT:-false}" == "true" ]]; then msg_dev "Breakpoint mode - opening shell in container ${CTID}" echo -e "${YW}Type 'exit' to return to host${CL}" pct enter "$CTID" echo "" echo -en "${YW}Container ${CTID} still running. Remove now? (y/N): ${CL}" if read -r response /dev/null || true pct destroy "$CTID" &>/dev/null || true msg_ok "Container ${CTID} removed" else msg_dev "Container ${CTID} kept for debugging" fi exit $install_exit_code fi # Prompt user for cleanup with 60s timeout echo "" # Extend error detection for non-exit-1 codes (exit 1 was already analyzed above) # The is_* flags were set above for exit code 1 log analysis; here we add # exit-code-specific detections for other codes. local error_explanation="" if declare -f explain_exit_code >/dev/null 2>&1; then error_explanation="$(explain_exit_code "$install_exit_code")" fi # OOM detection: exit codes 134 (SIGABRT/heap), 137 (SIGKILL/OOM), 243 (Node.js heap) if [[ $install_exit_code -eq 134 || $install_exit_code -eq 137 || $install_exit_code -eq 243 ]]; then is_oom=true fi # APT/DPKG detection: exit codes 100-102 (APT), 255 (DPKG with log evidence) case "$install_exit_code" in 100 | 101 | 102) is_apt_issue=true ;; 255) if [[ -f "$combined_log" ]] && grep -qiE 'dpkg|apt-get|apt\.conf|broken packages|unmet dependencies|E: Sub-process|E: Failed' "$combined_log"; then is_apt_issue=true fi ;; esac # Disk full / ENOSPC detection: errno -28 (ENOSPC), exit 228 (custom handler), exit 23 (curl write error) if [[ $install_exit_code -eq 228 || $install_exit_code -eq 23 ]]; then is_disk_full=true fi if [[ -f "$combined_log" ]] && grep -qiE 'ENOSPC|no space left on device|No space left on device|Disk quota exceeded|errno -28' "$combined_log"; then is_disk_full=true fi # Command not found detection if [[ $install_exit_code -eq 127 ]]; then is_cmd_not_found=true fi # Network-related detection (curl/apt/git fetch failures and transient network issues) case "$install_exit_code" in 6 | 7 | 22 | 28 | 35 | 52 | 56 | 57 | 75 | 78) is_network_issue=true ;; 100) # APT can fail due to network (Failed to fetch) if [[ -f "$combined_log" ]] && grep -qiE 'Failed to fetch|Could not resolve|Connection failed|Network is unreachable|Temporary failure resolving' "$combined_log"; then is_network_issue=true fi ;; 128) if [[ -f "$combined_log" ]] && grep -qiE 'RPC failed|early EOF|fetch-pack|HTTP/2 stream|Could not resolve host|Temporary failure resolving|Failed to fetch|Connection reset|Network is unreachable' "$combined_log"; then is_network_issue=true fi ;; esac # Show error explanation if available if [[ -n "$error_explanation" ]]; then echo -e "${TAB}${RD}Error: ${error_explanation}${CL}" echo "" fi # Show specific hints for known error types if [[ $install_exit_code -eq 10 ]]; then echo -e "${TAB}${INFO} This error usually means the container needs ${GN}privileged${CL} mode or Docker/nesting support." echo -e "${TAB}${INFO} Recreate with: Advanced Install → Container Type: ${GN}Privileged${CL}" echo "" fi if [[ $install_exit_code -eq 125 || $install_exit_code -eq 126 ]]; then echo -e "${TAB}${INFO} The command exists but cannot be executed. This may be a ${GN}permission${CL} issue." echo -e "${TAB}${INFO} If using Docker, ensure the container is ${GN}privileged${CL} or has correct permissions." echo "" fi if [[ "$is_disk_full" == true ]]; then echo -e "${TAB}${INFO} The container ran out of disk space during installation (${GN}ENOSPC${CL})." echo -e "${TAB}${INFO} Current disk size: ${GN}${DISK_SIZE} GB${CL}. A rebuild with doubled disk may resolve this." echo "" fi if [[ "$is_cmd_not_found" == true ]]; then local missing_cmd="" if [[ -f "$combined_log" ]]; then missing_cmd=$(grep -oiE '[a-zA-Z0-9_.-]+: command not found' "$combined_log" 2>/dev/null | tail -1 | sed 's/: command not found//') || true fi if [[ -n "$missing_cmd" ]]; then echo -e "${TAB}${INFO} Missing command: ${GN}${missing_cmd}${CL}" fi echo "" fi # Build recovery menu based on error type echo -e "${YW}What would you like to do?${CL}" echo "" echo -e " ${GN}1)${CL} Remove container and exit" echo -e " ${GN}2)${CL} Keep container for debugging" echo -e " ${GN}3)${CL} Retry with verbose mode (full rebuild)" local next_option=4 local APT_OPTION="" OOM_OPTION="" DNS_OPTION="" DISK_OPTION="" if [[ "$is_apt_issue" == true ]]; then if [[ "$var_os" == "alpine" ]]; then echo -e " ${GN}${next_option})${CL} Repair APK state and re-run install (in-place)" else echo -e " ${GN}${next_option})${CL} Repair APT/DPKG state and re-run install (in-place)" fi APT_OPTION=$next_option next_option=$((next_option + 1)) fi if [[ "$is_oom" == true ]]; then local recovery_attempt="${RECOVERY_ATTEMPT:-0}" if [[ $recovery_attempt -lt 2 ]]; then local new_ram=$((RAM_SIZE * 2)) local new_cpu=$((CORE_COUNT * 2)) echo -e " ${GN}${next_option})${CL} Retry with more resources (RAM: ${RAM_SIZE}→${new_ram} MiB, CPU: ${CORE_COUNT}→${new_cpu} cores)" OOM_OPTION=$next_option next_option=$((next_option + 1)) else echo -e " ${DGN}-)${CL} ${DGN}OOM retry exhausted (already retried ${recovery_attempt}x)${CL}" fi fi if [[ "$is_disk_full" == true ]]; then local disk_recovery_attempt="${DISK_RECOVERY_ATTEMPT:-0}" if [[ $disk_recovery_attempt -lt 2 ]]; then local new_disk=$((DISK_SIZE * 2)) echo -e " ${GN}${next_option})${CL} Retry with more disk space (Disk: ${DISK_SIZE}→${new_disk} GB)" DISK_OPTION=$next_option next_option=$((next_option + 1)) else echo -e " ${DGN}-)${CL} ${DGN}Disk resize retry exhausted (already retried ${disk_recovery_attempt}x)${CL}" fi fi if [[ "$is_network_issue" == true ]]; then echo -e " ${GN}${next_option})${CL} Retry with DNS override in LXC (8.8.8.8 / 1.1.1.1)" DNS_OPTION=$next_option next_option=$((next_option + 1)) fi local max_option=$((next_option - 1)) echo "" echo -en "${YW}Select option [1-${max_option}] (default: 1, auto-remove in 60s): ${CL}" local response="" if read -t 60 -r response; then case "${response:-1}" in 1) # Remove container echo -e "\n${TAB}${HOLD}${YW}Removing container ${CTID}${CL}" pct stop "$CTID" &>/dev/null || true pct destroy "$CTID" &>/dev/null || true echo -e "${BFR}${CM}${GN}Container ${CTID} removed${CL}" ;; 2) echo -e "\n${TAB}${YW}Container ${CTID} kept for debugging${CL}" # Dev mode: Setup MOTD/SSH for debugging access to broken container if [[ "${DEV_MODE_MOTD:-false}" == "true" ]]; then echo -e "${TAB}${HOLD}${DGN}Setting up MOTD and SSH for debugging...${CL}" if pct exec "$CTID" -- bash -c " source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/install.func) declare -f motd_ssh >/dev/null 2>&1 && motd_ssh || true " >/dev/null 2>&1; then local ct_ip=$(pct exec "$CTID" ip a s dev eth0 2>/dev/null | awk '/inet / {print $2}' | cut -d/ -f1) echo -e "${BFR}${CM}${GN}MOTD/SSH ready - SSH into container: ssh root@${ct_ip}${CL}" fi fi exit $install_exit_code ;; 3) # Retry with verbose mode (full rebuild) echo -e "\n${TAB}${HOLD}${YW}Removing container ${CTID} for rebuild...${CL}" pct stop "$CTID" &>/dev/null || true pct destroy "$CTID" &>/dev/null || true echo -e "${BFR}${CM}${GN}Container ${CTID} removed${CL}" echo "" # Get new container ID local old_ctid="$CTID" export CTID=$(get_valid_container_id "$CTID") export VERBOSE="yes" export var_verbose="yes" # Show rebuild summary echo -e "${YW}Rebuilding with preserved settings:${CL}" echo -e " Container ID: ${old_ctid} → ${CTID}" echo -e " RAM: ${RAM_SIZE} MiB | CPU: ${CORE_COUNT} cores | Disk: ${DISK_SIZE} GB" echo -e " Network: ${NET:-dhcp} | Bridge: ${BRG:-vmbr0}" echo -e " Verbose: ${GN}enabled${CL}" echo "" msg_info "Restarting installation..." # Re-run build_container build_container return $? ;; *) # Handle dynamic smart recovery options via named option variables local handled=false if [[ -n "${APT_OPTION}" && "${response}" == "${APT_OPTION}" ]]; then # Package manager in-place repair: fix broken state and re-run install script handled=true if [[ "$var_os" == "alpine" ]]; then echo -e "\n${TAB}${HOLD}${YW}Repairing APK state in container ${CTID}...${CL}" pct exec "$CTID" -- ash -c " apk fix 2>/dev/null || true apk cache clean 2>/dev/null || true apk update 2>/dev/null || true " >/dev/null 2>&1 || true echo -e "${BFR}${CM}${GN}APK state repaired in container ${CTID}${CL}" else echo -e "\n${TAB}${HOLD}${YW}Repairing APT/DPKG state in container ${CTID}...${CL}" pct exec "$CTID" -- bash -c " DEBIAN_FRONTEND=noninteractive dpkg --configure -a 2>/dev/null || true apt-get -f install -y 2>/dev/null || true apt-get clean 2>/dev/null apt-get update 2>/dev/null || true " >/dev/null 2>&1 || true echo -e "${BFR}${CM}${GN}APT/DPKG state repaired in container ${CTID}${CL}" fi echo "" export VERBOSE="yes" export var_verbose="yes" echo -e "${YW}Re-running installation in existing container ${CTID}:${CL}" echo -e " RAM: ${RAM_SIZE} MiB | CPU: ${CORE_COUNT} cores | Disk: ${DISK_SIZE} GB" echo -e " Verbose: ${GN}enabled${CL}" echo "" msg_info "Re-running installation script..." # Re-run install script in existing container (don't destroy/recreate) set +Eeuo pipefail trap - ERR lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/install/${var_install}.sh)" local apt_retry_exit=$? set -Eeuo pipefail trap 'error_handler' ERR # Check for error flag from retry local apt_retry_code=0 if [[ -n "${SESSION_ID:-}" ]]; then local retry_error_flag="/root/.install-${SESSION_ID}.failed" if pct exec "$CTID" -- test -f "$retry_error_flag" 2>/dev/null; then apt_retry_code=$(pct exec "$CTID" -- cat "$retry_error_flag" 2>/dev/null || echo "1") pct exec "$CTID" -- rm -f "$retry_error_flag" 2>/dev/null || true fi fi if [[ $apt_retry_code -eq 0 && $apt_retry_exit -ne 0 ]]; then apt_retry_code=$apt_retry_exit fi if [[ $apt_retry_code -eq 0 ]]; then msg_ok "Installation completed successfully after APT repair!" INSTALL_COMPLETE=true post_update_to_api "done" "0" "force" return 0 else msg_error "Installation still failed after APT repair (exit code: ${apt_retry_code})" install_exit_code=$apt_retry_code fi fi if [[ -n "${OOM_OPTION}" && "${response}" == "${OOM_OPTION}" ]]; then # Retry with doubled resources handled=true echo -e "\n${TAB}${HOLD}${YW}Removing container ${CTID} for rebuild with more resources...${CL}" pct stop "$CTID" &>/dev/null || true pct destroy "$CTID" &>/dev/null || true echo -e "${BFR}${CM}${GN}Container ${CTID} removed${CL}" echo "" local old_ctid="$CTID" local old_ram="$RAM_SIZE" local old_cpu="$CORE_COUNT" export CTID=$(get_valid_container_id "$CTID") export RAM_SIZE=$((RAM_SIZE * 2)) export CORE_COUNT=$((CORE_COUNT * 2)) export var_ram="$RAM_SIZE" export var_cpu="$CORE_COUNT" export VERBOSE="yes" export var_verbose="yes" export RECOVERY_ATTEMPT=$((${RECOVERY_ATTEMPT:-0} + 1)) echo -e "${YW}Rebuilding with increased resources (attempt ${RECOVERY_ATTEMPT}/2):${CL}" echo -e " Container ID: ${old_ctid} → ${CTID}" echo -e " RAM: ${old_ram} → ${GN}${RAM_SIZE}${CL} MiB (x2)" echo -e " CPU: ${old_cpu} → ${GN}${CORE_COUNT}${CL} cores (x2)" echo -e " Disk: ${DISK_SIZE} GB | Network: ${NET:-dhcp} | Bridge: ${BRG:-vmbr0}" echo -e " Verbose: ${GN}enabled${CL}" echo "" msg_info "Restarting installation..." build_container return $? fi if [[ -n "${DISK_OPTION}" && "${response}" == "${DISK_OPTION}" ]]; then # Retry with doubled disk size handled=true echo -e "\n${TAB}${HOLD}${YW}Removing container ${CTID} for rebuild with more disk space...${CL}" pct stop "$CTID" &>/dev/null || true pct destroy "$CTID" &>/dev/null || true echo -e "${BFR}${CM}${GN}Container ${CTID} removed${CL}" echo "" local old_ctid="$CTID" local old_disk="$DISK_SIZE" export CTID=$(get_valid_container_id "$CTID") export DISK_SIZE=$((DISK_SIZE * 2)) export var_disk="$DISK_SIZE" export VERBOSE="yes" export var_verbose="yes" export DISK_RECOVERY_ATTEMPT=$((${DISK_RECOVERY_ATTEMPT:-0} + 1)) echo -e "${YW}Rebuilding with increased disk space (attempt ${DISK_RECOVERY_ATTEMPT}/2):${CL}" echo -e " Container ID: ${old_ctid} → ${CTID}" echo -e " Disk: ${old_disk} → ${GN}${DISK_SIZE}${CL} GB (x2)" echo -e " RAM: ${RAM_SIZE} MiB | CPU: ${CORE_COUNT} cores" echo -e " Network: ${NET:-dhcp} | Bridge: ${BRG:-vmbr0}" echo -e " Verbose: ${GN}enabled${CL}" echo "" msg_info "Restarting installation..." build_container return $? fi if [[ -n "${DNS_OPTION}" && "${response}" == "${DNS_OPTION}" ]]; then # Retry with DNS override in LXC handled=true echo -e "\n${TAB}${HOLD}${YW}Removing container ${CTID} for rebuild with DNS override...${CL}" pct stop "$CTID" &>/dev/null || true pct destroy "$CTID" &>/dev/null || true echo -e "${BFR}${CM}${GN}Container ${CTID} removed${CL}" echo "" local old_ctid="$CTID" export CTID=$(get_valid_container_id "$CTID") export DNS_RETRY_OVERRIDE="true" export VERBOSE="yes" export var_verbose="yes" echo -e "${YW}Rebuilding with DNS override in LXC:${CL}" echo -e " Container ID: ${old_ctid} → ${CTID}" echo -e " DNS: ${GN}8.8.8.8, 1.1.1.1${CL} (inside LXC only)" echo -e " Verbose: ${GN}enabled${CL}" echo "" msg_info "Restarting installation..." build_container return $? fi if [[ "$handled" == false ]]; then echo -e "\n${TAB}${YW}Invalid option. Container ${CTID} kept.${CL}" exit $install_exit_code fi ;; esac else # Timeout - auto-remove echo "" msg_info "No response - removing container ${CTID}" pct stop "$CTID" &>/dev/null || true pct destroy "$CTID" &>/dev/null || true msg_ok "Container ${CTID} removed" fi # Force one final status update attempt after cleanup # This ensures status is updated even if the first attempt failed (e.g., HTTP 400) $STD echo -e "${TAB}⏳ Finalizing telemetry report..." post_update_to_api "failed" "$install_exit_code" "force" $STD echo -e "${TAB}${CM:-✔} Telemetry finalized" # Restore default job-control signal handling before exit trap - TSTP TTIN TTOU exit $install_exit_code fi # Re-enable error handling after successful install or recovery menu completion set -Eeuo pipefail trap 'error_handler' ERR } destroy_lxc() { if [[ -z "$CT_ID" ]]; then msg_error "No CT_ID found. Nothing to remove." return 65 fi # Abort on Ctrl-C / Ctrl-D / ESC trap 'echo; msg_error "Aborted by user (SIGINT/SIGQUIT)"; return 130' INT QUIT local prompt if ! read -rp "Remove this Container? " prompt /dev/null && pct destroy "$CT_ID" &>/dev/null; then msg_ok "Removed Container $CT_ID" else msg_error "Failed to remove Container $CT_ID" return 1 fi ;; "" | n | no) msg_custom "ℹ️" "${BL}" "Container was not removed." ;; *) msg_warn "Invalid response. Container was not removed." ;; esac } # ------------------------------------------------------------------------------ # Storage discovery / selection helpers # ------------------------------------------------------------------------------ resolve_storage_preselect() { local class="$1" preselect="$2" required_content="" case "$class" in template) required_content="vztmpl" ;; container) required_content="rootdir" ;; *) return 65 ;; esac [[ -z "$preselect" ]] && return 1 if ! pvesm status -content "$required_content" | awk 'NR>1{print $1}' | grep -qx -- "$preselect"; then msg_warn "Preselected storage '${preselect}' does not support content '${required_content}' (or not found)" return 238 fi local line total used free line="$(pvesm status | awk -v s="$preselect" 'NR>1 && $1==s {print $0}')" if [[ -z "$line" ]]; then STORAGE_INFO="n/a" else total="$(awk '{print $4}' <<<"$line")" used="$(awk '{print $5}' <<<"$line")" free="$(awk '{print $6}' <<<"$line")" local total_h used_h free_h if command -v numfmt >/dev/null 2>&1; then total_h="$(numfmt --to=iec --from-unit=1024 --suffix=B --format %.1f "$total" 2>/dev/null || echo "$total")" used_h="$(numfmt --to=iec --from-unit=1024 --suffix=B --format %.1f "$used" 2>/dev/null || echo "$used")" free_h="$(numfmt --to=iec --from-unit=1024 --suffix=B --format %.1f "$free" 2>/dev/null || echo "$free")" STORAGE_INFO="Free: ${free_h} Used: ${used_h}" else STORAGE_INFO="Free: ${free} Used: ${used}" fi fi STORAGE_RESULT="$preselect" return 0 } fix_gpu_gids() { if [[ -z "${GPU_TYPE:-}" ]]; then return 0 fi msg_info "Detecting and setting correct GPU group IDs" # Get actual GIDs from container local video_gid=$(pct exec "$CTID" -- sh -c "getent group video 2>/dev/null | cut -d: -f3") local render_gid=$(pct exec "$CTID" -- sh -c "getent group render 2>/dev/null | cut -d: -f3") # Create groups if they don't exist if [[ -z "$video_gid" ]]; then pct exec "$CTID" -- sh -c "groupadd -r video 2>/dev/null || true" >/dev/null 2>&1 video_gid=$(pct exec "$CTID" -- sh -c "getent group video 2>/dev/null | cut -d: -f3") [[ -z "$video_gid" ]] && video_gid="44" fi if [[ -z "$render_gid" ]]; then pct exec "$CTID" -- sh -c "groupadd -r render 2>/dev/null || true" >/dev/null 2>&1 render_gid=$(pct exec "$CTID" -- sh -c "getent group render 2>/dev/null | cut -d: -f3") [[ -z "$render_gid" ]] && render_gid="104" fi # Stop container to update config pct stop "$CTID" >/dev/null 2>&1 sleep 1 # Validate GIDs are numeric before sed [[ "$render_gid" =~ ^[0-9]+$ ]] || render_gid="104" [[ "$video_gid" =~ ^[0-9]+$ ]] || video_gid="44" # Update dev entries with correct GIDs sed -i.bak -E "s|(dev[0-9]+: /dev/dri/renderD[0-9]+),gid=[0-9]+|\1,gid=${render_gid}|g" "$LXC_CONFIG" sed -i -E "s|(dev[0-9]+: /dev/dri/card[0-9]+),gid=[0-9]+|\1,gid=${video_gid}|g" "$LXC_CONFIG" # Restart container pct start "$CTID" >/dev/null 2>&1 sleep 2 msg_ok "GPU passthrough configured (video:${video_gid}, render:${render_gid})" # For privileged containers: also fix permissions inside container if [[ "$CT_TYPE" == "0" ]]; then pct exec "$CTID" -- sh -c " if [ -d /dev/dri ]; then for dev in /dev/dri/*; do if [ -e \"\$dev\" ]; then case \"\$dev\" in *renderD*) chgrp ${render_gid} \"\$dev\" 2>/dev/null || true ;; *) chgrp ${video_gid} \"\$dev\" 2>/dev/null || true ;; esac chmod 660 \"\$dev\" 2>/dev/null || true fi done fi " >/dev/null 2>&1 fi } check_storage_support() { local CONTENT="$1" VALID=0 while IFS= read -r line; do local STORAGE_NAME STORAGE_NAME=$(awk '{print $1}' <<<"$line") [[ -n "$STORAGE_NAME" ]] && VALID=1 done < <(pvesm status -content "$CONTENT" 2>/dev/null | awk 'NR>1') [[ $VALID -eq 1 ]] } select_storage() { local CLASS=$1 CONTENT CONTENT_LABEL case $CLASS in container) CONTENT='rootdir' CONTENT_LABEL='Container' ;; template) CONTENT='vztmpl' CONTENT_LABEL='Container template' ;; iso) CONTENT='iso' CONTENT_LABEL='ISO image' ;; images) CONTENT='images' CONTENT_LABEL='VM Disk image' ;; backup) CONTENT='backup' CONTENT_LABEL='Backup' ;; snippets) CONTENT='snippets' CONTENT_LABEL='Snippets' ;; *) msg_error "Invalid storage class '$CLASS'" return 65 ;; esac declare -A STORAGE_MAP local -a MENU=() local COL_WIDTH=0 while read -r TAG TYPE _ TOTAL USED FREE _; do [[ -n "$TAG" && -n "$TYPE" ]] || continue local DISPLAY="${TAG} (${TYPE})" local USED_FMT=$(numfmt --to=iec --from-unit=1024 --format %.1f <<<"$USED") local FREE_FMT=$(numfmt --to=iec --from-unit=1024 --format %.1f <<<"$FREE") local INFO="Free: ${FREE_FMT}B Used: ${USED_FMT}B" STORAGE_MAP["$DISPLAY"]="$TAG" MENU+=("$DISPLAY" "$INFO" "OFF") ((${#DISPLAY} > COL_WIDTH)) && COL_WIDTH=${#DISPLAY} done < <(pvesm status -content "$CONTENT" | awk 'NR>1') if [[ ${#MENU[@]} -eq 0 ]]; then msg_error "No storage found for content type '$CONTENT'." return 2 fi if [[ $((${#MENU[@]} / 3)) -eq 1 ]]; then STORAGE_RESULT="${STORAGE_MAP[${MENU[0]}]}" STORAGE_INFO="${MENU[1]}" return 0 fi local WIDTH=$((COL_WIDTH + 42)) while true; do local DISPLAY_SELECTED DISPLAY_SELECTED=$(whiptail --backtitle "Proxmox VE Helper Scripts" \ --title "Storage Pools" \ --radiolist "Which storage pool for ${CONTENT_LABEL,,}?\n(Spacebar to select)" \ 16 "$WIDTH" 6 "${MENU[@]}" 3>&1 1>&2 2>&3) || { exit_script; } DISPLAY_SELECTED=$(sed 's/[[:space:]]*$//' <<<"$DISPLAY_SELECTED") if [[ -z "$DISPLAY_SELECTED" || -z "${STORAGE_MAP[$DISPLAY_SELECTED]+_}" ]]; then whiptail --msgbox "No valid storage selected. Please try again." 8 58 continue fi STORAGE_RESULT="${STORAGE_MAP[$DISPLAY_SELECTED]}" for ((i = 0; i < ${#MENU[@]}; i += 3)); do if [[ "${MENU[$i]}" == "$DISPLAY_SELECTED" ]]; then STORAGE_INFO="${MENU[$i + 1]}" break fi done # Validate storage space for container storage if [[ "$CLASS" == "container" && -n "${DISK_SIZE:-}" ]]; then validate_storage_space "$STORAGE_RESULT" "$DISK_SIZE" "yes" # Continue even if validation fails - user was warned fi return 0 done } # ------------------------------------------------------------------------------ # validate_storage_space() # # - Validates if storage has enough free space for container # - Takes storage name and required size in GB # - Returns 0 if enough space, 1 if not enough, 2 if storage unavailable # - Can optionally show whiptail warning # - Handles all storage types: dir, lvm, lvmthin, zfs, nfs, cifs, etc. # ------------------------------------------------------------------------------ validate_storage_space() { local storage="$1" local required_gb="${2:-8}" local show_dialog="${3:-no}" # Get full storage line from pvesm status local storage_line storage_line=$(pvesm status 2>/dev/null | awk -v s="$storage" '$1 == s {print $0}') # Check if storage exists and is active if [[ -z "$storage_line" ]]; then [[ "$show_dialog" == "yes" ]] && whiptail --msgbox "⚠️ Warning: Storage '$storage' not found!\n\nThe storage may be unavailable or disabled." 10 60 return 236 fi # Check storage status (column 3) local status status=$(awk '{print $3}' <<<"$storage_line") if [[ "$status" == "disabled" ]]; then [[ "$show_dialog" == "yes" ]] && whiptail --msgbox "⚠️ Warning: Storage '$storage' is disabled!\n\nPlease enable the storage first." 10 60 return 236 fi # Get storage type and free space (column 6) local storage_type storage_free storage_type=$(awk '{print $2}' <<<"$storage_line") storage_free=$(awk '{print $6}' <<<"$storage_line") # Some storage types (like PBS, iSCSI) don't report size info # In these cases, skip space validation if [[ -z "$storage_free" || "$storage_free" == "0" ]]; then # Silent pass for storages without size info return 0 fi local required_kb=$((required_gb * 1024 * 1024)) local free_gb_fmt free_gb_fmt=$(numfmt --to=iec --from-unit=1024 --suffix=B --format %.1f "$storage_free" 2>/dev/null || echo "${storage_free}KB") if [[ "$storage_free" -lt "$required_kb" ]]; then if [[ "$show_dialog" == "yes" ]]; then whiptail --msgbox "⚠️ Warning: Storage '$storage' may not have enough space!\n\nStorage Type: ${storage_type}\nRequired: ${required_gb}GB\nAvailable: ${free_gb_fmt}\n\nYou can continue, but creation might fail." 14 70 fi return 236 fi return 0 } # ============================================================================== # SECTION 8: CONTAINER CREATION # ============================================================================== # ------------------------------------------------------------------------------ # create_lxc_container() # # - Main function for creating LXC containers # - Handles all phases: validation, template discovery, container creation, # network config, storage, etc. # - Extensive error checking with detailed exit codes # ------------------------------------------------------------------------------ create_lxc_container() { # ------------------------------------------------------------------------------ # Optional verbose mode (debug tracing) # ------------------------------------------------------------------------------ if [[ "${CREATE_LXC_VERBOSE:-no}" == "yes" ]]; then set -x; fi # ------------------------------------------------------------------------------ # Helpers (dynamic versioning / template parsing) # ------------------------------------------------------------------------------ pkg_ver() { dpkg-query -W -f='${Version}\n' "$1" 2>/dev/null || echo ""; } pkg_cand() { apt-cache policy "$1" 2>/dev/null | awk '/Candidate:/ {print $2}'; } ver_ge() { dpkg --compare-versions "$1" ge "$2"; } ver_gt() { dpkg --compare-versions "$1" gt "$2"; } ver_lt() { dpkg --compare-versions "$1" lt "$2"; } # Extract Debian OS minor from template name: debian-13-standard_13.1-1_amd64.tar.zst => "13.1" parse_template_osver() { sed -n 's/.*_\([0-9][0-9]*\(\.[0-9]\+\)\?\)-.*/\1/p' <<<"$1"; } # Switch to the previous OS major version template and retry pct create # Determines the fallback version automatically based on available templates. # Returns: 0 = success, 1 = failed fallback_to_previous_os_version() { local old_template="$TEMPLATE" local os_type="${PCT_OSTYPE:-}" local current_ver="${PCT_OSVERSION:-}" # Determine template search pattern based on OS type local tpl_pattern="" case "$os_type" in debian | ubuntu) tpl_pattern="-standard_" ;; alpine | fedora | rocky | centos) tpl_pattern="-default_" ;; *) tpl_pattern="" ;; esac msg_info "Searching for an older $os_type template (current: $os_type $current_ver)" # Collect all available versions for this OS type (local + online) local -a all_versions=() # Local templates mapfile -t _local_vers < <( pveam list "$TEMPLATE_STORAGE" 2>/dev/null | awk -v os="$os_type" -v pat="$tpl_pattern" '$1 ~ ("^"os"|/"os) && $1 ~ pat {print $1}' | sed 's|.*/||' | sed -E "s/^${os_type}-([0-9]+(\.[0-9]+)?).*/\1/" | sort -u -V ) all_versions+=("${_local_vers[@]}") # Online templates (only if needed) if command -v timeout &>/dev/null; then timeout 30 pveam update >/dev/null 2>&1 || true else pveam update >/dev/null 2>&1 || true fi mapfile -t _online_vers < <( pveam available -section system 2>/dev/null | awk '{print $2}' | grep -E "^${os_type}-[0-9]" | { [[ -n "$tpl_pattern" ]] && grep "$tpl_pattern" || cat; } | sed -E "s/^${os_type}-([0-9]+(\.[0-9]+)?).*/\1/" | sort -u -V 2>/dev/null || true ) all_versions+=("${_online_vers[@]}") # Deduplicate and sort, find the highest version below current local fallback_ver="" fallback_ver=$(printf '%s\n' "${all_versions[@]}" | sort -u -V | awk -v cur="$current_ver" '{ # Compare major versions: extract major part split($0, a, ".") split(cur, b, ".") if (a[1]+0 < b[1]+0) ver=$0 } END { if (ver) print ver }') if [[ -z "$fallback_ver" ]]; then msg_error "No older $os_type template version found." return 1 fi msg_ok "Fallback version: $os_type $fallback_ver" # Find the actual template file for this version local fallback_search="${os_type}-${fallback_ver}" local fallback_template="" # Check local first mapfile -t _fb_local < <( pveam list "$TEMPLATE_STORAGE" 2>/dev/null | awk -v search="$fallback_search" -v pat="$tpl_pattern" '$1 ~ search && $1 ~ pat {print $1}' | sed 's|.*/||' | sort -t - -k 2 -V ) if [[ ${#_fb_local[@]} -gt 0 ]]; then fallback_template="${_fb_local[-1]}" else # Check online mapfile -t _fb_online < <( pveam available -section system 2>/dev/null | awk '{print $2}' | grep -E "^${fallback_search}.*${tpl_pattern}" | sort -t - -k 2 -V 2>/dev/null || true ) [[ ${#_fb_online[@]} -gt 0 ]] && fallback_template="${_fb_online[-1]}" fi if [[ -z "$fallback_template" ]]; then msg_error "No template found for $os_type $fallback_ver." return 1 fi msg_ok "Found template: $fallback_template" # Download if needed local fallback_path fallback_path="$(pvesm path "$TEMPLATE_STORAGE:vztmpl/$fallback_template" 2>/dev/null || true)" [[ -z "$fallback_path" ]] && fallback_path="/var/lib/vz/template/cache/$fallback_template" if [[ ! -f "$fallback_path" ]]; then msg_info "Downloading $os_type $fallback_ver template" if ! pveam download "$TEMPLATE_STORAGE" "$fallback_template" >>"${BUILD_LOG:-/dev/null}" 2>&1; then msg_error "Failed to download $os_type $fallback_ver template." return 1 fi msg_ok "Template downloaded" fi # Update variables TEMPLATE="$fallback_template" TEMPLATE_PATH="$fallback_path" PCT_OSVERSION="$fallback_ver" export PCT_OSVERSION # Retry pct create msg_info "Retrying container creation with $os_type $fallback_ver" if pct create "$CTID" "${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE}" $PCT_OPTIONS >>"$LOGFILE" 2>&1; then msg_ok "Container created successfully with $os_type $fallback_ver (fallback from $old_template)." return 0 else msg_error "Container creation with $os_type $fallback_ver also failed. See $LOGFILE" return 1 fi } # Offer upgrade for pve-container/lxc-pve if candidate > installed; optional auto-retry pct create # Returns: # 0 = no upgrade needed / container created after upgrade or explicit fallback # 1 = upgraded (and if do_retry=yes and retry succeeded, creation done) # 2 = user chose ignore # 3 = upgrade attempted but failed OR retry failed # 4 = user cancelled offer_lxc_stack_upgrade_and_maybe_retry() { local do_retry="${1:-no}" # yes|no local _pvec_i _pvec_c _lxcp_i _lxcp_c need=0 local _ans has_previous_os_version_template() { local os_type="${PCT_OSTYPE:-}" local current_ver="${PCT_OSVERSION:-}" local tpl_pattern="${TEMPLATE_PATTERN:-${TEMPLATE:-}}" local -a all_versions=() [[ -n "$os_type" && -n "$current_ver" ]] || return 1 mapfile -t _local_vers < <( pveam list "$TEMPLATE_STORAGE" 2>/dev/null | awk '{print $1}' | sed 's|.*/||' | grep -E "^${os_type}-[0-9]" | { [[ -n "$tpl_pattern" ]] && grep "$tpl_pattern" || cat; } | sed -E "s/^${os_type}-([0-9]+(\.[0-9]+)?).*/\1/" | sort -u -V ) all_versions+=("${_local_vers[@]}") if command -v timeout &>/dev/null; then timeout 30 pveam update >/dev/null 2>&1 || true else pveam update >/dev/null 2>&1 || true fi mapfile -t _online_vers < <( pveam available -section system 2>/dev/null | awk '{print $2}' | grep -E "^${os_type}-[0-9]" | { [[ -n "$tpl_pattern" ]] && grep "$tpl_pattern" || cat; } | sed -E "s/^${os_type}-([0-9]+(\.[0-9]+)?).*/\1/" | sort -u -V 2>/dev/null || true ) all_versions+=("${_online_vers[@]}") printf '%s\n' "${all_versions[@]}" | sort -u -V | awk -v cur="$current_ver" ' { split($0, a, ".") split(cur, b, ".") if (a[1]+0 < b[1]+0) found=1 } END { exit found ? 0 : 1 } ' } _pvec_i="$(pkg_ver pve-container)" _lxcp_i="$(pkg_ver lxc-pve)" _pvec_c="$(pkg_cand pve-container)" _lxcp_c="$(pkg_cand lxc-pve)" if [[ -n "$_pvec_c" && "$_pvec_c" != "none" ]]; then ver_gt "$_pvec_c" "${_pvec_i:-0}" && need=1 fi if [[ -n "$_lxcp_c" && "$_lxcp_c" != "none" ]]; then ver_gt "$_lxcp_c" "${_lxcp_i:-0}" && need=1 fi if [[ $need -eq 0 ]]; then msg_debug "No newer candidate for pve-container/lxc-pve (installed=$_pvec_i/$_lxcp_i, cand=$_pvec_c/$_lxcp_c)" return 0 fi msg_warn "An update for the Proxmox LXC stack is available" echo " pve-container: installed=${_pvec_i:-n/a} candidate=${_pvec_c:-n/a}" echo " lxc-pve : installed=${_lxcp_i:-n/a} candidate=${_lxcp_c:-n/a}" echo " note : option 1 runs host apt update + apt upgrade" echo # Offer older OS version fallback when template version might be too new for LXC stack local _has_fallback_option=false if [[ "$do_retry" == "yes" ]] && has_previous_os_version_template; then _has_fallback_option=true echo " [1] Run host upgrade now (recommended). WARNING: this runs apt upgrade and updates all Packages on your host!" echo " [2] Use an older ${PCT_OSTYPE} template instead (may not work with all scripts)" echo " [3] Ignore" echo " [4] Cancel" echo read -rp "Select option [1/2/3/4]: " _ans /dev/null; then msg_error "LXC stack upgrade caused PVE tool breakage (likely Perl module incompatibility)." msg_custom "⚠️" "${YW}" "A partial package upgrade has left the PVE stack in an inconsistent state." msg_custom "🔧" "${YW}" "Please run the following on the Proxmox host, then retry:" echo -e "${TAB} apt update && apt upgrade -y" echo -e "${TAB} reboot" return 3 fi if [[ "$do_retry" == "yes" ]]; then msg_info "Retrying container creation after upgrade" if pct create "$CTID" "${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE}" $PCT_OPTIONS >>"$LOGFILE" 2>&1; then msg_ok "Container created successfully after upgrade." return 0 else msg_error "pct create still failed after upgrade. See $LOGFILE" return 3 fi fi return 1 else msg_error "Upgrade failed. Please check APT output." return 3 fi ;; *) return 2 ;; esac } resolve_template_storage_path() { local storage="$1" local name="$2" local path="" path=$(pvesm path "${storage}:vztmpl/${name}" 2>/dev/null || true) if [[ -z "$path" ]]; then local storage_base storage_base=$(awk -v s="$storage" '$1==s {f=1} f && /path/ {print $2; exit}' /etc/pve/storage.cfg) if [[ -n "$storage_base" ]]; then path="${storage_base%/}/template/cache/$name" fi fi if [[ -z "$path" ]]; then path="/var/lib/vz/template/cache/$name" fi echo "$path" } arm64_template_variant() { local os="$1" local version="$2" case "$os" in debian) case "$version" in 11 | 11.*) echo "bullseye" ;; 12 | 12.*) echo "bookworm" ;; 13 | 13.*) echo "trixie" ;; *) echo "trixie" ;; esac ;; alpine) echo "3.22" ;; ubuntu) case "$version" in 20.04* | focal) echo "focal" ;; 24.04* | noble) echo "noble" ;; 24.10* | oracular) echo "oracular" ;; *) echo "jammy" ;; esac ;; *) echo "" return 1 ;; esac } download_arm64_template() { local os="$1" local variant="$2" local dest="$3" local dest_dir dest_dir=$(dirname "$dest") mkdir -p "$dest_dir" || { msg_error "Unable to create template directory '$dest_dir'." exit 207 } if [[ "$os" == "debian" ]]; then local api_url="https://api.github.com/repos/asylumexp/debian-ifupdown2-lxc/releases/latest" local release_json download_url release_json=$(curl -fsSL "$api_url") || { msg_error "Unable to query Debian template release metadata." exit 207 } download_url=$(grep -Eo "https://[^\"]*debian-${variant}-arm64-rootfs\.tar\.xz" <<<"$release_json" | head -n1) if [[ -z "$download_url" ]]; then msg_error "Could not find Debian ${variant} ARM64 template download URL." exit 207 fi msg_info "Downloading Debian ${variant} ARM64 template" if ! wget -q "$download_url" -O "$dest"; then msg_error "A problem occurred while downloading the Debian ARM64 template." exit 208 fi else local template_url="https://jenkins.linuxcontainers.org/job/image-${os}/architecture=arm64,release=${variant},variant=default/lastStableBuild/artifact/rootfs.tar.xz" msg_info "Downloading ${os^} ${variant} ARM64 template" if ! wget -q "$template_url" -O "$dest"; then msg_error "A problem occurred while downloading the ${os} ARM64 template." exit 208 fi fi msg_ok "Downloaded LXC Template" } # ------------------------------------------------------------------------------ # Required input variables # ------------------------------------------------------------------------------ [[ "${CTID:-}" ]] || { msg_error "You need to set 'CTID' variable." exit 203 } [[ "${PCT_OSTYPE:-}" ]] || { msg_error "You need to set 'PCT_OSTYPE' variable." exit 204 } msg_debug "CTID=$CTID" msg_debug "PCT_OSTYPE=$PCT_OSTYPE" msg_debug "PCT_OSVERSION=${PCT_OSVERSION:-default}" # ID checks [[ "$CTID" -ge 100 ]] || { msg_error "ID cannot be less than 100." exit 205 } if qm status "$CTID" &>/dev/null || pct status "$CTID" &>/dev/null; then msg_warn "Container/VM ID $CTID is already in use (detected late). Reassigning..." CTID=$(get_valid_container_id "$((CTID + 1))") export CTID msg_ok "Reassigned to container ID $CTID" fi # Report installation start to API early - captures failures in storage/template/create post_to_api # Transition to 'validation' — Proxmox-internal checks (storage, template, cluster) post_progress_to_api "validation" # Storage capability check check_storage_support "rootdir" || { msg_error "No valid storage found for 'rootdir' [Container]" exit 119 } check_storage_support "vztmpl" || { msg_error "No valid storage found for 'vztmpl' [Template]" exit 120 } # Template storage selection if resolve_storage_preselect template "${TEMPLATE_STORAGE:-}"; then TEMPLATE_STORAGE="$STORAGE_RESULT" TEMPLATE_STORAGE_INFO="$STORAGE_INFO" msg_ok "Storage ${BL}${TEMPLATE_STORAGE}${CL} (${TEMPLATE_STORAGE_INFO}) [Template]" else while true; do if [[ -z "${var_template_storage:-}" ]]; then if select_storage template; then TEMPLATE_STORAGE="$STORAGE_RESULT" TEMPLATE_STORAGE_INFO="$STORAGE_INFO" msg_ok "Storage ${BL}${TEMPLATE_STORAGE}${CL} (${TEMPLATE_STORAGE_INFO}) [Template]" break fi fi done fi # Container storage selection if resolve_storage_preselect container "${CONTAINER_STORAGE:-}"; then CONTAINER_STORAGE="$STORAGE_RESULT" CONTAINER_STORAGE_INFO="$STORAGE_INFO" msg_ok "Storage ${BL}${CONTAINER_STORAGE}${CL} (${CONTAINER_STORAGE_INFO}) [Container]" else if [[ -z "${var_container_storage:-}" ]]; then if select_storage container; then CONTAINER_STORAGE="$STORAGE_RESULT" CONTAINER_STORAGE_INFO="$STORAGE_INFO" msg_ok "Storage ${BL}${CONTAINER_STORAGE}${CL} (${CONTAINER_STORAGE_INFO}) [Container]" fi fi fi msg_info "Validating storage '$CONTAINER_STORAGE'" # Check if storage.cfg is accessible (pmxcfs must be mounted) if [[ ! -f /etc/pve/storage.cfg ]]; then if ! mountpoint -q /etc/pve 2>/dev/null; then msg_error "Proxmox cluster filesystem (pmxcfs) is not mounted at /etc/pve." msg_custom "🔧" "${YW}" "Try: systemctl restart pve-cluster" else msg_error "/etc/pve/storage.cfg does not exist." msg_custom "🔧" "${YW}" "Check Proxmox cluster filesystem integrity: pvecm status" fi exit 213 fi STORAGE_TYPE=$(grep -E "^[^:]+:[[:space:]]*$CONTAINER_STORAGE[[:space:]]*$" /etc/pve/storage.cfg | cut -d: -f1 | head -1 || true) # Fallback: use pvesm status to determine storage type if [[ -z "$STORAGE_TYPE" ]]; then STORAGE_TYPE=$(pvesm status -storage "$CONTAINER_STORAGE" 2>/dev/null | awk 'NR>1{print $2}') fi if [[ -z "$STORAGE_TYPE" ]]; then msg_error "Storage '$CONTAINER_STORAGE' not found in /etc/pve/storage.cfg" msg_custom "📋" "${YW}" "Available storages: $(pvesm status 2>/dev/null | awk 'NR>1{printf "%s (%s) ", $1, $2}' || echo 'n/a')" if [[ -r /etc/pve/storage.cfg ]]; then msg_custom "📋" "${YW}" "Storage definitions found in config:" grep -E '^[a-z]+:' /etc/pve/storage.cfg 2>/dev/null | while IFS= read -r _line; do echo "${TAB} $_line" done fi msg_custom "📖" "${YW}" "See https://pve.proxmox.com/wiki/Storage for storage configuration details." exit 213 fi case "$STORAGE_TYPE" in iscsidirect) msg_error "Storage '$CONTAINER_STORAGE' uses iSCSI-direct which does not support container rootfs." exit 212 ;; iscsi | zfs) msg_error "Storage '$CONTAINER_STORAGE' ($STORAGE_TYPE) does not support container rootdir content." exit 213 ;; cephfs) msg_error "Storage '$CONTAINER_STORAGE' uses CephFS which is not supported for LXC rootfs." exit 219 ;; pbs) msg_error "Storage '$CONTAINER_STORAGE' is a Proxmox Backup Server — cannot be used for containers." exit 224 ;; linstor | rbd | nfs | cifs) if ! pvesm status -storage "$CONTAINER_STORAGE" &>/dev/null; then msg_error "Storage '$CONTAINER_STORAGE' ($STORAGE_TYPE) is not accessible or inactive." exit 217 fi ;; esac if ! pvesm status -content rootdir 2>/dev/null | awk 'NR>1{print $1}' | grep -qx "$CONTAINER_STORAGE"; then msg_error "Storage '$CONTAINER_STORAGE' ($STORAGE_TYPE) does not support 'rootdir' content." msg_custom "💡" "${YW}" "Enable 'Disk image' (rootdir) for storage '${CONTAINER_STORAGE}' in:" msg_custom " " "${YW}" "Datacenter → Storage → ${CONTAINER_STORAGE} → Edit → Content" msg_custom "📖" "${YW}" "See: https://pve.proxmox.com/wiki/Storage" msg_custom "🔗" "${YW}" "Help: https://github.com/community-scripts/ProxmoxVE/discussions" exit 213 fi msg_ok "Storage '$CONTAINER_STORAGE' ($STORAGE_TYPE) validated" msg_info "Validating template storage '$TEMPLATE_STORAGE'" TEMPLATE_TYPE=$(grep -E "^[^:]+: $TEMPLATE_STORAGE$" /etc/pve/storage.cfg | cut -d: -f1 || true) if ! pvesm status -content vztmpl 2>/dev/null | awk 'NR>1{print $1}' | grep -qx "$TEMPLATE_STORAGE"; then msg_warn "Template storage '$TEMPLATE_STORAGE' may not support 'vztmpl'" fi msg_ok "Template storage '$TEMPLATE_STORAGE' validated" # Cluster quorum (if cluster) if [[ -f /etc/pve/corosync.conf ]]; then msg_info "Checking cluster quorum" if ! pvecm status | awk -F':' '/^Quorate/ { exit ($2 ~ /Yes/) ? 0 : 1 }'; then msg_error "Cluster is not quorate. Start all nodes or configure quorum device (QDevice)." exit 210 fi msg_ok "Cluster is quorate" fi # ------------------------------------------------------------------------------ # Template discovery & validation (ARM64 custom templates) # ------------------------------------------------------------------------------ msg_info "Preparing template" CUSTOM_TEMPLATE_VARIANT=$(arm64_template_variant "$PCT_OSTYPE" "${PCT_OSVERSION:-}") if [[ -z "$CUSTOM_TEMPLATE_VARIANT" ]]; then msg_error "No template mapping for ${PCT_OSTYPE} ${PCT_OSVERSION:-latest}" exit 207 fi TEMPLATE="${PCT_OSTYPE}-${CUSTOM_TEMPLATE_VARIANT}-rootfs.tar.xz" TEMPLATE_PATH="$(resolve_template_storage_path "$TEMPLATE_STORAGE" "$TEMPLATE")" TEMPLATE_SOURCE="custom-arm64" if [[ ! -f "$TEMPLATE_PATH" ]]; then download_arm64_template "$PCT_OSTYPE" "$CUSTOM_TEMPLATE_VARIANT" "$TEMPLATE_PATH" else msg_ok "Template ${BL}$TEMPLATE${CL} found locally." fi if [[ "$(stat -c%s "$TEMPLATE_PATH")" -lt 1000000 ]]; then msg_warn "Template file too small – re-downloading." rm -f "$TEMPLATE_PATH" download_arm64_template "$PCT_OSTYPE" "$CUSTOM_TEMPLATE_VARIANT" "$TEMPLATE_PATH" fi if ! tar -tf "$TEMPLATE_PATH" &>/dev/null; then msg_warn "Template appears corrupted – re-downloading." rm -f "$TEMPLATE_PATH" download_arm64_template "$PCT_OSTYPE" "$CUSTOM_TEMPLATE_VARIANT" "$TEMPLATE_PATH" fi msg_ok "Template ${BL}$TEMPLATE${CL} [$TEMPLATE_SOURCE]" msg_debug "Resolved TEMPLATE_PATH=$TEMPLATE_PATH" # ------------------------------------------------------------------------------ # Dynamic preflight for Debian 13.x: offer upgrade if available (no hard mins) # ------------------------------------------------------------------------------ if [[ "$PCT_OSTYPE" == "debian" ]]; then OSVER="$(parse_template_osver "$TEMPLATE")" if [[ -n "$OSVER" ]]; then offer_lxc_stack_upgrade_and_maybe_retry "no" || true fi fi # ------------------------------------------------------------------------------ # Create LXC Container # ------------------------------------------------------------------------------ msg_info "Creating LXC container" # Ensure subuid/subgid entries exist grep -q "root:100000:65536" /etc/subuid || echo "root:100000:65536" >>/etc/subuid grep -q "root:100000:65536" /etc/subgid || echo "root:100000:65536" >>/etc/subgid # PCT_OPTIONS is now a string (exported from build_container) # Add rootfs if not already specified if [[ ! "$PCT_OPTIONS" =~ "-rootfs" ]]; then PCT_OPTIONS="$PCT_OPTIONS -rootfs $CONTAINER_STORAGE:${PCT_DISK_SIZE:-8}" fi # Lock by template file (avoid concurrent template downloads/validation) lockfile="/tmp/template.${TEMPLATE}.lock" # Cleanup stale lock files (older than 1 hour - likely from crashed processes) if [[ -f "$lockfile" ]]; then local lock_age=$(($(date +%s) - $(stat -c %Y "$lockfile" 2>/dev/null || echo 0))) if [[ $lock_age -gt 3600 ]]; then msg_warn "Removing stale template lock file (age: ${lock_age}s)" rm -f "$lockfile" fi fi exec 9>"$lockfile" || { msg_error "Failed to create lock file '$lockfile'." exit 200 } # Retry logic for template lock (another container creation may be running) local lock_attempts=0 local max_lock_attempts=10 local lock_wait_time=30 while ! flock -w "$lock_wait_time" 9; do lock_attempts=$((lock_attempts + 1)) if [[ $lock_attempts -ge $max_lock_attempts ]]; then msg_error "Timeout while waiting for template lock after ${max_lock_attempts} attempts." msg_custom "💡" "${YW}" "Another container creation may be stuck. Check running processes or remove: $lockfile" exit 211 fi msg_custom "⏳" "${YW}" "Another container is being created with this template. Waiting... (attempt ${lock_attempts}/${max_lock_attempts})" done LOGFILE="/tmp/pct_create_${CTID}_$(date +%Y%m%d_%H%M%S)_${SESSION_ID}.log" # Helper: append pct_create log to BUILD_LOG before exit so combined log has full context _flush_pct_log() { if [[ -s "${LOGFILE:-}" && -n "${BUILD_LOG:-}" ]]; then { echo "" echo "--- pct create output (${LOGFILE}) ---" cat "$LOGFILE" echo "--- end pct create output ---" } >>"$BUILD_LOG" 2>/dev/null || true fi } # Validate template before pct create (while holding lock) if [[ ! -s "$TEMPLATE_PATH" || "$(stat -c%s "$TEMPLATE_PATH" 2>/dev/null || echo 0)" -lt 1000000 ]]; then msg_info "Template file missing or too small – downloading" rm -f "$TEMPLATE_PATH" download_arm64_template "$PCT_OSTYPE" "$CUSTOM_TEMPLATE_VARIANT" "$TEMPLATE_PATH" msg_ok "Template downloaded" elif ! tar -tf "$TEMPLATE_PATH" &>/dev/null; then msg_info "Template appears corrupted – re-downloading" rm -f "$TEMPLATE_PATH" download_arm64_template "$PCT_OSTYPE" "$CUSTOM_TEMPLATE_VARIANT" "$TEMPLATE_PATH" msg_ok "Template re-downloaded" fi # Release lock after template validation - pct create has its own internal locking exec 9>&- msg_debug "pct create command: pct create $CTID ${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE} $PCT_OPTIONS" msg_debug "Logfile: $LOGFILE" # First attempt (PCT_OPTIONS is a multi-line string, use it directly) # Disable globbing: unquoted $PCT_OPTIONS needs word-splitting but must not glob-expand # (e.g. passwords containing * or ? would match filenames otherwise) set -f if ! pct create "$CTID" "${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE}" $PCT_OPTIONS >"$LOGFILE" 2>&1; then msg_debug "Container creation failed on ${TEMPLATE_STORAGE}. Checking error..." # Check for Perl module breakage (partial PVE upgrade) if grep -qiE 'Compilation failed|Bareword.*not allowed' "$LOGFILE"; then msg_error "Container creation failed due to broken Perl modules on the PVE host." msg_custom "⚠️" "${YW}" "This usually happens after a partial PVE package upgrade." msg_custom "🔧" "${YW}" "Please run the following on the Proxmox host, then retry:" echo -e "${TAB} apt update && apt dist-upgrade -y" echo -e "${TAB} reboot" _flush_pct_log exit 232 fi # Check if CTID collision (race condition: ID claimed between validation and creation) if grep -qiE 'already exists|already in use' "$LOGFILE"; then local old_ctid="$CTID" CTID=$(get_valid_container_id "$((CTID + 1))") export CTID msg_warn "Container ID $old_ctid was claimed by another process. Retrying with ID $CTID" LOGFILE="/tmp/pct_create_${CTID}_$(date +%Y%m%d_%H%M%S)_${SESSION_ID}.log" if pct create "$CTID" "${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE}" $PCT_OPTIONS >"$LOGFILE" 2>&1; then msg_ok "Container successfully created with new ID $CTID" else msg_error "Container creation failed even with new ID $CTID. See $LOGFILE" _flush_pct_log exit 209 fi else # Not a CTID collision - check if template issue and retry with fresh download if grep -qiE 'unable to open|corrupt|invalid' "$LOGFILE"; then msg_info "Template may be corrupted – re-downloading" rm -f "$TEMPLATE_PATH" download_arm64_template "$PCT_OSTYPE" "$CUSTOM_TEMPLATE_VARIANT" "$TEMPLATE_PATH" elif ! tar -tf "$TEMPLATE_PATH" &>/dev/null; then msg_warn "Template appears corrupted – re-downloading." rm -f "$TEMPLATE_PATH" download_arm64_template "$PCT_OSTYPE" "$CUSTOM_TEMPLATE_VARIANT" "$TEMPLATE_PATH" fi # Retry after repair if ! pct create "$CTID" "${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE}" $PCT_OPTIONS >>"$LOGFILE" 2>&1; then # Fallback to local storage if not already on local if [[ "$TEMPLATE_STORAGE" != "local" ]]; then msg_info "Retrying container creation with fallback to local storage" LOCAL_TEMPLATE_PATH="/var/lib/vz/template/cache/$TEMPLATE" if [[ ! -f "$LOCAL_TEMPLATE_PATH" ]]; then msg_info "Downloading template to local..." download_arm64_template "$PCT_OSTYPE" "$CUSTOM_TEMPLATE_VARIANT" "$LOCAL_TEMPLATE_PATH" fi if ! pct create "$CTID" "local:vztmpl/${TEMPLATE}" $PCT_OPTIONS >>"$LOGFILE" 2>&1; then # Local fallback also failed - check for LXC stack version issue if grep -qiE 'unsupported .* version' "$LOGFILE"; then msg_warn "pct reported 'unsupported version' – LXC stack might be too old for this template" offer_lxc_stack_upgrade_and_maybe_retry "yes" rc=$? case $rc in 0) : ;; 2) msg_error "LXC stack upgrade ignored. Please inspect: $LOGFILE" _flush_pct_log exit 231 ;; 3) msg_error "LXC stack upgrade failed. Please inspect: $LOGFILE" _flush_pct_log exit 231 ;; 4) msg_error "Cancelled by user." _flush_pct_log exit 231 ;; esac else msg_error "Container creation failed. See $LOGFILE" if whiptail --yesno "pct create failed.\nDo you want to enable verbose debug mode and view detailed logs?" 12 70; then set -x pct create "$CTID" "local:vztmpl/${TEMPLATE}" $PCT_OPTIONS 2>&1 | tee -a "$LOGFILE" set +x fi _flush_pct_log exit 209 fi else msg_ok "Container successfully created using local fallback." fi else # Already on local storage and still failed - check LXC stack version if grep -qiE 'unsupported .* version' "$LOGFILE"; then msg_warn "pct reported 'unsupported version' – LXC stack might be too old for this template" offer_lxc_stack_upgrade_and_maybe_retry "yes" rc=$? case $rc in 0) : ;; # success - container created, continue 2) msg_error "LXC stack upgrade ignored. Please inspect: $LOGFILE" _flush_pct_log exit 231 ;; 3) msg_error "LXC stack upgrade failed. Please inspect: $LOGFILE" _flush_pct_log exit 231 ;; 4) msg_error "Cancelled by user." _flush_pct_log exit 231 ;; esac else msg_error "Container creation failed. See $LOGFILE" if whiptail --yesno "pct create failed.\nDo you want to enable verbose debug mode and view detailed logs?" 12 70; then set -x pct create "$CTID" "local:vztmpl/${TEMPLATE}" $PCT_OPTIONS 2>&1 | tee -a "$LOGFILE" set +x fi _flush_pct_log exit 209 fi fi else msg_ok "Container successfully created after template repair." fi fi # close CTID collision else-branch fi set +f # re-enable globbing after pct create block # Verify container exists (allow up to 10s for pmxcfs sync in clusters) local _pct_visible=false for _pct_check in {1..10}; do if pct list | awk '{print $1}' | grep -qx "$CTID"; then _pct_visible=true break fi sleep 1 done if [[ "$_pct_visible" != true ]]; then msg_error "Container ID $CTID not listed in 'pct list' after 10s. See $LOGFILE" msg_custom "🔧" "${YW}" "This can happen in clusters with pmxcfs sync delays." _flush_pct_log exit 215 fi # Verify config rootfs grep -q '^rootfs:' "/etc/pve/lxc/$CTID.conf" || { msg_error "RootFS entry missing in container config. See $LOGFILE" _flush_pct_log exit 216 } msg_ok "LXC Container ${BL}$CTID${CL} ${GN}was successfully created." # Append pct create log to BUILD_LOG for combined log visibility if [[ -s "$LOGFILE" && -n "${BUILD_LOG:-}" ]]; then { echo "" echo "--- pct create output ---" cat "$LOGFILE" echo "--- end pct create output ---" } >>"$BUILD_LOG" 2>/dev/null || true fi } # ============================================================================== # SECTION 9: POST-INSTALLATION & FINALIZATION # ============================================================================== # ------------------------------------------------------------------------------ # description() # # - Sets container description with formatted HTML content # - Includes: # * Community-Scripts logo # * Application name # * Links to GitHub, Discussions, Issues # * Ko-fi donation badge # - Restarts ping-instances.service if present (monitoring) # - Posts final "done" status to API telemetry # ------------------------------------------------------------------------------ description() { IP=$(pct exec "$CTID" ip a s dev eth0 | awk '/inet / {print $2}' | cut -d/ -f1) local script_slug script_url donate_url script_slug="${SCRIPT_SLUG:-${NSAPP}}" script_slug="$(echo "$script_slug" | tr '[:upper:]' '[:lower:]' | tr ' ' '-')" script_url="https://community-scripts.org/scripts/${script_slug}" donate_url="https://community-scripts.org/donate" # Generate LXC Description DESCRIPTION=$( cat < Logo

${APP} LXC

Sponsoring and donations

Open script page

GitHub Discussions Issues EOF ) pct set "$CTID" -description "$DESCRIPTION" if [[ -f /etc/systemd/system/ping-instances.service ]]; then systemctl start ping-instances.service fi # Optional host-side post-install hook # Path comes from var_post_install (default.vars / app.vars / advanced settings). # Runs ON THE PROXMOX HOST after the container is up and configured. # Exposed env vars: APP, NSAPP, CTID, IP, HN, STORAGE, BRG. # Output (stdout/stderr) is captured to /var/log/community-scripts/post-install-.log if [[ -n "${var_post_install:-}" ]]; then local _hook_log_dir="/var/log/community-scripts" local _hook_log="${_hook_log_dir}/post-install-${CTID}.log" mkdir -p "$_hook_log_dir" 2>/dev/null || true if [[ ! -f "${var_post_install}" ]]; then msg_error "Post-install hook not found on host: ${var_post_install}" whiptail --backtitle "Proxmox VE Helper Scripts" \ --title "POST-INSTALL HOOK FAILED" \ --msgbox "The configured post-install hook was not found on the Proxmox host:\n\n${var_post_install}\n\nThe LXC was created successfully, but the hook did NOT run." 14 72 || true else msg_info "Running post-install hook: ${var_post_install}" local _hook_rc=0 APP="$APP" NSAPP="${NSAPP:-}" CTID="$CTID" IP="$IP" HN="${HN:-}" \ STORAGE="${STORAGE:-}" BRG="${BRG:-}" \ bash "${var_post_install}" >"${_hook_log}" 2>&1 || _hook_rc=$? if [[ $_hook_rc -eq 0 ]]; then msg_ok "Post-install hook completed (log: ${_hook_log})" else msg_error "Post-install hook failed (rc=${_hook_rc}) – see ${_hook_log}" local _hook_tail="" _hook_tail="$(tail -n 15 "${_hook_log}" 2>/dev/null || true)" whiptail --backtitle "Proxmox VE Helper Scripts" \ --title "POST-INSTALL HOOK FAILED" \ --msgbox "Hook exited with code ${_hook_rc}.\n\nScript: ${var_post_install}\nLog: ${_hook_log}\n\n--- Last log lines ---\n${_hook_tail}\n\nThe LXC itself was created successfully." 22 78 || true fi fi fi INSTALL_COMPLETE=true post_update_to_api "done" "none" } # ============================================================================== # SECTION 10: ERROR HANDLING & EXIT TRAPS # ============================================================================== # ------------------------------------------------------------------------------ # ensure_log_on_host() # # - Ensures INSTALL_LOG points to a readable file on the host # - If INSTALL_LOG points to a container path (e.g. /root/.install-*), # tries to pull it from the container and create a combined log # - This allows get_error_text() to find actual error output for telemetry # - Uses timeout on pct pull to prevent hangs on dead/unresponsive containers # ------------------------------------------------------------------------------ ensure_log_on_host() { # Already readable on host? Nothing to do. [[ -n "${INSTALL_LOG:-}" && -s "${INSTALL_LOG}" ]] && return 0 # Try pulling from container and creating combined log if [[ -n "${CTID:-}" && -n "${SESSION_ID:-}" ]] && command -v pct &>/dev/null; then local combined_log="/tmp/${NSAPP:-lxc}-${CTID}-${SESSION_ID}.log" if [[ ! -s "$combined_log" ]]; then # Create combined log { echo "================================================================================" echo "COMBINED INSTALLATION LOG - ${APP:-LXC}" echo "Container ID: ${CTID}" echo "Session ID: ${SESSION_ID}" echo "Timestamp: $(date '+%Y-%m-%d %H:%M:%S')" echo "================================================================================" echo "" } >"$combined_log" 2>/dev/null || return 0 # Append BUILD_LOG if it exists if [[ -f "${BUILD_LOG:-}" ]]; then { echo "================================================================================" echo "PHASE 1: CONTAINER CREATION (Host)" echo "================================================================================" cat "${BUILD_LOG}" echo "" } >>"$combined_log" fi # Pull INSTALL_LOG from container (with timeout to prevent hangs on dead containers) local temp_log="/tmp/.install-temp-${SESSION_ID}.log" if timeout 8 pct pull "$CTID" "/root/.install-${SESSION_ID}.log" "$temp_log" 2>/dev/null; then { echo "================================================================================" echo "PHASE 2: APPLICATION INSTALLATION (Container)" echo "================================================================================" cat "$temp_log" echo "" } >>"$combined_log" rm -f "$temp_log" fi fi if [[ -s "$combined_log" ]]; then INSTALL_LOG="$combined_log" fi fi } # ============================================================================== # TRAP MANAGEMENT # ============================================================================== # All traps (ERR, EXIT, INT, TERM, HUP) are set by catch_errors() in # error_handler.func — called at the top of this file after sourcing. # # Do NOT set duplicate traps here. The handlers in error_handler.func # (on_exit, on_interrupt, on_terminate, on_hangup, error_handler) already: # - Send telemetry via post_update_to_api / _send_abort_telemetry # - Stop orphaned containers via _stop_container_if_installing # - Collect logs via ensure_log_on_host # - Clean up lock files and spinner processes # # Previously, inline traps here overwrote catch_errors() traps, causing: # - error_handler() never fired (no error output, no cleanup dialog) # - on_hangup() never fired (SSH disconnect → stuck records) # - Duplicated logic in two places (hard to debug) # ============================================================================== ================================================ FILE: misc/cloud-init.func ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: community-scripts ORG # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/branch/main/LICENSE # Revision: 1 # ============================================================================== # CLOUD-INIT.FUNC - VM CLOUD-INIT CONFIGURATION LIBRARY # ============================================================================== # # Universal helper library for Cloud-Init configuration in Proxmox VMs. # Provides functions for: # # - Native Proxmox Cloud-Init setup (user, password, network, SSH keys) # - Interactive configuration dialogs (whiptail) # - IP address retrieval via qemu-guest-agent # - Cloud-Init status monitoring and waiting # # Usage: # source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/cloud-init.func) # setup_cloud_init "$VMID" "$STORAGE" "$HN" "yes" # # Compatible with: Debian, Ubuntu, and all Cloud-Init enabled distributions # ============================================================================== # ============================================================================== # SECTION 1: CONFIGURATION DEFAULTS # ============================================================================== # These can be overridden before sourcing this library # Disable 'unbound variable' errors for this library (restored at end) _OLD_SET_STATE=$(set +o | grep -E 'set -(e|u|o)') set +u CLOUDINIT_DEFAULT_USER="${CLOUDINIT_DEFAULT_USER:-root}" CLOUDINIT_DNS_SERVERS="${CLOUDINIT_DNS_SERVERS:-1.1.1.1 8.8.8.8}" CLOUDINIT_SEARCH_DOMAIN="${CLOUDINIT_SEARCH_DOMAIN:-local}" CLOUDINIT_SSH_KEYS="${CLOUDINIT_SSH_KEYS:-}" # Empty by default - user must explicitly provide keys # ============================================================================== # SECTION 2: SSH KEY DISCOVERY AND SELECTION # ============================================================================== # ------------------------------------------------------------------------------ # _ci_ssh_extract_keys_from_file - Extracts valid SSH public keys from a file # ------------------------------------------------------------------------------ function _ci_ssh_extract_keys_from_file() { local file="$1" [[ -f "$file" && -r "$file" ]] || return 0 grep -E '^(ssh-(rsa|ed25519|dss|ecdsa)|ecdsa-sha2-)' "$file" 2>/dev/null || true } # ------------------------------------------------------------------------------ # _ci_ssh_discover_files - Scans standard paths for SSH keys # ------------------------------------------------------------------------------ function _ci_ssh_discover_files() { local -a cand=() shopt -s nullglob cand+=(/root/.ssh/authorized_keys /root/.ssh/authorized_keys2) cand+=(/root/.ssh/*.pub) cand+=(/etc/ssh/authorized_keys /etc/ssh/authorized_keys.d/*) shopt -u nullglob printf '%s\0' "${cand[@]}" } # ------------------------------------------------------------------------------ # _ci_ssh_build_choices - Builds whiptail checklist from SSH key files # # Sets: CI_SSH_CHOICES (array), CI_SSH_COUNT (int), CI_SSH_MAPFILE (path) # ------------------------------------------------------------------------------ function _ci_ssh_build_choices() { local -a files=("$@") CI_SSH_CHOICES=() CI_SSH_COUNT=0 CI_SSH_MAPFILE="$(mktemp)" local id key typ fp cmt base for f in "${files[@]}"; do [[ -f "$f" && -r "$f" ]] || continue base="$(basename -- "$f")" # Skip known_hosts and private keys case "$base" in known_hosts | known_hosts.* | config) continue ;; id_*) [[ "$f" != *.pub ]] && continue ;; esac while IFS= read -r key; do [[ -n "$key" ]] || continue typ="" fp="" cmt="" read -r _typ _b64 _cmt <<<"$key" typ="${_typ:-key}" cmt="${_cmt:-}" # Get fingerprint via ssh-keygen if available if command -v ssh-keygen >/dev/null 2>&1; then fp="$(printf '%s\n' "$key" | ssh-keygen -lf - 2>/dev/null | awk '{print $2}')" fi # Shorten long comments [[ ${#cmt} -gt 40 ]] && cmt="${cmt:0:37}..." CI_SSH_COUNT=$((CI_SSH_COUNT + 1)) id="K${CI_SSH_COUNT}" echo "${id}|${key}" >>"$CI_SSH_MAPFILE" CI_SSH_CHOICES+=("$id" "[$typ] ${fp:+$fp }${cmt:+$cmt }— ${base}" "OFF") done < <(_ci_ssh_extract_keys_from_file "$f") done } # ------------------------------------------------------------------------------ # configure_cloudinit_ssh_keys - Interactive SSH key selection for Cloud-Init # # Usage: configure_cloudinit_ssh_keys # Sets: CLOUDINIT_SSH_KEYS (path to temporary file with selected keys) # ------------------------------------------------------------------------------ function configure_cloudinit_ssh_keys() { local backtitle="Proxmox VE Helper Scripts" local ssh_key_mode # Create temp file for selected keys CLOUDINIT_SSH_KEYS_TEMP="$(mktemp)" : >"$CLOUDINIT_SSH_KEYS_TEMP" # Discover keys and build choices IFS=$'\0' read -r -d '' -a _def_files < <(_ci_ssh_discover_files && printf '\0') _ci_ssh_build_choices "${_def_files[@]}" local default_key_count="$CI_SSH_COUNT" if [[ "$default_key_count" -gt 0 ]]; then ssh_key_mode=$(whiptail --backtitle "$backtitle" --title "SSH KEY SOURCE" --menu \ "Provision SSH keys for Cloud-Init VM:" 14 72 4 \ "found" "Select from detected keys (${default_key_count})" \ "manual" "Paste a single public key" \ "folder" "Scan another folder (path or glob)" \ "none" "No SSH keys (password auth only)" 3>&1 1>&2 2>&3) || return 1 else ssh_key_mode=$(whiptail --backtitle "$backtitle" --title "SSH KEY SOURCE" --menu \ "No host keys detected. Choose:" 12 72 3 \ "manual" "Paste a single public key" \ "folder" "Scan another folder (path or glob)" \ "none" "No SSH keys (password auth only)" 3>&1 1>&2 2>&3) || return 1 fi case "$ssh_key_mode" in found) # Show checklist with individual keys local selection selection=$(whiptail --backtitle "$backtitle" --title "SELECT SSH KEYS" \ --checklist "Select one or more keys to import:" 20 140 10 "${CI_SSH_CHOICES[@]}" 3>&1 1>&2 2>&3) || return 1 for tag in $selection; do tag="${tag%\"}" tag="${tag#\"}" local line line=$(grep -E "^${tag}\|" "$CI_SSH_MAPFILE" | head -n1 | cut -d'|' -f2-) [[ -n "$line" ]] && printf '%s\n' "$line" >>"$CLOUDINIT_SSH_KEYS_TEMP" done local imported imported=$(wc -l <"$CLOUDINIT_SSH_KEYS_TEMP") echo -e "${ROOTSSH:- 🔑 }${BOLD}${DGN}SSH Keys: ${BGN}${imported} key(s) selected${CL}" ;; manual) local pubkey pubkey=$(whiptail --backtitle "$backtitle" --title "PASTE SSH PUBLIC KEY" \ --inputbox "Paste your SSH public key (ssh-rsa, ssh-ed25519, etc.):" 10 76 3>&1 1>&2 2>&3) || return 1 if [[ -n "$pubkey" ]]; then echo "$pubkey" >"$CLOUDINIT_SSH_KEYS_TEMP" echo -e "${ROOTSSH:- 🔑 }${BOLD}${DGN}SSH Keys: ${BGN}1 key added manually${CL}" else echo -e "${ROOTSSH:- 🔑 }${BOLD}${DGN}SSH Keys: ${BGN}none (empty input)${CL}" CLOUDINIT_SSH_KEYS="" rm -f "$CLOUDINIT_SSH_KEYS_TEMP" "$CI_SSH_MAPFILE" 2>/dev/null return 0 fi ;; folder) local glob_path glob_path=$(whiptail --backtitle "$backtitle" --title "SCAN FOLDER/GLOB" \ --inputbox "Enter a folder or glob to scan (e.g. /root/.ssh/*.pub):" 10 72 3>&1 1>&2 2>&3) || return 1 if [[ -n "$glob_path" ]]; then shopt -s nullglob local -a _scan_files=($glob_path) shopt -u nullglob if [[ "${#_scan_files[@]}" -gt 0 ]]; then _ci_ssh_build_choices "${_scan_files[@]}" if [[ "$CI_SSH_COUNT" -gt 0 ]]; then local folder_selection folder_selection=$(whiptail --backtitle "$backtitle" --title "SELECT FOLDER KEYS" \ --checklist "Select key(s) to import:" 20 140 10 "${CI_SSH_CHOICES[@]}" 3>&1 1>&2 2>&3) || return 1 for tag in $folder_selection; do tag="${tag%\"}" tag="${tag#\"}" local line line=$(grep -E "^${tag}\|" "$CI_SSH_MAPFILE" | head -n1 | cut -d'|' -f2-) [[ -n "$line" ]] && printf '%s\n' "$line" >>"$CLOUDINIT_SSH_KEYS_TEMP" done local imported imported=$(wc -l <"$CLOUDINIT_SSH_KEYS_TEMP") echo -e "${ROOTSSH:- 🔑 }${BOLD}${DGN}SSH Keys: ${BGN}${imported} key(s) from folder${CL}" else whiptail --backtitle "$backtitle" --msgbox "No keys found in: $glob_path" 8 60 fi else whiptail --backtitle "$backtitle" --msgbox "Path/glob returned no files." 8 60 fi fi ;; none | *) echo -e "${ROOTSSH:- 🔑 }${BOLD}${DGN}SSH Keys: ${BGN}none (password auth only)${CL}" CLOUDINIT_SSH_KEYS="" rm -f "$CLOUDINIT_SSH_KEYS_TEMP" "$CI_SSH_MAPFILE" 2>/dev/null return 0 ;; esac # Cleanup mapfile rm -f "$CI_SSH_MAPFILE" 2>/dev/null # Set the variable for setup_cloud_init to use if [[ -s "$CLOUDINIT_SSH_KEYS_TEMP" ]]; then CLOUDINIT_SSH_KEYS="$CLOUDINIT_SSH_KEYS_TEMP" else CLOUDINIT_SSH_KEYS="" rm -f "$CLOUDINIT_SSH_KEYS_TEMP" fi return 0 } # ============================================================================== # SECTION 3: HELPER FUNCTIONS # ============================================================================== # ------------------------------------------------------------------------------ # _ci_msg - Internal message helper with fallback # ------------------------------------------------------------------------------ function _ci_msg_info() { msg_info "$1" 2>/dev/null || echo "[INFO] $1"; } function _ci_msg_ok() { msg_ok "$1" 2>/dev/null || echo "[OK] $1"; } function _ci_msg_warn() { msg_warn "$1" 2>/dev/null || echo "[WARN] $1"; } function _ci_msg_error() { msg_error "$1" 2>/dev/null || echo "[ERROR] $1"; } # ------------------------------------------------------------------------------ # validate_ip_cidr - Validate IP address in CIDR format # Usage: validate_ip_cidr "192.168.1.100/24" && echo "Valid" # Returns: 0 if valid, 1 if invalid # ------------------------------------------------------------------------------ function validate_ip_cidr() { local ip_cidr="$1" # Match: 0-255.0-255.0-255.0-255/0-32 if [[ "$ip_cidr" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}/([0-9]|[1-2][0-9]|3[0-2])$ ]]; then # Validate each octet is 0-255 local ip="${ip_cidr%/*}" IFS='.' read -ra octets <<<"$ip" for octet in "${octets[@]}"; do ((octet > 255)) && return 1 done return 0 fi return 1 } # ------------------------------------------------------------------------------ # validate_ip - Validate plain IP address (no CIDR) # Usage: validate_ip "192.168.1.1" && echo "Valid" # ------------------------------------------------------------------------------ function validate_ip() { local ip="$1" if [[ "$ip" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then IFS='.' read -ra octets <<<"$ip" for octet in "${octets[@]}"; do ((octet > 255)) && return 1 done return 0 fi return 1 } # ============================================================================== # SECTION 3: MAIN CLOUD-INIT FUNCTIONS # ============================================================================== # ------------------------------------------------------------------------------ # setup_cloud_init - Configures Proxmox Native Cloud-Init # ------------------------------------------------------------------------------ # Parameters: # $1 - VMID (required) # $2 - Storage name (required) # $3 - Hostname (optional, default: vm-) # $4 - Enable Cloud-Init (yes/no, default: no) # $5 - User (optional, default: root) # $6 - Network mode (dhcp/static, default: dhcp) # $7 - Static IP (optional, format: 192.168.1.100/24) # $8 - Gateway (optional) # $9 - Nameservers (optional, default: 1.1.1.1 8.8.8.8) # # Returns: 0 on success, 1 on failure # Exports: CLOUDINIT_USER, CLOUDINIT_PASSWORD, CLOUDINIT_CRED_FILE # ============================================================================== function setup_cloud_init() { local vmid="$1" local storage="$2" local hostname="${3:-vm-${vmid}}" local enable="${4:-no}" local ciuser="${5:-$CLOUDINIT_DEFAULT_USER}" local network_mode="${6:-dhcp}" local static_ip="${7:-}" local gateway="${8:-}" local nameservers="${9:-$CLOUDINIT_DNS_SERVERS}" # Skip if not enabled if [ "$enable" != "yes" ]; then return 0 fi # Validate static IP if provided if [ "$network_mode" = "static" ]; then if [ -n "$static_ip" ] && ! validate_ip_cidr "$static_ip"; then _ci_msg_error "Invalid static IP format: $static_ip (expected: x.x.x.x/xx)" return 65 fi if [ -n "$gateway" ] && ! validate_ip "$gateway"; then _ci_msg_error "Invalid gateway IP format: $gateway" return 65 fi fi _ci_msg_info "Configuring Cloud-Init" # Create Cloud-Init drive (try ide2 first, then scsi1 as fallback) if ! qm set "$vmid" --ide2 "${storage}:cloudinit" >/dev/null 2>&1; then qm set "$vmid" --scsi1 "${storage}:cloudinit" >/dev/null 2>&1 fi # Set user qm set "$vmid" --ciuser "$ciuser" >/dev/null # Generate and set secure random password local cipassword=$(openssl rand -base64 16) qm set "$vmid" --cipassword "$cipassword" >/dev/null # Add SSH keys only if explicitly provided (not auto-imported from host) if [ -n "${CLOUDINIT_SSH_KEYS:-}" ] && [ -f "$CLOUDINIT_SSH_KEYS" ]; then qm set "$vmid" --sshkeys "$CLOUDINIT_SSH_KEYS" >/dev/null 2>&1 || true _ci_msg_info "SSH keys imported from: $CLOUDINIT_SSH_KEYS" fi # Configure network if [ "$network_mode" = "static" ] && [ -n "$static_ip" ] && [ -n "$gateway" ]; then qm set "$vmid" --ipconfig0 "ip=${static_ip},gw=${gateway}" >/dev/null else qm set "$vmid" --ipconfig0 "ip=dhcp" >/dev/null fi # Set DNS servers qm set "$vmid" --nameserver "$nameservers" >/dev/null # Set search domain qm set "$vmid" --searchdomain "$CLOUDINIT_SEARCH_DOMAIN" >/dev/null # Enable package upgrades on first boot (if supported by Proxmox version) qm set "$vmid" --ciupgrade 1 >/dev/null 2>&1 || true # Save credentials to file (with restrictive permissions) local cred_file="/tmp/${hostname}-${vmid}-cloud-init-credentials.txt" umask 077 cat >"$cred_file" < Proxmox UI Configuration: VM ${vmid} > Cloud-Init > Edit - User, Password, SSH Keys - Network (IP Config) - DNS, Search Domain ──────────────────────────────────────── 🗑️ To delete this file: rm -f ${cred_file} ──────────────────────────────────────── EOF chmod 600 "$cred_file" _ci_msg_ok "Cloud-Init configured (User: ${ciuser})" # Export for use in calling script (DO NOT display password here - will be shown in summary) export CLOUDINIT_USER="$ciuser" export CLOUDINIT_PASSWORD="$cipassword" export CLOUDINIT_CRED_FILE="$cred_file" return 0 } # ============================================================================== # SECTION 4: INTERACTIVE CONFIGURATION # ============================================================================== # ------------------------------------------------------------------------------ # configure_cloud_init_interactive - Whiptail dialog for Cloud-Init setup # ------------------------------------------------------------------------------ # Prompts user for Cloud-Init configuration choices # Returns configuration via exported variables: # - CLOUDINIT_ENABLE (yes/no) # - CLOUDINIT_USER # - CLOUDINIT_NETWORK_MODE (dhcp/static) # - CLOUDINIT_IP (if static) # - CLOUDINIT_GW (if static) # - CLOUDINIT_DNS # ------------------------------------------------------------------------------ function configure_cloud_init_interactive() { local default_user="${1:-root}" # Check if whiptail is available if ! command -v whiptail >/dev/null 2>&1; then echo "Warning: whiptail not available, skipping interactive configuration" export CLOUDINIT_ENABLE="no" return 127 fi # Ask if user wants to enable Cloud-Init if ! (whiptail --backtitle "Proxmox VE Helper Scripts" --title "CLOUD-INIT" \ --yesno "Enable Cloud-Init for VM configuration?\n\nCloud-Init allows automatic configuration of:\n• User accounts and passwords\n• SSH keys\n• Network settings (DHCP/Static)\n• DNS configuration\n\nYou can also configure these settings later in Proxmox UI." 16 68); then export CLOUDINIT_ENABLE="no" return 0 fi export CLOUDINIT_ENABLE="yes" # Username if CLOUDINIT_USER=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox \ "Cloud-Init Username" 8 58 "$default_user" --title "USERNAME" 3>&1 1>&2 2>&3); then export CLOUDINIT_USER="${CLOUDINIT_USER:-$default_user}" else export CLOUDINIT_USER="$default_user" fi # Network configuration if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "NETWORK MODE" \ --yesno "Use DHCP for network configuration?\n\nSelect 'No' for static IP configuration." 10 58); then export CLOUDINIT_NETWORK_MODE="dhcp" else export CLOUDINIT_NETWORK_MODE="static" # Static IP with validation while true; do if CLOUDINIT_IP=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox \ "Static IP Address (CIDR format)\nExample: 192.168.1.100/24" 9 58 "" --title "IP ADDRESS" 3>&1 1>&2 2>&3); then if validate_ip_cidr "$CLOUDINIT_IP"; then export CLOUDINIT_IP break else whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID IP" \ --msgbox "Invalid IP format: $CLOUDINIT_IP\n\nPlease use CIDR format: x.x.x.x/xx\nExample: 192.168.1.100/24" 10 50 fi else _ci_msg_warn "Static IP required, falling back to DHCP" export CLOUDINIT_NETWORK_MODE="dhcp" break fi done # Gateway with validation if [ "$CLOUDINIT_NETWORK_MODE" = "static" ]; then while true; do if CLOUDINIT_GW=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox \ "Gateway IP Address\nExample: 192.168.1.1" 8 58 "" --title "GATEWAY" 3>&1 1>&2 2>&3); then if validate_ip "$CLOUDINIT_GW"; then export CLOUDINIT_GW break else whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID GATEWAY" \ --msgbox "Invalid gateway format: $CLOUDINIT_GW\n\nPlease use format: x.x.x.x\nExample: 192.168.1.1" 10 50 fi else _ci_msg_warn "Gateway required, falling back to DHCP" export CLOUDINIT_NETWORK_MODE="dhcp" break fi done fi fi # DNS Servers if CLOUDINIT_DNS=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox \ "DNS Servers (space-separated)" 8 58 "1.1.1.1 8.8.8.8" --title "DNS SERVERS" 3>&1 1>&2 2>&3); then export CLOUDINIT_DNS="${CLOUDINIT_DNS:-1.1.1.1 8.8.8.8}" else export CLOUDINIT_DNS="1.1.1.1 8.8.8.8" fi return 0 } # ============================================================================== # SECTION 5: UTILITY FUNCTIONS # ============================================================================== # ------------------------------------------------------------------------------ # display_cloud_init_info - Show Cloud-Init summary after setup # ------------------------------------------------------------------------------ function display_cloud_init_info() { local vmid="$1" local hostname="${2:-}" if [ -n "$CLOUDINIT_CRED_FILE" ] && [ -f "$CLOUDINIT_CRED_FILE" ]; then if [ -n "${INFO:-}" ]; then echo -e "\n${INFO}${BOLD:-}${GN:-} Cloud-Init Configuration:${CL:-}" echo -e "${TAB:- }${DGN:-}User: ${BGN:-}${CLOUDINIT_USER:-root}${CL:-}" echo -e "${TAB:- }${DGN:-}Password: ${BGN:-}${CLOUDINIT_PASSWORD}${CL:-}" echo -e "${TAB:- }${DGN:-}Credentials: ${BL:-}${CLOUDINIT_CRED_FILE}${CL:-}" echo -e "${TAB:- }${RD:-}⚠️ Delete credentials file after noting password!${CL:-}" echo -e "${TAB:- }${YW:-}💡 Configure in Proxmox UI: VM ${vmid} > Cloud-Init${CL:-}" else echo "" echo "[INFO] Cloud-Init Configuration:" echo " User: ${CLOUDINIT_USER:-root}" echo " Password: ${CLOUDINIT_PASSWORD}" echo " Credentials: ${CLOUDINIT_CRED_FILE}" echo " ⚠️ Delete credentials file after noting password!" echo " Configure in Proxmox UI: VM ${vmid} > Cloud-Init" fi fi } # ------------------------------------------------------------------------------ # cleanup_cloud_init_credentials - Remove credentials file # ------------------------------------------------------------------------------ # Usage: cleanup_cloud_init_credentials # Call this after user has noted/saved the credentials # ------------------------------------------------------------------------------ function cleanup_cloud_init_credentials() { if [ -n "$CLOUDINIT_CRED_FILE" ] && [ -f "$CLOUDINIT_CRED_FILE" ]; then rm -f "$CLOUDINIT_CRED_FILE" _ci_msg_ok "Credentials file removed: $CLOUDINIT_CRED_FILE" unset CLOUDINIT_CRED_FILE return 0 fi return 1 } # ------------------------------------------------------------------------------ # has_cloud_init - Check if VM has Cloud-Init configured # ------------------------------------------------------------------------------ function has_cloud_init() { local vmid="$1" qm config "$vmid" 2>/dev/null | grep -qE "(ide2|scsi1):.*cloudinit" } # ------------------------------------------------------------------------------ # regenerate_cloud_init - Regenerate Cloud-Init configuration # ------------------------------------------------------------------------------ function regenerate_cloud_init() { local vmid="$1" if has_cloud_init "$vmid"; then _ci_msg_info "Regenerating Cloud-Init configuration" qm cloudinit update "$vmid" >/dev/null 2>&1 || true _ci_msg_ok "Cloud-Init configuration regenerated" return 0 else _ci_msg_warn "VM $vmid does not have Cloud-Init configured" return 1 fi } # ------------------------------------------------------------------------------ # get_vm_ip - Get VM IP address via qemu-guest-agent # ------------------------------------------------------------------------------ function get_vm_ip() { local vmid="$1" local timeout="${2:-30}" local elapsed=0 while [ $elapsed -lt $timeout ]; do local vm_ip=$(qm guest cmd "$vmid" network-get-interfaces 2>/dev/null | jq -r '.[] | select(.name != "lo") | ."ip-addresses"[]? | select(."ip-address-type" == "ipv4") | ."ip-address"' 2>/dev/null | head -1) if [ -n "$vm_ip" ]; then echo "$vm_ip" return 0 fi sleep 2 elapsed=$((elapsed + 2)) done return 7 } # ------------------------------------------------------------------------------ # wait_for_cloud_init - Wait for Cloud-Init to complete (requires SSH access) # ------------------------------------------------------------------------------ function wait_for_cloud_init() { local vmid="$1" local timeout="${2:-300}" local vm_ip="${3:-}" # Get IP if not provided if [ -z "$vm_ip" ]; then vm_ip=$(get_vm_ip "$vmid" 60) fi if [ -z "$vm_ip" ]; then _ci_msg_warn "Unable to determine VM IP address" return 7 fi _ci_msg_info "Waiting for Cloud-Init to complete on ${vm_ip}" local elapsed=0 while [ $elapsed -lt $timeout ]; do if timeout 10 ssh -o ConnectTimeout=5 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \ "${CLOUDINIT_USER:-root}@${vm_ip}" "cloud-init status --wait" 2>/dev/null; then _ci_msg_ok "Cloud-Init completed successfully" return 0 fi sleep 10 elapsed=$((elapsed + 10)) done _ci_msg_warn "Cloud-Init did not complete within ${timeout}s" return 150 } # ============================================================================== # SECTION 6: EXPORTS # ============================================================================== # Export all functions for use in other scripts export -f setup_cloud_init 2>/dev/null || true export -f configure_cloud_init_interactive 2>/dev/null || true export -f display_cloud_init_info 2>/dev/null || true export -f cleanup_cloud_init_credentials 2>/dev/null || true export -f has_cloud_init 2>/dev/null || true export -f regenerate_cloud_init 2>/dev/null || true export -f get_vm_ip 2>/dev/null || true export -f wait_for_cloud_init 2>/dev/null || true export -f validate_ip_cidr 2>/dev/null || true export -f validate_ip 2>/dev/null || true # Restore previous shell options if they were saved if [ -n "${_OLD_SET_STATE:-}" ]; then eval "$_OLD_SET_STATE" fi # ============================================================================== # SECTION 7: EXAMPLES & DOCUMENTATION # ============================================================================== : <<'EXAMPLES' # Example 1: Simple DHCP setup (most common) setup_cloud_init "$VMID" "$STORAGE" "$HN" "yes" # Example 2: Static IP setup setup_cloud_init "$VMID" "$STORAGE" "myserver" "yes" "root" "static" "192.168.1.100/24" "192.168.1.1" # Example 3: Interactive configuration in advanced_settings() configure_cloud_init_interactive "admin" if [ "$CLOUDINIT_ENABLE" = "yes" ]; then setup_cloud_init "$VMID" "$STORAGE" "$HN" "yes" "$CLOUDINIT_USER" \ "$CLOUDINIT_NETWORK_MODE" "$CLOUDINIT_IP" "$CLOUDINIT_GW" "$CLOUDINIT_DNS" fi # Example 4: Display info after VM creation display_cloud_init_info "$VMID" "$HN" # Example 5: Check if VM has Cloud-Init if has_cloud_init "$VMID"; then echo "Cloud-Init is configured" fi # Example 6: Wait for Cloud-Init to complete after VM start if [ "$START_VM" = "yes" ]; then qm start "$VMID" sleep 30 wait_for_cloud_init "$VMID" 300 fi # Example 7: Cleanup credentials file after user has noted password display_cloud_init_info "$VMID" "$HN" read -p "Have you saved the credentials? (y/N): " -r [[ $REPLY =~ ^[Yy]$ ]] && cleanup_cloud_init_credentials # Example 8: Validate IP before using if validate_ip_cidr "192.168.1.100/24"; then echo "Valid IP/CIDR" fi EXAMPLES ================================================ FILE: misc/core.func ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # License: MIT | https://raw.githubusercontent.com/asylumexp/Proxmox/main/LICENSE # ============================================================================== # CORE FUNCTIONS - LXC CONTAINER UTILITIES # ============================================================================== # # This file provides core utility functions for LXC container management # including colors, formatting, validation checks, message output, and # execution helpers used throughout the Community-Scripts ecosystem. # # Usage: # source <(curl -fsSL https://git.community-scripts.org/.../core.func) # load_functions # # ============================================================================== [[ -n "${_CORE_FUNC_LOADED:-}" ]] && return _CORE_FUNC_LOADED=1 # ============================================================================== # SECTION 1: INITIALIZATION & SETUP # ============================================================================== # ------------------------------------------------------------------------------ # load_functions() # # - Initializes all core utility groups (colors, formatting, icons, defaults) # - Ensures functions are loaded only once via __FUNCTIONS_LOADED flag # - Must be called at start of any script using these utilities # ------------------------------------------------------------------------------ load_functions() { [[ -n "${__FUNCTIONS_LOADED:-}" ]] && return __FUNCTIONS_LOADED=1 color formatting icons default_vars set_std_mode } # ------------------------------------------------------------------------------ # color() # # - Sets ANSI color codes for styled terminal output # - Variables: YW (yellow), YWB (yellow bright), BL (blue), RD (red) # GN (green), DGN (dark green), BGN (background green), CL (clear) # ------------------------------------------------------------------------------ color() { YW=$(echo "\033[33m") YWB=$'\e[93m' BL=$(echo "\033[36m") RD=$(echo "\033[01;31m") BGN=$(echo "\033[4;92m") GN=$(echo "\033[1;92m") DGN=$(echo "\033[32m") CL=$(echo "\033[m") } # ------------------------------------------------------------------------------ # color_spinner() # # - Sets ANSI color codes specifically for spinner animation # - Variables: CS_YW (spinner yellow), CS_YWB (spinner yellow bright), # CS_CL (spinner clear) # - Used by spinner() function to avoid color conflicts # ------------------------------------------------------------------------------ color_spinner() { CS_YW=$'\033[33m' CS_YWB=$'\033[93m' CS_CL=$'\033[m' } # ------------------------------------------------------------------------------ # formatting() # # - Defines formatting helpers for terminal output # - BFR: Backspace and clear line sequence # - BOLD: Bold text escape code # - TAB/TAB3: Indentation spacing # ------------------------------------------------------------------------------ formatting() { BFR="\\r\\033[K" BOLD=$(echo "\033[1m") HOLD=" " TAB=" " TAB3=" " } # ------------------------------------------------------------------------------ # icons() # # - Sets symbolic emoji icons used throughout user feedback # - Provides consistent visual indicators for success, error, info, etc. # - Icons: CM (checkmark), CROSS (error), INFO (info), HOURGLASS (wait), etc. # ------------------------------------------------------------------------------ icons() { CM="${TAB}✔️${TAB}" CROSS="${TAB}✖️${TAB}" DNSOK="✔️ " DNSFAIL="${TAB}✖️${TAB}" INFO="${TAB}💡${TAB}${CL}" OS="${TAB}🖥️${TAB}${CL}" OSVERSION="${TAB}🌟${TAB}${CL}" CONTAINERTYPE="${TAB}📦${TAB}${CL}" DISKSIZE="${TAB}💾${TAB}${CL}" CPUCORE="${TAB}🧠${TAB}${CL}" RAMSIZE="${TAB}🛠️${TAB}${CL}" SEARCH="${TAB}🔍${TAB}${CL}" VERBOSE_CROPPED="🔍${TAB}" VERIFYPW="${TAB}🔐${TAB}${CL}" CONTAINERID="${TAB}🆔${TAB}${CL}" HOSTNAME="${TAB}🏠${TAB}${CL}" BRIDGE="${TAB}🌉${TAB}${CL}" NETWORK="${TAB}📡${TAB}${CL}" GATEWAY="${TAB}🌐${TAB}${CL}" ICON_DISABLEIPV6="${TAB}🚫${TAB}${CL}" DEFAULT="${TAB}⚙️${TAB}${CL}" MACADDRESS="${TAB}🔗${TAB}${CL}" VLANTAG="${TAB}🏷️${TAB}${CL}" ROOTSSH="${TAB}🔑${TAB}${CL}" CREATING="${TAB}🚀${TAB}${CL}" ADVANCED="${TAB}🧩${TAB}${CL}" FUSE="${TAB}🗂️${TAB}${CL}" GPU="${TAB}🎮${TAB}${CL}" HOURGLASS="${TAB}⏳${TAB}" } # ------------------------------------------------------------------------------ # ensure_profile_loaded() # # - Sources /etc/profile.d/*.sh scripts if not already loaded # - Fixes PATH issues when running via pct enter/exec (non-login shells) # - Safe to call multiple times (uses guard variable) # - Should be called in update_script() or any script running inside LXC # ------------------------------------------------------------------------------ ensure_profile_loaded() { # Skip if already loaded or running on Proxmox host [[ -n "${_PROFILE_LOADED:-}" ]] && return command -v pveversion &>/dev/null && return # Source all profile.d scripts to ensure PATH is complete if [[ -d /etc/profile.d ]]; then for script in /etc/profile.d/*.sh; do [[ -r "$script" ]] && source "$script" || true done fi # Also ensure /usr/local/bin is in PATH (common install location) if [[ ":$PATH:" != *":/usr/local/bin:"* ]]; then export PATH="/usr/local/bin:$PATH" fi export _PROFILE_LOADED=1 } # ------------------------------------------------------------------------------ # default_vars() # # - Sets default retry and wait variables used for system actions # - RETRY_NUM: Maximum number of retry attempts (default: 10) # - RETRY_EVERY: Seconds to wait between retries (default: 3) # - i: Counter variable initialized to RETRY_NUM # ------------------------------------------------------------------------------ default_vars() { RETRY_NUM=10 RETRY_EVERY=3 i=$RETRY_NUM } # ------------------------------------------------------------------------------ # set_std_mode() # # - Sets default verbose mode for script and OS execution # - If VERBOSE=yes: STD="" (show all output) # - If VERBOSE=no: STD="silent" (suppress output via silent() wrapper) # - If DEV_MODE_TRACE=true: Enables bash tracing (set -x) # ------------------------------------------------------------------------------ set_std_mode() { if [ "${VERBOSE:-no}" = "yes" ]; then STD="" else STD="silent" fi # Enable bash tracing if trace mode active if [[ "${DEV_MODE_TRACE:-false}" == "true" ]]; then set -x export PS4='+(${BASH_SOURCE}:${LINENO}): ${FUNCNAME[0]:+${FUNCNAME[0]}(): }' fi } # ------------------------------------------------------------------------------ # parse_dev_mode() # # - Parses comma-separated dev_mode variable (e.g., "motd,keep,trace") # - Sets global flags for each mode: # * DEV_MODE_MOTD: Setup SSH/MOTD before installation # * DEV_MODE_KEEP: Never delete container on failure # * DEV_MODE_TRACE: Enable bash set -x tracing # * DEV_MODE_PAUSE: Pause after each msg_info step # * DEV_MODE_BREAKPOINT: Open shell on error instead of cleanup # * DEV_MODE_LOGS: Persist all logs to /var/log/community-scripts/ # * DEV_MODE_DRYRUN: Show commands without executing # - Call this early in script execution # ------------------------------------------------------------------------------ parse_dev_mode() { local mode # Initialize all flags to false export DEV_MODE_MOTD=false export DEV_MODE_KEEP=false export DEV_MODE_TRACE=false export DEV_MODE_PAUSE=false export DEV_MODE_BREAKPOINT=false export DEV_MODE_LOGS=false export DEV_MODE_DRYRUN=false # Parse comma-separated modes if [[ -n "${dev_mode:-}" ]]; then IFS=',' read -ra MODES <<<"$dev_mode" for mode in "${MODES[@]}"; do mode="$(echo "$mode" | xargs)" # Trim whitespace case "$mode" in motd) export DEV_MODE_MOTD=true ;; keep) export DEV_MODE_KEEP=true ;; trace) export DEV_MODE_TRACE=true ;; pause) export DEV_MODE_PAUSE=true ;; breakpoint) export DEV_MODE_BREAKPOINT=true ;; logs) export DEV_MODE_LOGS=true ;; dryrun) export DEV_MODE_DRYRUN=true ;; *) if declare -f msg_warn >/dev/null 2>&1; then msg_warn "Unknown dev_mode: '$mode' (ignored)" else echo "[WARN] Unknown dev_mode: '$mode' (ignored)" >&2 fi ;; esac done # Show active dev modes local active_modes=() [[ $DEV_MODE_MOTD == true ]] && active_modes+=("motd") [[ $DEV_MODE_KEEP == true ]] && active_modes+=("keep") [[ $DEV_MODE_TRACE == true ]] && active_modes+=("trace") [[ $DEV_MODE_PAUSE == true ]] && active_modes+=("pause") [[ $DEV_MODE_BREAKPOINT == true ]] && active_modes+=("breakpoint") [[ $DEV_MODE_LOGS == true ]] && active_modes+=("logs") [[ $DEV_MODE_DRYRUN == true ]] && active_modes+=("dryrun") if [[ ${#active_modes[@]} -gt 0 ]]; then if declare -f msg_custom >/dev/null 2>&1; then msg_custom "🔧" "${YWB}" "Dev modes active: ${active_modes[*]}" else echo "[DEV] Active modes: ${active_modes[*]}" >&2 fi fi fi } # ============================================================================== # SECTION 2: VALIDATION CHECKS # ============================================================================== # ------------------------------------------------------------------------------ # shell_check() # # - Verifies that the script is running under Bash shell # - Exits with error message if different shell is detected # - Required because scripts use Bash-specific features # ------------------------------------------------------------------------------ shell_check() { if [[ "$(ps -p $$ -o comm=)" != "bash" ]]; then clear msg_error "Your default shell is currently not set to Bash. To use these scripts, please switch to the Bash shell." echo -e "\nExiting..." sleep 2 exit 103 fi } # ------------------------------------------------------------------------------ # root_check() # # - Verifies script is running with root privileges # - Detects if executed via sudo (which can cause issues) # - Exits with error if not running as root directly # ------------------------------------------------------------------------------ root_check() { if [[ "$(id -u)" -ne 0 || $(ps -o comm= -p $PPID) == "sudo" ]]; then clear msg_error "Please run this script as root." echo -e "\nExiting..." sleep 2 exit 104 fi } # ------------------------------------------------------------------------------ # pve_check() # # - Validates Proxmox VE version compatibility # - Supported: PVE 8.0-8.9 and PVE 9.0-9.1 # - Exits with error message if unsupported version detected # ------------------------------------------------------------------------------ pve_check() { local PVE_VER PVE_VER="$(pveversion | awk -F'/' '{print $2}' | awk -F'-' '{print $1}')" # Check for Proxmox VE 8.x: allow 8.0–8.9 if [[ "$PVE_VER" =~ ^8\.([0-9]+) ]]; then local MINOR="${BASH_REMATCH[1]}" if ((MINOR < 0 || MINOR > 9)); then msg_error "This version of Proxmox VE is not supported." msg_error "Supported: Proxmox VE version 8.0 – 8.9" exit 105 fi return 0 fi # Check for Proxmox VE 9.x: allow 9.0–9.1 if [[ "$PVE_VER" =~ ^9\.([0-9]+) ]]; then local MINOR="${BASH_REMATCH[1]}" if ((MINOR < 0 || MINOR > 1)); then msg_error "This version of Proxmox VE is not yet supported." msg_error "Supported: Proxmox VE version 9.0 – 9.1" exit 105 fi return 0 fi # All other unsupported versions msg_error "This version of Proxmox VE is not supported." msg_error "Supported versions: Proxmox VE 8.0 – 8.9 or 9.0 – 9.1" exit 105 } # ------------------------------------------------------------------------------ # arch_check() # # - Validates system architecture is amd64/x86_64 # - Exits with error message for unsupported architectures (e.g., ARM/PiMox) # - Provides link to ARM64-compatible scripts # ------------------------------------------------------------------------------ arch_check() { if [ "$(dpkg --print-architecture)" != "arm64" ]; then msg_error "This script will not work with AMD64." msg_warn "Visit https://github.com/asylumexp/Proxmox for ARM64 support." sleep 2 exit 106 fi } # ------------------------------------------------------------------------------ # ssh_check() # # - Detects if script is running over SSH connection # - Warns user for external SSH connections (recommends Proxmox shell) # - Skips warning for local/same-subnet connections # - Does not abort execution, only warns # ------------------------------------------------------------------------------ ssh_check() { if [ -n "$SSH_CLIENT" ]; then local client_ip=$(awk '{print $1}' <<<"$SSH_CLIENT") local host_ip=$(hostname -I | awk '{print $1}') # Check if connection is local (Proxmox WebUI or same machine) # - localhost (127.0.0.1, ::1) # - same IP as host # - local network range (10.x, 172.16-31.x, 192.168.x) if [[ "$client_ip" == "127.0.0.1" || "$client_ip" == "::1" || "$client_ip" == "$host_ip" ]]; then return fi # Check if client is in same local network (optional, safer approach) local host_subnet=$(echo "$host_ip" | cut -d. -f1-3) local client_subnet=$(echo "$client_ip" | cut -d. -f1-3) if [[ "$host_subnet" == "$client_subnet" ]]; then return fi # Only warn for truly external connections msg_warn "Running via external SSH (client: $client_ip)." msg_warn "For better stability, consider using the Proxmox Shell (Console) instead." fi } # ============================================================================== # SECTION 3: EXECUTION HELPERS # ============================================================================== # ------------------------------------------------------------------------------ # get_active_logfile() # # - Returns the appropriate log file based on execution context # - _HOST_LOGFILE: Override for host context (keeps host logging on BUILD_LOG # even after INSTALL_LOG is exported for the container) # - INSTALL_LOG: Container operations (application installation) # - BUILD_LOG: Host operations (container creation) # - Fallback to BUILD_LOG if neither is set # ------------------------------------------------------------------------------ get_active_logfile() { # Host override: _HOST_LOGFILE is set (not exported) in build.func to keep # host-side logging in BUILD_LOG after INSTALL_LOG is exported for the container. # Without this, all host msg_info/msg_ok/msg_error would write to # /root/.install-SESSION.log (a container path) instead of BUILD_LOG. if [[ -n "${_HOST_LOGFILE:-}" ]]; then echo "$_HOST_LOGFILE" elif [[ -n "${INSTALL_LOG:-}" ]]; then echo "$INSTALL_LOG" elif [[ -n "${BUILD_LOG:-}" ]]; then echo "$BUILD_LOG" else # Fallback for legacy scripts echo "/tmp/build-$(date +%Y%m%d_%H%M%S).log" fi } # Legacy compatibility: SILENT_LOGFILE points to active log SILENT_LOGFILE="$(get_active_logfile)" # ------------------------------------------------------------------------------ # strip_ansi() # # - Removes ANSI escape sequences from input text # - Used to clean colored output for log files # - Handles both piped input and arguments # ------------------------------------------------------------------------------ strip_ansi() { if [[ $# -gt 0 ]]; then echo -e "$*" | sed 's/\x1b\[[0-9;]*m//g; s/\x1b\[[0-9;]*[a-zA-Z]//g' else sed 's/\x1b\[[0-9;]*m//g; s/\x1b\[[0-9;]*[a-zA-Z]//g' fi } # ------------------------------------------------------------------------------ # log_msg() # # - Writes message to active log file without ANSI codes # - Adds timestamp prefix for log correlation # - Creates log file if it doesn't exist # - Arguments: message text (can include ANSI codes, will be stripped) # ------------------------------------------------------------------------------ log_msg() { local msg="$*" local logfile logfile="$(get_active_logfile)" [[ -z "$msg" ]] && return [[ -z "$logfile" ]] && return # Ensure log directory exists mkdir -p "$(dirname "$logfile")" 2>/dev/null || true # Strip ANSI codes and write with timestamp local clean_msg clean_msg=$(strip_ansi "$msg") echo "[$(date '+%Y-%m-%d %H:%M:%S')] $clean_msg" >>"$logfile" } # ------------------------------------------------------------------------------ # log_section() # # - Writes a section header to the log file # - Used for separating different phases of installation # - Arguments: section name # ------------------------------------------------------------------------------ log_section() { local section="$1" local logfile logfile="$(get_active_logfile)" [[ -z "$logfile" ]] && return mkdir -p "$(dirname "$logfile")" 2>/dev/null || true { echo "" echo "================================================================================" echo "[$(date '+%Y-%m-%d %H:%M:%S')] $section" echo "================================================================================" } >>"$logfile" } # ------------------------------------------------------------------------------ # silent() # # - Executes command with output redirected to active log file # - On error: displays last 20 lines of log and exits with original exit code # - Temporarily disables error trap to capture exit code correctly # - Saves and restores previous error handling state (so callers that # intentionally disabled error handling aren't silently re-enabled) # - Sources explain_exit_code() for detailed error messages # ------------------------------------------------------------------------------ silent() { local cmd="$*" local caller_line="${BASH_LINENO[0]:-unknown}" local logfile="$(get_active_logfile)" # Dryrun mode: Show command without executing if [[ "${DEV_MODE_DRYRUN:-false}" == "true" ]]; then if declare -f msg_custom >/dev/null 2>&1; then msg_custom "🔍" "${BL}" "[DRYRUN] $cmd" else echo "[DRYRUN] $cmd" >&2 fi return 0 fi # Save current error handling state before disabling # This prevents re-enabling error handling when the caller intentionally # disabled it (e.g. build_container recovery section) local _restore_errexit=false [[ "$-" == *e* ]] && _restore_errexit=true set +Eeuo pipefail trap - ERR "$@" >>"$logfile" 2>&1 local rc=$? # Restore error handling ONLY if it was active before this call if $_restore_errexit; then set -Eeuo pipefail trap 'error_handler' ERR fi if [[ $rc -ne 0 ]]; then # Source explain_exit_code if needed if ! declare -f explain_exit_code >/dev/null 2>&1; then if ! source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/error_handler.func); then explain_exit_code() { echo "unknown (error_handler.func download failed)"; } fi fi # Return instead of exit so that callers can use `$STD cmd || true` # or `if $STD cmd; then ...` to handle errors gracefully. # When no || / if is used, set -e + ERR trap will still catch it # and error_handler() will display the error and exit. # # Set flag so error_handler knows to show log tail from silent's logfile export _SILENT_FAILED_RC="$rc" export _SILENT_FAILED_CMD="$cmd" export _SILENT_FAILED_LINE="$caller_line" export _SILENT_FAILED_LOG="$logfile" return "$rc" fi # Clear stale flags on success (prevents false positives if a previous # $STD cmd || true failed and a later non-silent command triggers error_handler) unset _SILENT_FAILED_RC _SILENT_FAILED_CMD _SILENT_FAILED_LINE _SILENT_FAILED_LOG 2>/dev/null || true } # ------------------------------------------------------------------------------ # apt_update_safe() # # - Runs apt-get update with graceful error handling # - On failure: shows warning with common causes instead of aborting # - Logs full output to active log file # - Returns 0 even on failure so the caller can continue # - Typical cause: enterprise repos returning 401 Unauthorized # # Usage: # apt_update_safe # Warn on failure, continue without aborting # ------------------------------------------------------------------------------ apt_update_safe() { local logfile logfile="$(get_active_logfile)" local _restore_errexit=false [[ "$-" == *e* ]] && _restore_errexit=true set +Eeuo pipefail trap - ERR apt-get update >>"$logfile" 2>&1 local rc=$? if $_restore_errexit; then set -Eeuo pipefail trap 'error_handler' ERR fi if [[ $rc -ne 0 ]]; then msg_warn "apt-get update exited with code ${rc} — some repositories may have failed." # Check log for common 401/403 enterprise repo issues if grep -qiE '401\s*Unauthorized|403\s*Forbidden|enterprise\.proxmox\.com' "$logfile" 2>/dev/null; then echo -e "${TAB}${INFO} ${YWB}Hint: Proxmox enterprise repository returned an auth error.${CL}" echo -e "${TAB} If you don't have a subscription, you can disable the enterprise" echo -e "${TAB} repo and use the no-subscription repo instead." fi echo -e "${TAB}${INFO} ${YWB}Continuing despite partial update failure — packages may still be installable.${CL}" echo "" fi return 0 } # ------------------------------------------------------------------------------ # spinner() # # - Displays animated spinner with rotating characters (⠋ ⠙ ⠹ ⠸ ⠼ ⠴ ⠦ ⠧ ⠇ ⠏) # - Shows SPINNER_MSG alongside animation # - Runs in infinite loop until killed by stop_spinner() # - Uses color_spinner() colors for output # ------------------------------------------------------------------------------ spinner() { local chars=(⠋ ⠙ ⠹ ⠸ ⠼ ⠴ ⠦ ⠧ ⠇ ⠏) local msg="${SPINNER_MSG:-Processing...}" local i=0 while true; do local index=$((i++ % ${#chars[@]})) printf "\r\033[2K%s %b" "${CS_YWB}${chars[$index]}${CS_CL}" "${CS_YWB}${msg}${CS_CL}" sleep 0.1 done } # ------------------------------------------------------------------------------ # clear_line() # # - Clears current terminal line using tput or ANSI escape codes # - Moves cursor to beginning of line (carriage return) # - Erases from cursor to end of line # - Fallback to ANSI codes if tput not available # ------------------------------------------------------------------------------ clear_line() { tput cr 2>/dev/null || echo -en "\r" tput el 2>/dev/null || echo -en "\033[K" } # ------------------------------------------------------------------------------ # stop_spinner() # # - Stops running spinner process by PID # - Reads PID from SPINNER_PID variable or /tmp/.spinner.pid file # - Attempts graceful kill, then forced kill if needed # - Cleans up temp file and resets terminal state # - Unsets SPINNER_PID and SPINNER_MSG variables # ------------------------------------------------------------------------------ stop_spinner() { local pid="${SPINNER_PID:-}" [[ -z "$pid" && -f /tmp/.spinner.pid ]] && pid=$(/dev/null; then sleep 0.05 kill -9 "$pid" 2>/dev/null || true wait "$pid" 2>/dev/null || true fi rm -f /tmp/.spinner.pid fi unset SPINNER_PID SPINNER_MSG stty sane 2>/dev/null || true stty -tostop 2>/dev/null || true } # ============================================================================== # SECTION 4: MESSAGE OUTPUT # ============================================================================== # ------------------------------------------------------------------------------ # msg_info() # # - Displays informational message with spinner animation # - Shows each unique message only once (tracked via MSG_INFO_SHOWN) # - In verbose/Alpine mode: shows hourglass icon instead of spinner # - Stops any existing spinner before starting new one # - Backgrounds spinner process and stores PID for later cleanup # ------------------------------------------------------------------------------ msg_info() { local msg="$1" [[ -z "$msg" ]] && return if ! declare -p MSG_INFO_SHOWN &>/dev/null || ! declare -A MSG_INFO_SHOWN &>/dev/null; then declare -gA MSG_INFO_SHOWN=() fi [[ -n "${MSG_INFO_SHOWN["$msg"]+x}" ]] && return MSG_INFO_SHOWN["$msg"]=1 # Log to file log_msg "[INFO] $msg" stop_spinner SPINNER_MSG="$msg" if is_verbose_mode || is_alpine; then local HOURGLASS="${TAB}⏳${TAB}" printf "\r\e[2K%s %b" "$HOURGLASS" "${YW}${msg}${CL}" >&2 # Pause mode: Wait for Enter after each step if [[ "${DEV_MODE_PAUSE:-false}" == "true" ]]; then echo -en "\n${YWB}[PAUSE]${CL} Press Enter to continue..." >&2 read -r fi return fi color_spinner spinner & SPINNER_PID=$! echo "$SPINNER_PID" >/tmp/.spinner.pid disown "$SPINNER_PID" 2>/dev/null || true # Pause mode: Stop spinner and wait if [[ "${DEV_MODE_PAUSE:-false}" == "true" ]]; then stop_spinner echo -en "\n${YWB}[PAUSE]${CL} Press Enter to continue..." >&2 read -r fi } # ------------------------------------------------------------------------------ # msg_ok() # # - Displays success message with checkmark icon # - Stops spinner and clears line before output # - Removes message from MSG_INFO_SHOWN to allow re-display # - Uses green color for success indication # ------------------------------------------------------------------------------ msg_ok() { local msg="$1" [[ -z "$msg" ]] && return stop_spinner clear_line echo -e "$CM ${GN}${msg}${CL}" log_msg "[OK] $msg" local sanitized_msg sanitized_msg=$(printf '%s' "$msg" | sed 's/\x1b\[[0-9;]*m//g; s/[^a-zA-Z0-9_]/_/g') unset 'MSG_INFO_SHOWN['"$sanitized_msg"']' 2>/dev/null || true } # ------------------------------------------------------------------------------ # msg_error() # # - Displays error message with cross/X icon # - Stops spinner before output # - Uses red color for error indication # - Outputs to stderr # ------------------------------------------------------------------------------ msg_error() { stop_spinner local msg="$1" echo -e "${BFR:-}${CROSS:-✖️} ${RD}${msg}${CL}" >&2 log_msg "[ERROR] $msg" } # ------------------------------------------------------------------------------ # msg_warn() # # - Displays warning message with info/lightbulb icon # - Stops spinner before output # - Uses bright yellow color for warning indication # - Outputs to stderr # ------------------------------------------------------------------------------ msg_warn() { stop_spinner local msg="$1" echo -e "${BFR:-}${INFO:-ℹ️} ${YWB}${msg}${CL}" >&2 log_msg "[WARN] $msg" } # ------------------------------------------------------------------------------ # msg_custom() # # - Displays custom message with user-defined symbol and color # - Arguments: symbol, color code, message text # - Stops spinner before output # - Useful for specialized status messages # ------------------------------------------------------------------------------ msg_custom() { local symbol="${1:-"[*]"}" local color="${2:-"\e[36m"}" local msg="${3:-}" [[ -z "$msg" ]] && return stop_spinner echo -e "${BFR:-} ${symbol} ${color}${msg}${CL:-\e[0m}" log_msg "$msg" } # ------------------------------------------------------------------------------ # msg_debug() # # - Displays debug message with timestamp when var_full_verbose=1 # - Automatically enables var_verbose if not already set # - Shows date/time prefix for log correlation # - Uses bright yellow color for debug output # ------------------------------------------------------------------------------ msg_debug() { if [[ "${var_full_verbose:-0}" == "1" ]]; then [[ "${var_verbose:-0}" != "1" ]] && var_verbose=1 echo -e "${YWB}[$(date '+%F %T')] [DEBUG]${CL} $*" fi } # ------------------------------------------------------------------------------ # msg_dev() # # - Display development mode messages with 🔧 icon # - Only shown when dev_mode is active # - Useful for debugging and development-specific output # - Format: [DEV] message with distinct formatting # - Usage: msg_dev "Container ready for debugging" # ------------------------------------------------------------------------------ msg_dev() { if [[ -n "${dev_mode:-}" ]]; then echo -e "${SEARCH}${BOLD}${DGN}🔧 [DEV]${CL} $*" fi } # # - Displays error message and immediately terminates script # - Sends SIGINT to current process to trigger error handler # - Use for unrecoverable errors that require immediate exit # ------------------------------------------------------------------------------ fatal() { msg_error "$1" kill -INT $$ } # ============================================================================== # SECTION 5: UTILITY FUNCTIONS # ============================================================================== # ------------------------------------------------------------------------------ # exit_script() # # - Called when user cancels an action # - Clears screen and displays exit message # - Exits with default exit code # ------------------------------------------------------------------------------ exit_script() { clear msg_error "User exited script" exit 0 } # ------------------------------------------------------------------------------ # get_header() # # - Downloads and caches application header ASCII art # - Falls back to local cache if already downloaded # - Determines app type (ct/vm) from APP_TYPE variable # - Returns header content or empty string on failure # ------------------------------------------------------------------------------ get_header() { local app_name=$(echo "${APP,,}" | tr -d ' ') local app_type=${APP_TYPE:-ct} # Default to 'ct' if not set local header_dir="${app_type}" [[ "$app_type" == "addon" ]] && header_dir="tools" local header_url="https://raw.githubusercontent.com/asylumexp/Proxmox/main/${header_dir}/headers/${app_name}" local local_header_path="/usr/local/community-scripts/headers/${app_type}/${app_name}" mkdir -p "$(dirname "$local_header_path")" if [ ! -s "$local_header_path" ]; then if ! curl -fsSL "$header_url" -o "$local_header_path"; then msg_warn "Failed to download header: $header_url" return 250 fi fi cat "$local_header_path" 2>/dev/null || true } # ------------------------------------------------------------------------------ # header_info() # # - Displays application header ASCII art at top of screen # - Clears screen before displaying header # - Detects terminal width for formatting # - Returns silently if header not available # ------------------------------------------------------------------------------ header_info() { # Guard against printing the header twice in the same session (e.g. when # the ct script calls header_info at global scope AND again inside # update_script()). [[ "${_HEADER_SHOWN:-0}" == "1" ]] && return 0 _HEADER_SHOWN=1 local app_name=$(echo "${APP,,}" | tr -d ' ') local header_content header_content=$(get_header "$app_name") || header_content="" clear local term_width term_width=$(tput cols 2>/dev/null || echo 120) if [ -n "$header_content" ]; then echo "$header_content" fi } # ------------------------------------------------------------------------------ # ensure_tput() # # - Ensures tput command is available for terminal control # - Installs ncurses-bin on Debian/Ubuntu or ncurses on Alpine # - Required for clear_line() and terminal width detection # ------------------------------------------------------------------------------ ensure_tput() { if ! command -v tput >/dev/null 2>&1; then if grep -qi 'alpine' /etc/os-release; then apk add --no-cache ncurses >/dev/null 2>&1 || msg_warn "Failed to install ncurses (tput may be unavailable)" elif command -v apt-get >/dev/null 2>&1; then apt-get update -qq >/dev/null apt-get install -y -qq ncurses-bin >/dev/null 2>&1 || msg_warn "Failed to install ncurses-bin (tput may be unavailable)" fi fi } # ------------------------------------------------------------------------------ # is_alpine() # # - Detects if running on Alpine Linux # - Checks var_os, PCT_OSTYPE, or /etc/os-release # - Returns 0 if Alpine, 1 otherwise # - Used to adjust behavior for Alpine-specific commands # ------------------------------------------------------------------------------ is_alpine() { local os_id="${var_os:-${PCT_OSTYPE:-}}" if [[ -z "$os_id" && -f /etc/os-release ]]; then os_id="$( . /etc/os-release 2>/dev/null echo "${ID:-}" )" fi [[ "$os_id" == "alpine" ]] } # ------------------------------------------------------------------------------ # is_verbose_mode() # # - Determines if script should run in verbose mode # - Checks VERBOSE and var_verbose variables # - Used by msg_info() to decide between spinner and static output # - Note: Non-TTY (pipe) scenarios are handled separately in msg_info() # to allow spinner output to pass through pipes (e.g. lxc-attach | tee) # ------------------------------------------------------------------------------ is_verbose_mode() { local verbose="${VERBOSE:-${var_verbose:-no}}" [[ "$verbose" != "no" ]] } # ------------------------------------------------------------------------------ # is_unattended() # # - Detects if script is running in unattended/non-interactive mode # - Checks MODE variable first (primary method) # - Falls back to legacy flags (PHS_SILENT, var_unattended) # - Returns 0 (true) if unattended, 1 (false) otherwise # - Used by prompt functions to auto-apply defaults # # Modes that are unattended: # - default (1) : Use script defaults, no prompts # - mydefaults (3) : Use user's default.vars, no prompts # - appdefaults (4) : Use app-specific defaults, no prompts # # Modes that are interactive: # - advanced (2) : Full wizard with all options # # Note: Even in advanced mode, install scripts run unattended because # all values are already collected during the wizard phase. # ------------------------------------------------------------------------------ is_unattended() { # Primary: Check MODE variable (case-insensitive) local mode="${MODE:-${mode:-}}" mode="${mode,,}" # lowercase case "$mode" in default | 1) return 0 ;; mydefaults | userdefaults | 3) return 0 ;; appdefaults | 4) return 0 ;; advanced | 2) # Advanced mode is interactive ONLY during wizard # Inside container (install scripts), it should be unattended # Check if we're inside a container (no pveversion command) if ! command -v pveversion &>/dev/null; then # We're inside the container - all values already collected return 0 fi # On host during wizard - interactive return 1 ;; esac # Legacy fallbacks for compatibility [[ "${PHS_SILENT:-0}" == "1" ]] && return 0 [[ "${var_unattended:-}" =~ ^(yes|true|1)$ ]] && return 0 [[ "${UNATTENDED:-}" =~ ^(yes|true|1)$ ]] && return 0 # No TTY available = unattended [[ ! -t 0 ]] && return 0 # Default: interactive return 1 } # ------------------------------------------------------------------------------ # show_missing_values_warning() # # - Displays a summary of required values that used fallback defaults # - Should be called at the end of install scripts # - Only shows warning if MISSING_REQUIRED_VALUES array has entries # - Provides clear guidance on what needs manual configuration # # Global: # MISSING_REQUIRED_VALUES - Array of variable names that need configuration # # Example: # # At end of install script: # show_missing_values_warning # ------------------------------------------------------------------------------ show_missing_values_warning() { if [[ ${#MISSING_REQUIRED_VALUES[@]} -gt 0 ]]; then echo "" echo -e "${YW}╔════════════════════════════════════════════════════════════╗${CL}" echo -e "${YW}║ ⚠️ MANUAL CONFIGURATION REQUIRED ║${CL}" echo -e "${YW}╠════════════════════════════════════════════════════════════╣${CL}" echo -e "${YW}║ The following values were not provided and need to be ║${CL}" echo -e "${YW}║ configured manually for the service to work properly: ║${CL}" echo -e "${YW}╟────────────────────────────────────────────────────────────╢${CL}" for val in "${MISSING_REQUIRED_VALUES[@]}"; do printf "${YW}║${CL} • %-56s ${YW}║${CL}\n" "$val" done echo -e "${YW}╟────────────────────────────────────────────────────────────╢${CL}" echo -e "${YW}║ Check the service configuration files or environment ║${CL}" echo -e "${YW}║ variables and update the placeholder values. ║${CL}" echo -e "${YW}╚════════════════════════════════════════════════════════════╝${CL}" echo "" return 1 fi return 0 } # ------------------------------------------------------------------------------ # prompt_confirm() # # - Prompts user for yes/no confirmation with timeout and unattended support # - In unattended mode: immediately returns default value # - In interactive mode: waits for user input with configurable timeout # - After timeout: auto-applies default value # # Arguments: # $1 - Prompt message (required) # $2 - Default value: "y" or "n" (optional, default: "n") # $3 - Timeout in seconds (optional, default: 60) # # Returns: # 0 - User confirmed (yes) # 1 - User declined (no) or timeout with default "n" # # Example: # if prompt_confirm "Proceed with installation?" "y" 30; then # echo "Installing..." # fi # # # Unattended: prompt_confirm will use default without waiting # var_unattended=yes # prompt_confirm "Delete files?" "n" && echo "Deleting" || echo "Skipped" # ------------------------------------------------------------------------------ prompt_confirm() { local message="${1:-Confirm?}" local default="${2:-n}" local timeout="${3:-60}" local response # Normalize default to lowercase default="${default,,}" [[ "$default" != "y" ]] && default="n" # Build prompt hint local hint if [[ "$default" == "y" ]]; then hint="[Y/n]" else hint="[y/N]" fi # Unattended mode: apply default immediately if is_unattended; then if [[ "$default" == "y" ]]; then return 0 else return 1 fi fi # Check if running in a TTY if [[ ! -t 0 ]]; then # Not a TTY, use default if [[ "$default" == "y" ]]; then return 0 else return 1 fi fi # Interactive prompt with timeout echo -en "${YW}${message} ${hint} (auto-${default} in ${timeout}s): ${CL}" if read -t "$timeout" -r response; then # User provided input response="${response,,}" # lowercase case "$response" in y | yes) return 0 ;; n | no) return 1 ;; "") # Empty response, use default if [[ "$default" == "y" ]]; then return 0 else return 1 fi ;; *) # Invalid input, use default echo -e "${YW}Invalid response, using default: ${default}${CL}" if [[ "$default" == "y" ]]; then return 0 else return 1 fi ;; esac else # Timeout occurred echo "" # Newline after timeout echo -e "${YW}Timeout - auto-selecting: ${default}${CL}" if [[ "$default" == "y" ]]; then return 0 else return 1 fi fi } # ------------------------------------------------------------------------------ # prompt_input() # # - Prompts user for text input with timeout and unattended support # - In unattended mode: immediately returns default value # - In interactive mode: waits for user input with configurable timeout # - After timeout: auto-applies default value # # Arguments: # $1 - Prompt message (required) # $2 - Default value (optional, default: "") # $3 - Timeout in seconds (optional, default: 60) # # Output: # Prints the user input or default value to stdout # # Example: # username=$(prompt_input "Enter username:" "admin" 30) # echo "Using username: $username" # # # With validation # while true; do # port=$(prompt_input "Enter port:" "8080" 30) # [[ "$port" =~ ^[0-9]+$ ]] && break # echo "Invalid port number" # done # ------------------------------------------------------------------------------ prompt_input() { local message="${1:-Enter value:}" local default="${2:-}" local timeout="${3:-60}" local response # Build display default hint local hint="" [[ -n "$default" ]] && hint=" (default: ${default})" # Unattended mode: return default immediately if is_unattended; then echo "$default" return 0 fi # Check if running in a TTY if [[ ! -t 0 ]]; then # Not a TTY, use default echo "$default" return 0 fi # Interactive prompt with timeout echo -en "${YW}${message}${hint} (auto-default in ${timeout}s): ${CL}" >&2 if read -t "$timeout" -r response; then # User provided input (or pressed Enter for empty) if [[ -n "$response" ]]; then echo "$response" else echo "$default" fi else # Timeout occurred echo "" >&2 # Newline after timeout echo -e "${YW}Timeout - using default: ${default}${CL}" >&2 echo "$default" fi } # ------------------------------------------------------------------------------ # prompt_input_required() # # - Prompts user for REQUIRED text input with fallback support # - In unattended mode: Uses fallback value if no env var set (with warning) # - In interactive mode: loops until user provides non-empty input # - Tracks missing required values for end-of-script summary # # Arguments: # $1 - Prompt message (required) # $2 - Fallback/example value for unattended mode (optional) # $3 - Timeout in seconds (optional, default: 120) # $4 - Environment variable name hint for error messages (optional) # # Output: # Prints the user input or fallback value to stdout # # Returns: # 0 - Success (value provided or fallback used) # 1 - Failed (interactive timeout without input) # # Global: # MISSING_REQUIRED_VALUES - Array tracking fields that used fallbacks # # Example: # # With fallback - script continues even in unattended mode # token=$(prompt_input_required "Enter API Token:" "YOUR_TOKEN_HERE" 60 "var_api_token") # # # Check at end of script if any values need manual configuration # if [[ ${#MISSING_REQUIRED_VALUES[@]} -gt 0 ]]; then # msg_warn "Please configure: ${MISSING_REQUIRED_VALUES[*]}" # fi # ------------------------------------------------------------------------------ # Global array to track missing required values declare -g -a MISSING_REQUIRED_VALUES=() prompt_input_required() { local message="${1:-Enter required value:}" local fallback="${2:-CHANGE_ME}" local timeout="${3:-120}" local env_var_hint="${4:-}" local response="" # Check if value is already set via environment variable (if hint provided) if [[ -n "$env_var_hint" ]]; then local env_value="${!env_var_hint:-}" if [[ -n "$env_value" ]]; then echo "$env_value" return 0 fi fi # Unattended mode: use fallback with warning if is_unattended; then if [[ -n "$env_var_hint" ]]; then echo -e "${YW}⚠ Required value '${env_var_hint}' not set - using fallback: ${fallback}${CL}" >&2 MISSING_REQUIRED_VALUES+=("$env_var_hint") else echo -e "${YW}⚠ Required value not provided - using fallback: ${fallback}${CL}" >&2 MISSING_REQUIRED_VALUES+=("(unnamed)") fi echo "$fallback" return 0 fi # Check if running in a TTY if [[ ! -t 0 ]]; then echo -e "${YW}⚠ Not interactive - using fallback: ${fallback}${CL}" >&2 MISSING_REQUIRED_VALUES+=("${env_var_hint:-unnamed}") echo "$fallback" return 0 fi # Interactive prompt - loop until non-empty input or use fallback on timeout local attempts=0 while [[ -z "$response" ]]; do attempts=$((attempts + 1)) if [[ $attempts -gt 3 ]]; then echo -e "${YW}Too many empty inputs - using fallback: ${fallback}${CL}" >&2 MISSING_REQUIRED_VALUES+=("${env_var_hint:-manual_input}") echo "$fallback" return 0 fi echo -en "${YW}${message} (required, timeout ${timeout}s): ${CL}" >&2 if read -t "$timeout" -r response; then if [[ -z "$response" ]]; then echo -e "${YW}This field is required. Please enter a value. (attempt ${attempts}/3)${CL}" >&2 fi else # Timeout occurred - use fallback echo "" >&2 echo -e "${YW}Timeout - using fallback value: ${fallback}${CL}" >&2 MISSING_REQUIRED_VALUES+=("${env_var_hint:-timeout}") echo "$fallback" return 0 fi done echo "$response" } # ------------------------------------------------------------------------------ # prompt_select() # # - Prompts user to select from a list of options with timeout support # - In unattended mode: immediately returns default selection # - In interactive mode: displays numbered menu and waits for choice # - After timeout: auto-applies default selection # # Arguments: # $1 - Prompt message (required) # $2 - Default option number, 1-based (optional, default: 1) # $3 - Timeout in seconds (optional, default: 60) # $4+ - Options to display (required, at least 2) # # Output: # Prints the selected option value to stdout # # Returns: # 0 - Success # 1 - No options provided or invalid state # # Example: # choice=$(prompt_select "Select database:" 1 30 "PostgreSQL" "MySQL" "SQLite") # echo "Selected: $choice" # # # With array # options=("Option A" "Option B" "Option C") # selected=$(prompt_select "Choose:" 2 60 "${options[@]}") # ------------------------------------------------------------------------------ prompt_select() { local message="${1:-Select option:}" local default="${2:-1}" local timeout="${3:-60}" shift 3 local options=("$@") local num_options=${#options[@]} # Validate options if [[ $num_options -eq 0 ]]; then msg_warn "prompt_select called with no options" echo "" >&2 return 65 fi # Validate default if [[ ! "$default" =~ ^[0-9]+$ ]] || [[ "$default" -lt 1 ]] || [[ "$default" -gt "$num_options" ]]; then default=1 fi # Unattended mode: return default immediately if is_unattended; then echo "${options[$((default - 1))]}" return 0 fi # Check if running in a TTY if [[ ! -t 0 ]]; then echo "${options[$((default - 1))]}" return 0 fi # Display menu echo -e "${YW}${message}${CL}" >&2 local i for i in "${!options[@]}"; do local num=$((i + 1)) if [[ $num -eq $default ]]; then echo -e " ${GN}${num})${CL} ${options[$i]} ${YW}(default)${CL}" >&2 else echo -e " ${GN}${num})${CL} ${options[$i]}" >&2 fi done # Interactive prompt with timeout echo -en "${YW}Select [1-${num_options}] (auto-select ${default} in ${timeout}s): ${CL}" >&2 local response if read -t "$timeout" -r response; then if [[ -z "$response" ]]; then # Empty response, use default echo "${options[$((default - 1))]}" elif [[ "$response" =~ ^[0-9]+$ ]] && [[ "$response" -ge 1 ]] && [[ "$response" -le "$num_options" ]]; then # Valid selection echo "${options[$((response - 1))]}" else # Invalid input, use default echo -e "${YW}Invalid selection, using default: ${options[$((default - 1))]}${CL}" >&2 echo "${options[$((default - 1))]}" fi else # Timeout occurred echo "" >&2 # Newline after timeout echo -e "${YW}Timeout - auto-selecting: ${options[$((default - 1))]}${CL}" >&2 echo "${options[$((default - 1))]}" fi } # ------------------------------------------------------------------------------ # prompt_password() # # - Prompts user for password input with hidden characters # - In unattended mode: returns default or generates random password # - Supports auto-generation of secure passwords # - After timeout: generates random password if allowed # # Arguments: # $1 - Prompt message (required) # $2 - Default value or "generate" for auto-generation (optional) # $3 - Timeout in seconds (optional, default: 60) # $4 - Minimum length for validation (optional, default: 0 = no minimum) # # Output: # Prints the password to stdout # # Example: # password=$(prompt_password "Enter password:" "generate" 30 8) # echo "Password set" # # # Require user input (no default) # db_pass=$(prompt_password "Database password:" "" 60 12) # ------------------------------------------------------------------------------ prompt_password() { local message="${1:-Enter password:}" local default="${2:-}" local timeout="${3:-60}" local min_length="${4:-0}" local response # Generate random password if requested local generated="" if [[ "$default" == "generate" ]]; then generated=$(openssl rand -base64 16 2>/dev/null | tr -dc 'a-zA-Z0-9' | head -c 16) [[ -z "$generated" ]] && generated=$(head /dev/urandom | tr -dc 'a-zA-Z0-9' | head -c 16) default="$generated" fi # Unattended mode: return default immediately if is_unattended; then echo "$default" return 0 fi # Check if running in a TTY if [[ ! -t 0 ]]; then echo "$default" return 0 fi # Build hint local hint="" if [[ -n "$generated" ]]; then hint=" (Enter for auto-generated)" elif [[ -n "$default" ]]; then hint=" (Enter for default)" fi [[ "$min_length" -gt 0 ]] && hint="${hint} [min ${min_length} chars]" # Interactive prompt with timeout (silent input) echo -en "${YW}${message}${hint} (timeout ${timeout}s): ${CL}" >&2 if read -t "$timeout" -rs response; then echo "" >&2 # Newline after hidden input if [[ -n "$response" ]]; then # Validate minimum length if [[ "$min_length" -gt 0 ]] && [[ ${#response} -lt "$min_length" ]]; then echo -e "${YW}Password too short (min ${min_length}), using default${CL}" >&2 echo "$default" else echo "$response" fi else echo "$default" fi else # Timeout occurred echo "" >&2 # Newline after timeout echo -e "${YW}Timeout - using generated password${CL}" >&2 echo "$default" fi } # ============================================================================== # SECTION 6: CLEANUP & MAINTENANCE # ============================================================================== # ------------------------------------------------------------------------------ # cleanup_lxc() # # - Cleans package manager and language caches (safe for installs AND updates) # - Supports Alpine (apk), Debian/Ubuntu (apt), Python, Node.js, Go, Rust, Ruby, PHP # - Uses fallback error handling to prevent cleanup failures from breaking installs # ------------------------------------------------------------------------------ cleanup_lxc() { msg_info "Cleaning up" if is_alpine; then $STD apk cache clean || true rm -rf /var/cache/apk/* else $STD apt -y autoremove 2>/dev/null || msg_warn "apt autoremove failed (non-critical)" $STD apt -y autoclean 2>/dev/null || msg_warn "apt autoclean failed (non-critical)" $STD apt -y clean 2>/dev/null || msg_warn "apt clean failed (non-critical)" fi find /tmp /var/tmp -type f -name 'tmp*' -delete 2>/dev/null || true find /tmp /var/tmp -type f -name 'tempfile*' -delete 2>/dev/null || true # Python if command -v pip &>/dev/null; then rm -rf /root/.cache/pip 2>/dev/null || true fi if command -v uv &>/dev/null; then rm -rf /root/.cache/uv 2>/dev/null || true fi # Node.js if command -v npm &>/dev/null; then rm -rf /root/.npm/_cacache /root/.npm/_logs 2>/dev/null || true fi if command -v yarn &>/dev/null; then rm -rf /root/.cache/yarn /root/.yarn/cache 2>/dev/null || true fi if command -v pnpm &>/dev/null; then pnpm store prune &>/dev/null || true fi # Go (only build cache, not modules) if command -v go &>/dev/null; then $STD go clean -cache 2>/dev/null || true fi # Rust (only registry cache, not build artifacts) if command -v cargo &>/dev/null; then rm -rf /root/.cargo/registry/cache /root/.cargo/.package-cache 2>/dev/null || true fi # Ruby if command -v gem &>/dev/null; then rm -rf /root/.gem/cache 2>/dev/null || true fi # PHP if command -v composer &>/dev/null; then rm -rf /root/.composer/cache 2>/dev/null || true fi msg_ok "Cleaned" # Send progress ping if available (defined in install.func) if declare -f post_progress_to_api &>/dev/null; then post_progress_to_api fi } # ------------------------------------------------------------------------------ # check_or_create_swap() # # - Checks if swap is active on system # - Offers to create swap file if none exists # - Prompts user for swap size in MB # - Creates /swapfile with specified size # - Activates swap immediately # - Returns 0 if swap active or successfully created, 1 if declined/failed # ------------------------------------------------------------------------------ check_or_create_swap() { msg_info "Checking for active swap" if swapon --noheadings --show | grep -q 'swap'; then msg_ok "Swap is active" return 0 fi msg_error "No active swap detected" if ! prompt_confirm "Do you want to create a swap file?" "n" 60; then msg_info "Skipping swap file creation" return 1 fi local swap_size_mb swap_size_mb=$(prompt_input "Enter swap size in MB (e.g., 2048 for 2GB):" "2048" 60) if ! [[ "$swap_size_mb" =~ ^[0-9]+$ ]]; then msg_error "Invalid swap size: '${swap_size_mb}' (must be a number in MB)" return 65 fi local swap_file="/swapfile" msg_info "Creating ${swap_size_mb}MB swap file at $swap_file" if ! dd if=/dev/zero of="$swap_file" bs=1M count="$swap_size_mb" status=progress; then msg_error "Failed to allocate swap file (dd failed)" return 150 fi if ! chmod 600 "$swap_file"; then msg_error "Failed to set permissions on $swap_file" return 150 fi if ! mkswap "$swap_file"; then msg_error "Failed to format swap file (mkswap failed)" return 150 fi if ! swapon "$swap_file"; then msg_error "Failed to activate swap (swapon failed)" return 150 fi msg_ok "Swap file created and activated successfully" } # ------------------------------------------------------------------------------ # Loads LOCAL_IP from persistent store or detects if missing. # # Description: # - Loads from /run/local-ip.env or performs runtime lookup # ------------------------------------------------------------------------------ function get_lxc_ip() { local IP_FILE="/run/local-ip.env" if [[ -f "$IP_FILE" ]]; then # shellcheck disable=SC1090 source "$IP_FILE" fi if [[ -z "${LOCAL_IP:-}" ]]; then get_current_ip() { local ip # Try direct interface lookup for eth0 FIRST (most reliable for LXC) - IPv4 ip=$(ip -4 addr show eth0 2>/dev/null | awk '/inet / {print $2}' | cut -d/ -f1 | head -n1) if [[ -n "$ip" && "$ip" =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then echo "$ip" return 0 fi # Fallback: Try hostname -I (returns IPv4 first if available) if command -v hostname >/dev/null 2>&1; then ip=$(hostname -I 2>/dev/null | awk '{print $1}') if [[ -n "$ip" && "$ip" =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then echo "$ip" return 0 fi fi # Try routing table with IPv4 targets local ipv4_targets=("8.8.8.8" "1.1.1.1" "default") for target in "${ipv4_targets[@]}"; do if [[ "$target" == "default" ]]; then ip=$(ip route get 1 2>/dev/null | awk '{for(i=1;i<=NF;i++) if ($i=="src") print $(i+1)}') else ip=$(ip route get "$target" 2>/dev/null | awk '{for(i=1;i<=NF;i++) if ($i=="src") print $(i+1)}') fi if [[ -n "$ip" ]]; then echo "$ip" return 0 fi done # IPv6 fallback: Try direct interface lookup for eth0 ip=$(ip -6 addr show eth0 scope global 2>/dev/null | awk '/inet6 / {print $2}' | cut -d/ -f1 | head -n1) if [[ -n "$ip" && "$ip" =~ : ]]; then echo "$ip" return 0 fi # IPv6 fallback: Try hostname -I for IPv6 if command -v hostname >/dev/null 2>&1; then ip=$(hostname -I 2>/dev/null | tr ' ' '\n' | grep -E ':' | head -n1) if [[ -n "$ip" && "$ip" =~ : ]]; then echo "$ip" return 0 fi fi # IPv6 fallback: Use routing table with IPv6 targets local ipv6_targets=("2001:4860:4860::8888" "2606:4700:4700::1111") for target in "${ipv6_targets[@]}"; do ip=$(ip -6 route get "$target" 2>/dev/null | awk '{for(i=1;i<=NF;i++) if ($i=="src") print $(i+1)}') if [[ -n "$ip" && "$ip" =~ : ]]; then echo "$ip" return 0 fi done return 6 } LOCAL_IP="$(get_current_ip || true)" if [[ -z "$LOCAL_IP" ]]; then msg_error "Could not determine LOCAL_IP (checked: eth0, hostname -I, ip route, IPv6 targets)" return 6 fi fi export LOCAL_IP } # ============================================================================== # SIGNAL TRAPS # ============================================================================== trap 'stop_spinner' EXIT INT TERM ================================================ FILE: misc/error_handler.func ================================================ #!/usr/bin/env bash # ------------------------------------------------------------------------------ # ERROR HANDLER - ERROR & SIGNAL MANAGEMENT # ------------------------------------------------------------------------------ # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # ------------------------------------------------------------------------------ # # Provides comprehensive error handling and signal management for all scripts. # Includes: # - Exit code explanations (shell, package managers, databases, custom codes) # - Error handler with detailed logging # - Signal handlers (EXIT, INT, TERM) # - Initialization function for trap setup # # Usage: # source <(curl -fsSL .../error_handler.func) # catch_errors # # ------------------------------------------------------------------------------ # ============================================================================== # SECTION 1: EXIT CODE EXPLANATIONS # ============================================================================== # ------------------------------------------------------------------------------ # explain_exit_code() # # - Canonical version is defined in api.func (sourced before this file) # - This section only provides a fallback if api.func was not loaded # - See api.func SECTION 1 for the authoritative exit code mappings # ------------------------------------------------------------------------------ if ! declare -f explain_exit_code &>/dev/null; then explain_exit_code() { local code="$1" case "$code" in 1) echo "General error / Operation not permitted" ;; 2) echo "Misuse of shell builtins (e.g. syntax error)" ;; 3) echo "General syntax or argument error" ;; 10) echo "Docker / privileged mode required (unsupported environment)" ;; 4) echo "curl: Feature not supported or protocol error" ;; 5) echo "curl: Could not resolve proxy" ;; 6) echo "curl: DNS resolution failed (could not resolve host)" ;; 7) echo "curl: Failed to connect (network unreachable / host down)" ;; 8) echo "curl: Server reply error (FTP/SFTP or apk untrusted key)" ;; 16) echo "curl: HTTP/2 framing layer error" ;; 18) echo "curl: Partial file (transfer not completed)" ;; 22) echo "curl: HTTP error returned (404, 429, 500+)" ;; 23) echo "curl: Write error (disk full or permissions)" ;; 24) echo "curl: Write to local file failed" ;; 25) echo "curl: Upload failed" ;; 26) echo "curl: Read error on local file (I/O)" ;; 27) echo "curl: Out of memory (memory allocation failed)" ;; 28) echo "curl: Operation timeout (network slow or server not responding)" ;; 30) echo "curl: FTP port command failed" ;; 32) echo "curl: FTP SIZE command failed" ;; 33) echo "curl: HTTP range error" ;; 34) echo "curl: HTTP post error" ;; 35) echo "curl: SSL/TLS handshake failed (certificate error)" ;; 36) echo "curl: FTP bad download resume" ;; 39) echo "curl: LDAP search failed" ;; 44) echo "curl: Internal error (bad function call order)" ;; 45) echo "curl: Interface error (failed to bind to specified interface)" ;; 46) echo "curl: Bad password entered" ;; 47) echo "curl: Too many redirects" ;; 48) echo "curl: Unknown command line option specified" ;; 51) echo "curl: SSL peer certificate or SSH host key verification failed" ;; 52) echo "curl: Empty reply from server (got nothing)" ;; 55) echo "curl: Failed sending network data" ;; 56) echo "curl: Receive error (connection reset by peer)" ;; 57) echo "curl: Unrecoverable poll/select error (system I/O failure)" ;; 59) echo "curl: Couldn't use specified SSL cipher" ;; 61) echo "curl: Bad/unrecognized transfer encoding" ;; 63) echo "curl: Maximum file size exceeded" ;; 75) echo "Temporary failure (retry later)" ;; 78) echo "curl: Remote file not found (404 on FTP/file)" ;; 79) echo "curl: SSH session error (key exchange/auth failed)" ;; 92) echo "curl: HTTP/2 stream error (protocol violation)" ;; 95) echo "curl: HTTP/3 layer error" ;; 64) echo "Usage error (wrong arguments)" ;; 65) echo "Data format error (bad input data)" ;; 66) echo "Input file not found (cannot open input)" ;; 67) echo "User not found (addressee unknown)" ;; 68) echo "Host not found (hostname unknown)" ;; 69) echo "Service unavailable" ;; 70) echo "Internal software error" ;; 71) echo "System error (OS-level failure)" ;; 72) echo "Critical OS file missing" ;; 73) echo "Cannot create output file" ;; 74) echo "I/O error" ;; 76) echo "Remote protocol error" ;; 77) echo "Permission denied" ;; 100) echo "APT: Package manager error (broken packages / dependency problems)" ;; 101) echo "APT: Configuration error (bad sources.list, malformed config)" ;; 102) echo "APT: Lock held by another process (dpkg/apt still running)" ;; # --- Script Validation & Setup (103-123) --- 103) echo "Validation: Shell is not Bash" ;; 104) echo "Validation: Not running as root (or invoked via sudo)" ;; 105) echo "Validation: Proxmox VE version not supported" ;; 106) echo "Validation: Architecture not supported (ARM / PiMox)" ;; 107) echo "Validation: Kernel key parameters unreadable" ;; 108) echo "Validation: Kernel key limits exceeded" ;; 109) echo "Proxmox: No available container ID after max attempts" ;; 110) echo "Proxmox: Failed to apply default.vars" ;; 111) echo "Proxmox: App defaults file not available" ;; 112) echo "Proxmox: Invalid install menu option" ;; 113) echo "LXC: Under-provisioned — user aborted update" ;; 114) echo "LXC: Storage too low — user aborted update" ;; 115) echo "Download: install.func download failed or incomplete" ;; 116) echo "Proxmox: Default bridge vmbr0 not found" ;; 117) echo "LXC: Container did not reach running state" ;; 118) echo "LXC: No IP assigned to container after timeout" ;; 119) echo "Proxmox: No valid storage for rootdir content" ;; 120) echo "Proxmox: No valid storage for vztmpl content" ;; 121) echo "LXC: Container network not ready (no IP after retries)" ;; 122) echo "LXC: No internet connectivity — user declined to continue" ;; 123) echo "LXC: Local IP detection failed" ;; 124) echo "Command timed out (timeout command)" ;; 125) echo "Command failed to start (Docker daemon or execution error)" ;; 126) echo "Command invoked cannot execute (permission problem?)" ;; 127) echo "Command not found" ;; 128) echo "Invalid argument to exit" ;; 129) echo "Killed by SIGHUP (terminal closed / hangup)" ;; 130) echo "Aborted by user (SIGINT)" ;; 131) echo "Killed by SIGQUIT (core dumped)" ;; 132) echo "Killed by SIGILL (illegal CPU instruction)" ;; 134) echo "Process aborted (SIGABRT - possibly Node.js heap overflow)" ;; 137) echo "Killed (SIGKILL / Out of memory?)" ;; 139) echo "Segmentation fault (core dumped)" ;; 141) echo "Broken pipe (SIGPIPE - output closed prematurely)" ;; 143) echo "Terminated (SIGTERM)" ;; 144) echo "Killed by signal 16 (SIGUSR1 / SIGSTKFLT)" ;; 146) echo "Killed by signal 18 (SIGTSTP)" ;; 150) echo "Systemd: Service failed to start" ;; 151) echo "Systemd: Service unit not found" ;; 152) echo "Permission denied (EACCES)" ;; 153) echo "Build/compile failed (make/gcc/cmake)" ;; 154) echo "Node.js: Native addon build failed (node-gyp)" ;; 160) echo "Python: Virtualenv / uv environment missing or broken" ;; 161) echo "Python: Dependency resolution failed" ;; 162) echo "Python: Installation aborted (permissions or EXTERNALLY-MANAGED)" ;; 170) echo "PostgreSQL: Connection failed (server not running / wrong socket)" ;; 171) echo "PostgreSQL: Authentication failed (bad user/password)" ;; 172) echo "PostgreSQL: Database does not exist" ;; 173) echo "PostgreSQL: Fatal error in query / syntax" ;; 180) echo "MySQL/MariaDB: Connection failed (server not running / wrong socket)" ;; 181) echo "MySQL/MariaDB: Authentication failed (bad user/password)" ;; 182) echo "MySQL/MariaDB: Database does not exist" ;; 183) echo "MySQL/MariaDB: Fatal error in query / syntax" ;; 190) echo "MongoDB: Connection failed (server not running)" ;; 191) echo "MongoDB: Authentication failed (bad user/password)" ;; 192) echo "MongoDB: Database not found" ;; 193) echo "MongoDB: Fatal query error" ;; 200) echo "Proxmox: Failed to create lock file" ;; 203) echo "Proxmox: Missing CTID variable" ;; 204) echo "Proxmox: Missing PCT_OSTYPE variable" ;; 205) echo "Proxmox: Invalid CTID (<100)" ;; 206) echo "Proxmox: CTID already in use" ;; 207) echo "Proxmox: Password contains unescaped special characters" ;; 208) echo "Proxmox: Invalid configuration (DNS/MAC/Network format)" ;; 209) echo "Proxmox: Container creation failed" ;; 210) echo "Proxmox: Cluster not quorate" ;; 211) echo "Proxmox: Timeout waiting for template lock" ;; 212) echo "Proxmox: Storage type 'iscsidirect' does not support containers (VMs only)" ;; 213) echo "Proxmox: Storage type does not support 'rootdir' content" ;; 214) echo "Proxmox: Not enough storage space" ;; 215) echo "Proxmox: Container created but not listed (ghost state)" ;; 216) echo "Proxmox: RootFS entry missing in config" ;; 217) echo "Proxmox: Storage not accessible" ;; 218) echo "Proxmox: Template file corrupted or incomplete" ;; 219) echo "Proxmox: CephFS does not support containers - use RBD" ;; 220) echo "Proxmox: Unable to resolve template path" ;; 221) echo "Proxmox: Template file not readable" ;; 222) echo "Proxmox: Template download failed" ;; 223) echo "Proxmox: Template not available after download" ;; 224) echo "Proxmox: PBS storage is for backups only" ;; 225) echo "Proxmox: No template available for OS/Version" ;; 231) echo "Proxmox: LXC stack upgrade failed" ;; # --- Tools & Addon Scripts (232-238) --- 232) echo "Tools: Wrong execution environment (run on PVE host, not inside LXC)" ;; 233) echo "Tools: Application not installed (update prerequisite missing)" ;; 234) echo "Tools: No LXC containers found or available" ;; 235) echo "Tools: Backup or restore operation failed" ;; 236) echo "Tools: Required hardware not detected" ;; 237) echo "Tools: Dependency package installation failed" ;; 238) echo "Tools: OS or distribution not supported for this addon" ;; 239) echo "npm/Node.js: Unexpected runtime error or dependency failure" ;; 243) echo "Node.js: Out of memory (JavaScript heap out of memory)" ;; 245) echo "Node.js: Invalid command-line option" ;; 246) echo "Node.js: Internal JavaScript Parse Error" ;; 247) echo "Node.js: Fatal internal error" ;; 248) echo "Node.js: Invalid C++ addon / N-API failure" ;; 249) echo "npm/pnpm/yarn: Unknown fatal error" ;; # --- Application Install/Update Errors (250-254) --- 250) echo "App: Download failed or version not determined" ;; 251) echo "App: File extraction failed (corrupt or incomplete archive)" ;; 252) echo "App: Required file or resource not found" ;; 253) echo "App: Data migration required — update aborted" ;; 254) echo "App: User declined prompt or input timed out" ;; 255) echo "DPKG: Fatal internal error" ;; *) echo "Unknown error" ;; esac } fi # ============================================================================== # SECTION 2: ERROR HANDLERS # ============================================================================== # ------------------------------------------------------------------------------ # error_handler() # # - Main error handler triggered by ERR trap # - Arguments: exit_code, command, line_number # - Behavior: # * Returns silently if exit_code is 0 (success) # * Sources explain_exit_code() for detailed error description # * Displays error message with: # - Line number where error occurred # - Exit code with explanation # - Command that failed # * Shows last 20 lines of SILENT_LOGFILE if available # * Copies log to container /root for later inspection # * Exits with original exit code # ------------------------------------------------------------------------------ error_handler() { local exit_code=${1:-$?} local command=${2:-${BASH_COMMAND:-unknown}} local line_number=${BASH_LINENO[0]:-unknown} command="${command//\$STD/}" # If error originated from silent(), use its captured metadata # This provides the actual command and line number instead of "silent ..." if [[ -n "${_SILENT_FAILED_RC:-}" ]]; then exit_code="$_SILENT_FAILED_RC" command="$_SILENT_FAILED_CMD" line_number="$_SILENT_FAILED_LINE" # Clear flags to prevent stale data on subsequent errors unset _SILENT_FAILED_RC _SILENT_FAILED_CMD _SILENT_FAILED_LINE fi if [[ "$exit_code" -eq 0 ]]; then return 0 fi # Stop spinner and restore cursor FIRST — before any output # This prevents spinner text overlapping with error messages if declare -f stop_spinner >/dev/null 2>&1; then stop_spinner 2>/dev/null || true fi printf "\e[?25h" local explanation explanation="$(explain_exit_code "$exit_code")" # ALWAYS report failure to API immediately - don't wait for container checks # This ensures we capture failures that occur before/after container exists if declare -f post_update_to_api &>/dev/null; then post_update_to_api "failed" "$exit_code" 2>/dev/null || true else # Container context: post_update_to_api not available (api.func not sourced) # Send status directly via curl so container failures are never lost _send_abort_telemetry "$exit_code" 2>/dev/null || true fi # Use msg_error if available, fallback to echo if declare -f msg_error >/dev/null 2>&1; then msg_error "in line ${line_number}: exit code ${exit_code} (${explanation}): while executing command ${command}" else echo -e "\n${RD}[ERROR]${CL} in line ${RD}${line_number}${CL}: exit code ${RD}${exit_code}${CL} (${explanation}): while executing command ${YWB}${command}${CL}\n" fi if [[ -n "${DEBUG_LOGFILE:-}" ]]; then { echo "------ ERROR ------" echo "Timestamp : $(date '+%Y-%m-%d %H:%M:%S')" echo "Exit Code : $exit_code ($explanation)" echo "Line : $line_number" echo "Command : $command" echo "-------------------" } >>"$DEBUG_LOGFILE" fi # Get active log file (BUILD_LOG or INSTALL_LOG) # Prefer silent()'s logfile when available (contains the actual command output) local active_log="" if [[ -n "${_SILENT_FAILED_LOG:-}" && -s "${_SILENT_FAILED_LOG}" ]]; then active_log="$_SILENT_FAILED_LOG" unset _SILENT_FAILED_LOG elif declare -f get_active_logfile >/dev/null 2>&1; then active_log="$(get_active_logfile)" elif [[ -n "${SILENT_LOGFILE:-}" ]]; then active_log="$SILENT_LOGFILE" fi # If active_log points to a container-internal path that doesn't exist on host, # fall back to BUILD_LOG (host-side log) if [[ -n "$active_log" && ! -s "$active_log" && -n "${BUILD_LOG:-}" && -s "${BUILD_LOG}" ]]; then active_log="$BUILD_LOG" fi # Show last log lines if available if [[ -n "$active_log" && -s "$active_log" ]]; then echo -e "\n${TAB}--- Last 20 lines of log ---" tail -n 20 "$active_log" echo -e "${TAB}-----------------------------------\n" fi # Detect probable Node.js heap OOM and print actionable guidance. # This avoids generic SIGABRT/SIGKILL confusion for frontend build failures. local node_oom_detected="false" local node_build_context="false" if [[ "$command" =~ (npm|pnpm|yarn|node|vite|turbo) ]]; then node_build_context="true" fi if [[ "$exit_code" == "243" ]]; then node_oom_detected="true" elif [[ -n "$active_log" && -s "$active_log" ]]; then if tail -n 200 "$active_log" 2>/dev/null | grep -Eqi 'Reached heap limit|JavaScript heap out of memory|Allocation failed - JavaScript heap out of memory|FATAL ERROR: Reached heap limit'; then node_oom_detected="true" fi fi if [[ "$node_oom_detected" == "true" ]] || { [[ "$node_build_context" == "true" ]] && [[ "$exit_code" =~ ^(134|137)$ ]]; }; then local heap_hint_mb="" # If explicitly configured, prefer the current value for troubleshooting output. if [[ -n "${NODE_OPTIONS:-}" ]] && [[ "${NODE_OPTIONS}" =~ max-old-space-size=([0-9]+) ]]; then heap_hint_mb="${BASH_REMATCH[1]}" elif [[ -n "${var_ram:-}" ]] && [[ "${var_ram}" =~ ^[0-9]+$ ]]; then heap_hint_mb=$((var_ram * 75 / 100)) else local mem_kb="" mem_kb=$(awk '/^MemTotal:/ {print $2; exit}' /proc/meminfo 2>/dev/null || echo "") if [[ "$mem_kb" =~ ^[0-9]+$ ]]; then local mem_mb=$((mem_kb / 1024)) heap_hint_mb=$((mem_mb * 75 / 100)) fi fi if [[ -z "$heap_hint_mb" ]] || ((heap_hint_mb < 1024)); then heap_hint_mb=1024 elif ((heap_hint_mb > 12288)); then heap_hint_mb=12288 fi if declare -f msg_warn >/dev/null 2>&1; then msg_warn "Possible Node.js heap OOM. Try: export NODE_OPTIONS=\"--max-old-space-size=${heap_hint_mb}\" and rerun the build." else echo -e "${YW}Possible Node.js heap OOM. Try: export NODE_OPTIONS=\"--max-old-space-size=${heap_hint_mb}\" and rerun the build.${CL}" fi fi # Detect context: Container (INSTALL_LOG set + inside container /root) vs Host if [[ -n "${INSTALL_LOG:-}" && -f "${INSTALL_LOG:-}" && -d /root ]]; then # CONTAINER CONTEXT: Copy log and create flag file for host local container_log="/root/.install-${SESSION_ID:-error}.log" cp "${INSTALL_LOG}" "$container_log" 2>/dev/null || true # Create error flag file with exit code for host detection echo "$exit_code" >"/root/.install-${SESSION_ID:-error}.failed" 2>/dev/null || true # Log path is shown by host as combined log - no need to show container path else # HOST CONTEXT: Show local log path and offer container cleanup if [[ -n "$active_log" && -s "$active_log" ]]; then if declare -f msg_custom >/dev/null 2>&1; then msg_custom "📋" "${YW}" "Full log: ${active_log}" else echo -e "${YW}Full log:${CL} ${BL}${active_log}${CL}" fi fi # Offer to remove container if it exists (build errors after container creation) if [[ -n "${CTID:-}" ]] && command -v pct &>/dev/null && pct status "$CTID" &>/dev/null; then echo "" if declare -f msg_custom >/dev/null 2>&1; then echo -en "${TAB}❓${TAB}${YW}Remove broken container ${CTID}? (Y/n) [auto-remove in 60s]: ${CL}" else echo -en "${YW}Remove broken container ${CTID}? (Y/n) [auto-remove in 60s]: ${CL}" fi # Read user response local response="" if read -t 60 -r response; then if [[ -z "$response" || "$response" =~ ^[Yy]$ ]]; then echo "" if declare -f msg_info >/dev/null 2>&1; then msg_info "Removing container ${CTID}" else echo -e "${YW}Removing container ${CTID}${CL}" fi pct stop "$CTID" &>/dev/null || true pct destroy "$CTID" &>/dev/null || true if declare -f msg_ok >/dev/null 2>&1; then msg_ok "Container ${CTID} removed" else echo -e "${GN}✔${CL} Container ${CTID} removed" fi elif [[ "$response" =~ ^[Nn]$ ]]; then echo "" if declare -f msg_warn >/dev/null 2>&1; then msg_warn "Container ${CTID} kept for debugging" else echo -e "${YW}Container ${CTID} kept for debugging${CL}" fi fi else # Timeout - auto-remove echo "" if declare -f msg_info >/dev/null 2>&1; then msg_info "No response - removing container ${CTID}" else echo -e "${YW}No response - removing container ${CTID}${CL}" fi pct stop "$CTID" &>/dev/null || true pct destroy "$CTID" &>/dev/null || true if declare -f msg_ok >/dev/null 2>&1; then msg_ok "Container ${CTID} removed" else echo -e "${GN}✔${CL} Container ${CTID} removed" fi fi # Force one final status update attempt after cleanup # This ensures status is updated even if the first attempt failed (e.g., HTTP 400) if declare -f post_update_to_api &>/dev/null; then post_update_to_api "failed" "$exit_code" "force" fi fi fi exit "$exit_code" } # ============================================================================== # SECTION 3: TELEMETRY & CLEANUP HELPERS FOR SIGNAL HANDLERS # ============================================================================== # ------------------------------------------------------------------------------ # _send_abort_telemetry() # # - Sends failure/abort status to telemetry API # - Works in BOTH host context (post_update_to_api available) and # container context (only curl available, api.func not sourced) # - Container context is critical: without this, container-side failures # and signal exits are never reported, leaving records stuck in # "installing" or "configuring" forever # - Arguments: $1 = exit_code # ------------------------------------------------------------------------------ _send_abort_telemetry() { local exit_code="${1:-1}" # Try full API function first (host context - api.func sourced) if declare -f post_update_to_api &>/dev/null; then post_update_to_api "failed" "$exit_code" 2>/dev/null || true return fi # Fallback: direct curl (container context - api.func NOT sourced) # This is the ONLY way containers can report failures to telemetry command -v curl &>/dev/null || return 0 [[ "${DIAGNOSTICS:-no}" == "no" ]] && return 0 [[ -z "${RANDOM_UUID:-}" ]] && return 0 # Collect last 200 log lines for error diagnosis (best-effort) # Container context has no get_full_log(), so we gather as much as possible local error_text="" local logfile="" if [[ -n "${INSTALL_LOG:-}" && -s "${INSTALL_LOG}" ]]; then logfile="${INSTALL_LOG}" elif [[ -n "${SILENT_LOGFILE:-}" && -s "${SILENT_LOGFILE}" ]]; then logfile="${SILENT_LOGFILE}" fi if [[ -n "$logfile" ]]; then error_text=$(tail -n 200 "$logfile" 2>/dev/null | sed 's/\x1b\[[0-9;]*[a-zA-Z]//g; s/\\/\\\\/g; s/"/\\"/g; s/\r//g' | tr '\n' '|' | sed 's/|$//' | head -c 16384 | tr -d '\000-\010\013\014\016-\037\177') || true fi # Prepend exit code explanation header (like build_error_string does on host) local explanation="" if declare -f explain_exit_code &>/dev/null; then explanation=$(explain_exit_code "$exit_code" 2>/dev/null) || true fi if [[ -n "$explanation" && -n "$error_text" ]]; then error_text="exit_code=${exit_code} | ${explanation}|---|${error_text}" elif [[ -n "$explanation" && -z "$error_text" ]]; then error_text="exit_code=${exit_code} | ${explanation}" fi # Calculate duration if start time is available local duration="" if [[ -n "${DIAGNOSTICS_START_TIME:-}" ]]; then duration=$(($(date +%s) - DIAGNOSTICS_START_TIME)) fi # Categorize error if function is available (may not be in minimal container context) local error_category="" if declare -f categorize_error &>/dev/null; then error_category=$(categorize_error "$exit_code" 2>/dev/null) || true fi # Build JSON payload with error context local payload payload="{\"random_id\":\"${RANDOM_UUID}\",\"execution_id\":\"${EXECUTION_ID:-${RANDOM_UUID}}\",\"type\":\"${TELEMETRY_TYPE:-lxc}\",\"nsapp\":\"${NSAPP:-${app:-unknown}}\",\"status\":\"failed\",\"exit_code\":${exit_code}" [[ -n "$error_text" ]] && payload="${payload},\"error\":\"${error_text}\"" [[ -n "$error_category" ]] && payload="${payload},\"error_category\":\"${error_category}\"" [[ -n "$duration" ]] && payload="${payload},\"duration\":${duration}" payload="${payload}}" local api_url="${TELEMETRY_URL:-https://telemetry.community-scripts.org/telemetry}" # 2 attempts (retry once on failure) — original had no retry local attempt for attempt in 1 2; do if curl -fsS -m 5 -X POST "$api_url" \ -H "Content-Type: application/json" \ -d "$payload" &>/dev/null; then return 0 fi [[ $attempt -eq 1 ]] && sleep 1 done return 0 } # ------------------------------------------------------------------------------ # _stop_container_if_installing() # # - Stops the LXC container if we're in the install phase # - Prevents orphaned container processes when the host exits due to a signal # (SSH disconnect, Ctrl+C, SIGTERM) — without this, the container keeps # running and may send "configuring" status AFTER the host already sent # "failed", leaving records permanently stuck in "configuring" # - Only acts when: # * CONTAINER_INSTALLING flag is set (during lxc-attach in build_container) # * CTID is set (container was created) # * pct command is available (we're on the Proxmox host, not inside a container) # - Does NOT destroy the container — just stops it for potential debugging # ------------------------------------------------------------------------------ _stop_container_if_installing() { [[ "${CONTAINER_INSTALLING:-}" == "true" ]] || return 0 [[ -n "${CTID:-}" ]] || return 0 command -v pct &>/dev/null || return 0 pct stop "$CTID" 2>/dev/null || true } # ============================================================================== # SECTION 4: SIGNAL HANDLERS # ============================================================================== # ------------------------------------------------------------------------------ # on_exit() # # - EXIT trap handler — runs on EVERY script termination # - Catches orphaned "installing"/"configuring" records: # * If post_to_api sent "installing" but post_update_to_api never ran # * Reports final status to prevent records stuck forever # - Best-effort log collection for failed installs # - Stops orphaned container processes on failure # - Cleans up lock files # ------------------------------------------------------------------------------ on_exit() { local exit_code=$? # Report orphaned telemetry records # Two scenarios handled: # 1. POST_TO_API_DONE=true but POST_UPDATE_DONE=false: Record was created but # never got a final status update → send abort/done now. # 2. POST_TO_API_DONE=false but DIAGNOSTICS=yes: Initial post failed (server # unreachable/timeout), but the server has fallback create-on-update logic, # so a status update can still create the record. Worth one last try. if [[ "${POST_UPDATE_DONE:-}" != "true" ]]; then if [[ "${POST_TO_API_DONE:-}" == "true" || "${DIAGNOSTICS:-no}" == "yes" ]]; then if [[ $exit_code -ne 0 ]]; then _send_abort_telemetry "$exit_code" elif [[ "${INSTALL_COMPLETE:-}" == "true" ]] && declare -f post_update_to_api >/dev/null 2>&1; then # Only report success if the install was explicitly marked complete. # Without this guard, early bailouts (e.g. user cancelled) with exit 0 # would be falsely reported as successful installations. post_update_to_api "done" "0" 2>/dev/null || true fi fi fi # Best-effort log collection on failure (non-critical, telemetry already sent) if [[ $exit_code -ne 0 ]] && declare -f ensure_log_on_host >/dev/null 2>&1; then ensure_log_on_host 2>/dev/null || true fi # Stop orphaned container if we're in the install phase and exiting with error if [[ $exit_code -ne 0 ]]; then _stop_container_if_installing fi [[ -n "${lockfile:-}" && -e "$lockfile" ]] && rm -f "$lockfile" exit "$exit_code" } # ------------------------------------------------------------------------------ # on_interrupt() # # - SIGINT (Ctrl+C) trap handler # - Reports status FIRST (time-critical: container may be dying) # - Stops orphaned container to prevent "configuring" ghost records # - Exits with code 130 (128 + SIGINT=2) # ------------------------------------------------------------------------------ on_interrupt() { # Stop spinner and restore cursor before any output if declare -f stop_spinner >/dev/null 2>&1; then stop_spinner 2>/dev/null || true fi printf "\e[?25h" 2>/dev/null || true _send_abort_telemetry "130" _stop_container_if_installing if declare -f msg_error >/dev/null 2>&1; then msg_error "Interrupted by user (SIGINT)" 2>/dev/null || true else echo -e "\n${RD}Interrupted by user (SIGINT)${CL}" 2>/dev/null || true fi exit 130 } # ------------------------------------------------------------------------------ # on_terminate() # # - SIGTERM trap handler # - Reports status FIRST (time-critical: process being killed) # - Stops orphaned container to prevent "configuring" ghost records # - Exits with code 143 (128 + SIGTERM=15) # ------------------------------------------------------------------------------ on_terminate() { # Stop spinner and restore cursor before any output if declare -f stop_spinner >/dev/null 2>&1; then stop_spinner 2>/dev/null || true fi printf "\e[?25h" 2>/dev/null || true _send_abort_telemetry "143" _stop_container_if_installing if declare -f msg_error >/dev/null 2>&1; then msg_error "Terminated by signal (SIGTERM)" 2>/dev/null || true else echo -e "\n${RD}Terminated by signal (SIGTERM)${CL}" 2>/dev/null || true fi exit 143 } # ------------------------------------------------------------------------------ # on_hangup() # # - SIGHUP trap handler (SSH disconnect, terminal closed) # - CRITICAL: This was previously MISSING from catch_errors(), causing # container processes to become orphans on SSH disconnect — the #1 cause # of records stuck in "installing" and "configuring" states # - Reports status via direct curl (terminal is already closed, no output) # - Stops orphaned container to prevent ghost records # - Exits with code 129 (128 + SIGHUP=1) # ------------------------------------------------------------------------------ on_hangup() { # Stop spinner (no cursor restore needed — terminal is already gone) if declare -f stop_spinner >/dev/null 2>&1; then stop_spinner 2>/dev/null || true fi _send_abort_telemetry "129" _stop_container_if_installing exit 129 } # ============================================================================== # SECTION 5: INITIALIZATION # ============================================================================== # ------------------------------------------------------------------------------ # catch_errors() # # - Initializes error handling and signal traps # - Enables strict error handling: # * set -Ee: Exit on error, inherit ERR trap in functions # * set -o pipefail: Pipeline fails if any command fails # * set -u: (optional) Exit on undefined variable (if STRICT_UNSET=1) # - Sets up traps: # * ERR → error_handler (script errors) # * EXIT → on_exit (any termination — cleanup + orphan detection) # * INT → on_interrupt (Ctrl+C) # * TERM → on_terminate (kill / systemd stop) # * HUP → on_hangup (SSH disconnect / terminal closed) # - Call this function early in every script # ------------------------------------------------------------------------------ catch_errors() { set -Ee -o pipefail if [ "${STRICT_UNSET:-0}" = "1" ]; then set -u fi trap 'error_handler' ERR trap on_exit EXIT trap on_interrupt INT trap on_terminate TERM trap on_hangup HUP } ================================================ FILE: misc/install.func ================================================ # Copyright (c) 2021-2026 community-scripts ORG # Author: tteck (tteckster) # Co-Author: MickLesk # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # ============================================================================== # INSTALL.FUNC - CONTAINER INSTALLATION & SETUP # ============================================================================== # # This file provides installation functions executed inside LXC containers # after creation. Handles: # # - Network connectivity verification (IPv4/IPv6) # - OS updates and package installation # - DNS resolution checks # - MOTD and SSH configuration # - Container customization and auto-login # # Usage: # - Sourced by -install.sh scripts # - Executes via pct exec inside container # - Requires internet connectivity # # ============================================================================== # ============================================================================== # SECTION 1: INITIALIZATION # ============================================================================== if ! command -v curl >/dev/null 2>&1; then printf "\r\e[2K%b" '\033[93m Setup Source \033[m' >&2 apt update >/dev/null 2>&1 apt install -y curl >/dev/null 2>&1 fi source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/core.func) source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/error_handler.func) load_functions catch_errors # Persist diagnostics setting inside container (exported from build.func) # so addon scripts running later can find the user's choice if [[ ! -f /usr/local/community-scripts/diagnostics ]]; then mkdir -p /usr/local/community-scripts echo "DIAGNOSTICS=${DIAGNOSTICS:-no}" >/usr/local/community-scripts/diagnostics fi # Get LXC IP address (must be called INSIDE container, after network is up) get_lxc_ip # ------------------------------------------------------------------------------ # post_progress_to_api() # # - Lightweight progress ping from inside the container # - Updates the existing telemetry record status # - Arguments: # * $1: status (optional, default: "configuring") # - Signals that the installation is actively progressing (not stuck) # - Fire-and-forget: never blocks or fails the script # - Only executes if DIAGNOSTICS=yes and RANDOM_UUID is set # ------------------------------------------------------------------------------ post_progress_to_api() { command -v curl &>/dev/null || return 0 [[ "${DIAGNOSTICS:-no}" == "no" ]] && return 0 [[ -z "${RANDOM_UUID:-}" ]] && return 0 local progress_status="${1:-configuring}" curl -fsS -m 5 -X POST "https://telemetry.community-scripts.org/telemetry" \ -H "Content-Type: application/json" \ -d "{\"random_id\":\"${RANDOM_UUID}\",\"execution_id\":\"${EXECUTION_ID:-${RANDOM_UUID}}\",\"type\":\"lxc\",\"nsapp\":\"${app:-unknown}\",\"status\":\"${progress_status}\"}" &>/dev/null || true } # ============================================================================== # SECTION 2: NETWORK & CONNECTIVITY # ============================================================================== # ------------------------------------------------------------------------------ # verb_ip6() # # - Configures IPv6 based on DISABLEIPV6 variable # - If DISABLEIPV6=yes: disables IPv6 via sysctl # - Sets verbose mode via set_std_mode() # ------------------------------------------------------------------------------ verb_ip6() { set_std_mode # Set STD mode based on VERBOSE if [ "${IPV6_METHOD:-}" = "disable" ]; then msg_info "Disabling IPv6 (this may affect some services)" mkdir -p /etc/sysctl.d $STD tee /etc/sysctl.d/99-disable-ipv6.conf >/dev/null </dev/null) || true fi for ((i = RETRY_NUM; i > 0; i--)); do if [ "$(hostname -I)" != "" ]; then break fi echo 1>&2 -en "${CROSS}${RD} No Network! " sleep $RETRY_EVERY done if [ "$(hostname -I)" = "" ]; then echo 1>&2 -e "\n${CROSS}${RD} No Network After $RETRY_NUM Tries${CL}" echo -e "${NETWORK}Check Network Settings" exit 121 fi rm -rf /usr/lib/python3.*/EXTERNALLY-MANAGED systemctl disable -q --now systemd-networkd-wait-online.service msg_ok "Set up Container OS" #msg_custom "${CM}" "${GN}" "Network Connected: ${BL}$(hostname -I)" msg_ok "Network Connected: ${BL}$(hostname -I)" post_progress_to_api } # ------------------------------------------------------------------------------ # network_check() # # - Comprehensive network connectivity check for IPv4 and IPv6 # - Tests connectivity to multiple DNS servers: # * IPv4: 1.1.1.1 (Cloudflare), 8.8.8.8 (Google), 9.9.9.9 (Quad9) # * IPv6: 2606:4700:4700::1111, 2001:4860:4860::8888, 2620:fe::fe # - Verifies DNS resolution for GitHub and Community-Scripts domains # - Prompts user to continue if no internet detected # - Uses fatal() on DNS resolution failure for critical hosts # ------------------------------------------------------------------------------ network_check() { set +e trap - ERR ipv4_connected=false ipv6_connected=false sleep 1 # Check IPv4 connectivity to Google, Cloudflare & Quad9 DNS servers. if ping -c 1 -W 1 1.1.1.1 &>/dev/null || ping -c 1 -W 1 8.8.8.8 &>/dev/null || ping -c 1 -W 1 9.9.9.9 &>/dev/null; then msg_ok "IPv4 Internet Connected" ipv4_connected=true else msg_error "IPv4 Internet Not Connected" fi # Check IPv6 connectivity to Google, Cloudflare & Quad9 DNS servers. if ping6 -c 1 -W 1 2606:4700:4700::1111 &>/dev/null || ping6 -c 1 -W 1 2001:4860:4860::8888 &>/dev/null || ping6 -c 1 -W 1 2620:fe::fe &>/dev/null; then msg_ok "IPv6 Internet Connected" ipv6_connected=true else msg_error "IPv6 Internet Not Connected" fi # If both IPv4 and IPv6 checks fail, prompt the user if [[ $ipv4_connected == false && $ipv6_connected == false ]]; then read -r -p "No Internet detected, would you like to continue anyway? " prompt if [[ "${prompt,,}" =~ ^(y|yes)$ ]]; then echo -e "${INFO}${RD}Expect Issues Without Internet${CL}" else echo -e "${NETWORK}Check Network Settings" exit 122 fi fi # DNS resolution checks for GitHub-related domains (IPv4 and/or IPv6) GIT_HOSTS=("github.com" "raw.githubusercontent.com" "api.github.com" "git.community-scripts.org") GIT_STATUS="Git DNS:" DNS_FAILED=false for HOST in "${GIT_HOSTS[@]}"; do RESOLVEDIP=$(getent hosts "$HOST" | awk '{ print $1 }' | grep -E '(^([0-9]{1,3}\.){3}[0-9]{1,3}$)|(^[a-fA-F0-9:]+$)' | head -n1) if [[ -z "$RESOLVEDIP" ]]; then GIT_STATUS+="$HOST:($DNSFAIL)" DNS_FAILED=true else GIT_STATUS+=" $HOST:($DNSOK)" fi done if [[ "$DNS_FAILED" == true ]]; then fatal "$GIT_STATUS" else msg_ok "$GIT_STATUS" fi set -e trap 'error_handler' ERR } # ============================================================================== # SECTION 3: OS UPDATE & PACKAGE MANAGEMENT # ============================================================================== # ------------------------------------------------------------------------------ # apt_update_safe() # # - Runs apt-get update with CDN mirror fallback # - On failure, detects distro (Debian/Ubuntu) and tries alternate mirrors # - Three-phase approach: global mirrors → primary mirror → regional mirrors # - Falls back to manual user prompt if all auto mirrors fail # - Detects hash mismatch, SSL errors, and generic apt failures # ------------------------------------------------------------------------------ apt_update_safe() { if $STD apt-get update; then return 0 fi local failed_mirror failed_mirror=$(grep -m1 -oP '(?<=URIs: https?://)[^/]+' /etc/apt/sources.list.d/debian.sources 2>/dev/null || grep -m1 -oP '(?<=deb https?://)[^/]+' /etc/apt/sources.list 2>/dev/null || echo "unknown") msg_warn "apt-get update failed (${failed_mirror}), trying alternate mirrors..." local distro distro=$(. /etc/os-release 2>/dev/null && echo "$ID" || echo "debian") local eu_mirrors us_mirrors ap_mirrors if [[ "$distro" == "ubuntu" ]]; then eu_mirrors="de.archive.ubuntu.com fr.archive.ubuntu.com se.archive.ubuntu.com nl.archive.ubuntu.com it.archive.ubuntu.com ch.archive.ubuntu.com mirrors.xtom.de" us_mirrors="us.archive.ubuntu.com archive.ubuntu.com mirrors.edge.kernel.org mirror.csclub.uwaterloo.ca mirrors.ocf.berkeley.edu mirror.math.princeton.edu" ap_mirrors="au.archive.ubuntu.com jp.archive.ubuntu.com kr.archive.ubuntu.com tw.archive.ubuntu.com mirror.aarnet.edu.au" else eu_mirrors="ftp.de.debian.org ftp.fr.debian.org ftp.nl.debian.org ftp.uk.debian.org ftp.ch.debian.org ftp.se.debian.org ftp.it.debian.org ftp.fau.de ftp.halifax.rwth-aachen.de debian.mirror.lrz.de mirror.init7.net debian.ethz.ch mirrors.dotsrc.org debian.mirrors.ovh.net" us_mirrors="ftp.us.debian.org ftp.ca.debian.org debian.csail.mit.edu mirrors.ocf.berkeley.edu mirrors.wikimedia.org debian.osuosl.org mirror.cogentco.com" ap_mirrors="ftp.au.debian.org ftp.jp.debian.org ftp.tw.debian.org ftp.kr.debian.org ftp.hk.debian.org ftp.sg.debian.org mirror.aarnet.edu.au mirror.nitc.ac.in" fi local tz regional others tz=$(cat /etc/timezone 2>/dev/null || echo "UTC") case "$tz" in Europe/* | Arctic/*) regional="$eu_mirrors" others="$us_mirrors $ap_mirrors" ;; America/*) regional="$us_mirrors" others="$eu_mirrors $ap_mirrors" ;; Asia/* | Australia/* | Pacific/*) regional="$ap_mirrors" others="$eu_mirrors $us_mirrors" ;; *) regional="" others="$eu_mirrors $us_mirrors $ap_mirrors" ;; esac echo 'Acquire::By-Hash "no";' >/etc/apt/apt.conf.d/99no-by-hash _try_apt_mirror() { local m=$1 for src in /etc/apt/sources.list.d/debian.sources /etc/apt/sources.list; do [[ -f "$src" ]] && sed -i "s|URIs: http[s]*://[^/]*/|URIs: http://${m}/|g; s|deb http[s]*://[^/]*/|deb http://${m}/|g" "$src" done rm -rf /var/lib/apt/lists/* local out out=$(apt-get update 2>&1) if echo "$out" | grep -qi "hashsum\|hash sum"; then msg_warn "Mirror ${m} failed (hash mismatch)" return 1 elif echo "$out" | grep -qi "SSL\|certificate"; then msg_warn "Mirror ${m} failed (SSL/certificate error)" return 1 elif echo "$out" | grep -q "^E:"; then msg_warn "Mirror ${m} failed (apt-get update error)" return 1 else msg_ok "CDN set to ${m}: tests passed" return 0 fi } _scan_reachable() { local result="" for m in $1; do if timeout 2 bash -c "echo >/dev/tcp/$m/80" 2>/dev/null; then result="$result $m" fi done echo "$result" | xargs } local apt_ok=false # Phase 1: Scan global mirrors first (independent of local CDN issues) local others_ok others_ok=$(_scan_reachable "$others") local others_pick others_pick=$(printf '%s\n' $others_ok | shuf | head -3 | xargs) for mirror in $others_pick; do msg_custom "${INFO}" "${YW}" "Attempting mirror: ${mirror}" if _try_apt_mirror "$mirror"; then apt_ok=true break fi done # Phase 2: Try primary mirror if [[ "$apt_ok" != true ]]; then local primary if [[ "$distro" == "ubuntu" ]]; then primary="archive.ubuntu.com" else primary="ftp.debian.org" fi if timeout 2 bash -c "echo >/dev/tcp/$primary/80" 2>/dev/null; then msg_custom "${INFO}" "${YW}" "Attempting mirror: ${primary}" if _try_apt_mirror "$primary"; then apt_ok=true fi fi fi # Phase 3: Fall back to regional mirrors if [[ "$apt_ok" != true ]]; then local regional_ok regional_ok=$(_scan_reachable "$regional") local regional_pick regional_pick=$(printf '%s\n' $regional_ok | shuf | head -3 | xargs) for mirror in $regional_pick; do msg_custom "${INFO}" "${YW}" "Attempting mirror: ${mirror}" if _try_apt_mirror "$mirror"; then apt_ok=true break fi done fi # Phase 4: All auto mirrors failed, prompt user if [[ "$apt_ok" != true ]]; then msg_warn "Multiple mirrors failed (possible CDN synchronization issue)." if [[ "$distro" == "ubuntu" ]]; then msg_warn "Find Ubuntu mirrors at: https://launchpad.net/ubuntu/+archivemirrors" else msg_warn "Find Debian mirrors at: https://www.debian.org/mirror/list" fi local custom_mirror while true; do read -rp " Enter a mirror hostname (or 'skip' to abort): " custom_mirror /etc/apt/apt.conf.d/00aptproxy local _proxy_raw="${CACHER_IP}" local _proxy_host _proxy_port _proxy_url # Parse host and port from URL or plain IP/hostname _proxy_host=$(echo "$_proxy_raw" | sed -e 's|https\?://||' -e 's|/.*||' | cut -d: -f1) _proxy_port=$(echo "$_proxy_raw" | sed -e 's|https\?://||' -e 's|/.*||' | cut -s -d: -f2) if [[ "$_proxy_raw" =~ ^https?:// ]]; then # Full URL provided — use as-is for proxy output, extract port for nc check _proxy_url="$_proxy_raw" _proxy_port="${_proxy_port:-80}" else # Legacy: plain IP or hostname — default to http + port 3142 _proxy_port="${_proxy_port:-3142}" _proxy_url="http://${_proxy_raw}:${_proxy_port}" fi cat </usr/local/bin/apt-proxy-detect.sh #!/bin/bash if nc -w1 -z "${_proxy_host}" ${_proxy_port}; then echo -n "${_proxy_url}" else echo -n "DIRECT" fi EOF chmod +x /usr/local/bin/apt-proxy-detect.sh fi apt_update_safe $STD apt-get -o Dpkg::Options::="--force-confold" -y dist-upgrade $STD apt-get install -y sudo curl mc gnupg2 openssh-server wget gcc rm -rf /usr/lib/python3.*/EXTERNALLY-MANAGED msg_ok "Updated Container OS" post_progress_to_api local tools_content tools_content=$(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/tools.func) || { msg_error "Failed to download tools.func" exit 115 } source /dev/stdin <<<"$tools_content" if ! declare -f fetch_and_deploy_gh_release >/dev/null 2>&1; then msg_error "tools.func loaded but incomplete — missing expected functions" exit 115 fi } # ============================================================================== # SECTION 4: MOTD & SSH CONFIGURATION # ============================================================================== # ------------------------------------------------------------------------------ # motd_ssh() # # - Configures Message of the Day (MOTD) with container information # - Creates /etc/profile.d/00_lxc-details.sh with: # * Application name # * Warning banner (DEV repository) # * OS name and version # * Hostname and IP address # * GitHub repository link # - Disables executable flag on /etc/update-motd.d/* scripts # - Enables root SSH access if SSH_ROOT=yes # - Configures TERM environment variable for better terminal support # ------------------------------------------------------------------------------ motd_ssh() { # Set terminal to 256-color mode grep -qxF "export TERM='xterm-256color'" /root/.bashrc || echo "export TERM='xterm-256color'" >>/root/.bashrc PROFILE_FILE="/etc/profile.d/00_lxc-details.sh" echo "echo -e \"\"" >"$PROFILE_FILE" echo -e "echo -e \"${BOLD}${APPLICATION} LXC Container${CL}"\" >>"$PROFILE_FILE" echo -e "echo -e \"${TAB}${GATEWAY}${YW} Provided by: ${GN}community-scripts & pimox-scripts ${YW}| GitHub: ${GN}https://github.com/asylumexp/ProxmoxVE${CL}\"" >>"$PROFILE_FILE" echo "echo \"\"" >>"$PROFILE_FILE" echo -e "echo -e \"${TAB}${OS}${YW} OS: ${GN}\$(grep ^NAME /etc/os-release | cut -d= -f2 | tr -d '\"') - Version: \$(grep ^VERSION_ID /etc/os-release | cut -d= -f2 | tr -d '\"')${CL}\"" >>"$PROFILE_FILE" echo -e "echo -e \"${TAB}${HOSTNAME}${YW} Hostname: ${GN}\$(hostname)${CL}\"" >>"$PROFILE_FILE" echo -e "echo -e \"${TAB}${INFO}${YW} IP Address: ${GN}\$(hostname -I | awk '{print \$1}')${CL}\"" >>"$PROFILE_FILE" # Disable default MOTD scripts chmod -x /etc/update-motd.d/* if [[ "${SSH_ROOT}" == "yes" ]]; then sed -i "s/#PermitRootLogin prohibit-password/PermitRootLogin yes/g" /etc/ssh/sshd_config systemctl restart sshd fi post_progress_to_api } # ============================================================================== # SECTION 5: CONTAINER CUSTOMIZATION # ============================================================================== # ------------------------------------------------------------------------------ # customize() # # - Customizes container for passwordless root login if PASSWORD is empty # - Configures getty for auto-login via /etc/systemd/system/container-getty@1.service.d/override.conf # - Creates /usr/bin/update script for easy application updates # - Injects SSH authorized keys if SSH_AUTHORIZED_KEY variable is set # - Sets proper permissions on SSH directories and key files # ------------------------------------------------------------------------------ customize() { if [[ "$PASSWORD" == "" ]]; then msg_info "Customizing Container" GETTY_OVERRIDE="/etc/systemd/system/container-getty@1.service.d/override.conf" mkdir -p "$(dirname "$GETTY_OVERRIDE")" cat <"$GETTY_OVERRIDE" [Service] ExecStart= ExecStart=-/sbin/agetty --autologin root --noclear --keep-baud tty%I 115200,38400,9600 \$TERM EOF systemctl daemon-reload systemctl restart "$(basename "$(dirname "$GETTY_OVERRIDE")" | sed 's/\.d//')" msg_ok "Customized Container" fi echo "bash -c \"\$(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/ct/${app}.sh)\"" >/usr/bin/update chmod +x /usr/bin/update if [[ -n "${SSH_AUTHORIZED_KEY}" ]]; then mkdir -p /root/.ssh echo "${SSH_AUTHORIZED_KEY}" >/root/.ssh/authorized_keys chmod 700 /root/.ssh chmod 600 /root/.ssh/authorized_keys fi post_progress_to_api } ================================================ FILE: misc/test-automation.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2025 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Automated Container Testing Script set -eEuo pipefail # Detect if running from ./misc directory and adjust to project root SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" if [[ "$(basename "$SCRIPT_DIR")" == "misc" ]]; then # Running from ./misc, change to parent directory cd "$SCRIPT_DIR/.." echo "Detected running from ./misc directory, switching to project root: $(pwd)" fi # Color codes RD='\033[01;31m' GN='\033[1;92m' YW='\033[1;93m' BL='\033[36m' CL='\033[m' BOLD='\033[1m' # Setup timestamp and logging TIMESTAMP=$(date +"%Y%m%d_%H%M%S") LOG_DIR="./logs/${TIMESTAMP}" SCRIPT_LOG_DIR="${LOG_DIR}/scripts" OUTPUT_LOG="${LOG_DIR}/output.log" SUMMARY_LOG="${LOG_DIR}/summary.log" # Concurrency / UI controls MAX_CONCURRENCY="${MAX_CONCURRENCY:-4}" REFRESH_INTERVAL="${REFRESH_INTERVAL:-1}" LINES_PER_PANE="${LINES_PER_PANE:-10}" USE_UI="${USE_UI:-auto}" # auto|yes|no # Result tracking declare -a ACCESSIBLE_CONTAINERS=() declare -a INACCESSIBLE_CONTAINERS=() declare -a NOT_TESTED_CONTAINERS=() declare -a SKIPPED_SCRIPTS=() declare -a FAILED_CONTAINERS=() # Create log directories mkdir -p "${SCRIPT_LOG_DIR}" # Function to log messages log_msg() { echo -e "$1" | tee -a "${OUTPUT_LOG}" } log_info() { log_msg "${BL}[INFO]${CL} $1" } log_ok() { log_msg "${GN}[OK]${CL} $1" } log_error() { log_msg "${RD}[ERROR]${CL} $1" } log_warn() { log_msg "${YW}[WARN]${CL} $1" } # Check if running on Proxmox if ! command -v pveversion >/dev/null 2>&1; then log_error "This script must be run on a Proxmox VE host" exit 1 fi # Parse statuses.json for 🧪 scripts log_info "Reading statuses.json for test scripts (🧪 status)..." if [[ ! -f "frontend/public/json/statuses.json" ]]; then log_error "statuses.json not found at frontend/public/json/statuses.json" exit 1 fi # Extract scripts with 🧪 status TEST_SCRIPTS=$(jq -r 'to_entries[] | select(.value == "🧪") | .key' frontend/public/json/statuses.json | sed 's/\.json$//') if [[ -z "$TEST_SCRIPTS" ]]; then log_warn "No scripts found with 🧪 status" exit 0 fi log_ok "Found $(echo "$TEST_SCRIPTS" | wc -l) scripts to test" # Convert test scripts to array for scheduling mapfile -t SCRIPT_LIST <<< "$TEST_SCRIPTS" # Determine if we should render UI (only if stdout is a TTY) if [[ "$USE_UI" == "auto" ]]; then if [ -t 1 ]; then USE_UI="yes" else USE_UI="no" fi fi # Initialize base ID for concurrent-safe allocation BASE_ID=$(pvesh get /cluster/nextid) ID_LOCK_FILE="${LOG_DIR}/.id.lock" ID_COUNTER_FILE="${LOG_DIR}/.id.counter" echo -n "${BASE_ID}" > "${ID_COUNTER_FILE}" # Allocate a unique, currently free CT ID (concurrency-safe) allocate_container_id() { local fd id exec {fd}>"${ID_LOCK_FILE}" flock "${fd}" id=$(cat "${ID_COUNTER_FILE}" 2>/dev/null || echo "${BASE_ID}") # Find the next free ID while pct config "$id" &>/dev/null; do id=$((id+1)) done # Reserve next ID for subsequent calls echo -n $((id+1)) > "${ID_COUNTER_FILE}" flock -u "${fd}" eval "exec ${fd}>&-" echo "$id" } # UI helpers hide_cursor() { tput civis 2>/dev/null || true; } show_cursor() { tput cnorm 2>/dev/null || true; } clear_screen() { tput clear 2>/dev/null || printf "\033c"; } move_cursor() { tput cup "$1" "$2" 2>/dev/null || printf "\033[$1;$2H"; } # Draw the dashboard with a fixed number of panes equal to MAX_CONCURRENCY draw_dashboard() { local cols=$(tput cols 2>/dev/null || echo 120) local rows=$(tput lines 2>/dev/null || echo 40) # Layout: up to 3 columns depending on terminal width local num_cols if (( cols >= 180 )); then num_cols=3 elif (( cols >= 100 )); then num_cols=2 else num_cols=1 fi if (( MAX_CONCURRENCY < num_cols )); then num_cols=$MAX_CONCURRENCY fi (( num_cols == 0 )) && return local total=$MAX_CONCURRENCY local num_rows=$(( (total + num_cols - 1) / num_cols )) local header_rows=2 local footer_rows=1 local cell_h=$(( (rows - header_rows - footer_rows) / (num_rows > 0 ? num_rows : 1) )) local cell_w=$(( cols / (num_cols > 0 ? num_cols : 1) )) local content_h=$(( cell_h - 2 )) local content_w=$(( cell_w - 2 )) local print_w=$(( cell_w - 1 )) if (( content_h < 2 )); then content_h=2; fi if (( content_w < 20 )); then content_w=20; fi clear_screen move_cursor 0 0; printf "Concurrent Test Runner | Concurrency: %s | %s\n" "$MAX_CONCURRENCY" "$(date +%H:%M:%S)" printf "%.0s-" $(seq 1 "$cols"); printf "\n" local idx=0 while (( idx < total )); do local r=$(( idx / num_cols )) local c=$(( idx % num_cols )) local top=$(( header_rows + r * cell_h )) local left=$(( c * cell_w )) local script="${SLOT_SCRIPT[$idx]:-}" local title="" local status="IDLE" local log_file="" local status_file="" local s_line="" f2="" f3="" f4="" if [[ -n "$script" ]]; then title="$script" log_file="${SCRIPT_LOG_DIR}/${script}.log" status_file="${SCRIPT_LOG_DIR}/${script}.status" if [[ -f "$status_file" ]]; then s_line=$(head -n1 "$status_file") IFS='|' read -r status f2 f3 f4 <<< "$s_line" else status="RUNNING" fi else title="Idle Slot" fi move_cursor "$top" "$left"; printf "%-${print_w}s\n" "[$status] $title" local start_y=$(( top + 1 )) local display_line="" case "$status" in RUNNING) if [[ -n "$script" && -f "$log_file" ]]; then local raw_last raw_last=$(tail -n 1 "$log_file" 2>/dev/null) # Strip carriage returns and ANSI escape sequences, expand tabs last_line=$(echo -n "$raw_last" | tr -d '\r' | sed -r 's/\x1B\[[0-9;]*[A-Za-z]//g' | sed -e $'s/\t/ /g') if [[ -z "${last_line//[[:space:]]/}" ]]; then display_line="(waiting for output)" else display_line="$last_line" fi else display_line="(starting)" fi ;; QUEUED) display_line="(queued)" ;; SKIPPED) display_line="${f2:-skipped}" ;; FAILED) display_line="Last: ${f4:-failed}" ;; ACCESSIBLE) display_line="${f3:-accessible}" ;; INACCESSIBLE) display_line="${f3:-inaccessible}" ;; NOT_TESTED) display_line="Last: ${f4:-no http}" ;; *) display_line="" ;; esac move_cursor "$start_y" "$left"; printf "%-${print_w}s" "${display_line:0:$print_w}" idx=$(( idx + 1 )) done # Footer: show currently testing scripts or next queued local footer_y=$(( rows - 1 )) local testing_line="Testing: " local any_running=0 for i in $(seq 0 $(( MAX_CONCURRENCY - 1 ))); do if [[ -n "${SLOT_SCRIPT[$i]:-}" ]]; then testing_line+="${SLOT_SCRIPT[$i]} " any_running=1 fi done if (( any_running == 0 )); then if (( started_jobs < total_jobs )); then testing_line+="${SCRIPT_LIST[$started_jobs]} (queued)" else testing_line+="Done" fi fi move_cursor "$footer_y" 0 testing_line+=" | Running: ${#JOB_PIDS[@]} | Finished: ${finished_jobs} | Total: ${total_jobs}" printf "%-${cols}s" "$testing_line" } # Function to extract HTTP URL from log file extract_http_url() { local log_file="$1" # Look for http:// or https:// URLs in the log (with IP:PORT format) # Common patterns: http://192.168.1.100:8080, https://10.0.0.1:3000 # Now supports HTTPS endpoints with self-signed certificates grep -oP 'https?://[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+:[0-9]+' "$log_file" | tail -1 } # Function to extract last completed step from log file extract_last_step() { local log_file="$1" # Look for msg_ok messages (success indicators) # Also look for common success patterns local last_step=$(grep -E '\[OK\]|\[✓\]|msg_ok|✔️|Completed Successfully' "$log_file" | tail -1 | sed 's/.*\(msg_ok\|OK\|✓\|✔️\)//' | sed 's/^[^a-zA-Z]*//' | cut -c1-80) if [[ -n "$last_step" ]]; then echo "$last_step" else echo "No step information available" fi } # Function to monitor log and show current step monitor_current_step() { local log_file="$1" local script_name="$2" local monitor_file="/tmp/monitor_${script_name}_$$.txt" # Background process to monitor log file ( while [ -f "$monitor_file" ]; do if [ -f "$log_file" ]; then # Look for lines with hourglass emoji (⏳) which indicates current operation # Also look for msg_info patterns as fallback local current=$(grep -E '⏳|msg_info|Installing|Setting up|Configuring|Downloading|Building|Starting' "$log_file" | tail -1 | sed 's/.*⏳[[:space:]]*//' | sed 's/.*msg_info[[:space:]]*//' | sed 's/^[[:space:]]*//' | cut -c1-70) if [[ -n "$current" ]]; then printf "\r\033[K%b[CURRENT]%b %s: %s" "${BL}" "${CL}" "${script_name}" "${current}" fi fi sleep 1 done printf "\r\033[K" ) & echo "$!" > "$monitor_file" } # Function to stop monitoring stop_monitor() { local script_name="$1" local monitor_file="/tmp/monitor_${script_name}_$$.txt" if [ -f "$monitor_file" ]; then local pid=$(cat "$monitor_file") kill "$pid" 2>/dev/null || true rm -f "$monitor_file" fi printf "\r\033[K" } # Function to test HTTP endpoint test_http_endpoint() { local url="$1" local timeout=10 # Try to connect to the URL # -k/--insecure allows HTTPS with invalid/self-signed certificates if curl -s -k --max-time "$timeout" --connect-timeout "$timeout" -o /dev/null -w "%{http_code}" "$url" | grep -qE "^(200|301|302|401|403)"; then return 0 else return 1 fi } # Function to cleanup container on failure cleanup_container() { local ctid="$1" if pct status "$ctid" &>/dev/null; then log_warn "Cleaning up container $ctid" pct stop "$ctid" 2>/dev/null || true sleep 2 pct destroy "$ctid" 2>/dev/null || true fi } # Function to create wrapper script for automated testing create_wrapper_script() { local original_script="$1" local wrapper_script="$2" # Create a wrapper that sources auto-build.func instead of build.func cat > "${wrapper_script}" <<'WRAPPER_EOF' #!/usr/bin/env bash # Automated testing wrapper - sources auto-build.func instead of build.func # Read the original script and replace build.func with auto-build.func SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" ORIGINAL_SCRIPT="ORIGINAL_SCRIPT_PATH" # Create temp file with modified source TEMP_SCRIPT=$(mktemp) sed 's|https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func|file://'"${SCRIPT_DIR}"'/misc/auto-build.func|g' "$ORIGINAL_SCRIPT" > "$TEMP_SCRIPT" # Execute the modified script bash "$TEMP_SCRIPT" EXIT_CODE=$? # Cleanup rm -f "$TEMP_SCRIPT" exit $EXIT_CODE WRAPPER_EOF # Replace placeholder with actual path sed -i "s|ORIGINAL_SCRIPT_PATH|${original_script}|g" "${wrapper_script}" chmod +x "${wrapper_script}" } # Function to test a single script test_script() { local script_name="$1" local script_path="ct/${script_name}.sh" local script_log="${SCRIPT_LOG_DIR}/${script_name}.log" local wrapper_script="/tmp/test_wrapper_${script_name}_$$.sh" local status_file="${SCRIPT_LOG_DIR}/${script_name}.status" log_info "==================== Testing: ${script_name} ====================" # Mark as running echo "RUNNING" > "$status_file" # Skip alpine scripts if [[ "$script_name" =~ alpine ]]; then log_warn "Skipping alpine script: ${script_name}" echo "SKIPPED|alpine" > "$status_file" SKIPPED_SCRIPTS+=("${script_name} (alpine - skipped)") return fi # Check if script exists if [[ ! -f "$script_path" ]]; then log_warn "Script not found: ${script_path}" echo "SKIPPED|not_found" > "$status_file" SKIPPED_SCRIPTS+=("${script_name} (not found)") return fi # Get next available container ID local next_id=$(allocate_container_id) log_info "Using container ID: ${next_id}" # Set environment variables for non-interactive execution export VERBOSE="yes" export DIAGNOSTICS="no" export var_verbose="yes" export AUTO_TEST_MODE="yes" # Create wrapper script (but use direct execution for simplicity) log_info "Starting container creation for ${script_name}..." # Temporarily replace build.func reference in script TEMP_SCRIPT=$(mktemp) sed 's|source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func)|source misc/auto-build.func|g' "${script_path}" > "$TEMP_SCRIPT" # Ensure child uses our allocated CTID export var_ctid="${next_id}" # Run the script and capture output if bash "${TEMP_SCRIPT}" > "${script_log}" 2>&1; then rm -f "$TEMP_SCRIPT" log_ok "Container creation completed for ${script_name}" # Extract last completed step local last_step=$(extract_last_step "${script_log}") log_info "Last step: ${last_step}" # Extract HTTP URL from log local http_url=$(extract_http_url "${script_log}") if [[ -n "$http_url" ]]; then log_info "Found HTTP endpoint: ${http_url}" # Wait a bit for service to start log_info "Waiting 5 seconds for service to initialize..." sleep 5 # Test the endpoint if test_http_endpoint "$http_url"; then log_ok "HTTP endpoint is accessible: ${http_url}" echo "ACCESSIBLE|${next_id}|${http_url}|${last_step}" > "$status_file" ACCESSIBLE_CONTAINERS+=("${script_name}:${next_id}:${http_url}:${last_step}") else log_error "HTTP endpoint is not accessible: ${http_url}" echo "INACCESSIBLE|${next_id}|${http_url}|${last_step}" > "$status_file" INACCESSIBLE_CONTAINERS+=("${script_name}:${next_id}:${http_url}:${last_step}") fi else log_warn "No HTTP endpoint found in output" echo "NOT_TESTED|${next_id}||${last_step}" > "$status_file" NOT_TESTED_CONTAINERS+=("${script_name}:${next_id}:${last_step}") fi # Stop the container after testing log_info "Stopping container ${next_id}..." pct stop "${next_id}" 2>/dev/null || true else log_error "Container creation failed for ${script_name}" # Extract last completed step even on failure local last_step=$(extract_last_step "${script_log}") log_error "Last completed step: ${last_step}" echo "FAILED|${next_id}||${last_step}" > "$status_file" FAILED_CONTAINERS+=("${script_name}:${next_id}:${last_step}") rm -f "$TEMP_SCRIPT" # Try to cleanup failed container cleanup_container "${next_id}" fi # Cleanup wrapper if it exists rm -f "${wrapper_script}" log_info "==================== Finished: ${script_name} ====================\n" } # Main concurrent testing controller log_info "Starting automated container testing..." log_info "Log directory: ${LOG_DIR}" log_info "" declare -A JOB_PIDS=() declare -A JOB_STARTED=() declare -a SLOT_SCRIPT=() for i in $(seq 0 $(( MAX_CONCURRENCY - 1 ))); do SLOT_SCRIPT[$i]=""; done total_jobs=${#SCRIPT_LIST[@]} started_jobs=0 finished_jobs=0 if [[ "$USE_UI" == "yes" ]]; then hide_cursor clear_screen fi while (( finished_jobs < total_jobs )); do # Launch new jobs up to MAX_CONCURRENCY while (( started_jobs < total_jobs )) && (( ${#JOB_PIDS[@]} < MAX_CONCURRENCY )); do # Find a free slot free_slot=-1 for i in $(seq 0 $(( MAX_CONCURRENCY - 1 ))); do if [[ -z "${SLOT_SCRIPT[$i]}" ]]; then free_slot=$i; break fi done (( free_slot == -1 )) && break script="${SCRIPT_LIST[$started_jobs]}" JOB_STARTED["$script"]=1 SLOT_SCRIPT[$free_slot]="$script" # Ensure log/status files exist : > "${SCRIPT_LOG_DIR}/${script}.log" echo "QUEUED" > "${SCRIPT_LOG_DIR}/${script}.status" # Start job test_script "$script" & pid=$! JOB_PIDS["$script"]=$pid started_jobs=$(( started_jobs + 1 )) # Small stagger to avoid thundering herd sleep 1 done # Draw UI if [[ "$USE_UI" == "yes" ]]; then draw_dashboard SCRIPT_LIST fi # Check for finished jobs for s in "${!JOB_PIDS[@]}"; do if ! kill -0 "${JOB_PIDS[$s]}" 2>/dev/null; then unset 'JOB_PIDS[$s]' # free its slot for i in $(seq 0 $(( MAX_CONCURRENCY - 1 ))); do if [[ "${SLOT_SCRIPT[$i]:-}" == "$s" ]]; then SLOT_SCRIPT[$i]="" break fi done finished_jobs=$(( finished_jobs + 1 )) fi done sleep "$REFRESH_INTERVAL" done if [[ "$USE_UI" == "yes" ]]; then show_cursor printf "\n" fi # Regenerate arrays from status files for summary ACCESSIBLE_CONTAINERS=() INACCESSIBLE_CONTAINERS=() NOT_TESTED_CONTAINERS=() FAILED_CONTAINERS=() SKIPPED_SCRIPTS=() for script in "${SCRIPT_LIST[@]}"; do status_file="${SCRIPT_LOG_DIR}/${script}.status" [[ ! -f "$status_file" ]] && continue IFS='|' read -r status id url last_step < "$status_file" case "$status" in ACCESSIBLE) ACCESSIBLE_CONTAINERS+=("${script}:${id}:${url}:${last_step}") ;; INACCESSIBLE) INACCESSIBLE_CONTAINERS+=("${script}:${id}:${url}:${last_step}") ;; NOT_TESTED) NOT_TESTED_CONTAINERS+=("${script}:${id}:${last_step}") ;; FAILED) FAILED_CONTAINERS+=("${script}:${id}:${last_step}") ;; SKIPPED) SKIPPED_SCRIPTS+=("${script} ($(echo "$id"))") ;; esac done # Generate summary report log_info "" log_info "==================== TESTING SUMMARY ====================" # Generate detailed summary for terminal/summary.log { echo "==========================================" echo "Automated Container Testing Summary" echo "Timestamp: ${TIMESTAMP}" echo "==========================================" echo "" echo "ACCESSIBLE CONTAINERS (${#ACCESSIBLE_CONTAINERS[@]}):" if [[ ${#ACCESSIBLE_CONTAINERS[@]} -gt 0 ]]; then for item in "${ACCESSIBLE_CONTAINERS[@]}"; do IFS=':' read -r name id url last_step <<< "$item" echo " ✅ ${name} (CT:${id}) - ${url}" echo " Last step: ${last_step}" done else echo " (none)" fi echo "" echo "INACCESSIBLE CONTAINERS (${#INACCESSIBLE_CONTAINERS[@]}):" if [[ ${#INACCESSIBLE_CONTAINERS[@]} -gt 0 ]]; then for item in "${INACCESSIBLE_CONTAINERS[@]}"; do IFS=':' read -r name id url last_step <<< "$item" echo " ❌ ${name} (CT:${id}) - ${url}" echo " Last step: ${last_step}" done else echo " (none)" fi echo "" echo "NOT TESTED (no HTTP endpoint found) (${#NOT_TESTED_CONTAINERS[@]}):" if [[ ${#NOT_TESTED_CONTAINERS[@]} -gt 0 ]]; then for item in "${NOT_TESTED_CONTAINERS[@]}"; do IFS=':' read -r name id last_step <<< "$item" echo " ⚠️ ${name} (CT:${id})" echo " Last step: ${last_step}" done else echo " (none)" fi echo "" echo "FAILED CONTAINERS (${#FAILED_CONTAINERS[@]}):" if [[ ${#FAILED_CONTAINERS[@]} -gt 0 ]]; then for item in "${FAILED_CONTAINERS[@]}"; do IFS=':' read -r name id last_step <<< "$item" echo " 💥 ${name} (CT:${id})" echo " Last completed step: ${last_step}" done else echo " (none)" fi echo "" echo "SKIPPED SCRIPTS (${#SKIPPED_SCRIPTS[@]}):" if [[ ${#SKIPPED_SCRIPTS[@]} -gt 0 ]]; then for item in "${SKIPPED_SCRIPTS[@]}"; do echo " ⏭️ ${item}" done else echo " (none)" fi echo "" echo "==========================================" echo "Total Scripts Processed: $(( ${#ACCESSIBLE_CONTAINERS[@]} + ${#INACCESSIBLE_CONTAINERS[@]} + ${#NOT_TESTED_CONTAINERS[@]} + ${#FAILED_CONTAINERS[@]} + ${#SKIPPED_SCRIPTS[@]} ))" echo "Success Rate: $(( ${#ACCESSIBLE_CONTAINERS[@]} * 100 / (${#ACCESSIBLE_CONTAINERS[@]} + ${#INACCESSIBLE_CONTAINERS[@]} + ${#NOT_TESTED_CONTAINERS[@]} + ${#FAILED_CONTAINERS[@]} + 1) ))%" echo "==========================================" } | tee "${SUMMARY_LOG}" # Generate simplified output.log in the requested format { echo "successful and http accessible:" if [[ ${#ACCESSIBLE_CONTAINERS[@]} -gt 0 ]]; then for item in "${ACCESSIBLE_CONTAINERS[@]}"; do IFS=':' read -r name id url last_step <<< "$item" echo "$name" done fi echo "" echo "successful and not accessible:" if [[ ${#INACCESSIBLE_CONTAINERS[@]} -gt 0 ]]; then for item in "${INACCESSIBLE_CONTAINERS[@]}"; do IFS=':' read -r name id url last_step <<< "$item" echo "$name" done fi if [[ ${#NOT_TESTED_CONTAINERS[@]} -gt 0 ]]; then for item in "${NOT_TESTED_CONTAINERS[@]}"; do IFS=':' read -r name id last_step <<< "$item" echo "$name" done fi echo "" echo "failed:" if [[ ${#FAILED_CONTAINERS[@]} -gt 0 ]]; then for item in "${FAILED_CONTAINERS[@]}"; do IFS=':' read -r name id last_step <<< "$item" echo "$name" done fi echo "" echo "skipped:" if [[ ${#SKIPPED_SCRIPTS[@]} -gt 0 ]]; then for item in "${SKIPPED_SCRIPTS[@]}"; do # Extract just the script name from "name (reason)" echo "$item" | cut -d' ' -f1 done fi } > "${OUTPUT_LOG}" log_ok "Testing completed. Results saved to:" log_info " - Summary: ${SUMMARY_LOG}" log_info " - Full log: ${OUTPUT_LOG}" log_info " - Script logs: ${SCRIPT_LOG_DIR}/" # Ask user if they want to destroy test containers echo "" read -p "Do you want to destroy all test containers? (y/N): " -n 1 -r echo if [[ $REPLY =~ ^[Yy]$ ]]; then log_info "Destroying test containers..." for item in "${ACCESSIBLE_CONTAINERS[@]}" "${INACCESSIBLE_CONTAINERS[@]}" "${NOT_TESTED_CONTAINERS[@]}" "${FAILED_CONTAINERS[@]}"; do IFS=':' read -r name id _ <<< "$item" if [[ -n "$id" ]] && pct status "$id" &>/dev/null; then log_info "Destroying container ${id} (${name})..." pct stop "$id" 2>/dev/null || true sleep 2 pct destroy "$id" 2>/dev/null || true fi done log_ok "All test containers destroyed" else log_info "Test containers left running for manual inspection" fi log_ok "Automation complete!" ================================================ FILE: misc/test-build.func ================================================ # Copyright (c) 2021-2025 tteck # Author: tteck (tteckster) # License: MIT # https://github.com/asylumexp/Proxmox/raw/main/LICENSE variables() { NSAPP=$(echo ${APP,,} | tr -d ' ') # This function sets the NSAPP variable by converting the value of the APP variable to lowercase and removing any spaces. var_install="${NSAPP}-install" # sets the var_install variable by appending "-install" to the value of NSAPP. INTEGER='^[0-9]+([.][0-9]+)?$' # it defines the INTEGER regular expression pattern. PVEHOST_NAME=$(hostname) # gets the Proxmox Hostname and sets it to Uppercase DIAGNOSTICS="yes" # sets the DIAGNOSTICS variable to "yes", used for the API call. METHOD="default" # sets the METHOD variable to "default", used for the API call. RANDOM_UUID="$(cat /proc/sys/kernel/random/uuid)" # generates a random UUID and sets it to the RANDOM_UUID variable. } source <(curl -s https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/api.func) # This function sets various color variables using ANSI escape codes for formatting text in the terminal. color() { # Colors YW=$(echo "\033[33m") YWB=$(echo "\033[93m") BL=$(echo "\033[36m") RD=$(echo "\033[01;31m") BGN=$(echo "\033[4;92m") GN=$(echo "\033[1;92m") DGN=$(echo "\033[32m") # Formatting CL=$(echo "\033[m") BOLD=$(echo "\033[1m") HOLD=" " TAB=" " # Icons CM="${TAB}✔️${TAB}" CROSS="${TAB}✖️${TAB}" INFO="${TAB}💡${TAB}${CL}" OS="${TAB}🖥️${TAB}${CL}" OSVERSION="${TAB}🌟${TAB}${CL}" CONTAINERTYPE="${TAB}📦${TAB}${CL}" DISKSIZE="${TAB}💾${TAB}${CL}" CPUCORE="${TAB}🧠${TAB}${CL}" RAMSIZE="${TAB}🛠️${TAB}${CL}" SEARCH="${TAB}🔍${TAB}${CL}" VERBOSE_CROPPED="🔍${TAB}" VERIFYPW="${TAB}🔐${TAB}${CL}" CONTAINERID="${TAB}🆔${TAB}${CL}" HOSTNAME="${TAB}🏠${TAB}${CL}" BRIDGE="${TAB}🌉${TAB}${CL}" NETWORK="${TAB}📡${TAB}${CL}" GATEWAY="${TAB}🌐${TAB}${CL}" DISABLEIPV6="${TAB}🚫${TAB}${CL}" DEFAULT="${TAB}⚙️${TAB}${CL}" MACADDRESS="${TAB}🔗${TAB}${CL}" VLANTAG="${TAB}🏷️${TAB}${CL}" ROOTSSH="${TAB}🔑${TAB}${CL}" CREATING="${TAB}🚀${TAB}${CL}" ADVANCED="${TAB}🧩${TAB}${CL}" } # This function enables error handling in the script by setting options and defining a trap for the ERR signal. catch_errors() { set -Eeuo pipefail trap 'error_handler $LINENO "$BASH_COMMAND"' ERR } # This function is called when an error occurs. It receives the exit code, line number, and command that caused the error, and displays an error message. error_handler() { source /dev/stdin <<<$(wget -qLO - https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/api.func) if [ -n "$SPINNER_PID" ] && ps -p $SPINNER_PID >/dev/null; then kill $SPINNER_PID >/dev/null; fi printf "\e[?25h" local exit_code="$?" local line_number="$1" local command="$2" local error_message="${RD}[ERROR]${CL} in line ${RD}$line_number${CL}: exit code ${RD}$exit_code${CL}: while executing command ${YW}$command${CL}" post_update_to_api "failed" "${command}" echo -e "\n$error_message\n" } # This function displays an informational message with logging support. declare -A MSG_INFO_SHOWN SPINNER_ACTIVE=0 SPINNER_PID="" SPINNER_MSG="" trap 'stop_spinner' EXIT INT TERM HUP start_spinner() { local msg="$1" local frames=(⠋ ⠙ ⠹ ⠸ ⠼ ⠴ ⠦ ⠧ ⠇ ⠏) local spin_i=0 local interval=0.1 SPINNER_MSG="$msg" printf "\r\e[2K" >&2 { while [[ "$SPINNER_ACTIVE" -eq 1 ]]; do printf "\r\e[2K%s %b" "${frames[spin_i]}" "${YW}${SPINNER_MSG}${CL}" >&2 spin_i=$(((spin_i + 1) % ${#frames[@]})) sleep "$interval" done } & SPINNER_PID=$! disown "$SPINNER_PID" } stop_spinner() { if [[ ${SPINNER_PID+v} && -n "$SPINNER_PID" ]] && kill -0 "$SPINNER_PID" 2>/dev/null; then kill "$SPINNER_PID" 2>/dev/null sleep 0.1 kill -0 "$SPINNER_PID" 2>/dev/null && kill -9 "$SPINNER_PID" 2>/dev/null wait "$SPINNER_PID" 2>/dev/null || true fi SPINNER_ACTIVE=0 unset SPINNER_PID } spinner_guard() { if [[ "$SPINNER_ACTIVE" -eq 1 ]] && [[ -n "$SPINNER_PID" ]]; then kill "$SPINNER_PID" 2>/dev/null wait "$SPINNER_PID" 2>/dev/null || true SPINNER_ACTIVE=0 unset SPINNER_PID fi } msg_info() { local msg="$1" [[ -n "${MSG_INFO_SHOWN["$msg"]+x}" ]] && return MSG_INFO_SHOWN["$msg"]=1 spinner_guard SPINNER_ACTIVE=1 start_spinner "$msg" } msg_ok() { local msg="$1" stop_spinner printf "\r\e[2K%s %b\n" "${CM}" "${GN}${msg}${CL}" >&2 unset MSG_INFO_SHOWN["$msg"] } msg_error() { stop_spinner local msg="$1" printf "\r\e[2K%s %b\n" "${CROSS}" "${RD}${msg}${CL}" >&2 log_message "ERROR" "$msg" } log_message() { local level="$1" local message="$2" local timestamp local logdate timestamp=$(date '+%Y-%m-%d %H:%M:%S') logdate=$(date '+%Y-%m-%d') LOGDIR="/usr/local/community-scripts/logs" mkdir -p "$LOGDIR" LOGFILE="${LOGDIR}/${logdate}_${NSAPP}.log" echo "$timestamp - $level: $message" >>"$LOGFILE" } # Check if the shell is using bash shell_check() { if [[ "$(basename "$SHELL")" != "bash" ]]; then clear msg_error "Your default shell is currently not set to Bash. To use these scripts, please switch to the Bash shell." echo -e "\nExiting..." sleep 2 exit fi } # Run as root only root_check() { if [[ "$(id -u)" -ne 0 || $(ps -o comm= -p $PPID) == "sudo" ]]; then clear msg_error "Please run this script as root." echo -e "\nExiting..." sleep 2 exit fi } # This function checks the version of Proxmox Virtual Environment (PVE) and exits if the version is not supported. pve_check() { if ! pveversion | grep -Eq "pve-manager/8\.[1-3](\.[0-9]+)*"; then msg_error "${CROSS}${RD}This version of Proxmox Virtual Environment is not supported" echo -e "Requires Proxmox Virtual Environment Version 8.1 or later." echo -e "Exiting..." sleep 2 exit fi } # When a node is running tens of containers, it's possible to exceed the kernel's cryptographic key storage allocations. # These are tuneable, so verify if the currently deployment is approaching the limits, advise the user on how to tune the limits, and exit the script. # https://cleveruptime.com/docs/files/proc-key-users | https://docs.kernel.org/security/keys/core.html maxkeys_check() { # Read kernel parameters per_user_maxkeys=$(cat /proc/sys/kernel/keys/maxkeys 2>/dev/null || echo 0) per_user_maxbytes=$(cat /proc/sys/kernel/keys/maxbytes 2>/dev/null || echo 0) # Exit if kernel parameters are unavailable if [[ "$per_user_maxkeys" -eq 0 || "$per_user_maxbytes" -eq 0 ]]; then echo -e "${CROSS}${RD} Error: Unable to read kernel parameters. Ensure proper permissions.${CL}" exit 1 fi # Fetch key usage for user ID 100000 (typical for containers) used_lxc_keys=$(awk '/100000:/ {print $2}' /proc/key-users 2>/dev/null || echo 0) used_lxc_bytes=$(awk '/100000:/ {split($5, a, "/"); print a[1]}' /proc/key-users 2>/dev/null || echo 0) # Calculate thresholds and suggested new limits threshold_keys=$((per_user_maxkeys - 100)) threshold_bytes=$((per_user_maxbytes - 1000)) new_limit_keys=$((per_user_maxkeys * 2)) new_limit_bytes=$((per_user_maxbytes * 2)) # Check if key or byte usage is near limits failure=0 if [[ "$used_lxc_keys" -gt "$threshold_keys" ]]; then echo -e "${CROSS}${RD} Warning: Key usage is near the limit (${used_lxc_keys}/${per_user_maxkeys}).${CL}" echo -e "${INFO} Suggested action: Set ${GN}kernel.keys.maxkeys=${new_limit_keys}${CL} in ${BOLD}/etc/sysctl.d/98-community-scripts.conf${CL}." failure=1 fi if [[ "$used_lxc_bytes" -gt "$threshold_bytes" ]]; then echo -e "${CROSS}${RD} Warning: Key byte usage is near the limit (${used_lxc_bytes}/${per_user_maxbytes}).${CL}" echo -e "${INFO} Suggested action: Set ${GN}kernel.keys.maxbytes=${new_limit_bytes}${CL} in ${BOLD}/etc/sysctl.d/98-community-scripts.conf${CL}." failure=1 fi # Provide next steps if issues are detected if [[ "$failure" -eq 1 ]]; then echo -e "${INFO} To apply changes, run: ${BOLD}service procps force-reload${CL}" exit 1 fi echo -e "${CM}${GN} All kernel key limits are within safe thresholds.${CL}" } # This function checks the system architecture and exits if it's not "arm64". arch_check() { if [ "$(dpkg --print-architecture)" != "arm64" ]; then echo -e "\n ${INFO}${YWB}This script will not work on non arm64 systems! \n" echo -e "\n ${YWB}Visit https://github.com/asylumexp/Proxmox for AMD64 support. \n" echo -e "Exiting..." sleep 2 exit fi } # Function to get the current IP address based on the distribution get_current_ip() { if [ -f /etc/os-release ]; then # Check for Debian/Ubuntu (uses hostname -I) if grep -qE 'ID=debian|ID=ubuntu' /etc/os-release; then CURRENT_IP=$(hostname -I | awk '{print $1}') # Check for Alpine (uses ip command) elif grep -q 'ID=alpine' /etc/os-release; then CURRENT_IP=$(ip -4 addr show eth0 | awk '/inet / {print $2}' | cut -d/ -f1 | head -n 1) else CURRENT_IP="Unknown" fi fi echo "$CURRENT_IP" } # Function to update the IP address in the MOTD file update_motd_ip() { MOTD_FILE="/etc/motd" if [ -f "$MOTD_FILE" ]; then # Remove existing IP Address lines to prevent duplication sed -i '/IP Address:/d' "$MOTD_FILE" IP=$(get_current_ip) # Add the new IP address echo -e "${TAB}${NETWORK}${YW} IP Address: ${GN}${IP}${CL}" >>"$MOTD_FILE" fi } # Function to download & save header files get_header() { local app_name=$(echo "${APP,,}" | tr -d ' ') local header_url="https://raw.githubusercontent.com/asylumexp/Proxmox/main/ct/headers/${app_name}" local local_header_path="/usr/local/community-scripts/headers/${app_name}" mkdir -p "$(dirname "$local_header_path")" if [ ! -s "$local_header_path" ]; then if ! curl -fsSL "$header_url" -o "$local_header_path"; then echo -e "Failed to download header for ${app_name}. No header will be displayed." return 1 fi fi cat "$local_header_path" } # This function sets the APP-Name into an ASCII Header in Slant, figlet needed on proxmox main node. header_info() { local app_name=$(echo ${APP,,} | tr -d ' ') local header_content # Download & save Header-File locally header_content=$(get_header "$app_name") if [ $? -ne 0 ]; then # Fallback: Doesn't show Header return 0 fi # Show ASCII-Header term_width=$(tput cols 2>/dev/null || echo 120) clear echo "$header_content" } # This function checks if the script is running through SSH and prompts the user to confirm if they want to proceed or exit. ssh_check() { if [ -n "${SSH_CLIENT:+x}" ]; then if whiptail --backtitle "Proxmox VE Helper Scripts" --defaultno --title "SSH DETECTED" --yesno "It's advisable to utilize the Proxmox shell rather than SSH, as there may be potential complications with variable retrieval. Proceed using SSH?" 10 72; then whiptail --backtitle "Proxmox VE Helper Scripts" --msgbox --title "Proceed using SSH" "You've chosen to proceed using SSH. If any issues arise, please run the script in the Proxmox shell before creating a repository issue." 10 72 else clear echo "Exiting due to SSH usage. Please consider using the Proxmox shell." exit fi fi } base_settings() { # Default Settings CT_TYPE="1" DISK_SIZE="4" CORE_COUNT="1" RAM_SIZE="1024" VERBOSE="${1:-no}" PW="" CT_ID=$NEXTID HN=$NSAPP BRG="vmbr0" NET="dhcp" GATE="" APT_CACHER="" APT_CACHER_IP="" DISABLEIP6="no" MTU="" SD="" NS="" MAC="" VLAN="" SSH="no" SSH_AUTHORIZED_KEY="" TAGS="community-script;" # Override default settings with variables from ct script CT_TYPE=${var_unprivileged:-$CT_TYPE} DISK_SIZE=${var_disk:-$DISK_SIZE} CORE_COUNT=${var_cpu:-$CORE_COUNT} RAM_SIZE=${var_ram:-$RAM_SIZE} VERB=${var_verbose:-$VERBOSE} TAGS="${TAGS}${var_tags:-}" # Since these 2 are only defined outside of default_settings function, we add a temporary fallback. TODO: To align everything, we should add these as constant variables (e.g. OSTYPE and OSVERSION), but that would currently require updating the default_settings function for all existing scripts if [ -z "$var_os" ]; then var_os="debian" fi if [ -z "$var_version" ]; then var_version="12" fi } # This function displays the default values for various settings. echo_default() { # Convert CT_TYPE to description CT_TYPE_DESC="Unprivileged" if [ "$CT_TYPE" -eq 0 ]; then CT_TYPE_DESC="Privileged" fi # Output the selected values with icons echo -e "${OS}${BOLD}${DGN}Operating System: ${BGN}$var_os${CL}" echo -e "${OSVERSION}${BOLD}${DGN}Version: ${BGN}$var_version${CL}" echo -e "${CONTAINERTYPE}${BOLD}${DGN}Container Type: ${BGN}$CT_TYPE_DESC${CL}" echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}${DISK_SIZE} GB${CL}" echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}${CORE_COUNT}${CL}" echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}${RAM_SIZE} MiB${CL}" echo -e "${CONTAINERID}${BOLD}${DGN}Container ID: ${BGN}${CT_ID}${CL}" if [ "$VERB" == "yes" ]; then echo -e "${SEARCH}${BOLD}${DGN}Verbose Mode: ${BGN}Enabled${CL}" fi echo -e "${CREATING}${BOLD}${BL}Creating a ${APP} LXC using the above default settings${CL}" echo -e " " } # This function is called when the user decides to exit the script. It clears the screen and displays an exit message. exit_script() { clear echo -e "\n${CROSS}${RD}User exited script${CL}\n" exit } # This function allows the user to configure advanced settings for the script. advanced_settings() { whiptail --backtitle "Proxmox VE Helper Scripts" --msgbox --title "Here is an instructional tip:" "To make a selection, use the Spacebar." 8 58 whiptail --backtitle "Proxmox VE Helper Scripts" --msgbox --title "Default distribution for $APP" "Default is: ${var_os} ${var_version} \n \nIf the default Linux distribution is not adhered to, script support will be discontinued. \n" 10 58 if [ "$var_os" != "alpine" ]; then var_default_os="${var_os}" var_os="" while [ -z "$var_os" ]; do if [ "$var_default_os" == "debian" ]; then if var_os=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "DISTRIBUTION" --radiolist "Choose Distribution" 10 58 2 \ "debian" "" ON \ "ubuntu" "" OFF \ 3>&1 1>&2 2>&3); then if [ -n "$var_os" ]; then echo -e "${OS}${BOLD}${DGN}Operating System: ${BGN}$var_os${CL}" fi else exit_script fi fi if [ "$var_default_os" == "ubuntu" ]; then if var_os=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "DISTRIBUTION" --radiolist "Choose Distribution" 10 58 2 \ "debian" "" OFF \ "ubuntu" "" ON \ 3>&1 1>&2 2>&3); then if [ -n "$var_os" ]; then echo -e "${OS}${BOLD}${DGN}Operating System: ${BGN}$var_os${CL}" fi else exit_script fi fi done fi if [ "$var_os" == "debian" ]; then var_default_version="${var_version}" var_version="" while [ -z "$var_version" ]; do if [ "$var_default_version" == "11" ]; then if var_version=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "DEBIAN VERSION" --radiolist "Choose Version" 10 58 2 \ "11" "Bullseye" ON \ "12" "Bookworm" OFF \ 3>&1 1>&2 2>&3); then if [ -n "$var_version" ]; then echo -e "${OSVERSION}${BOLD}${DGN}Version: ${BGN}$var_version${CL}" fi else exit_script fi fi if [ "$var_default_version" == "12" ]; then if var_version=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "DEBIAN VERSION" --radiolist "Choose Version" 10 58 2 \ "11" "Bullseye" OFF \ "12" "Bookworm" ON \ 3>&1 1>&2 2>&3); then if [ -n "$var_version" ]; then echo -e "${OSVERSION}${BOLD}${DGN}Version: ${BGN}$var_version${CL}" fi else exit_script fi fi done fi if [ "$var_os" == "ubuntu" ]; then var_default_version="${var_version}" var_version="" while [ -z "$var_version" ]; do if [ "$var_default_version" == "20.04" ]; then if var_version=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "UBUNTU VERSION" --radiolist "Choose Version" 10 58 4 \ "20.04" "Focal" ON \ "22.04" "Jammy" OFF \ "24.04" "Noble" OFF \ "24.10" "Oracular" OFF \ 3>&1 1>&2 2>&3); then if [ -n "$var_version" ]; then echo -e "${OSVERSION}${BOLD}${DGN}Version: ${BGN}$var_version${CL}" fi else exit_script fi elif [ "$var_default_version" == "22.04" ]; then if var_version=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "UBUNTU VERSION" --radiolist "Choose Version" 10 58 4 \ "20.04" "Focal" OFF \ "22.04" "Jammy" ON \ "24.04" "Noble" OFF \ "24.10" "Oracular" OFF \ 3>&1 1>&2 2>&3); then if [ -n "$var_version" ]; then echo -e "${OSVERSION}${BOLD}${DGN}Version: ${BGN}$var_version${CL}" fi else exit_script fi elif [ "$var_default_version" == "24.04" ]; then if var_version=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "UBUNTU VERSION" --radiolist "Choose Version" 10 58 4 \ "20.04" "Focal" OFF \ "22.04" "Jammy" OFF \ "24.04" "Noble" ON \ "24.10" "Oracular" OFF \ 3>&1 1>&2 2>&3); then if [ -n "$var_version" ]; then echo -e "${OSVERSION}${BOLD}${DGN}Version: ${BGN}$var_version${CL}" fi else exit_script fi else if var_version=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "UBUNTU VERSION" --radiolist "Choose Version" 10 58 4 \ "20.04" "Focal" OFF \ "22.04" "Jammy" OFF \ "24.04" "Noble" OFF \ "24.10" "Oracular" ON \ 3>&1 1>&2 2>&3); then if [ -n "$var_version" ]; then echo -e "${OSVERSION}${BOLD}${DGN}Version: ${BGN}$var_version${CL}" fi else exit_script fi fi done fi # Setting Default Tag for Advanced Settings TAGS="community-script;${var_tags:-}" CT_DEFAULT_TYPE="${CT_TYPE}" CT_TYPE="" while [ -z "$CT_TYPE" ]; do if [ "$CT_DEFAULT_TYPE" == "1" ]; then if CT_TYPE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "CONTAINER TYPE" --radiolist "Choose Type" 10 58 2 \ "1" "Unprivileged" ON \ "0" "Privileged" OFF \ 3>&1 1>&2 2>&3); then if [ -n "$CT_TYPE" ]; then CT_TYPE_DESC="Unprivileged" if [ "$CT_TYPE" -eq 0 ]; then CT_TYPE_DESC="Privileged" fi echo -e "${CONTAINERTYPE}${BOLD}${DGN}Container Type: ${BGN}$CT_TYPE_DESC${CL}" fi else exit_script fi fi if [ "$CT_DEFAULT_TYPE" == "0" ]; then if CT_TYPE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "CONTAINER TYPE" --radiolist "Choose Type" 10 58 2 \ "1" "Unprivileged" OFF \ "0" "Privileged" ON \ 3>&1 1>&2 2>&3); then if [ -n "$CT_TYPE" ]; then CT_TYPE_DESC="Unprivileged" if [ "$CT_TYPE" -eq 0 ]; then CT_TYPE_DESC="Privileged" fi echo -e "${CONTAINERTYPE}${BOLD}${DGN}Container Type: ${BGN}$CT_TYPE_DESC${CL}" fi else exit_script fi fi done while true; do if PW1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --passwordbox "\nSet Root Password (needed for root ssh access)" 9 58 --title "PASSWORD (leave blank for automatic login)" 3>&1 1>&2 2>&3); then if [[ ! -z "$PW1" ]]; then if [[ "$PW1" == *" "* ]]; then whiptail --msgbox "Password cannot contain spaces. Please try again." 8 58 elif [ ${#PW1} -lt 5 ]; then whiptail --msgbox "Password must be at least 5 characters long. Please try again." 8 58 else if PW2=$(whiptail --backtitle "Proxmox VE Helper Scripts" --passwordbox "\nVerify Root Password" 9 58 --title "PASSWORD VERIFICATION" 3>&1 1>&2 2>&3); then if [[ "$PW1" == "$PW2" ]]; then PW="-password $PW1" echo -e "${VERIFYPW}${BOLD}${DGN}Root Password: ${BGN}********${CL}" break else whiptail --msgbox "Passwords do not match. Please try again." 8 58 fi else exit_script fi fi else PW1="Automatic Login" PW="" echo -e "${VERIFYPW}${BOLD}${DGN}Root Password: ${BGN}$PW1${CL}" break fi else exit_script fi done if CT_ID=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Container ID" 8 58 $NEXTID --title "CONTAINER ID" 3>&1 1>&2 2>&3); then if [ -z "$CT_ID" ]; then CT_ID="$NEXTID" echo -e "${CONTAINERID}${BOLD}${DGN}Container ID: ${BGN}$CT_ID${CL}" else echo -e "${CONTAINERID}${BOLD}${DGN}Container ID: ${BGN}$CT_ID${CL}" fi else exit fi if CT_NAME=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Hostname" 8 58 $NSAPP --title "HOSTNAME" 3>&1 1>&2 2>&3); then if [ -z "$CT_NAME" ]; then HN="$NSAPP" else HN=$(echo ${CT_NAME,,} | tr -d ' ') fi echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}" else exit_script fi if DISK_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Disk Size in GB" 8 58 $var_disk --title "DISK SIZE" 3>&1 1>&2 2>&3); then if [ -z "$DISK_SIZE" ]; then DISK_SIZE="$var_disk" echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}${DISK_SIZE} GB${CL}" else if ! [[ $DISK_SIZE =~ $INTEGER ]]; then echo -e "{INFO}${HOLD}${RD} DISK SIZE MUST BE AN INTEGER NUMBER!${CL}" advanced_settings fi echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}${DISK_SIZE} GB${CL}" fi else exit_script fi if CORE_COUNT=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Allocate CPU Cores" 8 58 $var_cpu --title "CORE COUNT" 3>&1 1>&2 2>&3); then if [ -z "$CORE_COUNT" ]; then CORE_COUNT="$var_cpu" echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}$CORE_COUNT${CL}" else echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}$CORE_COUNT${CL}" fi else exit_script fi if RAM_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Allocate RAM in MiB" 8 58 $var_ram --title "RAM" 3>&1 1>&2 2>&3); then if [ -z "$RAM_SIZE" ]; then RAM_SIZE="$var_ram" echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}${RAM_SIZE} MiB${CL}" else echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}${RAM_SIZE} MiB${CL}" fi else exit_script fi if BRG=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a Bridge" 8 58 vmbr0 --title "BRIDGE" 3>&1 1>&2 2>&3); then if [ -z "$BRG" ]; then BRG="vmbr0" echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}" else echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}" fi else exit_script fi while true; do NET=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a Static IPv4 CIDR Address (/24)" 8 58 dhcp --title "IP ADDRESS" 3>&1 1>&2 2>&3) exit_status=$? if [ $exit_status -eq 0 ]; then if [ "$NET" = "dhcp" ]; then echo -e "${NETWORK}${BOLD}${DGN}IP Address: ${BGN}$NET${CL}" break else if [[ "$NET" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}/([0-9]|[1-2][0-9]|3[0-2])$ ]]; then echo -e "${NETWORK}${BOLD}${DGN}IP Address: ${BGN}$NET${CL}" break else whiptail --backtitle "Proxmox VE Helper Scripts" --msgbox "$NET is an invalid IPv4 CIDR address. Please enter a valid IPv4 CIDR address or 'dhcp'" 8 58 fi fi else exit_script fi done if [ "$NET" != "dhcp" ]; then while true; do GATE1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Enter gateway IP address" 8 58 --title "Gateway IP" 3>&1 1>&2 2>&3) if [ -z "$GATE1" ]; then whiptail --backtitle "Proxmox VE Helper Scripts" --msgbox "Gateway IP address cannot be empty" 8 58 elif [[ ! "$GATE1" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then whiptail --backtitle "Proxmox VE Helper Scripts" --msgbox "Invalid IP address format" 8 58 else GATE=",gw=$GATE1" echo -e "${GATEWAY}${BOLD}${DGN}Gateway IP Address: ${BGN}$GATE1${CL}" break fi done else GATE="" echo -e "${GATEWAY}${BOLD}${DGN}Gateway IP Address: ${BGN}Default${CL}" fi if [ "$var_os" == "alpine" ]; then APT_CACHER="" APT_CACHER_IP="" else if APT_CACHER_IP=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set APT-Cacher IP (leave blank for none)" 8 58 --title "APT-Cacher IP" 3>&1 1>&2 2>&3); then APT_CACHER="${APT_CACHER_IP:+yes}" echo -e "${NETWORK}${BOLD}${DGN}APT-Cacher IP Address: ${BGN}${APT_CACHER_IP:-Default}${CL}" else exit_script fi fi if (whiptail --backtitle "Proxmox VE Helper Scripts" --defaultno --title "IPv6" --yesno "Disable IPv6?" 10 58); then DISABLEIP6="yes" else DISABLEIP6="no" fi echo -e "${DISABLEIPV6}${BOLD}${DGN}Disable IPv6: ${BGN}$DISABLEIP6${CL}" if MTU1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Interface MTU Size (leave blank for default [The MTU of your selected vmbr, default is 1500])" 8 58 --title "MTU SIZE" 3>&1 1>&2 2>&3); then if [ -z $MTU1 ]; then MTU1="Default" MTU="" else MTU=",mtu=$MTU1" fi echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}$MTU1${CL}" else exit_script fi if SD=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a DNS Search Domain (leave blank for HOST)" 8 58 --title "DNS Search Domain" 3>&1 1>&2 2>&3); then if [ -z $SD ]; then SX=Host SD="" else SX=$SD SD="-searchdomain=$SD" fi echo -e "${SEARCH}${BOLD}${DGN}DNS Search Domain: ${BGN}$SX${CL}" else exit_script fi if NX=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a DNS Server IP (leave blank for HOST)" 8 58 --title "DNS SERVER IP" 3>&1 1>&2 2>&3); then if [ -z $NX ]; then NX=Host NS="" else NS="-nameserver=$NX" fi echo -e "${NETWORK}${BOLD}${DGN}DNS Server IP Address: ${BGN}$NX${CL}" else exit_script fi if MAC1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a MAC Address(leave blank for generated MAC)" 8 58 --title "MAC ADDRESS" 3>&1 1>&2 2>&3); then if [ -z $MAC1 ]; then MAC1="Default" MAC="" else MAC=",hwaddr=$MAC1" echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}$MAC1${CL}" fi else exit_script fi if VLAN1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a Vlan(leave blank for no VLAN)" 8 58 --title "VLAN" 3>&1 1>&2 2>&3); then if [ -z $VLAN1 ]; then VLAN1="Default" VLAN="" else VLAN=",tag=$VLAN1" fi echo -e "${VLANTAG}${BOLD}${DGN}Vlan: ${BGN}$VLAN1${CL}" else exit_script fi if ADV_TAGS=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Custom Tags?[If you remove all, there will be no tags!]" 8 58 ${TAGS} --title "Advanced Tags" 3>&1 1>&2 2>&3); then if [ -n "${ADV_TAGS}" ]; then ADV_TAGS=$(echo "$ADV_TAGS" | tr -d '[:space:]') TAGS="${ADV_TAGS}" else TAGS=";" fi echo -e "${NETWORK}${BOLD}${DGN}Tags: ${BGN}$TAGS${CL}" else exit_script fi if [[ "$PW" == -password* ]]; then if (whiptail --backtitle "Proxmox VE Helper Scripts" --defaultno --title "SSH ACCESS" --yesno "Enable Root SSH Access?" 10 58); then SSH="yes" else SSH="no" fi echo -e "${ROOTSSH}${BOLD}${DGN}Root SSH Access: ${BGN}$SSH${CL}" else SSH="no" echo -e "${ROOTSSH}${BOLD}${DGN}Root SSH Access: ${BGN}$SSH${CL}" fi if [[ "${SSH}" == "yes" ]]; then SSH_AUTHORIZED_KEY="$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "SSH Authorized key for root (leave empty for none)" 8 58 --title "SSH Key" 3>&1 1>&2 2>&3)" if [[ -z "${SSH_AUTHORIZED_KEY}" ]]; then echo "Warning: No SSH key provided." fi else SSH_AUTHORIZED_KEY="" fi if (whiptail --backtitle "Proxmox VE Helper Scripts" --defaultno --title "VERBOSE MODE" --yesno "Enable Verbose Mode?" 10 58); then VERB="yes" else VERB="no" fi echo -e "${SEARCH}${BOLD}${DGN}Verbose Mode: ${BGN}$VERB${CL}" if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "ADVANCED SETTINGS COMPLETE" --yesno "Ready to create ${APP} LXC?" 10 58); then echo -e "${CREATING}${BOLD}${RD}Creating a ${APP} LXC using the above advanced settings${CL}" else clear header_info echo -e "${ADVANCED}${BOLD}${RD}Using Advanced Settings on node $PVEHOST_NAME${CL}" advanced_settings fi } diagnostics_check() { if ! [ -d "/usr/local/community-scripts" ]; then mkdir -p /usr/local/community-scripts fi if ! [ -f "/usr/local/community-scripts/diagnostics" ]; then if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "DIAGNOSTICS" --yesno "Send Diagnostics of LXC Installation?\n\n(This only transmits data without user data, just RAM, CPU, LXC name, ...)" 10 58); then cat </usr/local/community-scripts/diagnostics DIAGNOSTICS=yes #This file is used to store the diagnostics settings for the Community-Scripts API. #https://github.com/community-scripts/ProxmoxVE/discussions/1836 #Your diagnostics will be sent to the Community-Scripts API for troubleshooting/statistical purposes. #You can review the data at https://community-scripts.github.io/ProxmoxVE/data #If you do not wish to send diagnostics, please set the variable 'DIAGNOSTICS' to "no" in /usr/local/community-scripts/diagnostics, or use the menue. #This will disable the diagnostics feature. #To send diagnostics, set the variable 'DIAGNOSTICS' to "yes" in /usr/local/community-scripts/diagnostics, or use the menue. #This will enable the diagnostics feature. #The following information will be sent: #"ct_type" #"disk_size" #"core_count" #"ram_size" #"os_type" #"os_version" #"disableip6" #"nsapp" #"method" #"pve_version" #"status" #If you have any concerns, please review the source code at /misc/build.func EOF DIAGNOSTICS="yes" else cat </usr/local/community-scripts/diagnostics DIAGNOSTICS=no #This file is used to store the diagnostics settings for the Community-Scripts API. #https://github.com/community-scripts/ProxmoxVE/discussions/1836 #Your diagnostics will be sent to the Community-Scripts API for troubleshooting/statistical purposes. #You can review the data at https://community-scripts.github.io/ProxmoxVE/data #If you do not wish to send diagnostics, please set the variable 'DIAGNOSTICS' to "no" in /usr/local/community-scripts/diagnostics, or use the menue. #This will disable the diagnostics feature. #To send diagnostics, set the variable 'DIAGNOSTICS' to "yes" in /usr/local/community-scripts/diagnostics, or use the menue. #This will enable the diagnostics feature. #The following information will be sent: #"ct_type" #"disk_size" #"core_count" #"ram_size" #"os_type" #"os_version" #"disableip6" #"nsapp" #"method" #"pve_version" #"status" #If you have any concerns, please review the source code at /misc/build.func EOF DIAGNOSTICS="no" fi else DIAGNOSTICS=$(awk -F '=' '/^DIAGNOSTICS/ {print $2}' /usr/local/community-scripts/diagnostics) fi } install_script() { pve_check shell_check root_check ssh_check maxkeys_check diagnostics_check if systemctl is-active -q ping-instances.service; then systemctl -q stop ping-instances.service fi NEXTID=$(pvesh get /cluster/nextid) timezone=$(cat /etc/timezone) header_info echo -e "${DEFAULT}${BOLD}${BL}Using Default Settings on node $PVEHOST_NAME (${VERBOSE_CROPPED}Verbose)${CL}" VERB="yes" METHOD="default" base_settings "$VERB" echo_default } check_container_resources() { # Check actual RAM & Cores current_ram=$(free -m | awk 'NR==2{print $2}') current_cpu=$(nproc) # Check whether the current RAM is less than the required RAM or the CPU cores are less than required if [[ "$current_ram" -lt "$var_ram" ]] || [[ "$current_cpu" -lt "$var_cpu" ]]; then echo -e "\n${INFO}${HOLD} ${GN}Required: ${var_cpu} CPU, ${var_ram}MB RAM ${CL}| ${RD}Current: ${current_cpu} CPU, ${current_ram}MB RAM${CL}" echo -e "${YWB}Please ensure that the ${APP} LXC is configured with at least ${var_cpu} vCPU and ${var_ram} MB RAM for the build process.${CL}\n" echo -ne "${INFO}${HOLD} May cause data loss! ${INFO} Continue update with under-provisioned LXC? " read -r prompt # Check if the input is 'yes', otherwise exit with status 1 if [[ ! ${prompt,,} =~ ^(yes)$ ]]; then echo -e "${CROSS}${HOLD} ${YWB}Exiting based on user input.${CL}" exit 1 fi else echo -e "" fi } check_container_storage() { # Check if the /boot partition is more than 80% full total_size=$(df /boot --output=size | tail -n 1) local used_size=$(df /boot --output=used | tail -n 1) usage=$((100 * used_size / total_size)) if ((usage > 80)); then # Prompt the user for confirmation to continue echo -e "${INFO}${HOLD} ${YWB}Warning: Storage is dangerously low (${usage}%).${CL}" echo -ne "Continue anyway? " read -r prompt # Check if the input is 'y' or 'yes', otherwise exit with status 1 if [[ ! ${prompt,,} =~ ^(y|yes)$ ]]; then echo -e "${CROSS}${HOLD}${YWB}Exiting based on user input.${CL}" exit 1 fi fi } start() { LOGDIR="/usr/local/community-scripts/logs" mkdir -p "$LOGDIR" if command -v pveversion >/dev/null 2>&1; then VERB="yes" METHOD="default" NEXTID=$(pvesh get /cluster/nextid) timezone=$(cat /etc/timezone) header_info echo -e "${DEFAULT}${BOLD}${BL}Using Default Settings on node $PVEHOST_NAME (${VERBOSE_CROPPED}Verbose)${CL}" base_settings "$VERB" echo_default install_script build_container description fi } # This function collects user settings and integrates all the collected information. build_container() { # if [ "$VERB" == "yes" ]; then set -x; fi if [ "$CT_TYPE" == "1" ]; then FEATURES="keyctl=1,nesting=1" else FEATURES="nesting=1" fi if [[ $DIAGNOSTICS == "yes" ]]; then post_to_api fi TEMP_DIR=$(mktemp -d) pushd $TEMP_DIR >/dev/null if [ "$var_os" == "alpine" ]; then export FUNCTIONS_FILE_PATH="$(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/alpine-install.func)" else export FUNCTIONS_FILE_PATH="$(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/install.func)" fi export RANDOM_UUID="$RANDOM_UUID" export CACHER="$APT_CACHER" export CACHER_IP="$APT_CACHER_IP" export tz="$timezone" export DISABLEIPV6="$DISABLEIP6" export APPLICATION="$APP" export app="$NSAPP" export PASSWORD="$PW" export VERBOSE="$VERB" export SSH_ROOT="${SSH}" export SSH_AUTHORIZED_KEY export CTID="$CT_ID" export CTTYPE="$CT_TYPE" export PCT_OSTYPE="$var_os" export PCT_OSVERSION="$var_version" export PCT_DISK_SIZE="$DISK_SIZE" export PCT_OPTIONS=" -features $FEATURES -hostname $HN -tags $TAGS $SD $NS -net0 name=eth0,bridge=$BRG$MAC,ip=$NET$GATE$VLAN$MTU -onboot 1 -cores $CORE_COUNT -memory $RAM_SIZE -unprivileged $CT_TYPE $PW " # This executes create_lxc.sh and creates the container and .conf file bash -c "$(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/ct/create_lxc.sh)" || exit $? LXC_CONFIG=/etc/pve/lxc/${CTID}.conf if [ "$CT_TYPE" == "0" ]; then cat <>$LXC_CONFIG # USB passthrough lxc.cgroup2.devices.allow: a lxc.cap.drop: lxc.cgroup2.devices.allow: c 188:* rwm lxc.cgroup2.devices.allow: c 189:* rwm lxc.mount.entry: /dev/serial/by-id dev/serial/by-id none bind,optional,create=dir lxc.mount.entry: /dev/ttyUSB0 dev/ttyUSB0 none bind,optional,create=file lxc.mount.entry: /dev/ttyUSB1 dev/ttyUSB1 none bind,optional,create=file lxc.mount.entry: /dev/ttyACM0 dev/ttyACM0 none bind,optional,create=file lxc.mount.entry: /dev/ttyACM1 dev/ttyACM1 none bind,optional,create=file EOF fi if [ "$CT_TYPE" == "0" ]; then if [[ "$APP" == "Channels" || "$APP" == "Emby" || "$APP" == "ErsatzTV" || "$APP" == "Frigate" || "$APP" == "Jellyfin" || "$APP" == "Plex" || "$APP" == "Scrypted" || "$APP" == "Tdarr" || "$APP" == "Unmanic" || "$APP" == "Ollama" || "$APP" == "FileFlows" ]]; then cat <>$LXC_CONFIG # VAAPI hardware transcoding lxc.cgroup2.devices.allow: c 226:0 rwm lxc.cgroup2.devices.allow: c 226:128 rwm lxc.cgroup2.devices.allow: c 29:0 rwm lxc.mount.entry: /dev/fb0 dev/fb0 none bind,optional,create=file lxc.mount.entry: /dev/dri dev/dri none bind,optional,create=dir lxc.mount.entry: /dev/dri/renderD128 dev/dri/renderD128 none bind,optional,create=file EOF fi else if [[ "$APP" == "Channels" || "$APP" == "Emby" || "$APP" == "ErsatzTV" || "$APP" == "Frigate" || "$APP" == "Jellyfin" || "$APP" == "Plex" || "$APP" == "Scrypted" || "$APP" == "Tdarr" || "$APP" == "Unmanic" || "$APP" == "Ollama" || "$APP" == "FileFlows" ]]; then if [[ -e "/dev/dri/renderD128" ]]; then if [[ -e "/dev/dri/card0" ]]; then cat <>$LXC_CONFIG # VAAPI hardware transcoding dev0: /dev/dri/card0,gid=44 dev1: /dev/dri/renderD128,gid=104 EOF else cat <>$LXC_CONFIG # VAAPI hardware transcoding dev0: /dev/dri/card1,gid=44 dev1: /dev/dri/renderD128,gid=104 EOF fi fi fi fi # This starts the container and executes -install.sh msg_info "Starting LXC Container" pct start "$CTID" msg_ok "Started LXC Container" if [ "$var_os" == "alpine" ]; then sleep 3 pct exec "$CTID" -- /bin/sh -c 'cat </etc/apk/repositories http://dl-cdn.alpinelinux.org/alpine/latest-stable/main http://dl-cdn.alpinelinux.org/alpine/latest-stable/community EOF' pct exec "$CTID" -- ash -c "apk add bash >/dev/null" fi lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/install/$var_install.sh)" || exit $? } # This function sets the description of the container. description() { IP=$(pct exec "$CTID" ip a s dev eth0 | awk '/inet / {print $2}' | cut -d/ -f1) # Generate LXC Description DESCRIPTION=$( cat < Logo

${APP} LXC

spend Coffee

GitHub Discussions Issues EOF ) # Set Description in LXC pct set "$CTID" -description "$DESCRIPTION" if [[ -f /etc/systemd/system/ping-instances.service ]]; then systemctl start ping-instances.service fi post_update_to_api "done" "none" } set_std_mode() { if [ "$VERB" = "yes" ]; then STD="" else STD="silent" fi } # Silent execution function silent() { if [ "$VERB" = "no" ]; then "$@" >>"$LOGFILE" 2>&1 else "$@" 2>&1 | tee -a "$LOGFILE" fi } exit_script() { exit_code=$? # Capture the exit status of the last executed command #200 exit codes indicate error in create_lxc.sh #100 exit codes indicate error in install.func if [ $exit_code -ne 0 ]; then case $exit_code in 100) post_update_to_api "failed" "100: Unexpected error in create_lxc.sh" ;; 101) post_update_to_api "failed" "101: No network connection detected in create_lxc.sh" ;; 200) post_update_to_api "failed" "200: LXC creation failed in create_lxc.sh" ;; 201) post_update_to_api "failed" "201: Invalid Storage class in create_lxc.sh" ;; 202) post_update_to_api "failed" "202: User aborted menu in create_lxc.sh" ;; 203) post_update_to_api "failed" "203: CTID not set in create_lxc.sh" ;; 204) post_update_to_api "failed" "204: PCT_OSTYPE not set in create_lxc.sh" ;; 205) post_update_to_api "failed" "205: CTID cannot be less than 100 in create_lxc.sh" ;; 206) post_update_to_api "failed" "206: CTID already in use in create_lxc.sh" ;; 207) post_update_to_api "failed" "207: Template not found in create_lxc.sh" ;; 208) post_update_to_api "failed" "208: Error downloading template in create_lxc.sh" ;; 209) post_update_to_api "failed" "209: Container creation failed, but template is intact in create_lxc.sh" ;; *) post_update_to_api "failed" "Unknown error, exit code: $exit_code in create_lxc.sh" ;; esac fi } trap 'exit_script' EXIT trap 'post_update_to_api "failed" "$BASH_COMMAND"' ERR trap 'post_update_to_api "failed" "INTERRUPTED"' SIGINT trap 'post_update_to_api "failed" "TERMINATED"' SIGTERM ================================================ FILE: misc/tools.func ================================================ #!/bin/bash # ============================================================================== # HELPER FUNCTIONS FOR PACKAGE MANAGEMENT # ============================================================================== # # This file provides unified helper functions for robust package installation # and repository management across Debian/Ubuntu OS upgrades. # # Key Features: # - Automatic retry logic for transient APT/network failures # - Unified keyring cleanup from all 3 locations # - Legacy installation cleanup (nvm, rbenv, rustup) # - OS-upgrade-safe repository preparation # - Service pattern matching for multi-version tools # - Debug mode for troubleshooting (TOOLS_DEBUG=true) # # Usage in install scripts: # source /dev/stdin <<< "$FUNCTIONS" # Load from build.func # prepare_repository_setup "mysql" # install_packages_with_retry "mysql-server" "mysql-client" # # Quick Reference (Core Helpers): # cleanup_tool_keyrings() - Remove keyrings from all 3 locations # stop_all_services() - Stop services by pattern (e.g. "php*-fpm") # verify_tool_version() - Validate installed version matches expected # cleanup_legacy_install() - Remove nvm, rbenv, rustup, etc. # prepare_repository_setup() - Cleanup repos + keyrings + validate APT # install_packages_with_retry() - Install with 3 retries and APT refresh # upgrade_packages_with_retry() - Upgrade with 3 retries and APT refresh # curl_with_retry() - Curl with retry logic and timeouts # # Debug Mode: # TOOLS_DEBUG=true ./script.sh - Enable verbose output for troubleshooting # # ============================================================================== # ------------------------------------------------------------------------------ # Debug helper - outputs to stderr when TOOLS_DEBUG is enabled # Usage: debug_log "message" # ------------------------------------------------------------------------------ debug_log() { if [[ "${TOOLS_DEBUG:-false}" == "true" || "${TOOLS_DEBUG:-0}" == "1" || "${DEBUG:-0}" == "1" ]]; then echo "[DEBUG] $*" >&2 fi } # ------------------------------------------------------------------------------ # Robust curl wrapper with retry logic, timeouts, and error handling # # Usage: # curl_with_retry "https://example.com/file" "/tmp/output" # curl_with_retry "https://api.github.com/..." "-" | jq . # CURL_RETRIES=5 curl_with_retry "https://slow.server/file" "/tmp/out" # # Parameters: # $1 - URL to download # $2 - Output file path (use "-" for stdout) # $3 - (optional) Additional curl options as string # # Variables: # CURL_RETRIES - Number of retries (default: 3) # CURL_TIMEOUT - Max time per attempt in seconds (default: 60) # CURL_CONNECT_TO - Connection timeout in seconds (default: 10) # # Returns: 0 on success, 1 on failure after all retries # ------------------------------------------------------------------------------ curl_with_retry() { local url="$1" local output="${2:--}" local extra_opts="${3:-}" local retries="${CURL_RETRIES:-3}" local timeout="${CURL_TIMEOUT:-60}" local connect_timeout="${CURL_CONNECT_TO:-10}" local attempt=1 local success=false local backoff=1 # Extract hostname for DNS pre-check local host host=$(echo "$url" | sed -E 's|^https?://([^/:]+).*|\1|') # DNS pre-check - fail fast if host is unresolvable if ! getent hosts "$host" &>/dev/null; then debug_log "DNS resolution failed for $host" return 6 fi while [[ $attempt -le $retries ]]; do debug_log "curl attempt $attempt/$retries: $url" local curl_cmd="curl -fsSL --connect-timeout $connect_timeout --max-time $timeout" [[ -n "$extra_opts" ]] && curl_cmd="$curl_cmd $extra_opts" if [[ "$output" == "-" ]]; then if $curl_cmd "$url"; then success=true break fi else if $curl_cmd -o "$output" "$url"; then success=true break fi fi debug_log "curl attempt $attempt failed (timeout=${timeout}s), waiting ${backoff}s before retry..." sleep "$backoff" # Exponential backoff: 1, 2, 4, 8... capped at 30s backoff=$((backoff * 2)) ((backoff > 30)) && backoff=30 # Double --max-time on each retry so slow connections can finish timeout=$((timeout * 2)) ((attempt++)) done if [[ "$success" == "true" ]]; then debug_log "curl successful: $url" return 0 else debug_log "curl FAILED after $retries attempts: $url" return 7 fi } # ------------------------------------------------------------------------------ # Robust curl wrapper for API calls (returns HTTP code + body) # # Usage: # response=$(curl_api_with_retry "https://api.github.com/repos/owner/repo/releases/latest") # http_code=$(curl_api_with_retry "https://api.github.com/..." "/tmp/body.json") # # Parameters: # $1 - URL to call # $2 - (optional) Output file for body (default: stdout) # $3 - (optional) Additional curl options as string # # Returns: HTTP status code, body in file or stdout # ------------------------------------------------------------------------------ curl_api_with_retry() { local url="$1" local body_file="${2:-}" local extra_opts="${3:-}" local retries="${CURL_RETRIES:-3}" local timeout="${CURL_TIMEOUT:-60}" local connect_timeout="${CURL_CONNECT_TO:-10}" local attempt=1 local http_code="" while [[ $attempt -le $retries ]]; do debug_log "curl API attempt $attempt/$retries: $url" local curl_cmd="curl -fsSL --connect-timeout $connect_timeout --max-time $timeout -w '%{http_code}'" [[ -n "$extra_opts" ]] && curl_cmd="$curl_cmd $extra_opts" if [[ -n "$body_file" ]]; then http_code=$($curl_cmd -o "$body_file" "$url" 2>/dev/null) || true else # Capture body and http_code separately local tmp_body="/tmp/curl_api_body_$$" http_code=$($curl_cmd -o "$tmp_body" "$url" 2>/dev/null) || true if [[ -f "$tmp_body" ]]; then cat "$tmp_body" rm -f "$tmp_body" fi fi # Success on 2xx codes if [[ "$http_code" =~ ^2[0-9]{2}$ ]]; then debug_log "curl API successful: $url (HTTP $http_code)" echo "$http_code" return 0 fi debug_log "curl API attempt $attempt failed (HTTP $http_code, timeout=${timeout}s), waiting ${attempt}s..." sleep "$attempt" # Double --max-time on each retry so slow connections can finish timeout=$((timeout * 2)) ((attempt++)) done debug_log "curl API FAILED after $retries attempts: $url" echo "$http_code" return 7 } # ------------------------------------------------------------------------------ # Download and install GPG key with retry logic and validation # # Usage: # download_gpg_key "https://example.com/key.gpg" "/etc/apt/keyrings/example.gpg" # download_gpg_key "https://example.com/key.asc" "/etc/apt/keyrings/example.gpg" "dearmor" # # Parameters: # $1 - URL to GPG key # $2 - Output path for keyring file # $3 - (optional) "dearmor" to convert ASCII-armored key to binary # # Features: # - Auto-detects key format (binary vs armored) # - Validates downloaded key # - Multiple mirror fallback support # # Returns: 0 on success, 1 on failure # ------------------------------------------------------------------------------ download_gpg_key() { local url="$1" local output="$2" local mode="${3:-auto}" # auto, dearmor, or binary local retries="${CURL_RETRIES:-3}" local timeout="${CURL_TIMEOUT:-30}" local temp_key temp_key=$(mktemp) mkdir -p "$(dirname "$output")" local attempt=1 while [[ $attempt -le $retries ]]; do debug_log "GPG key download attempt $attempt/$retries: $url" # Download to temp file first if ! curl -fsSL --connect-timeout 10 --max-time "$timeout" -o "$temp_key" "$url" 2>/dev/null; then debug_log "GPG key download attempt $attempt failed, waiting ${attempt}s..." sleep "$attempt" ((attempt++)) continue fi # Auto-detect key format if mode is auto if [[ "$mode" == "auto" ]]; then if file "$temp_key" 2>/dev/null | grep -qi "pgp\\|gpg\\|public key"; then mode="binary" elif grep -q "BEGIN PGP" "$temp_key" 2>/dev/null; then mode="dearmor" else # Try to detect by extension [[ "$url" == *.asc || "$url" == *.txt ]] && mode="dearmor" || mode="binary" fi fi # Process based on mode if [[ "$mode" == "dearmor" ]]; then if gpg --dearmor --yes -o "$output" <"$temp_key" 2>/dev/null && [[ -s "$output" ]]; then rm -f "$temp_key" debug_log "GPG key installed (dearmored): $output" return 0 fi else if mv "$temp_key" "$output" 2>/dev/null; then chmod 644 "$output" debug_log "GPG key installed: $output" return 0 fi fi debug_log "GPG key processing attempt $attempt failed" sleep "$attempt" ((attempt++)) done rm -f "$temp_key" debug_log "GPG key download FAILED after $retries attempts: $url" return 7 } # ------------------------------------------------------------------------------ # Cache installed version to avoid repeated checks # ------------------------------------------------------------------------------ cache_installed_version() { local app="$1" local version="$2" mkdir -p /var/cache/app-versions echo "$version" >"/var/cache/app-versions/${app}_version.txt" } get_cached_version() { local app="$1" mkdir -p /var/cache/app-versions if [[ -f "/var/cache/app-versions/${app}_version.txt" ]]; then cat "/var/cache/app-versions/${app}_version.txt" return 0 fi return 0 } # ------------------------------------------------------------------------------ # Clean up ALL keyring locations for a tool (unified helper) # Usage: cleanup_tool_keyrings "mariadb" "mysql" "postgresql" # ------------------------------------------------------------------------------ cleanup_tool_keyrings() { local tool_patterns=("$@") for pattern in "${tool_patterns[@]}"; do rm -f /usr/share/keyrings/${pattern}*.gpg \ /etc/apt/keyrings/${pattern}*.gpg \ /etc/apt/trusted.gpg.d/${pattern}*.gpg 2>/dev/null || true done } # ------------------------------------------------------------------------------ # Stop and disable all service instances matching a pattern # Usage: stop_all_services "php*-fpm" "mysql" "mariadb" # ------------------------------------------------------------------------------ stop_all_services() { local service_patterns=("$@") for pattern in "${service_patterns[@]}"; do # Find all matching services (grep || true to handle no matches) local services services=$(systemctl list-units --type=service --all 2>/dev/null | grep -oE "${pattern}[^ ]*\.service" 2>/dev/null | sort -u) || true if [[ -n "$services" ]]; then while read -r service; do $STD systemctl stop "$service" 2>/dev/null || true $STD systemctl disable "$service" 2>/dev/null || true done <<<"$services" fi done } # ------------------------------------------------------------------------------ # Verify installed tool version matches expected version # Returns: 0 if match, 1 if mismatch (with warning) # Usage: verify_tool_version "nodejs" "22" "$(node -v | grep -oP '^v\K[0-9]+')" # ------------------------------------------------------------------------------ verify_tool_version() { local tool_name="$1" local expected_version="$2" local installed_version="$3" # Extract major version for comparison local expected_major="${expected_version%%.*}" local installed_major="${installed_version%%.*}" if [[ "$installed_major" != "$expected_major" ]]; then msg_warn "$tool_name version mismatch: expected $expected_version, got $installed_version" return 1 fi return 0 } # ------------------------------------------------------------------------------ # Clean up legacy installation methods (nvm, rbenv, rustup, etc.) # Usage: cleanup_legacy_install "nodejs" -> removes nvm # ------------------------------------------------------------------------------ cleanup_legacy_install() { local tool_name="$1" case "$tool_name" in nodejs | node) if [[ -d "$HOME/.nvm" ]]; then msg_info "Removing legacy nvm installation" rm -rf "$HOME/.nvm" "$HOME/.npm" "$HOME/.bower" "$HOME/.config/yarn" 2>/dev/null || true sed -i '/NVM_DIR/d' "$HOME/.bashrc" "$HOME/.profile" 2>/dev/null || true msg_ok "Legacy nvm installation removed" fi ;; ruby) if [[ -d "$HOME/.rbenv" ]]; then msg_info "Removing legacy rbenv installation" rm -rf "$HOME/.rbenv" 2>/dev/null || true sed -i '/rbenv/d' "$HOME/.bashrc" "$HOME/.profile" 2>/dev/null || true msg_ok "Legacy rbenv installation removed" fi ;; rust) if [[ -d "$HOME/.cargo" ]] || [[ -d "$HOME/.rustup" ]]; then msg_info "Removing legacy rustup installation" rm -rf "$HOME/.cargo" "$HOME/.rustup" 2>/dev/null || true sed -i '/cargo/d' "$HOME/.bashrc" "$HOME/.profile" 2>/dev/null || true msg_ok "Legacy rustup installation removed" fi ;; go | golang) if [[ -d "$HOME/go" ]]; then msg_info "Removing legacy Go workspace" # Keep user code, just remove GOPATH env sed -i '/GOPATH/d' "$HOME/.bashrc" "$HOME/.profile" 2>/dev/null || true msg_ok "Legacy Go workspace cleaned" fi ;; esac } # ------------------------------------------------------------------------------ # Unified repository preparation before setup # Cleans up old repos, keyrings, and ensures APT is working # Usage: prepare_repository_setup "mariadb" "mysql" # ------------------------------------------------------------------------------ prepare_repository_setup() { local repo_names=("$@") # Clean up all old repository files for repo in "${repo_names[@]}"; do cleanup_old_repo_files "$repo" done # Clean up all keyrings cleanup_tool_keyrings "${repo_names[@]}" # Ensure APT is in working state ensure_apt_working || return 100 return 0 } # ------------------------------------------------------------------------------ # Install packages with retry logic # Usage: install_packages_with_retry "mysql-server" "mysql-client" # Features: # - Automatic dpkg recovery on failure # - Individual package fallback if batch fails # - Dependency resolution with apt-get -f install # ------------------------------------------------------------------------------ install_packages_with_retry() { local packages=("$@") local max_retries=3 local retry=0 # Pre-check: ensure dpkg is not in a broken state if dpkg --audit 2>&1 | grep -q .; then $STD dpkg --configure -a 2>/dev/null || true fi while [[ $retry -le $max_retries ]]; do if DEBIAN_FRONTEND=noninteractive $STD apt install -y \ -o Dpkg::Options::="--force-confdef" \ -o Dpkg::Options::="--force-confold" \ "${packages[@]}" 2>/dev/null; then return 0 fi retry=$((retry + 1)) if [[ $retry -le $max_retries ]]; then msg_warn "Package installation failed, retrying ($retry/$max_retries)..." # Progressive recovery steps based on retry count case $retry in 1) # First retry: just fix dpkg and update $STD dpkg --configure -a 2>/dev/null || true $STD apt update 2>/dev/null || true ;; 2) # Second retry: fix broken dependencies $STD apt --fix-broken install -y 2>/dev/null || true $STD apt update 2>/dev/null || true ;; 3) # Third retry: try installing packages one by one local failed=() for pkg in "${packages[@]}"; do if ! $STD apt install -y "$pkg" 2>/dev/null; then # Try with --fix-missing if ! $STD apt install -y --fix-missing "$pkg" 2>/dev/null; then failed+=("$pkg") fi fi done # If some packages installed, consider partial success if [[ ${#failed[@]} -lt ${#packages[@]} ]]; then if [[ ${#failed[@]} -gt 0 ]]; then msg_warn "Partially installed. Failed packages: ${failed[*]}" fi return 0 fi ;; esac sleep $((retry * 2)) fi done msg_error "Failed to install packages after $((max_retries + 1)) attempts: ${packages[*]}" return 100 } # ------------------------------------------------------------------------------ # Upgrade specific packages with retry logic # Usage: upgrade_packages_with_retry "mariadb-server" "mariadb-client" # ------------------------------------------------------------------------------ upgrade_packages_with_retry() { local packages=("$@") local max_retries=2 local retry=0 while [[ $retry -le $max_retries ]]; do if DEBIAN_FRONTEND=noninteractive $STD apt install --only-upgrade -y \ -o Dpkg::Options::="--force-confdef" \ -o Dpkg::Options::="--force-confold" \ "${packages[@]}" 2>/dev/null; then return 0 fi retry=$((retry + 1)) if [[ $retry -le $max_retries ]]; then msg_warn "Package upgrade failed, retrying ($retry/$max_retries)..." sleep 2 # Fix any interrupted dpkg operations before retry $STD dpkg --configure -a 2>/dev/null || true $STD apt update 2>/dev/null || true fi done msg_error "Failed to upgrade packages after $((max_retries + 1)) attempts: ${packages[*]}" return 100 } # ------------------------------------------------------------------------------ # Check if tool is already installed and optionally verify exact version # Returns: 0 if installed (with optional version match), 1 if not installed # Usage: is_tool_installed "mariadb" "11.4" || echo "Not installed" # ------------------------------------------------------------------------------ is_tool_installed() { local tool_name="$1" local required_version="${2:-}" local installed_version="" case "$tool_name" in mariadb) if command -v mariadb >/dev/null 2>&1; then installed_version=$(mariadb --version 2>/dev/null | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1 || true) fi ;; mysql) if command -v mysql >/dev/null 2>&1; then installed_version=$(mysql --version 2>/dev/null | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1 || true) fi ;; mongodb | mongod) if command -v mongod >/dev/null 2>&1; then installed_version=$(mongod --version 2>/dev/null | awk '/db version/{print $3}' | cut -d. -f1,2) fi ;; node | nodejs) if command -v node >/dev/null 2>&1; then installed_version=$(node -v 2>/dev/null | grep -oP '^v\K[0-9]+' || true) fi ;; php) if command -v php >/dev/null 2>&1; then installed_version=$(php -v 2>/dev/null | awk '/^PHP/{print $2}' | cut -d. -f1,2) fi ;; postgres | postgresql) if command -v psql >/dev/null 2>&1; then installed_version=$(psql --version 2>/dev/null | awk '{print $3}' | cut -d. -f1) fi ;; ruby) if command -v ruby >/dev/null 2>&1; then installed_version=$(ruby --version 2>/dev/null | awk '{print $2}' | cut -d. -f1,2) fi ;; rust | rustc) if command -v rustc >/dev/null 2>&1; then installed_version=$(rustc --version 2>/dev/null | awk '{print $2}') fi ;; go | golang) if command -v go >/dev/null 2>&1; then installed_version=$(go version 2>/dev/null | awk '{print $3}' | sed 's/go//') fi ;; clickhouse) if command -v clickhouse >/dev/null 2>&1; then installed_version=$(clickhouse --version 2>/dev/null | awk '{print $2}') fi ;; esac if [[ -z "$installed_version" ]]; then return 1 # Not installed fi if [[ -n "$required_version" && "$installed_version" != "$required_version" ]]; then echo "$installed_version" return 1 # Version mismatch fi echo "$installed_version" return 0 # Installed and version matches (if specified) } # ------------------------------------------------------------------------------ # Remove old tool version completely (purge + cleanup repos) # Usage: remove_old_tool_version "mariadb" "repository-name" # ------------------------------------------------------------------------------ remove_old_tool_version() { local tool_name="$1" local repo_name="${2:-$tool_name}" case "$tool_name" in mariadb) stop_all_services "mariadb" $STD apt purge -y 'mariadb*' >/dev/null 2>&1 || true cleanup_tool_keyrings "mariadb" ;; mysql) stop_all_services "mysql" $STD apt purge -y 'mysql*' >/dev/null 2>&1 || true rm -rf /var/lib/mysql 2>/dev/null || true cleanup_tool_keyrings "mysql" ;; mongodb) stop_all_services "mongod" $STD apt purge -y 'mongodb*' >/dev/null 2>&1 || true rm -rf /var/lib/mongodb 2>/dev/null || true cleanup_tool_keyrings "mongodb" ;; node | nodejs) $STD apt purge -y nodejs npm >/dev/null 2>&1 || true # Clean up npm global modules if command -v npm >/dev/null 2>&1; then npm list -g 2>/dev/null | grep -oE '^ \S+' | awk '{print $1}' 2>/dev/null | while read -r module; do npm uninstall -g "$module" >/dev/null 2>&1 || true done || true fi cleanup_legacy_install "nodejs" cleanup_tool_keyrings "nodesource" ;; php) stop_all_services "php.*-fpm" $STD apt purge -y 'php*' >/dev/null 2>&1 || true rm -rf /etc/php 2>/dev/null || true cleanup_tool_keyrings "deb.sury.org-php" "php" ;; postgresql) stop_all_services "postgresql" $STD apt purge -y 'postgresql*' >/dev/null 2>&1 || true # Keep data directory for safety (can be removed manually if needed) # rm -rf /var/lib/postgresql 2>/dev/null || true cleanup_tool_keyrings "postgresql" "pgdg" ;; java) $STD apt purge -y 'temurin*' 'adoptium*' 'openjdk*' >/dev/null 2>&1 || true cleanup_tool_keyrings "adoptium" ;; ruby) cleanup_legacy_install "ruby" $STD apt purge -y 'ruby*' >/dev/null 2>&1 || true ;; rust) cleanup_legacy_install "rust" ;; go | golang) rm -rf /usr/local/go 2>/dev/null || true cleanup_legacy_install "golang" ;; clickhouse) stop_all_services "clickhouse-server" $STD apt purge -y 'clickhouse*' >/dev/null 2>&1 || true rm -rf /var/lib/clickhouse 2>/dev/null || true cleanup_tool_keyrings "clickhouse" ;; esac # Clean up old repository files (both .list and .sources) cleanup_old_repo_files "$repo_name" return 0 } # ------------------------------------------------------------------------------ # Determine if tool update/upgrade is needed # Returns: 0 (update needed), 1 (already up-to-date) # Usage: if should_update_tool "mariadb" "11.4"; then ... fi # ------------------------------------------------------------------------------ should_update_tool() { local tool_name="$1" local target_version="$2" local current_version="" # Get currently installed version current_version=$(is_tool_installed "$tool_name" 2>/dev/null) || return 0 # Not installed = needs install # If versions are identical, no update needed if [[ "$current_version" == "$target_version" ]]; then return 1 # No update needed fi return 0 # Update needed } # ------------------------------------------------------------------------------ # Unified repository management for tools # Handles adding, updating, and verifying tool repositories # Usage: manage_tool_repository "mariadb" "11.4" "https://repo..." "GPG_key_url" # Supports: mariadb, mongodb, nodejs, postgresql, php, mysql # ------------------------------------------------------------------------------ manage_tool_repository() { local tool_name="$1" local version="$2" local repo_url="$3" local gpg_key_url="${4:-}" local distro_id repo_component suite distro_id=$(awk -F= '/^ID=/{print $2}' /etc/os-release | tr -d '"') case "$tool_name" in mariadb) if [[ -z "$repo_url" || -z "$gpg_key_url" ]]; then msg_error "MariaDB repository requires repo_url and gpg_key_url" return 65 fi # Clean old repos first cleanup_old_repo_files "mariadb" # Get suite for fallback handling local distro_codename distro_codename=$(awk -F= '/^VERSION_CODENAME=/{print $2}' /etc/os-release) suite=$(get_fallback_suite "$distro_id" "$distro_codename" "$repo_url/$distro_id") # Setup new repository using deb822 format setup_deb822_repo \ "mariadb" \ "$gpg_key_url" \ "$repo_url/$distro_id" \ "$suite" \ "main" return 0 ;; mongodb) if [[ -z "$repo_url" || -z "$gpg_key_url" ]]; then msg_error "MongoDB repository requires repo_url and gpg_key_url" return 65 fi # Clean old repos first cleanup_old_repo_files "mongodb" # Import GPG key with retry logic if ! download_gpg_key "$gpg_key_url" "/etc/apt/keyrings/mongodb-server-${version}.gpg" "dearmor"; then msg_error "Failed to download MongoDB GPG key" return 7 fi chmod 644 "/etc/apt/keyrings/mongodb-server-${version}.gpg" # Setup repository local distro_codename distro_codename=$(awk -F= '/^VERSION_CODENAME=/{print $2}' /etc/os-release) # Suite mapping with fallback for newer releases not yet supported by upstream if [[ "$distro_id" == "debian" ]]; then case "$distro_codename" in trixie | forky | sid) # Testing/unstable releases fallback to latest stable suite suite="bookworm" ;; bookworm) suite="bookworm" ;; bullseye) suite="bullseye" ;; *) # Unknown release: fallback to latest stable suite msg_warn "Unknown Debian release '${distro_codename}', using bookworm" suite="bookworm" ;; esac elif [[ "$distro_id" == "ubuntu" ]]; then case "$distro_codename" in oracular | plucky) # Newer releases fallback to latest LTS suite="noble" ;; noble) suite="noble" ;; jammy) suite="jammy" ;; focal) suite="focal" ;; *) # Unknown release: fallback to latest LTS msg_warn "Unknown Ubuntu release '${distro_codename}', using noble" suite="noble" ;; esac else # For other distros, try generic fallback suite=$(get_fallback_suite "$distro_id" "$distro_codename" "$repo_url") fi repo_component="main" [[ "$distro_id" == "ubuntu" ]] && repo_component="multiverse" cat </etc/apt/sources.list.d/mongodb-org-${version}.sources Types: deb URIs: ${repo_url} Suites: ${suite}/mongodb-org/${version} Components: ${repo_component} Architectures: $(dpkg --print-architecture) Signed-By: /etc/apt/keyrings/mongodb-server-${version}.gpg EOF return 0 ;; nodejs) if [[ -z "$repo_url" || -z "$gpg_key_url" ]]; then msg_error "Node.js repository requires repo_url and gpg_key_url" return 65 fi cleanup_old_repo_files "nodesource" # NodeSource uses deb822 format with GPG from repo local distro_codename distro_codename=$(awk -F= '/^VERSION_CODENAME=/{print $2}' /etc/os-release) # Download GPG key from NodeSource with retry logic if ! download_gpg_key "$gpg_key_url" "/etc/apt/keyrings/nodesource.gpg" "dearmor"; then msg_error "Failed to import NodeSource GPG key" return 7 fi cat </etc/apt/sources.list.d/nodesource.sources Types: deb URIs: $repo_url Suites: nodistro Components: main Architectures: $(dpkg --print-architecture) Signed-By: /etc/apt/keyrings/nodesource.gpg EOF return 0 ;; php) if [[ -z "$gpg_key_url" ]]; then msg_error "PHP repository requires gpg_key_url" return 65 fi cleanup_old_repo_files "php" # Download and install keyring with retry logic if ! curl_with_retry "$gpg_key_url" "/tmp/debsuryorg-archive-keyring.deb"; then msg_error "Failed to download PHP keyring" return 7 fi # Don't use /dev/null redirection for dpkg as it may use background processes dpkg -i /tmp/debsuryorg-archive-keyring.deb >>"$(get_active_logfile)" 2>&1 || { msg_error "Failed to install PHP keyring" rm -f /tmp/debsuryorg-archive-keyring.deb return 100 } rm -f /tmp/debsuryorg-archive-keyring.deb # Setup repository local distro_codename distro_codename=$(awk -F= '/^VERSION_CODENAME=/{print $2}' /etc/os-release) cat </etc/apt/sources.list.d/php.sources Types: deb URIs: https://packages.sury.org/php Suites: $distro_codename Components: main Architectures: $(dpkg --print-architecture) Signed-By: /usr/share/keyrings/deb.sury.org-php.gpg EOF return 0 ;; postgresql) if [[ -z "$gpg_key_url" ]]; then msg_error "PostgreSQL repository requires gpg_key_url" return 65 fi cleanup_old_repo_files "postgresql" # Import PostgreSQL key with retry logic if ! download_gpg_key "$gpg_key_url" "/etc/apt/keyrings/postgresql.gpg" "dearmor"; then msg_error "Failed to import PostgreSQL GPG key" return 7 fi # Setup repository local distro_codename distro_codename=$(awk -F= '/^VERSION_CODENAME=/{print $2}' /etc/os-release) cat </etc/apt/sources.list.d/postgresql.sources Types: deb URIs: http://apt.postgresql.org/pub/repos/apt Suites: $distro_codename-pgdg Components: main Architectures: $(dpkg --print-architecture) Signed-By: /etc/apt/keyrings/postgresql.gpg EOF return 0 ;; *) msg_error "Unknown tool repository: $tool_name" return 65 ;; esac return 0 } # ------------------------------------------------------------------------------ # Unified package upgrade function (with apt update caching) # ------------------------------------------------------------------------------ upgrade_package() { local package="$1" # Use same caching logic as ensure_dependencies local apt_cache_file="/var/cache/apt-update-timestamp" local current_time=$(date +%s) local last_update=0 if [[ -f "$apt_cache_file" ]]; then last_update=$(cat "$apt_cache_file" 2>/dev/null || echo 0) fi if ((current_time - last_update > 300)); then $STD apt update || { msg_warn "APT update failed in upgrade_package - continuing with cached packages" } echo "$current_time" >"$apt_cache_file" fi $STD apt install --only-upgrade -y "$package" || { msg_warn "Failed to upgrade $package" return 100 } } # ------------------------------------------------------------------------------ # Repository availability check with caching # ------------------------------------------------------------------------------ # Note: Must use -gA (global) because tools.func is sourced inside update_os() # function scope. Plain 'declare -A' would create a local variable that gets # destroyed when update_os() returns, causing "unbound variable" errors later # when setup_postgresql/verify_repo_available tries to access the cache key. declare -gA _REPO_CACHE 2>/dev/null || declare -A _REPO_CACHE 2>/dev/null || true verify_repo_available() { local repo_url="$1" local suite="$2" local cache_key="${repo_url}|${suite}" local cache_ttl=300 # 5 minutes # Check cache first (avoid repeated HTTP requests) if [[ -n "${_REPO_CACHE[$cache_key]:-}" ]]; then local cached_time cached_result cached_time=$(echo "${_REPO_CACHE[$cache_key]}" | cut -d'|' -f1) cached_result=$(echo "${_REPO_CACHE[$cache_key]}" | cut -d'|' -f2) if (($(date +%s) - cached_time < cache_ttl)); then [[ "$cached_result" == "1" ]] && return 0 || return 1 fi fi # Perform actual check with short timeout local result=1 if curl -fsSL --max-time 5 --connect-timeout 3 "${repo_url}/dists/${suite}/Release" &>/dev/null; then result=0 fi # Cache the result _REPO_CACHE[$cache_key]="$(date +%s)|$result" return $result } # ------------------------------------------------------------------------------ # Ensure dependencies are installed (with apt/apk update caching) # Supports both Debian (apt/dpkg) and Alpine (apk) systems # ------------------------------------------------------------------------------ ensure_dependencies() { local deps=("$@") local missing=() # Detect Alpine Linux if [[ -f /etc/alpine-release ]]; then for dep in "${deps[@]}"; do if command -v "$dep" &>/dev/null; then continue fi if apk info -e "$dep" &>/dev/null; then continue fi missing+=("$dep") done if [[ ${#missing[@]} -gt 0 ]]; then $STD apk add --no-cache "${missing[@]}" || { local failed=() for pkg in "${missing[@]}"; do if ! $STD apk add --no-cache "$pkg" 2>/dev/null; then failed+=("$pkg") fi done if [[ ${#failed[@]} -gt 0 ]]; then msg_error "Failed to install dependencies: ${failed[*]}" return 1 fi } fi return 0 fi # Debian/Ubuntu: Fast batch check using dpkg-query local installed_pkgs installed_pkgs=$(dpkg-query -W -f='${Package}\n' 2>/dev/null | sort -u) for dep in "${deps[@]}"; do # First check if command exists (for binaries like jq, curl) if command -v "$dep" &>/dev/null; then continue fi # Then check if package is installed if echo "$installed_pkgs" | grep -qx "$dep"; then continue fi missing+=("$dep") done if [[ ${#missing[@]} -gt 0 ]]; then # Only run apt update if not done recently (within last 5 minutes) local apt_cache_file="/var/cache/apt-update-timestamp" local current_time current_time=$(date +%s) local last_update=0 if [[ -f "$apt_cache_file" ]]; then last_update=$(cat "$apt_cache_file" 2>/dev/null || echo 0) fi if ((current_time - last_update > 300)); then # Ensure orphaned sources are cleaned before updating cleanup_orphaned_sources 2>/dev/null || true if ! $STD apt update; then ensure_apt_working || return 100 fi echo "$current_time" >"$apt_cache_file" fi $STD apt install -y "${missing[@]}" || { # Fallback: try installing one by one to identify problematic package local failed=() for pkg in "${missing[@]}"; do if ! $STD apt install -y "$pkg" 2>/dev/null; then failed+=("$pkg") fi done if [[ ${#failed[@]} -gt 0 ]]; then msg_error "Failed to install dependencies: ${failed[*]}" return 100 fi } fi } # ------------------------------------------------------------------------------ # Smart version comparison # ------------------------------------------------------------------------------ version_gt() { test "$(printf '%s\n' "$@" | sort -V | head -n 1)" != "$1" } # ------------------------------------------------------------------------------ # Get system architecture (normalized) # ------------------------------------------------------------------------------ get_system_arch() { local arch_type="${1:-dpkg}" # dpkg, uname, or both local arch case "$arch_type" in dpkg) arch=$(dpkg --print-architecture 2>/dev/null) ;; uname) arch=$(uname -m) [[ "$arch" == "x86_64" ]] && arch="amd64" [[ "$arch" == "aarch64" ]] && arch="arm64" ;; both | *) arch=$(dpkg --print-architecture 2>/dev/null || uname -m) [[ "$arch" == "x86_64" ]] && arch="amd64" [[ "$arch" == "aarch64" ]] && arch="arm64" ;; esac echo "$arch" } # ------------------------------------------------------------------------------ # Create temporary directory with automatic cleanup # ------------------------------------------------------------------------------ create_temp_dir() { local tmp_dir=$(mktemp -d) # Set trap to cleanup on EXIT, ERR, INT, TERM trap "rm -rf '$tmp_dir'" EXIT ERR INT TERM echo "$tmp_dir" } # ------------------------------------------------------------------------------ # Check if package is installed (supports both Debian and Alpine) # ------------------------------------------------------------------------------ is_package_installed() { local package="$1" if [[ -f /etc/alpine-release ]]; then apk info -e "$package" &>/dev/null else dpkg-query -W -f='${Status}' "$package" 2>/dev/null | grep -q "^install ok installed$" fi } # ------------------------------------------------------------------------------ # validate_github_token() # Checks a GitHub token via the /user endpoint. # Prints a status message and returns: # 0 - token is valid # 1 - token is invalid / expired (HTTP 401) # 2 - token has no public repo scope (HTTP 200 but missing scope) # 3 - network/API error # Also reports expiry date if the token carries an x-oauth-expiry header. # ------------------------------------------------------------------------------ validate_github_token() { local token="${1:-${GITHUB_TOKEN:-}}" [[ -z "$token" ]] && return 3 local response headers http_code expiry_date scopes headers=$(mktemp) response=$(curl -sSL -w "%{http_code}" \ -D "$headers" \ -o /dev/null \ -H "Authorization: Bearer $token" \ -H "Accept: application/vnd.github+json" \ -H "X-GitHub-Api-Version: 2022-11-28" \ "https://api.github.com/user" 2>/dev/null) || { rm -f "$headers" return 3 } http_code="$response" # Read expiry header (fine-grained PATs carry this) expiry_date=$(grep -i '^github-authentication-token-expiration:' "$headers" | sed 's/.*: *//' | tr -d '\r\n' || true) # Read token scopes (classic PATs) scopes=$(grep -i '^x-oauth-scopes:' "$headers" | sed 's/.*: *//' | tr -d '\r\n' || true) rm -f "$headers" case "$http_code" in 200) if [[ -n "$expiry_date" ]]; then msg_ok "GitHub token is valid (expires: $expiry_date)." else msg_ok "GitHub token is valid (no expiry / fine-grained PAT)." fi # Warn if classic PAT has no public_repo scope if [[ -n "$scopes" && "$scopes" != *"public_repo"* && "$scopes" != *"repo"* ]]; then msg_warn "Token has no 'public_repo' scope - private repos and some release APIs may fail." return 2 fi return 0 ;; 401) msg_error "GitHub token is invalid or expired (HTTP 401)." return 1 ;; *) msg_warn "GitHub token validation returned HTTP $http_code - treating as valid." return 0 ;; esac } # ------------------------------------------------------------------------------ # Prompt user to enter a GitHub Personal Access Token (PAT) interactively # Returns 0 if a valid token was provided, 1 otherwise # ------------------------------------------------------------------------------ prompt_for_github_token() { if [[ ! -t 0 ]]; then # Non-interactive: pick up var_github_token if set (from default.vars / app.vars / env) if [[ -z "${GITHUB_TOKEN:-}" && -n "${var_github_token:-}" ]]; then export GITHUB_TOKEN="${var_github_token}" msg_ok "GitHub token loaded from var_github_token." return 0 fi return 1 fi # Prefer var_github_token when already set and no interactive override needed if [[ -z "${GITHUB_TOKEN:-}" && -n "${var_github_token:-}" ]]; then export GITHUB_TOKEN="${var_github_token}" msg_ok "GitHub token loaded from var_github_token." validate_github_token || true return 0 fi local reply read -rp "${TAB}Would you like to enter a GitHub Personal Access Token (PAT)? [y/N]: " reply reply="${reply:-n}" if [[ ! "${reply,,}" =~ ^(y|yes)$ ]]; then return 1 fi local token while true; do read -rp "${TAB}Enter your GitHub PAT: " token # Trim leading/trailing whitespace token="$(echo "$token" | xargs)" if [[ -z "$token" ]]; then msg_warn "Token cannot be empty. Please try again." continue fi if [[ "$token" =~ [[:space:]] ]]; then msg_warn "Token must not contain spaces. Please try again." continue fi # Validate before accepting export GITHUB_TOKEN="$token" if validate_github_token "$token"; then break else msg_warn "Please enter a valid token, or press Ctrl+C to abort." unset GITHUB_TOKEN fi done msg_ok "GitHub token has been set." return 0 } # ------------------------------------------------------------------------------ # GitHub API call with authentication and rate limit handling # ------------------------------------------------------------------------------ github_api_call() { local url="$1" local output_file="${2:-/dev/stdout}" local max_retries=3 local retry_delay=2 local header_args=() [[ -n "${GITHUB_TOKEN:-}" ]] && header_args=(-H "Authorization: Bearer $GITHUB_TOKEN") local attempt=1 while ((attempt <= max_retries)); do local http_code http_code=$(curl -sSL -w "%{http_code}" -o "$output_file" \ -H "Accept: application/vnd.github+json" \ -H "X-GitHub-Api-Version: 2022-11-28" \ "${header_args[@]}" \ "$url" 2>/dev/null) || true case "$http_code" in 200) return 0 ;; 401) msg_error "GitHub API authentication failed (HTTP 401)." if [[ -n "${GITHUB_TOKEN:-}" ]]; then msg_error "Your GITHUB_TOKEN appears to be invalid or expired." else msg_error "The repository may require authentication." fi if prompt_for_github_token; then header_args=(-H "Authorization: Bearer $GITHUB_TOKEN") continue fi return 22 ;; 403) # Rate limit - check if we can retry if [[ $attempt -lt $max_retries ]]; then msg_warn "GitHub API rate limit, waiting ${retry_delay}s... (attempt $attempt/$max_retries)" sleep "$retry_delay" retry_delay=$((retry_delay * 2)) ((attempt++)) continue fi msg_error "GitHub API rate limit exceeded (HTTP 403)." if prompt_for_github_token; then header_args=(-H "Authorization: Bearer $GITHUB_TOKEN") retry_delay=2 attempt=1 continue fi msg_error "To increase the limit, export a GitHub token before running the script:" msg_error " export GITHUB_TOKEN=\"ghp_your_token_here\"" return 22 ;; 404) msg_error "GitHub repository or release not found (HTTP 404): $url" return 22 ;; 000 | "") if [[ $attempt -lt $max_retries ]]; then sleep "$retry_delay" ((attempt++)) continue fi msg_error "GitHub API connection failed (no response)." msg_error "Check your network/DNS: curl -sSL https://api.github.com/rate_limit" return 22 ;; *) if [[ $attempt -lt $max_retries ]]; then sleep "$retry_delay" ((attempt++)) continue fi msg_error "GitHub API call failed (HTTP $http_code)." return 22 ;; esac ((attempt++)) done msg_error "GitHub API call failed after ${max_retries} attempts: ${url}" return 22 } # ------------------------------------------------------------------------------ # Codeberg API call with retry logic # ------------------------------------------------------------------------------ codeberg_api_call() { local url="$1" local output_file="${2:-/dev/stdout}" local max_retries=3 local retry_delay=2 for attempt in $(seq 1 $max_retries); do local http_code http_code=$(curl -sSL -w "%{http_code}" -o "$output_file" \ -H "Accept: application/json" \ "$url" 2>/dev/null) || true case "$http_code" in 200) return 0 ;; 401) msg_error "Codeberg API authentication failed (HTTP 401)." return 22 ;; 403) # Rate limit - retry if [[ $attempt -lt $max_retries ]]; then msg_warn "Codeberg API rate limit, waiting ${retry_delay}s... (attempt $attempt/$max_retries)" sleep "$retry_delay" retry_delay=$((retry_delay * 2)) continue fi msg_error "Codeberg API rate limit exceeded (HTTP 403)." return 22 ;; 404) msg_error "Codeberg repository or release not found (HTTP 404): $url" return 22 ;; 000 | "") if [[ $attempt -lt $max_retries ]]; then sleep "$retry_delay" continue fi msg_error "Codeberg API connection failed (no response)." msg_error "Check your network/DNS: curl -sSL https://codeberg.org" return 22 ;; *) if [[ $attempt -lt $max_retries ]]; then sleep "$retry_delay" continue fi msg_error "Codeberg API call failed (HTTP $http_code)." return 22 ;; esac done msg_error "Codeberg API call failed after ${max_retries} attempts: ${url}" return 22 } should_upgrade() { local current="$1" local target="$2" [[ -z "$current" ]] && return 0 version_gt "$target" "$current" && return 0 return 1 } # ------------------------------------------------------------------------------ # Get OS information (cached for performance) # ------------------------------------------------------------------------------ get_os_info() { local field="${1:-all}" # id, codename, version, version_id, all # Cache OS info to avoid repeated file reads if [[ -z "${_OS_ID:-}" ]]; then export _OS_ID=$(awk -F= '/^ID=/{gsub(/"/,"",$2); print $2}' /etc/os-release) export _OS_CODENAME=$(awk -F= '/^VERSION_CODENAME=/{gsub(/"/,"",$2); print $2}' /etc/os-release) export _OS_VERSION=$(awk -F= '/^VERSION_ID=/{gsub(/"/,"",$2); print $2}' /etc/os-release) export _OS_VERSION_FULL=$(awk -F= '/^VERSION=/{gsub(/"/,"",$2); print $2}' /etc/os-release) fi case "$field" in id) echo "$_OS_ID" ;; codename) echo "$_OS_CODENAME" ;; version) echo "$_OS_VERSION" ;; version_id) echo "$_OS_VERSION" ;; version_full) echo "$_OS_VERSION_FULL" ;; all) echo "ID=$_OS_ID CODENAME=$_OS_CODENAME VERSION=$_OS_VERSION" ;; *) echo "$_OS_ID" ;; esac } # ------------------------------------------------------------------------------ # Check if running on specific OS # ------------------------------------------------------------------------------ is_debian() { [[ "$(get_os_info id)" == "debian" ]] } is_ubuntu() { [[ "$(get_os_info id)" == "ubuntu" ]] } is_alpine() { [[ "$(get_os_info id)" == "alpine" ]] } # ------------------------------------------------------------------------------ # Get Debian/Ubuntu major version # ------------------------------------------------------------------------------ get_os_version_major() { local version=$(get_os_info version) echo "${version%%.*}" } # ------------------------------------------------------------------------------ # Download file with retry logic and progress # ------------------------------------------------------------------------------ download_file() { local url="$1" local output="$2" local max_retries="${3:-3}" local show_progress="${4:-false}" local curl_opts=(-fsSL) [[ "$show_progress" == "true" ]] && curl_opts=(-fL#) for attempt in $(seq 1 $max_retries); do if curl "${curl_opts[@]}" -o "$output" "$url"; then return 0 fi if [[ $attempt -lt $max_retries ]]; then msg_warn "Download failed, retrying... (attempt $attempt/$max_retries)" sleep 2 fi done msg_error "Failed to download: $url" return 250 } # ------------------------------------------------------------------------------ # Get fallback suite for repository (comprehensive mapping) # ------------------------------------------------------------------------------ get_fallback_suite() { local distro_id="$1" local distro_codename="$2" local repo_base_url="$3" # Check if current codename works if verify_repo_available "$repo_base_url" "$distro_codename"; then echo "$distro_codename" return 0 fi # Build fallback chain based on distro local fallback_chain=() case "$distro_id" in debian) case "$distro_codename" in trixie | forky | sid) fallback_chain=("bookworm" "bullseye") ;; bookworm) fallback_chain=("bookworm" "bullseye") ;; bullseye) fallback_chain=("bullseye" "buster") ;; *) fallback_chain=("bookworm" "bullseye") ;; esac ;; ubuntu) case "$distro_codename" in oracular | plucky) fallback_chain=("noble" "jammy" "focal") ;; noble) fallback_chain=("noble" "jammy") ;; mantic | lunar) fallback_chain=("jammy" "focal") ;; jammy) fallback_chain=("jammy" "focal") ;; focal) fallback_chain=("focal" "bionic") ;; *) fallback_chain=("jammy" "focal") ;; esac ;; *) echo "$distro_codename" return 0 ;; esac # Try each fallback suite with actual HTTP check for suite in "${fallback_chain[@]}"; do if verify_repo_available "$repo_base_url" "$suite"; then debug_log "Fallback suite found: $suite for $distro_codename" echo "$suite" return 0 fi done # Last resort: return first fallback without verification echo "${fallback_chain[0]:-$distro_codename}" return 0 } # ------------------------------------------------------------------------------ # Verify package source and version # ------------------------------------------------------------------------------ verify_package_source() { local package="$1" local expected_version="$2" if apt-cache policy "$package" 2>/dev/null | grep -q "$expected_version"; then return 0 fi return 1 } # ------------------------------------------------------------------------------ # Check if running on LTS version # ------------------------------------------------------------------------------ is_lts_version() { local os_id=$(get_os_info id) local codename=$(get_os_info codename) if [[ "$os_id" == "ubuntu" ]]; then case "$codename" in focal | jammy | noble) return 0 ;; # 20.04, 22.04, 24.04 *) return 1 ;; esac elif [[ "$os_id" == "debian" ]]; then # Debian releases are all "stable" case "$codename" in bullseye | bookworm | trixie) return 0 ;; *) return 1 ;; esac fi return 1 } # ------------------------------------------------------------------------------ # Get optimal number of parallel jobs (cached) # Features: # - CPU count detection # - Memory-based limiting (1.5GB per job for safety) # - Current load awareness # - Container/VM detection for conservative limits # ------------------------------------------------------------------------------ get_parallel_jobs() { if [[ -z "${_PARALLEL_JOBS:-}" ]]; then local cpu_count cpu_count=$(nproc 2>/dev/null || grep -c ^processor /proc/cpuinfo 2>/dev/null || echo 1) local mem_mb mem_mb=$(free -m 2>/dev/null | awk '/^Mem:/{print $2}' || echo 1024) # Assume 1.5GB per compilation job for safety margin local max_by_mem=$((mem_mb / 1536)) ((max_by_mem < 1)) && max_by_mem=1 # Check current system load - reduce jobs if already loaded local load_1m load_1m=$(awk '{print int($1)}' /proc/loadavg 2>/dev/null || echo 0) local available_cpus=$((cpu_count - load_1m)) ((available_cpus < 1)) && available_cpus=1 # Take minimum of: available CPUs, memory-limited, and total CPUs local max_jobs=$cpu_count ((max_by_mem < max_jobs)) && max_jobs=$max_by_mem ((available_cpus < max_jobs)) && max_jobs=$available_cpus # Container detection - be more conservative in containers if [[ -f /.dockerenv ]] || grep -q 'lxc\|docker\|container' /proc/1/cgroup 2>/dev/null; then # Reduce by 25% in containers to leave headroom max_jobs=$((max_jobs * 3 / 4)) ((max_jobs < 1)) && max_jobs=1 fi # Final bounds check ((max_jobs < 1)) && max_jobs=1 ((max_jobs > cpu_count)) && max_jobs=$cpu_count export _PARALLEL_JOBS=$max_jobs debug_log "Parallel jobs: $_PARALLEL_JOBS (CPUs: $cpu_count, mem-limit: $max_by_mem, load: $load_1m)" fi echo "$_PARALLEL_JOBS" } # ------------------------------------------------------------------------------ # Get default PHP version for OS # Updated for latest distro releases # ------------------------------------------------------------------------------ get_default_php_version() { local os_id os_id=$(get_os_info id) local os_version os_version=$(get_os_version_major) case "$os_id" in debian) case "$os_version" in 14) echo "8.4" ;; # Debian 14 (Forky) - future 13) echo "8.3" ;; # Debian 13 (Trixie) 12) echo "8.2" ;; # Debian 12 (Bookworm) 11) echo "7.4" ;; # Debian 11 (Bullseye) *) echo "8.3" ;; # Default to latest stable esac ;; ubuntu) case "$os_version" in 26) echo "8.4" ;; # Ubuntu 26.04 - future 24) echo "8.3" ;; # Ubuntu 24.04 LTS (Noble) 22) echo "8.1" ;; # Ubuntu 22.04 LTS (Jammy) 20) echo "7.4" ;; # Ubuntu 20.04 LTS (Focal) *) echo "8.3" ;; # Default to latest stable esac ;; *) echo "8.3" ;; esac } # ------------------------------------------------------------------------------ # Get default Python version for OS # Updated for latest distro releases # ------------------------------------------------------------------------------ get_default_python_version() { local os_id os_id=$(get_os_info id) local os_version os_version=$(get_os_version_major) case "$os_id" in debian) case "$os_version" in 14) echo "3.13" ;; # Debian 14 (Forky) - future 13) echo "3.12" ;; # Debian 13 (Trixie) 12) echo "3.11" ;; # Debian 12 (Bookworm) 11) echo "3.9" ;; # Debian 11 (Bullseye) *) echo "3.12" ;; # Default to latest stable esac ;; ubuntu) case "$os_version" in 26) echo "3.13" ;; # Ubuntu 26.04 - future 24) echo "3.12" ;; # Ubuntu 24.04 LTS 22) echo "3.10" ;; # Ubuntu 22.04 LTS 20) echo "3.8" ;; # Ubuntu 20.04 LTS *) echo "3.12" ;; # Default to latest stable esac ;; *) echo "3.12" ;; esac } # ------------------------------------------------------------------------------ # Get default Node.js LTS version # ------------------------------------------------------------------------------ get_default_nodejs_version() { # Current LTS as of January 2026 (Node.js 24 LTS) echo "24" } # ------------------------------------------------------------------------------ # Check if package manager is locked # ------------------------------------------------------------------------------ is_apt_locked() { if fuser /var/lib/dpkg/lock-frontend &>/dev/null || fuser /var/lib/apt/lists/lock &>/dev/null || fuser /var/cache/apt/archives/lock &>/dev/null; then return 0 fi return 1 } # ------------------------------------------------------------------------------ # Wait for apt to be available # ------------------------------------------------------------------------------ wait_for_apt() { local max_wait="${1:-300}" # 5 minutes default local waited=0 while is_apt_locked; do if [[ $waited -ge $max_wait ]]; then msg_error "Timeout waiting for apt to be available" return 100 fi sleep 5 waited=$((waited + 5)) done return 0 } # ------------------------------------------------------------------------------ # Cleanup old repository files (migration helper) # ------------------------------------------------------------------------------ cleanup_old_repo_files() { local app="$1" # Remove old-style .list files (including backups) rm -f /etc/apt/sources.list.d/"${app}"*.list rm -f /etc/apt/sources.list.d/"${app}"*.list.save rm -f /etc/apt/sources.list.d/"${app}"*.list.distUpgrade rm -f /etc/apt/sources.list.d/"${app}"*.list.dpkg-* # Remove old GPG keys from trusted.gpg.d rm -f /etc/apt/trusted.gpg.d/"${app}"*.gpg # Remove keyrings from /etc/apt/keyrings rm -f /etc/apt/keyrings/"${app}"*.gpg # Remove ALL .sources files for this app (including the main one) # This ensures no orphaned .sources files reference deleted keyrings rm -f /etc/apt/sources.list.d/"${app}"*.sources } # ------------------------------------------------------------------------------ # Cleanup orphaned .sources files that reference missing keyrings # This prevents APT signature verification errors # Call this at the start of any setup function to ensure APT is in a clean state # ------------------------------------------------------------------------------ cleanup_orphaned_sources() { local sources_dir="/etc/apt/sources.list.d" local keyrings_dir="/etc/apt/keyrings" [[ ! -d "$sources_dir" ]] && return 0 while IFS= read -r -d '' sources_file; do local basename_file basename_file=$(basename "$sources_file") # NEVER remove debian.sources - this is the standard Debian repository if [[ "$basename_file" == "debian.sources" ]]; then continue fi # Extract Signed-By path from .sources file local keyring_path keyring_path=$(grep -E '^Signed-By:' "$sources_file" 2>/dev/null | awk '{print $2}' 2>/dev/null || true) # If keyring doesn't exist, remove the .sources file if [[ -n "$keyring_path" ]] && [[ ! -f "$keyring_path" ]]; then rm -f "$sources_file" fi done < <(find "$sources_dir" -name "*.sources" -print0 2>/dev/null) # Also check for broken symlinks in keyrings directory if [[ -d "$keyrings_dir" ]]; then find "$keyrings_dir" -type l ! -exec test -e {} \; -delete 2>/dev/null || true fi } # ------------------------------------------------------------------------------ # Ensure APT is in a working state before installing packages # This should be called at the start of any setup function # Features: # - Fixes interrupted dpkg operations # - Removes orphaned sources # - Handles lock file contention # - Progressive recovery with fallbacks # ------------------------------------------------------------------------------ ensure_apt_working() { local max_wait=60 # Maximum seconds to wait for apt lock # Wait for any existing apt/dpkg processes to finish local waited=0 while fuser /var/lib/dpkg/lock-frontend &>/dev/null || fuser /var/lib/apt/lists/lock &>/dev/null || fuser /var/cache/apt/archives/lock &>/dev/null; do if ((waited >= max_wait)); then msg_warn "APT lock held for ${max_wait}s, attempting to continue anyway" break fi debug_log "Waiting for APT lock (${waited}s)..." sleep 2 ((waited += 2)) done # Fix interrupted dpkg operations first # This can happen if a previous installation was interrupted (e.g., by script error) if dpkg --audit 2>&1 | grep -q .; then debug_log "Fixing interrupted dpkg operations" $STD dpkg --configure -a 2>/dev/null || true fi # Clean up orphaned sources first cleanup_orphaned_sources # Try to update package lists if ! $STD apt update 2>/dev/null; then debug_log "First apt update failed, trying recovery steps" # Step 1: Clear apt lists cache rm -rf /var/lib/apt/lists/* 2>/dev/null || true mkdir -p /var/lib/apt/lists/partial # Step 2: Clean up potentially broken sources cleanup_orphaned_sources # Step 3: Try again if ! $STD apt update 2>/dev/null; then # Step 4: More aggressive - remove all third-party sources msg_warn "APT update still failing, removing third-party sources" find /etc/apt/sources.list.d/ -type f \( -name "*.sources" -o -name "*.list" \) \ ! -name "debian.sources" -delete 2>/dev/null || true # Final attempt if ! $STD apt update; then msg_error "Cannot update package lists - APT is critically broken" return 100 fi fi fi return 0 } # ------------------------------------------------------------------------------ # Standardized deb822 repository setup (with optional Architectures) # Always runs apt update after repo creation to ensure package availability # ------------------------------------------------------------------------------ setup_deb822_repo() { local name="$1" local gpg_url="$2" local repo_url="$3" local suite="$4" local component="${5:-main}" local architectures="${6-}" # optional local enabled="${7-}" # optional: "true" or "false" # Validate required parameters if [[ -z "$name" || -z "$gpg_url" || -z "$repo_url" || -z "$suite" ]]; then msg_error "setup_deb822_repo: missing required parameters (name=$name repo=$repo_url suite=$suite)" return 65 fi # Cleanup cleanup_old_repo_files "$name" cleanup_orphaned_sources mkdir -p /etc/apt/keyrings || { msg_error "Failed to create /etc/apt/keyrings" return 252 } # Import GPG key (auto-detect binary vs ASCII-armored format) local tmp_gpg tmp_gpg=$(mktemp) || return 252 curl -fsSL "$gpg_url" -o "$tmp_gpg" || { msg_error "Failed to download GPG key for ${name}" rm -f "$tmp_gpg" return 7 } if grep -q "BEGIN PGP" "$tmp_gpg" 2>/dev/null; then # ASCII-armored — dearmor to binary gpg --dearmor --yes -o "/etc/apt/keyrings/${name}.gpg" <"$tmp_gpg" || { msg_error "Failed to install GPG key for ${name}" rm -f "$tmp_gpg" return 251 } else # Already binary — copy directly cp -f "$tmp_gpg" "/etc/apt/keyrings/${name}.gpg" || { msg_error "Failed to install GPG key for ${name}" rm -f "$tmp_gpg" return 252 } fi rm -f "$tmp_gpg" chmod 644 "/etc/apt/keyrings/${name}.gpg" # Write deb822 { echo "Types: deb" echo "URIs: $repo_url" echo "Suites: $suite" # Flat repositories (suite ending with "/" or "./") must not have Components if [[ "$suite" != *"/" && -n "$component" ]]; then echo "Components: $component" fi [[ -n "$architectures" ]] && echo "Architectures: $architectures" echo "Signed-By: /etc/apt/keyrings/${name}.gpg" [[ -n "$enabled" ]] && echo "Enabled: $enabled" } >/etc/apt/sources.list.d/${name}.sources $STD apt update || { msg_warn "apt update failed after adding repository: ${name}" } } # ------------------------------------------------------------------------------ # Package version hold/unhold helpers # ------------------------------------------------------------------------------ hold_package_version() { local package="$1" $STD apt-mark hold "$package" || { msg_warn "Failed to hold package version: ${package}" } } unhold_package_version() { local package="$1" $STD apt-mark unhold "$package" || { msg_warn "Failed to unhold package version: ${package}" } } # ------------------------------------------------------------------------------ # Safe service restart with verification # ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------ # Safe service restart with retry logic and wait-for-ready # Usage: safe_service_restart "nginx" [timeout_seconds] # ------------------------------------------------------------------------------ safe_service_restart() { local service="$1" local timeout="${2:-30}" # Default 30 second timeout local max_retries=2 local retry=0 while [[ $retry -le $max_retries ]]; do if systemctl is-active --quiet "$service"; then $STD systemctl restart "$service" else $STD systemctl start "$service" fi # Wait for service to become active with timeout local waited=0 while [[ $waited -lt $timeout ]]; do if systemctl is-active --quiet "$service"; then return 0 fi sleep 1 ((waited++)) done retry=$((retry + 1)) if [[ $retry -le $max_retries ]]; then debug_log "Service $service failed to start, retrying ($retry/$max_retries)..." # Try to stop completely before retry systemctl stop "$service" 2>/dev/null || true sleep 2 fi done msg_error "Failed to start $service after $max_retries retries" systemctl status "$service" --no-pager -l 2>/dev/null | head -20 || true return 150 } # ------------------------------------------------------------------------------ # Enable and start service (with error handling) # ------------------------------------------------------------------------------ enable_and_start_service() { local service="$1" if ! systemctl enable "$service" &>/dev/null; then msg_error "Failed to enable service: $service" return 150 fi if ! systemctl start "$service" &>/dev/null; then msg_error "Failed to start $service" systemctl status "$service" --no-pager return 150 fi return 0 } # ------------------------------------------------------------------------------ # Check if service is enabled # ------------------------------------------------------------------------------ is_service_enabled() { local service="$1" systemctl is-enabled --quiet "$service" 2>/dev/null } # ------------------------------------------------------------------------------ # Check if service is running # ------------------------------------------------------------------------------ is_service_running() { local service="$1" systemctl is-active --quiet "$service" 2>/dev/null } # ------------------------------------------------------------------------------ # Extract version from JSON (GitHub releases) # ------------------------------------------------------------------------------ extract_version_from_json() { local json="$1" local field="${2:-tag_name}" local strip_v="${3:-true}" ensure_dependencies jq local version version=$(echo "$json" | jq -r ".${field} // empty") if [[ -z "$version" ]]; then msg_warn "JSON field '${field}' is empty in API response" return 250 fi if [[ "$strip_v" == "true" ]]; then echo "${version#v}" else echo "$version" fi } # ------------------------------------------------------------------------------ # Get latest GitHub tag (for repos that only publish tags, not releases). # # Usage: # get_latest_gh_tag "owner/repo" [prefix] # # Arguments: # $1 - GitHub repo (owner/repo) # $2 - Optional prefix filter (e.g., "v" to only match tags starting with "v") # # Returns: # Latest tag name (stdout), or returns 1 on failure # ------------------------------------------------------------------------------ get_latest_gh_tag() { local repo="$1" local prefix="${2:-}" local temp_file temp_file=$(mktemp) local tag="" if [[ -n "$prefix" ]]; then # Use git/matching-refs API for server-side prefix filtering. This avoids # paging through unrelated tags (e.g. mongodb/mongo-tools where 100.x tags # only appear after page 4 of /tags). Returns ALL tags matching the prefix # in a single call, sorted lexicographically ascending; we pick the # highest version using `sort -V`. if ! github_api_call "https://api.github.com/repos/${repo}/git/matching-refs/tags/${prefix}" "$temp_file"; then rm -f "$temp_file" return 22 fi local count count=$(jq 'length' "$temp_file" 2>/dev/null || echo 0) if [[ "$count" -gt 0 ]]; then tag=$(jq -r '.[].ref' "$temp_file" | sed 's|^refs/tags/||' | sort -V | tail -n1) fi else # No prefix: just take the first (newest) tag from /tags if ! github_api_call "https://api.github.com/repos/${repo}/tags?per_page=1" "$temp_file"; then rm -f "$temp_file" return 22 fi tag=$(jq -r '.[0].name // empty' "$temp_file") fi rm -f "$temp_file" if [[ -z "$tag" ]]; then msg_error "No tags found for ${repo}" return 250 fi echo "$tag" } # ------------------------------------------------------------------------------ # Get latest GitHub release version with fallback to tags # Usage: get_latest_github_release "owner/repo" [strip_v] [include_prerelease] # ------------------------------------------------------------------------------ get_latest_github_release() { local repo="$1" local strip_v="${2:-true}" local temp_file=$(mktemp) if ! github_api_call "https://api.github.com/repos/${repo}/releases/latest" "$temp_file"; then msg_warn "GitHub API call failed for ${repo}" rm -f "$temp_file" return 22 fi local version version=$(extract_version_from_json "$(cat "$temp_file")" "tag_name" "$strip_v") rm -f "$temp_file" if [[ -z "$version" ]]; then msg_error "Could not determine latest version for ${repo}" return 250 fi echo "$version" } # ------------------------------------------------------------------------------ # Get latest Codeberg release version # ------------------------------------------------------------------------------ get_latest_codeberg_release() { local repo="$1" local strip_v="${2:-true}" local temp_file=$(mktemp) # Codeberg API: get all releases and pick the first non-draft/non-prerelease if ! codeberg_api_call "https://codeberg.org/api/v1/repos/${repo}/releases" "$temp_file"; then msg_warn "Codeberg API call failed for ${repo}" rm -f "$temp_file" return 22 fi local version # Codeberg uses same JSON structure but releases endpoint returns array version=$(jq -r '[.[] | select(.draft==false and .prerelease==false)][0].tag_name // empty' "$temp_file") if [[ "$strip_v" == "true" ]]; then version="${version#v}" fi rm -f "$temp_file" if [[ -z "$version" ]]; then msg_error "Could not determine latest version for ${repo}" return 250 fi echo "$version" } # ------------------------------------------------------------------------------ # Debug logging - using main debug_log function (line 40) # Supports both TOOLS_DEBUG and DEBUG environment variables # ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------ # Performance timing helper # ------------------------------------------------------------------------------ start_timer() { echo $(date +%s) } end_timer() { local start_time="$1" local label="${2:-Operation}" local end_time=$(date +%s) local duration=$((end_time - start_time)) } # ------------------------------------------------------------------------------ # GPG key fingerprint verification # ------------------------------------------------------------------------------ verify_gpg_fingerprint() { local key_file="$1" local expected_fingerprint="$2" local actual_fingerprint actual_fingerprint=$(gpg --show-keys --with-fingerprint --with-colons "$key_file" 2>&1 | grep -m1 '^fpr:' | cut -d: -f10) if [[ "$actual_fingerprint" == "$expected_fingerprint" ]]; then return 0 fi msg_error "GPG fingerprint mismatch! Expected: $expected_fingerprint, Got: $actual_fingerprint" return 65 } # ------------------------------------------------------------------------------ # Fetches and deploys a GitHub tag-based source tarball. # # Description: # - Downloads the source tarball for a given tag from GitHub # - Extracts to the target directory # - Writes the version to ~/. # # Usage: # fetch_and_deploy_gh_tag "guacd" "apache/guacamole-server" # fetch_and_deploy_gh_tag "guacd" "apache/guacamole-server" "latest" "/opt/guacamole-server" # # Arguments: # $1 - App name (used for version file ~/.) # $2 - GitHub repo (owner/repo) # $3 - Tag version (default: "latest" → auto-detect via get_latest_gh_tag) # $4 - Target directory (default: /opt/$app) # # Notes: # - Supports CLEAN_INSTALL=1 to wipe target before extracting # - For repos that only publish tags, not GitHub Releases # ------------------------------------------------------------------------------ fetch_and_deploy_gh_tag() { local app="$1" local repo="$2" local version="${3:-latest}" local target="${4:-/opt/$app}" local app_lc="" app_lc="$(echo "${app,,}" | tr -d ' ')" local version_file="$HOME/.${app_lc}" if [[ "$version" == "latest" ]]; then version=$(get_latest_gh_tag "$repo") || { msg_error "Failed to determine latest tag for ${repo}" return 250 } fi local current_version="" [[ -f "$version_file" ]] && current_version=$(<"$version_file") if [[ "$current_version" == "$version" ]]; then msg_ok "$app is already up-to-date ($version)" return 0 fi local tmpdir tmpdir=$(mktemp -d) || return 1 local tarball_url="https://github.com/${repo}/archive/refs/tags/${version}.tar.gz" local filename="${app_lc}-${version}.tar.gz" msg_info "Fetching GitHub tag: ${app} (${version})" download_file "$tarball_url" "$tmpdir/$filename" || { msg_error "Download failed: $tarball_url" rm -rf "$tmpdir" return 7 } mkdir -p "$target" if [[ "${CLEAN_INSTALL:-0}" == "1" ]]; then rm -rf "${target:?}/"* fi tar --no-same-owner -xzf "$tmpdir/$filename" -C "$tmpdir" || { msg_error "Failed to extract tarball" rm -rf "$tmpdir" return 251 } local unpack_dir unpack_dir=$(find "$tmpdir" -mindepth 1 -maxdepth 1 -type d | head -n1) shopt -s dotglob nullglob cp -r "$unpack_dir"/* "$target/" shopt -u dotglob nullglob rm -rf "$tmpdir" echo "$version" >"$version_file" msg_ok "Deployed ${app} ${version} to ${target}" return 0 } # ------------------------------------------------------------------------------ # Checks for new GitHub tag (for repos without releases). # # Description: # - Uses get_latest_gh_tag to fetch the latest tag # - Compares it to a local cached version (~/.) # - If newer, sets global CHECK_UPDATE_RELEASE and returns 0 # # Usage: # if check_for_gh_tag "guacd" "apache/guacamole-server"; then # fetch_and_deploy_gh_tag "guacd" "apache/guacamole-server" "/opt/guacamole-server" # fi # # Notes: # - For repos that only publish tags, not GitHub Releases # - Same interface as check_for_gh_release # ------------------------------------------------------------------------------ check_for_gh_tag() { local app="$1" local repo="$2" local prefix="${3:-}" local app_lc="" app_lc="$(echo "${app,,}" | tr -d ' ')" local current_file="$HOME/.${app_lc}" msg_info "Checking for update: ${app}" local latest="" latest=$(get_latest_gh_tag "$repo" "$prefix") || return 22 local current="" [[ -f "$current_file" ]] && current="$(<"$current_file")" if [[ -z "$current" || "$current" != "$latest" ]]; then CHECK_UPDATE_RELEASE="$latest" msg_ok "Update available: ${app} ${current:-not installed} → ${latest}" return 0 fi msg_ok "No update available: ${app} (${latest})" return 1 } # ============================================================================== # INSTALL FUNCTIONS # ============================================================================== # ------------------------------------------------------------------------------ # Checks for new GitHub release (latest tag). # # Description: # - Queries the GitHub API for the latest release tag # - Compares it to a local cached version (~/.) # - If newer, sets global CHECK_UPDATE_RELEASE and returns 0 # # Usage: # if check_for_gh_release "flaresolverr" "FlareSolverr/FlareSolverr" [optional] "v1.1.1"; then # # trigger update... # fi # exit 0 # } (end of update_script not from the function) # # Notes: # - Requires `jq` (auto-installed if missing) # - Does not modify anything, only checks version state # - Does not support pre-releases # ------------------------------------------------------------------------------ check_for_gh_release() { local app="$1" local source="$2" local pinned_version_in="${3:-}" # optional local pin_reason="${4:-}" # optional reason shown to user local app_lc="" app_lc="$(echo "${app,,}" | tr -d ' ')" local current_file="$HOME/.${app_lc}" msg_info "Checking for update: ${app}" # DNS check if ! getent hosts api.github.com >/dev/null 2>&1; then msg_error "Network error: cannot resolve api.github.com" return 6 fi ensure_dependencies jq # Build auth header if token is available local header_args=() [[ -n "${GITHUB_TOKEN:-}" ]] && header_args=(-H "Authorization: Bearer $GITHUB_TOKEN") # Try /latest endpoint for non-pinned versions (most efficient) local releases_json="" http_code="" # For pinned versions, query the specific release tag directly if [[ -n "$pinned_version_in" ]]; then http_code=$(curl -sSL --max-time 20 -w "%{http_code}" -o /tmp/gh_check.json \ -H 'Accept: application/vnd.github+json' \ -H 'X-GitHub-Api-Version: 2022-11-28' \ "${header_args[@]}" \ "https://api.github.com/repos/${source}/releases/tags/${pinned_version_in}" 2>/dev/null) || true if [[ "$http_code" == "200" ]] && [[ -s /tmp/gh_check.json ]]; then releases_json="[$(/dev/null) || true if [[ "$http_code" == "200" ]] && [[ -s /tmp/gh_check.json ]]; then releases_json="[$(/dev/null) || true if [[ "$http_code" == "200" ]] && [[ -s /tmp/gh_check.json ]]; then releases_json=$(/dev/null) if ((${#legacy_files[@]} == 1)); then current="$(<"${legacy_files[0]}")" echo "${current#v}" >"$current_file" rm -f "${legacy_files[0]}" fi fi current="${current#v}" # Pinned version handling if [[ -n "$pinned_version_in" ]]; then local pin_clean="${pinned_version_in#v}" local match_raw="" for i in "${!clean_tags[@]}"; do if [[ "${clean_tags[$i]}" == "$pin_clean" ]]; then match_raw="${raw_tags[$i]}" break fi done if [[ -z "$match_raw" ]]; then msg_error "Pinned version ${pinned_version_in} not found upstream" return 250 fi if [[ "$current" != "$pin_clean" ]]; then CHECK_UPDATE_RELEASE="$match_raw" msg_ok "Update available: ${app} ${current:-not installed} → ${pin_clean}" return 0 fi if [[ -n "$pin_reason" ]]; then msg_ok "No update available: ${app} (${current}) - update held back: ${pin_reason}" else msg_ok "No update available: ${app} (${current}) - update temporarily held back due to issues with newer releases" fi return 1 fi # No pinning → use latest if [[ -z "$current" || "$current" != "$latest_clean" ]]; then CHECK_UPDATE_RELEASE="$latest_raw" msg_ok "Update available: ${app} ${current:-not installed} → ${latest_clean}" return 0 fi msg_ok "No update available: ${app} (${latest_clean})" return 1 } # ------------------------------------------------------------------------------ # Checks for new Codeberg release (latest tag). # # Description: # - Queries the Codeberg API for the latest release tag # - Compares it to a local cached version (~/.) # - If newer, sets global CHECK_UPDATE_RELEASE and returns 0 # # Usage: # if check_for_codeberg_release "autocaliweb" "gelbphoenix/autocaliweb" [optional] "v0.11.3"; then # # trigger update... # fi # exit 0 # } (end of update_script not from the function) # # Notes: # - Requires `jq` (auto-installed if missing) # - Does not modify anything, only checks version state # - Does not support pre-releases # ------------------------------------------------------------------------------ check_for_codeberg_release() { local app="$1" local source="$2" local pinned_version_in="${3:-}" # optional local pin_reason="${4:-}" # optional reason shown to user local app_lc="${app,,}" local current_file="$HOME/.${app_lc}" msg_info "Checking for update: ${app}" # DNS check if ! getent hosts codeberg.org >/dev/null 2>&1; then msg_error "Network error: cannot resolve codeberg.org" return 6 fi ensure_dependencies jq # Fetch releases from Codeberg API local releases_json="" releases_json=$(curl -fsSL --max-time 20 \ -H 'Accept: application/json' \ "https://codeberg.org/api/v1/repos/${source}/releases" 2>/dev/null) || { msg_error "Unable to fetch releases for ${app} (codeberg.org/api/v1/repos/${source}/releases)" return 22 } mapfile -t raw_tags < <(jq -r '.[] | select(.draft==false and .prerelease==false) | .tag_name' <<<"$releases_json") if ((${#raw_tags[@]} == 0)); then msg_error "No stable releases found for ${app}" return 250 fi local clean_tags=() for t in "${raw_tags[@]}"; do # Only strip leading 'v' when followed by a digit (e.g. v1.2.3) if [[ "$t" =~ ^v[0-9] ]]; then clean_tags+=("${t:1}") else clean_tags+=("$t") fi done local latest_raw="${raw_tags[0]}" local latest_clean="${clean_tags[0]}" # current installed (stored without v) local current="" if [[ -f "$current_file" ]]; then current="$(<"$current_file")" else # Migration: search for any /opt/*_version.txt local legacy_files mapfile -t legacy_files < <(find /opt -maxdepth 1 -type f -name "*_version.txt" 2>/dev/null) if ((${#legacy_files[@]} == 1)); then current="$(<"${legacy_files[0]}")" echo "${current#v}" >"$current_file" rm -f "${legacy_files[0]}" fi fi current="${current#v}" # Pinned version handling if [[ -n "$pinned_version_in" ]]; then local pin_clean="${pinned_version_in#v}" local match_raw="" for i in "${!clean_tags[@]}"; do if [[ "${clean_tags[$i]}" == "$pin_clean" ]]; then match_raw="${raw_tags[$i]}" break fi done if [[ -z "$match_raw" ]]; then msg_error "Pinned version ${pinned_version_in} not found upstream" return 250 fi if [[ "$current" != "$pin_clean" ]]; then CHECK_UPDATE_RELEASE="$match_raw" msg_ok "Update available: ${app} ${current:-not installed} → ${pin_clean}" return 0 fi if [[ -n "$pin_reason" ]]; then msg_ok "No update available: ${app} (${current}) - update held back: ${pin_reason}" else msg_ok "No update available: ${app} (${current}) - update temporarily held back due to issues with newer releases" fi return 1 fi # No pinning → use latest if [[ -z "$current" || "$current" != "$latest_clean" ]]; then CHECK_UPDATE_RELEASE="$latest_raw" msg_ok "Update available: ${app} ${current:-not installed} → ${latest_clean}" return 0 fi msg_ok "No update available: ${app} (${latest_clean})" return 1 } # ------------------------------------------------------------------------------ # Creates and installs self-signed certificates. # # Description: # - Create a self-signed certificate with option to override application name # # Variables: # APP - Application name (default: $APPLICATION variable) # ------------------------------------------------------------------------------ create_self_signed_cert() { local APP_NAME="${1:-${APPLICATION}}" local HOSTNAME="$(hostname -f)" local IP="$(hostname -I | awk '{print $1}')" local APP_NAME_LC=$(echo "${APP_NAME,,}" | tr -d ' ') local CERT_DIR="/etc/ssl/${APP_NAME_LC}" local CERT_KEY="${CERT_DIR}/${APP_NAME_LC}.key" local CERT_CRT="${CERT_DIR}/${APP_NAME_LC}.crt" if [[ -f "$CERT_CRT" && -f "$CERT_KEY" ]]; then return 0 fi # Use ensure_dependencies for cleaner handling ensure_dependencies openssl || { msg_error "Failed to install OpenSSL" return 100 } mkdir -p "$CERT_DIR" $STD openssl req -new -newkey rsa:2048 -days 365 -nodes -x509 \ -subj "/CN=${HOSTNAME}" \ -addext "subjectAltName=DNS:${HOSTNAME},DNS:localhost,IP:${IP},IP:127.0.0.1" \ -keyout "$CERT_KEY" \ -out "$CERT_CRT" || { msg_error "Failed to create self-signed certificate" return 150 } chmod 600 "$CERT_KEY" chmod 644 "$CERT_CRT" } # ------------------------------------------------------------------------------ # Downloads file with optional progress indicator using pv. # # Arguments: # $1 - URL # $2 - Destination path # ------------------------------------------------------------------------------ function download_with_progress() { local url="$1" local output="$2" if [ -n "$SPINNER_PID" ] && ps -p "$SPINNER_PID" >/dev/null; then kill "$SPINNER_PID" >/dev/null; fi ensure_dependencies pv set -o pipefail # Content-Length aus HTTP-Header holen local content_length content_length=$(curl -fsSLI "$url" | awk '/Content-Length/ {print $2}' | tr -d '\r' || true) if [[ -z "$content_length" ]]; then if ! curl -fL# -o "$output" "$url"; then msg_error "Download failed: $url" return 7 fi else if ! curl -fsSL "$url" | pv -s "$content_length" >"$output"; then msg_error "Download failed: $url" return 7 fi fi } # ------------------------------------------------------------------------------ # Ensures /usr/local/bin is permanently in system PATH. # # Description: # - Adds to /etc/profile.d for login shells (SSH, noVNC) # - Adds to /root/.bashrc for non-login shells (pct enter) # ------------------------------------------------------------------------------ function ensure_usr_local_bin_persist() { # Skip on Proxmox host command -v pveversion &>/dev/null && return # Login shells: /etc/profile.d/ local PROFILE_FILE="/etc/profile.d/custom_path.sh" if [[ ! -f "$PROFILE_FILE" ]]; then echo 'export PATH="/usr/local/bin:$PATH"' >"$PROFILE_FILE" chmod +x "$PROFILE_FILE" fi # Non-login shells (pct enter): /root/.bashrc local BASHRC="/root/.bashrc" if [[ -f "$BASHRC" ]] && ! grep -q '/usr/local/bin' "$BASHRC"; then echo 'export PATH="/usr/local/bin:$PATH"' >>"$BASHRC" fi } # ------------------------------------------------------------------------------ # curl_download - Downloads a file with automatic retry and exponential backoff. # # Usage: curl_download # # Retries up to 5 times with increasing --max-time (60/120/240/480/960s). # Returns 0 on success, 1 if all attempts fail. # ------------------------------------------------------------------------------ function curl_download() { local output="$1" local url="$2" local timeouts=(60 120 240 480 960) for i in "${!timeouts[@]}"; do if curl --connect-timeout 15 --max-time "${timeouts[$i]}" -fsSL -o "$output" "$url"; then return 0 fi if ((i < ${#timeouts[@]} - 1)); then msg_warn "Download timed out after ${timeouts[$i]}s, retrying... (attempt $((i + 2))/${#timeouts[@]})" fi done return 7 } # ------------------------------------------------------------------------------ # Downloads and deploys latest Codeberg release (source, binary, tarball, asset). # # Description: # - Fetches latest release metadata from Codeberg API # - Supports the following modes: # - tarball: Source code tarball (default if omitted) # - source: Alias for tarball (same behavior) # - binary: .deb package install (arch-dependent) # - prebuild: Prebuilt .tar.gz archive (e.g. Go binaries) # - singlefile: Standalone binary (no archive, direct chmod +x install) # - tag: Direct tag download (bypasses Release API) # - Handles download, extraction/installation and version tracking in ~/. # # Parameters: # $1 APP - Application name (used for install path and version file) # $2 REPO - Codeberg repository in form user/repo # $3 MODE - Release type: # tarball → source tarball (.tar.gz) # binary → .deb file (auto-arch matched) # prebuild → prebuilt archive (e.g. tar.gz) # singlefile→ standalone binary (chmod +x) # tag → direct tag (bypasses Release API) # $4 VERSION - Optional release tag (default: latest) # $5 TARGET_DIR - Optional install path (default: /opt/) # $6 ASSET_FILENAME - Required for: # - prebuild → archive filename or pattern # - singlefile→ binary filename or pattern # # Examples: # # 1. Minimal: Fetch and deploy source tarball # fetch_and_deploy_codeberg_release "autocaliweb" "gelbphoenix/autocaliweb" # # # 2. Binary install via .deb asset (architecture auto-detected) # fetch_and_deploy_codeberg_release "myapp" "myuser/myapp" "binary" # # # 3. Prebuilt archive (.tar.gz) with asset filename match # fetch_and_deploy_codeberg_release "myapp" "myuser/myapp" "prebuild" "latest" "/opt/myapp" "myapp_Linux_x86_64.tar.gz" # # # 4. Single binary (chmod +x) # fetch_and_deploy_codeberg_release "myapp" "myuser/myapp" "singlefile" "v1.0.0" "/opt/myapp" "myapp-linux-amd64" # # # 5. Explicit tag version # fetch_and_deploy_codeberg_release "autocaliweb" "gelbphoenix/autocaliweb" "tag" "v0.11.3" "/opt/autocaliweb" # ------------------------------------------------------------------------------ function fetch_and_deploy_codeberg_release() { local app="$1" local repo="$2" local mode="${3:-tarball}" # tarball | binary | prebuild | singlefile | tag local version="${var_appversion:-${4:-latest}}" local target="${5:-/opt/$app}" local asset_pattern="${6:-}" local app_lc=$(echo "${app,,}" | tr -d ' ') local version_file="$HOME/.${app_lc}" local api_timeouts=(60 120 240) local current_version="" [[ -f "$version_file" ]] && current_version=$(<"$version_file") ensure_dependencies jq ### Tag Mode (bypass Release API) ### if [[ "$mode" == "tag" ]]; then if [[ "$version" == "latest" ]]; then msg_error "Mode 'tag' requires explicit version (not 'latest')" return 65 fi local tag_name="$version" [[ "$tag_name" =~ ^v ]] && version="${tag_name:1}" || version="$tag_name" if [[ "$current_version" == "$version" ]]; then $STD msg_ok "$app is already up-to-date (v$version)" return 0 fi # DNS check if ! getent hosts "codeberg.org" &>/dev/null; then msg_error "DNS resolution failed for codeberg.org – check /etc/resolv.conf or networking" return 6 fi local tmpdir tmpdir=$(mktemp -d) || return 252 msg_info "Fetching Codeberg tag: $app ($tag_name)" local safe_version="${version//@/_}" safe_version="${safe_version//\//_}" local filename="${app_lc}-${safe_version}.tar.gz" local download_success=false # Codeberg archive URL format: https://codeberg.org/{owner}/{repo}/archive/{tag}.tar.gz local archive_url="https://codeberg.org/$repo/archive/${tag_name}.tar.gz" if curl_download "$tmpdir/$filename" "$archive_url"; then download_success=true fi if [[ "$download_success" != "true" ]]; then msg_error "Download failed for $app ($tag_name)" rm -rf "$tmpdir" return 250 fi mkdir -p "$target" if [[ "${CLEAN_INSTALL:-0}" == "1" ]]; then rm -rf "${target:?}/"* fi tar --no-same-owner -xzf "$tmpdir/$filename" -C "$tmpdir" || { msg_error "Failed to extract tarball" rm -rf "$tmpdir" return 251 } local unpack_dir unpack_dir=$(find "$tmpdir" -mindepth 1 -maxdepth 1 -type d | head -n1) shopt -s dotglob nullglob cp -r "$unpack_dir"/* "$target/" shopt -u dotglob nullglob echo "$version" >"$version_file" msg_ok "Deployed: $app ($version)" rm -rf "$tmpdir" return 0 fi # Codeberg API: https://codeberg.org/api/v1/repos/{owner}/{repo}/releases local api_url="https://codeberg.org/api/v1/repos/$repo/releases" if [[ "$version" != "latest" ]]; then # Get release by tag: /repos/{owner}/{repo}/releases/tags/{tag} api_url="https://codeberg.org/api/v1/repos/$repo/releases/tags/$version" fi # dns pre check if ! getent hosts "codeberg.org" &>/dev/null; then msg_error "DNS resolution failed for codeberg.org – check /etc/resolv.conf or networking" return 6 fi local attempt=0 success=false resp http_code while ((attempt < ${#api_timeouts[@]})); do resp=$(curl --connect-timeout 10 --max-time "${api_timeouts[$attempt]}" -fsSL -w "%{http_code}" -o /tmp/codeberg_rel.json "$api_url") && success=true && break attempt=$((attempt + 1)) if ((attempt < ${#api_timeouts[@]})); then msg_warn "API request timed out after ${api_timeouts[$((attempt - 1))]}s, retrying... (attempt $((attempt + 1))/${#api_timeouts[@]})" fi done if ! $success; then msg_error "Failed to fetch release metadata from $api_url after ${#api_timeouts[@]} attempts" return 22 fi http_code="${resp:(-3)}" [[ "$http_code" != "200" ]] && { msg_error "Codeberg API returned HTTP $http_code" return 22 } local json tag_name json=$(/dev/null || uname -m) [[ "$arch" == "x86_64" ]] && arch="amd64" [[ "$arch" == "aarch64" ]] && arch="arm64" local assets url_match="" # Codeberg assets are in .assets[].browser_download_url assets=$(echo "$json" | jq -r '.assets[].browser_download_url') # If explicit filename pattern is provided, match that first if [[ -n "$asset_pattern" ]]; then for u in $assets; do case "${u##*/}" in $asset_pattern) url_match="$u" break ;; esac done fi # Fall back to architecture heuristic if [[ -z "$url_match" ]]; then for u in $assets; do if [[ "$u" =~ ($arch|amd64|x86_64|aarch64|arm64).*\.deb$ ]]; then url_match="$u" break fi done fi # Fallback: any .deb file if [[ -z "$url_match" ]]; then for u in $assets; do [[ "$u" =~ \.deb$ ]] && url_match="$u" && break done fi if [[ -z "$url_match" ]]; then msg_error "No suitable .deb asset found for $app" rm -rf "$tmpdir" return 252 fi filename="${url_match##*/}" curl_download "$tmpdir/$filename" "$url_match" || { msg_error "Download failed: $url_match" rm -rf "$tmpdir" return 250 } chmod 644 "$tmpdir/$filename" $STD apt install -y "$tmpdir/$filename" || { $STD dpkg -i "$tmpdir/$filename" || { msg_error "Both apt and dpkg installation failed" rm -rf "$tmpdir" return 100 } } ### Prebuild Mode ### elif [[ "$mode" == "prebuild" ]]; then local pattern="${6%\"}" pattern="${pattern#\"}" [[ -z "$pattern" ]] && { msg_error "Mode 'prebuild' requires 6th parameter (asset filename pattern)" rm -rf "$tmpdir" return 65 } local asset_url="" for u in $(echo "$json" | jq -r '.assets[].browser_download_url'); do filename_candidate="${u##*/}" case "$filename_candidate" in $pattern) asset_url="$u" break ;; esac done [[ -z "$asset_url" ]] && { msg_error "No asset matching '$pattern' found" rm -rf "$tmpdir" return 252 } filename="${asset_url##*/}" curl_download "$tmpdir/$filename" "$asset_url" || { msg_error "Download failed: $asset_url" rm -rf "$tmpdir" return 250 } local unpack_tmp unpack_tmp=$(mktemp -d) mkdir -p "$target" if [[ "${CLEAN_INSTALL:-0}" == "1" ]]; then rm -rf "${target:?}/"* fi if [[ "$filename" == *.zip ]]; then ensure_dependencies unzip unzip -q "$tmpdir/$filename" -d "$unpack_tmp" || { msg_error "Failed to extract ZIP archive" rm -rf "$tmpdir" "$unpack_tmp" return 251 } elif [[ "$filename" == *.tar.* || "$filename" == *.tgz ]]; then tar --no-same-owner -xf "$tmpdir/$filename" -C "$unpack_tmp" || { msg_error "Failed to extract TAR archive" rm -rf "$tmpdir" "$unpack_tmp" return 251 } else msg_error "Unsupported archive format: $filename" rm -rf "$tmpdir" "$unpack_tmp" return 251 fi local top_dirs top_dirs=$(find "$unpack_tmp" -mindepth 1 -maxdepth 1 -type d | wc -l) local top_entries inner_dir top_entries=$(find "$unpack_tmp" -mindepth 1 -maxdepth 1) if [[ "$(echo "$top_entries" | wc -l)" -eq 1 && -d "$top_entries" ]]; then inner_dir="$top_entries" shopt -s dotglob nullglob if compgen -G "$inner_dir/*" >/dev/null; then cp -r "$inner_dir"/* "$target/" || { msg_error "Failed to copy contents from $inner_dir to $target" rm -rf "$tmpdir" "$unpack_tmp" return 252 } else msg_error "Inner directory is empty: $inner_dir" rm -rf "$tmpdir" "$unpack_tmp" return 252 fi shopt -u dotglob nullglob else shopt -s dotglob nullglob if compgen -G "$unpack_tmp/*" >/dev/null; then cp -r "$unpack_tmp"/* "$target/" || { msg_error "Failed to copy contents to $target" rm -rf "$tmpdir" "$unpack_tmp" return 252 } else msg_error "Unpacked archive is empty" rm -rf "$tmpdir" "$unpack_tmp" return 252 fi shopt -u dotglob nullglob fi ### Singlefile Mode ### elif [[ "$mode" == "singlefile" ]]; then local pattern="${6%\"}" pattern="${pattern#\"}" [[ -z "$pattern" ]] && { msg_error "Mode 'singlefile' requires 6th parameter (asset filename pattern)" rm -rf "$tmpdir" return 65 } local asset_url="" for u in $(echo "$json" | jq -r '.assets[].browser_download_url'); do filename_candidate="${u##*/}" case "$filename_candidate" in $pattern) asset_url="$u" break ;; esac done [[ -z "$asset_url" ]] && { msg_error "No asset matching '$pattern' found" rm -rf "$tmpdir" return 252 } filename="${asset_url##*/}" mkdir -p "$target" local use_filename="${USE_ORIGINAL_FILENAME:-false}" local target_file="$app" [[ "$use_filename" == "true" ]] && target_file="$filename" curl_download "$target/$target_file" "$asset_url" || { msg_error "Download failed: $asset_url" rm -rf "$tmpdir" return 250 } if [[ "$target_file" != *.jar && -f "$target/$target_file" ]]; then chmod +x "$target/$target_file" fi else msg_error "Unknown mode: $mode" rm -rf "$tmpdir" return 65 fi echo "$version" >"$version_file" msg_ok "Deployed: $app ($version)" rm -rf "$tmpdir" } # ------------------------------------------------------------------------------ # Downloads and deploys latest GitHub release (source, binary, tarball, asset). # # Description: # - Fetches latest release metadata from GitHub API # - Supports the following modes: # - tarball: Source code tarball (default if omitted) # - source: Alias for tarball (same behavior) # - binary: .deb package install (arch-dependent) # - prebuild: Prebuilt .tar.gz archive (e.g. Go binaries) # - singlefile: Standalone binary (no archive, direct chmod +x install) # - Handles download, extraction/installation and version tracking in ~/. # # Parameters: # $1 APP - Application name (used for install path and version file) # $2 REPO - GitHub repository in form user/repo # $3 MODE - Release type: # tarball → source tarball (.tar.gz) # binary → .deb file (auto-arch matched) # prebuild → prebuilt archive (e.g. tar.gz) # singlefile→ standalone binary (chmod +x) # $4 VERSION - Optional release tag (default: latest) # $5 TARGET_DIR - Optional install path (default: /opt/) # $6 ASSET_FILENAME - Required for: # - prebuild → archive filename or pattern # - singlefile→ binary filename or pattern # # Optional: # - Set GITHUB_TOKEN env var to increase API rate limit (recommended for CI/CD). # # Examples: # # 1. Minimal: Fetch and deploy source tarball # fetch_and_deploy_gh_release "myapp" "myuser/myapp" # # # 2. Binary install via .deb asset (architecture auto-detected) # fetch_and_deploy_gh_release "myapp" "myuser/myapp" "binary" # # # 3. Prebuilt archive (.tar.gz) with asset filename match # fetch_and_deploy_gh_release "hanko" "teamhanko/hanko" "prebuild" "latest" "/opt/hanko" "hanko_Linux_x86_64.tar.gz" # # # 4. Single binary (chmod +x) like Argus, Promtail etc. # fetch_and_deploy_gh_release "argus" "release-argus/Argus" "singlefile" "0.26.3" "/opt/argus" "Argus-.*linux-amd64" # # Notes: # - For binary/prebuild/singlefile modes: if the target release has no # matching asset, the function scans older releases and prompts the user # (60s timeout, default yes) to use a previous version that has the asset. # ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------ # Scans older GitHub releases for a matching asset when the latest release # is missing the expected file. Used internally by fetch_and_deploy_gh_release. # # Arguments: # $1 - GitHub repo (owner/repo) # $2 - mode (binary|prebuild|singlefile) # $3 - asset_pattern (glob pattern for asset filename) # $4 - tag to skip (the already-checked release) # # Output: # Prints the release JSON of the first older release that has a matching asset. # Returns 0 on success, 1 if no matching release found or user declined. # ------------------------------------------------------------------------------ _gh_scan_older_releases() { local repo="$1" local mode="$2" local asset_pattern="$3" local skip_tag="$4" local header=() [[ -n "${GITHUB_TOKEN:-}" ]] && header=(-H "Authorization: token $GITHUB_TOKEN") local releases_list releases_list=$(curl --connect-timeout 10 --max-time 30 -fsSL \ -H 'Accept: application/vnd.github+json' \ -H 'X-GitHub-Api-Version: 2022-11-28' \ "${header[@]}" \ "https://api.github.com/repos/${repo}/releases?per_page=15" 2>/dev/null) || { msg_warn "Failed to fetch older releases for ${repo}" return 22 } local count count=$(echo "$releases_list" | jq 'length') for ((i = 0; i < count; i++)); do local rel_tag rel_draft rel_prerelease rel_tag=$(echo "$releases_list" | jq -r ".[$i].tag_name") rel_draft=$(echo "$releases_list" | jq -r ".[$i].draft") rel_prerelease=$(echo "$releases_list" | jq -r ".[$i].prerelease") # Skip drafts, prereleases, and the tag we already checked [[ "$rel_draft" == "true" || "$rel_prerelease" == "true" ]] && continue [[ "$rel_tag" == "$skip_tag" ]] && continue local has_match=false if [[ "$mode" == "binary" ]]; then local arch arch=$(dpkg --print-architecture 2>/dev/null || uname -m) [[ "$arch" == "x86_64" ]] && arch="amd64" [[ "$arch" == "aarch64" ]] && arch="arm64" # Check with explicit pattern first, then arch heuristic, then any .deb if [[ -n "$asset_pattern" ]]; then has_match=$(echo "$releases_list" | jq -r --arg pat "$asset_pattern" ".[$i].assets[].name" | while read -r name; do case "$name" in $asset_pattern) echo true break ;; esac done) fi if [[ "$has_match" != "true" ]]; then has_match=$(echo "$releases_list" | jq -r ".[$i].assets[].browser_download_url" | grep -qE "($arch|amd64|x86_64|aarch64|arm64).*\.deb$" && echo true) fi if [[ "$has_match" != "true" ]]; then has_match=$(echo "$releases_list" | jq -r ".[$i].assets[].browser_download_url" | grep -qE '\.deb$' && echo true) fi elif [[ "$mode" == "prebuild" || "$mode" == "singlefile" ]]; then has_match=$(echo "$releases_list" | jq -r ".[$i].assets[].name" | while read -r name; do case "$name" in $asset_pattern) echo true break ;; esac done) fi if [[ "$has_match" == "true" ]]; then local rel_version="$rel_tag" [[ "$rel_tag" =~ ^v ]] && rel_version="${rel_tag:1}" local use_fallback="y" if [[ -t 0 ]]; then msg_warn "Release ${skip_tag} has no matching asset. Previous release ${rel_tag} has a compatible asset." read -rp "Use version ${rel_tag} instead? [Y/n] (auto-yes in 60s): " -t 60 use_fallback || use_fallback="y" use_fallback="${use_fallback:-y}" fi if [[ "${use_fallback,,}" == "y" || "${use_fallback,,}" == "yes" ]]; then echo "$releases_list" | jq ".[$i]" return 0 else return 250 fi fi done return 250 } function fetch_and_deploy_gh_release() { local app="$1" local repo="$2" local mode="${3:-tarball}" # tarball | binary | prebuild | singlefile local version="${var_appversion:-${4:-latest}}" local target="${5:-/opt/$app}" local asset_pattern="${6:-}" # Validate app name to prevent /root/. directory issues if [[ -z "$app" ]]; then # Derive app name from repo if not provided app="${repo##*/}" if [[ -z "$app" ]]; then msg_error "fetch_and_deploy_gh_release requires app name or valid repo" return 65 fi fi local app_lc=$(echo "${app,,}" | tr -d ' ') local version_file="$HOME/.${app_lc}" local api_timeouts=(60 120 240) local current_version="" [[ -f "$version_file" ]] && current_version=$(<"$version_file") ensure_dependencies jq local api_url="https://api.github.com/repos/$repo/releases" [[ "$version" != "latest" ]] && api_url="$api_url/tags/$version" || api_url="$api_url/latest" local header=() [[ -n "${GITHUB_TOKEN:-}" ]] && header=(-H "Authorization: token $GITHUB_TOKEN") # dns pre check local gh_host gh_host=$(awk -F/ '{print $3}' <<<"$api_url") if ! getent hosts "$gh_host" &>/dev/null; then msg_error "DNS resolution failed for $gh_host – check /etc/resolv.conf or networking" return 6 fi local max_retries=${#api_timeouts[@]} retry_delay=2 attempt=1 success=false http_code while ((attempt <= max_retries)); do http_code=$(curl --connect-timeout 10 --max-time "${api_timeouts[$((attempt - 1))]:-240}" -sSL -w "%{http_code}" -o /tmp/gh_rel.json "${header[@]}" "$api_url" 2>/dev/null) || true if [[ "$http_code" == "200" ]]; then success=true break elif [[ "$http_code" == "401" ]]; then msg_error "GitHub API authentication failed (HTTP 401)." if [[ -n "${GITHUB_TOKEN:-}" ]]; then msg_error "Your GITHUB_TOKEN appears to be invalid or expired." else msg_error "The repository may require authentication." fi if prompt_for_github_token; then header=(-H "Authorization: token $GITHUB_TOKEN") continue fi break elif [[ "$http_code" == "403" ]]; then if ((attempt < max_retries)); then msg_warn "GitHub API rate limit hit, retrying in ${retry_delay}s... (attempt $attempt/$max_retries)" sleep "$retry_delay" retry_delay=$((retry_delay * 2)) else msg_error "GitHub API rate limit exceeded (HTTP 403)." if prompt_for_github_token; then header=(-H "Authorization: token $GITHUB_TOKEN") retry_delay=2 attempt=1 continue fi fi else sleep "$retry_delay" fi ((attempt++)) done if ! $success; then if [[ "$http_code" == "000" || -z "$http_code" ]]; then msg_error "GitHub API connection failed (no response)." msg_error "Check your network/DNS: curl -sSL https://api.github.com/rate_limit" elif [[ "$http_code" != "401" ]]; then msg_error "Failed to fetch release metadata (HTTP $http_code)" fi return 22 fi local json tag_name json=$(/dev/null || uname -m) [[ "$arch" == "x86_64" ]] && arch="amd64" [[ "$arch" == "aarch64" ]] && arch="arm64" local assets url_match="" assets=$(echo "$json" | jq -r '.assets[].browser_download_url') # If explicit filename pattern is provided (param $6), match that first if [[ -n "$asset_pattern" ]]; then for u in $assets; do case "${u##*/}" in $asset_pattern) url_match="$u" break ;; esac done fi # If no match via explicit pattern, fall back to architecture heuristic if [[ -z "$url_match" ]]; then for u in $assets; do if [[ "$u" =~ ($arch|aarch64|arm64).*\.deb$ ]]; then url_match="$u" break fi done fi # Fallback: any .deb file if [[ -z "$url_match" ]]; then for u in $assets; do [[ "$u" =~ \.deb$ ]] && url_match="$u" && break done fi # Fallback: scan older releases for a matching .deb asset if [[ -z "$url_match" ]]; then local fallback_json if fallback_json=$(_gh_scan_older_releases "$repo" "binary" "$asset_pattern" "$tag_name"); then json="$fallback_json" tag_name=$(echo "$json" | jq -r '.tag_name // .name // empty') [[ "$tag_name" =~ ^v ]] && version="${tag_name:1}" || version="$tag_name" msg_info "Fetching GitHub release: $app ($version)" assets=$(echo "$json" | jq -r '.assets[].browser_download_url') if [[ -n "$asset_pattern" ]]; then for u in $assets; do case "${u##*/}" in $asset_pattern) url_match="$u" break ;; esac done fi if [[ -z "$url_match" ]]; then for u in $assets; do if [[ "$u" =~ ($arch|amd64|x86_64|aarch64|arm64).*\.deb$ ]]; then url_match="$u" break fi done fi if [[ -z "$url_match" ]]; then for u in $assets; do [[ "$u" =~ \.deb$ ]] && url_match="$u" && break done fi fi fi if [[ -z "$url_match" ]]; then msg_error "No suitable .deb asset found for $app" rm -rf "$tmpdir" return 252 fi filename="${url_match##*/}" curl_download "$tmpdir/$filename" "$url_match" || { msg_error "Download failed: $url_match" rm -rf "$tmpdir" return 250 } chmod 644 "$tmpdir/$filename" # SYSTEMD_OFFLINE=1 prevents systemd-tmpfiles failures in unprivileged LXC (Debian 13+/systemd 257+) # Support DPKG_CONFOLD/DPKG_CONFNEW env vars for config file handling during .deb upgrades local dpkg_opts="" [[ "${DPKG_FORCE_CONFOLD:-}" == "1" ]] && dpkg_opts="-o Dpkg::Options::=--force-confold" [[ "${DPKG_FORCE_CONFNEW:-}" == "1" ]] && dpkg_opts="-o Dpkg::Options::=--force-confnew" DEBIAN_FRONTEND=noninteractive SYSTEMD_OFFLINE=1 $STD apt install -y $dpkg_opts "$tmpdir/$filename" || { SYSTEMD_OFFLINE=1 $STD dpkg -i "$tmpdir/$filename" || { msg_error "Both apt and dpkg installation failed" rm -rf "$tmpdir" return 100 } } ### Prebuild Mode ### elif [[ "$mode" == "prebuild" ]]; then local pattern="${6%\"}" pattern="${pattern#\"}" [[ -z "$pattern" ]] && { msg_error "Mode 'prebuild' requires 6th parameter (asset filename pattern)" rm -rf "$tmpdir" return 65 } local asset_url="" for u in $(echo "$json" | jq -r '.assets[].browser_download_url'); do filename_candidate="${u##*/}" case "$filename_candidate" in $pattern) asset_url="$u" break ;; esac done # Fallback: scan older releases for a matching asset if [[ -z "$asset_url" ]]; then local fallback_json if fallback_json=$(_gh_scan_older_releases "$repo" "prebuild" "$pattern" "$tag_name"); then json="$fallback_json" tag_name=$(echo "$json" | jq -r '.tag_name // .name // empty') [[ "$tag_name" =~ ^v ]] && version="${tag_name:1}" || version="$tag_name" msg_info "Fetching GitHub release: $app ($version)" for u in $(echo "$json" | jq -r '.assets[].browser_download_url'); do filename_candidate="${u##*/}" case "$filename_candidate" in $pattern) asset_url="$u" break ;; esac done fi fi [[ -z "$asset_url" ]] && { msg_error "No asset matching '$pattern' found" rm -rf "$tmpdir" return 252 } filename="${asset_url##*/}" curl_download "$tmpdir/$filename" "$asset_url" || { msg_error "Download failed: $asset_url" rm -rf "$tmpdir" return 250 } local unpack_tmp unpack_tmp=$(mktemp -d) mkdir -p "$target" if [[ "${CLEAN_INSTALL:-0}" == "1" ]]; then rm -rf "${target:?}/"* fi if [[ "$filename" == *.zip ]]; then ensure_dependencies unzip unzip -q "$tmpdir/$filename" -d "$unpack_tmp" || { msg_error "Failed to extract ZIP archive" rm -rf "$tmpdir" "$unpack_tmp" return 251 } elif [[ "$filename" == *.tar.* || "$filename" == *.tgz || "$filename" == *.txz ]]; then tar --no-same-owner -xf "$tmpdir/$filename" -C "$unpack_tmp" || { msg_error "Failed to extract TAR archive" rm -rf "$tmpdir" "$unpack_tmp" return 251 } else msg_error "Unsupported archive format: $filename" rm -rf "$tmpdir" "$unpack_tmp" return 65 fi local top_dirs top_dirs=$(find "$unpack_tmp" -mindepth 1 -maxdepth 1 -type d | wc -l) local top_entries inner_dir top_entries=$(find "$unpack_tmp" -mindepth 1 -maxdepth 1) if [[ "$(echo "$top_entries" | wc -l)" -eq 1 && -d "$top_entries" ]]; then # Strip leading folder inner_dir="$top_entries" shopt -s dotglob nullglob if compgen -G "$inner_dir/*" >/dev/null; then cp -r "$inner_dir"/* "$target/" || { msg_error "Failed to copy contents from $inner_dir to $target" rm -rf "$tmpdir" "$unpack_tmp" return 252 } else msg_error "Inner directory is empty: $inner_dir" rm -rf "$tmpdir" "$unpack_tmp" return 252 fi shopt -u dotglob nullglob else # Copy all contents shopt -s dotglob nullglob if compgen -G "$unpack_tmp/*" >/dev/null; then cp -r "$unpack_tmp"/* "$target/" || { msg_error "Failed to copy contents to $target" rm -rf "$tmpdir" "$unpack_tmp" return 252 } else msg_error "Unpacked archive is empty" rm -rf "$tmpdir" "$unpack_tmp" return 252 fi shopt -u dotglob nullglob fi ### Singlefile Mode ### elif [[ "$mode" == "singlefile" ]]; then local pattern="${6%\"}" pattern="${pattern#\"}" [[ -z "$pattern" ]] && { msg_error "Mode 'singlefile' requires 6th parameter (asset filename pattern)" rm -rf "$tmpdir" return 65 } local asset_url="" for u in $(echo "$json" | jq -r '.assets[].browser_download_url'); do filename_candidate="${u##*/}" case "$filename_candidate" in $pattern) asset_url="$u" break ;; esac done # Fallback: scan older releases for a matching asset if [[ -z "$asset_url" ]]; then local fallback_json if fallback_json=$(_gh_scan_older_releases "$repo" "singlefile" "$pattern" "$tag_name"); then json="$fallback_json" tag_name=$(echo "$json" | jq -r '.tag_name // .name // empty') [[ "$tag_name" =~ ^v ]] && version="${tag_name:1}" || version="$tag_name" msg_info "Fetching GitHub release: $app ($version)" for u in $(echo "$json" | jq -r '.assets[].browser_download_url'); do filename_candidate="${u##*/}" case "$filename_candidate" in $pattern) asset_url="$u" break ;; esac done fi fi [[ -z "$asset_url" ]] && { msg_error "No asset matching '$pattern' found" rm -rf "$tmpdir" return 252 } filename="${asset_url##*/}" mkdir -p "$target" local use_filename="${USE_ORIGINAL_FILENAME:-false}" local target_file="$app" [[ "$use_filename" == "true" ]] && target_file="$filename" curl_download "$target/$target_file" "$asset_url" || { msg_error "Download failed: $asset_url" rm -rf "$tmpdir" return 250 } if [[ "$target_file" != *.jar && -f "$target/$target_file" ]]; then chmod +x "$target/$target_file" fi else msg_error "Unknown mode: $mode" rm -rf "$tmpdir" return 65 fi echo "$version" >"$version_file" msg_ok "Deployed: $app ($version)" rm -rf "$tmpdir" } # ------------------------------------------------------------------------------ # Installs Adminer (Debian/Ubuntu via APT, Alpine via direct download). # # Description: # - Adds Adminer to Apache or web root # - Supports Alpine and Debian-based systems # ------------------------------------------------------------------------------ function setup_adminer() { if grep -qi alpine /etc/os-release; then msg_info "Setup Adminer (Alpine)" mkdir -p /var/www/localhost/htdocs/adminer if ! curl_with_retry "https://github.com/vrana/adminer/releases/latest/download/adminer.php" "/var/www/localhost/htdocs/adminer/index.php"; then msg_error "Failed to download Adminer" return 250 fi cache_installed_version "adminer" "latest-alpine" msg_ok "Setup Adminer (Alpine)" else msg_info "Setup Adminer (Debian/Ubuntu)" ensure_dependencies adminer $STD a2enconf adminer || { msg_error "Failed to enable Adminer Apache config" return 150 } $STD systemctl reload apache2 || { msg_error "Failed to reload Apache" return 150 } local VERSION VERSION=$(dpkg -s adminer 2>/dev/null | grep '^Version:' | awk '{print $2}' 2>/dev/null || echo 'unknown') cache_installed_version "adminer" "${VERSION:-unknown}" msg_ok "Setup Adminer (Debian/Ubuntu)" fi } # ------------------------------------------------------------------------------ # Installs or updates Composer globally (robust, idempotent). # # - Installs to /usr/local/bin/composer # - Removes old binaries/symlinks in /usr/bin, /bin, /root/.composer, etc. # - Ensures /usr/local/bin is in PATH (permanent) # - Auto-updates to latest version # ------------------------------------------------------------------------------ function setup_composer() { local COMPOSER_BIN="/usr/local/bin/composer" export COMPOSER_ALLOW_SUPERUSER=1 # Get currently installed version local INSTALLED_VERSION="" if [[ -x "$COMPOSER_BIN" ]]; then INSTALLED_VERSION=$("$COMPOSER_BIN" --version 2>/dev/null | awk '{print $3}') fi # Scenario 1: Already installed - just self-update if [[ -n "$INSTALLED_VERSION" ]]; then msg_info "Update Composer $INSTALLED_VERSION" $STD "$COMPOSER_BIN" self-update --no-interaction || { msg_warn "Composer self-update failed, continuing with current version" } local UPDATED_VERSION UPDATED_VERSION=$("$COMPOSER_BIN" --version 2>/dev/null | awk '{print $3}') cache_installed_version "composer" "$UPDATED_VERSION" msg_ok "Update Composer $UPDATED_VERSION" return 0 fi # Scenario 2: Fresh install msg_info "Setup Composer" for old in /usr/bin/composer /bin/composer /root/.composer/vendor/bin/composer; do [[ -e "$old" && "$old" != "$COMPOSER_BIN" ]] && rm -f "$old" done ensure_usr_local_bin_persist export PATH="/usr/local/bin:$PATH" if ! curl_with_retry "https://getcomposer.org/installer" "/tmp/composer-setup.php"; then msg_error "Failed to download Composer installer" return 250 fi $STD php /tmp/composer-setup.php --install-dir=/usr/local/bin --filename=composer || { msg_error "Failed to install Composer" rm -f /tmp/composer-setup.php return 150 } rm -f /tmp/composer-setup.php if [[ ! -x "$COMPOSER_BIN" ]]; then msg_error "Composer installation failed" return 127 fi chmod +x "$COMPOSER_BIN" $STD "$COMPOSER_BIN" self-update --no-interaction || { msg_warn "Composer self-update failed after fresh install" } local FINAL_VERSION FINAL_VERSION=$("$COMPOSER_BIN" --version 2>/dev/null | awk '{print $3}') cache_installed_version "composer" "$FINAL_VERSION" msg_ok "Setup Composer" } # ------------------------------------------------------------------------------ # Installs FFmpeg from source or prebuilt binary (Debian/Ubuntu only). # # Description: # - Downloads and builds FFmpeg from GitHub (https://github.com/FFmpeg/FFmpeg) # - Supports specific version override via FFMPEG_VERSION (e.g. n7.1.1) # - Supports build profile via FFMPEG_TYPE: # - minimal : x264, vpx, mp3 only # - medium : adds subtitles, fonts, opus, vorbis # - full : adds dav1d, svt-av1, zlib, numa # - binary : downloads static build (johnvansickle.com) # - Defaults to latest stable version and full feature set # # Notes: # - Requires: curl, jq, build-essential, and matching codec libraries # - Result is installed to /usr/local/bin/ffmpeg # ------------------------------------------------------------------------------ function setup_ffmpeg() { local TMP_DIR=$(mktemp -d) local GITHUB_REPO="FFmpeg/FFmpeg" local VERSION="${FFMPEG_VERSION:-latest}" local TYPE="${FFMPEG_TYPE:-full}" local BIN_PATH="/usr/local/bin/ffmpeg" # Get currently installed version local INSTALLED_VERSION="" if command -v ffmpeg &>/dev/null; then INSTALLED_VERSION=$(ffmpeg -version 2>/dev/null | head -n1 | awk '{print $3}') fi msg_info "Setup FFmpeg ${VERSION} ($TYPE)" # Binary fallback mode if [[ "$TYPE" == "binary" ]]; then if ! CURL_TIMEOUT=300 curl_with_retry "https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-amd64-static.tar.xz" "$TMP_DIR/ffmpeg.tar.xz"; then msg_error "Failed to download FFmpeg binary" rm -rf "$TMP_DIR" return 250 fi tar -xf "$TMP_DIR/ffmpeg.tar.xz" -C "$TMP_DIR" || { msg_error "Failed to extract FFmpeg binary" rm -rf "$TMP_DIR" return 251 } local EXTRACTED_DIR EXTRACTED_DIR=$(find "$TMP_DIR" -maxdepth 1 -type d -name "ffmpeg-*") cp "$EXTRACTED_DIR/ffmpeg" "$BIN_PATH" cp "$EXTRACTED_DIR/ffprobe" /usr/local/bin/ffprobe chmod +x "$BIN_PATH" /usr/local/bin/ffprobe local FINAL_VERSION=$($BIN_PATH -version 2>/dev/null | head -n1 | awk '{print $3}') rm -rf "$TMP_DIR" cache_installed_version "ffmpeg" "$FINAL_VERSION" ensure_usr_local_bin_persist [[ -n "$INSTALLED_VERSION" ]] && msg_ok "Upgrade FFmpeg $INSTALLED_VERSION → $FINAL_VERSION" || msg_ok "Setup FFmpeg $FINAL_VERSION" return 0 fi ensure_dependencies jq # Auto-detect latest stable version if none specified if [[ "$VERSION" == "latest" || -z "$VERSION" ]]; then local ffmpeg_tags ffmpeg_tags=$(curl -fsSL --max-time 15 "https://api.github.com/repos/${GITHUB_REPO}/tags" 2>/dev/null || echo "") if [[ -z "$ffmpeg_tags" ]]; then msg_warn "Could not fetch FFmpeg versions from GitHub, trying binary fallback" VERSION="" # Will trigger binary fallback below else VERSION=$(echo "$ffmpeg_tags" | jq -r '.[].name' 2>/dev/null | grep -E '^n[0-9]+\.[0-9]+\.[0-9]+$' | sort -V | tail -n1 || echo "") fi fi if [[ -z "$VERSION" ]]; then msg_info "Could not determine FFmpeg source version, using pre-built binary" VERSION="" # Will use binary fallback fi # Dependency selection local DEPS=(build-essential yasm nasm pkg-config) case "$TYPE" in minimal) DEPS+=(libx264-dev libvpx-dev libmp3lame-dev) ;; medium) DEPS+=(libx264-dev libvpx-dev libmp3lame-dev libfreetype6-dev libass-dev libopus-dev libvorbis-dev) ;; full) DEPS+=( libx264-dev libx265-dev libvpx-dev libmp3lame-dev libfreetype6-dev libass-dev libopus-dev libvorbis-dev libdav1d-dev zlib1g-dev libnuma-dev libva-dev libdrm-dev ) if apt-cache show libsvtav1enc-dev &>/dev/null; then DEPS+=(libsvtav1enc-dev) elif apt-cache show libsvtav1-dev &>/dev/null; then DEPS+=(libsvtav1-dev) fi ;; *) msg_error "Invalid FFMPEG_TYPE: $TYPE" rm -rf "$TMP_DIR" return 65 ;; esac ensure_dependencies "${DEPS[@]}" # Try to download source if VERSION is set if [[ -n "$VERSION" ]]; then if ! CURL_TIMEOUT=300 curl_with_retry "https://github.com/${GITHUB_REPO}/archive/refs/tags/${VERSION}.tar.gz" "$TMP_DIR/ffmpeg.tar.gz"; then msg_warn "Failed to download FFmpeg source ${VERSION}, falling back to pre-built binary" VERSION="" fi fi # If no source download (either VERSION empty or download failed), use binary if [[ -z "$VERSION" ]]; then msg_info "Setup FFmpeg from pre-built binary" if ! CURL_TIMEOUT=300 curl_with_retry "https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-amd64-static.tar.xz" "$TMP_DIR/ffmpeg.tar.xz"; then msg_error "Failed to download FFmpeg pre-built binary" rm -rf "$TMP_DIR" return 250 fi tar -xJf "$TMP_DIR/ffmpeg.tar.xz" -C "$TMP_DIR" || { msg_error "Failed to extract FFmpeg binary archive" rm -rf "$TMP_DIR" return 251 } if ! cp "$TMP_DIR/ffmpeg-"*/ffmpeg /usr/local/bin/ffmpeg 2>/dev/null; then msg_error "Failed to install FFmpeg binary" rm -rf "$TMP_DIR" return 150 fi cache_installed_version "ffmpeg" "static" rm -rf "$TMP_DIR" msg_ok "Setup FFmpeg from pre-built binary" return 0 fi tar -xzf "$TMP_DIR/ffmpeg.tar.gz" -C "$TMP_DIR" || { msg_error "Failed to extract FFmpeg source" rm -rf "$TMP_DIR" return 251 } cd "$TMP_DIR/FFmpeg-"* || { msg_error "Source extraction failed" rm -rf "$TMP_DIR" return 251 } local args=( --enable-gpl --enable-shared --enable-nonfree --disable-static --enable-libx264 --enable-libvpx --enable-libmp3lame ) if [[ "$TYPE" != "minimal" ]]; then args+=(--enable-libfreetype --enable-libass --enable-libopus --enable-libvorbis) fi if [[ "$TYPE" == "full" ]]; then args+=(--enable-libx265 --enable-libdav1d --enable-zlib) args+=(--enable-vaapi --enable-libdrm) fi if [[ ${#args[@]} -eq 0 ]]; then msg_error "FFmpeg configure args array is empty" rm -rf "$TMP_DIR" return 65 fi $STD ./configure "${args[@]}" || { msg_error "FFmpeg configure failed" rm -rf "$TMP_DIR" return 150 } $STD make -j"$(nproc)" || { msg_error "FFmpeg compilation failed" rm -rf "$TMP_DIR" return 150 } $STD make install || { msg_error "FFmpeg installation failed" rm -rf "$TMP_DIR" return 150 } echo "/usr/local/lib" >/etc/ld.so.conf.d/ffmpeg.conf $STD ldconfig ldconfig -p 2>/dev/null | grep libavdevice >/dev/null || { msg_error "libavdevice not registered with dynamic linker" rm -rf "$TMP_DIR" return 150 } if ! command -v ffmpeg &>/dev/null; then msg_error "FFmpeg installation failed" rm -rf "$TMP_DIR" return 150 fi local FINAL_VERSION FINAL_VERSION=$(ffmpeg -version 2>/dev/null | head -n1 | awk '{print $3}') rm -rf "$TMP_DIR" cache_installed_version "ffmpeg" "$FINAL_VERSION" ensure_usr_local_bin_persist [[ -n "$INSTALLED_VERSION" ]] && msg_ok "Upgrade FFmpeg $INSTALLED_VERSION → $FINAL_VERSION" || msg_ok "Setup FFmpeg $FINAL_VERSION" } # ------------------------------------------------------------------------------ # Installs Go (Golang) from official tarball. # # Description: # - Determines system architecture # - Downloads latest version if GO_VERSION not set # # Variables: # GO_VERSION - Version to install (e.g. 1.22.2 or latest) # ------------------------------------------------------------------------------ function setup_go() { local ARCH case "$(uname -m)" in x86_64) ARCH="amd64" ;; aarch64) ARCH="arm64" ;; *) msg_error "Unsupported architecture: $(uname -m)" return 236 ;; esac # Resolve "latest" version local GO_VERSION="${GO_VERSION:-latest}" if [[ "$GO_VERSION" == "latest" ]]; then local go_version_tmp go_version_tmp=$(curl_with_retry "https://go.dev/VERSION?m=text" "-" 2>/dev/null | head -n1 | sed 's/^go//') || true if [[ -z "$go_version_tmp" ]]; then msg_error "Could not determine latest Go version" return 250 fi GO_VERSION="$go_version_tmp" fi local GO_BIN="/usr/local/bin/go" local GO_INSTALL_DIR="/usr/local/go" # Get currently installed version local CURRENT_VERSION="" if [[ -x "$GO_BIN" ]]; then CURRENT_VERSION=$("$GO_BIN" version 2>/dev/null | awk '{print $3}' | sed 's/go//') fi # Scenario 1: Already at target version if [[ -n "$CURRENT_VERSION" && "$CURRENT_VERSION" == "$GO_VERSION" ]]; then cache_installed_version "go" "$GO_VERSION" return 0 fi # Scenario 2: Different version or not installed if [[ -n "$CURRENT_VERSION" && "$CURRENT_VERSION" != "$GO_VERSION" ]]; then msg_info "Upgrade Go from $CURRENT_VERSION to $GO_VERSION" remove_old_tool_version "go" else msg_info "Setup Go $GO_VERSION" fi local TARBALL="go${GO_VERSION}.linux-${ARCH}.tar.gz" local URL="https://go.dev/dl/${TARBALL}" local TMP_TAR=$(mktemp) if ! CURL_TIMEOUT=300 curl_with_retry "$URL" "$TMP_TAR"; then msg_error "Failed to download Go $GO_VERSION" rm -f "$TMP_TAR" return 250 fi $STD tar -C /usr/local -xzf "$TMP_TAR" || { msg_error "Failed to extract Go tarball" rm -f "$TMP_TAR" return 251 } ln -sf /usr/local/go/bin/go /usr/local/bin/go ln -sf /usr/local/go/bin/gofmt /usr/local/bin/gofmt rm -f "$TMP_TAR" cache_installed_version "go" "$GO_VERSION" ensure_usr_local_bin_persist msg_ok "Setup Go $GO_VERSION" } # ------------------------------------------------------------------------------ # Installs or updates Ghostscript (gs) from source. # # Description: # - Fetches latest release # - Builds and installs system-wide # ------------------------------------------------------------------------------ function setup_gs() { local TMP_DIR=$(mktemp -d) local CURRENT_VERSION=$(gs --version 2>/dev/null || echo "0") ensure_dependencies jq local RELEASE_JSON RELEASE_JSON=$(curl -fsSL --max-time 15 https://api.github.com/repos/ArtifexSoftware/ghostpdl-downloads/releases/latest 2>/dev/null || echo "") if [[ -z "$RELEASE_JSON" ]]; then msg_warn "Cannot fetch latest Ghostscript version from GitHub API" # Try to get from current version if command -v gs &>/dev/null; then gs --version | head -n1 cache_installed_version "ghostscript" "$CURRENT_VERSION" return 0 fi msg_error "Cannot determine Ghostscript version and no existing installation found" return 250 fi local LATEST_VERSION LATEST_VERSION=$(echo "$RELEASE_JSON" | jq -r '.tag_name' | sed 's/^gs//') local LATEST_VERSION_DOTTED LATEST_VERSION_DOTTED=$(echo "$RELEASE_JSON" | jq -r '.name' | grep -o '[0-9]\+\.[0-9]\+\.[0-9]\+') if [[ -z "$LATEST_VERSION" || -z "$LATEST_VERSION_DOTTED" ]]; then msg_warn "Could not determine latest Ghostscript version from GitHub - checking system" # Fallback: try to use system version or return error if [[ "$CURRENT_VERSION" == "0" ]]; then msg_error "Ghostscript not installed and cannot determine latest version" rm -rf "$TMP_DIR" return 250 fi rm -rf "$TMP_DIR" return 0 fi # Scenario 1: Already at latest version if [[ -n "$LATEST_VERSION_DOTTED" ]] && dpkg --compare-versions "$CURRENT_VERSION" ge "$LATEST_VERSION_DOTTED" 2>/dev/null; then cache_installed_version "ghostscript" "$LATEST_VERSION_DOTTED" rm -rf "$TMP_DIR" return 0 fi # Scenario 2: New install or upgrade if [[ "$CURRENT_VERSION" != "0" && "$CURRENT_VERSION" != "$LATEST_VERSION_DOTTED" ]]; then msg_info "Upgrade Ghostscript from $CURRENT_VERSION to $LATEST_VERSION_DOTTED" else msg_info "Setup Ghostscript $LATEST_VERSION_DOTTED" fi if ! CURL_TIMEOUT=180 curl_with_retry "https://github.com/ArtifexSoftware/ghostpdl-downloads/releases/download/gs${LATEST_VERSION}/ghostscript-${LATEST_VERSION_DOTTED}.tar.gz" "$TMP_DIR/ghostscript.tar.gz"; then msg_error "Failed to download Ghostscript" rm -rf "$TMP_DIR" return 250 fi if ! tar -xzf "$TMP_DIR/ghostscript.tar.gz" -C "$TMP_DIR"; then msg_error "Failed to extract Ghostscript archive" rm -rf "$TMP_DIR" return 251 fi # Verify directory exists before cd if [[ ! -d "$TMP_DIR/ghostscript-${LATEST_VERSION_DOTTED}" ]]; then msg_error "Ghostscript source directory not found: $TMP_DIR/ghostscript-${LATEST_VERSION_DOTTED}" rm -rf "$TMP_DIR" return 252 fi cd "$TMP_DIR/ghostscript-${LATEST_VERSION_DOTTED}" || { msg_error "Failed to enter Ghostscript source directory" rm -rf "$TMP_DIR" return 252 } ensure_dependencies build-essential libpng-dev zlib1g-dev $STD ./configure || { msg_error "Ghostscript configure failed" rm -rf "$TMP_DIR" return 150 } $STD make -j"$(nproc)" || { msg_error "Ghostscript compilation failed" rm -rf "$TMP_DIR" return 150 } $STD make install || { msg_error "Ghostscript installation failed" rm -rf "$TMP_DIR" return 150 } hash -r if [[ ! -x "$(command -v gs)" ]]; then if [[ -x /usr/local/bin/gs ]]; then ln -sf /usr/local/bin/gs /usr/bin/gs fi fi rm -rf "$TMP_DIR" cache_installed_version "ghostscript" "$LATEST_VERSION_DOTTED" ensure_usr_local_bin_persist msg_ok "Setup Ghostscript $LATEST_VERSION_DOTTED" } # ------------------------------------------------------------------------------ # Sets up Hardware Acceleration on debian or ubuntu. # # Description: # - Detects all available GPUs (Intel, AMD, NVIDIA) # - Allows user to select which GPU(s) to configure (with 60s timeout) # - Installs the correct libraries and packages for each GPU type # - Supports: Debian 11/12/13, Ubuntu 22.04/24.04 # - Intel: Legacy (Gen 6-8), Modern (Gen 9+), Arc # - AMD: Discrete GPUs, APUs, ROCm compute # - NVIDIA: Version-matched drivers from CUDA repository # # Notes: # - Some Intel packages are fetched from GitHub due to missing Debian packages # - NVIDIA requires matching host driver version # ------------------------------------------------------------------------------ function setup_hwaccel() { local service_user="${1:-}" # Check if user explicitly disabled GPU in advanced settings # ENABLE_GPU is exported from build.func if [[ "${ENABLE_GPU:-no}" == "no" ]]; then return 0 fi # Check if GPU passthrough is enabled (device nodes must exist) if [[ ! -d /dev/dri && ! -e /dev/nvidia0 && ! -e /dev/kfd ]]; then msg_warn "No GPU passthrough detected (/dev/dri, /dev/nvidia*, /dev/kfd not found) - skipping hardware acceleration setup" return 0 fi msg_info "Setup Hardware Acceleration" # Install pciutils if needed if ! command -v lspci &>/dev/null; then $STD apt -y update || { msg_warn "Failed to update package list" return 0 } $STD apt -y install pciutils || { msg_warn "Failed to install pciutils" return 0 } fi # ═══════════════════════════════════════════════════════════════════════════ # GPU Detection - Build list of all available GPUs with details # ═══════════════════════════════════════════════════════════════════════════ local -a GPU_LIST=() local -a GPU_TYPES=() local -a GPU_NAMES=() local gpu_count=0 # Get all GPU entries from lspci while IFS= read -r line; do [[ -z "$line" ]] && continue local pci_addr gpu_name gpu_type="" pci_addr=$(echo "$line" | awk '{print $1}') gpu_name=$(echo "$line" | sed 's/^[^ ]* [^:]*: //') # Determine GPU type # Note: Use -w (word boundary) for ATI to avoid matching "CorporATIon" if echo "$gpu_name" | grep -qi 'Intel'; then gpu_type="INTEL" # Subtype detection for Intel # Order matters: Check Arc first, then Gen9+ (UHD/Iris/HD 5xx-6xx), then Legacy (HD 2xxx-5xxx) # HD Graphics 530/630 = Gen 9 (Skylake/Kaby Lake) - 3 digits # HD Graphics 4600/5500 = Gen 7-8 (Haswell/Broadwell) - 4 digits starting with 2-5 if echo "$gpu_name" | grep -qiE 'Arc|DG[12]'; then gpu_type="INTEL_ARC" elif echo "$gpu_name" | grep -qiE 'UHD|Iris|HD Graphics [5-6][0-9]{2}[^0-9]|HD Graphics [5-6][0-9]{2}$'; then # HD Graphics 5xx/6xx (3 digits) = Gen 9+ (Skylake onwards) gpu_type="INTEL_GEN9+" elif echo "$gpu_name" | grep -qiE 'HD Graphics [2-5][0-9]{3}'; then # HD Graphics 2xxx-5xxx (4 digits) = Gen 6-8 Legacy gpu_type="INTEL_LEGACY" fi elif echo "$gpu_name" | grep -qiwE 'AMD|ATI|Radeon|Advanced Micro Devices'; then gpu_type="AMD" elif echo "$gpu_name" | grep -qi 'NVIDIA'; then gpu_type="NVIDIA" fi if [[ -n "$gpu_type" ]]; then GPU_LIST+=("$pci_addr") GPU_TYPES+=("$gpu_type") GPU_NAMES+=("$gpu_name") ((gpu_count++)) || true fi done < <(lspci 2>/dev/null | grep -Ei 'vga|3d|display') # Check for AMD APU via CPU vendor if no discrete GPU found local cpu_vendor cpu_vendor=$(lscpu 2>/dev/null | grep -i 'Vendor ID' | awk '{print $3}' 2>/dev/null || echo "") if [[ $gpu_count -eq 0 ]]; then if [[ "$cpu_vendor" == "AuthenticAMD" ]]; then GPU_LIST+=("integrated") GPU_TYPES+=("AMD_APU") GPU_NAMES+=("AMD APU (Integrated Graphics)") ((gpu_count++)) || true else msg_warn "No GPU detected - skipping hardware acceleration setup" return 0 fi fi # ═══════════════════════════════════════════════════════════════════════════ # GPU Selection - Let user choose which GPU(s) to configure # ═══════════════════════════════════════════════════════════════════════════ local -a SELECTED_INDICES=() local install_nvidia_drivers="yes" if [[ $gpu_count -eq 1 ]]; then # Single GPU - auto-select SELECTED_INDICES=(0) msg_ok "Detected GPU: ${GPU_NAMES[0]} (${GPU_TYPES[0]})" else # Multiple GPUs - show selection menu echo "" msg_custom "⚠" "${YW}" "Multiple GPUs detected:" echo "" for i in "${!GPU_LIST[@]}"; do local type_display="${GPU_TYPES[$i]}" case "${GPU_TYPES[$i]}" in INTEL_ARC) type_display="Intel Arc" ;; INTEL_GEN9+) type_display="Intel Gen9+" ;; INTEL_LEGACY) type_display="Intel Legacy" ;; INTEL) type_display="Intel" ;; AMD) type_display="AMD" ;; AMD_APU) type_display="AMD APU" ;; NVIDIA) type_display="NVIDIA" ;; esac printf " %d) [%s] %s\n" "$((i + 1))" "$type_display" "${GPU_NAMES[$i]}" done printf " A) Configure ALL GPUs\n" echo "" # Read with 60 second timeout local selection="" echo -n "Select GPU(s) to configure (1-${gpu_count}, A=all) [timeout 60s, default=all]: " if read -r -t 60 selection; then selection="${selection^^}" # uppercase else echo "" msg_info "Timeout - configuring all GPUs automatically" selection="A" fi # Parse selection if [[ "$selection" == "A" || -z "$selection" ]]; then # Select all for i in "${!GPU_LIST[@]}"; do SELECTED_INDICES+=("$i") done elif [[ "$selection" =~ ^[0-9,]+$ ]]; then # Parse comma-separated numbers IFS=',' read -ra nums <<<"$selection" for num in "${nums[@]}"; do num=$(echo "$num" | tr -d ' ') if [[ "$num" =~ ^[0-9]+$ ]] && ((num >= 1 && num <= gpu_count)); then SELECTED_INDICES+=("$((num - 1))") fi done else # Invalid - default to all msg_warn "Invalid selection - configuring all GPUs" for i in "${!GPU_LIST[@]}"; do SELECTED_INDICES+=("$i") done fi fi # Ask whether to install NVIDIA drivers in the container local nvidia_selected="no" for idx in "${SELECTED_INDICES[@]}"; do if [[ "${GPU_TYPES[$idx]}" == "NVIDIA" ]]; then nvidia_selected="yes" break fi done if [[ "$nvidia_selected" == "yes" ]]; then if [[ -n "${INSTALL_NVIDIA_DRIVERS:-}" ]]; then install_nvidia_drivers="${INSTALL_NVIDIA_DRIVERS}" else echo "" msg_custom "🎮" "${GN}" "NVIDIA GPU passthrough detected" local nvidia_reply="" read -r -t 60 -p "${TAB3}⚙️ Install NVIDIA driver libraries in the container? [Y/n] (auto-yes in 60s): " nvidia_reply || nvidia_reply="" case "${nvidia_reply,,}" in n | no) install_nvidia_drivers="no" ;; *) install_nvidia_drivers="yes" ;; esac fi fi # ═══════════════════════════════════════════════════════════════════════════ # OS Detection # ═══════════════════════════════════════════════════════════════════════════ local os_id os_codename os_version os_id=$(grep -oP '(?<=^ID=).+' /etc/os-release 2>/dev/null | tr -d '"' || echo "debian") os_codename=$(grep -oP '(?<=^VERSION_CODENAME=).+' /etc/os-release 2>/dev/null | tr -d '"' || echo "unknown") os_version=$(grep -oP '(?<=^VERSION_ID=).+' /etc/os-release 2>/dev/null | tr -d '"' || echo "") [[ -z "$os_id" ]] && os_id="debian" local in_ct="${CTTYPE:-0}" # ═══════════════════════════════════════════════════════════════════════════ # Process Selected GPUs # ═══════════════════════════════════════════════════════════════════════════ for idx in "${SELECTED_INDICES[@]}"; do local gpu_type="${GPU_TYPES[$idx]}" local gpu_name="${GPU_NAMES[$idx]}" msg_info "Configuring: ${gpu_name}" case "$gpu_type" in # ───────────────────────────────────────────────────────────────────────── # Intel Arc GPUs (DG1, DG2, Arc A-series) # ───────────────────────────────────────────────────────────────────────── INTEL_ARC) _setup_intel_arc "$os_id" "$os_codename" ;; # ───────────────────────────────────────────────────────────────────────── # Intel Gen 9+ (Skylake 2015+: UHD, Iris, HD 6xx+) # ───────────────────────────────────────────────────────────────────────── INTEL_GEN9+ | INTEL) _setup_intel_modern "$os_id" "$os_codename" ;; # ───────────────────────────────────────────────────────────────────────── # Intel Legacy (Gen 6-8: HD 2000-5999, Sandy Bridge to Broadwell) # ───────────────────────────────────────────────────────────────────────── INTEL_LEGACY) _setup_intel_legacy "$os_id" "$os_codename" ;; # ───────────────────────────────────────────────────────────────────────── # AMD Discrete GPUs # ───────────────────────────────────────────────────────────────────────── AMD) _setup_amd_gpu "$os_id" "$os_codename" ;; # ───────────────────────────────────────────────────────────────────────── # AMD APU (Integrated Graphics) # ───────────────────────────────────────────────────────────────────────── AMD_APU) _setup_amd_apu "$os_id" "$os_codename" ;; # ───────────────────────────────────────────────────────────────────────── # NVIDIA GPUs # ───────────────────────────────────────────────────────────────────────── NVIDIA) if [[ "$install_nvidia_drivers" == "yes" ]]; then _setup_nvidia_gpu "$os_id" "$os_codename" "$os_version" else msg_warn "Skipping NVIDIA driver installation (user opted to install manually)" fi ;; esac done # ═══════════════════════════════════════════════════════════════════════════ # Device Permissions # ═══════════════════════════════════════════════════════════════════════════ _setup_gpu_permissions "$in_ct" "$service_user" cache_installed_version "hwaccel" "1.0" msg_ok "Setup Hardware Acceleration" } # ══════════════════════════════════════════════════════════════════════════════ # Resolve the IGC tag that the latest compute-runtime was built against. # Must be called AFTER a fetch_and_deploy_gh_release for intel/compute-runtime # so that /tmp/gh_rel.json contains the compute-runtime release metadata. # Sets the variable named by $1 (default: igc_tag) to the discovered tag. # ══════════════════════════════════════════════════════════════════════════════ _resolve_igc_tag() { local -n _out_ref="${1:-igc_tag}" _out_ref="latest" if [[ -f /tmp/gh_rel.json ]]; then local _body _parsed _body=$(jq -r '.body // empty' /tmp/gh_rel.json 2>/dev/null) || return 0 _parsed=$(grep -oP 'intel-graphics-compiler/releases/tag/\K[^\s\)]+' <<<"$_body" | head -1) [[ -n "$_parsed" ]] && _out_ref="$_parsed" fi } # ══════════════════════════════════════════════════════════════════════════════ # Intel Arc GPU Setup # ══════════════════════════════════════════════════════════════════════════════ _setup_intel_arc() { local os_id="$1" os_codename="$2" msg_info "Installing Intel Arc GPU drivers" if [[ "$os_id" == "ubuntu" ]]; then # Ubuntu 22.04+ has Arc support in HWE kernel $STD apt -y install \ intel-media-va-driver-non-free \ intel-opencl-icd \ libmfx-gen1.2 \ vainfo \ intel-gpu-tools 2>/dev/null || msg_warn "Some Intel Arc packages failed" elif [[ "$os_id" == "debian" ]]; then # Add non-free repos _add_debian_nonfree "$os_codename" # For Trixie/Sid: Fetch latest drivers from GitHub (Debian repo packages may be too old or missing) # For Bookworm: Use repo packages (GitHub latest requires libstdc++6 >= 13.1, unavailable on Bookworm) if [[ "$os_codename" == "trixie" || "$os_codename" == "sid" ]]; then msg_info "Fetching Intel compute-runtime from GitHub for Arc support" # Fetch a compute-runtime package first so /tmp/gh_rel.json is populated, # then resolve the matching IGC tag from the release notes. # libigdgmm - bundled in compute-runtime releases fetch_and_deploy_gh_release "libigdgmm12" "intel/compute-runtime" "binary" "latest" "" "libigdgmm12_*_amd64.deb" || true local igc_tag _resolve_igc_tag igc_tag # Intel Graphics Compiler – pinned to the version compute-runtime expects fetch_and_deploy_gh_release "intel-igc-core" "intel/intel-graphics-compiler" "binary" "$igc_tag" "" "intel-igc-core-2_*_amd64.deb" || true fetch_and_deploy_gh_release "intel-igc-opencl" "intel/intel-graphics-compiler" "binary" "$igc_tag" "" "intel-igc-opencl-2_*_amd64.deb" || true # Compute Runtime (depends on IGC and gmmlib) fetch_and_deploy_gh_release "intel-opencl-icd" "intel/compute-runtime" "binary" "latest" "" "intel-opencl-icd_*_amd64.deb" || true fetch_and_deploy_gh_release "intel-level-zero-gpu" "intel/compute-runtime" "binary" "latest" "" "libze-intel-gpu1_*_amd64.deb" || true fi $STD apt -y install \ intel-media-va-driver-non-free \ ocl-icd-libopencl1 \ libvpl2 \ libmfx-gen1.2 \ vainfo \ intel-gpu-tools 2>/dev/null || msg_warn "Some Intel Arc packages failed" # Bookworm has compatible versions of these packages in repos [[ "$os_codename" == "bookworm" ]] && $STD apt -y install intel-opencl-icd libigdgmm12 2>/dev/null || true fi msg_ok "Intel Arc GPU configured" } # ══════════════════════════════════════════════════════════════════════════════ # Intel Modern GPU Setup (Gen 9+) # ══════════════════════════════════════════════════════════════════════════════ _setup_intel_modern() { local os_id="$1" os_codename="$2" msg_info "Installing Intel Gen 9+ GPU drivers" if [[ "$os_id" == "ubuntu" ]]; then $STD apt -y install \ va-driver-all \ intel-media-va-driver \ ocl-icd-libopencl1 \ vainfo \ intel-gpu-tools 2>/dev/null || msg_warn "Some Intel packages failed" # Try non-free driver for better codec support $STD apt -y install intel-media-va-driver-non-free 2>/dev/null || true $STD apt -y install intel-opencl-icd 2>/dev/null || true $STD apt -y install libmfx-gen1.2 2>/dev/null || true elif [[ "$os_id" == "debian" ]]; then _add_debian_nonfree "$os_codename" # For Trixie/Sid: Fetch from GitHub (Debian packages too old or missing) if [[ "$os_codename" == "trixie" || "$os_codename" == "sid" ]]; then msg_info "Fetching Intel compute-runtime from GitHub" # Fetch a compute-runtime package first so /tmp/gh_rel.json is populated, # then resolve the matching IGC tag from the release notes. # libigdgmm first (bundled in compute-runtime releases) fetch_and_deploy_gh_release "libigdgmm12" "intel/compute-runtime" "binary" "latest" "" "libigdgmm12_*_amd64.deb" || true local igc_tag _resolve_igc_tag igc_tag # Intel Graphics Compiler – pinned to the version compute-runtime expects fetch_and_deploy_gh_release "intel-igc-core" "intel/intel-graphics-compiler" "binary" "$igc_tag" "" "intel-igc-core-2_*_amd64.deb" || true fetch_and_deploy_gh_release "intel-igc-opencl" "intel/intel-graphics-compiler" "binary" "$igc_tag" "" "intel-igc-opencl-2_*_amd64.deb" || true # Compute Runtime fetch_and_deploy_gh_release "intel-opencl-icd" "intel/compute-runtime" "binary" "latest" "" "intel-opencl-icd_*_amd64.deb" || true fi $STD apt -y install \ intel-media-va-driver-non-free \ ocl-icd-libopencl1 \ vainfo \ libmfx-gen1.2 \ intel-gpu-tools 2>/dev/null || msg_warn "Some Intel packages failed" # Bookworm has intel-opencl-icd in repos (compatible version) [[ "$os_codename" == "bookworm" ]] && $STD apt -y install intel-opencl-icd libigdgmm12 2>/dev/null || true fi msg_ok "Intel Gen 9+ GPU configured" } # ══════════════════════════════════════════════════════════════════════════════ # Intel Legacy GPU Setup (Gen 6-8) # ══════════════════════════════════════════════════════════════════════════════ _setup_intel_legacy() { local os_id="$1" os_codename="$2" msg_info "Installing Intel Legacy GPU drivers (Gen 6-8)" # Legacy GPUs use i965 driver - stable repo packages only $STD apt -y install \ va-driver-all \ i965-va-driver \ mesa-va-drivers \ ocl-icd-libopencl1 \ vainfo \ intel-gpu-tools 2>/dev/null || msg_warn "Some Intel legacy packages failed" # beignet provides OpenCL for older Intel GPUs (Sandy Bridge to Broadwell) # Note: beignet-opencl-icd was removed in Debian 12+ and Ubuntu 22.04+ # Check if package is available before attempting installation if apt-cache show beignet-opencl-icd &>/dev/null; then $STD apt -y install beignet-opencl-icd 2>/dev/null || msg_warn "beignet-opencl-icd installation failed (optional)" else msg_warn "beignet-opencl-icd not available - OpenCL support for legacy Intel GPU limited" msg_warn "Note: Hardware video encoding/decoding (VA-API) still works without OpenCL" fi msg_ok "Intel Legacy GPU configured" } # ══════════════════════════════════════════════════════════════════════════════ # AMD Discrete GPU Setup # ══════════════════════════════════════════════════════════════════════════════ _setup_amd_gpu() { local os_id="$1" os_codename="$2" msg_info "Installing AMD GPU drivers" # Core Mesa drivers $STD apt -y install \ mesa-va-drivers \ mesa-vdpau-drivers \ mesa-opencl-icd \ ocl-icd-libopencl1 \ libdrm-amdgpu1 \ vainfo \ clinfo 2>/dev/null || msg_warn "Some AMD packages failed" # Firmware for AMD GPUs if [[ "$os_id" == "debian" ]]; then _add_debian_nonfree_firmware "$os_codename" $STD apt -y install firmware-amd-graphics 2>/dev/null || msg_warn "AMD firmware not available" fi # Ubuntu includes AMD firmware in linux-firmware by default # ROCm compute stack (OpenCL + HIP) _setup_rocm "$os_id" "$os_codename" msg_ok "AMD GPU configured" } # ══════════════════════════════════════════════════════════════════════════════ # AMD APU Setup (Integrated Graphics) # ══════════════════════════════════════════════════════════════════════════════ _setup_amd_apu() { local os_id="$1" os_codename="$2" msg_info "Installing AMD APU drivers" $STD apt -y install \ mesa-va-drivers \ mesa-vdpau-drivers \ mesa-opencl-icd \ ocl-icd-libopencl1 \ vainfo 2>/dev/null || msg_warn "Some AMD APU packages failed" if [[ "$os_id" == "debian" ]]; then _add_debian_nonfree_firmware "$os_codename" $STD apt -y install firmware-amd-graphics 2>/dev/null || true fi msg_ok "AMD APU configured" } # ══════════════════════════════════════════════════════════════════════════════ # AMD ROCm Compute Setup # Adds ROCm repository and installs the ROCm compute stack for AMD GPUs/APUs. # Provides: OpenCL, HIP, rocm-smi, rocminfo # Supported: Debian 12/13, Ubuntu 22.04/24.04 (amd64 only) # ══════════════════════════════════════════════════════════════════════════════ _setup_rocm() { local os_id="$1" os_codename="$2" # Only amd64 is supported if [[ "$(dpkg --print-architecture 2>/dev/null)" != "amd64" ]]; then msg_warn "ROCm is only available for amd64 — skipping" return 0 fi local ROCM_VERSION="7.2" local ROCM_REPO_CODENAME # Map OS codename to ROCm repository codename (Ubuntu-based repos) case "${os_id}-${os_codename}" in debian-bookworm) ROCM_REPO_CODENAME="jammy" ;; debian-trixie | debian-sid) ROCM_REPO_CODENAME="noble" ;; ubuntu-jammy) ROCM_REPO_CODENAME="jammy" ;; ubuntu-noble) ROCM_REPO_CODENAME="noble" ;; *) msg_warn "ROCm not supported on ${os_id} ${os_codename} — skipping" return 0 ;; esac msg_info "Installing ROCm ${ROCM_VERSION} compute stack" # ROCm main repository (userspace compute libs) setup_deb822_repo \ "rocm" \ "https://repo.radeon.com/rocm/rocm.gpg.key" \ "https://repo.radeon.com/rocm/apt/${ROCM_VERSION}" \ "${ROCM_REPO_CODENAME}" \ "main" \ "amd64" || { msg_warn "Failed to add ROCm repository — skipping ROCm" return 0 } # Note: The amdgpu/latest/ubuntu repo (kernel driver packages) is intentionally # omitted — kernel drivers are managed by the Proxmox host, not the LXC container. # Only the ROCm userspace compute stack is needed inside the container. # Pin ROCm packages to prefer radeon repo cat </etc/apt/preferences.d/rocm-pin-600 Package: * Pin: release o=repo.radeon.com Pin-Priority: 600 EOF # apt update with retry — repo.radeon.com CDN can be mid-sync (transient size mismatches). # Run with ERR trap disabled so a transient failure does not abort the entire install. local _apt_ok=0 for _attempt in 1 2 3; do if ( set +e apt-get update -qq 2>&1 exit $? ) 2>/dev/null; then _apt_ok=1 break fi msg_warn "apt update failed (attempt ${_attempt}/3) — AMD repo may be temporarily unavailable, retrying in 30s…" sleep 30 done if [[ $_apt_ok -eq 0 ]]; then msg_warn "apt update still failing after 3 attempts — skipping ROCm install" return 0 fi # Install only runtime packages — full 'rocm' meta-package includes 15GB+ dev tools $STD apt install -y rocm-opencl-runtime rocm-hip-runtime rocm-smi-lib 2>/dev/null || { msg_warn "ROCm runtime install failed — trying minimal set" $STD apt install -y rocm-opencl-runtime rocm-smi-lib 2>/dev/null || msg_warn "ROCm minimal install also failed" } # Group membership for GPU access usermod -aG render,video root 2>/dev/null || true # Environment (PATH + LD_LIBRARY_PATH) if [[ -d /opt/rocm ]]; then cat <<'ENVEOF' >/etc/profile.d/rocm.sh export PATH="$PATH:/opt/rocm/bin" export LD_LIBRARY_PATH="${LD_LIBRARY_PATH:+$LD_LIBRARY_PATH:}/opt/rocm/lib" ENVEOF chmod +x /etc/profile.d/rocm.sh # Also make available for current session / systemd services echo "/opt/rocm/lib" >/etc/ld.so.conf.d/rocm.conf ldconfig 2>/dev/null || true fi if [[ -x /opt/rocm/bin/rocminfo ]]; then msg_ok "ROCm ${ROCM_VERSION} installed" else msg_warn "ROCm installed but rocminfo not found — GPU may not be available in container" fi } # ══════════════════════════════════════════════════════════════════════════════ # NVIDIA GPU Setup # ══════════════════════════════════════════════════════════════════════════════ _setup_nvidia_gpu() { local os_id="$1" os_codename="$2" os_version="$3" msg_info "Installing NVIDIA GPU drivers" # Prevent interactive dialogs (e.g., "Mismatching nvidia kernel module" whiptail) export DEBIAN_FRONTEND=noninteractive export NEEDRESTART_MODE=a # Detect host driver version (passed through via /proc) # Format varies by driver type: # Proprietary: "NVRM version: NVIDIA UNIX x86_64 Kernel Module 550.54.14 Thu..." # Open: "NVRM version: NVIDIA UNIX Open Kernel Module for x86_64 590.48.01 Release..." # Use regex to extract version number (###.##.## or ###.## pattern) local nvidia_host_version="" if [[ -f /proc/driver/nvidia/version ]]; then nvidia_host_version=$(grep -oP '\d{3,}\.\d+(\.\d+)?' /proc/driver/nvidia/version 2>/dev/null | head -1 || true) fi if [[ -z "$nvidia_host_version" ]]; then msg_warn "NVIDIA host driver version not found in /proc/driver/nvidia/version" msg_warn "Ensure NVIDIA drivers are installed on host and GPU passthrough is enabled" $STD apt-get -y install va-driver-all vainfo 2>/dev/null || true return 0 fi msg_info "Host NVIDIA driver version: ${nvidia_host_version}" if [[ "$os_id" == "debian" ]]; then # Enable non-free components if [[ -f /etc/apt/sources.list.d/debian.sources ]]; then if ! grep -q "non-free" /etc/apt/sources.list.d/debian.sources 2>/dev/null; then sed -i -E 's/Components: (.*)$/Components: \1 contrib non-free non-free-firmware/g' /etc/apt/sources.list.d/debian.sources 2>/dev/null || true fi fi $STD apt-get -y update 2>/dev/null || msg_warn "apt update failed - continuing anyway" # For Debian 13 Trixie/Sid: Use Debian's own nvidia packages first (better compatibility) # NVIDIA's CUDA repo targets Debian 12 and may not have amd64 packages for Trixie if [[ "$os_codename" == "trixie" || "$os_codename" == "sid" ]]; then msg_info "Debian ${os_codename}: Using Debian's NVIDIA packages" # Extract major version for flexible matching (580.126.09 -> 580) local nvidia_major_version="${nvidia_host_version%%.*}" # Check what versions are actually available local available_version="" available_version=$(apt-cache madison libcuda1 2>/dev/null | awk '{print $3}' | grep -E "^${nvidia_major_version}\." | head -1 || true) if [[ -n "$available_version" ]]; then msg_info "Found available NVIDIA version: ${available_version}" local nvidia_pkgs="libcuda1=${available_version} libnvcuvid1=${available_version} libnvidia-encode1=${available_version} libnvidia-ml1=${available_version}" if $STD apt-get -y -o Dpkg::Options::="--force-confold" install --no-install-recommends $nvidia_pkgs 2>/dev/null; then msg_ok "Installed NVIDIA libraries (${available_version})" else msg_warn "Failed to install NVIDIA ${available_version} - trying unversioned" $STD apt-get -y -o Dpkg::Options::="--force-confold" install --no-install-recommends libcuda1 libnvcuvid1 libnvidia-encode1 libnvidia-ml1 2>/dev/null || true fi else # No matching major version - try latest available or unversioned msg_warn "No NVIDIA packages for version ${nvidia_major_version}.x found in repos" available_version=$(apt-cache madison libcuda1 2>/dev/null | awk '{print $3}' | head -1 || true) if [[ -n "$available_version" ]]; then msg_info "Trying latest available: ${available_version} (may cause version mismatch)" $STD apt-get -y -o Dpkg::Options::="--force-confold" install --no-install-recommends \ libcuda1="${available_version}" libnvcuvid1="${available_version}" \ libnvidia-encode1="${available_version}" libnvidia-ml1="${available_version}" 2>/dev/null || $STD apt-get -y -o Dpkg::Options::="--force-confold" install --no-install-recommends \ libcuda1 libnvcuvid1 libnvidia-encode1 libnvidia-ml1 2>/dev/null || msg_warn "NVIDIA library installation failed - GPU compute may not work" else msg_warn "No NVIDIA packages available in Debian repos - GPU support disabled" fi fi $STD apt-get -y -o Dpkg::Options::="--force-confold" install --no-install-recommends nvidia-smi 2>/dev/null || true else # Debian 11/12: Use NVIDIA CUDA repository for version matching local cuda_repo="debian12" case "$os_codename" in bullseye) cuda_repo="debian11" ;; bookworm) cuda_repo="debian12" ;; esac # Add NVIDIA CUDA repository if [[ ! -f /usr/share/keyrings/cuda-archive-keyring.gpg ]]; then msg_info "Adding NVIDIA CUDA repository (${cuda_repo})" local cuda_keyring cuda_keyring="$(mktemp)" if curl -fsSL -o "$cuda_keyring" "https://developer.download.nvidia.com/compute/cuda/repos/${cuda_repo}/x86_64/cuda-keyring_1.1-1_all.deb" 2>/dev/null; then $STD dpkg -i "$cuda_keyring" 2>/dev/null || true else msg_warn "Failed to download NVIDIA CUDA keyring" fi rm -f "$cuda_keyring" fi # Pin NVIDIA repo for version matching cat <<'NVIDIA_PIN' >/etc/apt/preferences.d/nvidia-cuda-pin Package: * Pin: origin developer.download.nvidia.com Pin-Priority: 1001 NVIDIA_PIN $STD apt-get -y update 2>/dev/null || msg_warn "apt update failed - continuing anyway" # Extract major version for flexible matching (580.126.09 -> 580) local nvidia_major_version="${nvidia_host_version%%.*}" # Check what versions are actually available in CUDA repo local available_version="" available_version=$(apt-cache madison libcuda1 2>/dev/null | awk '{print $3}' | grep -E "^${nvidia_major_version}\." | head -1 || true) if [[ -n "$available_version" ]]; then msg_info "Installing NVIDIA libraries (version ${available_version})" local nvidia_pkgs="libcuda1=${available_version} libnvcuvid1=${available_version} libnvidia-encode1=${available_version} libnvidia-ml1=${available_version}" if $STD apt-get -y -o Dpkg::Options::="--force-confold" install --no-install-recommends $nvidia_pkgs 2>/dev/null; then msg_ok "Installed version-matched NVIDIA libraries" else msg_warn "Version-pinned install failed - trying unpinned" $STD apt-get -y -o Dpkg::Options::="--force-confold" install --no-install-recommends libcuda1 libnvcuvid1 libnvidia-encode1 libnvidia-ml1 2>/dev/null || msg_warn "NVIDIA library installation failed" fi else msg_warn "No NVIDIA packages for version ${nvidia_major_version}.x in CUDA repo (host: ${nvidia_host_version})" # Try latest available version available_version=$(apt-cache madison libcuda1 2>/dev/null | awk '{print $3}' | head -1 || true) if [[ -n "$available_version" ]]; then msg_info "Trying latest available: ${available_version} (version mismatch warning)" if $STD apt-get -y -o Dpkg::Options::="--force-confold" install --no-install-recommends \ libcuda1="${available_version}" libnvcuvid1="${available_version}" \ libnvidia-encode1="${available_version}" libnvidia-ml1="${available_version}" 2>/dev/null; then msg_ok "Installed NVIDIA libraries (${available_version}) - version differs from host" else $STD apt-get -y -o Dpkg::Options::="--force-confold" install --no-install-recommends libcuda1 libnvcuvid1 libnvidia-encode1 libnvidia-ml1 2>/dev/null || msg_warn "NVIDIA library installation failed" fi else msg_warn "No NVIDIA packages available in CUDA repo - GPU support disabled" fi fi $STD apt-get -y -o Dpkg::Options::="--force-confold" install --no-install-recommends nvidia-smi 2>/dev/null || true fi elif [[ "$os_id" == "ubuntu" ]]; then # Ubuntu versioning local ubuntu_cuda_repo="" case "$os_version" in 22.04) ubuntu_cuda_repo="ubuntu2204" ;; 24.04) ubuntu_cuda_repo="ubuntu2404" ;; *) ubuntu_cuda_repo="ubuntu2204" ;; # Fallback esac # Add NVIDIA CUDA repository for Ubuntu if [[ ! -f /usr/share/keyrings/cuda-archive-keyring.gpg ]]; then msg_info "Adding NVIDIA CUDA repository (${ubuntu_cuda_repo})" local cuda_keyring cuda_keyring="$(mktemp)" if curl -fsSL -o "$cuda_keyring" "https://developer.download.nvidia.com/compute/cuda/repos/${ubuntu_cuda_repo}/x86_64/cuda-keyring_1.1-1_all.deb" 2>/dev/null; then $STD dpkg -i "$cuda_keyring" 2>/dev/null || true else msg_warn "Failed to download NVIDIA CUDA keyring" fi rm -f "$cuda_keyring" fi $STD apt-get -y update 2>/dev/null || msg_warn "apt update failed - continuing anyway" # Extract major version for flexible matching local nvidia_major_version="${nvidia_host_version%%.*}" # Check what versions are available local available_version="" available_version=$(apt-cache madison libcuda1 2>/dev/null | awk '{print $3}' | grep -E "^${nvidia_major_version}\." | head -1 || true) if [[ -n "$available_version" ]]; then msg_info "Installing NVIDIA libraries (version ${available_version})" local nvidia_pkgs="libcuda1=${available_version} libnvcuvid1=${available_version} libnvidia-encode1=${available_version} libnvidia-ml1=${available_version}" if $STD apt-get -y -o Dpkg::Options::="--force-confold" install --no-install-recommends $nvidia_pkgs 2>/dev/null; then msg_ok "Installed version-matched NVIDIA libraries" else # Fallback to Ubuntu repo packages with versioned nvidia-utils msg_warn "CUDA repo install failed - trying Ubuntu native packages (nvidia-utils-${nvidia_major_version})" if $STD apt-get -y -o Dpkg::Options::="--force-confold" install --no-install-recommends \ libnvidia-decode-${nvidia_major_version} libnvidia-encode-${nvidia_major_version} nvidia-utils-${nvidia_major_version} 2>/dev/null; then msg_ok "Installed Ubuntu NVIDIA packages (${nvidia_major_version})" else msg_warn "NVIDIA driver installation failed - please install manually: apt install nvidia-utils-${nvidia_major_version}" fi fi else msg_warn "No NVIDIA packages for version ${nvidia_major_version}.x in CUDA repo" # Fallback to Ubuntu repo packages with versioned nvidia-utils msg_info "Trying Ubuntu native packages (nvidia-utils-${nvidia_major_version})" if $STD apt-get -y -o Dpkg::Options::="--force-confold" install --no-install-recommends \ libnvidia-decode-${nvidia_major_version} libnvidia-encode-${nvidia_major_version} nvidia-utils-${nvidia_major_version} 2>/dev/null; then msg_ok "Installed Ubuntu NVIDIA packages (${nvidia_major_version})" else msg_warn "NVIDIA driver installation failed - please install manually: apt install nvidia-utils-${nvidia_major_version}" fi fi fi # VA-API for hybrid setups (Intel + NVIDIA) $STD apt-get -y install va-driver-all vainfo 2>/dev/null || true # Fix GLX alternatives: nvidia-alternative diverts mesa libs but in LXC # containers the nvidia GLX libs are typically missing, leaving libGL.so.1 # pointing nowhere. Fall back to mesa if nvidia GLX dir is empty/missing. if command -v update-glx &>/dev/null; then local nvidia_glx_dir="/usr/lib/nvidia" if [[ ! -f "${nvidia_glx_dir}/libGL.so.1" ]] && [[ -d /usr/lib/mesa-diverted ]]; then msg_info "NVIDIA GLX libs missing in container - falling back to mesa" $STD update-glx --set glx /usr/lib/mesa-diverted 2>/dev/null || true ldconfig 2>/dev/null || true fi fi msg_ok "NVIDIA GPU configured" } # ══════════════════════════════════════════════════════════════════════════════ # Helper: Add Debian non-free repositories # ══════════════════════════════════════════════════════════════════════════════ _add_debian_nonfree() { local os_codename="$1" [[ -f /etc/apt/sources.list.d/non-free.sources ]] && return 0 case "$os_codename" in bullseye) cat <<'EOF' >/etc/apt/sources.list.d/non-free.sources Types: deb URIs: http://deb.debian.org/debian Suites: bullseye bullseye-updates Components: non-free Signed-By: /usr/share/keyrings/debian-archive-keyring.gpg EOF ;; bookworm) cat <<'EOF' >/etc/apt/sources.list.d/non-free.sources Types: deb URIs: http://deb.debian.org/debian Suites: bookworm bookworm-updates Components: non-free non-free-firmware Signed-By: /usr/share/keyrings/debian-archive-keyring.gpg EOF ;; trixie | sid) cat <<'EOF' >/etc/apt/sources.list.d/non-free.sources Types: deb URIs: http://deb.debian.org/debian Suites: trixie trixie-updates Components: non-free non-free-firmware Signed-By: /usr/share/keyrings/debian-archive-keyring.gpg Types: deb URIs: http://deb.debian.org/debian-security Suites: trixie-security Components: non-free non-free-firmware Signed-By: /usr/share/keyrings/debian-archive-keyring.gpg EOF ;; esac $STD apt -y update } # ══════════════════════════════════════════════════════════════════════════════ # Helper: Add Debian non-free-firmware repository # ══════════════════════════════════════════════════════════════════════════════ _add_debian_nonfree_firmware() { local os_codename="$1" [[ -f /etc/apt/sources.list.d/non-free-firmware.sources ]] && return 0 case "$os_codename" in bullseye) # Debian 11 uses 'non-free' component (no separate non-free-firmware) cat <<'EOF' >/etc/apt/sources.list.d/non-free-firmware.sources Types: deb URIs: http://deb.debian.org/debian Suites: bullseye bullseye-updates Components: non-free Signed-By: /usr/share/keyrings/debian-archive-keyring.gpg Types: deb URIs: http://deb.debian.org/debian-security Suites: bullseye-security Components: non-free Signed-By: /usr/share/keyrings/debian-archive-keyring.gpg EOF ;; bookworm) cat <<'EOF' >/etc/apt/sources.list.d/non-free-firmware.sources Types: deb URIs: http://deb.debian.org/debian Suites: bookworm bookworm-updates Components: non-free-firmware Signed-By: /usr/share/keyrings/debian-archive-keyring.gpg Types: deb URIs: http://deb.debian.org/debian-security Suites: bookworm-security Components: non-free-firmware Signed-By: /usr/share/keyrings/debian-archive-keyring.gpg EOF ;; trixie | sid) cat <<'EOF' >/etc/apt/sources.list.d/non-free-firmware.sources Types: deb URIs: http://deb.debian.org/debian Suites: trixie trixie-updates Components: non-free-firmware Signed-By: /usr/share/keyrings/debian-archive-keyring.gpg Types: deb URIs: http://deb.debian.org/debian-security Suites: trixie-security Components: non-free-firmware Signed-By: /usr/share/keyrings/debian-archive-keyring.gpg EOF ;; esac $STD apt -y update } # ══════════════════════════════════════════════════════════════════════════════ # Helper: Setup GPU device permissions # ══════════════════════════════════════════════════════════════════════════════ _setup_gpu_permissions() { local in_ct="$1" local service_user="${2:-}" # /dev/dri permissions (Intel/AMD) if [[ "$in_ct" == "0" && -d /dev/dri ]]; then if ls /dev/dri/card* /dev/dri/renderD* &>/dev/null; then chgrp video /dev/dri 2>/dev/null || true chmod 755 /dev/dri 2>/dev/null || true chmod 660 /dev/dri/* 2>/dev/null || true $STD adduser "$(id -u -n)" video 2>/dev/null || true $STD adduser "$(id -u -n)" render 2>/dev/null || true # Sync GID with host local host_video_gid host_render_gid host_video_gid=$(getent group video | cut -d: -f3) host_render_gid=$(getent group render | cut -d: -f3) if [[ -n "$host_video_gid" ]]; then sed -i "s/^video:x:[0-9]*:/video:x:$host_video_gid:/" /etc/group 2>/dev/null || true fi if [[ -n "$host_render_gid" ]]; then sed -i "s/^render:x:[0-9]*:/render:x:$host_render_gid:/" /etc/group 2>/dev/null || true fi # Verify VA-API if command -v vainfo &>/dev/null; then if vainfo &>/dev/null; then msg_info "VA-API verified and working" else msg_warn "vainfo test failed - check GPU passthrough" fi fi fi fi # /dev/nvidia* permissions (NVIDIA) if ls /dev/nvidia* &>/dev/null 2>&1; then msg_info "Configuring NVIDIA device permissions" for nvidia_dev in /dev/nvidia*; do [[ -e "$nvidia_dev" ]] && { chgrp video "$nvidia_dev" 2>/dev/null || true chmod 660 "$nvidia_dev" 2>/dev/null || true } done if [[ -d /dev/nvidia-caps ]]; then chmod 755 /dev/nvidia-caps 2>/dev/null || true for caps_dev in /dev/nvidia-caps/*; do [[ -e "$caps_dev" ]] && { chgrp video "$caps_dev" 2>/dev/null || true chmod 660 "$caps_dev" 2>/dev/null || true } done fi # Verify nvidia-smi if command -v nvidia-smi &>/dev/null; then if nvidia-smi &>/dev/null; then msg_info "nvidia-smi verified and working" else msg_warn "nvidia-smi test failed - check driver version match" fi fi fi # /dev/kfd permissions (AMD ROCm) if [[ -e /dev/kfd ]]; then chgrp render /dev/kfd 2>/dev/null || true chmod 660 /dev/kfd 2>/dev/null || true msg_info "AMD ROCm compute device configured" fi # Add service user to render and video groups for GPU hardware acceleration if [[ -n "$service_user" ]]; then usermod -aG render "$service_user" 2>/dev/null || true usermod -aG video "$service_user" 2>/dev/null || true fi } # ------------------------------------------------------------------------------ # Installs ImageMagick 7 from source (Debian/Ubuntu only). # # Description: # - Downloads the latest ImageMagick source tarball # - Builds and installs ImageMagick to /usr/local # - Configures dynamic linker (ldconfig) # # Notes: # - Requires: build-essential, libtool, libjpeg-dev, libpng-dev, etc. # ------------------------------------------------------------------------------ function setup_imagemagick() { local TMP_DIR=$(mktemp -d) local BINARY_PATH="/usr/local/bin/magick" # Get currently installed version local INSTALLED_VERSION="" if command -v magick &>/dev/null; then INSTALLED_VERSION=$(magick -version | awk '/^Version/ {print $3}') fi msg_info "Setup ImageMagick" ensure_dependencies \ build-essential \ libtool \ libjpeg-dev \ libpng-dev \ libtiff-dev \ libwebp-dev \ libheif-dev \ libde265-dev \ libopenjp2-7-dev \ libxml2-dev \ liblcms2-dev \ libfreetype6-dev \ libraw-dev \ libfftw3-dev \ liblqr-1-0-dev \ libgsl-dev \ pkg-config \ ghostscript if ! CURL_TIMEOUT=180 curl_with_retry "https://imagemagick.org/archive/ImageMagick.tar.gz" "$TMP_DIR/ImageMagick.tar.gz"; then msg_error "Failed to download ImageMagick" rm -rf "$TMP_DIR" return 250 fi tar -xzf "$TMP_DIR/ImageMagick.tar.gz" -C "$TMP_DIR" || { msg_error "Failed to extract ImageMagick" rm -rf "$TMP_DIR" return 251 } cd "$TMP_DIR"/ImageMagick-* || { msg_error "Source extraction failed" rm -rf "$TMP_DIR" return 251 } $STD ./configure --disable-static || { msg_error "ImageMagick configure failed" rm -rf "$TMP_DIR" return 150 } $STD make -j"$(nproc)" || { msg_error "ImageMagick compilation failed" rm -rf "$TMP_DIR" return 150 } $STD make install || { msg_error "ImageMagick installation failed" rm -rf "$TMP_DIR" return 150 } $STD ldconfig /usr/local/lib if [[ ! -x "$BINARY_PATH" ]]; then msg_error "ImageMagick installation failed" rm -rf "$TMP_DIR" return 150 fi local FINAL_VERSION FINAL_VERSION=$("$BINARY_PATH" -version | awk '/^Version/ {print $3}') rm -rf "$TMP_DIR" cache_installed_version "imagemagick" "$FINAL_VERSION" ensure_usr_local_bin_persist if [[ -n "$INSTALLED_VERSION" ]]; then msg_ok "Upgrade ImageMagick $INSTALLED_VERSION → $FINAL_VERSION" else msg_ok "Setup ImageMagick $FINAL_VERSION" fi } # ------------------------------------------------------------------------------ # Installs Temurin JDK via Adoptium APT repository. # # Description: # - Removes previous JDK if version mismatch # - Installs or upgrades to specified JAVA_VERSION # # Variables: # JAVA_VERSION - Temurin JDK version to install (e.g. 17, 21) # ------------------------------------------------------------------------------ function setup_java() { local JAVA_VERSION="${JAVA_VERSION:-21}" local DISTRO_ID DISTRO_CODENAME DISTRO_ID=$(awk -F= '/^ID=/{print $2}' /etc/os-release | tr -d '"') DISTRO_CODENAME=$(awk -F= '/VERSION_CODENAME/ { print $2 }' /etc/os-release) local DESIRED_PACKAGE="temurin-${JAVA_VERSION}-jdk" # Prepare repository (cleanup + validation) prepare_repository_setup "adoptium" || { msg_error "Failed to prepare Adoptium repository" return 100 } # Add repo if needed if [[ ! -f /etc/apt/sources.list.d/adoptium.sources ]]; then local SUITE SUITE=$(get_fallback_suite "$DISTRO_ID" "$DISTRO_CODENAME" "https://packages.adoptium.net/artifactory/deb") setup_deb822_repo \ "adoptium" \ "https://packages.adoptium.net/artifactory/api/gpg/key/public" \ "https://packages.adoptium.net/artifactory/deb" \ "$SUITE" \ "main" fi # Get currently installed version local INSTALLED_VERSION="" INSTALLED_VERSION=$(dpkg-query -W -f '${Package}\n' 2>/dev/null | grep -oP '^temurin-\K[0-9]+(?=-jdk$)' | head -n1 || echo "") # Scenario 1: Already at correct version if [[ "$INSTALLED_VERSION" == "$JAVA_VERSION" ]]; then msg_info "Update Temurin JDK $JAVA_VERSION" ensure_apt_working || return 100 upgrade_packages_with_retry "$DESIRED_PACKAGE" || { msg_error "Failed to update Temurin JDK" return 100 } cache_installed_version "temurin-jdk" "$JAVA_VERSION" msg_ok "Update Temurin JDK $JAVA_VERSION" return 0 fi # Scenario 2: Different version - remove old and install new if [[ -n "$INSTALLED_VERSION" ]]; then msg_info "Upgrade Temurin JDK from $INSTALLED_VERSION to $JAVA_VERSION" $STD apt purge -y "temurin-${INSTALLED_VERSION}-jdk" || true else msg_info "Setup Temurin JDK $JAVA_VERSION" fi ensure_apt_working || return 100 # Install with retry logic install_packages_with_retry "$DESIRED_PACKAGE" || { msg_error "Failed to install Temurin JDK $JAVA_VERSION" return 100 } cache_installed_version "temurin-jdk" "$JAVA_VERSION" msg_ok "Setup Temurin JDK $JAVA_VERSION" } # ------------------------------------------------------------------------------ # Installs a local IP updater script using networkd-dispatcher. # # Description: # - Stores current IP in /run/local-ip.env # - Automatically runs on network changes # ------------------------------------------------------------------------------ function setup_local_ip_helper() { local BASE_DIR="/usr/local/community-scripts/ip-management" local SCRIPT_PATH="$BASE_DIR/update_local_ip.sh" local IP_FILE="/run/local-ip.env" local DISPATCHER_SCRIPT="/etc/networkd-dispatcher/routable.d/10-update-local-ip.sh" # Check if already set up if [[ -f "$SCRIPT_PATH" && -f "$DISPATCHER_SCRIPT" ]]; then msg_info "Update Local IP Helper" cache_installed_version "local-ip-helper" "1.0" msg_ok "Update Local IP Helper" else msg_info "Setup Local IP Helper" fi mkdir -p "$BASE_DIR" # Install networkd-dispatcher if not present if ! dpkg -s networkd-dispatcher >/dev/null 2>&1; then ensure_dependencies networkd-dispatcher || { msg_error "Failed to install networkd-dispatcher" return 100 } fi # Write update_local_ip.sh cat <<'EOF' >"$SCRIPT_PATH" #!/bin/bash set -euo pipefail IP_FILE="/run/local-ip.env" mkdir -p "$(dirname "$IP_FILE")" get_current_ip() { local ip # Try IPv4 targets first local ipv4_targets=("8.8.8.8" "1.1.1.1" "192.168.1.1" "10.0.0.1" "172.16.0.1" "default") for target in "${ipv4_targets[@]}"; do if [[ "$target" == "default" ]]; then ip=$(ip route get 1 2>/dev/null | awk '{for(i=1;i<=NF;i++) if ($i=="src") print $(i+1)}') else ip=$(ip route get "$target" 2>/dev/null | awk '{for(i=1;i<=NF;i++) if ($i=="src") print $(i+1)}') fi if [[ -n "$ip" ]]; then echo "$ip" return 0 fi done # IPv6 fallback: Try direct interface lookup for eth0 ip=$(ip -6 addr show eth0 scope global 2>/dev/null | awk '/inet6 / {print $2}' | cut -d/ -f1 | head -n1) if [[ -n "$ip" && "$ip" =~ : ]]; then echo "$ip" return 0 fi # IPv6 fallback: Use routing table with IPv6 targets (Google DNS, Cloudflare DNS) local ipv6_targets=("2001:4860:4860::8888" "2606:4700:4700::1111") for target in "${ipv6_targets[@]}"; do ip=$(ip -6 route get "$target" 2>/dev/null | awk '{for(i=1;i<=NF;i++) if ($i=="src") print $(i+1)}') if [[ -n "$ip" && "$ip" =~ : ]]; then echo "$ip" return 0 fi done return 1 } current_ip="$(get_current_ip)" if [[ -z "$current_ip" ]]; then echo "[ERROR] Could not detect local IP" >&2 exit 123 fi if [[ -f "$IP_FILE" ]]; then source "$IP_FILE" [[ "$LOCAL_IP" == "$current_ip" ]] && exit 0 fi echo "LOCAL_IP=$current_ip" > "$IP_FILE" echo "[INFO] LOCAL_IP updated to $current_ip" EOF chmod +x "$SCRIPT_PATH" # Install dispatcher hook mkdir -p "$(dirname "$DISPATCHER_SCRIPT")" cat <"$DISPATCHER_SCRIPT" #!/bin/bash $SCRIPT_PATH EOF chmod +x "$DISPATCHER_SCRIPT" systemctl enable -q --now networkd-dispatcher.service || { msg_warn "Failed to enable networkd-dispatcher service" } cache_installed_version "local-ip-helper" "1.0" msg_ok "Setup Local IP Helper" } # ------------------------------------------------------------------------------ # Installs or updates MariaDB. # # Description: # - Uses Debian/Ubuntu distribution packages by default (most reliable) # - Only uses official MariaDB repository when a specific version is requested # - Detects current MariaDB version and replaces it if necessary # - Preserves existing database data # # Variables: # MARIADB_VERSION - MariaDB version to install (optional) # - Not set or "latest": Uses distribution packages (recommended) # - Specific version (e.g. "11.4", "12.2"): Uses MariaDB official repo # ------------------------------------------------------------------------------ setup_mariadb() { local MARIADB_VERSION="${MARIADB_VERSION:-latest}" local USE_DISTRO_PACKAGES=false # Ensure non-interactive mode for all apt operations export DEBIAN_FRONTEND=noninteractive export NEEDRESTART_MODE=a export NEEDRESTART_SUSPEND=1 # Determine installation method: # - "latest" or empty: Use distribution packages (avoids mirror issues) # - Specific version: Use MariaDB official repository if [[ "$MARIADB_VERSION" == "latest" || -z "$MARIADB_VERSION" ]]; then USE_DISTRO_PACKAGES=true msg_info "Setup MariaDB (distribution packages)" else msg_info "Setup MariaDB $MARIADB_VERSION (official repository)" fi # Get currently installed version local CURRENT_VERSION="" CURRENT_VERSION=$(is_tool_installed "mariadb" 2>/dev/null) || true # Pre-configure debconf to prevent any interactive prompts during install/upgrade debconf-set-selections </dev/null | grep -E "Candidate:" | awk '{print $2}' | grep -oP '^\d+:\K\d+\.\d+\.\d+' || echo "") if [[ -n "$DISTRO_VERSION" ]]; then # Compare versions - if current is higher, keep it local CURRENT_MAJOR DISTRO_MAJOR CURRENT_MAJOR=$(echo "$CURRENT_VERSION" | awk -F. '{print $1}') DISTRO_MAJOR=$(echo "$DISTRO_VERSION" | awk -F. '{print $1}') if [[ "$CURRENT_MAJOR" -gt "$DISTRO_MAJOR" ]]; then msg_warn "MariaDB $CURRENT_VERSION is already installed (higher than distro $DISTRO_VERSION)" msg_warn "Keeping existing installation to preserve data integrity" msg_warn "To use distribution packages, manually remove MariaDB first" _setup_mariadb_runtime_dir cache_installed_version "mariadb" "$CURRENT_VERSION" msg_ok "Setup MariaDB $CURRENT_VERSION (existing installation kept)" return 0 fi fi fi # Install or upgrade MariaDB from distribution packages if ! install_packages_with_retry "mariadb-server" "mariadb-client"; then msg_error "Failed to install MariaDB packages from distribution" return 100 fi # Get installed version for caching local INSTALLED_VERSION="" INSTALLED_VERSION=$(mariadb --version 2>/dev/null | grep -oP '\d+\.\d+\.\d+' | head -n1 || echo "distro") # Configure runtime directory and finish _setup_mariadb_runtime_dir cache_installed_version "mariadb" "$INSTALLED_VERSION" msg_ok "Setup MariaDB $INSTALLED_VERSION (distribution packages)" return 0 fi # ============================================================================ # OFFICIAL REPOSITORY PATH (only when specific version requested) # ============================================================================ # First, check if there's an old/broken repository that needs cleanup if [[ -f /etc/apt/sources.list.d/mariadb.sources ]] || [[ -f /etc/apt/sources.list.d/mariadb.list ]]; then local OLD_REPO_VERSION="" OLD_REPO_VERSION=$(grep -oP 'repo/\K[0-9]+\.[0-9]+(\.[0-9]+)?' /etc/apt/sources.list.d/mariadb.sources 2>/dev/null || grep -oP 'repo/\K[0-9]+\.[0-9]+(\.[0-9]+)?' /etc/apt/sources.list.d/mariadb.list 2>/dev/null || echo "") # Check if old repo points to a different version if [[ -n "$OLD_REPO_VERSION" ]] && [[ "${OLD_REPO_VERSION%.*}" != "${MARIADB_VERSION%.*}" ]]; then msg_info "Cleaning up old MariaDB repository (was: $OLD_REPO_VERSION, requested: $MARIADB_VERSION)" cleanup_old_repo_files "mariadb" $STD apt update || msg_warn "APT update had issues, continuing..." fi fi # Scenario 1: Already installed at target version - just update packages if [[ -n "$CURRENT_VERSION" && "$CURRENT_VERSION" == "$MARIADB_VERSION" ]]; then msg_info "Update MariaDB $MARIADB_VERSION" # Ensure APT is working ensure_apt_working || return 100 # Check if repository needs to be refreshed if [[ -f /etc/apt/sources.list.d/mariadb.sources ]]; then local REPO_VERSION="" REPO_VERSION=$(grep -oP 'repo/\K[0-9]+\.[0-9]+' /etc/apt/sources.list.d/mariadb.sources 2>/dev/null || echo "") if [[ -n "$REPO_VERSION" && "$REPO_VERSION" != "${MARIADB_VERSION%.*}" ]]; then msg_warn "Repository version mismatch, updating..." manage_tool_repository "mariadb" "$MARIADB_VERSION" "http://mirror.mariadb.org/repo/$MARIADB_VERSION" \ "https://mariadb.org/mariadb_release_signing_key.asc" || { msg_error "Failed to update MariaDB repository" return 100 } fi fi # Perform upgrade with retry logic ensure_apt_working || return 100 upgrade_packages_with_retry "mariadb-server" "mariadb-client" || { msg_error "Failed to upgrade MariaDB packages" return 100 } cache_installed_version "mariadb" "$MARIADB_VERSION" msg_ok "Update MariaDB $MARIADB_VERSION" return 0 fi # Scenario 2b: Different version installed - clean upgrade if [[ -n "$CURRENT_VERSION" && "$CURRENT_VERSION" != "$MARIADB_VERSION" ]]; then msg_info "Upgrade MariaDB from $CURRENT_VERSION to $MARIADB_VERSION" remove_old_tool_version "mariadb" fi # Scenario 3: Fresh install or version change with specific version # Prepare repository (cleanup + validation) prepare_repository_setup "mariadb" || { msg_error "Failed to prepare MariaDB repository" return 100 } # Install required dependencies first local mariadb_deps=() for dep in gawk rsync socat libdbi-perl pv; do if apt-cache search "^${dep}$" 2>/dev/null | grep -q .; then mariadb_deps+=("$dep") fi done if [[ ${#mariadb_deps[@]} -gt 0 ]]; then $STD apt install -y "${mariadb_deps[@]}" 2>/dev/null || true fi # Setup repository manage_tool_repository "mariadb" "$MARIADB_VERSION" "http://mirror.mariadb.org/repo/$MARIADB_VERSION" \ "https://mariadb.org/mariadb_release_signing_key.asc" || { msg_error "Failed to setup MariaDB repository" return 100 } # Install packages with retry logic if ! install_packages_with_retry "mariadb-server" "mariadb-client"; then # Fallback: try distribution packages msg_warn "Failed to install MariaDB $MARIADB_VERSION from official repo, falling back to distribution packages..." cleanup_old_repo_files "mariadb" $STD apt update || { msg_warn "APT update also failed, continuing with cache" } if install_packages_with_retry "mariadb-server" "mariadb-client"; then local FALLBACK_VERSION="" FALLBACK_VERSION=$(mariadb --version 2>/dev/null | grep -oP '\d+\.\d+\.\d+' | head -n1 || echo "distro") msg_warn "Installed MariaDB $FALLBACK_VERSION from distribution instead of requested $MARIADB_VERSION" _setup_mariadb_runtime_dir cache_installed_version "mariadb" "$FALLBACK_VERSION" msg_ok "Setup MariaDB $FALLBACK_VERSION (fallback to distribution packages)" return 0 else msg_error "Failed to install MariaDB packages (both official repo and distribution)" return 100 fi fi _setup_mariadb_runtime_dir cache_installed_version "mariadb" "$MARIADB_VERSION" msg_ok "Setup MariaDB $MARIADB_VERSION" } # ------------------------------------------------------------------------------ # Helper function: Configure MariaDB runtime directory persistence # ------------------------------------------------------------------------------ _setup_mariadb_runtime_dir() { # Configure tmpfiles.d to ensure /run/mysqld directory is created on boot # This fixes the issue where MariaDB fails to start after container reboot # Create tmpfiles.d configuration with error handling if ! printf '# Ensure /run/mysqld directory exists with correct permissions for MariaDB\nd /run/mysqld 0755 mysql mysql -\n' >/etc/tmpfiles.d/mariadb.conf; then msg_warn "Failed to create /etc/tmpfiles.d/mariadb.conf - runtime directory may not persist on reboot" fi # Create the directory now if it doesn't exist # Verify mysql user exists before attempting ownership change if [[ ! -d /run/mysqld ]]; then mkdir -p /run/mysqld # Set permissions first (works regardless of user existence) chmod 755 /run/mysqld # Set ownership only if mysql user exists if getent passwd mysql >/dev/null 2>&1; then chown mysql:mysql /run/mysqld else msg_warn "mysql user not found - directory created with correct permissions but ownership not set" fi fi } # ------------------------------------------------------------------------------ # Creates MariaDB database with user, charset and optional extra grants/modes # # Description: # - Generates password if empty # - Creates database with utf8mb4_unicode_ci # - Creates local user with password # - Grants full access to this DB # - Optional: apply extra GRANT statements (comma-separated) # - Optional: apply custom GLOBAL sql_mode # - Saves credentials to file # - Exports variables for use in calling script # # Usage: # MARIADB_DB_NAME="myapp_db" MARIADB_DB_USER="myapp_user" setup_mariadb_db # MARIADB_DB_NAME="domain_monitor" MARIADB_DB_USER="domainmonitor" setup_mariadb_db # MARIADB_DB_NAME="myapp" MARIADB_DB_USER="myapp" MARIADB_DB_EXTRA_GRANTS="GRANT SELECT ON \`mysql\`.\`time_zone_name\`" setup_mariadb_db # MARIADB_DB_NAME="ghostfolio" MARIADB_DB_USER="ghostfolio" MARIADB_DB_SQL_MODE="" setup_mariadb_db # # Variables: # MARIADB_DB_NAME - Database name (required) # MARIADB_DB_USER - Database user (required) # MARIADB_DB_PASS - User password (optional, auto-generated if empty) # MARIADB_DB_EXTRA_GRANTS - Comma-separated GRANT statements (optional) # Example: "GRANT SELECT ON \`mysql\`.\`time_zone_name\`" # MARIADB_DB_SQL_MODE - Optional global sql_mode override (e.g. "", "STRICT_TRANS_TABLES") # MARIADB_DB_CREDS_FILE - Credentials file path (optional, default: ~/${APPLICATION}.creds) # # Exports: # MARIADB_DB_NAME, MARIADB_DB_USER, MARIADB_DB_PASS # ------------------------------------------------------------------------------ function setup_mariadb_db() { if [[ -z "${MARIADB_DB_NAME:-}" || -z "${MARIADB_DB_USER:-}" ]]; then msg_error "MARIADB_DB_NAME and MARIADB_DB_USER must be set before calling setup_mariadb_db" return 65 fi if [[ -z "${MARIADB_DB_PASS:-}" ]]; then MARIADB_DB_PASS=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | head -c13) fi msg_info "Setting up MariaDB Database" $STD mariadb -u root -e "CREATE DATABASE \`$MARIADB_DB_NAME\` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;" $STD mariadb -u root -e "CREATE USER '$MARIADB_DB_USER'@'localhost' IDENTIFIED BY '$MARIADB_DB_PASS';" $STD mariadb -u root -e "GRANT ALL ON \`$MARIADB_DB_NAME\`.* TO '$MARIADB_DB_USER'@'localhost';" # Optional extra grants if [[ -n "${MARIADB_DB_EXTRA_GRANTS:-}" ]]; then IFS=',' read -ra G_LIST <<<"${MARIADB_DB_EXTRA_GRANTS:-}" for g in "${G_LIST[@]}"; do g=$(echo "$g" | xargs) $STD mariadb -u root -e "$g TO '$MARIADB_DB_USER'@'localhost';" done fi # Optional sql_mode override if [[ -n "${MARIADB_DB_SQL_MODE:-}" ]]; then $STD mariadb -u root -e "SET GLOBAL sql_mode='${MARIADB_DB_SQL_MODE:-}';" fi $STD mariadb -u root -e "FLUSH PRIVILEGES;" local app_name="${APPLICATION,,}" local CREDS_FILE="${MARIADB_DB_CREDS_FILE:-${HOME}/${app_name}.creds}" { echo "MariaDB Credentials" echo "Database: $MARIADB_DB_NAME" echo "User: $MARIADB_DB_USER" echo "Password: $MARIADB_DB_PASS" } >>"$CREDS_FILE" msg_ok "Set up MariaDB Database" export MARIADB_DB_NAME export MARIADB_DB_USER export MARIADB_DB_PASS } # ------------------------------------------------------------------------------ # Installs or updates MongoDB to specified version. # # Description: # - Preserves data across installations # - Adds official MongoDB repo # # Variables: # MONGO_VERSION - MongoDB version to install (e.g. 7.0, 8.2) # ------------------------------------------------------------------------------ function setup_mongodb() { local MONGO_VERSION="${MONGO_VERSION:-8.0}" local DISTRO_ID DISTRO_CODENAME DISTRO_ID=$(get_os_info id) DISTRO_CODENAME=$(get_os_info codename) # Ensure non-interactive mode for all apt operations export DEBIAN_FRONTEND=noninteractive export NEEDRESTART_MODE=a export NEEDRESTART_SUSPEND=1 # Check AVX support if ! grep -qm1 'avx[^ ]*' /proc/cpuinfo; then local major="${MONGO_VERSION%%.*}" if ((major > 5)); then msg_error "MongoDB ${MONGO_VERSION} requires AVX support, which is not available on this system." return 236 fi fi case "$DISTRO_ID" in ubuntu) MONGO_BASE_URL="https://repo.mongodb.org/apt/ubuntu" ;; debian) MONGO_BASE_URL="https://repo.mongodb.org/apt/debian" ;; *) msg_error "Unsupported distribution: $DISTRO_ID" return 238 ;; esac # Get currently installed version local INSTALLED_VERSION="" INSTALLED_VERSION=$(is_tool_installed "mongodb" 2>/dev/null) || true # Scenario 1: Already at target version - just update packages if [[ -n "$INSTALLED_VERSION" && "$INSTALLED_VERSION" == "$MONGO_VERSION" ]]; then msg_info "Update MongoDB $MONGO_VERSION" ensure_apt_working || return 100 # Perform upgrade with retry logic upgrade_packages_with_retry "mongodb-org" || { msg_error "Failed to upgrade MongoDB" return 100 } cache_installed_version "mongodb" "$MONGO_VERSION" msg_ok "Update MongoDB $MONGO_VERSION" return 0 fi # Scenario 2: Different version installed - clean upgrade if [[ -n "$INSTALLED_VERSION" && "$INSTALLED_VERSION" != "$MONGO_VERSION" ]]; then msg_info "Upgrade MongoDB from $INSTALLED_VERSION to $MONGO_VERSION" remove_old_tool_version "mongodb" else msg_info "Setup MongoDB $MONGO_VERSION" fi cleanup_orphaned_sources # Prepare repository (cleanup + validation) prepare_repository_setup "mongodb" || { msg_error "Failed to prepare MongoDB repository" return 100 } # Setup repository # MongoDB 8.x versions beyond 8.0 reuse the server-8.0.asc PGP key local MONGO_KEY_VERSION="${MONGO_VERSION}" [[ "${MONGO_VERSION}" == 8.[1-9]* ]] && MONGO_KEY_VERSION="8.0" manage_tool_repository "mongodb" "$MONGO_VERSION" "$MONGO_BASE_URL" \ "https://www.mongodb.org/static/pgp/server-${MONGO_KEY_VERSION}.asc" || { msg_error "Failed to setup MongoDB repository" return 100 } # Wait for repo to settle $STD apt update || { msg_error "APT update failed — invalid MongoDB repo for ${DISTRO_ID}-${DISTRO_CODENAME}?" return 100 } # Install MongoDB with retry logic install_packages_with_retry "mongodb-org" || { msg_error "Failed to install MongoDB packages" return 100 } if ! command -v mongod >/dev/null 2>&1; then msg_error "MongoDB binary not found after installation" return 127 fi mkdir -p /var/lib/mongodb chown -R mongodb:mongodb /var/lib/mongodb $STD systemctl enable mongod || { msg_warn "Failed to enable mongod service" } safe_service_restart mongod # Verify MongoDB version local INSTALLED_VERSION INSTALLED_VERSION=$(mongod --version 2>/dev/null | grep -oP 'db version v\K[0-9]+\.[0-9]+' | head -n1 || echo "0.0") verify_tool_version "MongoDB" "$MONGO_VERSION" "$INSTALLED_VERSION" || true cache_installed_version "mongodb" "$MONGO_VERSION" msg_ok "Setup MongoDB $MONGO_VERSION" } # ------------------------------------------------------------------------------ # Installs or upgrades MySQL. # # Description: # - By default uses distro repository (Debian/Ubuntu apt) for stability # - Optionally uses official MySQL repository for specific versions # - Detects existing MySQL installation # - Purges conflicting packages before installation # - Supports clean upgrade # - Handles Debian Trixie libaio1t64 transition # # Variables: # USE_MYSQL_REPO - Use official MySQL repository (default: true) # Set to "false" to use distro packages instead # MYSQL_VERSION - MySQL version to install when using official repo # (e.g. 8.0, 8.4) (default: 8.0) # # Examples: # setup_mysql # Uses official MySQL repo, 8.0 # MYSQL_VERSION="8.4" setup_mysql # Specific version from MySQL repo # USE_MYSQL_REPO=false setup_mysql # Uses distro package instead # ------------------------------------------------------------------------------ function setup_mysql() { local MYSQL_VERSION="${MYSQL_VERSION:-8.0}" local USE_MYSQL_REPO="${USE_MYSQL_REPO:-true}" local DISTRO_ID DISTRO_CODENAME DISTRO_ID=$(awk -F= '/^ID=/{print $2}' /etc/os-release | tr -d '"') DISTRO_CODENAME=$(awk -F= '/^VERSION_CODENAME=/{print $2}' /etc/os-release) # Ensure non-interactive mode for all apt operations export DEBIAN_FRONTEND=noninteractive export NEEDRESTART_MODE=a export NEEDRESTART_SUSPEND=1 # Get currently installed version local CURRENT_VERSION="" CURRENT_VERSION=$(is_tool_installed "mysql" 2>/dev/null) || true # Scenario 1: Use distro repository (default, most stable) if [[ "$USE_MYSQL_REPO" != "true" && "$USE_MYSQL_REPO" != "TRUE" && "$USE_MYSQL_REPO" != "1" ]]; then msg_info "Setup MySQL (distro package)" # If already installed, just update if [[ -n "$CURRENT_VERSION" ]]; then msg_info "Update MySQL $CURRENT_VERSION" ensure_apt_working || return 100 upgrade_packages_with_retry "default-mysql-server" "default-mysql-client" || upgrade_packages_with_retry "mysql-server" "mysql-client" || upgrade_packages_with_retry "mariadb-server" "mariadb-client" || { msg_error "Failed to upgrade MySQL/MariaDB packages" return 100 } cache_installed_version "mysql" "$CURRENT_VERSION" msg_ok "Update MySQL $CURRENT_VERSION" return 0 fi # Fresh install from distro repo ensure_apt_working || return 100 export DEBIAN_FRONTEND=noninteractive # Try default-mysql-server first, fallback to mysql-server, then mariadb if apt-cache search "^default-mysql-server$" 2>/dev/null | grep -q .; then install_packages_with_retry "default-mysql-server" "default-mysql-client" || { msg_warn "default-mysql-server failed, trying mysql-server" install_packages_with_retry "mysql-server" "mysql-client" || { msg_warn "mysql-server failed, trying mariadb as fallback" install_packages_with_retry "mariadb-server" "mariadb-client" || { msg_error "Failed to install any MySQL/MariaDB from distro repository" return 100 } } } elif apt-cache search "^mysql-server$" 2>/dev/null | grep -q .; then install_packages_with_retry "mysql-server" "mysql-client" || { msg_warn "mysql-server failed, trying mariadb as fallback" install_packages_with_retry "mariadb-server" "mariadb-client" || { msg_error "Failed to install any MySQL/MariaDB from distro repository" return 100 } } else # Distro doesn't have MySQL, use MariaDB install_packages_with_retry "mariadb-server" "mariadb-client" || { msg_error "Failed to install MariaDB from distro repository" return 100 } fi # Get installed version local INSTALLED_VERSION="" INSTALLED_VERSION=$(is_tool_installed "mysql" 2>/dev/null) || true if [[ -z "$INSTALLED_VERSION" ]]; then INSTALLED_VERSION=$(is_tool_installed "mariadb" 2>/dev/null) || true fi cache_installed_version "mysql" "${INSTALLED_VERSION:-distro}" msg_ok "Setup MySQL/MariaDB ${INSTALLED_VERSION:-from distro}" return 0 fi # Scenario 2: Use official MySQL repository (USE_MYSQL_REPO=true) # Scenario 2a: Already at target version - just update packages if [[ -n "$CURRENT_VERSION" && "$CURRENT_VERSION" == "$MYSQL_VERSION" ]]; then msg_info "Update MySQL $MYSQL_VERSION" ensure_apt_working || return 100 # Perform upgrade with retry logic (non-fatal if fails) upgrade_packages_with_retry "mysql-server" "mysql-client" || { msg_warn "MySQL package upgrade had issues, continuing with current version" } cache_installed_version "mysql" "$MYSQL_VERSION" msg_ok "Update MySQL $MYSQL_VERSION" return 0 fi # Scenario 2: Different version installed - clean upgrade if [[ -n "$CURRENT_VERSION" && "$CURRENT_VERSION" != "$MYSQL_VERSION" ]]; then msg_info "Upgrade MySQL from $CURRENT_VERSION to $MYSQL_VERSION" remove_old_tool_version "mysql" else msg_info "Setup MySQL $MYSQL_VERSION" fi # Prepare repository (cleanup + validation) prepare_repository_setup "mysql" || { msg_error "Failed to prepare MySQL repository" return 100 } # Debian 13+ Fix: MySQL 8.0 incompatible with libaio1t64, use 8.4 LTS if [[ "$DISTRO_ID" == "debian" && "$DISTRO_CODENAME" =~ ^(trixie|forky|sid)$ ]]; then msg_info "Debian ${DISTRO_CODENAME} detected → using MySQL 8.4 LTS (libaio1t64 compatible)" if ! download_gpg_key "https://repo.mysql.com/RPM-GPG-KEY-mysql-2023" "/etc/apt/keyrings/mysql.gpg" "dearmor"; then msg_error "Failed to import MySQL GPG key" return 7 fi cat >/etc/apt/sources.list.d/mysql.sources </dev/null | grep -q . && install_packages_with_retry "mysql-server" "mysql-client"; then mysql_install_success=true elif apt-cache search "^mysql-community-server$" 2>/dev/null | grep -q . && install_packages_with_retry "mysql-community-server" "mysql-community-client"; then mysql_install_success=true elif apt-cache search "^mysql$" 2>/dev/null | grep -q . && install_packages_with_retry "mysql"; then mysql_install_success=true fi if [[ "$mysql_install_success" == false ]]; then msg_error "MySQL ${MYSQL_VERSION} package not available for suite ${SUITE}" return 100 fi # Verify mysql command is accessible if ! command -v mysql >/dev/null 2>&1; then hash -r if ! command -v mysql >/dev/null 2>&1; then msg_error "MySQL installed but mysql command still not found" return 127 fi fi cache_installed_version "mysql" "$MYSQL_VERSION" msg_ok "Setup MySQL $MYSQL_VERSION" } # ------------------------------------------------------------------------------ # Installs Node.js and optional global modules. # # Description: # - Installs specified Node.js version using NodeSource APT repo # - Optionally installs or updates global npm modules # # Variables: # NODE_VERSION - Node.js version to install (default: 24 LTS) # NODE_MODULE - Comma-separated list of global modules (e.g. "yarn,@vue/cli@5.0.0") # ------------------------------------------------------------------------------ function setup_nodejs() { local NODE_VERSION="${NODE_VERSION:-24}" local NODE_MODULE="${NODE_MODULE:-}" # ALWAYS clean up legacy installations first (nvm, etc.) to prevent conflicts cleanup_legacy_install "nodejs" # Get currently installed version local CURRENT_NODE_VERSION="" CURRENT_NODE_VERSION=$(is_tool_installed "nodejs" 2>/dev/null) || true # Ensure jq is available for JSON parsing if ! command -v jq &>/dev/null; then $STD apt update $STD apt install -y jq || { msg_error "Failed to install jq" return 100 } fi # Scenario 1: Already installed at target version - upgrade to latest minor/patch + update packages/modules if [[ -n "$CURRENT_NODE_VERSION" && "$CURRENT_NODE_VERSION" == "$NODE_VERSION" ]]; then msg_info "Update Node.js $NODE_VERSION" ensure_apt_working || return 100 # Upgrade to the latest minor/patch release from NodeSource $STD apt-get install -y --only-upgrade nodejs 2>/dev/null || true # Pin npm to 11.11.0 to work around Node.js 22.22.2 regression (nodejs/node#62425) $STD npm install -g npm@11.11.0 2>/dev/null || true cache_installed_version "nodejs" "$NODE_VERSION" msg_ok "Update Node.js $NODE_VERSION" else # Scenario 2: Different version installed - clean upgrade if [[ -n "$CURRENT_NODE_VERSION" && "$CURRENT_NODE_VERSION" != "$NODE_VERSION" ]]; then msg_info "Upgrade Node.js from $CURRENT_NODE_VERSION to $NODE_VERSION" remove_old_tool_version "nodejs" else msg_info "Setup Node.js $NODE_VERSION" fi # Remove ALL Debian nodejs packages BEFORE adding NodeSource repo if dpkg -l 2>/dev/null | grep -qE "^ii.*(nodejs|libnode|node-cjs|node-acorn|node-balanced|node-brace|node-minimatch|node-undici|node-xtend|node-corepack)"; then msg_info "Removing Debian-packaged Node.js and dependencies" $STD apt purge -y nodejs nodejs-doc libnode* node-* 2>/dev/null || true $STD apt autoremove -y 2>/dev/null || true $STD apt clean 2>/dev/null || true fi # Remove any APT pinning (not needed) rm -f /etc/apt/preferences.d/nodesource 2>/dev/null || true # Prepare repository (cleanup + validation) prepare_repository_setup "nodesource" || { msg_error "Failed to prepare Node.js repository" return 250 } # Setup NodeSource repository manage_tool_repository "nodejs" "$NODE_VERSION" "https://deb.nodesource.com/node_${NODE_VERSION}.x" "https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key" || { msg_error "Failed to setup Node.js repository" return 250 } # Force APT cache refresh after repository setup $STD apt update || { msg_warn "apt update failed after Node.js repository setup" } ensure_dependencies curl ca-certificates gnupg install_packages_with_retry "nodejs" || { msg_error "Failed to install Node.js ${NODE_VERSION} from NodeSource" return 100 } # Verify Node.js was installed correctly if ! command -v node >/dev/null 2>&1; then msg_error "Node.js binary not found after installation" return 127 fi local INSTALLED_NODE_VERSION INSTALLED_NODE_VERSION=$(node -v 2>/dev/null | grep -oP '^v\K[0-9]+' || echo "0") verify_tool_version "Node.js" "$NODE_VERSION" "$INSTALLED_NODE_VERSION" || true # Verify npm is available (should come with NodeSource nodejs) if ! command -v npm >/dev/null 2>&1; then msg_error "npm not found after Node.js installation - repository issue?" return 127 fi # Pin npm to 11.11.0 to work around Node.js 22.22.2 regression (nodejs/node#62425) local NPM_VERSION NPM_VERSION=$(npm -v 2>/dev/null || echo "0") if [[ "$NPM_VERSION" != "0" ]]; then $STD npm install -g npm@11.11.0 2>/dev/null || { msg_warn "Failed to update npm to 11.11.0 (continuing with bundled npm $NPM_VERSION)" } fi cache_installed_version "nodejs" "$NODE_VERSION" msg_ok "Setup Node.js $NODE_VERSION" fi # Set a safe default heap limit for Node.js builds if not explicitly provided. # Priority: # 1) NODE_OPTIONS (caller/user override) # 2) NODE_MAX_OLD_SPACE_SIZE (explicit MB override) # 3) var_ram (LXC memory setting, MB) # 4) /proc/meminfo (runtime memory detection) # Auto value is clamped to 1024..12288 MB. if [[ -z "${NODE_OPTIONS:-}" ]]; then local node_heap_mb="" if [[ -n "${NODE_MAX_OLD_SPACE_SIZE:-}" ]] && [[ "${NODE_MAX_OLD_SPACE_SIZE}" =~ ^[0-9]+$ ]]; then node_heap_mb="${NODE_MAX_OLD_SPACE_SIZE}" elif [[ -n "${var_ram:-}" ]] && [[ "${var_ram}" =~ ^[0-9]+$ ]]; then node_heap_mb=$((var_ram * 75 / 100)) else local total_mem_kb="" total_mem_kb=$(awk '/^MemTotal:/ {print $2; exit}' /proc/meminfo 2>/dev/null || echo "") if [[ "$total_mem_kb" =~ ^[0-9]+$ ]]; then local total_mem_mb=$((total_mem_kb / 1024)) node_heap_mb=$((total_mem_mb * 75 / 100)) fi fi if [[ -z "$node_heap_mb" ]] || ((node_heap_mb < 1024)); then node_heap_mb=1024 elif ((node_heap_mb > 12288)); then node_heap_mb=12288 fi export NODE_OPTIONS="--max-old-space-size=${node_heap_mb}" fi # Ensure valid working directory for npm (avoids uv_cwd error) if [[ ! -d /opt ]]; then mkdir -p /opt fi cd /opt || { msg_error "Failed to set safe working directory before npm install" return 127 } # Install global Node modules if [[ -n "$NODE_MODULE" ]]; then IFS=',' read -ra MODULES <<<"$NODE_MODULE" # Pin pnpm to v10 to avoid breaking changes from newer major versions for i in "${!MODULES[@]}"; do if [[ "${MODULES[$i]}" =~ ^pnpm(@.*)?$ ]]; then MODULES[$i]="pnpm@^10" fi done local failed_modules=0 for mod in "${MODULES[@]}"; do local MODULE_NAME MODULE_REQ_VERSION MODULE_INSTALLED_VERSION if [[ "$mod" == @*/*@* ]]; then # Scoped package with version, e.g. @vue/cli-service@latest MODULE_NAME="${mod%@*}" MODULE_REQ_VERSION="${mod##*@}" elif [[ "$mod" == *"@"* ]]; then # Unscoped package with version, e.g. yarn@latest MODULE_NAME="${mod%@*}" MODULE_REQ_VERSION="${mod##*@}" else # No version specified MODULE_NAME="$mod" MODULE_REQ_VERSION="latest" fi # Check if the module is already installed if $STD npm list -g --depth=0 "$MODULE_NAME" 2>&1 | grep -q "$MODULE_NAME@"; then MODULE_INSTALLED_VERSION="$(npm list -g --depth=0 "$MODULE_NAME" 2>&1 | grep "$MODULE_NAME@" | awk -F@ '{print $2}' 2>/dev/null | tr -d '[:space:]' || echo '')" if [[ "$MODULE_REQ_VERSION" != "latest" && "$MODULE_REQ_VERSION" != "$MODULE_INSTALLED_VERSION" ]]; then msg_info "Updating $MODULE_NAME from v$MODULE_INSTALLED_VERSION to v$MODULE_REQ_VERSION" if ! $STD npm install -g "${MODULE_NAME}@${MODULE_REQ_VERSION}" 2>/dev/null; then msg_warn "Failed to update $MODULE_NAME to version $MODULE_REQ_VERSION" ((failed_modules++)) || true continue fi elif [[ "$MODULE_REQ_VERSION" == "latest" ]]; then msg_info "Updating $MODULE_NAME to latest version" if ! $STD npm install -g "${MODULE_NAME}@latest" 2>/dev/null; then msg_warn "Failed to update $MODULE_NAME to latest version" ((failed_modules++)) || true continue fi fi else msg_info "Installing $MODULE_NAME@$MODULE_REQ_VERSION" if ! $STD npm install -g "${MODULE_NAME}@${MODULE_REQ_VERSION}" 2>/dev/null; then msg_warn "Failed to install $MODULE_NAME@$MODULE_REQ_VERSION" ((failed_modules++)) || true continue fi fi done if [[ $failed_modules -eq 0 ]]; then msg_ok "Installed Node.js modules: $NODE_MODULE" else msg_warn "Installed Node.js modules with $failed_modules failure(s): $NODE_MODULE" fi fi } # ------------------------------------------------------------------------------ # Installs PHP with selected modules and configures Apache/FPM support. # # Description: # - Adds Sury PHP repo if needed # - Installs default and user-defined modules # - Patches php.ini for CLI, Apache, and FPM as needed # - Handles built-in modules gracefully (e.g., opcache in PHP 8.5+) # - Skips unavailable packages without failing # # Variables: # PHP_VERSION - PHP version to install (default: 8.4) # PHP_MODULE - Additional comma-separated modules # PHP_APACHE - Set YES to enable PHP with Apache # PHP_FPM - Set YES to enable PHP-FPM # PHP_MEMORY_LIMIT - (default: 512M) # PHP_UPLOAD_MAX_FILESIZE - (default: 128M) # PHP_POST_MAX_SIZE - (default: 128M) # PHP_MAX_EXECUTION_TIME - (default: 300) # # Notes on modules: # - Base modules (always installed): bcmath, cli, curl, gd, intl, mbstring, # readline, xml, zip, common # - Extended modules (commonly needed): mysql, sqlite3, pgsql, redis, # imagick, bz2, ldap, soap, imap, gmp, apcu # - Some modules are built-in depending on PHP version: # * PHP 8.5+: opcache is built-in (no separate package) # * All versions: ctype, fileinfo, iconv, tokenizer, phar, posix, etc. # are part of php-common # - Unavailable modules are skipped with a warning, not an error # ------------------------------------------------------------------------------ function setup_php() { local PHP_VERSION="${PHP_VERSION:-8.4}" local PHP_MODULE="${PHP_MODULE:-}" local PHP_APACHE="${PHP_APACHE:-NO}" local PHP_FPM="${PHP_FPM:-NO}" local DISTRO_ID DISTRO_CODENAME DISTRO_ID=$(awk -F= '/^ID=/{print $2}' /etc/os-release | tr -d '"') DISTRO_CODENAME=$(awk -F= '/^VERSION_CODENAME=/{print $2}' /etc/os-release) # Parse version for compatibility checks local PHP_MAJOR="${PHP_VERSION%%.*}" local PHP_MINOR="${PHP_VERSION#*.}" PHP_MINOR="${PHP_MINOR%%.*}" # Modules that are ALWAYS part of php-common (no separate package needed) # These are either built-in or virtual packages provided by php-common local BUILTIN_MODULES="calendar,ctype,exif,ffi,fileinfo,ftp,gettext,iconv,pdo,phar,posix,shmop,sockets,sysvmsg,sysvsem,sysvshm,tokenizer" # Modules that became built-in in specific PHP versions # PHP 8.5+: opcache is now part of the core local BUILTIN_85="" if [[ "$PHP_MAJOR" -gt 8 ]] || [[ "$PHP_MAJOR" -eq 8 && "$PHP_MINOR" -ge 5 ]]; then BUILTIN_85="opcache" fi # Base modules - essential for most PHP applications # Note: 'common' provides many built-in extensions local BASE_MODULES="cli,common,bcmath,curl,dom,gd,gmp,intl,mbstring,readline,xml,zip" # Add opcache only for PHP < 8.5 (it's built-in starting from 8.5) if [[ "$PHP_MAJOR" -lt 8 ]] || [[ "$PHP_MAJOR" -eq 8 && "$PHP_MINOR" -lt 5 ]]; then BASE_MODULES="${BASE_MODULES},opcache" fi # Extended default modules - commonly needed by web applications # These cover ~90% of typical use cases without bloat local EXTENDED_MODULES="mysql,sqlite3,pgsql,redis,imagick,bz2,apcu" local COMBINED_MODULES="${BASE_MODULES},${EXTENDED_MODULES}" local PHP_MEMORY_LIMIT="${PHP_MEMORY_LIMIT:-512M}" local PHP_UPLOAD_MAX_FILESIZE="${PHP_UPLOAD_MAX_FILESIZE:-128M}" local PHP_POST_MAX_SIZE="${PHP_POST_MAX_SIZE:-128M}" local PHP_MAX_EXECUTION_TIME="${PHP_MAX_EXECUTION_TIME:-300}" # Merge with user-defined modules if [[ -n "$PHP_MODULE" ]]; then COMBINED_MODULES="${COMBINED_MODULES},${PHP_MODULE}" fi # Filter out built-in modules (they don't have separate packages) local FILTERED_MODULES="" IFS=',' read -ra ALL_MODULES <<<"$COMBINED_MODULES" for mod in "${ALL_MODULES[@]}"; do mod=$(echo "$mod" | tr -d '[:space:]') [[ -z "$mod" ]] && continue # Skip if it's a known built-in module if echo ",$BUILTIN_MODULES,$BUILTIN_85," | grep -qi ",$mod,"; then continue fi # Add to filtered list if [[ -z "$FILTERED_MODULES" ]]; then FILTERED_MODULES="$mod" else FILTERED_MODULES="${FILTERED_MODULES},$mod" fi done # Deduplicate COMBINED_MODULES=$(echo "$FILTERED_MODULES" | tr ',' '\n' | awk '!seen[$0]++' | paste -sd, -) # Get current PHP-CLI version local CURRENT_PHP="" CURRENT_PHP=$(is_tool_installed "php" 2>/dev/null) || true # Remove conflicting PHP version before pinning if [[ -n "$CURRENT_PHP" && "$CURRENT_PHP" != "$PHP_VERSION" ]]; then msg_info "Removing conflicting PHP ${CURRENT_PHP} (need ${PHP_VERSION})" stop_all_services "php.*-fpm" $STD apt purge -y "php*" 2>/dev/null || true $STD apt autoremove -y 2>/dev/null || true fi # NOW create pinning for the desired version mkdir -p /etc/apt/preferences.d cat </etc/apt/preferences.d/php-pin Package: php${PHP_VERSION}* Pin: version ${PHP_VERSION}.* Pin-Priority: 1001 Package: php[0-9].* Pin: release o=packages.sury.org-php Pin-Priority: -1 EOF # Setup repository prepare_repository_setup "php" "deb.sury.org-php" || { msg_error "Failed to prepare PHP repository" return 100 } # Use different repository based on OS if [[ "$DISTRO_ID" == "ubuntu" ]]; then # Ubuntu: Use ondrej/php PPA msg_info "Adding ondrej/php PPA for Ubuntu" $STD apt install -y software-properties-common || { msg_error "Failed to install software-properties-common" return 100 } # Don't use $STD for add-apt-repository as it uses background processes add-apt-repository -y ppa:ondrej/php >>"$(get_active_logfile)" 2>&1 else # Debian: Use Sury repository manage_tool_repository "php" "$PHP_VERSION" "" "https://packages.sury.org/debsuryorg-archive-keyring.deb" || { msg_error "Failed to setup PHP repository" return 100 } fi ensure_apt_working || return 100 $STD apt update || { msg_warn "apt update failed after PHP repository setup" } # Get available PHP version from repository local AVAILABLE_PHP_VERSION="" AVAILABLE_PHP_VERSION=$(apt-cache show "php${PHP_VERSION}" 2>/dev/null | grep -m1 "^Version:" | awk '{print $2}' 2>/dev/null | cut -d- -f1 || true) if [[ -z "$AVAILABLE_PHP_VERSION" ]]; then msg_error "PHP ${PHP_VERSION} not found in configured repositories" return 100 fi # Build module list - verify each package exists before adding local MODULE_LIST="php${PHP_VERSION}" local SKIPPED_MODULES="" IFS=',' read -ra MODULES <<<"$COMBINED_MODULES" for mod in "${MODULES[@]}"; do mod=$(echo "$mod" | tr -d '[:space:]') [[ -z "$mod" ]] && continue local pkg_name="php${PHP_VERSION}-${mod}" # Check if package exists in repository if apt-cache show "$pkg_name" &>/dev/null; then MODULE_LIST+=" $pkg_name" else # Package doesn't exist - could be built-in or renamed if [[ -z "$SKIPPED_MODULES" ]]; then SKIPPED_MODULES="$mod" else SKIPPED_MODULES="${SKIPPED_MODULES}, $mod" fi fi done # Log skipped modules (informational, not an error) if [[ -n "$SKIPPED_MODULES" ]]; then msg_info "Skipping unavailable/built-in modules: $SKIPPED_MODULES" fi if [[ "$PHP_FPM" == "YES" ]]; then if apt-cache show "php${PHP_VERSION}-fpm" &>/dev/null; then MODULE_LIST+=" php${PHP_VERSION}-fpm" else msg_warn "php${PHP_VERSION}-fpm not available" fi # Create systemd override for PHP-FPM to fix runtime directory issues in LXC containers mkdir -p /etc/systemd/system/php${PHP_VERSION}-fpm.service.d/ cat </etc/systemd/system/php${PHP_VERSION}-fpm.service.d/override.conf [Service] RuntimeDirectory=php RuntimeDirectoryMode=0755 EOF $STD systemctl daemon-reload fi # install apache2 with PHP support if requested if [[ "$PHP_APACHE" == "YES" ]]; then if ! dpkg -l 2>/dev/null | grep -q "libapache2-mod-php${PHP_VERSION}"; then msg_info "Installing Apache with PHP ${PHP_VERSION} module" install_packages_with_retry "apache2" || { msg_error "Failed to install Apache" return 100 } install_packages_with_retry "libapache2-mod-php${PHP_VERSION}" || { msg_warn "Failed to install libapache2-mod-php${PHP_VERSION}, continuing without Apache module" } fi fi # Install PHP packages (pinning via preferences.d ensures correct version) msg_info "Installing PHP ${PHP_VERSION} packages" # First attempt: Install all verified packages at once if ! $STD apt install -y $MODULE_LIST 2>/dev/null; then msg_warn "Bulk installation failed, attempting individual installation" # Install main package first (critical) if ! $STD apt install -y "php${PHP_VERSION}" 2>/dev/null; then msg_error "Failed to install php${PHP_VERSION}" return 100 fi # Try to install Apache module individually if requested if [[ "$PHP_APACHE" == "YES" ]]; then $STD apt install -y "libapache2-mod-php${PHP_VERSION}" 2>/dev/null || { msg_warn "Could not install libapache2-mod-php${PHP_VERSION}" } fi # Try to install each package individually for pkg in $MODULE_LIST; do [[ "$pkg" == "php${PHP_VERSION}" ]] && continue # Already installed $STD apt install -y "$pkg" 2>/dev/null || { msg_warn "Could not install $pkg - continuing without it" } done fi cache_installed_version "php" "$PHP_VERSION" # Patch all relevant php.ini files local PHP_INI_PATHS=("/etc/php/${PHP_VERSION}/cli/php.ini") [[ "$PHP_FPM" == "YES" ]] && PHP_INI_PATHS+=("/etc/php/${PHP_VERSION}/fpm/php.ini") [[ "$PHP_APACHE" == "YES" ]] && PHP_INI_PATHS+=("/etc/php/${PHP_VERSION}/apache2/php.ini") for ini in "${PHP_INI_PATHS[@]}"; do if [[ -f "$ini" ]]; then $STD sed -i "s|^memory_limit = .*|memory_limit = ${PHP_MEMORY_LIMIT}|" "$ini" $STD sed -i "s|^upload_max_filesize = .*|upload_max_filesize = ${PHP_UPLOAD_MAX_FILESIZE}|" "$ini" $STD sed -i "s|^post_max_size = .*|post_max_size = ${PHP_POST_MAX_SIZE}|" "$ini" $STD sed -i "s|^max_execution_time = .*|max_execution_time = ${PHP_MAX_EXECUTION_TIME}|" "$ini" fi done # Patch Apache configuration if needed if [[ "$PHP_APACHE" == "YES" ]]; then for mod in $(ls /etc/apache2/mods-enabled/ 2>/dev/null | grep -E '^php[0-9]\.[0-9]\.conf$' | sed 's/\.conf//'); do if [[ "$mod" != "php${PHP_VERSION}" ]]; then $STD a2dismod "$mod" || true fi done $STD a2enmod mpm_prefork $STD a2enmod "php${PHP_VERSION}" safe_service_restart apache2 || true fi # Enable and restart PHP-FPM if requested if [[ "$PHP_FPM" == "YES" ]]; then if systemctl list-unit-files | grep -q "php${PHP_VERSION}-fpm.service"; then $STD systemctl enable php${PHP_VERSION}-fpm safe_service_restart php${PHP_VERSION}-fpm fi fi # Verify PHP installation - critical check if ! command -v php >/dev/null 2>&1; then msg_error "PHP installation verification failed - php command not found" return 127 fi local INSTALLED_VERSION=$(php -v 2>/dev/null | awk '/^PHP/{print $2}' | cut -d. -f1,2) if [[ "$INSTALLED_VERSION" != "$PHP_VERSION" ]]; then msg_error "PHP version mismatch: requested ${PHP_VERSION} but got ${INSTALLED_VERSION}" msg_error "This indicates a critical package installation issue" # Don't cache wrong version return 127 fi cache_installed_version "php" "$INSTALLED_VERSION" msg_ok "Setup PHP ${INSTALLED_VERSION}" } # ------------------------------------------------------------------------------ # Installs or upgrades PostgreSQL and optional extensions/modules. # # Description: # - By default uses distro repository (Debian/Ubuntu apt) for stability # - Optionally uses official PGDG repository for specific versions # - Detects existing PostgreSQL version # - Dumps all databases before upgrade # - Installs optional PG_MODULES (e.g. postgis, contrib, cron) # - Restores dumped data post-upgrade # # Variables: # USE_PGDG_REPO - Use official PGDG repository (default: true) # Set to "false" to use distro packages instead # PG_VERSION - Major PostgreSQL version (e.g. 15, 16) (default: 16) # PG_MODULES - Comma-separated list of modules (e.g. "postgis,contrib,cron") # # Examples: # setup_postgresql # Uses PGDG repo, PG 16 # PG_VERSION="17" setup_postgresql # Specific version from PGDG # USE_PGDG_REPO=false setup_postgresql # Uses distro package instead # PG_VERSION="17" PG_MODULES="cron" setup_postgresql # With pg_cron module # ------------------------------------------------------------------------------ # Internal helper: Configure shared_preload_libraries for pg_cron _configure_pg_cron_preload() { local modules="${1:-}" [[ -z "$modules" ]] && return 0 if [[ ",$modules," == *",cron,"* ]]; then local current_libs current_libs=$(sudo -u postgres psql -tAc "SHOW shared_preload_libraries;" 2>/dev/null || echo "") if [[ "$current_libs" != *"pg_cron"* ]]; then local new_libs="${current_libs:+${current_libs},}pg_cron" $STD sudo -u postgres psql -c "ALTER SYSTEM SET shared_preload_libraries = '${new_libs}';" $STD systemctl restart postgresql fi fi } setup_postgresql() { local PG_VERSION="${PG_VERSION:-16}" local PG_MODULES="${PG_MODULES:-}" local USE_PGDG_REPO="${USE_PGDG_REPO:-true}" local DISTRO_ID DISTRO_CODENAME DISTRO_ID=$(awk -F= '/^ID=/{print $2}' /etc/os-release | tr -d '"') DISTRO_CODENAME=$(awk -F= '/^VERSION_CODENAME=/{print $2}' /etc/os-release) # Ensure non-interactive mode for all apt operations export DEBIAN_FRONTEND=noninteractive export NEEDRESTART_MODE=a export NEEDRESTART_SUSPEND=1 # Get currently installed version local CURRENT_PG_VERSION="" if command -v psql >/dev/null; then CURRENT_PG_VERSION="$(psql -V 2>/dev/null | awk '{print $3}' | cut -d. -f1)" fi # Scenario 1: Use distro repository (default, most stable) if [[ "$USE_PGDG_REPO" != "true" && "$USE_PGDG_REPO" != "TRUE" && "$USE_PGDG_REPO" != "1" ]]; then msg_info "Setup PostgreSQL (distro package)" # If already installed, just update if [[ -n "$CURRENT_PG_VERSION" ]]; then msg_info "Update PostgreSQL $CURRENT_PG_VERSION" ensure_apt_working || return 100 upgrade_packages_with_retry "postgresql" "postgresql-client" || true cache_installed_version "postgresql" "$CURRENT_PG_VERSION" msg_ok "Update PostgreSQL $CURRENT_PG_VERSION" # Still install modules if specified if [[ -n "$PG_MODULES" ]]; then IFS=',' read -ra MODULES <<<"$PG_MODULES" for module in "${MODULES[@]}"; do $STD apt install -y "postgresql-${CURRENT_PG_VERSION}-${module}" 2>/dev/null || true done fi _configure_pg_cron_preload "$PG_MODULES" return 0 fi # Fresh install from distro repo ensure_apt_working || return 100 export DEBIAN_FRONTEND=noninteractive install_packages_with_retry "postgresql" "postgresql-client" || { msg_error "Failed to install PostgreSQL from distro repository" return 100 } # Get installed version local INSTALLED_VERSION="" if command -v psql >/dev/null; then INSTALLED_VERSION="$(psql -V 2>/dev/null | awk '{print $3}' | cut -d. -f1)" fi $STD systemctl enable --now postgresql 2>/dev/null || true # Add PostgreSQL binaries to PATH if [[ -n "$INSTALLED_VERSION" ]] && ! grep -q '/usr/lib/postgresql' /etc/environment 2>/dev/null; then echo 'PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/lib/postgresql/'"${INSTALLED_VERSION}"'/bin"' >/etc/environment fi cache_installed_version "postgresql" "${INSTALLED_VERSION:-distro}" msg_ok "Setup PostgreSQL ${INSTALLED_VERSION:-from distro}" # Install optional modules if [[ -n "$PG_MODULES" && -n "$INSTALLED_VERSION" ]]; then IFS=',' read -ra MODULES <<<"$PG_MODULES" for module in "${MODULES[@]}"; do $STD apt install -y "postgresql-${INSTALLED_VERSION}-${module}" 2>/dev/null || true done fi _configure_pg_cron_preload "$PG_MODULES" return 0 fi # Scenario 2: Use official PGDG repository (USE_PGDG_REPO=true) # Scenario 2a: Already at correct version if [[ "$CURRENT_PG_VERSION" == "$PG_VERSION" ]]; then msg_info "Update PostgreSQL $PG_VERSION" ensure_apt_working || return 100 # Perform upgrade with retry logic (non-fatal if fails) upgrade_packages_with_retry "postgresql-${PG_VERSION}" "postgresql-client-${PG_VERSION}" 2>/dev/null || true cache_installed_version "postgresql" "$PG_VERSION" msg_ok "Update PostgreSQL $PG_VERSION" # Still install modules if specified if [[ -n "$PG_MODULES" ]]; then IFS=',' read -ra MODULES <<<"$PG_MODULES" for module in "${MODULES[@]}"; do $STD apt install -y "postgresql-${PG_VERSION}-${module}" 2>/dev/null || true done fi _configure_pg_cron_preload "$PG_MODULES" return 0 fi # Scenario 2: Different version - backup, remove old, install new if [[ -n "$CURRENT_PG_VERSION" ]]; then msg_info "Upgrade PostgreSQL from $CURRENT_PG_VERSION to $PG_VERSION" msg_info "Creating backup of PostgreSQL $CURRENT_PG_VERSION databases..." local PG_BACKUP_FILE="/var/lib/postgresql/backup_$(date +%F)_v${CURRENT_PG_VERSION}.sql" $STD runuser -u postgres -- pg_dumpall >"$PG_BACKUP_FILE" || { msg_error "Failed to backup PostgreSQL databases" return 150 } $STD systemctl stop postgresql || true $STD apt purge -y "postgresql-${CURRENT_PG_VERSION}" "postgresql-client-${CURRENT_PG_VERSION}" 2>/dev/null || true else msg_info "Setup PostgreSQL $PG_VERSION" fi # Scenario 3: Fresh install or after removal - setup repo and install prepare_repository_setup "pgdg" "postgresql" || { msg_error "Failed to prepare PostgreSQL repository" return 100 } local SUITE case "$DISTRO_CODENAME" in trixie | forky | sid) if verify_repo_available "https://apt.postgresql.org/pub/repos/apt" "trixie-pgdg"; then SUITE="trixie-pgdg" else msg_warn "PGDG repo not available for ${DISTRO_CODENAME}, falling back to distro packages" USE_PGDG_REPO=false setup_postgresql return $? fi ;; *) SUITE=$(get_fallback_suite "$DISTRO_ID" "$DISTRO_CODENAME" "https://apt.postgresql.org/pub/repos/apt") SUITE="${SUITE}-pgdg" ;; esac setup_deb822_repo \ "pgdg" \ "https://www.postgresql.org/media/keys/ACCC4CF8.asc" \ "https://apt.postgresql.org/pub/repos/apt" \ "$SUITE" \ "main" if ! $STD apt update; then msg_error "APT update failed for PostgreSQL repository" return 100 fi # Install ssl-cert dependency if available if apt-cache search "^ssl-cert$" 2>/dev/null | grep -q .; then $STD apt install -y ssl-cert 2>/dev/null || true fi # Try multiple PostgreSQL package patterns with retry logic local pg_install_success=false if apt-cache search "^postgresql-${PG_VERSION}$" 2>/dev/null | grep -q . && install_packages_with_retry "postgresql-${PG_VERSION}" "postgresql-client-${PG_VERSION}"; then pg_install_success=true fi if [[ "$pg_install_success" == false ]] && apt-cache search "^postgresql-server-${PG_VERSION}$" 2>/dev/null | grep -q . && $STD apt install -y "postgresql-server-${PG_VERSION}" "postgresql-client-${PG_VERSION}" 2>/dev/null; then pg_install_success=true fi if [[ "$pg_install_success" == false ]] && apt-cache search "^postgresql$" 2>/dev/null | grep -q . && $STD apt install -y postgresql postgresql-client 2>/dev/null; then pg_install_success=true fi if [[ "$pg_install_success" == false ]]; then msg_error "PostgreSQL package not available for suite ${SUITE}" return 100 fi if ! command -v psql >/dev/null 2>&1; then msg_error "PostgreSQL installed but psql command not found" return 127 fi # Restore database backup if we upgraded from previous version if [[ -n "$CURRENT_PG_VERSION" && -n "${PG_BACKUP_FILE:-}" && -f "${PG_BACKUP_FILE}" ]]; then msg_info "Restoring PostgreSQL databases from backup..." $STD runuser -u postgres -- psql <"$PG_BACKUP_FILE" 2>/dev/null || { msg_warn "Failed to restore database backup - this may be expected for major version upgrades" } fi $STD systemctl enable --now postgresql 2>/dev/null || { msg_warn "Failed to enable/start PostgreSQL service" } # Add PostgreSQL binaries to PATH if ! grep -q '/usr/lib/postgresql' /etc/environment 2>/dev/null; then echo 'PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/lib/postgresql/'"${PG_VERSION}"'/bin"' >/etc/environment fi cache_installed_version "postgresql" "$PG_VERSION" msg_ok "Setup PostgreSQL $PG_VERSION" # Install optional modules if [[ -n "$PG_MODULES" ]]; then IFS=',' read -ra MODULES <<<"$PG_MODULES" for module in "${MODULES[@]}"; do $STD apt install -y "postgresql-${PG_VERSION}-${module}" 2>/dev/null || { msg_warn "Failed to install PostgreSQL module: ${module}" } done fi _configure_pg_cron_preload "$PG_MODULES" } # ------------------------------------------------------------------------------ # Creates PostgreSQL database with user and optional extensions # # Description: # - Creates PostgreSQL role with login and password # - Creates database with UTF8 encoding and template0 # - Installs optional extensions (postgis, pgvector, etc.) # - Configures ALTER ROLE settings for Django/Rails compatibility # - Saves credentials to file # - Exports variables for use in calling script # # Usage: # PG_DB_NAME="myapp_db" PG_DB_USER="myapp_user" setup_postgresql_db # PG_DB_NAME="immich" PG_DB_USER="immich" PG_DB_EXTENSIONS="pgvector" setup_postgresql_db # PG_DB_NAME="ghostfolio" PG_DB_USER="ghostfolio" PG_DB_GRANT_SUPERUSER="true" setup_postgresql_db # PG_DB_NAME="adventurelog" PG_DB_USER="adventurelog" PG_DB_EXTENSIONS="postgis" setup_postgresql_db # PG_DB_NAME="splitpro" PG_DB_USER="splitpro" PG_DB_EXTENSIONS="pg_cron" setup_postgresql_db # # Variables: # PG_DB_NAME - Database name (required) # PG_DB_USER - Database user (required) # PG_DB_PASS - Database password (optional, auto-generated if empty) # PG_DB_EXTENSIONS - Comma-separated list of extensions (optional, e.g. "postgis,pgvector") # PG_DB_GRANT_SUPERUSER - Grant SUPERUSER privilege (optional, "true" to enable, security risk!) # PG_DB_SCHEMA_PERMS - Grant schema-level permissions (optional, "true" to enable) # PG_DB_SKIP_ALTER_ROLE - Skip ALTER ROLE settings (optional, "true" to skip) # PG_DB_CREDS_FILE - Credentials file path (optional, default: ~/${APPLICATION}.creds) # # Exports: # PG_DB_NAME, PG_DB_USER, PG_DB_PASS - For use in calling script # ------------------------------------------------------------------------------ function setup_postgresql_db() { # Validation if [[ -z "${PG_DB_NAME:-}" || -z "${PG_DB_USER:-}" ]]; then msg_error "PG_DB_NAME and PG_DB_USER must be set before calling setup_postgresql_db" return 65 fi # Generate password if not provided if [[ -z "${PG_DB_PASS:-}" ]]; then PG_DB_PASS=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | head -c13) fi msg_info "Setting up PostgreSQL Database" $STD sudo -u postgres psql -c "CREATE ROLE $PG_DB_USER WITH LOGIN PASSWORD '$PG_DB_PASS';" $STD sudo -u postgres psql -c "CREATE DATABASE $PG_DB_NAME WITH OWNER $PG_DB_USER ENCODING 'UTF8' TEMPLATE template0;" # Configure pg_cron database BEFORE creating the extension (must be set before pg_cron loads) if [[ -n "${PG_DB_EXTENSIONS:-}" ]] && [[ ",${PG_DB_EXTENSIONS//[[:space:]]/}," == *",pg_cron,"* ]]; then $STD sudo -u postgres psql -c "ALTER SYSTEM SET cron.database_name = '${PG_DB_NAME}';" $STD sudo -u postgres psql -c "ALTER SYSTEM SET cron.timezone = 'UTC';" $STD systemctl restart postgresql fi # Install extensions (comma-separated) if [[ -n "${PG_DB_EXTENSIONS:-}" ]]; then IFS=',' read -ra EXT_LIST <<<"${PG_DB_EXTENSIONS:-}" for ext in "${EXT_LIST[@]}"; do ext=$(echo "$ext" | xargs) # Trim whitespace $STD sudo -u postgres psql -d "$PG_DB_NAME" -c "CREATE EXTENSION IF NOT EXISTS $ext;" done fi # Grant pg_cron schema permissions to DB user if [[ -n "${PG_DB_EXTENSIONS:-}" ]] && [[ ",${PG_DB_EXTENSIONS//[[:space:]]/}," == *",pg_cron,"* ]]; then $STD sudo -u postgres psql -d "$PG_DB_NAME" -c "GRANT USAGE ON SCHEMA cron TO ${PG_DB_USER};" $STD sudo -u postgres psql -d "$PG_DB_NAME" -c "GRANT ALL ON ALL TABLES IN SCHEMA cron TO ${PG_DB_USER};" fi # ALTER ROLE settings for Django/Rails compatibility (unless skipped) if [[ "${PG_DB_SKIP_ALTER_ROLE:-}" != "true" ]]; then $STD sudo -u postgres psql -c "ALTER ROLE $PG_DB_USER SET client_encoding TO 'utf8';" $STD sudo -u postgres psql -c "ALTER ROLE $PG_DB_USER SET default_transaction_isolation TO 'read committed';" $STD sudo -u postgres psql -c "ALTER ROLE $PG_DB_USER SET timezone TO 'UTC';" fi # Schema permissions (if requested) if [[ "${PG_DB_SCHEMA_PERMS:-}" == "true" ]]; then $STD sudo -u postgres psql -c "GRANT ALL PRIVILEGES ON DATABASE $PG_DB_NAME TO $PG_DB_USER;" $STD sudo -u postgres psql -c "ALTER USER $PG_DB_USER CREATEDB;" $STD sudo -u postgres psql -d "$PG_DB_NAME" -c "GRANT ALL ON SCHEMA public TO $PG_DB_USER;" $STD sudo -u postgres psql -d "$PG_DB_NAME" -c "GRANT CREATE ON SCHEMA public TO $PG_DB_USER;" $STD sudo -u postgres psql -d "$PG_DB_NAME" -c "ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON TABLES TO $PG_DB_USER;" $STD sudo -u postgres psql -d "$PG_DB_NAME" -c "ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON SEQUENCES TO $PG_DB_USER;" fi # Superuser grant (if requested - WARNING!) if [[ "${PG_DB_GRANT_SUPERUSER:-}" == "true" ]]; then msg_warn "Granting SUPERUSER privilege (security risk!)" $STD sudo -u postgres psql -c "GRANT ALL PRIVILEGES ON DATABASE $PG_DB_NAME to $PG_DB_USER;" $STD sudo -u postgres psql -c "ALTER USER $PG_DB_USER WITH SUPERUSER;" fi # Save credentials local app_name="${APPLICATION,,}" local CREDS_FILE="${PG_DB_CREDS_FILE:-${HOME}/${app_name}.creds}" { echo "PostgreSQL Credentials" echo "Database: $PG_DB_NAME" echo "User: $PG_DB_USER" echo "Password: $PG_DB_PASS" } >>"$CREDS_FILE" msg_ok "Set up PostgreSQL Database" # Export for use in calling script export PG_DB_NAME export PG_DB_USER export PG_DB_PASS } # ------------------------------------------------------------------------------ # Installs rbenv and ruby-build, installs Ruby and optionally Rails. # # Description: # - Downloads rbenv and ruby-build from GitHub # - Compiles and installs target Ruby version # - Optionally installs Rails via gem # # Variables: # RUBY_VERSION - Ruby version to install (default: 3.4.4) # RUBY_INSTALL_RAILS - true/false to install Rails (default: true) # ------------------------------------------------------------------------------ function setup_ruby() { local RUBY_VERSION="${RUBY_VERSION:-3.4.4}" local RUBY_INSTALL_RAILS="${RUBY_INSTALL_RAILS:-true}" local RBENV_DIR="$HOME/.rbenv" local RBENV_BIN="$RBENV_DIR/bin/rbenv" local PROFILE_FILE="$HOME/.profile" local TMP_DIR=$(mktemp -d) # Get currently installed Ruby version local CURRENT_RUBY_VERSION="" if [[ -x "$RBENV_BIN" ]]; then CURRENT_RUBY_VERSION=$("$RBENV_BIN" global 2>/dev/null || echo "") fi # Scenario 1: Already at correct Ruby version if [[ "$CURRENT_RUBY_VERSION" == "$RUBY_VERSION" ]]; then msg_info "Update Ruby $RUBY_VERSION" cache_installed_version "ruby" "$RUBY_VERSION" msg_ok "Update Ruby $RUBY_VERSION" return 0 fi # Scenario 2: Different version - reinstall if [[ -n "$CURRENT_RUBY_VERSION" ]]; then msg_info "Upgrade Ruby from $CURRENT_RUBY_VERSION to $RUBY_VERSION" else msg_info "Setup Ruby $RUBY_VERSION" fi ensure_apt_working || return 100 # Install build dependencies with fallbacks local ruby_deps=() local dep_variations=( "jq" "autoconf" "patch" "build-essential" "libssl-dev" "libyaml-dev" "libreadline-dev|libreadline6-dev" "zlib1g-dev" "libgmp-dev" "libncurses-dev|libncurses5-dev" "libffi-dev" "libgdbm-dev" "libdb-dev" "uuid-dev" ) for dep_pattern in "${dep_variations[@]}"; do if [[ "$dep_pattern" == *"|"* ]]; then IFS='|' read -ra variations <<<"$dep_pattern" for var in "${variations[@]}"; do if apt-cache search "^${var}$" 2>/dev/null | grep -q .; then ruby_deps+=("$var") break fi done else if apt-cache search "^${dep_pattern}$" 2>/dev/null | grep -q .; then ruby_deps+=("$dep_pattern") fi fi done if [[ ${#ruby_deps[@]} -gt 0 ]]; then $STD apt install -y "${ruby_deps[@]}" 2>/dev/null || true else msg_error "No Ruby build dependencies available" rm -rf "$TMP_DIR" return 100 fi # Download and build rbenv if needed if [[ ! -x "$RBENV_BIN" ]]; then local RBENV_RELEASE local rbenv_json rbenv_json=$(curl -fsSL --max-time 15 https://api.github.com/repos/rbenv/rbenv/releases/latest 2>/dev/null || echo "") if [[ -z "$rbenv_json" ]]; then msg_error "Failed to fetch latest rbenv version from GitHub" rm -rf "$TMP_DIR" return 7 fi RBENV_RELEASE=$(echo "$rbenv_json" | jq -r '.tag_name' 2>/dev/null | sed 's/^v//' || echo "") if [[ -z "$RBENV_RELEASE" ]]; then msg_error "Could not parse rbenv version from GitHub response" rm -rf "$TMP_DIR" return 250 fi if ! curl_with_retry "https://github.com/rbenv/rbenv/archive/refs/tags/v${RBENV_RELEASE}.tar.gz" "$TMP_DIR/rbenv.tar.gz"; then msg_error "Failed to download rbenv" rm -rf "$TMP_DIR" return 7 fi tar -xzf "$TMP_DIR/rbenv.tar.gz" -C "$TMP_DIR" || { msg_error "Failed to extract rbenv" rm -rf "$TMP_DIR" return 251 } mkdir -p "$RBENV_DIR" cp -r "$TMP_DIR/rbenv-${RBENV_RELEASE}/." "$RBENV_DIR/" (cd "$RBENV_DIR" && src/configure && $STD make -C src) || { msg_error "Failed to build rbenv" rm -rf "$TMP_DIR" return 150 } # Setup profile if ! grep -q 'rbenv init' "$PROFILE_FILE" 2>/dev/null; then echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >>"$PROFILE_FILE" echo 'eval "$(rbenv init -)"' >>"$PROFILE_FILE" fi fi # Install ruby-build plugin if [[ ! -d "$RBENV_DIR/plugins/ruby-build" ]]; then local RUBY_BUILD_RELEASE local ruby_build_json ruby_build_json=$(curl -fsSL --max-time 15 https://api.github.com/repos/rbenv/ruby-build/releases/latest 2>/dev/null || echo "") if [[ -z "$ruby_build_json" ]]; then msg_error "Failed to fetch latest ruby-build version from GitHub" rm -rf "$TMP_DIR" return 7 fi RUBY_BUILD_RELEASE=$(echo "$ruby_build_json" | jq -r '.tag_name' 2>/dev/null | sed 's/^v//' || echo "") if [[ -z "$RUBY_BUILD_RELEASE" ]]; then msg_error "Could not parse ruby-build version from GitHub response" rm -rf "$TMP_DIR" return 250 fi if ! curl_with_retry "https://github.com/rbenv/ruby-build/archive/refs/tags/v${RUBY_BUILD_RELEASE}.tar.gz" "$TMP_DIR/ruby-build.tar.gz"; then msg_error "Failed to download ruby-build" rm -rf "$TMP_DIR" return 7 fi tar -xzf "$TMP_DIR/ruby-build.tar.gz" -C "$TMP_DIR" || { msg_error "Failed to extract ruby-build" rm -rf "$TMP_DIR" return 251 } mkdir -p "$RBENV_DIR/plugins/ruby-build" cp -r "$TMP_DIR/ruby-build-${RUBY_BUILD_RELEASE}/." "$RBENV_DIR/plugins/ruby-build/" fi # Setup PATH and install Ruby version export PATH="$RBENV_DIR/bin:$PATH" eval "$("$RBENV_BIN" init - bash)" 2>/dev/null || true if ! "$RBENV_BIN" versions --bare 2>/dev/null | grep -qx "$RUBY_VERSION"; then $STD "$RBENV_BIN" install "$RUBY_VERSION" || { msg_error "Failed to install Ruby $RUBY_VERSION" rm -rf "$TMP_DIR" return 150 } fi "$RBENV_BIN" global "$RUBY_VERSION" || { msg_error "Failed to set Ruby $RUBY_VERSION as global version" rm -rf "$TMP_DIR" return 150 } hash -r # Install Rails if requested if [[ "$RUBY_INSTALL_RAILS" == "true" ]]; then $STD gem install rails || { msg_warn "Failed to install Rails - Ruby installation successful" } fi rm -rf "$TMP_DIR" cache_installed_version "ruby" "$RUBY_VERSION" msg_ok "Setup Ruby $RUBY_VERSION" } # ------------------------------------------------------------------------------ # Installs or updates MeiliSearch search engine. # # Description: # - Fresh install: Downloads binary, creates config/service, starts # - Update: Checks for new release, updates binary if available # - Waits for service to be ready before returning # - Exports API keys for use by caller # # Variables: # MEILISEARCH_BIND - Bind address (default: 127.0.0.1:7700) # MEILISEARCH_ENV - Environment: production/development (default: production) # MEILISEARCH_DB_PATH - Database path (default: /var/lib/meilisearch/data) # # Exports: # MEILISEARCH_MASTER_KEY - The master key for admin access # MEILISEARCH_API_KEY - The default search API key # MEILISEARCH_API_KEY_UID - The UID of the default API key # # Example (install script): # setup_meilisearch # # Example (CT update_script): # setup_meilisearch # ------------------------------------------------------------------------------ function setup_meilisearch() { local MEILISEARCH_BIND="${MEILISEARCH_BIND:-127.0.0.1:7700}" local MEILISEARCH_ENV="${MEILISEARCH_ENV:-production}" local MEILISEARCH_DB_PATH="${MEILISEARCH_DB_PATH:-/var/lib/meilisearch/data}" local MEILISEARCH_DUMP_DIR="${MEILISEARCH_DUMP_DIR:-/var/lib/meilisearch/dumps}" local MEILISEARCH_SNAPSHOT_DIR="${MEILISEARCH_SNAPSHOT_DIR:-/var/lib/meilisearch/snapshots}" # Get bind address for health checks local MEILISEARCH_HOST="${MEILISEARCH_BIND%%:*}" local MEILISEARCH_PORT="${MEILISEARCH_BIND##*:}" [[ "$MEILISEARCH_HOST" == "0.0.0.0" ]] && MEILISEARCH_HOST="127.0.0.1" # Update mode: MeiliSearch already installed if [[ -f /usr/bin/meilisearch ]]; then if check_for_gh_release "meilisearch" "meilisearch/meilisearch"; then msg_info "Updating MeiliSearch" # Get current and new version for compatibility check local CURRENT_VERSION NEW_VERSION CURRENT_VERSION=$(/usr/bin/meilisearch --version 2>/dev/null | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1) || CURRENT_VERSION="0.0.0" NEW_VERSION="${CHECK_UPDATE_RELEASE#v}" # Extract major.minor for comparison (Meilisearch requires dump/restore between minor versions) local CURRENT_MAJOR_MINOR NEW_MAJOR_MINOR CURRENT_MAJOR_MINOR=$(echo "$CURRENT_VERSION" | cut -d. -f1,2) NEW_MAJOR_MINOR=$(echo "$NEW_VERSION" | cut -d. -f1,2) # Determine if migration is needed (different major.minor = incompatible DB format) local NEEDS_MIGRATION=false if [[ "$CURRENT_MAJOR_MINOR" != "$NEW_MAJOR_MINOR" ]]; then NEEDS_MIGRATION=true msg_info "MeiliSearch version change detected (${CURRENT_VERSION} → ${NEW_VERSION}), preparing data migration" fi # Read config values for dump/restore local MEILI_HOST MEILI_PORT MEILI_MASTER_KEY MEILI_DUMP_DIR MEILI_HOST="${MEILISEARCH_HOST:-127.0.0.1}" MEILI_PORT="${MEILISEARCH_PORT:-7700}" MEILI_DUMP_DIR="${MEILISEARCH_DUMP_DIR:-/var/lib/meilisearch/dumps}" MEILI_MASTER_KEY=$(grep -E "^master_key\s*=" /etc/meilisearch.toml 2>/dev/null | sed 's/.*=\s*"\(.*\)"/\1/' | tr -d ' ' || true) # Create dump before update if migration is needed local DUMP_UID="" if [[ "$NEEDS_MIGRATION" == "true" ]] && [[ -n "$MEILI_MASTER_KEY" ]]; then msg_info "Creating MeiliSearch data dump before upgrade" # Trigger dump creation local DUMP_RESPONSE DUMP_RESPONSE=$(curl -s -X POST "http://${MEILI_HOST}:${MEILI_PORT}/dumps" \ -H "Authorization: Bearer ${MEILI_MASTER_KEY}" \ -H "Content-Type: application/json" 2>/dev/null) || true # The initial response only contains taskUid, not dumpUid # dumpUid is only available after the task completes local TASK_UID TASK_UID=$(echo "$DUMP_RESPONSE" | grep -oP '"taskUid":\s*\K[0-9]+' || true) if [[ -n "$TASK_UID" ]]; then msg_info "Waiting for dump task ${TASK_UID} to complete..." local MAX_WAIT=120 local WAITED=0 local TASK_RESULT="" while [[ $WAITED -lt $MAX_WAIT ]]; do TASK_RESULT=$(curl -s "http://${MEILI_HOST}:${MEILI_PORT}/tasks/${TASK_UID}" \ -H "Authorization: Bearer ${MEILI_MASTER_KEY}" 2>/dev/null) || true local TASK_STATUS TASK_STATUS=$(echo "$TASK_RESULT" | grep -oP '"status":\s*"\K[^"]+' || true) if [[ "$TASK_STATUS" == "succeeded" ]]; then # Extract dumpUid from the completed task details DUMP_UID=$(echo "$TASK_RESULT" | grep -oP '"dumpUid":\s*"\K[^"]+' || true) if [[ -n "$DUMP_UID" ]]; then msg_ok "MeiliSearch dump created successfully: ${DUMP_UID}" else msg_warn "Dump task succeeded but could not extract dumpUid" fi break elif [[ "$TASK_STATUS" == "failed" ]]; then local ERROR_MSG ERROR_MSG=$(echo "$TASK_RESULT" | grep -oP '"message":\s*"\K[^"]+' || echo "Unknown error") msg_warn "MeiliSearch dump failed: ${ERROR_MSG}" break fi sleep 2 WAITED=$((WAITED + 2)) done if [[ $WAITED -ge $MAX_WAIT ]]; then msg_warn "MeiliSearch dump timed out after ${MAX_WAIT}s" fi else msg_warn "Could not trigger MeiliSearch dump (no taskUid in response)" msg_info "Response was: ${DUMP_RESPONSE:-empty}" fi fi # If migration is needed but dump failed, we have options: # 1. Abort the update (safest, but annoying) # 2. Backup data directory and proceed (allows manual recovery) # 3. Just proceed and hope for the best (dangerous) # We choose option 2: backup and proceed with warning if [[ "$NEEDS_MIGRATION" == "true" ]] && [[ -z "$DUMP_UID" ]]; then local MEILI_DB_PATH MEILI_DB_PATH=$(grep -E "^db_path\s*=" /etc/meilisearch.toml 2>/dev/null | sed 's/.*=\s*"\(.*\)"/\1/' | tr -d ' ' || true) MEILI_DB_PATH="${MEILI_DB_PATH:-/var/lib/meilisearch/data}" if [[ -d "$MEILI_DB_PATH" ]] && [[ -n "$(ls -A "$MEILI_DB_PATH" 2>/dev/null)" ]]; then local BACKUP_PATH="${MEILI_DB_PATH}.backup.$(date +%Y%m%d%H%M%S)" msg_warn "Backing up MeiliSearch data to ${BACKUP_PATH}" mv "$MEILI_DB_PATH" "$BACKUP_PATH" mkdir -p "$MEILI_DB_PATH" msg_info "Data backed up. After update, you may need to reindex your data." msg_info "Old data is preserved at: ${BACKUP_PATH}" fi fi # Stop service and update binary systemctl stop meilisearch fetch_and_deploy_gh_release "meilisearch" "meilisearch/meilisearch" "binary" # If migration needed and dump was created, remove old data and import dump if [[ "$NEEDS_MIGRATION" == "true" ]] && [[ -n "$DUMP_UID" ]]; then local MEILI_DB_PATH MEILI_DB_PATH=$(grep -E "^db_path\s*=" /etc/meilisearch.toml 2>/dev/null | sed 's/.*=\s*"\(.*\)"/\1/' | tr -d ' ' || true) MEILI_DB_PATH="${MEILI_DB_PATH:-/var/lib/meilisearch/data}" msg_info "Removing old MeiliSearch database for migration" rm -rf "${MEILI_DB_PATH:?}"/* # Import dump using CLI flag (this is the supported method) local DUMP_FILE="${MEILI_DUMP_DIR}/${DUMP_UID}.dump" if [[ -f "$DUMP_FILE" ]]; then msg_info "Importing dump: ${DUMP_FILE}" # Start meilisearch with --import-dump flag # This is a one-time import that happens during startup /usr/bin/meilisearch --config-file-path /etc/meilisearch.toml --import-dump "$DUMP_FILE" >/dev/null 2>&1 & local MEILI_PID=$! # Wait for meilisearch to become healthy (import happens during startup) msg_info "Waiting for MeiliSearch to import and start..." local MAX_WAIT=300 local WAITED=0 while [[ $WAITED -lt $MAX_WAIT ]]; do if curl -sf "http://${MEILI_HOST}:${MEILI_PORT}/health" &>/dev/null; then msg_ok "MeiliSearch is healthy after import" break fi # Check if process is still running if ! kill -0 $MEILI_PID 2>/dev/null; then msg_warn "MeiliSearch process exited during import" break fi sleep 3 WAITED=$((WAITED + 3)) done # Stop the manual process kill $MEILI_PID 2>/dev/null || true wait $MEILI_PID 2>/dev/null || true sleep 2 # Start via systemd for proper management systemctl start meilisearch if systemctl is-active --quiet meilisearch; then msg_ok "MeiliSearch migrated successfully" else msg_warn "MeiliSearch failed to start after migration - check logs with: journalctl -u meilisearch" fi else msg_warn "Dump file not found: ${DUMP_FILE}" systemctl start meilisearch fi else systemctl start meilisearch fi msg_ok "Updated MeiliSearch" fi return 0 fi # Fresh install msg_info "Setup MeiliSearch" # Install binary fetch_and_deploy_gh_release "meilisearch" "meilisearch/meilisearch" "binary" || { msg_error "Failed to install MeiliSearch binary" return 250 } # Download default config curl -fsSL https://raw.githubusercontent.com/meilisearch/meilisearch/latest/config.toml -o /etc/meilisearch.toml || { msg_error "Failed to download MeiliSearch config" return 7 } # Generate master key MEILISEARCH_MASTER_KEY=$(openssl rand -base64 12) export MEILISEARCH_MASTER_KEY # Configure sed -i \ -e "s|^env =.*|env = \"${MEILISEARCH_ENV}\"|" \ -e "s|^# master_key =.*|master_key = \"${MEILISEARCH_MASTER_KEY}\"|" \ -e "s|^db_path =.*|db_path = \"${MEILISEARCH_DB_PATH}\"|" \ -e "s|^dump_dir =.*|dump_dir = \"${MEILISEARCH_DUMP_DIR}\"|" \ -e "s|^snapshot_dir =.*|snapshot_dir = \"${MEILISEARCH_SNAPSHOT_DIR}\"|" \ -e 's|^# no_analytics = true|no_analytics = true|' \ -e "s|^http_addr =.*|http_addr = \"${MEILISEARCH_BIND}\"|" \ /etc/meilisearch.toml # Create data directories mkdir -p "${MEILISEARCH_DB_PATH}" "${MEILISEARCH_DUMP_DIR}" "${MEILISEARCH_SNAPSHOT_DIR}" # Create systemd service cat </etc/systemd/system/meilisearch.service [Unit] Description=Meilisearch After=network.target [Service] ExecStart=/usr/bin/meilisearch --config-file-path /etc/meilisearch.toml Restart=always [Install] WantedBy=multi-user.target EOF # Enable and start service systemctl daemon-reload systemctl enable -q --now meilisearch # Wait for MeiliSearch to be ready (up to 30 seconds) for i in {1..30}; do if curl -s -o /dev/null -w "%{http_code}" "http://${MEILISEARCH_HOST}:${MEILISEARCH_PORT}/health" 2>/dev/null | grep -q "200"; then break fi sleep 1 done # Verify service is running if ! systemctl is-active --quiet meilisearch; then msg_error "MeiliSearch service failed to start" return 150 fi # Get API keys with retry logic MEILISEARCH_API_KEY="" for i in {1..10}; do MEILISEARCH_API_KEY=$(curl -s -X GET "http://${MEILISEARCH_HOST}:${MEILISEARCH_PORT}/keys" \ -H "Authorization: Bearer ${MEILISEARCH_MASTER_KEY}" 2>/dev/null | grep -o '"key":"[^"]*"' | head -n 1 | sed 's/"key":"//;s/"//') || true [[ -n "$MEILISEARCH_API_KEY" ]] && break sleep 2 done MEILISEARCH_API_KEY_UID=$(curl -s -X GET "http://${MEILISEARCH_HOST}:${MEILISEARCH_PORT}/keys" \ -H "Authorization: Bearer ${MEILISEARCH_MASTER_KEY}" 2>/dev/null | grep -o '"uid":"[^"]*"' | head -n 1 | sed 's/"uid":"//;s/"//') || true export MEILISEARCH_API_KEY export MEILISEARCH_API_KEY_UID # Cache version local MEILISEARCH_VERSION MEILISEARCH_VERSION=$(/usr/bin/meilisearch --version 2>/dev/null | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1) || true cache_installed_version "meilisearch" "${MEILISEARCH_VERSION:-unknown}" msg_ok "Setup MeiliSearch ${MEILISEARCH_VERSION:-}" } # ------------------------------------------------------------------------------ # Installs or upgrades ClickHouse database server. # # Description: # - Adds ClickHouse official repository # - Installs specified version # - Configures systemd service # - Supports Debian/Ubuntu with fallback mechanism # # Variables: # CLICKHOUSE_VERSION - ClickHouse version to install (default: latest) # ------------------------------------------------------------------------------ function setup_clickhouse() { local CLICKHOUSE_VERSION="${CLICKHOUSE_VERSION:-latest}" local DISTRO_ID DISTRO_CODENAME DISTRO_ID=$(awk -F= '/^ID=/{print $2}' /etc/os-release | tr -d '"') DISTRO_CODENAME=$(awk -F= '/^VERSION_CODENAME=/{print $2}' /etc/os-release) # Ensure non-interactive mode for all apt operations export DEBIAN_FRONTEND=noninteractive export NEEDRESTART_MODE=a export NEEDRESTART_SUSPEND=1 # Resolve "latest" version if [[ "$CLICKHOUSE_VERSION" == "latest" ]]; then CLICKHOUSE_VERSION=$(curl -fsSL --max-time 15 https://packages.clickhouse.com/tgz/stable/ 2>/dev/null | grep -oP 'clickhouse-common-static-\K[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+' | sort -V | tail -n1 || echo "") # Fallback to GitHub API if package server failed if [[ -z "$CLICKHOUSE_VERSION" ]]; then CLICKHOUSE_VERSION=$(curl -fsSL --max-time 15 https://api.github.com/repos/ClickHouse/ClickHouse/releases/latest 2>/dev/null | grep -oP '"tag_name":\s*"v\K[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+' | head -n1 || echo "") fi [[ -z "$CLICKHOUSE_VERSION" ]] && { msg_error "Could not determine latest ClickHouse version from any source" return 250 } fi # Get currently installed version local CURRENT_VERSION="" if command -v clickhouse-server >/dev/null 2>&1; then CURRENT_VERSION=$(clickhouse-server --version 2>/dev/null | grep -oP 'version \K[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+' | head -n1) fi # Scenario 1: Already at target version - just update packages if [[ -n "$CURRENT_VERSION" && "$CURRENT_VERSION" == "$CLICKHOUSE_VERSION" ]]; then msg_info "Update ClickHouse $CLICKHOUSE_VERSION" ensure_apt_working || return 100 # Perform upgrade with retry logic (non-fatal if fails) upgrade_packages_with_retry "clickhouse-server" "clickhouse-client" || { msg_warn "ClickHouse package upgrade had issues, continuing with current version" } cache_installed_version "clickhouse" "$CLICKHOUSE_VERSION" msg_ok "Update ClickHouse $CLICKHOUSE_VERSION" return 0 fi # Scenario 2: Different version - clean upgrade if [[ -n "$CURRENT_VERSION" && "$CURRENT_VERSION" != "$CLICKHOUSE_VERSION" ]]; then msg_info "Upgrade ClickHouse from $CURRENT_VERSION to $CLICKHOUSE_VERSION" stop_all_services "clickhouse-server" remove_old_tool_version "clickhouse" else msg_info "Setup ClickHouse $CLICKHOUSE_VERSION" fi ensure_dependencies apt-transport-https ca-certificates dirmngr gnupg # Prepare repository (cleanup + validation) prepare_repository_setup "clickhouse" || { msg_error "Failed to prepare ClickHouse repository" return 100 } # Setup repository (ClickHouse uses 'stable' suite) setup_deb822_repo \ "clickhouse" \ "https://packages.clickhouse.com/rpm/lts/repodata/repomd.xml.key" \ "https://packages.clickhouse.com/deb" \ "stable" \ "main" # Install packages with retry logic $STD apt update || { msg_error "APT update failed for ClickHouse repository" return 100 } install_packages_with_retry "clickhouse-server" "clickhouse-client" || { msg_error "Failed to install ClickHouse packages" return 100 } # Verify installation if ! command -v clickhouse-server >/dev/null 2>&1; then msg_error "ClickHouse installation completed but clickhouse-server command not found" return 127 fi # Setup data directory mkdir -p /var/lib/clickhouse if id clickhouse >/dev/null 2>&1; then chown -R clickhouse:clickhouse /var/lib/clickhouse fi # Enable and start service $STD systemctl enable clickhouse-server || { msg_warn "Failed to enable clickhouse-server service" } safe_service_restart clickhouse-server || true cache_installed_version "clickhouse" "$CLICKHOUSE_VERSION" msg_ok "Setup ClickHouse $CLICKHOUSE_VERSION" } # ------------------------------------------------------------------------------ # Installs Rust toolchain and optional global crates via cargo. # # Description: # - Installs rustup (if missing) # - Installs or updates desired Rust toolchain (stable, nightly, or versioned) # - Installs or updates specified global crates using `cargo install` # # Notes: # - Skips crate install if exact version is already present # - Updates crate if newer version or different version is requested # # Variables: # RUST_TOOLCHAIN - Rust toolchain to install (default: stable) # RUST_CRATES - Comma-separated list of crates (e.g. "cargo-edit,wasm-pack@0.12.1") # ------------------------------------------------------------------------------ function setup_rust() { local RUST_TOOLCHAIN="${RUST_TOOLCHAIN:-stable}" local RUST_CRATES="${RUST_CRATES:-}" local CARGO_BIN="${HOME}/.cargo/bin" # Get currently installed version local CURRENT_VERSION="" if command -v rustc &>/dev/null; then CURRENT_VERSION=$(rustc --version 2>/dev/null | awk '{print $2}' 2>/dev/null) || true fi # Scenario 1: Rustup not installed - fresh install if ! command -v rustup &>/dev/null; then msg_info "Setup Rust ($RUST_TOOLCHAIN)" curl -fsSL https://sh.rustup.rs | $STD sh -s -- -y --default-toolchain "$RUST_TOOLCHAIN" || { msg_error "Failed to install Rust" return 7 } export PATH="$CARGO_BIN:$PATH" echo 'export PATH="$HOME/.cargo/bin:$PATH"' >>"$HOME/.profile" # Verify installation if ! command -v rustc >/dev/null 2>&1; then msg_error "Rust binary not found after installation" return 127 fi local RUST_VERSION RUST_VERSION=$(rustc --version 2>/dev/null | awk '{print $2}' 2>/dev/null) || true if [[ -z "$RUST_VERSION" ]]; then msg_error "Failed to determine Rust version" return 250 fi cache_installed_version "rust" "$RUST_VERSION" msg_ok "Setup Rust $RUST_VERSION" else # Scenario 2: Rustup already installed - update/maintain msg_info "Update Rust ($RUST_TOOLCHAIN)" # Ensure default toolchain is set $STD rustup default "$RUST_TOOLCHAIN" 2>/dev/null || { # If default fails, install the toolchain first $STD rustup install "$RUST_TOOLCHAIN" || { msg_error "Failed to install Rust toolchain $RUST_TOOLCHAIN" return 150 } $STD rustup default "$RUST_TOOLCHAIN" || { msg_error "Failed to set default Rust toolchain" return 150 } } # Update to latest patch version $STD rustup update "$RUST_TOOLCHAIN" /dev/null | awk '{print $2}' 2>/dev/null) || true if [[ -z "$RUST_VERSION" ]]; then msg_error "Failed to determine Rust version after update" return 250 fi cache_installed_version "rust" "$RUST_VERSION" msg_ok "Update Rust $RUST_VERSION" fi # Install global crates if [[ -n "$RUST_CRATES" ]]; then msg_info "Processing Rust crates: $RUST_CRATES" IFS=',' read -ra CRATES <<<"$RUST_CRATES" for crate in "${CRATES[@]}"; do crate=$(echo "$crate" | xargs) # trim whitespace [[ -z "$crate" ]] && continue # skip empty entries local NAME VER INSTALLED_VER CRATE_LIST if [[ "$crate" == *"@"* ]]; then NAME="${crate%@*}" VER="${crate##*@}" else NAME="$crate" VER="" fi # Get list of installed crates once CRATE_LIST=$(cargo install --list 2>/dev/null || echo "") # Check if already installed if echo "$CRATE_LIST" | grep -q "^${NAME} "; then INSTALLED_VER=$(echo "$CRATE_LIST" | grep "^${NAME} " | head -1 | awk '{print $2}' 2>/dev/null | tr -d 'v:' || echo '') if [[ -n "$VER" && "$VER" != "$INSTALLED_VER" ]]; then msg_info "Upgrading $NAME from v$INSTALLED_VER to v$VER" $STD cargo install "$NAME" --version "$VER" --force || { msg_error "Failed to install $NAME@$VER" return 150 } msg_ok "Upgraded $NAME to v$VER" elif [[ -z "$VER" ]]; then msg_info "Upgrading $NAME to latest" $STD cargo install "$NAME" --force || { msg_error "Failed to upgrade $NAME" return 150 } local NEW_VER=$(cargo install --list 2>/dev/null | grep "^${NAME} " | head -1 | awk '{print $2}' 2>/dev/null | tr -d 'v:' || echo 'unknown') msg_ok "Upgraded $NAME to v$NEW_VER" else msg_ok "$NAME v$INSTALLED_VER already installed" fi else msg_info "Installing $NAME${VER:+@$VER}" if [[ -n "$VER" ]]; then $STD cargo install "$NAME" --version "$VER" || { msg_error "Failed to install $NAME@$VER" return 150 } msg_ok "Installed $NAME v$VER" else $STD cargo install "$NAME" || { msg_error "Failed to install $NAME" return 150 } local NEW_VER=$(cargo install --list 2>/dev/null | grep "^${NAME} " | head -1 | awk '{print $2}' 2>/dev/null | tr -d 'v:' || echo 'unknown') msg_ok "Installed $NAME v$NEW_VER" fi fi done msg_ok "Processed Rust crates" fi } # ------------------------------------------------------------------------------ # Installs or upgrades uv (Python package manager) from GitHub releases. # - Downloads platform-specific tarball (no install.sh!) # - Extracts uv binary # - Places it in /usr/local/bin # - Optionally installs a specific Python version via uv # ------------------------------------------------------------------------------ function setup_uv() { local UV_BIN="/usr/local/bin/uv" local UVX_BIN="/usr/local/bin/uvx" local TMP_DIR=$(mktemp -d) local CACHED_VERSION # trap for TMP Cleanup trap "rm -rf '$TMP_DIR'" EXIT CACHED_VERSION=$(get_cached_version "uv") # Architecture Detection local ARCH=$(uname -m) local OS_TYPE="" local UV_TAR="" if grep -qi "alpine" /etc/os-release; then OS_TYPE="musl" else OS_TYPE="gnu" fi case "$ARCH" in x86_64) UV_TAR="uv-x86_64-unknown-linux-${OS_TYPE}.tar.gz" ;; aarch64) UV_TAR="uv-aarch64-unknown-linux-${OS_TYPE}.tar.gz" ;; i686) UV_TAR="uv-i686-unknown-linux-${OS_TYPE}.tar.gz" ;; *) msg_error "Unsupported architecture: $ARCH (supported: x86_64, aarch64, i686)" return 236 ;; esac ensure_dependencies jq # Fetch latest version local releases_json releases_json=$(curl -fsSL --max-time 15 \ "https://api.github.com/repos/astral-sh/uv/releases/latest" 2>/dev/null || echo "") if [[ -z "$releases_json" ]]; then msg_error "Could not fetch latest uv version from GitHub API" return 7 fi local LATEST_VERSION LATEST_VERSION=$(echo "$releases_json" | jq -r '.tag_name' 2>/dev/null | sed 's/^v//') if [[ -z "$LATEST_VERSION" ]]; then msg_error "Could not parse uv version from GitHub API response" return 250 fi # Get currently installed version local INSTALLED_VERSION="" if [[ -x "$UV_BIN" ]]; then INSTALLED_VERSION=$("$UV_BIN" --version 2>/dev/null | awk '{print $2}') fi # Scenario 1: Already at latest version if [[ -n "$INSTALLED_VERSION" && "$INSTALLED_VERSION" == "$LATEST_VERSION" ]]; then cache_installed_version "uv" "$LATEST_VERSION" # Check if uvx is needed and missing if [[ "${USE_UVX:-NO}" == "YES" ]] && [[ ! -x "$UVX_BIN" ]]; then msg_info "Installing uvx wrapper" _install_uvx_wrapper || return 252 msg_ok "uvx wrapper installed" fi return 0 fi # Scenario 2: New install or upgrade if [[ -n "$INSTALLED_VERSION" && "$INSTALLED_VERSION" != "$LATEST_VERSION" ]]; then msg_info "Upgrade uv from $INSTALLED_VERSION to $LATEST_VERSION" else msg_info "Setup uv $LATEST_VERSION" fi local UV_URL="https://github.com/astral-sh/uv/releases/download/${LATEST_VERSION}/${UV_TAR}" if ! curl_with_retry "$UV_URL" "$TMP_DIR/uv.tar.gz"; then msg_error "Failed to download uv from $UV_URL" return 7 fi # Extract $STD tar -xzf "$TMP_DIR/uv.tar.gz" -C "$TMP_DIR" || { msg_error "Failed to extract uv" return 251 } # Find and install uv binary (tarball extracts to uv-VERSION-ARCH/ directory) local UV_BINARY=$(find "$TMP_DIR" -name "uv" -type f -executable | head -n1) if [[ ! -f "$UV_BINARY" ]]; then msg_error "Could not find uv binary in extracted tarball" return 127 fi $STD /usr/bin/install -m 755 "$UV_BINARY" "$UV_BIN" || { msg_error "Failed to install uv binary" return 252 } ensure_usr_local_bin_persist export PATH="/usr/local/bin:$PATH" # Optional: Install uvx wrapper if [[ "${USE_UVX:-NO}" == "YES" ]]; then msg_info "Installing uvx wrapper" _install_uvx_wrapper || { msg_error "Failed to install uvx wrapper" return 252 } msg_ok "uvx wrapper installed" fi # Optional: Generate shell completions $STD uv generate-shell-completion bash >/etc/bash_completion.d/uv 2>/dev/null || true if [[ -d /usr/share/zsh/site-functions ]]; then $STD uv generate-shell-completion zsh >/usr/share/zsh/site-functions/_uv 2>/dev/null || true fi # Optional: Install specific Python version if requested if [[ -n "${PYTHON_VERSION:-}" ]]; then msg_info "Installing Python $PYTHON_VERSION via uv" $STD uv python install "$PYTHON_VERSION" || { msg_error "Failed to install Python $PYTHON_VERSION" return 150 } msg_ok "Python $PYTHON_VERSION installed" fi cache_installed_version "uv" "$LATEST_VERSION" msg_ok "Setup uv $LATEST_VERSION" } # Helper function to install uvx wrapper _install_uvx_wrapper() { local UVX_BIN="/usr/local/bin/uvx" cat >"$UVX_BIN" <<'EOF' #!/bin/bash # uvx - Run Python applications from PyPI as command-line tools # Wrapper for: uv tool run exec /usr/local/bin/uv tool run "$@" EOF chmod +x "$UVX_BIN" return 0 } # ------------------------------------------------------------------------------ # Installs or updates yq (mikefarah/yq - Go version). # # Description: # - Checks if yq is installed and from correct source # - Compares with latest release on GitHub # - Updates if outdated or wrong implementation # ------------------------------------------------------------------------------ function setup_yq() { local TMP_DIR=$(mktemp -d) local BINARY_PATH="/usr/local/bin/yq" local GITHUB_REPO="mikefarah/yq" ensure_dependencies jq ensure_usr_local_bin_persist # Remove non-mikefarah implementations if command -v yq &>/dev/null; then if ! yq --version 2>&1 | grep -q 'mikefarah'; then rm -f "$(command -v yq)" fi fi local LATEST_VERSION local releases_json releases_json=$(curl -fsSL --max-time 15 "https://api.github.com/repos/${GITHUB_REPO}/releases/latest" 2>/dev/null || echo "") if [[ -z "$releases_json" ]]; then msg_error "Could not fetch latest yq version from GitHub API" rm -rf "$TMP_DIR" return 250 fi LATEST_VERSION=$(echo "$releases_json" | jq -r '.tag_name' 2>/dev/null | sed 's/^v//' || echo "") if [[ -z "$LATEST_VERSION" ]]; then msg_error "Could not parse yq version from GitHub API response" rm -rf "$TMP_DIR" return 250 fi # Get currently installed version local INSTALLED_VERSION="" if command -v yq &>/dev/null && yq --version 2>&1 | grep -q 'mikefarah'; then INSTALLED_VERSION=$(yq --version 2>/dev/null | awk '{print $NF}' | sed 's/^v//') fi # Scenario 1: Already at latest version if [[ -n "$INSTALLED_VERSION" && "$INSTALLED_VERSION" == "$LATEST_VERSION" ]]; then cache_installed_version "yq" "$LATEST_VERSION" rm -rf "$TMP_DIR" return 0 fi # Scenario 2: New install or upgrade if [[ -n "$INSTALLED_VERSION" && "$INSTALLED_VERSION" != "$LATEST_VERSION" ]]; then msg_info "Upgrade yq from $INSTALLED_VERSION to $LATEST_VERSION" else msg_info "Setup yq $LATEST_VERSION" fi if ! curl_with_retry "https://github.com/${GITHUB_REPO}/releases/download/v${LATEST_VERSION}/yq_linux_amd64" "$TMP_DIR/yq"; then msg_error "Failed to download yq" rm -rf "$TMP_DIR" return 250 fi chmod +x "$TMP_DIR/yq" mv "$TMP_DIR/yq" "$BINARY_PATH" || { msg_error "Failed to install yq" rm -rf "$TMP_DIR" return 252 } rm -rf "$TMP_DIR" hash -r local FINAL_VERSION FINAL_VERSION=$("$BINARY_PATH" --version 2>/dev/null | awk '{print $NF}' | sed 's/^v//') cache_installed_version "yq" "$FINAL_VERSION" msg_ok "Setup yq $FINAL_VERSION" } # ------------------------------------------------------------------------------ # Docker Engine Installation and Management (All-In-One) # # Description: # - By default uses distro repository (docker.io) for stability # - Optionally uses official Docker repository for latest features # - Detects and migrates old Docker installations # - Optional: Installs/Updates Portainer CE # - Updates running containers interactively # - Cleans up legacy repository files # # Usage: # setup_docker # Uses distro package (recommended) # USE_DOCKER_REPO=true setup_docker # Uses official Docker repo # DOCKER_PORTAINER="true" setup_docker # DOCKER_LOG_DRIVER="json-file" setup_docker # # Variables: # USE_DOCKER_REPO - Set to "true" to use official Docker repository # (default: false, uses distro docker.io package) # DOCKER_PORTAINER - Install Portainer CE (optional, "true" to enable) # DOCKER_LOG_DRIVER - Log driver (optional, default: "journald") # DOCKER_SKIP_UPDATES - Skip container update check (optional, "true" to skip) # # Features: # - Uses stable distro packages by default # - Migrates from get.docker.com to repository-based installation # - Updates Docker Engine if newer version available # - Interactive container update with multi-select # - Portainer installation and update support # ------------------------------------------------------------------------------ function setup_docker() { local docker_installed=false local portainer_installed=false local USE_DOCKER_REPO="${USE_DOCKER_REPO:-false}" # Check if Docker is already installed if command -v docker &>/dev/null; then docker_installed=true DOCKER_CURRENT_VERSION=$(docker --version | grep -oP '\d+\.\d+\.\d+' | head -1) msg_info "Docker $DOCKER_CURRENT_VERSION detected" fi # Check if Portainer is running if docker ps --format '{{.Names}}' 2>/dev/null | grep -q '^portainer$'; then portainer_installed=true msg_info "Portainer container detected" fi # Scenario 1: Use distro repository (default, most stable) if [[ "$USE_DOCKER_REPO" != "true" && "$USE_DOCKER_REPO" != "TRUE" && "$USE_DOCKER_REPO" != "1" ]]; then # Install or upgrade Docker from distro repo if [ "$docker_installed" = true ]; then msg_info "Checking for Docker updates (distro package)" ensure_apt_working || return 100 upgrade_packages_with_retry "docker.io" "docker-compose" || true DOCKER_CURRENT_VERSION=$(docker --version | grep -oP '\d+\.\d+\.\d+' | head -1) msg_ok "Docker is up-to-date ($DOCKER_CURRENT_VERSION)" else msg_info "Installing Docker (distro package)" ensure_apt_working || return 100 # Install docker.io and docker-compose from distro if ! install_packages_with_retry "docker.io"; then msg_error "Failed to install docker.io from distro repository" return 100 fi # docker-compose is optional $STD apt install -y docker-compose 2>/dev/null || true DOCKER_CURRENT_VERSION=$(docker --version | grep -oP '\d+\.\d+\.\d+' | head -1) msg_ok "Installed Docker $DOCKER_CURRENT_VERSION (distro package)" fi # Configure daemon.json local log_driver="${DOCKER_LOG_DRIVER:-journald}" mkdir -p /etc/docker if [ ! -f /etc/docker/daemon.json ]; then cat </etc/docker/daemon.json { "log-driver": "$log_driver" } EOF fi # Enable and start Docker systemctl enable -q --now docker # Continue to Portainer section below else # Scenario 2: Use official Docker repository (USE_DOCKER_REPO=true) # Cleanup old repository configurations if [ -f /etc/apt/sources.list.d/docker.list ]; then msg_info "Migrating from old Docker repository format" rm -f /etc/apt/sources.list.d/docker.list rm -f /etc/apt/keyrings/docker.asc fi # Setup/Update Docker repository msg_info "Setting up Docker Repository" setup_deb822_repo \ "docker" \ "https://download.docker.com/linux/$(get_os_info id)/gpg" \ "https://download.docker.com/linux/$(get_os_info id)" \ "$(get_os_info codename)" \ "stable" \ "$(dpkg --print-architecture)" # Install or upgrade Docker if [ "$docker_installed" = true ]; then msg_info "Checking for Docker updates" DOCKER_LATEST_VERSION=$(apt-cache policy docker-ce | grep Candidate | awk '{print $2}' 2>/dev/null | cut -d':' -f2 | cut -d'-' -f1 || echo '') if [ "$DOCKER_CURRENT_VERSION" != "$DOCKER_LATEST_VERSION" ]; then msg_info "Updating Docker $DOCKER_CURRENT_VERSION → $DOCKER_LATEST_VERSION" $STD apt install -y --only-upgrade \ docker-ce \ docker-ce-cli \ containerd.io \ docker-buildx-plugin \ docker-compose-plugin || { msg_error "Failed to update Docker packages" return 100 } msg_ok "Updated Docker to $DOCKER_LATEST_VERSION" else msg_ok "Docker is up-to-date ($DOCKER_CURRENT_VERSION)" fi else msg_info "Installing Docker" $STD apt install -y \ docker-ce \ docker-ce-cli \ containerd.io \ docker-buildx-plugin \ docker-compose-plugin || { msg_error "Failed to install Docker packages" return 100 } DOCKER_CURRENT_VERSION=$(docker --version | grep -oP '\d+\.\d+\.\d+' | head -1) msg_ok "Installed Docker $DOCKER_CURRENT_VERSION" fi # Configure daemon.json local log_driver="${DOCKER_LOG_DRIVER:-journald}" mkdir -p /etc/docker if [ ! -f /etc/docker/daemon.json ]; then cat </etc/docker/daemon.json { "log-driver": "$log_driver" } EOF fi # Enable and start Docker systemctl enable -q --now docker fi # Portainer Management (common for both modes) if [[ "${DOCKER_PORTAINER:-}" == "true" ]]; then if [ "$portainer_installed" = true ]; then msg_info "Checking for Portainer updates" PORTAINER_CURRENT=$(docker inspect portainer --format='{{.Config.Image}}' 2>/dev/null | cut -d':' -f2) PORTAINER_LATEST=$(curl -fsSL https://registry.hub.docker.com/v2/repositories/portainer/portainer-ce/tags?page_size=100 | grep -oP '"name":"\K[0-9]+\.[0-9]+\.[0-9]+"' | head -1 | tr -d '"') if [ "$PORTAINER_CURRENT" != "$PORTAINER_LATEST" ]; then read -r -p "${TAB3}Update Portainer $PORTAINER_CURRENT → $PORTAINER_LATEST? " prompt if [[ ${prompt,,} =~ ^(y|yes)$ ]]; then msg_info "Updating Portainer" docker stop portainer docker rm portainer docker pull portainer/portainer-ce:latest docker run -d \ -p 9000:9000 \ -p 9443:9443 \ --name=portainer \ --restart=always \ -v /var/run/docker.sock:/var/run/docker.sock \ -v portainer_data:/data \ portainer/portainer-ce:latest msg_ok "Updated Portainer to $PORTAINER_LATEST" fi else msg_ok "Portainer is up-to-date ($PORTAINER_CURRENT)" fi else msg_info "Installing Portainer" docker volume create portainer_data docker run -d \ -p 9000:9000 \ -p 9443:9443 \ --name=portainer \ --restart=always \ -v /var/run/docker.sock:/var/run/docker.sock \ -v portainer_data:/data \ portainer/portainer-ce:latest LOCAL_IP=$(hostname -I | awk '{print $1}') msg_ok "Installed Portainer (http://${LOCAL_IP}:9000)" fi fi # Interactive Container Update Check if [[ "${DOCKER_SKIP_UPDATES:-}" != "true" ]] && [ "$docker_installed" = true ]; then msg_info "Checking for container updates" # Get list of running containers with update status local containers_with_updates=() local container_info=() local index=1 while IFS= read -r container; do local name=$(echo "$container" | awk '{print $1}') local image=$(echo "$container" | awk '{print $2}') local current_digest=$(docker inspect "$name" --format='{{.Image}}' 2>/dev/null | cut -d':' -f2 | cut -c1-12) # Pull latest image digest docker pull "$image" >/dev/null 2>&1 local latest_digest=$(docker inspect "$image" --format='{{.Id}}' 2>/dev/null | cut -d':' -f2 | cut -c1-12) if [ "$current_digest" != "$latest_digest" ]; then containers_with_updates+=("$name") container_info+=("${index}) ${name} (${image})") ((index++)) fi done < <(docker ps --format '{{.Names}} {{.Image}}') if [ ${#containers_with_updates[@]} -gt 0 ]; then echo "" echo "${TAB3}Container updates available:" for info in "${container_info[@]}"; do echo "${TAB3} $info" done echo "" read -r -p "${TAB3}Select containers to update (e.g., 1,3,5 or 'all' or 'none'): " selection if [[ ${selection,,} == "all" ]]; then for container in "${containers_with_updates[@]}"; do msg_info "Updating container: $container" docker stop "$container" docker rm "$container" # Note: This requires the original docker run command - best to recreate via compose msg_ok "Stopped and removed $container (please recreate with updated image)" done elif [[ ${selection,,} != "none" ]]; then IFS=',' read -ra SELECTED <<<"$selection" for num in "${SELECTED[@]}"; do num=$(echo "$num" | xargs) # trim whitespace if [[ "$num" =~ ^[0-9]+$ ]] && [ "$num" -ge 1 ] && [ "$num" -le "${#containers_with_updates[@]}" ]; then container="${containers_with_updates[$((num - 1))]}" msg_info "Updating container: $container" docker stop "$container" docker rm "$container" msg_ok "Stopped and removed $container (please recreate with updated image)" fi done fi else msg_ok "All containers are up-to-date" fi fi msg_ok "Docker setup completed" } # ------------------------------------------------------------------------------ # Fetch and deploy from URL # Downloads an archive (zip, tar.gz, or .deb) from a URL and extracts/installs it # # Usage: fetch_and_deploy_from_url "url" "directory" # url - URL to the archive (zip, tar.gz, or .deb) # directory - Destination path where the archive will be extracted # (not used for .deb packages) # # Examples: # fetch_and_deploy_from_url "https://example.com/app.tar.gz" "/opt/myapp" # fetch_and_deploy_from_url "https://example.com/app.zip" "/opt/myapp" # fetch_and_deploy_from_url "https://example.com/package.deb" "" # ------------------------------------------------------------------------------ function fetch_and_deploy_from_url() { local url="$1" local directory="${2:-}" if [[ -z "$url" ]]; then msg_error "URL parameter is required" return 65 fi local filename="${url##*/}" msg_info "Downloading from $url" local tmpdir tmpdir=$(mktemp -d) || { msg_error "Failed to create temporary directory" return 252 } curl -fsSL -o "$tmpdir/$filename" "$url" || { msg_error "Download failed: $url" rm -rf "$tmpdir" return 250 } # Auto-detect archive type using file description local file_desc file_desc=$(file -b "$tmpdir/$filename") local archive_type="unknown" if [[ "$file_desc" =~ gzip.*compressed|gzip\ compressed\ data ]]; then archive_type="tar" elif [[ "$file_desc" =~ Zip.*archive|ZIP\ archive ]]; then archive_type="zip" elif [[ "$file_desc" =~ Debian.*package|Debian\ binary\ package ]]; then archive_type="deb" elif [[ "$file_desc" =~ POSIX.*tar.*archive|tar\ archive ]]; then archive_type="tar" else msg_error "Unsupported or unknown archive type: $file_desc" rm -rf "$tmpdir" return 65 fi msg_info "Detected archive type: $archive_type (file type: $file_desc)" if [[ "$archive_type" == "deb" ]]; then msg_info "Installing .deb package" chmod 644 "$tmpdir/$filename" $STD apt install -y "$tmpdir/$filename" || { $STD dpkg -i "$tmpdir/$filename" || { msg_error "Both apt and dpkg installation failed" rm -rf "$tmpdir" return 100 } } rm -rf "$tmpdir" msg_ok "Successfully installed .deb package" return 0 fi if [[ -z "$directory" ]]; then msg_error "Directory parameter is required for archive extraction" rm -rf "$tmpdir" return 65 fi msg_info "Extracting archive to $directory" mkdir -p "$directory" if [[ "${CLEAN_INSTALL:-0}" == "1" ]]; then rm -rf "${directory:?}/"* fi local unpack_tmp unpack_tmp=$(mktemp -d) if [[ "$archive_type" == "zip" ]]; then ensure_dependencies unzip unzip -q "$tmpdir/$filename" -d "$unpack_tmp" || { msg_error "Failed to extract ZIP archive" rm -rf "$tmpdir" "$unpack_tmp" return 251 } elif [[ "$archive_type" == "tar" ]]; then tar --no-same-owner -xf "$tmpdir/$filename" -C "$unpack_tmp" || { msg_error "Failed to extract TAR archive" rm -rf "$tmpdir" "$unpack_tmp" return 251 } fi local top_entries top_entries=$(find "$unpack_tmp" -mindepth 1 -maxdepth 1) if [[ "$(echo "$top_entries" | wc -l)" -eq 1 && -d "$top_entries" ]]; then local inner_dir="$top_entries" shopt -s dotglob nullglob if compgen -G "$inner_dir/*" >/dev/null; then cp -r "$inner_dir"/* "$directory/" || { msg_error "Failed to copy contents from $inner_dir to $directory" rm -rf "$tmpdir" "$unpack_tmp" return 252 } else msg_error "Inner directory is empty: $inner_dir" rm -rf "$tmpdir" "$unpack_tmp" return 252 fi shopt -u dotglob nullglob else shopt -s dotglob nullglob if compgen -G "$unpack_tmp/*" >/dev/null; then cp -r "$unpack_tmp"/* "$directory/" || { msg_error "Failed to copy contents to $directory" rm -rf "$tmpdir" "$unpack_tmp" return 252 } else msg_error "Unpacked archive is empty" rm -rf "$tmpdir" "$unpack_tmp" return 252 fi shopt -u dotglob nullglob fi rm -rf "$tmpdir" "$unpack_tmp" msg_ok "Successfully deployed archive to $directory" return 0 } setup_nonfree() { local sources_file="/etc/apt/sources.list.d/debian-nonfree.sources" if [ ! -f "$sources_file" ]; then cat <$sources_file Types: deb URIs: http://deb.debian.org/debian Suites: trixie trixie-updates Components: main contrib non-free non-free-firmware Types: deb URIs: http://security.debian.org/debian-security Suites: trixie-security Components: main contrib non-free non-free-firmware EOF fi $STD apt update return 0 } # ------------------------------------------------------------------------------ # Get latest GitLab release version. # Usage: get_latest_gitlab_release "owner/repo" [strip_v] # ------------------------------------------------------------------------------ get_latest_gitlab_release() { local repo="$1" local strip_v="${2:-true}" local repo_encoded repo_encoded=$(printf '%s' "$repo" | sed 's|/|%2F|g') local header=() [[ -n "${GITLAB_TOKEN:-}" ]] && header=(-H "PRIVATE-TOKEN: $GITLAB_TOKEN") local temp_file temp_file=$(mktemp) local http_code http_code=$(curl --connect-timeout 10 --max-time 30 -sSL \ -w "%{http_code}" -o "$temp_file" \ "${header[@]}" \ "https://gitlab.com/api/v4/projects/$repo_encoded/releases?per_page=1&order_by=released_at&sort=desc" 2>/dev/null) || true if [[ "$http_code" != "200" ]]; then rm -f "$temp_file" msg_warn "GitLab API call failed for ${repo} (HTTP ${http_code})" return 22 fi local version version=$(jq -r '.[0].tag_name // empty' "$temp_file") rm -f "$temp_file" if [[ -z "$version" ]]; then msg_error "Could not determine latest version for ${repo}" return 250 fi if [[ "$strip_v" == "true" ]]; then [[ "$version" =~ ^v[0-9] ]] && version="${version:1}" fi echo "$version" } # ------------------------------------------------------------------------------ # Checks for new GitLab release (latest tag). # # Description: # - Queries the GitLab API for the latest release tag # - Compares it to a local cached version (~/.) # - If newer, sets global CHECK_UPDATE_RELEASE and returns 0 # # Usage: # if check_for_gl_release "myapp" "owner/repo" [optional] "v1.2.3"; then # # trigger update... # fi # exit 0 # } (end of update_script not from the function) # # Notes: # - Requires `jq` (auto-installed if missing) # - Supports GITLAB_TOKEN env var for private/rate-limited repos # - Does not modify anything, only checks version state # ------------------------------------------------------------------------------ check_for_gl_release() { local app="$1" local source="$2" local pinned_version_in="${3:-}" # optional local pin_reason="${4:-}" # optional reason shown to user local app_lc="${app,,}" local current_file="$HOME/.${app_lc}" msg_info "Checking for update: ${app}" # DNS check if ! getent hosts gitlab.com >/dev/null 2>&1; then msg_error "Network error: cannot resolve gitlab.com" return 6 fi ensure_dependencies jq local repo_encoded repo_encoded=$(printf '%s' "$source" | sed 's|/|%2F|g') local header=() [[ -n "${GITLAB_TOKEN:-}" ]] && header=(-H "PRIVATE-TOKEN: $GITLAB_TOKEN") local releases_json="" http_code="" # For pinned versions, try to fetch the specific release tag first if [[ -n "$pinned_version_in" ]]; then local pinned_encoded="${pinned_version_in//\//%2F}" http_code=$(curl -sSL --max-time 20 -w "%{http_code}" -o /tmp/gl_check.json \ "${header[@]}" \ "https://gitlab.com/api/v4/projects/$repo_encoded/releases/$pinned_encoded" 2>/dev/null) || true if [[ "$http_code" == "200" ]] && [[ -s /tmp/gl_check.json ]]; then releases_json="[$(/dev/null) || true if [[ "$http_code" == "200" ]] && [[ -s /tmp/gl_check.json ]]; then releases_json=$(/dev/null) if ((${#legacy_files[@]} == 1)); then current="$(<"${legacy_files[0]}")" echo "${current#v}" >"$current_file" rm -f "${legacy_files[0]}" fi fi if [[ "$current" =~ ^v[0-9] ]]; then current="${current:1}" fi # Pinned version handling if [[ -n "$pinned_version_in" ]]; then local pin_clean if [[ "$pinned_version_in" =~ ^v[0-9] ]]; then pin_clean="${pinned_version_in:1}" else pin_clean="$pinned_version_in" fi local match_raw="" for i in "${!clean_tags[@]}"; do if [[ "${clean_tags[$i]}" == "$pin_clean" ]]; then match_raw="${raw_tags[$i]}" break fi done if [[ -z "$match_raw" ]]; then msg_error "Pinned version ${pinned_version_in} not found upstream" return 250 fi if [[ "$current" != "$pin_clean" ]]; then CHECK_UPDATE_RELEASE="$match_raw" msg_ok "Update available: ${app} ${current:-not installed} → ${pin_clean}" return 0 fi if [[ -n "$pin_reason" ]]; then msg_ok "No update available: ${app} (${current}) - update held back: ${pin_reason}" else msg_ok "No update available: ${app} (${current}) - update temporarily held back due to issues with newer releases" fi return 1 fi # No pinning → use latest if [[ -z "$current" || "$current" != "$latest_clean" ]]; then CHECK_UPDATE_RELEASE="$latest_raw" msg_ok "Update available: ${app} ${current:-not installed} → ${latest_clean}" return 0 fi msg_ok "No update available: ${app} (${latest_clean})" return 1 } # ------------------------------------------------------------------------------ # Scan older GitLab releases for a matching asset (fallback helper). # # Description: # When the latest release does not contain the expected asset # (e.g. .deb for the current arch, or a custom pattern), walks back # through up to 15 recent releases and returns the first release JSON # that has a matching asset. Used internally by fetch_and_deploy_gl_release. # # Usage (internal): # _gl_scan_older_releases "owner/repo" "owner%2Frepo" "https://gitlab.com" \ # "binary|prebuild|singlefile" "$asset_pattern" "$skip_tag" # # Returns: # - stdout: JSON of the matching release (single object) on success # - 0 on success, 22 on API error, 250 if no match found # ------------------------------------------------------------------------------ _gl_scan_older_releases() { local repo="$1" local repo_encoded="$2" local base_url="${3:-https://gitlab.com}" local mode="$4" local asset_pattern="$5" local skip_tag="$6" local header=() [[ -n "${GITLAB_TOKEN:-}" ]] && header=(-H "PRIVATE-TOKEN: $GITLAB_TOKEN") local releases_list releases_list=$(curl --connect-timeout 10 --max-time 30 -fsSL \ "${header[@]}" \ "${base_url}/api/v4/projects/${repo_encoded}/releases?per_page=15&order_by=released_at&sort=desc" 2>/dev/null) || { msg_warn "Failed to fetch older releases for ${repo}" return 22 } local count count=$(echo "$releases_list" | jq 'length' 2>/dev/null || echo 0) [[ "$count" -eq 0 ]] && return 250 for ((i = 0; i < count; i++)); do local rel_tag rel_tag=$(echo "$releases_list" | jq -r ".[$i].tag_name") # Skip the tag we already checked [[ "$rel_tag" == "$skip_tag" ]] && continue # Asset URLs for this release (direct_asset_url preferred, fallback to url) local asset_urls asset_urls=$(echo "$releases_list" | jq -r ".[$i].assets.links // [] | .[] | .direct_asset_url // .url") [[ -z "$asset_urls" ]] && continue local has_match=false if [[ "$mode" == "binary" ]]; then local arch arch=$(dpkg --print-architecture 2>/dev/null || uname -m) [[ "$arch" == "x86_64" ]] && arch="amd64" [[ "$arch" == "aarch64" ]] && arch="arm64" # Check with explicit pattern first, then arch heuristic, then any .deb if [[ -n "$asset_pattern" ]]; then while read -r u; do case "${u##*/}" in $asset_pattern) has_match=true break ;; esac done <<<"$asset_urls" fi if [[ "$has_match" != "true" ]]; then echo "$asset_urls" | grep -qE "($arch|amd64|x86_64|aarch64|arm64).*\.deb$" && has_match=true fi if [[ "$has_match" != "true" ]]; then echo "$asset_urls" | grep -qE '\.deb$' && has_match=true fi elif [[ "$mode" == "prebuild" || "$mode" == "singlefile" ]]; then while read -r u; do case "${u##*/}" in $asset_pattern) has_match=true break ;; esac done <<<"$asset_urls" fi if [[ "$has_match" == "true" ]]; then local use_fallback="y" if [[ -t 0 ]]; then msg_warn "Release ${skip_tag} has no matching asset. Previous release ${rel_tag} has a compatible asset." read -rp "Use version ${rel_tag} instead? [Y/n] (auto-yes in 60s): " -t 60 use_fallback || use_fallback="y" use_fallback="${use_fallback:-y}" fi if [[ "${use_fallback,,}" == "y" || "${use_fallback,,}" == "yes" ]]; then echo "$releases_list" | jq ".[$i]" return 0 else return 250 fi fi done return 250 } function fetch_and_deploy_gl_release() { local app="$1" local repo="$2" local mode="${3:-tarball}" local version="${var_appversion:-${4:-latest}}" local target="${5:-/opt/$app}" local asset_pattern="${6:-}" if [[ -z "$app" ]]; then app="${repo##*/}" if [[ -z "$app" ]]; then msg_error "fetch_and_deploy_gl_release requires app name or valid repo" return 1 fi fi local app_lc=$(echo "${app,,}" | tr -d ' ') local version_file="$HOME/.${app_lc}" local api_timeout="--connect-timeout 10 --max-time 60" local download_timeout="--connect-timeout 15 --max-time 900" local current_version="" [[ -f "$version_file" ]] && current_version=$(<"$version_file") ensure_dependencies jq local repo_encoded repo_encoded=$(printf '%s' "$repo" | sed 's|/|%2F|g') local api_base="https://gitlab.com/api/v4/projects/$repo_encoded/releases" local api_url if [[ "$version" != "latest" ]]; then api_url="$api_base/$version" else api_url="$api_base?per_page=1&order_by=released_at&sort=desc" fi local header=() [[ -n "${GITLAB_TOKEN:-}" ]] && header=(-H "PRIVATE-TOKEN: $GITLAB_TOKEN") local max_retries=3 retry_delay=2 attempt=1 success=false http_code while ((attempt <= max_retries)); do http_code=$(curl $api_timeout -sSL -w "%{http_code}" -o /tmp/gl_rel.json "${header[@]}" "$api_url" 2>/dev/null) || true if [[ "$http_code" == "200" ]]; then success=true break elif [[ "$http_code" == "429" ]]; then if ((attempt < max_retries)); then msg_warn "GitLab API rate limit hit, retrying in ${retry_delay}s... (attempt $attempt/$max_retries)" sleep "$retry_delay" retry_delay=$((retry_delay * 2)) fi else sleep "$retry_delay" fi ((attempt++)) done if ! $success; then if [[ "$http_code" == "401" ]]; then msg_error "GitLab API authentication failed (HTTP 401)." if [[ -n "${GITLAB_TOKEN:-}" ]]; then msg_error "Your GITLAB_TOKEN appears to be invalid or expired." else msg_error "The repository may require authentication. Try: export GITLAB_TOKEN=\"glpat-your_token\"" fi elif [[ "$http_code" == "404" ]]; then msg_error "GitLab project or release not found (HTTP 404)." msg_error "Ensure '$repo' is correct and the project is accessible." elif [[ "$http_code" == "429" ]]; then msg_error "GitLab API rate limit exceeded (HTTP 429)." msg_error "To increase the limit, export a GitLab token before running the script:" msg_error " export GITLAB_TOKEN=\"glpat-your_token_here\"" elif [[ "$http_code" == "000" || -z "$http_code" ]]; then msg_error "GitLab API connection failed (no response)." msg_error "Check your network/DNS: curl -sSL https://gitlab.com/api/v4/version" else msg_error "Failed to fetch release metadata (HTTP $http_code)" fi return 1 fi local json tag_name json=$(/dev/null || uname -m) [[ "$arch" == "x86_64" ]] && arch="amd64" [[ "$arch" == "aarch64" ]] && arch="arm64" local assets url_match="" assets=$(_gl_asset_urls "$json") if [[ -n "$asset_pattern" ]]; then for u in $assets; do case "${u##*/}" in $asset_pattern) url_match="$u" break ;; esac done fi if [[ -z "$url_match" ]]; then for u in $assets; do if [[ "$u" =~ ($arch|amd64|x86_64|aarch64|arm64).*\.deb$ ]]; then url_match="$u" break fi done fi if [[ -z "$url_match" ]]; then for u in $assets; do [[ "$u" =~ \.deb$ ]] && url_match="$u" && break done fi if [[ -z "$url_match" ]]; then local fallback_json if fallback_json=$(_gl_scan_older_releases "$repo" "$repo_encoded" "https://gitlab.com" "binary" "$asset_pattern" "$tag_name"); then json="$fallback_json" tag_name=$(echo "$json" | jq -r '.tag_name // empty') [[ "$tag_name" =~ ^v[0-9] ]] && version="${tag_name:1}" || version="$tag_name" msg_info "Fetching GitLab release: $app ($version)" assets=$(_gl_asset_urls "$json") if [[ -n "$asset_pattern" ]]; then for u in $assets; do case "${u##*/}" in $asset_pattern) url_match="$u" break ;; esac done fi if [[ -z "$url_match" ]]; then for u in $assets; do [[ "$u" =~ ($arch|amd64|x86_64|aarch64|arm64).*\.deb$ ]] && url_match="$u" && break done fi if [[ -z "$url_match" ]]; then for u in $assets; do [[ "$u" =~ \.deb$ ]] && url_match="$u" && break done fi fi fi if [[ -z "$url_match" ]]; then msg_error "No suitable .deb asset found for $app" rm -rf "$tmpdir" return 1 fi filename="${url_match##*/}" curl $download_timeout -fsSL "${header[@]}" -o "$tmpdir/$filename" "$url_match" || { msg_error "Download failed: $url_match" rm -rf "$tmpdir" return 1 } chmod 644 "$tmpdir/$filename" local dpkg_opts="" [[ "${DPKG_FORCE_CONFOLD:-}" == "1" ]] && dpkg_opts="-o Dpkg::Options::=--force-confold" [[ "${DPKG_FORCE_CONFNEW:-}" == "1" ]] && dpkg_opts="-o Dpkg::Options::=--force-confnew" DEBIAN_FRONTEND=noninteractive SYSTEMD_OFFLINE=1 $STD apt install -y $dpkg_opts "$tmpdir/$filename" || { SYSTEMD_OFFLINE=1 $STD dpkg -i "$tmpdir/$filename" || { msg_error "Both apt and dpkg installation failed" rm -rf "$tmpdir" return 1 } } ### Prebuild Mode ### elif [[ "$mode" == "prebuild" ]]; then local pattern="${6%\"}" pattern="${pattern#\"}" [[ -z "$pattern" ]] && { msg_error "Mode 'prebuild' requires 6th parameter (asset filename pattern)" rm -rf "$tmpdir" return 1 } local asset_url="" for u in $(_gl_asset_urls "$json"); do filename_candidate="${u##*/}" case "$filename_candidate" in $pattern) asset_url="$u" break ;; esac done if [[ -z "$asset_url" ]]; then local fallback_json if fallback_json=$(_gl_scan_older_releases "$repo" "$repo_encoded" "https://gitlab.com" "prebuild" "$pattern" "$tag_name"); then json="$fallback_json" tag_name=$(echo "$json" | jq -r '.tag_name // empty') [[ "$tag_name" =~ ^v[0-9] ]] && version="${tag_name:1}" || version="$tag_name" msg_info "Fetching GitLab release: $app ($version)" for u in $(_gl_asset_urls "$json"); do filename_candidate="${u##*/}" case "$filename_candidate" in $pattern) asset_url="$u" break ;; esac done fi fi [[ -z "$asset_url" ]] && { msg_error "No asset matching '$pattern' found" rm -rf "$tmpdir" return 1 } filename="${asset_url##*/}" curl $download_timeout -fsSL "${header[@]}" -o "$tmpdir/$filename" "$asset_url" || { msg_error "Download failed: $asset_url" rm -rf "$tmpdir" return 1 } local unpack_tmp unpack_tmp=$(mktemp -d) mkdir -p "$target" if [[ "${CLEAN_INSTALL:-0}" == "1" ]]; then rm -rf "${target:?}/"* fi if [[ "$filename" == *.zip ]]; then ensure_dependencies unzip unzip -q "$tmpdir/$filename" -d "$unpack_tmp" || { msg_error "Failed to extract ZIP archive" rm -rf "$tmpdir" "$unpack_tmp" return 1 } elif [[ "$filename" == *.tar.* || "$filename" == *.tgz || "$filename" == *.txz ]]; then tar --no-same-owner -xf "$tmpdir/$filename" -C "$unpack_tmp" || { msg_error "Failed to extract TAR archive" rm -rf "$tmpdir" "$unpack_tmp" return 1 } else msg_error "Unsupported archive format: $filename" rm -rf "$tmpdir" "$unpack_tmp" return 1 fi local top_entries inner_dir top_entries=$(find "$unpack_tmp" -mindepth 1 -maxdepth 1) if [[ "$(echo "$top_entries" | wc -l)" -eq 1 && -d "$top_entries" ]]; then inner_dir="$top_entries" shopt -s dotglob nullglob if compgen -G "$inner_dir/*" >/dev/null; then cp -r "$inner_dir"/* "$target/" || { msg_error "Failed to copy contents from $inner_dir to $target" rm -rf "$tmpdir" "$unpack_tmp" return 1 } else msg_error "Inner directory is empty: $inner_dir" rm -rf "$tmpdir" "$unpack_tmp" return 1 fi shopt -u dotglob nullglob else shopt -s dotglob nullglob if compgen -G "$unpack_tmp/*" >/dev/null; then cp -r "$unpack_tmp"/* "$target/" || { msg_error "Failed to copy contents to $target" rm -rf "$tmpdir" "$unpack_tmp" return 1 } else msg_error "Unpacked archive is empty" rm -rf "$tmpdir" "$unpack_tmp" return 1 fi shopt -u dotglob nullglob fi ### Singlefile Mode ### elif [[ "$mode" == "singlefile" ]]; then local pattern="${6%\"}" pattern="${pattern#\"}" [[ -z "$pattern" ]] && { msg_error "Mode 'singlefile' requires 6th parameter (asset filename pattern)" rm -rf "$tmpdir" return 1 } local asset_url="" for u in $(_gl_asset_urls "$json"); do filename_candidate="${u##*/}" case "$filename_candidate" in $pattern) asset_url="$u" break ;; esac done if [[ -z "$asset_url" ]]; then local fallback_json if fallback_json=$(_gl_scan_older_releases "$repo" "$repo_encoded" "https://gitlab.com" "singlefile" "$pattern" "$tag_name"); then json="$fallback_json" tag_name=$(echo "$json" | jq -r '.tag_name // empty') [[ "$tag_name" =~ ^v[0-9] ]] && version="${tag_name:1}" || version="$tag_name" msg_info "Fetching GitLab release: $app ($version)" for u in $(_gl_asset_urls "$json"); do filename_candidate="${u##*/}" case "$filename_candidate" in $pattern) asset_url="$u" break ;; esac done fi fi [[ -z "$asset_url" ]] && { msg_error "No asset matching '$pattern' found" rm -rf "$tmpdir" return 1 } filename="${asset_url##*/}" mkdir -p "$target" local use_filename="${USE_ORIGINAL_FILENAME:-false}" local target_file="$app" [[ "$use_filename" == "true" ]] && target_file="$filename" curl $download_timeout -fsSL "${header[@]}" -o "$target/$target_file" "$asset_url" || { msg_error "Download failed: $asset_url" rm -rf "$tmpdir" return 1 } if [[ "$target_file" != *.jar && -f "$target/$target_file" ]]; then chmod +x "$target/$target_file" fi else msg_error "Unknown mode: $mode" rm -rf "$tmpdir" return 1 fi echo "$version" >"$version_file" msg_ok "Deployed: $app ($version)" rm -rf "$tmpdir" } # ------------------------------------------------------------------------------ # Download NLTK data packages directly from GitHub, bypassing Python. # Avoids CPU-instruction failures (SIGILL) on older hardware lacking AVX. # # Usage: # setup_nltk "averaged_perceptron_tagger_eng" "/nltk_data" # setup_nltk "snowball_data stopwords punkt_tab" "/usr/share/nltk_data" # # Parameters: # $1 - Space-separated list of NLTK package IDs # $2 - Target directory (default: /usr/share/nltk_data) # # Returns: 0 on success, non-zero if any package failed # ------------------------------------------------------------------------------ function setup_nltk() { local packages="${1:?setup_nltk requires at least one package name}" local target_dir="${2:-/usr/share/nltk_data}" local NLTK_INDEX_URL="https://raw.githubusercontent.com/nltk/nltk_data/gh-pages/index.xml" local index_xml rc=0 ensure_dependencies unzip index_xml=$(curl_with_retry "$NLTK_INDEX_URL" "-") || { msg_error "Failed to fetch NLTK package index" return 1 } local pkg for pkg in $packages; do msg_info "Downloading NLTK: $pkg" local pkg_line subdir pkg_url do_unzip tmp_zip pkg_line=$(echo "$index_xml" | grep "id=\"${pkg}\"" | head -1) if [[ -z "$pkg_line" ]]; then msg_error "NLTK package not found in index: $pkg" rc=1 continue fi subdir=$(echo "$pkg_line" | grep -oP 'subdir="\K[^"]+') pkg_url=$(echo "$pkg_line" | grep -oP 'url="\K[^"]+') do_unzip=$(echo "$pkg_line" | grep -oP 'unzip="\K[^"]+') if [[ -z "$subdir" || -z "$pkg_url" ]]; then msg_error "Could not parse NLTK index entry for: $pkg" rc=1 continue fi mkdir -p "${target_dir}/${subdir}" tmp_zip=$(mktemp --suffix=.zip) if CURL_TIMEOUT=120 curl_with_retry "$pkg_url" "$tmp_zip"; then if [[ "$do_unzip" == "1" ]]; then $STD unzip -q -o "$tmp_zip" -d "${target_dir}/${subdir}/" rm -f "$tmp_zip" else mv "$tmp_zip" "${target_dir}/${subdir}/${pkg}.zip" fi msg_ok "Downloaded NLTK: $pkg" else msg_error "Failed to download NLTK package: $pkg" rm -f "$tmp_zip" rc=1 fi done return $rc } ================================================ FILE: misc/vm-core.func ================================================ # Copyright (c) 2021-2026 community-scripts ORG # License: MIT | https://git.community-scripts.org/community-scripts/ProxmoxVE/raw/branch/main/LICENSE set -euo pipefail SPINNER_PID="" SPINNER_ACTIVE=0 SPINNER_MSG="" declare -A MSG_INFO_SHOWN # ------------------------------------------------------------------------------ # Loads core utility groups once (colors, formatting, icons, defaults). # ------------------------------------------------------------------------------ [[ -n "${_CORE_FUNC_LOADED:-}" ]] && return _CORE_FUNC_LOADED=1 load_functions() { [[ -n "${__FUNCTIONS_LOADED:-}" ]] && return __FUNCTIONS_LOADED=1 color formatting icons default_vars set_std_mode shell_check get_valid_nextid cleanup_vmid cleanup check_root pve_check arch_check } # Function to download & save header files get_header() { local app_name=$(echo "${APP,,}" | tr ' ' '-') local app_type=${APP_TYPE:-vm} local header_url="https://git.community-scripts.org/community-scripts/ProxmoxVE/raw/branch/main/${app_type}/headers/${app_name}" local local_header_path="/usr/local/community-scripts/headers/${app_type}/${app_name}" mkdir -p "$(dirname "$local_header_path")" if [ ! -s "$local_header_path" ]; then if ! curl -fsSL "$header_url" -o "$local_header_path"; then return 250 fi fi cat "$local_header_path" 2>/dev/null || true } header_info() { local app_name=$(echo "${APP,,}" | tr ' ' '-') local header_content header_content=$(get_header "$app_name") || header_content="" clear local term_width term_width=$(tput cols 2>/dev/null || echo 120) if [ -n "$header_content" ]; then echo "$header_content" fi } # ------------------------------------------------------------------------------ # Sets ANSI color codes used for styled terminal output. # ------------------------------------------------------------------------------ color() { YW=$(echo "\033[33m") YWB=$(echo "\033[93m") BL=$(echo "\033[36m") RD=$(echo "\033[01;31m") BGN=$(echo "\033[4;92m") GN=$(echo "\033[1;92m") DGN=$(echo "\033[32m") CL=$(echo "\033[m") } # ------------------------------------------------------------------------------ # Defines formatting helpers like tab, bold, and line reset sequences. # ------------------------------------------------------------------------------ formatting() { BFR="\\r\\033[K" BOLD=$(echo "\033[1m") HOLD=" " TAB=" " TAB3=" " } # ------------------------------------------------------------------------------ # Sets symbolic icons used throughout user feedback and prompts. # ------------------------------------------------------------------------------ icons() { CM="${TAB}✔️${TAB}" CROSS="${TAB}✖️${TAB}" DNSOK="✔️ " DNSFAIL="${TAB}✖️${TAB}" INFO="${TAB}💡${TAB}${CL}" OS="${TAB}🖥️${TAB}${CL}" OSVERSION="${TAB}🌟${TAB}${CL}" CONTAINERTYPE="${TAB}📦${TAB}${CL}" DISKSIZE="${TAB}💾${TAB}${CL}" CPUCORE="${TAB}🧠${TAB}${CL}" RAMSIZE="${TAB}🛠️${TAB}${CL}" SEARCH="${TAB}🔍${TAB}${CL}" VERBOSE_CROPPED="🔍${TAB}" VERIFYPW="${TAB}🔐${TAB}${CL}" CONTAINERID="${TAB}🆔${TAB}${CL}" HOSTNAME="${TAB}🏠${TAB}${CL}" BRIDGE="${TAB}🌉${TAB}${CL}" NETWORK="${TAB}📡${TAB}${CL}" GATEWAY="${TAB}🌐${TAB}${CL}" DISABLEIPV6="${TAB}🚫${TAB}${CL}" ICON_DISABLEIPV6="${TAB}🚫${TAB}${CL}" DEFAULT="${TAB}⚙️${TAB}${CL}" MACADDRESS="${TAB}🔗${TAB}${CL}" VLANTAG="${TAB}🏷️${TAB}${CL}" ROOTSSH="${TAB}🔑${TAB}${CL}" CREATING="${TAB}🚀${TAB}${CL}" ADVANCED="${TAB}🧩${TAB}${CL}" FUSE="${TAB}🗂️${TAB}${CL}" GPU="${TAB}🎮${TAB}${CL}" HOURGLASS="${TAB}⏳${TAB}" } # ------------------------------------------------------------------------------ # Sets default verbose mode for script and os execution. # ------------------------------------------------------------------------------ set_std_mode() { if [ "${VERBOSE:-no}" = "yes" ]; then STD="" else STD="silent" fi } # ------------------------------------------------------------------------------ # default_vars() # # - Sets default retry and wait variables used for system actions # - RETRY_NUM: Maximum number of retry attempts (default: 10) # - RETRY_EVERY: Seconds to wait between retries (default: 3) # ------------------------------------------------------------------------------ default_vars() { RETRY_NUM=10 RETRY_EVERY=3 i=$RETRY_NUM } # ------------------------------------------------------------------------------ # get_active_logfile() # # - Returns the appropriate log file based on execution context # - BUILD_LOG: Host operations (VM creation) # - Fallback to /tmp/build-.log if not set # ------------------------------------------------------------------------------ get_active_logfile() { if [[ -n "${BUILD_LOG:-}" ]]; then echo "$BUILD_LOG" else # Fallback for legacy scripts echo "/tmp/build-$(date +%Y%m%d_%H%M%S).log" fi } # ------------------------------------------------------------------------------ # silent() # # - Executes command with output redirected to active log file # - On error: displays last 20 lines of log and exits with original exit code # - Temporarily disables error trap to capture exit code correctly # - Sources explain_exit_code() for detailed error messages # ------------------------------------------------------------------------------ silent() { local cmd="$*" local caller_line="${BASH_LINENO[0]:-unknown}" local logfile="$(get_active_logfile)" set +Eeuo pipefail trap - ERR "$@" >>"$logfile" 2>&1 local rc=$? set -Eeuo pipefail trap 'error_handler' ERR if [[ $rc -ne 0 ]]; then # Return instead of exit so that callers can use `$STD cmd || true` # When no || is used, set -e + ERR trap catches it via error_handler() export _SILENT_FAILED_RC="$rc" export _SILENT_FAILED_CMD="$cmd" export _SILENT_FAILED_LINE="$caller_line" export _SILENT_FAILED_LOG="$logfile" return "$rc" fi # Clear stale flags on success unset _SILENT_FAILED_RC _SILENT_FAILED_CMD _SILENT_FAILED_LINE _SILENT_FAILED_LOG 2>/dev/null || true } # ------------------------------------------------------------------------------ # Performs a curl request with retry logic and inline feedback. # ------------------------------------------------------------------------------ run_curl() { if [ "$VERB" = "no" ]; then curl "$@" >/dev/null 2>>/tmp/curl_error.log else curl "$@" 2>>/tmp/curl_error.log fi } curl_handler() { local args=() local url="" local max_retries=0 delay=2 attempt=1 local exit_code has_output_file=false for arg in "$@"; do if [[ "$arg" != -* && -z "$url" ]]; then url="$arg" fi [[ "$arg" == "-o" || "$arg" == --output ]] && has_output_file=true args+=("$arg") done if [[ -z "$url" ]]; then msg_error "no valid url or option entered for curl_handler" exit 64 fi $STD msg_info "Fetching: $url" while :; do if $has_output_file; then $STD run_curl "${args[@]}" exit_code=$? else $STD result=$(run_curl "${args[@]}") exit_code=$? fi if [[ $exit_code -eq 0 ]]; then stop_spinner msg_ok "Fetched: $url" $has_output_file || printf '%s' "$result" return 0 fi if ((attempt >= max_retries)); then stop_spinner if [ -s /tmp/curl_error.log ]; then local curl_stderr curl_stderr=$(&2 sleep "$delay" ((attempt++)) done } # ------------------------------------------------------------------------------ # Handles specific curl error codes and displays descriptive messages. # ------------------------------------------------------------------------------ __curl_err_handler() { local exit_code="$1" local target="$2" local curl_msg="$3" case $exit_code in 1) msg_error "Unsupported protocol: $target" ;; 2) msg_error "Curl init failed: $target" ;; 3) msg_error "Malformed URL: $target" ;; 5) msg_error "Proxy resolution failed: $target" ;; 6) msg_error "Host resolution failed: $target" ;; 7) msg_error "Connection failed: $target" ;; 9) msg_error "Access denied: $target" ;; 18) msg_error "Partial file transfer: $target" ;; 22) msg_error "HTTP error (e.g. 400/404): $target" ;; 23) msg_error "Write error on local system: $target" ;; 26) msg_error "Read error from local file: $target" ;; 28) msg_error "Timeout: $target" ;; 35) msg_error "SSL connect error: $target" ;; 47) msg_error "Too many redirects: $target" ;; 51) msg_error "SSL cert verify failed: $target" ;; 52) msg_error "Empty server response: $target" ;; 55) msg_error "Send error: $target" ;; 56) msg_error "Receive error: $target" ;; 60) msg_error "SSL CA not trusted: $target" ;; 67) msg_error "Login denied by server: $target" ;; 78) msg_error "Remote file not found (404): $target" ;; *) msg_error "Curl failed with code $exit_code: $target" ;; esac [[ -n "$curl_msg" ]] && printf "%s\n" "$curl_msg" >&2 exit "$exit_code" } # ------------------------------------------------------------------------------ # shell_check() # # - Verifies that the script is running under Bash shell # - Exits with error message if different shell is detected # ------------------------------------------------------------------------------ shell_check() { if [[ "$(ps -p $$ -o comm=)" != "bash" ]]; then clear msg_error "Your default shell is currently not set to Bash. To use these scripts, please switch to the Bash shell." echo -e "\nExiting..." sleep 2 exit 103 fi } # ------------------------------------------------------------------------------ # clear_line() # # - Clears current terminal line using tput or ANSI escape codes # - Moves cursor to beginning of line (carriage return) # - Fallback to ANSI codes if tput not available # ------------------------------------------------------------------------------ clear_line() { tput cr 2>/dev/null || echo -en "\r" tput el 2>/dev/null || echo -en "\033[K" } # ------------------------------------------------------------------------------ # is_verbose_mode() # # - Determines if script should run in verbose mode # - Checks VERBOSE and var_verbose variables # - Note: Non-TTY (pipe) scenarios are handled separately in msg_info() # ------------------------------------------------------------------------------ is_verbose_mode() { local verbose="${VERBOSE:-${var_verbose:-no}}" [[ "$verbose" != "no" ]] } ### dev spinner ### SPINNER_ACTIVE=0 SPINNER_PID="" SPINNER_MSG="" declare -A MSG_INFO_SHOWN=() # Trap cleanup on various signals trap 'cleanup_spinner' EXIT INT TERM HUP # Cleans up spinner process on exit cleanup_spinner() { stop_spinner # Additional cleanup if needed } start_spinner() { local msg="${1:-Processing...}" local frames=(⠋ ⠙ ⠹ ⠸ ⠼ ⠴ ⠦ ⠧ ⠇ ⠏) local spin_i=0 local interval=0.1 # Set message and clear current line SPINNER_MSG="$msg" printf "\r\e[2K" >&2 # Stop any existing spinner stop_spinner # Set active flag SPINNER_ACTIVE=1 # Start spinner in background { while [[ "$SPINNER_ACTIVE" -eq 1 ]]; do printf "\r\e[2K%s %b" "${TAB}${frames[spin_i]}${TAB}" "${YW}${SPINNER_MSG}${CL}" >&2 spin_i=$(((spin_i + 1) % ${#frames[@]})) sleep "$interval" done } & SPINNER_PID=$! # Disown to prevent getting "Terminated" messages disown "$SPINNER_PID" 2>/dev/null || true } stop_spinner() { # Check if spinner is active and PID exists if [[ "$SPINNER_ACTIVE" -eq 1 ]] && [[ -n "${SPINNER_PID}" ]]; then SPINNER_ACTIVE=0 if kill -0 "$SPINNER_PID" 2>/dev/null; then kill "$SPINNER_PID" 2>/dev/null # Give it a moment to terminate sleep 0.1 # Force kill if still running if kill -0 "$SPINNER_PID" 2>/dev/null; then kill -9 "$SPINNER_PID" 2>/dev/null fi # Wait for process but ignore errors wait "$SPINNER_PID" 2>/dev/null || true fi # Clear spinner line printf "\r\e[2K" >&2 SPINNER_PID="" fi } spinner_guard() { # Safely stop spinner if it's running if [[ "$SPINNER_ACTIVE" -eq 1 ]] && [[ -n "${SPINNER_PID}" ]]; then stop_spinner fi } msg_info() { local msg="${1:-Information message}" # Only show each message once unless reset if [[ -n "${MSG_INFO_SHOWN["$msg"]+x}" ]]; then return fi MSG_INFO_SHOWN["$msg"]=1 spinner_guard start_spinner "$msg" } msg_ok() { local msg="${1:-Operation completed successfully}" stop_spinner printf "\r\e[2K%s %b\n" "${CM}" "${GN}${msg}${CL}" >&2 # Remove from shown messages to allow it to be shown again local sanitized_msg sanitized_msg=$(printf '%s' "$msg" | sed 's/\x1b\[[0-9;]*m//g; s/[^a-zA-Z0-9_]/_/g') unset 'MSG_INFO_SHOWN['"$sanitized_msg"']' 2>/dev/null || true } msg_error() { local msg="${1:-An error occurred}" stop_spinner printf "\r\e[2K%s %b\n" "${CROSS}" "${RD}${msg}${CL}" >&2 } msg_warn() { stop_spinner local msg="$1" echo -e "${BFR:-}${INFO:-ℹ️} ${YWB}${msg}${CL}" >&2 } # Helper function to display a message with custom symbol and color msg_custom() { local symbol="${1:-*}" local color="${2:-$CL}" local msg="${3:-Custom message}" [[ -z "$msg" ]] && return stop_spinner printf "\r\e[2K%s %b\n" "$symbol" "${color}${msg}${CL}" >&2 } # ------------------------------------------------------------------------------ # msg_debug() # # - Displays debug message with timestamp when var_full_verbose=1 # - Automatically enables var_verbose if not already set # - Uses bright yellow color for debug output # ------------------------------------------------------------------------------ msg_debug() { if [[ "${var_full_verbose:-0}" == "1" ]]; then [[ "${var_verbose:-0}" != "1" ]] && var_verbose=1 echo -e "${YWB}[$(date '+%F %T')] [DEBUG]${CL} $*" fi } # Displays error message and immediately terminates script fatal() { msg_error "$1" kill -INT $$ } get_valid_nextid() { local try_id try_id=$(pvesh get /cluster/nextid) while true; do if [ -f "/etc/pve/qemu-server/${try_id}.conf" ] || [ -f "/etc/pve/lxc/${try_id}.conf" ]; then try_id=$((try_id + 1)) continue fi if lvs --noheadings -o lv_name | grep -qE "(^|[-_])${try_id}($|[-_])"; then try_id=$((try_id + 1)) continue fi break done echo "$try_id" } cleanup_vmid() { if [[ -z "${VMID:-}" ]]; then return fi if qm status "$VMID" &>/dev/null; then qm stop "$VMID" &>/dev/null qm destroy "$VMID" &>/dev/null fi } cleanup() { local exit_code=$? if [[ "$(dirs -p | wc -l)" -gt 1 ]]; then popd >/dev/null || true fi # Report final telemetry status if post_to_api_vm was called but no update was sent if [[ "${POST_TO_API_DONE:-}" == "true" && "${POST_UPDATE_DONE:-}" != "true" ]]; then if declare -f post_update_to_api >/dev/null 2>&1; then if [[ $exit_code -ne 0 ]]; then post_update_to_api "failed" "$exit_code" else # Exited cleanly but description()/success was never called — shouldn't happen post_update_to_api "failed" "1" fi fi fi } check_root() { if [[ "$(id -u)" -ne 0 || $(ps -o comm= -p $PPID) == "sudo" ]]; then clear msg_error "Please run this script as root." echo -e "\nExiting..." sleep 2 exit 104 fi } pve_check() { if ! pveversion | grep -Eq "pve-manager/(8\.[1-4]|9\.[0-1])(\.[0-9]+)*"; then msg_error "This version of Proxmox Virtual Environment is not supported" echo -e "Requires Proxmox Virtual Environment Version 8.1 - 8.4 or 9.0 - 9.1." echo -e "Exiting..." sleep 2 exit 105 fi } arch_check() { if [ "$(dpkg --print-architecture)" != "amd64" ]; then echo -e "\n ${INFO}${YWB}This script will not work with PiMox! \n" echo -e "\n ${YWB}Visit https://github.com/asylumexp/Proxmox for ARM64 support. \n" echo -e "Exiting..." sleep 2 exit 106 fi } exit_script() { clear echo -e "\n${CROSS}${RD}User exited script${CL}\n" exit 0 } check_hostname_conflict() { local hostname="$1" if qm list | awk '{print $2}' | grep -qx "$hostname"; then msg_error "Hostname $hostname already in use by another VM." exit 206 fi } set_description() { local app_name script_slug script_url donate_url app_name=$(echo "${APP,,}" | tr ' ' '-') script_slug="${SCRIPT_SLUG:-${app_name}}" script_slug="$(echo "$script_slug" | tr '[:upper:]' '[:lower:]' | tr ' ' '-')" script_url="https://community-scripts.org/scripts/${script_slug}" donate_url="https://community-scripts.org/donate" DESCRIPTION=$( cat < Logo

${NSAPP} VM

Sponsoring and donations

Open script page

GitHub Discussions Issues EOF ) qm set "$VMID" -description "$DESCRIPTION" >/dev/null } ================================================ FILE: tools/addon/add-netbird-lxc.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # Co-Author: MickLesk (Canbiz) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://netbird.io/ | Github: https://github.com/netbirdio/netbird function header_info { clear cat <<"EOF" _ __ __ ____ _ __ / | / /__ / /_/ __ )(_)________/ / / |/ / _ \/ __/ __ / / ___/ __ / / /| / __/ /_/ /_/ / / / / /_/ / /_/ |_/\___/\__/_____/_/_/ \__,_/ EOF } header_info set -e # Telemetry source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func) 2>/dev/null || true declare -f init_tool_telemetry &>/dev/null && init_tool_telemetry "add-netbird-lxc" "addon" while true; do read -p "This will add NetBird to an existing LXC Container ONLY. Proceed(y/n)?" yn case $yn in [Yy]*) break ;; [Nn]*) exit ;; *) echo "Please answer yes or no." ;; esac done header_info echo "Loading..." function msg() { local TEXT="$1" echo -e "$TEXT" } NODE=$(hostname) MSG_MAX_LENGTH=0 while read -r line; do TAG=$(echo "$line" | awk '{print $1}') ITEM=$(echo "$line" | awk '{print substr($0,36)}') OFFSET=2 if [[ $((${#ITEM} + $OFFSET)) -gt ${MSG_MAX_LENGTH:-} ]]; then MSG_MAX_LENGTH=$((${#ITEM} + $OFFSET)) fi CTID_MENU+=("$TAG" "$ITEM " "OFF") done < <(pct list | awk 'NR>1') while [ -z "${CTID:+x}" ]; do CTID=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "Containers on $NODE" --radiolist \ "\nSelect a container to add NetBird to:\n" \ 16 $(($MSG_MAX_LENGTH + 23)) 6 \ "${CTID_MENU[@]}" 3>&1 1>&2 2>&3) done LXC_STATUS=$(pct status "$CTID" | awk '{print $2}') if [[ "$LXC_STATUS" != "running" ]]; then msg "\e[1;33m The container $CTID is not running. Starting it now...\e[0m" pct start "$CTID" while [[ "$(pct status "$CTID" | awk '{print $2}')" != "running" ]]; do msg "\e[1;33m Waiting for the container to start...\e[0m" sleep 2 done msg "\e[1;32m Container $CTID is now running.\e[0m" fi DISTRO=$(pct exec "$CTID" -- cat /etc/os-release | grep -w "ID" | cut -d'=' -f2 | tr -d '"') if [[ "$DISTRO" != "debian" && "$DISTRO" != "ubuntu" ]]; then msg "\e[1;31m Error: This script only supports Debian or Ubuntu LXC containers. Detected: $DISTRO. Aborting...\e[0m" exit 238 fi CTID_CONFIG_PATH=/etc/pve/lxc/${CTID}.conf cat <>$CTID_CONFIG_PATH lxc.cgroup2.devices.allow: c 10:200 rwm lxc.mount.entry: /dev/net/tun dev/net/tun none bind,create=file EOF header_info msg "Installing NetBird..." pct exec "$CTID" -- bash -c ' if ! command -v curl &>/dev/null; then apt-get update -qq apt-get install -y curl >/dev/null fi apt install -y ca-certificates gpg &>/dev/null curl -fsSL "https://pkgs.netbird.io/debian/public.key" | gpg --dearmor >/usr/share/keyrings/netbird-archive-keyring.gpg echo "deb [signed-by=/usr/share/keyrings/netbird-archive-keyring.gpg] https://pkgs.netbird.io/debian stable main" >/etc/apt/sources.list.d/netbird.list apt-get update &>/dev/null apt-get install -y netbird-ui &>/dev/null if systemctl list-unit-files docker.service &>/dev/null; then mkdir -p /etc/systemd/system/netbird.service.d cat </etc/systemd/system/netbird.service.d/after-docker.conf [Unit] After=docker.service Wants=docker.service OVERRIDE systemctl daemon-reload fi ' msg "\e[1;32m ✔ Installed NetBird.\e[0m" sleep 2 msg "\e[1;31m Reboot ${CTID} LXC to apply the changes, then run netbird up in the LXC console\e[0m" ================================================ FILE: tools/addon/add-tailscale-lxc.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE set -Eeuo pipefail trap 'echo -e "\n[ERROR] in line $LINENO: exit code $?"' ERR function header_info() { clear cat <<"EOF" ______ _ __ __ /_ __/___ _(_) /_____________ _/ /__ / / / __ `/ / / ___/ ___/ __ `/ / _ \ / / / /_/ / / (__ ) /__/ /_/ / / __/ /_/ \__,_/_/_/____/\___/\__,_/_/\___/ EOF } function msg_info() { echo -e " \e[1;36m➤\e[0m $1"; } function msg_ok() { echo -e " \e[1;32m✔\e[0m $1"; } function msg_error() { echo -e " \e[1;31m✖\e[0m $1"; } # Telemetry source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func) 2>/dev/null || true declare -f init_tool_telemetry &>/dev/null && init_tool_telemetry "add-tailscale-lxc" "addon" header_info if ! command -v pveversion &>/dev/null; then msg_error "This script must be run on the Proxmox VE host (not inside an LXC container)" exit 232 fi while true; do read -rp "This will add Tailscale to an existing LXC Container ONLY. Proceed (y/n)? " yn case "$yn" in [Yy]*) break ;; [Nn]*) exit 0 ;; *) echo "Please answer yes or no." ;; esac done header_info msg_info "Loading container list..." NODE=$(hostname) MSG_MAX_LENGTH=0 CTID_MENU=() while read -r line; do TAG=$(echo "$line" | awk '{print $1}') ITEM=$(echo "$line" | awk '{print substr($0,36)}') OFFSET=2 ((${#ITEM} + OFFSET > MSG_MAX_LENGTH)) && MSG_MAX_LENGTH=$((${#ITEM} + OFFSET)) CTID_MENU+=("$TAG" "$ITEM" "OFF") done < <(pct list | awk 'NR>1') CTID="" while [[ -z "${CTID}" ]]; do CTID=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "Containers on $NODE" --radiolist \ "\nSelect a container to add Tailscale to:\n" \ 16 $((MSG_MAX_LENGTH + 23)) 6 \ "${CTID_MENU[@]}" 3>&1 1>&2 2>&3) || exit 0 done CTID_CONFIG_PATH="/etc/pve/lxc/${CTID}.conf" # Skip if already configured grep -q "lxc.cgroup2.devices.allow: c 10:200 rwm" "$CTID_CONFIG_PATH" || echo "lxc.cgroup2.devices.allow: c 10:200 rwm" >>"$CTID_CONFIG_PATH" grep -q "lxc.mount.entry: /dev/net/tun" "$CTID_CONFIG_PATH" || echo "lxc.mount.entry: /dev/net/tun dev/net/tun none bind,create=file" >>"$CTID_CONFIG_PATH" header_info msg_info "Installing Tailscale in CT $CTID" pct exec "$CTID" -- sh -c ' set -e # Detect OS inside container if [ -f /etc/alpine-release ]; then # ── Alpine Linux ── echo "[INFO] Alpine Linux detected, installing Tailscale via apk..." # Enable community repo if not already enabled if ! grep -q "^[^#].*community" /etc/apk/repositories 2>/dev/null; then ALPINE_VERSION=$(cat /etc/alpine-release | cut -d. -f1,2) echo "https://dl-cdn.alpinelinux.org/alpine/v${ALPINE_VERSION}/community" >> /etc/apk/repositories fi apk update apk add --no-cache tailscale # Enable and start Tailscale service rc-update add tailscale default 2>/dev/null || true rc-service tailscale start 2>/dev/null || true else # ── Debian / Ubuntu ── export DEBIAN_FRONTEND=noninteractive # Source os-release properly (handles quoted values) . /etc/os-release # Fallback if DNS is poisoned or blocked ORIG_RESOLV="/etc/resolv.conf" BACKUP_RESOLV="/tmp/resolv.conf.backup" # Check DNS resolution using multiple methods (dig may not be installed) dns_check_failed=true if command -v dig >/dev/null 2>&1; then if dig +short pkgs.tailscale.com 2>/dev/null | grep -qvE "^127\.|^0\.0\.0\.0$|^$"; then dns_check_failed=false fi elif command -v host >/dev/null 2>&1; then if host pkgs.tailscale.com 2>/dev/null | grep -q "has address"; then dns_check_failed=false fi elif command -v nslookup >/dev/null 2>&1; then if nslookup pkgs.tailscale.com 2>/dev/null | grep -q "Address:"; then dns_check_failed=false fi elif command -v getent >/dev/null 2>&1; then if getent hosts pkgs.tailscale.com >/dev/null 2>&1; then dns_check_failed=false fi else # No DNS tools available, try curl directly and assume DNS works dns_check_failed=false fi if $dns_check_failed; then echo "[INFO] DNS resolution for pkgs.tailscale.com failed (blocked or redirected)." echo "[INFO] Temporarily overriding /etc/resolv.conf with Cloudflare DNS (1.1.1.1)" cp "$ORIG_RESOLV" "$BACKUP_RESOLV" echo "nameserver 1.1.1.1" >"$ORIG_RESOLV" fi if ! command -v curl >/dev/null 2>&1; then echo "[INFO] curl not found, installing..." apt-get update -qq apt-get install -y curl >/dev/null fi # Ensure keyrings directory exists mkdir -p /usr/share/keyrings curl -fsSL "https://pkgs.tailscale.com/stable/${ID}/${VERSION_CODENAME}.noarmor.gpg" \ | tee /usr/share/keyrings/tailscale-archive-keyring.gpg >/dev/null echo "deb [signed-by=/usr/share/keyrings/tailscale-archive-keyring.gpg] https://pkgs.tailscale.com/stable/${ID} ${VERSION_CODENAME} main" \ >/etc/apt/sources.list.d/tailscale.list apt-get update -qq apt-get install -y tailscale >/dev/null if [ -f /tmp/resolv.conf.backup ]; then echo "[INFO] Restoring original /etc/resolv.conf" mv /tmp/resolv.conf.backup /etc/resolv.conf fi fi ' TAGS=$(awk -F': ' '/^tags:/ {print $2}' "$CTID_CONFIG_PATH") TAGS="${TAGS:+$TAGS; }tailscale" pct set "$CTID" -tags "$TAGS" msg_ok "Tailscale installed on CT $CTID" msg_info "Reboot the container, then run 'tailscale up' inside the container to activate." ================================================ FILE: tools/addon/adguardhome-sync.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/bakito/adguardhome-sync if ! command -v curl &>/dev/null; then printf "\r\e[2K%b" '\033[93m Setup Source \033[m' >&2 if [[ -f "/etc/alpine-release" ]]; then apk -U add curl >/dev/null 2>&1 else apt-get update >/dev/null 2>&1 apt-get install -y curl >/dev/null 2>&1 fi fi source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/core.func) source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/tools.func) source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/error_handler.func) source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/api.func) 2>/dev/null || true # Enable error handling set -Eeuo pipefail trap 'error_handler' ERR # ============================================================================== # CONFIGURATION # ============================================================================== APP="AdGuardHome-Sync" APP_TYPE="addon" INSTALL_PATH="/opt/adguardhome-sync" CONFIG_PATH="/opt/adguardhome-sync/adguardhome-sync.yaml" DEFAULT_PORT=8080 # Initialize all core functions (colors, formatting, icons, STD mode) load_functions # ============================================================================== # HEADER # ============================================================================== function header_info { clear cat <<"EOF" ___ __ ____ __ _____ / | ____/ /___ ___ ______ __________/ / / / /___ ____ ___ ___ / ___/__ ______ _____ / /| |/ __ / __ `/ / / / __ `/ ___/ __ / /_/ / __ \/ __ `__ \/ _ \ \__ \/ / / / __ \/ ___/ / ___ / /_/ / /_/ / /_/ / /_/ / / / /_/ / __ / /_/ / / / / / / __/ ___/ / /_/ / / / / /__ /_/ |_\__,_/\__, /\__,_/\__,_/_/ \__,_/_/ /_/\____/_/ /_/ /_/\___/ /____/\__, /_/ /_/\___/ /____/ /____/ EOF } # ============================================================================== # HELPER FUNCTIONS # ============================================================================== get_ip() { hostname -I 2>/dev/null | awk '{print $1}' || ip -4 addr show scope global 2>/dev/null | awk '/inet / {print $2}' | cut -d/ -f1 | head -n1 || echo "127.0.0.1" } # ============================================================================== # OS DETECTION # ============================================================================== if [[ -f "/etc/alpine-release" ]]; then OS="Alpine" SERVICE_PATH="/etc/init.d/adguardhome-sync" elif [[ -f "/etc/debian_version" ]]; then OS="Debian" SERVICE_PATH="/etc/systemd/system/adguardhome-sync.service" else msg_error "Unsupported OS detected. Exiting." exit 238 fi # ============================================================================== # DEPENDENCY CHECK # ============================================================================== if ! command -v jq &>/dev/null; then printf "\r\e[2K%b" '\033[93m Installing jq \033[m' >&2 if [[ "$OS" == "Alpine" ]]; then apk -U add jq >/dev/null 2>&1 fi fi # ============================================================================== # UNINSTALL # ============================================================================== function uninstall() { msg_info "Uninstalling ${APP}" if [[ "$OS" == "Alpine" ]]; then rc-service adguardhome-sync stop &>/dev/null || true rc-update del adguardhome-sync &>/dev/null || true rm -f "$SERVICE_PATH" else systemctl disable --now adguardhome-sync.service &>/dev/null || true rm -f "$SERVICE_PATH" fi rm -rf "$INSTALL_PATH" rm -f "/usr/local/bin/update_adguardhome-sync" rm -f "$HOME/.adguardhome-sync" msg_ok "${APP} has been uninstalled" } # ============================================================================== # UPDATE # ============================================================================== function update() { if check_for_gh_release "adguardhome-sync" "bakito/adguardhome-sync"; then msg_info "Stopping service" if [[ "$OS" == "Alpine" ]]; then rc-service adguardhome-sync stop &>/dev/null || true else systemctl stop adguardhome-sync.service &>/dev/null || true fi msg_ok "Stopped service" msg_info "Backing up configuration" cp "$CONFIG_PATH" /tmp/adguardhome-sync.yaml.bak 2>/dev/null || true msg_ok "Backed up configuration" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "adguardhome-sync" "bakito/adguardhome-sync" "prebuild" "latest" "$INSTALL_PATH" "adguardhome-sync_*_linux_arm64.tar.gz" msg_info "Restoring configuration" cp /tmp/adguardhome-sync.yaml.bak "$CONFIG_PATH" 2>/dev/null || true rm -f /tmp/adguardhome-sync.yaml.bak msg_ok "Restored configuration" msg_info "Starting service" if [[ "$OS" == "Alpine" ]]; then rc-service adguardhome-sync start else systemctl start adguardhome-sync.service fi msg_ok "Started service" msg_ok "Updated successfully!" exit fi } # ============================================================================== # INSTALL # ============================================================================== function install() { local ip ip=$(get_ip) fetch_and_deploy_gh_release "adguardhome-sync" "bakito/adguardhome-sync" "prebuild" "latest" "$INSTALL_PATH" "adguardhome-sync_*_linux_arm64.tar.gz" # Gather configuration from user echo "" echo -e "${TAB}Enter details for your AdGuard Home instances." echo -e "${TAB}The Origin is your primary instance, Replica will sync from it." echo "" # Origin instance echo -e "${YW}── Origin (Primary) Instance ──${CL}" local origin_url origin_user origin_pass read -rp " Origin URL (e.g., http://192.168.1.1): " origin_url origin_url="${origin_url:-http://192.168.1.1}" # Add http:// if no protocol specified [[ ! "$origin_url" =~ ^https?:// ]] && origin_url="http://${origin_url}" read -rp " Origin Username [admin]: " origin_user origin_user="${origin_user:-admin}" read -rsp " Origin Password: " origin_pass echo "" origin_pass="${origin_pass:-changeme}" # Replica instance echo "" echo -e "${YW}── Replica Instance ──${CL}" local replica_url replica_user replica_pass read -rp " Replica URL (e.g., http://192.168.1.2): " replica_url replica_url="${replica_url:-http://192.168.1.2}" # Add http:// if no protocol specified [[ ! "$replica_url" =~ ^https?:// ]] && replica_url="http://${replica_url}" read -rp " Replica Username [admin]: " replica_user replica_user="${replica_user:-admin}" read -rsp " Replica Password: " replica_pass echo "" replica_pass="${replica_pass:-changeme}" echo "" msg_info "Creating configuration" cat <"$CONFIG_PATH" # AdGuardHome-Sync Configuration # Documentation: https://github.com/bakito/adguardhome-sync # Cron expression for sync interval (e.g., every 2 hours: "0 */2 * * *") cron: "0 */2 * * *" # Run sync on startup runOnStart: true # Continue sync on errors continueOnError: false # Origin AdGuardHome instance (primary) origin: url: "${origin_url}" username: "${origin_user}" password: "${origin_pass}" insecureSkipVerify: false # Replica instances (one or more) replicas: - url: "${replica_url}" username: "${replica_user}" password: "${replica_pass}" insecureSkipVerify: false # Add more replicas as needed: # - url: "http://192.168.1.3" # username: "admin" # password: "changeme" # API settings (web UI) api: port: ${DEFAULT_PORT} darkMode: true metrics: enabled: false # Sync features (all enabled by default) features: dns: accessLists: true serverConfig: true rewrites: true dhcp: serverConfig: true staticLeases: true generalSettings: true queryLogConfig: true statsConfig: true clientSettings: true services: true filters: true theme: true EOF chmod 600 "$CONFIG_PATH" msg_ok "Created configuration" msg_info "Creating service" if [[ "$OS" == "Alpine" ]]; then cat <"$SERVICE_PATH" #!/sbin/openrc-run name="adguardhome-sync" description="AdGuardHome Sync" command="${INSTALL_PATH}/adguardhome-sync" command_args="run --config ${CONFIG_PATH}" command_background=true pidfile="/run/\${RC_SVCNAME}.pid" output_log="/var/log/adguardhome-sync.log" error_log="/var/log/adguardhome-sync.log" depend() { need net after firewall } EOF chmod +x "$SERVICE_PATH" rc-update add adguardhome-sync default rc-service adguardhome-sync start else cat <"$SERVICE_PATH" [Unit] Description=AdGuardHome Sync After=network.target [Service] Type=simple ExecStart=${INSTALL_PATH}/adguardhome-sync run --config ${CONFIG_PATH} Restart=always RestartSec=10 [Install] WantedBy=multi-user.target EOF systemctl daemon-reload systemctl enable --now adguardhome-sync &>/dev/null fi msg_ok "Created and started service" # Create update script msg_info "Creating update script" cat <<'UPDATEEOF' >/usr/local/bin/update_adguardhome-sync #!/usr/bin/env bash # AdGuardHome-Sync Update Script type=update bash -c "$(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/tools/addon/adguardhome-sync.sh)" UPDATEEOF chmod +x /usr/local/bin/update_adguardhome-sync msg_ok "Created update script (/usr/local/bin/update_adguardhome-sync)" echo "" msg_ok "${APP} installed successfully" msg_ok "Web UI: ${BL}http://${ip}:${DEFAULT_PORT}${CL}" msg_ok "Config: ${BL}${CONFIG_PATH}${CL}" echo "" msg_warn "Edit the config file to add your AdGuardHome instances!" msg_warn " Origin: Your primary AdGuardHome instance" msg_warn " Replicas: One or more replica instances to sync to" } # ============================================================================== # MAIN # ============================================================================== # Handle type=update (called from update script) if [[ "${type:-}" == "update" ]]; then header_info if [[ -d "$INSTALL_PATH" && -f "$INSTALL_PATH/adguardhome-sync" ]]; then update else msg_error "${APP} is not installed. Nothing to update." exit 233 fi exit 0 fi header_info IP=$(get_ip) # Check if already installed if [[ -d "$INSTALL_PATH" && -f "$INSTALL_PATH/adguardhome-sync" ]]; then msg_warn "${APP} is already installed." echo "" echo -n "${TAB}Uninstall ${APP}? (y/N): " read -r uninstall_prompt if [[ "${uninstall_prompt,,}" =~ ^(y|yes)$ ]]; then uninstall exit 0 fi echo -n "${TAB}Update ${APP}? (y/N): " read -r update_prompt if [[ "${update_prompt,,}" =~ ^(y|yes)$ ]]; then update exit 0 fi msg_warn "No action selected. Exiting." exit 0 fi # Fresh installation msg_warn "${APP} is not installed." echo "" echo -e "${TAB}${INFO} This will install:" echo -e "${TAB} - AdGuardHome-Sync (Go binary)" echo -e "${TAB} - Systemd/OpenRC service" echo -e "${TAB} - Web UI on port ${DEFAULT_PORT}" echo "" echo -n "${TAB}Install ${APP}? (y/N): " read -r install_prompt if [[ "${install_prompt,,}" =~ ^(y|yes)$ ]]; then install else msg_warn "Installation cancelled. Exiting." exit 0 fi ================================================ FILE: tools/addon/all-templates.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT # https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE function header_info { clear cat <<"EOF" ___ ____ ______ __ __ / _ | / / / /_ __/__ __ _ ___ / /__ _/ /____ ___ / __ |/ / / / / / -_) ' \/ _ \/ / _ `/ __/ -_|_-< /_/ |_/_/_/ /_/ \__/_/_/_/ .__/_/\_,_/\__/\__/___/ /_/ EOF } set -eEuo pipefail shopt -s expand_aliases alias die='EXIT=$? LINE=$LINENO error_exit' trap die ERR function error_exit() { trap - ERR local DEFAULT='Unknown failure occured.' local REASON="\e[97m${1:-$DEFAULT}\e[39m" local FLAG="\e[91m[ERROR] \e[93m$EXIT@$LINE" msg "$FLAG $REASON" 1>&2 [ ! -z ${CTID-} ] && cleanup_ctid exit $EXIT } function warn() { local REASON="\e[97m$1\e[39m" local FLAG="\e[93m[WARNING]\e[39m" msg "$FLAG $REASON" } function info() { local REASON="$1" local FLAG="\e[36m[INFO]\e[39m" msg "$FLAG $REASON" } function msg() { local TEXT="$1" echo -e "$TEXT" } # Telemetry source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func) 2>/dev/null || true declare -f init_tool_telemetry &>/dev/null && init_tool_telemetry "all-templates" "addon" function validate_container_id() { local ctid="$1" # Check if ID is numeric if ! [[ "$ctid" =~ ^[0-9]+$ ]]; then return 1 fi # Check if config file exists for VM or LXC if [[ -f "/etc/pve/qemu-server/${ctid}.conf" ]] || [[ -f "/etc/pve/lxc/${ctid}.conf" ]]; then return 1 fi # Check if ID is used in LVM logical volumes if lvs --noheadings -o lv_name 2>/dev/null | grep -qE "(^|[-_])${ctid}($|[-_])"; then return 1 fi return 0 } function get_valid_container_id() { local suggested_id="${1:-$(pvesh get /cluster/nextid)}" while ! validate_container_id "$suggested_id"; do suggested_id=$((suggested_id + 1)) done echo "$suggested_id" } function cleanup_ctid() { if pct status $CTID &>/dev/null; then if [ "$(pct status $CTID | awk '{print $2}')" == "running" ]; then pct stop $CTID fi pct destroy $CTID fi } # Stop Proxmox VE Monitor-All if running if systemctl is-active -q ping-instances.service; then systemctl stop ping-instances.service fi header_info echo "Loading..." pveam update >/dev/null 2>&1 whiptail --backtitle "Proxmox VE Helper Scripts" --title "All Templates" --yesno "This will allow for the creation of one of the many Template LXC Containers. Proceed?" 10 68 TEMPLATE_MENU=() MSG_MAX_LENGTH=0 while read -r TAG ITEM; do OFFSET=2 ((${#ITEM} + OFFSET > MSG_MAX_LENGTH)) && MSG_MAX_LENGTH=${#ITEM}+OFFSET TEMPLATE_MENU+=("$ITEM" "$TAG " "OFF") done < <(pveam available) TEMPLATE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "All Template LXCs" --radiolist "\nSelect a Template LXC to create:\n" 16 $((MSG_MAX_LENGTH + 58)) 10 "${TEMPLATE_MENU[@]}" 3>&1 1>&2 2>&3 | tr -d '"') [ -z "$TEMPLATE" ] && { whiptail --backtitle "Proxmox VE Helper Scripts" --title "No Template LXC Selected" --msgbox "It appears that no Template LXC container was selected" 10 68 msg "Done" exit } # Setup script environment NAME=$(echo "$TEMPLATE" | grep -oE '^[^-]+-[^-]+') PASS="$(openssl rand -base64 8)" # Get valid Container ID CTID=$(pvesh get /cluster/nextid) if ! validate_container_id "$CTID"; then warn "Container ID $CTID is already in use." CTID=$(get_valid_container_id "$CTID") info "Using next available ID: $CTID" fi PCT_OPTIONS=" -features keyctl=1,nesting=1 -hostname $NAME -tags community-script -onboot 0 -cores 2 -memory 2048 -password $PASS -net0 name=eth0,bridge=vmbr0,ip=dhcp -unprivileged 1 " DEFAULT_PCT_OPTIONS=( -arch $(dpkg --print-architecture) ) # Set the CONTENT and CONTENT_LABEL variables function select_storage() { local CLASS=$1 local CONTENT local CONTENT_LABEL case $CLASS in container) CONTENT='rootdir' CONTENT_LABEL='Container' ;; template) CONTENT='vztmpl' CONTENT_LABEL='Container template' ;; *) false || die "Invalid storage class." ;; esac # Query all storage locations local -a MENU while read -r line; do local TAG=$(echo $line | awk '{print $1}') local TYPE=$(echo $line | awk '{printf "%-10s", $2}') local FREE=$(echo $line | numfmt --field 4-6 --from-unit=K --to=iec --format %.2f | awk '{printf( "%9sB", $6)}') local ITEM=" Type: $TYPE Free: $FREE " local OFFSET=2 if [[ $((${#ITEM} + $OFFSET)) -gt ${MSG_MAX_LENGTH:-} ]]; then local MSG_MAX_LENGTH=$((${#ITEM} + $OFFSET)) fi MENU+=("$TAG" "$ITEM" "OFF") done < <(pvesm status -content $CONTENT | awk 'NR>1') # Select storage location if [ $((${#MENU[@]} / 3)) -eq 0 ]; then warn "'$CONTENT_LABEL' needs to be selected for at least one storage location." die "Unable to detect valid storage location." elif [ $((${#MENU[@]} / 3)) -eq 1 ]; then printf ${MENU[0]} else local STORAGE while [ -z "${STORAGE:+x}" ]; do STORAGE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "Storage Pools" --radiolist \ "Which storage pool would you like to use for the ${CONTENT_LABEL,,}?\n\n" \ 16 $(($MSG_MAX_LENGTH + 23)) 6 \ "${MENU[@]}" 3>&1 1>&2 2>&3) || die "Menu aborted." done printf $STORAGE fi } header_info # Get template storage TEMPLATE_STORAGE=$(select_storage template) info "Using '$TEMPLATE_STORAGE' for template storage." # Get container storage CONTAINER_STORAGE=$(select_storage container) info "Using '$CONTAINER_STORAGE' for container storage." # Download template msg "Downloading LXC template (Patience)..." pveam download $TEMPLATE_STORAGE $TEMPLATE >/dev/null || die "A problem occured while downloading the LXC template." # Create variable for 'pct' options PCT_OPTIONS=(${PCT_OPTIONS[@]:-${DEFAULT_PCT_OPTIONS[@]}}) [[ " ${PCT_OPTIONS[@]} " =~ " -rootfs " ]] || PCT_OPTIONS+=(-rootfs $CONTAINER_STORAGE:${PCT_DISK_SIZE:-8}) # Create LXC msg "Creating LXC container..." pct create $CTID ${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE} ${PCT_OPTIONS[@]} >/dev/null || die "A problem occured while trying to create container." # Save password echo "$NAME password: ${PASS}" >>~/$NAME.creds # file is located in the Proxmox root directory # Start container msg "Starting LXC Container..." pct start "$CTID" sleep 5 # Get container IP set +eEuo pipefail max_attempts=5 attempt=1 IP="" while [[ $attempt -le $max_attempts ]]; do IP=$(pct exec $CTID ip a show dev eth0 | grep -oP 'inet \K[^/]+') if [[ -n $IP ]]; then break else warn "Attempt $attempt: IP address not found. Pausing for 5 seconds..." sleep 5 ((attempt++)) fi done if [[ -z $IP ]]; then warn "Maximum number of attempts reached. IP address not found." IP="NOT FOUND" fi set -eEuo pipefail # Start Proxmox VE Monitor-All if available if [[ -f /etc/systemd/system/ping-instances.service ]]; then systemctl start ping-instances.service fi # Success message header_info echo info "LXC container '$CTID' was successfully created, and its IP address is ${IP}." echo info "Proceed to the LXC console to complete the setup." echo info "login: root" info "password: $PASS" echo ================================================ FILE: tools/addon/arcane.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: summoningpixels # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/getarcaneapp/arcane if ! command -v curl &>/dev/null; then printf "\r\e[2K%b" '\033[93m Setup Source \033[m' >&2 apt-get update >/dev/null 2>&1 apt-get install -y curl >/dev/null 2>&1 fi source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/core.func) source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/tools.func) source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/error_handler.func) source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func) 2>/dev/null || true declare -f init_tool_telemetry &>/dev/null && init_tool_telemetry "arcane" "addon" # Enable error handling set -Eeuo pipefail trap 'error_handler' ERR # ============================================================================== # CONFIGURATION # ============================================================================== APP="Arcane" APP_TYPE="addon" INSTALL_PATH="/opt/arcane" COMPOSE_FILE="${INSTALL_PATH}/compose.yaml" ENV_FILE="${INSTALL_PATH}/.env" DEFAULT_PORT=3552 # Initialize all core functions (colors, formatting, icons, STD mode) load_functions # ============================================================================== # HEADER # ============================================================================== function header_info { clear cat <<"EOF" ___ ____ _________ _ ________ / | / __ \/ ____/ | / | / / ____/ / /| | / /_/ / / / /| | / |/ / __/ / ___ |/ _, _/ /___/ ___ |/ /| / /___ /_/ |_/_/ |_|\____/_/ |_/_/ |_/_____/ EOF } # ============================================================================== # UNINSTALL # ============================================================================== function uninstall() { msg_info "Uninstalling ${APP}" if [[ -f "$COMPOSE_FILE" ]]; then msg_info "Stopping and removing Docker containers" cd "$INSTALL_PATH" $STD docker compose down --volumes --remove-orphans msg_ok "Stopped and removed Docker containers" fi rm -rf "$INSTALL_PATH" rm -f "/usr/local/bin/update_arcane" msg_ok "${APP} has been uninstalled" } # ============================================================================== # UPDATE # ============================================================================== function update() { msg_info "Pulling latest ${APP} image" cd "$INSTALL_PATH" $STD docker compose pull msg_ok "Pulled latest image" msg_info "Restarting ${APP}" $STD docker compose up -d --remove-orphans msg_ok "Restarted ${APP}" msg_ok "Updated successfully" exit } # ============================================================================== # CHECK DOCKER # ============================================================================== function check_docker() { if ! command -v docker &>/dev/null; then msg_error "Docker is not installed. This script requires an existing Docker LXC. Exiting." exit 10 fi if ! docker compose version &>/dev/null; then msg_error "Docker Compose plugin is not available. Please install it before running this script. Exiting." exit 10 fi msg_ok "Docker $(docker --version | cut -d' ' -f3 | tr -d ',') and Docker Compose are available" } # ============================================================================== # INSTALL # ============================================================================== function install() { check_docker msg_info "Creating install directory" mkdir -p "$INSTALL_PATH" msg_ok "Created ${INSTALL_PATH}" # Generate secrets and config values local ENCRYPTION_KEY JWT_SECRET PROJ_DIR ENCRYPTION_KEY=$(openssl rand -base64 32 | tr -dc 'a-zA-Z0-9' | head -c32) JWT_SECRET=$(openssl rand -base64 32 | tr -dc 'a-zA-Z0-9' | head -c32) PROJ_DIR="/etc/arcane/projects" msg_info "Creating stacks directory" mkdir -p "$PROJ_DIR" msg_ok "Created ${PROJ_DIR}" msg_info "Downloading Docker Compose file" curl -fsSL "https://raw.githubusercontent.com/getarcaneapp/arcane/refs/heads/main/docker/examples/compose.basic.yaml" -o "$COMPOSE_FILE" msg_ok "Downloaded Docker Compose file" msg_info "Downloading .env file" curl -fsSL "https://raw.githubusercontent.com/getarcaneapp/arcane/refs/heads/main/.env.example" -o "$ENV_FILE" chmod 600 "$ENV_FILE" msg_ok "Downloaded .env file" msg_info "Configuring compose and env files" sed -i '/^[[:space:]]*#/!s|/host/path/to/projects|'"$PROJ_DIR"'|g' "$COMPOSE_FILE" sed -i '/^[[:space:]]*#/!s|ENCRYPTION_KEY=.*|ENCRYPTION_KEY='"$ENCRYPTION_KEY"'|g' "$COMPOSE_FILE" sed -i '/^[[:space:]]*#/!s|JWT_SECRET=.*|JWT_SECRET='"$JWT_SECRET"'|g' "$COMPOSE_FILE" sed -i '/^[[:space:]]*#/!s|APP_URL=.*|APP_URL=http://localhost:'"$DEFAULT_PORT"'|g' "$ENV_FILE" sed -i '/^[[:space:]]*#/!s|ENCRYPTION_KEY=.*|#&|g' "$ENV_FILE" sed -i '/^[[:space:]]*#/!s|JWT_SECRET=.*|#&|g' "$ENV_FILE" msg_ok "Configured compose and env files" msg_info "Starting ${APP}" cd "$INSTALL_PATH" $STD docker compose up -d msg_ok "Started ${APP}" # Create update script msg_info "Creating update script" cat <<'UPDATEEOF' >/usr/local/bin/update_arcane #!/usr/bin/env bash # Arcane Update Script type=update bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/tools/addon/arcane.sh)" UPDATEEOF chmod +x /usr/local/bin/update_arcane msg_ok "Created update script (/usr/local/bin/update_arcane)" echo "" msg_ok "${APP} is reachable at: ${BL}http://${LOCAL_IP}:${DEFAULT_PORT}${CL}" echo "" echo -e "Arcane Credentials" echo -e "==================" echo -e "User: arcane" echo -e "Password: arcane-admin" echo "" msg_warn "On first access, you'll be prompted to change your password." } # ============================================================================== # MAIN # ============================================================================== # Handle type=update (called from update script) if [[ "${type:-}" == "update" ]]; then header_info if [[ -f "$COMPOSE_FILE" ]]; then update else msg_error "${APP} is not installed. Nothing to update." exit 233 fi exit 0 fi header_info get_lxc_ip # Check if already installed if [[ -f "$COMPOSE_FILE" ]]; then msg_warn "${APP} is already installed." echo "" echo -n "${TAB}Uninstall ${APP}? (y/N): " read -r uninstall_prompt if [[ "${uninstall_prompt,,}" =~ ^(y|yes)$ ]]; then uninstall exit 0 fi echo -n "${TAB}Update ${APP}? (y/N): " read -r update_prompt if [[ "${update_prompt,,}" =~ ^(y|yes)$ ]]; then update exit 0 fi msg_warn "No action selected. Exiting." exit 0 fi # Fresh installation msg_warn "${APP} is not installed." echo "" echo -e "${TAB}${INFO} This will install:" echo -e "${TAB} - Arcane (via Docker Compose)" echo "" echo -n "${TAB}Install ${APP}? (y/N): " read -r install_prompt if [[ "${install_prompt,,}" =~ ^(y|yes)$ ]]; then install else msg_warn "Installation cancelled. Exiting." exit 0 fi ================================================ FILE: tools/addon/coder-code-server.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://coder.com/ | Github: https://github.com/coder/code-server function header_info { cat <<"EOF" ______ __ _____ / ____/___ ____/ /__ / ___/___ ______ _____ _____ / / / __ \/ __ / _ \ \__ \/ _ \/ ___/ | / / _ \/ ___/ / /___/ /_/ / /_/ / __/ ___/ / __/ / | |/ / __/ / \____/\____/\__,_/\___/ /____/\___/_/ |___/\___/_/ EOF } IP=$(hostname -I | awk '{print $1}') YW=$(echo "\033[33m") BL=$(echo "\033[36m") RD=$(echo "\033[01;31m") BGN=$(echo "\033[4;92m") GN=$(echo "\033[1;92m") DGN=$(echo "\033[32m") CL=$(echo "\033[m") BFR="\\r\\033[K" HOLD="-" CM="${GN}✓${CL}" APP="Coder Code Server" hostname="$(hostname)" # Telemetry source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func) 2>/dev/null || true declare -f init_tool_telemetry &>/dev/null && init_tool_telemetry "coder-code-server" "addon" set -o errexit set -o errtrace set -o nounset set -o pipefail shopt -s expand_aliases alias die='EXIT=$? LINE=$LINENO error_exit' trap die ERR function error_exit() { trap - ERR local reason="Unknown failure occured." local msg="${1:-$reason}" local flag="${RD}‼ ERROR ${CL}$EXIT@$LINE" echo -e "$flag $msg" 1>&2 exit "$EXIT" } clear header_info if command -v pveversion >/dev/null 2>&1; then echo -e "⚠️ Can't Install on Proxmox " exit fi if [ -e /etc/alpine-release ]; then echo -e "⚠️ Can't Install on Alpine" exit fi while true; do read -p "This will Install ${APP} on $hostname. Proceed(y/n)?" yn case $yn in [Yy]*) break ;; [Nn]*) exit ;; *) echo "Please answer yes or no." ;; esac done function msg_info() { local msg="$1" echo -ne " ${HOLD} ${YW}${msg}..." } function msg_ok() { local msg="$1" echo -e "${BFR} ${CM} ${GN}${msg}${CL}" } msg_info "Installing Dependencies" apt-get update &>/dev/null apt-get install -y curl &>/dev/null apt-get install -y git &>/dev/null msg_ok "Installed Dependencies" VERSION=$(curl -fsSL https://api.github.com/repos/coder/code-server/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4) }') msg_info "Installing Code-Server v${VERSION}" curl -fOL https://github.com/coder/code-server/releases/download/v"$VERSION"/code-server_"${VERSION}"_arm64.deb &>/dev/null dpkg -i code-server_"${VERSION}"_arm64.deb &>/dev/null rm -rf code-server_"${VERSION}"_arm64.deb mkdir -p ~/.config/code-server/ cat <~/.config/code-server/config.yaml bind-addr: 0.0.0.0:8680 auth: none password: cert: false EOF systemctl enable -q --now code-server@"$USER" systemctl restart code-server@"$USER" if ! systemctl is-active --quiet code-server@"$USER"; then error_exit "code-server service failed to start." fi msg_ok "Installed Code-Server v${VERSION} on $hostname" echo -e "${APP} should be reachable by going to the following URL. ${BL}http://$IP:8680${CL} \n" ================================================ FILE: tools/addon/coolify.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://coolify.io/ | Github: https://github.com/coollabsio/coolify if ! command -v curl &>/dev/null; then printf "\r\e[2K%b" '\033[93m Setup Source \033[m' >&2 if [[ -f /etc/alpine-release ]]; then apk update >/dev/null 2>&1 apk add --no-cache curl >/dev/null 2>&1 else apt-get update >/dev/null 2>&1 apt-get install -y curl >/dev/null 2>&1 fi fi source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/core.func) source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/tools.func) source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/error_handler.func) source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func) 2>/dev/null || true declare -f init_tool_telemetry &>/dev/null && init_tool_telemetry "coolify" "addon" # Enable error handling set -Eeuo pipefail trap 'error_handler' ERR # ============================================================================== # CONFIGURATION # ============================================================================== APP="Coolify" APP_TYPE="addon" INSTALL_PATH="/data/coolify" DEFAULT_PORT=8000 # Initialize all core functions (colors, formatting, icons, STD mode) load_functions # ============================================================================== # UNINSTALL # ============================================================================== function uninstall() { msg_info "Uninstalling ${APP}" if command -v docker &>/dev/null; then msg_info "Stopping and removing Docker containers" cd /data/coolify/source 2>/dev/null && docker compose down --remove-orphans 2>/dev/null || true $STD docker stop $(docker ps -aq) 2>/dev/null || true $STD docker rm $(docker ps -aq) 2>/dev/null || true $STD docker network prune -f 2>/dev/null || true msg_ok "Stopped and removed Docker containers" fi rm -rf "$INSTALL_PATH" msg_ok "${APP} has been uninstalled" } # ============================================================================== # UPDATE # ============================================================================== function update() { msg_info "Updating ${APP}" $STD bash <(curl -fsSL https://cdn.coollabs.io/coolify/install.sh) msg_ok "Updated ${APP}" msg_ok "Updated successfully" exit } # ============================================================================== # PROXMOX HOST CHECK # ============================================================================== function check_proxmox_host() { if command -v pveversion &>/dev/null; then msg_error "Running on the Proxmox host is NOT recommended!" msg_error "This should be executed inside an LXC container." echo "" echo -n "${TAB}Continue anyway? (y/N): " read -r confirm if [[ ! "${confirm,,}" =~ ^(y|yes)$ ]]; then msg_warn "Aborted. Please run this inside an LXC container." exit 0 fi msg_warn "Proceeding on Proxmox host at your own risk!" fi } # ============================================================================== # CHECK / INSTALL DOCKER # ============================================================================== function check_or_install_docker() { if command -v docker &>/dev/null; then msg_ok "Docker $(docker --version | cut -d' ' -f3 | tr -d ',') is available" if docker compose version &>/dev/null; then msg_ok "Docker Compose is available" else msg_error "Docker Compose plugin is not available. Please install it." exit 10 fi return fi msg_warn "Docker is not installed." echo -n "${TAB}Install Docker now? (y/N): " read -r install_docker_prompt if [[ ! "${install_docker_prompt,,}" =~ ^(y|yes)$ ]]; then msg_error "Docker is required for ${APP}. Exiting." exit 10 fi msg_info "Installing Docker" if [[ -f /etc/alpine-release ]]; then $STD apk add docker docker-cli-compose $STD rc-service docker start $STD rc-update add docker default else DOCKER_CONFIG_PATH='/etc/docker/daemon.json' mkdir -p "$(dirname "$DOCKER_CONFIG_PATH")" echo -e '{\n "log-driver": "journald"\n}' >"$DOCKER_CONFIG_PATH" $STD sh <(curl -fsSL https://get.docker.com) fi msg_ok "Installed Docker" } # ============================================================================== # INSTALL # ============================================================================== function install() { check_or_install_docker msg_info "Installing dependencies" if [[ -f /etc/alpine-release ]]; then $STD apk add --no-cache git openssl else $STD apt-get update $STD apt-get install -y git openssl fi msg_ok "Installed dependencies" msg_warn "WARNING: This will run an external installer from https://coolify.io/" msg_warn "The following code is NOT maintained or audited by our repository." msg_warn "Review: https://cdn.coollabs.io/coolify/install.sh" echo "" echo -n "${TAB}Do you want to continue? (y/N): " read -r confirm if [[ ! "${confirm,,}" =~ ^(y|yes)$ ]]; then msg_warn "Installation cancelled. Exiting." exit 0 fi msg_info "Installing ${APP} (this installs Docker and pulls containers)" $STD bash <(curl -fsSL https://cdn.coollabs.io/coolify/install.sh) msg_ok "Installed ${APP}" echo "" msg_ok "${APP} is reachable at: ${BL}http://${LOCAL_IP}:${DEFAULT_PORT}${CL}" } # ============================================================================== # MAIN # ============================================================================== # Handle type=update (called from update script) if [[ "${type:-}" == "update" ]]; then header_info if [[ -d "$INSTALL_PATH" ]]; then update else msg_error "${APP} is not installed. Nothing to update." exit 233 fi exit 0 fi header_info check_proxmox_host get_lxc_ip # Check if already installed if [[ -d "$INSTALL_PATH" ]]; then msg_warn "${APP} is already installed." echo "" echo -n "${TAB}Uninstall ${APP}? (y/N): " read -r uninstall_prompt if [[ "${uninstall_prompt,,}" =~ ^(y|yes)$ ]]; then uninstall exit 0 fi echo -n "${TAB}Update ${APP}? (y/N): " read -r update_prompt if [[ "${update_prompt,,}" =~ ^(y|yes)$ ]]; then update exit 0 fi msg_warn "No action selected. Exiting." exit 0 fi # Fresh installation msg_warn "${APP} is not installed." echo "" echo -e "${TAB}${INFO} This will install:" echo -e "${TAB} - Coolify (via external installer)" echo -e "${TAB} - Docker (if not already installed)" echo "" echo -n "${TAB}Install ${APP}? (y/N): " read -r install_prompt if [[ "${install_prompt,,}" =~ ^(y|yes)$ ]]; then install else msg_warn "Installation cancelled. Exiting." exit 0 fi ================================================ FILE: tools/addon/copyparty.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/9001/copyparty if ! command -v curl &>/dev/null; then printf "\r\e[2K%b" '\033[93m Setup Source \033[m' >&2 apt-get update >/dev/null 2>&1 apt-get install -y curl >/dev/null 2>&1 fi source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/core.func) source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/tools.func) source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/error_handler.func) source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func) 2>/dev/null || true declare -f init_tool_telemetry &>/dev/null && init_tool_telemetry "copyparty" "addon" # Enable error handling set -Eeuo pipefail trap 'error_handler' ERR load_functions # ============================================================================== # CONFIGURATION # ============================================================================== VERBOSE=${var_verbose:-no} APP="CopyParty" APP_TYPE="addon" BIN_PATH="/usr/local/bin/copyparty-sfx.py" CONF_PATH="/etc/copyparty.conf" LOG_PATH="/var/log/copyparty" DATA_PATH="/var/lib/copyparty" SVC_USER="copyparty" SVC_GROUP="copyparty" SRC_URL="https://github.com/9001/copyparty/releases/latest/download/copyparty-sfx.py" DEFAULT_PORT=3923 # ============================================================================== # OS DETECTION # ============================================================================== if [[ -f "/etc/alpine-release" ]]; then OS="Alpine" PKG_MANAGER="apk add --no-cache" SERVICE_PATH="/etc/init.d/copyparty" elif grep -qE 'ID=debian|ID=ubuntu' /etc/os-release; then OS="Debian" PKG_MANAGER="apt-get install -y" SERVICE_PATH="/etc/systemd/system/copyparty.service" else msg_error "Unsupported OS detected. Exiting." exit 238 fi # ============================================================================== # HEADER # ============================================================================== function header_info() { clear cat <<"EOF" ______ ____ __ / ____/___ ____ __ __/ __ \____ ______/ /___ __ / / / __ \/ __ \/ / / / /_/ / __ `/ ___/ __/ / / / / /___/ /_/ / /_/ / /_/ / ____/ /_/ / / / /_/ /_/ / \____/\____/ .___/\__, /_/ \__,_/_/ \__/\__, / /_/ /____/ /____/ EOF } # ============================================================================== # HELPER FUNCTIONS # ============================================================================== # ============================================================================== # HELPER FUNCTIONS # ============================================================================== function setup_user_and_dirs() { msg_info "Creating $SVC_USER user and directories" if ! id "$SVC_USER" &>/dev/null; then if [[ "$OS" == "Debian" ]]; then useradd -r -s /sbin/nologin -d "$DATA_PATH" "$SVC_USER" else addgroup -S "$SVC_GROUP" 2>/dev/null || true adduser -S -D -H -G "$SVC_GROUP" -h "$DATA_PATH" -s /sbin/nologin "$SVC_USER" 2>/dev/null || true fi fi mkdir -p "$DATA_PATH" "$LOG_PATH" chown -R "$SVC_USER:$SVC_GROUP" "$DATA_PATH" "$LOG_PATH" chmod 755 "$DATA_PATH" "$LOG_PATH" msg_ok "User/Group/Dirs ready" } # ============================================================================== # UNINSTALL # ============================================================================== function uninstall() { msg_info "Uninstalling ${APP}" if [[ "$OS" == "Alpine" ]]; then rc-service copyparty stop &>/dev/null || true rc-update del copyparty &>/dev/null || true rm -f "$SERVICE_PATH" else systemctl disable --now copyparty.service &>/dev/null || true rm -f "$SERVICE_PATH" fi rm -f "$BIN_PATH" "$CONF_PATH" rm -rf "$DATA_PATH" "$LOG_PATH" userdel "$SVC_USER" 2>/dev/null || true groupdel "$SVC_GROUP" 2>/dev/null || true rm -f "/usr/local/bin/update_copyparty" rm -f "$HOME/.copyparty" msg_ok "${APP} has been uninstalled" } # ============================================================================== # UPDATE # ============================================================================== function update() { if check_for_gh_release "copyparty-sfx.py" "9001/copyparty"; then msg_info "Stopping service" if [[ "$OS" == "Alpine" ]]; then rc-service copyparty stop &>/dev/null || true else systemctl stop copyparty.service &>/dev/null || true fi msg_ok "Stopped service" msg_info "Updating ${APP}" curl -fsSL "$SRC_URL" -o "$BIN_PATH" chmod +x "$BIN_PATH" chown "$SVC_USER:$SVC_GROUP" "$BIN_PATH" msg_ok "Updated ${APP}" msg_info "Starting service" if [[ "$OS" == "Alpine" ]]; then rc-service copyparty start else systemctl start copyparty.service fi msg_ok "Started service" msg_ok "Updated successfully!" exit fi } # ============================================================================== # INSTALL # ============================================================================== function install() { local port data_path enable_auth admin_user admin_pass echo "" read -rp "${TAB}Enter port for ${APP} [${DEFAULT_PORT}]: " port port=${port:-$DEFAULT_PORT} read -rp "${TAB}Set data directory [${DATA_PATH}]: " data_path data_path=${data_path:-$DATA_PATH} echo -n "${TAB}Enable authentication? (Y/n): " read -r enable_auth if [[ "${enable_auth,,}" =~ ^(n|no)$ ]]; then admin_user="" admin_pass="" msg_ok "Configured without authentication" else read -rp "${TAB}Set admin username [admin]: " admin_user admin_user=${admin_user:-admin} read -rsp "${TAB}Set admin password [community-scripts.org]: " admin_pass echo "" admin_pass=${admin_pass:-community-scripts.org} msg_ok "Configured with admin user: ${admin_user}" fi msg_info "Installing dependencies" if [[ "$OS" == "Debian" ]]; then $STD $PKG_MANAGER python3 python3-pil ffmpeg curl else $STD $PKG_MANAGER python3 py3-pillow ffmpeg curl fi msg_ok "Dependencies installed (with thumbnail support)" setup_user_and_dirs # Use data_path if provided if [[ "$data_path" != "$DATA_PATH" ]]; then DATA_PATH="$data_path" mkdir -p "$DATA_PATH" chown "$SVC_USER:$SVC_GROUP" "$DATA_PATH" fi msg_info "Downloading ${APP}" curl -fsSL "$SRC_URL" -o "$BIN_PATH" chmod +x "$BIN_PATH" chown "$SVC_USER:$SVC_GROUP" "$BIN_PATH" msg_ok "Downloaded to ${BIN_PATH}" msg_info "Creating configuration" cat <"$CONF_PATH" [global] p: ${port} ansi e2dsa e2ts theme: 2 grid no-robots force-js lo: ${LOG_PATH}/cpp-%Y-%m%d.txt.xz EOF if [[ -n "$admin_user" && -n "$admin_pass" ]]; then cat <>"$CONF_PATH" [accounts] ${admin_user}: ${admin_pass} EOF fi cat <>"$CONF_PATH" [/] ${DATA_PATH} accs: EOF if [[ -n "$admin_user" ]]; then cat <>"$CONF_PATH" rw: * rwmda: ${admin_user} EOF else cat <>"$CONF_PATH" rw: * EOF fi chmod 640 "$CONF_PATH" chown "$SVC_USER:$SVC_GROUP" "$CONF_PATH" msg_ok "Created configuration" msg_info "Creating service" if [[ "$OS" == "Alpine" ]]; then cat <<'SERVICEEOF' >"$SERVICE_PATH" #!/sbin/openrc-run name="copyparty" description="CopyParty file server" command="$(command -v python3)" command_args="/usr/local/bin/copyparty-sfx.py -c /etc/copyparty.conf" command_background=true directory="/var/lib/copyparty" pidfile="/run/copyparty.pid" output_log="/var/log/copyparty/copyparty.log" error_log="/var/log/copyparty/copyparty.err" depend() { need net } SERVICEEOF chmod +x "$SERVICE_PATH" $STD rc-update add copyparty default $STD rc-service copyparty start else cat <"$SERVICE_PATH" [Unit] Description=CopyParty file server After=network.target [Service] User=${SVC_USER} Group=${SVC_GROUP} WorkingDirectory=${DATA_PATH} ExecStart=/usr/bin/python3 ${BIN_PATH} -c ${CONF_PATH} Restart=always StandardOutput=append:${LOG_PATH}/copyparty.log StandardError=append:${LOG_PATH}/copyparty.err [Install] WantedBy=multi-user.target SERVICEEOF systemctl daemon-reload systemctl enable --now copyparty.service &>/dev/null fi msg_ok "Created and started service" # Create update script msg_info "Creating update script" ensure_usr_local_bin_persist cat <<'UPDATEEOF' >/usr/local/bin/update_copyparty #!/usr/bin/env bash # CopyParty Update Script type=update bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/tools/addon/copyparty.sh)" UPDATEEOF chmod +x /usr/local/bin/update_copyparty msg_ok "Created update script (/usr/local/bin/update_copyparty)" echo "" msg_ok "${APP} installed successfully" msg_ok "Web UI: ${BL}http://${LOCAL_IP}:${port}${CL}" msg_ok "Storage: ${BL}${DATA_PATH}${CL}" msg_ok "Config: ${BL}${CONF_PATH}${CL}" if [[ -n "$admin_user" ]]; then echo "" msg_ok "Login: ${GN}${admin_user}${CL} / ${GN}${admin_pass}${CL}" fi } # ============================================================================== # MAIN # ============================================================================== header_info ensure_usr_local_bin_persist get_lxc_ip # Handle type=update (called from update script) if [[ "${type:-}" == "update" ]]; then if [[ -f "$BIN_PATH" ]]; then update else msg_error "${APP} is not installed. Nothing to update." exit 233 fi exit 0 fi # Check if already installed if [[ -f "$BIN_PATH" ]]; then msg_warn "${APP} is already installed." echo "" echo -n "${TAB}Uninstall ${APP}? (y/N): " read -r uninstall_prompt if [[ "${uninstall_prompt,,}" =~ ^(y|yes)$ ]]; then uninstall exit 0 fi echo -n "${TAB}Update ${APP}? (y/N): " read -r update_prompt if [[ "${update_prompt,,}" =~ ^(y|yes)$ ]]; then update exit 0 fi msg_warn "No action selected. Exiting." exit 0 fi # Fresh installation msg_warn "${APP} is not installed." echo "" echo -e "${TAB}${INFO} This will install:" echo -e "${TAB} - CopyParty (Python file server)" echo -e "${TAB} - Thumbnail support (Pillow, FFmpeg)" echo -e "${TAB} - Systemd/OpenRC service" echo "" echo -n "${TAB}Install ${APP}? (y/N): " read -r install_prompt if [[ "${install_prompt,,}" =~ ^(y|yes)$ ]]; then install else msg_warn "Installation cancelled. Exiting." exit 0 fi ================================================ FILE: tools/addon/cronmaster.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/fccview/cronmaster if ! command -v curl &>/dev/null; then printf "\r\e[2K%b" '\033[93m Setup Source \033[m' >&2 apt-get update >/dev/null 2>&1 apt-get install -y curl >/dev/null 2>&1 fi source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/core.func) source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/tools.func) source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/error_handler.func) source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func) 2>/dev/null || true declare -f init_tool_telemetry &>/dev/null && init_tool_telemetry "cronmaster" "addon" # Enable error handling set -Eeuo pipefail trap 'error_handler' ERR load_functions # ============================================================================== # CONFIGURATION # ============================================================================== APP="CronMaster" APP_TYPE="addon" INSTALL_PATH="/opt/cronmaster" CONFIG_PATH="/opt/cronmaster/.env" SERVICE_PATH="/etc/systemd/system/cronmaster.service" DEFAULT_PORT=3000 # ============================================================================== # HEADER # ============================================================================== function header_info { clear cat <<"EOF" ______ __ ___ __ / ____/________ ____ / |/ /___ ______/ /____ _____ / / / ___/ __ \/ __ \/ /|_/ / __ `/ ___/ __/ _ \/ ___/ / /___/ / / /_/ / / / / / / / /_/ (__ ) /_/ __/ / \____/_/ \____/_/ /_/_/ /_/\__,_/____/\__/\___/_/ EOF } # ============================================================================== # OS DETECTION # ============================================================================== if ! grep -qE 'ID=debian|ID=ubuntu' /etc/os-release 2>/dev/null; then echo -e "${CROSS} Unsupported OS detected. This script only supports Debian and Ubuntu." exit 238 fi # ============================================================================== # UNINSTALL # ============================================================================== function uninstall() { msg_info "Uninstalling ${APP}" systemctl disable --now cronmaster.service &>/dev/null || true rm -f "$SERVICE_PATH" rm -rf "$INSTALL_PATH" rm -f "/usr/local/bin/update_cronmaster" rm -f "$HOME/.cronmaster" rm -f "/root/cronmaster.creds" msg_ok "${APP} has been uninstalled" } # ============================================================================== # UPDATE # ============================================================================== function update() { if check_for_gh_release "cronmaster" "fccview/cronmaster"; then msg_info "Stopping service" systemctl stop cronmaster.service &>/dev/null || true msg_ok "Stopped service" msg_info "Backing up configuration" cp "$CONFIG_PATH" /tmp/cronmaster.env.bak 2>/dev/null || true msg_ok "Backed up configuration" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "cronmaster" "fccview/cronmaster" "prebuild" "latest" "$INSTALL_PATH" "cronmaster_*_prebuild.tar.gz" msg_info "Restoring configuration" cp /tmp/cronmaster.env.bak "$CONFIG_PATH" 2>/dev/null || true rm -f /tmp/cronmaster.env.bak msg_ok "Restored configuration" msg_info "Starting service" systemctl start cronmaster msg_ok "Started service" msg_ok "Updated successfully" exit fi } # ============================================================================== # INSTALL # ============================================================================== function install() { # Setup Node.js (only installs if not present or different version) if command -v node &>/dev/null; then msg_ok "Node.js already installed ($(node -v))" else NODE_VERSION="22" setup_nodejs fi fetch_and_deploy_gh_release "cronmaster" "fccview/cronmaster" "prebuild" "latest" "$INSTALL_PATH" "cronmaster_*_prebuild.tar.gz" local AUTH_PASS AUTH_PASS="$(openssl rand -base64 18 | cut -c1-13)" msg_info "Creating configuration" cat <"$CONFIG_PATH" NODE_ENV=production AUTH_PASSWORD=${AUTH_PASS} PORT=${DEFAULT_PORT} HOSTNAME=0.0.0.0 NEXT_TELEMETRY_DISABLED=1 EOF chmod 600 "$CONFIG_PATH" msg_ok "Created configuration" msg_info "Creating service" cat <"$SERVICE_PATH" [Unit] Description=CronMaster Service After=network.target [Service] Type=simple User=root WorkingDirectory=${INSTALL_PATH} EnvironmentFile=${CONFIG_PATH} ExecStart=/usr/bin/node ${INSTALL_PATH}/server.js Restart=always RestartSec=10 [Install] WantedBy=multi-user.target EOF systemctl enable -q --now cronmaster msg_ok "Created and started service" # Create update script msg_info "Creating update script" ensure_usr_local_bin_persist cat <<'EOF' >/usr/local/bin/update_cronmaster #!/usr/bin/env bash # CronMaster Update Script type=update bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/tools/addon/cronmaster.sh)" EOF chmod +x /usr/local/bin/update_cronmaster msg_ok "Created update script (/usr/local/bin/update_cronmaster)" # Save credentials local CREDS_FILE="/root/cronmaster.creds" cat <"$CREDS_FILE" CronMaster Credentials ====================== Password: ${AUTH_PASS} Web UI: http://${LOCAL_IP}:${DEFAULT_PORT} EOF echo "" msg_ok "${APP} is reachable at: ${BL}http://${LOCAL_IP}:${DEFAULT_PORT}${CL}" msg_ok "Credentials saved to: ${BL}${CREDS_FILE}${CL}" echo "" } # ============================================================================== # MAIN # ============================================================================== header_info ensure_usr_local_bin_persist get_lxc_ip # Handle type=update (called from update script) if [[ "${type:-}" == "update" ]]; then if [[ -d "$INSTALL_PATH" ]]; then update else msg_error "${APP} is not installed. Nothing to update." exit 233 fi exit 0 fi # Check if already installed if [[ -d "$INSTALL_PATH" && -n "$(ls -A "$INSTALL_PATH" 2>/dev/null)" ]]; then msg_warn "${APP} is already installed." echo "" echo -n "${TAB}Uninstall ${APP}? (y/N): " read -r uninstall_prompt if [[ "${uninstall_prompt,,}" =~ ^(y|yes)$ ]]; then uninstall exit 0 fi echo -n "${TAB}Update ${APP}? (y/N): " read -r update_prompt if [[ "${update_prompt,,}" =~ ^(y|yes)$ ]]; then update exit 0 fi msg_warn "No action selected. Exiting." exit 0 fi # Fresh installation msg_warn "${APP} is not installed." echo "" echo -e "${TAB}${INFO} This will install:" echo -e "${TAB} - Node.js 22" echo -e "${TAB} - CronMaster (prebuild)" echo "" echo -n "${TAB}Install ${APP}? (y/N): " read -r install_prompt if [[ "${install_prompt,,}" =~ ^(y|yes)$ ]]; then install else msg_warn "Installation cancelled. Exiting." exit 0 fi ================================================ FILE: tools/addon/crowdsec.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.crowdsec.net/ | Github: https://github.com/crowdsecurity/crowdsec YW=$(echo "\033[33m") BL=$(echo "\033[36m") RD=$(echo "\033[01;31m") BGN=$(echo "\033[4;92m") GN=$(echo "\033[1;92m") DGN=$(echo "\033[32m") CL=$(echo "\033[m") BFR="\\r\\033[K" HOLD="-" CM="${GN}✓${CL}" APP="CrowdSec" hostname="$(hostname)" # Telemetry source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func) 2>/dev/null || true declare -f init_tool_telemetry &>/dev/null && init_tool_telemetry "crowdsec" "addon" set -o errexit set -o errtrace set -o nounset set -o pipefail shopt -s expand_aliases alias die='EXIT=$? LINE=$LINENO error_exit' trap die ERR function error_exit() { trap - ERR local reason="Unknown failure occured." local msg="${1:-$reason}" local flag="${RD}‼ ERROR ${CL}$EXIT@$LINE" echo -e "$flag $msg" 1>&2 exit "$EXIT" } if command -v pveversion >/dev/null 2>&1; then echo -e "⚠️ Can't Install on Proxmox " exit fi while true; do read -p "This will Install ${APP} on $hostname. Proceed(y/n)?" yn case $yn in [Yy]*) break ;; [Nn]*) exit ;; *) echo "Please answer yes or no." ;; esac done clear function header_info() { echo -e "${BL} _____ _ _____ / ____| | |/ ____| | | _ __ _____ ____| | (___ ___ ___ | | | __/ _ \ \ /\ / / _ |\___ \ / _ \/ __| | |____| | | (_) \ V V / (_| |____) | __/ (__ \_____|_| \___/ \_/\_/ \__ _|_____/ \___|\___| ${CL}" } header_info function msg_info() { local msg="$1" echo -ne " ${HOLD} ${YW}${msg}..." } function msg_ok() { local msg="$1" echo -e "${BFR} ${CM} ${GN}${msg}${CL}" } msg_info "Setting up ${APP} Repository" apt-get update &>/dev/null apt-get install -y curl &>/dev/null apt-get install -y gnupg &>/dev/null curl -fsSL "https://install.crowdsec.net" | bash &>/dev/null msg_ok "Setup ${APP} Repository" msg_info "Installing ${APP}" apt-get update &>/dev/null apt-get install -y crowdsec &>/dev/null msg_ok "Installed ${APP} on $hostname" msg_info "Installing ${APP} Common Bouncer" apt-get install -y crowdsec-firewall-bouncer-iptables &>/dev/null msg_ok "Installed ${APP} Common Bouncer" msg_ok "Completed successfully!\n" ================================================ FILE: tools/addon/dockge.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: tteck (tteckster) | Addon: MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://dockge.kuma.pet/ | Github: https://github.com/louislam/dockge if ! command -v curl &>/dev/null; then printf "\r\e[2K%b" '\033[93m Setup Source \033[m' >&2 if [[ -f /etc/alpine-release ]]; then apk update >/dev/null 2>&1 apk add --no-cache curl >/dev/null 2>&1 else apt-get update >/dev/null 2>&1 apt-get install -y curl >/dev/null 2>&1 fi fi source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/core.func) source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/tools.func) source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/error_handler.func) source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func) 2>/dev/null || true declare -f init_tool_telemetry &>/dev/null && init_tool_telemetry "dockge" "addon" # Enable error handling set -Eeuo pipefail trap 'error_handler' ERR # ============================================================================== # CONFIGURATION # ============================================================================== APP="Dockge" APP_TYPE="addon" INSTALL_PATH="/opt/dockge" STACKS_PATH="/opt/stacks" COMPOSE_FILE="${INSTALL_PATH}/compose.yaml" DEFAULT_PORT=5001 # Initialize all core functions (colors, formatting, icons, STD mode) load_functions # ============================================================================== # UNINSTALL # ============================================================================== function uninstall() { msg_info "Uninstalling ${APP}" if [[ -f "$COMPOSE_FILE" ]]; then msg_info "Stopping and removing Docker containers" cd "$INSTALL_PATH" $STD docker compose down --remove-orphans msg_ok "Stopped and removed Docker containers" fi rm -rf "$INSTALL_PATH" msg_ok "${APP} has been uninstalled" msg_warn "Stacks directory ${STACKS_PATH} was NOT removed. Delete manually if no longer needed." } # ============================================================================== # UPDATE # ============================================================================== function update() { msg_info "Pulling latest ${APP} image" cd "$INSTALL_PATH" $STD docker compose pull msg_ok "Pulled latest image" msg_info "Restarting ${APP}" $STD docker compose up -d --remove-orphans msg_ok "Restarted ${APP}" msg_ok "Updated successfully" exit } # ============================================================================== # PROXMOX HOST CHECK # ============================================================================== function check_proxmox_host() { if command -v pveversion &>/dev/null; then msg_error "Running on the Proxmox host is NOT recommended!" msg_error "This should be executed inside an LXC container." echo "" echo -n "${TAB}Continue anyway? (y/N): " read -r confirm if [[ ! "${confirm,,}" =~ ^(y|yes)$ ]]; then msg_warn "Aborted. Please run this inside an LXC container." exit 0 fi msg_warn "Proceeding on Proxmox host at your own risk!" fi } # ============================================================================== # CHECK / INSTALL DOCKER # ============================================================================== function check_or_install_docker() { if command -v docker &>/dev/null; then msg_ok "Docker $(docker --version | cut -d' ' -f3 | tr -d ',') is available" if docker compose version &>/dev/null; then msg_ok "Docker Compose is available" else msg_error "Docker Compose plugin is not available. Please install it." exit 10 fi return fi msg_warn "Docker is not installed." echo -n "${TAB}Install Docker now? (y/N): " read -r install_docker_prompt if [[ ! "${install_docker_prompt,,}" =~ ^(y|yes)$ ]]; then msg_error "Docker is required for ${APP}. Exiting." exit 10 fi msg_info "Installing Docker" if [[ -f /etc/alpine-release ]]; then $STD apk add docker docker-cli-compose $STD rc-service docker start $STD rc-update add docker default else DOCKER_CONFIG_PATH='/etc/docker/daemon.json' mkdir -p "$(dirname "$DOCKER_CONFIG_PATH")" echo -e '{\n "log-driver": "journald"\n}' >"$DOCKER_CONFIG_PATH" $STD sh <(curl -fsSL https://get.docker.com) fi msg_ok "Installed Docker" } # ============================================================================== # INSTALL # ============================================================================== function install() { check_or_install_docker msg_info "Creating install directories" mkdir -p "$INSTALL_PATH" "$STACKS_PATH" msg_ok "Created ${INSTALL_PATH} and ${STACKS_PATH}" msg_info "Downloading Docker Compose file" curl -fsSL "https://raw.githubusercontent.com/louislam/dockge/master/compose.yaml" -o "$COMPOSE_FILE" msg_ok "Downloaded Docker Compose file" msg_info "Starting ${APP}" cd "$INSTALL_PATH" $STD docker compose up -d msg_ok "Started ${APP}" echo "" msg_ok "${APP} is reachable at: ${BL}http://${LOCAL_IP}:${DEFAULT_PORT}${CL}" } # ============================================================================== # MAIN # ============================================================================== # Handle type=update (called from update script) if [[ "${type:-}" == "update" ]]; then header_info if [[ -f "$COMPOSE_FILE" ]]; then update else msg_error "${APP} is not installed. Nothing to update." exit 233 fi exit 0 fi header_info check_proxmox_host get_lxc_ip # Check if already installed if [[ -f "$COMPOSE_FILE" ]]; then msg_warn "${APP} is already installed." echo "" echo -n "${TAB}Uninstall ${APP}? (y/N): " read -r uninstall_prompt if [[ "${uninstall_prompt,,}" =~ ^(y|yes)$ ]]; then uninstall exit 0 fi echo -n "${TAB}Update ${APP}? (y/N): " read -r update_prompt if [[ "${update_prompt,,}" =~ ^(y|yes)$ ]]; then update exit 0 fi msg_warn "No action selected. Exiting." exit 0 fi # Fresh installation msg_warn "${APP} is not installed." echo "" echo -e "${TAB}${INFO} This will install:" echo -e "${TAB} - Dockge (via Docker Compose)" echo "" echo -n "${TAB}Install ${APP}? (y/N): " read -r install_prompt if [[ "${install_prompt,,}" =~ ^(y|yes)$ ]]; then install else msg_warn "Installation cancelled. Exiting." exit 0 fi ================================================ FILE: tools/addon/dokploy.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://dokploy.com/ | Github: https://github.com/Dokploy/dokploy if ! command -v curl &>/dev/null; then printf "\r\e[2K%b" '\033[93m Setup Source \033[m' >&2 if [[ -f /etc/alpine-release ]]; then apk update >/dev/null 2>&1 apk add --no-cache curl >/dev/null 2>&1 else apt-get update >/dev/null 2>&1 apt-get install -y curl >/dev/null 2>&1 fi fi source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/core.func) source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/tools.func) source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/error_handler.func) source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func) 2>/dev/null || true declare -f init_tool_telemetry &>/dev/null && init_tool_telemetry "dokploy" "addon" # Enable error handling set -Eeuo pipefail trap 'error_handler' ERR # ============================================================================== # CONFIGURATION # ============================================================================== APP="Dokploy" APP_TYPE="addon" INSTALL_PATH="/etc/dokploy" DEFAULT_PORT=3000 # Initialize all core functions (colors, formatting, icons, STD mode) load_functions # ============================================================================== # UNINSTALL # ============================================================================== function uninstall() { msg_info "Uninstalling ${APP}" if command -v docker &>/dev/null; then msg_info "Stopping and removing Docker containers" $STD docker stop $(docker ps -aq) 2>/dev/null || true $STD docker rm $(docker ps -aq) 2>/dev/null || true $STD docker network prune -f 2>/dev/null || true msg_ok "Stopped and removed Docker containers" fi rm -rf "$INSTALL_PATH" msg_ok "${APP} has been uninstalled" } # ============================================================================== # UPDATE # ============================================================================== function update() { msg_info "Updating ${APP}" $STD curl -sSL https://dokploy.com/install.sh | bash -s update msg_ok "Updated ${APP}" msg_ok "Updated successfully" exit } # ============================================================================== # PROXMOX HOST CHECK # ============================================================================== function check_proxmox_host() { if command -v pveversion &>/dev/null; then msg_error "Running on the Proxmox host is NOT recommended!" msg_error "This should be executed inside an LXC container." echo "" echo -n "${TAB}Continue anyway? (y/N): " read -r confirm if [[ ! "${confirm,,}" =~ ^(y|yes)$ ]]; then msg_warn "Aborted. Please run this inside an LXC container." exit 0 fi msg_warn "Proceeding on Proxmox host at your own risk!" fi } # ============================================================================== # CHECK / INSTALL DOCKER # ============================================================================== function check_or_install_docker() { if command -v docker &>/dev/null; then msg_ok "Docker $(docker --version | cut -d' ' -f3 | tr -d ',') is available" if docker compose version &>/dev/null; then msg_ok "Docker Compose is available" else msg_error "Docker Compose plugin is not available. Please install it." exit 10 fi return fi msg_warn "Docker is not installed." echo -n "${TAB}Install Docker now? (y/N): " read -r install_docker_prompt if [[ ! "${install_docker_prompt,,}" =~ ^(y|yes)$ ]]; then msg_error "Docker is required for ${APP}. Exiting." exit 10 fi msg_info "Installing Docker" if [[ -f /etc/alpine-release ]]; then $STD apk add docker docker-cli-compose $STD rc-service docker start $STD rc-update add docker default else DOCKER_CONFIG_PATH='/etc/docker/daemon.json' mkdir -p "$(dirname "$DOCKER_CONFIG_PATH")" echo -e '{\n "log-driver": "journald"\n}' >"$DOCKER_CONFIG_PATH" $STD sh <(curl -fsSL https://get.docker.com) fi msg_ok "Installed Docker" } # ============================================================================== # INSTALL # ============================================================================== function install() { check_or_install_docker msg_info "Installing dependencies" if [[ -f /etc/alpine-release ]]; then $STD apk add --no-cache git openssl else $STD apt-get update $STD apt-get install -y git openssl redis fi msg_ok "Installed dependencies" msg_warn "WARNING: This will run an external installer from https://dokploy.com/" msg_warn "The following code is NOT maintained or audited by our repository." echo "" echo -n "${TAB}Do you want to continue? (y/N): " read -r confirm if [[ ! "${confirm,,}" =~ ^(y|yes)$ ]]; then msg_warn "Installation cancelled. Exiting." exit 0 fi msg_info "Installing ${APP} (this installs Docker and pulls containers)" $STD bash <(curl -sSL https://dokploy.com/install.sh) msg_ok "Installed ${APP}" echo "" msg_ok "${APP} is reachable at: ${BL}http://${LOCAL_IP}:${DEFAULT_PORT}${CL}" } # ============================================================================== # MAIN # ============================================================================== # Handle type=update (called from update script) if [[ "${type:-}" == "update" ]]; then header_info if [[ -d "$INSTALL_PATH" ]]; then update else msg_error "${APP} is not installed. Nothing to update." exit 233 fi exit 0 fi header_info check_proxmox_host get_lxc_ip # Check if already installed if [[ -d "$INSTALL_PATH" ]]; then msg_warn "${APP} is already installed." echo "" echo -n "${TAB}Uninstall ${APP}? (y/N): " read -r uninstall_prompt if [[ "${uninstall_prompt,,}" =~ ^(y|yes)$ ]]; then uninstall exit 0 fi echo -n "${TAB}Update ${APP}? (y/N): " read -r update_prompt if [[ "${update_prompt,,}" =~ ^(y|yes)$ ]]; then update exit 0 fi msg_warn "No action selected. Exiting." exit 0 fi # Fresh installation msg_warn "${APP} is not installed." echo "" echo -e "${TAB}${INFO} This will install:" echo -e "${TAB} - Dokploy (via external installer)" echo -e "${TAB} - Docker (if not already installed)" echo -e "${TAB} - Redis" echo "" echo -n "${TAB}Install ${APP}? (y/N): " read -r install_prompt if [[ "${install_prompt,,}" =~ ^(y|yes)$ ]]; then install else msg_warn "Installation cancelled. Exiting." exit 0 fi ================================================ FILE: tools/addon/filebrowser-quantum.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE function header_info() { clear cat <<"EOF" _______ __ ____ ____ __ / ____(_) /__ / __ )_________ _ __________ _____ / __ \__ ______ _____ / /___ ______ ___ / /_ / / / _ \/ __ / ___/ __ \ | /| / / ___/ _ \/ ___/ / / / / / / / __ `/ __ \/ __/ / / / __ `__ \ / __/ / / / __/ /_/ / / / /_/ / |/ |/ (__ ) __/ / / /_/ / /_/ / /_/ / / / / /_/ /_/ / / / / / / /_/ /_/_/\___/_____/_/ \____/|__/|__/____/\___/_/ \___\_\__,_/\__,_/_/ /_/\__/\__,_/_/ /_/ /_/ EOF } YW=$(echo "\033[33m") GN=$(echo "\033[1;92m") RD=$(echo "\033[01;31m") BL=$(echo "\033[36m") CL=$(echo "\033[m") CM="${GN}✔️${CL}" CROSS="${RD}✖️${CL}" INFO="${BL}ℹ️${CL}" APP="FileBrowser Quantum" INSTALL_PATH="/usr/local/bin/filebrowser" CONFIG_PATH="/usr/local/community-scripts/fq-config.yaml" DEFAULT_PORT=8080 SRC_DIR="/" TMP_BIN="/tmp/filebrowser.$$" # Telemetry source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func) 2>/dev/null || true declare -f init_tool_telemetry &>/dev/null && init_tool_telemetry "filebrowser-quantum" "addon" # Get primary IP IFACE=$(ip -4 route | awk '/default/ {print $5; exit}') IP=$(ip -4 addr show "$IFACE" | awk '/inet / {print $2}' | cut -d/ -f1 | head -n 1) [[ -z "$IP" ]] && IP=$(hostname -I | awk '{print $1}') [[ -z "$IP" ]] && IP="127.0.0.1" # Proxmox Host Warning if [[ -d "/etc/pve" ]]; then echo -e "${RD}⚠️ Warning: Running this addon directly on the Proxmox host is not recommended!${CL}" echo -e "${YW} Only the boot disk will be visible — passthrough drives will not be indexed.${CL}" echo -e "${YW} This causes incorrect disk usage stats and incomplete file browsing.${CL}" echo -e "${YW} Run this addon inside an LXC or VM instead and mount your drives there.${CL}" echo "" echo -n "Continue anyway on the Proxmox host? (y/N): " read -r host_confirm if [[ ! "${host_confirm,,}" =~ ^(y|yes)$ ]]; then echo -e "${YW}Aborted.${CL}" exit 0 fi fi # OS Detection if [[ -f "/etc/alpine-release" ]]; then OS="Alpine" SERVICE_PATH="/etc/init.d/filebrowser" PKG_MANAGER="apk add --no-cache" elif [[ -f "/etc/debian_version" ]]; then OS="Debian" SERVICE_PATH="/etc/systemd/system/filebrowser.service" PKG_MANAGER="apt-get install -y" else echo -e "${CROSS} Unsupported OS detected. Exiting." exit 238 fi header_info function msg_info() { echo -e "${INFO} ${YW}$1...${CL}"; } function msg_ok() { echo -e "${CM} ${GN}$1${CL}"; } function msg_error() { echo -e "${CROSS} ${RD}$1${CL}"; } # Detect legacy FileBrowser installation LEGACY_DB="/usr/local/community-scripts/filebrowser.db" LEGACY_BIN="/usr/local/bin/filebrowser" LEGACY_SERVICE_DEB="/etc/systemd/system/filebrowser.service" LEGACY_SERVICE_ALP="/etc/init.d/filebrowser" if [[ -f "$LEGACY_DB" || -f "$LEGACY_BIN" && ! -f "$CONFIG_PATH" ]]; then echo -e "${YW}⚠️ Detected legacy FileBrowser installation.${CL}" echo -n "Uninstall legacy FileBrowser and continue with Quantum install? (y/n): " read -r remove_legacy if [[ "${remove_legacy,,}" =~ ^(y|yes)$ ]]; then msg_info "Uninstalling legacy FileBrowser" if [[ -f "$LEGACY_SERVICE_DEB" ]]; then systemctl disable --now filebrowser.service &>/dev/null rm -f "$LEGACY_SERVICE_DEB" elif [[ -f "$LEGACY_SERVICE_ALP" ]]; then rc-service filebrowser stop &>/dev/null rc-update del filebrowser &>/dev/null rm -f "$LEGACY_SERVICE_ALP" fi rm -f "$LEGACY_BIN" "$LEGACY_DB" msg_ok "Legacy FileBrowser removed" else echo -e "${YW}❌ Installation aborted by user.${CL}" exit 0 fi fi # Existing installation if [[ -f "$INSTALL_PATH" ]]; then echo -e "${YW}⚠️ ${APP} is already installed.${CL}" echo -n "Uninstall ${APP}? (y/N): " read -r uninstall_prompt if [[ "${uninstall_prompt,,}" =~ ^(y|yes)$ ]]; then msg_info "Uninstalling ${APP}" if [[ "$OS" == "Debian" ]]; then systemctl disable --now filebrowser.service &>/dev/null rm -f "$SERVICE_PATH" else rc-service filebrowser stop &>/dev/null rc-update del filebrowser &>/dev/null rm -f "$SERVICE_PATH" fi rm -f "$INSTALL_PATH" "$CONFIG_PATH" msg_ok "${APP} has been uninstalled." exit 0 fi echo -n "Update ${APP}? (y/N): " read -r update_prompt if [[ "${update_prompt,,}" =~ ^(y|yes)$ ]]; then msg_info "Updating ${APP}" if ! command -v curl &>/dev/null; then $PKG_MANAGER curl &>/dev/null; fi curl -fsSL https://github.com/gtsteffaniak/filebrowser/releases/latest/download/linux-arm64-filebrowser -o "$TMP_BIN" chmod +x "$TMP_BIN" mv -f "$TMP_BIN" /usr/local/bin/filebrowser msg_ok "Updated ${APP}" exit 0 else echo -e "${YW}⚠️ Update skipped. Exiting.${CL}" exit 0 fi fi echo -e "${YW}⚠️ ${APP} is not installed.${CL}" echo -n "Enter port number (Default: ${DEFAULT_PORT}): " read -r PORT PORT=${PORT:-$DEFAULT_PORT} echo -n "Install ${APP}? (y/n): " read -r install_prompt if ! [[ "${install_prompt,,}" =~ ^(y|yes)$ ]]; then echo -e "${YW}⚠️ Installation skipped. Exiting.${CL}" exit 0 fi msg_info "Installing ${APP} on ${OS}" $PKG_MANAGER curl ffmpeg &>/dev/null curl -fsSL https://github.com/gtsteffaniak/filebrowser/releases/latest/download/linux-arm64-filebrowser -o "$TMP_BIN" chmod +x "$TMP_BIN" mv -f "$TMP_BIN" /usr/local/bin/filebrowser msg_ok "Installed ${APP}" msg_info "Preparing configuration directory" mkdir -p /usr/local/community-scripts chown root:root /usr/local/community-scripts chmod 755 /usr/local/community-scripts msg_ok "Directory prepared" echo -n "Use No Authentication? (y/N): " read -r noauth_prompt # === YAML CONFIG GENERATION === if [[ "${noauth_prompt,,}" =~ ^(y|yes)$ ]]; then cat <"$CONFIG_PATH" server: port: $PORT sources: - path: "$SRC_DIR" name: "RootFS" config: denyByDefault: false disableIndexing: false indexingIntervalMinutes: 240 conditionals: rules: - neverWatchPath: "/proc" - neverWatchPath: "/sys" - neverWatchPath: "/dev" - neverWatchPath: "/run" - neverWatchPath: "/tmp" - neverWatchPath: "/lost+found" auth: methods: noauth: true EOF msg_ok "Configured with no authentication" else cat <"$CONFIG_PATH" server: port: $PORT sources: - path: "$SRC_DIR" name: "RootFS" config: denyByDefault: false disableIndexing: false indexingIntervalMinutes: 240 conditionals: rules: - neverWatchPath: "/proc" - neverWatchPath: "/sys" - neverWatchPath: "/dev" - neverWatchPath: "/run" - neverWatchPath: "/tmp" - neverWatchPath: "/lost+found" auth: adminUsername: admin adminPassword: community-scripts.org EOF msg_ok "Configured with default admin (admin / community-scripts.org)" fi msg_info "Creating service" if [[ "$OS" == "Debian" ]]; then cat <"$SERVICE_PATH" [Unit] Description=FileBrowser Quantum After=network.target [Service] User=root WorkingDirectory=/usr/local/community-scripts ExecStart=/usr/local/bin/filebrowser -c $CONFIG_PATH Restart=always [Install] WantedBy=multi-user.target EOF systemctl enable --now filebrowser &>/dev/null else cat <"$SERVICE_PATH" #!/sbin/openrc-run command="/usr/local/bin/filebrowser" command_args="-c $CONFIG_PATH" command_background=true directory="/usr/local/community-scripts" pidfile="/usr/local/community-scripts/pidfile" depend() { need net } EOF chmod +x "$SERVICE_PATH" rc-update add filebrowser default &>/dev/null rc-service filebrowser start &>/dev/null fi msg_ok "Service created successfully" echo -e "${CM} ${GN}${APP} is reachable at: ${BL}http://$IP:$PORT${CL}" ================================================ FILE: tools/addon/filebrowser.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: tteck (tteckster) | Co-Author: MickLesk # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE function header_info { clear cat <<"EOF" _______ __ ____ / ____(_) /__ / __ )_________ _ __________ _____ / /_ / / / _ \/ __ / ___/ __ \ | /| / / ___/ _ \/ ___/ / __/ / / / __/ /_/ / / / /_/ / |/ |/ (__ ) __/ / /_/ /_/_/\___/_____/_/ \____/|__/|__/____/\___/_/ EOF } YW=$(echo "\033[33m") GN=$(echo "\033[1;92m") RD=$(echo "\033[01;31m") BL=$(echo "\033[36m") CL=$(echo "\033[m") CM="${GN}✔️${CL}" CROSS="${RD}✖️${CL}" INFO="${BL}ℹ️${CL}" APP="FileBrowser" INSTALL_PATH="/usr/local/bin/filebrowser" DB_PATH="/usr/local/community-scripts/filebrowser.db" DEFAULT_PORT=8080 # Telemetry source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func) 2>/dev/null || true declare -f init_tool_telemetry &>/dev/null && init_tool_telemetry "filebrowser" "addon" # Get first non-loopback IP & Detect primary network interface dynamically IFACE=$(ip -4 route | awk '/default/ {print $5; exit}') IP=$(ip -4 addr show "$IFACE" | awk '/inet / {print $2}' | cut -d/ -f1 | head -n 1) [[ -z "$IP" ]] && IP=$(hostname -I | awk '{print $1}') [[ -z "$IP" ]] && IP="127.0.0.1" # Proxmox Host Warning if [[ -d "/etc/pve" ]]; then echo -e "${RD}⚠️ Warning: Running this addon directly on the Proxmox host is not recommended!${CL}" echo -e "${YW} Only the boot disk will be visible — passthrough drives will not be indexed.${CL}" echo -e "${YW} This causes incorrect disk usage stats and incomplete file browsing.${CL}" echo -e "${YW} Run this addon inside an LXC or VM instead and mount your drives there.${CL}" echo "" echo -n "Continue anyway on the Proxmox host? (y/N): " read -r host_confirm if [[ ! "${host_confirm,,}" =~ ^(y|yes)$ ]]; then echo -e "${YW}Aborted.${CL}" exit 0 fi fi # Detect OS if [[ -f "/etc/alpine-release" ]]; then OS="Alpine" SERVICE_PATH="/etc/init.d/filebrowser" PKG_MANAGER="apk add --no-cache" elif [[ -f "/etc/debian_version" ]]; then OS="Debian" SERVICE_PATH="/etc/systemd/system/filebrowser.service" PKG_MANAGER="apt-get install -y" else echo -e "${CROSS} Unsupported OS detected. Exiting." exit 238 fi header_info function msg_info() { local msg="$1" echo -e "${INFO} ${YW}${msg}...${CL}" } function msg_ok() { local msg="$1" echo -e "${CM} ${GN}${msg}${CL}" } function msg_error() { local msg="$1" echo -e "${CROSS} ${RD}${msg}${CL}" } if [ -f "$INSTALL_PATH" ]; then echo -e "${YW}⚠️ ${APP} is already installed.${CL}" read -r -p "Would you like to uninstall ${APP}? (y/N): " uninstall_prompt if [[ "${uninstall_prompt,,}" =~ ^(y|yes)$ ]]; then msg_info "Uninstalling ${APP}" if [[ "$OS" == "Debian" ]]; then systemctl disable --now filebrowser.service &>/dev/null rm -f "$SERVICE_PATH" else rc-service filebrowser stop &>/dev/null rc-update del filebrowser &>/dev/null rm -f "$SERVICE_PATH" fi rm -f "$INSTALL_PATH" "$DB_PATH" msg_ok "${APP} has been uninstalled." exit 0 fi read -r -p "Would you like to update ${APP}? (y/N): " update_prompt if [[ "${update_prompt,,}" =~ ^(y|yes)$ ]]; then msg_info "Updating ${APP}" if ! command -v curl &>/dev/null; then $PKG_MANAGER curl &>/dev/null; fi curl -fsSL "https://github.com/filebrowser/filebrowser/releases/latest/download/linux-arm64-filebrowser.tar.gz" | tar -xzv -C /usr/local/bin &>/dev/null chmod +x "$INSTALL_PATH" msg_ok "Updated ${APP}" exit 0 else echo -e "${YW}⚠️ Update skipped. Exiting.${CL}" exit 0 fi fi echo -e "${YW}⚠️ ${APP} is not installed.${CL}" read -r -p "Enter port number (Default: ${DEFAULT_PORT}): " PORT PORT=${PORT:-$DEFAULT_PORT} read -r -p "Would you like to install ${APP}? (y/n): " install_prompt if [[ "${install_prompt,,}" =~ ^(y|yes)$ ]]; then msg_info "Installing ${APP} on ${OS}" $PKG_MANAGER wget tar curl &>/dev/null curl -fsSL "https://github.com/filebrowser/filebrowser/releases/latest/download/linux-arm64-filebrowser.tar.gz" | tar -xzv -C /usr/local/bin &>/dev/null chmod +x "$INSTALL_PATH" msg_ok "Installed ${APP}" msg_info "Creating FileBrowser directory" mkdir -p /usr/local/community-scripts chown root:root /usr/local/community-scripts chmod 755 /usr/local/community-scripts touch "$DB_PATH" chown root:root "$DB_PATH" chmod 644 "$DB_PATH" msg_ok "Directory created successfully" read -r -p "Would you like to use No Authentication? (y/N): " auth_prompt if [[ "${auth_prompt,,}" =~ ^(y|yes)$ ]]; then msg_info "Configuring No Authentication" cd /usr/local/community-scripts filebrowser config init -a '0.0.0.0' -p "$PORT" -d "$DB_PATH" &>/dev/null filebrowser config set -a '0.0.0.0' -p "$PORT" -d "$DB_PATH" &>/dev/null filebrowser config set --auth.method=noauth --database "$DB_PATH" &>/dev/null if ! filebrowser users update 1 --perm.admin --database "$DB_PATH" &>/dev/null; then filebrowser users add admin community-scripts.org --perm.admin --database "$DB_PATH" &>/dev/null fi msg_ok "No Authentication configured" else msg_info "Setting up default authentication" cd /usr/local/community-scripts filebrowser config init -a '0.0.0.0' -p "$PORT" -d "$DB_PATH" &>/dev/null filebrowser config set -a '0.0.0.0' -p "$PORT" -d "$DB_PATH" &>/dev/null filebrowser users add admin community-scripts.org --perm.admin --database "$DB_PATH" &>/dev/null msg_ok "Default authentication configured (admin:community-scripts.org)" fi msg_info "Creating service" if [[ "$OS" == "Debian" ]]; then cat <"$SERVICE_PATH" [Unit] Description=Filebrowser After=network-online.target [Service] User=root WorkingDirectory=/usr/local/community-scripts ExecStartPre=/bin/touch /usr/local/community-scripts/filebrowser.db ExecStartPre=/usr/local/bin/filebrowser config set -a "0.0.0.0" -p ${PORT} -d /usr/local/community-scripts/filebrowser.db ExecStart=/usr/local/bin/filebrowser -r / -d /usr/local/community-scripts/filebrowser.db -p ${PORT} Restart=always [Install] WantedBy=multi-user.target EOF systemctl enable -q --now filebrowser else cat <"$SERVICE_PATH" #!/sbin/openrc-run command="/usr/local/bin/filebrowser" command_args="-r / -d $DB_PATH -p $PORT" command_background=true pidfile="/var/run/filebrowser.pid" directory="/usr/local/community-scripts" depend() { need net } EOF chmod +x "$SERVICE_PATH" rc-update add filebrowser default &>/dev/null rc-service filebrowser start &>/dev/null fi msg_ok "Service created successfully" echo -e "${CM} ${GN}${APP} is reachable at: ${BL}http://$IP:$PORT${CL}" else echo -e "${YW}⚠️ Installation skipped. Exiting.${CL}" exit 0 fi ================================================ FILE: tools/addon/glances.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) | MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE function header_info { clear cat <<"EOF" ________ / ____/ /___ _____ ________ _____ / / __/ / __ `/ __ \/ ___/ _ \/ ___/ / /_/ / / /_/ / / / / /__/ __(__ ) \____/_/\__,_/_/ /_/\___/\___/____/ EOF } APP="Glances" YW=$(echo "\033[33m") GN=$(echo "\033[1;92m") RD=$(echo "\033[01;31m") BL=$(echo "\033[36m") CL=$(echo "\033[m") CM="${GN}✔️${CL}" CROSS="${RD}✖️${CL}" INFO="${BL}ℹ️${CL}" function msg_info() { echo -e "${INFO} ${YW}$1...${CL}"; } function msg_ok() { echo -e "${CM} ${GN}$1${CL}"; } function msg_error() { echo -e "${CROSS} ${RD}$1${CL}"; } # Telemetry source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func) 2>/dev/null || true declare -f init_tool_telemetry &>/dev/null && init_tool_telemetry "glances" "addon" get_lxc_ip() { if command -v hostname >/dev/null 2>&1 && hostname -I 2>/dev/null; then hostname -I | awk '{print $1}' elif command -v ip >/dev/null 2>&1; then ip -4 addr show scope global | awk '/inet / {print $2}' | cut -d/ -f1 | head -n1 else echo "127.0.0.1" fi } IP=$(get_lxc_ip) install_glances_debian() { msg_info "Installing dependencies" apt-get update >/dev/null 2>&1 apt-get install -y gcc lm-sensors wireless-tools curl >/dev/null 2>&1 msg_ok "Installed dependencies" msg_info "Setting up Python + uv" source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/tools.func) setup_uv PYTHON_VERSION="3.12" msg_ok "Setup Python + uv" msg_info "Installing $APP (with web UI)" cd /opt mkdir -p glances cd glances uv venv --clear source .venv/bin/activate >/dev/null 2>&1 uv pip install --upgrade pip wheel setuptools >/dev/null 2>&1 uv pip install "glances[web]" >/dev/null 2>&1 deactivate msg_ok "Installed $APP" msg_info "Creating systemd service" cat </etc/systemd/system/glances.service [Unit] Description=Glances - An eye on your system After=network.target [Service] Type=simple ExecStart=/opt/glances/.venv/bin/glances -w Restart=on-failure WorkingDirectory=/opt/glances [Install] WantedBy=multi-user.target EOF systemctl enable -q --now glances msg_ok "Created systemd service" echo -e "\n$APP is now running at: http://$IP:61208\n" } # update on Debian/Ubuntu update_glances_debian() { if [[ ! -d /opt/glances/.venv ]]; then msg_error "$APP is not installed" exit 233 fi msg_info "Updating $APP" cd /opt/glances source .venv/bin/activate uv pip install --upgrade "glances[web]" >/dev/null 2>&1 deactivate systemctl restart glances msg_ok "Updated successfully!" } # uninstall on Debian/Ubuntu uninstall_glances_debian() { msg_info "Uninstalling $APP" systemctl disable -q --now glances || true rm -f /etc/systemd/system/glances.service rm -rf /opt/glances msg_ok "Removed $APP" } # install on Alpine install_glances_alpine() { msg_info "Installing dependencies" apk update >/dev/null 2>&1 $STD apk add --no-cache \ gcc musl-dev linux-headers python3-dev \ python3 py3-pip py3-virtualenv lm-sensors wireless-tools curl >/dev/null 2>&1 msg_ok "Installed dependencies" msg_info "Setting up Python + uv" source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/tools.func) setup_uv PYTHON_VERSION="3.12" msg_ok "Setup Python + uv" msg_info "Installing $APP (with web UI)" cd /opt mkdir -p glances cd glances uv venv --clear source .venv/bin/activate uv pip install --upgrade pip wheel setuptools >/dev/null 2>&1 uv pip install "glances[web]" >/dev/null 2>&1 deactivate msg_ok "Installed $APP" msg_info "Creating OpenRC service" cat <<'EOF' >/etc/init.d/glances #!/sbin/openrc-run command="/opt/glances/.venv/bin/glances" command_args="-w" command_background="yes" pidfile="/run/glances.pid" name="glances" description="Glances monitoring tool" EOF chmod +x /etc/init.d/glances rc-update add glances default rc-service glances start msg_ok "Created OpenRC service" echo -e "\n$APP is now running at: http://$IP:61208\n" } # update on Alpine update_glances_alpine() { if [[ ! -d /opt/glances/.venv ]]; then msg_error "$APP is not installed" exit 233 fi msg_info "Updating $APP" cd /opt/glances source .venv/bin/activate uv pip install --upgrade "glances[web]" >/dev/null 2>&1 deactivate rc-service glances restart msg_ok "Updated successfully!" } # uninstall on Alpine uninstall_glances_alpine() { msg_info "Uninstalling $APP" rc-service glances stop || true rc-update del glances || true rm -f /etc/init.d/glances rm -rf /opt/glances msg_ok "Removed $APP" } # options menu OPTIONS=(Install "Install $APP" Update "Update $APP" Uninstall "Uninstall $APP") CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "$APP" --menu "Select an option:" 12 58 3 \ "${OPTIONS[@]}" 3>&1 1>&2 2>&3 || true) # OS detection if grep -qi "alpine" /etc/os-release; then case "$CHOICE" in Install) install_glances_alpine ;; Update) update_glances_alpine ;; Uninstall) uninstall_glances_alpine ;; *) exit 0 ;; esac else case "$CHOICE" in Install) install_glances_debian ;; Update) update_glances_debian ;; Uninstall) uninstall_glances_debian ;; *) exit 0 ;; esac fi ================================================ FILE: tools/addon/homebrew.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: MorganCSIT | MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://brew.sh | Github: https://github.com/Homebrew/brew if ! command -v curl &>/dev/null; then printf "\r\e[2K%b" '\033[93m Setup Source \033[m' >&2 apt-get update >/dev/null 2>&1 apt-get install -y curl >/dev/null 2>&1 fi source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/core.func) source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/tools.func) source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/error_handler.func) source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func) 2>/dev/null || true # Enable error handling set -Eeuo pipefail trap 'error_handler' ERR load_functions init_tool_telemetry "" "addon" # ============================================================================== # CONFIGURATION # ============================================================================== VERBOSE=${var_verbose:-no} APP="homebrew" APP_TYPE="tools" INSTALL_PATH="/home/linuxbrew/.linuxbrew" # ============================================================================== # OS DETECTION # ============================================================================== if [[ -f "/etc/alpine-release" ]]; then echo -e "${CROSS} Alpine is not supported by Homebrew. Exiting." exit 1 elif grep -qE 'ID=debian|ID=ubuntu' /etc/os-release; then OS="Debian" else echo -e "${CROSS} Unsupported OS detected. Exiting." exit 1 fi # ============================================================================== # UNINSTALL # ============================================================================== function uninstall() { msg_info "Uninstalling Homebrew" BREW_USER=$(awk -F: '$3 >= 1000 && $3 < 65534 { print $1; exit }' /etc/passwd) if [[ -n "$BREW_USER" ]]; then BREW_USER_HOME=$(getent passwd "$BREW_USER" | cut -d: -f6) for rc_file in "$BREW_USER_HOME/.bashrc" "$BREW_USER_HOME/.profile"; do if [[ -f "$rc_file" ]]; then sed -i '/# Homebrew (Linuxbrew)/,/^fi$/d' "$rc_file" fi done fi rm -rf /home/linuxbrew rm -f /etc/profile.d/homebrew.sh groupdel linuxbrew &>/dev/null || true msg_ok "Homebrew has been uninstalled" } # ============================================================================== # INSTALL # ============================================================================== function install() { msg_info "Detecting Non-Root User" BREW_USER=$(awk -F: '$3 >= 1000 && $3 < 65534 { print $1; exit }' /etc/passwd) if [[ -z "$BREW_USER" ]]; then msg_warn "No non-root user found (uid >= 1000). Homebrew cannot run as root." read -r -p "${TAB}Create a 'brew' user automatically? (y/N): " create_user_prompt if [[ "${create_user_prompt,,}" =~ ^(y|yes)$ ]]; then msg_info "Creating user 'brew'" useradd -m -s /bin/bash brew BREW_USER="brew" msg_ok "Created user 'brew'" else msg_error "Cannot install Homebrew without a non-root user. Exiting." exit 1 fi fi msg_ok "Detected User: $BREW_USER" msg_info "Installing Dependencies" $STD apt update $STD apt install -y build-essential git file procps msg_ok "Installed Dependencies" msg_info "Setting Up Homebrew Prefix" export PATH="/usr/sbin:$PATH" groupadd -f linuxbrew mkdir -p /home/linuxbrew/.linuxbrew chown -R "$BREW_USER":linuxbrew /home/linuxbrew chmod 2775 /home/linuxbrew chmod 2775 /home/linuxbrew/.linuxbrew usermod -aG linuxbrew "$BREW_USER" msg_ok "Set Up Homebrew Prefix" msg_info "Installing Homebrew" $STD su - "$BREW_USER" -c 'NONINTERACTIVE=1 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"' msg_ok "Installed Homebrew" msg_info "Configuring Shell Integration" cat <<'EOF' >/etc/profile.d/homebrew.sh #!/bin/bash if [ -d "/home/linuxbrew/.linuxbrew" ]; then eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)" fi EOF chmod +x /etc/profile.d/homebrew.sh BREW_USER_HOME=$(getent passwd "$BREW_USER" | cut -d: -f6) BREW_SHELL_BLOCK='\n# Homebrew (Linuxbrew)\nif [ -d "/home/linuxbrew/.linuxbrew" ]; then\n eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)"\nfi' for rc_file in "$BREW_USER_HOME/.bashrc" "$BREW_USER_HOME/.profile"; do if ! grep -q 'linuxbrew' "$rc_file" 2>/dev/null; then echo -e "$BREW_SHELL_BLOCK" >>"$rc_file" fi done msg_ok "Configured Shell Integration" msg_info "Verifying Installation" $STD su - "$BREW_USER" -c 'eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)" && brew --version' msg_ok "Homebrew Verified" echo "" msg_ok "Homebrew installed successfully" msg_ok "Ready for user: ${BL}${BREW_USER}${CL}" echo "" echo -e "${TAB}${INFO} Usage: Switch to the brew user with a login shell:" echo -e "${TAB} ${BL}su - ${BREW_USER}${CL}" echo -e "${TAB} Then run: ${BL}brew install ${CL}" echo -e "${TAB} Update with: ${BL}brew update${CL}" } # ============================================================================== # MAIN # ============================================================================== header_info if [[ -d "$INSTALL_PATH" ]]; then msg_warn "Homebrew is already installed." echo "" read -r -p "${TAB}Uninstall Homebrew? (y/N): " uninstall_prompt if [[ "${uninstall_prompt,,}" =~ ^(y|yes)$ ]]; then uninstall exit 0 fi msg_warn "No action selected. Exiting." exit 0 fi # Fresh installation msg_warn "Homebrew is not installed." echo "" echo -e "${TAB}${INFO} This will install:" echo -e "${TAB} - Homebrew (Linuxbrew) package manager" echo -e "${TAB} - Shell integration for the detected non-root user" echo "" read -r -p "${TAB}Install Homebrew? (y/N): " install_prompt if [[ "${install_prompt,,}" =~ ^(y|yes)$ ]]; then install else msg_warn "Installation cancelled. Exiting." exit 0 fi ================================================ FILE: tools/addon/immich-public-proxy.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: vhsdream # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/alangrainger/immich-public-proxy if ! command -v curl &>/dev/null; then printf "\r\e[2K%b" '\033[93m Setup Source \033[m' >&2 apt-get update >/dev/null 2>&1 apt-get install -y curl >/dev/null 2>&1 fi source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/core.func) source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/tools.func) source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/error_handler.func) source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/api.func) 2>/dev/null || true # Enable error handling set -Eeuo pipefail trap 'error_handler' ERR # ============================================================================== # CONFIGURATION # ============================================================================== APP="Immich Public Proxy" APP_TYPE="addon" INSTALL_PATH="/opt/immich-proxy" CONFIG_PATH="/opt/immich-proxy/app" DEFAULT_PORT=3000 # Initialize all core functions (colors, formatting, icons, $STD mode) load_functions # ============================================================================== # HEADER # ============================================================================== function header_info { clear cat <<"EOF" ____ _ __ ____ / _/___ ___ ____ ___ (_)____/ /_ / __ \_________ _ ____ __ / // __ `__ \/ __ `__ \/ / ___/ __ \______/ /_/ / ___/ __ \| |/_/ / / / _/ // / / / / / / / / / / / /__/ / / /_____/ ____/ / / /_/ /> /dev/null || true rm -f "$SERVICE_PATH" rm -rf "$INSTALL_PATH" rm -f "/usr/local/bin/update_immich-public-proxy" rm -f "$HOME/.immichpublicproxy" msg_ok "${APP} has been uninstalled" } # ============================================================================== # UPDATE # ============================================================================== function update() { if check_for_gh_release "Immich Public Proxy" "alangrainger/immich-public-proxy"; then msg_info "Stopping service" systemctl stop immich-proxy.service &>/dev/null || true msg_ok "Stopped service" msg_info "Backing up configuration" cp "$CONFIG_PATH"/.env /tmp/ipp.env.bak 2>/dev/null || true cp "$CONFIG_PATH"/config.json /tmp/ipp.config.json.bak 2>/dev/null || true msg_ok "Backed up configuration" NODE_VERSION="24" setup_nodejs CLEAN_INSTALL=1 fetch_and_deploy_gh_release "Immich Public Proxy" "alangrainger/immich-public-proxy" "tarball" "latest" "$INSTALL_PATH" msg_info "Restoring configuration" cp /tmp/ipp.env.bak "$CONFIG_PATH"/.env 2>/dev/null || true cp /tmp/ipp.config.json.bak "$CONFIG_PATH"/config.json 2>/dev/null || true rm -f /tmp/ipp.*.bak msg_ok "Restored configuration" msg_info "Installing dependencies" cd "$CONFIG_PATH" $STD npm install msg_ok "Installed dependencies" msg_info "Building ${APP}" $STD npm run build msg_ok "Built ${APP}" msg_info "Updating service" create_service msg_ok "Updated service" msg_info "Starting service" systemctl start immich-proxy msg_ok "Started service" msg_ok "Updated successfully" exit fi } function create_service() { cat <"$SERVICE_PATH" [Unit] Description=Immich Public Proxy After=network.target [Service] Type=simple User=root WorkingDirectory=${INSTALL_PATH}/app EnvironmentFile=${CONFIG_PATH}/.env ExecStart=/usr/bin/node ${INSTALL_PATH}/app/dist/index.js Restart=always RestartSec=10 [Install] WantedBy=multi-user.target EOF systemctl daemon-reload } # ============================================================================== # INSTALL # ============================================================================== function install() { NODE_VERSION="24" setup_nodejs # Force fresh download by removing version cache rm -f "$HOME/.immichpublicproxy" fetch_and_deploy_gh_release "Immich Public Proxy" "alangrainger/immich-public-proxy" "tarball" "latest" "$INSTALL_PATH" msg_info "Installing dependencies" cd "$CONFIG_PATH" $STD npm install msg_ok "Installed dependencies" msg_info "Building ${APP}" $STD npm run build msg_ok "Built ${APP}" MAX_ATTEMPTS=3 attempt=0 while true; do attempt=$((attempt + 1)) read -rp "${TAB3}Enter your LOCAL Immich IP or domain (ex. 192.168.1.100 or immich.local.lan): " DOMAIN if [[ -z "$DOMAIN" ]]; then if ((attempt >= MAX_ATTEMPTS)); then DOMAIN="${LOCAL_IP:-localhost}" msg_warn "Using fallback: $DOMAIN" break fi msg_warn "Domain cannot be empty! (Attempt $attempt/$MAX_ATTEMPTS)" elif [[ "$DOMAIN" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then valid_ip=true IFS='.' read -ra octets <<<"$DOMAIN" for octet in "${octets[@]}"; do if ((octet > 255)); then valid_ip=false break fi done if $valid_ip; then break else msg_warn "Invalid IP address!" fi elif [[ "$DOMAIN" =~ ^[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(\.[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*\.[a-zA-Z]{2,}$ || "$DOMAIN" == "localhost" ]]; then break else msg_warn "Invalid domain format!" fi done msg_info "Creating configuration" cat <"$CONFIG_PATH"/.env NODE_ENV=production IMMICH_URL=http://${DOMAIN}:2283 EOF chmod 600 "$CONFIG_PATH"/.env msg_ok "Created configuration" msg_info "Creating service" create_service systemctl enable -q --now immich-proxy msg_ok "Created and started service" # Create update script (simple wrapper that calls this addon with type=update) msg_info "Creating update script" cat <<'UPDATEEOF' >/usr/local/bin/update_immich-public-proxy #!/usr/bin/env bash # Immich Public Proxy Update Script type=update bash -c "$(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/tools/addon/immich-public-proxy.sh)" UPDATEEOF chmod +x /usr/local/bin/update_immich-public-proxy msg_ok "Created update script (/usr/local/bin/update_immich-public-proxy)" echo "" msg_ok "${APP} is reachable at: ${BL}http://${LOCAL_IP}:${DEFAULT_PORT}${CL}" echo "" msg_warn "Additional configuration is available at '/opt/immich-proxy/app/config.json'" } # ============================================================================== # MAIN # ============================================================================== # Handle type=update (called from update script) if [[ "${type:-}" == "update" ]]; then header_info if [[ -d "$INSTALL_PATH" && -f "$SERVICE_PATH" ]]; then update else msg_error "${APP} is not installed. Nothing to update." exit 233 fi exit 0 fi header_info get_lxc_ip # Check if already installed if [[ -d "$INSTALL_PATH" && -f "$SERVICE_PATH" ]]; then msg_warn "${APP} is already installed." echo "" echo -n "${TAB}Uninstall ${APP}? (y/N): " read -r uninstall_prompt if [[ "${uninstall_prompt,,}" =~ ^(y|yes)$ ]]; then uninstall exit 0 fi echo -n "${TAB}Update ${APP}? (y/N): " read -r update_prompt if [[ "${update_prompt,,}" =~ ^(y|yes)$ ]]; then update exit 0 fi msg_warn "No action selected. Exiting." exit 0 fi # Fresh installation msg_warn "${APP} is not installed." echo "" echo -e "${TAB}${INFO} This will install:" echo -e "${TAB} - Node.js 24" echo -e "${TAB} - Immich Public Proxy" echo "" echo -n "${TAB}Install ${APP}? (y/N): " read -r install_prompt if [[ "${install_prompt,,}" =~ ^(y|yes)$ ]]; then install else msg_warn "Installation cancelled. Exiting." exit 0 fi ================================================ FILE: tools/addon/jellystat.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/CyferShepard/Jellystat if ! command -v curl &>/dev/null; then printf "\r\e[2K%b" '\033[93m Setup Source \033[m' >&2 apt-get update >/dev/null 2>&1 apt-get install -y curl >/dev/null 2>&1 fi source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/core.func) source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/tools.func) source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/error_handler.func) source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/api.func) 2>/dev/null || true # Enable error handling set -Eeuo pipefail trap 'error_handler' ERR # ============================================================================== # CONFIGURATION # ============================================================================== APP="Jellystat" APP_TYPE="addon" INSTALL_PATH="/opt/jellystat" CONFIG_PATH="/opt/jellystat/.env" DEFAULT_PORT=3000 # Initialize all core functions (colors, formatting, icons, STD mode) load_functions # ============================================================================== # HEADER # ============================================================================== function header_info { clear cat <<"EOF" __ ____ __ __ / /__ / / /_ _______/ /_____ _/ /_ __ / / _ \/ / / / / / ___/ __/ __ `/ __/ / /_/ / __/ / / /_/ (__ ) /_/ /_/ / /_ \____/\___/_/_/\__, /____/\__/\__,_/\__/ /____/ EOF } # ============================================================================== # OS DETECTION # ============================================================================== if [[ -f "/etc/alpine-release" ]]; then msg_error "Alpine is not supported for ${APP}. Use Debian/Ubuntu." exit 238 elif [[ -f "/etc/debian_version" ]]; then OS="Debian" SERVICE_PATH="/etc/systemd/system/jellystat.service" else echo -e "${CROSS} Unsupported OS detected. Exiting." exit 238 fi # ============================================================================== # UNINSTALL # ============================================================================== function uninstall() { msg_info "Uninstalling ${APP}" systemctl disable --now jellystat.service &>/dev/null || true rm -f "$SERVICE_PATH" rm -rf "$INSTALL_PATH" rm -f "/usr/local/bin/update_jellystat" rm -f "$HOME/.jellystat" msg_ok "${APP} has been uninstalled" # Ask about PostgreSQL database removal echo "" echo -n "${TAB}Also remove PostgreSQL database 'jellystat'? (y/N): " read -r db_prompt if [[ "${db_prompt,,}" =~ ^(y|yes)$ ]]; then if command -v psql &>/dev/null; then msg_info "Removing PostgreSQL database and user" $STD sudo -u postgres psql -c "DROP DATABASE IF EXISTS jellystat;" &>/dev/null || true $STD sudo -u postgres psql -c "DROP USER IF EXISTS jellystat;" &>/dev/null || true msg_ok "Removed PostgreSQL database 'jellystat' and user 'jellystat'" else msg_warn "PostgreSQL not found - database may have been removed already" fi else msg_warn "PostgreSQL database was NOT removed. Remove manually if needed:" echo -e "${TAB} sudo -u postgres psql -c \"DROP DATABASE jellystat;\"" echo -e "${TAB} sudo -u postgres psql -c \"DROP USER jellystat;\"" fi } # ============================================================================== # UPDATE # ============================================================================== function update() { if check_for_gh_release "jellystat" "CyferShepard/Jellystat"; then msg_info "Stopping service" systemctl stop jellystat.service &>/dev/null || true msg_ok "Stopped service" msg_info "Backing up configuration" cp "$CONFIG_PATH" /tmp/jellystat.env.bak 2>/dev/null || true msg_ok "Backed up configuration" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "jellystat" "CyferShepard/Jellystat" "tarball" "latest" "$INSTALL_PATH" msg_info "Restoring configuration" cp /tmp/jellystat.env.bak "$CONFIG_PATH" 2>/dev/null || true rm -f /tmp/jellystat.env.bak msg_ok "Restored configuration" msg_info "Installing dependencies" cd "$INSTALL_PATH" $STD npm install msg_ok "Installed dependencies" msg_info "Building ${APP}" $STD npm run build msg_ok "Built ${APP}" msg_info "Starting service" systemctl start jellystat msg_ok "Started service" msg_ok "Updated successfully" exit fi } # ============================================================================== # INSTALL # ============================================================================== function install() { # Setup Node.js (only installs if not present or different version) if command -v node &>/dev/null; then msg_ok "Node.js already installed ($(node -v))" else NODE_VERSION="22" setup_nodejs fi # Setup PostgreSQL (only installs if not present) if command -v psql &>/dev/null; then msg_ok "PostgreSQL already installed" else PG_VERSION="17" setup_postgresql fi # Create database and user (skip if already exists) local DB_NAME="jellystat" local DB_USER="jellystat" local DB_PASS msg_info "Setting up PostgreSQL database" # Check if database already exists if sudo -u postgres psql -lqt 2>/dev/null | cut -d \| -f 1 | grep -qw "$DB_NAME"; then msg_warn "Database '${DB_NAME}' already exists - skipping creation" echo -n "${TAB}Enter existing database password for '${DB_USER}': " read -rs DB_PASS echo "" else # Generate new password DB_PASS=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | head -c16) # Check if user exists, create if not if sudo -u postgres psql -tAc "SELECT 1 FROM pg_roles WHERE rolname='${DB_USER}'" 2>/dev/null | grep -q 1; then msg_info "User '${DB_USER}' exists, updating password" $STD sudo -u postgres psql -c "ALTER USER ${DB_USER} WITH PASSWORD '${DB_PASS}';" || { msg_error "Failed to update PostgreSQL user" return 1 } else $STD sudo -u postgres psql -c "CREATE USER ${DB_USER} WITH PASSWORD '${DB_PASS}';" || { msg_error "Failed to create PostgreSQL user" return 1 } fi # Create database (use template0 for UTF8 encoding compatibility) $STD sudo -u postgres psql -c "CREATE DATABASE ${DB_NAME} WITH OWNER ${DB_USER} ENCODING 'UTF8' LC_COLLATE='C' LC_CTYPE='C' TEMPLATE template0;" || { msg_error "Failed to create PostgreSQL database" return 1 } $STD sudo -u postgres psql -c "GRANT ALL PRIVILEGES ON DATABASE ${DB_NAME} TO ${DB_USER};" || { msg_error "Failed to grant privileges" return 1 } # Grant schema permissions (required for PostgreSQL 15+) $STD sudo -u postgres psql -d "${DB_NAME}" -c "GRANT ALL ON SCHEMA public TO ${DB_USER};" || true # Configure pg_hba.conf for password authentication on localhost local PG_HBA PG_HBA=$(sudo -u postgres psql -tAc "SHOW hba_file;" 2>/dev/null | tr -d ' ') if [[ -n "$PG_HBA" && -f "$PG_HBA" ]]; then # Check if md5/scram-sha-256 auth is already configured for local connections if ! grep -qE "^host\s+${DB_NAME}\s+${DB_USER}\s+127.0.0.1" "$PG_HBA"; then msg_info "Configuring PostgreSQL authentication" # Add password auth for jellystat user on localhost (before the default rules) sed -i "/^# IPv4 local connections:/a host ${DB_NAME} ${DB_USER} 127.0.0.1/32 scram-sha-256" "$PG_HBA" sed -i "/^# IPv4 local connections:/a host ${DB_NAME} ${DB_USER} ::1/128 scram-sha-256" "$PG_HBA" # Reload PostgreSQL to apply changes systemctl reload postgresql msg_ok "Configured PostgreSQL authentication" fi fi msg_ok "Created PostgreSQL database '${DB_NAME}'" fi # Generate JWT Secret local JWT_SECRET JWT_SECRET=$(openssl rand -base64 32 | tr -dc 'a-zA-Z0-9' | head -c32) # Force fresh download by removing version cache rm -f "$HOME/.jellystat" fetch_and_deploy_gh_release "jellystat" "CyferShepard/Jellystat" "tarball" "latest" "$INSTALL_PATH" msg_info "Installing dependencies" cd "$INSTALL_PATH" $STD npm install msg_ok "Installed dependencies" msg_info "Building ${APP}" $STD npm run build msg_ok "Built ${APP}" msg_info "Creating configuration" cat <"$CONFIG_PATH" # Jellystat Configuration # Database POSTGRES_USER=${DB_USER} POSTGRES_PASSWORD=${DB_PASS} POSTGRES_IP=localhost POSTGRES_PORT=5432 POSTGRES_DB=${DB_NAME} # Security JWT_SECRET=${JWT_SECRET} # Server JS_LISTEN_IP=0.0.0.0 JS_BASE_URL=/ TZ=$(cat /etc/timezone 2>/dev/null || echo "UTC") # Optional: GeoLite for IP Geolocation # JS_GEOLITE_ACCOUNT_ID= # JS_GEOLITE_LICENSE_KEY= # Optional: Master Override (if you forget your password) # JS_USER=admin # JS_PASSWORD=admin # Optional: Minimum playback duration to record (seconds) # MINIMUM_SECONDS_TO_INCLUDE_PLAYBACK=1 # Optional: Self-signed certificates REJECT_SELF_SIGNED_CERTIFICATES=true EOF chmod 600 "$CONFIG_PATH" msg_ok "Created configuration" msg_info "Creating service" cat <"$SERVICE_PATH" [Unit] Description=Jellystat - Statistics for Jellyfin After=network.target postgresql.service [Service] Type=simple User=root WorkingDirectory=${INSTALL_PATH}/backend EnvironmentFile=${CONFIG_PATH} ExecStart=/usr/bin/node ${INSTALL_PATH}/backend/server.js Restart=always RestartSec=10 [Install] WantedBy=multi-user.target EOF systemctl enable --now jellystat &>/dev/null msg_ok "Created and started service" # Create update script (simple wrapper that calls this addon with type=update) msg_info "Creating update script" cat <<'UPDATEEOF' >/usr/local/bin/update_jellystat #!/usr/bin/env bash # Jellystat Update Script type=update bash -c "$(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/tools/addon/jellystat.sh)" UPDATEEOF chmod +x /usr/local/bin/update_jellystat msg_ok "Created update script (/usr/local/bin/update_jellystat)" # Save credentials local CREDS_FILE="/root/jellystat.creds" cat <"$CREDS_FILE" Jellystat Credentials ===================== Database User: ${DB_USER} Database Password: ${DB_PASS} Database Name: ${DB_NAME} JWT Secret: ${JWT_SECRET} Web UI: http://${LOCAL_IP}:${DEFAULT_PORT} EOF chmod 600 "$CREDS_FILE" echo "" msg_ok "${APP} is reachable at: ${BL}http://${LOCAL_IP}:${DEFAULT_PORT}${CL}" msg_ok "Credentials saved to: ${BL}${CREDS_FILE}${CL}" echo "" msg_warn "On first access, you'll need to configure your Jellyfin server connection." } # ============================================================================== # MAIN # ============================================================================== # Handle type=update (called from update script) if [[ "${type:-}" == "update" ]]; then header_info if [[ -d "$INSTALL_PATH" && -f "$INSTALL_PATH/package.json" ]]; then update else msg_error "${APP} is not installed. Nothing to update." exit 233 fi exit 0 fi header_info get_lxc_ip # Check if already installed if [[ -d "$INSTALL_PATH" && -f "$INSTALL_PATH/package.json" ]]; then msg_warn "${APP} is already installed." echo "" echo -n "${TAB}Uninstall ${APP}? (y/N): " read -r uninstall_prompt if [[ "${uninstall_prompt,,}" =~ ^(y|yes)$ ]]; then uninstall exit 0 fi echo -n "${TAB}Update ${APP}? (y/N): " read -r update_prompt if [[ "${update_prompt,,}" =~ ^(y|yes)$ ]]; then update exit 0 fi msg_warn "No action selected. Exiting." exit 0 fi # Fresh installation msg_warn "${APP} is not installed." echo "" echo -e "${TAB}${INFO} This will install:" echo -e "${TAB} - Node.js 22" echo -e "${TAB} - PostgreSQL 17" echo -e "${TAB} - Jellystat" echo "" echo -n "${TAB}Install ${APP}? (y/N): " read -r install_prompt if [[ "${install_prompt,,}" =~ ^(y|yes)$ ]]; then install else msg_warn "Installation cancelled. Exiting." exit 0 fi ================================================ FILE: tools/addon/komodo.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://komo.do/ | Github: https://github.com/mbecker20/komodo if ! command -v curl &>/dev/null; then printf "\r\e[2K%b" '\033[93m Setup Source \033[m' >&2 apt-get update >/dev/null 2>&1 || apk update >/dev/null 2>&1 apt-get install -y curl >/dev/null 2>&1 || apk add --no-cache curl >/dev/null 2>&1 fi source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/core.func) source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/tools.func) source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/error_handler.func) source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func) 2>/dev/null || true declare -f init_tool_telemetry &>/dev/null && init_tool_telemetry "komodo" "addon" # Enable error handling set -Eeuo pipefail trap 'error_handler' ERR # ============================================================================== # CONFIGURATION # ============================================================================== APP="Komodo" APP_TYPE="addon" INSTALL_PATH="/opt/komodo" COMPOSE_ENV="${INSTALL_PATH}/compose.env" DEFAULT_PORT=9120 # Initialize all core functions (colors, formatting, icons, STD mode) load_functions # ============================================================================== # HELPERS # ============================================================================== function find_compose_file() { COMPOSE_FILE=$(find "$INSTALL_PATH" -maxdepth 1 -type f -name '*.compose.yaml' ! -name 'compose.env' | head -n1) if [[ -z "${COMPOSE_FILE:-}" ]]; then msg_error "No valid compose file found in ${INSTALL_PATH}!" exit 233 fi COMPOSE_BASENAME=$(basename "$COMPOSE_FILE") } function check_legacy_db() { if [[ "$COMPOSE_BASENAME" == "sqlite.compose.yaml" || "$COMPOSE_BASENAME" == "postgres.compose.yaml" ]]; then msg_error "Detected outdated Komodo setup using SQLite or PostgreSQL (FerretDB v1)." echo -e "${YW}This configuration is no longer supported since Komodo v1.18.0.${CL}" echo -e "${YW}Please follow the migration guide:${CL}" echo -e "${BGN}https://github.com/community-scripts/ProxmoxVE/discussions/5689${CL}\n" exit 238 fi } # ============================================================================== # UNINSTALL # ============================================================================== function uninstall() { msg_info "Uninstalling ${APP}" find_compose_file msg_info "Stopping and removing Docker containers" cd "$INSTALL_PATH" $STD docker compose -p komodo -f "$COMPOSE_FILE" --env-file "$COMPOSE_ENV" down --volumes --remove-orphans msg_ok "Stopped and removed Docker containers" rm -rf "$INSTALL_PATH" msg_ok "${APP} has been uninstalled" } # ============================================================================== # UPDATE # ============================================================================== function update() { find_compose_file check_legacy_db msg_info "Updating ${APP}" BACKUP_FILE="${INSTALL_PATH}/${COMPOSE_BASENAME}.bak_$(date +%Y%m%d_%H%M%S)" cp "$COMPOSE_FILE" "$BACKUP_FILE" || { msg_error "Failed to create backup of ${COMPOSE_BASENAME}!" exit 235 } cp "$COMPOSE_ENV" "${COMPOSE_ENV}.bak_$(date +%Y%m%d_%H%M%S)" 2>/dev/null || true GITHUB_URL="https://raw.githubusercontent.com/moghtech/komodo/main/compose/${COMPOSE_BASENAME}" if ! curl -fsSL "$GITHUB_URL" -o "$COMPOSE_FILE"; then msg_error "Failed to download ${COMPOSE_BASENAME} from GitHub!" mv "$BACKUP_FILE" "$COMPOSE_FILE" exit 115 fi # === v2 migration: image tag (latest is deprecated) === if grep -q '^COMPOSE_KOMODO_IMAGE_TAG=latest' "$COMPOSE_ENV"; then msg_info "Migrating to Komodo v2 image tag" sed -i 's/^COMPOSE_KOMODO_IMAGE_TAG=latest/COMPOSE_KOMODO_IMAGE_TAG=2/' "$COMPOSE_ENV" msg_ok "Migrated image tag to :2" fi # === v2 migration: DB credential variable names === if grep -q '^KOMODO_DB_USERNAME=' "$COMPOSE_ENV"; then msg_info "Migrating database credential variables" sed -i 's/^KOMODO_DB_USERNAME=/KOMODO_DATABASE_USERNAME=/' "$COMPOSE_ENV" sed -i 's/^KOMODO_DB_PASSWORD=/KOMODO_DATABASE_PASSWORD=/' "$COMPOSE_ENV" msg_ok "Migrated DB credential variables" fi # === v2 migration: remove deprecated passkey (replaced by PKI) === if grep -q '^KOMODO_PASSKEY=' "$COMPOSE_ENV"; then sed -i '/^KOMODO_PASSKEY=/d' "$COMPOSE_ENV" fi # === v2 migration: ensure PERIPHERY_CORE_PUBLIC_KEYS is set === if ! grep -q 'PERIPHERY_CORE_PUBLIC_KEYS' "$COMPOSE_ENV"; then echo '## Use the public key generated by Core.' >> "$COMPOSE_ENV" echo 'PERIPHERY_CORE_PUBLIC_KEYS=file:/config/keys/core.pub' >> "$COMPOSE_ENV" fi # === ensure backups path is set === if ! grep -q 'COMPOSE_KOMODO_BACKUPS_PATH=' "$COMPOSE_ENV"; then echo 'COMPOSE_KOMODO_BACKUPS_PATH=/etc/komodo/backups' >>"$COMPOSE_ENV" fi $STD docker compose -p komodo -f "$COMPOSE_FILE" --env-file "$COMPOSE_ENV" pull $STD docker compose -p komodo -f "$COMPOSE_FILE" --env-file "$COMPOSE_ENV" up -d msg_ok "Updated ${APP}" msg_ok "Updated successfully" exit } # ============================================================================== # PROXMOX HOST CHECK # ============================================================================== function check_proxmox_host() { if command -v pveversion &>/dev/null; then msg_error "Running on the Proxmox host is NOT recommended!" msg_error "This should be executed inside an LXC container." echo "" echo -n "${TAB}Continue anyway? (y/N): " read -r confirm if [[ ! "${confirm,,}" =~ ^(y|yes)$ ]]; then msg_warn "Aborted. Please run this inside an LXC container." exit 0 fi msg_warn "Proceeding on Proxmox host at your own risk!" fi } # ============================================================================== # CHECK / INSTALL DOCKER # ============================================================================== function ensure_openssl() { if command -v openssl &>/dev/null; then return fi msg_info "Installing openssl" if [[ -f /etc/alpine-release ]]; then $STD apk add openssl elif command -v apt-get &>/dev/null; then $STD apt-get update $STD apt-get install -y openssl else msg_error "openssl is required but could not be installed automatically." exit 10 fi msg_ok "Installed openssl" } function check_or_install_docker() { if command -v docker &>/dev/null; then msg_ok "Docker $(docker --version | cut -d' ' -f3 | tr -d ',') is available" if docker compose version &>/dev/null; then msg_ok "Docker Compose is available" else msg_error "Docker Compose plugin is not available. Please install it." exit 10 fi ensure_openssl return fi msg_warn "Docker is not installed." echo -n "${TAB}Install Docker now? (y/N): " read -r install_docker_prompt if [[ ! "${install_docker_prompt,,}" =~ ^(y|yes)$ ]]; then msg_error "Docker is required for ${APP}. Exiting." exit 10 fi msg_info "Installing Docker" if [[ -f /etc/alpine-release ]]; then $STD apk add docker docker-cli-compose $STD rc-service docker start $STD rc-update add docker default else DOCKER_CONFIG_PATH='/etc/docker/daemon.json' mkdir -p "$(dirname "$DOCKER_CONFIG_PATH")" echo -e '{\n "log-driver": "journald"\n}' >"$DOCKER_CONFIG_PATH" $STD sh <(curl -fsSL https://get.docker.com) fi msg_ok "Installed Docker" ensure_openssl } # ============================================================================== # INSTALL # ============================================================================== function install() { check_or_install_docker echo -e "${TAB}Choose the database for Komodo:" echo -e "${TAB} 1) MongoDB (recommended)" echo -e "${TAB} 2) FerretDB" echo -n "${TAB}Enter your choice (default: 1): " read -r DB_CHOICE DB_CHOICE=${DB_CHOICE:-1} case $DB_CHOICE in 1) DB_COMPOSE_FILE="mongo.compose.yaml" ;; 2) DB_COMPOSE_FILE="ferretdb.compose.yaml" ;; *) msg_warn "Invalid choice. Defaulting to MongoDB." DB_COMPOSE_FILE="mongo.compose.yaml" ;; esac msg_info "Creating install directory" mkdir -p "$INSTALL_PATH" msg_ok "Created ${INSTALL_PATH}" msg_info "Downloading Docker Compose file" curl -fsSL "https://raw.githubusercontent.com/moghtech/komodo/main/compose/$DB_COMPOSE_FILE" -o "${INSTALL_PATH}/${DB_COMPOSE_FILE}" msg_ok "Downloaded ${DB_COMPOSE_FILE}" msg_info "Configuring environment" curl -fsSL "https://raw.githubusercontent.com/moghtech/komodo/main/compose/compose.env" -o "$COMPOSE_ENV" DB_PASSWORD=$(openssl rand -base64 16 | tr -d '/+=') ADMIN_PASSWORD=$(openssl rand -base64 8 | tr -d '/+=') WEBHOOK_SECRET=$(openssl rand -base64 24 | tr -d '/+=') JWT_SECRET=$(openssl rand -base64 24 | tr -d '/+=') sed -i "s/^KOMODO_DATABASE_USERNAME=.*/KOMODO_DATABASE_USERNAME=komodo_admin/" "$COMPOSE_ENV" sed -i "s/^KOMODO_DATABASE_PASSWORD=.*/KOMODO_DATABASE_PASSWORD=${DB_PASSWORD}/" "$COMPOSE_ENV" sed -i "s/^KOMODO_INIT_ADMIN_PASSWORD=changeme/KOMODO_INIT_ADMIN_PASSWORD=${ADMIN_PASSWORD}/" "$COMPOSE_ENV" sed -i "s/^KOMODO_WEBHOOK_SECRET=.*/KOMODO_WEBHOOK_SECRET=${WEBHOOK_SECRET}/" "$COMPOSE_ENV" sed -i "s/^KOMODO_JWT_SECRET=.*/KOMODO_JWT_SECRET=${JWT_SECRET}/" "$COMPOSE_ENV" msg_ok "Configured environment" msg_info "Starting ${APP}" cd "$INSTALL_PATH" $STD docker compose -p komodo -f "${INSTALL_PATH}/${DB_COMPOSE_FILE}" --env-file "$COMPOSE_ENV" up -d msg_ok "Started ${APP}" { echo "Komodo Credentials" echo "" echo "Admin User : admin" echo "Admin Password: $ADMIN_PASSWORD" } >>~/komodo.creds echo "" msg_ok "${APP} is reachable at: ${BL}http://${LOCAL_IP}:${DEFAULT_PORT}${CL}" echo "" echo -e " Komodo Credentials" echo -e " ==================" echo -e " User : admin" echo -e " Password: ${ADMIN_PASSWORD}" echo "" msg_info "Credentials saved to ~/komodo.creds" } # ============================================================================== # MAIN # ============================================================================== # Handle type=update (called from update script) if [[ "${type:-}" == "update" ]]; then header_info COMPOSE_FILE="" COMPOSE_BASENAME="" if [[ -d "$INSTALL_PATH" ]]; then update else msg_error "${APP} is not installed. Nothing to update." exit 233 fi exit 0 fi header_info check_proxmox_host get_lxc_ip # Declare variables used by find_compose_file COMPOSE_FILE="" COMPOSE_BASENAME="" # Check if already installed if [[ -d "$INSTALL_PATH" ]]; then msg_warn "${APP} is already installed." echo "" echo -n "${TAB}Uninstall ${APP}? (y/N): " read -r uninstall_prompt if [[ "${uninstall_prompt,,}" =~ ^(y|yes)$ ]]; then uninstall exit 0 fi echo -n "${TAB}Update ${APP}? (y/N): " read -r update_prompt if [[ "${update_prompt,,}" =~ ^(y|yes)$ ]]; then update exit 0 fi msg_warn "No action selected. Exiting." exit 0 fi # Fresh installation msg_warn "${APP} is not installed." echo "" echo -e "${TAB}${INFO} This will install:" echo -e "${TAB} - Komodo (via Docker Compose)" echo -e "${TAB} - MongoDB or FerretDB (your choice)" echo "" echo -n "${TAB}Install ${APP}? (y/N): " read -r install_prompt if [[ "${install_prompt,,}" =~ ^(y|yes)$ ]]; then install else msg_warn "Installation cancelled. Exiting." exit 0 fi ================================================ FILE: tools/addon/netdata.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.netdata.cloud/ | Github: https://github.com/netdata/netdata function header_info { clear cat <<"EOF" _ __ __ ____ __ / | / /__ / /_/ __ \____ _/ /_____ _ / |/ / _ \/ __/ / / / __ `/ __/ __ `/ / /| / __/ /_/ /_/ / /_/ / /_/ /_/ / /_/ |_/\___/\__/_____/\__,_/\__/\__,_/ EOF } YW=$(echo "\033[33m") BL=$(echo "\033[36m") RD=$(echo "\033[01;31m") GN=$(echo "\033[1;92m") CL=$(echo "\033[m") BFR="\\r\\033[K" HOLD="-" CM="${GN}✓${CL}" silent() { "$@" >/dev/null 2>&1; } # Telemetry source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func) 2>/dev/null || true declare -f init_tool_telemetry &>/dev/null && init_tool_telemetry "netdata" "addon" set -e header_info echo "Loading..." function msg_info() { local msg="$1" echo -ne " ${HOLD} ${YW}${msg}..." } function msg_ok() { local msg="$1" echo -e "${BFR} ${CM} ${GN}${msg}${CL}" } function msg_error() { echo -e "${RD}✗ $1${CL}"; } # This function checks the version of Proxmox Virtual Environment (PVE) and exits if the version is not supported. # Supported: Proxmox VE 8.0.x – 8.9.x and 9.0–9.1.x pve_check() { local PVE_VER PVE_VER="$(pveversion | awk -F'/' '{print $2}' | awk -F'-' '{print $1}')" # Check for Proxmox VE 8.x: allow 8.0–8.9 if [[ "$PVE_VER" =~ ^8\.([0-9]+) ]]; then local MINOR="${BASH_REMATCH[1]}" if ((MINOR < 0 || MINOR > 9)); then msg_error "This version of Proxmox VE is not supported." msg_error "Supported: Proxmox VE version 8.0 – 8.9" exit 105 fi return 0 fi # Check for Proxmox VE 9.x: allow 9.0–9.1.x if [[ "$PVE_VER" =~ ^9\.([0-9]+) ]]; then local MINOR="${BASH_REMATCH[1]}" if ((MINOR < 0 || MINOR > 1)); then msg_error "This version of Proxmox VE is not yet supported." msg_error "Supported: Proxmox VE version 9.0–9.1.x" exit 105 fi return 0 fi # All other unsupported versions msg_error "This version of Proxmox VE is not supported." msg_error "Supported versions: Proxmox VE 8.0 – 8.9 or 9.0–9.1.x" exit 105 } detect_codename() { source /etc/os-release if [[ "$ID" != "debian" ]]; then msg_error "Unsupported base OS: $ID (only Proxmox VE / Debian supported)." exit 238 fi CODENAME="${VERSION_CODENAME:-}" if [[ -z "$CODENAME" ]]; then msg_error "Could not detect Debian codename." exit 71 fi echo "$CODENAME" } get_latest_repo_pkg() { local REPO_URL=$1 curl -fsSL "$REPO_URL" | grep -oP 'netdata-repo_[^"]+all\.deb' | sort -V | tail -n1 } install() { header_info while true; do read -p "Are you sure you want to install NetData on Proxmox VE host. Proceed(y/n)? " yn case $yn in [Yy]*) break ;; [Nn]*) exit ;; *) echo "Please answer yes or no." ;; esac done read -r -p "Verbose mode? " prompt [[ ${prompt,,} =~ ^(y|yes)$ ]] && STD="" || STD="silent" CODENAME=$(detect_codename) REPO_URL="https://repo.netdata.cloud/repos/repoconfig/debian/${CODENAME}/" msg_info "Setting up repository" $STD apt-get install -y debian-keyring PKG=$(get_latest_repo_pkg "$REPO_URL") if [[ -z "$PKG" ]]; then msg_error "Could not find netdata-repo package for Debian $CODENAME" exit 237 fi curl -fsSL "${REPO_URL}${PKG}" -o "$PKG" $STD dpkg -i "$PKG" rm -f "$PKG" msg_ok "Set up repository" msg_info "Installing Netdata" $STD apt-get update $STD apt-get install -y netdata msg_ok "Installed Netdata" msg_ok "Completed successfully!\n" echo -e "\n Netdata should be reachable at${BL} http://$(hostname -I | awk '{print $1}'):19999 ${CL}\n" } uninstall() { header_info read -r -p "Verbose mode? " prompt [[ ${prompt,,} =~ ^(y|yes)$ ]] && STD="" || STD="silent" msg_info "Uninstalling Netdata" systemctl stop netdata || true rm -rf /var/log/netdata /var/lib/netdata /var/cache/netdata /etc/netdata/go.d rm -rf /etc/apt/trusted.gpg.d/netdata-archive-keyring.gpg /etc/apt/sources.list.d/netdata.list $STD apt-get remove --purge -y netdata netdata-repo systemctl daemon-reload $STD apt autoremove -y $STD userdel netdata || true msg_ok "Uninstalled Netdata" msg_ok "Completed successfully!\n" } header_info pve_check OPTIONS=(Install "Install NetData on Proxmox VE" Uninstall "Uninstall NetData from Proxmox VE") CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "NetData" \ --menu "Select an option:" 10 58 2 "${OPTIONS[@]}" 3>&1 1>&2 2>&3) case $CHOICE in "Install") install ;; "Uninstall") uninstall ;; *) echo "Exiting..." exit 0 ;; esac ================================================ FILE: tools/addon/nextcloud-exporter.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: CrazyWolf13 # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/xperimental/nextcloud-exporter if ! command -v curl &>/dev/null; then printf "\r\e[2K%b" '\033[93m Setup Source \033[m' >&2 apt-get update >/dev/null 2>&1 apt-get install -y curl >/dev/null 2>&1 fi source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/core.func) source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/tools.func) source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/error_handler.func) source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/api.func) 2>/dev/null || true # Enable error handling set -Eeuo pipefail trap 'error_handler' ERR load_functions # ============================================================================== # CONFIGURATION # ============================================================================== VERBOSE=${var_verbose:-no} APP="nextcloud-exporter" APP_TYPE="tools" BINARY_PATH="/usr/bin/nextcloud-exporter" CONFIG_PATH="/etc/nextcloud-exporter.env" SERVICE_PATH="/etc/systemd/system/nextcloud-exporter.service" # ============================================================================== # OS DETECTION # ============================================================================== if ! grep -qE 'ID=debian|ID=ubuntu' /etc/os-release 2>/dev/null; then echo -e "${CROSS} Unsupported OS detected. This script only supports Debian and Ubuntu." exit 238 fi # ============================================================================== # UNINSTALL # ============================================================================== function uninstall() { msg_info "Uninstalling Nextcloud-Exporter" systemctl disable -q --now nextcloud-exporter rm -f "$SERVICE_PATH" if dpkg -l | grep -q nextcloud-exporter; then $STD apt-get remove -y nextcloud-exporter || $STD dpkg -r nextcloud-exporter fi rm -f "$CONFIG_PATH" rm -f "/usr/local/bin/update_nextcloud-exporter" rm -f "$HOME/.nextcloud-exporter" msg_ok "Nextcloud-Exporter has been uninstalled" } # ============================================================================== # UPDATE # ============================================================================== function update() { if check_for_gh_release "nextcloud-exporter" "xperimental/nextcloud-exporter"; then msg_info "Stopping service" systemctl stop nextcloud-exporter msg_ok "Stopped service" fetch_and_deploy_gh_release "nextcloud-exporter" "xperimental/nextcloud-exporter" "binary" "latest" msg_info "Starting service" systemctl start nextcloud-exporter msg_ok "Started service" msg_ok "Updated successfully!" exit fi } # ============================================================================== # INSTALL # ============================================================================== function install() { read -erp "Enter URL of Nextcloud, example: (http://127.0.0.1:8080): " NEXTCLOUD_SERVER read -rsp "Enter Nextcloud auth token (press Enter to use username/password instead): " NEXTCLOUD_AUTH_TOKEN printf "\n" if [[ -z "$NEXTCLOUD_AUTH_TOKEN" ]]; then read -erp "Enter Nextcloud username: " NEXTCLOUD_USERNAME read -rsp "Enter Nextcloud password: " NEXTCLOUD_PASSWORD printf "\n" fi read -erp "Query additional info for apps? [Y/n]: " QUERY_APPS if [[ "${QUERY_APPS,,}" =~ ^(n|no)$ ]]; then NEXTCLOUD_INFO_APPS="false" fi read -erp "Query update information? [Y/n]: " QUERY_UPDATES if [[ "${QUERY_UPDATES,,}" =~ ^(n|no)$ ]]; then NEXTCLOUD_INFO_UPDATE="false" fi read -erp "Do you want to skip TLS-Verification (if using a self-signed Certificate on Nextcloud) [y/N]: " SKIP_TLS if [[ "${SKIP_TLS,,}" =~ ^(y|yes)$ ]]; then NEXTCLOUD_TLS_SKIP_VERIFY="true" fi fetch_and_deploy_gh_release "nextcloud-exporter" "xperimental/nextcloud-exporter" "binary" "latest" msg_info "Creating configuration" cat <"$CONFIG_PATH" # https://github.com/xperimental/nextcloud-exporter NEXTCLOUD_SERVER="${NEXTCLOUD_SERVER}" NEXTCLOUD_AUTH_TOKEN="${NEXTCLOUD_AUTH_TOKEN:-}" NEXTCLOUD_USERNAME="${NEXTCLOUD_USERNAME:-}" NEXTCLOUD_PASSWORD="${NEXTCLOUD_PASSWORD:-}" NEXTCLOUD_INFO_UPDATE=${NEXTCLOUD_INFO_UPDATE:-"true"} NEXTCLOUD_INFO_APPS=${NEXTCLOUD_INFO_APPS:-"true"} NEXTCLOUD_TLS_SKIP_VERIFY=${NEXTCLOUD_TLS_SKIP_VERIFY:-"false"} NEXTCLOUD_LISTEN_ADDRESS=":9205" EOF msg_ok "Created configuration" msg_info "Creating service" cat <"$SERVICE_PATH" [Unit] Description=nextcloud-exporter After=network.target [Service] User=root EnvironmentFile=$CONFIG_PATH ExecStart=$BINARY_PATH Restart=always [Install] WantedBy=multi-user.target EOF systemctl daemon-reload systemctl enable -q --now nextcloud-exporter msg_ok "Created and started service" # Create update script msg_info "Creating update script" ensure_usr_local_bin_persist cat <<'UPDATEEOF' >/usr/local/bin/update_nextcloud-exporter #!/usr/bin/env bash # nextcloud-exporter Update Script type=update bash -c "$(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/tools/addon/nextcloud-exporter.sh)" UPDATEEOF chmod +x /usr/local/bin/update_nextcloud-exporter msg_ok "Created update script (/usr/local/bin/update_nextcloud-exporter)" echo "" msg_ok "Nextcloud-Exporter installed successfully" msg_ok "Metrics: ${BL}http://${LOCAL_IP}:9205/metrics${CL}" msg_ok "Config: ${BL}${CONFIG_PATH}${CL}" } # ============================================================================== # MAIN # ============================================================================== header_info ensure_usr_local_bin_persist get_lxc_ip # Handle type=update (called from update script) if [[ "${type:-}" == "update" ]]; then if [[ -f "$BINARY_PATH" ]]; then update else msg_error "Nextcloud-Exporter is not installed. Nothing to update." exit 233 fi exit 0 fi # Check if already installed if [[ -f "$BINARY_PATH" ]]; then msg_warn "Nextcloud-Exporter is already installed." echo "" echo -n "${TAB}Uninstall Nextcloud-Exporter? (y/N): " read -r uninstall_prompt if [[ "${uninstall_prompt,,}" =~ ^(y|yes)$ ]]; then uninstall exit 0 fi echo -n "${TAB}Update Nextcloud-Exporter? (y/N): " read -r update_prompt if [[ "${update_prompt,,}" =~ ^(y|yes)$ ]]; then update exit 0 fi msg_warn "No action selected. Exiting." exit 0 fi # Fresh installation msg_warn "Nextcloud-Exporter is not installed." echo "" echo -e "${TAB}${INFO} This will install:" echo -e "${TAB} - Nextcloud Exporter (binary)" echo -e "${TAB} - Systemd service" echo "" echo -n "${TAB}Install Nextcloud-Exporter? (y/N): " read -r install_prompt if [[ "${install_prompt,,}" =~ ^(y|yes)$ ]]; then install else msg_warn "Installation cancelled. Exiting." exit 0 fi ================================================ FILE: tools/addon/olivetin.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://docs.olivetin.app/ | Github: https://github.com/OliveTin/OliveTin function header_info { clear cat <<"EOF" ____ ___ _______ / __ \/ (_) _____/_ __(_)___ / / / / / / | / / _ \/ / / / __ \ / /_/ / / /| |/ / __/ / / / / / / \____/_/_/ |___/\___/_/ /_/_/ /_/ EOF } IP=$(hostname -I | awk '{print $1}') YW=$(echo "\033[33m") BL=$(echo "\033[36m") GN=$(echo "\033[1;92m") CL=$(echo "\033[m") BFR="\\r\\033[K" HOLD="-" CM="${GN}✓${CL}" APP="OliveTin" hostname="$(hostname)" # Telemetry source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func) 2>/dev/null || true declare -f init_tool_telemetry &>/dev/null && init_tool_telemetry "olivetin" "addon" set-e header_info while true; do read -p "This will Install ${APP} on $hostname. Proceed(y/n)?" yn case $yn in [Yy]*) break ;; [Nn]*) exit ;; *) echo "Please answer yes or no." ;; esac done header_info function msg_info() { local msg="$1" echo -ne " ${HOLD} ${YW}${msg}..." } function msg_ok() { local msg="$1" echo -e "${BFR} ${CM} ${GN}${msg}${CL}" } msg_info "Installing ${APP}" if ! command -v curl &>/dev/null; then apt-get update >/dev/null 2>&1 apt-get install -y curl >/dev/null 2>&1 fi curl -fsSL "https://github.com/OliveTin/OliveTin/releases/latest/download/OliveTin_linux_arm64.deb" -o $(basename "https://github.com/OliveTin/OliveTin/releases/latest/download/OliveTin_linux_arm64.deb") dpkg -i OliveTin_linux_arm64.deb &>/dev/null systemctl enable --now OliveTin &>/dev/null rm OliveTin_linux_arm64.deb msg_ok "Installed ${APP} on $hostname" msg_ok "Completed successfully!\n" echo -e "${APP} should be reachable by going to the following URL. ${BL}http://$IP:1337${CL} \n" ================================================ FILE: tools/addon/phpmyadmin.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE function header_info { clear cat <<"EOF" ____ __ __ ___ ___ __ _ / __ \/ /_ ____ / |/ /_ __/ | ____/ /___ ___ (_)___ / /_/ / __ \/ __ \/ /|_/ / / / / /| |/ __ / __ `__ \/ / __ \ / ____/ / / / /_/ / / / / /_/ / ___ / /_/ / / / / / / / / / / /_/ /_/ /_/ .___/_/ /_/\__, /_/ |_\__,_/_/ /_/ /_/_/_/ /_/ /_/ /____/ EOF } YW=$(echo "\033[33m") GN=$(echo "\033[1;92m") RD=$(echo "\033[01;31m") BL=$(echo "\033[36m") CL=$(echo "\033[m") CM="${GN}✔️${CL}" CROSS="${RD}✖️${CL}" INFO="${BL}ℹ️${CL}" APP="phpMyAdmin" INSTALL_DIR_DEBIAN="/var/www/html/phpMyAdmin" INSTALL_DIR_ALPINE="/usr/share/phpmyadmin" # Telemetry source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func) 2>/dev/null || true declare -f init_tool_telemetry &>/dev/null && init_tool_telemetry "phpmyadmin" "addon" IFACE=$(ip -4 route | awk '/default/ {print $5; exit}') IP=$(ip -4 addr show "$IFACE" | awk '/inet / {print $2}' | cut -d/ -f1 | head -n 1) [[ -z "$IP" ]] && IP=$(hostname -I | awk '{print $1}') [[ -z "$IP" ]] && IP="127.0.0.1" # Detect OS if [[ -f "/etc/alpine-release" ]]; then OS="Alpine" PKG_MANAGER_INSTALL="apk add --no-cache" PKG_QUERY="apk info -e" INSTALL_DIR="$INSTALL_DIR_ALPINE" elif [[ -f "/etc/debian_version" ]]; then OS="Debian" PKG_MANAGER_INSTALL="apt-get install -y" PKG_QUERY="dpkg -l" INSTALL_DIR="$INSTALL_DIR_DEBIAN" else echo -e "${CROSS} Unsupported OS detected. Exiting." exit 238 fi header_info function msg_info() { echo -e "${INFO} ${YW}${1}...${CL}"; } function msg_ok() { echo -e "${CM} ${GN}${1}${CL}"; } function msg_error() { echo -e "${CROSS} ${RD}${1}${CL}"; } function check_internet() { if ! command -v curl &>/dev/null; then apt-get update >/dev/null 2>&1 apt-get install -y curl >/dev/null 2>&1 fi msg_info "Checking Internet connectivity to GitHub" HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" https://github.com) if [[ "$HTTP_CODE" -ge 200 && "$HTTP_CODE" -lt 400 ]]; then msg_ok "Internet connectivity OK" else msg_error "Internet connectivity or GitHub unreachable (Status $HTTP_CODE). Exiting." exit 115 fi } function is_phpmyadmin_installed() { if [[ "$OS" == "Debian" ]]; then [[ -f "$INSTALL_DIR/config.inc.php" ]] else [[ -d "$INSTALL_DIR_ALPINE" ]] && rc-service lighttpd status &>/dev/null fi } function install_php_and_modules() { msg_info "Checking existing PHP installation" if command -v php >/dev/null 2>&1; then PHP_VERSION=$(php -r 'echo PHP_VERSION;') msg_ok "Found PHP version $PHP_VERSION" else msg_info "PHP not found, will install PHP core" fi if [[ "$OS" == "Debian" ]]; then PHP_MODULES=("php" "php-mysqli" "php-mbstring" "php-zip" "php-gd" "php-json" "php-curl") MISSING_PACKAGES=() for pkg in "${PHP_MODULES[@]}"; do if ! dpkg -l | grep -qw "$pkg"; then MISSING_PACKAGES+=("$pkg") fi done if [[ ${#MISSING_PACKAGES[@]} -gt 0 ]]; then msg_info "Installing missing PHP packages: ${MISSING_PACKAGES[*]}" if ! apt-get update &>/dev/null || ! apt-get install -y "${MISSING_PACKAGES[@]}" &>/dev/null; then msg_error "Failed to install required PHP modules. Exiting." exit 237 fi msg_ok "Installed missing PHP packages" else msg_ok "All required PHP modules are already installed" fi else msg_info "Installing Lighttpd and PHP for Alpine" $PKG_MANAGER_INSTALL lighttpd php php-fpm php-session php-json php-mysqli curl tar openssl &>/dev/null msg_ok "Installed Lighttpd and PHP" fi } function install_phpmyadmin() { msg_info "Fetching latest phpMyAdmin release from GitHub" LATEST_VERSION_RAW=$(curl -s https://api.github.com/repos/phpmyadmin/phpmyadmin/releases/latest | grep tag_name | cut -d '"' -f4) LATEST_VERSION=$(echo "$LATEST_VERSION_RAW" | sed -e 's/^RELEASE_//' -e 's/_/./g') if [[ -z "$LATEST_VERSION" ]]; then msg_error "Could not determine latest phpMyAdmin version from GitHub – falling back to 5.2.2" LATEST_VERSION="RELEASE_5_2_2" fi msg_ok "Latest version: $LATEST_VERSION" TARBALL_URL="https://files.phpmyadmin.net/phpMyAdmin/${LATEST_VERSION}/phpMyAdmin-${LATEST_VERSION}-all-languages.tar.gz" msg_info "Downloading ${TARBALL_URL}" if ! curl -fsSL "$TARBALL_URL" -o /tmp/phpmyadmin.tar.gz; then msg_error "Download failed: $TARBALL_URL" exit 115 fi mkdir -p "$INSTALL_DIR" tar xf /tmp/phpmyadmin.tar.gz --strip-components=1 -C "$INSTALL_DIR" } function configure_phpmyadmin() { if [[ "$OS" == "Debian" ]]; then cp "$INSTALL_DIR/config.sample.inc.php" "$INSTALL_DIR/config.inc.php" SECRET=$(openssl rand -base64 24) sed -i "s#\$cfg\['blowfish_secret'\] = '';#\$cfg['blowfish_secret'] = '${SECRET}';#" "$INSTALL_DIR/config.inc.php" chmod 660 "$INSTALL_DIR/config.inc.php" chown -R www-data:www-data "$INSTALL_DIR" systemctl restart apache2 msg_ok "Configured phpMyAdmin with Apache" else msg_info "Configuring Lighttpd for phpMyAdmin (Alpine detected)" mkdir -p /etc/lighttpd cat </etc/lighttpd/lighttpd.conf server.modules = ( "mod_access", "mod_alias", "mod_accesslog", "mod_fastcgi" ) server.document-root = "${INSTALL_DIR}" server.port = 80 index-file.names = ( "index.php", "index.html" ) fastcgi.server = ( ".php" => (( "host" => "127.0.0.1", "port" => 9000, "check-local" => "disable" )) ) alias.url = ( "/phpMyAdmin/" => "${INSTALL_DIR}/" ) accesslog.filename = "/var/log/lighttpd/access.log" server.errorlog = "/var/log/lighttpd/error.log" EOF msg_info "Starting PHP-FPM and Lighttpd" PHP_VERSION=$(php -r 'echo PHP_MAJOR_VERSION . PHP_MINOR_VERSION;') PHP_FPM_SERVICE="php-fpm${PHP_VERSION}" if $STD rc-service "$PHP_FPM_SERVICE" start && $STD rc-update add "$PHP_FPM_SERVICE" default; then msg_ok "Started PHP-FPM service: $PHP_FPM_SERVICE" else msg_error "Failed to start PHP-FPM service: $PHP_FPM_SERVICE" exit 150 fi $STD rc-service lighttpd start $STD rc-update add lighttpd default msg_ok "Configured and started Lighttpd successfully" fi } function uninstall_phpmyadmin() { msg_info "Stopping Webserver" if [[ "$OS" == "Debian" ]]; then systemctl stop apache2 else $STD rc-service lighttpd stop $STD rc-service php-fpm stop fi msg_info "Removing phpMyAdmin directory" rm -rf "$INSTALL_DIR" if [[ "$OS" == "Alpine" ]]; then msg_info "Removing Lighttpd config" rm -f /etc/lighttpd/lighttpd.conf $STD rc-service php-fpm restart $STD rc-service lighttpd restart else $STD systemctl restart apache2 fi msg_ok "Uninstalled phpMyAdmin" } function update_phpmyadmin() { msg_info "Fetching latest phpMyAdmin release from GitHub" LATEST_VERSION_RAW=$(curl -s https://api.github.com/repos/phpmyadmin/phpmyadmin/releases/latest | grep tag_name | cut -d '"' -f4) LATEST_VERSION=$(echo "$LATEST_VERSION_RAW" | sed -e 's/^RELEASE_//' -e 's/_/./g') if [[ -z "$LATEST_VERSION" ]]; then msg_error "Could not determine latest phpMyAdmin version from GitHub – falling back to 5.2.2" LATEST_VERSION="5.2.2" fi msg_ok "Latest version: $LATEST_VERSION" TARBALL_URL="https://files.phpmyadmin.net/phpMyAdmin/${LATEST_VERSION}/phpMyAdmin-${LATEST_VERSION}-all-languages.tar.gz" msg_info "Downloading ${TARBALL_URL}" if ! curl -fsSL "$TARBALL_URL" -o /tmp/phpmyadmin.tar.gz; then msg_error "Download failed: $TARBALL_URL" exit 115 fi BACKUP_DIR="/tmp/phpmyadmin-backup-$(date +%Y%m%d-%H%M%S)" mkdir -p "$BACKUP_DIR" BACKUP_ITEMS=("config.inc.php" "upload" "save" "tmp" "themes") msg_info "Backing up existing phpMyAdmin data" for item in "${BACKUP_ITEMS[@]}"; do [[ -e "$INSTALL_DIR/$item" ]] && cp -a "$INSTALL_DIR/$item" "$BACKUP_DIR/" && echo " ↪︎ $item" done msg_ok "Backup completed: $BACKUP_DIR" tar xf /tmp/phpmyadmin.tar.gz --strip-components=1 -C "$INSTALL_DIR" msg_ok "Extracted phpMyAdmin $LATEST_VERSION" msg_info "Restoring preserved files" for item in "${BACKUP_ITEMS[@]}"; do [[ -e "$BACKUP_DIR/$item" ]] && cp -a "$BACKUP_DIR/$item" "$INSTALL_DIR/" && echo " ↪︎ $item restored" done msg_ok "Restoration completed" configure_phpmyadmin } if is_phpmyadmin_installed; then echo -e "${YW}⚠️ ${APP} is already installed at ${INSTALL_DIR}.${CL}" read -r -p "Would you like to Update (1), Uninstall (2) or Cancel (3)? [1/2/3]: " action action="${action//[[:space:]]/}" case "$action" in 1) check_internet update_phpmyadmin ;; 2) uninstall_phpmyadmin ;; 3) echo -e "${YW}⚠️ Action cancelled. Exiting.${CL}" exit 0 ;; *) echo -e "${YW}⚠️ Invalid input. Exiting.${CL}" exit 112 ;; esac else read -r -p "Would you like to install ${APP}? (y/n): " install_prompt install_prompt="${install_prompt//[[:space:]]/}" if [[ "${install_prompt,,}" =~ ^(y|yes)$ ]]; then check_internet install_php_and_modules install_phpmyadmin configure_phpmyadmin if [[ "$OS" == "Debian" ]]; then echo -e "${CM} ${GN}${APP} is reachable at: ${BL}http://${IP}/phpMyAdmin${CL}" else echo -e "${CM} ${GN}${APP} is reachable at: ${BL}http://${IP}/${CL}" fi else echo -e "${YW}⚠️ Installation skipped. Exiting.${CL}" exit 0 fi fi ================================================ FILE: tools/addon/pihole-exporter.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: CrazyWolf13 # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/eko/pihole-exporter/ if ! command -v curl &>/dev/null; then printf "\r\e[2K%b" '\033[93m Setup Source \033[m' >&2 apt-get update >/dev/null 2>&1 apt-get install -y curl >/dev/null 2>&1 fi source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/core.func) source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/tools.func) source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/error_handler.func) source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func) 2>/dev/null || true declare -f init_tool_telemetry &>/dev/null && init_tool_telemetry "pihole-exporter" "addon" # Enable error handling set -Eeuo pipefail trap 'error_handler' ERR load_functions # ============================================================================== # CONFIGURATION # ============================================================================== VERBOSE=${var_verbose:-no} APP="pihole-exporter" APP_TYPE="tools" INSTALL_PATH="/opt/pihole-exporter" CONFIG_PATH="/opt/pihole-exporter.env" # ============================================================================== # OS DETECTION # ============================================================================== if [[ -f "/etc/alpine-release" ]]; then OS="Alpine" SERVICE_PATH="/etc/init.d/pihole-exporter" elif grep -qE 'ID=debian|ID=ubuntu' /etc/os-release; then OS="Debian" SERVICE_PATH="/etc/systemd/system/pihole-exporter.service" else echo -e "${CROSS} Unsupported OS detected. Exiting." exit 238 fi # ============================================================================== # UNINSTALL # ============================================================================== function uninstall() { msg_info "Uninstalling Pihole-Exporter" if [[ "$OS" == "Alpine" ]]; then rc-service pihole-exporter stop &>/dev/null rc-update del pihole-exporter &>/dev/null rm -f "$SERVICE_PATH" else systemctl disable -q --now pihole-exporter rm -f "$SERVICE_PATH" fi rm -rf "$INSTALL_PATH" "$CONFIG_PATH" rm -f "/usr/local/bin/update_pihole-exporter" rm -f "$HOME/.pihole-exporter" msg_ok "Pihole-Exporter has been uninstalled" } # ============================================================================== # UPDATE # ============================================================================== function update() { if check_for_gh_release "pihole-exporter" "eko/pihole-exporter"; then msg_info "Stopping service" if [[ "$OS" == "Alpine" ]]; then rc-service pihole-exporter stop &>/dev/null else systemctl stop pihole-exporter fi msg_ok "Stopped service" fetch_and_deploy_gh_release "pihole-exporter" "eko/pihole-exporter" "tarball" "latest" setup_go msg_info "Building Pihole-Exporter" cd /opt/pihole-exporter/ $STD /usr/local/bin/go build -o ./pihole-exporter msg_ok "Built Pihole-Exporter" msg_info "Starting service" if [[ "$OS" == "Alpine" ]]; then rc-service pihole-exporter start &>/dev/null else systemctl start pihole-exporter fi msg_ok "Started service" msg_ok "Updated successfully!" exit fi } # ============================================================================== # INSTALL # ============================================================================== function install() { read -erp "Enter the protocol to use (http/https), default https: " pihole_PROTOCOL read -erp "Enter the hostname of Pihole, example: (127.0.0.1): " pihole_HOSTNAME read -erp "Enter the port of Pihole, default 443: " pihole_PORT read -rsp "Enter Pihole password: " pihole_PASSWORD printf "\n" read -erp "Do you want to skip TLS-Verification (if using a self-signed Certificate on Pi-Hole) [y/N]: " SKIP_TLS if [[ "${SKIP_TLS,,}" =~ ^(y|yes)$ ]]; then pihole_SKIP_TLS="true" fi fetch_and_deploy_gh_release "pihole-exporter" "eko/pihole-exporter" "tarball" "latest" setup_go msg_info "Building Pihole-Exporter on ${OS}" cd /opt/pihole-exporter/ $STD /usr/local/bin/go build -o ./pihole-exporter msg_ok "Built Pihole-Exporter" msg_info "Creating configuration" cat <"$CONFIG_PATH" # https://github.com/eko/pihole-exporter/?tab=readme-ov-file#available-cli-options PIHOLE_PASSWORD="${pihole_PASSWORD}" PIHOLE_HOSTNAME="${pihole_HOSTNAME:-127.0.0.1}" PIHOLE_PORT="${pihole_PORT:-443}" SKIP_TLS_VERIFICATION="${pihole_SKIP_TLS:-false}" PIHOLE_PROTOCOL="${pihole_PROTOCOL:-https}" EOF msg_ok "Created configuration" msg_info "Creating service" if [[ "$OS" == "Debian" ]]; then cat <"$SERVICE_PATH" [Unit] Description=pihole-exporter After=network.target [Service] User=root WorkingDirectory=/opt/pihole-exporter EnvironmentFile=$CONFIG_PATH ExecStart=/opt/pihole-exporter/pihole-exporter Restart=always [Install] WantedBy=multi-user.target EOF systemctl daemon-reload systemctl enable -q --now pihole-exporter else cat <"$SERVICE_PATH" #!/sbin/openrc-run name="pihole-exporter" description="Pi-hole Exporter for Prometheus" command="${INSTALL_PATH}/pihole-exporter" command_background=true directory="/opt/pihole-exporter" pidfile="/run/\${RC_SVCNAME}.pid" output_log="/var/log/pihole-exporter.log" error_log="/var/log/pihole-exporter.log" depend() { need net after firewall } start_pre() { if [ -f "$CONFIG_PATH" ]; then export \$(grep -v '^#' $CONFIG_PATH | xargs) fi } EOF chmod +x "$SERVICE_PATH" $STD rc-update add pihole-exporter default $STD rc-service pihole-exporter start fi msg_ok "Created and started service" # Create update script msg_info "Creating update script" ensure_usr_local_bin_persist cat <<'UPDATEEOF' >/usr/local/bin/update_pihole-exporter #!/usr/bin/env bash # pihole-exporter Update Script type=update bash -c "$(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/tools/addon/pihole-exporter.sh)" UPDATEEOF chmod +x /usr/local/bin/update_pihole-exporter msg_ok "Created update script (/usr/local/bin/update_pihole-exporter)" echo "" msg_ok "Pihole-Exporter installed successfully" msg_ok "Metrics: ${BL}http://${LOCAL_IP}:9617/metrics${CL}" msg_ok "Config: ${BL}${CONFIG_PATH}${CL}" } # ============================================================================== # MAIN # ============================================================================== header_info ensure_usr_local_bin_persist get_lxc_ip # Handle type=update (called from update script) if [[ "${type:-}" == "update" ]]; then if [[ -d "$INSTALL_PATH" && -f "$INSTALL_PATH/pihole-exporter" ]]; then update else msg_error "Pihole-Exporter is not installed. Nothing to update." exit 233 fi exit 0 fi # Check if already installed if [[ -d "$INSTALL_PATH" && -f "$INSTALL_PATH/pihole-exporter" ]]; then msg_warn "Pihole-Exporter is already installed." echo "" echo -n "${TAB}Uninstall Pihole-Exporter? (y/N): " read -r uninstall_prompt if [[ "${uninstall_prompt,,}" =~ ^(y|yes)$ ]]; then uninstall exit 0 fi echo -n "${TAB}Update Pihole-Exporter? (y/N): " read -r update_prompt if [[ "${update_prompt,,}" =~ ^(y|yes)$ ]]; then update exit 0 fi msg_warn "No action selected. Exiting." exit 0 fi # Fresh installation msg_warn "Pihole-Exporter is not installed." echo "" echo -e "${TAB}${INFO} This will install:" echo -e "${TAB} - Pi-hole Exporter (Go binary)" echo -e "${TAB} - Systemd/OpenRC service" echo "" echo -n "${TAB}Install Pihole-Exporter? (y/N): " read -r install_prompt if [[ "${install_prompt,,}" =~ ^(y|yes)$ ]]; then install else msg_warn "Installation cancelled. Exiting." exit 0 fi ================================================ FILE: tools/addon/prometheus-paperless-ngx-exporter.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: Andy Grunwald (andygrunwald) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/hansmi/prometheus-paperless-exporter source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/core.func) source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/tools.func) source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/error_handler.func) source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func) 2>/dev/null || true declare -f init_tool_telemetry &>/dev/null && init_tool_telemetry "prometheus-paperless-ngx-exporter" "addon" # Enable error handling set -Eeuo pipefail trap 'error_handler' ERR load_functions # ============================================================================== # CONFIGURATION # ============================================================================== VERBOSE=${var_verbose:-no} APP="prometheus-paperless-ngx-exporter" APP_TYPE="tools" BINARY_PATH="/usr/bin/prometheus-paperless-exporter" CONFIG_PATH="/etc/prometheus-paperless-ngx-exporter/config.env" SERVICE_PATH="/etc/systemd/system/prometheus-paperless-ngx-exporter.service" AUTH_TOKEN_FILE="/etc/prometheus-paperless-ngx-exporter/paperless_auth_token_file" # ============================================================================== # OS DETECTION # ============================================================================== if ! grep -qE 'ID=debian|ID=ubuntu' /etc/os-release 2>/dev/null; then echo -e "${CROSS} Unsupported OS detected. This script only supports Debian and Ubuntu." exit 238 fi # ============================================================================== # UNINSTALL # ============================================================================== function uninstall() { msg_info "Uninstalling Prometheus-Paperless-NGX-Exporter" systemctl disable -q --now prometheus-paperless-ngx-exporter if dpkg -l | grep -q prometheus-paperless-exporter; then $STD apt-get remove -y prometheus-paperless-exporter || $STD dpkg -r prometheus-paperless-exporter fi rm -f "$SERVICE_PATH" rm -rf /etc/prometheus-paperless-ngx-exporter rm -f "/usr/local/bin/update_prometheus-paperless-ngx-exporter" rm -f "$HOME/.prometheus-paperless-ngx-exporter" msg_ok "Prometheus-Paperless-NGX-Exporter has been uninstalled" } # ============================================================================== # UPDATE # ============================================================================== function update() { if check_for_gh_release "prom-paperless-exp" "hansmi/prometheus-paperless-exporter"; then msg_info "Stopping service" systemctl stop prometheus-paperless-ngx-exporter msg_ok "Stopped service" fetch_and_deploy_gh_release "prom-paperless-exp" "hansmi/prometheus-paperless-exporter" "binary" "latest" msg_info "Starting service" systemctl start prometheus-paperless-ngx-exporter msg_ok "Started service" msg_ok "Updated successfully!" exit fi } # ============================================================================== # INSTALL # ============================================================================== function install() { read -erp "Enter URL of Paperless-NGX, example: (http://127.0.0.1:8000): " PAPERLESS_URL read -rsp "Enter Paperless-NGX authentication token: " PAPERLESS_AUTH_TOKEN printf "\n" fetch_and_deploy_gh_release "prom-paperless-exp" "hansmi/prometheus-paperless-exporter" "binary" "latest" msg_info "Creating configuration" mkdir -p /etc/prometheus-paperless-ngx-exporter cat <"$CONFIG_PATH" # https://github.com/hansmi/prometheus-paperless-exporter PAPERLESS_URL="${PAPERLESS_URL}" EOF echo "${PAPERLESS_AUTH_TOKEN}" >"$AUTH_TOKEN_FILE" chmod 600 "$AUTH_TOKEN_FILE" msg_ok "Created configuration" msg_info "Creating service" cat <"$SERVICE_PATH" [Unit] Description=Prometheus Paperless NGX Exporter Wants=network-online.target After=network-online.target [Service] User=root EnvironmentFile=$CONFIG_PATH ExecStart=$BINARY_PATH \\ --paperless_url=\${PAPERLESS_URL} \\ --paperless_auth_token_file=$AUTH_TOKEN_FILE Restart=always [Install] WantedBy=multi-user.target EOF systemctl daemon-reload systemctl enable -q --now prometheus-paperless-ngx-exporter msg_ok "Created and started service" # Create update script msg_info "Creating update script" ensure_usr_local_bin_persist cat <<'UPDATEEOF' >/usr/local/bin/update_prometheus-paperless-ngx-exporter #!/usr/bin/env bash # prometheus-paperless-ngx-exporter Update Script type=update bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/tools/addon/prometheus-paperless-ngx-exporter.sh)" UPDATEEOF chmod +x /usr/local/bin/update_prometheus-paperless-ngx-exporter msg_ok "Created update script (/usr/local/bin/update_prometheus-paperless-ngx-exporter)" echo "" msg_ok "Prometheus-Paperless-NGX-Exporter installed successfully" msg_ok "Metrics: ${BL}http://${LOCAL_IP}:8081/metrics${CL}" msg_ok "Config: ${BL}${CONFIG_PATH}${CL}" } # ============================================================================== # MAIN # ============================================================================== header_info ensure_usr_local_bin_persist get_lxc_ip # Handle type=update (called from update script) if [[ "${type:-}" == "update" ]]; then if [[ -f "$BINARY_PATH" ]]; then update else msg_error "Prometheus-Paperless-NGX-Exporter is not installed. Nothing to update." exit 233 fi exit 0 fi # Check if already installed if [[ -f "$BINARY_PATH" ]]; then msg_warn "Prometheus-Paperless-NGX-Exporter is already installed." echo "" echo -n "${TAB}Uninstall Prometheus-Paperless-NGX-Exporter? (y/N): " read -r uninstall_prompt if [[ "${uninstall_prompt,,}" =~ ^(y|yes)$ ]]; then uninstall exit 0 fi echo -n "${TAB}Update Prometheus-Paperless-NGX-Exporter? (y/N): " read -r update_prompt if [[ "${update_prompt,,}" =~ ^(y|yes)$ ]]; then update exit 0 fi msg_warn "No action selected. Exiting." exit 0 fi # Fresh installation msg_warn "Prometheus-Paperless-NGX-Exporter is not installed." echo "" echo -e "${TAB}${INFO} This will install:" echo -e "${TAB} - Prometheus Paperless NGX Exporter (binary)" echo -e "${TAB} - Systemd service" echo "" echo -n "${TAB}Install Prometheus-Paperless-NGX-Exporter? (y/N): " read -r install_prompt if [[ "${install_prompt,,}" =~ ^(y|yes)$ ]]; then install else msg_warn "Installation cancelled. Exiting." exit 0 fi ================================================ FILE: tools/addon/pyenv.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://pyenv.run/ | Github: https://github.com/pyenv/pyenv set -e YW=$(echo "\033[33m") RD=$(echo "\033[01;31m") BL=$(echo "\033[36m") GN=$(echo "\033[1;92m") CL=$(echo "\033[m") CM="${GN}✓${CL}" CROSS="${RD}✗${CL}" BFR="\\r\\033[K" HOLD="-" function msg_info() { local msg="$1" echo -ne " ${HOLD} ${YW}${msg}..." } function msg_ok() { local msg="$1" echo -e "${BFR} ${CM} ${GN}${msg}${CL}" } function msg_error() { local msg="$1" echo -e "${BFR} ${CROSS} ${RD}${msg}${CL}" } # Telemetry source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func) 2>/dev/null || true declare -f init_tool_telemetry &>/dev/null && init_tool_telemetry "pyenv" "addon" if command -v pveversion >/dev/null 2>&1; then msg_error "Can't Install on Proxmox " exit fi msg_info "Installing pyenv" apt-get install -y \ make \ build-essential \ libjpeg-dev \ libpcap-dev \ libssl-dev \ zlib1g-dev \ libbz2-dev \ libreadline-dev \ libsqlite3-dev \ autoconf \ git \ curl \ sudo \ llvm \ libncursesw5-dev \ xz-utils \ tk-dev \ libxml2-dev \ libxmlsec1-dev \ libffi-dev \ libopenjp2-7 \ libtiff5 \ libturbojpeg0-dev \ liblzma-dev &>/dev/null git clone https://github.com/pyenv/pyenv.git ~/.pyenv &>/dev/null set +e echo 'export PYENV_ROOT="$HOME/.pyenv"' >>~/.bashrc echo 'export PATH="$PYENV_ROOT/bin:$PATH"' >>~/.bashrc echo -e 'if command -v pyenv 1>/dev/null 2>&1; then\n eval "$(pyenv init --path)"\nfi' >>~/.bashrc msg_ok "Installed pyenv" . ~/.bashrc set -e msg_info "Installing Python 3.11.1" pyenv install 3.11.1 &>/dev/null pyenv global 3.11.1 msg_ok "Installed Python 3.11.1" read -r -p "Would you like to install Home Assistant Beta? " prompt if [[ "${prompt,,}" =~ ^(y|yes)$ ]]; then msg_info "Installing Home Assistant Beta" cat </etc/systemd/system/homeassistant.service [Unit] Description=Home Assistant After=network-online.target [Service] Type=simple WorkingDirectory=/root/.homeassistant ExecStart=/srv/homeassistant/bin/hass -c "/root/.homeassistant" RestartForceExitStatus=100 [Install] WantedBy=multi-user.target EOF mkdir /srv/homeassistant cd /srv/homeassistant python3 -m venv . source bin/activate python3 -m pip install wheel &>/dev/null pip3 install --upgrade pip &>/dev/null pip3 install psycopg2-binary &>/dev/null pip3 install --pre homeassistant &>/dev/null systemctl enable homeassistant &>/dev/null msg_ok "Installed Home Assistant Beta" echo -e " Go to $(hostname -I | awk '{print $1}'):8123" hass fi read -r -p "Would you like to install ESPHome Beta? " prompt if [[ "${prompt,,}" =~ ^(y|yes)$ ]]; then msg_info "Installing ESPHome Beta" mkdir /srv/esphome cd /srv/esphome python3 -m venv . source bin/activate python3 -m pip install wheel &>/dev/null pip3 install --upgrade pip &>/dev/null pip3 install --pre esphome &>/dev/null cat </srv/esphome/start.sh #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT # https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE source /srv/esphome/bin/activate esphome dashboard /srv/esphome/ EOF chmod +x start.sh cat </etc/systemd/system/esphomedashboard.service [Unit] Description=ESPHome Dashboard Service After=network.target [Service] Type=simple User=root WorkingDirectory=/srv/esphome ExecStart=/srv/esphome/start.sh RestartSec=30 Restart=on-failure [Install] WantedBy=multi-user.target EOF systemctl enable --now esphomedashboard &>/dev/null msg_ok "Installed ESPHome Beta" echo -e " Go to $(hostname -I | awk '{print $1}'):6052" exec $SHELL fi read -r -p "Would you like to install Matter-Server (Beta)? " prompt if [[ "${prompt,,}" =~ ^(y|yes)$ ]]; then msg_info "Installing Matter Server" apt-get install -y \ libcairo2-dev \ libjpeg62-turbo-dev \ libgirepository1.0-dev \ libpango1.0-dev \ libgif-dev \ g++ &>/dev/null python3 -m pip install wheel pip3 install --upgrade pip pip install python-matter-server[server] msg_ok "Installed Matter Server" echo -e "Start server > python -m matter_server.server" fi msg_ok "\nFinished\n" exec $SHELL ================================================ FILE: tools/addon/qbittorrent-exporter.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: CrazyWolf13 # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://github.com/martabal/qbittorrent-exporter if ! command -v curl &>/dev/null; then printf "\r\e[2K%b" '\033[93m Setup Source \033[m' >&2 apt-get update >/dev/null 2>&1 apt-get install -y curl >/dev/null 2>&1 fi source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/core.func) source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/tools.func) source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/error_handler.func) source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func) 2>/dev/null || true declare -f init_tool_telemetry &>/dev/null && init_tool_telemetry "qbittorrent-exporter" "addon" # Enable error handling set -Eeuo pipefail trap 'error_handler' ERR load_functions # ============================================================================== # CONFIGURATION # ============================================================================== VERBOSE=${var_verbose:-no} APP="qbittorrent-exporter" APP_TYPE="tools" INSTALL_PATH="/opt/qbittorrent-exporter" CONFIG_PATH="/opt/qbittorrent-exporter.env" # ============================================================================== # OS DETECTION # ============================================================================== if [[ -f "/etc/alpine-release" ]]; then OS="Alpine" SERVICE_PATH="/etc/init.d/qbittorrent-exporter" elif grep -qE 'ID=debian|ID=ubuntu' /etc/os-release; then OS="Debian" SERVICE_PATH="/etc/systemd/system/qbittorrent-exporter.service" else echo -e "${CROSS} Unsupported OS detected. Exiting." exit 238 fi # ============================================================================== # UNINSTALL # ============================================================================== function uninstall() { msg_info "Uninstalling qBittorrent-Exporter" if [[ "$OS" == "Alpine" ]]; then rc-service qbittorrent-exporter stop &>/dev/null rc-update del qbittorrent-exporter &>/dev/null rm -f "$SERVICE_PATH" else systemctl disable -q --now qbittorrent-exporter rm -f "$SERVICE_PATH" fi rm -rf "$INSTALL_PATH" "$CONFIG_PATH" rm -f "/usr/local/bin/update_qbittorrent-exporter" rm -f "$HOME/.qbittorrent-exporter" msg_ok "qBittorrent-Exporter has been uninstalled" } # ============================================================================== # UPDATE # ============================================================================== function update() { if check_for_gh_release "qbittorrent-exporter" "martabal/qbittorrent-exporter"; then if [[ "$(printf '%s\n' "2.0.0" "$CHECK_UPDATE_RELEASE" | sort -V | tail -n1)" == "$CHECK_UPDATE_RELEASE" ]] && \ ! grep -q "QBITTORRENT_API_KEY" "$CONFIG_PATH" 2>/dev/null; then echo "" msg_warn "Version 2.0.0 introduces a breaking change: username/password login has been replaced by an API key." echo -e "${TAB3}${INFO} You must create an API key in qBittorrent under Tools > Options > Web UI > API key" echo "" echo -n "${TAB3}Enter your qBittorrent API key (or press Enter to abort): " read -r QBITTORRENT_API_KEY if [[ -z "$QBITTORRENT_API_KEY" ]]; then msg_warn "No API key provided. Update aborted." exit 0 fi sed -i '/^QBITTORRENT_USERNAME=/d' "$CONFIG_PATH" sed -i '/^QBITTORRENT_PASSWORD=/d' "$CONFIG_PATH" echo "QBITTORRENT_API_KEY=\"${QBITTORRENT_API_KEY}\"" >>"$CONFIG_PATH" msg_ok "API key saved to configuration" fi msg_info "Stopping service" if [[ "$OS" == "Alpine" ]]; then rc-service qbittorrent-exporter stop &>/dev/null else systemctl stop qbittorrent-exporter fi msg_ok "Stopped service" fetch_and_deploy_gh_release "qbittorrent-exporter" "martabal/qbittorrent-exporter" "tarball" "latest" setup_go msg_info "Building qBittorrent-Exporter" cd /opt/qbittorrent-exporter $STD /usr/local/bin/go build -o ./qbittorrent-exporter msg_ok "Built qBittorrent-Exporter" msg_info "Starting service" if [[ "$OS" == "Alpine" ]]; then rc-service qbittorrent-exporter start &>/dev/null else systemctl start qbittorrent-exporter fi msg_ok "Started service" msg_ok "Updated successfully!" exit fi } # ============================================================================== # INSTALL # ============================================================================== function install() { read -erp "${TAB3}Enter URL of qBittorrent, example: (http://127.0.0.1:8080): " QBITTORRENT_BASE_URL echo -e "${TAB3}${INFO} Create an API key in qBittorrent under Tools > Options > Web UI > API key" read -erp "${TAB3}Enter qBittorrent API key: " QBITTORRENT_API_KEY fetch_and_deploy_gh_release "qbittorrent-exporter" "martabal/qbittorrent-exporter" "tarball" "latest" setup_go msg_info "Building qBittorrent-Exporter on ${OS}" cd /opt/qbittorrent-exporter $STD /usr/local/bin/go build -o ./qbittorrent-exporter msg_ok "Built qBittorrent-Exporter" msg_info "Creating configuration" cat <"$CONFIG_PATH" # https://github.com/martabal/qbittorrent-exporter?tab=readme-ov-file#parameters QBITTORRENT_BASE_URL="${QBITTORRENT_BASE_URL}" QBITTORRENT_API_KEY="${QBITTORRENT_API_KEY}" EOF msg_ok "Created configuration" msg_info "Creating service" if [[ "$OS" == "Debian" ]]; then cat <"$SERVICE_PATH" [Unit] Description=qbittorrent-exporter After=network.target [Service] User=root WorkingDirectory=/opt/qbittorrent-exporter EnvironmentFile=$CONFIG_PATH ExecStart=/opt/qbittorrent-exporter/qbittorrent-exporter Restart=always [Install] WantedBy=multi-user.target EOF systemctl daemon-reload systemctl enable -q --now qbittorrent-exporter else cat <"$SERVICE_PATH" #!/sbin/openrc-run name="qbittorrent-exporter" description="qBittorrent Exporter for Prometheus" command="${INSTALL_PATH}/qbittorrent-exporter" command_background=true directory="/opt/qbittorrent-exporter" pidfile="/run/\${RC_SVCNAME}.pid" output_log="/var/log/qbittorrent-exporter.log" error_log="/var/log/qbittorrent-exporter.log" depend() { need net after firewall } start_pre() { if [ -f "$CONFIG_PATH" ]; then export \$(grep -v '^#' $CONFIG_PATH | xargs) fi } EOF chmod +x "$SERVICE_PATH" $STD rc-update add qbittorrent-exporter default $STD rc-service qbittorrent-exporter start fi msg_ok "Created and started service" # Create update script msg_info "Creating update script" ensure_usr_local_bin_persist cat <<'UPDATEEOF' >/usr/local/bin/update_qbittorrent-exporter #!/usr/bin/env bash # qbittorrent-exporter Update Script type=update bash -c "$(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/tools/addon/qbittorrent-exporter.sh)" UPDATEEOF chmod +x /usr/local/bin/update_qbittorrent-exporter msg_ok "Created update script (/usr/local/bin/update_qbittorrent-exporter)" echo "" msg_ok "qBittorrent-Exporter installed successfully" msg_ok "Metrics: ${BL}http://${LOCAL_IP}:8090/metrics${CL}" msg_ok "Config: ${BL}${CONFIG_PATH}${CL}" } # ============================================================================== # MAIN # ============================================================================== header_info ensure_usr_local_bin_persist get_lxc_ip # Handle type=update (called from update script) if [[ "${type:-}" == "update" ]]; then if [[ -d "$INSTALL_PATH" && -f "$INSTALL_PATH/qbittorrent-exporter" ]]; then update else msg_error "qBittorrent-Exporter is not installed. Nothing to update." exit 233 fi exit 0 fi # Check if already installed if [[ -d "$INSTALL_PATH" && -f "$INSTALL_PATH/qbittorrent-exporter" ]]; then msg_warn "qBittorrent-Exporter is already installed." echo "" echo -n "${TAB}Uninstall qBittorrent-Exporter? (y/N): " read -r uninstall_prompt if [[ "${uninstall_prompt,,}" =~ ^(y|yes)$ ]]; then uninstall exit 0 fi echo -n "${TAB}Update qBittorrent-Exporter? (y/N): " read -r update_prompt if [[ "${update_prompt,,}" =~ ^(y|yes)$ ]]; then update exit 0 fi msg_warn "No action selected. Exiting." exit 0 fi # Fresh installation msg_warn "qBittorrent-Exporter is not installed." echo "" echo -e "${TAB}${INFO} This will install:" echo -e "${TAB} - qBittorrent Exporter (Go binary)" echo -e "${TAB} - Systemd/OpenRC service" echo "" echo -n "${TAB}Install qBittorrent-Exporter? (y/N): " read -r install_prompt if [[ "${install_prompt,,}" =~ ^(y|yes)$ ]]; then install else msg_warn "Installation cancelled. Exiting." exit 0 fi ================================================ FILE: tools/addon/runtipi.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://runtipi.io/ | Github: https://github.com/runtipi/runtipi if ! command -v curl &>/dev/null; then printf "\r\e[2K%b" '\033[93m Setup Source \033[m' >&2 if [[ -f /etc/alpine-release ]]; then apk update >/dev/null 2>&1 apk add --no-cache curl >/dev/null 2>&1 else apt-get update >/dev/null 2>&1 apt-get install -y curl >/dev/null 2>&1 fi fi source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/core.func) source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/tools.func) source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/error_handler.func) source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func) 2>/dev/null || true declare -f init_tool_telemetry &>/dev/null && init_tool_telemetry "runtipi" "addon" # Enable error handling set -Eeuo pipefail trap 'error_handler' ERR # ============================================================================== # CONFIGURATION # ============================================================================== APP="Runtipi" APP_TYPE="addon" INSTALL_PATH="/opt/runtipi" DEFAULT_PORT=80 # Initialize all core functions (colors, formatting, icons, STD mode) load_functions # ============================================================================== # PROXMOX HOST CHECK # ============================================================================== function check_proxmox_host() { if command -v pveversion &>/dev/null; then msg_error "Running on the Proxmox host is NOT recommended!" msg_error "This should be executed inside an LXC container." echo "" echo -n "${TAB}Continue anyway? (y/N): " read -r confirm if [[ ! "${confirm,,}" =~ ^(y|yes)$ ]]; then msg_warn "Aborted. Please run this inside an LXC container." exit 0 fi msg_warn "Proceeding on Proxmox host at your own risk!" fi } # ============================================================================== # CHECK / INSTALL DOCKER # ============================================================================== function check_or_install_docker() { if command -v docker &>/dev/null; then msg_ok "Docker $(docker --version | cut -d' ' -f3 | tr -d ',') is available" if docker compose version &>/dev/null; then msg_ok "Docker Compose is available" else msg_error "Docker Compose plugin is not available. Please install it." exit 10 fi return fi msg_warn "Docker is not installed." echo -n "${TAB}Install Docker now? (y/N): " read -r install_docker_prompt if [[ ! "${install_docker_prompt,,}" =~ ^(y|yes)$ ]]; then msg_error "Docker is required for ${APP}. Exiting." exit 10 fi msg_info "Installing Docker" DOCKER_CONFIG_PATH='/etc/docker/daemon.json' mkdir -p "$(dirname "$DOCKER_CONFIG_PATH")" echo -e '{\n "log-driver": "journald"\n}' >"$DOCKER_CONFIG_PATH" $STD sh <(curl -fsSL https://get.docker.com) msg_ok "Installed Docker" } # ============================================================================== # UNINSTALL # ============================================================================== function uninstall() { msg_info "Uninstalling ${APP}" if [[ -f "${INSTALL_PATH}/runtipi-cli" ]]; then msg_info "Stopping ${APP}" cd "$INSTALL_PATH" $STD ./runtipi-cli stop 2>/dev/null || true msg_ok "Stopped ${APP}" fi if command -v docker &>/dev/null; then msg_info "Removing Docker containers" cd "$INSTALL_PATH" 2>/dev/null && $STD docker compose down --remove-orphans 2>/dev/null || true msg_ok "Removed Docker containers" fi rm -rf "$INSTALL_PATH" msg_ok "${APP} has been uninstalled" } # ============================================================================== # UPDATE # ============================================================================== function update() { msg_info "Updating ${APP}" cd "$INSTALL_PATH" $STD ./runtipi-cli update latest msg_ok "Updated ${APP}" msg_ok "Updated successfully" exit } # ============================================================================== # INSTALL # ============================================================================== function install() { check_or_install_docker msg_info "Installing dependencies" $STD apt-get update $STD apt-get install -y openssl msg_ok "Installed dependencies" msg_warn "WARNING: This will run an external installer from https://runtipi.io/" msg_warn "The following code is NOT maintained or audited by our repository." msg_warn "Review: https://raw.githubusercontent.com/runtipi/runtipi/master/scripts/install.sh" echo "" echo -n "${TAB}Do you want to continue? (y/N): " read -r confirm if [[ ! "${confirm,,}" =~ ^(y|yes)$ ]]; then msg_warn "Installation cancelled. Exiting." exit 0 fi msg_info "Installing ${APP} (this pulls Docker containers)" DOCKER_CONFIG_PATH='/etc/docker/daemon.json' mkdir -p "$(dirname "$DOCKER_CONFIG_PATH")" [[ ! -f "$DOCKER_CONFIG_PATH" ]] && echo -e '{\n "log-driver": "journald"\n}' >"$DOCKER_CONFIG_PATH" cd /opt curl -fsSL "https://raw.githubusercontent.com/runtipi/runtipi/master/scripts/install.sh" -o "install.sh" chmod +x install.sh $STD ./install.sh chmod 660 /opt/runtipi/state/settings.json 2>/dev/null || true rm -f /opt/install.sh msg_ok "Installed ${APP}" echo "" msg_ok "${APP} is reachable at: ${BL}http://${LOCAL_IP}:${DEFAULT_PORT}${CL}" } # ============================================================================== # MAIN # ============================================================================== # Handle type=update (called from update script) if [[ "${type:-}" == "update" ]]; then header_info if [[ -d "$INSTALL_PATH" ]]; then update else msg_error "${APP} is not installed. Nothing to update." exit 233 fi exit 0 fi if [[ -f /etc/alpine-release ]]; then msg_error "${APP} does not support Alpine Linux. Please use a Debian or Ubuntu based LXC." exit 238 fi header_info check_proxmox_host get_lxc_ip # Check if already installed if [[ -d "$INSTALL_PATH" ]]; then msg_warn "${APP} is already installed." echo "" echo -n "${TAB}Uninstall ${APP}? (y/N): " read -r uninstall_prompt if [[ "${uninstall_prompt,,}" =~ ^(y|yes)$ ]]; then uninstall exit 0 fi echo -n "${TAB}Update ${APP}? (y/N): " read -r update_prompt if [[ "${update_prompt,,}" =~ ^(y|yes)$ ]]; then update exit 0 fi msg_warn "No action selected. Exiting." exit 0 fi # Fresh installation msg_warn "${APP} is not installed." echo "" echo -e "${TAB}${INFO} This will install:" echo -e "${TAB} - Runtipi (via external installer)" echo -e "${TAB} - Docker (if not already installed)" echo "" echo -n "${TAB}Install ${APP}? (y/N): " read -r install_prompt if [[ "${install_prompt,,}" =~ ^(y|yes)$ ]]; then install else msg_warn "Installation cancelled. Exiting." exit 0 fi ================================================ FILE: tools/addon/sparkyfitness-garmin.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: Tom Frenzel (tomfrenzel) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/CodeWithCJ/SparkyFitness if ! command -v curl &>/dev/null; then printf "\r\e[2K%b" '\033[93m Setup Source \033[m' >&2 apt-get update >/dev/null 2>&1 apt-get install -y curl >/dev/null 2>&1 fi source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/core.func) source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/tools.func) source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/error_handler.func) source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func) 2>/dev/null || true declare -f init_tool_telemetry &>/dev/null && init_tool_telemetry "sparkyfitness-garmin" "addon" # Enable error handling set -Eeuo pipefail trap 'error_handler' ERR load_functions # ============================================================================== # CONFIGURATION # ============================================================================== APP="SparkyFitness-Garmin" APP_TYPE="addon" INSTALL_PATH="/opt/sparkyfitness-garmin" CONFIG_PATH="/etc/sparkyfitness-garmin/.env" SERVICE_PATH="/etc/systemd/system/sparkyfitness-garmin.service" DEFAULT_PORT=8000 # ============================================================================== # OS DETECTION # ============================================================================== if ! grep -qE 'ID=debian|ID=ubuntu' /etc/os-release 2>/dev/null; then echo -e "${CROSS} Unsupported OS detected. This script only supports Debian and Ubuntu." exit 238 fi # ============================================================================== # SparkyFitness LXC DETECTION # ============================================================================== if [[ ! -d /opt/sparkyfitness ]]; then echo -e "${CROSS} No SparkyFitness installation detected. This addon must be installed within a container that already has SparkyFitness installed." exit 238 fi # ============================================================================== # UNINSTALL # ============================================================================== function uninstall() { msg_info "Uninstalling ${APP}" systemctl disable --now sparkyfitness-garmin.service &>/dev/null || true rm -rf "$SERVICE_PATH" "$CONFIG_PATH" "$INSTALL_PATH" ~/.sparkyfitness-garmin msg_ok "${APP} has been uninstalled" } # ============================================================================== # UPDATE # ============================================================================== function update() { if check_for_gh_release "sparkyfitness-garmin" "CodeWithCJ/SparkyFitness"; then PYTHON_VERSION="3.13" setup_uv msg_info "Stopping service" systemctl stop sparkyfitness-garmin.service &>/dev/null || true msg_ok "Stopped service" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "sparkyfitness-garmin" "CodeWithCJ/SparkyFitness" "tarball" "latest" $INSTALL_PATH cd $INSTALL_PATH/SparkyFitnessGarmin $STD uv venv --clear .venv $STD uv pip install -r requirements.txt msg_info "Starting service" systemctl start sparkyfitness-garmin msg_ok "Started service" msg_ok "Updated successfully" exit fi } # ============================================================================== # INSTALL # ============================================================================== function install() { PYTHON_VERSION="3.13" setup_uv fetch_and_deploy_gh_release "sparkyfitness-garmin" "CodeWithCJ/SparkyFitness" "tarball" "latest" $INSTALL_PATH msg_info "Setting up ${APP}" mkdir -p "/etc/sparkyfitness-garmin" cp "/opt/sparkyfitness-garmin/docker/.env.example" $CONFIG_PATH cd $INSTALL_PATH/SparkyFitnessGarmin $STD uv venv --clear .venv $STD uv pip install -r requirements.txt sed -i -e "s|^#\?GARMIN_MICROSERVICE_URL=.*|GARMIN_MICROSERVICE_URL=http://${LOCAL_IP}:${DEFAULT_PORT}|" $CONFIG_PATH cat </etc/systemd/system/sparkyfitness-garmin.service [Unit] Description=${APP} After=network.target sparkyfitness-server.service Requires=sparkyfitness-server.service [Service] Type=simple WorkingDirectory=$INSTALL_PATH/SparkyFitnessGarmin EnvironmentFile=$CONFIG_PATH ExecStart=$INSTALL_PATH/SparkyFitnessGarmin/.venv/bin/python3 -m uvicorn main:app --host 0.0.0.0 --port ${DEFAULT_PORT} Restart=always RestartSec=5 [Install] WantedBy=multi-user.target EOF systemctl enable -q --now sparkyfitness-garmin msg_ok "Set up ${APP} - reachable at http://${LOCAL_IP}:${DEFAULT_PORT}" msg_ok "You might need to update the GARMIN_MICROSERVICE_URL in your SparkyFitness .env file to http://${LOCAL_IP}:${DEFAULT_PORT}" } # ============================================================================== # MAIN # ============================================================================== header_info ensure_usr_local_bin_persist get_lxc_ip # Handle type=update (called from update script) if [[ "${type:-}" == "update" ]]; then if [[ -d "$INSTALL_PATH" ]]; then update else msg_error "${APP} is not installed. Nothing to update." exit 233 fi exit 0 fi # Check if already installed if [[ -d "$INSTALL_PATH" && -n "$(ls -A "$INSTALL_PATH" 2>/dev/null)" ]]; then msg_warn "${APP} is already installed." echo "" echo -n "${TAB}Uninstall ${APP}? (y/N): " read -r uninstall_prompt if [[ "${uninstall_prompt,,}" =~ ^(y|yes)$ ]]; then uninstall exit 0 fi echo -n "${TAB}Update ${APP}? (y/N): " read -r update_prompt if [[ "${update_prompt,,}" =~ ^(y|yes)$ ]]; then update exit 0 fi msg_warn "No action selected. Exiting." exit 0 fi # Fresh installation msg_warn "${APP} is not installed." echo "" echo -e "${TAB}${INFO} This will install:" echo -e "${TAB} - UV (Python Version Manager)" echo -e "${TAB} - SparkyFitness Garmin Microservice" echo "" echo -n "${TAB}Install ${APP}? (y/N): " read -r install_prompt if [[ "${install_prompt,,}" =~ ^(y|yes)$ ]]; then install else msg_warn "Installation cancelled. Exiting." exit 0 fi ================================================ FILE: tools/addon/webmin.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.webmin.com/ | Github: https://github.com/webmin/webmin function header_info { clear cat <<"EOF" _ __ __ _ | | /| / /__ / / __ _ (_)__ | |/ |/ / -_) _ \/ ' \/ / _ \ |__/|__/\__/_.__/_/_/_/_/_//_/ EOF } set -eEuo pipefail YW=$(echo "\033[33m") BL=$(echo "\033[36m") BGN=$(echo "\033[4;92m") GN=$(echo "\033[1;92m") DGN=$(echo "\033[32m") CL=$(echo "\033[m") CM="${GN}✓${CL}" BFR="\\r\\033[K" HOLD="-" msg_info() { local msg="$1" echo -ne " ${HOLD} ${YW}${msg}..." } msg_ok() { local msg="$1" echo -e "${BFR} ${CM} ${GN}${msg}${CL}" } # Telemetry source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func) 2>/dev/null || true declare -f init_tool_telemetry &>/dev/null && init_tool_telemetry "webmin" "addon" header_info whiptail --backtitle "Proxmox VE Helper Scripts" --title "Webmin Installer" --yesno "This Will Install Webmin on this LXC Container. Proceed?" 10 58 msg_info "Installing Prerequisites" apt update &>/dev/null apt-get -y install libnet-ssleay-perl libauthen-pam-perl libio-pty-perl unzip shared-mime-info curl &>/dev/null msg_ok "Installed Prerequisites" LATEST=$(curl -fsSL https://api.github.com/repos/webmin/webmin/releases/latest | grep '"tag_name":' | cut -d'"' -f4) msg_info "Downloading Webmin" curl -fsSL "https://github.com/webmin/webmin/releases/download/$LATEST/webmin_${LATEST}_all.deb" -o $(basename "https://github.com/webmin/webmin/releases/download/$LATEST/webmin_${LATEST}_all.deb") msg_ok "Downloaded Webmin" msg_info "Installing Webmin" dpkg -i webmin_${LATEST}_all.deb &>/dev/null /usr/share/webmin/changepass.pl /etc/webmin root root &>/dev/null rm -rf /root/webmin_${LATEST}_all.deb msg_ok "Installed Webmin" IP=$(hostname -I | cut -f1 -d ' ') echo -e "Successfully Installed!! Webmin should be reachable by going to ${BL}https://${IP}:10000${CL}" ================================================ FILE: tools/copy-data/README.md ================================================

Copy data to another LXC (run in the Proxmox Shell)

To copy data from Home Assistant Container to Home Assistant Container
``` bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/tools/copy-data/home-assistant-container-copy-data-home-assistant-container.sh)" ```
To copy data from Home Assistant Container to Home Assistant Core
``` bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/tools/copy-data/home-assistant-container-copy-data-home-assistant-core.sh)" ```
To copy data from Home Assistant Container to Podman Home Assistant
``` bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/tools/copy-data/home-assistant-container-copy-data-podman-home-assistant.sh)" ```
To copy data from Podman Home Assistant to Home Assistant Container
``` bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/tools/copy-data/podman-home-assistant-copy-data-home-assistant-container.sh)" ```
To copy data from Home Assistant Core to Home Assistant Container
``` bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/tools/copy-data/home-assistant-core-copy-data-home-assistant-container.sh)" ```
To copy data from Home Assistant Core to Home Assistant Core
``` bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/tools/copy-data/home-assistant-core-copy-data-home-assistant-core.sh)" ```
To copy data from Plex to Plex
``` bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/tools/copy-data/plex-copy-data-plex.sh)" ```
To copy data from Zigbee2MQTT to Zigbee2MQTT
``` bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/tools/copy-data/z2m-copy-data-z2m.sh)" ```
To copy data from Zwavejs2MQTT to Zwave JS UI
``` bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/tools/copy-data/zwavejs2mqtt-copy-data-zwavejsui.sh)" ``` ================================================ FILE: tools/copy-data/home-assistant-container-copy-data-home-assistant-container.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT # https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Use to copy all data from one Home Assistant LXC to another # run from the Proxmox Shell clear if ! command -v pveversion >/dev/null 2>&1; then echo -e "⚠️ Run from the Proxmox Shell" exit fi while true; do read -p "Use to copy all data from one Home Assistant LXC to another. Proceed(y/n)?" yn case $yn in [Yy]*) break ;; [Nn]*) exit ;; *) echo "Please answer yes or no." ;; esac done set -o errexit set -o errtrace set -o nounset set -o pipefail shopt -s expand_aliases alias die='EXIT=$? LINE=$LINENO error_exit' trap die ERR trap cleanup EXIT function error_exit() { trap - ERR local DEFAULT='Unknown failure occured.' local REASON="\e[97m${1:-$DEFAULT}\e[39m" local FLAG="\e[91m[ERROR] \e[93m$EXIT@$LINE" msg "$FLAG $REASON" exit "$EXIT" } function warn() { local REASON="\e[97m$1\e[39m" local FLAG="\e[93m[WARNING]\e[39m" msg "$FLAG $REASON" } function info() { local REASON="$1" local FLAG="\e[36m[INFO]\e[39m" msg "$FLAG $REASON" } function msg() { local TEXT="$1" echo -e "$TEXT" } function cleanup() { [ -d "${CTID_FROM_PATH:-}" ] && pct unmount "$CTID_FROM" [ -d "${CTID_TO_PATH:-}" ] && pct unmount "$CTID_TO" popd >/dev/null rm -rf "$TEMP_DIR" } TEMP_DIR=$(mktemp -d) pushd "$TEMP_DIR" >/dev/null TITLE="Home Assistant LXC Data Copy" while read -r line; do TAG=$(echo "$line" | awk '{print $1}') ITEM=$(echo "$line" | awk '{print substr($0,36)}') OFFSET=2 if [[ $((${#ITEM} + $OFFSET)) -gt ${MSG_MAX_LENGTH:-} ]]; then MSG_MAX_LENGTH=$((${#ITEM} + $OFFSET)) fi CTID_MENU+=("$TAG" "$ITEM " "OFF") done < <(pct list | awk 'NR>1') while [ -z "${CTID_FROM:+x}" ]; do CTID_FROM=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "$TITLE" --radiolist \ "\nWhich HA LXC would you like to copy FROM?\n" \ 16 $(($MSG_MAX_LENGTH + 23)) 6 \ "${CTID_MENU[@]}" 3>&1 1>&2 2>&3) done while [ -z "${CTID_TO:+x}" ]; do CTID_TO=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "$TITLE" --radiolist \ "\nWhich HA LXC would you like to copy TO?\n" \ 16 $(($MSG_MAX_LENGTH + 23)) 6 \ "${CTID_MENU[@]}" 3>&1 1>&2 2>&3) done for i in ${!CTID_MENU[@]}; do [ "${CTID_MENU[$i]}" == "$CTID_FROM" ] && CTID_FROM_HOSTNAME=$(sed 's/[[:space:]]*$//' <<<"${CTID_MENU[$i + 1]}") [ "${CTID_MENU[$i]}" == "$CTID_TO" ] && CTID_TO_HOSTNAME=$(sed 's/[[:space:]]*$//' <<<"${CTID_MENU[$i + 1]}") done whiptail --backtitle "Proxmox VE Helper Scripts" --defaultno --title "$TITLE" --yesno \ "Are you sure you want to copy data between the following LXCs? $CTID_FROM (${CTID_FROM_HOSTNAME}) -> $CTID_TO (${CTID_TO_HOSTNAME}) Version: 2022.01.23" 13 50 info "Home Assistant Data from '$CTID_FROM' to '$CTID_TO'" if [ $(pct status "$CTID_TO" | sed 's/.* //') == 'running' ]; then msg "Stopping '$CTID_TO'..." pct stop "$CTID_TO" fi msg "Mounting Container Disks..." DOCKER_PATH=/var/lib/docker/volumes/hass_config/ CTID_FROM_PATH=$(pct mount "$CTID_FROM" | sed -n "s/.*'\(.*\)'/\1/p") || die "There was a problem mounting the root disk of LXC '${CTID_FROM}'." [ -d "${CTID_FROM_PATH}${DOCKER_PATH}" ] || die "Home Assistant directories in '$CTID_FROM' not found." CTID_TO_PATH=$(pct mount "$CTID_TO" | sed -n "s/.*'\(.*\)'/\1/p") || die "There was a problem mounting the root disk of LXC '${CTID_TO}'." [ -d "${CTID_TO_PATH}${DOCKER_PATH}" ] || die "Home Assistant directories in '$CTID_TO' not found." rm -rf "${CTID_TO_PATH}"${DOCKER_PATH} mkdir "${CTID_TO_PATH}"${DOCKER_PATH} msg "Copying Data Between Containers..." RSYNC_OPTIONS=( --archive --hard-links --sparse --xattrs --no-inc-recursive --info=progress2 ) msg "<======== Docker Data ========>" rsync "${RSYNC_OPTIONS[*]}" "${CTID_FROM_PATH}"${DOCKER_PATH} "${CTID_TO_PATH}"${DOCKER_PATH} echo -en "\e[1A\e[0K\e[1A\e[0K" info "Successfully Transferred Data." # Use to copy all data from one Home Assistant LXC to another # run from the Proxmox Shell # bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/mainmain/tools/copy-data//home-assistant-container-copy-data-home-assistant-container.sh)" ================================================ FILE: tools/copy-data/home-assistant-container-copy-data-home-assistant-core.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT # https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE clear if ! command -v pveversion >/dev/null 2>&1; then echo -e "⚠️ Run from the Proxmox Shell" exit fi while true; do read -p "Use to copy all data from a Home Assistant Container LXC to a Home Assistant Core LXC. Proceed(y/n)?" yn case $yn in [Yy]*) break ;; [Nn]*) exit ;; *) echo "Please answer yes or no." ;; esac done set -o errexit set -o errtrace set -o nounset set -o pipefail shopt -s expand_aliases alias die='EXIT=$? LINE=$LINENO error_exit' trap die ERR trap cleanup EXIT function error_exit() { trap - ERR local DEFAULT='Unknown failure occured.' local REASON="\e[97m${1:-$DEFAULT}\e[39m" local FLAG="\e[91m[ERROR] \e[93m$EXIT@$LINE" msg "$FLAG $REASON" exit $EXIT } function warn() { local REASON="\e[97m$1\e[39m" local FLAG="\e[93m[WARNING]\e[39m" msg "$FLAG $REASON" } function info() { local REASON="$1" local FLAG="\e[36m[INFO]\e[39m" msg "$FLAG $REASON" } function msg() { local TEXT="$1" echo -e "$TEXT" } function cleanup() { [ -d "${CTID_FROM_PATH:-}" ] && pct unmount $CTID_FROM [ -d "${CTID_TO_PATH:-}" ] && pct unmount $CTID_TO popd >/dev/null rm -rf $TEMP_DIR } TEMP_DIR=$(mktemp -d) pushd $TEMP_DIR >/dev/null TITLE="Home Assistant LXC Data Copy" while read -r line; do TAG=$(echo "$line" | awk '{print $1}') ITEM=$(echo "$line" | awk '{print substr($0,36)}') OFFSET=2 if [[ $((${#ITEM} + $OFFSET)) -gt ${MSG_MAX_LENGTH:-} ]]; then MSG_MAX_LENGTH=$((${#ITEM} + $OFFSET)) fi CTID_MENU+=("$TAG" "$ITEM " "OFF") done < <(pct list | awk 'NR>1') while [ -z "${CTID_FROM:+x}" ]; do CTID_FROM=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "$TITLE" --radiolist \ "\nWhich HA Container LXC would you like to copy FROM?\n" \ 16 $(($MSG_MAX_LENGTH + 23)) 6 \ "${CTID_MENU[@]}" 3>&1 1>&2 2>&3) done while [ -z "${CTID_TO:+x}" ]; do CTID_TO=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "$TITLE" --radiolist \ "\nWhich HA Core LXC would you like to copy TO?\n" \ 16 $(($MSG_MAX_LENGTH + 23)) 6 \ "${CTID_MENU[@]}" 3>&1 1>&2 2>&3) done for i in ${!CTID_MENU[@]}; do [ "${CTID_MENU[$i]}" == "$CTID_FROM" ] && CTID_FROM_HOSTNAME=$(sed 's/[[:space:]]*$//' <<<${CTID_MENU[$i + 1]}) [ "${CTID_MENU[$i]}" == "$CTID_TO" ] && CTID_TO_HOSTNAME=$(sed 's/[[:space:]]*$//' <<<${CTID_MENU[$i + 1]}) done whiptail --backtitle "Proxmox VE Helper Scripts" --defaultno --title "$TITLE" --yesno \ "Are you sure you want to copy data between the following LXCs? $CTID_FROM (${CTID_FROM_HOSTNAME}) -> $CTID_TO (${CTID_TO_HOSTNAME}) Version: 2022.10.02" 13 50 info "Home Assistant Data from '$CTID_FROM' to '$CTID_TO'" if [ $(pct status $CTID_TO | sed 's/.* //') == 'running' ]; then msg "Stopping '$CTID_TO'..." pct stop $CTID_TO fi msg "Mounting Container Disks..." DOCKER_PATH=/var/lib/docker/volumes/hass_config/_data CORE_PATH=/root/.homeassistant CTID_FROM_PATH=$(pct mount $CTID_FROM | sed -n "s/.*'\(.*\)'/\1/p") || die "There was a problem mounting the root disk of LXC '${CTID_FROM}'." [ -d "${CTID_FROM_PATH}${DOCKER_PATH}" ] || die "Home Assistant directories in '$CTID_FROM' not found." CTID_TO_PATH=$(pct mount $CTID_TO | sed -n "s/.*'\(.*\)'/\1/p") || die "There was a problem mounting the root disk of LXC '${CTID_TO}'." [ -d "${CTID_TO_PATH}${CORE_PATH}" ] || die "Home Assistant directories in '$CTID_TO' not found." msg "Copying Data..." RSYNC_OPTIONS=( --archive --hard-links --sparse --xattrs --no-inc-recursive --info=progress2 ) msg "<======== Docker Data ========>" rsync ${RSYNC_OPTIONS[*]} ${CTID_FROM_PATH}${DOCKER_PATH} ${CTID_TO_PATH}${CORE_PATH} echo -en "\e[1A\e[0K\e[1A\e[0K" info "Successfully Transferred Data." # Use to copy all data from a Home Assistant Container LXC to a Home Assistant Core LXC # run from the Proxmox Shell # bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/mainmain/tools/copy-data//home-assistant-container-copy-data-home-assistant-core.sh)" ================================================ FILE: tools/copy-data/home-assistant-container-copy-data-podman-home-assistant.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT # https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Use to copy all data from a Home Assistant LXC to a Podman Home Assistant LXC. # run from the Proxmox Shell clear if ! command -v pveversion >/dev/null 2>&1; then echo -e "⚠️ Run from the Proxmox Shell" exit fi while true; do read -p "Use to copy all data from a Home Assistant LXC to a Podman Home Assistant LXC. Proceed(y/n)?" yn case $yn in [Yy]*) break ;; [Nn]*) exit ;; *) echo "Please answer yes or no." ;; esac done set -o errexit set -o errtrace set -o nounset set -o pipefail shopt -s expand_aliases alias die='EXIT=$? LINE=$LINENO error_exit' trap die ERR trap cleanup EXIT function error_exit() { trap - ERR local DEFAULT='Unknown failure occured.' local REASON="\e[97m${1:-$DEFAULT}\e[39m" local FLAG="\e[91m[ERROR] \e[93m$EXIT@$LINE" msg "$FLAG $REASON" exit $EXIT } function warn() { local REASON="\e[97m$1\e[39m" local FLAG="\e[93m[WARNING]\e[39m" msg "$FLAG $REASON" } function info() { local REASON="$1" local FLAG="\e[36m[INFO]\e[39m" msg "$FLAG $REASON" } function msg() { local TEXT="$1" echo -e "$TEXT" } function cleanup() { [ -d "${CTID_FROM_PATH:-}" ] && pct unmount $CTID_FROM [ -d "${CTID_TO_PATH:-}" ] && pct unmount $CTID_TO popd >/dev/null rm -rf $TEMP_DIR } TEMP_DIR=$(mktemp -d) pushd $TEMP_DIR >/dev/null TITLE="Home Assistant LXC Data Copy" while read -r line; do TAG=$(echo "$line" | awk '{print $1}') ITEM=$(echo "$line" | awk '{print substr($0,36)}') OFFSET=2 if [[ $((${#ITEM} + $OFFSET)) -gt ${MSG_MAX_LENGTH:-} ]]; then MSG_MAX_LENGTH=$((${#ITEM} + $OFFSET)) fi CTID_MENU+=("$TAG" "$ITEM " "OFF") done < <(pct list | awk 'NR>1') while [ -z "${CTID_FROM:+x}" ]; do CTID_FROM=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "$TITLE" --radiolist \ "\nWhich HA LXC would you like to copy FROM?\n" \ 16 $(($MSG_MAX_LENGTH + 23)) 6 \ "${CTID_MENU[@]}" 3>&1 1>&2 2>&3) done while [ -z "${CTID_TO:+x}" ]; do CTID_TO=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "$TITLE" --radiolist \ "\nWhich HA Podman LXC would you like to copy TO?\n" \ 16 $(($MSG_MAX_LENGTH + 23)) 6 \ "${CTID_MENU[@]}" 3>&1 1>&2 2>&3) done for i in ${!CTID_MENU[@]}; do [ "${CTID_MENU[$i]}" == "$CTID_FROM" ] && CTID_FROM_HOSTNAME=$(sed 's/[[:space:]]*$//' <<<${CTID_MENU[$i + 1]}) [ "${CTID_MENU[$i]}" == "$CTID_TO" ] && CTID_TO_HOSTNAME=$(sed 's/[[:space:]]*$//' <<<${CTID_MENU[$i + 1]}) done whiptail --backtitle "Proxmox VE Helper Scripts" --defaultno --title "$TITLE" --yesno \ "Are you sure you want to copy data between the following LXCs? $CTID_FROM (${CTID_FROM_HOSTNAME}) -> $CTID_TO (${CTID_TO_HOSTNAME}) Version: 2022.02.12" 13 50 info "Home Assistant Data from '$CTID_FROM' to '$CTID_TO'" if [ $(pct status $CTID_TO | sed 's/.* //') == 'running' ]; then msg "Stopping '$CTID_TO'..." pct stop $CTID_TO fi msg "Mounting Container Disks..." DOCKER_PATH=/var/lib/docker/volumes/hass_config/ PODMAN_PATH=/var/lib/containers/storage/volumes/hass_config/ CTID_FROM_PATH=$(pct mount $CTID_FROM | sed -n "s/.*'\(.*\)'/\1/p") || die "There was a problem mounting the root disk of LXC '${CTID_FROM}'." [ -d "${CTID_FROM_PATH}${DOCKER_PATH}" ] || die "Home Assistant directories in '$CTID_FROM' not found." CTID_TO_PATH=$(pct mount $CTID_TO | sed -n "s/.*'\(.*\)'/\1/p") || die "There was a problem mounting the root disk of LXC '${CTID_TO}'." [ -d "${CTID_TO_PATH}${PODMAN_PATH}" ] || die "Home Assistant directories in '$CTID_TO' not found." rm -rf ${CTID_TO_PATH}${PODMAN_PATH} mkdir ${CTID_TO_PATH}${PODMAN_PATH} msg "Copying Data Between Containers..." RSYNC_OPTIONS=( --archive --hard-links --sparse --xattrs --no-inc-recursive --info=progress2 ) msg "<======== Docker Data ========>" rsync ${RSYNC_OPTIONS[*]} ${CTID_FROM_PATH}${DOCKER_PATH} ${CTID_TO_PATH}${PODMAN_PATH} echo -en "\e[1A\e[0K\e[1A\e[0K" info "Successfully Transferred Data." # Use to copy all data from a Home Assistant LXC to a Podman Home Assistant LXC # run from the Proxmox Shell # bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/mainmain/tools/copy-data//home-assistant-container-copy-data-podman-home-assistant.sh)" ================================================ FILE: tools/copy-data/home-assistant-core-copy-data-home-assistant-container.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT # https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE clear if ! command -v pveversion >/dev/null 2>&1; then echo -e "⚠️ Run from the Proxmox Shell" exit fi while true; do read -p "Use to copy all data from a Home Assistant Core LXC to a Home Assistant Container LXC. Proceed(y/n)?" yn case $yn in [Yy]*) break ;; [Nn]*) exit ;; *) echo "Please answer yes or no." ;; esac done set -o errexit set -o errtrace set -o nounset set -o pipefail shopt -s expand_aliases alias die='EXIT=$? LINE=$LINENO error_exit' trap die ERR trap cleanup EXIT function error_exit() { trap - ERR local DEFAULT='Unknown failure occured.' local REASON="\e[97m${1:-$DEFAULT}\e[39m" local FLAG="\e[91m[ERROR] \e[93m$EXIT@$LINE" msg "$FLAG $REASON" exit $EXIT } function warn() { local REASON="\e[97m$1\e[39m" local FLAG="\e[93m[WARNING]\e[39m" msg "$FLAG $REASON" } function info() { local REASON="$1" local FLAG="\e[36m[INFO]\e[39m" msg "$FLAG $REASON" } function msg() { local TEXT="$1" echo -e "$TEXT" } function cleanup() { [ -d "${CTID_FROM_PATH:-}" ] && pct unmount $CTID_FROM [ -d "${CTID_TO_PATH:-}" ] && pct unmount $CTID_TO popd >/dev/null rm -rf $TEMP_DIR } TEMP_DIR=$(mktemp -d) pushd $TEMP_DIR >/dev/null TITLE="Home Assistant LXC Data Copy" while read -r line; do TAG=$(echo "$line" | awk '{print $1}') ITEM=$(echo "$line" | awk '{print substr($0,36)}') OFFSET=2 if [[ $((${#ITEM} + $OFFSET)) -gt ${MSG_MAX_LENGTH:-} ]]; then MSG_MAX_LENGTH=$((${#ITEM} + $OFFSET)) fi CTID_MENU+=("$TAG" "$ITEM " "OFF") done < <(pct list | awk 'NR>1') while [ -z "${CTID_FROM:+x}" ]; do CTID_FROM=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "$TITLE" --radiolist \ "\nWhich HA Core LXC would you like to copy FROM?\n" \ 16 $(($MSG_MAX_LENGTH + 23)) 6 \ "${CTID_MENU[@]}" 3>&1 1>&2 2>&3) done while [ -z "${CTID_TO:+x}" ]; do CTID_TO=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "$TITLE" --radiolist \ "\nWhich HA Container LXC would you like to copy TO?\n" \ 16 $(($MSG_MAX_LENGTH + 23)) 6 \ "${CTID_MENU[@]}" 3>&1 1>&2 2>&3) done for i in ${!CTID_MENU[@]}; do [ "${CTID_MENU[$i]}" == "$CTID_FROM" ] && CTID_FROM_HOSTNAME=$(sed 's/[[:space:]]*$//' <<<${CTID_MENU[$i + 1]}) [ "${CTID_MENU[$i]}" == "$CTID_TO" ] && CTID_TO_HOSTNAME=$(sed 's/[[:space:]]*$//' <<<${CTID_MENU[$i + 1]}) done whiptail --backtitle "Proxmox VE Helper Scripts" --defaultno --title "$TITLE" --yesno \ "Are you sure you want to copy data between the following LXCs? $CTID_FROM (${CTID_FROM_HOSTNAME}) -> $CTID_TO (${CTID_TO_HOSTNAME}) Version: 2022.10.02" 13 50 info "Home Assistant Data from '$CTID_FROM' to '$CTID_TO'" if [ $(pct status $CTID_TO | sed 's/.* //') == 'running' ]; then msg "Stopping '$CTID_TO'..." pct stop $CTID_TO fi msg "Mounting Container Disks..." DOCKER_PATH=/var/lib/docker/volumes/hass_config/_data CORE_PATH=/root/.homeassistant CTID_FROM_PATH=$(pct mount $CTID_FROM | sed -n "s/.*'\(.*\)'/\1/p") || die "There was a problem mounting the root disk of LXC '${CTID_FROM}'." [ -d "${CTID_FROM_PATH}${CORE_PATH}" ] || die "Home Assistant directories in '$CTID_FROM' not found." CTID_TO_PATH=$(pct mount $CTID_TO | sed -n "s/.*'\(.*\)'/\1/p") || die "There was a problem mounting the root disk of LXC '${CTID_TO}'." [ -d "${CTID_TO_PATH}${DOCKER_PATH}" ] || die "Home Assistant directories in '$CTID_TO' not found." msg "Copying Data..." RSYNC_OPTIONS=( --archive --hard-links --sparse --xattrs --no-inc-recursive --info=progress2 ) msg "<======== Docker Data ========>" rsync ${RSYNC_OPTIONS[*]} ${CTID_FROM_PATH}${CORE_PATH} ${CTID_TO_PATH}${DOCKER_PATH} echo -en "\e[1A\e[0K\e[1A\e[0K" info "Successfully Transferred Data." # Use to copy all data from a Home Assistant Core LXC to a Home Assistant Container LXC # run from the Proxmox Shell # bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/mainmain/tools/copy-data//home-assistant-core-copy-data-home-assistant-container.sh)" ================================================ FILE: tools/copy-data/home-assistant-core-copy-data-home-assistant-core.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT # https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE clear if ! command -v pveversion >/dev/null 2>&1; then echo -e "⚠️ Run from the Proxmox Shell" exit fi while true; do read -p "Use to copy all data from a Home Assistant Core LXC to a Home Assistant Core LXC. Proceed(y/n)?" yn case $yn in [Yy]*) break ;; [Nn]*) exit ;; *) echo "Please answer yes or no." ;; esac done set -o errexit set -o errtrace set -o nounset set -o pipefail shopt -s expand_aliases alias die='EXIT=$? LINE=$LINENO error_exit' trap die ERR trap cleanup EXIT function error_exit() { trap - ERR local DEFAULT='Unknown failure occured.' local REASON="\e[97m${1:-$DEFAULT}\e[39m" local FLAG="\e[91m[ERROR] \e[93m$EXIT@$LINE" msg "$FLAG $REASON" exit $EXIT } function info() { local REASON="$1" local FLAG="\e[36m[INFO]\e[39m" msg "$FLAG $REASON" } function msg() { local TEXT="$1" echo -e "$TEXT" } function cleanup() { [ -d "${CTID_FROM_PATH:-}" ] && pct unmount $CTID_FROM [ -d "${CTID_TO_PATH:-}" ] && pct unmount $CTID_TO popd >/dev/null rm -rf $TEMP_DIR } TEMP_DIR=$(mktemp -d) pushd $TEMP_DIR >/dev/null TITLE="Home Assistant LXC Data Copy" while read -r line; do TAG=$(echo "$line" | awk '{print $1}') ITEM=$(echo "$line" | awk '{print substr($0,36)}') OFFSET=2 if [[ $((${#ITEM} + $OFFSET)) -gt ${MSG_MAX_LENGTH:-} ]]; then MSG_MAX_LENGTH=$((${#ITEM} + $OFFSET)) fi CTID_MENU+=("$TAG" "$ITEM " "OFF") done < <(pct list | awk 'NR>1') while [ -z "${CTID_FROM:+x}" ]; do CTID_FROM=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "$TITLE" --radiolist \ "\nWhich HA Core LXC would you like to copy FROM?\n" \ 16 $(($MSG_MAX_LENGTH + 23)) 6 \ "${CTID_MENU[@]}" 3>&1 1>&2 2>&3) done while [ -z "${CTID_TO:+x}" ]; do CTID_TO=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "$TITLE" --radiolist \ "\nWhich HA Core LXC would you like to copy TO?\n" \ 16 $(($MSG_MAX_LENGTH + 23)) 6 \ "${CTID_MENU[@]}" 3>&1 1>&2 2>&3) done for i in ${!CTID_MENU[@]}; do [ "${CTID_MENU[$i]}" == "$CTID_FROM" ] && CTID_FROM_HOSTNAME=$(sed 's/[[:space:]]*$//' <<<${CTID_MENU[$i + 1]}) [ "${CTID_MENU[$i]}" == "$CTID_TO" ] && CTID_TO_HOSTNAME=$(sed 's/[[:space:]]*$//' <<<${CTID_MENU[$i + 1]}) done whiptail --backtitle "Proxmox VE Helper Scripts" --defaultno --title "$TITLE" --yesno \ "Are you sure you want to copy data between the following LXCs? $CTID_FROM (${CTID_FROM_HOSTNAME}) -> $CTID_TO (${CTID_TO_HOSTNAME}) Version: 2022.10.03" 13 50 info "Home Assistant Data from '$CTID_FROM' to '$CTID_TO'" if [ $(pct status $CTID_FROM | sed 's/.* //') == 'running' ]; then msg "Stopping '$CTID_FROM'..." pct stop $CTID_FROM fi if [ $(pct status $CTID_TO | sed 's/.* //') == 'running' ]; then msg "Stopping '$CTID_TO'..." pct stop $CTID_TO fi msg "Mounting Container Disks..." CORE_PATH=/root/.homeassistant CORE_PATH2=/root/ CTID_FROM_PATH=$(pct mount $CTID_FROM | sed -n "s/.*'\(.*\)'/\1/p") || die "There was a problem mounting the root disk of LXC '${CTID_FROM}'." [ -d "${CTID_FROM_PATH}${CORE_PATH}" ] || die "Home Assistant directories in '$CTID_FROM' not found." CTID_TO_PATH=$(pct mount $CTID_TO | sed -n "s/.*'\(.*\)'/\1/p") || die "There was a problem mounting the root disk of LXC '${CTID_TO}'." [ -d "${CTID_TO_PATH}${CORE_PATH2}" ] || die "Home Assistant directories in '$CTID_TO' not found." msg "Copying Data..." RSYNC_OPTIONS=( --archive --hard-links --sparse --xattrs --no-inc-recursive --info=progress2 ) msg "<======== Docker Data ========>" rsync ${RSYNC_OPTIONS[*]} ${CTID_FROM_PATH}${CORE_PATH} ${CTID_TO_PATH}${CORE_PATH2} echo -en "\e[1A\e[0K\e[1A\e[0K" info "Successfully Transferred Data." # Use to copy all data from a Home Assistant Core LXC to a Home Assistant Container LXC # run from the Proxmox Shell # bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/mainmain/tools/copy-data//home-assistant-core-copy-data-home-assistant-core.sh)" ================================================ FILE: tools/copy-data/plex-copy-data-plex.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT # https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Use to copy all data from one Plex Media Server LXC to another # run from the Proxmox Shell clear if ! command -v pveversion >/dev/null 2>&1; then echo -e "⚠️ Run from the Proxmox Shell" exit fi while true; do read -p "Use to copy all data from one Plex Media Server LXC to another. Proceed(y/n)?" yn case $yn in [Yy]*) break ;; [Nn]*) exit ;; *) echo "Please answer yes or no." ;; esac done set -o errexit set -o errtrace set -o nounset set -o pipefail shopt -s expand_aliases alias die='EXIT=$? LINE=$LINENO error_exit' trap die ERR trap cleanup EXIT function error_exit() { trap - ERR local DEFAULT='Unknown failure occured.' local REASON="\e[97m${1:-$DEFAULT}\e[39m" local FLAG="\e[91m[ERROR] \e[93m$EXIT@$LINE" msg "$FLAG $REASON" exit $EXIT } function warn() { local REASON="\e[97m$1\e[39m" local FLAG="\e[93m[WARNING]\e[39m" msg "$FLAG $REASON" } function info() { local REASON="$1" local FLAG="\e[36m[INFO]\e[39m" msg "$FLAG $REASON" } function msg() { local TEXT="$1" echo -e "$TEXT" } function cleanup() { [ -d "${CTID_FROM_PATH:-}" ] && pct unmount $CTID_FROM [ -d "${CTID_TO_PATH:-}" ] && pct unmount $CTID_TO popd >/dev/null rm -rf $TEMP_DIR } TEMP_DIR=$(mktemp -d) pushd $TEMP_DIR >/dev/null TITLE="Plex Media Server LXC Data Copy" while read -r line; do TAG=$(echo "$line" | awk '{print $1}') ITEM=$(echo "$line" | awk '{print substr($0,36)}') OFFSET=2 if [[ $((${#ITEM} + $OFFSET)) -gt ${MSG_MAX_LENGTH:-} ]]; then MSG_MAX_LENGTH=$((${#ITEM} + $OFFSET)) fi CTID_MENU+=("$TAG" "$ITEM " "OFF") done < <(pct list | awk 'NR>1') while [ -z "${CTID_FROM:+x}" ]; do CTID_FROM=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "$TITLE" --radiolist \ "\nWhich Plex Media Server LXC would you like to copy FROM?\n" \ 16 $(($MSG_MAX_LENGTH + 23)) 6 \ "${CTID_MENU[@]}" 3>&1 1>&2 2>&3) done while [ -z "${CTID_TO:+x}" ]; do CTID_TO=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "$TITLE" --radiolist \ "\nWhich Plex Media Server LXC would you like to copy TO?\n" \ 16 $(($MSG_MAX_LENGTH + 23)) 6 \ "${CTID_MENU[@]}" 3>&1 1>&2 2>&3) done for i in ${!CTID_MENU[@]}; do [ "${CTID_MENU[$i]}" == "$CTID_FROM" ] && CTID_FROM_HOSTNAME=$(sed 's/[[:space:]]*$//' <<<${CTID_MENU[$i + 1]}) [ "${CTID_MENU[$i]}" == "$CTID_TO" ] && CTID_TO_HOSTNAME=$(sed 's/[[:space:]]*$//' <<<${CTID_MENU[$i + 1]}) done whiptail --backtitle "Proxmox VE Helper Scripts" --defaultno --title "$TITLE" --yesno \ "Are you sure you want to copy data between the following LXCs? $CTID_FROM (${CTID_FROM_HOSTNAME}) -> $CTID_TO (${CTID_TO_HOSTNAME}) Version: 2022.01.24" 13 50 info "Plex Media Server Data from '$CTID_FROM' to '$CTID_TO'" if [ $(pct status $CTID_TO | sed 's/.* //') == 'running' ]; then msg "Stopping '$CTID_TO'..." pct stop $CTID_TO fi msg "Mounting Container Disks..." DATA_PATH=/var/lib/plexmediaserver/Library/ CTID_FROM_PATH=$(pct mount $CTID_FROM | sed -n "s/.*'\(.*\)'/\1/p") || die "There was a problem mounting the root disk of LXC '${CTID_FROM}'." [ -d "${CTID_FROM_PATH}${DATA_PATH}" ] || die "Plex Media Server directories in '$CTID_FROM' not found." CTID_TO_PATH=$(pct mount $CTID_TO | sed -n "s/.*'\(.*\)'/\1/p") || die "There was a problem mounting the root disk of LXC '${CTID_TO}'." [ -d "${CTID_TO_PATH}${DATA_PATH}" ] || die "Plex Media Server directories in '$CTID_TO' not found." #rm -rf ${CTID_TO_PATH}${DATA_PATH} #mkdir ${CTID_TO_PATH}${DATA_PATH} msg "Copying Data Between Containers..." RSYNC_OPTIONS=( --archive --hard-links --sparse --xattrs --no-inc-recursive --info=progress2 ) msg "<======== Plex Media Server Data ========>" rsync ${RSYNC_OPTIONS[*]} ${CTID_FROM_PATH}${DATA_PATH} ${CTID_TO_PATH}${DATA_PATH} echo -en "\e[1A\e[0K\e[1A\e[0K" info "Successfully Transferred Data." # Use to copy all data from one Plex Media Server LXC to another # run from the Proxmox Shell # bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/mainmain/tools/copy-data//plex-copy-data-plex.sh)" ================================================ FILE: tools/copy-data/podman-home-assistant-copy-data-home-assistant-container.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT # https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Use to copy all data from a Podman Home Assistant LXC to a Docker Home Assistant LXC. # run from the Proxmox Shell clear if ! command -v pveversion >/dev/null 2>&1; then echo -e "⚠️ Run from the Proxmox Shell" exit fi while true; do read -p "Use to copy all data from a Podman Home Assistant LXC to a Docker Home Assistant LXC. Proceed(y/n)?" yn case $yn in [Yy]*) break ;; [Nn]*) exit ;; *) echo "Please answer yes or no." ;; esac done set -o errexit set -o errtrace set -o nounset set -o pipefail shopt -s expand_aliases alias die='EXIT=$? LINE=$LINENO error_exit' trap die ERR trap cleanup EXIT function error_exit() { trap - ERR local DEFAULT='Unknown failure occured.' local REASON="\e[97m${1:-$DEFAULT}\e[39m" local FLAG="\e[91m[ERROR] \e[93m$EXIT@$LINE" msg "$FLAG $REASON" exit $EXIT } function warn() { local REASON="\e[97m$1\e[39m" local FLAG="\e[93m[WARNING]\e[39m" msg "$FLAG $REASON" } function info() { local REASON="$1" local FLAG="\e[36m[INFO]\e[39m" msg "$FLAG $REASON" } function msg() { local TEXT="$1" echo -e "$TEXT" } function cleanup() { [ -d "${CTID_FROM_PATH:-}" ] && pct unmount $CTID_FROM [ -d "${CTID_TO_PATH:-}" ] && pct unmount $CTID_TO popd >/dev/null rm -rf $TEMP_DIR } TEMP_DIR=$(mktemp -d) pushd $TEMP_DIR >/dev/null TITLE="Home Assistant LXC Data Copy" while read -r line; do TAG=$(echo "$line" | awk '{print $1}') ITEM=$(echo "$line" | awk '{print substr($0,36)}') OFFSET=2 if [[ $((${#ITEM} + $OFFSET)) -gt ${MSG_MAX_LENGTH:-} ]]; then MSG_MAX_LENGTH=$((${#ITEM} + $OFFSET)) fi CTID_MENU+=("$TAG" "$ITEM " "OFF") done < <(pct list | awk 'NR>1') while [ -z "${CTID_FROM:+x}" ]; do CTID_FROM=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "$TITLE" --radiolist \ "\nWhich HA Podman LXC would you like to copy FROM?\n" \ 16 $(($MSG_MAX_LENGTH + 23)) 6 \ "${CTID_MENU[@]}" 3>&1 1>&2 2>&3) done while [ -z "${CTID_TO:+x}" ]; do CTID_TO=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "$TITLE" --radiolist \ "\nWhich HA LXC would you like to copy TO?\n" \ 16 $(($MSG_MAX_LENGTH + 23)) 6 \ "${CTID_MENU[@]}" 3>&1 1>&2 2>&3) done for i in ${!CTID_MENU[@]}; do [ "${CTID_MENU[$i]}" == "$CTID_FROM" ] && CTID_FROM_HOSTNAME=$(sed 's/[[:space:]]*$//' <<<${CTID_MENU[$i + 1]}) [ "${CTID_MENU[$i]}" == "$CTID_TO" ] && CTID_TO_HOSTNAME=$(sed 's/[[:space:]]*$//' <<<${CTID_MENU[$i + 1]}) done whiptail --backtitle "Proxmox VE Helper Scripts" --defaultno --title "$TITLE" --yesno \ "Are you sure you want to copy data between the following LXCs? $CTID_FROM (${CTID_FROM_HOSTNAME}) -> $CTID_TO (${CTID_TO_HOSTNAME}) Version: 2022.03.31" 13 50 info "Home Assistant Data from '$CTID_FROM' to '$CTID_TO'" if [ $(pct status $CTID_TO | sed 's/.* //') == 'running' ]; then msg "Stopping '$CTID_TO'..." pct stop $CTID_TO fi msg "Mounting Container Disks..." DOCKER_PATH=/var/lib/docker/volumes/hass_config/ PODMAN_PATH=/var/lib/containers/storage/volumes/hass_config/ CTID_FROM_PATH=$(pct mount $CTID_FROM | sed -n "s/.*'\(.*\)'/\1/p") || die "There was a problem mounting the root disk of LXC '${CTID_FROM}'." [ -d "${CTID_FROM_PATH}${PODMAN_PATH}" ] || die "Home Assistant directories in '$CTID_FROM' not found." CTID_TO_PATH=$(pct mount $CTID_TO | sed -n "s/.*'\(.*\)'/\1/p") || die "There was a problem mounting the root disk of LXC '${CTID_TO}'." [ -d "${CTID_TO_PATH}${DOCKER_PATH}" ] || die "Home Assistant directories in '$CTID_TO' not found." rm -rf ${CTID_TO_PATH}${DOCKER_PATH} mkdir ${CTID_TO_PATH}${DOCKER_PATH} msg "Copying Data Between Containers..." RSYNC_OPTIONS=( --archive --hard-links --sparse --xattrs --no-inc-recursive --info=progress2 ) msg "<======== Data ========>" rsync ${RSYNC_OPTIONS[*]} ${CTID_FROM_PATH}${PODMAN_PATH} ${CTID_TO_PATH}${DOCKER_PATH} echo -en "\e[1A\e[0K\e[1A\e[0K" info "Successfully Transferred Data." # Use to copy all data from a Podman Home Assistant LXC to a Docker Home Assistant LXC. # run from the Proxmox Shell # bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/mainmain/tools/copy-data//podman-home-assistant-copy-data-home-assistant-container.sh)" ================================================ FILE: tools/copy-data/z2m-copy-data-z2m.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT # https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Use to copy all data from one Zigbee2MQTT LXC to another # run from the Proxmox Shell clear if ! command -v pveversion >/dev/null 2>&1; then echo -e "⚠️ Run from the Proxmox Shell" exit fi while true; do read -p "Use to copy all data from one Zigbee2MQTT LXC to another. Proceed(y/n)?" yn case $yn in [Yy]*) break ;; [Nn]*) exit ;; *) echo "Please answer yes or no." ;; esac done set -o errexit set -o errtrace set -o nounset set -o pipefail shopt -s expand_aliases alias die='EXIT=$? LINE=$LINENO error_exit' trap die ERR trap cleanup EXIT function error_exit() { trap - ERR local DEFAULT='Unknown failure occured.' local REASON="\e[97m${1:-$DEFAULT}\e[39m" local FLAG="\e[91m[ERROR] \e[93m$EXIT@$LINE" msg "$FLAG $REASON" exit $EXIT } function warn() { local REASON="\e[97m$1\e[39m" local FLAG="\e[93m[WARNING]\e[39m" msg "$FLAG $REASON" } function info() { local REASON="$1" local FLAG="\e[36m[INFO]\e[39m" msg "$FLAG $REASON" } function msg() { local TEXT="$1" echo -e "$TEXT" } function cleanup() { [ -d "${CTID_FROM_PATH:-}" ] && pct unmount $CTID_FROM [ -d "${CTID_TO_PATH:-}" ] && pct unmount $CTID_TO popd >/dev/null rm -rf $TEMP_DIR } TEMP_DIR=$(mktemp -d) pushd $TEMP_DIR >/dev/null TITLE="Zigbee2MQTT LXC Data Copy" while read -r line; do TAG=$(echo "$line" | awk '{print $1}') ITEM=$(echo "$line" | awk '{print substr($0,36)}') OFFSET=2 if [[ $((${#ITEM} + $OFFSET)) -gt ${MSG_MAX_LENGTH:-} ]]; then MSG_MAX_LENGTH=$((${#ITEM} + $OFFSET)) fi CTID_MENU+=("$TAG" "$ITEM " "OFF") done < <(pct list | awk 'NR>1') while [ -z "${CTID_FROM:+x}" ]; do CTID_FROM=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "$TITLE" --radiolist \ "\nWhich Zigbee2MQTT LXC would you like to copy FROM?\n" \ 16 $(($MSG_MAX_LENGTH + 23)) 6 \ "${CTID_MENU[@]}" 3>&1 1>&2 2>&3) done while [ -z "${CTID_TO:+x}" ]; do CTID_TO=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "$TITLE" --radiolist \ "\nWhich Zigbee2MQTT LXC would you like to copy TO?\n" \ 16 $(($MSG_MAX_LENGTH + 23)) 6 \ "${CTID_MENU[@]}" 3>&1 1>&2 2>&3) done for i in ${!CTID_MENU[@]}; do [ "${CTID_MENU[$i]}" == "$CTID_FROM" ] && CTID_FROM_HOSTNAME=$(sed 's/[[:space:]]*$//' <<<${CTID_MENU[$i + 1]}) [ "${CTID_MENU[$i]}" == "$CTID_TO" ] && CTID_TO_HOSTNAME=$(sed 's/[[:space:]]*$//' <<<${CTID_MENU[$i + 1]}) done whiptail --backtitle "Proxmox VE Helper Scripts" --defaultno --title "$TITLE" --yesno \ "Are you sure you want to copy data between the following LXCs? $CTID_FROM (${CTID_FROM_HOSTNAME}) -> $CTID_TO (${CTID_TO_HOSTNAME}) Version: 2022.01.23" 13 50 info "Zigbee2MQTT Data from '$CTID_FROM' to '$CTID_TO'" if [ $(pct status $CTID_TO | sed 's/.* //') == 'running' ]; then msg "Stopping '$CTID_TO'..." pct stop $CTID_TO fi msg "Mounting Container Disks..." DATA_PATH=/opt/zigbee2mqtt/data/ CTID_FROM_PATH=$(pct mount $CTID_FROM | sed -n "s/.*'\(.*\)'/\1/p") || die "There was a problem mounting the root disk of LXC '${CTID_FROM}'." [ -d "${CTID_FROM_PATH}${DATA_PATH}" ] || die "Zigbee2igbee2MQTT directories in '$CTID_FROM' not found." CTID_TO_PATH=$(pct mount $CTID_TO | sed -n "s/.*'\(.*\)'/\1/p") || die "There was a problem mounting the root disk of LXC '${CTID_TO}'." [ -d "${CTID_TO_PATH}${DATA_PATH}" ] || die "Zigbee2MQTT directories in '$CTID_TO' not found." #rm -rf ${CTID_TO_PATH}${DATA_PATH} #mkdir ${CTID_TO_PATH}${DATA_PATH} msg "Copying Data Between Containers..." RSYNC_OPTIONS=( --archive --hard-links --sparse --xattrs --no-inc-recursive --info=progress2 ) msg "<======== Zigbee2MQTT Data ========>" rsync ${RSYNC_OPTIONS[*]} ${CTID_FROM_PATH}${DATA_PATH} ${CTID_TO_PATH}${DATA_PATH} echo -en "\e[1A\e[0K\e[1A\e[0K" info "Successfully Transferred Data." # Use to copy all data from one Zigbee2MQTT LXC to another # run from the Proxmox Shell # bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/mainmain/tools/copy-data//z2m-copy-data-z2m.sh)" ================================================ FILE: tools/copy-data/zwavejs2mqtt-copy-data-zwavejsui.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT # https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Use to copy all data from a Zwavejs2MQTT LXC to a Z-wave JS UI LXC # run from the Proxmox Shell clear if ! command -v pveversion >/dev/null 2>&1; then echo -e "⚠️ Run from the Proxmox Shell" exit fi while true; do read -p "Use to copy all data from a Zwavejs2MQTT LXC to a Z-wave JS UI LXC. Proceed(y/n)?" yn case $yn in [Yy]*) break ;; [Nn]*) exit ;; *) echo "Please answer yes or no." ;; esac done clear set -o errexit set -o errtrace set -o nounset set -o pipefail shopt -s expand_aliases alias die='EXIT=$? LINE=$LINENO error_exit' trap die ERR trap cleanup EXIT function error_exit() { trap - ERR local DEFAULT='Unknown failure occured.' local REASON="\e[97m${1:-$DEFAULT}\e[39m" local FLAG="\e[91m[ERROR] \e[93m$EXIT@$LINE" msg "$FLAG $REASON" exit "$EXIT" } function warn() { local REASON="\e[97m$1\e[39m" local FLAG="\e[93m[WARNING]\e[39m" msg "$FLAG $REASON" } function info() { local REASON="$1" local FLAG="\e[36m[INFO]\e[39m" msg "$FLAG $REASON" } function msg() { local TEXT="$1" echo -e "$TEXT" } function cleanup() { [ -d "${CTID_FROM_PATH:-}" ] && pct unmount "$CTID_FROM" [ -d "${CTID_TO_PATH:-}" ] && pct unmount "$CTID_TO" popd >/dev/null rm -rf "$TEMP_DIR" } TEMP_DIR=$(mktemp -d) pushd "$TEMP_DIR" >/dev/null TITLE="Zigbee2MQTT to Z-wave JS UI Data Copy" while read -r line; do TAG=$(echo "$line" | awk '{print $1}') ITEM=$(echo "$line" | awk '{print substr($0,36)}') OFFSET=2 if [[ $((${#ITEM} + $OFFSET)) -gt ${MSG_MAX_LENGTH:-} ]]; then MSG_MAX_LENGTH=$((${#ITEM} + $OFFSET)) fi CTID_MENU+=("$TAG" "$ITEM " "OFF") done < <(pct list | awk 'NR>1') while [ -z "${CTID_FROM:+x}" ]; do CTID_FROM=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "$TITLE" --radiolist \ "\nWhich Zwavejs2MQTT LXC would you like to copy FROM?\n" \ 16 $(($MSG_MAX_LENGTH + 23)) 6 \ "${CTID_MENU[@]}" 3>&1 1>&2 2>&3) done while [ -z "${CTID_TO:+x}" ]; do CTID_TO=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "$TITLE" --radiolist \ "\nWhich Z-wave JS UI LXC would you like to copy TO?\n" \ 16 $(($MSG_MAX_LENGTH + 23)) 6 \ "${CTID_MENU[@]}" 3>&1 1>&2 2>&3) done for i in ${!CTID_MENU[@]}; do [ "${CTID_MENU[$i]}" == "$CTID_FROM" ] && CTID_FROM_HOSTNAME=$(sed 's/[[:space:]]*$//' <<<"${CTID_MENU[$i + 1]}") [ "${CTID_MENU[$i]}" == "$CTID_TO" ] && CTID_TO_HOSTNAME=$(sed 's/[[:space:]]*$//' <<<"${CTID_MENU[$i + 1]}") done whiptail --backtitle "Proxmox VE Helper Scripts" --defaultno --title "$TITLE" --yesno \ "Are you sure you want to copy data between the following LXCs? $CTID_FROM (${CTID_FROM_HOSTNAME}) -> $CTID_TO (${CTID_TO_HOSTNAME}) Version: 2022.09.21" 13 50 info "Zwavejs2MQTT Data from '$CTID_FROM' to '$CTID_TO'" if [ $(pct status "$CTID_TO" | sed 's/.* //') == 'running' ]; then msg "Stopping '$CTID_TO'..." pct stop "$CTID_TO" fi msg "Mounting Container Disks..." DATA_PATH=/opt/zwavejs2mqtt/store/ DATA_PATH_NEW=/opt/zwave-js-ui/store/ CTID_FROM_PATH=$(pct mount "$CTID_FROM" | sed -n "s/.*'\(.*\)'/\1/p") || die "There was a problem mounting the root disk of LXC '${CTID_FROM}'." [ -d "${CTID_FROM_PATH}${DATA_PATH}" ] || die "Zwavejs2MQTT directories in '$CTID_FROM' not found." CTID_TO_PATH=$(pct mount "$CTID_TO" | sed -n "s/.*'\(.*\)'/\1/p") || die "There was a problem mounting the root disk of LXC '${CTID_TO}'." [ -d "${CTID_TO_PATH}${DATA_PATH_NEW}" ] || die "Zwavejs2MQTT directories in '$CTID_TO' not found." #rm -rf ${CTID_TO_PATH}${DATA_PATH} #mkdir ${CTID_TO_PATH}${DATA_PATH} msg "Copying Data Between Containers..." RSYNC_OPTIONS=( --archive --hard-links --sparse --xattrs --no-inc-recursive --info=progress2 ) msg "<======== Zwavejs Data ========>" rsync "${RSYNC_OPTIONS[*]}" "${CTID_FROM_PATH}"${DATA_PATH} "${CTID_TO_PATH}"${DATA_PATH_NEW} echo -en "\e[1A\e[0K\e[1A\e[0K" info "Successfully Transferred Data." # Use to copy all data from a Zwavejs2MQTT LXC to a Z-wave JS UI LXC # run from the Proxmox Shell # bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/mainmain/tools/copy-data//zwavejs2mqtt-copy-data-zwavejsui.sh)" ================================================ FILE: tools/headers/add-iptag ================================================ ________ ______ / _/ __ \ /_ __/___ _____ _ / // /_/ /_____/ / / __ `/ __ `/ _/ // ____/_____/ / / /_/ / /_/ / /___/_/ /_/ \__,_/\__, / /____/ ================================================ FILE: tools/headers/adguardhome-sync ================================================ ___ ________ ____ __ _____ / | ____/ / ____/_ ______ __________/ / / / /___ ____ ___ ___ / ___/__ ______ _____ / /| |/ __ / / __/ / / / __ `/ ___/ __ / /_/ / __ \/ __ `__ \/ _ \______\__ \/ / / / __ \/ ___/ / ___ / /_/ / /_/ / /_/ / /_/ / / / /_/ / __ / /_/ / / / / / / __/_____/__/ / /_/ / / / / /__ /_/ |_\__,_/\____/\__,_/\__,_/_/ \__,_/_/ /_/\____/_/ /_/ /_/\___/ /____/\__, /_/ /_/\___/ /____/ ================================================ FILE: tools/headers/arcane ================================================ ___ / | ______________ _____ ___ / /| | / ___/ ___/ __ `/ __ \/ _ \ / ___ |/ / / /__/ /_/ / / / / __/ /_/ |_/_/ \___/\__,_/_/ /_/\___/ ================================================ FILE: tools/headers/coder-code-server ================================================ ______ __ ______ __ _____ / ____/___ ____/ /__ _____ / ____/___ ____/ /__ / ___/___ ______ _____ _____ / / / __ \/ __ / _ \/ ___/ / / / __ \/ __ / _ \ \__ \/ _ \/ ___/ | / / _ \/ ___/ / /___/ /_/ / /_/ / __/ / / /___/ /_/ / /_/ / __/ ___/ / __/ / | |/ / __/ / \____/\____/\__,_/\___/_/ \____/\____/\__,_/\___/ /____/\___/_/ |___/\___/_/ ================================================ FILE: tools/headers/container-restore-from-backup ================================================ __ __ ___ _ __ __ ______ __ _ / / / /___ ____ ___ ___ / | __________(_)____/ /_____ _____ / /_ / ____/___ ____ / /_____ _(_)___ ___ _____ / /_/ / __ \/ __ `__ \/ _ \ / /| | / ___/ ___/ / ___/ __/ __ `/ __ \/ __/ / / / __ \/ __ \/ __/ __ `/ / __ \/ _ \/ ___/ / __ / /_/ / / / / / / __/ / ___ |(__ |__ ) (__ ) /_/ /_/ / / / / /_ / /___/ /_/ / / / / /_/ /_/ / / / / / __/ / /_/ /_/\____/_/ /_/ /_/\___/ /_/ |_/____/____/_/____/\__/\__,_/_/ /_/\__/ \____/\____/_/ /_/\__/\__,_/_/_/ /_/\___/_/ ================================================ FILE: tools/headers/coolify ================================================ ______ ___ ____ / ____/___ ____ / (_) __/_ __ / / / __ \/ __ \/ / / /_/ / / / / /___/ /_/ / /_/ / / / __/ /_/ / \____/\____/\____/_/_/_/ \__, / /____/ ================================================ FILE: tools/headers/copyparty ================================================ ______ ____ __ / ____/___ ____ __ __/ __ \____ ______/ /___ __ / / / __ \/ __ \/ / / / /_/ / __ `/ ___/ __/ / / / / /___/ /_/ / /_/ / /_/ / ____/ /_/ / / / /_/ /_/ / \____/\____/ .___/\__, /_/ \__,_/_/ \__/\__, / /_/ /____/ /____/ ================================================ FILE: tools/headers/core-restore-from-backup ================================================ __ __ ___ _ __ __ ______ / / / /___ ____ ___ ___ / | __________(_)____/ /_____ _____ / /_ / ____/___ ________ / /_/ / __ \/ __ `__ \/ _ \ / /| | / ___/ ___/ / ___/ __/ __ `/ __ \/ __/ / / / __ \/ ___/ _ \ / __ / /_/ / / / / / / __/ / ___ |(__ |__ ) (__ ) /_/ /_/ / / / / /_ / /___/ /_/ / / / __/ /_/ /_/\____/_/ /_/ /_/\___/ /_/ |_/____/____/_/____/\__/\__,_/_/ /_/\__/ \____/\____/_/ \___/ ================================================ FILE: tools/headers/cronmaster ================================================ ______ __ ___ __ / ____/________ ____ / |/ /___ ______/ /____ _____ / / / ___/ __ \/ __ \/ /|_/ / __ `/ ___/ __/ _ \/ ___/ / /___/ / / /_/ / / / / / / / /_/ (__ ) /_/ __/ / \____/_/ \____/_/ /_/_/ /_/\__,_/____/\__/\___/_/ ================================================ FILE: tools/headers/crowdsec ================================================ ______ _______ / ____/________ _ ______/ / ___/___ _____ / / / ___/ __ \ | /| / / __ /\__ \/ _ \/ ___/ / /___/ / / /_/ / |/ |/ / /_/ /___/ / __/ /__ \____/_/ \____/|__/|__/\__,_//____/\___/\___/ ================================================ FILE: tools/headers/dockge ================================================ ____ __ / __ \____ _____/ /______ ____ / / / / __ \/ ___/ //_/ __ `/ _ \ / /_/ / /_/ / /__/ ,< / /_/ / __/ /_____/\____/\___/_/|_|\__, /\___/ /____/ ================================================ FILE: tools/headers/dokploy ================================================ ____ __ __ / __ \____ / /______ / /___ __ __ / / / / __ \/ //_/ __ \/ / __ \/ / / / / /_/ / /_/ / ,< / /_/ / / /_/ / /_/ / /_____/\____/_/|_/ .___/_/\____/\__, / /_/ /____/ ================================================ FILE: tools/headers/filebrowser ================================================ _______ __ ____ / ____(_) /__ / __ )_________ _ __________ _____ / /_ / / / _ \/ __ / ___/ __ \ | /| / / ___/ _ \/ ___/ / __/ / / / __/ /_/ / / / /_/ / |/ |/ (__ ) __/ / /_/ /_/_/\___/_____/_/ \____/|__/|__/____/\___/_/ ================================================ FILE: tools/headers/filebrowser-quantum ================================================ _______ __ ____ ____ __ / ____(_) /__ / __ )_________ _ __________ _____ / __ \__ ______ _____ / /___ ______ ___ / /_ / / / _ \/ __ / ___/ __ \ | /| / / ___/ _ \/ ___/ / / / / / / / __ `/ __ \/ __/ / / / __ `__ \ / __/ / / / __/ /_/ / / / /_/ / |/ |/ (__ ) __/ / / /_/ / /_/ / /_/ / / / / /_/ /_/ / / / / / / /_/ /_/_/\___/_____/_/ \____/|__/|__/____/\___/_/ \___\_\__,_/\__,_/_/ /_/\__/\__,_/_/ /_/ /_/ ================================================ FILE: tools/headers/glances ================================================ ________ / ____/ /___ _____ ________ _____ / / __/ / __ `/ __ \/ ___/ _ \/ ___/ / /_/ / / /_/ / / / / /__/ __(__ ) \____/_/\__,_/_/ /_/\___/\___/____/ ================================================ FILE: tools/headers/homebrew ================================================ __ __ / /_ ____ ____ ___ ___ / /_ ________ _ __ / __ \/ __ \/ __ `__ \/ _ \/ __ \/ ___/ _ \ | /| / / / / / / /_/ / / / / / / __/ /_/ / / / __/ |/ |/ / /_/ /_/\____/_/ /_/ /_/\___/_.___/_/ \___/|__/|__/ ================================================ FILE: tools/headers/immich-public-proxy ================================================ ____ _ __ ____ __ ___ ____ / _/___ ___ ____ ___ (_)____/ /_ / __ \__ __/ /_ / (_)____ / __ \_________ _ ____ __ / // __ `__ \/ __ `__ \/ / ___/ __ \ / /_/ / / / / __ \/ / / ___/ / /_/ / ___/ __ \| |/_/ / / / _/ // / / / / / / / / / / / /__/ / / / / ____/ /_/ / /_/ / / / /__ / ____/ / / /_/ /> dict: if not STATUSES_FILE.exists(): return {} return json.loads(STATUSES_FILE.read_text(encoding="utf-8")) def save_statuses(statuses: dict) -> None: sorted_statuses = dict(sorted(statuses.items(), key=lambda kv: kv[0])) STATUSES_FILE.parent.mkdir(parents=True, exist_ok=True) STATUSES_FILE.write_text( json.dumps(sorted_statuses, indent=4, ensure_ascii=False) + "\n", encoding="utf-8", ) print(f"Statuses updated: {STATUSES_FILE}") def iter_json_script_files() -> list[str]: if not JSON_DIR.exists(): return [] files = [] for p in sorted(JSON_DIR.iterdir()): if p.is_file() and p.suffix == ".json" and p.name not in EXCLUDE_FILES: files.append(p.name) return files def add_pending_status() -> None: """Set status to 🚧 for any JSON script missing a status, then sort and save.""" statuses = load_statuses() changed = False for fname in iter_json_script_files(): if fname not in statuses or statuses.get(fname) is None: statuses[fname] = "🚧" print(f"Pending status added for {fname}") changed = True if changed: save_statuses(statuses) else: print("No missing statuses found.") def remove_missing_statuses() -> None: """Remove entries from statuses.json where the corresponding file no longer exists.""" statuses = load_statuses() existing = set(iter_json_script_files()) removed_any = False for fname in list(statuses.keys()): if fname in EXCLUDE_FILES: continue if fname not in existing: statuses.pop(fname, None) print(f"Removed status for missing script: {fname}") removed_any = True if removed_any: save_statuses(statuses) else: print("No missing scripts found in statuses.json.") def replace_build_func_url() -> None: """Replace build.func URL in ./ct files.""" old_url = "https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func" new_url = "https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func" ct_dir = REPO_ROOT / "ct" print("Checking and replacing build.func URLs...") updated = 0 for path in ct_dir.rglob("*"): if not path.is_file(): continue # most of these are .sh, but avoid missing any with no suffix if path.name.startswith("."): continue try: content = path.read_text(encoding="utf-8") except UnicodeDecodeError: continue if old_url in content: path.write_text(content.replace(old_url, new_url), encoding="utf-8") updated += 1 print(f"Updated: {path.relative_to(REPO_ROOT)}") print(f"build.func URL updates: {updated}") def update_license_url() -> None: """Replace old license header URL with this fork's license URL.""" old = "# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE" new = "# License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE" print("Checking and updating license headers...") updated = 0 for path in REPO_ROOT.rglob("*"): if not path.is_file(): continue if path.suffix not in {".sh", ".func"}: continue try: content = path.read_text(encoding="utf-8") except UnicodeDecodeError: continue if old in content: path.write_text(content.replace(old, new), encoding="utf-8") updated += 1 print(f"Updated license in {path.relative_to(REPO_ROOT)}") print(f"License header updates: {updated}") def search_externally_managed() -> None: """Search ./install for EXTERNALLY-MANAGED removal + setup_uv.""" target_dir = REPO_ROOT / "install" pattern = re.compile(r"rm -rf /usr/lib/python3\.\*/EXTERNALLY-MANAGED") for path in target_dir.rglob("*"): if not path.is_file(): continue try: content = path.read_text(encoding="utf-8") except (UnicodeDecodeError, PermissionError): continue if pattern.search(content) and "setup_uv" in content: print(path.relative_to(REPO_ROOT)) def post_merge_maintenance() -> None: """Run the standard maintenance pass after an upstream merge.""" add_pending_status() remove_missing_statuses() update_license_url() replace_build_func_url() def build_parser() -> argparse.ArgumentParser: p = argparse.ArgumentParser(description="Proxmox fork maintenance helpers") sub = p.add_subparsers(dest="cmd", required=True) sub.add_parser("post-merge", help="Run the standard post-merge maintenance pass") sub.add_parser("add-pending-status", help="Set 🚧 for scripts missing a status") sub.add_parser("remove-missing-statuses", help="Remove statuses for scripts that no longer exist") sub.add_parser("update-license-url", help="Update license header URL in scripts") sub.add_parser("replace-build-func-url", help="Replace build.func URL in ct scripts") sub.add_parser("search-externally-managed", help="Search install/ for EXTERNALLY-MANAGED with setup_uv") return p def main() -> int: args = build_parser().parse_args() if args.cmd == "post-merge": post_merge_maintenance() elif args.cmd == "add-pending-status": add_pending_status() elif args.cmd == "remove-missing-statuses": remove_missing_statuses() elif args.cmd == "update-license-url": update_license_url() elif args.cmd == "replace-build-func-url": replace_build_func_url() elif args.cmd == "search-externally-managed": search_externally_managed() return 0 if __name__ == "__main__": raise SystemExit(main()) ================================================ FILE: tools/pve/add-iptag.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (Canbiz) && Desert_Gamer # License: MIT function header_info { clear cat <<"EOF" ___ ____ _____ |_ _| _ \ _ |_ _|_ _ __ _ | || |_) (_) | |/ _` |/ _` | | || __/ _ | | (_| | (_| | |___|_| (_) |_|\__,_|\__, | |___/ EOF } clear header_info APP="IP-Tag" hostname=$(hostname) # Color variables YW=$(echo "\033[33m") GN=$(echo "\033[1;92m") RD=$(echo "\033[01;31m") CL=$(echo "\033[m") BFR="\\r\\033[K" HOLD=" " CM="${GN}✓${CL} " CROSS="${RD}✗${CL} " # Telemetry source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func) 2>/dev/null || true declare -f init_tool_telemetry &>/dev/null && init_tool_telemetry "add-iptag" "pve" # Stop any running spinner stop_spinner() { if [ -n "$SPINNER_PID" ] && kill -0 "$SPINNER_PID" 2>/dev/null; then kill -TERM "$SPINNER_PID" 2>/dev/null wait "$SPINNER_PID" 2>/dev/null fi SPINNER_PID="" printf "\e[?25h\r" } # Error handler for displaying error messages error_handler() { stop_spinner local exit_code="$?" local line_number="$1" local command="$2" local error_message="${RD}[ERROR]${CL} in line ${RD}$line_number${CL}: exit code ${RD}$exit_code${CL}: while executing command ${YW}$command${CL}" echo -e "\n$error_message\n" } # Spinner for progress indication spinner() { local msg="$1" local frames=('⠋' '⠙' '⠹' '⠸' '⠼' '⠴' '⠦' '⠧' '⠇' '⠏') local spin_i=0 local interval=0.1 trap 'exit 0' TERM INT printf "\e[?25l" 2>/dev/null while true; do printf "\r%s ${YW}%s${CL}" "${frames[spin_i]}" "$msg" 2>/dev/null || exit 0 spin_i=$(((spin_i + 1) % ${#frames[@]})) sleep "$interval" || exit 0 done } # Info message msg_info() { local msg="$1" stop_spinner spinner "$msg" & SPINNER_PID=$! disown $SPINNER_PID 2>/dev/null } # Success message msg_ok() { stop_spinner local msg="$1" echo -e "${BFR}${CM}${GN}${msg}${CL}" } # Error message msg_error() { stop_spinner local msg="$1" echo -e "${BFR}${CROSS}${RD}${msg}${CL}" } # Migrate configuration from old path to new migrate_config() { local old_config="/opt/lxc-iptag" local new_config="/opt/iptag/iptag.conf" if [[ -f "$old_config" ]]; then msg_info "Migrating configuration from old path" if cp "$old_config" "$new_config" &>/dev/null; then rm -rf "$old_config" &>/dev/null msg_ok "Configuration migrated and old config removed" else msg_error "Failed to migrate configuration" fi fi } # Update existing installation update_installation() { msg_info "Updating IP-Tag Scripts" systemctl stop iptag.service &>/dev/null msg_ok "Stopped IP-Tag service" # Create directory if it doesn't exist if [[ ! -d "/opt/iptag" ]]; then mkdir -p /opt/iptag fi # Create new config file (check if exists and ask user) if [[ -f "/opt/iptag/iptag.conf" ]]; then echo -e "\n${YW}Configuration file already exists.${CL}" echo -e "${YW}Note: No critical changes were made in this version.${CL}" while true; do read -p "Do you want to replace it with defaults? (y/n): " yn case $yn in [Yy]*) interactive_config_setup msg_info "Replacing configuration file" generate_config >/opt/iptag/iptag.conf msg_ok "Configuration file replaced with defaults" break ;; [Nn]*) echo -e "${GN}✓ Keeping existing configuration file${CL}" break ;; *) echo -e "${RD}Please answer yes or no.${CL}" ;; esac done else interactive_config_setup msg_info "Creating new configuration file" generate_config >/opt/iptag/iptag.conf msg_ok "Created new configuration file at /opt/iptag/iptag.conf" fi # Update main script msg_info "Updating main script" generate_main_script >/opt/iptag/iptag chmod +x /opt/iptag/iptag msg_ok "Updated main script" # Update service file msg_info "Updating service file" generate_service >/lib/systemd/system/iptag.service msg_ok "Updated service file" msg_info "Creating manual run command" cat <<'EOF' >/usr/local/bin/iptag-run #!/usr/bin/env bash CONFIG_FILE="/opt/iptag/iptag.conf" SCRIPT_FILE="/opt/iptag/iptag" if [[ ! -f "$SCRIPT_FILE" ]]; then echo "✗ Main script not found: $SCRIPT_FILE" exit 1 fi export FORCE_SINGLE_RUN=true exec "$SCRIPT_FILE" EOF chmod +x /usr/local/bin/iptag-run msg_ok "Created iptag-run executable - You can execute this manually by entering “iptag-run” in the Proxmox host, so the script is executed by hand." msg_info "Restarting service" systemctl daemon-reload &>/dev/null systemctl enable -q --now iptag.service &>/dev/null msg_ok "Updated IP-Tag Scripts" # Show configuration information after update show_post_install_info } # Install only command without service install_command_only() { msg_info "Installing IP-Tag Command Only" # Create directory if it doesn't exist if [[ ! -d "/opt/iptag" ]]; then mkdir -p /opt/iptag fi # Migrate config if needed migrate_config # Interactive configuration setup if [[ ! -f /opt/iptag/iptag.conf ]]; then interactive_config_setup_command msg_info "Setup Configuration" generate_config >/opt/iptag/iptag.conf msg_ok "Created configuration file at /opt/iptag/iptag.conf" else stop_spinner echo -e "\n${YW}Configuration file already exists.${CL}" read -p "Do you want to reconfigure tag format? (y/n): " reconfigure case $reconfigure in [Yy]*) interactive_config_setup_command msg_info "Updating Configuration" generate_config >/opt/iptag/iptag.conf msg_ok "Updated configuration file" ;; *) msg_ok "Keeping existing configuration file" ;; esac fi # Setup main script msg_info "Setup Main Script" generate_main_script >/opt/iptag/iptag chmod +x /opt/iptag/iptag msg_ok "Created main script" # Create manual run command msg_info "Creating iptag-run command" cat <<'EOF' >/usr/local/bin/iptag-run #!/usr/bin/env bash CONFIG_FILE="/opt/iptag/iptag.conf" SCRIPT_FILE="/opt/iptag/iptag" if [[ ! -f "$SCRIPT_FILE" ]]; then echo "✗ Main script not found: $SCRIPT_FILE" exit 1 fi export FORCE_SINGLE_RUN=true exec "$SCRIPT_FILE" EOF chmod +x /usr/local/bin/iptag-run msg_ok "Created iptag-run command" msg_ok "IP-Tag Command installed successfully! Use 'iptag-run' to run manually." } # Show post-installation information show_post_install_info() { stop_spinner echo -e "\n${YW}=== Next Steps ===${CL}" # Show usage information if command -v iptag-run >/dev/null 2>&1; then echo -e "${YW}Run IP tagging manually: ${GN}iptag-run${CL}" echo -e "${YW}Add to cron for scheduled execution if needed${CL}" echo -e "" fi echo -e "${RD}IMPORTANT: Configure your network subnets!${CL}" echo -e "" echo -e "${YW}Configuration file: ${GN}/opt/iptag/iptag.conf${CL}" echo -e "" echo -e "${YW}Edit CIDR_LIST with your actual subnets:${CL}" echo -e "${GN}nano /opt/iptag/iptag.conf${CL} ${YW}or${CL} ${GN}vim /opt/iptag/iptag.conf${CL}" echo -e "" echo -e "${YW}Example configuration:${CL}" echo -e "${GN}CIDR_LIST=(${CL}" echo -e "${GN} 192.168.1.0/24 # Your actual subnet${CL}" echo -e "${GN} 10.10.0.0/16 # Another subnet${CL}" echo -e "${GN})${CL}" echo -e "" } # Interactive configuration setup for command-only (TAG_FORMAT only) interactive_config_setup_command() { echo -e "\n${YW}=== Configuration Setup ===${CL}" # TAG_FORMAT configuration echo -e "\n${YW}Select tag format:${CL}" echo -e "${GN}1)${CL} last_two_octets - Show last two octets (e.g., 0.100) [Default]" echo -e "${GN}2)${CL} last_octet - Show only last octet (e.g., 100)" echo -e "${GN}3)${CL} full - Show full IP address (e.g., 192.168.0.100)" while true; do read -p "Enter your choice (1-3) [1]: " tag_choice case ${tag_choice:-1} in 1) TAG_FORMAT="last_two_octets" echo -e "${GN}✓ Selected: last_two_octets${CL}" break ;; 2) TAG_FORMAT="last_octet" echo -e "${GN}✓ Selected: last_octet${CL}" break ;; 3) TAG_FORMAT="full" echo -e "${GN}✓ Selected: full${CL}" break ;; *) echo -e "${RD}Please enter 1, 2, or 3.${CL}" ;; esac done # Set default LOOP_INTERVAL for command mode LOOP_INTERVAL=300 } # Interactive configuration setup for service (TAG_FORMAT + LOOP_INTERVAL) interactive_config_setup() { echo -e "\n${YW}=== Configuration Setup ===${CL}" # TAG_FORMAT configuration echo -e "\n${YW}Select tag format:${CL}" echo -e "${GN}1)${CL} last_two_octets - Show last two octets (e.g., 0.100) [Default]" echo -e "${GN}2)${CL} last_octet - Show only last octet (e.g., 100)" echo -e "${GN}3)${CL} full - Show full IP address (e.g., 192.168.0.100)" while true; do read -p "Enter your choice (1-3) [1]: " tag_choice case ${tag_choice:-1} in 1) TAG_FORMAT="last_two_octets" echo -e "${GN}✓ Selected: last_two_octets${CL}" break ;; 2) TAG_FORMAT="last_octet" echo -e "${GN}✓ Selected: last_octet${CL}" break ;; 3) TAG_FORMAT="full" echo -e "${GN}✓ Selected: full${CL}" break ;; *) echo -e "${RD}Please enter 1, 2, or 3.${CL}" ;; esac done # LOOP_INTERVAL configuration echo -e "\n${YW}Set check interval (in seconds):${CL}" echo -e "${YW}Default: 300 seconds (5 minutes)${CL}" echo -e "${YW}Recommended range: 300-3600 seconds${CL}" while true; do read -p "Enter interval in seconds [300]: " interval_input interval_input=${interval_input:-300} if [[ $interval_input =~ ^[0-9]+$ ]] && [ $interval_input -ge 300 ] && [ $interval_input -le 7200 ]; then LOOP_INTERVAL=$interval_input echo -e "${GN}✓ Selected: ${LOOP_INTERVAL} seconds${CL}" break else echo -e "${RD}Please enter a valid number between 300 and 7200 seconds.${CL}" fi done } # Generate configuration file content generate_config() { cat <&2 fi } # Color constants readonly RED='\033[0;31m' readonly GREEN='\033[0;32m' readonly YELLOW='\033[0;33m' readonly BLUE='\033[0;34m' readonly PURPLE='\033[0;35m' readonly CYAN='\033[0;36m' readonly WHITE='\033[1;37m' readonly GRAY='\033[0;37m' readonly NC='\033[0m' # No Color # Logging functions with colors log_success() { echo -e "${GREEN}✓${NC} $*" } log_info() { echo -e "${BLUE}ℹ${NC} $*" } log_warning() { echo -e "${YELLOW}⚠${NC} $*" } log_error() { echo -e "${RED}✗${NC} $*" } log_change() { echo -e "${CYAN}~${NC} $*" } log_unchanged() { echo -e "${GRAY}=${NC} $*" } # Check if IP is in CIDR ip_in_cidr() { local ip="$1" cidr="$2" debug_log "ip_in_cidr: checking '$ip' against '$cidr'" # Manual CIDR check - более надёжный метод debug_log "ip_in_cidr: using manual check (bypassing ipcalc)" local network prefix IFS='/' read -r network prefix <<< "$cidr" # Convert IP and network to integers for comparison local ip_int net_int mask IFS='.' read -r a b c d <<< "$ip" ip_int=$(( (a << 24) + (b << 16) + (c << 8) + d )) IFS='.' read -r a b c d <<< "$network" net_int=$(( (a << 24) + (b << 16) + (c << 8) + d )) # Create subnet mask mask=$(( 0xFFFFFFFF << (32 - prefix) )) # Apply mask and compare local ip_masked=$((ip_int & mask)) local net_masked=$((net_int & mask)) debug_log "ip_in_cidr: IP=$ip ($ip_int), Network=$network ($net_int), Prefix=$prefix" debug_log "ip_in_cidr: Mask=$mask (hex: $(printf '0x%08x' $mask))" debug_log "ip_in_cidr: IP&Mask=$ip_masked ($(printf '%d.%d.%d.%d' $((ip_masked>>24&255)) $((ip_masked>>16&255)) $((ip_masked>>8&255)) $((ip_masked&255))))" debug_log "ip_in_cidr: Net&Mask=$net_masked ($(printf '%d.%d.%d.%d' $((net_masked>>24&255)) $((net_masked>>16&255)) $((net_masked>>8&255)) $((net_masked&255))))" if (( ip_masked == net_masked )); then debug_log "ip_in_cidr: manual check PASSED - IP is in CIDR" return 0 else debug_log "ip_in_cidr: manual check FAILED - IP is NOT in CIDR" return 1 fi } # Format IP address according to the configuration format_ip_tag() { local ip="$1" [[ -z "$ip" ]] && return local format="${TAG_FORMAT:-$DEFAULT_TAG_FORMAT}" case "$format" in "last_octet") echo "${ip##*.}" ;; "last_two_octets") echo "${ip#*.*.}" ;; *) echo "$ip" ;; esac } # Check if IP is in any CIDRs ip_in_cidrs() { local ip="$1" cidrs="$2" [[ -z "$cidrs" ]] && return 1 local IFS=' ' debug_log "Checking IP '$ip' against CIDRs: '$cidrs'" for cidr in $cidrs; do debug_log "Testing IP '$ip' against CIDR '$cidr'" if ip_in_cidr "$ip" "$cidr"; then debug_log "IP '$ip' matches CIDR '$cidr' - PASSED" return 0 else debug_log "IP '$ip' does not match CIDR '$cidr'" fi done debug_log "IP '$ip' failed all CIDR checks" return 1 } # Check if IP is valid is_valid_ipv4() { local ip="$1" [[ "$ip" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]] || return 1 local IFS='.' parts read -ra parts <<< "$ip" for part in "${parts[@]}"; do (( part >= 0 && part <= 255 )) || return 1 done return 0 } # Get VM IPs using improved methods get_vm_ips() { local vmid=$1 ips="" local vm_config="/etc/pve/qemu-server/${vmid}.conf" [[ ! -f "$vm_config" ]] && return debug_log "vm $vmid: starting IP detection" # Check if VM is running first local vm_status="" if command -v qm >/dev/null 2>&1; then vm_status=$(qm status "$vmid" 2>/dev/null | awk '{print $2}') fi if [[ "$vm_status" != "running" ]]; then debug_log "vm $vmid: not running (status: $vm_status)" return fi # Get MAC addresses from config local mac_addresses=$(grep -E "^net[0-9]+:" "$vm_config" | grep -oE "([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}" | head -3) debug_log "vm $vmid: found MACs: $mac_addresses" # Method 1: QM guest agent (most reliable for current IP) if command -v qm >/dev/null 2>&1; then debug_log "vm $vmid: trying qm guest agent first" local qm_ips=$(timeout 8 qm guest cmd "$vmid" network-get-interfaces 2>/dev/null | grep -oE '([0-9]{1,3}\.){3}[0-9]{1,3}' | grep -v "127.0.0.1" | head -3) for qm_ip in $qm_ips; do if [[ "$qm_ip" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then debug_log "vm $vmid: found IP $qm_ip via qm guest cmd" ips+="$qm_ip " fi done fi # Method 2: Fresh ARP table lookup (force refresh) if [[ -n "$mac_addresses" ]]; then debug_log "vm $vmid: refreshing ARP table and checking" # Try to refresh ARP table by pinging network ranges for mac in $mac_addresses; do local mac_lower=$(echo "$mac" | tr '[:upper:]' '[:lower:]') # First check current ARP table local current_ip=$(ip neighbor show | grep "$mac_lower" | grep -oE '([0-9]{1,3}\.){3}[0-9]{1,3}' | head -1) # If found in ARP, verify it's still valid by trying to ping if [[ -n "$current_ip" && "$current_ip" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then debug_log "vm $vmid: found IP $current_ip in ARP table for MAC $mac_lower, verifying..." # Quick ping test to verify IP is still active if timeout 2 ping -c 1 "$current_ip" >/dev/null 2>&1; then debug_log "vm $vmid: verified IP $current_ip is active via ping" ips+="$current_ip " else debug_log "vm $vmid: IP $current_ip failed ping verification, removing from ARP" # Remove stale ARP entry ip neighbor del "$current_ip" dev $(ip route get "$current_ip" 2>/dev/null | grep -oE 'dev [^ ]+' | cut -d' ' -f2) 2>/dev/null || true fi fi done fi # Method 3: DHCP leases (backup method) if [[ -z "$ips" ]]; then debug_log "vm $vmid: checking DHCP leases as fallback" for mac in $mac_addresses; do local mac_lower=$(echo "$mac" | tr '[:upper:]' '[:lower:]') for dhcp_file in "/var/lib/dhcp/dhcpd.leases" "/var/lib/dhcpcd5/dhcpcd.leases"; do if [[ -f "$dhcp_file" ]]; then # Look for most recent lease for this MAC local dhcp_ip=$(tac "$dhcp_file" 2>/dev/null | grep -A 10 "ethernet $mac_lower" | grep "binding state active" -A 5 | grep -oE "([0-9]{1,3}\.){3}[0-9]{1,3}" | head -1) if [[ -n "$dhcp_ip" && "$dhcp_ip" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then debug_log "vm $vmid: found IP $dhcp_ip via DHCP leases" # Verify this IP responds if timeout 2 ping -c 1 "$dhcp_ip" >/dev/null 2>&1; then debug_log "vm $vmid: verified DHCP IP $dhcp_ip is active" ips+="$dhcp_ip " break 2 else debug_log "vm $vmid: DHCP IP $dhcp_ip failed ping test" fi fi fi done done fi # Remove duplicates and clean up local unique_ips=$(echo "$ips" | tr ' ' '\n' | sort -u | tr '\n' ' ') unique_ips="${unique_ips% }" debug_log "vm $vmid: final IPs: '$unique_ips'" echo "$unique_ips" } # Cache for configs to avoid repeated reads declare -A CONFIG_CACHE declare -A IP_CACHE # Update tags for container or VM update_tags() { local type="$1" vmid="$2" local current_ips_full local current_tags_raw="" # Get IPs with caching local cache_key="${type}_${vmid}" if [[ -n "${IP_CACHE[$cache_key]:-}" ]]; then current_ips_full="${IP_CACHE[$cache_key]}" debug_log "$type $vmid: using cached IPs" else if [[ "$type" == "lxc" ]]; then current_ips_full=$(get_lxc_ips "${vmid}") else current_ips_full=$(get_vm_ips "${vmid}") fi IP_CACHE[$cache_key]="$current_ips_full" fi # Get current tags (optimized file reading) if [[ "$type" == "lxc" ]]; then local config_file="/etc/pve/lxc/${vmid}.conf" if [[ -f "$config_file" ]]; then current_tags_raw=$(grep "^tags:" "$config_file" 2>/dev/null | cut -d: -f2 | sed 's/^[[:space:]]*//') fi else local vm_config="/etc/pve/qemu-server/${vmid}.conf" if [[ -f "$vm_config" ]]; then current_tags_raw=$(grep "^tags:" "$vm_config" 2>/dev/null | cut -d: -f2 | sed 's/^[[:space:]]*//') fi fi local current_tags=() next_tags=() current_ip_tags=() if [[ -n "$current_tags_raw" ]]; then mapfile -t current_tags < <(echo "$current_tags_raw" | sed 's/;/\n/g') fi # Separate IP/numeric and user tags for tag in "${current_tags[@]}"; do if is_valid_ipv4 "${tag}" || [[ "$tag" =~ ^[0-9]+(\.[0-9]+)*$ ]]; then current_ip_tags+=("${tag}") else next_tags+=("${tag}") fi done # Generate new IP tags from current IPs local formatted_ips=() debug_log "$type $vmid current_ips_full: '$current_ips_full'" debug_log "$type $vmid CIDR_LIST: ${CIDR_LIST[*]}" for ip in $current_ips_full; do [[ -z "$ip" ]] && continue debug_log "$type $vmid processing IP: '$ip'" if is_valid_ipv4 "$ip"; then debug_log "$type $vmid IP '$ip' is valid" if ip_in_cidrs "$ip" "${CIDR_LIST[*]}"; then debug_log "$type $vmid IP '$ip' passed CIDR check" local formatted_ip=$(format_ip_tag "$ip") debug_log "$type $vmid formatted '$ip' -> '$formatted_ip'" [[ -n "$formatted_ip" ]] && formatted_ips+=("$formatted_ip") else debug_log "$type $vmid IP '$ip' failed CIDR check" fi else debug_log "$type $vmid IP '$ip' is invalid" fi done debug_log "$type $vmid final formatted_ips: ${formatted_ips[*]}" # If LXC and no IPs detected, do not touch tags at all if [[ "$type" == "lxc" && ${#formatted_ips[@]} -eq 0 ]]; then log_unchanged "LXC ${GRAY}${vmid}${NC}: No IP detected, tags unchanged" return fi # Prepend new IP tags to the beginning of the tag list local final_tags=() for new_ip in "${formatted_ips[@]}"; do final_tags+=("$new_ip") done for tag in "${next_tags[@]}"; do final_tags+=("$tag") done next_tags=("${final_tags[@]}") # Update tags if there are changes local old_tags_str=$(IFS=';'; echo "${current_tags[*]}") local new_tags_str=$(IFS=';'; echo "${next_tags[*]}") debug_log "$type $vmid old_tags: '$old_tags_str'" debug_log "$type $vmid new_tags: '$new_tags_str'" debug_log "$type $vmid tags_equal: $([[ "$old_tags_str" == "$new_tags_str" ]] && echo true || echo false)" if [[ "$old_tags_str" != "$new_tags_str" ]]; then # Determine what changed local old_ip_tags_count=${#current_ip_tags[@]} local new_ip_tags_count=${#formatted_ips[@]} # Build detailed change message local change_details="" if [[ $old_ip_tags_count -eq 0 ]]; then change_details="added ${new_ip_tags_count} IP tag(s): [${GREEN}${formatted_ips[*]}${NC}]" else # Compare old and new IP tags local added_tags=() removed_tags=() common_tags=() # Find removed tags for old_tag in "${current_ip_tags[@]}"; do local found=false for new_tag in "${formatted_ips[@]}"; do if [[ "$old_tag" == "$new_tag" ]]; then found=true break fi done if [[ "$found" == false ]]; then removed_tags+=("$old_tag") else common_tags+=("$old_tag") fi done # Find added tags for new_tag in "${formatted_ips[@]}"; do local found=false for old_tag in "${current_ip_tags[@]}"; do if [[ "$new_tag" == "$old_tag" ]]; then found=true break fi done if [[ "$found" == false ]]; then added_tags+=("$new_tag") fi done # Build change message local change_parts=() if [[ ${#added_tags[@]} -gt 0 ]]; then change_parts+=("added [${GREEN}${added_tags[*]}${NC}]") fi if [[ ${#removed_tags[@]} -gt 0 ]]; then change_parts+=("removed [${YELLOW}${removed_tags[*]}${NC}]") fi if [[ ${#common_tags[@]} -gt 0 ]]; then change_parts+=("kept [${GRAY}${common_tags[*]}${NC}]") fi change_details=$(IFS=', '; echo "${change_parts[*]}") fi log_change "${type^^} ${CYAN}${vmid}${NC}: ${change_details}" if [[ "$type" == "lxc" ]]; then pct set "${vmid}" -tags "$(IFS=';'; echo "${next_tags[*]}")" &>/dev/null else local vm_config="/etc/pve/qemu-server/${vmid}.conf" if [[ -f "$vm_config" ]]; then sed -i '/^tags:/d' "$vm_config" if [[ ${#next_tags[@]} -gt 0 ]]; then echo "tags: $(IFS=';'; echo "${next_tags[*]}")" >> "$vm_config" fi fi fi else # Tags unchanged local ip_count=${#formatted_ips[@]} local status_msg="" if [[ $ip_count -eq 0 ]]; then status_msg="No IPs detected" elif [[ $ip_count -eq 1 ]]; then status_msg="IP tag [${GRAY}${formatted_ips[0]}${NC}] unchanged" else status_msg="${ip_count} IP tags [${GRAY}${formatted_ips[*]}${NC}] unchanged" fi log_unchanged "${type^^} ${GRAY}${vmid}${NC}: ${status_msg}" fi } # Update all instances of specified type update_all_tags() { local type="$1" vmids count=0 # Get list of all containers/VMs if [[ "$type" == "lxc" ]]; then vmids=($(pct list 2>/dev/null | grep -v VMID | awk '{print $1}')) else # More efficient: direct file listing instead of ls+sed vmids=() for conf in /etc/pve/qemu-server/*.conf; do [[ -f "$conf" ]] || continue local basename="${conf##*/}" vmids+=("${basename%.conf}") done fi count=${#vmids[@]} [[ $count -eq 0 ]] && return # Display processing header with color if [[ "$type" == "lxc" ]]; then log_info "Processing ${WHITE}${count}${NC} LXC container(s)" else log_info "Processing ${WHITE}${count}${NC} virtual machine(s)" fi # Process each VM/LXC container local processed=0 for vmid in "${vmids[@]}"; do update_tags "$type" "$vmid" ((processed++)) done # Add completion message if [[ "$type" == "lxc" ]]; then log_success "Completed processing LXC containers" else log_success "Completed processing virtual machines" fi } # Main check function check() { local start_time=$(date +%s) log_info "Starting periodic check" # Clear caches before each run CONFIG_CACHE=() IP_CACHE=() # Update LXC containers update_all_tags "lxc" # Update VMs update_all_tags "vm" local end_time=$(date +%s) local duration=$((end_time - start_time)) log_success "Check completed in ${WHITE}${duration}${NC} seconds" } # Main loop main() { # Display startup message echo -e "\n${PURPLE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" log_success "IP-Tag service started successfully" echo -e "${BLUE}ℹ${NC} Loop interval: ${WHITE}${LOOP_INTERVAL:-300}${NC} seconds" echo -e "${BLUE}ℹ${NC} Debug mode: ${WHITE}${DEBUG:-false}${NC}" echo -e "${BLUE}ℹ${NC} Tag format: ${WHITE}${TAG_FORMAT:-full}${NC}" echo -e "${BLUE}ℹ${NC} Allowed CIDRs: ${WHITE}${CIDR_LIST[*]}${NC}" echo -e "${PURPLE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}\n" if [[ "$FORCE_SINGLE_RUN" == "true" ]]; then check exit 0 fi while true; do check sleep "${LOOP_INTERVAL:-300}" done } # Simple LXC IP detection get_lxc_ips() { local vmid=$1 debug_log "lxc $vmid: starting IP detection" # Check if LXC is running local lxc_status=$(pct status "${vmid}" 2>/dev/null | awk '{print $2}') if [[ "$lxc_status" != "running" ]]; then debug_log "lxc $vmid: not running (status: $lxc_status)" return fi local ips="" # Method 1: Check Proxmox config for ALL static IPs (multiple interfaces) local pve_lxc_config="/etc/pve/lxc/${vmid}.conf" if [[ -f "$pve_lxc_config" ]]; then local static_ips=$(grep -E "^net[0-9]+:" "$pve_lxc_config" 2>/dev/null | grep -oE 'ip=([0-9]{1,3}\.){3}[0-9]{1,3}' | cut -d'=' -f2) if [[ -n "$static_ips" ]]; then while IFS= read -r ip; do if [[ "$ip" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then debug_log "lxc $vmid: found static IP $ip in config" ips="${ips}${ips:+ }${ip}" fi done <<< "$static_ips" fi fi # Method 2: ARP table lookup for ALL MAC addresses if no static IPs found if [[ -z "$ips" && -f "$pve_lxc_config" ]]; then local mac_addrs=$(grep -Eo 'hwaddr=([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}' "$pve_lxc_config" | cut -d'=' -f2) if [[ -n "$mac_addrs" ]]; then while IFS= read -r mac_addr; do [[ -z "$mac_addr" ]] && continue local arp_ip=$(ip neighbor show | grep -i "$mac_addr" | grep -oE '([0-9]{1,3}\.){3}[0-9]{1,3}' | head -1) if [[ -n "$arp_ip" && "$arp_ip" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then debug_log "lxc $vmid: found IP $arp_ip via ARP table for MAC $mac_addr" ips="${ips}${ips:+ }${arp_ip}" fi done <<< "$mac_addrs" fi fi # Method 3: Direct container command to get ALL IPs if previous methods failed if [[ -z "$ips" ]]; then local container_ips=$(timeout 5s pct exec "$vmid" -- ip -4 addr show 2>/dev/null | grep -oE 'inet ([0-9]{1,3}\.){3}[0-9]{1,3}' | awk '{print $2}' | grep -v '127.0.0.1') if [[ -n "$container_ips" ]]; then while IFS= read -r ip; do if is_valid_ipv4 "$ip"; then debug_log "lxc $vmid: found IP $ip via pct exec" ips="${ips}${ips:+ }${ip}" fi done <<< "$container_ips" fi fi # Remove duplicates and clean up local unique_ips=$(echo "$ips" | tr ' ' '\n' | sort -u | tr '\n' ' ') unique_ips="${unique_ips% }" debug_log "lxc $vmid: final IPs: '$unique_ips'" echo "$unique_ips" } main EOF } # Choose installation/update mode echo -e "\n${YW}Choose action:${CL}" echo -e "${GN}1)${CL} Install with automatic service (recommended)" echo -e "${GN}2)${CL} Install command only (manual execution)" echo -e "${GN}3)${CL} Update existing installation" echo -e "${RD}4)${CL} Cancel" while true; do read -p "Enter your choice (1-4): " choice case $choice in 1) INSTALL_MODE="service" echo -e "${GN}✓ Selected: Service installation${CL}" break ;; 2) INSTALL_MODE="command" echo -e "${GN}✓ Selected: Command-only installation${CL}" break ;; 3) echo -e "${GN}✓ Selected: Update installation${CL}" update_installation exit 0 ;; 4) msg_error "Action cancelled." exit 0 ;; *) msg_error "Please enter 1, 2, 3, or 4." ;; esac done echo -e "\n${YW}This will install ${APP} on ${hostname} in $INSTALL_MODE mode.${CL}" while true; do read -p "Proceed? (y/n): " yn case $yn in [Yy]*) break ;; [Nn]*) msg_error "Installation cancelled." exit ;; *) msg_error "Please answer yes or no." ;; esac done if ! pveversion | grep -Eq "pve-manager/(8\.[0-4]|9\.[0-9]+)(\.[0-9]+)*"; then msg_error "This version of Proxmox Virtual Environment is not supported" msg_error "⚠ Requires Proxmox Virtual Environment Version 8.0–8.4 or 9.x." msg_error "Exiting..." sleep 2 exit fi msg_info "Installing Dependencies" apt-get update &>/dev/null apt-get install -y ipcalc net-tools &>/dev/null msg_ok "Installed Dependencies" # Execute installation based on selected mode if [[ "$INSTALL_MODE" == "service" ]]; then # Full service installation msg_info "Setting up IP-Tag Scripts" mkdir -p /opt/iptag msg_ok "Setup IP-Tag Scripts" # Migrate config if needed migrate_config # Interactive configuration setup if [[ ! -f /opt/iptag/iptag.conf ]]; then interactive_config_setup msg_info "Setup Default Config" generate_config >/opt/iptag/iptag.conf msg_ok "Setup default config" else stop_spinner echo -e "\n${YW}Configuration file already exists.${CL}" read -p "Do you want to reconfigure tag format and loop interval? (y/n): " reconfigure case $reconfigure in [Yy]*) interactive_config_setup msg_info "Updating Configuration" generate_config >/opt/iptag/iptag.conf msg_ok "Updated configuration file" ;; *) msg_ok "Keeping existing configuration file" ;; esac fi msg_info "Setup Main Function" generate_main_script >/opt/iptag/iptag chmod +x /opt/iptag/iptag msg_ok "Setup Main Function" msg_info "Creating Service" generate_service >/lib/systemd/system/iptag.service msg_ok "Created Service" msg_info "Starting Service" systemctl daemon-reload &>/dev/null systemctl enable -q --now iptag.service &>/dev/null msg_ok "Started Service" msg_info "Creating manual run command" cat <<'EOF' >/usr/local/bin/iptag-run #!/usr/bin/env bash CONFIG_FILE="/opt/iptag/iptag.conf" SCRIPT_FILE="/opt/iptag/iptag" if [[ ! -f "$SCRIPT_FILE" ]]; then echo "✗ Main script not found: $SCRIPT_FILE" exit 1 fi export FORCE_SINGLE_RUN=true exec "$SCRIPT_FILE" EOF chmod +x /usr/local/bin/iptag-run msg_ok "Created iptag-run command" echo -e "\n${GN}${APP} service installation completed successfully! ${CL}" echo -e "${YW}The service is now running automatically.${CL}" echo -e "${YW}You can also run it manually with: ${GN}iptag-run${CL}\n" # Show configuration information show_post_install_info elif [[ "$INSTALL_MODE" == "command" ]]; then # Command-only installation install_command_only stop_spinner echo -e "\n${GN}${APP} command installation completed successfully! ${CL}" # Show configuration information show_post_install_info fi # Clean up any running spinner and exit stop_spinner exit 0 ================================================ FILE: tools/pve/clean-lxcs.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # License: MIT # https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE function header_info() { clear cat <<"EOF" ________ __ _ ________ / ____/ /__ ____ _____ / / | |/ / ____/ / / / / _ \/ __ `/ __ \ / / | / / / /___/ / __/ /_/ / / / / / /___/ / /___ \____/_/\___/\__,_/_/ /_/ /_____/_/|_\____/ EOF } set -eEuo pipefail BL="\033[36m" RD="\033[01;31m" CM='\xE2\x9C\x94\033' GN="\033[1;92m" CL="\033[m" # Telemetry source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func) 2>/dev/null || true declare -f init_tool_telemetry &>/dev/null && init_tool_telemetry "clean-lxcs" "pve" header_info echo "Loading..." whiptail --backtitle "Proxmox VE Helper Scripts" --title "Proxmox VE LXC Updater" --yesno "This will clean logs, cache and update package lists on selected LXC Containers. Proceed?" 10 58 NODE=$(hostname) EXCLUDE_MENU=() MSG_MAX_LENGTH=0 while read -r TAG ITEM; do OFFSET=2 ((${#ITEM} + OFFSET > MSG_MAX_LENGTH)) && MSG_MAX_LENGTH=${#ITEM}+OFFSET EXCLUDE_MENU+=("$TAG" "$ITEM " "OFF") done < <(pct list | awk 'NR>1') excluded_containers=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "Containers on $NODE" --checklist "\nSelect containers to skip from cleaning:\n" \ 16 $((MSG_MAX_LENGTH + 23)) 6 "${EXCLUDE_MENU[@]}" 3>&1 1>&2 2>&3 | tr -d '"') if [ $? -ne 0 ]; then exit fi function run_lxc_clean() { local container=$1 header_info name=$(pct exec "$container" hostname) pct exec "$container" -- bash -c ' BL="\033[36m"; GN="\033[1;92m"; CL="\033[m" name=$(hostname) if [ -e /etc/alpine-release ]; then echo -e "${BL}[Info]${GN} Cleaning $name (Alpine)${CL}\n" apk cache clean find /var/log -type f -delete 2>/dev/null find /tmp -mindepth 1 -delete 2>/dev/null apk update elif [ -e /etc/redhat-release ]; then echo -e "${BL}[Info]${GN} Cleaning $name (CentOS)${CL}\n" yum clean all find /var/log -type f -delete 2>/dev/null find /tmp -mindepth 1 -delete 2>/dev/null yum update yum upgrade -y else echo -e "${BL}[Info]${GN} Cleaning $name (Debian/Ubuntu)${CL}\n" find /var/cache -type f -delete 2>/dev/null find /var/log -type f -delete 2>/dev/null find /tmp -mindepth 1 -delete 2>/dev/null apt -y --purge autoremove apt -y autoclean rm -rf /var/lib/apt/lists/* apt update fi ' } for container in $(pct list | awk '{if(NR>1) print $1}'); do if [[ " ${excluded_containers[@]} " =~ " $container " ]]; then header_info echo -e "${BL}[Info]${GN} Skipping ${BL}$container${CL}" sleep 1 continue fi os=$(pct config "$container" | awk '/^ostype/ {print $2}') # Supported: debian, ubuntu, alpine, centos if [ "$os" != "debian" ] && [ "$os" != "ubuntu" ] && [ "$os" != "alpine" ] && [ "$os" != "centos" ]; then header_info echo -e "${BL}[Info]${GN} Skipping ${RD}$container is not Debian, Ubuntu, Alpine or Red Hat Compatible${CL} \n" sleep 1 continue fi status=$(pct status "$container") template=$(pct config "$container" | grep -q "template:" && echo "true" || echo "false") if [ "$template" == "false" ] && [ "$status" == "status: stopped" ]; then echo -e "${BL}[Info]${GN} Starting${BL} $container ${CL} \n" pct start "$container" echo -e "${BL}[Info]${GN} Waiting For${BL} $container${CL}${GN} To Start ${CL} \n" sleep 5 run_lxc_clean "$container" echo -e "${BL}[Info]${GN} Shutting down${BL} $container ${CL} \n" pct shutdown "$container" & elif [ "$status" == "status: running" ]; then run_lxc_clean "$container" fi done wait header_info echo -e "${GN} Finished, Selected Containers Cleaned. ${CL} \n" ================================================ FILE: tools/pve/clean-orphaned-lvm.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE function header_info { clear cat <<"EOF" ____ ________ ____ __ __ __ _ ____ ___ / __ \_________ _ ______ ___ ____ _ __ / ____/ /__ ____ _____ / __ \_________ / /_ ____ _____ ___ ____/ / / /| | / / |/ /____ / /_/ / ___/ __ \| |/_/ __ `__ \/ __ \| |/_/ / / / / _ \/ __ `/ __ \ / / / / ___/ __ \/ __ \/ __ `/ __ \/ _ \/ __ / / / | | / / /|_/ / ___/ / ____/ / / /_/ /> < / /___/ / __/ /_/ / / / / / /_/ / / / /_/ / / / / /_/ / / / / __/ /_/ / / /__| |/ / / / (__ ) /_/ /_/ \____/_/|_/_/ /_/ /_/\____/_/|_| \____/_/\___/\__,_/_/ /_/ \____/_/ / .___/_/ /_/\__,_/_/ /_/\___/\__,_/ /_____/___/_/ /_/____/ /_/ EOF } # Telemetry source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func) 2>/dev/null || true declare -f init_tool_telemetry &>/dev/null && init_tool_telemetry "clean-orphaned-lvm" "pve" # Function to check for orphaned LVM volumes function find_orphaned_lvm { echo -e "\n🔍 Scanning for orphaned LVM volumes...\n" orphaned_volumes=() while read -r lv vg size seg_type; do # Exclude system-critical LVs and Ceph OSDs if [[ "$lv" == "data" || "$lv" == "root" || "$lv" == "swap" || "$lv" =~ ^osd-block- ]]; then continue fi # Exclude thin pools (any name) if [[ "$seg_type" == "thin-pool" ]]; then continue fi container_id=$(echo "$lv" | grep -oE "[0-9]+" | head -1) # Check if the ID exists as a VM or LXC container on any cluster node if compgen -G "/etc/pve/nodes/*/lxc/${container_id}.conf" >/dev/null 2>&1 || compgen -G "/etc/pve/nodes/*/qemu-server/${container_id}.conf" >/dev/null 2>&1; then continue fi orphaned_volumes+=("$lv" "$vg" "$size") done < <(lvs --noheadings -o lv_name,vg_name,lv_size,seg_type --separator ' ' 2>/dev/null | awk '{print $1, $2, $3, $4}') # Display orphaned volumes echo -e "❗ The following orphaned LVM volumes were found:\n" printf "%-25s %-10s %-10s\n" "LV Name" "VG" "Size" printf "%-25s %-10s %-10s\n" "-------------------------" "----------" "----------" for ((i = 0; i < ${#orphaned_volumes[@]}; i += 3)); do printf "%-25s %-10s %-10s\n" "${orphaned_volumes[i]}" "${orphaned_volumes[i + 1]}" "${orphaned_volumes[i + 2]}" done echo "" } # Function to delete selected volumes function delete_orphaned_lvm { for ((i = 0; i < ${#orphaned_volumes[@]}; i += 3)); do lv="${orphaned_volumes[i]}" vg="${orphaned_volumes[i + 1]}" size="${orphaned_volumes[i + 2]}" read -p "❓ Do you want to delete $lv (VG: $vg, Size: $size)? [y/N]: " confirm if [[ "$confirm" =~ ^[Yy]$ ]]; then echo -e "🗑️ Deleting $lv from $vg..." lvremove -f "$vg/$lv" if [ $? -eq 0 ]; then echo -e "✅ Successfully deleted $lv.\n" else echo -e "❌ Failed to delete $lv.\n" fi else echo -e "⚠️ Skipping $lv.\n" fi done } # Run script header_info find_orphaned_lvm delete_orphaned_lvm echo -e "✅ Cleanup process completed!\n" ================================================ FILE: tools/pve/container-restore-from-backup.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT # https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE clear if command -v pveversion >/dev/null 2>&1; then echo -e "⚠️ Can't Run from the Proxmox Shell" exit fi YW=$(echo "\033[33m") BL=$(echo "\033[36m") RD=$(echo "\033[01;31m") BGN=$(echo "\033[4;92m") GN=$(echo "\033[1;92m") DGN=$(echo "\033[32m") CL=$(echo "\033[m") BFR="\\r\\033[K" HOLD="-" CM="${GN}✓${CL}" CROSS="${RD}✗${CL}" APP="Home Assistant Container" while true; do read -p "This will restore ${APP} from a backup. Proceed(y/n)?" yn case $yn in [Yy]*) break ;; [Nn]*) exit ;; *) echo "Please answer yes or no." ;; esac done clear function header_info { cat <<"EOF" __ __ ___ _ __ __ / / / /___ ____ ___ ___ / | __________(_)____/ /_____ _____ / /_ / /_/ / __ \/ __ `__ \/ _ \ / /| | / ___/ ___/ / ___/ __/ __ `/ __ \/ __/ / __ / /_/ / / / / / / __/ / ___ |(__ |__ ) (__ ) /_/ /_/ / / / / /_ /_/ /_/\____/_/ /_/ /_/\___/ /_/ |_/____/____/_/____/\__/\__,_/_/ /_/\__/ RESTORE FROM BACKUP EOF } header_info # Telemetry source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func) 2>/dev/null || true declare -f init_tool_telemetry &>/dev/null && init_tool_telemetry "container-restore" "pve" function msg_info() { local msg="$1" echo -ne " ${HOLD} ${YW}${msg}..." } function msg_ok() { local msg="$1" echo -e "${BFR} ${CM} ${GN}${msg}${CL}" } function msg_error() { local msg="$1" echo -e "${BFR} ${CROSS} ${RD}${msg}${CL}" } if [ -z "$(ls -A /var/lib/docker/volumes/hass_config/_data/backups/)" ]; then msg_error "No backups found! \n" exit 235 fi DIR=/var/lib/docker/volumes/hass_config/_data/restore if [ -d "$DIR" ]; then msg_ok "Restore Directory Exists." else mkdir -p /var/lib/docker/volumes/hass_config/_data/restore msg_ok "Created Restore Directory." fi cd /var/lib/docker/volumes/hass_config/_data/backups/ PS3="Please enter your choice: " files="$(ls -A .)" select filename in ${files}; do msg_ok "You selected ${BL}${filename}${CL}" break done msg_info "Stopping Home Assistant" docker stop homeassistant &>/dev/null msg_ok "Stopped Home Assistant" msg_info "Restoring Home Assistant using ${filename}" tar xvf ${filename} -C /var/lib/docker/volumes/hass_config/_data/restore &>/dev/null cd /var/lib/docker/volumes/hass_config/_data/restore tar -xvf homeassistant.tar.gz &>/dev/null if ! command -v rsync >/dev/null 2>&1; then apt-get install -y rsync &>/dev/null; fi rsync -a /var/lib/docker/volumes/hass_config/_data/restore/data/ /var/lib/docker/volumes/hass_config/_data rm -rf /var/lib/docker/volumes/hass_config/_data/restore/* msg_ok "Restore Complete" msg_ok "Starting Home Assistant \n" docker start homeassistant ================================================ FILE: tools/pve/core-restore-from-backup.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT # https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE clear if command -v pveversion >/dev/null 2>&1; then echo -e "⚠️ Can't Run from the Proxmox Shell" exit fi YW=$(echo "\033[33m") BL=$(echo "\033[36m") RD=$(echo "\033[01;31m") BGN=$(echo "\033[4;92m") GN=$(echo "\033[1;92m") DGN=$(echo "\033[32m") CL=$(echo "\033[m") BFR="\\r\\033[K" HOLD="-" CM="${GN}✓${CL}" CROSS="${RD}✗${CL}" APP="Home Assistant Core" while true; do read -p "This will restore ${APP} from a backup. Proceed(y/n)?" yn case $yn in [Yy]*) break ;; [Nn]*) exit ;; *) echo "Please answer yes or no." ;; esac done clear function header_info { cat <<"EOF" __ __ ___ _ __ __ ______ / / / /___ ____ ___ ___ / | __________(_)____/ /_____ _____ / /_ / ____/___ ________ / /_/ / __ \/ __ `__ \/ _ \ / /| | / ___/ ___/ / ___/ __/ __ `/ __ \/ __/ / / / __ \/ ___/ _ \ / __ / /_/ / / / / / / __/ / ___ |(__ |__ ) (__ ) /_/ /_/ / / / / /_ / /___/ /_/ / / / __/ /_/ /_/\____/_/ /_/ /_/\___/ /_/ |_/____/____/_/____/\__/\__,_/_/ /_/\__/ \____/\____/_/ \___/ RESTORE FROM BACKUP EOF } header_info # Telemetry source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func) 2>/dev/null || true declare -f init_tool_telemetry &>/dev/null && init_tool_telemetry "core-restore" "pve" function msg_info() { local msg="$1" echo -ne " ${HOLD} ${YW}${msg}..." } function msg_ok() { local msg="$1" echo -e "${BFR} ${CM} ${GN}${msg}${CL}" } function msg_error() { local msg="$1" echo -e "${BFR} ${CROSS} ${RD}${msg}${CL}" } if [ -z "$(ls -A /root/.homeassistant/backups/)" ]; then msg_error "No backups found! \n" exit 235 fi DIR=/root/.homeassistant/restore if [ -d "$DIR" ]; then msg_ok "Restore Directory Exists." else mkdir -p /root/.homeassistant/restore msg_ok "Created Restore Directory." fi cd /root/.homeassistant/backups/ PS3="Please enter your choice: " files="$(ls -A .)" select filename in ${files}; do msg_ok "You selected ${BL}${filename}${CL}" break done msg_info "Stopping Home Assistant" sudo service homeassistant stop msg_ok "Stopped Home Assistant" msg_info "Restoring Home Assistant using ${filename}" tar xvf ${filename} -C /root/.homeassistant/restore &>/dev/null cd /root/.homeassistant/restore tar -xvf homeassistant.tar.gz &>/dev/null if ! command -v rsync >/dev/null 2>&1; then apt-get install -y rsync &>/dev/null; fi rsync -a /root/.homeassistant/restore/data/ /root/.homeassistant rm -rf /root/.homeassistant/restore/* msg_ok "Restore Complete" msg_ok "Starting Home Assistant \n" sudo service homeassistant start ================================================ FILE: tools/pve/cron-update-lxcs.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # # This script manages a local cron job for automatic LXC container OS updates. # The update script is downloaded once, displayed for review, and installed # locally. Cron runs the local copy — no remote code execution at runtime. # # bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/tools/pve/cron-update-lxcs.sh)" set -euo pipefail REPO_URL="https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main" SCRIPT_URL="${REPO_URL}/tools/pve/update-lxcs-cron.sh" LOCAL_SCRIPT="/usr/local/bin/update-lxcs.sh" CONF_FILE="/etc/update-lxcs.conf" LOG_FILE="/var/log/update-lxcs-cron.log" CRON_ENTRY="0 0 * * 0 ${LOCAL_SCRIPT} >>${LOG_FILE} 2>&1" clear cat <<"EOF" ______ __ __ __ __ __ _ ________ / ____/________ ____ / / / /___ ____/ /___ _/ /____ / / | |/ / ____/____ / / / ___/ __ \/ __ \ / / / / __ \/ __ / __ `/ __/ _ \ / / | / / / ___/ / /___/ / / /_/ / / / / / /_/ / /_/ / /_/ / /_/ / /_/ __/ / /___/ / /___(__ ) \____/_/ \____/_/ /_/ \____/ .___/\__,_/\__,_/\__/\___/ /_____/_/|_\____/____/ /_/ EOF info() { echo -e "\n \e[36m[Info]\e[0m $1"; } ok() { echo -e " \e[32m[OK]\e[0m $1"; } err() { echo -e " \e[31m[Error]\e[0m $1" >&2; } confirm() { local prompt="${1:-Proceed?}" while true; do read -rp " ${prompt} (y/n): " yn case $yn in [Yy]*) return 0 ;; [Nn]*) return 1 ;; *) echo " Please answer yes or no." ;; esac done } download_script() { local tmp tmp=$(mktemp) if ! curl -fsSL -o "$tmp" "$SCRIPT_URL"; then err "Failed to download script from:\n ${SCRIPT_URL}" rm -f "$tmp" return 1 fi echo "$tmp" } review_script() { local file="$1" local hash hash=$(sha256sum "$file" | awk '{print $1}') echo "" echo -e " \e[1;33m─── Script Content ───────────────────────────────────────────\e[0m" cat "$file" echo -e " \e[1;33m──────────────────────────────────────────────────────────────\e[0m" echo -e " \e[36mSHA256:\e[0m ${hash}" echo -e " \e[36mSource:\e[0m ${SCRIPT_URL}" echo "" } remove_legacy_cron() { if crontab -l -u root 2>/dev/null | grep -q "update-lxcs-cron.sh"; then (crontab -l -u root 2>/dev/null | grep -v "update-lxcs-cron.sh") | crontab -u root - ok "Removed legacy curl-based cron entry" fi } add() { info "Downloading update script..." local tmp tmp=$(download_script) || exit 1 local hash hash=$(sha256sum "$tmp" | awk '{print $1}') echo "" echo -e " \e[1;33m─── Installation Summary ─────────────────────────────────────\e[0m" echo -e " \e[36mSource:\e[0m ${SCRIPT_URL}" echo -e " \e[36mSHA256:\e[0m ${hash}" echo -e " \e[36mInstall to:\e[0m ${LOCAL_SCRIPT}" echo -e " \e[36mConfig:\e[0m ${CONF_FILE}" echo -e " \e[36mLog file:\e[0m ${LOG_FILE}" echo -e " \e[36mCron schedule:\e[0m Every Sunday at midnight (0 0 * * 0)" echo -e " \e[1;33m──────────────────────────────────────────────────────────────\e[0m" echo "" if confirm "Review script content before installing?"; then review_script "$tmp" fi if ! confirm "Install this script and activate cron schedule?"; then rm -f "$tmp" echo " Aborted." exit 0 fi remove_legacy_cron install -m 0755 "$tmp" "$LOCAL_SCRIPT" rm -f "$tmp" ok "Installed script to ${LOCAL_SCRIPT}" if [[ ! -f "$CONF_FILE" ]]; then cat >"$CONF_FILE" <<'CONF' # Configuration for automatic LXC container OS updates. # Add container IDs to exclude from updates (comma-separated): # EXCLUDE=100,101,102 EXCLUDE= CONF ok "Created config ${CONF_FILE}" fi ( crontab -l -u root 2>/dev/null | grep -v "${LOCAL_SCRIPT}" || true echo "${CRON_ENTRY}" ) | crontab -u root - ok "Added cron schedule: Every Sunday at midnight" echo "" echo -e " \e[36mLocal script:\e[0m ${LOCAL_SCRIPT}" echo -e " \e[36mConfig:\e[0m ${CONF_FILE}" echo -e " \e[36mLog file:\e[0m ${LOG_FILE}" echo "" } remove() { if crontab -l -u root 2>/dev/null | grep -q "${LOCAL_SCRIPT}"; then (crontab -l -u root 2>/dev/null | grep -v "${LOCAL_SCRIPT}") | crontab -u root - ok "Removed cron schedule" fi remove_legacy_cron [[ -f "$LOCAL_SCRIPT" ]] && rm -f "$LOCAL_SCRIPT" && ok "Removed ${LOCAL_SCRIPT}" [[ -f "$LOG_FILE" ]] && rm -f "$LOG_FILE" && ok "Removed ${LOG_FILE}" echo -e "\n Cron Update LXCs has been fully removed." echo -e " \e[90mNote: ${CONF_FILE} was kept (remove manually if desired).\e[0m" } update_script() { if [[ ! -f "$LOCAL_SCRIPT" ]]; then err "No local script found at ${LOCAL_SCRIPT}. Use 'Add' first." exit 1 fi info "Downloading latest version..." local tmp tmp=$(download_script) || exit 1 if command -v diff &>/dev/null; then local changes changes=$(diff --color=auto "$LOCAL_SCRIPT" "$tmp" 2>/dev/null || true) if [[ -z "$changes" ]]; then ok "Script is already up-to-date (no changes)." rm -f "$tmp" return fi echo "" echo -e " \e[1;33m─── Changes ──────────────────────────────────────────────────\e[0m" echo "$changes" echo -e " \e[1;33m──────────────────────────────────────────────────────────────\e[0m" else review_script "$tmp" fi local new_hash old_hash new_hash=$(sha256sum "$tmp" | awk '{print $1}') old_hash=$(sha256sum "$LOCAL_SCRIPT" | awk '{print $1}') echo -e " \e[36mCurrent SHA256:\e[0m ${old_hash}" echo -e " \e[36mNew SHA256:\e[0m ${new_hash}" echo "" if ! confirm "Apply update?"; then rm -f "$tmp" echo " Aborted." return fi install -m 0755 "$tmp" "$LOCAL_SCRIPT" rm -f "$tmp" ok "Updated ${LOCAL_SCRIPT}" } view_script() { if [[ ! -f "$LOCAL_SCRIPT" ]]; then err "No local script found at ${LOCAL_SCRIPT}. Use 'Add' first." exit 1 fi local view_choice view_choice=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "View Script" --menu "What do you want to view?" 12 60 3 \ "Worker" "Installed update script (${LOCAL_SCRIPT##*/})" \ "Cron" "Cron schedule & configuration" \ "Both" "Show everything" \ 3>&1 1>&2 2>&3) || return 0 case "$view_choice" in "Worker") view_worker_script ;; "Cron") view_cron_config ;; "Both") view_cron_config && echo "" && view_worker_script ;; esac } view_worker_script() { local hash hash=$(sha256sum "$LOCAL_SCRIPT" | awk '{print $1}') echo "" echo -e " \e[1;33m─── ${LOCAL_SCRIPT} ───\e[0m" cat "$LOCAL_SCRIPT" echo -e " \e[1;33m──────────────────────────────────────────────────────────────\e[0m" echo -e " \e[36mSHA256:\e[0m ${hash}" echo -e " \e[36mInstalled:\e[0m $(stat -c '%y' "$LOCAL_SCRIPT" 2>/dev/null | cut -d. -f1)" echo "" } view_cron_config() { echo "" echo -e " \e[1;33m─── Cron Configuration ───────────────────────────────────────\e[0m" if crontab -l -u root 2>/dev/null | grep -q "${LOCAL_SCRIPT}"; then local entry entry=$(crontab -l -u root 2>/dev/null | grep "${LOCAL_SCRIPT}") echo -e " \e[36mCron entry:\e[0m ${entry}" local schedule schedule=$(echo "$entry" | awk '{print $1,$2,$3,$4,$5}') echo -e " \e[36mSchedule:\e[0m ${schedule} ($(cron_to_human "$schedule"))" else echo -e " \e[31mCron:\e[0m Not configured" fi if [[ -f "$CONF_FILE" ]]; then echo -e " \e[36mConfig file:\e[0m ${CONF_FILE}" local excludes excludes=$(grep -oP '^\s*EXCLUDE\s*=\s*\K.*' "$CONF_FILE" 2>/dev/null || true) echo -e " \e[36mExcluded:\e[0m ${excludes:-(none)}" echo "" echo -e " \e[90m--- ${CONF_FILE} ---\e[0m" cat "$CONF_FILE" else echo -e " \e[36mConfig file:\e[0m (not created yet)" fi if [[ -f "$LOG_FILE" ]]; then local log_size log_size=$(du -h "$LOG_FILE" | awk '{print $1}') echo -e " \e[36mLog file:\e[0m ${LOG_FILE} (${log_size})" fi echo -e " \e[1;33m──────────────────────────────────────────────────────────────\e[0m" echo "" } cron_to_human() { local schedule="$1" case "$schedule" in "0 0 * * 0") echo "Every Sunday at midnight" ;; "0 0 * * *") echo "Daily at midnight" ;; "0 * * * *") echo "Every hour" ;; *) echo "Custom schedule" ;; esac } show_status() { echo "" if [[ -f "$LOCAL_SCRIPT" ]]; then local hash hash=$(sha256sum "$LOCAL_SCRIPT" | awk '{print $1}') ok "Script installed: ${LOCAL_SCRIPT}" echo -e " \e[36mSHA256:\e[0m ${hash}" echo -e " \e[36mInstalled:\e[0m $(stat -c '%y' "$LOCAL_SCRIPT" 2>/dev/null | cut -d. -f1)" else err "Script not installed" fi if crontab -l -u root 2>/dev/null | grep -q "${LOCAL_SCRIPT}"; then local schedule schedule=$(crontab -l -u root 2>/dev/null | grep "${LOCAL_SCRIPT}" | awk '{print $1,$2,$3,$4,$5}') ok "Cron active: ${schedule}" else err "Cron not configured" fi if [[ -f "$CONF_FILE" ]]; then local excludes excludes=$(grep -oP '^\s*EXCLUDE\s*=\s*\K.*' "$CONF_FILE" 2>/dev/null || echo "(none)") echo -e " \e[36mExcluded:\e[0m ${excludes:-"(none)"}" fi if [[ -f "$LOG_FILE" ]]; then local log_size last_run log_size=$(du -h "$LOG_FILE" | awk '{print $1}') last_run=$(grep -oP '^\s+\K\w.*' "$LOG_FILE" | tail -1) echo -e " \e[36mLog file:\e[0m ${LOG_FILE} (${log_size})" [[ -n "${last_run:-}" ]] && echo -e " \e[36mLast run:\e[0m ${last_run}" else echo -e " \e[36mLog file:\e[0m (no runs yet)" fi echo "" } run_now() { if [[ ! -f "$LOCAL_SCRIPT" ]]; then err "No local script found at ${LOCAL_SCRIPT}. Use 'Add' first." exit 1 fi info "Running update script now..." bash "$LOCAL_SCRIPT" | tee -a "$LOG_FILE" ok "Run completed. Log appended to ${LOG_FILE}" } rotate_log() { if [[ ! -f "$LOG_FILE" ]]; then info "No log file to rotate." return fi local log_size log_size=$(stat -c '%s' "$LOG_FILE" 2>/dev/null || echo 0) local log_size_h log_size_h=$(du -h "$LOG_FILE" | awk '{print $1}') if confirm "Rotate log file? (current size: ${log_size_h})"; then mv "$LOG_FILE" "${LOG_FILE}.old" ok "Rotated: ${LOG_FILE} → ${LOG_FILE}.old" fi } OPTIONS=( Add "Download, review & install cron schedule" Remove "Remove cron schedule & local script" Update "Update local script from repository" Status "Show installation status & last run" Run "Run update script now (manual trigger)" View "View cron config & installed script" Rotate "Rotate log file" ) CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "Cron Update LXCs" --menu "Select an option:" 16 68 7 \ "${OPTIONS[@]}" 3>&1 1>&2 2>&3) || exit 0 case $CHOICE in "Add") add ;; "Remove") remove ;; "Update") update_script ;; "Status") show_status ;; "Run") run_now ;; "View") view_script ;; "Rotate") rotate_log ;; esac ================================================ FILE: tools/pve/execute.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: jeroenzwart # License: MIT # https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE function header_info() { clear cat <<"EOF" ______ __ __ _ ________ / ____/ _____ _______ __/ /____ / / | |/ / ____/ / __/ | |/_/ _ \/ ___/ / / / __/ _ \ / / | / / / /____> /dev/null || true declare -f init_tool_telemetry &>/dev/null && init_tool_telemetry "execute-lxcs" "pve" header_info echo "Loading..." whiptail --backtitle "Proxmox VE Helper Scripts" --title "Proxmox VE LXC Execute" --yesno "This will execute a command inside selected LXC Containers. Proceed?" 10 58 NODE=$(hostname) EXCLUDE_MENU=() MSG_MAX_LENGTH=0 while read -r TAG ITEM; do OFFSET=2 ((${#ITEM} + OFFSET > MSG_MAX_LENGTH)) && MSG_MAX_LENGTH=${#ITEM}+OFFSET EXCLUDE_MENU+=("$TAG" "$ITEM " "OFF") done < <(pct list | awk 'NR>1') excluded_containers=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "Containers on $NODE" --checklist "\nSelect containers to skip from executing:\n" \ 16 $((MSG_MAX_LENGTH + 23)) 6 "${EXCLUDE_MENU[@]}" 3>&1 1>&2 2>&3 | tr -d '"') if [ $? -ne 0 ]; then exit fi read -r -p "Enter here command for inside the containers: " custom_command header_info echo "One moment please...\n" function execute_in() { container=$1 name=$(pct exec "$container" hostname) echo -e "${BL}[Info]${GN} Execute inside${BL} ${name}${GN} with output: ${CL}" if ! pct exec "$container" -- bash -c "command ${custom_command} >/dev/null 2>&1"; then echo -e "${BL}[Info]${GN} Skipping ${name} ${RD}$container has no command: ${custom_command}" else pct exec "$container" -- bash -c "${custom_command}" | tee fi } for container in $(pct list | awk '{if(NR>1) print $1}'); do if [[ " ${excluded_containers[@]} " =~ " $container " ]]; then echo -e "${BL}[Info]${GN} Skipping ${BL}$container${CL}" else os=$(pct config "$container" | awk '/^ostype/ {print $2}') if [ "$os" != "debian" ] && [ "$os" != "ubuntu" ]; then echo -e "${BL}[Info]${GN} Skipping ${name} ${RD}$container is not Debian or Ubuntu ${CL}" continue fi status=$(pct status "$container") template=$(pct config "$container" | grep -q "template:" && echo "true" || echo "false") if [ "$template" == "false" ] && [ "$status" == "status: stopped" ]; then echo -e "${BL}[Info]${GN} Starting${BL} $container ${CL}" pct start "$container" echo -e "${BL}[Info]${GN} Waiting For${BL} $container${CL}${GN} To Start ${CL}" sleep 5 execute_in "$container" echo -e "${BL}[Info]${GN} Shutting down${BL} $container ${CL}" pct shutdown "$container" & elif [ "$status" == "status: running" ]; then execute_in "$container" fi fi done wait echo -e "${GN} Finished, execute command inside selected containers. ${CL} \n" ================================================ FILE: tools/pve/frigate-support.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT # https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE function header_info { clear cat <<"EOF" ____ _ __ ____ __ / __/___(_)__ ____ _/ /____ / __/_ _____ ___ ___ ____/ /_ / _// __/ / _ `/ _ `/ __/ -_) _\ \/ // / _ \/ _ \/ _ \/ __/ __/ /_/ /_/ /_/\_, /\_,_/\__/\__/ /___/\_,_/ .__/ .__/\___/_/ \__/ /___/ /_/ /_/ EOF } # Telemetry source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func) 2>/dev/null || true declare -f init_tool_telemetry &>/dev/null && init_tool_telemetry "frigate-support" "pve" header_info while true; do read -p "This will Prepare a LXC Container for Frigate. Proceed (y/n)?" yn case $yn in [Yy]*) break ;; [Nn]*) exit ;; *) echo "Please answer yes or no." ;; esac done header_info # The array of device types # CHAR_DEVS+=(major:minor) CHAR_DEVS+=("1:1") # mem CHAR_DEVS+=("29:0") # fb0 CHAR_DEVS+=("188:.*") # ttyUSB* CHAR_DEVS+=("189:.*") # bus/usb/* CHAR_DEVS+=("226:0") # card0 CHAR_DEVS+=("226:128") # renderD128 # Proccess char device string for char_dev in ${CHAR_DEVS[@]}; do [ ! -z "${CHAR_DEV_STRING-}" ] && CHAR_DEV_STRING+=" -o" CHAR_DEV_STRING+=" -regex \".*/${char_dev}\"" done # Store autodev hook script in a variable read -r -d '' HOOK_SCRIPT <<-EOF || true for char_dev in \$(find /sys/dev/char -regextype sed $CHAR_DEV_STRING); do dev="/dev/\$(sed -n "/DEVNAME/ s/^.*=\(.*\)$/\1/p" \${char_dev}/uevent)"; mkdir -p \$(dirname \${LXC_ROOTFS_MOUNT}\${dev}); for link in \$(udevadm info --query=property \$dev | sed -n "s/DEVLINKS=//p"); do mkdir -p \${LXC_ROOTFS_MOUNT}\$(dirname \$link); cp -dpR \$link \${LXC_ROOTFS_MOUNT}\${link}; done; cp -dpR \$dev \${LXC_ROOTFS_MOUNT}\${dev}; done; EOF # Remove newline char from the variable HOOK_SCRIPT=${HOOK_SCRIPT//$'\n'/} # Generate menu of LXC containers in current node NODE=$(hostname) while read -r line; do TAG=$(echo "$line" | awk '{print $1}') ITEM=$(echo "$line" | awk '{print substr($0,36)}') OFFSET=2 if [[ $((${#ITEM} + $OFFSET)) -gt ${MSG_MAX_LENGTH:-} ]]; then MSG_MAX_LENGTH=$((${#ITEM} + $OFFSET)) fi CTID_MENU+=("$TAG" "$ITEM " "OFF") done < <(pct list | awk 'NR>1') # Selection menu for LXC containers while [ -z "${CTID:+x}" ]; do CTID=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "Containers on $NODE" --radiolist \ "\nSelect a container to add support:\n" \ 16 $(($MSG_MAX_LENGTH + 23)) 6 \ "${CTID_MENU[@]}" 3>&1 1>&2 2>&3) done # Add autodev settings CTID_CONFIG_PATH=/etc/pve/lxc/${CTID}.conf sed '/autodev/d' "$CTID_CONFIG_PATH" >CTID.conf cat CTID.conf >"$CTID_CONFIG_PATH" cat <>"$CTID_CONFIG_PATH" lxc.autodev: 1 lxc.hook.autodev: bash -c '$HOOK_SCRIPT' EOF echo -e "\e[1;33m \nFinished....Reboot ${CTID} LXC to apply the changes.\n \e[0m" # In the Proxmox web shell run # bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/tools/pve/frigate-support.sh)" # Reboot the LXC to apply the changes ================================================ FILE: tools/pve/fstrim.sh ================================================ #!/usr/bin/env bash set -eEuo pipefail function header_info() { clear cat <<"EOF" _______ __ __ ______ _ / ____(_) /__ _______ _______/ /____ ____ ___ /_ __/____(_)___ ___ / /_ / / / _ \/ ___/ / / / ___/ __/ _ \/ __ `__ \ / / / ___/ / __ `__ \ / __/ / / / __(__ ) /_/ (__ ) /_/ __/ / / / / / / / / / / / / / / / / /_/ /_/_/\___/____/\__, /____/\__/\___/_/ /_/ /_/ /_/ /_/ /_/_/ /_/ /_/ /____/ EOF } BL="\033[36m" RD="\033[01;31m" GN="\033[1;92m" CL="\033[m" # Telemetry source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func) 2>/dev/null || true declare -f init_tool_telemetry &>/dev/null && init_tool_telemetry "fstrim" "pve" LOGFILE="/var/log/fstrim.log" touch "$LOGFILE" chmod 600 "$LOGFILE" echo -e "\n----- $(date '+%Y-%m-%d %H:%M:%S') | fstrim Run by $(whoami) on $(hostname) -----" >>"$LOGFILE" header_info echo "Loading..." whiptail --backtitle "Proxmox VE Helper Scripts" \ --title "About fstrim (LXC)" \ --msgbox "The 'fstrim' command releases unused blocks back to the storage device. This only makes sense for containers on SSD, NVMe, Thin-LVM, or storage with discard/TRIM support.\n\nIf your root filesystem or container disks are on classic HDDs, thick LVM, or unsupported storage types, running fstrim will have no effect.\n\nRecommended:\n- Use fstrim only on SSD, NVMe, or thin-provisioned storage with discard enabled.\n- For ZFS, ensure 'autotrim=on' is set on your pool.\n" 16 88 ROOT_FS=$(df -Th "/" | awk 'NR==2 {print $2}') if [ "$ROOT_FS" != "ext4" ]; then whiptail --backtitle "Proxmox VE Helper Scripts" \ --title "Warning" \ --yesno "Root filesystem is not ext4 ($ROOT_FS).\nContinue anyway?" 12 80 || exit 0 fi NODE=$(hostname) EXCLUDE_MENU=() STOPPED_MENU=() MAX_NAME_LEN=0 MAX_STAT_LEN=0 # Build arrays with one pct list mapfile -t CTLINES < <(pct list | awk 'NR>1') for LINE in "${CTLINES[@]}"; do CTID=$(awk '{print $1}' <<<"$LINE") STATUS=$(awk '{print $2}' <<<"$LINE") NAME=$(awk '{print $3}' <<<"$LINE") ((${#NAME} > MAX_NAME_LEN)) && MAX_NAME_LEN=${#NAME} ((${#STATUS} > MAX_STAT_LEN)) && MAX_STAT_LEN=${#STATUS} done FMT="%-${MAX_NAME_LEN}s | %-${MAX_STAT_LEN}s" for LINE in "${CTLINES[@]}"; do CTID=$(awk '{print $1}' <<<"$LINE") STATUS=$(awk '{print $2}' <<<"$LINE") NAME=$(awk '{print $3}' <<<"$LINE") DESC=$(printf "$FMT" "$NAME" "$STATUS") EXCLUDE_MENU+=("$CTID" "$DESC" "OFF") if [[ "$STATUS" == "stopped" ]]; then STOPPED_MENU+=("$CTID" "$DESC" "OFF") fi done excluded_containers_raw=$(whiptail --backtitle "Proxmox VE Helper Scripts" \ --title "Containers on $NODE" \ --checklist "\nSelect containers to skip from trimming:\n" \ 20 $((MAX_NAME_LEN + MAX_STAT_LEN + 20)) 12 "${EXCLUDE_MENU[@]}" 3>&1 1>&2 2>&3) [ $? -ne 0 ] && exit read -ra EXCLUDED <<<$(echo "$excluded_containers_raw" | tr -d '"') TO_START=() if [ ${#STOPPED_MENU[@]} -gt 0 ]; then for ((i = 0; i < ${#STOPPED_MENU[@]}; i += 3)); do CTID="${STOPPED_MENU[i]}" DESC="${STOPPED_MENU[i + 1]}" if [[ " ${EXCLUDED[*]} " =~ " $CTID " ]]; then continue fi header_info echo -e "${BL}[Info]${GN} Container $CTID ($DESC) is currently stopped.${CL}" read -rp "Temporarily start for fstrim? [y/N]: " answer if [[ "$answer" =~ ^[Yy]$ ]]; then TO_START+=("$CTID") fi done fi declare -A WAS_STOPPED for ct in "${TO_START[@]}"; do WAS_STOPPED["$ct"]=1 done function trim_container() { local container="$1" local name="$2" header_info echo -e "${BL}[Info]${GN} Trimming ${BL}$container${CL} \n" local before_trim after_trim local lv_name="vm-${container}-disk-0" if lvs --noheadings -o lv_name 2>/dev/null | grep -qw "$lv_name"; then before_trim=$(lvs --noheadings -o lv_name,data_percent 2>/dev/null | awk -v ctid="$lv_name" '$1 == ctid {gsub(/%/, "", $2); print $2}') [[ -n "$before_trim" ]] && echo -e "${RD}Data before trim $before_trim%${CL}" || echo -e "${RD}Data before trim: not available${CL}" else before_trim="" echo -e "${RD}Data before trim: not available (non-LVM storage)${CL}" fi local fstrim_output fstrim_output=$(pct fstrim "$container" 2>&1) if echo "$fstrim_output" | grep -qi "not supported"; then echo -e "${RD}fstrim isnt supported on this storage!${CL}" elif echo "$fstrim_output" | grep -Eq '([0-9]+(\.[0-9]+)?\s*[KMGT]?B)'; then echo -e "${GN}fstrim result: $fstrim_output${CL}" else echo -e "${RD}fstrim result: $fstrim_output${CL}" fi if lvs --noheadings -o lv_name 2>/dev/null | grep -qw "$lv_name"; then after_trim=$(lvs --noheadings -o lv_name,data_percent 2>/dev/null | awk -v ctid="$lv_name" '$1 == ctid {gsub(/%/, "", $2); print $2}') [[ -n "$after_trim" ]] && echo -e "${GN}Data after trim $after_trim%${CL}" || echo -e "${GN}Data after trim: not available${CL}" else after_trim="" echo -e "${GN}Data after trim: not available (non-LVM storage)${CL}" fi # Logging echo "$(date '+%Y-%m-%d %H:%M:%S') | CTID=$container | Name=$name | Before=${before_trim:-N/A}% | After=${after_trim:-N/A}% | fstrim: $fstrim_output" >>"$LOGFILE" sleep 0.5 } for LINE in "${CTLINES[@]}"; do CTID=$(awk '{print $1}' <<<"$LINE") STATUS=$(awk '{print $2}' <<<"$LINE") NAME=$(awk '{print $3}' <<<"$LINE") if [[ " ${EXCLUDED[*]} " =~ " $CTID " ]]; then header_info echo -e "${BL}[Info]${GN} Skipping $CTID ($NAME, excluded)${CL}" sleep 0.5 continue fi if pct config "$CTID" | grep -q "template:"; then header_info echo -e "${BL}[Info]${GN} Skipping $CTID ($NAME, template)${CL}\n" sleep 0.5 continue fi if [[ "$STATUS" != "running" ]]; then if [[ -n "${WAS_STOPPED[$CTID]:-}" ]]; then header_info echo -e "${BL}[Info]${GN} Starting $CTID ($NAME) for trim...${CL}" pct start "$CTID" sleep 2 else header_info echo -e "${BL}[Info]${GN} Skipping $CTID ($NAME, not running, not selected)${CL}" sleep 0.5 continue fi fi trim_container "$CTID" "$NAME" if [[ -n "${WAS_STOPPED[$CTID]:-}" ]]; then read -rp "Stop LXC $CTID ($NAME) again after trim? [Y/n]: " answer if [[ ! "$answer" =~ ^[Nn]$ ]]; then header_info echo -e "${BL}[Info]${GN} Stopping $CTID ($NAME) again...${CL}" pct stop "$CTID" sleep 1 else header_info echo -e "${BL}[Info]${GN} Leaving $CTID ($NAME) running as requested.${CL}" sleep 1 fi fi done header_info echo -e "${GN}Finished, LXC Containers Trimmed.${CL} \n" echo -e "${BL}If you want to see the complete log: cat $LOGFILE${CL}" exit 0 ================================================ FILE: tools/pve/host-backup.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT # https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE function header_info { clear cat <<"EOF" __ __ __ ___ __ / // /__ ___ / /_ / _ )___ _____/ /____ _____ / _ / _ \(_-/dev/null || true declare -f init_tool_telemetry &>/dev/null && init_tool_telemetry "host-backup" "pve" # Function to perform backup function perform_backup { local BACKUP_PATH local DIR local DIR_DASH local BACKUP_FILE local selected_directories=() # Get backup path from user BACKUP_PATH=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "\nDefaults to /root/\ne.g. /mnt/backups/" 11 68 --title "Directory to backup to:" 3>&1 1>&2 2>&3) || return # Default to /root/ if no input BACKUP_PATH="${BACKUP_PATH:-/root/}" # Get directory to work in from user DIR=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "\nDefaults to /etc/\ne.g. /root/, /var/lib/pve-cluster/ etc." 11 68 --title "Directory to work in:" 3>&1 1>&2 2>&3) || return # Default to /etc/ if no input DIR="${DIR:-/etc/}" DIR_DASH=$(echo "$DIR" | tr '/' '-') BACKUP_FILE="$(hostname)${DIR_DASH}backup" # Build a list of directories for backup local CTID_MENU=() CTID_MENU=("ALL" "Backup all folders" "OFF") while read -r dir; do CTID_MENU+=("$(basename "$dir")" "$dir " "OFF") done < <(ls -d "${DIR}"*) # Allow the user to select directories local HOST_BACKUP while [ -z "${HOST_BACKUP:+x}" ]; do HOST_BACKUP=$(whiptail --backtitle "Proxmox VE Host Backup" --title "Working in the ${DIR} directory " --checklist \ "\nSelect what files/directories to backup:\n" 16 $(((${#DIRNAME} + 2) + 88)) 6 "${CTID_MENU[@]}" 3>&1 1>&2 2>&3) || return for selected_dir in ${HOST_BACKUP//\"/}; do if [[ "$selected_dir" == "ALL" ]]; then # if ALL was chosen, secure all folders selected_directories=("${DIR}"*/) break else selected_directories+=("${DIR}$selected_dir") fi done done # Perform the backup header_info echo -e "This will create a backup in\e[1;33m $BACKUP_PATH \e[0mfor these files and directories\e[1;33m ${selected_directories[*]} \e[0m" read -p "Press ENTER to continue..." header_info echo "Working..." tar -czf "$BACKUP_PATH$BACKUP_FILE-$(date +%Y_%m_%dT%H_%M).tar.gz" --absolute-names "${selected_directories[@]}" header_info echo -e "\nFinished" echo -e "\e[1;33m \nA backup is rendered ineffective when it remains stored on the host.\n \e[0m" sleep 2 } # Main script execution loop while true; do if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "Proxmox VE Host Backup" --yesno "This will create backups for particular files and directories located within a designated directory. Proceed?" 10 88); then perform_backup else break fi done ================================================ FILE: tools/pve/hw-acceleration.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT # https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Execute within the Proxmox shell # bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/tools/pve/hw-acceleration.sh)" set -e function header_info { clear cat <<"EOF" __ ___ __ ___ __ __ _ / // / | /| / / / _ |___________ / /__ _______ _/ /_(_)__ ___ / _ /| |/ |/ / / __ / __/ __/ -_) / -_) __/ _ `/ __/ / _ \/ _ \ /_//_/ |__/|__/ /_/ |_\__/\__/\__/_/\__/_/ \_,_/\__/_/\___/_//_/ EOF } YW=$(echo "\033[33m") BL=$(echo "\033[36m") RD=$(echo "\033[01;31m") GN=$(echo "\033[1;92m") CL=$(echo "\033[m") BFR="\\r\\033[K" HOLD="-" CM="${GN}✓${CL}" set -e # Telemetry source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func) 2>/dev/null || true declare -f init_tool_telemetry &>/dev/null && init_tool_telemetry "hw-acceleration" "pve" header_info echo "Loading..." function msg_info() { local msg="$1" echo -ne " ${HOLD} ${YW}${msg}..." } function msg_ok() { local msg="$1" echo -e "${BFR} ${CM} ${GN}${msg}${CL}" } if ! pveversion | grep -Eq "pve-manager/8\.[0-4](\.[0-9]+)*"; then msg_error "This version of Proxmox Virtual Environment is not supported" echo -e "Requires PVE Version 8.1 or higher" echo -e "Exiting..." sleep 2 exit fi whiptail --backtitle "Proxmox VE Helper Scripts" --title "Add Intel HW Acceleration" --yesno "This Will Add Intel HW Acceleration to an existing LXC Container. Proceed?" 8 72 NODE=$(hostname) PREV_MENU=() MSG_MAX_LENGTH=0 privileged_containers=$(pct list | awk 'NR>1 && system("grep -q \047unprivileged: 1\047 /etc/pve/lxc/" $1 ".conf")') if [ -z "$privileged_containers" ]; then whiptail --msgbox "No Privileged Containers Found." 10 58 exit fi while read -r TAG ITEM; do OFFSET=2 ((${#ITEM} + OFFSET > MSG_MAX_LENGTH)) && MSG_MAX_LENGTH=${#ITEM}+OFFSET PREV_MENU+=("$TAG" "$ITEM " "OFF") done < <(echo "$privileged_containers") privileged_container=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "Privileged Containers on $NODE" --checklist "\nSelect a Container To Add Intel HW Acceleration:\n" 16 $((MSG_MAX_LENGTH + 23)) 6 "${PREV_MENU[@]}" 3>&1 1>&2 2>&3 | tr -d '"') header_info read -r -p "Verbose mode? " prompt if [[ ${prompt,,} =~ ^(y|yes)$ ]]; then STD="" else STD="silent" fi header_info cat <>/etc/pve/lxc/"${privileged_container}".conf lxc.cgroup2.devices.allow: c 226:0 rwm lxc.cgroup2.devices.allow: c 226:128 rwm lxc.cgroup2.devices.allow: c 29:0 rwm lxc.mount.entry: /dev/fb0 dev/fb0 none bind,optional,create=file lxc.mount.entry: /dev/dri dev/dri none bind,optional,create=dir lxc.mount.entry: /dev/dri/renderD128 dev/dri/renderD128 none bind,optional,create=file EOF read -r -p "Do you need the intel-media-va-driver-non-free driver (Debian 12 only)? " prompt if [[ ${prompt,,} =~ ^(y|yes)$ ]]; then header_info msg_info "Installing Hardware Acceleration (non-free)" pct exec "${privileged_container}" -- bash -c "cat </etc/apt/sources.list.d/non-free.list deb http://deb.debian.org/debian bookworm main contrib non-free non-free-firmware deb-src http://deb.debian.org/debian bookworm main contrib non-free non-free-firmware deb http://deb.debian.org/debian-security bookworm-security main contrib non-free non-free-firmware deb-src http://deb.debian.org/debian-security bookworm-security main contrib non-free non-free-firmware deb http://deb.debian.org/debian bookworm-updates main contrib non-free non-free-firmware deb-src http://deb.debian.org/debian bookworm-updates main contrib non-free non-free-firmware EOF" pct exec "${privileged_container}" -- bash -c "silent() { \"\$@\" >/dev/null 2>&1; } && $STD apt-get update && $STD apt-get install -y intel-media-va-driver-non-free ocl-icd-libopencl1 intel-opencl-icd vainfo intel-gpu-tools && $STD adduser \$(id -u -n) video && $STD adduser \$(id -u -n) render" msg_ok "Installed Hardware Acceleration (non-free)" else header_info msg_info "Installing Hardware Acceleration" pct exec "${privileged_container}" -- bash -c "silent() { \"\$@\" >/dev/null 2>&1; } && $STD apt-get install -y va-driver-all ocl-icd-libopencl1 intel-opencl-icd vainfo intel-gpu-tools && chgrp video /dev/dri && chmod 755 /dev/dri && $STD adduser \$(id -u -n) video && $STD adduser \$(id -u -n) render" msg_ok "Installed Hardware Acceleration" fi sleep 1 whiptail --backtitle "Proxmox VE Helper Scripts" --msgbox --title "Added tools" "vainfo, execute command 'vainfo'\nintel-gpu-tools, execute command 'intel_gpu_top'" 8 58 msg_ok "Completed successfully!\n" echo -e "Reboot container ${BL}$privileged_container${CL} to apply the new settings\n" ================================================ FILE: tools/pve/kernel-clean.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk # License: MIT # https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE function header_info { clear cat <<"EOF" __ __ __ ________ / //_/__ _________ ___ / / / ____/ /__ ____ _____ / ,< / _ \/ ___/ __ \/ _ \/ / / / / / _ \/ __ `/ __ \ / /| / __/ / / / / / __/ / / /___/ / __/ /_/ / / / / /_/ |_\___/_/ /_/ /_/\___/_/ \____/_/\___/\__,_/_/ /_/ EOF } # Color variables YW="\033[33m" GN="\033[1;92m" RD="\033[01;31m" CL="\033[m" # Telemetry source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func) 2>/dev/null || true declare -f init_tool_telemetry &>/dev/null && init_tool_telemetry "kernel-clean" "pve" # Detect current kernel current_kernel=$(uname -r) available_kernels=$(dpkg --list | grep 'kernel-.*-pve' | awk '{print $2}' | grep -v "$current_kernel" | sort -V) header_info if [ -z "$available_kernels" ]; then echo -e "${GN}No old kernels detected. Current kernel: ${current_kernel}${CL}" exit 0 fi echo -e "${GN}Currently running kernel: ${current_kernel}${CL}" echo -e "${YW}Available kernels for removal:${CL}" echo "$available_kernels" | nl -w 2 -s '. ' echo -e "\n${YW}Select kernels to remove (comma-separated, e.g., 1,2):${CL}" read -r selected # Parse selection IFS=',' read -r -a selected_indices <<<"$selected" kernels_to_remove=() for index in "${selected_indices[@]}"; do kernel=$(echo "$available_kernels" | sed -n "${index}p") if [ -n "$kernel" ]; then kernels_to_remove+=("$kernel") fi done if [ ${#kernels_to_remove[@]} -eq 0 ]; then echo -e "${RD}No valid selection made. Exiting.${CL}" exit 0 fi # Confirm removal echo -e "${YW}Kernels to be removed:${CL}" printf "%s\n" "${kernels_to_remove[@]}" read -rp "Proceed with removal? (y/n): " confirm if [[ "$confirm" != "y" ]]; then echo -e "${RD}Aborted.${CL}" exit 0 fi # Remove kernels for kernel in "${kernels_to_remove[@]}"; do echo -e "${YW}Removing $kernel...${CL}" if apt-get purge -y "$kernel" >/dev/null 2>&1; then echo -e "${GN}Successfully removed: $kernel${CL}" else echo -e "${RD}Failed to remove: $kernel. Check dependencies.${CL}" fi done # Clean up and update GRUB echo -e "${YW}Cleaning up...${CL}" apt-get autoremove -y >/dev/null 2>&1 && update-grub >/dev/null 2>&1 echo -e "${GN}Cleanup and GRUB update complete.${CL}" ================================================ FILE: tools/pve/kernel-pin.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT # https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE function header_info { clear cat <<"EOF" __ __ __ ____ _ / //_/__ _________ ___ / / / __ \(_)___ / ,< / _ \/ ___/ __ \/ _ \/ / / /_/ / / __ \ / /| / __/ / / / / / __/ / / ____/ / / / / /_/ |_\___/_/ /_/ /_/\___/_/ /_/ /_/_/ /_/ EOF } YW=$(echo "\033[33m") RD=$(echo "\033[01;31m") GN=$(echo "\033[1;92m") CL=$(echo "\033[m") BFR="\\r\\033[K" HOLD="-" CM="${GN}✓${CL}" current_kernel=$(uname -r) available_kernels=$(dpkg --list | grep 'kernel-.*-pve' | awk '{print substr($2, 16, length($2)-22)}') # Telemetry source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func) 2>/dev/null || true declare -f init_tool_telemetry &>/dev/null && init_tool_telemetry "kernel-pin" "pve" header_info function msg_info() { local msg="$1" echo -ne " ${HOLD} ${YW}${msg}..." } function msg_ok() { local msg="$1" echo -e "${BFR} ${CM} ${GN}${msg}${CL}" } whiptail --backtitle "Proxmox VE Helper Scripts" --title "Proxmox VE Kernel Pin" --yesno "This will Pin/Unpin Kernel Images, Proceed?" 10 68 KERNEL_MENU=() MSG_MAX_LENGTH=0 while read -r TAG ITEM; do OFFSET=2 ((${#ITEM} + OFFSET > MSG_MAX_LENGTH)) && MSG_MAX_LENGTH=${#ITEM}+OFFSET KERNEL_MENU+=("$TAG" "$ITEM " "OFF") done < <(echo "$available_kernels") pin_kernel=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "Current Kernel $current_kernel" --radiolist "\nSelect Kernel to pin:\nCancel to Unpin any Kernel" 16 $((MSG_MAX_LENGTH + 58)) 6 "${KERNEL_MENU[@]}" 3>&1 1>&2 2>&3 | tr -d '"') [ -z "$pin_kernel" ] && { whiptail --backtitle "Proxmox VE Helper Scripts" --title "No Kernel Selected" --msgbox "It appears that no Kernel was selected\nUnpinning any pinned Kernel" 10 68 msg_info "Unpinning any Kernel" proxmox-boot-tool kernel unpin &>/dev/null msg_ok "Unpinned any Kernel\n" proxmox-boot-tool kernel list echo "" msg_ok "Finished\n" echo -e "${RD} REBOOT${CL}" exit } whiptail --backtitle "Proxmox VE Helper Scripts" --title "Proxmox VE Kernel Pin" --yesno "Would you like to pin the $pin_kernel Kernel?" 10 68 msg_info "Pinning $pin_kernel" proxmox-boot-tool kernel pin $pin_kernel &>/dev/null msg_ok "Successfully Pinned $pin_kernel\n" proxmox-boot-tool kernel list echo "" msg_ok "Finished\n" echo -e "${RD} REBOOT${CL}" ================================================ FILE: tools/pve/lxc-delete.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE function header_info { clear cat <<"EOF" ____ __ _ ________ ____ __ __ / __ \_________ _ ______ ___ ____ _ __ / / | |/ / ____/ / __ \___ / /__ / /____ / /_/ / ___/ __ \| |/_/ __ `__ \/ __ \| |/_/ / / | / / / / / / _ \/ / _ \/ __/ _ \ / ____/ / / /_/ /> < / /___/ / /___ / /_/ / __/ / __/ /_/ __/ /_/ /_/ \____/_/|_/_/ /_/ /_/\____/_/|_| /_____/_/|_\____/ /_____/\___/_/\___/\__/\___/ EOF } spinner() { local pid=$1 local delay=0.1 local spinstr='|/-\' while ps -p $pid >/dev/null; do printf " [%c] " "$spinstr" spinstr=${spinstr#?}${spinstr%"${spinstr#?}"} sleep $delay printf "\r" done printf " \r" } set -eEuo pipefail YW=$(echo "\033[33m") BL=$(echo "\033[36m") RD=$(echo "\033[01;31m") GN=$(echo "\033[1;92m") CL=$(echo "\033[m") TAB=" " CM="${TAB}✔️${TAB}${CL}" # Telemetry source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func) 2>/dev/null || true declare -f init_tool_telemetry &>/dev/null && init_tool_telemetry "lxc-delete" "pve" header_info echo "Loading..." whiptail --backtitle "Proxmox VE Helper Scripts" --title "Proxmox VE LXC Deletion" --yesno "This will delete LXC containers. Proceed?" 10 58 NODE=$(hostname) containers=$(pct list | tail -n +2 | awk '{print $0 " " $4}') if [ -z "$containers" ]; then whiptail --title "LXC Container Delete" --msgbox "No LXC containers available!" 10 60 exit 234 fi menu_items=("ALL" "Delete ALL containers" "OFF") # Add as first option FORMAT="%-10s %-15s %-10s" while read -r container; do container_id=$(echo $container | awk '{print $1}') container_name=$(echo $container | awk '{print $2}') container_status=$(echo $container | awk '{print $3}') formatted_line=$(printf "$FORMAT" "$container_name" "$container_status") menu_items+=("$container_id" "$formatted_line" "OFF") done <<<"$containers" CHOICES=$(whiptail --title "LXC Container Delete" \ --checklist "Select LXC containers to delete:" 25 60 13 \ "${menu_items[@]}" 3>&2 2>&1 1>&3) if [ -z "$CHOICES" ]; then whiptail --title "LXC Container Delete" \ --msgbox "No containers selected!" 10 60 exit 0 fi read -p "Delete containers manually or automatically? (Default: manual) m/a: " DELETE_MODE DELETE_MODE=${DELETE_MODE:-m} selected_ids=$(echo "$CHOICES" | tr -d '"' | tr -s ' ' '\n') # If "ALL" is selected, override with all container IDs if echo "$selected_ids" | grep -q "^ALL$"; then selected_ids=$(echo "$containers" | awk '{print $1}') fi for container_id in $selected_ids; do status=$(pct status $container_id) if [ "$status" == "status: running" ]; then echo -e "${BL}[Info]${GN} Stopping container $container_id...${CL}" pct stop $container_id & sleep 5 echo -e "${BL}[Info]${GN} Container $container_id stopped.${CL}" fi if [[ "$DELETE_MODE" == "a" ]]; then echo -e "${BL}[Info]${GN} Automatically deleting container $container_id...${CL}" pct destroy "$container_id" -f & pid=$! spinner $pid [ $? -eq 0 ] && echo "Container $container_id deleted." || whiptail --title "Error" --msgbox "Failed to delete container $container_id." 10 60 else read -p "Delete container $container_id? (y/N): " CONFIRM if [[ "$CONFIRM" =~ ^[Yy]$ ]]; then echo -e "${BL}[Info]${GN} Deleting container $container_id...${CL}" pct destroy "$container_id" -f & pid=$! spinner $pid [ $? -eq 0 ] && echo "Container $container_id deleted." || whiptail --title "Error" --msgbox "Failed to delete container $container_id." 10 60 else echo -e "${BL}[Info]${RD} Skipping container $container_id...${CL}" fi fi done header_info echo -e "${GN}Deletion process completed.${CL}\n" ================================================ FILE: tools/pve/microcode.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT # https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE function header_info { clear cat <<"EOF" ____ __ ____ __ / __ \_________ ________ ______________ _____ / |/ (_)_____________ _________ ____/ /__ / /_/ / ___/ __ \/ ___/ _ \/ ___/ ___/ __ \/ ___/ / /|_/ / / ___/ ___/ __ \/ ___/ __ \/ __ / _ \ / ____/ / / /_/ / /__/ __(__ |__ ) /_/ / / / / / / / /__/ / / /_/ / /__/ /_/ / /_/ / __/ /_/ /_/ \____/\___/\___/____/____/\____/_/ /_/ /_/_/\___/_/ \____/\___/\____/\__,_/\___/ EOF } RD=$(echo "\033[01;31m") YW=$(echo "\033[33m") GN=$(echo "\033[1;92m") CL=$(echo "\033[m") BFR="\\r\\033[K" HOLD="-" CM="${GN}✓${CL}" CROSS="${RD}✗${CL}" msg_info() { echo -ne " ${HOLD} ${YW}$1..."; } msg_ok() { echo -e "${BFR} ${CM} ${GN}$1${CL}"; } msg_error() { echo -e "${BFR} ${CROSS} ${RD}$1${CL}"; } # Telemetry source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func) 2>/dev/null || true declare -f init_tool_telemetry &>/dev/null && init_tool_telemetry "microcode" "pve" header_info current_microcode=$(journalctl -k | grep -i 'microcode: Current revision:' | grep -oP 'Current revision: \K0x[0-9a-f]+') [ -z "$current_microcode" ] && current_microcode="Not found." intel() { if ! dpkg -s iucode-tool >/dev/null 2>&1; then msg_info "Installing iucode-tool (Intel microcode updater)" apt-get install -y iucode-tool &>/dev/null msg_ok "Installed iucode-tool" else msg_ok "Intel iucode-tool is already installed" sleep 1 fi intel_microcode=$(curl -fsSL "https://ftp.debian.org/debian/pool/non-free-firmware/i/intel-microcode//" | grep -o 'href="[^"]*amd64.deb"' | sed 's/href="//;s/"//') [ -z "$intel_microcode" ] && { whiptail --backtitle "Proxmox VE Helper Scripts" --title "No Microcode Found" --msgbox "It appears there were no microcode packages found\n Try again later." 10 68 msg_info "Exiting" sleep 1 msg_ok "Done" exit } MICROCODE_MENU=() MSG_MAX_LENGTH=0 while read -r TAG ITEM; do OFFSET=2 ((${#ITEM} + OFFSET > MSG_MAX_LENGTH)) && MSG_MAX_LENGTH=${#ITEM}+OFFSET MICROCODE_MENU+=("$TAG" "$ITEM " "OFF") done < <(echo "$intel_microcode") microcode=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "Current Microcode revision:${current_microcode}" --radiolist "\nSelect a microcode package to install:\n" 16 $((MSG_MAX_LENGTH + 58)) 6 "${MICROCODE_MENU[@]}" 3>&1 1>&2 2>&3 | tr -d '"') [ -z "$microcode" ] && { whiptail --backtitle "Proxmox VE Helper Scripts" --title "No Microcode Selected" --msgbox "It appears that no microcode packages were selected" 10 68 msg_info "Exiting" sleep 1 msg_ok "Done" exit } msg_info "Downloading the Intel Processor Microcode Package $microcode" curl -fsSL "http://ftp.debian.org/debian/pool/non-free-firmware/i/intel-microcode/$microcode" -o $(basename "http://ftp.debian.org/debian/pool/non-free-firmware/i/intel-microcode/$microcode") msg_ok "Downloaded the Intel Processor Microcode Package $microcode" msg_info "Installing $microcode (Patience)" dpkg -i $microcode &>/dev/null msg_ok "Installed $microcode" msg_info "Cleaning up" rm $microcode msg_ok "Cleaned" echo -e "\nIn order to apply the changes, a system reboot will be necessary.\n" } amd() { amd_microcode=$(curl -fsSL "https://ftp.debian.org/debian/pool/non-free-firmware/a/amd64-microcode///" | grep -o 'href="[^"]*amd64.deb"' | sed 's/href="//;s/"//') [ -z "$amd_microcode" ] && { whiptail --backtitle "Proxmox VE Helper Scripts" --title "No Microcode Found" --msgbox "It appears there were no microcode packages found\n Try again later." 10 68 msg_info "Exiting" sleep 1 msg_ok "Done" exit } MICROCODE_MENU=() MSG_MAX_LENGTH=0 while read -r TAG ITEM; do OFFSET=2 ((${#ITEM} + OFFSET > MSG_MAX_LENGTH)) && MSG_MAX_LENGTH=${#ITEM}+OFFSET MICROCODE_MENU+=("$TAG" "$ITEM " "OFF") done < <(echo "$amd_microcode") microcode=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "Current Microcode revision:${current_microcode}" --radiolist "\nSelect a microcode package to install:\n" 16 $((MSG_MAX_LENGTH + 58)) 6 "${MICROCODE_MENU[@]}" 3>&1 1>&2 2>&3 | tr -d '"') [ -z "$microcode" ] && { whiptail --backtitle "Proxmox VE Helper Scripts" --title "No Microcode Selected" --msgbox "It appears that no microcode packages were selected" 10 68 msg_info "Exiting" sleep 1 msg_ok "Done" exit } msg_info "Downloading the AMD Processor Microcode Package $microcode" curl -fsSL "https://ftp.debian.org/debian/pool/non-free-firmware/a/amd64-microcode/$microcode" -o $(basename "https://ftp.debian.org/debian/pool/non-free-firmware/a/amd64-microcode/$microcode") msg_ok "Downloaded the AMD Processor Microcode Package $microcode" msg_info "Installing $microcode (Patience)" dpkg -i $microcode &>/dev/null msg_ok "Installed $microcode" msg_info "Cleaning up" rm $microcode msg_ok "Cleaned" echo -e "\nIn order to apply the changes, a system reboot will be necessary.\n" } if ! command -v pveversion >/dev/null 2>&1; then header_info msg_error "No PVE Detected!" exit fi whiptail --backtitle "Proxmox VE Helper Scripts" --title "Proxmox VE Processor Microcode" --yesno "This will check for CPU microcode packages with the option to install. Proceed?" 10 58 msg_info "Checking CPU Vendor" cpu=$(lscpu | grep -oP 'Vendor ID:\s*\K\S+' | head -n 1) if [ "$cpu" == "GenuineIntel" ]; then msg_ok "${cpu} was detected" sleep 1 intel elif [ "$cpu" == "AuthenticAMD" ]; then msg_ok "${cpu} was detected" sleep 1 amd else msg_error "${cpu} is not supported" exit fi ================================================ FILE: tools/pve/monitor-all.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT # https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE clear cat <<"EOF" __ ___ _ __ ___ ____ / |/ /___ ____ (_) /_____ _____ / | / / / / /|_/ / __ \/ __ \/ / __/ __ \/ ___/ / /| | / / / / / / / /_/ / / / / / /_/ /_/ / / / ___ |/ / / /_/ /_/\____/_/ /_/_/\__/\____/_/ /_/ |_/_/_/ EOF # Telemetry source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func) 2>/dev/null || true declare -f init_tool_telemetry &>/dev/null && init_tool_telemetry "monitor-all" "pve" add() { echo -e "\n IMPORTANT: Tag-Based Monitoring Enabled" echo "Only VMs and containers with the tag 'mon-restart' will be automatically restarted by this service." echo echo "🔧 How to add the tag:" echo " → Proxmox Web UI: Go to VM/CT → Options → Tags → Add 'mon-restart'" echo " → CLI: qm set -tags mon-restart" echo " pct set -tags mon-restart" echo while true; do read -p "This script will add Monitor All to Proxmox VE. Proceed (y/n)? " yn case $yn in [Yy]*) break ;; [Nn]*) exit ;; *) echo "Please answer yes or no." ;; esac done cat <<'EOF' >/usr/local/bin/ping-instances.sh #!/usr/bin/env bash # Read excluded instances from command line arguments excluded_instances=("$@") echo "Excluded instances: ${excluded_instances[@]}" while true; do for instance in $(pct list | awk 'NR>1 {print $1}'; qm list | awk 'NR>1 {print $1}'); do # Skip excluded instances if [[ " ${excluded_instances[@]} " =~ " ${instance} " ]]; then echo "Skipping $instance because it is excluded" continue fi # Determine type and set config command if pct status $instance >/dev/null 2>&1; then type="ct" config_cmd="pct config" else type="vm" config_cmd="qm config" fi # Skip templates and onboot-disabled onboot=$($config_cmd $instance | grep -q "onboot: 0" || ( ! $config_cmd $instance | grep -q "onboot" ) && echo "true" || echo "false") template=$($config_cmd $instance | grep -q "^template:" && echo "true" || echo "false") if [ "$onboot" == "true" ]; then echo "Skipping $instance because it is set not to boot" continue elif [ "$template" == "true" ]; then echo "Skipping $instance because it is a template" continue fi # Check for mon-restart tag has_tag=$($config_cmd $instance | grep -q "tags:.*mon-restart" && echo "true" || echo "false") if [ "$has_tag" != "true" ]; then echo "Skipping $instance because it does not have 'mon-restart' tag" continue fi # Responsiveness check and restart if needed if [ "$type" == "vm" ]; then # Check if guest agent responds if qm guest cmd $instance ping >/dev/null 2>&1; then echo "VM $instance is responsive via guest agent" else echo "$(date): VM $instance is not responding to agent ping, restarting..." if qm status $instance | grep -q "status: running"; then qm stop $instance >/dev/null 2>&1 sleep 5 fi qm start $instance >/dev/null 2>&1 fi else # Container: get IP and ping IP=$(pct exec $instance ip a s dev eth0 | awk '/inet / {print $2}' | cut -d/ -f1 | head -n1) if ! ping -c 1 $IP >/dev/null 2>&1; then echo "$(date): CT $instance is not responding, restarting..." pct stop $instance >/dev/null 2>&1 sleep 5 pct start $instance >/dev/null 2>&1 else echo "CT $instance is responsive" fi fi done echo "$(date): Pausing for 5 minutes..." sleep 300 done >/var/log/ping-instances.log 2>&1 EOF touch /var/log/ping-instances.log chmod +x /usr/local/bin/ping-instances.sh cat </etc/systemd/system/ping-instances.timer [Unit] Description=Delay ping-instances.service by 5 minutes [Timer] OnBootSec=300 OnUnitActiveSec=300 [Install] WantedBy=timers.target EOF cat </etc/systemd/system/ping-instances.service [Unit] Description=Ping instances every 5 minutes and restart if necessary After=ping-instances.timer Requires=ping-instances.timer [Service] Type=simple # To exclude specific instances, pass IDs to ExecStart, e.g.: # ExecStart=/usr/local/bin/ping-instances.sh 100 200 # Instances must also have the 'mon-restart' tag to be monitored ExecStart=/usr/local/bin/ping-instances.sh Restart=always StandardOutput=file:/var/log/ping-instances.log StandardError=file:/var/log/ping-instances.log [Install] WantedBy=multi-user.target EOF systemctl daemon-reload systemctl enable -q --now ping-instances.timer systemctl enable -q --now ping-instances.service clear echo -e "\n Monitor All installed." echo "📄 To view logs: cat /var/log/ping-instances.log" echo "⚙️ Make sure your VMs or containers have the 'mon-restart' tag to be monitored." } remove() { systemctl disable -q --now ping-instances.timer systemctl disable -q --now ping-instances.service rm -f /etc/systemd/system/ping-instances.service rm -f /etc/systemd/system/ping-instances.timer rm -f /usr/local/bin/ping-instances.sh rm -f /var/log/ping-instances.log echo "Monitor All removed from Proxmox VE" } OPTIONS=(Add "Add Monitor-All to Proxmox VE" Remove "Remove Monitor-All from Proxmox VE") CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "Monitor-All for Proxmox VE" --menu "Select an option:" 10 58 2 \ "${OPTIONS[@]}" 3>&1 1>&2 2>&3) case $CHOICE in "Add") add ;; "Remove") remove ;; *) echo "Exiting..." exit 0 ;; esac ================================================ FILE: tools/pve/nic-offloading-fix.sh ================================================ #!/usr/bin/env bash # Creates a systemd service to disable NIC offloading features for Intel e1000e and e1000 interfaces # Author: rcastley # License: MIT YW=$(echo "\033[33m") YWB=$'\e[93m' BL=$(echo "\033[36m") RD=$(echo "\033[01;31m") BGN=$(echo "\033[4;92m") GN=$(echo "\033[1;92m") DGN=$(echo "\033[32m") CL=$(echo "\033[m") TAB=" " CM="${TAB}✔️${TAB}" CROSS="${TAB}✖️${TAB}" INFO="${TAB}ℹ️${TAB}${CL}" WARN="${TAB}⚠️${TAB}${CL}" function header_info { clear cat <<"EOF" _ ____________ ____ __________ ___ ____ _ __ __ / | / / _/ ____/ / __ \/ __/ __/ /___ ____ _____/ (_)___ ____ _ / __ \(_)________ _/ /_ / /__ _____ / |/ // // / / / / / /_/ /_/ / __ \/ __ `/ __ / / __ \/ __ `/ / / / / / ___/ __ `/ __ \/ / _ \/ ___/ / /| // // /___ / /_/ / __/ __/ / /_/ / /_/ / /_/ / / / / / /_/ / / /_/ / (__ ) /_/ / /_/ / / __/ / /_/ |_/___/\____/ \____/_/ /_/ /_/\____/\__,_/\__,_/_/_/ /_/\__, / /_____/_/____/\__,_/_.___/_/\___/_/ /____/ Enhanced version supporting both e1000e and e1000 drivers EOF } # Telemetry source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func) 2>/dev/null || true declare -f init_tool_telemetry &>/dev/null && init_tool_telemetry "nic-offloading-fix" "pve" header_info function msg_info() { echo -e "${INFO} ${YW}${1}...${CL}"; } function msg_ok() { echo -e "${CM} ${GN}${1}${CL}"; } function msg_error() { echo -e "${CROSS} ${RD}${1}${CL}"; } function msg_warn() { echo -e "${WARN} ${YWB}${1}"; } # Check for root privileges if [ "$(id -u)" -ne 0 ]; then msg_error "Error: This script must be run as root." exit 104 fi if ! command -v ethtool >/dev/null 2>&1; then msg_info "Installing ethtool" apt-get update &>/dev/null apt-get install -y ethtool &>/dev/null || { msg_error "Failed to install ethtool. Exiting." exit 237 } msg_ok "ethtool installed successfully" fi # Get list of network interfaces using Intel e1000e or e1000 drivers INTERFACES=() COUNT=0 msg_info "Searching for Intel e1000e and e1000 interfaces" for device in /sys/class/net/*; do interface="$(basename "$device")" # or adjust the rest of the usages below, as mostly you'll use the path anyway # Skip loopback interface and virtual interfaces if [[ "$interface" != "lo" ]] && [[ ! "$interface" =~ ^(tap|fwbr|veth|vmbr|bonding_masters) ]]; then # Check if the interface uses the e1000e or e1000 driver driver=$(basename $(readlink -f /sys/class/net/$interface/device/driver 2>/dev/null) 2>/dev/null) if [[ "$driver" == "e1000e" ]] || [[ "$driver" == "e1000" ]]; then # Get MAC address for additional identification mac=$(cat /sys/class/net/$interface/address 2>/dev/null) INTERFACES+=("$interface" "Intel $driver NIC ($mac)") ((COUNT++)) fi fi done # Check if any Intel e1000e/e1000 interfaces were found if [ ${#INTERFACES[@]} -eq 0 ]; then whiptail --title "Error" --msgbox "No Intel e1000e or e1000 network interfaces found!" 10 60 msg_error "No Intel e1000e or e1000 network interfaces found! Exiting." exit 236 fi msg_ok "Found ${BL}$COUNT${GN} Intel e1000e/e1000 interfaces" # Create a checklist for interface selection with all interfaces initially checked INTERFACES_CHECKLIST=() for ((i = 0; i < ${#INTERFACES[@]}; i += 2)); do INTERFACES_CHECKLIST+=("${INTERFACES[i]}" "${INTERFACES[i + 1]}" "ON") done # Show interface selection checklist SELECTED_INTERFACES=$(whiptail --backtitle "Intel e1000e/e1000 NIC Offloading Disabler" --title "Network Interfaces" \ --separate-output --checklist "Select Intel e1000e/e1000 network interfaces\n(Space to toggle, Enter to confirm):" 15 80 6 \ "${INTERFACES_CHECKLIST[@]}" 3>&1 1>&2 2>&3) exitstatus=$? if [ $exitstatus != 0 ]; then msg_info "User canceled. Exiting." exit 0 fi # Check if any interfaces were selected if [ -z "$SELECTED_INTERFACES" ]; then msg_error "No interfaces selected. Exiting." exit 0 fi # Convert the selected interfaces into an array readarray -t INTERFACE_ARRAY <<<"$SELECTED_INTERFACES" # Show the number of selected interfaces INTERFACE_COUNT=${#INTERFACE_ARRAY[@]} # Print selected interfaces with their driver types for iface in "${INTERFACE_ARRAY[@]}"; do driver=$(basename $(readlink -f /sys/class/net/$iface/device/driver 2>/dev/null) 2>/dev/null) msg_ok "Selected interface: ${BL}$iface${GN} (${BL}$driver${GN})" done # Ask for confirmation with the list of selected interfaces CONFIRMATION_MSG="You have selected the following interface(s):\n\n" for iface in "${INTERFACE_ARRAY[@]}"; do SPEED=$(cat /sys/class/net/$iface/speed 2>/dev/null || echo "Unknown") MAC=$(cat /sys/class/net/$iface/address 2>/dev/null) DRIVER=$(basename $(readlink -f /sys/class/net/$iface/device/driver 2>/dev/null) 2>/dev/null) CONFIRMATION_MSG+="- $iface (Driver: $DRIVER, MAC: $MAC, Speed: ${SPEED}Mbps)\n" done CONFIRMATION_MSG+="\nThis will create systemd service(s) to disable offloading features.\n\nProceed?" if ! whiptail --backtitle "Intel e1000e/e1000 NIC Offloading Disabler" --title "Confirmation" \ --yesno "$CONFIRMATION_MSG" 20 80; then msg_info "User canceled. Exiting." exit 0 fi # Loop through all selected interfaces and create services for each for SELECTED_INTERFACE in "${INTERFACE_ARRAY[@]}"; do # Get the driver type for this specific interface DRIVER=$(basename $(readlink -f /sys/class/net/$SELECTED_INTERFACE/device/driver 2>/dev/null) 2>/dev/null) # Create service name for this interface SERVICE_NAME="disable-nic-offload-$SELECTED_INTERFACE.service" SERVICE_PATH="/etc/systemd/system/$SERVICE_NAME" # Create the service file with driver-specific optimizations msg_info "Creating systemd service for interface: ${BL}$SELECTED_INTERFACE${YW} (${BL}$DRIVER${YW})" # Start with the common part of the service file cat >"$SERVICE_PATH" </dev/null) 2>/dev/null) if systemctl is-active --quiet "$SERVICE_NAME"; then SVC_STATUS="Active" else SVC_STATUS="Inactive" fi if systemctl is-enabled --quiet "$SERVICE_NAME"; then BOOT_SVC_STATUS="Enabled" else BOOT_SVC_STATUS="Disabled" fi SUMMARY_MSG+="- $iface ($DRIVER): $SVC_STATUS, Boot: $BOOT_SVC_STATUS\n" done # Show summary results whiptail --backtitle "Intel e1000e/e1000 NIC Offloading Disabler" --title "Success" --msgbox "$SUMMARY_MSG" 22 80 msg_ok "Intel e1000e/e1000 optimization complete for ${#INTERFACE_ARRAY[@]} interface(s)!" # Show verification commands echo "" msg_info "Verification commands:" for iface in "${INTERFACE_ARRAY[@]}"; do echo -e "${TAB}${BL}ethtool -k $iface${CL} ${YW}# Check offloading status${CL}" echo -e "${TAB}${BL}systemctl status disable-nic-offload-$iface.service${CL} ${YW}# Check service status${CL}" done exit 0 ================================================ FILE: tools/pve/pbs-microcode.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: DonPablo1010 | Co-Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE function header_info { clear cat <<"EOF" ____ __ ____ __ / __ \_________ ________ ______________ _____ / |/ (_)_____________ _________ ____/ /__ / /_/ / ___/ __ \/ ___/ _ \/ ___/ ___/ __ \/ ___/ / /|_/ / / ___/ ___/ __ \/ ___/ __ \/ __ / _ \ / ____/ / / /_/ / /__/ __(__ |__ ) /_/ / / / / / / / /__/ / / /_/ / /__/ /_/ / /_/ / __/ /_/ /_/ \____/\___/\___/____/____/\____/_/ /_/ /_/_/\___/_/ \____/\___/\____/\__,_/\___/ Proxmox Backup Server Processor Microcode Updater EOF } # Color definitions RD=$(echo "\033[01;31m") YW=$(echo "\033[33m") GN=$(echo "\033[1;92m") CL=$(echo "\033[m") BFR="\\r\\033[K" HOLD="-" CM="${GN}✓${CL}" CROSS="${RD}✗${CL}" msg_info() { echo -ne " ${HOLD} ${YW}$1..."; } # Telemetry source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func) 2>/dev/null || true declare -f init_tool_telemetry &>/dev/null && init_tool_telemetry "pbs-microcode" "pve" msg_ok() { echo -e "${BFR} ${CM} ${GN}$1${CL}"; } msg_error() { echo -e "${BFR} ${CROSS} ${RD}$1${CL}"; } header_info # Check if running on bare metal using systemd-detect-virt. virt=$(systemd-detect-virt) if [ "$virt" != "none" ]; then msg_error "This script must be run on bare metal. Detected virtual environment: $virt" exit 232 fi # Attempt to obtain the current loaded microcode revision current_microcode=$(journalctl -k | grep -i 'microcode: Current revision:' | grep -oP 'Current revision: \K0x[0-9a-f]+') [ -z "$current_microcode" ] && current_microcode="Not found." intel() { if ! dpkg -s iucode-tool >/dev/null 2>&1; then msg_info "Installing iucode-tool (Intel microcode updater)" apt-get install -y iucode-tool &>/dev/null msg_ok "Installed iucode-tool" else msg_ok "Intel iucode-tool is already installed" sleep 1 fi intel_microcode=$(curl -fsSL "https://ftp.debian.org/debian/pool/non-free-firmware/i/intel-microcode/" | grep -o 'href="[^"]*amd64.deb"' | sed 's/href="//;s/"//') [ -z "$intel_microcode" ] && { whiptail --backtitle "Proxmox Backup Server Helper Scripts" --title "No Microcode Found" --msgbox "No microcode packages were found.\nTry again later." 10 68 msg_info "Exiting" sleep 1 msg_ok "Done" exit } MICROCODE_MENU=() MSG_MAX_LENGTH=0 while read -r TAG ITEM; do OFFSET=2 ((${#ITEM} + OFFSET > MSG_MAX_LENGTH)) && MSG_MAX_LENGTH=$((${#ITEM} + OFFSET)) MICROCODE_MENU+=("$TAG" "$ITEM " "OFF") done < <(echo "$intel_microcode") microcode=$(whiptail --backtitle "Proxmox Backup Server Helper Scripts" \ --title "Current Microcode Revision: ${current_microcode}" \ --radiolist "\nSelect a microcode package to install:\n" \ 16 $((MSG_MAX_LENGTH + 58)) 6 "${MICROCODE_MENU[@]}" 3>&1 1>&2 2>&3 | tr -d '"') [ -z "$microcode" ] && { whiptail --backtitle "Proxmox Backup Server Helper Scripts" --title "No Microcode Selected" --msgbox "No microcode package was selected." 10 68 msg_info "Exiting" sleep 1 msg_ok "Done" exit } msg_info "Downloading Intel processor microcode package $microcode" curl -fsSL "http://ftp.debian.org/debian/pool/non-free-firmware/i/intel-microcode/$microcode" -o $(basename "http://ftp.debian.org/debian/pool/non-free-firmware/i/intel-microcode/$microcode") msg_ok "Downloaded Intel processor microcode package $microcode" msg_info "Installing $microcode (this might take a while)" dpkg -i $microcode &>/dev/null msg_ok "Installed $microcode" msg_info "Cleaning up" rm $microcode msg_ok "Clean up complete" echo -e "\nA system reboot is required to apply the changes.\n" } amd() { amd_microcode=$(curl -fsSL "https://ftp.debian.org/debian/pool/non-free-firmware/a/amd64-microcode/" | grep -o 'href="[^"]*amd64.deb"' | sed 's/href="//;s/"//') [ -z "$amd_microcode" ] && { whiptail --backtitle "Proxmox Backup Server Helper Scripts" --title "No Microcode Found" --msgbox "No microcode packages were found.\nTry again later." 10 68 msg_info "Exiting" sleep 1 msg_ok "Done" exit } MICROCODE_MENU=() MSG_MAX_LENGTH=0 while read -r TAG ITEM; do OFFSET=2 ((${#ITEM} + OFFSET > MSG_MAX_LENGTH)) && MSG_MAX_LENGTH=$((${#ITEM} + OFFSET)) MICROCODE_MENU+=("$TAG" "$ITEM " "OFF") done < <(echo "$amd_microcode") microcode=$(whiptail --backtitle "Proxmox Backup Server Helper Scripts" \ --title "Current Microcode Revision: ${current_microcode}" \ --radiolist "\nSelect a microcode package to install:\n" \ 16 $((MSG_MAX_LENGTH + 58)) 6 "${MICROCODE_MENU[@]}" 3>&1 1>&2 2>&3 | tr -d '"') [ -z "$microcode" ] && { whiptail --backtitle "Proxmox Backup Server Helper Scripts" --title "No Microcode Selected" --msgbox "No microcode package was selected." 10 68 msg_info "Exiting" sleep 1 msg_ok "Done" exit } msg_info "Downloading AMD processor microcode package $microcode" curl -fsSL "https://ftp.debian.org/debian/pool/non-free-firmware/a/amd64-microcode/$microcode" -o $(basename "https://ftp.debian.org/debian/pool/non-free-firmware/a/amd64-microcode/$microcode") msg_ok "Downloaded AMD processor microcode package $microcode" msg_info "Installing $microcode (this might take a while)" dpkg -i $microcode &>/dev/null msg_ok "Installed $microcode" msg_info "Cleaning up" rm $microcode msg_ok "Clean up complete" echo -e "\nA system reboot is required to apply the changes.\n" } # Check if this is a Proxmox Backup Server by verifying the presence of the datastore config. if [ ! -f /etc/proxmox-backup/user.cfg ]; then header_info msg_error "Proxmox Backup Server not detected!" exit fi whiptail --backtitle "Proxmox Backup Server Helper Scripts" \ --title "Proxmox Backup Server Processor Microcode" \ --yesno "This script searches for CPU microcode packages and offers the option to install them.\nProceed?" 10 68 msg_info "Checking CPU vendor" cpu=$(lscpu | grep -oP 'Vendor ID:\s*\K\S+' | head -n 1) if [ "$cpu" == "GenuineIntel" ]; then msg_ok "${cpu} detected" sleep 1 intel elif [ "$cpu" == "AuthenticAMD" ]; then msg_ok "${cpu} detected" sleep 1 amd else msg_error "CPU vendor ${cpu} is not supported" exit fi ================================================ FILE: tools/pve/pbs3-upgrade.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT # https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE header_info() { clear cat <<"EOF" ____ ____ __________ __ ______ __________ ___ ____ ______ / __ \/ __ ) ___/__ / / / / / __ \/ ____/ __ \/ | / __ \/ ____/ / /_/ / __ \__ \ /_ < / / / / /_/ / / __/ /_/ / /| | / / / / __/ / ____/ /_/ /__/ /__/ / / /_/ / ____/ /_/ / _, _/ ___ |/ /_/ / /___ /_/ /_____/____/____/ \____/_/ \____/_/ |_/_/ |_/_____/_____/ EOF } RD=$(echo "\033[01;31m") YW=$(echo "\033[33m") GN=$(echo "\033[1;92m") CL=$(echo "\033[m") BFR="\\r\\033[K" HOLD="-" CM="${GN}✓${CL}" CROSS="${RD}✗${CL}" set -euo pipefail shopt -s inherit_errexit nullglob msg_info() { local msg="$1" echo -ne " ${HOLD} ${YW}${msg}..." } msg_ok() { local msg="$1" echo -e "${BFR} ${CM} ${GN}${msg}${CL}" } msg_error() { local msg="$1" echo -e "${BFR} ${CROSS} ${RD}${msg}${CL}" } # Telemetry source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func) 2>/dev/null || true declare -f init_tool_telemetry &>/dev/null && init_tool_telemetry "pbs3-upgrade" "pve" start_routines() { header_info CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "PBS 2 BACKUP" --menu "\nMake a backup of /etc/proxmox-backup to ensure that in the worst case, any relevant configuration can be recovered?" 14 58 2 \ "yes" " " \ "no" " " 3>&2 2>&1 1>&3) case $CHOICE in yes) msg_info "Backing up Proxmox Backup Server 2" tar czf "pbs2-etc-backup-$(date -I).tar.gz" -C "/etc" "proxmox-backup" msg_ok "Backed up Proxmox Backup Server 2" ;; no) msg_error "Selected no to Backing up Proxmox Backup Server 2" ;; esac CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "PBS 3 SOURCES" --menu "This will set the correct sources to update and install Proxmox Backup Server 3.\n \nChange to Proxmox Backup Server 3 sources?" 14 58 2 \ "yes" " " \ "no" " " 3>&2 2>&1 1>&3) case $CHOICE in yes) msg_info "Changing to Proxmox Backup Server 3 Sources" cat </etc/apt/sources.list deb http://deb.debian.org/debian bookworm main contrib deb http://deb.debian.org/debian bookworm-updates main contrib deb http://security.debian.org/debian-security bookworm-security main contrib EOF msg_ok "Changed to Proxmox Backup Server 3 Sources" ;; no) msg_error "Selected no to Correcting Proxmox Backup Server 3 Sources" ;; esac CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "PBS3-ENTERPRISE" --menu "The 'pbs-enterprise' repository is only available to users who have purchased a Proxmox VE subscription.\n \nDisable 'pbs-enterprise' repository?" 14 58 2 \ "yes" " " \ "no" " " 3>&2 2>&1 1>&3) case $CHOICE in yes) msg_info "Disabling 'pbs-enterprise' repository" cat </etc/apt/sources.list.d/pbs-enterprise.list # deb https://enterprise.proxmox.com/debian/pbs bookworm pbs-enterprise EOF msg_ok "Disabled 'pbs-enterprise' repository" ;; no) msg_error "Selected no to Disabling 'pbs-enterprise' repository" ;; esac CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "PBS3-NO-SUBSCRIPTION" --menu "The 'pbs-no-subscription' repository provides access to all of the open-source components of Proxmox Backup Server.\n \nEnable 'pbs-no-subscription' repository?" 14 58 2 \ "yes" " " \ "no" " " 3>&2 2>&1 1>&3) case $CHOICE in yes) msg_info "Enabling 'pbs-no-subscription' repository" cat </etc/apt/sources.list.d/pbs-install-repo.list deb http://download.proxmox.com/debian/pbs bookworm pbs-no-subscription EOF msg_ok "Enabled 'pbs-no-subscription' repository" ;; no) msg_error "Selected no to Enabling 'pbs-no-subscription' repository" ;; esac CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "PBS3 TEST" --menu "The 'pbstest' repository can give advanced users access to new features and updates before they are officially released.\n \nAdd (Disabled) 'pbstest' repository?" 14 58 2 \ "yes" " " \ "no" " " 3>&2 2>&1 1>&3) case $CHOICE in yes) msg_info "Adding 'pbstest' repository and set disabled" cat </etc/apt/sources.list.d/pbstest-for-beta.list # deb http://download.proxmox.com/debian/pbs bookworm pbstest EOF msg_ok "Added 'pbstest' repository" ;; no) msg_error "Selected no to Adding 'pbstest' repository" ;; esac CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "PBS 3 UPDATE" --menu "\nUpdate to Proxmox Backup Server 3 now?" 11 58 2 \ "yes" " " \ "no" " " 3>&2 2>&1 1>&3) case $CHOICE in yes) msg_info "Updating to Proxmox Backup Server 3 (Patience)" apt-get update DEBIAN_FRONTEND=noninteractive apt-get -o Dpkg::Options::="--force-confold" dist-upgrade -y msg_ok "Updated to Proxmox Backup Server 3" ;; no) msg_error "Selected no to Updating to Proxmox Backup Server 3" ;; esac CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "REBOOT" --menu "\nReboot Proxmox Backup Server 3 now? (recommended)" 11 58 2 \ "yes" " " \ "no" " " 3>&2 2>&1 1>&3) case $CHOICE in yes) msg_info "Rebooting Proxmox Backup Server 3" sleep 2 msg_ok "Completed Install Routines" reboot ;; no) msg_error "Selected no to Rebooting Proxmox Backup Server 3 (Reboot recommended)" msg_ok "Completed Install Routines" ;; esac } header_info while true; do read -p "Start the Update to Proxmox Backup Server 3 Script (y/n)?" yn case $yn in [Yy]*) break ;; [Nn]*) clear exit ;; *) echo "Please answer yes or no." ;; esac done start_routines ================================================ FILE: tools/pve/pbs4-upgrade.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE header_info() { clear cat <<"EOF" ____ ____ _____ __ __ __ __ __ / __ \/ __ ) ___// // / / / / /___ ____ __________ _____/ /__ / /_/ / __ \__ \/ // /_ / / / / __ \/ __ `/ ___/ __ `/ __ / _ \ / ____/ /_/ /__/ /__ __/ / /_/ / /_/ / /_/ / / / /_/ / /_/ / __/ /_/ /_____/____/ /_/ \____/ .___/\__, /_/ \__,_/\__,_/\___/ /_/ /____/ EOF } RD=$(echo "\033[01;31m") YW=$(echo "\033[33m") GN=$(echo "\033[1;92m") CL=$(echo "\033[m") BFR="\\r\\033[K" HOLD="-" CM="${GN}✓${CL}" CROSS="${RD}✗${CL}" set -euo pipefail shopt -s inherit_errexit nullglob msg_info() { echo -ne " ${HOLD} ${YW}$1..."; } msg_ok() { echo -e "${BFR} ${CM} ${GN}$1${CL}"; } msg_error() { echo -e "${BFR} ${CROSS} ${RD}$1${CL}"; } # Telemetry source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func) 2>/dev/null || true declare -f init_tool_telemetry &>/dev/null && init_tool_telemetry "pbs4-upgrade" "pve" start_routines() { header_info CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "PBS 3 BACKUP" --menu \ "\nMake a backup of /etc/proxmox-backup to ensure recovery in worst case?" 14 58 2 \ "yes" " " "no" " " 3>&2 2>&1 1>&3) case $CHOICE in yes) msg_info "Backing up Proxmox Backup Server 3" tar czf "pbs3-etc-backup-$(date -I).tar.gz" -C "/etc" "proxmox-backup" msg_ok "Backed up Proxmox Backup Server 3" ;; no) msg_error "Selected no to Backup" ;; esac # --- Debian 13 Sources --- CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "PBS 4 SOURCES" --menu \ "Switch to Debian 13 (Trixie) sources for PBS 4?" 14 58 2 "yes" " " "no" " " 3>&2 2>&1 1>&3) case $CHOICE in yes) msg_info "Switching to Debian 13 (Trixie) Sources" rm -f /etc/apt/sources.list.d/*.list if [ -f /etc/apt/sources.list ]; then sed -i '/proxmox/d;/bookworm/d' /etc/apt/sources.list fi cat >/etc/apt/sources.list.d/debian.sources <&2 2>&1 1>&3) case $CHOICE in yes) msg_info "Adding 'pbs-enterprise' repository" cat >/etc/apt/sources.list.d/pbs-enterprise.sources <&2 2>&1 1>&3) case $CHOICE in yes) msg_info "Adding 'pbs-no-subscription' repository" cat >/etc/apt/sources.list.d/proxmox.sources <&2 2>&1 1>&3) case $CHOICE in yes) msg_info "Adding 'pbs-test' repository (disabled)" cat >/etc/apt/sources.list.d/pbs-test.sources <&2 2>&1 1>&3) case $CHOICE in yes) msg_info "Upgrading to Proxmox Backup Server 4 (Patience)" apt update DEBIAN_FRONTEND=noninteractive apt -o Dpkg::Options::="--force-confold" dist-upgrade -y msg_ok "System upgraded to PBS 4" ;; no) msg_error "Selected no to upgrade" ;; esac # --- Reboot --- CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "REBOOT" --menu \ "\nReboot Proxmox Backup Server 4 now? (recommended)" 11 58 2 "yes" " " "no" " " \ 3>&2 2>&1 1>&3) case $CHOICE in yes) msg_info "Rebooting PBS 4" sleep 2 msg_ok "Upgrade Complete" reboot ;; no) msg_error "Selected no to Reboot (Reboot recommended)" msg_ok "Upgrade Complete" ;; esac } header_info while true; do read -rp "Start the Upgrade to Proxmox Backup Server 4 Script (y/n)? " yn case $yn in [Yy]*) break ;; [Nn]*) clear exit ;; *) echo "Please answer yes or no." ;; esac done start_routines ================================================ FILE: tools/pve/post-install-hook-examples.sh ================================================ #!/usr/bin/env bash # ============================================================================ # Community-Scripts ProxmoxVE — Post-Install Hook: Example Library # ---------------------------------------------------------------------------- # This file is NOT meant to be executed as-is. # It is a collection of complete, copy-pasteable example hooks for the # optional `var_post_install` feature in build.func. # # HOW IT WORKS # ------------ # In the ct/*.sh CT scripts (or via Advanced Settings → Step 28) you can # point `var_post_install` to an absolute path on the Proxmox HOST, e.g.: # # # in /root/.community-scripts/default.vars # var_post_install=/opt/community-scripts/hooks/notify.sh # # # OR per-app, in app.vars # var_post_install=/opt/community-scripts/hooks/vaultwarden-postprovision.sh # # # OR interactively in the Advanced Settings whiptail (Step 28). # # The hook runs ON THE PROXMOX HOST (NOT inside the LXC) as root, # AFTER the container is fully provisioned, started and the description # is set. stdout/stderr is captured to: # # /var/log/community-scripts/post-install-.log # # AVAILABLE ENV VARIABLES # ----------------------- # APP - Pretty name (e.g. "Vaultwarden") # NSAPP - Slug / lowercase (e.g. "vaultwarden") # CTID - Numeric container ID (e.g. "103") # IP - IPv4 address of the LXC (e.g. "192.168.1.50") # HN - Hostname (e.g. "vaultwarden") # STORAGE - Storage where the rootfs lives (e.g. "local-lvm") # BRG - Bridge (e.g. "vmbr0") # # GENERAL TIPS # ------------ # - Use `set -euo pipefail` so failures actually surface. # - Use `|| true` on best-effort steps you do not want to abort the hook. # - The file just needs to be a valid script. `+x` is optional — it is # invoked via `bash `. Shebang is honored only if you call it # yourself; otherwise the shebang line is purely cosmetic. # - If the hook exits non-zero, the user gets a whiptail popup with the # last 15 log lines. The LXC creation itself is NOT rolled back. # - Keep hooks idempotent — they may be re-run if you recreate a CT. # # HOW TO USE THIS FILE # -------------------- # 1. Copy ONE example block (between the BEGIN/END markers) into a new # file on the Proxmox host, e.g. /opt/community-scripts/hooks/notify.sh # 2. chmod +x /opt/community-scripts/hooks/notify.sh (optional) # 3. Set var_post_install in default.vars / app.vars or pick the path # in Advanced Settings. # ============================================================================ # ============================================================================ # ▼▼▼ EXAMPLE 1 — BEGIN ▼▼▼ # ---------------------------------------------------------------------------- # Name : minimal-logger.sh # Purpose : Append every newly created LXC to a single CSV-ish log. # Difficulty : ⭐ Beginner # Side effects: Writes to /var/log/community-scripts/created-lxcs.log # Use case : You just want a paper trail of "what got created when". # ============================================================================ #!/usr/bin/env bash set -euo pipefail LOG_DIR="/var/log/community-scripts" LOG_FILE="${LOG_DIR}/created-lxcs.log" mkdir -p "$LOG_DIR" # Header on first use if [[ ! -s "$LOG_FILE" ]]; then echo "timestamp;ctid;app;hostname;ip;bridge;storage" >"$LOG_FILE" fi printf '%s;%s;%s;%s;%s;%s;%s\n' \ "$(date -Iseconds)" \ "${CTID}" \ "${APP}" \ "${HN}" \ "${IP}" \ "${BRG}" \ "${STORAGE}" \ >>"$LOG_FILE" echo "Logged ${APP} (CTID=${CTID}) to ${LOG_FILE}" # ▲▲▲ EXAMPLE 1 — END ▲▲▲ # ============================================================================ # ▼▼▼ EXAMPLE 2 — BEGIN ▼▼▼ # ---------------------------------------------------------------------------- # Name : discord-gotify-notify.sh # Purpose : Send a rich Discord embed AND a Gotify push notification # whenever a new LXC is provisioned. # Difficulty : ⭐⭐ Intermediate # Requires : curl on the host (default), reachable webhook URLs. # Side effects: Outbound HTTPS to Discord + your Gotify server. # ============================================================================ #!/usr/bin/env bash set -euo pipefail # --- CONFIG (edit me) ------------------------------------------------------- DISCORD_WEBHOOK="https://discord.com/api/webhooks/XXXXXXXX/YYYYYYYY" GOTIFY_URL="https://gotify.example.com" GOTIFY_TOKEN="AbCdEfGhIjKlMnO" GOTIFY_PRIORITY=5 # ---------------------------------------------------------------------------- # Resolve the Proxmox node's hostname for context NODE="$(hostname -s)" TS="$(date -Iseconds)" # --- Discord embed ---------------------------------------------------------- read -r -d '' DISCORD_PAYLOAD </dev/null || echo "WARN: Discord webhook failed (non-fatal)" # --- Gotify push ------------------------------------------------------------ curl -fsS --max-time 10 \ -H "X-Gotify-Key: ${GOTIFY_TOKEN}" \ -F "title=Proxmox: ${APP} LXC created" \ -F "message=CTID=${CTID} IP=${IP} HN=${HN} on ${NODE}" \ -F "priority=${GOTIFY_PRIORITY}" \ "${GOTIFY_URL}/message" \ >/dev/null || echo "WARN: Gotify push failed (non-fatal)" echo "Notifications dispatched for CTID=${CTID}" # ▲▲▲ EXAMPLE 2 — END ▲▲▲ # ============================================================================ # ▼▼▼ EXAMPLE 3 — BEGIN ▼▼▼ # ---------------------------------------------------------------------------- # Name : auto-pool-tags-backup.sh # Purpose : Add the new LXC to a Proxmox pool, append cluster-wide tags, # register a DNS record in pi-hole, and trigger an immediate # snapshot backup to a configured storage. # Difficulty : ⭐⭐⭐ Advanced # Requires : pvesh, pct, vzdump (host-side; available by default on PVE), # a reachable pi-hole admin API. # ============================================================================ #!/usr/bin/env bash set -euo pipefail # --- CONFIG (edit me) ------------------------------------------------------- TARGET_POOL="auto-lxc" EXTRA_TAGS=("auto-provisioned" "${NSAPP}") # community-script tag is set by build.func BACKUP_STORAGE="pbs-main" # set to "" to skip initial backup PIHOLE_HOST="192.168.1.5" PIHOLE_PASSWORD="changeme" # web-UI password DNS_DOMAIN="lan" # FQDN will be ${HN}.${DNS_DOMAIN} # ---------------------------------------------------------------------------- # 1) Ensure the pool exists, then attach the CT if ! pvesh get "/pools/${TARGET_POOL}" >/dev/null 2>&1; then echo "Creating pool: ${TARGET_POOL}" pvesh create /pools --poolid "${TARGET_POOL}" --comment "Auto-created by post-install hook" || true fi echo "Adding CTID=${CTID} to pool=${TARGET_POOL}" pvesh set "/pools/${TARGET_POOL}" --vms "${CTID}" || echo "WARN: pool attach failed (non-fatal)" # 2) Merge new tags with existing ones (preserve community-script etc.) CURRENT_TAGS="$(pct config "${CTID}" | awk -F': ' '/^tags:/{print $2}')" declare -A TAG_SET IFS=';' read -r -a CUR_ARR <<<"${CURRENT_TAGS:-}" for t in "${CUR_ARR[@]}"; do [[ -n "$t" ]] && TAG_SET["$t"]=1; done for t in "${EXTRA_TAGS[@]}"; do [[ -n "$t" ]] && TAG_SET["$t"]=1; done NEW_TAGS="$( IFS=';' echo "${!TAG_SET[*]}" )" echo "Setting tags: ${NEW_TAGS}" pct set "${CTID}" --tags "${NEW_TAGS}" || echo "WARN: tag update failed (non-fatal)" # 3) Register DNS in pi-hole (custom DNS record) FQDN="${HN}.${DNS_DOMAIN}" echo "Registering DNS: ${FQDN} → ${IP} on pi-hole ${PIHOLE_HOST}" SID="$(curl -fsS --max-time 5 \ -d "pw=${PIHOLE_PASSWORD}" \ "http://${PIHOLE_HOST}/api/auth" 2>/dev/null | sed -nE 's/.*"sid":"([^"]+)".*/\1/p' || true)" if [[ -n "${SID}" ]]; then curl -fsS --max-time 5 -X PUT \ -H "Content-Type: application/json" \ -H "sid: ${SID}" \ -d "{\"hosts\":[\"${IP} ${FQDN}\"]}" \ "http://${PIHOLE_HOST}/api/config/dns/hosts" >/dev/null || echo "WARN: pi-hole DNS update failed (non-fatal)" curl -fsS --max-time 5 -X DELETE -H "sid: ${SID}" "http://${PIHOLE_HOST}/api/auth" >/dev/null || true else echo "WARN: could not obtain pi-hole session (skipping DNS)" fi # 4) Initial backup (best-effort, can take a few minutes) if [[ -n "${BACKUP_STORAGE}" ]]; then if pvesh get "/storage/${BACKUP_STORAGE}" >/dev/null 2>&1; then echo "Triggering initial backup of CTID=${CTID} to ${BACKUP_STORAGE}" vzdump "${CTID}" \ --storage "${BACKUP_STORAGE}" \ --mode snapshot \ --compress zstd \ --notes-template "Initial backup of ${APP} (CTID=${CTID})" \ --notification-mode auto || echo "WARN: initial backup failed (non-fatal)" else echo "Backup storage '${BACKUP_STORAGE}' not found — skipping." fi fi echo "Post-provision routine complete for ${APP} (CTID=${CTID})" # ▲▲▲ EXAMPLE 3 — END ▲▲▲ # ============================================================================ # ▼▼▼ EXAMPLE 4 — BEGIN ▼▼▼ # ---------------------------------------------------------------------------- # Name : inject-ssh-and-monitoring.sh # Purpose : Push the host's admin SSH key into the new LXC, install the # Beszel monitoring agent inside the container, and register # an Uptime-Kuma HTTP push monitor for the LXC's IP. # Difficulty : ⭐⭐⭐ Advanced # Requires : pct (host), curl (inside LXC), reachable Beszel hub + # Uptime-Kuma push URL. # ============================================================================ #!/usr/bin/env bash set -euo pipefail # --- CONFIG (edit me) ------------------------------------------------------- ADMIN_KEY="/root/.ssh/admin_ed25519.pub" BESZEL_HUB_URL="http://192.168.1.10:8090" BESZEL_AGENT_KEY="ssh-ed25519 AAAA... beszel@hub" # public key of the hub UPTIME_KUMA_PUSH_BASE="http://uptime.lan/api/push/abc123" # ---------------------------------------------------------------------------- # 1) Inject the admin SSH key if [[ -f "${ADMIN_KEY}" ]]; then echo "Pushing admin SSH key into CTID=${CTID}" pct exec "${CTID}" -- mkdir -p /root/.ssh pct exec "${CTID}" -- chmod 700 /root/.ssh pct push "${CTID}" "${ADMIN_KEY}" /root/.ssh/authorized_keys pct exec "${CTID}" -- chmod 600 /root/.ssh/authorized_keys else echo "WARN: ${ADMIN_KEY} not found on host — skipping SSH key injection" fi # 2) Wait for outbound networking inside the CT (max 30 s) echo "Waiting for network inside CTID=${CTID}…" for _ in $(seq 1 30); do if pct exec "${CTID}" -- bash -c 'getent hosts deb.debian.org >/dev/null 2>&1'; then break fi sleep 1 done # 3) Install Beszel agent inside the LXC echo "Installing Beszel agent inside CTID=${CTID}" pct exec "${CTID}" -- bash -s <<'AGENT_INSTALL' || echo "WARN: Beszel install failed" set -euo pipefail ARCH="$(uname -m)" case "$ARCH" in x86_64) ARCH_TAG=amd64 ;; aarch64) ARCH_TAG=arm64 ;; *) echo "Unsupported arch: $ARCH"; exit 1 ;; esac TMP=$(mktemp -d) cd "$TMP" curl -fsSL "https://github.com/henrygd/beszel/releases/latest/download/beszel-agent_linux_${ARCH_TAG}.tar.gz" \ | tar -xz install -m 0755 beszel-agent /usr/local/bin/beszel-agent cat >/etc/systemd/system/beszel-agent.service </dev/null || echo "WARN: Uptime-Kuma push failed (non-fatal)" echo "Provisioned monitoring for ${APP} (CTID=${CTID}, IP=${IP})" # ▲▲▲ EXAMPLE 4 — END ▲▲▲ # ============================================================================ # ▼▼▼ EXAMPLE 5 — BEGIN ▼▼▼ # ---------------------------------------------------------------------------- # Name : per-app-router.sh # Purpose : Single dispatcher hook that runs different actions # depending on the app being installed (NSAPP). Useful when # you want ONE hook for the whole cluster but distinct # behavior for, e.g., databases vs media services. # Difficulty : ⭐⭐⭐ Advanced # ============================================================================ #!/usr/bin/env bash set -euo pipefail # --- CONFIG (edit me) ------------------------------------------------------- DEFAULT_DNS_SUFFIX="lan" PROM_FILE_SD_DIR="/etc/prometheus/file_sd" # on the host that runs Prometheus # ---------------------------------------------------------------------------- log() { printf '[%s] %s\n' "$(date +%H:%M:%S)" "$*"; } # ---------- shared helpers -------------------------------------------------- register_prometheus_target() { local job="$1" port="$2" local file="${PROM_FILE_SD_DIR}/${job}.json" mkdir -p "${PROM_FILE_SD_DIR}" if [[ ! -f "$file" ]]; then echo "[]" >"$file"; fi python3 - "$file" "${IP}:${port}" "${HN}" "${NSAPP}" <<'PY' import json, sys path, target, hn, app = sys.argv[1:5] data = json.load(open(path)) # Avoid duplicates data = [b for b in data if target not in b.get("targets", [])] data.append({"targets": [target], "labels": {"hostname": hn, "app": app}}) json.dump(data, open(path, "w"), indent=2) PY log "Registered Prometheus target ${IP}:${port} in ${file}" } set_ct_options() { local cores="$1" mem="$2" desc="$3" pct set "${CTID}" --cores "${cores}" --memory "${mem}" || true pct set "${CTID}" --description "${desc}" || true } # ---------- per-app dispatch ------------------------------------------------ log "Dispatching post-install for NSAPP=${NSAPP} CTID=${CTID}" case "${NSAPP}" in # ------ Databases --------------------------------------------------------- postgresql | mariadb | mongodb | redis | valkey) log "Database role: bumping resources & adding to backup-critical pool" set_ct_options 4 4096 "DB: ${APP}" pvesh set /pools/db-critical --vms "${CTID}" 2>/dev/null || true register_prometheus_target "${NSAPP}-exporter" 9187 ;; # ------ *arr media stack -------------------------------------------------- sonarr | radarr | prowlarr | lidarr | readarr | bazarr) log "Media-arr role: tagging + Sonarr/Radarr API webhook" pct set "${CTID}" --tags "community-script;media;arr-stack" || true curl -fsS --max-time 5 -X POST \ "http://media-hub.${DEFAULT_DNS_SUFFIX}/hooks/arr-added" \ -H "Content-Type: application/json" \ -d "{\"app\":\"${NSAPP}\",\"ctid\":${CTID},\"ip\":\"${IP}\"}" \ >/dev/null || log "WARN: media-hub webhook failed" ;; # ------ Web apps that should sit behind NPM/Traefik ---------------------- vaultwarden | paperless-ngx | nextcloud | immich | bookstack) log "Web app role: registering reverse-proxy entry" curl -fsS --max-time 5 -X POST \ "http://traefik.${DEFAULT_DNS_SUFFIX}/api/dynamic-add" \ -H "Content-Type: application/json" \ -d "$( cat </dev/null || log "WARN: traefik registration failed" register_prometheus_target "blackbox-http" 80 ;; # ------ Default fallback -------------------------------------------------- *) log "No special handling for ${NSAPP} — applying generic defaults" register_prometheus_target "node-exporter" 9100 ;; esac log "Finished dispatcher for ${APP} (CTID=${CTID})" # ▲▲▲ EXAMPLE 5 — END ▲▲▲ # ============================================================================ # END OF EXAMPLES # ============================================================================ ================================================ FILE: tools/pve/post-pbs-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: tteck (tteckster) | MickLesk (CanbiZ) | thost96 # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE header_info() { clear cat <<"EOF" ____ ____ _____ ____ __ ____ __ ____ / __ \/ __ ) ___/ / __ \____ _____/ /_ / _/___ _____/ /_____ _/ / / / /_/ / __ \__ \ / /_/ / __ \/ ___/ __/ / // __ \/ ___/ __/ __ `/ / / / ____/ /_/ /__/ / / ____/ /_/ (__ ) /_ _/ // / / (__ ) /_/ /_/ / / / /_/ /_____/____/ /_/ \____/____/\__/ /___/_/ /_/____/\__/\__,_/_/_/ EOF } RD=$(echo "\033[01;31m") YW=$(echo "\033[33m") GN=$(echo "\033[1;92m") CL=$(echo "\033[m") BFR="\\r\\033[K" HOLD="-" CM="${GN}✓${CL}" CROSS="${RD}✗${CL}" set -euo pipefail shopt -s inherit_errexit nullglob msg_info() { echo -ne " ${HOLD} ${YW}$1..."; } msg_ok() { echo -e "${BFR} ${CM} ${GN}$1${CL}"; } msg_error() { echo -e "${BFR} ${CROSS} ${RD}$1${CL}"; } # Telemetry source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func) 2>/dev/null || true declare -f init_tool_telemetry &>/dev/null && init_tool_telemetry "post-pbs-install" "pve" # ---- helpers ---- get_pbs_codename() { awk -F'=' '/^VERSION_CODENAME=/{print $2}' /etc/os-release } repo_state_list() { local repo="$1" local file="" local state="missing" for f in /etc/apt/sources.list /etc/apt/sources.list.d/*.list; do [[ -f "$f" ]] || continue if grep -q "$repo" "$f"; then file="$f" if grep -qE "^[^#].*${repo}" "$f"; then state="active" elif grep -qE "^#.*${repo}" "$f"; then state="disabled" fi break fi done echo "$state $file" } component_exists_in_sources() { local component="$1" grep -h -E "^[^#]*Components:[^#]*\b${component}\b" /etc/apt/sources.list.d/*.sources 2>/dev/null | grep -q . } require_whiptail() { if ! command -v whiptail >/dev/null 2>&1; then msg_error "Missing dependency: whiptail" echo -e "Install it first (e.g. apt update && apt install -y whiptail), then re-run this script." exit 127 fi } # ---- main ---- main() { header_info echo -e "\nThis script will Perform Post Install Routines.\n" while true; do read -rp "Start the Proxmox Backup Server Post Install Script (y/n)? " yn case $yn in [Yy]*) break ;; [Nn]*) clear exit ;; *) echo "Please answer yes or no." ;; esac done if command -v pveversion >/dev/null 2>&1; then echo -e "\n🛑 PVE Detected, Wrong Script!\n" exit 232 fi local CODENAME CODENAME="$(get_pbs_codename)" case "$CODENAME" in bookworm) require_whiptail start_routines_3 ;; trixie) require_whiptail start_routines_4 ;; *) msg_error "Unsupported Debian codename: $CODENAME" echo -e "Supported: bookworm (PBS 3.x) and trixie (PBS 4.x)" exit 105 ;; esac } # ---- PBS 3.x (Bookworm) ---- start_routines_3() { header_info local VERSION="bookworm" # --- Debian sources --- CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "PBS SOURCES" --menu \ "Correct Debian sources for Proxmox Backup Server 3.x?" 14 58 2 "yes" " " "no" " " 3>&2 2>&1 1>&3) case $CHOICE in yes) msg_info "Correcting Debian Sources" cat </etc/apt/sources.list deb http://deb.debian.org/debian ${VERSION} main contrib deb http://deb.debian.org/debian ${VERSION}-updates main contrib deb http://security.debian.org/debian-security ${VERSION}-security main contrib EOF msg_ok "Corrected Debian Sources" ;; no) msg_error "Selected no to Correcting Debian Sources" ;; esac # --- Enterprise repo --- read -r state file <<<"$(repo_state_list pbs-enterprise)" case $state in active) sed -i "s/^[^#].*pbs-enterprise/# &/" "$file" msg_ok "Disabled 'pbs-enterprise' repository" ;; disabled) msg_ok "'pbs-enterprise' already disabled" ;; missing) cat >/etc/apt/sources.list.d/pbs-enterprise.list </etc/apt/sources.list.d/pbs-install-repo.list </etc/apt/sources.list.d/pbstest-for-beta.list <&2 2>&1 1>&3) case $CHOICE in yes) msg_info "Correcting Debian Sources (deb822)" rm -f /etc/apt/sources.list.d/*.list if [ -f /etc/apt/sources.list ]; then sed -i '/proxmox/d;/bookworm/d' /etc/apt/sources.list fi cat >/etc/apt/sources.list.d/debian.sources <&2 2>&1 1>&3) case $CHOICE in yes) msg_info "Disabling 'pbs-enterprise' repository" # Use Enabled: false instead of commenting to avoid malformed entry if grep -q "^Enabled:" /etc/apt/sources.list.d/pbs-enterprise.sources 2>/dev/null; then sed -i 's/^Enabled:.*/Enabled: false/' /etc/apt/sources.list.d/pbs-enterprise.sources else echo "Enabled: false" >>/etc/apt/sources.list.d/pbs-enterprise.sources fi msg_ok "Disabled 'pbs-enterprise' repository" ;; no) msg_error "Keeping 'pbs-enterprise' active (subscription required!)" ;; esac else cat >/etc/apt/sources.list.d/pbs-enterprise.sources </etc/apt/sources.list.d/proxmox.sources </etc/apt/sources.list.d/pbs-test.sources <&2 2>&1 1>&3) case $CHOICE in yes) whiptail --backtitle "Proxmox VE Helper Scripts" --msgbox \ "Supporting the software's development team is essential.\nPlease consider buying a subscription." 10 58 msg_info "Disabling subscription nag" echo "DPkg::Post-Invoke { \"if [ -s /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js ] && ! grep -q -F 'NoMoreNagging' /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js; then sed -i '/data\\.status/{s/\\!//;s/active/NoMoreNagging/}' /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js; fi\" };" >/etc/apt/apt.conf.d/no-nag-script msg_ok "Disabled subscription nag (clear browser cache!)" ;; no) msg_error "Selected no to Disabling subscription nag" rm -f /etc/apt/apt.conf.d/no-nag-script 2>/dev/null ;; esac apt --reinstall install proxmox-widget-toolkit &>/dev/null || msg_error "Widget toolkit reinstall failed" # Update CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "UPDATE" --menu \ "Update Proxmox Backup Server now?" 11 58 2 "yes" " " "no" " " 3>&2 2>&1 1>&3) case $CHOICE in yes) msg_info "Updating Proxmox Backup Server (Patience)" apt update &>/dev/null || msg_error "apt update failed" apt -y dist-upgrade &>/dev/null || msg_error "apt dist-upgrade failed" msg_ok "Updated Proxmox Backup Server" ;; no) msg_error "Selected no to updating Proxmox Backup Server" ;; esac # Reminder whiptail --backtitle "Proxmox VE Helper Scripts" --title "Post-Install Reminder" --msgbox \ "IMPORTANT: Please run this script on every PBS node individually if you have multiple nodes. After completing these steps, it is strongly recommended to REBOOT your node. After the upgrade or post-install routines, always clear your browser cache or perform a hard reload (Ctrl+Shift+R) before using the PBS Web UI to avoid UI display issues." 20 80 # Reboot CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "REBOOT" --menu \ "Reboot Proxmox Backup Server now? (recommended)" 11 58 2 "yes" " " "no" " " 3>&2 2>&1 1>&3) case $CHOICE in yes) msg_info "Rebooting PBS" sleep 2 msg_ok "Completed Post Install Routines" reboot ;; no) msg_error "Selected no to Reboot (Reboot recommended)" msg_ok "Completed Post Install Routines" ;; esac } main ================================================ FILE: tools/pve/post-pmg-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: thost96 (thost96) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE header_info() { clear cat <<"EOF" ____ __ _________ ____ __ ____ __ ____ / __ \/ |/ / ____/ / __ \____ _____/ /_ / _/___ _____/ /_____ _/ / / / /_/ / /|_/ / / __ / /_/ / __ \/ ___/ __/ / // __ \/ ___/ __/ __ `/ / / / ____/ / / / /_/ / / ____/ /_/ (__ ) /_ _/ // / / (__ ) /_/ /_/ / / / /_/ /_/ /_/\____/ /_/ \____/____/\__/ /___/_/ /_/____/\__/\__,_/_/_/ EOF } RD=$(echo "\033[01;31m") YW=$(echo "\033[33m") GN=$(echo "\033[1;92m") CL=$(echo "\033[m") BFR="\\r\\033[K" HOLD="-" CM="${GN}✓${CL}" CROSS="${RD}✗${CL}" set -euo pipefail shopt -s inherit_errexit nullglob msg_info() { local msg="$1" echo -ne " ${HOLD} ${YW}${msg}..." } msg_ok() { local msg="$1" echo -e "${BFR} ${CM} ${GN}${msg}${CL}" } msg_error() { local msg="$1" echo -e "${BFR} ${CROSS} ${RD}${msg}${CL}" } # Telemetry source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func) 2>/dev/null || true declare -f init_tool_telemetry &>/dev/null && init_tool_telemetry "post-pmg-install" "pve" if ! dpkg -s proxmox-mailgateway-container >/dev/null 2>&1 && ! dpkg -s proxmox-mailgateway >/dev/null 2>&1; then msg_error "This script is only intended for Proxmox Mail Gateway" exit 232 fi repo_state() { # $1 = repo name (e.g. pmg-enterprise, pmg-no-subscription, pmgtest) local repo="$1" local file="" local state="missing" for f in /etc/apt/sources.list /etc/apt/sources.list.d/*.list /etc/apt/sources.list.d/*.sources; do [[ -f "$f" ]] || continue if grep -q "$repo" "$f"; then file="$f" if [[ "$f" == *.sources ]]; then # deb822 format: check Enabled field if grep -qiE '^Enabled:\s*no' "$f"; then state="disabled" else state="active" fi else # legacy format if grep -qE "^[^#].*${repo}" "$f"; then state="active" elif grep -qE "^#.*${repo}" "$f"; then state="disabled" fi fi break fi done echo "$state $file" } toggle_repo() { # $1 = file, $2 = action (enable|disable) local file="$1" action="$2" if [[ "$file" == *.sources ]]; then if [[ "$action" == "disable" ]]; then if grep -qiE '^Enabled:' "$file"; then sed -i 's/^Enabled:.*/Enabled: no/' "$file" else echo "Enabled: no" >>"$file" fi else sed -i 's/^Enabled:.*/Enabled: yes/' "$file" fi else if [[ "$action" == "disable" ]]; then sed -i '/^[^#]/s/^/# /' "$file" else sed -i 's/^# *//' "$file" fi fi } start_routines() { header_info VERSION="$(awk -F'=' '/^VERSION_CODENAME=/{ print $NF }' /etc/os-release)" # ---- SOURCES ---- CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "PMG SOURCES" --menu \ "This will set the correct Debian sources for Proxmox Mail Gateway.\n\nCorrect sources?" 14 58 2 \ "yes" " " \ "no" " " 3>&2 2>&1 1>&3) case $CHOICE in yes) msg_info "Correcting Debian Sources" cat </etc/apt/sources.list.d/debian.sources Types: deb URIs: http://deb.debian.org/debian Suites: ${VERSION} ${VERSION}-updates Components: main contrib non-free non-free-firmware Signed-By: /usr/share/keyrings/debian-archive-keyring.gpg Types: deb URIs: http://security.debian.org/debian-security Suites: ${VERSION}-security Components: main contrib non-free non-free-firmware Signed-By: /usr/share/keyrings/debian-archive-keyring.gpg EOF rm -f /etc/apt/sources.list msg_ok "Corrected Debian Sources" ;; no) msg_error "Selected no to Correcting Debian Sources" ;; esac # ---- PMG-ENTERPRISE ---- read -r state file <<<"$(repo_state pmg-enterprise)" case $state in active) CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "PMG-ENTERPRISE" \ --menu "'pmg-enterprise' repository is currently ENABLED.\n\nWhat do you want to do?" 14 58 3 \ "keep" "Keep as is" \ "disable" "Comment out (disable)" \ "delete" "Delete repo file" \ 3>&2 2>&1 1>&3) case $CHOICE in keep) msg_ok "Kept 'pmg-enterprise' repository" ;; disable) msg_info "Disabling 'pmg-enterprise' repository" toggle_repo "$file" disable msg_ok "Disabled 'pmg-enterprise' repository" ;; delete) msg_info "Deleting 'pmg-enterprise' repository file" rm -f "$file" msg_ok "Deleted 'pmg-enterprise' repository file" ;; esac ;; disabled) CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "PMG-ENTERPRISE" \ --menu "'pmg-enterprise' repository is currently DISABLED.\n\nWhat do you want to do?" 14 58 3 \ "enable" "Uncomment (enable)" \ "keep" "Keep disabled" \ "delete" "Delete repo file" \ 3>&2 2>&1 1>&3) case $CHOICE in enable) msg_info "Enabling 'pmg-enterprise' repository" toggle_repo "$file" enable msg_ok "Enabled 'pmg-enterprise' repository" ;; keep) msg_ok "Kept 'pmg-enterprise' repository disabled" ;; delete) msg_info "Deleting 'pmg-enterprise' repository file" rm -f "$file" msg_ok "Deleted 'pmg-enterprise' repository file" ;; esac ;; missing) CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "PMG-ENTERPRISE" \ --menu "Add 'pmg-enterprise' repository?\n\nOnly for subscription customers." 14 58 2 \ "no" " " \ "yes" " " \ --default-item "no" \ 3>&2 2>&1 1>&3) case $CHOICE in yes) msg_info "Adding 'pmg-enterprise' repository" cat >/etc/apt/sources.list.d/pmg-enterprise.sources <&2 2>&1 1>&3) case $CHOICE in keep) msg_ok "Kept 'pmg-no-subscription' repository" ;; disable) msg_info "Disabling 'pmg-no-subscription' repository" toggle_repo "$file" disable msg_ok "Disabled 'pmg-no-subscription' repository" ;; delete) msg_info "Deleting 'pmg-no-subscription' repository file" rm -f "$file" msg_ok "Deleted 'pmg-no-subscription' repository file" ;; esac ;; disabled) CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "PMG-NO-SUBSCRIPTION" \ --menu "'pmg-no-subscription' repository is currently DISABLED.\n\nWhat do you want to do?" 14 58 3 \ "enable" "Uncomment (enable)" \ "keep" "Keep disabled" \ "delete" "Delete repo file" \ 3>&2 2>&1 1>&3) case $CHOICE in enable) msg_info "Enabling 'pmg-no-subscription' repository" toggle_repo "$file" enable msg_ok "Enabled 'pmg-no-subscription' repository" ;; keep) msg_ok "Kept 'pmg-no-subscription' repository disabled" ;; delete) msg_info "Deleting 'pmg-no-subscription' repository file" rm -f "$file" msg_ok "Deleted 'pmg-no-subscription' repository file" ;; esac ;; missing) CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "PMG-NO-SUBSCRIPTION" \ --menu "Add 'pmg-no-subscription' repository?" 14 58 2 \ "yes" " " \ "no" " " \ 3>&2 2>&1 1>&3) case $CHOICE in yes) msg_info "Adding 'pmg-no-subscription' repository" cat >/etc/apt/sources.list.d/pmg-no-subscription.sources <&2 2>&1 1>&3) case $CHOICE in yes) msg_info "Adding 'pmgtest' repository (disabled)" cat >/etc/apt/sources.list.d/pmgtest.sources <&2 2>&1 1>&3) case $CHOICE in yes) whiptail --backtitle "Proxmox VE Helper Scripts" --msgbox --title "Support Subscriptions" \ "Supporting the software's development team is essential.\nPlease consider buying a subscription." 10 58 msg_info "Disabling subscription nag" cat >/etc/apt/apt.conf.d/no-nag-script <<'EOF' DPkg::Post-Invoke { "if [ -s /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js ] && ! grep -q -F 'NoMoreNagging' /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js; then sed -i '/data\.status/{s/\!//;s/active/NoMoreNagging/}' /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js; fi"; }; EOF cat >/etc/apt/apt.conf.d/no-nag-script-pmgmanagerlib-mobile <<'EOF' DPkg::Post-Invoke { "if [ -s /usr/share/javascript/pmg-gui/js/pmgmanagerlib-mobile.js ] && ! grep -q -F 'NoMoreNagging' /usr/share/javascript/pmg-gui/js/pmgmanagerlib-mobile.js; then sed -i '/data\.status/{s/\!//;s/active/NoMoreNagging/}' /usr/share/javascript/pmg-gui/js/pmgmanagerlib-mobile.js; fi"; }; EOF msg_ok "Disabled subscription nag (clear browser cache!)" ;; no) msg_error "Selected no to Disabling subscription nag" rm -f /etc/apt/apt.conf.d/no-nag-script 2>/dev/null ;; esac apt --reinstall install proxmox-widget-toolkit pmg-gui &>/dev/null || msg_error "Widget toolkit reinstall failed" # ---- UPDATE ---- CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "UPDATE" --menu \ "Update Proxmox Mail Gateway now?" 11 58 2 "yes" " " "no" " " 3>&2 2>&1 1>&3) case $CHOICE in yes) msg_info "Updating Proxmox Mail Gateway (Patience)" apt update &>/dev/null || msg_error "apt update failed" apt -y dist-upgrade &>/dev/null || msg_error "apt dist-upgrade failed" msg_ok "Updated Proxmox Mail Gateway" ;; no) msg_error "Selected no to updating Proxmox Mail Gateway" ;; esac # ---- REMINDER ---- whiptail --backtitle "Proxmox VE Helper Scripts" --title "Post-Install Reminder" --msgbox \ "IMPORTANT: Please run this script on every PMG node individually if you have multiple nodes. After completing these steps, it is strongly recommended to REBOOT your node. After the upgrade or post-install routines, always clear your browser cache or perform a hard reload (Ctrl+Shift+R) before using the PMG Web UI to avoid UI display issues." 20 80 # ---- REBOOT ---- CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "REBOOT" --menu \ "Reboot Proxmox Mail Gateway now? (recommended)" 11 58 2 "yes" " " "no" " " 3>&2 2>&1 1>&3) case $CHOICE in yes) msg_info "Rebooting Proxmox Mail Gateway" sleep 2 msg_ok "Completed Post Install Routines" reboot ;; no) msg_error "Selected no to reboot (Reboot recommended)" msg_ok "Completed Post Install Routines" ;; esac } header_info echo -e "\nThis script will Perform Post Install Routines.\n" while true; do read -rp "Start the Proxmox Mail Gateway Post Install Script (y/n)? " yn case $yn in [Yy]*) break ;; [Nn]*) clear exit ;; *) echo "Please answer yes or no." ;; esac done start_routines ================================================ FILE: tools/pve/post-pve-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteckster | MickLesk (CanbiZ) # License: MIT # https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE header_info() { clear cat <<"EOF" ____ _ ________ ____ __ ____ __ ____ / __ \ | / / ____/ / __ \____ _____/ /_ / _/___ _____/ /_____ _/ / / / /_/ / | / / __/ / /_/ / __ \/ ___/ __/ / // __ \/ ___/ __/ __ `/ / / / ____/| |/ / /___ / ____/ /_/ (__ ) /_ _/ // / / (__ ) /_/ /_/ / / / /_/ |___/_____/ /_/ \____/____/\__/ /___/_/ /_/____/\__/\__,_/_/_/ EOF } RD=$(echo "\033[01;31m") YW=$(echo "\033[33m") GN=$(echo "\033[1;92m") CL=$(echo "\033[m") BFR="\\r\\033[K" HOLD="-" CM="${GN}✓${CL}" CROSS="${RD}✗${CL}" set -euo pipefail shopt -s inherit_errexit nullglob msg_info() { local msg="$1" echo -ne " ${HOLD} ${YW}${msg}..." } msg_ok() { local msg="$1" echo -e "${BFR} ${CM} ${GN}${msg}${CL}" } msg_error() { local msg="$1" echo -e "${BFR} ${CROSS} ${RD}${msg}${CL}" } # Telemetry source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func) 2>/dev/null || true declare -f init_tool_telemetry &>/dev/null && init_tool_telemetry "post-pve-install" "pve" get_pve_version() { local pve_ver pve_ver="$(pveversion | awk -F'/' '{print $2}' | awk -F'-' '{print $1}')" echo "$pve_ver" } get_pve_major_minor() { local ver="$1" local major minor IFS='.' read -r major minor _ <<<"$ver" echo "$major $minor" } component_exists_in_sources() { local component="$1" grep -h -E "^[^#]*Components:[^#]*\b${component}\b" /etc/apt/sources.list.d/*.sources 2>/dev/null | grep -q . } main() { header_info echo -e "\nThis script will Perform Post Install Routines.\n" while true; do read -p "Start the Proxmox VE Post Install Script (y/n)? " yn case $yn in [Yy]*) break ;; [Nn]*) clear exit ;; *) echo "Please answer yes or no." ;; esac done local PVE_VERSION PVE_MAJOR PVE_MINOR PVE_VERSION="$(get_pve_version)" read -r PVE_MAJOR PVE_MINOR <<<"$(get_pve_major_minor "$PVE_VERSION")" if [[ "$PVE_MAJOR" == "8" ]]; then if ((PVE_MINOR < 0 || PVE_MINOR > 9)); then msg_error "Unsupported Proxmox 8 version" exit 105 fi start_routines_8 elif [[ "$PVE_MAJOR" == "9" ]]; then if ((PVE_MINOR < 0 || PVE_MINOR > 1)); then msg_error "Only Proxmox 9.0-9.1.x is currently supported" exit 105 fi start_routines_9 else msg_error "Unsupported Proxmox VE major version: $PVE_MAJOR" echo -e "Supported: 8.0–8.9.x and 9.0–9.1.x" exit 105 fi } start_routines_8() { header_info # === Bookworm/8.x: .list-Files === CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "SOURCES" --menu "The package manager will use the correct sources to update and install packages on your Proxmox VE server.\n \nCorrect Proxmox VE sources?" 14 58 2 \ "yes" " " \ "no" " " 3>&2 2>&1 1>&3) case $CHOICE in yes) msg_info "Correcting Proxmox VE Sources" cat </etc/apt/sources.list deb http://deb.debian.org/debian bookworm main contrib deb http://deb.debian.org/debian bookworm-updates main contrib deb http://security.debian.org/debian-security bookworm-security main contrib EOF echo 'APT::Get::Update::SourceListWarnings::NonFreeFirmware "false";' >/etc/apt/apt.conf.d/no-bookworm-firmware.conf msg_ok "Corrected Proxmox VE Sources" ;; no) msg_error "Selected no to Correcting Proxmox VE Sources" ;; esac CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "PVE-ENTERPRISE" --menu "The 'pve-enterprise' repository is only available to users who have purchased a Proxmox VE subscription.\n \nDisable 'pve-enterprise' repository?" 14 58 2 \ "yes" " " \ "no" " " 3>&2 2>&1 1>&3) case $CHOICE in yes) msg_info "Disabling 'pve-enterprise' repository" cat </etc/apt/sources.list.d/pve-enterprise.list # deb https://enterprise.proxmox.com/debian/pve bookworm pve-enterprise EOF msg_ok "Disabled 'pve-enterprise' repository" ;; no) msg_error "Selected no to Disabling 'pve-enterprise' repository" ;; esac CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "PVE-NO-SUBSCRIPTION" --menu "The 'pve-no-subscription' repository provides access to all of the open-source components of Proxmox VE.\n \nEnable 'pve-no-subscription' repository?" 14 58 2 \ "yes" " " \ "no" " " 3>&2 2>&1 1>&3) case $CHOICE in yes) msg_info "Enabling 'pve-no-subscription' repository" cat </etc/apt/sources.list.d/pve-install-repo.list deb http://download.proxmox.com/debian/pve bookworm pve-no-subscription EOF msg_ok "Enabled 'pve-no-subscription' repository" ;; no) msg_error "Selected no to Enabling 'pve-no-subscription' repository" ;; esac CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "CEPH PACKAGE REPOSITORIES" --menu "The 'Ceph Package Repositories' provides access to both the 'no-subscription' and 'enterprise' repositories (initially disabled).\n \nCorrect 'ceph package sources?" 14 58 2 \ "yes" " " \ "no" " " 3>&2 2>&1 1>&3) case $CHOICE in yes) msg_info "Correcting 'ceph package repositories'" cat </etc/apt/sources.list.d/ceph.list # deb https://enterprise.proxmox.com/debian/ceph-quincy bookworm enterprise # deb http://download.proxmox.com/debian/ceph-quincy bookworm no-subscription # deb https://enterprise.proxmox.com/debian/ceph-reef bookworm enterprise # deb http://download.proxmox.com/debian/ceph-reef bookworm no-subscription EOF msg_ok "Corrected 'ceph package repositories'" ;; no) msg_error "Selected no to Correcting 'ceph package repositories'" ;; esac CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "PVETEST" --menu "The 'pvetest' repository can give advanced users access to new features and updates before they are officially released.\n \nAdd (Disabled) 'pvetest' repository?" 14 58 2 \ "yes" " " \ "no" " " 3>&2 2>&1 1>&3) case $CHOICE in yes) msg_info "Adding 'pvetest' repository and set disabled" cat </etc/apt/sources.list.d/pvetest-for-beta.list # deb http://download.proxmox.com/debian/pve bookworm pvetest EOF msg_ok "Added 'pvetest' repository" ;; no) msg_error "Selected no to Adding 'pvetest' repository" ;; esac post_routines_common } start_routines_9() { header_info # check if deb822 Sources (*.sources) exist if find /etc/apt/sources.list.d/ -maxdepth 1 -name '*.sources' | grep -q .; then whiptail --backtitle "Proxmox VE Helper Scripts" --title "Deb822 sources detected" \ --msgbox "Modern deb822 sources (*.sources) already exist.\n\nNo changes to sources format required.\n\nYou may still have legacy sources.list or .list files, which you can disable in the next step." 12 65 || true else check_and_disable_legacy_sources() { local LEGACY_COUNT=0 local listfile="/etc/apt/sources.list" # Check sources.list if [[ -f "$listfile" ]] && grep -qE '^\s*deb ' "$listfile"; then ((++LEGACY_COUNT)) fi # Check .list files local list_files list_files=$(find /etc/apt/sources.list.d/ -type f -name "*.list" 2>/dev/null) if [[ -n "$list_files" ]]; then LEGACY_COUNT=$((LEGACY_COUNT + $(echo "$list_files" | wc -l))) fi if ((LEGACY_COUNT > 0)); then # Show summary to user local MSG="Legacy APT sources found:\n" [[ -f "$listfile" ]] && MSG+=" - /etc/apt/sources.list\n" [[ -n "$list_files" ]] && MSG+="$(echo "$list_files" | sed 's|^| - |')\n" MSG+="\nDo you want to disable (comment out/rename) all legacy sources and use ONLY deb822 .sources format?\n\nRecommended for Proxmox VE 9." whiptail --backtitle "Proxmox VE Helper Scripts" --title "Disable legacy sources?" \ --yesno "$MSG" 18 80 if [[ $? -eq 0 ]]; then # Backup and disable sources.list if [[ -f "$listfile" ]] && grep -qE '^\s*deb ' "$listfile"; then cp "$listfile" "$listfile.bak" sed -i '/^\s*deb /s/^/# Disabled by Proxmox Helper Script /' "$listfile" msg_ok "Disabled entries in sources.list (backup: sources.list.bak)" fi # Rename all .list files to .list.bak if [[ -n "$list_files" ]]; then while IFS= read -r f; do mv "$f" "$f.bak" done <<<"$list_files" msg_ok "Renamed legacy .list files to .bak" fi else msg_error "Kept legacy sources as-is (may cause APT warnings)" fi fi } check_and_disable_legacy_sources # === Trixie/9.x: deb822 .sources === CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "SOURCES" --menu \ "The package manager will use the correct sources to update and install packages on your Proxmox VE 9 server.\n\nMigrate to deb822 sources format?" 14 58 2 \ "yes" " " \ "no" " " 3>&2 2>&1 1>&3) case $CHOICE in yes) msg_info "Correcting Proxmox VE Sources (deb822)" # remove all existing .list files rm -f /etc/apt/sources.list.d/*.list # remove bookworm and proxmox entries from sources.list (if it exists) if [ -f /etc/apt/sources.list ]; then sed -i '/proxmox/d;/bookworm/d' /etc/apt/sources.list fi # Create new deb822 sources cat >/etc/apt/sources.list.d/debian.sources <&2 2>&1 1>&3) case $CHOICE in keep) msg_ok "Kept 'pve-enterprise' repository" ;; disable) msg_info "Disabling 'pve-enterprise' repository" # Use Enabled: false instead of commenting to avoid malformed entry for file in /etc/apt/sources.list.d/*.sources; do if grep -q "Components:.*pve-enterprise" "$file"; then if grep -q "^Enabled:" "$file"; then sed -i 's/^Enabled:.*/Enabled: false/' "$file" else echo "Enabled: false" >>"$file" fi fi done msg_ok "Disabled 'pve-enterprise' repository" ;; delete) msg_info "Deleting 'pve-enterprise' repository file" for file in /etc/apt/sources.list.d/*.sources; do if grep -q "Components:.*pve-enterprise" "$file"; then rm -f "$file" fi done msg_ok "Deleted 'pve-enterprise' repository file" ;; esac else CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" \ --title "PVE-ENTERPRISE" \ --menu "The 'pve-enterprise' repository is only available to users who have purchased a Proxmox VE subscription.\n\nAdd 'pve-enterprise' repository (deb822)?" 14 58 2 \ "no" " " \ "yes" " " \ --default-item "no" \ 3>&2 2>&1 1>&3) case $CHOICE in yes) msg_info "Adding 'pve-enterprise' repository (deb822)" cat >/etc/apt/sources.list.d/pve-enterprise.sources </dev/null; then CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" \ --title "CEPH-ENTERPRISE" \ --menu "'ceph enterprise' repository already exists.\n\nWhat do you want to do?" 14 58 2 \ "keep" "Keep as is" \ "disable" "Comment out (disable) this repo" \ "delete" "Delete this repo file" \ 3>&2 2>&1 1>&3) case $CHOICE in keep) msg_ok "Kept 'ceph enterprise' repository" ;; disable) msg_info "Disabling 'ceph enterprise' repository" # Use Enabled: false instead of commenting to avoid malformed entry for file in /etc/apt/sources.list.d/*.sources; do if grep -q "enterprise.proxmox.com.*ceph" "$file"; then if grep -q "^Enabled:" "$file"; then sed -i 's/^Enabled:.*/Enabled: false/' "$file" else echo "Enabled: false" >>"$file" fi fi done msg_ok "Disabled 'ceph enterprise' repository" ;; delete) msg_info "Deleting 'ceph enterprise' repository file" for file in /etc/apt/sources.list.d/*.sources; do if grep -q "enterprise.proxmox.com.*ceph" "$file"; then rm -f "$file" fi done msg_ok "Deleted 'ceph enterprise' repository file" ;; esac fi # ---- PVE-NO-SUBSCRIPTION ---- REPO_FILE="" REPO_ACTIVE=0 REPO_COMMENTED=0 for file in /etc/apt/sources.list.d/*.sources; do if grep -q "Components:.*pve-no-subscription" "$file"; then REPO_FILE="$file" if grep -E '^[^#]*Components:.*pve-no-subscription' "$file" >/dev/null; then REPO_ACTIVE=1 elif grep -E '^#.*Components:.*pve-no-subscription' "$file" >/dev/null; then REPO_COMMENTED=1 fi break fi done if [[ "$REPO_ACTIVE" -eq 1 ]]; then CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" \ --title "PVE-NO-SUBSCRIPTION" \ --menu "'pve-no-subscription' repository is currently ENABLED.\n\nWhat do you want to do?" 14 58 3 \ "keep" "Keep as is" \ "disable" "Comment out (disable)" \ "delete" "Delete repo file" \ 3>&2 2>&1 1>&3) case $CHOICE in keep) msg_ok "Kept 'pve-no-subscription' repository" ;; disable) msg_info "Disabling (commenting) 'pve-no-subscription' repository" sed -i '/^\s*Types:/,/^$/s/^\([^#].*\)$/# \1/' "$REPO_FILE" msg_ok "Disabled 'pve-no-subscription' repository" ;; delete) msg_info "Deleting 'pve-no-subscription' repository file" rm -f "$REPO_FILE" msg_ok "Deleted 'pve-no-subscription' repository file" ;; esac elif [[ "$REPO_COMMENTED" -eq 1 ]]; then CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" \ --title "PVE-NO-SUBSCRIPTION" \ --menu "'pve-no-subscription' repository is currently DISABLED (commented out).\n\nWhat do you want to do?" 14 58 3 \ "enable" "Uncomment (enable)" \ "keep" "Keep disabled" \ "delete" "Delete repo file" \ 3>&2 2>&1 1>&3) case $CHOICE in enable) msg_info "Enabling (uncommenting) 'pve-no-subscription' repository" sed -i '/^#\s*Types:/,/^$/s/^#\s*//' "$REPO_FILE" msg_ok "Enabled 'pve-no-subscription' repository" ;; keep) msg_ok "Kept 'pve-no-subscription' repository disabled" ;; delete) msg_info "Deleting 'pve-no-subscription' repository file" rm -f "$REPO_FILE" msg_ok "Deleted 'pve-no-subscription' repository file" ;; esac else CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "PVE-NO-SUBSCRIPTION" \ --menu "The 'pve-no-subscription' repository provides access to all of the open-source components of Proxmox VE.\n\nAdd 'pve-no-subscription' repository (deb822)?" 14 58 2 \ "yes" " " \ "no" " " 3>&2 2>&1 1>&3) case $CHOICE in yes) msg_info "Adding 'pve-no-subscription' repository (deb822)" cat >/etc/apt/sources.list.d/proxmox.sources <&2 2>&1 1>&3) case $CHOICE in yes) msg_info "Adding 'ceph package repositories' (deb822)" cat >/etc/apt/sources.list.d/ceph.sources </dev/null; then if grep -q "^Enabled:" "$file"; then sed -i 's/^Enabled:.*/Enabled: false/' "$file" else echo "Enabled: false" >>"$file" fi fi done find /etc/apt/sources.list.d/ -type f -name "*.list" \ -exec sed -i '/enterprise.proxmox.com.*ceph/s/^/# /' {} \; msg_ok "Disabled all Ceph Enterprise repositories" ;; esac fi # ---- PVETEST ---- if component_exists_in_sources "pve-test"; then msg_ok "'pve-test' repository already exists (skipped)" else CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "PVETEST" \ --menu "The 'pve-test' repository can give advanced users access to new features and updates before they are officially released.\n\nAdd (Disabled) 'pvetest' repository (deb822)?" 14 58 2 \ "yes" " " \ "no" " " 3>&2 2>&1 1>&3) case $CHOICE in yes) msg_info "Adding 'pve-test' repository (deb822, disabled)" cat >/etc/apt/sources.list.d/pve-test.sources <&2 2>&1 1>&3) case $CHOICE in yes) whiptail --backtitle "Proxmox VE Helper Scripts" --msgbox --title "Support Subscriptions" "Supporting the software's development team is essential. Check their official website's Support Subscriptions for pricing. Without their dedicated work, we wouldn't have this exceptional software." 10 58 msg_info "Disabling subscription nag" # Create external script, this is needed because DPkg::Post-Invoke is fidly with quote interpretation mkdir -p /usr/local/bin cat >/usr/local/bin/pve-remove-nag.sh <<'EOF' #!/bin/sh WEB_JS=/usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js if [ -s "$WEB_JS" ] && ! grep -q NoMoreNagging "$WEB_JS"; then echo "Patching Web UI nag..." sed -i -e "/data\.status/ s/!//" -e "/data\.status/ s/active/NoMoreNagging/" "$WEB_JS" fi MOBILE_TPL=/usr/share/pve-yew-mobile-gui/index.html.tpl MARKER="" if [ -f "$MOBILE_TPL" ] && ! grep -q "$MARKER" "$MOBILE_TPL"; then echo "Patching Mobile UI nag..." printf "%s\n" \ "$MARKER" \ "" \ "" >> "$MOBILE_TPL" fi EOF chmod 755 /usr/local/bin/pve-remove-nag.sh cat >/etc/apt/apt.conf.d/no-nag-script <<'EOF' DPkg::Post-Invoke { "/usr/local/bin/pve-remove-nag.sh"; }; EOF chmod 644 /etc/apt/apt.conf.d/no-nag-script msg_ok "Disabled subscription nag (Delete browser cache)" ;; no) whiptail --backtitle "Proxmox VE Helper Scripts" --msgbox --title "Support Subscriptions" "Supporting the software's development team is essential. Check their official website's Support Subscriptions for pricing. Without their dedicated work, we wouldn't have this exceptional software." 10 58 msg_error "Selected no to Disabling subscription nag" [[ -f /etc/apt/apt.conf.d/no-nag-script ]] && rm /etc/apt/apt.conf.d/no-nag-script ;; esac apt --reinstall install proxmox-widget-toolkit &>/dev/null || msg_error "Widget toolkit reinstall failed" if ! systemctl is-active --quiet pve-ha-lrm; then CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "HIGH AVAILABILITY" --menu "Enable high availability?" 10 58 2 \ "yes" " " \ "no" " " 3>&2 2>&1 1>&3) case $CHOICE in yes) msg_info "Enabling high availability" systemctl enable -q --now pve-ha-lrm systemctl enable -q --now pve-ha-crm systemctl enable -q --now corosync msg_ok "Enabled high availability" ;; no) msg_error "Selected no to Enabling high availability" ;; esac fi if systemctl is-active --quiet pve-ha-lrm; then CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "HIGH AVAILABILITY" --menu "If you plan to utilize a single node instead of a clustered environment, you can disable unnecessary high availability (HA) services, thus reclaiming system resources.\n\nIf HA becomes necessary at a later stage, the services can be re-enabled.\n\nDisable high availability?" 18 58 2 \ "yes" " " \ "no" " " 3>&2 2>&1 1>&3) case $CHOICE in yes) msg_info "Disabling high availability" systemctl disable -q --now pve-ha-lrm systemctl disable -q --now pve-ha-crm msg_ok "Disabled high availability" CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "COROSYNC" --menu "Disable Corosync for a Proxmox VE Cluster?" 10 58 2 \ "yes" " " \ "no" " " 3>&2 2>&1 1>&3) case $CHOICE in yes) msg_info "Disabling Corosync" systemctl disable -q --now corosync msg_ok "Disabled Corosync" ;; no) msg_error "Selected no to Disabling Corosync" ;; esac ;; no) msg_error "Selected no to Disabling high availability" ;; esac fi CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "UPDATE" --menu "\nUpdate Proxmox VE now?" 11 58 2 \ "yes" " " \ "no" " " 3>&2 2>&1 1>&3) case $CHOICE in yes) msg_info "Updating Proxmox VE (Patience)" apt update &>/dev/null || msg_error "apt update failed" apt -y dist-upgrade &>/dev/null || msg_error "apt dist-upgrade failed" msg_ok "Updated Proxmox VE" ;; no) msg_error "Selected no to Updating Proxmox VE" ;; esac # Final message for all hosts in cluster and browser cache whiptail --backtitle "Proxmox VE Helper Scripts" --title "Post-Install Reminder" --msgbox \ "IMPORTANT: If you have multiple Proxmox VE hosts in a cluster, please make sure to run this script on every node individually. After completing these steps, it is strongly recommended to REBOOT your node. After the upgrade or post-install routines, always clear your browser cache or perform a hard reload (Ctrl+Shift+R) before using the Proxmox VE Web UI to avoid UI display issues. " 20 80 CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "REBOOT" --menu "\nReboot Proxmox VE now? (recommended)" 11 58 2 \ "yes" " " \ "no" " " 3>&2 2>&1 1>&3) case $CHOICE in yes) msg_info "Rebooting Proxmox VE" sleep 2 msg_ok "Completed Post Install Routines" reboot ;; no) msg_error "Selected no to Rebooting Proxmox VE (Reboot recommended)" msg_ok "Completed Post Install Routines" ;; esac } main ================================================ FILE: tools/pve/pve-privilege-converter.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk # Adapted from onethree7 (https://github.com/onethree7/proxmox-lxc-privilege-converter) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE if ! command -v curl >/dev/null 2>&1; then printf "\r\e[2K%b" '\033[93m Setup Source \033[m' >&2 apt-get update >/dev/null 2>&1 apt-get install -y curl >/dev/null 2>&1 fi source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVE/raw/branch/main/misc/core.func) source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func) 2>/dev/null || true load_functions declare -f init_tool_telemetry &>/dev/null && init_tool_telemetry "pve-privilege-converter" "pve" set -euo pipefail shopt -s inherit_errexit nullglob APP="PVE-Privilege-Converter" APP_TYPE="tools" header_info "$APP" check_root() { if [[ $EUID -ne 0 ]]; then msg_error "Script must be run as root" exit 104 fi } select_target_storage_and_container_id() { echo -e "\nSelect target storage for restored container:\n" mapfile -t target_storages < <(pvesm status --content images | awk 'NR > 1 {print $1}') for i in "${!target_storages[@]}"; do printf "%s) %s\n" "$((i + 1))" "${target_storages[$i]}" done while true; do read -rp "Enter number of target storage: " choice if [[ "$choice" =~ ^[0-9]+$ ]] && ((choice >= 1 && choice <= ${#target_storages[@]})); then TARGET_STORAGE="${target_storages[$((choice - 1))]}" break else echo "Invalid selection. Try again." fi done next_free_id=$(pvesh get /cluster/nextid 2>/dev/null || echo 999) [[ "$next_free_id" =~ ^[0-9]+$ ]] || next_free_id=999 echo "" read -rp "Suggested next free container ID: $next_free_id. Enter new container ID [default: $next_free_id]: " NEW_CONTAINER_ID NEW_CONTAINER_ID="${NEW_CONTAINER_ID:-$next_free_id}" } select_container() { mapfile -t lxc_list_raw < <(pct list | awk 'NR > 1 {print $1, $3}') lxc_list=() for entry in "${lxc_list_raw[@]}"; do [[ -n "$entry" ]] && lxc_list+=("$entry") done if [[ ${#lxc_list[@]} -eq 0 ]]; then msg_error "No containers found" exit 234 fi PS3="Enter number of container to convert: " select opt in "${lxc_list[@]}"; do if [[ -n "$opt" ]]; then read -r CONTAINER_ID CONTAINER_NAME <<<"$opt" CONTAINER_NAME="${CONTAINER_NAME:-}" break else echo "Invalid selection. Try again." fi done } select_backup_storage() { echo -e "Select backup storage (temporary vzdump location):" mapfile -t backup_storages < <(pvesm status --content backup | awk 'NR > 1 {print $1}') local PS3="Enter number of backup storage: " select opt in "${backup_storages[@]}"; do if [[ -n "$opt" ]]; then BACKUP_STORAGE="$opt" break else echo "Invalid selection. Try again." fi done } backup_container() { msg_custom "📦" "\e[36m" "Backing up container $CONTAINER_ID" vzdump_output=$(mktemp) vzdump "$CONTAINER_ID" --compress zstd --storage "$BACKUP_STORAGE" --mode snapshot | tee "$vzdump_output" BACKUP_PATH=$(awk '/tar.zst/ {print $NF}' "$vzdump_output" | tr -d "'") if [ -z "$BACKUP_PATH" ] || ! grep -q "Backup job finished successfully" "$vzdump_output"; then rm "$vzdump_output" msg_error "Backup failed" exit 235 fi rm "$vzdump_output" msg_ok "Backup complete: $BACKUP_PATH" } perform_conversion() { if pct config "$CONTAINER_ID" | grep -q 'unprivileged: 1'; then UNPRIVILEGED=true else UNPRIVILEGED=false fi msg_custom "🛠️" "\e[36m" "Restoring as $(if $UNPRIVILEGED; then echo privileged; else echo unprivileged; fi) container" restore_opts=("$NEW_CONTAINER_ID" "$BACKUP_PATH" --storage "$TARGET_STORAGE") if $UNPRIVILEGED; then restore_opts+=(--unprivileged false) else restore_opts+=(--unprivileged) fi if pct restore "${restore_opts[@]}" -ignore-unpack-errors 1; then msg_ok "Conversion successful" else msg_error "Conversion failed" exit 235 fi } manage_states() { read -rp "Shutdown source and start new container? [Y/n]: " answer answer=${answer:-Y} if [[ $answer =~ ^[Yy] ]]; then if pct status "$CONTAINER_ID" | grep -q running; then pct shutdown "$CONTAINER_ID" for i in {1..36}; do sleep 5 ! pct status "$CONTAINER_ID" | grep -q running && break done if pct status "$CONTAINER_ID" | grep -q running; then read -rp "Timeout reached. Force shutdown? [Y/n]: " force if [[ ${force:-Y} =~ ^[Yy] ]]; then pkill -9 -f "lxc-start -F -n $CONTAINER_ID" fi fi else msg_custom "ℹ️" "\e[36m" "Source container $CONTAINER_ID is already stopped" fi pct start "$NEW_CONTAINER_ID" msg_ok "New container started" else msg_custom "ℹ️" "\e[36m" "Skipped container state change" fi } cleanup_files() { read -rp "Delete backup archive? [$BACKUP_PATH] [Y/n]: " cleanup if [[ ${cleanup:-Y} =~ ^[Yy] ]]; then rm -f "$BACKUP_PATH" && msg_ok "Removed backup archive" else msg_custom "💾" "\e[36m" "Retained backup archive" fi } summary() { local conversion="Unknown" if [[ -n "${UNPRIVILEGED:-}" ]]; then if $UNPRIVILEGED; then conversion="Unprivileged → Privileged" else conversion="Privileged → Unprivileged" fi fi echo msg_custom "📄" "\e[36m" "Summary:" msg_custom " " "\e[36m" "$(printf "%-22s %s" "Original Container:" "$CONTAINER_ID ($CONTAINER_NAME)")" msg_custom " " "\e[36m" "$(printf "%-22s %s" "Backup Storage:" "$BACKUP_STORAGE")" msg_custom " " "\e[36m" "$(printf "%-22s %s" "Target Storage:" "$TARGET_STORAGE")" msg_custom " " "\e[36m" "$(printf "%-22s %s" "Backup Path:" "$BACKUP_PATH")" msg_custom " " "\e[36m" "$(printf "%-22s %s" "New Container ID:" "$NEW_CONTAINER_ID")" msg_custom " " "\e[36m" "$(printf "%-22s %s" "Privilege Conversion:" "$conversion")" echo } main() { header_info check_root select_container select_backup_storage backup_container select_target_storage_and_container_id perform_conversion manage_states cleanup_files summary } main ================================================ FILE: tools/pve/pve8-upgrade.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT # https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE header_info() { clear cat <<"EOF" ____ _ ____________ __ ______ __________ ___ ____ ______ / __ \ | / / ____( __ ) / / / / __ \/ ____/ __ \/ | / __ \/ ____/ / /_/ / | / / __/ / __ | / / / / /_/ / / __/ /_/ / /| | / / / / __/ / ____/| |/ / /___/ /_/ / / /_/ / ____/ /_/ / _, _/ ___ |/ /_/ / /___ /_/ |___/_____/\____/ \____/_/ \____/_/ |_/_/ |_/_____/_____/ EOF } RD=$(echo "\033[01;31m") YW=$(echo "\033[33m") GN=$(echo "\033[1;92m") CL=$(echo "\033[m") BFR="\\r\\033[K" HOLD="-" CM="${GN}✓${CL}" CROSS="${RD}✗${CL}" set -euo pipefail shopt -s inherit_errexit nullglob msg_info() { local msg="$1" echo -ne " ${HOLD} ${YW}${msg}..." } msg_ok() { local msg="$1" echo -e "${BFR} ${CM} ${GN}${msg}${CL}" } msg_error() { local msg="$1" echo -e "${BFR} ${CROSS} ${RD}${msg}${CL}" } # Telemetry source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func) 2>/dev/null || true declare -f init_tool_telemetry &>/dev/null && init_tool_telemetry "pve8-upgrade" "pve" start_routines() { header_info whiptail --backtitle "Proxmox VE Helper Scripts" --msgbox --title "PVE8 SOURCES" "This will set the correct sources to update and install Proxmox VE 8." 10 58 msg_info "Changing to Proxmox VE 8 Sources" cat </etc/apt/sources.list deb http://ftp.debian.org/debian bookworm main contrib deb http://ftp.debian.org/debian bookworm-updates main contrib deb http://security.debian.org/debian-security bookworm-security main contrib EOF msg_ok "Changed to Proxmox VE 8 Sources" whiptail --backtitle "Proxmox VE Helper Scripts" --msgbox --title "PVE8-ENTERPRISE" "The 'pve-enterprise' repository is only available to users who have purchased a Proxmox VE subscription." 10 58 msg_info "Disabling 'pve-enterprise' repository" cat </etc/apt/sources.list.d/pve-enterprise.list # deb https://enterprise.proxmox.com/debian/pve bookworm pve-enterprise EOF msg_ok "Disabled 'pve-enterprise' repository" whiptail --backtitle "Proxmox VE Helper Scripts" --msgbox --title "PVE8-NO-SUBSCRIPTION" "The 'pve-no-subscription' repository provides access to all of the open-source components of Proxmox VE." 10 58 msg_info "Enabling 'pve-no-subscription' repository" cat </etc/apt/sources.list.d/pve-install-repo.list deb http://download.proxmox.com/debian/pve bookworm pve-no-subscription EOF msg_ok "Enabled 'pve-no-subscription' repository" whiptail --backtitle "Proxmox VE Helper Scripts" --msgbox --title "PVE8 CEPH PACKAGE REPOSITORIES" "The 'Ceph Package Repositories' provides access to both the 'no-subscription' and 'enterprise' repositories." 10 58 msg_info "Enabling 'ceph package repositories'" cat </etc/apt/sources.list.d/ceph.list # deb https://enterprise.proxmox.com/debian/ceph-quincy bookworm enterprise deb http://download.proxmox.com/debian/ceph-quincy bookworm no-subscription EOF msg_ok "Enabled 'ceph package repositories'" whiptail --backtitle "Proxmox VE Helper Scripts" --msgbox --title "PVE8 TEST" "The 'pvetest' repository can give advanced users access to new features and updates before they are officially released (Disabled)." 10 58 msg_info "Adding 'pvetest' repository and set disabled" cat </etc/apt/sources.list.d/pvetest-for-beta.list # deb http://download.proxmox.com/debian/pve bookworm pvetest EOF msg_ok "Added 'pvetest' repository" whiptail --backtitle "Proxmox VE Helper Scripts" --msgbox --title "PVE8 UPDATE" "Updating to Proxmox VE 8" 10 58 msg_info "Updating to Proxmox VE 8 (Patience)" apt-get update DEBIAN_FRONTEND=noninteractive apt-get -o Dpkg::Options::="--force-confold" dist-upgrade -y msg_ok "Updated to Proxmox VE 8" CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "REBOOT" --menu "\nReboot Proxmox VE 8 now? (recommended)" 11 58 2 \ "yes" " " \ "no" " " 3>&2 2>&1 1>&3) case $CHOICE in yes) msg_info "Rebooting Proxmox VE 8" sleep 2 msg_ok "Completed Install Routines" reboot ;; no) msg_error "Selected no to Rebooting Proxmox VE 8 (Reboot recommended)" msg_ok "Completed Install Routines" ;; esac } header_info while true; do read -p "Start the Update to Proxmox VE 8 Script (y/n)?" yn case $yn in [Yy]*) break ;; [Nn]*) clear exit ;; *) echo "Please answer yes or no." ;; esac done if ! command -v pveversion >/dev/null 2>&1; then header_info msg_error "\n No PVE Detected!\n" exit fi if ! pveversion | grep -Eq "pve-manager/(7\.4-(16|17|18|19))"; then header_info msg_error "This version of Proxmox Virtual Environment is not supported" echo -e " PVE Version 7.4-16 or higher is required." echo -e "\nExiting..." sleep 3 exit fi start_routines ================================================ FILE: tools/pve/scaling-governor.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT # https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE set -e # Telemetry source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func) 2>/dev/null || true declare -f init_tool_telemetry &>/dev/null && init_tool_telemetry "scaling-governor" "pve" header_info() { clear cat < MSG_MAX_LENGTH)) && MSG_MAX_LENGTH=${#ITEM}+OFFSET GOVERNORS_MENU+=("$TAG" "$ITEM " "OFF") done < <(cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_available_governors | tr ' ' '\n' | grep -v "$current_governor") scaling_governor=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "Current CPU Scaling Governor is set to $current_governor" --checklist "\nSelect the Scaling Governor to use:\n" 16 $((MSG_MAX_LENGTH + 58)) 6 "${GOVERNORS_MENU[@]}" 3>&1 1>&2 2>&3 | tr -d '"') [ -z "$scaling_governor" ] && { whiptail --backtitle "Proxmox VE Helper Scripts" --title "No CPU Scaling Governor Selected" --msgbox "It appears that no CPU Scaling Governor was selected" 10 68 clear exit } echo "${scaling_governor}" | tee /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor >/dev/null current_governor=$(cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor) whiptail --backtitle "Proxmox VE Helper Scripts" --msgbox --title "Current CPU Scaling Governor" "\nCurrent CPU Scaling Governor has been set to $current_governor\n" 10 60 CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "CPU Scaling Governor" --menu "This will establish a crontab to maintain the CPU Scaling Governor configuration across reboots.\n \nSetup a crontab?" 14 68 2 \ "yes" " " \ "no" " " 3>&2 2>&1 1>&3) case $CHOICE in yes) set +e NEW_CRONTAB_COMMAND="(sleep 60 && echo \"$current_governor\" | tee /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor)" EXISTING_CRONTAB=$(crontab -l 2>/dev/null) if [[ -n "$EXISTING_CRONTAB" ]]; then TEMP_CRONTAB_FILE=$(mktemp) echo "$EXISTING_CRONTAB" | grep -v "@reboot (sleep 60 && echo*" >"$TEMP_CRONTAB_FILE" crontab "$TEMP_CRONTAB_FILE" rm "$TEMP_CRONTAB_FILE" fi ( crontab -l 2>/dev/null echo "@reboot $NEW_CRONTAB_COMMAND" ) | crontab - echo -e "\nCrontab Set (use 'crontab -e' to check)" ;; no) echo -e "\n\033[31mNOTE: Settings return to default after reboot\033[m\n" ;; esac echo -e "Current CPU Scaling Governor is set to \033[36m$current_governor\033[m\n" ================================================ FILE: tools/pve/update-apps.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: BvdBerg01 | Co-Author: remz1337 # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/refs/heads/main/misc/core.func) source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/api.func) 2>/dev/null || true declare -f init_tool_telemetry &>/dev/null && init_tool_telemetry "update-apps" "pve" # ============================================================================= # CONFIGURATION VARIABLES # Set these variables to skip interactive prompts (Whiptail dialogs) # ============================================================================= # var_backup: Enable/disable backup before update # Options: "yes" | "no" | "" (empty = interactive prompt) var_backup="${var_backup:-}" # var_backup_storage: Storage location for backups (only used if var_backup=yes) # Options: Storage name from /etc/pve/storage.cfg (e.g., "local", "nas-backup") # Leave empty for interactive selection var_backup_storage="${var_backup_storage:-}" # var_container: Which containers to update # Options: # - "all" : All containers with community-scripts tags # - "all_running" : Only running containers with community-scripts tags # - "all_stopped" : Only stopped containers with community-scripts tags # - "101,102,109" : Comma-separated list of specific container IDs # - "" : Interactive selection via Whiptail var_container="${var_container:-}" # var_unattended: Run updates without user interaction inside containers # Options: "yes" | "no" | "" (empty = interactive prompt) var_unattended="${var_unattended:-}" # var_skip_confirm: Skip initial confirmation dialog # Options: "yes" | "no" (default: no) var_skip_confirm="${var_skip_confirm:-no}" # var_auto_reboot: Automatically reboot containers that require it after update # Options: "yes" | "no" | "" (empty = interactive prompt) var_auto_reboot="${var_auto_reboot:-}" # var_continue_on_error: Continue updating remaining containers if one update fails # Options: "yes" | "no" (default: no = stop on first error) # Note: containers with backups always attempt restore on failure regardless of this setting var_continue_on_error="${var_continue_on_error:-no}" # var_dry_run: Check for available updates without applying them # Options: "yes" | "no" (default: no) # Output: lists each container with current vs. latest version # Note: requires the container to be running; does not modify any container var_dry_run="${var_dry_run:-no}" # var_tags: Optionally override the tags used for auto-detection # Options: "community-script|proxmox-helper-scripts" (default) var_tags="${var_tags:-community-script|proxmox-helper-scripts}" # ============================================================================= # JSON CONFIG EXPORT # Run with --export-config to output current configuration as JSON # ============================================================================= function export_config_json() { cat </dev/null pct pull "$1" /usr/bin/update update 2>/dev/null service=$(cat update | sed 's|.*/ct/||g' | sed 's|\.sh).*||g') popd >/dev/null } function dry_run_container() { local container="$1" local service="$2" # Extract app name and source repo directly from check_for_gh_release call in the ct script # Pattern: check_for_gh_release "appname" "owner/repo" local check_line app_name app_lc source_repo check_line=$(echo "$script" | grep -m1 'check_for_gh_release') if [[ -z "$check_line" ]]; then echo -e "${YW}[DRY-RUN]${CL} Container $container ($service): no check_for_gh_release found — skipping" DRY_RUN_RESULT="no check_for_gh_release found — skipping" return fi app_name=$(echo "$check_line" | cut -d'"' -f2) source_repo=$(echo "$check_line" | cut -d'"' -f4) app_lc=$(echo "${app_name,,}" | tr -d ' ') if [[ -z "$source_repo" || "$source_repo" != *"/"* ]]; then echo -e "${YW}[DRY-RUN]${CL} Container $container ($service): cannot parse source repo — skipping" DRY_RUN_RESULT="cannot parse source repo — skipping" return fi # Read installed version from container (stored by check_for_gh_release as ~/.) local current_version current_version=$(pct exec "$container" -- bash -c "cat \$HOME/.${app_lc} 2>/dev/null" 2>/dev/null || true) current_version="${current_version#v}" # Query latest release from GitHub API local latest_version latest_version=$(curl -sSL --max-time 10 \ -H 'Accept: application/vnd.github+json' \ -H 'X-GitHub-Api-Version: 2022-11-28' \ "https://api.github.com/repos/${source_repo}/releases/latest" 2>/dev/null | grep '"tag_name"' | head -1 | cut -d'"' -f4 | sed 's/^v//') if [[ -z "$latest_version" ]]; then echo -e "${YW}[DRY-RUN]${CL} Container $container ($service): cannot fetch latest version from $source_repo" DRY_RUN_RESULT="cannot fetch latest version from $source_repo" return fi if [[ -z "$current_version" ]]; then echo -e "${BL}[DRY-RUN]${CL} Container $container ($service): installed version unknown, latest: ${latest_version} (${source_repo})" DRY_RUN_RESULT="version unknown — latest: ${latest_version}" elif [[ "$current_version" == "$latest_version" ]]; then echo -e "${GN}[DRY-RUN]${CL} Container $container ($service): up to date (${current_version})" DRY_RUN_RESULT="up to date (${current_version})" else echo -e "${YW}[DRY-RUN]${CL} Container $container ($service): update available ${current_version} → ${latest_version}" DRY_RUN_RESULT="update available ${current_version} → ${latest_version}" fi } function backup_container() { msg_info "Creating backup for container $1" vzdump $1 --compress zstd --storage $STORAGE_CHOICE -notes-template "{{guestname}} - community-scripts backup updater" >/dev/null 2>&1 status=$? if [ $status -eq 0 ]; then msg_ok "Backup created" else msg_error "Backup failed for container $1" exit 235 fi } function get_backup_storages() { STORAGES=$(awk ' /^[a-z]+:/ { if (name != "") { if (has_backup || (!has_content && type == "dir")) print name } split($0, a, ":") type = a[1] name = a[2] gsub(/^[ \t]+|[ \t]+$/, "", name) has_content = 0 has_backup = 0 } /^[ \t]*content/ { has_content = 1 if ($0 ~ /backup/) has_backup = 1 } END { if (name != "") { if (has_backup || (!has_content && type == "dir")) print name } } ' /etc/pve/storage.cfg) } # Structured result tracking for the final summary report # Each entry: "CTID|service|STATUS|details" declare -a UPDATE_RESULTS=() function log_result() { # log_result
UPDATE_RESULTS+=("${1}|${2}|${3}|${4}") } header_info # ============================================================================= # LOGGING SETUP # Key events are written directly to a timestamped log file under # /usr/local/community-scripts/update_apps/ — this avoids any stdout # redirection that would break interactive spinners or whiptail dialogs. # The full summary table is appended at the end of the run. # ============================================================================= LOG_DIR="/usr/local/community-scripts/update_apps" mkdir -p "$LOG_DIR" LOG_FILE="${LOG_DIR}/$(date '+%Y%m%d_%H%M%S').log" echo "Update started: $(date '+%Y-%m-%d %H:%M:%S')" >"$LOG_FILE" function log_write() { echo "[$(date '+%H:%M:%S')] $*" >>"$LOG_FILE" } # Skip confirmation if var_skip_confirm is set to yes if [[ "$var_skip_confirm" != "yes" ]]; then whiptail --backtitle "Proxmox VE Helper Scripts" --title "LXC App Update" --yesno "This will update apps in LXCs installed by Helper-Scripts. Proceed?" 10 58 || exit fi tags_formatted="${var_tags//|/, }" msg_info "Loading all possible LXC containers from Proxmox VE with tags: ${tags_formatted}. This may take a few seconds..." NODE=$(hostname) containers=$(pct list | tail -n +2 | awk '{print $0 " " $4}') if [ -z "$containers" ]; then whiptail --title "LXC Container Update" --msgbox "No LXC containers available!" 10 60 exit 234 fi menu_items=() FORMAT="%-10s %-15s %-10s" TAGS="${var_tags:-community-script|proxmox-helper-scripts}" while read -r container; do container_id=$(echo $container | awk '{print $1}') container_name=$(echo $container | awk '{print $2}') container_status=$(echo $container | awk '{print $3}') formatted_line=$(printf "$FORMAT" "$container_name" "$container_status") if pct config "$container_id" | grep -qE "[^-][; ](${TAGS}).*"; then menu_items+=("$container_id" "$formatted_line" "OFF") fi done <<<"$containers" msg_ok "Loaded $((${#menu_items[@]} / 3)) containers" # Determine container selection based on var_container if [[ -n "$var_container" ]]; then case "$var_container" in all) # Select all containers with matching tags CHOICE="" for ((i = 0; i < ${#menu_items[@]}; i += 3)); do CHOICE="$CHOICE ${menu_items[$i]}" done CHOICE=$(echo "$CHOICE" | xargs) ;; all_running) # Select only running containers with matching tags CHOICE="" for ((i = 0; i < ${#menu_items[@]}; i += 3)); do cid="${menu_items[$i]}" if pct status "$cid" 2>/dev/null | grep -q "running"; then CHOICE="$CHOICE $cid" fi done CHOICE=$(echo "$CHOICE" | xargs) ;; all_stopped) # Select only stopped containers with matching tags CHOICE="" for ((i = 0; i < ${#menu_items[@]}; i += 3)); do cid="${menu_items[$i]}" if pct status "$cid" 2>/dev/null | grep -q "stopped"; then CHOICE="$CHOICE $cid" fi done CHOICE=$(echo "$CHOICE" | xargs) ;; *) # Assume comma-separated list of container IDs CHOICE=$(echo "$var_container" | tr ',' ' ') ;; esac if [[ -z "$CHOICE" ]]; then msg_error "No containers matched the selection criteria: $var_container ${var_tags:-community-script|proxmox-helper-scripts}" exit 234 fi msg_ok "Selected containers: $CHOICE" else CHOICE=$(whiptail --title "LXC Container Update" \ --checklist "Select LXC containers to update:" 25 60 13 \ "${menu_items[@]}" 3>&2 2>&1 1>&3 | tr -d '"') if [ -z "$CHOICE" ]; then whiptail --title "LXC Container Update" \ --msgbox "No containers selected!" 10 60 exit 0 fi fi header_info # Determine backup choice based on var_backup # Dry-run never needs a backup — skip the prompt entirely if [[ "$var_dry_run" == "yes" ]]; then BACKUP_CHOICE="no" elif [[ -n "$var_backup" ]]; then BACKUP_CHOICE="$var_backup" else BACKUP_CHOICE="no" if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "LXC Container Update" --yesno "Do you want to backup your containers before update?" 10 58); then BACKUP_CHOICE="yes" fi fi # Determine unattended update based on var_unattended # Dry-run never executes updates — skip the prompt entirely if [[ "$var_dry_run" == "yes" ]]; then UNATTENDED_UPDATE="no" elif [[ -n "$var_unattended" ]]; then UNATTENDED_UPDATE="$var_unattended" else UNATTENDED_UPDATE="no" if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "LXC Container Update" --yesno "Run updates unattended?" 10 58); then UNATTENDED_UPDATE="yes" fi fi if [ "$BACKUP_CHOICE" == "yes" ]; then get_backup_storages if [ -z "$STORAGES" ]; then msg_error "No storage with 'backup' support found!" exit 119 fi # Determine storage based on var_backup_storage if [[ -n "$var_backup_storage" ]]; then # Validate that the specified storage exists and supports backups if echo "$STORAGES" | grep -qw "$var_backup_storage"; then STORAGE_CHOICE="$var_backup_storage" msg_ok "Using backup storage: $STORAGE_CHOICE" else msg_error "Specified backup storage '$var_backup_storage' not found or doesn't support backups!" msg_info "Available storages: $(echo $STORAGES | tr '\n' ' ')" exit 119 fi else MENU_ITEMS=() for STORAGE in $STORAGES; do MENU_ITEMS+=("$STORAGE" "") done STORAGE_CHOICE=$(whiptail --title "Select storage device" --menu "Select a storage device (Only storage devices with 'backup' support are listed):" 15 50 5 "${MENU_ITEMS[@]}" 3>&1 1>&2 2>&3) if [ -z "$STORAGE_CHOICE" ]; then msg_error "No storage selected!" exit 0 fi fi fi UPDATE_CMD="update;" if [ "$UNATTENDED_UPDATE" == "yes" ]; then UPDATE_CMD="export PHS_SILENT=1;update;" fi containers_needing_reboot=() for container in $CHOICE; do echo -e "${BL}[INFO]${CL} Updating container $container" log_write "Container $container: starting" if [ "$BACKUP_CHOICE" == "yes" ]; then backup_container $container fi os=$(pct config "$container" | awk '/^ostype/ {print $2}') status=$(pct status $container) template=$(pct config $container | grep -q "template:" && echo "true" || echo "false") if [ "$template" == "false" ] && [ "$status" == "status: stopped" ]; then echo -e "${BL}[Info]${GN} Starting${BL} $container ${CL} \n" pct start $container echo -e "${BL}[Info]${GN} Waiting For${BL} $container${CL}${GN} To Start ${CL} \n" sleep 5 fi #1) Detect service using the service name in the update command detect_service $container #1.1) If update script not detected, return if [ -z "${service}" ]; then echo -e "${YW}[WARN]${CL} Update script not found. Skipping to next container" log_result "$container" "(unknown)" "SKIPPED" "No update script found in container" log_write "Container $container: SKIPPED — no update script found" continue else echo -e "${BL}[INFO]${CL} Detected service: ${GN}${service}${CL}" log_write "Container $container: detected service '$service'" fi #2) Extract service build/update resource requirements from config/installation file script=$(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/ct/${service}.sh) #2.1) Check if the script downloaded successfully if [ $? -ne 0 ]; then echo -e "${RD}[ERROR]${CL} Issue while downloading install script." echo -e "${YW}[WARN]${CL} Unable to assess build resource requirements. Proceeding with current resources." fi config=$(pct config "$container") build_cpu=$(echo "$script" | { grep -m 1 "var_cpu" || test $? = 1; } | sed 's|.*=||g' | sed 's|"||g' | sed 's|.*var_cpu:-||g' | sed 's|}||g') build_ram=$(echo "$script" | { grep -m 1 "var_ram" || test $? = 1; } | sed 's|.*=||g' | sed 's|"||g' | sed 's|.*var_ram:-||g' | sed 's|}||g') run_cpu=$(echo "$script" | { grep -m 1 "pct set \$CTID -cores" || test $? = 1; } | sed 's|.*cores ||g') run_ram=$(echo "$script" | { grep -m 1 "pct set \$CTID -memory" || test $? = 1; } | sed 's|.*memory ||g') current_cpu=$(echo "$config" | grep -m 1 "cores:" | sed 's|cores: ||g') current_ram=$(echo "$config" | grep -m 1 "memory:" | sed 's|memory: ||g') #Test if all values are valid (>0) if [ -z "${run_cpu}" ] || [ "$run_cpu" -le 0 ]; then #echo "No valid value found for run_cpu. Assuming same as current configuration." run_cpu=$current_cpu fi if [ -z "${run_ram}" ] || [ "$run_ram" -le 0 ]; then #echo "No valid value found for run_ram. Assuming same as current configuration." run_ram=$current_ram fi if [ -z "${build_cpu}" ] || [ "$build_cpu" -le 0 ]; then #echo "No valid value found for build_cpu. Assuming same as current configuration." build_cpu=$current_cpu fi if [ -z "${build_ram}" ] || [ "$build_ram" -le 0 ]; then #echo "No valid value found for build_ram. Assuming same as current configuration." build_ram=$current_ram fi UPDATE_BUILD_RESOURCES=0 if [ "$build_cpu" -gt "$run_cpu" ] || [ "$build_ram" -gt "$run_ram" ]; then UPDATE_BUILD_RESOURCES=1 fi #3) if build resources are different than run resources, then: if [ "$UPDATE_BUILD_RESOURCES" -eq "1" ] && [[ "$var_dry_run" != "yes" ]]; then pct set "$container" --cores "$build_cpu" --memory "$build_ram" fi #3.5) Dry-run: report update availability without applying if [[ "$var_dry_run" == "yes" ]]; then DRY_RUN_RESULT="" dry_run_container "$container" "$service" log_result "$container" "$service" "DRY-RUN" "${DRY_RUN_RESULT:-version check only}" log_write "Container $container ($service): DRY-RUN — ${DRY_RUN_RESULT:-version check only}" continue fi #4) Update service, using the update command # Prepend a no-op 'clear' wrapper to PATH so update scripts calling clear # don't fail without a TTY — works for all shells incl. ash (no export -f) SETUP_CMD="mkdir -p /tmp/.nc; printf '#!/bin/sh\n:\n' > /tmp/.nc/clear; chmod +x /tmp/.nc/clear; export PATH=/tmp/.nc:\$PATH; export TERM=dumb; " case "$os" in alpine) pct exec "$container" -- ash -c "${SETUP_CMD}${UPDATE_CMD}" ;; archlinux) pct exec "$container" -- bash -c "${SETUP_CMD}${UPDATE_CMD}" ;; fedora | rocky | centos | alma) pct exec "$container" -- bash -c "${SETUP_CMD}${UPDATE_CMD}" ;; ubuntu | debian | devuan) pct exec "$container" -- bash -c "${SETUP_CMD}${UPDATE_CMD}" ;; opensuse) pct exec "$container" -- bash -c "${SETUP_CMD}${UPDATE_CMD}" ;; esac exit_code=$? #5) if build resources are different than run resources, then: if [ "$UPDATE_BUILD_RESOURCES" -eq "1" ]; then pct set "$container" --cores "$run_cpu" --memory "$run_ram" fi if pct exec "$container" -- [ -e "/var/run/reboot-required" ]; then # Get the container's hostname and add it to the list container_hostname=$(pct exec "$container" hostname) containers_needing_reboot+=("$container ($container_hostname)") fi if [ "$template" == "false" ] && [ "$status" == "status: stopped" ]; then echo -e "${BL}[Info]${GN} Shutting down${BL} $container ${CL} \n" pct shutdown $container &>/dev/null & fi if [ $exit_code -eq 0 ]; then msg_ok "Updated container $container" log_result "$container" "$service" "OK" "Updated successfully" log_write "Container $container ($service): OK" elif [ $exit_code -eq 75 ]; then echo -e "${YW}[WARN]${CL} Container $container skipped (requires interactive mode)" log_result "$container" "$service" "SKIPPED" "Requires interactive mode (exit 75)" log_write "Container $container ($service): SKIPPED — requires interactive mode" elif [ $exit_code -eq 113 ]; then echo -e "${YW}[WARN]${CL} Container $container skipped (under-provisioned: increase CPU/RAM to match template)" log_result "$container" "$service" "SKIPPED" "Under-provisioned — increase CPU/RAM to match template" log_write "Container $container ($service): SKIPPED — under-provisioned" elif [ $exit_code -eq 114 ]; then echo -e "${YW}[WARN]${CL} Container $container skipped (storage critically low on /boot)" log_result "$container" "$service" "SKIPPED" "Storage critically low on /boot (>80%)" log_write "Container $container ($service): SKIPPED — storage critically low on /boot" elif [ "$BACKUP_CHOICE" == "yes" ]; then msg_error "Update failed for container $container (exit code: $exit_code) — attempting restore" log_write "Container $container ($service): FAILED (exit $exit_code) — attempting restore" msg_info "Restoring LXC $container from backup ($STORAGE_CHOICE)" pct stop $container LXC_STORAGE=$(pct config $container | awk -F '[:,]' '/rootfs/ {print $2}') BACKUP_ENTRY=$(pvesm list "$STORAGE_CHOICE" 2>/dev/null | awk -v ctid="$container" '$1 ~ "vzdump-lxc-"ctid"-" || $1 ~ "/ct/"ctid"/" {print $1}' | sort -r | head -n1) if [ -z "$BACKUP_ENTRY" ]; then msg_error "No backup found in storage $STORAGE_CHOICE for container $container" log_result "$container" "$service" "FAILED" "Update failed (exit $exit_code) — no backup found for restore" log_write "Container $container ($service): FAILED — no backup found for restore" exit 235 fi msg_info "Restoring from: $BACKUP_ENTRY" pct restore $container "$BACKUP_ENTRY" --storage $LXC_STORAGE --force >/dev/null 2>&1 restorestatus=$? if [ $restorestatus -eq 0 ]; then pct start $container msg_ok "Container $container successfully restored from backup" log_result "$container" "$service" "RESTORED" "Update failed (exit $exit_code) — restored from backup" log_write "Container $container ($service): RESTORED from $BACKUP_ENTRY" else msg_error "Restore failed for container $container" log_result "$container" "$service" "FAILED" "Update failed (exit $exit_code) — restore also failed" log_write "Container $container ($service): FAILED — restore also failed" exit 235 fi else msg_error "Update failed for container $container (exit code: $exit_code)" log_result "$container" "$service" "FAILED" "Exit code $exit_code" log_write "Container $container ($service): FAILED (exit $exit_code)" if [[ "$var_continue_on_error" == "yes" ]]; then echo -e "${YW}[WARN]${CL} Continuing to next container (var_continue_on_error=yes)" continue else exit "$exit_code" fi fi done wait header_info if [[ "$var_dry_run" == "yes" ]]; then echo -e "${GN}Dry-run complete. No containers were modified.${CL}\n" else echo -e "${GN}The process is complete, and the containers have been successfully updated.${CL}\n" fi # ============================================================================= # SUMMARY REPORT # ============================================================================= if [ "${#UPDATE_RESULTS[@]}" -gt 0 ]; then SEPARATOR="━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" HEADER=$(printf " %-8s %-22s %-10s %s" "CTID" "Service" "Status" "Details") # terminal output (with colours) echo "" echo "$SEPARATOR" echo "$HEADER" echo "$SEPARATOR" for entry in "${UPDATE_RESULTS[@]}"; do IFS='|' read -r _ctid _svc _status _details <<<"$entry" case "$_status" in OK) _color="${GN}" ;; FAILED) _color="${RD}" ;; RESTORED) _color="${YW}" ;; *) _color="${YW}" ;; esac printf " %-8s %-22s ${_color}%-10s${CL} %s\n" "$_ctid" "$_svc" "$_status" "$_details" done echo "$SEPARATOR" echo "" echo "Full log: $LOG_FILE" echo "" # append plain-text summary to log file { echo "" echo "Update finished: $(date '+%Y-%m-%d %H:%M:%S')" echo "$SEPARATOR" echo "$HEADER" echo "$SEPARATOR" for entry in "${UPDATE_RESULTS[@]}"; do IFS='|' read -r _ctid _svc _status _details <<<"$entry" printf " %-8s %-22s %-10s %s\n" "$_ctid" "$_svc" "$_status" "$_details" done echo "$SEPARATOR" } >>"$LOG_FILE" fi if [ "${#containers_needing_reboot[@]}" -gt 0 ]; then echo -e "${RD}The following containers require a reboot:${CL}" for container_name in "${containers_needing_reboot[@]}"; do echo "$container_name" done # Determine reboot choice based on var_auto_reboot REBOOT_CHOICE="no" if [[ -n "$var_auto_reboot" ]]; then REBOOT_CHOICE="$var_auto_reboot" else echo -ne "${INFO} Do you wish to reboot these containers? " read -r prompt if [[ ${prompt,,} =~ ^(yes)$ ]]; then REBOOT_CHOICE="yes" fi fi if [[ "$REBOOT_CHOICE" == "yes" ]]; then echo -e "${CROSS}${HOLD} ${YWB}Rebooting containers.${CL}" for container_name in "${containers_needing_reboot[@]}"; do container=$(echo $container_name | cut -d " " -f 1) pct reboot ${container} done fi fi ================================================ FILE: tools/pve/update-lxcs-cron.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # # This script is installed locally by cron-update-lxcs.sh and executed # by cron. It updates all LXC containers using their native package manager. # Ensure full PATH when running via cron (pct lives in /usr/sbin) export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin CONF_FILE="/etc/update-lxcs.conf" echo -e "\n $(date)" # Collect excluded containers from arguments excluded_containers=("$@") # Merge exclusions from config file if it exists if [[ -f "$CONF_FILE" ]]; then conf_exclude=$(grep -oP '^\s*EXCLUDE\s*=\s*\K[0-9,]+' "$CONF_FILE" 2>/dev/null || true) IFS=',' read -ra conf_ids <<<"$conf_exclude" for id in "${conf_ids[@]}"; do id="${id// /}" [[ -n "$id" ]] && excluded_containers+=("$id") done fi function update_container() { local container=$1 local name name=$(pct exec "$container" hostname 2>/dev/null || echo "unknown") local os os=$(pct config "$container" | awk '/^ostype/ {print $2}') echo -e "\n [Info] Updating $container : $name (os: $os)" case "$os" in alpine) pct exec "$container" -- ash -c "apk -U upgrade" ;; archlinux) pct exec "$container" -- bash -c "pacman -Syyu --noconfirm" ;; fedora | rocky | centos | alma) pct exec "$container" -- bash -c "dnf -y update && dnf -y upgrade" ;; ubuntu | debian | devuan) pct exec "$container" -- bash -c "apt-get update && DEBIAN_FRONTEND=noninteractive apt-get -o Dpkg::Options::='--force-confold' dist-upgrade -y; rm -rf /usr/lib/python3.*/EXTERNALLY-MANAGED" ;; opensuse) pct exec "$container" -- bash -c "zypper ref && zypper --non-interactive dup" ;; *) echo " [Warn] Unknown OS type '$os' for container $container, skipping" ;; esac } for container in $(pct list | awk '{if(NR>1) print $1}'); do excluded=false for excluded_container in "${excluded_containers[@]}"; do if [ "$container" == "$excluded_container" ]; then excluded=true break fi done if [ "$excluded" == true ]; then echo -e "[Info] Skipping $container" sleep 1 else status=$(pct status "$container") if pct config "$container" 2>/dev/null | grep -q "^template:"; then echo -e "[Info] Skipping template $container" continue fi if [ "$status" == "status: stopped" ]; then echo -e "[Info] Starting $container" pct start "$container" sleep 5 update_container "$container" || echo " [Error] Update failed for $container" # check if patchmon agent is present in container and run a report if found if pct exec "$container" -- [ -e "/usr/local/bin/patchmon-agent" ]; then echo -e "${BL}[Info]${GN} patchmon-agent found in ${BL} $container ${CL}, triggering report. \n" pct exec "$container" -- "/usr/local/bin/patchmon-agent" "report" fi echo -e "[Info] Shutting down $container" pct shutdown "$container" --timeout 60 & elif [ "$status" == "status: running" ]; then update_container "$container" || echo " [Error] Update failed for $container" # check if patchmon agent is present in container and run a report if found if pct exec "$container" -- [ -e "/usr/local/bin/patchmon-agent" ]; then echo -e "${BL}[Info]${GN} patchmon-agent found in ${BL} $container ${CL}, triggering report. \n" pct exec "$container" -- "/usr/local/bin/patchmon-agent" "report" fi fi fi done wait ================================================ FILE: tools/pve/update-lxcs.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT # https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE function header_info() { clear cat <<"EOF" __ __ __ __ __ _ ________ / / / /___ ____/ /___ _/ /____ / / | |/ / ____/ / / / / __ \/ __ / __ `/ __/ _ \ / / | / / / /_/ / /_/ / /_/ / /_/ / /_/ __/ / /___/ / /___ \____/ .___/\__,_/\__,_/\__/\___/ /_____/_/|_\____/ /_/ EOF } set -eEuo pipefail YW=$(echo "\033[33m") BL=$(echo "\033[36m") RD=$(echo "\033[01;31m") CM='\xE2\x9C\x94\033' GN=$(echo "\033[1;92m") CL=$(echo "\033[m") # Telemetry source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func) 2>/dev/null || true declare -f init_tool_telemetry &>/dev/null && init_tool_telemetry "update-lxcs" "pve" header_info echo "Loading..." whiptail --backtitle "Proxmox VE Helper Scripts" --title "Proxmox VE LXC Updater" --yesno "This Will Update LXC Containers. Proceed?" 10 58 if whiptail --backtitle "Proxmox VE Helper Scripts" --title "Skip Not-Running Containers" --yesno "Do you want to skip containers that are not currently running?" 10 58; then SKIP_STOPPED="yes" else SKIP_STOPPED="no" fi NODE=$(hostname) EXCLUDE_MENU=() MSG_MAX_LENGTH=0 while read -r TAG ITEM; do OFFSET=2 ((${#ITEM} + OFFSET > MSG_MAX_LENGTH)) && MSG_MAX_LENGTH=${#ITEM}+OFFSET EXCLUDE_MENU+=("$TAG" "$ITEM " "OFF") done < <(pct list | awk 'NR>1') excluded_containers=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "Containers on $NODE" --checklist "\nSelect containers to skip from updates:\n" 16 $((MSG_MAX_LENGTH + 23)) 6 "${EXCLUDE_MENU[@]}" 3>&1 1>&2 2>&3 | tr -d '"') function needs_reboot() { local container=$1 local os=$(pct config "$container" | awk '/^ostype/ {print $2}') local reboot_required_file="/var/run/reboot-required.pkgs" if [ -f "$reboot_required_file" ]; then if [[ "$os" == "ubuntu" || "$os" == "debian" ]]; then if pct exec "$container" -- [ -s "$reboot_required_file" ]; then return 0 fi fi fi return 1 } function update_container() { container=$1 header_info name=$(pct exec "$container" hostname) os=$(pct config "$container" | awk '/^ostype/ {print $2}') if [[ "$os" == "ubuntu" || "$os" == "debian" || "$os" == "fedora" ]]; then disk_info=$(pct exec "$container" df /boot | awk 'NR==2{gsub("%","",$5); printf "%s %.1fG %.1fG %.1fG", $5, $3/1024/1024, $2/1024/1024, $4/1024/1024 }') read -ra disk_info_array <<<"$disk_info" echo -e "${BL}[Info]${GN} Updating ${BL}$container${CL} : ${GN}$name${CL} - ${YW}Boot Disk: ${disk_info_array[0]}% full [${disk_info_array[1]}/${disk_info_array[2]} used, ${disk_info_array[3]} free]${CL}\n" else echo -e "${BL}[Info]${GN} Updating ${BL}$container${CL} : ${GN}$name${CL} - ${YW}[No disk info for ${os}]${CL}\n" fi case "$os" in alpine) pct exec "$container" -- ash -c "apk -U upgrade" ;; archlinux) pct exec "$container" -- bash -c "pacman -Syyu --noconfirm" ;; fedora | rocky | centos | alma) pct exec "$container" -- bash -c "dnf -y update && dnf -y upgrade" ;; ubuntu | debian | devuan) pct exec "$container" -- bash -c "apt-get update 2>/dev/null | grep 'packages.*upgraded'; apt list --upgradable 2>/dev/null | cat && apt-get -yq dist-upgrade 2>&1; rm -rf /usr/lib/python3.*/EXTERNALLY-MANAGED || true" ;; opensuse) pct exec "$container" -- bash -c "zypper ref && zypper --non-interactive dup" ;; esac } containers_needing_reboot=() header_info for container in $(pct list | awk '{if(NR>1) print $1}'); do if [[ " ${excluded_containers[@]} " =~ " $container " ]]; then header_info echo -e "${BL}[Info]${GN} Skipping ${BL}$container${CL}" sleep 1 else status=$(pct status $container) if [ "$SKIP_STOPPED" == "yes" ] && [ "$status" == "status: stopped" ]; then header_info echo -e "${BL}[Info]${GN} Skipping ${BL}$container${CL}${GN} (not running)${CL}" sleep 1 continue fi template=$(pct config $container | grep -q "template:" && echo "true" || echo "false") if [ "$template" == "false" ] && [ "$status" == "status: stopped" ]; then echo -e "${BL}[Info]${GN} Starting${BL} $container ${CL} \n" pct start $container echo -e "${BL}[Info]${GN} Waiting For${BL} $container${CL}${GN} To Start ${CL} \n" sleep 5 update_container $container echo -e "${BL}[Info]${GN} Shutting down${BL} $container ${CL} \n" pct shutdown $container & elif [ "$status" == "status: running" ]; then update_container $container fi if [ "$status" == "status: running" ]; then if pct exec "$container" -- [ -e "/var/run/reboot-required" ]; then # Get the container's hostname and add it to the list container_hostname=$(pct exec "$container" hostname) containers_needing_reboot+=("$container ($container_hostname)") fi # check if patchmon agent is present in container and run a report if found if pct exec "$container" -- [ -e "/usr/local/bin/patchmon-agent" ]; then echo -e "${BL}[Info]${GN} patchmon-agent found in ${BL} $container ${CL}, triggering report. \n" pct exec "$container" -- "/usr/local/bin/patchmon-agent" "report" fi fi fi done wait header_info echo -e "${GN}The process is complete, and the containers have been successfully updated.${CL}\n" if [ "${#containers_needing_reboot[@]}" -gt 0 ]; then echo -e "${RD}The following containers require a reboot:${CL}" for container_name in "${containers_needing_reboot[@]}"; do echo "$container_name" done fi echo "" ================================================ FILE: tools/pve/update-repo.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: MickLesk # License: MIT # https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE function header_info { clear cat <<"EOF" __ __ __ __ ____ / / / /___ ____/ /___ _/ /____ / __ \___ ____ ____ / / / / __ \/ __ / __ `/ __/ _ \ / /_/ / _ \/ __ \/ __ \ / /_/ / /_/ / /_/ / /_/ / /_/ __/ / _, _/ __/ /_/ / /_/ / \____/ .___/\__,_/\__,_/\__/\___/ /_/ |_|\___/ .___/\____/ /_/ /_/ EOF } set -eEuo pipefail BL=$(echo "\033[36m") RD=$(echo "\033[01;31m") GN=$(echo "\033[1;92m") CL=$(echo "\033[m") # Telemetry source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func) 2>/dev/null || true declare -f init_tool_telemetry &>/dev/null && init_tool_telemetry "update-repo" "pve" header_info echo "Loading..." NODE=$(hostname) function update_container() { container=$1 os=$(pct config "$container" | awk '/^ostype/ {print $2}') if [[ "$os" == "ubuntu" || "$os" == "debian" ]]; then echo -e "${BL}[Info]${GN} Checking /usr/bin/update in ${BL}$container${CL} (OS: ${GN}$os${CL})" if pct exec "$container" -- [ -e /usr/bin/update ]; then if pct exec "$container" -- grep -q "community-scripts/ProxmoxVE" /usr/bin/update; then echo -e "${RD}[No Change]${CL} /usr/bin/update is already up to date in ${BL}$container${CL}.\n" elif pct exec "$container" -- grep -q -v "tteck" /usr/bin/update; then echo -e "${RD}[Warning]${CL} /usr/bin/update in ${BL}$container${CL} contains a different entry (${RD}tteck${CL}). No changes made.\n" else pct exec "$container" -- bash -c "sed -i 's/tteck\\/Proxmox/community-scripts\\/ProxmoxVE/g' /usr/bin/update" if pct exec "$container" -- grep -q "community-scripts/ProxmoxVE" /usr/bin/update; then echo -e "${GN}[Success]${CL} /usr/bin/update updated in ${BL}$container${CL}.\n" else echo -e "${RD}[Error]${CL} /usr/bin/update in ${BL}$container${CL} could not be updated properly.\n" fi fi else echo -e "${RD}[Error]${CL} /usr/bin/update not found in container ${BL}$container${CL}.\n" fi else echo -e "${BL}[Info]${GN} Skipping ${BL}$container${CL} (not Debian/Ubuntu)\n" fi } header_info for container in $(pct list | awk '{if(NR>1) print $1}'); do update_container "$container" done header_info echo -e "${GN}The process is complete. The repositories have been switched to community-scripts/ProxmoxVE.${CL}\n" ================================================ FILE: tools/pve/usb-passthrough.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT # https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE echo -e "\e[1;33m This script will allow USB passthrough to a PRIVILEGED LXC Container ONLY\e[0m" while true; do read -p "Did you replace 106 with your LXC ID? Proceed(y/n)?" yn case $yn in [Yy]*) break ;; [Nn]*) exit ;; *) echo "Please answer yes or no." ;; esac done TEMP_DIR=$(mktemp -d) pushd $TEMP_DIR >/dev/null CHAR_DEVS+=("166:.*") CHAR_DEVS+=("188:.*") CHAR_DEVS+=("189:.*") for char_dev in ${CHAR_DEVS[@]}; do [ ! -z "${CHAR_DEV_STRING-}" ] && CHAR_DEV_STRING+=" -o" CHAR_DEV_STRING+=" -regex \".*/${char_dev}\"" done read -r -d '' HOOK_SCRIPT <<-EOF || true for char_dev in \$(find /sys/dev/char -regextype sed $CHAR_DEV_STRING); do dev="/dev/\$(sed -n "/DEVNAME/ s/^.*=\(.*\)$/\1/p" \${char_dev}/uevent)"; mkdir -p \$(dirname \${LXC_ROOTFS_MOUNT}\${dev}); for link in \$(udevadm info --query=property \$dev | sed -n "s/DEVLINKS=//p"); do mkdir -p \${LXC_ROOTFS_MOUNT}\$(dirname \$link); cp -dpR \$link \${LXC_ROOTFS_MOUNT}\${link}; done; cp -dpR \$dev \${LXC_ROOTFS_MOUNT}\${dev}; done; EOF HOOK_SCRIPT=${HOOK_SCRIPT//$'\n'/} CTID=$1 CTID_CONFIG_PATH=/etc/pve/lxc/${CTID}.conf sed '/autodev/d' $CTID_CONFIG_PATH >CTID.conf cat CTID.conf >$CTID_CONFIG_PATH cat <>$CTID_CONFIG_PATH lxc.autodev: 1 lxc.hook.autodev: bash -c '$HOOK_SCRIPT' EOF echo -e "\e[1;33m Finished....Reboot ${CTID} LXC to apply the changes \e[0m" ================================================ FILE: turnkey/turnkey.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source shared libraries source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func) source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/core.func) source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/error_handler.func) load_functions catch_errors APP="TurnKey LXC" NSAPP="turnkey" DIAGNOSTICS="no" METHOD="default" RANDOM_UUID="$(cat /proc/sys/kernel/random/uuid)" EXECUTION_ID="${RANDOM_UUID}" header_info() { clear cat <<"EOF" ______ __ __ __ _ _______ /_ __/_ _________ / //_/__ __ __ / / | |/_/ ___/ / / / // / __/ _ \/ ,< / -_) // / / /___> /dev/null; then local cluster_ids cluster_ids=$(pvesh get /cluster/resources --type vm --output-format json 2>/dev/null | grep -oP '"vmid":\s*\K[0-9]+' 2>/dev/null || true) if [[ -n "$cluster_ids" ]] && echo "$cluster_ids" | grep -qw "$ctid"; then return 1 fi fi # Local fallback if [[ -f "/etc/pve/qemu-server/${ctid}.conf" ]] || [[ -f "/etc/pve/lxc/${ctid}.conf" ]]; then return 1 fi # Check all cluster nodes if [[ -d "/etc/pve/nodes" ]]; then for node_dir in /etc/pve/nodes/*/; do if [[ -f "${node_dir}qemu-server/${ctid}.conf" ]] || [[ -f "${node_dir}lxc/${ctid}.conf" ]]; then return 1 fi done fi # Check LVM volumes if lvs --noheadings -o lv_name 2>/dev/null | grep -qE "(^|[-_])${ctid}($|[-_])"; then return 1 fi return 0 } get_valid_container_id() { local suggested_id="${1:-$(pvesh get /cluster/nextid 2>/dev/null || echo 100)}" while ! validate_container_id "$suggested_id"; do suggested_id=$((suggested_id + 1)) done echo "$suggested_id" } cleanup_ctid() { if pct status "$CTID" &>/dev/null; then if [[ "$(pct status "$CTID" | awk '{print $2}')" == "running" ]]; then pct stop "$CTID" fi pct destroy "$CTID" fi } select_storage() { local class="$1" content content_label case "$class" in container) content='rootdir' content_label='Container' ;; template) content='vztmpl' content_label='Container template' ;; *) msg_error "Invalid storage class '$class'" return 1 ;; esac local -a MENU=() local MSG_MAX_LENGTH=0 while read -r line; do local TAG TYPE FREE ITEM OFFSET=2 TAG=$(echo "$line" | awk '{print $1}') TYPE=$(echo "$line" | awk '{printf "%-10s", $2}') FREE=$(echo "$line" | numfmt --field 4-6 --from-unit=K --to=iec --format %.2f | awk '{printf( "%9sB", $6)}') ITEM=" Type: $TYPE Free: $FREE " ((${#ITEM} + OFFSET > MSG_MAX_LENGTH)) && MSG_MAX_LENGTH=$((${#ITEM} + OFFSET)) MENU+=("$TAG" "$ITEM" "OFF") done < <(pvesm status -content "$content" | awk 'NR>1') if [[ $((${#MENU[@]} / 3)) -eq 0 ]]; then msg_error "'$content_label' needs to be selected for at least one storage location." return 1 elif [[ $((${#MENU[@]} / 3)) -eq 1 ]]; then printf '%s' "${MENU[0]}" else local STORAGE while [[ -z "${STORAGE:+x}" ]]; do STORAGE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "Storage Pools" --radiolist \ "Which storage pool for the ${content_label,,}?\n\n" \ 16 $((MSG_MAX_LENGTH + 23)) 6 \ "${MENU[@]}" 3>&1 1>&2 2>&3) || exit_script done printf '%s' "$STORAGE" fi } # ============================================================================== # MAIN # ============================================================================== # Cleanup on error: destroy container, report telemetry, and restart monitor turnkey_cleanup() { local exit_code=$? if [[ $exit_code -ne 0 ]]; then # Report failure to telemetry if [[ "${POST_TO_API_DONE:-}" == "true" && "${POST_UPDATE_DONE:-}" != "true" ]]; then post_update_to_api "failed" "$exit_code" 2>/dev/null || true fi # Destroy failed container if [[ -n "${CTID:-}" ]]; then cleanup_ctid 2>/dev/null || true fi fi if [[ -f /etc/systemd/system/ping-instances.service ]]; then systemctl start ping-instances.service 2>/dev/null || true fi } trap turnkey_cleanup EXIT # Stop Proxmox VE Monitor-All if running if systemctl is-active -q ping-instances.service; then systemctl stop ping-instances.service fi pve_check shell_check root_check # Read diagnostics preference (same logic as build.func diagnostics_check) DIAG_CONFIG="/usr/local/community-scripts/diagnostics" if [[ -f "$DIAG_CONFIG" ]]; then DIAGNOSTICS=$(awk -F '=' '/^DIAGNOSTICS/ {print $2}' "$DIAG_CONFIG") || true DIAGNOSTICS="${DIAGNOSTICS:-no}" fi header_info whiptail --backtitle "Proxmox VE Helper Scripts" --title "TurnKey LXCs" --yesno \ "This will allow for the creation of one of the many TurnKey LXC Containers. Proceed?" 10 68 || exit_script # Update template catalog early so the menu reflects the latest available templates msg_info "Updating LXC template list" pveam update >/dev/null msg_ok "Updated LXC template list" # Build TurnKey selection menu dynamically from available templates # Requires gawk for regex capture groups in match() command -v gawk &>/dev/null || apt-get install -y gawk &>/dev/null declare -A TURNKEY_TEMPLATES TURNKEY_MENU=() MSG_MAX_LENGTH=0 while IFS=$'\t' read -r TEMPLATE_FILE TAG ITEM; do TURNKEY_TEMPLATES["$TAG"]="$TEMPLATE_FILE" OFFSET=2 ((${#ITEM} + OFFSET > MSG_MAX_LENGTH)) && MSG_MAX_LENGTH=$((${#ITEM} + OFFSET)) TURNKEY_MENU+=("$TAG" "$ITEM " "OFF") done < <(pveam available -section turnkeylinux | gawk '{ tpl = $2 if (match(tpl, /debian-([0-9]+)-turnkey-([^_]+)_([^_]+)_/, m)) { app = m[2]; deb = m[1]; ver = m[3] display = app gsub(/-/, " ", display) n = split(display, words, " ") display = "" for (i = 1; i <= n; i++) { words[i] = toupper(substr(words[i], 1, 1)) substr(words[i], 2) display = display (i > 1 ? " " : "") words[i] } tag = app "-" deb printf "%s\t%s\t%s | Debian %s | %s\n", tpl, tag, display, deb, ver } }' | sort -t$'\t' -k2,2) if [[ ${#TURNKEY_MENU[@]} -eq 0 ]]; then msg_error "No TurnKey templates found. Check your internet connection or template repository." exit 1 fi selected=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "TurnKey LXCs" --radiolist \ "\nSelect a TurnKey LXC to create:\n" 20 $((MSG_MAX_LENGTH + 58)) 12 \ "${TURNKEY_MENU[@]}" 3>&1 1>&2 2>&3 | tr -d '"') || exit_script if [[ -z "$selected" ]]; then whiptail --backtitle "Proxmox VE Helper Scripts" --title "No TurnKey LXC Selected" \ --msgbox "It appears that no TurnKey LXC container was selected" 10 68 exit_script fi # Extract template filename and app name from selection TEMPLATE="${TURNKEY_TEMPLATES[$selected]}" turnkey="${selected%-*}" # Generate random password PASS="$(openssl rand -base64 8)" # Prompt for Container ID NEXT_ID=$(pvesh get /cluster/nextid 2>/dev/null || echo 100) while true; do CTID=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "Container ID" \ --inputbox "Enter the container ID..." 8 40 "$NEXT_ID" 3>&1 1>&2 2>&3) || exit_script if [[ -z "$CTID" ]]; then msg_error "No Container ID selected" exit_script fi if ! validate_container_id "$CTID"; then SUGGESTED_ID=$(get_valid_container_id "$CTID") if whiptail --backtitle "Proxmox VE Helper Scripts" --title "ID Already In Use" --yesno \ "Container/VM ID $CTID is already in use.\n\nWould you like to use the next available ID ($SUGGESTED_ID)?" 10 58; then CTID="$SUGGESTED_ID" break fi else break fi done # Prompt for Hostname HOST_NAME=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "Hostname" \ --inputbox "Enter the container hostname..." 8 40 "turnkey-${turnkey}" 3>&1 1>&2 2>&3) || exit_script # Container options PCT_OPTIONS=( -features keyctl=1,nesting=1 -hostname "$HOST_NAME" -tags community-script -onboot 1 -cores 2 -memory 2048 -password "$PASS" -net0 name=eth0,bridge=vmbr0,ip=dhcp -unprivileged 1 -arch "$(dpkg --print-architecture)" ) # Storage selection TEMPLATE_STORAGE=$(select_storage template) || { msg_error "Failed to select template storage" exit 1 } msg_ok "Using '${BL}${TEMPLATE_STORAGE}${CL}' for template storage" CONTAINER_STORAGE=$(select_storage container) || { msg_error "Failed to select container storage" exit 1 } msg_ok "Using '${BL}${CONTAINER_STORAGE}${CL}' for container storage" # Download template if not already cached if ! pveam list "$TEMPLATE_STORAGE" | grep -q "$TEMPLATE"; then msg_info "Downloading LXC template" pveam download "$TEMPLATE_STORAGE" "$TEMPLATE" >/dev/null || { msg_error "Failed to download LXC template '${TEMPLATE}'" exit 1 } msg_ok "Downloaded LXC template" fi # Add rootfs if not specified [[ " ${PCT_OPTIONS[*]} " =~ " -rootfs " ]] || PCT_OPTIONS+=(-rootfs "${CONTAINER_STORAGE}:${PCT_DISK_SIZE:-8}") # Set telemetry variables for the selected turnkey TELEMETRY_TYPE="turnkey" NSAPP="turnkey-${turnkey}" CT_TYPE=1 DISK_SIZE="${PCT_DISK_SIZE:-8}" CORE_COUNT=2 RAM_SIZE=2048 var_os="turnkey" var_version="${turnkey}" # Report installation start to telemetry post_to_api # Create LXC container msg_info "Creating LXC container" pct create "$CTID" "${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE}" "${PCT_OPTIONS[@]}" >/dev/null || { msg_error "Failed to create container" exit 1 } msg_ok "Created LXC container (ID: ${BL}${CTID}${CL})" # Save credentials securely CREDS_FILE=~/turnkey-${turnkey}.creds echo "TurnKey ${turnkey} password: ${PASS}" >>"$CREDS_FILE" chmod 600 "$CREDS_FILE" # Configure TUN device access for VPN-based turnkeys TUN_DEVICE_REQUIRED=("openvpn") if printf '%s\n' "${TUN_DEVICE_REQUIRED[@]}" | grep -qw "${turnkey}"; then msg_info "Configuring TUN device access for ${turnkey}" { echo "lxc.cgroup2.devices.allow: c 10:200 rwm" echo "lxc.mount.entry: /dev/net/tun dev/net/tun none bind,create=file 0 0" } >>"/etc/pve/lxc/${CTID}.conf" msg_ok "TUN device access configured" sleep 5 fi # Start container msg_info "Starting LXC container" pct start "$CTID" msg_ok "Started LXC container" sleep 10 # Detect container IP msg_info "Detecting IP address" IP="" for attempt in $(seq 1 5); do IP=$(pct exec "$CTID" -- ip -4 a show dev eth0 2>/dev/null | grep -oP 'inet \K[^/]+' || true) if [[ -n "$IP" ]]; then break fi [[ $attempt -lt 5 ]] && sleep 5 done if [[ -z "$IP" ]]; then msg_warn "IP address not found after 5 attempts" IP="NOT FOUND" else msg_ok "IP address: ${BL}${IP}${CL}" fi # Report success to telemetry post_update_to_api "done" "none" # Success summary header_info echo msg_ok "TurnKey ${BL}${turnkey}${CL} LXC container '${BL}${CTID}${CL}' was successfully created." echo echo -e " ${TAB}${YW}IP Address:${CL} ${BL}${IP}${CL}" echo -e " ${TAB}${YW}Login:${CL} ${GN}root${CL}" echo -e " ${TAB}${YW}Password:${CL} ${GN}${PASS}${CL}" echo echo -e " ${TAB}Proceed to the LXC console to complete the TurnKey setup." echo -e " ${TAB}Credentials stored in: ${BL}~/turnkey-${turnkey}.creds${CL}" echo ================================================ FILE: vm/archlinux-vm.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE source /dev/stdin <<<$(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/api.func) function header_info { clear cat <<"EOF" ___ __ __ _ _ ____ ___ / | __________/ /_ / / (_)___ __ ___ __ | | / / |/ / / /| | / ___/ ___/ __ \ / / / / __ \/ / / / |/_/ | | / / /|_/ / / ___ |/ / / /__/ / / / / /___/ / / / / /_/ /> < | |/ / / / / /_/ |_/_/ \___/_/ /_/ /_____/_/_/ /_/\__,_/_/|_| |___/_/ /_/ EOF } header_info echo -e "\n Loading..." GEN_MAC=02:$(openssl rand -hex 5 | awk '{print toupper($0)}' | sed 's/\(..\)/\1:/g; s/.$//') RANDOM_UUID="$(cat /proc/sys/kernel/random/uuid)" METHOD="" NSAPP="archlinux-vm" var_os="arch-linux" var_version="n.d." YW=$(echo "\033[33m") BL=$(echo "\033[36m") RD=$(echo "\033[01;31m") BGN=$(echo "\033[4;92m") GN=$(echo "\033[1;92m") DGN=$(echo "\033[32m") CL=$(echo "\033[m") CL=$(echo "\033[m") BOLD=$(echo "\033[1m") BFR="\\r\\033[K" HOLD=" " TAB=" " CM="${TAB}✔️${TAB}${CL}" CROSS="${TAB}✖️${TAB}${CL}" INFO="${TAB}💡${TAB}${CL}" OS="${TAB}🖥️${TAB}${CL}" CONTAINERTYPE="${TAB}📦${TAB}${CL}" DISKSIZE="${TAB}💾${TAB}${CL}" CPUCORE="${TAB}🧠${TAB}${CL}" RAMSIZE="${TAB}🛠️${TAB}${CL}" CONTAINERID="${TAB}🆔${TAB}${CL}" HOSTNAME="${TAB}🏠${TAB}${CL}" BRIDGE="${TAB}🌉${TAB}${CL}" GATEWAY="${TAB}🌐${TAB}${CL}" DEFAULT="${TAB}⚙️${TAB}${CL}" MACADDRESS="${TAB}🔗${TAB}${CL}" VLANTAG="${TAB}🏷️${TAB}${CL}" CREATING="${TAB}🚀${TAB}${CL}" ADVANCED="${TAB}🧩${TAB}${CL}" CLOUD="${TAB}☁️${TAB}${CL}" THIN="discard=on,ssd=1," set -e trap 'error_handler $LINENO "$BASH_COMMAND"' ERR trap cleanup EXIT trap 'post_update_to_api "failed" "130"' SIGINT trap 'post_update_to_api "failed" "143"' SIGTERM trap 'post_update_to_api "failed" "129"; exit 129' SIGHUP function error_handler() { local exit_code="$?" local line_number="$1" local command="$2" local error_message="${RD}[ERROR]${CL} in line ${RD}$line_number${CL}: exit code ${RD}$exit_code${CL}: while executing command ${YW}$command${CL}" post_update_to_api "failed" "${exit_code}" echo -e "\n$error_message\n" cleanup_vmid } function get_valid_nextid() { local try_id try_id=$(pvesh get /cluster/nextid) while true; do if [ -f "/etc/pve/qemu-server/${try_id}.conf" ] || [ -f "/etc/pve/lxc/${try_id}.conf" ]; then try_id=$((try_id + 1)) continue fi if lvs --noheadings -o lv_name | grep -qE "(^|[-_])${try_id}($|[-_])"; then try_id=$((try_id + 1)) continue fi break done echo "$try_id" } function cleanup_vmid() { if qm status $VMID &>/dev/null; then qm stop $VMID &>/dev/null qm destroy $VMID &>/dev/null fi } function cleanup() { local exit_code=$? popd >/dev/null if [[ "${POST_TO_API_DONE:-}" == "true" && "${POST_UPDATE_DONE:-}" != "true" ]]; then if [[ $exit_code -eq 0 ]]; then post_update_to_api "done" "none" else post_update_to_api "failed" "$exit_code" fi fi rm -rf $TEMP_DIR } TEMP_DIR=$(mktemp -d) pushd $TEMP_DIR >/dev/null if whiptail --backtitle "Proxmox VE Helper Scripts" --title "Arch Linux VM" --yesno "This will create a New Arch Linux VM. Proceed?" 10 58; then : else header_info && echo -e "${CROSS}${RD}User exited script${CL}\n" && exit fi function msg_info() { local msg="$1" echo -ne "${TAB}${YW}${HOLD}${msg}${HOLD}" } function msg_ok() { local msg="$1" echo -e "${BFR}${CM}${GN}${msg}${CL}" } function msg_error() { local msg="$1" echo -e "${BFR}${CROSS}${RD}${msg}${CL}" } function check_root() { if [[ "$(id -u)" -ne 0 || $(ps -o comm= -p $PPID) == "sudo" ]]; then clear msg_error "Please run this script as root." echo -e "\nExiting..." sleep 2 exit fi } # This function checks the version of Proxmox Virtual Environment (PVE) and exits if the version is not supported. # Supported: Proxmox VE 8.0.x – 8.9.x, 9.0 and 9.1 pve_check() { local PVE_VER PVE_VER="$(pveversion | awk -F'/' '{print $2}' | awk -F'-' '{print $1}')" # Check for Proxmox VE 8.x: allow 8.0–8.9 if [[ "$PVE_VER" =~ ^8\.([0-9]+) ]]; then local MINOR="${BASH_REMATCH[1]}" if ((MINOR < 0 || MINOR > 9)); then msg_error "This version of Proxmox VE is not supported." msg_error "Supported: Proxmox VE version 8.0 – 8.9" exit 105 fi return 0 fi # Check for Proxmox VE 9.x: allow 9.0 and 9.1 if [[ "$PVE_VER" =~ ^9\.([0-9]+) ]]; then local MINOR="${BASH_REMATCH[1]}" if ((MINOR < 0 || MINOR > 1)); then msg_error "This version of Proxmox VE is not supported." msg_error "Supported: Proxmox VE version 9.0 – 9.1" exit 105 fi return 0 fi # All other unsupported versions msg_error "This version of Proxmox VE is not supported." msg_error "Supported versions: Proxmox VE 8.0 – 8.x or 9.0 – 9.1" exit 105 } function arch_check() { if [ "$(dpkg --print-architecture)" != "amd64" ]; then echo -e "\n ${INFO}${YWB}This script will not work with PiMox! \n" echo -e "\n ${YWB}Visit https://github.com/asylumexp/Proxmox for ARM64 support. \n" echo -e "Exiting..." sleep 2 exit fi } function ssh_check() { if command -v pveversion >/dev/null 2>&1; then if [ -n "${SSH_CLIENT:+x}" ]; then if whiptail --backtitle "Proxmox VE Helper Scripts" --defaultno --title "SSH DETECTED" --yesno "It's suggested to use the Proxmox shell instead of SSH, since SSH can create issues while gathering variables. Would you like to proceed with using SSH?" 10 62; then echo "you've been warned" else clear exit fi fi fi } function exit-script() { clear echo -e "\n${CROSS}${RD}User exited script${CL}\n" exit } function default_settings() { VMID=$(get_valid_nextid) MACHINE="" DISK_SIZE="4G" DISK_CACHE="" HN="arch-linux" CPU_TYPE="" CORE_COUNT="1" RAM_SIZE="1024" BRG="vmbr0" MAC="$GEN_MAC" VLAN="" MTU="" START_VM="yes" METHOD="default" echo -e "${CONTAINERID}${BOLD}${DGN}Virtual Machine ID: ${BGN}${VMID}${CL}" echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}i440fx${CL}" echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}${DISK_SIZE}${CL}" echo -e "${DISKSIZE}${BOLD}${DGN}Disk Cache: ${BGN}None${CL}" echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}${HN}${CL}" echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}KVM64${CL}" echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}${CORE_COUNT}${CL}" echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}${RAM_SIZE}${CL}" echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}${BRG}${CL}" echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}${MAC}${CL}" echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}Default${CL}" echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}Default${CL}" echo -e "${GATEWAY}${BOLD}${DGN}Start VM when completed: ${BGN}yes${CL}" echo -e "${CREATING}${BOLD}${DGN}Creating a Arch Linux VM using the above default settings${CL}" } function advanced_settings() { METHOD="advanced" [ -z "${VMID:-}" ] && VMID=$(get_valid_nextid) while true; do if VMID=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Virtual Machine ID" 8 58 "$VMID" --title "VIRTUAL MACHINE ID" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z "$VMID" ]; then VMID=$(get_valid_nextid) fi if pct status "$VMID" &>/dev/null || qm status "$VMID" &>/dev/null; then echo -e "${CROSS}${RD} ID $VMID is already in use${CL}" sleep 2 continue fi echo -e "${CONTAINERID}${BOLD}${DGN}Virtual Machine ID: ${BGN}$VMID${CL}" break else exit-script fi done if MACH=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "MACHINE TYPE" --radiolist --cancel-button Exit-Script "Choose Type" 10 58 2 \ "i440fx" "Machine i440fx" ON \ "q35" "Machine q35" OFF \ 3>&1 1>&2 2>&3); then if [ "$MACH" = q35 ]; then echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}$MACH${CL}" MACHINE=" -machine q35" else echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}$MACH${CL}" MACHINE="" fi else exit-script fi if DISK_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Disk Size in GiB (e.g., 10, 20)" 8 58 "$DISK_SIZE" --title "DISK SIZE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then DISK_SIZE=$(echo "$DISK_SIZE" | tr -d ' ') if [[ "$DISK_SIZE" =~ ^[0-9]+$ ]]; then DISK_SIZE="${DISK_SIZE}G" echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}$DISK_SIZE${CL}" elif [[ "$DISK_SIZE" =~ ^[0-9]+G$ ]]; then echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}$DISK_SIZE${CL}" else echo -e "${DISKSIZE}${BOLD}${RD}Invalid Disk Size. Please use a number (e.g., 10 or 10G).${CL}" exit-script fi else exit-script fi if DISK_CACHE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "DISK CACHE" --radiolist "Choose" --cancel-button Exit-Script 10 58 2 \ "0" "None (Default)" ON \ "1" "Write Through" OFF \ 3>&1 1>&2 2>&3); then if [ "$DISK_CACHE" = "1" ]; then echo -e "${DISKSIZE}${BOLD}${DGN}Disk Cache: ${BGN}Write Through${CL}" DISK_CACHE="cache=writethrough," else echo -e "${DISKSIZE}${BOLD}${DGN}Disk Cache: ${BGN}None${CL}" DISK_CACHE="" fi else exit-script fi if VM_NAME=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Hostname" 8 58 arch-linux --title "HOSTNAME" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z "$VM_NAME" ]; then HN="arch-linux" echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}" else HN=$(echo "${VM_NAME,,}" | tr -cs 'a-z0-9-' '-' | sed 's/^-//;s/-$//') if [ "$HN" != "${VM_NAME,,}" ]; then whiptail --backtitle "Proxmox VE Helper Scripts" --title "HOSTNAME ADJUSTED" --msgbox "Invalid characters detected. Hostname has been adjusted to:\n\n $HN" 10 58 fi echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}" fi else exit-script fi if CPU_TYPE1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "CPU MODEL" --radiolist "Choose" --cancel-button Exit-Script 10 58 2 \ "0" "KVM64 (Default)" ON \ "1" "Host" OFF \ 3>&1 1>&2 2>&3); then if [ "$CPU_TYPE1" = "1" ]; then echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}Host${CL}" CPU_TYPE=" -cpu host" else echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}KVM64${CL}" CPU_TYPE="" fi else exit-script fi while true; do if CORE_COUNT=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Allocate CPU Cores" 8 58 2 --title "CORE COUNT" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z "$CORE_COUNT" ]; then CORE_COUNT="2"; fi if [[ "$CORE_COUNT" =~ ^[1-9][0-9]*$ ]]; then echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}$CORE_COUNT${CL}" break fi whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "CPU Cores must be a positive integer (e.g., 2)." 8 58 else exit-script fi done while true; do if RAM_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Allocate RAM in MiB" 8 58 2048 --title "RAM" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z "$RAM_SIZE" ]; then RAM_SIZE="2048"; fi if [[ "$RAM_SIZE" =~ ^[1-9][0-9]*$ ]]; then echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}$RAM_SIZE${CL}" break fi whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "RAM Size must be a positive integer in MiB (e.g., 2048)." 8 58 else exit-script fi done if BRG=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a Bridge" 8 58 vmbr0 --title "BRIDGE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z "$BRG" ]; then BRG="vmbr0" echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}" else echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}" fi else exit-script fi while true; do if MAC1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a MAC Address" 8 58 "$GEN_MAC" --title "MAC ADDRESS" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z "$MAC1" ]; then MAC="$GEN_MAC" echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}$MAC${CL}" break fi if [[ "$MAC1" =~ ^([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}$ ]]; then MAC="$MAC1" echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}$MAC1${CL}" break fi whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "Invalid MAC address format. Use XX:XX:XX:XX:XX:XX (e.g., AA:BB:CC:DD:EE:FF)." 8 58 else exit-script fi done while true; do if VLAN1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a Vlan(leave blank for default)" 8 58 --title "VLAN" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z "$VLAN1" ]; then VLAN1="Default" VLAN="" echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}$VLAN1${CL}" break fi if [[ "$VLAN1" =~ ^[0-9]+$ ]] && [ "$VLAN1" -ge 1 ] && [ "$VLAN1" -le 4094 ]; then VLAN=",tag=$VLAN1" echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}$VLAN1${CL}" break fi whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "VLAN must be a number between 1 and 4094, or leave blank for default." 8 58 else exit-script fi done while true; do if MTU1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Interface MTU Size (leave blank for default)" 8 58 --title "MTU SIZE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z "$MTU1" ]; then MTU1="Default" MTU="" echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}$MTU1${CL}" break fi if [[ "$MTU1" =~ ^[0-9]+$ ]] && [ "$MTU1" -ge 576 ] && [ "$MTU1" -le 65520 ]; then MTU=",mtu=$MTU1" echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}$MTU1${CL}" break fi whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "MTU Size must be a number between 576 and 65520, or leave blank for default." 8 58 else exit-script fi done if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "START VIRTUAL MACHINE" --yesno "Start VM when completed?" 10 58); then echo -e "${GATEWAY}${BOLD}${DGN}Start VM when completed: ${BGN}yes${CL}" START_VM="yes" else echo -e "${GATEWAY}${BOLD}${DGN}Start VM when completed: ${BGN}no${CL}" START_VM="no" fi if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "ADVANCED SETTINGS COMPLETE" --yesno "Ready to create a Arch Linux VM?" --no-button Do-Over 10 58); then echo -e "${CREATING}${BOLD}${DGN}Creating a Arch Linux VM using the above advanced settings${CL}" else header_info echo -e "${ADVANCED}${BOLD}${RD}Using Advanced Settings${CL}" advanced_settings fi } function start_script() { if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "SETTINGS" --yesno "Use Default Settings?" --no-button Advanced 10 58); then header_info echo -e "${DEFAULT}${BOLD}${BL}Using Default Settings${CL}" default_settings else header_info echo -e "${ADVANCED}${BOLD}${RD}Using Advanced Settings${CL}" advanced_settings fi } check_root pve_check ssh_check start_script post_to_api_vm msg_info "Validating Storage" while read -r line; do TAG=$(echo "$line" | awk '{print $1}') TYPE=$(echo "$line" | awk '{printf "%-10s", $2}') FREE=$(echo "$line" | numfmt --field 4-6 --from-unit=K --to=iec --format %.2f | awk '{printf( "%9sB", $6)}') ITEM=" Type: $TYPE Free: $FREE " OFFSET=2 if [[ $((${#ITEM} + $OFFSET)) -gt ${MSG_MAX_LENGTH:-} ]]; then MSG_MAX_LENGTH=$((${#ITEM} + $OFFSET)) fi STORAGE_MENU+=("$TAG" "$ITEM" "OFF") done < <(pvesm status -content images | awk 'NR>1') VALID=$(pvesm status -content images | awk 'NR>1') if [ -z "$VALID" ]; then msg_error "Unable to detect a valid storage location." exit elif [ $((${#STORAGE_MENU[@]} / 3)) -eq 1 ]; then STORAGE=${STORAGE_MENU[0]} else while [ -z "${STORAGE:+x}" ]; do STORAGE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "Storage Pools" --radiolist \ "Which storage pool would you like to use for ${HN}?\nTo make a selection, use the Spacebar.\n" \ 16 $(($MSG_MAX_LENGTH + 23)) 6 \ "${STORAGE_MENU[@]}" 3>&1 1>&2 2>&3) done fi msg_ok "Using ${CL}${BL}$STORAGE${CL} ${GN}for Storage Location." msg_ok "Virtual Machine ID is ${CL}${BL}$VMID${CL}." msg_info "Retrieving the URL for the Arch Linux .iso File" URL=https://geo.mirror.pkgbuild.com/iso/latest/archlinux-x86_64.iso sleep 2 msg_ok "${CL}${BL}${URL}${CL}" curl -f#SL -o "$(basename "$URL")" "$URL" echo -en "\e[1A\e[0K" FILE=$(basename $URL) msg_ok "Downloaded ${CL}${BL}${FILE}${CL}" STORAGE_TYPE=$(pvesm status -storage "$STORAGE" | awk 'NR>1 {print $2}') case $STORAGE_TYPE in nfs | dir | cifs) DISK_EXT=".qcow2" DISK_REF="$VMID/" DISK_IMPORT="--format qcow2" THIN="" ;; btrfs) DISK_EXT=".raw" DISK_REF="$VMID/" DISK_IMPORT="--format raw" THIN="" ;; *) DISK_EXT="" DISK_REF="" DISK_IMPORT="--format raw" ;; esac msg_info "Creating a Arch Linux VM" qm create $VMID -agent 1${MACHINE} -tablet 0 -localtime 1 -bios ovmf${CPU_TYPE} -cores $CORE_COUNT -memory $RAM_SIZE \ -name $HN -tags community-script -net0 virtio,bridge=$BRG,macaddr=$MAC$VLAN$MTU -onboot 1 -ostype l26 -scsihw virtio-scsi-pci if qm disk import --help >/dev/null 2>&1; then IMPORT_CMD=(qm disk import) else IMPORT_CMD=(qm importdisk) fi IMPORT_OUT="$("${IMPORT_CMD[@]}" "$VMID" "${FILE}" "$STORAGE" ${DISK_IMPORT:-} 2>&1 || true)" DISK_REF_IMPORTED="$(printf '%s\n' "$IMPORT_OUT" | sed -n "s/.*successfully imported disk '\([^']\+\)'.*/\1/p" | tr -d "\r\"'")" [[ -z "$DISK_REF_IMPORTED" ]] && DISK_REF_IMPORTED="$(pvesm list "$STORAGE" | awk -v id="$VMID" '$5 ~ ("vm-"id"-disk-") {print $1":"$5}' | sort | tail -n1)" [[ -z "$DISK_REF_IMPORTED" ]] && { msg_error "Unable to determine imported disk reference." echo "$IMPORT_OUT" exit 226 } msg_ok "Imported disk (${CL}${BL}${DISK_REF_IMPORTED}${CL})" qm set $VMID \ -efidisk0 ${STORAGE}:0,efitype=4m \ -scsi0 ${DISK_REF_IMPORTED},${DISK_CACHE}${THIN%,} \ -ide2 ${STORAGE}:cloudinit \ -boot order=scsi0 \ -serial0 socket >/dev/null DESCRIPTION=$( cat < Logo

Arch Linux VM

spend Coffee

GitHub Discussions Issues EOF ) qm set $VMID -description "$DESCRIPTION" >/dev/null if [ -n "$DISK_SIZE" ]; then msg_info "Resizing disk to $DISK_SIZE GB" qm resize $VMID scsi0 ${DISK_SIZE} >/dev/null else msg_info "Using default disk size of $DEFAULT_DISK_SIZE GB" qm resize $VMID scsi0 ${DEFAULT_DISK_SIZE} >/dev/null fi msg_ok "Created a Arch Linux VM ${CL}${BL}(${HN})" if [ "$START_VM" == "yes" ]; then msg_info "Starting Arch Linux VM" qm start $VMID msg_ok "Started Arch Linux VM" fi post_update_to_api "done" "none" msg_ok "Completed successfully!\n" ================================================ FILE: vm/debian-13-vm.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE source /dev/stdin <<<$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func) function header_info { clear cat <<"EOF" ____ __ _ ________ / __ \___ / /_ (_)___ _____ < /__ / / / / / _ \/ __ \/ / __ `/ __ \ / / /_ < / /_/ / __/ /_/ / / /_/ / / / / / /___/ / /_____/\___/_.___/_/\__,_/_/ /_/ /_//____/ (Trixie) EOF } header_info echo -e "\n Loading..." GEN_MAC=02:$(openssl rand -hex 5 | awk '{print toupper($0)}' | sed 's/\(..\)/\1:/g; s/.$//') RANDOM_UUID="$(cat /proc/sys/kernel/random/uuid)" METHOD="" NSAPP="debian-13-vm" var_os="debian" var_version="13" YW=$(echo "\033[33m") BL=$(echo "\033[36m") RD=$(echo "\033[01;31m") BGN=$(echo "\033[4;92m") GN=$(echo "\033[1;92m") DGN=$(echo "\033[32m") CL=$(echo "\033[m") CL=$(echo "\033[m") BOLD=$(echo "\033[1m") BFR="\\r\\033[K" HOLD=" " TAB=" " CM="${TAB}✔️${TAB}${CL}" CROSS="${TAB}✖️${TAB}${CL}" INFO="${TAB}💡${TAB}${CL}" OS="${TAB}🖥️${TAB}${CL}" CONTAINERTYPE="${TAB}📦${TAB}${CL}" DISKSIZE="${TAB}💾${TAB}${CL}" CPUCORE="${TAB}🧠${TAB}${CL}" RAMSIZE="${TAB}🛠️${TAB}${CL}" CONTAINERID="${TAB}🆔${TAB}${CL}" HOSTNAME="${TAB}🏠${TAB}${CL}" BRIDGE="${TAB}🌉${TAB}${CL}" GATEWAY="${TAB}🌐${TAB}${CL}" DEFAULT="${TAB}⚙️${TAB}${CL}" MACADDRESS="${TAB}🔗${TAB}${CL}" VLANTAG="${TAB}🏷️${TAB}${CL}" CREATING="${TAB}🚀${TAB}${CL}" ADVANCED="${TAB}🧩${TAB}${CL}" CLOUD="${TAB}☁️${TAB}${CL}" THIN="discard=on,ssd=1," set -e trap 'error_handler $LINENO "$BASH_COMMAND"' ERR trap cleanup EXIT trap 'post_update_to_api "failed" "130"' SIGINT trap 'post_update_to_api "failed" "143"' SIGTERM trap 'post_update_to_api "failed" "129"; exit 129' SIGHUP function error_handler() { local exit_code="$?" local line_number="$1" local command="$2" local error_message="${RD}[ERROR]${CL} in line ${RD}$line_number${CL}: exit code ${RD}$exit_code${CL}: while executing command ${YW}$command${CL}" post_update_to_api "failed" "${exit_code}" echo -e "\n$error_message\n" cleanup_vmid } function get_valid_nextid() { local try_id try_id=$(pvesh get /cluster/nextid) while true; do if [ -f "/etc/pve/qemu-server/${try_id}.conf" ] || [ -f "/etc/pve/lxc/${try_id}.conf" ]; then try_id=$((try_id + 1)) continue fi if lvs --noheadings -o lv_name | grep -qE "(^|[-_])${try_id}($|[-_])"; then try_id=$((try_id + 1)) continue fi break done echo "$try_id" } function cleanup_vmid() { if qm status $VMID &>/dev/null; then qm stop $VMID &>/dev/null qm destroy $VMID &>/dev/null fi } function cleanup() { local exit_code=$? popd >/dev/null if [[ "${POST_TO_API_DONE:-}" == "true" && "${POST_UPDATE_DONE:-}" != "true" ]]; then if [[ $exit_code -eq 0 ]]; then post_update_to_api "done" "none" else post_update_to_api "failed" "$exit_code" fi fi rm -rf $TEMP_DIR } TEMP_DIR=$(mktemp -d) pushd $TEMP_DIR >/dev/null if whiptail --backtitle "Proxmox VE Helper Scripts" --title "Debian 13 VM" --yesno "This will create a New Debian 13 VM. Proceed?" 10 58; then : else header_info && echo -e "${CROSS}${RD}User exited script${CL}\n" && exit fi function msg_info() { local msg="$1" echo -ne "${TAB}${YW}${HOLD}${msg}${HOLD}" } function msg_ok() { local msg="$1" echo -e "${BFR}${CM}${GN}${msg}${CL}" } function msg_error() { local msg="$1" echo -e "${BFR}${CROSS}${RD}${msg}${CL}" } function check_root() { if [[ "$(id -u)" -ne 0 || $(ps -o comm= -p $PPID) == "sudo" ]]; then clear msg_error "Please run this script as root." echo -e "\nExiting..." sleep 2 exit fi } # This function checks the version of Proxmox Virtual Environment (PVE) and exits if the version is not supported. # Supported: Proxmox VE 8.0.x – 8.9.x, 9.0 and 9.1 pve_check() { local PVE_VER PVE_VER="$(pveversion | awk -F'/' '{print $2}' | awk -F'-' '{print $1}')" # Check for Proxmox VE 8.x: allow 8.0–8.9 if [[ "$PVE_VER" =~ ^8\.([0-9]+) ]]; then local MINOR="${BASH_REMATCH[1]}" if ((MINOR < 0 || MINOR > 9)); then msg_error "This version of Proxmox VE is not supported." msg_error "Supported: Proxmox VE version 8.0 – 8.9" exit 105 fi return 0 fi # Check for Proxmox VE 9.x: allow 9.0 and 9.1 if [[ "$PVE_VER" =~ ^9\.([0-9]+) ]]; then local MINOR="${BASH_REMATCH[1]}" if ((MINOR < 0 || MINOR > 1)); then msg_error "This version of Proxmox VE is not supported." msg_error "Supported: Proxmox VE version 9.0 – 9.1" exit 105 fi return 0 fi # All other unsupported versions msg_error "This version of Proxmox VE is not supported." msg_error "Supported versions: Proxmox VE 8.0 – 8.x or 9.0 – 9.1" exit 105 } function arch_check() { if [ "$(dpkg --print-architecture)" != "amd64" ]; then echo -e "\n ${INFO}${YWB}This script will not work with PiMox! \n" echo -e "\n ${YWB}Visit https://github.com/asylumexp/Proxmox for ARM64 support. \n" echo -e "Exiting..." sleep 2 exit fi } function ssh_check() { if command -v pveversion >/dev/null 2>&1; then if [ -n "${SSH_CLIENT:+x}" ]; then if whiptail --backtitle "Proxmox VE Helper Scripts" --defaultno --title "SSH DETECTED" --yesno "It's suggested to use the Proxmox shell instead of SSH, since SSH can create issues while gathering variables. Would you like to proceed with using SSH?" 10 62; then echo "you've been warned" else clear exit fi fi fi } function exit-script() { clear echo -e "\n${CROSS}${RD}User exited script${CL}\n" exit } function select_cloud_init() { if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "CLOUD-INIT" \ --yesno "Enable Cloud-Init for VM configuration?\n\nCloud-Init allows automatic configuration of:\n- User accounts and passwords\n- SSH keys\n- Network settings (DHCP/Static)\n- DNS configuration\n\nYou can also configure these settings later in Proxmox UI.\n\nNote: Without Cloud-Init, the nocloud image will be used with console auto-login." --defaultno 18 68); then CLOUD_INIT="yes" echo -e "${CLOUD}${BOLD}${DGN}Cloud-Init: ${BGN}yes${CL}" else CLOUD_INIT="no" echo -e "${CLOUD}${BOLD}${DGN}Cloud-Init: ${BGN}no${CL}" fi } function default_settings() { VMID=$(get_valid_nextid) FORMAT=",efitype=4m" MACHINE="" DISK_SIZE="8G" DISK_CACHE="" HN="debian" CPU_TYPE="" CORE_COUNT="2" RAM_SIZE="2048" BRG="vmbr0" MAC="$GEN_MAC" VLAN="" MTU="" START_VM="yes" METHOD="default" echo -e "${CONTAINERID}${BOLD}${DGN}Virtual Machine ID: ${BGN}${VMID}${CL}" echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}i440fx${CL}" echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}${DISK_SIZE}${CL}" echo -e "${DISKSIZE}${BOLD}${DGN}Disk Cache: ${BGN}None${CL}" echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}${HN}${CL}" echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}KVM64${CL}" echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}${CORE_COUNT}${CL}" echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}${RAM_SIZE}${CL}" echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}${BRG}${CL}" echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}${MAC}${CL}" echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}Default${CL}" echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}Default${CL}" select_cloud_init echo -e "${GATEWAY}${BOLD}${DGN}Start VM when completed: ${BGN}yes${CL}" echo -e "${CREATING}${BOLD}${DGN}Creating a Debian 13 VM using the above default settings${CL}" } function advanced_settings() { METHOD="advanced" [ -z "${VMID:-}" ] && VMID=$(get_valid_nextid) while true; do if VMID=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Virtual Machine ID" 8 58 "$VMID" --title "VIRTUAL MACHINE ID" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z "$VMID" ]; then VMID=$(get_valid_nextid) fi if pct status "$VMID" &>/dev/null || qm status "$VMID" &>/dev/null; then echo -e "${CROSS}${RD} ID $VMID is already in use${CL}" sleep 2 continue fi echo -e "${CONTAINERID}${BOLD}${DGN}Virtual Machine ID: ${BGN}$VMID${CL}" break else exit-script fi done if MACH=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "MACHINE TYPE" --radiolist --cancel-button Exit-Script "Choose Type" 10 58 2 \ "i440fx" "Machine i440fx" ON \ "q35" "Machine q35" OFF \ 3>&1 1>&2 2>&3); then if [ "$MACH" = q35 ]; then echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}$MACH${CL}" FORMAT="" MACHINE=" -machine q35" else echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}$MACH${CL}" FORMAT=",efitype=4m" MACHINE="" fi else exit-script fi if DISK_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Disk Size in GiB (e.g., 10, 20)" 8 58 "$DISK_SIZE" --title "DISK SIZE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then DISK_SIZE=$(echo "$DISK_SIZE" | tr -d ' ') if [[ "$DISK_SIZE" =~ ^[0-9]+$ ]]; then DISK_SIZE="${DISK_SIZE}G" echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}$DISK_SIZE${CL}" elif [[ "$DISK_SIZE" =~ ^[0-9]+G$ ]]; then echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}$DISK_SIZE${CL}" else echo -e "${DISKSIZE}${BOLD}${RD}Invalid Disk Size. Please use a number (e.g., 10 or 10G).${CL}" exit-script fi else exit-script fi if DISK_CACHE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "DISK CACHE" --radiolist "Choose" --cancel-button Exit-Script 10 58 2 \ "0" "None (Default)" ON \ "1" "Write Through" OFF \ 3>&1 1>&2 2>&3); then if [ "$DISK_CACHE" = "1" ]; then echo -e "${DISKSIZE}${BOLD}${DGN}Disk Cache: ${BGN}Write Through${CL}" DISK_CACHE="cache=writethrough," else echo -e "${DISKSIZE}${BOLD}${DGN}Disk Cache: ${BGN}None${CL}" DISK_CACHE="" fi else exit-script fi if VM_NAME=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Hostname" 8 58 debian --title "HOSTNAME" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z "$VM_NAME" ]; then HN="debian" echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}" else HN=$(echo "${VM_NAME,,}" | tr -cs 'a-z0-9-' '-' | sed 's/^-//;s/-$//') if [ "$HN" != "${VM_NAME,,}" ]; then whiptail --backtitle "Proxmox VE Helper Scripts" --title "HOSTNAME ADJUSTED" --msgbox "Invalid characters detected. Hostname has been adjusted to:\n\n $HN" 10 58 fi echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}" fi else exit-script fi if CPU_TYPE1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "CPU MODEL" --radiolist "Choose" --cancel-button Exit-Script 10 58 2 \ "0" "KVM64 (Default)" ON \ "1" "Host" OFF \ 3>&1 1>&2 2>&3); then if [ "$CPU_TYPE1" = "1" ]; then echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}Host${CL}" CPU_TYPE=" -cpu host" else echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}KVM64${CL}" CPU_TYPE="" fi else exit-script fi while true; do if CORE_COUNT=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Allocate CPU Cores" 8 58 2 --title "CORE COUNT" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z "$CORE_COUNT" ]; then CORE_COUNT="2"; fi if [[ "$CORE_COUNT" =~ ^[1-9][0-9]*$ ]]; then echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}$CORE_COUNT${CL}" break fi whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "CPU Cores must be a positive integer (e.g., 2)." 8 58 else exit-script fi done while true; do if RAM_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Allocate RAM in MiB" 8 58 2048 --title "RAM" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z "$RAM_SIZE" ]; then RAM_SIZE="2048"; fi if [[ "$RAM_SIZE" =~ ^[1-9][0-9]*$ ]]; then echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}$RAM_SIZE${CL}" break fi whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "RAM Size must be a positive integer in MiB (e.g., 2048)." 8 58 else exit-script fi done if BRG=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a Bridge" 8 58 vmbr0 --title "BRIDGE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z "$BRG" ]; then BRG="vmbr0" echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}" else echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}" fi else exit-script fi while true; do if MAC1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a MAC Address" 8 58 "$GEN_MAC" --title "MAC ADDRESS" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z "$MAC1" ]; then MAC="$GEN_MAC" echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}$MAC${CL}" break fi if [[ "$MAC1" =~ ^([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}$ ]]; then MAC="$MAC1" echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}$MAC1${CL}" break fi whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "Invalid MAC address format. Use XX:XX:XX:XX:XX:XX (e.g., AA:BB:CC:DD:EE:FF)." 8 58 else exit-script fi done while true; do if VLAN1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a Vlan(leave blank for default)" 8 58 --title "VLAN" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z "$VLAN1" ]; then VLAN1="Default" VLAN="" echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}$VLAN1${CL}" break fi if [[ "$VLAN1" =~ ^[0-9]+$ ]] && [ "$VLAN1" -ge 1 ] && [ "$VLAN1" -le 4094 ]; then VLAN=",tag=$VLAN1" echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}$VLAN1${CL}" break fi whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "VLAN must be a number between 1 and 4094, or leave blank for default." 8 58 else exit-script fi done while true; do if MTU1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Interface MTU Size (leave blank for default)" 8 58 --title "MTU SIZE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z "$MTU1" ]; then MTU1="Default" MTU="" echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}$MTU1${CL}" break fi if [[ "$MTU1" =~ ^[0-9]+$ ]] && [ "$MTU1" -ge 576 ] && [ "$MTU1" -le 65520 ]; then MTU=",mtu=$MTU1" echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}$MTU1${CL}" break fi whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "MTU Size must be a number between 576 and 65520, or leave blank for default." 8 58 else exit-script fi done select_cloud_init if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "START VIRTUAL MACHINE" --yesno "Start VM when completed?" 10 58); then echo -e "${GATEWAY}${BOLD}${DGN}Start VM when completed: ${BGN}yes${CL}" START_VM="yes" else echo -e "${GATEWAY}${BOLD}${DGN}Start VM when completed: ${BGN}no${CL}" START_VM="no" fi if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "ADVANCED SETTINGS COMPLETE" --yesno "Ready to create a Debian 13 VM?" --no-button Do-Over 10 58); then echo -e "${CREATING}${BOLD}${DGN}Creating a Debian 13 VM using the above advanced settings${CL}" else header_info echo -e "${ADVANCED}${BOLD}${RD}Using Advanced Settings${CL}" advanced_settings fi } function start_script() { if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "SETTINGS" --yesno "Use Default Settings?" --no-button Advanced 10 58); then header_info echo -e "${DEFAULT}${BOLD}${BL}Using Default Settings${CL}" default_settings else header_info echo -e "${ADVANCED}${BOLD}${RD}Using Advanced Settings${CL}" advanced_settings fi } check_root arch_check pve_check ssh_check start_script post_to_api_vm msg_info "Validating Storage" while read -r line; do TAG=$(echo "$line" | awk '{print $1}') TYPE=$(echo "$line" | awk '{printf "%-10s", $2}') FREE=$(echo "$line" | numfmt --field 4-6 --from-unit=K --to=iec --format %.2f | awk '{printf( "%9sB", $6)}') ITEM=" Type: $TYPE Free: $FREE " OFFSET=2 if [[ $((${#ITEM} + $OFFSET)) -gt ${MSG_MAX_LENGTH:-} ]]; then MSG_MAX_LENGTH=$((${#ITEM} + $OFFSET)) fi STORAGE_MENU+=("$TAG" "$ITEM" "OFF") done < <(pvesm status -content images | awk 'NR>1') VALID=$(pvesm status -content images | awk 'NR>1') if [ -z "$VALID" ]; then msg_error "Unable to detect a valid storage location." exit elif [ $((${#STORAGE_MENU[@]} / 3)) -eq 1 ]; then STORAGE=${STORAGE_MENU[0]} else while [ -z "${STORAGE:+x}" ]; do STORAGE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "Storage Pools" --radiolist \ "Which storage pool would you like to use for ${HN}?\nTo make a selection, use the Spacebar.\n" \ 16 $(($MSG_MAX_LENGTH + 23)) 6 \ "${STORAGE_MENU[@]}" 3>&1 1>&2 2>&3) done fi msg_ok "Using ${CL}${BL}$STORAGE${CL} ${GN}for Storage Location." msg_ok "Virtual Machine ID is ${CL}${BL}$VMID${CL}." # ============================================================================== # PREREQUISITES # ============================================================================== if ! command -v virt-customize &>/dev/null; then msg_info "Installing libguestfs-tools" apt-get update >/dev/null 2>&1 apt-get install -y libguestfs-tools >/dev/null 2>&1 msg_ok "Installed libguestfs-tools" fi msg_info "Retrieving the URL for the Debian 13 Qcow2 Disk Image" if [ "$CLOUD_INIT" == "yes" ]; then URL=https://cloud.debian.org/images/cloud/trixie/latest/debian-13-genericcloud-amd64.qcow2 else URL=https://cloud.debian.org/images/cloud/trixie/latest/debian-13-nocloud-amd64.qcow2 fi sleep 2 msg_ok "${CL}${BL}${URL}${CL}" curl -f#SL -o "$(basename "$URL")" "$URL" echo -en "\e[1A\e[0K" FILE=$(basename $URL) msg_ok "Downloaded ${CL}${BL}${FILE}${CL}" # ============================================================================== # IMAGE CUSTOMIZATION # ============================================================================== msg_info "Customizing ${FILE} image" WORK_FILE=$(mktemp --suffix=.qcow2) cp "$FILE" "$WORK_FILE" # Set hostname virt-customize -q -a "$WORK_FILE" --hostname "${HN}" >/dev/null 2>&1 # Prepare for unique machine-id on first boot virt-customize -q -a "$WORK_FILE" --run-command "truncate -s 0 /etc/machine-id" >/dev/null 2>&1 virt-customize -q -a "$WORK_FILE" --run-command "rm -f /var/lib/dbus/machine-id" >/dev/null 2>&1 # Disable systemd-firstboot to prevent interactive prompts blocking the console virt-customize -q -a "$WORK_FILE" --run-command "systemctl disable systemd-firstboot.service 2>/dev/null; rm -f /etc/systemd/system/sysinit.target.wants/systemd-firstboot.service; ln -sf /dev/null /etc/systemd/system/systemd-firstboot.service" >/dev/null 2>&1 || true # Pre-seed firstboot settings so it won't prompt even if triggered virt-customize -q -a "$WORK_FILE" --run-command "echo 'Etc/UTC' > /etc/timezone && ln -sf /usr/share/zoneinfo/Etc/UTC /etc/localtime" >/dev/null 2>&1 || true virt-customize -q -a "$WORK_FILE" --run-command "touch /etc/locale.conf" >/dev/null 2>&1 || true if [ "$CLOUD_INIT" == "yes" ]; then # Cloud-Init handles SSH and login virt-customize -q -a "$WORK_FILE" --run-command "sed -i 's/^#*PermitRootLogin.*/PermitRootLogin yes/' /etc/ssh/sshd_config" >/dev/null 2>&1 || true virt-customize -q -a "$WORK_FILE" --run-command "sed -i 's/^#*PasswordAuthentication.*/PasswordAuthentication yes/' /etc/ssh/sshd_config" >/dev/null 2>&1 || true else # Configure auto-login on serial console (ttyS0) and virtual console (tty1) virt-customize -q -a "$WORK_FILE" --run-command "mkdir -p /etc/systemd/system/serial-getty@ttyS0.service.d" >/dev/null 2>&1 || true virt-customize -q -a "$WORK_FILE" --run-command 'cat > /etc/systemd/system/serial-getty@ttyS0.service.d/autologin.conf << EOF [Service] ExecStart= ExecStart=-/sbin/agetty --autologin root --noclear %I \$TERM EOF' >/dev/null 2>&1 || true virt-customize -q -a "$WORK_FILE" --run-command "mkdir -p /etc/systemd/system/getty@tty1.service.d" >/dev/null 2>&1 || true virt-customize -q -a "$WORK_FILE" --run-command 'cat > /etc/systemd/system/getty@tty1.service.d/autologin.conf << EOF [Service] ExecStart= ExecStart=-/sbin/agetty --autologin root --noclear %I \$TERM EOF' >/dev/null 2>&1 || true fi msg_ok "Customized image" STORAGE_TYPE=$(pvesm status -storage "$STORAGE" | awk 'NR>1 {print $2}') case $STORAGE_TYPE in nfs | dir) DISK_EXT=".qcow2" DISK_REF="$VMID/" DISK_IMPORT="-format qcow2" THIN="" ;; btrfs) DISK_EXT=".raw" DISK_REF="$VMID/" DISK_IMPORT="-format raw" FORMAT=",efitype=4m" THIN="" ;; *) DISK_EXT="" DISK_REF="" DISK_IMPORT="-format raw" ;; esac for i in {0,1}; do disk="DISK$i" eval DISK"${i}"=vm-"${VMID}"-disk-"${i}"${DISK_EXT:-} eval DISK"${i}"_REF="${STORAGE}":"${DISK_REF:-}"${!disk} done msg_info "Creating a Debian 13 VM" qm create $VMID -agent 1${MACHINE} -tablet 0 -localtime 1 -bios ovmf${CPU_TYPE} -cores $CORE_COUNT -memory $RAM_SIZE \ -name $HN -tags community-script -net0 virtio,bridge=$BRG,macaddr=$MAC$VLAN$MTU -onboot 1 -ostype l26 -scsihw virtio-scsi-pci pvesm alloc $STORAGE $VMID $DISK0 4M 1>&/dev/null qm importdisk $VMID ${WORK_FILE} $STORAGE ${DISK_IMPORT:-} 1>&/dev/null if [ "$CLOUD_INIT" == "yes" ]; then qm set $VMID \ -efidisk0 ${DISK0_REF}${FORMAT} \ -scsi0 ${DISK1_REF},${DISK_CACHE}${THIN}size=${DISK_SIZE} \ -scsi1 ${STORAGE}:cloudinit \ -boot order=scsi0 \ -serial0 socket >/dev/null else qm set $VMID \ -efidisk0 ${DISK0_REF}${FORMAT} \ -scsi0 ${DISK1_REF},${DISK_CACHE}${THIN}size=${DISK_SIZE} \ -boot order=scsi0 \ -serial0 socket >/dev/null fi # Clean up work file rm -f "$WORK_FILE" DESCRIPTION=$( cat < Logo

Debian VM

spend Coffee

GitHub Discussions Issues EOF ) qm set $VMID -description "$DESCRIPTION" >/dev/null if [ -n "$DISK_SIZE" ]; then msg_info "Resizing disk to $DISK_SIZE GB" qm resize $VMID scsi0 ${DISK_SIZE} >/dev/null else msg_info "Using default disk size of $DEFAULT_DISK_SIZE GB" qm resize $VMID scsi0 ${DEFAULT_DISK_SIZE} >/dev/null fi msg_ok "Created a Debian 13 VM ${CL}${BL}(${HN})" if [ "$START_VM" == "yes" ]; then msg_info "Starting Debian 13 VM" qm start $VMID msg_ok "Started Debian 13 VM" fi msg_ok "Completed successfully!\n" echo "More Info at https://github.com/community-scripts/ProxmoxVE/discussions/836" ================================================ FILE: vm/debian-vm.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2025 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE source /dev/stdin <<< $(wget -qLO - https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/api.func) function header_info { clear cat <<"EOF" ____ __ _ ______ / __ \___ / /_ (_)___ _____ < /__ \ / / / / _ \/ __ \/ / __ `/ __ \ / /__/ / / /_/ / __/ /_/ / / /_/ / / / / / // __/ /_____/\___/_.___/_/\__,_/_/ /_/ /_//____/ EOF } header_info echo -e "\n Loading..." GEN_MAC=02:$(openssl rand -hex 5 | awk '{print toupper($0)}' | sed 's/\(..\)/\1:/g; s/.$//') RANDOM_UUID="$(cat /proc/sys/kernel/random/uuid)" METHOD="" NSAPP="debian-vm" var_os="debian" var_version="12" YW=$(echo "\033[33m") BL=$(echo "\033[36m") RD=$(echo "\033[01;31m") BGN=$(echo "\033[4;92m") GN=$(echo "\033[1;92m") DGN=$(echo "\033[32m") CL=$(echo "\033[m") CL=$(echo "\033[m") BOLD=$(echo "\033[1m") BFR="\\r\\033[K" HOLD=" " TAB=" " CM="${TAB}✔️${TAB}${CL}" CROSS="${TAB}✖️${TAB}${CL}" INFO="${TAB}💡${TAB}${CL}" OS="${TAB}🖥️${TAB}${CL}" CONTAINERTYPE="${TAB}📦${TAB}${CL}" DISKSIZE="${TAB}💾${TAB}${CL}" CPUCORE="${TAB}🧠${TAB}${CL}" RAMSIZE="${TAB}🛠️${TAB}${CL}" CONTAINERID="${TAB}🆔${TAB}${CL}" HOSTNAME="${TAB}🏠${TAB}${CL}" BRIDGE="${TAB}🌉${TAB}${CL}" GATEWAY="${TAB}🌐${TAB}${CL}" DEFAULT="${TAB}⚙️${TAB}${CL}" MACADDRESS="${TAB}🔗${TAB}${CL}" VLANTAG="${TAB}🏷️${TAB}${CL}" CREATING="${TAB}🚀${TAB}${CL}" ADVANCED="${TAB}🧩${TAB}${CL}" CLOUD="${TAB}☁️${TAB}${CL}" THIN="discard=on,ssd=1," set -e trap 'error_handler $LINENO "$BASH_COMMAND"' ERR trap cleanup EXIT trap 'post_update_to_api "failed" "130"' SIGINT trap 'post_update_to_api "failed" "143"' SIGTERM trap 'post_update_to_api "failed" "129"; exit 129' SIGHUP function error_handler() { local exit_code="$?" local line_number="$1" local command="$2" local error_message="${RD}[ERROR]${CL} in line ${RD}$line_number${CL}: exit code ${RD}$exit_code${CL}: while executing command ${YW}$command${CL}" post_update_to_api "failed" "${exit_code}" echo -e "\n$error_message\n" cleanup_vmid } function get_valid_nextid() { local try_id try_id=$(pvesh get /cluster/nextid) while true; do if [ -f "/etc/pve/qemu-server/${try_id}.conf" ] || [ -f "/etc/pve/lxc/${try_id}.conf" ]; then try_id=$((try_id + 1)) continue fi if lvs --noheadings -o lv_name | grep -qE "(^|[-_])${try_id}($|[-_])"; then try_id=$((try_id + 1)) continue fi break done echo "$try_id" } function cleanup_vmid() { if qm status $VMID &>/dev/null; then qm stop $VMID &>/dev/null qm destroy $VMID &>/dev/null fi } function cleanup() { local exit_code=$? popd >/dev/null if [[ "${POST_TO_API_DONE:-}" == "true" && "${POST_UPDATE_DONE:-}" != "true" ]]; then if [[ $exit_code -eq 0 ]]; then post_update_to_api "done" "none" else post_update_to_api "failed" "$exit_code" fi fi rm -rf $TEMP_DIR } TEMP_DIR=$(mktemp -d) pushd $TEMP_DIR >/dev/null if whiptail --backtitle "Proxmox VE Helper Scripts" --title "Debian 12 VM" --yesno "This will create a New Debian 12 VM. Proceed?" 10 58; then : else header_info && echo -e "${CROSS}${RD}User exited script${CL}\n" && exit fi function msg_info() { local msg="$1" echo -ne "${TAB}${YW}${HOLD}${msg}${HOLD}" } function msg_ok() { local msg="$1" echo -e "${BFR}${CM}${GN}${msg}${CL}" } function msg_error() { local msg="$1" echo -e "${BFR}${CROSS}${RD}${msg}${CL}" } function check_root() { if [[ "$(id -u)" -ne 0 || $(ps -o comm= -p $PPID) == "sudo" ]]; then clear msg_error "Please run this script as root." echo -e "\nExiting..." sleep 2 exit fi } # This function checks the version of Proxmox Virtual Environment (PVE) and exits if the version is not supported. # Supported: Proxmox VE 8.0.x – 8.9.x, 9.0 and 9.1 pve_check() { local PVE_VER PVE_VER="$(pveversion | awk -F'/' '{print $2}' | awk -F'-' '{print $1}')" # Check for Proxmox VE 8.x: allow 8.0–8.9 if [[ "$PVE_VER" =~ ^8\.([0-9]+) ]]; then local MINOR="${BASH_REMATCH[1]}" if ((MINOR < 0 || MINOR > 9)); then msg_error "This version of Proxmox VE is not supported." msg_error "Supported: Proxmox VE version 8.0 – 8.9" exit 105 fi return 0 fi # Check for Proxmox VE 9.x: allow 9.0 and 9.1 if [[ "$PVE_VER" =~ ^9\.([0-9]+) ]]; then local MINOR="${BASH_REMATCH[1]}" if ((MINOR < 0 || MINOR > 1)); then msg_error "This version of Proxmox VE is not supported." msg_error "Supported: Proxmox VE version 9.0 – 9.1" exit 105 fi return 0 fi # All other unsupported versions msg_error "This version of Proxmox VE is not supported." msg_error "Supported versions: Proxmox VE 8.0 – 8.x or 9.0 – 9.1" exit 105 } function arch_check() { if [ "$(dpkg --print-architecture)" != "arm64" ]; then echo -e "\n ${INFO}${YWB}This script only works on PiMox! \n" echo -e "\n ${YWB}Visit https://github.com/community-scripts/ProxmoxVE for AMD64 support. \n" echo -e "Exiting..." sleep 2 exit fi } function ssh_check() { if command -v pveversion >/dev/null 2>&1; then if [ -n "${SSH_CLIENT:+x}" ]; then if whiptail --backtitle "Proxmox VE Helper Scripts" --defaultno --title "SSH DETECTED" --yesno "It's suggested to use the Proxmox shell instead of SSH, since SSH can create issues while gathering variables. Would you like to proceed with using SSH?" 10 62; then echo "you've been warned" else clear exit fi fi fi } function exit-script() { clear echo -e "\n${CROSS}${RD}User exited script${CL}\n" exit } function default_settings() { VMID=$(get_valid_nextid) FORMAT=",efitype=4m" MACHINE="" DISK_SIZE="8G" DISK_CACHE="" HN="debian" CPU_TYPE="" CORE_COUNT="2" RAM_SIZE="2048" BRG="vmbr0" MAC="$GEN_MAC" VLAN="" MTU="" START_VM="yes" CLOUD_INIT="no" METHOD="default" echo -e "${CONTAINERID}${BOLD}${DGN}Virtual Machine ID: ${BGN}${VMID}${CL}" echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}i440fx${CL}" echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}${DISK_SIZE}${CL}" echo -e "${DISKSIZE}${BOLD}${DGN}Disk Cache: ${BGN}None${CL}" echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}${HN}${CL}" echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}KVM64${CL}" echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}${CORE_COUNT}${CL}" echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}${RAM_SIZE}${CL}" echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}${BRG}${CL}" echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}${MAC}${CL}" echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}Default${CL}" echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}Default${CL}" echo -e "${CLOUD}${BOLD}${DGN}Configure Cloud-init: ${BGN}no${CL}" echo -e "${GATEWAY}${BOLD}${DGN}Start VM when completed: ${BGN}yes${CL}" echo -e "${CREATING}${BOLD}${DGN}Creating a Debian 12 VM using the above default settings${CL}" } function advanced_settings() { METHOD="advanced" [ -z "${VMID:-}" ] && VMID=$(get_valid_nextid) while true; do if VMID=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Virtual Machine ID" 8 58 $VMID --title "VIRTUAL MACHINE ID" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z "$VMID" ]; then VMID=$(get_valid_nextid) fi if pct status "$VMID" &>/dev/null || qm status "$VMID" &>/dev/null; then echo -e "${CROSS}${RD} ID $VMID is already in use${CL}" sleep 2 continue fi echo -e "${CONTAINERID}${BOLD}${DGN}Virtual Machine ID: ${BGN}$VMID${CL}" break else exit-script fi done if MACH=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "MACHINE TYPE" --radiolist --cancel-button Exit-Script "Choose Type" 10 58 2 \ "i440fx" "Machine i440fx" ON \ "q35" "Machine q35" OFF \ 3>&1 1>&2 2>&3); then if [ $MACH = q35 ]; then echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}$MACH${CL}" FORMAT="" MACHINE=" -machine q35" else echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}$MACH${CL}" FORMAT=",efitype=4m" MACHINE="" fi else exit-script fi if DISK_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Disk Size in GiB (e.g., 10, 20)" 8 58 "$DISK_SIZE" --title "DISK SIZE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then DISK_SIZE=$(echo "$DISK_SIZE" | tr -d ' ') if [[ "$DISK_SIZE" =~ ^[0-9]+$ ]]; then DISK_SIZE="${DISK_SIZE}G" echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}$DISK_SIZE${CL}" elif [[ "$DISK_SIZE" =~ ^[0-9]+G$ ]]; then echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}$DISK_SIZE${CL}" else echo -e "${DISKSIZE}${BOLD}${RD}Invalid Disk Size. Please use a number (e.g., 10 or 10G).${CL}" exit-script fi else exit-script fi if DISK_CACHE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "DISK CACHE" --radiolist "Choose" --cancel-button Exit-Script 10 58 2 \ "0" "None (Default)" ON \ "1" "Write Through" OFF \ 3>&1 1>&2 2>&3); then if [ $DISK_CACHE = "1" ]; then echo -e "${DISKSIZE}${BOLD}${DGN}Disk Cache: ${BGN}Write Through${CL}" DISK_CACHE="cache=writethrough," else echo -e "${DISKSIZE}${BOLD}${DGN}Disk Cache: ${BGN}None${CL}" DISK_CACHE="" fi else exit-script fi if VM_NAME=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Hostname" 8 58 debian --title "HOSTNAME" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z $VM_NAME ]; then HN="debian" echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}" else HN=$(echo "${VM_NAME,,}" | tr -cs 'a-z0-9-' '-' | sed 's/^-//;s/-$//') if [ "$HN" != "${VM_NAME,,}" ]; then whiptail --backtitle "Proxmox VE Helper Scripts" --title "HOSTNAME ADJUSTED" --msgbox "Invalid characters detected. Hostname has been adjusted to:\n\n $HN" 10 58 fi echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}" fi else exit-script fi if CPU_TYPE1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "CPU MODEL" --radiolist "Choose" --cancel-button Exit-Script 10 58 2 \ "0" "KVM64 (Default)" ON \ "1" "Host" OFF \ 3>&1 1>&2 2>&3); then if [ $CPU_TYPE1 = "1" ]; then echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}Host${CL}" CPU_TYPE=" -cpu host" else echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}KVM64${CL}" CPU_TYPE="" fi else exit-script fi while true; do if CORE_COUNT=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Allocate CPU Cores" 8 58 2 --title "CORE COUNT" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z "$CORE_COUNT" ]; then CORE_COUNT="2"; fi if [[ "$CORE_COUNT" =~ ^[1-9][0-9]*$ ]]; then echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}$CORE_COUNT${CL}" break fi whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "CPU Cores must be a positive integer (e.g., 2)." 8 58 else exit-script fi done while true; do if RAM_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Allocate RAM in MiB" 8 58 2048 --title "RAM" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z "$RAM_SIZE" ]; then RAM_SIZE="2048"; fi if [[ "$RAM_SIZE" =~ ^[1-9][0-9]*$ ]]; then echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}$RAM_SIZE${CL}" break fi whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "RAM Size must be a positive integer in MiB (e.g., 2048)." 8 58 else exit-script fi done if BRG=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a Bridge" 8 58 vmbr0 --title "BRIDGE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z $BRG ]; then BRG="vmbr0" echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}" else echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}" fi else exit-script fi while true; do if MAC1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a MAC Address" 8 58 $GEN_MAC --title "MAC ADDRESS" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z "$MAC1" ]; then MAC="$GEN_MAC" echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}$MAC${CL}" break fi if [[ "$MAC1" =~ ^([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}$ ]]; then MAC="$MAC1" echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}$MAC1${CL}" break fi whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "Invalid MAC address format. Use XX:XX:XX:XX:XX:XX (e.g., AA:BB:CC:DD:EE:FF)." 8 58 else exit-script fi done while true; do if VLAN1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a Vlan(leave blank for default)" 8 58 --title "VLAN" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z "$VLAN1" ]; then VLAN1="Default" VLAN="" echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}$VLAN1${CL}" break fi if [[ "$VLAN1" =~ ^[0-9]+$ ]] && [ "$VLAN1" -ge 1 ] && [ "$VLAN1" -le 4094 ]; then VLAN=",tag=$VLAN1" echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}$VLAN1${CL}" break fi whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "VLAN must be a number between 1 and 4094, or leave blank for default." 8 58 else exit-script fi done while true; do if MTU1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Interface MTU Size (leave blank for default)" 8 58 --title "MTU SIZE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z "$MTU1" ]; then MTU1="Default" MTU="" echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}$MTU1${CL}" break fi if [[ "$MTU1" =~ ^[0-9]+$ ]] && [ "$MTU1" -ge 576 ] && [ "$MTU1" -le 65520 ]; then MTU=",mtu=$MTU1" echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}$MTU1${CL}" break fi whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "MTU Size must be a number between 576 and 65520, or leave blank for default." 8 58 else exit-script fi done if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "CLOUD-INIT" --yesno "Configure the VM with Cloud-init?" --defaultno 10 58); then echo -e "${CLOUD}${BOLD}${DGN}Configure Cloud-init: ${BGN}yes${CL}" CLOUD_INIT="yes" else echo -e "${CLOUD}${BOLD}${DGN}Configure Cloud-init: ${BGN}no${CL}" CLOUD_INIT="no" fi if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "START VIRTUAL MACHINE" --yesno "Start VM when completed?" 10 58); then echo -e "${GATEWAY}${BOLD}${DGN}Start VM when completed: ${BGN}yes${CL}" START_VM="yes" else echo -e "${GATEWAY}${BOLD}${DGN}Start VM when completed: ${BGN}no${CL}" START_VM="no" fi if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "ADVANCED SETTINGS COMPLETE" --yesno "Ready to create a Debian 12 VM?" --no-button Do-Over 10 58); then echo -e "${CREATING}${BOLD}${DGN}Creating a Debian 12 VM using the above advanced settings${CL}" else header_info echo -e "${ADVANCED}${BOLD}${RD}Using Advanced Settings${CL}" advanced_settings fi } function start_script() { if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "SETTINGS" --yesno "Use Default Settings?" --no-button Advanced 10 58); then header_info echo -e "${DEFAULT}${BOLD}${BL}Using Default Settings${CL}" default_settings else header_info echo -e "${ADVANCED}${BOLD}${RD}Using Advanced Settings${CL}" advanced_settings fi } check_root arch_check pve_check ssh_check start_script post_to_api_vm msg_info "Validating Storage" while read -r line; do TAG=$(echo $line | awk '{print $1}') TYPE=$(echo $line | awk '{printf "%-10s", $2}') FREE=$(echo $line | numfmt --field 4-6 --from-unit=K --to=iec --format %.2f | awk '{printf( "%9sB", $6)}') ITEM=" Type: $TYPE Free: $FREE " OFFSET=2 if [[ $((${#ITEM} + $OFFSET)) -gt ${MSG_MAX_LENGTH:-} ]]; then MSG_MAX_LENGTH=$((${#ITEM} + $OFFSET)) fi STORAGE_MENU+=("$TAG" "$ITEM" "OFF") done < <(pvesm status -content images | awk 'NR>1') VALID=$(pvesm status -content images | awk 'NR>1') if [ -z "$VALID" ]; then msg_error "Unable to detect a valid storage location." exit elif [ $((${#STORAGE_MENU[@]} / 3)) -eq 1 ]; then STORAGE=${STORAGE_MENU[0]} else while [ -z "${STORAGE:+x}" ]; do STORAGE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "Storage Pools" --radiolist \ "Which storage pool would you like to use for ${HN}?\nTo make a selection, use the Spacebar.\n" \ 16 $(($MSG_MAX_LENGTH + 23)) 6 \ "${STORAGE_MENU[@]}" 3>&1 1>&2 2>&3) done fi msg_ok "Using ${CL}${BL}$STORAGE${CL} ${GN}for Storage Location." msg_ok "Virtual Machine ID is ${CL}${BL}$VMID${CL}." msg_info "Retrieving the URL for the Debian 12 Qcow2 Disk Image" if [ "$CLOUD_INIT" == "yes" ]; then URL=https://cloud.debian.org/images/cloud/bookworm/latest/debian-12-genericcloud-arm64.qcow2 else URL=https://cloud.debian.org/images/cloud/bookworm/latest/debian-12-nocloud-arm64.qcow2 fi sleep 2 msg_ok "${CL}${BL}${URL}${CL}" curl -f#SL -o "$(basename "$URL")" "$URL" echo -en "\e[1A\e[0K" FILE=$(basename $URL) msg_ok "Downloaded ${CL}${BL}${FILE}${CL}" STORAGE_TYPE=$(pvesm status -storage $STORAGE | awk 'NR>1 {print $2}') case $STORAGE_TYPE in nfs | dir) DISK_EXT=".qcow2" DISK_REF="$VMID/" DISK_IMPORT="-format qcow2" THIN="" ;; btrfs) DISK_EXT=".raw" DISK_REF="$VMID/" DISK_IMPORT="-format raw" FORMAT=",efitype=4m" THIN="" ;; *) DISK_EXT="" DISK_REF="" DISK_IMPORT="-format raw" ;; esac for i in {0,1}; do disk="DISK$i" eval DISK${i}=vm-${VMID}-disk-${i}${DISK_EXT:-} eval DISK${i}_REF=${STORAGE}:${DISK_REF:-}${!disk} done msg_info "Creating a Debian 12 VM" qm create $VMID -agent 1${MACHINE} -tablet 0 -localtime 1 -bios ovmf${CPU_TYPE} -cores $CORE_COUNT -memory $RAM_SIZE \ -name $HN -tags community-script -net0 virtio,bridge=$BRG,macaddr=$MAC$VLAN$MTU -onboot 1 -ostype l26 -scsihw virtio-scsi-pci pvesm alloc $STORAGE $VMID $DISK0 64M 1>&/dev/null qm importdisk $VMID ${FILE} $STORAGE ${DISK_IMPORT:-} 1>&/dev/null if [ "$CLOUD_INIT" == "yes" ]; then qm set $VMID \ -efidisk0 ${DISK0_REF}${FORMAT} \ -scsi0 ${DISK1_REF},${DISK_CACHE}${THIN}size=${DISK_SIZE} \ -scsi1 ${STORAGE}:cloudinit \ -boot order=scsi0 \ -serial0 socket >/dev/null else qm set $VMID \ -efidisk0 ${DISK0_REF}${FORMAT} \ -scsi0 ${DISK1_REF},${DISK_CACHE}${THIN}size=${DISK_SIZE} \ -boot order=scsi0 \ -serial0 socket >/dev/null fi DESCRIPTION=$( cat < Logo

Debian VM

spend Coffee

GitHub Discussions Issues EOF ) qm set "$VMID" -description "$DESCRIPTION" >/dev/null msg_ok "Created a Debian 12 VM ${CL}${BL}(${HN})" if [ "$START_VM" == "yes" ]; then msg_info "Starting Debian 12 VM" qm start $VMID msg_ok "Started Debian 12 VM" fi msg_ok "Completed successfully!\n" echo "More Info at https://github.com/community-scripts/ProxmoxVE/discussions/836" ================================================ FILE: vm/docker-vm.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: thost96 (thost96) | michelroegl-brunner | MickLesk # License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE # ============================================================================== # Docker VM - Creates a Docker-ready Virtual Machine # ============================================================================== source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVE/raw/branch/main/misc/api.func) 2>/dev/null source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVE/raw/branch/main/misc/vm-core.func) 2>/dev/null source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVE/raw/branch/main/misc/cloud-init.func) 2>/dev/null || true load_functions # ============================================================================== # SCRIPT VARIABLES # ============================================================================== APP="Docker" APP_TYPE="vm" NSAPP="docker-vm" var_os="debian" var_version="13" GEN_MAC=02:$(openssl rand -hex 5 | awk '{print toupper($0)}' | sed 's/\(..\)/\1:/g; s/.$//') RANDOM_UUID="$(cat /proc/sys/kernel/random/uuid)" METHOD="" DISK_SIZE="10G" USE_CLOUD_INIT="no" OS_TYPE="" OS_VERSION="" THIN="discard=on,ssd=1," # ============================================================================== # ERROR HANDLING & CLEANUP # ============================================================================== set -e trap 'error_handler $LINENO "$BASH_COMMAND"' ERR trap cleanup EXIT trap 'post_update_to_api "failed" "130"' SIGINT trap 'post_update_to_api "failed" "143"' SIGTERM trap 'post_update_to_api "failed" "129"; exit 129' SIGHUP function error_handler() { local exit_code="$?" local line_number="$1" local command="$2" local error_message="${RD}[ERROR]${CL} in line ${RD}$line_number${CL}: exit code ${RD}$exit_code${CL}: while executing command ${YW}$command${CL}" post_update_to_api "failed" "${exit_code}" echo -e "\n$error_message\n" cleanup_vmid } # ============================================================================== # OS SELECTION FUNCTIONS # ============================================================================== function select_os() { if OS_CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "SELECT OS" --radiolist \ "Choose Operating System for Docker VM" 14 68 4 \ "debian13" "Debian 13 (Trixie) - Latest" ON \ "debian12" "Debian 12 (Bookworm) - Stable" OFF \ "ubuntu2404" "Ubuntu 24.04 LTS (Noble)" OFF \ "ubuntu2204" "Ubuntu 22.04 LTS (Jammy)" OFF \ 3>&1 1>&2 2>&3); then case $OS_CHOICE in debian13) OS_TYPE="debian" OS_VERSION="13" OS_CODENAME="trixie" OS_DISPLAY="Debian 13 (Trixie)" ;; debian12) OS_TYPE="debian" OS_VERSION="12" OS_CODENAME="bookworm" OS_DISPLAY="Debian 12 (Bookworm)" ;; ubuntu2404) OS_TYPE="ubuntu" OS_VERSION="24.04" OS_CODENAME="noble" OS_DISPLAY="Ubuntu 24.04 LTS" ;; ubuntu2204) OS_TYPE="ubuntu" OS_VERSION="22.04" OS_CODENAME="jammy" OS_DISPLAY="Ubuntu 22.04 LTS" ;; esac echo -e "${OS}${BOLD}${DGN}Operating System: ${BGN}${OS_DISPLAY}${CL}" else exit_script fi } function select_cloud_init() { if [ "$OS_TYPE" = "ubuntu" ]; then USE_CLOUD_INIT="yes" echo -e "${CLOUD:-${TAB}☁️${TAB}${CL}}${BOLD}${DGN}Cloud-Init: ${BGN}yes (Ubuntu requires Cloud-Init)${CL}" return fi if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "CLOUD-INIT" \ --yesno "Enable Cloud-Init for VM configuration?\n\nCloud-Init allows automatic configuration of:\n- User accounts and passwords\n- SSH keys\n- Network settings (DHCP/Static)\n- DNS configuration\n\nYou can also configure these settings later in Proxmox UI.\n\nNote: Debian without Cloud-Init will use nocloud image with console auto-login." 18 68); then USE_CLOUD_INIT="yes" echo -e "${CLOUD:-${TAB}☁️${TAB}${CL}}${BOLD}${DGN}Cloud-Init: ${BGN}yes${CL}" else USE_CLOUD_INIT="no" echo -e "${CLOUD:-${TAB}☁️${TAB}${CL}}${BOLD}${DGN}Cloud-Init: ${BGN}no${CL}" fi } function get_image_url() { local arch=$(dpkg --print-architecture) case $OS_TYPE in debian) if [ "$USE_CLOUD_INIT" = "yes" ]; then echo "https://cloud.debian.org/images/cloud/${OS_CODENAME}/latest/debian-${OS_VERSION}-generic-${arch}.qcow2" else echo "https://cloud.debian.org/images/cloud/${OS_CODENAME}/latest/debian-${OS_VERSION}-nocloud-${arch}.qcow2" fi ;; ubuntu) echo "https://cloud-images.ubuntu.com/${OS_CODENAME}/current/${OS_CODENAME}-server-cloudimg-${arch}.img" ;; esac } # ============================================================================== # SETTINGS FUNCTIONS # ============================================================================== function default_settings() { select_os select_cloud_init VMID=$(get_valid_nextid) FORMAT="" MACHINE=" -machine q35" DISK_CACHE="" DISK_SIZE="10G" HN="docker" CPU_TYPE=" -cpu host" CORE_COUNT="2" RAM_SIZE="4096" BRG="vmbr0" MAC="$GEN_MAC" VLAN="" MTU="" START_VM="yes" METHOD="default" echo -e "${CONTAINERID}${BOLD}${DGN}Virtual Machine ID: ${BGN}${VMID}${CL}" echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}Q35 (Modern)${CL}" echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}${DISK_SIZE}${CL}" echo -e "${DISKSIZE}${BOLD}${DGN}Disk Cache: ${BGN}None${CL}" echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}${HN}${CL}" echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}Host${CL}" echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}${CORE_COUNT}${CL}" echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}${RAM_SIZE}${CL}" echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}${BRG}${CL}" echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}${MAC}${CL}" echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}Default${CL}" echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}Default${CL}" echo -e "${GATEWAY}${BOLD}${DGN}Start VM when completed: ${BGN}yes${CL}" echo -e "${CREATING}${BOLD}${DGN}Creating a Docker VM using the above settings${CL}" } function advanced_settings() { select_os select_cloud_init # SSH Key selection for Cloud-Init VMs if [ "$USE_CLOUD_INIT" = "yes" ]; then configure_cloudinit_ssh_keys || true fi METHOD="advanced" [ -z "${VMID:-}" ] && VMID=$(get_valid_nextid) # VM ID while true; do if VMID=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Virtual Machine ID" 8 58 $VMID --title "VIRTUAL MACHINE ID" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z "$VMID" ]; then VMID=$(get_valid_nextid) fi if pct status "$VMID" &>/dev/null || qm status "$VMID" &>/dev/null; then echo -e "${CROSS}${RD} ID $VMID is already in use${CL}" sleep 2 continue fi echo -e "${CONTAINERID}${BOLD}${DGN}Virtual Machine ID: ${BGN}$VMID${CL}" break else exit_script fi done # Machine Type if MACH=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "MACHINE TYPE" --radiolist --cancel-button Exit-Script "Choose Type" 10 58 2 \ "q35" "Q35 (Modern, PCIe)" ON \ "i440fx" "i440fx (Legacy, PCI)" OFF \ 3>&1 1>&2 2>&3); then if [ $MACH = q35 ]; then echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}Q35 (Modern)${CL}" FORMAT="" MACHINE=" -machine q35" else echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}i440fx (Legacy)${CL}" FORMAT=",efitype=4m" MACHINE="" fi else exit_script fi # Disk Size if DISK_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Disk Size in GiB (e.g., 10, 20)" 8 58 "$DISK_SIZE" --title "DISK SIZE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then DISK_SIZE=$(echo "$DISK_SIZE" | tr -d ' ') if [[ "$DISK_SIZE" =~ ^[0-9]+$ ]]; then DISK_SIZE="${DISK_SIZE}G" echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}$DISK_SIZE${CL}" elif [[ "$DISK_SIZE" =~ ^[0-9]+G$ ]]; then echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}$DISK_SIZE${CL}" else echo -e "${DISKSIZE}${BOLD}${RD}Invalid Disk Size. Please use a number (e.g., 10 or 10G).${CL}" exit_script fi else exit_script fi # Disk Cache if DISK_CACHE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "DISK CACHE" --radiolist "Choose" --cancel-button Exit-Script 10 58 2 \ "0" "None (Default)" ON \ "1" "Write Through" OFF \ 3>&1 1>&2 2>&3); then if [ $DISK_CACHE = "1" ]; then echo -e "${DISKSIZE}${BOLD}${DGN}Disk Cache: ${BGN}Write Through${CL}" DISK_CACHE="cache=writethrough," else echo -e "${DISKSIZE}${BOLD}${DGN}Disk Cache: ${BGN}None${CL}" DISK_CACHE="" fi else exit_script fi # Hostname if VM_NAME=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Hostname" 8 58 docker --title "HOSTNAME" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z $VM_NAME ]; then HN="docker" else HN=$(echo "${VM_NAME,,}" | tr -cs 'a-z0-9-' '-' | sed 's/^-//;s/-$//') if [ "$HN" != "${VM_NAME,,}" ]; then whiptail --backtitle "Proxmox VE Helper Scripts" --title "HOSTNAME ADJUSTED" --msgbox "Invalid characters detected. Hostname has been adjusted to:\n\n $HN" 10 58 fi fi echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}" else exit_script fi # CPU Model if CPU_TYPE1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "CPU MODEL" --radiolist "Choose" --cancel-button Exit-Script 10 58 2 \ "1" "Host (Recommended)" ON \ "0" "KVM64" OFF \ 3>&1 1>&2 2>&3); then if [ $CPU_TYPE1 = "1" ]; then echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}Host${CL}" CPU_TYPE=" -cpu host" else echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}KVM64${CL}" CPU_TYPE="" fi else exit_script fi # CPU Cores while true; do if CORE_COUNT=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Allocate CPU Cores" 8 58 2 --title "CORE COUNT" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z "$CORE_COUNT" ]; then CORE_COUNT="2"; fi if [[ "$CORE_COUNT" =~ ^[1-9][0-9]*$ ]]; then echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}$CORE_COUNT${CL}" break fi whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "CPU Cores must be a positive integer (e.g., 2)." 8 58 else exit_script fi done # RAM Size while true; do if RAM_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Allocate RAM in MiB" 8 58 4096 --title "RAM" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z "$RAM_SIZE" ]; then RAM_SIZE="4096"; fi if [[ "$RAM_SIZE" =~ ^[1-9][0-9]*$ ]]; then echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}$RAM_SIZE${CL}" break fi whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "RAM Size must be a positive integer in MiB (e.g., 4096)." 8 58 else exit_script fi done # Bridge if BRG=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a Bridge" 8 58 vmbr0 --title "BRIDGE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z $BRG ]; then BRG="vmbr0" fi echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}" else exit_script fi # MAC Address while true; do if MAC1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a MAC Address" 8 58 $GEN_MAC --title "MAC ADDRESS" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z "$MAC1" ]; then MAC="$GEN_MAC" echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}$MAC${CL}" break fi if [[ "$MAC1" =~ ^([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}$ ]]; then MAC="$MAC1" echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}$MAC${CL}" break fi whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "Invalid MAC address format. Use XX:XX:XX:XX:XX:XX (e.g., AA:BB:CC:DD:EE:FF)." 8 58 else exit_script fi done # VLAN while true; do if VLAN1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a Vlan (leave blank for default)" 8 58 --title "VLAN" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z "$VLAN1" ]; then VLAN1="Default" VLAN="" echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}$VLAN1${CL}" break fi if [[ "$VLAN1" =~ ^[0-9]+$ ]] && [ "$VLAN1" -ge 1 ] && [ "$VLAN1" -le 4094 ]; then VLAN=",tag=$VLAN1" echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}$VLAN1${CL}" break fi whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "VLAN must be a number between 1 and 4094, or leave blank for default." 8 58 else exit_script fi done # MTU while true; do if MTU1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Interface MTU Size (leave blank for default)" 8 58 --title "MTU SIZE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z "$MTU1" ]; then MTU1="Default" MTU="" echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}$MTU1${CL}" break fi if [[ "$MTU1" =~ ^[0-9]+$ ]] && [ "$MTU1" -ge 576 ] && [ "$MTU1" -le 65520 ]; then MTU=",mtu=$MTU1" echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}$MTU1${CL}" break fi whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "MTU Size must be a number between 576 and 65520, or leave blank for default." 8 58 else exit_script fi done # Start VM if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "START VIRTUAL MACHINE" --yesno "Start VM when completed?" 10 58); then echo -e "${GATEWAY}${BOLD}${DGN}Start VM when completed: ${BGN}yes${CL}" START_VM="yes" else echo -e "${GATEWAY}${BOLD}${DGN}Start VM when completed: ${BGN}no${CL}" START_VM="no" fi # Confirm if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "ADVANCED SETTINGS COMPLETE" --yesno "Ready to create a Docker VM?" --no-button Do-Over 10 58); then echo -e "${CREATING}${BOLD}${DGN}Creating a Docker VM using the above advanced settings${CL}" else header_info echo -e "${ADVANCED}${BOLD}${RD}Using Advanced Settings${CL}" advanced_settings fi } function start_script() { if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "SETTINGS" --yesno "Use Default Settings?" --no-button Advanced 10 58); then header_info echo -e "${DEFAULT}${BOLD}${BL}Using Default Settings${CL}" default_settings else header_info echo -e "${ADVANCED}${BOLD}${RD}Using Advanced Settings${CL}" advanced_settings fi } # ============================================================================== # MAIN EXECUTION # ============================================================================== header_info check_root pve_check if whiptail --backtitle "Proxmox VE Helper Scripts" --title "Docker VM" --yesno "This will create a New Docker VM. Proceed?" 10 58; then : else header_info && echo -e "${CROSS}${RD}User exited script${CL}\n" && exit fi start_script post_to_api_vm # ============================================================================== # STORAGE SELECTION # ============================================================================== msg_info "Validating Storage" while read -r line; do TAG=$(echo $line | awk '{print $1}') TYPE=$(echo $line | awk '{printf "%-10s", $2}') FREE=$(echo $line | numfmt --field 4-6 --from-unit=K --to=iec --format %.2f | awk '{printf( "%9sB", $6)}') ITEM=" Type: $TYPE Free: $FREE " OFFSET=2 if [[ $((${#ITEM} + $OFFSET)) -gt ${MSG_MAX_LENGTH:-} ]]; then MSG_MAX_LENGTH=$((${#ITEM} + $OFFSET)) fi STORAGE_MENU+=("$TAG" "$ITEM" "OFF") done < <(pvesm status -content images | awk 'NR>1') VALID=$(pvesm status -content images | awk 'NR>1') if [ -z "$VALID" ]; then msg_error "Unable to detect a valid storage location." exit elif [ $((${#STORAGE_MENU[@]} / 3)) -eq 1 ]; then STORAGE=${STORAGE_MENU[0]} else if [ -n "$SPINNER_PID" ] && ps -p $SPINNER_PID >/dev/null; then kill $SPINNER_PID >/dev/null; fi printf "\e[?25h" while [ -z "${STORAGE:+x}" ]; do STORAGE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "Storage Pools" --radiolist \ "Which storage pool would you like to use for ${HN}?\nTo make a selection, use the Spacebar.\n" \ 16 $(($MSG_MAX_LENGTH + 23)) 6 \ "${STORAGE_MENU[@]}" 3>&1 1>&2 2>&3) done fi msg_ok "Using ${CL}${BL}$STORAGE${CL} ${GN}for Storage Location." msg_ok "Virtual Machine ID is ${CL}${BL}$VMID${CL}." # ============================================================================== # PREREQUISITES # ============================================================================== if ! command -v virt-customize &>/dev/null; then msg_info "Installing libguestfs-tools" apt-get -qq update >/dev/null apt-get -qq install libguestfs-tools lsb-release -y >/dev/null apt-get -qq install dhcpcd-base -y >/dev/null 2>&1 || true msg_ok "Installed libguestfs-tools" fi # ============================================================================== # IMAGE DOWNLOAD # ============================================================================== msg_info "Retrieving the URL for the ${OS_DISPLAY} Qcow2 Disk Image" URL=$(get_image_url) CACHE_DIR="/var/lib/vz/template/cache" CACHE_FILE="$CACHE_DIR/$(basename "$URL")" mkdir -p "$CACHE_DIR" msg_ok "${CL}${BL}${URL}${CL}" if [[ ! -s "$CACHE_FILE" ]]; then curl -f#SL -o "$CACHE_FILE" "$URL" echo -en "\e[1A\e[0K" msg_ok "Downloaded ${CL}${BL}$(basename "$CACHE_FILE")${CL}" else msg_ok "Using cached image ${CL}${BL}$(basename "$CACHE_FILE")${CL}" fi # ============================================================================== # STORAGE TYPE DETECTION # ============================================================================== STORAGE_TYPE=$(pvesm status -storage "$STORAGE" | awk 'NR>1 {print $2}') case $STORAGE_TYPE in nfs | dir) DISK_EXT=".qcow2" DISK_REF="$VMID/" DISK_IMPORT="--format qcow2" THIN="" ;; btrfs) DISK_EXT=".raw" DISK_REF="$VMID/" DISK_IMPORT="--format raw" FORMAT=",efitype=4m" THIN="" ;; *) DISK_EXT="" DISK_REF="" DISK_IMPORT="--format raw" ;; esac # ============================================================================== # IMAGE CUSTOMIZATION WITH DOCKER # ============================================================================== msg_info "Preparing ${OS_DISPLAY} image with Docker" WORK_FILE=$(mktemp --suffix=.qcow2) cp "$CACHE_FILE" "$WORK_FILE" export LIBGUESTFS_BACKEND_SETTINGS=dns=8.8.8.8,1.1.1.1 DOCKER_PREINSTALLED="no" # Install qemu-guest-agent and Docker during image customization msg_info "Installing base packages in image" if virt-customize -a "$WORK_FILE" --install qemu-guest-agent,curl,ca-certificates >/dev/null 2>&1; then msg_ok "Installed base packages" msg_info "Installing Docker (this may take 2-5 minutes)" if virt-customize -q -a "$WORK_FILE" --run-command "curl -fsSL https://get.docker.com | sh" >/dev/null 2>&1 && virt-customize -q -a "$WORK_FILE" --run-command "systemctl enable docker" >/dev/null 2>&1; then msg_ok "Installed Docker" msg_info "Configuring Docker daemon" # Optimize Docker daemon configuration virt-customize -q -a "$WORK_FILE" --run-command "mkdir -p /etc/docker" >/dev/null 2>&1 virt-customize -q -a "$WORK_FILE" --run-command 'cat > /etc/docker/daemon.json << EOF { "storage-driver": "overlay2", "log-driver": "json-file", "log-opts": { "max-size": "10m", "max-file": "3" } } EOF' >/dev/null 2>&1 DOCKER_PREINSTALLED="yes" msg_ok "Configured Docker daemon" else msg_ok "Docker will be installed on first boot" fi else msg_ok "Packages will be installed on first boot" fi msg_info "Finalizing image (hostname, SSH config)" # Set hostname and prepare for unique machine-id virt-customize -q -a "$WORK_FILE" --hostname "${HN}" >/dev/null 2>&1 || true virt-customize -q -a "$WORK_FILE" --run-command "truncate -s 0 /etc/machine-id" >/dev/null 2>&1 || true virt-customize -q -a "$WORK_FILE" --run-command "rm -f /var/lib/dbus/machine-id" >/dev/null 2>&1 || true # Configure SSH for Cloud-Init if [ "$USE_CLOUD_INIT" = "yes" ]; then virt-customize -q -a "$WORK_FILE" --run-command "sed -i 's/^#*PermitRootLogin.*/PermitRootLogin yes/' /etc/ssh/sshd_config" >/dev/null 2>&1 || true virt-customize -q -a "$WORK_FILE" --run-command "sed -i 's/^#*PasswordAuthentication.*/PasswordAuthentication yes/' /etc/ssh/sshd_config" >/dev/null 2>&1 || true else # Configure auto-login for nocloud images (no Cloud-Init) virt-customize -q -a "$WORK_FILE" --run-command "mkdir -p /etc/systemd/system/serial-getty@ttyS0.service.d" >/dev/null 2>&1 || true virt-customize -q -a "$WORK_FILE" --run-command 'cat > /etc/systemd/system/serial-getty@ttyS0.service.d/autologin.conf << EOF [Service] ExecStart= ExecStart=-/sbin/agetty --autologin root --noclear %I \$TERM EOF' >/dev/null 2>&1 || true virt-customize -q -a "$WORK_FILE" --run-command "mkdir -p /etc/systemd/system/getty@tty1.service.d" >/dev/null 2>&1 || true virt-customize -q -a "$WORK_FILE" --run-command 'cat > /etc/systemd/system/getty@tty1.service.d/autologin.conf << EOF [Service] ExecStart= ExecStart=-/sbin/agetty --autologin root --noclear %I \$TERM EOF' >/dev/null 2>&1 || true fi msg_ok "Finalized image" # Create first-boot Docker install script (fallback if virt-customize failed) if [ "$DOCKER_PREINSTALLED" = "no" ]; then if virt-customize -q -a "$WORK_FILE" --run-command 'cat > /root/install-docker.sh << "DOCKERSCRIPT" #!/bin/bash exec > /var/log/install-docker.log 2>&1 echo "[$(date)] Starting Docker installation" for i in {1..30}; do ping -c 1 8.8.8.8 >/dev/null 2>&1 && break sleep 2 done apt-get update apt-get install -y qemu-guest-agent curl ca-certificates curl -fsSL https://get.docker.com | sh systemctl enable docker systemctl start docker mkdir -p /etc/docker cat > /etc/docker/daemon.json << DAEMON { "storage-driver": "overlay2", "log-driver": "json-file", "log-opts": { "max-size": "10m", "max-file": "3" } } DAEMON systemctl restart docker touch /root/.docker-installed echo "[$(date)] Docker installation completed" DOCKERSCRIPT chmod +x /root/install-docker.sh' >/dev/null 2>&1; then virt-customize -q -a "$WORK_FILE" --run-command 'cat > /etc/systemd/system/install-docker.service << "DOCKERSERVICE" [Unit] Description=Install Docker on First Boot After=network-online.target Wants=network-online.target ConditionPathExists=!/root/.docker-installed [Service] Type=oneshot ExecStart=/root/install-docker.sh RemainAfterExit=yes [Install] WantedBy=multi-user.target DOCKERSERVICE systemctl enable install-docker.service' >/dev/null 2>&1 || true else msg_warn "virt-customize failed for this image. Docker must be installed manually after first boot:" msg_warn " curl -fsSL https://get.docker.com | sh" fi fi # Resize disk to target size msg_info "Resizing disk image to ${DISK_SIZE}" qemu-img resize "$WORK_FILE" "${DISK_SIZE}" >/dev/null 2>&1 msg_ok "Resized disk image" # ============================================================================== # VM CREATION # ============================================================================== msg_info "Creating Docker VM shell" qm create $VMID -agent 1${MACHINE} -tablet 0 -localtime 1 -bios ovmf${CPU_TYPE} -cores $CORE_COUNT -memory $RAM_SIZE \ -name $HN -tags community-script -net0 virtio,bridge=$BRG,macaddr=$MAC$VLAN$MTU -onboot 1 -ostype l26 -scsihw virtio-scsi-pci >/dev/null msg_ok "Created VM shell" # ============================================================================== # DISK IMPORT # ============================================================================== msg_info "Importing disk into storage ($STORAGE)" if qm disk import --help >/dev/null 2>&1; then IMPORT_CMD=(qm disk import) else IMPORT_CMD=(qm importdisk) fi IMPORT_OUT="$("${IMPORT_CMD[@]}" "$VMID" "$WORK_FILE" "$STORAGE" ${DISK_IMPORT:-} 2>&1 || true)" DISK_REF_IMPORTED="$(printf '%s\n' "$IMPORT_OUT" | sed -n "s/.*successfully imported disk '\([^']\+\)'.*/\1/p" | tr -d "\r\"'")" [[ -z "$DISK_REF_IMPORTED" ]] && DISK_REF_IMPORTED="$(pvesm list "$STORAGE" | awk -v id="$VMID" '$5 ~ ("vm-"id"-disk-") {print $1":"$5}' | sort | tail -n1)" [[ -z "$DISK_REF_IMPORTED" ]] && { msg_error "Unable to determine imported disk reference." echo "$IMPORT_OUT" exit 226 } msg_ok "Imported disk (${CL}${BL}${DISK_REF_IMPORTED}${CL})" # Clean up work file rm -f "$WORK_FILE" # ============================================================================== # VM CONFIGURATION # ============================================================================== msg_info "Attaching EFI and root disk" qm set "$VMID" \ --efidisk0 "${STORAGE}:0,efitype=4m" \ --scsi0 "${DISK_REF_IMPORTED},${DISK_CACHE}${THIN%,}" \ --boot order=scsi0 \ --serial0 socket >/dev/null qm set $VMID --agent enabled=1 >/dev/null msg_ok "Attached EFI and root disk" # Set VM description set_description # Cloud-Init configuration if [ "$USE_CLOUD_INIT" = "yes" ]; then msg_info "Configuring Cloud-Init" setup_cloud_init "$VMID" "$STORAGE" "$HN" "yes" msg_ok "Cloud-Init configured" fi # Start VM if [ "$START_VM" == "yes" ]; then msg_info "Starting Docker VM" qm start $VMID >/dev/null 2>&1 msg_ok "Started Docker VM" fi # ============================================================================== # FINAL OUTPUT # ============================================================================== VM_IP="" if [ "$START_VM" == "yes" ]; then set +e for i in {1..10}; do VM_IP=$(qm guest cmd "$VMID" network-get-interfaces 2>/dev/null | jq -r '.[] | select(.name != "lo") | ."ip-addresses"[]? | select(."ip-address-type" == "ipv4") | ."ip-address"' 2>/dev/null | grep -v "^127\." | head -1) || true [ -n "$VM_IP" ] && break sleep 3 done set -e fi echo -e "\n${INFO}${BOLD}${GN}Docker VM Configuration Summary:${CL}" echo -e "${TAB}${DGN}VM ID: ${BGN}${VMID}${CL}" echo -e "${TAB}${DGN}Hostname: ${BGN}${HN}${CL}" echo -e "${TAB}${DGN}OS: ${BGN}${OS_DISPLAY}${CL}" [ -n "$VM_IP" ] && echo -e "${TAB}${DGN}IP Address: ${BGN}${VM_IP}${CL}" if [ "$DOCKER_PREINSTALLED" = "yes" ]; then echo -e "${TAB}${DGN}Docker: ${BGN}Pre-installed (via get.docker.com)${CL}" else echo -e "${TAB}${DGN}Docker: ${BGN}Installing on first boot${CL}" echo -e "${TAB}${YW}⚠️ Wait 2-3 minutes for installation to complete${CL}" echo -e "${TAB}${YW}⚠️ Check progress: ${BL}cat /var/log/install-docker.log${CL}" fi if [ "$USE_CLOUD_INIT" = "yes" ]; then display_cloud_init_info "$VMID" "$HN" 2>/dev/null || true fi post_update_to_api "done" "none" msg_ok "Completed successfully!\n" ================================================ FILE: vm/haos-vm.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE source /dev/stdin <<<$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func) function header_info { clear cat <<"EOF" __ __ ___ _ __ __ ____ _____ / / / /___ ____ ___ ___ / | __________(_)____/ /_____ _____ / /_ / __ \/ ___/ / /_/ / __ \/ __ `__ \/ _ \ / /| | / ___/ ___/ / ___/ __/ __ `/ __ \/ __/ / / / /\__ \ / __ / /_/ / / / / / / __/ / ___ |(__ |__ ) (__ ) /_/ /_/ / / / / /_ / /_/ /___/ / /_/ /_/\____/_/ /_/ /_/\___/ /_/ |_/____/____/_/____/\__/\__,_/_/ /_/\__/ \____//____/ EOF } header_info echo -e "\n Loading..." GEN_MAC=02:$(openssl rand -hex 5 | awk '{print toupper($0)}' | sed 's/\(..\)/\1:/g; s/.$//') RANDOM_UUID="$(cat /proc/sys/kernel/random/uuid)" VERSIONS=(stable beta dev) METHOD="" NSAPP="haos-vm" var_os="homeassistant" DISK_SIZE="32G" CPU_TYPE="-cpu host" # arm64 guests require host CPU model # Get HAOS versions for generic-aarch64 (arm64) from the channel JSONs for version in "${VERSIONS[@]}"; do eval "$version=$(curl -fsSL https://raw.githubusercontent.com/home-assistant/version/master/${version}.json \ | sed -n 's/.*"generic-aarch64": *"\([^"]*\)".*/\1/p')" done YW=$(echo "\033[33m") BL=$(echo "\033[36m") HA=$(echo "\033[1;34m") RD=$(echo "\033[01;31m") BGN=$(echo "\033[4;92m") GN=$(echo "\033[1;92m") DGN=$(echo "\033[32m") CL=$(echo "\033[m") BOLD=$(echo "\033[1m") BFR="\\r\\033[K" HOLD=" " TAB=" " CM="${TAB}✔️${TAB}${CL}" CROSS="${TAB}✖️${TAB}${CL}" INFO="${TAB}💡${TAB}${CL}" OS="${TAB}🖥️${TAB}${CL}" CONTAINERTYPE="${TAB}📦${TAB}${CL}" DISKSIZE="${TAB}💾${TAB}${CL}" CPUCORE="${TAB}🧠${TAB}${CL}" RAMSIZE="${TAB}🛠️${TAB}${CL}" CONTAINERID="${TAB}🆔${TAB}${CL}" HOSTNAME="${TAB}🏠${TAB}${CL}" BRIDGE="${TAB}🌉${TAB}${CL}" GATEWAY="${TAB}🌐${TAB}${CL}" DEFAULT="${TAB}⚙️${TAB}${CL}" MACADDRESS="${TAB}🔗${TAB}${CL}" VLANTAG="${TAB}🏷️${TAB}${CL}" CREATING="${TAB}🚀${TAB}${CL}" ADVANCED="${TAB}🧩${TAB}${CL}" CLOUD="${TAB}☁️${TAB}${CL}" THIN="discard=on,ssd=1," set -e trap 'error_handler $LINENO "$BASH_COMMAND"' ERR trap cleanup EXIT trap 'post_update_to_api "failed" "130"' SIGINT trap 'post_update_to_api "failed" "143"' SIGTERM trap 'post_update_to_api "failed" "129"; exit 129' SIGHUP function error_handler() { local exit_code="$?" local line_number="$1" local command="$2" local error_message="${RD}[ERROR]${CL} in line ${RD}$line_number${CL}: exit code ${RD}$exit_code${CL}: while executing command ${YW}$command${CL}" post_update_to_api "failed" "${exit_code}" echo -e "\n$error_message\n" cleanup_vmid } function get_valid_nextid() { local try_id try_id=$(pvesh get /cluster/nextid) while true; do if [ -f "/etc/pve/qemu-server/${try_id}.conf" ] || [ -f "/etc/pve/lxc/${try_id}.conf" ]; then try_id=$((try_id + 1)) continue fi if lvs --noheadings -o lv_name | grep -qE "(^|[-_])${try_id}($|[-_])"; then try_id=$((try_id + 1)) continue fi break done echo "$try_id" } function cleanup_vmid() { if qm status $VMID &>/dev/null; then qm stop $VMID &>/dev/null qm destroy $VMID &>/dev/null fi } function cleanup() { local exit_code=$? popd >/dev/null # Only send telemetry if post_to_api_vm was called (installing status was sent) if [[ "${POST_TO_API_DONE:-}" == "true" && "${POST_UPDATE_DONE:-}" != "true" ]]; then if [[ $exit_code -eq 0 ]]; then post_update_to_api "done" "none" else post_update_to_api "failed" "$exit_code" fi fi rm -rf $TEMP_DIR } TEMP_DIR=$(mktemp -d) pushd $TEMP_DIR >/dev/null if whiptail --backtitle "Proxmox VE Helper Scripts" --title "Homeassistant OS VM" --yesno "This will create a New Homeassistant OS VM. Proceed?" 10 58; then : else header_info && echo -e "${CROSS}${RD}User exited script${CL}\n" && exit fi function msg_info() { local msg="$1" echo -ne "${TAB}${YW}${HOLD}${msg}${HOLD}" } function msg_ok() { local msg="$1" echo -e "${BFR}${CM}${GN}${msg}${CL}" } function msg_error() { local msg="$1" echo -e "${BFR}${CROSS}${RD}${msg}${CL}" } function check_root() { if [[ "$(id -u)" -ne 0 || $(ps -o comm= -p $PPID) == "sudo" ]]; then clear msg_error "Please run this script as root." echo -e "\nExiting..." sleep 2 exit fi } # This function checks the version of Proxmox Virtual Environment (PVE) and exits if the version is not supported. # Supported: Proxmox VE 8.0.x – 8.9.x, 9.0 and 9.1 pve_check() { local PVE_VER PVE_VER="$(pveversion | awk -F'/' '{print $2}' | awk -F'-' '{print $1}')" # Check for Proxmox VE 8.x: allow 8.0–8.9 if [[ "$PVE_VER" =~ ^8\.([0-9]+) ]]; then local MINOR="${BASH_REMATCH[1]}" if ((MINOR < 0 || MINOR > 9)); then msg_error "This version of Proxmox VE is not supported." msg_error "Supported: Proxmox VE version 8.0 – 8.9" exit 105 fi return 0 fi # Check for Proxmox VE 9.x: allow 9.0 and 9.1 if [[ "$PVE_VER" =~ ^9\.([0-9]+) ]]; then local MINOR="${BASH_REMATCH[1]}" if ((MINOR < 0 || MINOR > 1)); then msg_error "This version of Proxmox VE is not supported." msg_error "Supported: Proxmox VE version 9.0 – 9.1" exit 105 fi return 0 fi # All other unsupported versions msg_error "This version of Proxmox VE is not supported." msg_error "Supported versions: Proxmox VE 8.0 – 8.x or 9.0 – 9.1" exit 105 } function arch_check() { if [ "$(dpkg --print-architecture)" != "arm64" ]; then echo -e "\n ${INFO}${YWB}This script is for arm64! \n" echo -e "Exiting..." sleep 2 exit fi } function ssh_check() { if command -v pveversion >/dev/null 2>&1; then if [ -n "${SSH_CLIENT:+x}" ]; then if whiptail --backtitle "Proxmox VE Helper Scripts" --defaultno --title "SSH DETECTED" --yesno "It's suggested to use the Proxmox shell instead of SSH, since SSH can create issues while gathering variables. Would you like to proceed with using SSH?" 10 62; then echo "you've been warned" else clear exit fi fi fi } function exit-script() { clear echo -e "\n${CROSS}${RD}User exited script${CL}\n" exit } # Ensure pv is installed or abort with instructions function ensure_pv() { if ! command -v pv &>/dev/null; then msg_info "Installing required package: pv" if ! apt-get update -qq &>/dev/null || ! apt-get install -y pv &>/dev/null; then msg_error "Failed to install pv automatically." echo -e "\nPlease run manually on the Proxmox host:\n apt install pv\n" exit 237 fi msg_ok "Installed pv" fi } # Download an .xz file and validate it # Args: $1=url $2=cache_file function download_and_validate_xz() { local url="$1" local file="$2" # If file exists, check validity if [[ -s "$file" ]]; then if xz -t "$file" &>/dev/null; then msg_ok "Using cached image $(basename "$file")" return 0 else msg_error "Cached file $(basename "$file") is corrupted. Deleting and retrying download..." rm -f "$file" fi fi # Download fresh file msg_info "Downloading image: $(basename "$file")" if ! curl -fSL -o "$file" "$url"; then msg_error "Download failed: $url" rm -f "$file" exit 115 fi # Validate again if ! xz -t "$file" &>/dev/null; then msg_error "Downloaded file $(basename "$file") is corrupted. Please try again later." rm -f "$file" exit 115 fi msg_ok "Downloaded and validated $(basename "$file")" } # Extract .xz with pv # Args: $1=cache_file $2=target_img function extract_xz_with_pv() { set -o pipefail local file="$1" local target="$2" msg_info "Decompressing $(basename "$file") to $target" if ! xz -dc "$file" | pv -N "Extracting" >"$target"; then msg_error "Failed to extract $file" rm -f "$target" exit 115 fi msg_ok "Decompressed to $target" } function default_settings() { BRANCH="$stable" VMID=$(get_valid_nextid) FORMAT="" DISK_SIZE="32G" HN="haos-${BRANCH}" DISK_CACHE="cache=writethrough," CORE_COUNT="2" RAM_SIZE="4096" BRG="vmbr0" MAC="$GEN_MAC" VLAN="" MTU="" START_VM="yes" METHOD="default" echo -e "${CONTAINERID}${BOLD}${DGN}Virtual Machine ID: ${BGN}${VMID}${CL}" echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}${DISK_SIZE}${CL}" echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}${HN}${CL}" echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}Host${CL}" echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}${CORE_COUNT}${CL}" echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}${RAM_SIZE}${CL}" echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}${BRG}${CL}" echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}${MAC}${CL}" echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}Default${CL}" echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}Default${CL}" echo -e "${GATEWAY}${BOLD}${DGN}Start VM when completed: ${BGN}yes${CL}" echo -e "${CREATING}${BOLD}${DGN}Creating a Homeassistant OS VM using the above default settings${CL}" } function advanced_settings() { METHOD="advanced" if BRANCH=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "Homeassistant OS Version" --radiolist "Choose Version" --cancel-button Exit-Script 10 58 3 \ "$stable" "Stable " ON \ "$beta" "Beta " OFF \ "$dev" "Dev " OFF \ 3>&1 1>&2 2>&3); then var_version="${BRANCH}" echo -e "${DGN}Using HAOS Version: ${BGN}$BRANCH${CL}" else exit-script fi [ -z "${VMID:-}" ] && VMID=$(get_valid_nextid) while true; do if VMID=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Virtual Machine ID" 8 58 $VMID --title "VIRTUAL MACHINE ID" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z "$VMID" ]; then VMID=$(get_valid_nextid) fi if pct status "$VMID" &>/dev/null || qm status "$VMID" &>/dev/null; then echo -e "${CROSS}${RD} ID $VMID is already in use${CL}" sleep 2 continue fi echo -e "${CONTAINERID}${BOLD}${DGN}Virtual Machine ID: ${BGN}$VMID${CL}" break else exit-script fi done if DISK_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Disk Size in GiB (e.g., 10, 20)" 8 58 "$DISK_SIZE" --title "DISK SIZE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then DISK_SIZE=$(echo "$DISK_SIZE" | tr -d ' ') if [[ "$DISK_SIZE" =~ ^[0-9]+$ ]]; then DISK_SIZE="${DISK_SIZE}G" echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}$DISK_SIZE${CL}" elif [[ "$DISK_SIZE" =~ ^[0-9]+G$ ]]; then echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}$DISK_SIZE${CL}" else echo -e "${DISKSIZE}${BOLD}${RD}Invalid Disk Size. Please use a number (e.g., 10 or 10G).${CL}" exit-script fi else exit-script fi if DISK_CACHE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "DISK CACHE" --radiolist "Choose" --cancel-button Exit-Script 10 58 2 \ "0" "None" OFF \ "1" "Write Through (Default)" ON \ 3>&1 1>&2 2>&3); then if [ $DISK_CACHE = "1" ]; then echo -e "${DISKSIZE}${BOLD}${DGN}Disk Cache: ${BGN}Write Through${CL}" DISK_CACHE="cache=writethrough," else echo -e "${DISKSIZE}${BOLD}${DGN}Disk Cache: ${BGN}None${CL}" DISK_CACHE="" fi else exit-script fi if VM_NAME=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Hostname" 8 58 haos${BRANCH} --title "HOSTNAME" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z $VM_NAME ]; then HN="haos${BRANCH}" echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}" else HN=$(echo "${VM_NAME,,}" | tr -cs 'a-z0-9-' '-' | sed 's/^-//;s/-$//') if [ "$HN" != "${VM_NAME,,}" ]; then whiptail --backtitle "Proxmox VE Helper Scripts" --title "HOSTNAME ADJUSTED" --msgbox "Invalid characters detected. Hostname has been adjusted to:\n\n $HN" 10 58 fi echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}" fi else exit-script fi echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}Host${CL}" while true; do if CORE_COUNT=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Allocate CPU Cores" 8 58 2 --title "CORE COUNT" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z "$CORE_COUNT" ]; then CORE_COUNT="2"; fi if [[ "$CORE_COUNT" =~ ^[1-9][0-9]*$ ]]; then echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}$CORE_COUNT${CL}" break fi whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "CPU Cores must be a positive integer (e.g., 2)." 8 58 else exit-script fi done while true; do if RAM_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Allocate RAM in MiB" 8 58 2048 --title "RAM" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z "$RAM_SIZE" ]; then RAM_SIZE="4096"; fi if [[ "$RAM_SIZE" =~ ^[1-9][0-9]*$ ]]; then echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}$RAM_SIZE${CL}" break fi whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "RAM Size must be a positive integer in MiB (e.g., 4096)." 8 58 else exit-script fi done if BRG=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a Bridge" 8 58 vmbr0 --title "BRIDGE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z $BRG ]; then BRG="vmbr0" echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}" else echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}" fi else exit-script fi while true; do if MAC1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a MAC Address" 8 58 $GEN_MAC --title "MAC ADDRESS" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z "$MAC1" ]; then MAC="$GEN_MAC" echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}$MAC${CL}" break fi if [[ "$MAC1" =~ ^([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}$ ]]; then MAC="$MAC1" echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}$MAC1${CL}" break fi whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "Invalid MAC address format. Use XX:XX:XX:XX:XX:XX (e.g., AA:BB:CC:DD:EE:FF)." 8 58 else exit-script fi done while true; do if VLAN1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a Vlan(leave blank for default)" 8 58 --title "VLAN" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z "$VLAN1" ]; then VLAN1="Default" VLAN="" echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}$VLAN1${CL}" break fi if [[ "$VLAN1" =~ ^[0-9]+$ ]] && [ "$VLAN1" -ge 1 ] && [ "$VLAN1" -le 4094 ]; then VLAN=",tag=$VLAN1" echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}$VLAN1${CL}" break fi whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "VLAN must be a number between 1 and 4094, or leave blank for default." 8 58 else exit-script fi done while true; do if MTU1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Interface MTU Size (leave blank for default)" 8 58 --title "MTU SIZE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z "$MTU1" ]; then MTU1="Default" MTU="" echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}$MTU1${CL}" break fi if [[ "$MTU1" =~ ^[0-9]+$ ]] && [ "$MTU1" -ge 576 ] && [ "$MTU1" -le 65520 ]; then MTU=",mtu=$MTU1" echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}$MTU1${CL}" break fi whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "MTU Size must be a number between 576 and 65520, or leave blank for default." 8 58 else exit-script fi done if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "START VIRTUAL MACHINE" --yesno "Start VM when completed?" 10 58); then echo -e "${DGN}Start VM when completed: ${BGN}yes${CL}" START_VM="yes" else echo -e "${DGN}Start VM when completed: ${BGN}no${CL}" START_VM="no" fi if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "ADVANCED SETTINGS COMPLETE" --yesno "Ready to create Homeassistant OS ${BRANCH} VM?" --no-button Do-Over 10 58); then echo -e "${RD}Creating a Homeassistant OS VM using the above advanced settings${CL}" else header_info echo -e "${RD}Using Advanced Settings${CL}" advanced_settings fi } function start_script() { if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "SETTINGS" --yesno "Use Default Settings?" --no-button Advanced 10 58); then header_info echo -e "${BL}Using Default Settings${CL}" default_settings else header_info echo -e "${RD}Using Advanced Settings${CL}" advanced_settings fi } check_root arch_check pve_check ssh_check ensure_pv start_script post_to_api_vm msg_info "Validating Storage" while read -r line; do TAG=$(echo $line | awk '{print $1}') TYPE=$(echo $line | awk '{printf "%-10s", $2}') FREE=$(echo $line | numfmt --field 4-6 --from-unit=K --to=iec --format %.2f | awk '{printf( "%9sB", $6)}') ITEM=" Type: $TYPE Free: $FREE " OFFSET=2 if [[ $((${#ITEM} + $OFFSET)) -gt ${MSG_MAX_LENGTH:-} ]]; then MSG_MAX_LENGTH=$((${#ITEM} + $OFFSET)) fi STORAGE_MENU+=("$TAG" "$ITEM" "OFF") done < <(pvesm status -content images | awk 'NR>1') VALID=$(pvesm status -content images | awk 'NR>1') if [ -z "$VALID" ]; then msg_error "Unable to detect a valid storage location." exit elif [ $((${#STORAGE_MENU[@]} / 3)) -eq 1 ]; then STORAGE=${STORAGE_MENU[0]} else while [ -z "${STORAGE:+x}" ]; do if [ -n "$SPINNER_PID" ] && ps -p $SPINNER_PID >/dev/null; then kill $SPINNER_PID >/dev/null; fi printf "\e[?25h" STORAGE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "Storage Pools" --radiolist \ "Which storage pool would you like to use for ${HN}?\nTo make a selection, use the Spacebar.\n" \ 16 $(($MSG_MAX_LENGTH + 23)) 6 \ "${STORAGE_MENU[@]}" 3>&1 1>&2 2>&3) done fi msg_ok "Using ${CL}${BL}$STORAGE${CL} ${GN}for Storage Location." msg_ok "Virtual Machine ID is ${CL}${BL}$VMID${CL}." var_version="${BRANCH}" msg_info "Retrieving the URL for Home Assistant ${BRANCH} Disk Image (generic-aarch64)" # Use generic-aarch64 (arm64), instead of ova (x86_64) if [ "$BRANCH" == "$dev" ]; then URL="https://os-artifacts.home-assistant.io/${BRANCH}/haos_generic-aarch64-${BRANCH}.qcow2.xz" else URL="https://github.com/home-assistant/operating-system/releases/download/${BRANCH}/haos_generic-aarch64-${BRANCH}.qcow2.xz" fi CACHE_DIR="/var/lib/vz/template/cache" CACHE_FILE="$CACHE_DIR/$(basename "$URL")" FILE_IMG="/var/lib/vz/template/tmp/$(basename "${CACHE_FILE%.xz}")" mkdir -p "$CACHE_DIR" "$(dirname "$FILE_IMG")" msg_ok "${CL}${BL}${URL}${CL}" download_and_validate_xz "$URL" "$CACHE_FILE" msg_info "Creating Home Assistant OS VM shell" qm create "$VMID" -bios ovmf -agent 1 -tablet 0 -localtime 1 ${CPU_TYPE} \ -cores "$CORE_COUNT" -memory "$RAM_SIZE" -name "$HN" -tags community-script \ -net0 "virtio,bridge=$BRG,macaddr=$MAC$VLAN$MTU" -onboot 1 -ostype l26 -scsihw virtio-scsi-pci >/dev/null msg_ok "Created VM shell" extract_xz_with_pv "$CACHE_FILE" "$FILE_IMG" msg_info "Importing disk into storage ($STORAGE)" if qm disk import --help >/dev/null 2>&1; then IMPORT_CMD=(qm disk import) else IMPORT_CMD=(qm importdisk) fi msg_info "Creating EFI disk first (must be disk-0)" qm set "$VMID" --efidisk0 "${STORAGE}:0,efitype=4m,size=64M" >/dev/null msg_info "Importing HAOS disk (will become disk-1)" IMPORT_OUT="$("${IMPORT_CMD[@]}" "$VMID" "$FILE_IMG" "$STORAGE" --format raw 2>&1 || true)" DISK_REF="$(printf '%s\n' "$IMPORT_OUT" | sed -n "s/.*successfully imported disk '\([^']\+\)'.*/\1/p")" [[ -z "$DISK_REF" ]] && DISK_REF="$(pvesm list "$STORAGE" | awk -v id="$VMID" '$5 ~ ("vm-"id"-disk-") {print $1":"$5}' | sort | tail -n1)" [[ -z "$DISK_REF" ]] && { msg_error "Unable to determine imported disk reference." echo "$IMPORT_OUT" exit 226 } msg_ok "Imported disk (${CL}${BL}${DISK_REF}${CL})" msg_info "Attaching HAOS disk as scsi0" qm set "$VMID" --scsi0 "${DISK_REF},${DISK_CACHE}ssd=1,discard=on" >/dev/null msg_info "Setting boot order" qm set "$VMID" --boot order=scsi0 --serial0 socket >/dev/null rm -f "$FILE_IMG" qm set "$VMID" --agent enabled=1 >/dev/null msg_ok "Attached EFI and root disk" msg_info "Resizing disk to $DISK_SIZE" qm resize $VMID scsi0 ${DISK_SIZE} >/dev/null msg_ok "Resized disk" DESCRIPTION=$( cat < Logo

Homeassistant OS VM

spend Coffee

GitHub Discussions Issues EOF ) qm set $VMID -description "$DESCRIPTION" >/dev/null msg_ok "Created Homeassistant OS VM ${CL}${BL}(${HN})" if whiptail --backtitle "Proxmox VE Helper Scripts" --title "Image Cache" \ --yesno "Keep downloaded Home Assistant OS image for future VMs?\n\nFile: $CACHE_FILE" 10 70; then msg_ok "Keeping cached image" else rm -f "$CACHE_FILE" msg_ok "Deleted cached image" fi if [ "$START_VM" == "yes" ]; then msg_info "Starting Home Assistant OS VM" qm start $VMID msg_ok "Started Home Assistant OS VM" fi post_update_to_api "done" "none" msg_ok "Completed successfully!\n" ================================================ FILE: vm/headers/docker-vm ================================================ ____ __ / __ \____ _____/ /_____ _____ / / / / __ \/ ___/ //_/ _ \/ ___/ / /_/ / /_/ / /__/ ,< / __/ / /_____/\____/\___/_/|_|\___/_/ ================================================ FILE: vm/headers/owncloud-vm ================================================ ______ __ __ ________ __ _ ____ ___ /_ __/_ ___________ / //_/__ __ __ ____ _ ______ / ____/ /___ __ ______/ / | | / / |/ / / / / / / / ___/ __ \/ ,< / _ \/ / / / / __ \ | /| / / __ \/ / / / __ \/ / / / __ / | | / / /|_/ / / / / /_/ / / / / / / /| / __/ /_/ / / /_/ / |/ |/ / / / / /___/ / /_/ / /_/ / /_/ / | |/ / / / / /_/ \__,_/_/ /_/ /_/_/ |_\___/\__, / \____/|__/|__/_/ /_/\____/_/\____/\__,_/\__,_/ |___/_/ /_/ /____/ ================================================ FILE: vm/mikrotik-routeros.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT # https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE source /dev/stdin <<<$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func) function header_info { clear cat <<"EOF" __ ____ __ __ _ __ ____ __ ____ _____ ________ ______ / |/ (_) /___________ / /_(_) /__ / __ \____ __ __/ /____ _____/ __ \/ ___/ / ____/ / / / __ \ / /|_/ / / //_/ ___/ __ \/ __/ / //_/ / /_/ / __ \/ / / / __/ _ \/ ___/ / / /\__ \ / / / /_/ / /_/ / / / / / / ,< / / / /_/ / /_/ / ,< / _, _/ /_/ / /_/ / /_/ __/ / / /_/ /___/ / / /___/ __ / _, _/ /_/ /_/_/_/|_/_/ \____/\__/_/_/|_| /_/ |_|\____/\__,_/\__/\___/_/ \____//____/ \____/_/ /_/_/ |_| EOF } header_info echo -e "Loading..." GEN_MAC=$(echo '00 60 2f'$(od -An -N3 -t xC /dev/urandom) | sed -e 's/ /:/g' | tr '[:lower:]' '[:upper:]') RANDOM_UUID="$(cat /proc/sys/kernel/random/uuid)" METHOD="" NSAPP="mikrotik-routeros" var_os="mikrotik" var_version=" " DISK_SIZE="1G" YW=$(echo "\033[33m") BL=$(echo "\033[36m") RD=$(echo "\033[01;31m") BGN=$(echo "\033[4;92m") GN=$(echo "\033[1;92m") DGN=$(echo "\033[32m") CL=$(echo "\033[m") CL=$(echo "\033[m") BOLD=$(echo "\033[1m") BFR="\\r\\033[K" HOLD=" " TAB=" " CM="${TAB}✔️${TAB}${CL}" CROSS="${TAB}✖️${TAB}${CL}" INFO="${TAB}💡${TAB}${CL}" OS="${TAB}🖥️${TAB}${CL}" CONTAINERTYPE="${TAB}📦${TAB}${CL}" DISKSIZE="${TAB}💾${TAB}${CL}" CPUCORE="${TAB}🧠${TAB}${CL}" RAMSIZE="${TAB}🛠️${TAB}${CL}" CONTAINERID="${TAB}🆔${TAB}${CL}" HOSTNAME="${TAB}🏠${TAB}${CL}" BRIDGE="${TAB}🌉${TAB}${CL}" GATEWAY="${TAB}🌐${TAB}${CL}" DEFAULT="${TAB}⚙️${TAB}${CL}" MACADDRESS="${TAB}🔗${TAB}${CL}" VLANTAG="${TAB}🏷️${TAB}${CL}" CREATING="${TAB}🚀${TAB}${CL}" ADVANCED="${TAB}🧩${TAB}${CL}" CLOUD="${TAB}☁️${TAB}${CL}" THIN="discard=on,ssd=1," set -e trap 'error_handler $LINENO "$BASH_COMMAND"' ERR trap cleanup EXIT trap 'post_update_to_api "failed" "130"' SIGINT trap 'post_update_to_api "failed" "143"' SIGTERM trap 'post_update_to_api "failed" "129"; exit 129' SIGHUP function error_handler() { local exit_code="$?" local line_number="$1" local command="$2" local error_message="${RD}[ERROR]${CL} in line ${RD}$line_number${CL}: exit code ${RD}$exit_code${CL}: while executing command ${YW}$command${CL}" post_update_to_api "failed" "${exit_code}" echo -e "\n$error_message\n" cleanup_vmid } function get_valid_nextid() { local try_id try_id=$(pvesh get /cluster/nextid) while true; do if [ -f "/etc/pve/qemu-server/${try_id}.conf" ] || [ -f "/etc/pve/lxc/${try_id}.conf" ]; then try_id=$((try_id + 1)) continue fi if lvs --noheadings -o lv_name | grep -qE "(^|[-_])${try_id}($|[-_])"; then try_id=$((try_id + 1)) continue fi break done echo "$try_id" } function cleanup_vmid() { if qm status $VMID &>/dev/null; then qm stop $VMID &>/dev/null qm destroy $VMID &>/dev/null fi } function cleanup() { local exit_code=$? popd >/dev/null if [[ "${POST_TO_API_DONE:-}" == "true" && "${POST_UPDATE_DONE:-}" != "true" ]]; then if [[ $exit_code -eq 0 ]]; then post_update_to_api "done" "none" else post_update_to_api "failed" "$exit_code" fi fi rm -rf $TEMP_DIR } TEMP_DIR=$(mktemp -d) pushd $TEMP_DIR >/dev/null if whiptail --backtitle "Proxmox VE Helper Scripts" --title "Mikrotik RouterOS CHR VM" --yesno "This will create a Mikrotik RouterOS CHR VM. Proceed?" 10 58; then : else header_info && echo -e "${CROSS}${RD}User exited script${CL}\n" && exit fi function msg_info() { local msg="$1" echo -ne "${TAB}${YW}${HOLD}${msg}${HOLD}" } function msg_ok() { local msg="$1" echo -e "${BFR}${CM}${GN}${msg}${CL}" } function msg_error() { local msg="$1" echo -e "${BFR}${CROSS}${RD}${msg}${CL}" } function check_root() { if [[ "$(id -u)" -ne 0 || $(ps -o comm= -p $PPID) == "sudo" ]]; then clear msg_error "Please run this script as root." echo -e "\nExiting..." sleep 2 exit fi } # This function checks the version of Proxmox Virtual Environment (PVE) and exits if the version is not supported. # Supported: Proxmox VE 8.0.x – 8.9.x, 9.0 and 9.1 pve_check() { local PVE_VER PVE_VER="$(pveversion | awk -F'/' '{print $2}' | awk -F'-' '{print $1}')" # Check for Proxmox VE 8.x: allow 8.0–8.9 if [[ "$PVE_VER" =~ ^8\.([0-9]+) ]]; then local MINOR="${BASH_REMATCH[1]}" if ((MINOR < 0 || MINOR > 9)); then msg_error "This version of Proxmox VE is not supported." msg_error "Supported: Proxmox VE version 8.0 – 8.9" exit 105 fi return 0 fi # Check for Proxmox VE 9.x: allow 9.0 and 9.1 if [[ "$PVE_VER" =~ ^9\.([0-9]+) ]]; then local MINOR="${BASH_REMATCH[1]}" if ((MINOR < 0 || MINOR > 1)); then msg_error "This version of Proxmox VE is not supported." msg_error "Supported: Proxmox VE version 9.0 – 9.1" exit 105 fi return 0 fi # All other unsupported versions msg_error "This version of Proxmox VE is not supported." msg_error "Supported versions: Proxmox VE 8.0 – 8.x or 9.0 – 9.1" exit 105 } function arch_check() { if [ "$(dpkg --print-architecture)" != "amd64" ]; then echo -e "\n ${INFO}${YWB}This script will not work with PiMox! \n" echo -e "\n ${YWB}Visit https://github.com/asylumexp/Proxmox for ARM64 support. \n" echo -e "Exiting..." sleep 2 exit fi } function ssh_check() { if command -v pveversion >/dev/null 2>&1; then if [ -n "${SSH_CLIENT:+x}" ]; then if whiptail --backtitle "Proxmox VE Helper Scripts" --defaultno --title "SSH DETECTED" --yesno "It's suggested to use the Proxmox shell instead of SSH, since SSH can create issues while gathering variables. Would you like to proceed with using SSH?" 10 62; then echo "you've been warned" else clear exit fi fi fi } function exit-script() { clear echo -e "\n${CROSS}${RD}User exited script${CL}\n" exit } function default_settings() { VMID=$(get_valid_nextid) FORMAT=",efitype=4m" MACHINE="" DISK_SIZE="8G" DISK_CACHE="" HN="mikrotik-routeros-chr" CPU_TYPE="" CORE_COUNT="2" RAM_SIZE="512" BRG="vmbr0" MAC="$GEN_MAC" VLAN="" MTU="" START_VM="yes" CLOUD_INIT="no" METHOD="default" echo -e "${CONTAINERID}${BOLD}${DGN}Virtual Machine ID: ${BGN}${VMID}${CL}" echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}i440fx${CL}" echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}${DISK_SIZE}${CL}" echo -e "${DISKSIZE}${BOLD}${DGN}Disk Cache: ${BGN}None${CL}" echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}${HN}${CL}" echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}KVM64${CL}" echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}${CORE_COUNT}${CL}" echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}${RAM_SIZE}${CL}" echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}${BRG}${CL}" echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}${MAC}${CL}" echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}Default${CL}" echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}Default${CL}" echo -e "${GATEWAY}${BOLD}${DGN}Start VM when completed: ${BGN}yes${CL}" echo -e "${CREATING}${BOLD}${DGN}Creating a Mikrotik RouterOS VM using the above default settings${CL}" } function get_mikrotik_version() { local mode="$1" local rss_url local tree_name case "$mode" in s) rss_url="https://cdn.mikrotik.com/routeros/latest-stable.rss" ;; d) rss_url="https://cdn.mikrotik.com/routeros/latest-development.rss" ;; l) rss_url="https://cdn.mikrotik.com/routeros/latest-long-term.rss" ;; t) rss_url="https://cdn.mikrotik.com/routeros/latest-testing.rss" ;; *) return 0 ;; esac local rss_content rss_content=$(curl -fsSL $rss_url 2>/dev/null) if [ -n "$rss_content" ]; then local version version=$(echo "$rss_content" | grep -oP 'RouterOS \K[0-9.]+(?= \[)' 2>/dev/null || echo "$rss_content" | sed -n 's/.*<title>RouterOS \([0-9.]\+\) \[.*/\1/p' 2>/dev/null) if [[ "$version" =~ ^[0-9]+\.[0-9]+ ]]; then echo "$version" return 0 fi fi case "$mode" in s) tree_name="Stable release tree" ;; d) tree_name="Development release tree" ;; l) tree_name="Long-term release tree" ;; t) tree_name="Testing release tree" ;; esac local html html=$(curl -fsSL "https://mikrotik.com/download/changelogs" 2>/dev/null) if [ -n "$html" ]; then local start_line start_line=$(echo "$html" | grep -n "$tree_name" | cut -d: -f1 | head -n1) if [[ "$start_line" =~ ^[0-9]+$ ]]; then local line line=$(echo "$html" | tail -n +"$start_line" | grep -m 1 -E "c-(stable|longTerm|testing|development)-v|RouterOS [0-9]+\.[0-9]+" 2>/dev/null) local version version=$(echo "$line" | sed -n 's/.*c-[^"]*-v\([0-9_.a-zA-Z-]\+\).*/\1/p' | tr '_' '.' 2>/dev/null) [ -z "$version" ] && version=$(echo "$line" | grep -oP 'RouterOS \K[0-9]+\.[0-9]+(\.[0-9]+)?' 2>/dev/null) if [[ "$version" =~ ^[0-9]+\.[0-9]+ ]]; then echo "$version" return 0 fi fi fi for minor in $(seq 50 -1 15); do local test_version="7.${minor}" if curl -fsSL -I "https://download.mikrotik.com/routeros/${test_version}/chr-${test_version}.img.zip" 2>/dev/null | grep -q "200 OK"; then echo "$test_version" return 0 fi done return 0 } function advanced_settings() { METHOD="advanced" [ -z "${VMID:-}" ] && VMID=$(get_valid_nextid) while true; do if VMID=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Virtual Machine ID" 8 58 $VMID --title "VIRTUAL MACHINE ID" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z "$VMID" ]; then VMID=$(get_valid_nextid) fi if pct status "$VMID" &>/dev/null || qm status "$VMID" &>/dev/null; then echo -e "${CROSS}${RD} ID $VMID is already in use${CL}" sleep 2 continue fi echo -e "${CONTAINERID}${BOLD}${DGN}Virtual Machine ID: ${BGN}$VMID${CL}" break else exit-script fi done if MACH=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "MACHINE TYPE" --radiolist --cancel-button Exit-Script "Choose Type" 10 58 2 \ "i440fx" "Machine i440fx" ON \ "q35" "Machine q35" OFF \ 3>&1 1>&2 2>&3); then if [ $MACH = q35 ]; then echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}$MACH${CL}" FORMAT="" MACHINE=" -machine q35" else echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}$MACH${CL}" FORMAT=",efitype=4m" MACHINE="" fi else exit-script fi if DISK_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Disk Size in GiB (e.g., 10, 20)" 8 58 "$DISK_SIZE" --title "DISK SIZE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then DISK_SIZE=$(echo "$DISK_SIZE" | tr -d ' ') if [[ "$DISK_SIZE" =~ ^[0-9]+$ ]]; then DISK_SIZE="${DISK_SIZE}G" echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}$DISK_SIZE${CL}" elif [[ "$DISK_SIZE" =~ ^[0-9]+G$ ]]; then echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}$DISK_SIZE${CL}" else echo -e "${DISKSIZE}${BOLD}${RD}Invalid Disk Size. Please use a number (e.g., 10 or 10G).${CL}" exit-script fi else exit-script fi if DISK_CACHE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "DISK CACHE" --radiolist "Choose" --cancel-button Exit-Script 10 58 2 \ "0" "None (Default)" ON \ "1" "Write Through" OFF \ 3>&1 1>&2 2>&3); then if [ $DISK_CACHE = "1" ]; then echo -e "${DISKSIZE}${BOLD}${DGN}Disk Cache: ${BGN}Write Through${CL}" DISK_CACHE="cache=writethrough," else echo -e "${DISKSIZE}${BOLD}${DGN}Disk Cache: ${BGN}None${CL}" DISK_CACHE="" fi else exit-script fi if VM_NAME=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Hostname" 8 58 mikrotik-routeros-chr --title "HOSTNAME" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z $VM_NAME ]; then HN="mikrotik-routeros-chr" echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}" else HN=$(echo "${VM_NAME,,}" | tr -cs 'a-z0-9-' '-' | sed 's/^-//;s/-$//') if [ "$HN" != "${VM_NAME,,}" ]; then whiptail --backtitle "Proxmox VE Helper Scripts" --title "HOSTNAME ADJUSTED" --msgbox "Invalid characters detected. Hostname has been adjusted to:\n\n $HN" 10 58 fi echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}" fi else exit-script fi if CPU_TYPE1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "CPU MODEL" --radiolist "Choose" --cancel-button Exit-Script 10 58 2 \ "0" "KVM64 (Default)" ON \ "1" "Host" OFF \ 3>&1 1>&2 2>&3); then if [ $CPU_TYPE1 = "1" ]; then echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}Host${CL}" CPU_TYPE=" -cpu host" else echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}KVM64${CL}" CPU_TYPE="" fi else exit-script fi while true; do if CORE_COUNT=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Allocate CPU Cores" 8 58 2 --title "CORE COUNT" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z "$CORE_COUNT" ]; then CORE_COUNT="2"; fi if [[ "$CORE_COUNT" =~ ^[1-9][0-9]*$ ]]; then echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}$CORE_COUNT${CL}" break fi whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "CPU Cores must be a positive integer (e.g., 2)." 8 58 else exit-script fi done while true; do if RAM_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Allocate RAM in MiB" 8 58 2048 --title "RAM" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z "$RAM_SIZE" ]; then RAM_SIZE="2048"; fi if [[ "$RAM_SIZE" =~ ^[1-9][0-9]*$ ]]; then echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}$RAM_SIZE${CL}" break fi whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "RAM Size must be a positive integer in MiB (e.g., 2048)." 8 58 else exit-script fi done if BRG=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a Bridge" 8 58 vmbr0 --title "BRIDGE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z $BRG ]; then BRG="vmbr0" echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}" else echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}" fi else exit-script fi while true; do if MAC1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a MAC Address" 8 58 $GEN_MAC --title "MAC ADDRESS" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z "$MAC1" ]; then MAC="$GEN_MAC" echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}$MAC${CL}" break fi if [[ "$MAC1" =~ ^([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}$ ]]; then MAC="$MAC1" echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}$MAC1${CL}" break fi whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "Invalid MAC address format. Use XX:XX:XX:XX:XX:XX (e.g., AA:BB:CC:DD:EE:FF)." 8 58 else exit-script fi done while true; do if VLAN1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a Vlan(leave blank for default)" 8 58 --title "VLAN" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z "$VLAN1" ]; then VLAN1="Default" VLAN="" echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}$VLAN1${CL}" break fi if [[ "$VLAN1" =~ ^[0-9]+$ ]] && [ "$VLAN1" -ge 1 ] && [ "$VLAN1" -le 4094 ]; then VLAN=",tag=$VLAN1" echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}$VLAN1${CL}" break fi whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "VLAN must be a number between 1 and 4094, or leave blank for default." 8 58 else exit-script fi done while true; do if MTU1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Interface MTU Size (leave blank for default)" 8 58 --title "MTU SIZE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z "$MTU1" ]; then MTU1="Default" MTU="" echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}$MTU1${CL}" break fi if [[ "$MTU1" =~ ^[0-9]+$ ]] && [ "$MTU1" -ge 576 ] && [ "$MTU1" -le 65520 ]; then MTU=",mtu=$MTU1" echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}$MTU1${CL}" break fi whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "MTU Size must be a number between 576 and 65520, or leave blank for default." 8 58 else exit-script fi done if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "START VIRTUAL MACHINE" --yesno "Start VM when completed?" 10 58); then echo -e "${GATEWAY}${BOLD}${DGN}Start VM when completed: ${BGN}yes${CL}" START_VM="yes" else echo -e "${GATEWAY}${BOLD}${DGN}Start VM when completed: ${BGN}no${CL}" START_VM="no" fi if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "ADVANCED SETTINGS COMPLETE" --yesno "Ready to create a Mikrotik RouterOS CHR VM?" --no-button Do-Over 10 58); then echo -e "${CREATING}${BOLD}${DGN}Creating a Mikrotik RouterOS CHR VM using the above advanced settings${CL}" else header_info echo -e "${ADVANCED}${BOLD}${RD}Using Advanced Settings${CL}" advanced_settings fi } function start_script() { if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "SETTINGS" --yesno "Use Default Settings?" --no-button Advanced 10 58); then header_info echo -e "${DEFAULT}${BOLD}${BL}Using Default Settings${CL}" default_settings else header_info echo -e "${ADVANCED}${BOLD}${RD}Using Advanced Settings${CL}" advanced_settings fi } check_root arch_check pve_check ssh_check start_script post_to_api_vm msg_info "Validating Storage" while read -r line; do TAG=$(echo $line | awk '{print $1}') TYPE=$(echo $line | awk '{printf "%-10s", $2}') FREE=$(echo $line | numfmt --field 4-6 --from-unit=K --to=iec --format %.2f | awk '{printf( "%9sB", $6)}') ITEM=" Type: $TYPE Free: $FREE " OFFSET=2 if [[ $((${#ITEM} + $OFFSET)) -gt ${MSG_MAX_LENGTH:-} ]]; then MSG_MAX_LENGTH=$((${#ITEM} + $OFFSET)) fi STORAGE_MENU+=("$TAG" "$ITEM" "OFF") done < <(pvesm status -content images | awk 'NR>1') VALID=$(pvesm status -content images | awk 'NR>1') if [ -z "$VALID" ]; then echo -e "\n${RD}⚠ Unable to detect a valid storage location.${CL}" echo -e "Exiting..." exit elif [ $((${#STORAGE_MENU[@]} / 3)) -eq 1 ]; then STORAGE=${STORAGE_MENU[0]} else while [ -z "${STORAGE:+x}" ]; do STORAGE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "Storage Pools" --radiolist \ "Which storage pool would you like to use for the Mikrotik RouterOS CHR VM?\n\n" \ 16 $(($MSG_MAX_LENGTH + 23)) 6 \ "${STORAGE_MENU[@]}" 3>&1 1>&2 2>&3) done fi msg_ok "Using ${CL}${BL}$STORAGE${CL} ${GN}for Storage Location." msg_ok "Virtual Machine ID is ${CL}${BL}$VMID${CL}." msg_info "Getting URL for Latest Mikrotik RouterOS CHR Disk Image" MIK_VER=$(get_mikrotik_version s) if [ -n "$MIK_VER" ]; then msg_ok "Latest stable version: ${CL}${BL}$MIK_VER${CL}." else msg_error "Could not get latest version" msg_ok "Defaulting to version 7.20" MIK_VER="7.20" fi URL=https://download.mikrotik.com/routeros/$MIK_VER/chr-$MIK_VER.img.zip sleep 2 msg_ok "Downloading from URL: ${CL}${BL}${URL}${CL}" curl -f#SL -o "$(basename "$URL")" "$URL" echo -en "\e[1A\e[0K" FILE=$(basename $URL) msg_ok "Downloaded ${CL}${BL}$FILE${CL}" msg_info "Extracting Mikrotik RouterOS CHR Disk Image" gunzip -f -S .zip $FILE STORAGE_TYPE=$(pvesm status -storage $STORAGE | awk 'NR>1 {print $2}') case $STORAGE_TYPE in nfs | dir) DISK_EXT=".qcow2" DISK_REF="$VMID/" DISK_IMPORT="-format qcow2" ;; btrfs) DISK_EXT=".raw" DISK_REF="$VMID/" DISK_IMPORT="-format raw" ;; zfspool) DISK_EXT="" DISK_REF="" DISK_IMPORT="-format raw" ;; *) DISK_EXT="" DISK_REF="" DISK_IMPORT="-format raw" ;; esac DISK_VAR="vm-${VMID}-disk-0${DISK_EXT:-}" DISK_REF="${STORAGE}:${DISK_REF:-}${DISK_VAR:-}" msg_ok "Extracted Mikrotik RouterOS CHR Disk Image" msg_info "Creating Mikrotik RouterOS CHR VM" qm create $VMID -tablet 0 -localtime 1 -cores $CORE_COUNT -memory $RAM_SIZE -name $HN \ -tags community-script -net0 virtio,bridge=$BRG,macaddr=$MAC$VLAN$MTU \ -onboot 1 -ostype l26 -scsihw virtio-scsi-pci qm importdisk $VMID ${FILE%.*} $STORAGE ${DISK_IMPORT:-} 1>&/dev/null qm set $VMID \ -scsi0 "$DISK_REF" \ -boot order=scsi0 >/dev/null DESCRIPTION=$( cat <<EOF <div align='center'> <a href='https://community-scripts.org' target='_blank' rel='noopener noreferrer'> <img src='https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/images/logo-81x112.png' alt='Logo' style='width:81px;height:112px;'/> </a> <h2 style='font-size: 24px; margin: 20px 0;'>Mikrotik RouterOS CHR</h2> <p style='margin: 16px 0;'> <a href='https://ko-fi.com/community_scripts' target='_blank' rel='noopener noreferrer'> <img src='https://img.shields.io/badge/☕-Buy us a coffee-blue' alt='spend Coffee' /> </a> </p> <span style='margin: 0 10px;'> <i class="fa fa-github fa-fw" style="color: #f5f5f5;"></i> <a href='https://github.com/community-scripts/ProxmoxVE' target='_blank' rel='noopener noreferrer' style='text-decoration: none; color: #00617f;'>GitHub</a> </span> <span style='margin: 0 10px;'> <i class="fa fa-comments fa-fw" style="color: #f5f5f5;"></i> <a href='https://github.com/community-scripts/ProxmoxVE/discussions' target='_blank' rel='noopener noreferrer' style='text-decoration: none; color: #00617f;'>Discussions</a> </span> <span style='margin: 0 10px;'> <i class="fa fa-exclamation-circle fa-fw" style="color: #f5f5f5;"></i> <a href='https://github.com/community-scripts/ProxmoxVE/issues' target='_blank' rel='noopener noreferrer' style='text-decoration: none; color: #00617f;'>Issues</a> </span> </div> EOF ) qm set $VMID -description "$DESCRIPTION" >/dev/null if [ -n "$DISK_SIZE" ]; then msg_info "Resizing disk to $DISK_SIZE GB" qm resize $VMID scsi0 ${DISK_SIZE} >/dev/null else msg_info "Using default disk size of $DEFAULT_DISK_SIZE GB" qm resize $VMID scsi0 ${DEFAULT_DISK_SIZE} >/dev/null fi msg_ok "Mikrotik RouterOS CHR VM ${CL}${BL}(${HN})" if [ "$START_VM" == "yes" ]; then msg_info "Starting Mikrotik RouterOS CHR VM" qm start $VMID msg_ok "Started Mikrotik RouterOS CHR VM" fi post_update_to_api "done" "none" msg_ok "Completed successfully!\n" ================================================ FILE: vm/nextcloud-vm.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT # https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE source /dev/stdin <<<$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func) function header_info { clear cat <<"EOF" ______ __ __ _ __ __ __ __ _ ____ ___ /_ __/_ _________ / //_/__ __ __ / |/ /____ __/ /_____/ /__ __ _____/ / | | / / |/ / / / / // / __/ _ \/ ,< / -_) // / / / -_) \ / __/ __/ / _ \/ // / _ / | |/ / /|_/ / /_/ \_,_/_/ /_//_/_/|_|\__/\_, / /_/|_/\__/_\_\\__/\__/_/\___/\_,_/\_,_/ |___/_/ /_/ /___/ EOF } header_info echo -e "\n Loading..." GEN_MAC=02:$(openssl rand -hex 5 | awk '{print toupper($0)}' | sed 's/\(..\)/\1:/g; s/.$//') RANDOM_UUID="$(cat /proc/sys/kernel/random/uuid)" METHOD="" NSAPP="nextcloud-vm" var_os="turnkey-nextcloud" var_version="n.d." YW=$(echo "\033[33m") BL=$(echo "\033[36m") RD=$(echo "\033[01;31m") BGN=$(echo "\033[4;92m") GN=$(echo "\033[1;92m") DGN=$(echo "\033[32m") CL=$(echo "\033[m") CL=$(echo "\033[m") BOLD=$(echo "\033[1m") BFR="\\r\\033[K" HOLD=" " TAB=" " CM="${TAB}✔️${TAB}${CL}" CROSS="${TAB}✖️${TAB}${CL}" INFO="${TAB}💡${TAB}${CL}" OS="${TAB}🖥️${TAB}${CL}" CONTAINERTYPE="${TAB}📦${TAB}${CL}" DISKSIZE="${TAB}💾${TAB}${CL}" CPUCORE="${TAB}🧠${TAB}${CL}" RAMSIZE="${TAB}🛠️${TAB}${CL}" CONTAINERID="${TAB}🆔${TAB}${CL}" HOSTNAME="${TAB}🏠${TAB}${CL}" BRIDGE="${TAB}🌉${TAB}${CL}" GATEWAY="${TAB}🌐${TAB}${CL}" DEFAULT="${TAB}⚙️${TAB}${CL}" MACADDRESS="${TAB}🔗${TAB}${CL}" VLANTAG="${TAB}🏷️${TAB}${CL}" CREATING="${TAB}🚀${TAB}${CL}" ADVANCED="${TAB}🧩${TAB}${CL}" CLOUD="${TAB}☁️${TAB}${CL}" THIN="discard=on,ssd=1," set -e trap 'error_handler $LINENO "$BASH_COMMAND"' ERR trap cleanup EXIT trap 'post_update_to_api "failed" "130"' SIGINT trap 'post_update_to_api "failed" "143"' SIGTERM trap 'post_update_to_api "failed" "129"; exit 129' SIGHUP function error_handler() { local exit_code="$?" local line_number="$1" local command="$2" local error_message="${RD}[ERROR]${CL} in line ${RD}$line_number${CL}: exit code ${RD}$exit_code${CL}: while executing command ${YW}$command${CL}" post_update_to_api "failed" "${exit_code}" echo -e "\n$error_message\n" cleanup_vmid } function get_valid_nextid() { local try_id try_id=$(pvesh get /cluster/nextid) while true; do if [ -f "/etc/pve/qemu-server/${try_id}.conf" ] || [ -f "/etc/pve/lxc/${try_id}.conf" ]; then try_id=$((try_id + 1)) continue fi if lvs --noheadings -o lv_name | grep -qE "(^|[-_])${try_id}($|[-_])"; then try_id=$((try_id + 1)) continue fi break done echo "$try_id" } function cleanup_vmid() { if qm status $VMID &>/dev/null; then qm stop $VMID &>/dev/null qm destroy $VMID &>/dev/null fi } function cleanup() { local exit_code=$? popd >/dev/null if [[ "${POST_TO_API_DONE:-}" == "true" && "${POST_UPDATE_DONE:-}" != "true" ]]; then if [[ $exit_code -eq 0 ]]; then post_update_to_api "done" "none" else post_update_to_api "failed" "$exit_code" fi fi rm -rf $TEMP_DIR } TEMP_DIR=$(mktemp -d) pushd $TEMP_DIR >/dev/null if whiptail --backtitle "Proxmox VE Helper Scripts" --title "Nextcloud VM" --yesno "This will create a New Nextcloud VM. Proceed?" 10 58; then : else header_info && echo -e "${CROSS}${RD}User exited script${CL}\n" && exit fi function msg_info() { local msg="$1" echo -ne "${TAB}${YW}${HOLD}${msg}${HOLD}" } function msg_ok() { local msg="$1" echo -e "${BFR}${CM}${GN}${msg}${CL}" } function msg_error() { local msg="$1" echo -e "${BFR}${CROSS}${RD}${msg}${CL}" } function check_root() { if [[ "$(id -u)" -ne 0 || $(ps -o comm= -p $PPID) == "sudo" ]]; then clear msg_error "Please run this script as root." echo -e "\nExiting..." sleep 2 exit fi } # This function checks the version of Proxmox Virtual Environment (PVE) and exits if the version is not supported. # Supported: Proxmox VE 8.0.x – 8.9.x, 9.0 and 9.1 pve_check() { local PVE_VER PVE_VER="$(pveversion | awk -F'/' '{print $2}' | awk -F'-' '{print $1}')" # Check for Proxmox VE 8.x: allow 8.0–8.9 if [[ "$PVE_VER" =~ ^8\.([0-9]+) ]]; then local MINOR="${BASH_REMATCH[1]}" if ((MINOR < 0 || MINOR > 9)); then msg_error "This version of Proxmox VE is not supported." msg_error "Supported: Proxmox VE version 8.0 – 8.9" exit 105 fi return 0 fi # Check for Proxmox VE 9.x: allow 9.0 and 9.1 if [[ "$PVE_VER" =~ ^9\.([0-9]+) ]]; then local MINOR="${BASH_REMATCH[1]}" if ((MINOR < 0 || MINOR > 1)); then msg_error "This version of Proxmox VE is not supported." msg_error "Supported: Proxmox VE version 9.0 – 9.1" exit 105 fi return 0 fi # All other unsupported versions msg_error "This version of Proxmox VE is not supported." msg_error "Supported versions: Proxmox VE 8.0 – 8.x or 9.0 – 9.1" exit 105 } function arch_check() { if [ "$(dpkg --print-architecture)" != "amd64" ]; then echo -e "\n ${INFO}${YWB}This script will not work with PiMox! \n" echo -e "\n ${YWB}Visit https://github.com/asylumexp/Proxmox for ARM64 support. \n" echo -e "Exiting..." sleep 2 exit fi } function ssh_check() { if command -v pveversion >/dev/null 2>&1; then if [ -n "${SSH_CLIENT:+x}" ]; then if whiptail --backtitle "Proxmox VE Helper Scripts" --defaultno --title "SSH DETECTED" --yesno "It's suggested to use the Proxmox shell instead of SSH, since SSH can create issues while gathering variables. Would you like to proceed with using SSH?" 10 62; then echo "you've been warned" else clear exit fi fi fi } function exit-script() { clear echo -e "\n${CROSS}${RD}User exited script${CL}\n" exit } function default_settings() { VMID=$(get_valid_nextid) FORMAT=",efitype=4m" MACHINE="" DISK_SIZE="10G" DISK_CACHE="" HN="nextcloud-vm" CPU_TYPE="" CORE_COUNT="2" RAM_SIZE="2048" BRG="vmbr0" MAC="$GEN_MAC" VLAN="" MTU="" START_VM="yes" METHOD="default" echo -e "${CONTAINERID}${BOLD}${DGN}Virtual Machine ID: ${BGN}${VMID}${CL}" echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}i440fx${CL}" echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}${DISK_SIZE}${CL}" echo -e "${DISKSIZE}${BOLD}${DGN}Disk Cache: ${BGN}None${CL}" echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}${HN}${CL}" echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}KVM64${CL}" echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}${CORE_COUNT}${CL}" echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}${RAM_SIZE}${CL}" echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}${BRG}${CL}" echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}${MAC}${CL}" echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}Default${CL}" echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}Default${CL}" echo -e "${GATEWAY}${BOLD}${DGN}Start VM when completed: ${BGN}yes${CL}" echo -e "${CREATING}${BOLD}${DGN}Creating a Nextcloud VM using the above default settings${CL}" } function advanced_settings() { METHOD="advanced" [ -z "${VMID:-}" ] && VMID=$(get_valid_nextid) while true; do if VMID=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Virtual Machine ID" 8 58 $VMID --title "VIRTUAL MACHINE ID" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z "$VMID" ]; then VMID=$(get_valid_nextid) fi if pct status "$VMID" &>/dev/null || qm status "$VMID" &>/dev/null; then echo -e "${CROSS}${RD} ID $VMID is already in use${CL}" sleep 2 continue fi echo -e "${CONTAINERID}${BOLD}${DGN}Virtual Machine ID: ${BGN}$VMID${CL}" break else exit-script fi done if MACH=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "MACHINE TYPE" --radiolist --cancel-button Exit-Script "Choose Type" 10 58 2 \ "i440fx" "Machine i440fx" ON \ "q35" "Machine q35" OFF \ 3>&1 1>&2 2>&3); then if [ $MACH = q35 ]; then echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}$MACH${CL}" FORMAT="" MACHINE=" -machine q35" else echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}$MACH${CL}" FORMAT=",efitype=4m" MACHINE="" fi else exit-script fi if DISK_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Disk Size in GiB (e.g., 10, 20)" 8 58 "$DISK_SIZE" --title "DISK SIZE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then DISK_SIZE=$(echo "$DISK_SIZE" | tr -d ' ') if [[ "$DISK_SIZE" =~ ^[0-9]+$ ]]; then DISK_SIZE="${DISK_SIZE}G" echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}$DISK_SIZE${CL}" elif [[ "$DISK_SIZE" =~ ^[0-9]+G$ ]]; then echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}$DISK_SIZE${CL}" else echo -e "${DISKSIZE}${BOLD}${RD}Invalid Disk Size. Please use a number (e.g., 10 or 10G).${CL}" exit-script fi else exit-script fi if DISK_CACHE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "DISK CACHE" --radiolist "Choose" --cancel-button Exit-Script 10 58 2 \ "0" "None (Default)" ON \ "1" "Write Through" OFF \ 3>&1 1>&2 2>&3); then if [ $DISK_CACHE = "1" ]; then echo -e "${DISKSIZE}${BOLD}${DGN}Disk Cache: ${BGN}Write Through${CL}" DISK_CACHE="cache=writethrough," else echo -e "${DISKSIZE}${BOLD}${DGN}Disk Cache: ${BGN}None${CL}" DISK_CACHE="" fi else exit-script fi if VM_NAME=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Hostname" 8 58 nextcloud-vm --title "HOSTNAME" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z $VM_NAME ]; then HN="nextcloud-vm" echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}" else HN=$(echo "${VM_NAME,,}" | tr -cs 'a-z0-9-' '-' | sed 's/^-//;s/-$//') if [ "$HN" != "${VM_NAME,,}" ]; then whiptail --backtitle "Proxmox VE Helper Scripts" --title "HOSTNAME ADJUSTED" --msgbox "Invalid characters detected. Hostname has been adjusted to:\n\n $HN" 10 58 fi echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}" fi else exit-script fi if CPU_TYPE1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "CPU MODEL" --radiolist "Choose" --cancel-button Exit-Script 10 58 2 \ "0" "KVM64 (Default)" ON \ "1" "Host" OFF \ 3>&1 1>&2 2>&3); then if [ $CPU_TYPE1 = "1" ]; then echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}Host${CL}" CPU_TYPE=" -cpu host" else echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}KVM64${CL}" CPU_TYPE="" fi else exit-script fi while true; do if CORE_COUNT=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Allocate CPU Cores" 8 58 2 --title "CORE COUNT" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z "$CORE_COUNT" ]; then CORE_COUNT="2"; fi if [[ "$CORE_COUNT" =~ ^[1-9][0-9]*$ ]]; then echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}$CORE_COUNT${CL}" break fi whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "CPU Cores must be a positive integer (e.g., 2)." 8 58 else exit-script fi done while true; do if RAM_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Allocate RAM in MiB" 8 58 2048 --title "RAM" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z "$RAM_SIZE" ]; then RAM_SIZE="2048"; fi if [[ "$RAM_SIZE" =~ ^[1-9][0-9]*$ ]]; then echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}$RAM_SIZE${CL}" break fi whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "RAM Size must be a positive integer in MiB (e.g., 2048)." 8 58 else exit-script fi done if BRG=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a Bridge" 8 58 vmbr0 --title "BRIDGE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z $BRG ]; then BRG="vmbr0" echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}" else echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}" fi else exit-script fi while true; do if MAC1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a MAC Address" 8 58 $GEN_MAC --title "MAC ADDRESS" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z "$MAC1" ]; then MAC="$GEN_MAC" echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}$MAC${CL}" break fi if [[ "$MAC1" =~ ^([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}$ ]]; then MAC="$MAC1" echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}$MAC1${CL}" break fi whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "Invalid MAC address format. Use XX:XX:XX:XX:XX:XX (e.g., AA:BB:CC:DD:EE:FF)." 8 58 else exit-script fi done while true; do if VLAN1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a Vlan(leave blank for default)" 8 58 --title "VLAN" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z "$VLAN1" ]; then VLAN1="Default" VLAN="" echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}$VLAN1${CL}" break fi if [[ "$VLAN1" =~ ^[0-9]+$ ]] && [ "$VLAN1" -ge 1 ] && [ "$VLAN1" -le 4094 ]; then VLAN=",tag=$VLAN1" echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}$VLAN1${CL}" break fi whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "VLAN must be a number between 1 and 4094, or leave blank for default." 8 58 else exit-script fi done while true; do if MTU1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Interface MTU Size (leave blank for default)" 8 58 --title "MTU SIZE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z "$MTU1" ]; then MTU1="Default" MTU="" echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}$MTU1${CL}" break fi if [[ "$MTU1" =~ ^[0-9]+$ ]] && [ "$MTU1" -ge 576 ] && [ "$MTU1" -le 65520 ]; then MTU=",mtu=$MTU1" echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}$MTU1${CL}" break fi whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "MTU Size must be a number between 576 and 65520, or leave blank for default." 8 58 else exit-script fi done if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "START VIRTUAL MACHINE" --yesno "Start VM when completed?" 10 58); then echo -e "${GATEWAY}${BOLD}${DGN}Start VM when completed: ${BGN}yes${CL}" START_VM="yes" else echo -e "${GATEWAY}${BOLD}${DGN}Start VM when completed: ${BGN}no${CL}" START_VM="no" fi if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "ADVANCED SETTINGS COMPLETE" --yesno "Ready to create a Nextcloud VM?" --no-button Do-Over 10 58); then echo -e "${CREATING}${BOLD}${DGN}Creating a Nextcloud VM using the above advanced settings${CL}" else header_info echo -e "${ADVANCED}${BOLD}${RD}Using Advanced Settings${CL}" advanced_settings fi } function start_script() { if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "SETTINGS" --yesno "Use Default Settings?" --no-button Advanced 10 58); then header_info echo -e "${BL}Using Default Settings${CL}" default_settings else header_info echo -e "${RD}Using Advanced Settings${CL}" advanced_settings fi } check_root arch_check pve_check ssh_check start_script post_to_api_vm msg_info "Validating Storage" while read -r line; do TAG=$(echo $line | awk '{print $1}') TYPE=$(echo $line | awk '{printf "%-10s", $2}') FREE=$(echo $line | numfmt --field 4-6 --from-unit=K --to=iec --format %.2f | awk '{printf( "%9sB", $6)}') ITEM=" Type: $TYPE Free: $FREE " OFFSET=2 if [[ $((${#ITEM} + $OFFSET)) -gt ${MSG_MAX_LENGTH:-} ]]; then MSG_MAX_LENGTH=$((${#ITEM} + $OFFSET)) fi STORAGE_MENU+=("$TAG" "$ITEM" "OFF") done < <(pvesm status -content images | awk 'NR>1') VALID=$(pvesm status -content images | awk 'NR>1') if [ -z "$VALID" ]; then msg_error "Unable to detect a valid storage location." exit elif [ $((${#STORAGE_MENU[@]} / 3)) -eq 1 ]; then STORAGE=${STORAGE_MENU[0]} else while [ -z "${STORAGE:+x}" ]; do STORAGE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "Storage Pools" --radiolist \ "Which storage pool would you like to use for ${HN}?\nTo make a selection, use the Spacebar.\n" \ 16 $(($MSG_MAX_LENGTH + 23)) 6 \ "${STORAGE_MENU[@]}" 3>&1 1>&2 2>&3) done fi msg_ok "Using ${CL}${BL}$STORAGE${CL} ${GN}for Storage Location." msg_ok "Virtual Machine ID is ${CL}${BL}$VMID${CL}." msg_info "Retrieving the URL for the $NAME Disk Image" URL=http://mirror.turnkeylinux.org/turnkeylinux/images/iso/turnkey-nextcloud-18.1-bookworm-amd64.iso sleep 2 msg_ok "${CL}${BL}${URL}${CL}" curl -f#SL -o "$(basename "$URL")" "$URL" echo -en "\e[1A\e[0K" FILE=$(basename $URL) msg_ok "Downloaded ${CL}${BL}${FILE}${CL}" STORAGE_TYPE=$(pvesm status -storage $STORAGE | awk 'NR>1 {print $2}') case $STORAGE_TYPE in nfs | dir) DISK_EXT=".raw" DISK_REF="$VMID/" DISK_IMPORT="-format raw" THIN="" ;; btrfs) DISK_EXT=".raw" DISK_REF="$VMID/" DISK_IMPORT="-format raw" FORMAT=",efitype=4m" THIN="" ;; *) DISK_EXT="" DISK_REF="" DISK_IMPORT="-format raw" ;; esac for i in {0,1,2}; do disk="DISK$i" eval DISK${i}=vm-${VMID}-disk-${i}${DISK_EXT:-} eval DISK${i}_REF=${STORAGE}:${DISK_REF:-}${!disk} done msg_info "Creating a $NAME" qm create $VMID -agent 1${MACHINE} -tablet 0 -localtime 1 -bios seabios${CPU_TYPE} -cores $CORE_COUNT -memory $RAM_SIZE \ -name $HN -tags community-script -net0 virtio,bridge=$BRG,macaddr=$MAC$VLAN$MTU -onboot 1 -ostype l26 -scsihw virtio-scsi-pci pvesm alloc $STORAGE $VMID $DISK0 4M 1>&/dev/null pvesm alloc $STORAGE $VMID $DISK1 12G 1>&/dev/null qm importdisk $VMID ${FILE} $STORAGE ${DISK_IMPORT:-} 1>&/dev/null qm set $VMID \ -efidisk0 ${DISK0_REF}${FORMAT} \ -scsi0 ${DISK1_REF},${DISK_CACHE}${THIN} \ -scsi1 ${DISK2_REF},${DISK_CACHE}${THIN} \ -boot order='scsi1;scsi0' >/dev/null DESCRIPTION=$( cat <<EOF <div align='center'> <a href='https://community-scripts.org' target='_blank' rel='noopener noreferrer'> <img src='https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/images/logo-81x112.png' alt='Logo' style='width:81px;height:112px;'/> </a> <h2 style='font-size: 24px; margin: 20px 0;'>Nextcloud VM</h2> <p style='margin: 16px 0;'> <a href='https://ko-fi.com/community_scripts' target='_blank' rel='noopener noreferrer'> <img src='https://img.shields.io/badge/☕-Buy us a coffee-blue' alt='spend Coffee' /> </a> </p> <span style='margin: 0 10px;'> <i class="fa fa-github fa-fw" style="color: #f5f5f5;"></i> <a href='https://github.com/community-scripts/ProxmoxVE' target='_blank' rel='noopener noreferrer' style='text-decoration: none; color: #00617f;'>GitHub</a> </span> <span style='margin: 0 10px;'> <i class="fa fa-comments fa-fw" style="color: #f5f5f5;"></i> <a href='https://github.com/community-scripts/ProxmoxVE/discussions' target='_blank' rel='noopener noreferrer' style='text-decoration: none; color: #00617f;'>Discussions</a> </span> <span style='margin: 0 10px;'> <i class="fa fa-exclamation-circle fa-fw" style="color: #f5f5f5;"></i> <a href='https://github.com/community-scripts/ProxmoxVE/issues' target='_blank' rel='noopener noreferrer' style='text-decoration: none; color: #00617f;'>Issues</a> </span> </div> EOF ) qm set $VMID -description "$DESCRIPTION" >/dev/null if [ -n "$DISK_SIZE" ]; then msg_info "Resizing disk to $DISK_SIZE GB" qm resize $VMID scsi0 ${DISK_SIZE} >/dev/null else msg_info "Using default disk size of $DEFAULT_DISK_SIZE GB" qm resize $VMID scsi0 ${DEFAULT_DISK_SIZE} >/dev/null fi msg_ok "Created a $NAME ${CL}${BL}(${HN})" if [ "$START_VM" == "yes" ]; then msg_info "Starting $NAME" qm start $VMID msg_ok "Started $NAME" fi post_update_to_api "done" "none" msg_ok "Completed successfully!\n" ================================================ FILE: vm/openwrt-vm.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # Jon Spriggs (jontheniceguy) # License: MIT # https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Based on work from https://i12bretro.github.io/tutorials/0405.html source /dev/stdin <<<$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func) function header_info { clear cat <<"EOF" ____ _ __ __ / __ \____ ___ ____| | / /____/ /_ / / / / __ \/ _ \/ __ \ | /| / / ___/ __/ / /_/ / /_/ / __/ / / / |/ |/ / / / /_ \____/ .___/\___/_/ /_/|__/|__/_/ \__/ /_/ W I R E L E S S F R E E D O M EOF } header_info echo -e "\n Loading..." RANDOM_UUID="$(cat /proc/sys/kernel/random/uuid)" METHOD="" NSAPP="openwrt-vm" var_os="openwrt" var_version=" " DISK_SIZE="1G" GEN_MAC=02:$(openssl rand -hex 5 | awk '{print toupper($0)}' | sed 's/\(..\)/\1:/g; s/.$//') GEN_MAC_LAN=02:$(openssl rand -hex 5 | awk '{print toupper($0)}' | sed 's/\(..\)/\1:/g; s/.$//') YW=$(echo "\033[33m") BL=$(echo "\033[36m") HA=$(echo "\033[1;34m") RD=$(echo "\033[01;31m") BGN=$(echo "\033[4;92m") GN=$(echo "\033[1;92m") DGN=$(echo "\033[32m") CL=$(echo "\033[m") BOLD=$(echo "\033[1m") BFR="\\r\\033[K" HOLD=" " TAB=" " CM="${TAB}✔️${TAB}${CL}" CROSS="${TAB}✖️${TAB}${CL}" INFO="${TAB}💡${TAB}${CL}" OS="${TAB}🖥️${TAB}${CL}" CONTAINERTYPE="${TAB}📦${TAB}${CL}" DISKSIZE="${TAB}💾${TAB}${CL}" CPUCORE="${TAB}🧠${TAB}${CL}" RAMSIZE="${TAB}🛠️${TAB}${CL}" CONTAINERID="${TAB}🆔${TAB}${CL}" HOSTNAME="${TAB}🏠${TAB}${CL}" BRIDGE="${TAB}🌉${TAB}${CL}" GATEWAY="${TAB}🌐${TAB}${CL}" DEFAULT="${TAB}⚙️${TAB}${CL}" MACADDRESS="${TAB}🔗${TAB}${CL}" VLANTAG="${TAB}🏷️${TAB}${CL}" CREATING="${TAB}🚀${TAB}${CL}" ADVANCED="${TAB}🧩${TAB}${CL}" CLOUD="${TAB}☁️${TAB}${CL}" set -Eeo pipefail trap 'error_handler $LINENO "$BASH_COMMAND"' ERR trap cleanup EXIT trap 'post_update_to_api "failed" "130"' SIGINT trap 'post_update_to_api "failed" "143"' SIGTERM trap 'post_update_to_api "failed" "129"; exit 129' SIGHUP function error_handler() { local exit_code="$?" local line_number="$1" local command="$2" post_update_to_api "failed" "$exit_code" local error_message="${RD}[ERROR]${CL} in line ${RD}$line_number${CL}: exit code ${RD}$exit_code${CL}: while executing command ${YW}$command${CL}" echo -e "\n$error_message\n" cleanup_vmid } function get_valid_nextid() { local try_id try_id=$(pvesh get /cluster/nextid) while true; do if [ -f "/etc/pve/qemu-server/${try_id}.conf" ] || [ -f "/etc/pve/lxc/${try_id}.conf" ]; then try_id=$((try_id + 1)) continue fi if lvs --noheadings -o lv_name | grep -qE "(^|[-_])${try_id}($|[-_])"; then try_id=$((try_id + 1)) continue fi break done echo "$try_id" } function cleanup_vmid() { if qm status $VMID &>/dev/null; then qm stop $VMID &>/dev/null qm destroy $VMID &>/dev/null fi } function cleanup() { local exit_code=$? popd >/dev/null if [[ "${POST_TO_API_DONE:-}" == "true" && "${POST_UPDATE_DONE:-}" != "true" ]]; then if [[ $exit_code -eq 0 ]]; then post_update_to_api "done" "none" else post_update_to_api "failed" "$exit_code" fi fi rm -rf $TEMP_DIR } TEMP_DIR=$(mktemp -d) pushd $TEMP_DIR >/dev/null function send_line_to_vm() { echo -e "${DGN}Sending line: ${YW}$1${CL}" for ((i = 0; i < ${#1}; i++)); do character=${1:i:1} case $character in " ") character="spc" ;; "-") character="minus" ;; "=") character="equal" ;; ",") character="comma" ;; ".") character="dot" ;; "/") character="slash" ;; "'") character="apostrophe" ;; ";") character="semicolon" ;; '\') character="backslash" ;; '`') character="grave_accent" ;; "[") character="bracket_left" ;; "]") character="bracket_right" ;; "_") character="shift-minus" ;; "+") character="shift-equal" ;; "?") character="shift-slash" ;; "<") character="shift-comma" ;; ">") character="shift-dot" ;; '"') character="shift-apostrophe" ;; ":") character="shift-semicolon" ;; "|") character="shift-backslash" ;; "~") character="shift-grave_accent" ;; "{") character="shift-bracket_left" ;; "}") character="shift-bracket_right" ;; "A") character="shift-a" ;; "B") character="shift-b" ;; "C") character="shift-c" ;; "D") character="shift-d" ;; "E") character="shift-e" ;; "F") character="shift-f" ;; "G") character="shift-g" ;; "H") character="shift-h" ;; "I") character="shift-i" ;; "J") character="shift-j" ;; "K") character="shift-k" ;; "L") character="shift-l" ;; "M") character="shift-m" ;; "N") character="shift-n" ;; "O") character="shift-o" ;; "P") character="shift-p" ;; "Q") character="shift-q" ;; "R") character="shift-r" ;; "S") character="shift-s" ;; "T") character="shift-t" ;; "U") character="shift-u" ;; "V") character="shift-v" ;; "W") character="shift-w" ;; "X") character="shift=x" ;; "Y") character="shift-y" ;; "Z") character="shift-z" ;; "!") character="shift-1" ;; "@") character="shift-2" ;; "#") character="shift-3" ;; '$') character="shift-4" ;; "%") character="shift-5" ;; "^") character="shift-6" ;; "&") character="shift-7" ;; "*") character="shift-8" ;; "(") character="shift-9" ;; ")") character="shift-0" ;; esac qm sendkey $VMID "$character" done qm sendkey $VMID ret } TEMP_DIR=$(mktemp -d) pushd $TEMP_DIR >/dev/null if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "OpenWrt VM" --yesno "This will create a New OpenWrt VM. Proceed?" 10 58); then : else header_info && echo -e "⚠ User exited script \n" && exit fi function msg_info() { local msg="$1" echo -ne " ${HOLD} ${YW}${msg}..." } function msg_ok() { local msg="$1" echo -e "${BFR} ${CM} ${GN}${msg}${CL}" } function msg_error() { local msg="$1" echo -e "${BFR} ${CROSS} ${RD}${msg}${CL}" } # This function checks the version of Proxmox Virtual Environment (PVE) and exits if the version is not supported. # Supported: Proxmox VE 8.0.x – 8.9.x, 9.0 and 9.1 pve_check() { local PVE_VER PVE_VER="$(pveversion | awk -F'/' '{print $2}' | awk -F'-' '{print $1}')" # Check for Proxmox VE 8.x: allow 8.0–8.9 if [[ "$PVE_VER" =~ ^8\.([0-9]+) ]]; then local MINOR="${BASH_REMATCH[1]}" if ((MINOR < 0 || MINOR > 9)); then msg_error "This version of Proxmox VE is not supported." msg_error "Supported: Proxmox VE version 8.0 – 8.9" exit 105 fi return 0 fi # Check for Proxmox VE 9.x: allow 9.0 and 9.1 if [[ "$PVE_VER" =~ ^9\.([0-9]+) ]]; then local MINOR="${BASH_REMATCH[1]}" if ((MINOR < 0 || MINOR > 1)); then msg_error "This version of Proxmox VE is not supported." msg_error "Supported: Proxmox VE version 9.0 – 9.1" exit 105 fi return 0 fi # All other unsupported versions msg_error "This version of Proxmox VE is not supported." msg_error "Supported versions: Proxmox VE 8.0 – 8.x or 9.0 – 9.1" exit 105 } function arch_check() { if [ "$(dpkg --print-architecture)" != "amd64" ]; then echo -e "\n ${CROSS} This script will not work with PiMox! \n" echo -e "Exiting..." sleep 2 exit fi } function ssh_check() { if command -v pveversion >/dev/null 2>&1; then if [ -n "${SSH_CLIENT:+x}" ]; then if whiptail --backtitle "Proxmox VE Helper Scripts" --defaultno --title "SSH DETECTED" --yesno "It's suggested to use the Proxmox shell instead of SSH, since SSH can create issues while gathering variables. Would you like to proceed with using SSH?" 10 62; then echo "you've been warned" else clear exit fi fi fi } function exit-script() { clear echo -e "⚠ User exited script \n" exit } function default_settings() { VMID=$(get_valid_nextid) HN="openwrt" CORE_COUNT="1" RAM_SIZE="256" BRG="vmbr0" LAN_BRG="vmbr0" MAC=$GEN_MAC LAN_MAC=$GEN_MAC_LAN VLAN="" LAN_VLAN="" LAN_IP_ADDR="192.168.1.1" LAN_NETMASK="255.255.255.0" MTU="" START_VM="yes" METHOD="default" DISK_SIZE="1G" echo -e "${CONTAINERID}${BOLD}${DGN}VMID: ${BGN}${VMID}${CL}" echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}${HN}${CL}" echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}${CORE_COUNT}${CL}" echo -e "${RAMSIZE}${BOLD}${DGN}RAM: ${BGN}${RAM_SIZE}${CL}" echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}${DISK_SIZE}${CL}" echo -e "${BRIDGE}${BOLD}${DGN}WAN Bridge: ${BGN}${BRG}${CL}" echo -e "${BRIDGE}${BOLD}${DGN}LAN Bridge: ${BGN}${LAN_BRG}${CL}" echo -e "${MACADDRESS}${BOLD}${DGN}WAN MAC: ${BGN}${MAC}${CL}" echo -e "${MACADDRESS}${BOLD}${DGN}LAN MAC: ${BGN}${LAN_MAC}${CL}" } function advanced_settings() { METHOD="advanced" [ -z "${VMID:-}" ] && VMID=$(get_valid_nextid) while true; do if VMID=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Virtual Machine ID" 8 58 $VMID --title "VIRTUAL MACHINE ID" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z "$VMID" ]; then VMID=$(get_valid_nextid) fi if pct status "$VMID" &>/dev/null || qm status "$VMID" &>/dev/null; then echo -e "${CROSS}${RD} ID $VMID is already in use${CL}" sleep 2 continue fi echo -e "${DGN}Virtual Machine ID: ${BGN}$VMID${CL}" break else exit-script fi done if VM_NAME=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Hostname" 8 58 openwrt --title "HOSTNAME" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z $VM_NAME ]; then HN="openwrt" else HN=$(echo "${VM_NAME,,}" | tr -cs 'a-z0-9-' '-' | sed 's/^-//;s/-$//') if [ "$HN" != "${VM_NAME,,}" ]; then whiptail --backtitle "Proxmox VE Helper Scripts" --title "HOSTNAME ADJUSTED" --msgbox "Invalid characters detected. Hostname has been adjusted to:\n\n $HN" 10 58 fi fi echo -e "${DGN}Using Hostname: ${BGN}$HN${CL}" else exit-script fi while true; do if CORE_COUNT=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Allocate CPU Cores" 8 58 1 --title "CORE COUNT" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z "$CORE_COUNT" ]; then CORE_COUNT="1"; fi if [[ "$CORE_COUNT" =~ ^[1-9][0-9]*$ ]]; then echo -e "${DGN}Allocated Cores: ${BGN}$CORE_COUNT${CL}" break fi whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "CPU Cores must be a positive integer (e.g., 1)." 8 58 else exit-script fi done while true; do if RAM_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Allocate RAM in MiB" 8 58 256 --title "RAM" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z "$RAM_SIZE" ]; then RAM_SIZE="256"; fi if [[ "$RAM_SIZE" =~ ^[1-9][0-9]*$ ]]; then echo -e "${DGN}Allocated RAM: ${BGN}$RAM_SIZE${CL}" break fi whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "RAM Size must be a positive integer in MiB (e.g., 256)." 8 58 else exit-script fi done if DISK_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" \ --inputbox "Set Disk Size in GiB (e.g., 1, 2, 4)" 8 58 "1" \ --title "DISK SIZE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [[ "$DISK_SIZE" =~ ^[0-9]+$ ]]; then DISK_SIZE="${DISK_SIZE}G" fi echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}${DISK_SIZE}${CL}" else exit-script fi if BRG=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a WAN Bridge" 8 58 vmbr0 --title "WAN BRIDGE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z $BRG ]; then BRG="vmbr0" fi echo -e "${DGN}Using WAN Bridge: ${BGN}$BRG${CL}" else exit-script fi if LAN_BRG=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a LAN Bridge" 8 58 vmbr0 --title "LAN BRIDGE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z $LAN_BRG ]; then LAN_BRG="vmbr0" fi echo -e "${DGN}Using LAN Bridge: ${BGN}$LAN_BRG${CL}" else exit-script fi if LAN_IP_ADDR=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a router IP" 8 58 $LAN_IP_ADDR --title "LAN IP ADDRESS" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z $LAN_IP_ADDR ]; then LAN_IP_ADDR="192.168.1.1" fi echo -e "${DGN}Using LAN IP ADDRESS: ${BGN}$LAN_IP_ADDR${CL}" else exit-script fi if LAN_NETMASK=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a router netmask" 8 58 $LAN_NETMASK --title "LAN NETMASK" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z $LAN_NETMASK ]; then LAN_NETMASK="255.255.255.0" fi echo -e "${DGN}Using LAN NETMASK: ${BGN}$LAN_NETMASK${CL}" else exit-script fi if MAC1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a WAN MAC Address" 8 58 $GEN_MAC --title "WAN MAC ADDRESS" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z $MAC1 ]; then MAC="$GEN_MAC" else MAC="$MAC1" fi echo -e "${DGN}Using WAN MAC Address: ${BGN}$MAC${CL}" else exit-script fi if MAC2=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a LAN MAC Address" 8 58 $GEN_MAC_LAN --title "LAN MAC ADDRESS" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z $MAC2 ]; then LAN_MAC="$GEN_MAC_LAN" else LAN_MAC="$MAC2" fi echo -e "${DGN}Using LAN MAC Address: ${BGN}$LAN_MAC${CL}" else exit-script fi while true; do if VLAN1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a WAN Vlan (leave blank for default)" 8 58 --title "WAN VLAN" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z "$VLAN1" ]; then VLAN1="Default" VLAN="" echo -e "${DGN}Using WAN Vlan: ${BGN}$VLAN1${CL}" break fi if [[ "$VLAN1" =~ ^[0-9]+$ ]] && [ "$VLAN1" -ge 1 ] && [ "$VLAN1" -le 4094 ]; then VLAN=",tag=$VLAN1" echo -e "${DGN}Using WAN Vlan: ${BGN}$VLAN1${CL}" break fi whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "VLAN must be a number between 1 and 4094, or leave blank for default." 8 58 else exit-script fi done while true; do if VLAN2=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a LAN Vlan" 8 58 999 --title "LAN VLAN" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z "$VLAN2" ]; then VLAN2="Default" LAN_VLAN="" echo -e "${DGN}Using LAN Vlan: ${BGN}$VLAN2${CL}" break fi if [[ "$VLAN2" =~ ^[0-9]+$ ]] && [ "$VLAN2" -ge 1 ] && [ "$VLAN2" -le 4094 ]; then LAN_VLAN=",tag=$VLAN2" echo -e "${DGN}Using LAN Vlan: ${BGN}$VLAN2${CL}" break fi whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "VLAN must be a number between 1 and 4094, or leave blank for default." 8 58 else exit-script fi done while true; do if MTU1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Interface MTU Size (leave blank for default)" 8 58 --title "MTU SIZE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z "$MTU1" ]; then MTU1="Default" MTU="" echo -e "${DGN}Using Interface MTU Size: ${BGN}$MTU1${CL}" break fi if [[ "$MTU1" =~ ^[0-9]+$ ]] && [ "$MTU1" -ge 576 ] && [ "$MTU1" -le 65520 ]; then MTU=",mtu=$MTU1" echo -e "${DGN}Using Interface MTU Size: ${BGN}$MTU1${CL}" break fi whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "MTU Size must be a number between 576 and 65520, or leave blank for default." 8 58 else exit-script fi done if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "START VIRTUAL MACHINE" --yesno "Start VM when completed?" 10 58); then START_VM="yes" else START_VM="no" fi echo -e "${DGN}Start VM when completed: ${BGN}$START_VM${CL}" if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "ADVANCED SETTINGS COMPLETE" --yesno "Ready to create OpenWrt VM?" --no-button Do-Over 10 58); then echo -e "${RD}Creating a OpenWrt VM using the above advanced settings${CL}" else header_info echo -e "${RD}Using Advanced Settings${CL}" advanced_settings fi } function start_script() { if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "SETTINGS" --yesno "Use Default Settings?" --no-button Advanced 10 58); then header_info echo -e "${BL}Using Default Settings${CL}" default_settings else header_info echo -e "${RD}Using Advanced Settings${CL}" advanced_settings fi } arch_check pve_check ssh_check start_script post_to_api_vm msg_info "Validating Storage" while read -r line; do TAG=$(echo $line | awk '{print $1}') TYPE=$(echo $line | awk '{printf "%-10s", $2}') FREE=$(echo $line | numfmt --field 4-6 --from-unit=K --to=iec --format %.2f | awk '{printf( "%9sB", $6)}') ITEM=" Type: $TYPE Free: $FREE " OFFSET=2 if [[ $((${#ITEM} + $OFFSET)) -gt ${MSG_MAX_LENGTH:-} ]]; then MSG_MAX_LENGTH=$((${#ITEM} + $OFFSET)) fi STORAGE_MENU+=("$TAG" "$ITEM" "OFF") done < <(pvesm status -content images | awk 'NR>1') VALID=$(pvesm status -content images | awk 'NR>1') if [ -z "$VALID" ]; then echo -e "\n${RD}⚠ Unable to detect a valid storage location.${CL}" echo -e "Exiting..." exit elif [ $((${#STORAGE_MENU[@]} / 3)) -eq 1 ]; then STORAGE=${STORAGE_MENU[0]} else while [ -z "${STORAGE:+x}" ]; do STORAGE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "Storage Pools" --radiolist \ "Which storage pool would you like to use for the OpenWrt VM?\n\n" \ 16 $(($MSG_MAX_LENGTH + 23)) 6 \ "${STORAGE_MENU[@]}" 3>&1 1>&2 2>&3) done fi msg_ok "Using ${CL}${BL}$STORAGE${CL} ${GN}for Storage Location." msg_ok "Virtual Machine ID is ${CL}${BL}$VMID${CL}." msg_info "Getting URL for OpenWrt Disk Image" response=$(curl -fsSL https://openwrt.org) stableversion=$(echo "$response" | sed -n 's/.*Current stable release - OpenWrt \([0-9.]\+\).*/\1/p' | head -n 1) URL="https://downloads.openwrt.org/releases/$stableversion/targets/x86/64/openwrt-$stableversion-x86-64-generic-ext4-combined.img.gz" msg_ok "${CL}${BL}${URL}${CL}" curl -f#SL -o "$(basename "$URL")" "$URL" FILE=$(basename "$URL") msg_ok "Downloaded ${CL}${BL}$FILE${CL}" gunzip -f "$FILE" >/dev/null 2>&1 || true FILE="${FILE%.*}" msg_ok "Extracted OpenWrt Disk Image ${CL}${BL}$FILE${CL}" msg_info "Creating OpenWrt VM" qm create $VMID -cores $CORE_COUNT -memory $RAM_SIZE -name $HN \ -onboot 1 -ostype l26 -scsihw virtio-scsi-pci --tablet 0 if [[ "$(pvesm status | awk -v s=$STORAGE '$1==s {print $2}')" == "dir" ]]; then qm set $VMID -efidisk0 ${STORAGE}:0,efitype=4m,size=4M else pvesm alloc $STORAGE $VMID vm-$VMID-disk-0 4M >/dev/null qm set $VMID -efidisk0 ${STORAGE}:vm-$VMID-disk-0,efitype=4m,size=4M fi IMPORT_OUT="$(qm importdisk $VMID $FILE $STORAGE --format raw 2>&1 || true)" DISK_REF="$(printf '%s\n' "$IMPORT_OUT" | sed -n "s/.*successfully imported disk '\([^']\+\)'.*/\1/p")" if [[ -z "$DISK_REF" ]]; then DISK_REF="$(pvesm list "$STORAGE" | awk -v id="$VMID" '$1 ~ ("vm-"id"-disk-") {print $1}' | sort | tail -n1)" fi if [[ -z "$DISK_REF" ]]; then msg_error "Unable to determine imported disk reference." echo "$IMPORT_OUT" exit 226 fi qm set $VMID \ -efidisk0 ${STORAGE}:0,efitype=4m,size=4M \ -scsi0 ${DISK_REF} \ -boot order=scsi0 \ -tags community-script >/dev/null msg_ok "Attached disk" msg_info "Resizing disk to ${DISK_SIZE}" qm disk resize "$VMID" scsi0 "${DISK_SIZE}" >/dev/null msg_ok "Resized disk to ${DISK_SIZE}" DESCRIPTION=$( cat <<EOF <div align='center'> <a href='https://community-scripts.org' target='_blank' rel='noopener noreferrer'> <img src='https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/images/logo-81x112.png' alt='Logo' style='width:81px;height:112px;'/> </a> <h2 style='font-size: 24px; margin: 20px 0;'>OpenWrt VM</h2> <p style='margin: 16px 0;'> <a href='https://ko-fi.com/community_scripts' target='_blank' rel='noopener noreferrer'> <img src='https://img.shields.io/badge/☕-Buy us a coffee-blue' alt='spend Coffee' /> </a> </p> <span style='margin: 0 10px;'> <i class="fa fa-github fa-fw" style="color: #f5f5f5;"></i> <a href='https://github.com/community-scripts/ProxmoxVE' target='_blank' rel='noopener noreferrer' style='text-decoration: none; color: #00617f;'>GitHub</a> </span> <span style='margin: 0 10px;'> <i class="fa fa-comments fa-fw" style="color: #f5f5f5;"></i> <a href='https://github.com/community-scripts/ProxmoxVE/discussions' target='_blank' rel='noopener noreferrer' style='text-decoration: none; color: #00617f;'>Discussions</a> </span> <span style='margin: 0 10px;'> <i class="fa fa-exclamation-circle fa-fw" style="color: #f5f5f5;"></i> <a href='https://github.com/community-scripts/ProxmoxVE/issues' target='_blank' rel='noopener noreferrer' style='text-decoration: none; color: #00617f;'>Issues</a> </span> </div> EOF ) qm set $VMID -description "$DESCRIPTION" >/dev/null msg_ok "Created OpenWrt VM ${CL}${BL}(${HN})" msg_info "OpenWrt is being started in order to configure the network interfaces." qm start $VMID sleep 15 msg_info "Waiting for OpenWrt to boot..." for i in {1..30}; do if qm status "$VMID" | grep -q "running"; then sleep 5 msg_ok "OpenWrt is running" break fi sleep 1 done msg_ok "Network interfaces are being configured as OpenWrt initiates." if qm status "$VMID" | grep -q "running"; then send_line_to_vm "" send_line_to_vm "uci delete network.@device[0]" send_line_to_vm "uci set network.wan=interface" send_line_to_vm "uci set network.wan.device=eth1" send_line_to_vm "uci set network.wan.proto=dhcp" send_line_to_vm "uci delete network.lan" send_line_to_vm "uci set network.lan=interface" send_line_to_vm "uci set network.lan.device=eth0" send_line_to_vm "uci set network.lan.proto=static" send_line_to_vm "uci set network.lan.ipaddr=${LAN_IP_ADDR}" send_line_to_vm "uci set network.lan.netmask=${LAN_NETMASK}" send_line_to_vm "uci commit" send_line_to_vm "poweroff" msg_ok "Network interfaces configured in OpenWrt" else msg_error "VM is not running" exit 226 fi msg_info "Waiting for OpenWrt to shut down..." until qm status "$VMID" | grep -q "stopped"; do sleep 2 done msg_ok "OpenWrt has shut down" msg_info "Adding bridge interfaces on Proxmox side" qm set $VMID \ -net0 virtio,bridge=${LAN_BRG},macaddr=${LAN_MAC}${LAN_VLAN}${MTU} \ -net1 virtio,bridge=${BRG},macaddr=${MAC}${VLAN}${MTU} >/dev/null msg_ok "Bridge interfaces added" if [ "$START_VM" = "yes" ]; then msg_info "Starting OpenWrt VM" qm start $VMID msg_ok "Started OpenWrt VM" fi VLAN_FINISH="" if [ -z "$VLAN" ] && [ "$VLAN2" != "999" ]; then VLAN_FINISH=" Please remember to adjust the VLAN tags to suit your network." fi post_update_to_api "done" "none" msg_ok "Completed Successfully!${VLAN_FINISH:+\n$VLAN_FINISH}" ================================================ FILE: vm/opnsense-vm.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: michelroegl-brunner # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE source /dev/stdin <<<$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func) function header_info { clear cat <<"EOF" ____ ____ _ __ / __ \/ __ \/ | / /_______ ____ ________ / / / / /_/ / |/ / ___/ _ \/ __ \/ ___/ _ \ / /_/ / ____/ /| (__ ) __/ / / (__ ) __/ \____/_/ /_/ |_/____/\___/_/ /_/____/\___/ EOF } header_info echo -e "Loading..." #API VARIABLES RANDOM_UUID="$(cat /proc/sys/kernel/random/uuid)" METHOD="" NSAPP="opnsense-vm" var_os="opnsense" var_version="26.1" # GEN_MAC=02:$(openssl rand -hex 5 | awk '{print toupper($0)}' | sed 's/\(..\)/\1:/g; s/.$//') GEN_MAC_LAN=02:$(openssl rand -hex 5 | awk '{print toupper($0)}' | sed 's/\(..\)/\1:/g; s/.$//') YW=$(echo "\033[33m") BL=$(echo "\033[36m") HA=$(echo "\033[1;34m") RD=$(echo "\033[01;31m") BGN=$(echo "\033[4;92m") GN=$(echo "\033[1;92m") DGN=$(echo "\033[32m") CL=$(echo "\033[m") BFR="\\r\\033[K" HOLD="-" CM="${GN}✓${CL}" CROSS="${RD}✗${CL}" set -Eeo pipefail trap 'error_handler $LINENO "$BASH_COMMAND"' ERR trap cleanup EXIT trap 'post_update_to_api "failed" "130"' SIGINT trap 'post_update_to_api "failed" "143"' SIGTERM trap 'post_update_to_api "failed" "129"; exit 129' SIGHUP function error_handler() { local exit_code="$?" local line_number="$1" local command="$2" post_update_to_api "failed" "$exit_code" local error_message="${RD}[ERROR]${CL} in line ${RD}$line_number${CL}: exit code ${RD}$exit_code${CL}: while executing command ${YW}$command${CL}" echo -e "\n$error_message\n" cleanup_vmid } function get_valid_nextid() { local try_id try_id=$(pvesh get /cluster/nextid) while true; do if [ -f "/etc/pve/qemu-server/${try_id}.conf" ] || [ -f "/etc/pve/lxc/${try_id}.conf" ]; then try_id=$((try_id + 1)) continue fi if lvs --noheadings -o lv_name | grep -qE "(^|[-_])${try_id}($|[-_])"; then try_id=$((try_id + 1)) continue fi break done echo "$try_id" } function cleanup_vmid() { if qm status $VMID &>/dev/null; then qm stop $VMID &>/dev/null qm destroy $VMID &>/dev/null fi } function cleanup() { local exit_code=$? popd >/dev/null if [[ "${POST_TO_API_DONE:-}" == "true" && "${POST_UPDATE_DONE:-}" != "true" ]]; then if [[ $exit_code -eq 0 ]]; then post_update_to_api "done" "none" else post_update_to_api "failed" "$exit_code" fi fi rm -rf $TEMP_DIR } function check_disk_space() { local path="$1" local required_gb="$2" local available_kb=$(df -k "$path" | awk 'NR==2 {print $4}') local available_gb=$((available_kb / 1024 / 1024)) if [ $available_gb -lt $required_gb ]; then return 1 fi return 0 } # Use disk-backed temp directory to avoid tmpfs/RAM size limits in /tmp if [ -d "/var/tmp" ] && check_disk_space "/var/tmp" 20; then TEMP_DIR=$(mktemp -d /var/tmp/opnsense-vm.XXXXXX) elif [ -d "/tmp" ] && check_disk_space "/tmp" 20; then TEMP_DIR=$(mktemp -d) else # Fallback: try /var/tmp anyway, disk space check will catch it later TEMP_DIR=$(mktemp -d /var/tmp/opnsense-vm.XXXXXX) fi pushd $TEMP_DIR >/dev/null function send_line_to_vm() { echo -e "${DGN}Sending line: ${YW}$1${CL}" for ((i = 0; i < ${#1}; i++)); do character=${1:i:1} case $character in " ") character="spc" ;; "-") character="minus" ;; "=") character="equal" ;; ",") character="comma" ;; ".") character="dot" ;; "/") character="slash" ;; "'") character="apostrophe" ;; ";") character="semicolon" ;; '\') character="backslash" ;; '`') character="grave_accent" ;; "[") character="bracket_left" ;; "]") character="bracket_right" ;; "_") character="shift-minus" ;; "+") character="shift-equal" ;; "?") character="shift-slash" ;; "<") character="shift-comma" ;; ">") character="shift-dot" ;; '"') character="shift-apostrophe" ;; ":") character="shift-semicolon" ;; "|") character="shift-backslash" ;; "~") character="shift-grave_accent" ;; "{") character="shift-bracket_left" ;; "}") character="shift-bracket_right" ;; "A") character="shift-a" ;; "B") character="shift-b" ;; "C") character="shift-c" ;; "D") character="shift-d" ;; "E") character="shift-e" ;; "F") character="shift-f" ;; "G") character="shift-g" ;; "H") character="shift-h" ;; "I") character="shift-i" ;; "J") character="shift-j" ;; "K") character="shift-k" ;; "L") character="shift-l" ;; "M") character="shift-m" ;; "N") character="shift-n" ;; "O") character="shift-o" ;; "P") character="shift-p" ;; "Q") character="shift-q" ;; "R") character="shift-r" ;; "S") character="shift-s" ;; "T") character="shift-t" ;; "U") character="shift-u" ;; "V") character="shift-v" ;; "W") character="shift-w" ;; "X") character="shift-x" ;; "Y") character="shift-y" ;; "Z") character="shift-z" ;; "!") character="shift-1" ;; "@") character="shift-2" ;; "#") character="shift-3" ;; '$') character="shift-4" ;; "%") character="shift-5" ;; "^") character="shift-6" ;; "&") character="shift-7" ;; "*") character="shift-8" ;; "(") character="shift-9" ;; ")") character="shift-0" ;; esac qm sendkey $VMID "$character" done qm sendkey $VMID ret } if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "OPNsense VM" --yesno "This will create a New OPNsense VM. Proceed?" 10 58); then : else header_info && echo -e "⚠ User exited script \n" && exit fi function msg_info() { local msg="$1" echo -ne " ${HOLD} ${YW}${msg}..." } function msg_ok() { local msg="$1" echo -e "${BFR} ${CM} ${GN}${msg}${CL}" } function msg_error() { local msg="$1" echo -e "${BFR} ${CROSS} ${RD}${msg}${CL}" } # This function checks the version of Proxmox Virtual Environment (PVE) and exits if the version is not supported. # Supported: Proxmox VE 8.0.x – 8.9.x, 9.0 and 9.1 pve_check() { local PVE_VER PVE_VER="$(pveversion | awk -F'/' '{print $2}' | awk -F'-' '{print $1}')" # Check for Proxmox VE 8.x: allow 8.0–8.9 if [[ "$PVE_VER" =~ ^8\.([0-9]+) ]]; then local MINOR="${BASH_REMATCH[1]}" if ((MINOR < 0 || MINOR > 9)); then msg_error "This version of Proxmox VE is not supported." msg_error "Supported: Proxmox VE version 8.0 – 8.9" exit 105 fi return 0 fi # Check for Proxmox VE 9.x: allow 9.0 and 9.1 if [[ "$PVE_VER" =~ ^9\.([0-9]+) ]]; then local MINOR="${BASH_REMATCH[1]}" if ((MINOR < 0 || MINOR > 1)); then msg_error "This version of Proxmox VE is not supported." msg_error "Supported: Proxmox VE version 9.0 – 9.1" exit 105 fi return 0 fi # All other unsupported versions msg_error "This version of Proxmox VE is not supported." msg_error "Supported versions: Proxmox VE 8.0 – 8.x or 9.0 – 9.1" exit 105 } function arch_check() { if [ "$(dpkg --print-architecture)" != "amd64" ]; then echo -e "\n ${CROSS} This script will not work with PiMox! \n" echo -e "Exiting..." sleep 2 exit fi } function ssh_check() { if command -v pveversion >/dev/null 2>&1; then if [ -n "${SSH_CLIENT:+x}" ]; then if whiptail --backtitle "Proxmox VE Helper Scripts" --defaultno --title "SSH DETECTED" --yesno "It's suggested to use the Proxmox shell instead of SSH, since SSH can create issues while gathering variables. Would you like to proceed with using SSH?" 10 62; then echo "you've been warned" else clear exit fi fi fi } function exit-script() { clear echo -e "⚠ User exited script \n" exit } function get_available_bridges() { ip -o link show type bridge 2>/dev/null | awk -F': ' '{print $2}' | sort } function default_settings() { VMID=$(get_valid_nextid) FORMAT=",efitype=4m" MACHINE="" DISK_CACHE="" HN="opnsense" CPU_TYPE="" CORE_COUNT="4" RAM_SIZE="8192" BRG="vmbr0" IP_ADDR="" WAN_IP_ADDR="" LAN_GW="" WAN_GW="" NETMASK="" WAN_NETMASK="" VLAN="" MAC=$GEN_MAC WAN_MAC=$GEN_MAC_LAN WAN_BRG="" MTU="" START_VM="yes" METHOD="default" # Detect available bridges local AVAILABLE_BRIDGES AVAILABLE_BRIDGES=$(get_available_bridges) local BRIDGE_COUNT BRIDGE_COUNT=$(echo "$AVAILABLE_BRIDGES" | wc -l) echo -e "${DGN}Using Virtual Machine ID: ${BGN}${VMID}${CL}" echo -e "${DGN}Using Hostname: ${BGN}${HN}${CL}" echo -e "${DGN}Allocated Cores: ${BGN}${CORE_COUNT}${CL}" echo -e "${DGN}Allocated RAM: ${BGN}${RAM_SIZE}${CL}" if ! ip link show "${BRG}" &>/dev/null; then msg_error "Bridge '${BRG}' does not exist" exit else echo -e "${DGN}Using LAN Bridge: ${BGN}${BRG}${CL}" fi echo -e "${DGN}Using LAN VLAN: ${BGN}Default${CL}" echo -e "${DGN}Using LAN MAC Address: ${BGN}${MAC}${CL}" # Determine available network modes based on bridge count local DEFAULT_WAN_BRG DEFAULT_WAN_BRG=$(echo "$AVAILABLE_BRIDGES" | grep -v "^${BRG}$" | head -n1 || true) if [ "$BRIDGE_COUNT" -ge 2 ]; then # Multiple bridges available - offer dual or single mode if NETWORK_MODE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "NETWORK CONFIGURATION" --radiolist --cancel-button Exit-Script \ "Choose network setup mode for OPNsense:\n" 14 70 2 \ "dual" "Dual Interface (Firewall/Router) - uses ${DEFAULT_WAN_BRG}" ON \ "single" "Single Interface (Proxy/VPN/IDS Server)" OFF \ 3>&1 1>&2 2>&3); then if [ "$NETWORK_MODE" = "dual" ]; then WAN_BRG="$DEFAULT_WAN_BRG" echo -e "${DGN}Network Mode: ${BGN}Dual Interface (Firewall)${CL}" echo -e "${DGN}Using WAN Bridge: ${BGN}${WAN_BRG}${CL}" echo -e "${DGN}Using WAN MAC Address: ${BGN}${WAN_MAC}${CL}" else echo -e "${DGN}Network Mode: ${BGN}Single Interface (Proxy/VPN/IDS)${CL}" WAN_BRG="" fi else exit-script fi else # Only one bridge available - single interface mode only echo -e "${DGN}Network Mode: ${BGN}Single Interface (Proxy/VPN/IDS)${CL}" echo -e "${YW} (Only one bridge detected, dual interface requires a second bridge)${CL}" WAN_BRG="" fi echo -e "${DGN}Using Interface MTU Size: ${BGN}Default${CL}" echo -e "${DGN}Start VM when completed: ${BGN}yes${CL}" echo -e "${BL}Creating a OPNsense VM using the above default settings${CL}" } function advanced_settings() { local ip_regex='^([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})$' METHOD="advanced" [ -z "${VMID:-}" ] && VMID=$(get_valid_nextid) while true; do if VMID=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Virtual Machine ID" 8 58 $VMID --title "VIRTUAL MACHINE ID" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z "$VMID" ]; then VMID=$(get_valid_nextid) fi if pct status "$VMID" &>/dev/null || qm status "$VMID" &>/dev/null; then echo -e "${CROSS}${RD} ID $VMID is already in use${CL}" sleep 2 continue fi echo -e "${DGN}Virtual Machine ID: ${BGN}$VMID${CL}" break else exit-script fi done if MACH=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "MACHINE TYPE" --radiolist --cancel-button Exit-Script "Choose Type" 10 58 2 \ "i440fx" "Machine i440fx" ON \ "q35" "Machine q35" OFF \ 3>&1 1>&2 2>&3); then if [ $MACH = q35 ]; then echo -e "${DGN}Using Machine Type: ${BGN}$MACH${CL}" FORMAT="" MACHINE=" -machine q35" else echo -e "${DGN}Using Machine Type: ${BGN}$MACH${CL}" FORMAT=",efitype=4m" MACHINE="" fi else exit-script fi if CPU_TYPE1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "CPU MODEL" --radiolist "Choose" --cancel-button Exit-Script 10 58 2 \ "0" "KVM64 (Default)" ON \ "1" "Host" OFF \ 3>&1 1>&2 2>&3); then if [ $CPU_TYPE1 = "1" ]; then echo -e "${DGN}Using CPU Model: ${BGN}Host${CL}" CPU_TYPE=" -cpu host" else echo -e "${DGN}Using CPU Model: ${BGN}KVM64${CL}" CPU_TYPE="" fi else exit-script fi if DISK_CACHE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "DISK CACHE" --radiolist "Choose" --cancel-button Exit-Script 10 58 2 \ "0" "None (Default)" ON \ "1" "Write Through" OFF \ 3>&1 1>&2 2>&3); then if [ $DISK_CACHE = "1" ]; then echo -e "${DGN}Using Disk Cache: ${BGN}Write Through${CL}" DISK_CACHE="cache=writethrough," else echo -e "${DGN}Using Disk Cache: ${BGN}None${CL}" DISK_CACHE="" fi else exit-script fi if VM_NAME=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Hostname" 8 58 OPNsense --title "HOSTNAME" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z "$VM_NAME" ]; then HN="OPNsense" else HN=$(echo "${VM_NAME,,}" | tr -cs 'a-z0-9-' '-' | sed 's/^-//;s/-$//') if [ "$HN" != "${VM_NAME,,}" ]; then whiptail --backtitle "Proxmox VE Helper Scripts" --title "HOSTNAME ADJUSTED" --msgbox "Invalid characters detected. Hostname has been adjusted to:\n\n $HN" 10 58 fi fi echo -e "${DGN}Using Hostname: ${BGN}$HN${CL}" else exit-script fi while true; do if CORE_COUNT=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Allocate CPU Cores" 8 58 4 --title "CORE COUNT" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z "$CORE_COUNT" ]; then CORE_COUNT="4"; fi if [[ "$CORE_COUNT" =~ ^[1-9][0-9]*$ ]]; then echo -e "${DGN}Allocated Cores: ${BGN}$CORE_COUNT${CL}" break fi whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "CPU Cores must be a positive integer (e.g., 4)." 8 58 else exit-script fi done while true; do if RAM_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Allocate RAM in MiB" 8 58 8192 --title "RAM" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z "$RAM_SIZE" ]; then RAM_SIZE="8192"; fi if [[ "$RAM_SIZE" =~ ^[1-9][0-9]*$ ]]; then echo -e "${DGN}Allocated RAM: ${BGN}$RAM_SIZE${CL}" break fi whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "RAM Size must be a positive integer in MiB (e.g., 8192)." 8 58 else exit-script fi done if BRG=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a LAN Bridge" 8 58 vmbr0 --title "LAN BRIDGE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z $BRG ]; then BRG="vmbr0" fi if ! ip link show "${BRG}" &>/dev/null; then msg_error "Bridge '${BRG}' does not exist" exit fi echo -e "${DGN}Using LAN Bridge: ${BGN}$BRG${CL}" else exit-script fi if IP_ADDR=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a LAN IP" 8 58 $IP_ADDR --title "LAN IP ADDRESS" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z $IP_ADDR ]; then echo -e "${DGN}Using DHCP AS LAN IP ADDRESS${CL}" else if [[ -n "$IP_ADDR" && ! "$IP_ADDR" =~ $ip_regex ]]; then msg_error "Invalid IP Address format for LAN IP. Needs to be 0.0.0.0, was $IP_ADDR" exit fi echo -e "${DGN}Using LAN IP ADDRESS: ${BGN}$IP_ADDR${CL}" if LAN_GW=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a LAN GATEWAY IP" 8 58 $LAN_GW --title "LAN GATEWAY IP ADDRESS" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z $LAN_GW ]; then echo -e "${DGN}Gateway needs to be set if ip is not dhcp${CL}" exit-script fi if [[ -n "$LAN_GW" && ! "$LAN_GW" =~ $ip_regex ]]; then msg_error "Invalid IP Address format for Gateway. Needs to be 0.0.0.0, was $LAN_GW" exit fi echo -e "${DGN}Using LAN GATEWAY ADDRESS: ${BGN}$LAN_GW${CL}" fi if NETMASK=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a LAN netmmask (24 for example)" 8 58 $NETMASK --title "LAN NETMASK" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z $NETMASK ]; then echo -e "${DGN}Netmask needs to be set if ip is not dhcp${CL}" fi if [[ -n "$NETMASK" && ! ("$NETMASK" =~ ^[0-9]+$ && "$NETMASK" -ge 1 && "$NETMASK" -le 32) ]]; then msg_error "Invalid LAN NETMASK format. Needs to be 1-32, was $NETMASK" exit fi echo -e "${DGN}Using LAN NETMASK: ${BGN}$NETMASK${CL}" else exit-script fi fi else exit-script fi # Build WAN bridge selection from available bridges (excluding LAN bridge) local WAN_BRIDGES WAN_BRIDGES=$(get_available_bridges | grep -v "^${BRG}$" || true) if [ -z "$WAN_BRIDGES" ]; then msg_error "No additional bridge available for WAN. Only '${BRG}' exists." msg_error "Create a second bridge (e.g. vmbr1) in Proxmox network config first." exit fi local WAN_MENU=() local first=true while IFS= read -r brg; do if $first; then WAN_MENU+=("$brg" "" "ON") first=false else WAN_MENU+=("$brg" "" "OFF") fi done <<<"$WAN_BRIDGES" if WAN_BRG=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "WAN BRIDGE" --radiolist "Select WAN Bridge" 14 58 6 \ "${WAN_MENU[@]}" 3>&1 1>&2 2>&3); then if [ -z "$WAN_BRG" ]; then WAN_BRG=$(echo "$WAN_BRIDGES" | head -n1) fi echo -e "${DGN}Using WAN Bridge: ${BGN}$WAN_BRG${CL}" else exit-script fi if WAN_IP_ADDR=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a WAN IP" 8 58 $WAN_IP_ADDR --title "WAN IP ADDRESS" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z $WAN_IP_ADDR ]; then echo -e "${DGN}Using DHCP AS WAN IP ADDRESS${CL}" else if [[ -n "$WAN_IP_ADDR" && ! "$WAN_IP_ADDR" =~ $ip_regex ]]; then msg_error "Invalid IP Address format for WAN IP. Needs to be 0.0.0.0, was $WAN_IP_ADDR" exit fi echo -e "${DGN}Using WAN IP ADDRESS: ${BGN}$WAN_IP_ADDR${CL}" if WAN_GW=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a WAN GATEWAY IP" 8 58 $WAN_GW --title "WAN GATEWAY IP ADDRESS" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z $WAN_GW ]; then echo -e "${DGN}Gateway needs to be set if ip is not dhcp${CL}" exit-script fi if [[ -n "$WAN_GW" && ! "$WAN_GW" =~ $ip_regex ]]; then msg_error "Invalid IP Address format for WAN Gateway. Needs to be 0.0.0.0, was $WAN_GW" exit fi echo -e "${DGN}Using WAN GATEWAY ADDRESS: ${BGN}$WAN_GW${CL}" else exit-script fi if WAN_NETMASK=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a WAN netmmask (24 for example)" 8 58 $WAN_NETMASK --title "WAN NETMASK" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z $WAN_NETMASK ]; then echo -e "${DGN}WAN Netmask needs to be set if ip is not dhcp${CL}" fi if [[ -n "$WAN_NETMASK" && ! ("$WAN_NETMASK" =~ ^[0-9]+$ && "$WAN_NETMASK" -ge 1 && "$WAN_NETMASK" -le 32) ]]; then msg_error "Invalid WAN NETMASK format. Needs to be 1-32, was $WAN_NETMASK" exit fi echo -e "${DGN}Using WAN NETMASK: ${BGN}$WAN_NETMASK${CL}" else exit-script fi fi else exit-script fi if MAC1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a WAN MAC Address" 8 58 $GEN_MAC --title "WAN MAC ADDRESS" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z $MAC1 ]; then MAC="$GEN_MAC" else MAC="$MAC1" fi echo -e "${DGN}Using LAN MAC Address: ${BGN}$MAC${CL}" else exit-script fi if MAC2=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a LAN MAC Address" 8 58 $GEN_MAC_LAN --title "LAN MAC ADDRESS" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z $MAC2 ]; then WAN_MAC="$GEN_MAC_LAN" else WAN_MAC="$MAC2" fi echo -e "${DGN}Using WAN MAC Address: ${BGN}$WAN_MAC${CL}" else exit-script fi if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "ADVANCED SETTINGS COMPLETE" --yesno "Ready to create OPNsense VM?" --no-button Do-Over 10 58); then echo -e "${RD}Creating a OPNsense VM using the above advanced settings${CL}" else header_info echo -e "${RD}Using Advanced Settings${CL}" advanced_settings fi } function start_script() { if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "SETTINGS" --yesno "Use Default Settings?" --no-button Advanced 10 58); then header_info echo -e "${BL}Using Default Settings${CL}" default_settings else header_info echo -e "${RD}Using Advanced Settings${CL}" advanced_settings fi } arch_check pve_check ssh_check start_script post_to_api_vm msg_info "Validating Storage" while read -r line; do TAG=$(echo $line | awk '{print $1}') TYPE=$(echo $line | awk '{printf "%-10s", $2}') FREE=$(echo $line | numfmt --field 4-6 --from-unit=K --to=iec --format %.2f | awk '{printf( "%9sB", $6)}') ITEM=" Type: $TYPE Free: $FREE " OFFSET=2 if [[ $((${#ITEM} + $OFFSET)) -gt ${MSG_MAX_LENGTH:-} ]]; then MSG_MAX_LENGTH=$((${#ITEM} + $OFFSET)) fi STORAGE_MENU+=("$TAG" "$ITEM" "OFF") done < <(pvesm status -content images | awk 'NR>1') VALID=$(pvesm status -content images | awk 'NR>1') if [ -z "$VALID" ]; then msg_error "Unable to detect a valid storage location." exit elif [ $((${#STORAGE_MENU[@]} / 3)) -eq 1 ]; then STORAGE=${STORAGE_MENU[0]} else while [ -z "${STORAGE:+x}" ]; do STORAGE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "Storage Pools" --radiolist \ "Which storage pool would you like to use for ${HN}?\nTo make a selection, use the Spacebar.\n" \ 16 $(($MSG_MAX_LENGTH + 23)) 6 \ "${STORAGE_MENU[@]}" 3>&1 1>&2 2>&3) done fi msg_ok "Using ${CL}${BL}$STORAGE${CL} ${GN}for Storage Location." msg_ok "Virtual Machine ID is ${CL}${BL}$VMID${CL}." msg_info "Retrieving the URL for the OPNsense Qcow2 Disk Image" # Use latest stable FreeBSD amd64 qcow2 VM image (generic, not UFS/ZFS) RELEASE_LIST="$(curl -s https://download.freebsd.org/releases/VM-IMAGES/ | grep -Eo '[0-9]+\.[0-9]+-RELEASE' | sort -Vr | uniq)" URL="" FREEBSD_VER="" for ver in $RELEASE_LIST; do candidate="https://download.freebsd.org/releases/VM-IMAGES/${ver}/amd64/Latest/FreeBSD-${ver}-amd64.qcow2.xz" if curl -fsI "$candidate" >/dev/null 2>&1; then FREEBSD_VER="$ver" URL="$candidate" break fi done if [ -z "$URL" ]; then msg_error "Could not find generic FreeBSD amd64 qcow2 image (non-UFS/ZFS)." exit 115 fi msg_ok "Download URL: ${CL}${BL}${URL}${CL}" # Check available disk space (require at least 20GB for safety) if ! check_disk_space "$TEMP_DIR" 20; then AVAILABLE_GB=$(df -h "$TEMP_DIR" | awk 'NR==2 {print $4}') msg_error "Insufficient disk space in temporary directory ($TEMP_DIR)." msg_error "Available: ${AVAILABLE_GB}, Required: ~20GB for FreeBSD image decompression." msg_error "Please free up space or ensure /tmp has sufficient storage." exit 214 fi msg_info "Downloading FreeBSD Image" curl -f#SL -o "$(basename "$URL")" "$URL" echo -en "\e[1A\e[0K" msg_ok "Downloaded ${CL}${BL}$(basename "$URL")${CL}" # Check disk space again before decompression if ! check_disk_space "$TEMP_DIR" 15; then AVAILABLE_GB=$(df -h "$TEMP_DIR" | awk 'NR==2 {print $4}') msg_error "Insufficient disk space for decompression." msg_error "Available: ${AVAILABLE_GB}, Required: ~15GB for decompressed image." exit 214 fi msg_info "Decompressing FreeBSD Image (this may take a few minutes)" FILE=FreeBSD.qcow2 if ! unxz -cv $(basename $URL) >${FILE}; then msg_error "Failed to decompress FreeBSD image." msg_error "This is usually caused by insufficient disk space." df -h "$TEMP_DIR" exit 115 fi # Remove the compressed file to save space rm -f "$(basename "$URL")" msg_ok "Decompressed ${CL}${BL}${FILE}${CL}" STORAGE_TYPE=$(pvesm status -storage $STORAGE | awk 'NR>1 {print $2}') case $STORAGE_TYPE in nfs | dir) DISK_EXT=".qcow2" DISK_REF="$VMID/" DISK_IMPORT="-format qcow2" THIN="" ;; btrfs) DISK_EXT=".raw" DISK_REF="$VMID/" DISK_IMPORT="-format raw" FORMAT=",efitype=4m" THIN="" ;; *) DISK_EXT="" DISK_REF="" DISK_IMPORT="-format raw" ;; esac for i in {0,1}; do disk="DISK$i" eval DISK${i}=vm-${VMID}-disk-${i}${DISK_EXT:-} eval DISK${i}_REF=${STORAGE}:${DISK_REF:-}${!disk} done msg_info "Creating a OPNsense VM" qm create $VMID -agent 1${MACHINE} -tablet 0 -localtime 1 -bios ovmf${CPU_TYPE} -cores $CORE_COUNT -memory $RAM_SIZE \ -name $HN -tags community-script -net0 virtio,bridge=$BRG,macaddr=$MAC$VLAN$MTU -onboot 1 -ostype l26 -scsihw virtio-scsi-pci # Retry pvesm alloc on transient zfs_request "got timeout" errors (#14127) alloc_attempt=1 alloc_max=4 alloc_delay=5 while :; do alloc_err=$(pvesm alloc $STORAGE $VMID $DISK0 4M 2>&1 >/dev/null) && break if [[ "$alloc_err" == *"got timeout"* && $alloc_attempt -lt $alloc_max ]]; then msg_warn "pvesm alloc hit zfs timeout (attempt $alloc_attempt/$alloc_max), retrying in ${alloc_delay}s..." pvesm free "${DISK0_REF}" &>/dev/null || true sleep "$alloc_delay" alloc_attempt=$((alloc_attempt + 1)) alloc_delay=$((alloc_delay * 2)) continue fi echo -e "$alloc_err" >&2 exit 220 done qm importdisk $VMID ${FILE} $STORAGE ${DISK_IMPORT:-} &>/dev/null qm set $VMID \ -efidisk0 ${DISK0_REF}${FORMAT} \ -scsi0 ${DISK1_REF},${DISK_CACHE}${THIN}size=2G \ -boot order=scsi0 \ -serial0 socket \ -tags community-script >/dev/null qm resize $VMID scsi0 20G >/dev/null DESCRIPTION=$( cat <<EOF <div align='center'> <a href='https://community-scripts.org' target='_blank' rel='noopener noreferrer'> <img src='https://raw.githubusercontent.com/michelroegl-brunner/ProxmoxVE/refs/heads/develop/misc/images/logo-81x112.png' alt='Logo' style='width:81px;height:112px;'/> </a> <h2 style='font-size: 24px; margin: 20px 0;'>OPNsense VM</h2> <p style='margin: 16px 0;'> <a href='https://ko-fi.com/community_scripts' target='_blank' rel='noopener noreferrer'> <img src='https://img.shields.io/badge/☕-Buy us a coffee-blue' alt='spend Coffee' /> </a> </p> <span style='margin: 0 10px;'> <i class="fa fa-github fa-fw" style="color: #f5f5f5;"></i> <a href='https://github.com/community-scripts/ProxmoxVE' target='_blank' rel='noopener noreferrer' style='text-decoration: none; color: #00617f;'>GitHub</a> </span> <span style='margin: 0 10px;'> <i class="fa fa-comments fa-fw" style="color: #f5f5f5;"></i> <a href='https://github.com/community-scripts/ProxmoxVE/discussions' target='_blank' rel='noopener noreferrer' style='text-decoration: none; color: #00617f;'>Discussions</a> </span> <span style='margin: 0 10px;'> <i class="fa fa-exclamation-circle fa-fw" style="color: #f5f5f5;"></i> <a href='https://github.com/community-scripts/ProxmoxVE/issues' target='_blank' rel='noopener noreferrer' style='text-decoration: none; color: #00617f;'>Issues</a> </span> </div> EOF ) qm set $VMID -description "$DESCRIPTION" >/dev/null msg_info "Bridge interfaces are being added." qm set $VMID \ -net0 virtio,bridge=${BRG},macaddr=${MAC}${VLAN}${MTU} 2>/dev/null msg_ok "Bridge interfaces have been successfully added." msg_ok "Created a OPNsense VM ${CL}${BL}(${HN})" msg_ok "Starting OPNsense VM (Patience this takes 20-30 minutes)" qm start $VMID sleep 90 send_line_to_vm "root" send_line_to_vm "fetch https://raw.githubusercontent.com/opnsense/update/master/src/bootstrap/opnsense-bootstrap.sh.in" if [ -n "$WAN_BRG" ]; then msg_info "Adding WAN interface" qm set $VMID \ -net1 virtio,bridge=${WAN_BRG},macaddr=${WAN_MAC} &>/dev/null msg_ok "WAN interface added" sleep 5 # Brief pause after adding network interface fi send_line_to_vm "sh ./opnsense-bootstrap.sh.in -y -f -r 26.1" msg_ok "OPNsense VM is being installed, do not close the terminal, or the installation will fail." #We need to wait for the OPNsense build proccess to finish, this takes a few minutes sleep 1000 send_line_to_vm "root" send_line_to_vm "opnsense" send_line_to_vm "2" if [ "$IP_ADDR" != "" ]; then send_line_to_vm "1" send_line_to_vm "n" send_line_to_vm "${IP_ADDR}" send_line_to_vm "${NETMASK}" send_line_to_vm "${LAN_GW}" send_line_to_vm "n" send_line_to_vm " " send_line_to_vm "n" send_line_to_vm "n" send_line_to_vm " " send_line_to_vm "n" send_line_to_vm "n" send_line_to_vm "n" send_line_to_vm "n" send_line_to_vm "n" else send_line_to_vm "1" send_line_to_vm "y" send_line_to_vm "n" send_line_to_vm "n" send_line_to_vm " " send_line_to_vm "n" send_line_to_vm "n" send_line_to_vm "n" fi #Wait for config changes to be saved sleep 20 if [ -n "$WAN_BRG" ] && [ "$WAN_IP_ADDR" != "" ]; then send_line_to_vm "2" send_line_to_vm "2" send_line_to_vm "n" send_line_to_vm "${WAN_IP_ADDR}" send_line_to_vm "${NETMASK}" send_line_to_vm "${LAN_GW}" send_line_to_vm "n" send_line_to_vm " " send_line_to_vm "n" send_line_to_vm " " send_line_to_vm "n" send_line_to_vm "n" send_line_to_vm "n" fi sleep 10 send_line_to_vm "0" msg_ok "Started OPNsense VM" msg_ok "Completed successfully!\n" if [ "$IP_ADDR" != "" ]; then echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP_ADDR}${CL}" else echo -e "${INFO}${YW} LAN IP was DHCP.${CL}" echo -e "${INFO}${BGN}To find the IP login to the VM shell${CL}" fi ================================================ FILE: vm/owncloud-vm.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT # https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE source /dev/stdin <<<$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func) function header_info { clear cat <<"EOF" ______ __ __ _______ __ _ ____ ___ /_ __/_ _________ / //_/__ __ __ ___ _ _____ / ___/ /__ __ _____/ / | | / / |/ / / / / // / __/ _ \/ ,< / -_) // / / _ \ |/|/ / _ \/ /__/ / _ \/ // / _ / | |/ / /|_/ / /_/ \_,_/_/ /_//_/_/|_|\__/\_, / \___/__,__/_//_/\___/_/\___/\_,_/\_,_/ |___/_/ /_/ /___/ EOF } header_info echo -e "\n Loading..." GEN_MAC=02:$(openssl rand -hex 5 | awk '{print toupper($0)}' | sed 's/\(..\)/\1:/g; s/.$//') RANDOM_UUID="$(cat /proc/sys/kernel/random/uuid)" METHOD="" NSAPP="owncloud-vm" var_os="owncloud" var_version="18.0" APP="TurnKey ownCloud VM" YW=$(echo "\033[33m") BL=$(echo "\033[36m") RD=$(echo "\033[01;31m") BGN=$(echo "\033[4;92m") GN=$(echo "\033[1;92m") DGN=$(echo "\033[32m") CL=$(echo "\033[m") CL=$(echo "\033[m") BOLD=$(echo "\033[1m") BFR="\\r\\033[K" HOLD=" " TAB=" " CM="${TAB}✔️${TAB}${CL}" CROSS="${TAB}✖️${TAB}${CL}" INFO="${TAB}💡${TAB}${CL}" OS="${TAB}🖥️${TAB}${CL}" CONTAINERTYPE="${TAB}📦${TAB}${CL}" DISKSIZE="${TAB}💾${TAB}${CL}" CPUCORE="${TAB}🧠${TAB}${CL}" RAMSIZE="${TAB}🛠️${TAB}${CL}" CONTAINERID="${TAB}🆔${TAB}${CL}" HOSTNAME="${TAB}🏠${TAB}${CL}" BRIDGE="${TAB}🌉${TAB}${CL}" GATEWAY="${TAB}🌐${TAB}${CL}" DEFAULT="${TAB}⚙️${TAB}${CL}" MACADDRESS="${TAB}🔗${TAB}${CL}" VLANTAG="${TAB}🏷️${TAB}${CL}" CREATING="${TAB}🚀${TAB}${CL}" ADVANCED="${TAB}🧩${TAB}${CL}" CLOUD="${TAB}☁️${TAB}${CL}" THIN="discard=on,ssd=1," set -e trap 'error_handler $LINENO "$BASH_COMMAND"' ERR trap cleanup EXIT trap 'post_update_to_api "failed" "130"' SIGINT trap 'post_update_to_api "failed" "143"' SIGTERM trap 'post_update_to_api "failed" "129"; exit 129' SIGHUP function error_handler() { local exit_code="$?" local line_number="$1" local command="$2" local error_message="${RD}[ERROR]${CL} in line ${RD}$line_number${CL}: exit code ${RD}$exit_code${CL}: while executing command ${YW}$command${CL}" post_update_to_api "failed" "${exit_code}" echo -e "\n$error_message\n" cleanup_vmid } function get_valid_nextid() { local try_id try_id=$(pvesh get /cluster/nextid) while true; do if [ -f "/etc/pve/qemu-server/${try_id}.conf" ] || [ -f "/etc/pve/lxc/${try_id}.conf" ]; then try_id=$((try_id + 1)) continue fi if lvs --noheadings -o lv_name | grep -qE "(^|[-_])${try_id}($|[-_])"; then try_id=$((try_id + 1)) continue fi break done echo "$try_id" } function cleanup_vmid() { if qm status $VMID &>/dev/null; then qm stop $VMID &>/dev/null qm destroy $VMID &>/dev/null fi } function cleanup() { local exit_code=$? popd >/dev/null if [[ "${POST_TO_API_DONE:-}" == "true" && "${POST_UPDATE_DONE:-}" != "true" ]]; then if [[ $exit_code -eq 0 ]]; then post_update_to_api "done" "none" else post_update_to_api "failed" "$exit_code" fi fi rm -rf $TEMP_DIR } TEMP_DIR=$(mktemp -d) pushd $TEMP_DIR >/dev/null if whiptail --backtitle "Proxmox VE Helper Scripts" --title "Owncloud VM" --yesno "This will create a New Owncloud VM. Proceed?" 10 58; then : else header_info && echo -e "${CROSS}${RD}User exited script${CL}\n" && exit fi function msg_info() { local msg="$1" echo -ne "${TAB}${YW}${HOLD}${msg}${HOLD}" } function msg_ok() { local msg="$1" echo -e "${BFR}${CM}${GN}${msg}${CL}" } function msg_error() { local msg="$1" echo -e "${BFR}${CROSS}${RD}${msg}${CL}" } function check_root() { if [[ "$(id -u)" -ne 0 || $(ps -o comm= -p $PPID) == "sudo" ]]; then clear msg_error "Please run this script as root." echo -e "\nExiting..." sleep 2 exit fi } # This function checks the version of Proxmox Virtual Environment (PVE) and exits if the version is not supported. # Supported: Proxmox VE 8.0.x – 8.9.x, 9.0 and 9.1 pve_check() { local PVE_VER PVE_VER="$(pveversion | awk -F'/' '{print $2}' | awk -F'-' '{print $1}')" # Check for Proxmox VE 8.x: allow 8.0–8.9 if [[ "$PVE_VER" =~ ^8\.([0-9]+) ]]; then local MINOR="${BASH_REMATCH[1]}" if ((MINOR < 0 || MINOR > 9)); then msg_error "This version of Proxmox VE is not supported." msg_error "Supported: Proxmox VE version 8.0 – 8.9" exit 105 fi return 0 fi # Check for Proxmox VE 9.x: allow 9.0 and 9.1 if [[ "$PVE_VER" =~ ^9\.([0-9]+) ]]; then local MINOR="${BASH_REMATCH[1]}" if ((MINOR < 0 || MINOR > 1)); then msg_error "This version of Proxmox VE is not supported." msg_error "Supported: Proxmox VE version 9.0 – 9.1" exit 105 fi return 0 fi # All other unsupported versions msg_error "This version of Proxmox VE is not supported." msg_error "Supported versions: Proxmox VE 8.0 – 8.x or 9.0 – 9.1" exit 105 } function arch_check() { if [ "$(dpkg --print-architecture)" != "amd64" ]; then echo -e "\n ${INFO}${YWB}This script will not work with PiMox! \n" echo -e "\n ${YWB}Visit https://github.com/asylumexp/Proxmox for ARM64 support. \n" echo -e "Exiting..." sleep 2 exit fi } function ssh_check() { if command -v pveversion >/dev/null 2>&1; then if [ -n "${SSH_CLIENT:+x}" ]; then if whiptail --backtitle "Proxmox VE Helper Scripts" --defaultno --title "SSH DETECTED" --yesno "It's suggested to use the Proxmox shell instead of SSH, since SSH can create issues while gathering variables. Would you like to proceed with using SSH?" 10 62; then echo "you've been warned" else clear exit fi fi fi } function exit-script() { clear echo -e "\n${CROSS}${RD}User exited script${CL}\n" exit } function default_settings() { VMID=$(get_valid_nextid) FORMAT=",efitype=4m" MACHINE="" DISK_SIZE="10G" DISK_CACHE="" HN="owncloud-vm" CPU_TYPE="" CORE_COUNT="2" RAM_SIZE="2048" BRG="vmbr0" MAC="$GEN_MAC" VLAN="" MTU="" START_VM="yes" METHOD="default" echo -e "${CONTAINERID}${BOLD}${DGN}Virtual Machine ID: ${BGN}${VMID}${CL}" echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}i440fx${CL}" echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}${DISK_SIZE}${CL}" echo -e "${DISKSIZE}${BOLD}${DGN}Disk Cache: ${BGN}None${CL}" echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}${HN}${CL}" echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}KVM64${CL}" echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}${CORE_COUNT}${CL}" echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}${RAM_SIZE}${CL}" echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}${BRG}${CL}" echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}${MAC}${CL}" echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}Default${CL}" echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}Default${CL}" echo -e "${GATEWAY}${BOLD}${DGN}Start VM when completed: ${BGN}yes${CL}" echo -e "${CREATING}${BOLD}${DGN}Creating a Owncloud VM using the above default settings${CL}" } function advanced_settings() { METHOD="advanced" [ -z "${VMID:-}" ] && VMID=$(get_valid_nextid) while true; do if VMID=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Virtual Machine ID" 8 58 $VMID --title "VIRTUAL MACHINE ID" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z "$VMID" ]; then VMID=$(get_valid_nextid) fi if pct status "$VMID" &>/dev/null || qm status "$VMID" &>/dev/null; then echo -e "${CROSS}${RD} ID $VMID is already in use${CL}" sleep 2 continue fi echo -e "${CONTAINERID}${BOLD}${DGN}Virtual Machine ID: ${BGN}$VMID${CL}" break else exit-script fi done if MACH=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "MACHINE TYPE" --radiolist --cancel-button Exit-Script "Choose Type" 10 58 2 \ "i440fx" "Machine i440fx" ON \ "q35" "Machine q35" OFF \ 3>&1 1>&2 2>&3); then if [ $MACH = q35 ]; then echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}$MACH${CL}" FORMAT="" MACHINE=" -machine q35" else echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}$MACH${CL}" FORMAT=",efitype=4m" MACHINE="" fi else exit-script fi if DISK_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Disk Size in GiB (e.g., 10, 20)" 8 58 "$DISK_SIZE" --title "DISK SIZE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then DISK_SIZE=$(echo "$DISK_SIZE" | tr -d ' ') if [[ "$DISK_SIZE" =~ ^[0-9]+$ ]]; then DISK_SIZE="${DISK_SIZE}G" echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}$DISK_SIZE${CL}" elif [[ "$DISK_SIZE" =~ ^[0-9]+G$ ]]; then echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}$DISK_SIZE${CL}" else echo -e "${DISKSIZE}${BOLD}${RD}Invalid Disk Size. Please use a number (e.g., 10 or 10G).${CL}" exit-script fi else exit-script fi if DISK_CACHE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "DISK CACHE" --radiolist "Choose" --cancel-button Exit-Script 10 58 2 \ "0" "None (Default)" ON \ "1" "Write Through" OFF \ 3>&1 1>&2 2>&3); then if [ $DISK_CACHE = "1" ]; then echo -e "${DISKSIZE}${BOLD}${DGN}Disk Cache: ${BGN}Write Through${CL}" DISK_CACHE="cache=writethrough," else echo -e "${DISKSIZE}${BOLD}${DGN}Disk Cache: ${BGN}None${CL}" DISK_CACHE="" fi else exit-script fi if VM_NAME=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Hostname" 8 58 owncloud-vm --title "HOSTNAME" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z $VM_NAME ]; then HN="owncloud-vm" echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}" else HN=$(echo "${VM_NAME,,}" | tr -cs 'a-z0-9-' '-' | sed 's/^-//;s/-$//') if [ "$HN" != "${VM_NAME,,}" ]; then whiptail --backtitle "Proxmox VE Helper Scripts" --title "HOSTNAME ADJUSTED" --msgbox "Invalid characters detected. Hostname has been adjusted to:\n\n $HN" 10 58 fi echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}" fi else exit-script fi if CPU_TYPE1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "CPU MODEL" --radiolist "Choose" --cancel-button Exit-Script 10 58 2 \ "0" "KVM64 (Default)" ON \ "1" "Host" OFF \ 3>&1 1>&2 2>&3); then if [ $CPU_TYPE1 = "1" ]; then echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}Host${CL}" CPU_TYPE=" -cpu host" else echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}KVM64${CL}" CPU_TYPE="" fi else exit-script fi while true; do if CORE_COUNT=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Allocate CPU Cores" 8 58 2 --title "CORE COUNT" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z "$CORE_COUNT" ]; then CORE_COUNT="2"; fi if [[ "$CORE_COUNT" =~ ^[1-9][0-9]*$ ]]; then echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}$CORE_COUNT${CL}" break fi whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "CPU Cores must be a positive integer (e.g., 2)." 8 58 else exit-script fi done while true; do if RAM_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Allocate RAM in MiB" 8 58 2048 --title "RAM" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z "$RAM_SIZE" ]; then RAM_SIZE="2048"; fi if [[ "$RAM_SIZE" =~ ^[1-9][0-9]*$ ]]; then echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}$RAM_SIZE${CL}" break fi whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "RAM Size must be a positive integer in MiB (e.g., 2048)." 8 58 else exit-script fi done if BRG=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a Bridge" 8 58 vmbr0 --title "BRIDGE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z $BRG ]; then BRG="vmbr0" echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}" else echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}" fi else exit-script fi while true; do if MAC1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a MAC Address" 8 58 $GEN_MAC --title "MAC ADDRESS" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z "$MAC1" ]; then MAC="$GEN_MAC" echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}$MAC${CL}" break fi if [[ "$MAC1" =~ ^([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}$ ]]; then MAC="$MAC1" echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}$MAC1${CL}" break fi whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "Invalid MAC address format. Use XX:XX:XX:XX:XX:XX (e.g., AA:BB:CC:DD:EE:FF)." 8 58 else exit-script fi done while true; do if VLAN1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a Vlan(leave blank for default)" 8 58 --title "VLAN" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z "$VLAN1" ]; then VLAN1="Default" VLAN="" echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}$VLAN1${CL}" break fi if [[ "$VLAN1" =~ ^[0-9]+$ ]] && [ "$VLAN1" -ge 1 ] && [ "$VLAN1" -le 4094 ]; then VLAN=",tag=$VLAN1" echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}$VLAN1${CL}" break fi whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "VLAN must be a number between 1 and 4094, or leave blank for default." 8 58 else exit-script fi done while true; do if MTU1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Interface MTU Size (leave blank for default)" 8 58 --title "MTU SIZE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z "$MTU1" ]; then MTU1="Default" MTU="" echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}$MTU1${CL}" break fi if [[ "$MTU1" =~ ^[0-9]+$ ]] && [ "$MTU1" -ge 576 ] && [ "$MTU1" -le 65520 ]; then MTU=",mtu=$MTU1" echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}$MTU1${CL}" break fi whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "MTU Size must be a number between 576 and 65520, or leave blank for default." 8 58 else exit-script fi done if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "START VIRTUAL MACHINE" --yesno "Start VM when completed?" 10 58); then echo -e "${GATEWAY}${BOLD}${DGN}Start VM when completed: ${BGN}yes${CL}" START_VM="yes" else echo -e "${GATEWAY}${BOLD}${DGN}Start VM when completed: ${BGN}no${CL}" START_VM="no" fi if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "ADVANCED SETTINGS COMPLETE" --yesno "Ready to create a Owncloud VM?" --no-button Do-Over 10 58); then echo -e "${CREATING}${BOLD}${DGN}Creating a Owncloud VM using the above advanced settings${CL}" else header_info echo -e "${ADVANCED}${BOLD}${RD}Using Advanced Settings${CL}" advanced_settings fi } function start_script() { if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "SETTINGS" --yesno "Use Default Settings?" --no-button Advanced 10 58); then header_info echo -e "${DEFAULT}${BOLD}${BL}Using Default Settings${CL}" default_settings else header_info echo -e "${ADVANCED}${BOLD}${RD}Using Advanced Settings${CL}" advanced_settings fi } function start_script() { if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "SETTINGS" --yesno "Use Default Settings?" --no-button Advanced 10 58); then header_info echo -e "${BL}Using Default Settings${CL}" default_settings else header_info echo -e "${RD}Using Advanced Settings${CL}" advanced_settings fi } check_root arch_check pve_check ssh_check start_script post_to_api_vm msg_info "Validating Storage" while read -r line; do TAG=$(echo $line | awk '{print $1}') TYPE=$(echo $line | awk '{printf "%-10s", $2}') FREE=$(echo $line | numfmt --field 4-6 --from-unit=K --to=iec --format %.2f | awk '{printf( "%9sB", $6)}') ITEM=" Type: $TYPE Free: $FREE " OFFSET=2 if [[ $((${#ITEM} + $OFFSET)) -gt ${MSG_MAX_LENGTH:-} ]]; then MSG_MAX_LENGTH=$((${#ITEM} + $OFFSET)) fi STORAGE_MENU+=("$TAG" "$ITEM" "OFF") done < <(pvesm status -content images | awk 'NR>1') VALID=$(pvesm status -content images | awk 'NR>1') if [ -z "$VALID" ]; then msg_error "Unable to detect a valid storage location." exit elif [ $((${#STORAGE_MENU[@]} / 3)) -eq 1 ]; then STORAGE=${STORAGE_MENU[0]} else while [ -z "${STORAGE:+x}" ]; do STORAGE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "Storage Pools" --radiolist \ "Which storage pool would you like to use for ${HN}?\nTo make a selection, use the Spacebar.\n" \ 16 $(($MSG_MAX_LENGTH + 23)) 6 \ "${STORAGE_MENU[@]}" 3>&1 1>&2 2>&3) done fi msg_ok "Using ${CL}${BL}$STORAGE${CL} ${GN}for Storage Location." msg_ok "Virtual Machine ID is ${CL}${BL}$VMID${CL}." msg_info "Retrieving the URL for the $APP Disk Image" URL=http://mirror.turnkeylinux.org/turnkeylinux/images/iso/turnkey-owncloud-18.0-bookworm-amd64.iso sleep 2 msg_ok "${CL}${BL}${URL}${CL}" curl -f#SL -o "$(basename "$URL")" "$URL" echo -en "\e[1A\e[0K" FILE=$(basename $URL) msg_ok "Downloaded ${CL}${BL}${FILE}${CL}" STORAGE_TYPE=$(pvesm status -storage $STORAGE | awk 'NR>1 {print $2}') case $STORAGE_TYPE in nfs | dir) DISK_EXT=".raw" DISK_REF="$VMID/" DISK_IMPORT="-format raw" THIN="" ;; btrfs) DISK_EXT=".raw" DISK_REF="$VMID/" DISK_IMPORT="-format raw" FORMAT=",efitype=4m" THIN="" ;; *) DISK_EXT="" DISK_REF="" DISK_IMPORT="-format raw" ;; esac for i in {0,1,2}; do disk="DISK$i" eval DISK${i}=vm-${VMID}-disk-${i}${DISK_EXT:-} eval DISK${i}_REF=${STORAGE}:${DISK_REF:-}${!disk} done msg_info "Creating a $APP" qm create $VMID -agent 1${MACHINE} -tablet 0 -localtime 1 -bios seabios${CPU_TYPE} -cores $CORE_COUNT -memory $RAM_SIZE \ -name $HN -tags community-script -net0 virtio,bridge=$BRG,macaddr=$MAC$VLAN$MTU -onboot 1 -ostype l26 -scsihw virtio-scsi-pci pvesm alloc $STORAGE $VMID $DISK0 4M 1>&/dev/null pvesm alloc $STORAGE $VMID $DISK1 12G 1>&/dev/null qm importdisk $VMID ${FILE} $STORAGE ${DISK_IMPORT:-} 1>&/dev/null qm set $VMID \ -efidisk0 ${DISK0_REF}${FORMAT} \ -scsi0 ${DISK1_REF},${DISK_CACHE}${THIN} \ -scsi1 ${DISK2_REF},${DISK_CACHE}${THIN} \ -boot order='scsi1;scsi0' >/dev/null DESCRIPTION=$( cat <<EOF <div align='center'> <a href='https://community-scripts.org' target='_blank' rel='noopener noreferrer'> <img src='https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/images/logo-81x112.png' alt='Logo' style='width:81px;height:112px;'/> </a> <h2 style='font-size: 24px; margin: 20px 0;'>Owncloud VM</h2> <p style='margin: 16px 0;'> <a href='https://ko-fi.com/community_scripts' target='_blank' rel='noopener noreferrer'> <img src='https://img.shields.io/badge/☕-Buy us a coffee-blue' alt='spend Coffee' /> </a> </p> <span style='margin: 0 10px;'> <i class="fa fa-github fa-fw" style="color: #f5f5f5;"></i> <a href='https://github.com/community-scripts/ProxmoxVE' target='_blank' rel='noopener noreferrer' style='text-decoration: none; color: #00617f;'>GitHub</a> </span> <span style='margin: 0 10px;'> <i class="fa fa-comments fa-fw" style="color: #f5f5f5;"></i> <a href='https://github.com/community-scripts/ProxmoxVE/discussions' target='_blank' rel='noopener noreferrer' style='text-decoration: none; color: #00617f;'>Discussions</a> </span> <span style='margin: 0 10px;'> <i class="fa fa-exclamation-circle fa-fw" style="color: #f5f5f5;"></i> <a href='https://github.com/community-scripts/ProxmoxVE/issues' target='_blank' rel='noopener noreferrer' style='text-decoration: none; color: #00617f;'>Issues</a> </span> </div> EOF ) qm set $VMID -description "$DESCRIPTION" >/dev/null if [ -n "$DISK_SIZE" ]; then msg_info "Resizing disk to $DISK_SIZE GB" qm resize $VMID scsi0 ${DISK_SIZE} >/dev/null else msg_info "Using default disk size of $DEFAULT_DISK_SIZE GB" qm resize $VMID scsi0 ${DEFAULT_DISK_SIZE} >/dev/null fi msg_ok "Created a $APP ${CL}${BL}(${HN})" if [ "$START_VM" == "yes" ]; then msg_info "Starting $APP" qm start $VMID msg_ok "Started $APP" fi post_update_to_api "done" "none" msg_ok "Completed successfully!\n" ================================================ FILE: vm/pimox-haos-vm.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT # https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE source /dev/stdin <<<$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func) function header_info { cat <<"EOF" ____ _ __ ___ / __ \(_) |/ /___ _ __ / /_/ / / /|_/ / __ \| |/_/ / ____/ / / / / /_/ /> < __ __ /_/_ /_/_/ /_/\____/_/|_| __ ____ _____ / / / /___ ____ ___ ___ / | __________(_)____/ /_____ _____ / /_ / __ \/ ___/ / /_/ / __ \/ __ `__ \/ _ \ / /| | / ___/ ___/ / ___/ __/ __ `/ __ \/ __/ / / / /\__ \ / __ / /_/ / / / / / / __/ / ___ |(__ |__ ) (__ ) /_/ /_/ / / / / /_ / /_/ /___/ / /_/ /_/\____/_/ /_/ /_/\___/ /_/ |_/____/____/_/____/\__/\__,_/_/ /_/\__/ \____//____/ EOF } clear header_info echo -e "Loading..." RANDOM_UUID="$(cat /proc/sys/kernel/random/uuid)" METHOD="" NSAPP="pimox-haos-vm" var_os="pimox-haos" var_version=" " DISK_SIZE="32G" GEN_MAC=$(echo '00 60 2f'$(od -An -N3 -t xC /dev/urandom) | sed -e 's/ /:/g' | tr '[:lower:]' '[:upper:]') USEDID=$(pvesh get /cluster/resources --type vm --output-format yaml | egrep -i 'vmid' | awk '{print substr($2, 1, length($2)-0) }') STABLE=$(curl -fsSL https://raw.githubusercontent.com/home-assistant/version/master/stable.json | grep "ova" | awk '{print substr($2, 2, length($2)-3) }') BETA=$(curl -fsSL https://raw.githubusercontent.com/home-assistant/version/master/beta.json | grep "ova" | awk '{print substr($2, 2, length($2)-3) }') DEV=$(curl -fsSL https://raw.githubusercontent.com/home-assistant/version/master/dev.json | grep "ova" | awk '{print substr($2, 2, length($2)-3) }') YW=$(echo "\033[33m") BL=$(echo "\033[36m") HA=$(echo "\033[1;34m") RD=$(echo "\033[01;31m") BGN=$(echo "\033[4;92m") GN=$(echo "\033[1;92m") DGN=$(echo "\033[32m") CL=$(echo "\033[m") BFR="\\r\\033[K" HOLD="-" CM="${GN}✓${CL}" INFO="" YWB="\033[1;33m" CROSS="${RD}✗${CL}" set -o errexit set -o errtrace set -o nounset set -o pipefail ARCH_CHECK=true DIAGNOSTICS=true shopt -s expand_aliases alias die='EXIT=$? LINE=$LINENO error_exit' trap die ERR trap cleanup EXIT trap 'post_update_to_api "failed" "130"' SIGINT trap 'post_update_to_api "failed" "143"' SIGTERM trap 'post_update_to_api "failed" "129"; exit 129' SIGHUP function error_handler() { local exit_code="$?" local line_number="$1" local command="$2" local error_message="${RD}[ERROR]${CL} in line ${RD}$line_number${CL}: exit code ${RD}$exit_code${CL}: while executing command ${YW}$command${CL}" post_update_to_api "failed" "${exit_code}" echo -e "\n$error_message\n" cleanup_vmid } function get_valid_nextid() { local try_id try_id=$(pvesh get /cluster/nextid) while true; do if [ -f "/etc/pve/qemu-server/${try_id}.conf" ] || [ -f "/etc/pve/lxc/${try_id}.conf" ]; then try_id=$((try_id + 1)) continue fi if lvs --noheadings -o lv_name | grep -qE "(^|[-_])${try_id}($|[-_])"; then try_id=$((try_id + 1)) continue fi break done echo "$try_id" } function cleanup_vmid() { if $(qm status $VMID &>/dev/null); then if [ "$(qm status $VMID | awk '{print $2}')" == "running" ]; then qm stop $VMID fi qm destroy $VMID fi } function cleanup() { local exit_code=$? popd >/dev/null if [[ "${POST_TO_API_DONE:-}" == "true" && "${POST_UPDATE_DONE:-}" != "true" ]]; then if [[ $exit_code -eq 0 ]]; then post_update_to_api "done" "none" else post_update_to_api "failed" "$exit_code" fi fi rm -rf $TEMP_DIR } TEMP_DIR=$(mktemp -d) pushd $TEMP_DIR >/dev/null if ! command -v whiptail &>/dev/null; then echo "Installing whiptail..." apt-get update &>/dev/null apt-get install -y whiptail &>/dev/null fi function msg_info() { local msg="$1" echo -ne "${TAB}${YW}${HOLD}${msg}${HOLD}" } function msg_ok() { local msg="$1" echo -e "${BFR}${CM}${GN}${msg}${CL}" } function msg_error() { local msg="$1" echo -e "${BFR}${CROSS}${RD}${msg}${CL}" } function check_root() { if [[ "$(id -u)" -ne 0 || $(ps -o comm= -p $PPID) == "sudo" ]]; then clear msg_error "Please run this script as root." echo -e "\nExiting..." sleep 2 exit fi } # This function checks the version of Proxmox Virtual Environment (PVE) and exits if the version is not supported. # Supported: Proxmox VE 8.0.x – 8.9.x, 9.0 and 9.1 pve_check() { local PVE_VER PVE_VER="$(pveversion | awk -F'/' '{print $2}' | awk -F'-' '{print $1}')" # Check for Proxmox VE 8.x: allow 8.0–8.9 if [[ "$PVE_VER" =~ ^8\.([0-9]+) ]]; then local MINOR="${BASH_REMATCH[1]}" if ((MINOR < 0 || MINOR > 9)); then msg_error "This version of Proxmox VE is not supported." msg_error "Supported: Proxmox VE version 8.0 – 8.9" exit 105 fi return 0 fi # Check for Proxmox VE 9.x: allow 9.0 and 9.1 if [[ "$PVE_VER" =~ ^9\.([0-9]+) ]]; then local MINOR="${BASH_REMATCH[1]}" if ((MINOR < 0 || MINOR > 1)); then msg_error "This version of Proxmox VE is not supported." msg_error "Supported: Proxmox VE version 9.0 – 9.1" exit 105 fi return 0 fi # All other unsupported versions msg_error "This version of Proxmox VE is not supported." msg_error "Supported versions: Proxmox VE 8.0 – 8.x or 9.0 – 9.1" exit 105 } function arch_check() { local ARCH ARCH=$(dpkg --print-architecture) if [ "$ARCH" != "arm64" ]; then echo -e "\n This script will not work on AMD64! \n" echo -e "Exiting..." sleep 2 exit 1 fi } function msg_info() { local msg="$1" echo -ne " ${HOLD} ${YW}${msg}..." } function msg_ok() { local msg="$1" echo -e "${BFR} ${CM} ${GN}${msg}${CL}" } function msg_error() { local msg="$1" echo -e "${BFR} ${CROSS} ${RD}${msg}${CL}" } function default_settings() { METHOD="default" echo -e "${DGN}Using HAOS Version: ${BGN}${STABLE}${CL}" BRANCH=${STABLE} VMID=$(get_valid_nextid) echo -e "${DGN}Using Virtual Machine ID: ${BGN}$VMID${CL}" echo -e "${DGN}Using Hostname: ${BGN}haos${STABLE}${CL}" HN=haos${STABLE} echo -e "${DGN}Allocated Cores: ${BGN}2${CL}" CORE_COUNT="2" echo -e "${DGN}Allocated RAM: ${BGN}4096${CL}" RAM_SIZE="4096" echo -e "${DGN}Using Bridge: ${BGN}vmbr0${CL}" BRG="vmbr0" echo -e "${DGN}Using MAC Address: ${BGN}$GEN_MAC${CL}" MAC=$GEN_MAC echo -e "${DGN}Using VLAN: ${BGN}Default${CL}" VLAN="" echo -e "${DGN}Using Interface MTU Size: ${BGN}Default${CL}" MTU="" echo -e "${DGN}Start VM when completed: ${BGN}yes${CL}" START_VM="yes" echo -e "${BL}Creating a HAOS VM using the above default settings${CL}" } function advanced_settings() { METHOD="advanced" BRANCH=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "HAOS VERSION" --radiolist "Choose Version" --cancel-button Exit-Script 10 58 3 \ "$STABLE" "Stable" ON \ "$BETA" "Beta" OFF \ "$DEV" "Dev" OFF \ 3>&1 1>&2 2>&3) exitstatus=$? if [ $exitstatus = 0 ]; then echo -e "${DGN}Using HAOS Version: ${BGN}$BRANCH${CL}"; fi [ -z "${VMID:-}" ] && VMID=$(get_valid_nextid) VMID=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Virtual Machine ID" 8 58 $VMID --title "VIRTUAL MACHINE ID" --cancel-button Exit-Script 3>&1 1>&2 2>&3) exitstatus=$? if [ -z $VMID ]; then VMID="$VMID" echo -e "${DGN}Virtual Machine: ${BGN}$VMID${CL}" else if echo "$USEDID" | egrep -q "$VMID"; then echo -e "\n🚨 ${RD}ID $VMID is already in use${CL} \n" echo -e "Exiting Script \n" sleep 2 exit else if [ $exitstatus = 0 ]; then echo -e "${DGN}Virtual Machine ID: ${BGN}$VMID${CL}"; fi fi fi VM_NAME=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Hostname" 8 58 haos${BRANCH} --title "HOSTNAME" --cancel-button Exit-Script 3>&1 1>&2 2>&3) exitstatus=$? if [ -z $VM_NAME ]; then HN="haos${BRANCH}" echo -e "${DGN}Using Hostname: ${BGN}$HN${CL}" else if [ $exitstatus = 0 ]; then HN=$(echo "${VM_NAME,,}" | tr -cs 'a-z0-9-' '-' | sed 's/^-//;s/-$//') if [ "$HN" != "${VM_NAME,,}" ]; then whiptail --backtitle "Proxmox VE Helper Scripts" --title "HOSTNAME ADJUSTED" --msgbox "Invalid characters detected. Hostname has been adjusted to:\n\n $HN" 10 58 fi echo -e "${DGN}Using Hostname: ${BGN}$HN${CL}" fi fi while true; do CORE_COUNT=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Allocate CPU Cores" 8 58 2 --title "CORE COUNT" --cancel-button Exit-Script 3>&1 1>&2 2>&3) exitstatus=$? if [ $exitstatus -ne 0 ]; then exit-script; fi if [ -z "$CORE_COUNT" ]; then CORE_COUNT="2"; fi if [[ "$CORE_COUNT" =~ ^[1-9][0-9]*$ ]]; then echo -e "${DGN}Allocated Cores: ${BGN}$CORE_COUNT${CL}" break fi whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "CPU Cores must be a positive integer (e.g., 2)." 8 58 done while true; do RAM_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Allocate RAM in MiB" 8 58 4096 --title "RAM" --cancel-button Exit-Script 3>&1 1>&2 2>&3) exitstatus=$? if [ $exitstatus -ne 0 ]; then exit-script; fi if [ -z "$RAM_SIZE" ]; then RAM_SIZE="4096"; fi if [[ "$RAM_SIZE" =~ ^[1-9][0-9]*$ ]]; then echo -e "${DGN}Allocated RAM: ${BGN}$RAM_SIZE${CL}" break fi whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "RAM Size must be a positive integer in MiB (e.g., 4096)." 8 58 done BRG=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a Bridge" 8 58 vmbr0 --title "BRIDGE" --cancel-button Exit-Script 3>&1 1>&2 2>&3) exitstatus=$? if [ -z $BRG ]; then BRG="vmbr0" echo -e "${DGN}Using Bridge: ${BGN}$BRG${CL}" else if [ $exitstatus = 0 ]; then echo -e "${DGN}Using Bridge: ${BGN}$BRG${CL}"; fi fi while true; do MAC1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a MAC Address" 8 58 $GEN_MAC --title "MAC ADDRESS" --cancel-button Exit-Script 3>&1 1>&2 2>&3) exitstatus=$? if [ $exitstatus -ne 0 ]; then exit-script; fi if [ -z "$MAC1" ]; then MAC="$GEN_MAC" echo -e "${DGN}Using MAC Address: ${BGN}$MAC${CL}" break fi if [[ "$MAC1" =~ ^([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}$ ]]; then MAC="$MAC1" echo -e "${DGN}Using MAC Address: ${BGN}$MAC1${CL}" break fi whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "Invalid MAC address format. Use XX:XX:XX:XX:XX:XX (e.g., AA:BB:CC:DD:EE:FF)." 8 58 done while true; do VLAN1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a Vlan(leave blank for default)" 8 58 --title "VLAN" --cancel-button Exit-Script 3>&1 1>&2 2>&3) exitstatus=$? if [ $exitstatus -ne 0 ]; then exit-script; fi if [ -z "$VLAN1" ]; then VLAN1="Default" VLAN="" echo -e "${DGN}Using Vlan: ${BGN}$VLAN1${CL}" break fi if [[ "$VLAN1" =~ ^[0-9]+$ ]] && [ "$VLAN1" -ge 1 ] && [ "$VLAN1" -le 4094 ]; then VLAN=",tag=$VLAN1" echo -e "${DGN}Using Vlan: ${BGN}$VLAN1${CL}" break fi whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "VLAN must be a number between 1 and 4094, or leave blank for default." 8 58 done while true; do MTU1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Interface MTU Size (leave blank for default)" 8 58 --title "MTU SIZE" --cancel-button Exit-Script 3>&1 1>&2 2>&3) exitstatus=$? if [ $exitstatus -ne 0 ]; then exit-script; fi if [ -z "$MTU1" ]; then MTU1="Default" MTU="" echo -e "${DGN}Using Interface MTU Size: ${BGN}$MTU1${CL}" break fi if [[ "$MTU1" =~ ^[0-9]+$ ]] && [ "$MTU1" -ge 576 ] && [ "$MTU1" -le 65520 ]; then MTU=",mtu=$MTU1" echo -e "${DGN}Using Interface MTU Size: ${BGN}$MTU1${CL}" break fi whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "MTU Size must be a number between 576 and 65520, or leave blank for default." 8 58 done if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "START VIRTUAL MACHINE" --yesno "Start VM when completed?" 10 58); then echo -e "${DGN}Start VM when completed: ${BGN}yes${CL}" START_VM="yes" else echo -e "${DGN}Start VM when completed: ${BGN}no${CL}" START_VM="no" fi if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "ADVANCED SETTINGS COMPLETE" --yesno "Ready to create HAOS ${BRANCH} VM?" --no-button Do-Over 10 58); then echo -e "${RD}Creating a HAOS VM using the above advanced settings${CL}" else clear header_info echo -e "${RD}Using Advanced Settings${CL}" advanced_settings fi } function START_SCRIPT() { if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "SETTINGS" --yesno "Use Default Settings?" --no-button Advanced 10 58); then clear header_info echo -e "${BL}Using Default Settings${CL}" default_settings else clear header_info echo -e "${RD}Using Advanced Settings${CL}" advanced_settings fi } arch_check START_SCRIPT post_to_api_vm while read -r line; do TAG=$(echo $line | awk '{print $1}') TYPE=$(echo $line | awk '{printf "%-10s", $2}') FREE=$(echo $line | numfmt --field 4-6 --from-unit=K --to=iec --format %.2f | awk '{printf( "%9sB", $6)}') ITEM=" Type: $TYPE Free: $FREE " OFFSET=2 if [[ $((${#ITEM} + $OFFSET)) -gt ${MSG_MAX_LENGTH:-} ]]; then MSG_MAX_LENGTH=$((${#ITEM} + $OFFSET)) fi STORAGE_MENU+=("$TAG" "$ITEM" "OFF") done < <(pvesm status -content images | awk 'NR>1') if [ $((${#STORAGE_MENU[@]} / 3)) -eq 0 ]; then echo -e "'Disk image' needs to be selected for at least one storage location." die "Unable to detect valid storage location." elif [ $((${#STORAGE_MENU[@]} / 3)) -eq 1 ]; then STORAGE=${STORAGE_MENU[0]} else while [ -z "${STORAGE:+x}" ]; do STORAGE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "Storage Pools" --radiolist \ "Which storage pool would you like to use for the HAOS VM?\n\n" \ 16 $(($MSG_MAX_LENGTH + 23)) 6 \ "${STORAGE_MENU[@]}" 3>&1 1>&2 2>&3) done fi msg_ok "Using ${CL}${BL}$STORAGE${CL} ${GN}for Storage Location." msg_ok "Virtual Machine ID is ${CL}${BL}$VMID${CL}." msg_info "Getting URL for Home Assistant ${BRANCH} Disk Image" URL=https://github.com/home-assistant/operating-system/releases/download/${BRANCH}/haos_generic-aarch64-${BRANCH}.qcow2.xz sleep 2 msg_ok "${CL}${BL}${URL}${CL}" curl -f#SL -o "$(basename "$URL")" "$URL" echo -en "\e[1A\e[0K" FILE=$(basename $URL) msg_ok "Downloaded ${CL}${BL}haos_generic-aarch64-${BRANCH}.qcow2.xz${CL}" msg_info "Extracting Disk Image" unxz $FILE STORAGE_TYPE=$(pvesm status -storage $STORAGE | awk 'NR>1 {print $2}') case $STORAGE_TYPE in nfs | dir) DISK_EXT=".qcow2" DISK_REF="$VMID/" DISK_IMPORT="-format qcow2" ;; *) DISK_EXT="" DISK_REF="" DISK_IMPORT="-format raw" ;; esac for i in {0,1}; do disk="DISK$i" eval DISK${i}=vm-${VMID}-disk-${i}${DISK_EXT:-} eval DISK${i}_REF=${STORAGE}:${DISK_REF:-}${!disk} done msg_ok "Extracted Disk Image" msg_info "Creating HAOS VM" qm create $VMID -bios ovmf -cores $CORE_COUNT -memory $RAM_SIZE -name $HN \ -net0 virtio,bridge=$BRG,macaddr=$MAC$VLAN$MTU -onboot 1 -ostype l26 -scsihw virtio-scsi-pci pvesm alloc $STORAGE $VMID $DISK0 64M 1>&/dev/null qm importdisk $VMID ${FILE%.*} $STORAGE ${DISK_IMPORT:-} 1>&/dev/null qm set $VMID \ -efidisk0 ${DISK0_REF},efitype=4m,size=64M \ -scsi0 ${DISK1_REF},size=32G >/dev/null qm set $VMID \ -boot order=scsi0 \ -description "<div align='center'><a href='https://Helper-Scripts.com'><img src='https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/images/logo-81x112.png'/></a> # Home Assistant OS <a href='https://ko-fi.com/D1D7EP4GF'><img src='https://img.shields.io/badge/☕-Buy me a coffee-blue' /></a> </div>" >/dev/null msg_ok "Created HAOS VM ${CL}${BL}(${HN})" if [ "$START_VM" == "yes" ]; then msg_info "Starting Home Assistant OS VM" qm start $VMID msg_ok "Started Home Assistant OS VM" fi post_update_to_api "done" "none" msg_ok "Completed successfully!\n" ================================================ FILE: vm/truenas-vm.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: juronja # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE # Source: https://www.truenas.com/truenas-community-edition/ source /dev/stdin <<<$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func) function header_info() { clear cat <<"EOF" ______ _ _____ _____ /_ __/______ _____ / | / / | / ___/ / / / ___/ / / / _ \/ |/ / /| | \__ \ / / / / / /_/ / __/ /| / ___ |___/ / /_/ /_/ \__,_/\___/_/ |_/_/ |_/____/ (Community Edition) EOF } header_info echo -e "\n Loading..." GEN_MAC=02:$(openssl rand -hex 5 | awk '{print toupper($0)}' | sed 's/\(..\)/\1:/g; s/.$//') RANDOM_UUID="$(cat /proc/sys/kernel/random/uuid)" METHOD="" NSAPP="truenas-vm" YW=$(echo "\033[33m") BL=$(echo "\033[36m") RD=$(echo "\033[01;31m") BGN=$(echo "\033[4;92m") GN=$(echo "\033[1;92m") DGN=$(echo "\033[32m") CL=$(echo "\033[m") CL=$(echo "\033[m") BOLD=$(echo "\033[1m") BFR="\\r\\033[K" HOLD=" " TAB=" " CM="${TAB}✔️${TAB}${CL}" CROSS="${TAB}✖️${TAB}${CL}" INFO="${TAB}💡${TAB}${CL}" OS="${TAB}🖥️${TAB}${CL}" CONTAINERTYPE="${TAB}📦${TAB}${CL}" ISO="${TAB}📀${TAB}${CL}" DISKSIZE="${TAB}💾${TAB}${CL}" CPUCORE="${TAB}🧠${TAB}${CL}" RAMSIZE="${TAB}🛠️${TAB}${CL}" CONTAINERID="${TAB}🆔${TAB}${CL}" HOSTNAME="${TAB}🏠${TAB}${CL}" BRIDGE="${TAB}🌉${TAB}${CL}" GATEWAY="${TAB}🌐${TAB}${CL}" DISK="${TAB}💽${TAB}${CL}" DEFAULT="${TAB}⚙️${TAB}${CL}" MACADDRESS="${TAB}🔗${TAB}${CL}" VLANTAG="${TAB}🏷️${TAB}${CL}" CREATING="${TAB}🚀${TAB}${CL}" ADVANCED="${TAB}🧩${TAB}${CL}" CLOUD="${TAB}☁️${TAB}${CL}" set -e trap 'error_handler $LINENO "$BASH_COMMAND"' ERR trap cleanup EXIT trap 'post_update_to_api "failed" "130"' SIGINT trap 'post_update_to_api "failed" "143"' SIGTERM trap 'post_update_to_api "failed" "129"; exit 129' SIGHUP function error_handler() { local exit_code="$?" local line_number="$1" local command="$2" local error_message="${RD}[ERROR]${CL} in line ${RD}$line_number${CL}: exit code ${RD}$exit_code${CL}: while executing command ${YW}$command${CL}" post_update_to_api "failed" "${command}" echo -e "\n$error_message\n" cleanup_vmid } function truenas_iso_lookup() { local BASE_URL="https://download.truenas.com" local current_year=$(date +%y) local last_year=$(date -d "1 year ago" +%y) local year_pattern="${current_year}\.|${last_year}\." declare -A latest_stables local pre_releases=() local all_paths=$( curl -sL "$BASE_URL" | grep -oE 'href="[^"]+\.iso"' | sed 's/href="//; s/"$//' | grep -vE '(MASTER|ALPHA)' | grep -E "$year_pattern" ) while read -r path; do local filename=$(basename "$path") local version=$(echo "$filename" | sed -E 's/.*TrueNAS-SCALE-([0-9]{2}\.[0-9]{2}(\.[0-9]+)*(-RC[0-9]|-BETA[0-9])?)\.iso.*/\1/') if [[ "$version" =~ (RC|BETA) ]]; then pre_releases+=("$path") else local major_version=$(echo "$version" | cut -d'.' -f1,2) local current_stored_path=${latest_stables["$major_version"]} if [[ -z "$current_stored_path" ]]; then latest_stables["$major_version"]="$path" else local stored_version=$(basename "$current_stored_path" | sed -E 's/.*TrueNAS-SCALE-([0-9]{2}\.[0-9]{2}(\.[0-9]+)*)\.iso.*/\1/') if printf '%s\n' "$version" "$stored_version" | sort -V | tail -n 1 | grep -q "$version"; then latest_stables["$major_version"]="$path" fi fi fi done <<<"$all_paths" for key in "${!latest_stables[@]}"; do echo "${latest_stables[$key]#/}" done for pre in "${pre_releases[@]}"; do echo "${pre#/}" done | sort -V } function get_valid_nextid() { local try_id try_id=$(pvesh get /cluster/nextid) while true; do if [ -f "/etc/pve/qemu-server/${try_id}.conf" ] || [ -f "/etc/pve/lxc/${try_id}.conf" ]; then try_id=$((try_id + 1)) continue fi if lvs --noheadings -o lv_name | grep -qE "(^|[-_])${try_id}($|[-_])"; then try_id=$((try_id + 1)) continue fi break done echo "$try_id" } function cleanup_vmid() { if qm status $VMID &>/dev/null; then qm stop $VMID &>/dev/null qm destroy $VMID &>/dev/null fi } function cleanup() { popd >/dev/null post_update_to_api "done" "none" rm -rf $TEMP_DIR } TEMP_DIR=$(mktemp -d) pushd $TEMP_DIR >/dev/null if whiptail --backtitle "Proxmox VE Helper Scripts" --title "TrueNAS VM" --yesno "This will create a New TrueNAS VM. Proceed?" 10 58; then : else header_info && echo -e "${CROSS}${RD}User exited script${CL}\n" && exit fi function msg_info() { local msg="$1" echo -ne "${TAB}${YW}${HOLD}${msg}${HOLD}" } function msg_ok() { local msg="$1" echo -e "${BFR}${CM}${GN}${msg}${CL}" } function msg_error() { local msg="$1" echo -e "${BFR}${CROSS}${RD}${msg}${CL}" } function check_root() { if [[ "$(id -u)" -ne 0 || $(ps -o comm= -p $PPID) == "sudo" ]]; then clear msg_error "Please run this script as root." echo -e "\nExiting..." sleep 2 exit fi } function pve_check() { local PVE_VER PVE_VER="$(pveversion | awk -F'/' '{print $2}' | awk -F'-' '{print $1}')" if [[ "$PVE_VER" =~ ^8\.([0-9]+) ]]; then local MINOR="${BASH_REMATCH[1]}" if ((MINOR < 0 || MINOR > 9)); then msg_error "This version of Proxmox VE is not supported." msg_error "Supported: Proxmox VE version 8.0 – 8.9" exit 105 fi return 0 fi if [[ "$PVE_VER" =~ ^9\.([0-9]+) ]]; then local MINOR="${BASH_REMATCH[1]}" if ((MINOR < 0 || MINOR > 1)); then msg_error "This version of Proxmox VE is not yet supported." msg_error "Supported: Proxmox VE version 9.0 – 9.1" exit 105 fi return 0 fi msg_error "This version of Proxmox VE is not supported." msg_error "Supported versions: Proxmox VE 8.0 – 8.x or 9.0 – 9.1" exit 105 } function arch_check() { if [ "$(dpkg --print-architecture)" != "amd64" ]; then echo -e "\n ${INFO}${YWB}This script will not work with PiMox! \n" echo -e "\n ${YWB}Visit https://github.com/asylumexp/Proxmox for ARM64 support. \n" echo -e "Exiting..." sleep 2 exit fi } function ssh_check() { if command -v pveversion >/dev/null 2>&1; then if [ -n "${SSH_CLIENT:+x}" ]; then if whiptail --backtitle "Proxmox VE Helper Scripts" --defaultno --title "SSH DETECTED" --yesno "It's suggested to use the Proxmox shell instead of SSH, since SSH can create issues while gathering variables. Would you like to proceed with using SSH?" 10 62; then echo "you've been warned" else clear exit fi fi fi } function exit-script() { clear echo -e "\n${CROSS}${RD}User exited script${CL}\n" exit } function default_settings() { VMID=$(get_valid_nextid) ISO_DEFAULT="latest stable" FORMAT="" MACHINE="q35" DISK_SIZE="16" HN="truenas" CPU_TYPE="host" CORE_COUNT="2" RAM_SIZE="8192" BRG="vmbr0" MAC="$GEN_MAC" VLAN="" MTU="" START_VM="yes" METHOD="default" echo -e "${CONTAINERID}${BOLD}${DGN}Virtual Machine ID: ${BGN}${VMID}${CL}" echo -e "${ISO}${BOLD}${DGN}ISO Chosen: ${BGN}${ISO_DEFAULT}${CL}" echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}${MACHINE}${CL}" echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}${DISK_SIZE}${CL}" echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}${HN}${CL}" echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}${CPU_TYPE}${CL}" echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}${CORE_COUNT}${CL}" echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}${RAM_SIZE}${CL}" echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}${BRG}${CL}" echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}${MAC}${CL}" echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}Default${CL}" echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}Default${CL}" echo -e "${GATEWAY}${BOLD}${DGN}Start VM when completed: ${BGN}yes${CL}" echo -e "${CREATING}${BOLD}${DGN}Creating a TrueNAS VM using the above default settings${CL}" } function advanced_settings() { DISK_SIZE="16" HN="truenas" CORE_COUNT="2" RAM_SIZE="8192" BRG="vmbr0" METHOD="advanced" [ -z "${VMID:-}" ] && VMID=$(get_valid_nextid) while true; do if VMID=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Virtual Machine ID" 8 58 $VMID --title "VIRTUAL MACHINE ID" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z "$VMID" ]; then VMID=$(get_valid_nextid) fi if pct status "$VMID" &>/dev/null || qm status "$VMID" &>/dev/null; then echo -e "${CROSS}${RD} ID $VMID is already in use${CL}" sleep 2 continue fi echo -e "${CONTAINERID}${BOLD}${DGN}Virtual Machine ID: ${BGN}$VMID${CL}" break else exit-script fi done ISOARRAY=() mapfile -t ALL_ISOS < <(truenas_iso_lookup | sort -V) ISO_COUNT=${#ALL_ISOS[@]} if [ $ISO_COUNT -eq 0 ]; then echo "No ISOs found." exit 115 fi # Identify the index of the last stable release LAST_STABLE_INDEX=-1 for i in "${!ALL_ISOS[@]}"; do if [[ ! "${ALL_ISOS[$i]}" =~ (BETA|RC) ]]; then LAST_STABLE_INDEX=$i fi done # Build the whiptail array for i in "${!ALL_ISOS[@]}"; do ISOPATH="${ALL_ISOS[$i]}" FILENAME=$(basename "$ISOPATH") # Select ON if it's the last stable found, OR fallback to last item if no stable exists if [[ "$i" -eq "$LAST_STABLE_INDEX" ]]; then ISOARRAY+=("$ISOPATH" "$FILENAME" "ON") elif [[ "$LAST_STABLE_INDEX" -eq -1 && "$i" -eq "$((ISO_COUNT - 1))" ]]; then # Fallback: if somehow no stable is found, select the very last item ISOARRAY+=("$ISOPATH" "$FILENAME" "ON") else ISOARRAY+=("$ISOPATH" "$FILENAME" "OFF") fi done if SELECTED_ISO=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "SELECT ISO TO INSTALL" --notags --radiolist "\nSelect version (BETA/RC/Latest stable):" 20 58 12 "${ISOARRAY[@]}" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then echo -e "${ISO}${BOLD}${DGN}ISO Chosen: ${BGN}$(basename "$SELECTED_ISO")${CL}" else exit-script fi if DISK_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Disk Size in GiB (e.g., 10, 20)" 8 58 "$DISK_SIZE" --title "DISK SIZE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then DISK_SIZE=$(echo "$DISK_SIZE" | tr -d ' ') if [[ "$DISK_SIZE" =~ ^[0-9]+$ ]]; then echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}$DISK_SIZE${CL}" else echo -e "${DISKSIZE}${BOLD}${RD}Invalid Disk Size. Please use a number (e.g., 10).${CL}" exit-script fi else exit-script fi if VM_NAME=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Hostname" 8 58 "$HN" --title "HOSTNAME" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z $VM_NAME ]; then echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}" else HN=$(echo "${VM_NAME,,}" | tr -cs 'a-z0-9-' '-' | sed 's/^-//;s/-$//') if [ "$HN" != "${VM_NAME,,}" ]; then whiptail --backtitle "Proxmox VE Helper Scripts" --title "HOSTNAME ADJUSTED" --msgbox "Invalid characters detected. Hostname has been adjusted to:\n\n $HN" 10 58 fi echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}" fi else exit-script fi if CPU_TYPE1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "CPU MODEL" --radiolist "Choose CPU Model" --cancel-button Exit-Script 10 58 2 \ "KVM64" "Default – safe for migration/compatibility" OFF \ "Host" "Use host CPU features (faster, no migration)" ON \ 3>&1 1>&2 2>&3); then case "$CPU_TYPE1" in Host) echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}Host${CL}" CPU_TYPE="host" ;; *) echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}KVM64${CL}" CPU_TYPE="" ;; esac else exit-script fi while true; do if CORE_COUNT=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Allocate CPU Cores" 8 58 "$CORE_COUNT" --title "CORE COUNT" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z "$CORE_COUNT" ]; then CORE_COUNT="2"; fi if [[ "$CORE_COUNT" =~ ^[1-9][0-9]*$ ]]; then echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}$CORE_COUNT${CL}" break fi whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "CPU Cores must be a positive integer (e.g., 2)." 8 58 else exit-script fi done while true; do if RAM_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Allocate RAM in MiB" 8 58 "$RAM_SIZE" --title "RAM" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z "$RAM_SIZE" ]; then RAM_SIZE="8192"; fi if [[ "$RAM_SIZE" =~ ^[1-9][0-9]*$ ]]; then echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}$RAM_SIZE${CL}" break fi whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "RAM Size must be a positive integer in MiB (e.g., 8192)." 8 58 else exit-script fi done if BRG=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a Bridge" 8 58 "$BRG" --title "BRIDGE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z $BRG ]; then BRG="vmbr0" echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}" else echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}" fi else exit-script fi while true; do if MAC1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a MAC Address" 8 58 $GEN_MAC --title "MAC ADDRESS" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z "$MAC1" ]; then MAC="$GEN_MAC" echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}$MAC${CL}" break fi if [[ "$MAC1" =~ ^([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}$ ]]; then MAC="$MAC1" echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}$MAC1${CL}" break fi whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "Invalid MAC address format. Use XX:XX:XX:XX:XX:XX (e.g., AA:BB:CC:DD:EE:FF)." 8 58 else exit-script fi done while true; do if VLAN1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a Vlan(leave blank for default)" 8 58 --title "VLAN" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z "$VLAN1" ]; then VLAN1="Default" VLAN="" echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}$VLAN1${CL}" break fi if [[ "$VLAN1" =~ ^[0-9]+$ ]] && [ "$VLAN1" -ge 1 ] && [ "$VLAN1" -le 4094 ]; then VLAN=",tag=$VLAN1" echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}$VLAN1${CL}" break fi whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "VLAN must be a number between 1 and 4094, or leave blank for default." 8 58 else exit-script fi done while true; do if MTU1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Interface MTU Size (leave blank for default)" 8 58 --title "MTU SIZE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z "$MTU1" ]; then MTU1="Default" MTU="" echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}$MTU1${CL}" break fi if [[ "$MTU1" =~ ^[0-9]+$ ]] && [ "$MTU1" -ge 576 ] && [ "$MTU1" -le 65520 ]; then MTU=",mtu=$MTU1" echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}$MTU1${CL}" break fi whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "MTU Size must be a number between 576 and 65520, or leave blank for default." 8 58 else exit-script fi done if (whiptail --backtitle "Proxmox VE Helper Scripts" --defaultno --title "IMPORT ONBOARD DISKS" --yesno "Would you like to import onboard disks?" 10 58); then echo -e "${DISK}${BOLD}${DGN}Import onboard disks: ${BGN}yes${CL}" IMPORT_DISKS="yes" else echo -e "${DISK}${BOLD}${DGN}Import onboard disks: ${BGN}no${CL}" IMPORT_DISKS="no" fi if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "START VIRTUAL MACHINE" --yesno "Start VM when completed?" 10 58); then echo -e "${GATEWAY}${BOLD}${DGN}Start VM when completed: ${BGN}yes${CL}" START_VM="yes" else echo -e "${GATEWAY}${BOLD}${DGN}Start VM when completed: ${BGN}no${CL}" START_VM="no" fi if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "ADVANCED SETTINGS COMPLETE" --yesno "Ready to create a TrueNAS VM?" --no-button Do-Over 10 58); then echo -e "${CREATING}${BOLD}${DGN}Creating a TrueNAS VM using the above advanced settings${CL}" else header_info echo -e "${ADVANCED}${BOLD}${RD}Using Advanced Settings${CL}" advanced_settings fi } function start_script() { if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "SETTINGS" --yesno "Use Default Settings?" --no-button Advanced 10 58); then header_info echo -e "${DEFAULT}${BOLD}${BL}Using Default Settings${CL}" default_settings else header_info echo -e "${ADVANCED}${BOLD}${RD}Using Advanced Settings${CL}" advanced_settings fi } check_root arch_check pve_check ssh_check start_script post_to_api_vm msg_info "Validating Storage" while read -r line; do TAG=$(echo $line | awk '{print $1}') TYPE=$(echo $line | awk '{printf "%-10s", $2}') FREE=$(echo $line | numfmt --field 4-6 --from-unit=K --to=iec --format %.2f | awk '{printf( "%9sB", $6)}') ITEM=" Type: $TYPE Free: $FREE " OFFSET=2 if [[ $((${#ITEM} + $OFFSET)) -gt ${MSG_MAX_LENGTH:-} ]]; then MSG_MAX_LENGTH=$((${#ITEM} + $OFFSET)) fi STORAGE_MENU+=("$TAG" "$ITEM" "OFF") done < <(pvesm status -content images | awk 'NR>1') VALID=$(pvesm status -content images | awk 'NR>1') if [ -z "$VALID" ]; then msg_error "Unable to detect a valid storage location." exit elif [ $((${#STORAGE_MENU[@]} / 3)) -eq 1 ]; then STORAGE=${STORAGE_MENU[0]} else while [ -z "${STORAGE:+x}" ]; do if [ -n "$SPINNER_PID" ] && ps -p $SPINNER_PID >/dev/null; then kill $SPINNER_PID >/dev/null; fi printf "\e[?25h" STORAGE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "Storage Pools" --radiolist \ "Which storage pool would you like to use for ${HN}?\nTo make a selection, use the Spacebar.\n" \ 16 $(($MSG_MAX_LENGTH + 23)) 6 \ "${STORAGE_MENU[@]}" 3>&1 1>&2 2>&3) done fi msg_ok "Using ${CL}${BL}$STORAGE${CL} ${GN}for Storage Location." msg_ok "Virtual Machine ID is ${CL}${BL}$VMID${CL}." if [ -z "${SELECTED_ISO:-}" ]; then SELECTED_ISO=$(truenas_iso_lookup | grep -vE 'RC|BETA' | sort -V | tail -n 1) if [ -z "$SELECTED_ISO" ]; then msg_error "Could not find a stable ISO for fallback." exit 115 fi fi FULL_URL="https://download.truenas.com/${SELECTED_ISO#/}" ISO_NAME=$(basename "$FULL_URL") CACHE_DIR="/var/lib/vz/template/iso" CACHE_FILE="$CACHE_DIR/$ISO_NAME" if [[ ! -s "$CACHE_FILE" ]]; then msg_info "Retrieving the ISO for the TrueNAS Disk Image" curl -f#SL -o "$CACHE_FILE" "$FULL_URL" msg_ok "Downloaded ${CL}${BL}$(basename "$CACHE_FILE")${CL}" else msg_ok "Using cached image ${CL}${BL}$(basename "$CACHE_FILE")${CL}" fi set -o pipefail msg_info "Creating TrueNAS VM shell" qm create "$VMID" -machine q35 -bios ovmf -agent enabled=1 -tablet 0 -localtime 1 -cpu "$CPU_TYPE" \ -cores "$CORE_COUNT" -memory "$RAM_SIZE" -balloon 0 -name "$HN" -tags community-script \ -net0 "virtio,bridge=$BRG,macaddr=$MAC$VLAN$MTU" -onboot 1 -ostype l26 \ -efidisk0 $STORAGE:1,efitype=4m,pre-enrolled-keys=0 -sata0 $STORAGE:$DISK_SIZE,ssd=1 \ -scsihw virtio-scsi-single -cdrom local:iso/$ISO_NAME -vga virtio >/dev/null msg_ok "Created VM shell" if [ "$IMPORT_DISKS" == "yes" ]; then msg_info "Importing onboard disks" DISKARRAY=() SCSI_NR=0 while read -r LSOUTPUT; do TRUNCATED="${LSOUTPUT:0:45}" if [ ${#LSOUTPUT} -gt 45 ]; then TRUNCATED="${TRUNCATED}..." fi DISKARRAY+=("$LSOUTPUT" "$TRUNCATED" "OFF") done < <(ls /dev/disk/by-id | grep -E '^ata-|^nvme-|^usb-' | grep -v 'part') SELECTIONS=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "SELECT DISKS TO IMPORT" --checklist "\nSelect disk IDs to import. (Use Spacebar to select)\n" --notags --cancel-button "Exit Script" 20 58 10 "${DISKARRAY[@]}" 3>&1 1>&2 2>&3 | tr -d '"') || exit for SELECTION in $SELECTIONS; do ((++SCSI_NR)) ID_SERIAL=$(udevadm info --query=property --value --property=ID_SERIAL_SHORT "/dev/disk/by-id/$SELECTION") ID_SERIAL=${ID_SERIAL:0:20} qm set $VMID --scsi$SCSI_NR /dev/disk/by-id/$SELECTION,serial=$ID_SERIAL done msg_ok "Disks imported successfully" fi DESCRIPTION=$( cat <<EOF <div align='center'> <a href='https://community-scripts.org' target='_blank' rel='noopener noreferrer'> <img src='https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/images/logo-81x112.png' alt='Logo' style='width:81px;height:112px;'/> </a> <h2 style='font-size: 24px; margin: 20px 0;'>TrueNAS Community Edition</h2> <p style='margin: 16px 0;'> <a href='https://ko-fi.com/community_scripts' target='_blank' rel='noopener noreferrer'> <img src='https://img.shields.io/badge/☕-Buy us a coffee-blue' alt='spend Coffee' /> </a> </p> <span style='margin: 0 10px;'> <i class="fa fa-github fa-fw" style="color: #f5f5f5;"></i> <a href='https://github.com/community-scripts/ProxmoxVE' target='_blank' rel='noopener noreferrer' style='text-decoration: none; color: #00617f;'>GitHub</a> </span> <span style='margin: 0 10px;'> <i class="fa fa-comments fa-fw" style="color: #f5f5f5;"></i> <a href='https://github.com/community-scripts/ProxmoxVE/discussions' target='_blank' rel='noopener noreferrer' style='text-decoration: none; color: #00617f;'>Discussions</a> </span> <span style='margin: 0 10px;'> <i class="fa fa-exclamation-circle fa-fw" style="color: #f5f5f5;"></i> <a href='https://github.com/community-scripts/ProxmoxVE/issues' target='_blank' rel='noopener noreferrer' style='text-decoration: none; color: #00617f;'>Issues</a> </span> </div> EOF ) qm set "$VMID" -description "$DESCRIPTION" >/dev/null sleep 3 msg_ok "Created a TrueNAS VM ${CL}${BL}(${HN})" if [ "$START_VM" == "yes" ]; then msg_info "Starting TrueNAS VM" qm start $VMID msg_ok "Started TrueNAS VM" fi msg_ok "Completed Successfully!\n" ================================================ FILE: vm/ubuntu2204-vm.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE source /dev/stdin <<< $(wget -qLO - https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/api.func) function header_info { clear cat <<"EOF" __ ____ __ ___ ___ ____ __ __ _ ____ ___ / / / / /_ __ ______ / /___ __ |__ \|__ \ / __ \/ // / | | / / |/ / / / / / __ \/ / / / __ \/ __/ / / / __/ /__/ / / / / / // /_ | | / / /|_/ / / /_/ / /_/ / /_/ / / / / /_/ /_/ / / __// __/_/ /_/ /__ __/ | |/ / / / / \____/_.___/\__,_/_/ /_/\__/\__,_/ /____/____(_)____/ /_/ |___/_/ /_/ EOF } header_info echo -e "\n Loading..." GEN_MAC=02:$(openssl rand -hex 5 | awk '{print toupper($0)}' | sed 's/\(..\)/\1:/g; s/.$//') RANDOM_UUID="$(cat /proc/sys/kernel/random/uuid)" METHOD="" NSAPP="ubuntu2204-vm" var_os="ubuntu" var_version="2204" YW=$(echo "\033[33m") BL=$(echo "\033[36m") RD=$(echo "\033[01;31m") BGN=$(echo "\033[4;92m") GN=$(echo "\033[1;92m") DGN=$(echo "\033[32m") CL=$(echo "\033[m") BOLD=$(echo "\033[1m") BFR="\\r\\033[K" HOLD=" " TAB=" " CM="${TAB}✔️${TAB}${CL}" CROSS="${TAB}✖️${TAB}${CL}" INFO="${TAB}💡${TAB}${CL}" OS="${TAB}🖥️${TAB}${CL}" CONTAINERTYPE="${TAB}📦${TAB}${CL}" DISKSIZE="${TAB}💾${TAB}${CL}" CPUCORE="${TAB}🧠${TAB}${CL}" RAMSIZE="${TAB}🛠️${TAB}${CL}" CONTAINERID="${TAB}🆔${TAB}${CL}" HOSTNAME="${TAB}🏠${TAB}${CL}" BRIDGE="${TAB}🌉${TAB}${CL}" GATEWAY="${TAB}🌐${TAB}${CL}" DEFAULT="${TAB}⚙️${TAB}${CL}" MACADDRESS="${TAB}🔗${TAB}${CL}" VLANTAG="${TAB}🏷️${TAB}${CL}" CREATING="${TAB}🚀${TAB}${CL}" ADVANCED="${TAB}🧩${TAB}${CL}" THIN="discard=on,ssd=1," set -e trap 'error_handler $LINENO "$BASH_COMMAND"' ERR trap cleanup EXIT trap 'post_update_to_api "failed" "130"' SIGINT trap 'post_update_to_api "failed" "143"' SIGTERM trap 'post_update_to_api "failed" "129"; exit 129' SIGHUP function error_handler() { local exit_code="$?" local line_number="$1" local command="$2" post_update_to_api "failed" "$exit_code" local error_message="${RD}[ERROR]${CL} in line ${RD}$line_number${CL}: exit code ${RD}$exit_code${CL}: while executing command ${YW}$command${CL}" echo -e "\n$error_message\n" cleanup_vmid } function get_valid_nextid() { local try_id try_id=$(pvesh get /cluster/nextid) while true; do if [ -f "/etc/pve/qemu-server/${try_id}.conf" ] || [ -f "/etc/pve/lxc/${try_id}.conf" ]; then try_id=$((try_id + 1)) continue fi if lvs --noheadings -o lv_name | grep -qE "(^|[-_])${try_id}($|[-_])"; then try_id=$((try_id + 1)) continue fi break done echo "$try_id" } function cleanup_vmid() { if qm status $VMID &>/dev/null; then qm stop $VMID &>/dev/null qm destroy $VMID &>/dev/null fi } function cleanup() { local exit_code=$? popd >/dev/null if [[ "${POST_TO_API_DONE:-}" == "true" && "${POST_UPDATE_DONE:-}" != "true" ]]; then if [[ $exit_code -eq 0 ]]; then post_update_to_api "done" "none" else post_update_to_api "failed" "$exit_code" fi fi rm -rf $TEMP_DIR } TEMP_DIR=$(mktemp -d) pushd $TEMP_DIR >/dev/null if whiptail --backtitle "Proxmox VE Helper Scripts" --title "Ubuntu 22.04 VM" --yesno "This will create a New Ubuntu 22.04 VM. Proceed?" 10 58; then : else header_info && echo -e "${CROSS}${RD}User exited script${CL}\n" && exit fi function msg_info() { local msg="$1" echo -ne "${TAB}${YW}${HOLD}${msg}${HOLD}" } function msg_ok() { local msg="$1" echo -e "${BFR}${CM}${GN}${msg}${CL}" } function msg_error() { local msg="$1" echo -e "${BFR}${CROSS}${RD}${msg}${CL}" } function check_root() { if [[ "$(id -u)" -ne 0 || $(ps -o comm= -p $PPID) == "sudo" ]]; then clear msg_error "Please run this script as root." echo -e "\nExiting..." sleep 2 exit fi } # This function checks the version of Proxmox Virtual Environment (PVE) and exits if the version is not supported. # Supported: Proxmox VE 8.0.x – 8.9.x, 9.0 and 9.1 pve_check() { local PVE_VER PVE_VER="$(pveversion | awk -F'/' '{print $2}' | awk -F'-' '{print $1}')" # Check for Proxmox VE 8.x: allow 8.0–8.9 if [[ "$PVE_VER" =~ ^8\.([0-9]+) ]]; then local MINOR="${BASH_REMATCH[1]}" if ((MINOR < 0 || MINOR > 9)); then msg_error "This version of Proxmox VE is not supported." msg_error "Supported: Proxmox VE version 8.0 – 8.9" exit 105 fi return 0 fi # Check for Proxmox VE 9.x: allow 9.0 and 9.1 if [[ "$PVE_VER" =~ ^9\.([0-9]+) ]]; then local MINOR="${BASH_REMATCH[1]}" if ((MINOR < 0 || MINOR > 1)); then msg_error "This version of Proxmox VE is not supported." msg_error "Supported: Proxmox VE version 9.0 – 9.1" exit 105 fi return 0 fi # All other unsupported versions msg_error "This version of Proxmox VE is not supported." msg_error "Supported versions: Proxmox VE 8.0 – 8.x or 9.0 – 9.1" exit 105 } function arch_check() { if [ "$(dpkg --print-architecture)" != "arm64" ]; then echo -e "\n ${INFO}${YWB}This script only works on PiMox! \n" echo -e "\n ${YWB}Visit https://github.com/community-scripts/ProxmoxVE for AMD64 support. \n" echo -e "Exiting..." sleep 2 exit fi } function ssh_check() { if command -v pveversion >/dev/null 2>&1; then if [ -n "${SSH_CLIENT:+x}" ]; then if whiptail --backtitle "Proxmox VE Helper Scripts" --defaultno --title "SSH DETECTED" --yesno "It's suggested to use the Proxmox shell instead of SSH, since SSH can create issues while gathering variables. Would you like to proceed with using SSH?" 10 62; then echo "you've been warned" else clear exit fi fi fi } function exit-script() { clear echo -e "\n${CROSS}${RD}User exited script${CL}\n" exit } function default_settings() { VMID=$(get_valid_nextid) FORMAT=",efitype=4m" MACHINE="" DISK_SIZE="5G" DISK_CACHE="" HN="ubuntu" CPU_TYPE="" CORE_COUNT="2" RAM_SIZE="2048" BRG="vmbr0" MAC="$GEN_MAC" VLAN="" MTU="" START_VM="yes" METHOD="default" echo -e "${CONTAINERID}${BOLD}${DGN}Virtual Machine ID: ${BGN}${VMID}${CL}" echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}i440fx${CL}" echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}${DISK_SIZE}${CL}" echo -e "${DISKSIZE}${BOLD}${DGN}Disk Cache: ${BGN}None${CL}" echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}${HN}${CL}" echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}KVM64${CL}" echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}${CORE_COUNT}${CL}" echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}${RAM_SIZE}${CL}" echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}${BRG}${CL}" echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}${MAC}${CL}" echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}Default${CL}" echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}Default${CL}" echo -e "${GATEWAY}${BOLD}${DGN}Start VM when completed: ${BGN}yes${CL}" echo -e "${CREATING}${BOLD}${DGN}Creating a Ubuntu 22.04 VM using the above default settings${CL}" } function advanced_settings() { METHOD="advanced" [ -z "${VMID:-}" ] && VMID=$(get_valid_nextid) while true; do if VMID=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Virtual Machine ID" 8 58 $VMID --title "VIRTUAL MACHINE ID" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z "$VMID" ]; then VMID=$(get_valid_nextid) fi if pct status "$VMID" &>/dev/null || qm status "$VMID" &>/dev/null; then echo -e "${CROSS}${RD} ID $VMID is already in use${CL}" sleep 2 continue fi echo -e "${CONTAINERID}${BOLD}${DGN}Virtual Machine ID: ${BGN}$VMID${CL}" break else exit-script fi done if MACH=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "MACHINE TYPE" --radiolist --cancel-button Exit-Script "Choose Type" 10 58 2 \ "i440fx" "Machine i440fx" ON \ "q35" "Machine q35" OFF \ 3>&1 1>&2 2>&3); then if [ $MACH = q35 ]; then echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}$MACH${CL}" FORMAT="" MACHINE=" -machine q35" else echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}$MACH${CL}" FORMAT=",efitype=4m" MACHINE="" fi else exit-script fi if DISK_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Disk Size in GiB (e.g., 10, 20)" 8 58 "$DISK_SIZE" --title "DISK SIZE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then DISK_SIZE=$(echo "$DISK_SIZE" | tr -d ' ') if [[ "$DISK_SIZE" =~ ^[0-9]+$ ]]; then DISK_SIZE="${DISK_SIZE}G" echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}$DISK_SIZE${CL}" elif [[ "$DISK_SIZE" =~ ^[0-9]+G$ ]]; then echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}$DISK_SIZE${CL}" else echo -e "${DISKSIZE}${BOLD}${RD}Invalid Disk Size. Please use a number (e.g., 10 or 10G).${CL}" exit-script fi else exit-script fi if DISK_CACHE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "DISK CACHE" --radiolist "Choose" --cancel-button Exit-Script 10 58 2 \ "0" "None (Default)" ON \ "1" "Write Through" OFF \ 3>&1 1>&2 2>&3); then if [ $DISK_CACHE = "1" ]; then echo -e "${DISKSIZE}${BOLD}${DGN}Disk Cache: ${BGN}Write Through${CL}" DISK_CACHE="cache=writethrough," else echo -e "${DISKSIZE}${BOLD}${DGN}Disk Cache: ${BGN}None${CL}" DISK_CACHE="" fi else exit-script fi if VM_NAME=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Hostname" 8 58 ubuntu --title "HOSTNAME" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z $VM_NAME ]; then HN="ubuntu" echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}" else HN=$(echo "${VM_NAME,,}" | tr -cs 'a-z0-9-' '-' | sed 's/^-//;s/-$//') if [ "$HN" != "${VM_NAME,,}" ]; then whiptail --backtitle "Proxmox VE Helper Scripts" --title "HOSTNAME ADJUSTED" --msgbox "Invalid characters detected. Hostname has been adjusted to:\n\n $HN" 10 58 fi echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}" fi else exit-script fi if CPU_TYPE1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "CPU MODEL" --radiolist "Choose" --cancel-button Exit-Script 10 58 2 \ "0" "KVM64 (Default)" ON \ "1" "Host" OFF \ 3>&1 1>&2 2>&3); then if [ $CPU_TYPE1 = "1" ]; then echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}Host${CL}" CPU_TYPE=" -cpu host" else echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}KVM64${CL}" CPU_TYPE="" fi else exit-script fi while true; do if CORE_COUNT=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Allocate CPU Cores" 8 58 2 --title "CORE COUNT" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z "$CORE_COUNT" ]; then CORE_COUNT="2"; fi if [[ "$CORE_COUNT" =~ ^[1-9][0-9]*$ ]]; then echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}$CORE_COUNT${CL}" break fi whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "CPU Cores must be a positive integer (e.g., 2)." 8 58 else exit-script fi done while true; do if RAM_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Allocate RAM in MiB" 8 58 2048 --title "RAM" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z "$RAM_SIZE" ]; then RAM_SIZE="2048"; fi if [[ "$RAM_SIZE" =~ ^[1-9][0-9]*$ ]]; then echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}$RAM_SIZE${CL}" break fi whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "RAM Size must be a positive integer in MiB (e.g., 2048)." 8 58 else exit-script fi done if BRG=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a Bridge" 8 58 vmbr0 --title "BRIDGE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z $BRG ]; then BRG="vmbr0" echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}" else echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}" fi else exit-script fi while true; do if MAC1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a MAC Address" 8 58 $GEN_MAC --title "MAC ADDRESS" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z "$MAC1" ]; then MAC="$GEN_MAC" echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}$MAC${CL}" break fi if [[ "$MAC1" =~ ^([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}$ ]]; then MAC="$MAC1" echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}$MAC1${CL}" break fi whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "Invalid MAC address format. Use XX:XX:XX:XX:XX:XX (e.g., AA:BB:CC:DD:EE:FF)." 8 58 else exit-script fi done while true; do if VLAN1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a Vlan(leave blank for default)" 8 58 --title "VLAN" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z "$VLAN1" ]; then VLAN1="Default" VLAN="" echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}$VLAN1${CL}" break fi if [[ "$VLAN1" =~ ^[0-9]+$ ]] && [ "$VLAN1" -ge 1 ] && [ "$VLAN1" -le 4094 ]; then VLAN=",tag=$VLAN1" echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}$VLAN1${CL}" break fi whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "VLAN must be a number between 1 and 4094, or leave blank for default." 8 58 else exit-script fi done while true; do if MTU1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Interface MTU Size (leave blank for default)" 8 58 --title "MTU SIZE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z "$MTU1" ]; then MTU1="Default" MTU="" echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}$MTU1${CL}" break fi if [[ "$MTU1" =~ ^[0-9]+$ ]] && [ "$MTU1" -ge 576 ] && [ "$MTU1" -le 65520 ]; then MTU=",mtu=$MTU1" echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}$MTU1${CL}" break fi whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "MTU Size must be a number between 576 and 65520, or leave blank for default." 8 58 else exit-script fi done if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "START VIRTUAL MACHINE" --yesno "Start VM when completed?" 10 58); then echo -e "${GATEWAY}${BOLD}${DGN}Start VM when completed: ${BGN}yes${CL}" START_VM="yes" else echo -e "${GATEWAY}${BOLD}${DGN}Start VM when completed: ${BGN}no${CL}" START_VM="no" fi if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "ADVANCED SETTINGS COMPLETE" --yesno "Ready to create a Ubuntu 22.04 VM?" --no-button Do-Over 10 58); then echo -e "${CREATING}${BOLD}${DGN}Creating a Ubuntu 22.04 VM using the above advanced settings${CL}" else header_info echo -e "${ADVANCED}${BOLD}${RD}Using Advanced Settings${CL}" advanced_settings fi } function start_script() { if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "SETTINGS" --yesno "Use Default Settings?" --no-button Advanced 10 58); then header_info echo -e "${DEFAULT}${BOLD}${BL}Using Default Settings${CL}" default_settings else header_info echo -e "${ADVANCED}${BOLD}${RD}Using Advanced Settings${CL}" advanced_settings fi } check_root arch_check pve_check ssh_check start_script post_to_api_vm msg_info "Validating Storage" while read -r line; do TAG=$(echo $line | awk '{print $1}') TYPE=$(echo $line | awk '{printf "%-10s", $2}') FREE=$(echo $line | numfmt --field 4-6 --from-unit=K --to=iec --format %.2f | awk '{printf( "%9sB", $6)}') ITEM=" Type: $TYPE Free: $FREE " OFFSET=2 if [[ $((${#ITEM} + $OFFSET)) -gt ${MSG_MAX_LENGTH:-} ]]; then MSG_MAX_LENGTH=$((${#ITEM} + $OFFSET)) fi STORAGE_MENU+=("$TAG" "$ITEM" "OFF") done < <(pvesm status -content images | awk 'NR>1') VALID=$(pvesm status -content images | awk 'NR>1') if [ -z "$VALID" ]; then msg_error "Unable to detect a valid storage location." exit elif [ $((${#STORAGE_MENU[@]} / 3)) -eq 1 ]; then STORAGE=${STORAGE_MENU[0]} else while [ -z "${STORAGE:+x}" ]; do STORAGE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "Storage Pools" --radiolist \ "Which storage pool would you like to use for ${HN}?\nTo make a selection, use the Spacebar.\n" \ 16 $(($MSG_MAX_LENGTH + 23)) 6 \ "${STORAGE_MENU[@]}" 3>&1 1>&2 2>&3) done fi msg_ok "Using ${CL}${BL}$STORAGE${CL} ${GN}for Storage Location." msg_ok "Virtual Machine ID is ${CL}${BL}$VMID${CL}." msg_info "Retrieving the URL for the Ubuntu 22.04 Disk Image" URL=https://cloud-images.ubuntu.com/jammy/current/jammy-server-cloudimg-arm64.img sleep 2 msg_ok "${CL}${BL}${URL}${CL}" curl -f#SL -o "$(basename "$URL")" "$URL" echo -en "\e[1A\e[0K" FILE=$(basename $URL) msg_ok "Downloaded ${CL}${BL}${FILE}${CL}" STORAGE_TYPE=$(pvesm status -storage $STORAGE | awk 'NR>1 {print $2}') case $STORAGE_TYPE in nfs | dir | cifs) DISK_EXT=".qcow2" DISK_REF="$VMID/" DISK_IMPORT="-format qcow2" THIN="" ;; btrfs) DISK_EXT=".raw" DISK_REF="$VMID/" DISK_IMPORT="-format raw" FORMAT=",efitype=4m" THIN="" ;; *) DISK_EXT="" DISK_REF="" DISK_IMPORT="-format raw" ;; esac for i in {0,1}; do disk="DISK$i" eval DISK${i}=vm-${VMID}-disk-${i}${DISK_EXT:-} eval DISK${i}_REF=${STORAGE}:${DISK_REF:-}${!disk} done msg_info "Creating a Ubuntu 22.04 VM" qm create $VMID -agent 1${MACHINE} -tablet 0 -localtime 1 -bios ovmf${CPU_TYPE} -cores $CORE_COUNT -memory $RAM_SIZE \ -name $HN -tags community-script -net0 virtio,bridge=$BRG,macaddr=$MAC$VLAN$MTU -onboot 1 -ostype l26 -scsihw virtio-scsi-pci pvesm alloc $STORAGE $VMID $DISK0 64M 1>&/dev/null qm importdisk $VMID ${FILE} $STORAGE ${DISK_IMPORT:-} 1>&/dev/null qm set $VMID \ -efidisk0 ${DISK0_REF}${FORMAT} \ -scsi0 ${DISK1_REF},${DISK_CACHE}${THIN}size=${DISK_SIZE} \ -ide2 ${STORAGE}:cloudinit \ -boot order=scsi0 \ -serial0 socket >/dev/null DESCRIPTION=$( cat <<EOF <div align='center'> <a href='https://community-scripts.org' target='_blank' rel='noopener noreferrer'> <img src='https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/images/logo-81x112.png' alt='Logo' style='width:81px;height:112px;'/> </a> <h2 style='font-size: 24px; margin: 20px 0;'>ubuntu VM</h2> <p style='margin: 16px 0;'> <a href='https://ko-fi.com/community_scripts' target='_blank' rel='noopener noreferrer'> <img src='https://img.shields.io/badge/☕-Buy us a coffee-blue' alt='spend Coffee' /> </a> </p> <span style='margin: 0 10px;'> <i class="fa fa-github fa-fw" style="color: #f5f5f5;"></i> <a href='https://github.com/community-scripts/ProxmoxVE' target='_blank' rel='noopener noreferrer' style='text-decoration: none; color: #00617f;'>GitHub</a> </span> <span style='margin: 0 10px;'> <i class="fa fa-comments fa-fw" style="color: #f5f5f5;"></i> <a href='https://github.com/community-scripts/ProxmoxVE/discussions' target='_blank' rel='noopener noreferrer' style='text-decoration: none; color: #00617f;'>Discussions</a> </span> <span style='margin: 0 10px;'> <i class="fa fa-exclamation-circle fa-fw" style="color: #f5f5f5;"></i> <a href='https://github.com/community-scripts/ProxmoxVE/issues' target='_blank' rel='noopener noreferrer' style='text-decoration: none; color: #00617f;'>Issues</a> </span> </div> EOF ) qm set "$VMID" -description "$DESCRIPTION" >/dev/null msg_ok "Created a Ubuntu 22.04 VM ${CL}${BL}(${HN})" if [ "$START_VM" == "yes" ]; then msg_info "Starting Ubuntu 22.04 VM" qm start $VMID msg_ok "Started Ubuntu 22.04 VM" fi post_update_to_api "done" "none" msg_ok "Completed successfully!\n" echo -e "Setup Cloud-Init before starting \n More info at https://github.com/community-scripts/ProxmoxVE/discussions/272 \n" ================================================ FILE: vm/ubuntu2404-vm.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT # https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE source /dev/stdin <<< $(wget -qLO - https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/api.func) function header_info { clear cat <<"EOF" __ ____ __ ___ __ __ ____ __ __ _ ____ ___ / / / / /_ __ ______ / /___ __ |__ \/ // / / __ \/ // / | | / / |/ / / / / / __ \/ / / / __ \/ __/ / / / __/ / // /_ / / / / // /_ | | / / /|_/ / / /_/ / /_/ / /_/ / / / / /_/ /_/ / / __/__ __// /_/ /__ __/ | |/ / / / / \____/_.___/\__,_/_/ /_/\__/\__,_/ /____/ /_/ (_)____/ /_/ |___/_/ /_/ EOF } header_info echo -e "\n Loading..." GEN_MAC=02:$(openssl rand -hex 5 | awk '{print toupper($0)}' | sed 's/\(..\)/\1:/g; s/.$//') RANDOM_UUID="$(cat /proc/sys/kernel/random/uuid)" METHOD="" NSAPP="ubuntu2404-vm" var_os="ubuntu" var_version="2404" YW=$(echo "\033[33m") BL=$(echo "\033[36m") RD=$(echo "\033[01;31m") BGN=$(echo "\033[4;92m") GN=$(echo "\033[1;92m") DGN=$(echo "\033[32m") CL=$(echo "\033[m") CL=$(echo "\033[m") BOLD=$(echo "\033[1m") BFR="\\r\\033[K" HOLD=" " TAB=" " CM="${TAB}✔️${TAB}${CL}" CROSS="${TAB}✖️${TAB}${CL}" INFO="${TAB}💡${TAB}${CL}" OS="${TAB}🖥️${TAB}${CL}" CONTAINERTYPE="${TAB}📦${TAB}${CL}" DISKSIZE="${TAB}💾${TAB}${CL}" CPUCORE="${TAB}🧠${TAB}${CL}" RAMSIZE="${TAB}🛠️${TAB}${CL}" CONTAINERID="${TAB}🆔${TAB}${CL}" HOSTNAME="${TAB}🏠${TAB}${CL}" BRIDGE="${TAB}🌉${TAB}${CL}" GATEWAY="${TAB}🌐${TAB}${CL}" DEFAULT="${TAB}⚙️${TAB}${CL}" MACADDRESS="${TAB}🔗${TAB}${CL}" VLANTAG="${TAB}🏷️${TAB}${CL}" CREATING="${TAB}🚀${TAB}${CL}" ADVANCED="${TAB}🧩${TAB}${CL}" THIN="discard=on,ssd=1," set -e trap 'error_handler $LINENO "$BASH_COMMAND"' ERR trap cleanup EXIT trap 'post_update_to_api "failed" "130"' SIGINT trap 'post_update_to_api "failed" "143"' SIGTERM trap 'post_update_to_api "failed" "129"; exit 129' SIGHUP function error_handler() { local exit_code="$?" local line_number="$1" local command="$2" post_update_to_api "failed" "$exit_code" local error_message="${RD}[ERROR]${CL} in line ${RD}$line_number${CL}: exit code ${RD}$exit_code${CL}: while executing command ${YW}$command${CL}" echo -e "\n$error_message\n" cleanup_vmid } function get_valid_nextid() { local try_id try_id=$(pvesh get /cluster/nextid) while true; do if [ -f "/etc/pve/qemu-server/${try_id}.conf" ] || [ -f "/etc/pve/lxc/${try_id}.conf" ]; then try_id=$((try_id + 1)) continue fi if lvs --noheadings -o lv_name | grep -qE "(^|[-_])${try_id}($|[-_])"; then try_id=$((try_id + 1)) continue fi break done echo "$try_id" } function cleanup_vmid() { if qm status $VMID &>/dev/null; then qm stop $VMID &>/dev/null qm destroy $VMID &>/dev/null fi } function cleanup() { local exit_code=$? popd >/dev/null if [[ "${POST_TO_API_DONE:-}" == "true" && "${POST_UPDATE_DONE:-}" != "true" ]]; then if [[ $exit_code -eq 0 ]]; then post_update_to_api "done" "none" else post_update_to_api "failed" "$exit_code" fi fi rm -rf $TEMP_DIR } TEMP_DIR=$(mktemp -d) pushd $TEMP_DIR >/dev/null if whiptail --backtitle "Proxmox VE Helper Scripts" --title "Ubuntu 24.04 VM" --yesno "This will create a New Ubuntu 24.04 VM. Proceed?" 10 58; then : else header_info && echo -e "${CROSS}${RD}User exited script${CL}\n" && exit fi function msg_info() { local msg="$1" echo -ne "${TAB}${YW}${HOLD}${msg}${HOLD}" } function msg_ok() { local msg="$1" echo -e "${BFR}${CM}${GN}${msg}${CL}" } function msg_error() { local msg="$1" echo -e "${BFR}${CROSS}${RD}${msg}${CL}" } function check_root() { if [[ "$(id -u)" -ne 0 || $(ps -o comm= -p $PPID) == "sudo" ]]; then clear msg_error "Please run this script as root." echo -e "\nExiting..." sleep 2 exit fi } # This function checks the version of Proxmox Virtual Environment (PVE) and exits if the version is not supported. # Supported: Proxmox VE 8.0.x – 8.9.x, 9.0 and 9.1 pve_check() { local PVE_VER PVE_VER="$(pveversion | awk -F'/' '{print $2}' | awk -F'-' '{print $1}')" # Check for Proxmox VE 8.x: allow 8.0–8.9 if [[ "$PVE_VER" =~ ^8\.([0-9]+) ]]; then local MINOR="${BASH_REMATCH[1]}" if ((MINOR < 0 || MINOR > 9)); then msg_error "This version of Proxmox VE is not supported." msg_error "Supported: Proxmox VE version 8.0 – 8.9" exit 105 fi return 0 fi # Check for Proxmox VE 9.x: allow 9.0 and 9.1 if [[ "$PVE_VER" =~ ^9\.([0-9]+) ]]; then local MINOR="${BASH_REMATCH[1]}" if ((MINOR < 0 || MINOR > 1)); then msg_error "This version of Proxmox VE is not supported." msg_error "Supported: Proxmox VE version 9.0 – 9.1" exit 105 fi return 0 fi # All other unsupported versions msg_error "This version of Proxmox VE is not supported." msg_error "Supported versions: Proxmox VE 8.0 – 8.x or 9.0 – 9.1" exit 105 } function arch_check() { if [ "$(dpkg --print-architecture)" != "arm64" ]; then echo -e "\n ${INFO}${YWB}This script only works on PiMox! \n" echo -e "\n ${YWB}Visit https://github.com/community-scripts/ProxmoxVE for AMD64 support. \n" echo -e "Exiting..." sleep 2 exit fi } function ssh_check() { if command -v pveversion >/dev/null 2>&1; then if [ -n "${SSH_CLIENT:+x}" ]; then if whiptail --backtitle "Proxmox VE Helper Scripts" --defaultno --title "SSH DETECTED" --yesno "It's suggested to use the Proxmox shell instead of SSH, since SSH can create issues while gathering variables. Would you like to proceed with using SSH?" 10 62; then echo "you've been warned" else clear exit fi fi fi } function exit-script() { clear echo -e "\n${CROSS}${RD}User exited script${CL}\n" exit } function default_settings() { VMID=$(get_valid_nextid) FORMAT=",efitype=4m" MACHINE="" DISK_SIZE="7G" DISK_CACHE="" HN="ubuntu" CPU_TYPE="" CORE_COUNT="2" RAM_SIZE="2048" BRG="vmbr0" MAC="$GEN_MAC" VLAN="" MTU="" START_VM="yes" METHOD="default" echo -e "${CONTAINERID}${BOLD}${DGN}Virtual Machine ID: ${BGN}${VMID}${CL}" echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}i440fx${CL}" echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}${DISK_SIZE}${CL}" echo -e "${DISKSIZE}${BOLD}${DGN}Disk Cache: ${BGN}None${CL}" echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}${HN}${CL}" echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}KVM64${CL}" echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}${CORE_COUNT}${CL}" echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}${RAM_SIZE}${CL}" echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}${BRG}${CL}" echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}${MAC}${CL}" echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}Default${CL}" echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}Default${CL}" echo -e "${GATEWAY}${BOLD}${DGN}Start VM when completed: ${BGN}yes${CL}" echo -e "${CREATING}${BOLD}${DGN}Creating a Ubuntu 24.04 VM using the above default settings${CL}" } function advanced_settings() { METHOD="advanced" [ -z "${VMID:-}" ] && VMID=$(get_valid_nextid) while true; do if VMID=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Virtual Machine ID" 8 58 $VMID --title "VIRTUAL MACHINE ID" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z "$VMID" ]; then VMID=$(get_valid_nextid) fi if pct status "$VMID" &>/dev/null || qm status "$VMID" &>/dev/null; then echo -e "${CROSS}${RD} ID $VMID is already in use${CL}" sleep 2 continue fi echo -e "${CONTAINERID}${BOLD}${DGN}Virtual Machine ID: ${BGN}$VMID${CL}" break else exit-script fi done if MACH=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "MACHINE TYPE" --radiolist --cancel-button Exit-Script "Choose Type" 10 58 2 \ "i440fx" "Machine i440fx" ON \ "q35" "Machine q35" OFF \ 3>&1 1>&2 2>&3); then if [ $MACH = q35 ]; then echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}$MACH${CL}" FORMAT="" MACHINE=" -machine q35" else echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}$MACH${CL}" FORMAT=",efitype=4m" MACHINE="" fi else exit-script fi if DISK_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Disk Size in GiB (e.g., 10, 20)" 8 58 "$DISK_SIZE" --title "DISK SIZE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then DISK_SIZE=$(echo "$DISK_SIZE" | tr -d ' ') if [[ "$DISK_SIZE" =~ ^[0-9]+$ ]]; then DISK_SIZE="${DISK_SIZE}G" echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}$DISK_SIZE${CL}" elif [[ "$DISK_SIZE" =~ ^[0-9]+G$ ]]; then echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}$DISK_SIZE${CL}" else echo -e "${DISKSIZE}${BOLD}${RD}Invalid Disk Size. Please use a number (e.g., 10 or 10G).${CL}" exit-script fi else exit-script fi if DISK_CACHE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "DISK CACHE" --radiolist "Choose" --cancel-button Exit-Script 10 58 2 \ "0" "None (Default)" ON \ "1" "Write Through" OFF \ 3>&1 1>&2 2>&3); then if [ $DISK_CACHE = "1" ]; then echo -e "${DISKSIZE}${BOLD}${DGN}Disk Cache: ${BGN}Write Through${CL}" DISK_CACHE="cache=writethrough," else echo -e "${DISKSIZE}${BOLD}${DGN}Disk Cache: ${BGN}None${CL}" DISK_CACHE="" fi else exit-script fi if VM_NAME=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Hostname" 8 58 ubuntu --title "HOSTNAME" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z $VM_NAME ]; then HN="ubuntu" echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}" else HN=$(echo "${VM_NAME,,}" | tr -cs 'a-z0-9-' '-' | sed 's/^-//;s/-$//') if [ "$HN" != "${VM_NAME,,}" ]; then whiptail --backtitle "Proxmox VE Helper Scripts" --title "HOSTNAME ADJUSTED" --msgbox "Invalid characters detected. Hostname has been adjusted to:\n\n $HN" 10 58 fi echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}" fi else exit-script fi if CPU_TYPE1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "CPU MODEL" --radiolist "Choose" --cancel-button Exit-Script 10 58 2 \ "0" "KVM64 (Default)" ON \ "1" "Host" OFF \ 3>&1 1>&2 2>&3); then if [ $CPU_TYPE1 = "1" ]; then echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}Host${CL}" CPU_TYPE=" -cpu host" else echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}KVM64${CL}" CPU_TYPE="" fi else exit-script fi while true; do if CORE_COUNT=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Allocate CPU Cores" 8 58 2 --title "CORE COUNT" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z "$CORE_COUNT" ]; then CORE_COUNT="2"; fi if [[ "$CORE_COUNT" =~ ^[1-9][0-9]*$ ]]; then echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}$CORE_COUNT${CL}" break fi whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "CPU Cores must be a positive integer (e.g., 2)." 8 58 else exit-script fi done while true; do if RAM_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Allocate RAM in MiB" 8 58 2048 --title "RAM" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z "$RAM_SIZE" ]; then RAM_SIZE="2048"; fi if [[ "$RAM_SIZE" =~ ^[1-9][0-9]*$ ]]; then echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}$RAM_SIZE${CL}" break fi whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "RAM Size must be a positive integer in MiB (e.g., 2048)." 8 58 else exit-script fi done if BRG=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a Bridge" 8 58 vmbr0 --title "BRIDGE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z $BRG ]; then BRG="vmbr0" echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}" else echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}" fi else exit-script fi while true; do if MAC1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a MAC Address" 8 58 $GEN_MAC --title "MAC ADDRESS" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z "$MAC1" ]; then MAC="$GEN_MAC" echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}$MAC${CL}" break fi if [[ "$MAC1" =~ ^([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}$ ]]; then MAC="$MAC1" echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}$MAC1${CL}" break fi whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "Invalid MAC address format. Use XX:XX:XX:XX:XX:XX (e.g., AA:BB:CC:DD:EE:FF)." 8 58 else exit-script fi done while true; do if VLAN1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a Vlan(leave blank for default)" 8 58 --title "VLAN" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z "$VLAN1" ]; then VLAN1="Default" VLAN="" echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}$VLAN1${CL}" break fi if [[ "$VLAN1" =~ ^[0-9]+$ ]] && [ "$VLAN1" -ge 1 ] && [ "$VLAN1" -le 4094 ]; then VLAN=",tag=$VLAN1" echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}$VLAN1${CL}" break fi whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "VLAN must be a number between 1 and 4094, or leave blank for default." 8 58 else exit-script fi done while true; do if MTU1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Interface MTU Size (leave blank for default)" 8 58 --title "MTU SIZE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z "$MTU1" ]; then MTU1="Default" MTU="" echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}$MTU1${CL}" break fi if [[ "$MTU1" =~ ^[0-9]+$ ]] && [ "$MTU1" -ge 576 ] && [ "$MTU1" -le 65520 ]; then MTU=",mtu=$MTU1" echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}$MTU1${CL}" break fi whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "MTU Size must be a number between 576 and 65520, or leave blank for default." 8 58 else exit-script fi done if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "START VIRTUAL MACHINE" --yesno "Start VM when completed?" 10 58); then echo -e "${GATEWAY}${BOLD}${DGN}Start VM when completed: ${BGN}yes${CL}" START_VM="yes" else echo -e "${GATEWAY}${BOLD}${DGN}Start VM when completed: ${BGN}no${CL}" START_VM="no" fi if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "ADVANCED SETTINGS COMPLETE" --yesno "Ready to create a Ubuntu 24.04 VM?" --no-button Do-Over 10 58); then echo -e "${CREATING}${BOLD}${DGN}Creating a Ubuntu 24.04 VM using the above advanced settings${CL}" else header_info echo -e "${ADVANCED}${BOLD}${RD}Using Advanced Settings${CL}" advanced_settings fi } function start_script() { if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "SETTINGS" --yesno "Use Default Settings?" --no-button Advanced 10 58); then header_info echo -e "${DEFAULT}${BOLD}${BL}Using Default Settings${CL}" default_settings else header_info echo -e "${ADVANCED}${BOLD}${RD}Using Advanced Settings${CL}" advanced_settings fi } check_root arch_check pve_check ssh_check start_script post_to_api_vm msg_info "Validating Storage" while read -r line; do TAG=$(echo $line | awk '{print $1}') TYPE=$(echo $line | awk '{printf "%-10s", $2}') FREE=$(echo $line | numfmt --field 4-6 --from-unit=K --to=iec --format %.2f | awk '{printf( "%9sB", $6)}') ITEM=" Type: $TYPE Free: $FREE " OFFSET=2 if [[ $((${#ITEM} + $OFFSET)) -gt ${MSG_MAX_LENGTH:-} ]]; then MSG_MAX_LENGTH=$((${#ITEM} + $OFFSET)) fi STORAGE_MENU+=("$TAG" "$ITEM" "OFF") done < <(pvesm status -content images | awk 'NR>1') VALID=$(pvesm status -content images | awk 'NR>1') if [ -z "$VALID" ]; then msg_error "Unable to detect a valid storage location." exit elif [ $((${#STORAGE_MENU[@]} / 3)) -eq 1 ]; then STORAGE=${STORAGE_MENU[0]} else while [ -z "${STORAGE:+x}" ]; do STORAGE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "Storage Pools" --radiolist \ "Which storage pool would you like to use for ${HN}?\nTo make a selection, use the Spacebar.\n" \ 16 $(($MSG_MAX_LENGTH + 23)) 6 \ "${STORAGE_MENU[@]}" 3>&1 1>&2 2>&3) done fi msg_ok "Using ${CL}${BL}$STORAGE${CL} ${GN}for Storage Location." msg_ok "Virtual Machine ID is ${CL}${BL}$VMID${CL}." msg_info "Retrieving the URL for the Ubuntu 24.04 Disk Image" URL=https://cloud-images.ubuntu.com/noble/current/noble-server-cloudimg-arm64.img sleep 2 msg_ok "${CL}${BL}${URL}${CL}" curl -f#SL -o "$(basename "$URL")" "$URL" echo -en "\e[1A\e[0K" FILE=$(basename $URL) msg_ok "Downloaded ${CL}${BL}${FILE}${CL}" STORAGE_TYPE=$(pvesm status -storage $STORAGE | awk 'NR>1 {print $2}') case $STORAGE_TYPE in nfs | dir | cifs) DISK_EXT=".qcow2" DISK_REF="$VMID/" DISK_IMPORT="-format qcow2" THIN="" ;; btrfs) DISK_EXT=".raw" DISK_REF="$VMID/" DISK_IMPORT="-format raw" FORMAT=",efitype=4m" THIN="" ;; *) DISK_EXT="" DISK_REF="" DISK_IMPORT="-format raw" ;; esac for i in {0,1}; do disk="DISK$i" eval DISK${i}=vm-${VMID}-disk-${i}${DISK_EXT:-} eval DISK${i}_REF=${STORAGE}:${DISK_REF:-}${!disk} done msg_info "Creating a Ubuntu 24.04 VM" qm create $VMID -agent 1${MACHINE} -tablet 0 -localtime 1 -bios ovmf${CPU_TYPE} -cores $CORE_COUNT -memory $RAM_SIZE \ -name $HN -tags community-script -net0 virtio,bridge=$BRG,macaddr=$MAC$VLAN$MTU -onboot 1 -ostype l26 -scsihw virtio-scsi-pci pvesm alloc $STORAGE $VMID $DISK0 64M 1>&/dev/null qm importdisk $VMID ${FILE} $STORAGE ${DISK_IMPORT:-} 1>&/dev/null qm set $VMID \ -efidisk0 ${DISK0_REF}${FORMAT} \ -scsi0 ${DISK1_REF},${DISK_CACHE}${THIN}size=${DISK_SIZE} \ -ide2 ${STORAGE}:cloudinit \ -boot order=scsi0 \ -serial0 socket >/dev/null DESCRIPTION=$( cat <<EOF <div align='center'> <a href='https://community-scripts.org' target='_blank' rel='noopener noreferrer'> <img src='https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/images/logo-81x112.png' alt='Logo' style='width:81px;height:112px;'/> </a> <h2 style='font-size: 24px; margin: 20px 0;'>ubuntu VM</h2> <p style='margin: 16px 0;'> <a href='https://ko-fi.com/community_scripts' target='_blank' rel='noopener noreferrer'> <img src='https://img.shields.io/badge/☕-Buy us a coffee-blue' alt='spend Coffee' /> </a> </p> <span style='margin: 0 10px;'> <i class="fa fa-github fa-fw" style="color: #f5f5f5;"></i> <a href='https://github.com/community-scripts/ProxmoxVE' target='_blank' rel='noopener noreferrer' style='text-decoration: none; color: #00617f;'>GitHub</a> </span> <span style='margin: 0 10px;'> <i class="fa fa-comments fa-fw" style="color: #f5f5f5;"></i> <a href='https://github.com/community-scripts/ProxmoxVE/discussions' target='_blank' rel='noopener noreferrer' style='text-decoration: none; color: #00617f;'>Discussions</a> </span> <span style='margin: 0 10px;'> <i class="fa fa-exclamation-circle fa-fw" style="color: #f5f5f5;"></i> <a href='https://github.com/community-scripts/ProxmoxVE/issues' target='_blank' rel='noopener noreferrer' style='text-decoration: none; color: #00617f;'>Issues</a> </span> </div> EOF ) qm set "$VMID" -description "$DESCRIPTION" >/dev/null msg_ok "Created a Ubuntu 24.04 VM ${CL}${BL}(${HN})" if [ "$START_VM" == "yes" ]; then msg_info "Starting Ubuntu 24.04 VM" qm start $VMID msg_ok "Started Ubuntu 24.04 VM" fi post_update_to_api "done" "none" msg_ok "Completed successfully!\n" echo -e "Setup Cloud-Init before starting \n More info at https://github.com/community-scripts/ProxmoxVE/discussions/272 \n" ================================================ FILE: vm/ubuntu2504-vm.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE source /dev/stdin <<<$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func) function header_info { clear cat <<"EOF" __ ____ __ ___ ______ ____ __ __ _ ____ ___ / / / / /_ __ ______ / /___ __ |__ \ / ____// __ \/ // / | | / / |/ / / / / / __ \/ / / / __ \/ __/ / / / __/ //___ \ / / / / // /_ | | / / /|_/ / / /_/ / /_/ / /_/ / / / / /_/ /_/ / / __/____/ // /_/ /__ __/ | |/ / / / / \____/_.___/\__,_/_/ /_/\__/\__,_/ /____/_____(_)____/ /_/ |___/_/ /_/ (Plucky Puffin) EOF } header_info echo -e "\n Loading..." GEN_MAC=02:$(openssl rand -hex 5 | awk '{print toupper($0)}' | sed 's/\(..\)/\1:/g; s/.$//') RANDOM_UUID="$(cat /proc/sys/kernel/random/uuid)" METHOD="" NSAPP="ubuntu2504-vm" var_os="ubuntu" var_version="2504" YW=$(echo "\033[33m") BL=$(echo "\033[36m") RD=$(echo "\033[01;31m") BGN=$(echo "\033[4;92m") GN=$(echo "\033[1;92m") DGN=$(echo "\033[32m") CL=$(echo "\033[m") CL=$(echo "\033[m") BOLD=$(echo "\033[1m") BFR="\\r\\033[K" HOLD=" " TAB=" " CM="${TAB}✔️${TAB}${CL}" CROSS="${TAB}✖️${TAB}${CL}" INFO="${TAB}💡${TAB}${CL}" OS="${TAB}🖥️${TAB}${CL}" CONTAINERTYPE="${TAB}📦${TAB}${CL}" DISKSIZE="${TAB}💾${TAB}${CL}" CPUCORE="${TAB}🧠${TAB}${CL}" RAMSIZE="${TAB}🛠️${TAB}${CL}" CONTAINERID="${TAB}🆔${TAB}${CL}" HOSTNAME="${TAB}🏠${TAB}${CL}" BRIDGE="${TAB}🌉${TAB}${CL}" GATEWAY="${TAB}🌐${TAB}${CL}" DEFAULT="${TAB}⚙️${TAB}${CL}" MACADDRESS="${TAB}🔗${TAB}${CL}" VLANTAG="${TAB}🏷️${TAB}${CL}" CREATING="${TAB}🚀${TAB}${CL}" ADVANCED="${TAB}🧩${TAB}${CL}" THIN="discard=on,ssd=1," set -e trap 'error_handler $LINENO "$BASH_COMMAND"' ERR trap cleanup EXIT trap 'post_update_to_api "failed" "130"' SIGINT trap 'post_update_to_api "failed" "143"' SIGTERM trap 'post_update_to_api "failed" "129"; exit 129' SIGHUP function error_handler() { local exit_code="$?" local line_number="$1" local command="$2" post_update_to_api "failed" "$exit_code" local error_message="${RD}[ERROR]${CL} in line ${RD}$line_number${CL}: exit code ${RD}$exit_code${CL}: while executing command ${YW}$command${CL}" echo -e "\n$error_message\n" cleanup_vmid } function get_valid_nextid() { local try_id try_id=$(pvesh get /cluster/nextid) while true; do if [ -f "/etc/pve/qemu-server/${try_id}.conf" ] || [ -f "/etc/pve/lxc/${try_id}.conf" ]; then try_id=$((try_id + 1)) continue fi if lvs --noheadings -o lv_name | grep -qE "(^|[-_])${try_id}($|[-_])"; then try_id=$((try_id + 1)) continue fi break done echo "$try_id" } function cleanup_vmid() { if qm status $VMID &>/dev/null; then qm stop $VMID &>/dev/null qm destroy $VMID &>/dev/null fi } function cleanup() { local exit_code=$? popd >/dev/null if [[ "${POST_TO_API_DONE:-}" == "true" && "${POST_UPDATE_DONE:-}" != "true" ]]; then if [[ $exit_code -eq 0 ]]; then post_update_to_api "done" "none" else post_update_to_api "failed" "$exit_code" fi fi rm -rf $TEMP_DIR } TEMP_DIR=$(mktemp -d) pushd $TEMP_DIR >/dev/null if whiptail --backtitle "Proxmox VE Helper Scripts" --title "Ubuntu 25.04 VM" --yesno "This will create a New Ubuntu 25.04 VM. Proceed?" 10 58; then : else header_info && echo -e "${CROSS}${RD}User exited script${CL}\n" && exit fi function msg_info() { local msg="$1" echo -ne "${TAB}${YW}${HOLD}${msg}${HOLD}" } function msg_ok() { local msg="$1" echo -e "${BFR}${CM}${GN}${msg}${CL}" } function msg_error() { local msg="$1" echo -e "${BFR}${CROSS}${RD}${msg}${CL}" } function check_root() { if [[ "$(id -u)" -ne 0 || $(ps -o comm= -p $PPID) == "sudo" ]]; then clear msg_error "Please run this script as root." echo -e "\nExiting..." sleep 2 exit fi } # This function checks the version of Proxmox Virtual Environment (PVE) and exits if the version is not supported. # Supported: Proxmox VE 8.0.x – 8.9.x, 9.0 and 9.1 pve_check() { local PVE_VER PVE_VER="$(pveversion | awk -F'/' '{print $2}' | awk -F'-' '{print $1}')" # Check for Proxmox VE 8.x: allow 8.0–8.9 if [[ "$PVE_VER" =~ ^8\.([0-9]+) ]]; then local MINOR="${BASH_REMATCH[1]}" if ((MINOR < 0 || MINOR > 9)); then msg_error "This version of Proxmox VE is not supported." msg_error "Supported: Proxmox VE version 8.0 – 8.9" exit 105 fi return 0 fi # Check for Proxmox VE 9.x: allow 9.0 and 9.1 if [[ "$PVE_VER" =~ ^9\.([0-9]+) ]]; then local MINOR="${BASH_REMATCH[1]}" if ((MINOR < 0 || MINOR > 1)); then msg_error "This version of Proxmox VE is not supported." msg_error "Supported: Proxmox VE version 9.0 – 9.1" exit 105 fi return 0 fi # All other unsupported versions msg_error "This version of Proxmox VE is not supported." msg_error "Supported versions: Proxmox VE 8.0 – 8.x or 9.0 – 9.1" exit 105 } function arch_check() { if [ "$(dpkg --print-architecture)" != "arm64" ]; then echo -e "\n ${INFO}${YWB}This script will not work with Proxmox! \n" echo -e "\n ${YWB}Visit https://github.com/community-scripts/ProxmoxVE for AMD64 support. \n" echo -e "Exiting..." sleep 2 exit fi } function ssh_check() { if command -v pveversion >/dev/null 2>&1; then if [ -n "${SSH_CLIENT:+x}" ]; then if whiptail --backtitle "Proxmox VE Helper Scripts" --defaultno --title "SSH DETECTED" --yesno "It's suggested to use the Proxmox shell instead of SSH, since SSH can create issues while gathering variables. Would you like to proceed with using SSH?" 10 62; then echo "you've been warned" else clear exit fi fi fi } function exit-script() { clear echo -e "\n${CROSS}${RD}User exited script${CL}\n" exit } function default_settings() { VMID=$(get_valid_nextid) FORMAT=",efitype=4m" MACHINE="" DISK_SIZE="7G" DISK_CACHE="" HN="ubuntu" CPU_TYPE="" CORE_COUNT="2" RAM_SIZE="2048" BRG="vmbr0" MAC="$GEN_MAC" VLAN="" MTU="" START_VM="no" METHOD="default" echo -e "${CONTAINERID}${BOLD}${DGN}Virtual Machine ID: ${BGN}${VMID}${CL}" echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}i440fx${CL}" echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}${DISK_SIZE}${CL}" echo -e "${DISKSIZE}${BOLD}${DGN}Disk Cache: ${BGN}None${CL}" echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}${HN}${CL}" echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}KVM64${CL}" echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}${CORE_COUNT}${CL}" echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}${RAM_SIZE}${CL}" echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}${BRG}${CL}" echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}${MAC}${CL}" echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}Default${CL}" echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}Default${CL}" echo -e "${GATEWAY}${BOLD}${DGN}Start VM when completed: ${BGN}yes${CL}" echo -e "${CREATING}${BOLD}${DGN}Creating a Ubuntu 25.04 VM using the above default settings${CL}" } function advanced_settings() { METHOD="advanced" [ -z "${VMID:-}" ] && VMID=$(get_valid_nextid) while true; do if VMID=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Virtual Machine ID" 8 58 $VMID --title "VIRTUAL MACHINE ID" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z "$VMID" ]; then VMID=$(get_valid_nextid) fi if pct status "$VMID" &>/dev/null || qm status "$VMID" &>/dev/null; then echo -e "${CROSS}${RD} ID $VMID is already in use${CL}" sleep 2 continue fi echo -e "${CONTAINERID}${BOLD}${DGN}Virtual Machine ID: ${BGN}$VMID${CL}" break else exit-script fi done if MACH=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "MACHINE TYPE" --radiolist --cancel-button Exit-Script "Choose Type" 10 58 2 \ "i440fx" "Machine i440fx" ON \ "q35" "Machine q35" OFF \ 3>&1 1>&2 2>&3); then if [ $MACH = q35 ]; then echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}$MACH${CL}" FORMAT="" MACHINE=" -machine q35" else echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}$MACH${CL}" FORMAT=",efitype=4m" MACHINE="" fi else exit-script fi if DISK_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Disk Size in GiB (e.g., 10, 20)" 8 58 "$DISK_SIZE" --title "DISK SIZE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then DISK_SIZE=$(echo "$DISK_SIZE" | tr -d ' ') if [[ "$DISK_SIZE" =~ ^[0-9]+$ ]]; then DISK_SIZE="${DISK_SIZE}G" echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}$DISK_SIZE${CL}" elif [[ "$DISK_SIZE" =~ ^[0-9]+G$ ]]; then echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}$DISK_SIZE${CL}" else echo -e "${DISKSIZE}${BOLD}${RD}Invalid Disk Size. Please use a number (e.g., 10 or 10G).${CL}" exit-script fi else exit-script fi if DISK_CACHE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "DISK CACHE" --radiolist "Choose" --cancel-button Exit-Script 10 58 2 \ "0" "None (Default)" ON \ "1" "Write Through" OFF \ 3>&1 1>&2 2>&3); then if [ $DISK_CACHE = "1" ]; then echo -e "${DISKSIZE}${BOLD}${DGN}Disk Cache: ${BGN}Write Through${CL}" DISK_CACHE="cache=writethrough," else echo -e "${DISKSIZE}${BOLD}${DGN}Disk Cache: ${BGN}None${CL}" DISK_CACHE="" fi else exit-script fi if VM_NAME=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Hostname" 8 58 ubuntu --title "HOSTNAME" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z $VM_NAME ]; then HN="ubuntu" echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}" else HN=$(echo "${VM_NAME,,}" | tr -cs 'a-z0-9-' '-' | sed 's/^-//;s/-$//') if [ "$HN" != "${VM_NAME,,}" ]; then whiptail --backtitle "Proxmox VE Helper Scripts" --title "HOSTNAME ADJUSTED" --msgbox "Invalid characters detected. Hostname has been adjusted to:\n\n $HN" 10 58 fi echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}" fi else exit-script fi if CPU_TYPE1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "CPU MODEL" --radiolist "Choose" --cancel-button Exit-Script 10 58 2 \ "0" "KVM64 (Default)" ON \ "1" "Host" OFF \ 3>&1 1>&2 2>&3); then if [ $CPU_TYPE1 = "1" ]; then echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}Host${CL}" CPU_TYPE=" -cpu host" else echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}KVM64${CL}" CPU_TYPE="" fi else exit-script fi while true; do if CORE_COUNT=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Allocate CPU Cores" 8 58 2 --title "CORE COUNT" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z "$CORE_COUNT" ]; then CORE_COUNT="2"; fi if [[ "$CORE_COUNT" =~ ^[1-9][0-9]*$ ]]; then echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}$CORE_COUNT${CL}" break fi whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "CPU Cores must be a positive integer (e.g., 2)." 8 58 else exit-script fi done while true; do if RAM_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Allocate RAM in MiB" 8 58 2048 --title "RAM" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z "$RAM_SIZE" ]; then RAM_SIZE="2048"; fi if [[ "$RAM_SIZE" =~ ^[1-9][0-9]*$ ]]; then echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}$RAM_SIZE${CL}" break fi whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "RAM Size must be a positive integer in MiB (e.g., 2048)." 8 58 else exit-script fi done if BRG=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a Bridge" 8 58 vmbr0 --title "BRIDGE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z $BRG ]; then BRG="vmbr0" echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}" else echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}" fi else exit-script fi while true; do if MAC1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a MAC Address" 8 58 $GEN_MAC --title "MAC ADDRESS" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z "$MAC1" ]; then MAC="$GEN_MAC" echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}$MAC${CL}" break fi if [[ "$MAC1" =~ ^([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}$ ]]; then MAC="$MAC1" echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}$MAC1${CL}" break fi whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "Invalid MAC address format. Use XX:XX:XX:XX:XX:XX (e.g., AA:BB:CC:DD:EE:FF)." 8 58 else exit-script fi done while true; do if VLAN1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a Vlan(leave blank for default)" 8 58 --title "VLAN" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z "$VLAN1" ]; then VLAN1="Default" VLAN="" echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}$VLAN1${CL}" break fi if [[ "$VLAN1" =~ ^[0-9]+$ ]] && [ "$VLAN1" -ge 1 ] && [ "$VLAN1" -le 4094 ]; then VLAN=",tag=$VLAN1" echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}$VLAN1${CL}" break fi whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "VLAN must be a number between 1 and 4094, or leave blank for default." 8 58 else exit-script fi done while true; do if MTU1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Interface MTU Size (leave blank for default)" 8 58 --title "MTU SIZE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z "$MTU1" ]; then MTU1="Default" MTU="" echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}$MTU1${CL}" break fi if [[ "$MTU1" =~ ^[0-9]+$ ]] && [ "$MTU1" -ge 576 ] && [ "$MTU1" -le 65520 ]; then MTU=",mtu=$MTU1" echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}$MTU1${CL}" break fi whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "MTU Size must be a number between 576 and 65520, or leave blank for default." 8 58 else exit-script fi done if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "START VIRTUAL MACHINE" --yesno "Start VM when completed?" 10 58); then echo -e "${GATEWAY}${BOLD}${DGN}Start VM when completed: ${BGN}yes${CL}" START_VM="yes" else echo -e "${GATEWAY}${BOLD}${DGN}Start VM when completed: ${BGN}no${CL}" START_VM="no" fi if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "ADVANCED SETTINGS COMPLETE" --yesno "Ready to create a Ubuntu 25.04 VM?" --no-button Do-Over 10 58); then echo -e "${CREATING}${BOLD}${DGN}Creating a Ubuntu 25.04 VM using the above advanced settings${CL}" else header_info echo -e "${ADVANCED}${BOLD}${RD}Using Advanced Settings${CL}" advanced_settings fi } function start_script() { if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "SETTINGS" --yesno "Use Default Settings?" --no-button Advanced 10 58); then header_info echo -e "${DEFAULT}${BOLD}${BL}Using Default Settings${CL}" default_settings else header_info echo -e "${ADVANCED}${BOLD}${RD}Using Advanced Settings${CL}" advanced_settings fi } check_root arch_check pve_check ssh_check start_script post_to_api_vm msg_info "Validating Storage" while read -r line; do TAG=$(echo $line | awk '{print $1}') TYPE=$(echo $line | awk '{printf "%-10s", $2}') FREE=$(echo $line | numfmt --field 4-6 --from-unit=K --to=iec --format %.2f | awk '{printf( "%9sB", $6)}') ITEM=" Type: $TYPE Free: $FREE " OFFSET=2 if [[ $((${#ITEM} + $OFFSET)) -gt ${MSG_MAX_LENGTH:-} ]]; then MSG_MAX_LENGTH=$((${#ITEM} + $OFFSET)) fi STORAGE_MENU+=("$TAG" "$ITEM" "OFF") done < <(pvesm status -content images | awk 'NR>1') VALID=$(pvesm status -content images | awk 'NR>1') if [ -z "$VALID" ]; then msg_error "Unable to detect a valid storage location." exit elif [ $((${#STORAGE_MENU[@]} / 3)) -eq 1 ]; then STORAGE=${STORAGE_MENU[0]} else while [ -z "${STORAGE:+x}" ]; do STORAGE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "Storage Pools" --radiolist \ "Which storage pool would you like to use for ${HN}?\nTo make a selection, use the Spacebar.\n" \ 16 $(($MSG_MAX_LENGTH + 23)) 6 \ "${STORAGE_MENU[@]}" 3>&1 1>&2 2>&3) done fi msg_ok "Using ${CL}${BL}$STORAGE${CL} ${GN}for Storage Location." msg_ok "Virtual Machine ID is ${CL}${BL}$VMID${CL}." msg_info "Retrieving the URL for the Ubuntu 25.04 Disk Image" URL=https://cloud-images.ubuntu.com/plucky/current/plucky-server-cloudimg-arm64.img sleep 2 msg_ok "${CL}${BL}${URL}${CL}" curl -f#SL -o "$(basename "$URL")" "$URL" echo -en "\e[1A\e[0K" FILE=$(basename $URL) msg_ok "Downloaded ${CL}${BL}${FILE}${CL}" STORAGE_TYPE=$(pvesm status -storage $STORAGE | awk 'NR>1 {print $2}') case $STORAGE_TYPE in nfs | dir | cifs) DISK_EXT=".qcow2" DISK_REF="$VMID/" DISK_IMPORT="-format qcow2" THIN="" ;; btrfs) DISK_EXT=".raw" DISK_REF="$VMID/" DISK_IMPORT="-format raw" FORMAT=",efitype=4m" THIN="" ;; *) DISK_EXT="" DISK_REF="" DISK_IMPORT="-format raw" ;; esac for i in {0,1}; do disk="DISK$i" eval DISK${i}=vm-${VMID}-disk-${i}${DISK_EXT:-} eval DISK${i}_REF=${STORAGE}:${DISK_REF:-}${!disk} done msg_info "Creating a Ubuntu 25.04 VM" qm create $VMID -agent 1${MACHINE} -tablet 0 -localtime 1 -bios ovmf${CPU_TYPE} -cores $CORE_COUNT -memory $RAM_SIZE \ -name $HN -tags community-script -net0 virtio,bridge=$BRG,macaddr=$MAC$VLAN$MTU -onboot 1 -ostype l26 -scsihw virtio-scsi-pci pvesm alloc $STORAGE $VMID $DISK0 64M 1>&/dev/null qm importdisk $VMID ${FILE} $STORAGE ${DISK_IMPORT:-} 1>&/dev/null qm set $VMID \ -efidisk0 ${DISK0_REF}${FORMAT} \ -scsi0 ${DISK1_REF},${DISK_CACHE}${THIN}size=${DISK_SIZE} \ -ide2 ${STORAGE}:cloudinit \ -boot order=scsi0 \ -serial0 socket >/dev/null DESCRIPTION=$( cat <<EOF <div align='center'> <a href='https://community-scripts.org' target='_blank' rel='noopener noreferrer'> <img src='https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/images/logo-81x112.png' alt='Logo' style='width:81px;height:112px;'/> </a> <h2 style='font-size: 24px; margin: 20px 0;'>ubuntu VM</h2> <p style='margin: 16px 0;'> <a href='https://ko-fi.com/community_scripts' target='_blank' rel='noopener noreferrer'> <img src='https://img.shields.io/badge/☕-Buy us a coffee-blue' alt='spend Coffee' /> </a> </p> <span style='margin: 0 10px;'> <i class="fa fa-github fa-fw" style="color: #f5f5f5;"></i> <a href='https://github.com/community-scripts/ProxmoxVE' target='_blank' rel='noopener noreferrer' style='text-decoration: none; color: #00617f;'>GitHub</a> </span> <span style='margin: 0 10px;'> <i class="fa fa-comments fa-fw" style="color: #f5f5f5;"></i> <a href='https://github.com/community-scripts/ProxmoxVE/discussions' target='_blank' rel='noopener noreferrer' style='text-decoration: none; color: #00617f;'>Discussions</a> </span> <span style='margin: 0 10px;'> <i class="fa fa-exclamation-circle fa-fw" style="color: #f5f5f5;"></i> <a href='https://github.com/community-scripts/ProxmoxVE/issues' target='_blank' rel='noopener noreferrer' style='text-decoration: none; color: #00617f;'>Issues</a> </span> </div> EOF ) qm set "$VMID" -description "$DESCRIPTION" >/dev/null msg_ok "Created a Ubuntu 25.04 VM ${CL}${BL}(${HN})" if [ "$START_VM" == "yes" ]; then msg_info "Starting Ubuntu 25.04 VM" qm start $VMID msg_ok "Started Ubuntu 25.04 VM" fi post_update_to_api "done" "none" msg_ok "Completed successfully!\n" echo -e "Setup Cloud-Init before starting \n More info at https://github.com/community-scripts/ProxmoxVE/discussions/272 \n" ================================================ FILE: vm/umbrel-os-vm.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE source /dev/stdin <<<$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func) function header_info { clear cat <<"EOF" __ __ __ __ ____ _____ _ ____ ___ / / / /___ ___ / /_ ________ / / / __ \/ ___/ | | / / |/ / / / / / __ `__ \/ __ \/ ___/ _ \/ / / / / /\__ \ | | / / /|_/ / / /_/ / / / / / / /_/ / / / __/ / / /_/ /___/ / | |/ / / / / \____/_/ /_/ /_/_.___/_/ \___/_/ \____//____/ |___/_/ /_/ EOF } header_info echo -e "\n Loading..." GEN_MAC=02:$(openssl rand -hex 5 | awk '{print toupper($0)}' | sed 's/\(..\)/\1:/g; s/.$//') RANDOM_UUID="$(cat /proc/sys/kernel/random/uuid)" METHOD="" NSAPP="umbrel-os-vm" var_os="umbrel-os" var_version="n.d." YW=$(echo "\033[33m") BL=$(echo "\033[36m") HA=$(echo "\033[1;34m") RD=$(echo "\033[01;31m") BGN=$(echo "\033[4;92m") GN=$(echo "\033[1;92m") DGN=$(echo "\033[32m") CL=$(echo "\033[m") BOLD=$(echo "\033[1m") BFR="\\r\\033[K" HOLD=" " TAB=" " CM="${TAB}✔️${TAB}${CL}" CROSS="${TAB}✖️${TAB}${CL}" INFO="${TAB}💡${TAB}${CL}" OS="${TAB}🖥️${TAB}${CL}" CONTAINERTYPE="${TAB}📦${TAB}${CL}" DISKSIZE="${TAB}💾${TAB}${CL}" CPUCORE="${TAB}🧠${TAB}${CL}" RAMSIZE="${TAB}🛠️${TAB}${CL}" CONTAINERID="${TAB}🆔${TAB}${CL}" HOSTNAME="${TAB}🏠${TAB}${CL}" BRIDGE="${TAB}🌉${TAB}${CL}" GATEWAY="${TAB}🌐${TAB}${CL}" DEFAULT="${TAB}⚙️${TAB}${CL}" MACADDRESS="${TAB}🔗${TAB}${CL}" VLANTAG="${TAB}🏷️${TAB}${CL}" CREATING="${TAB}🚀${TAB}${CL}" ADVANCED="${TAB}🧩${TAB}${CL}" THIN="discard=on,ssd=1," set -e trap 'error_handler $LINENO "$BASH_COMMAND"' ERR trap cleanup EXIT trap 'post_update_to_api "failed" "130"' SIGINT trap 'post_update_to_api "failed" "143"' SIGTERM trap 'post_update_to_api "failed" "129"; exit 129' SIGHUP function error_handler() { local exit_code="$?" local line_number="$1" local command="$2" local error_message="${RD}[ERROR]${CL} in line ${RD}$line_number${CL}: exit code ${RD}$exit_code${CL}: while executing command ${YW}$command${CL}" post_update_to_api "failed" "${exit_code}" echo -e "\n$error_message\n" cleanup_vmid } function get_valid_nextid() { local try_id try_id=$(pvesh get /cluster/nextid) while true; do if [ -f "/etc/pve/qemu-server/${try_id}.conf" ] || [ -f "/etc/pve/lxc/${try_id}.conf" ]; then try_id=$((try_id + 1)) continue fi if lvs --noheadings -o lv_name | grep -qE "(^|[-_])${try_id}($|[-_])"; then try_id=$((try_id + 1)) continue fi break done echo "$try_id" } function cleanup_vmid() { if qm status $VMID &>/dev/null; then qm stop $VMID &>/dev/null qm destroy $VMID &>/dev/null fi } function cleanup() { local exit_code=$? popd >/dev/null if [[ "${POST_TO_API_DONE:-}" == "true" && "${POST_UPDATE_DONE:-}" != "true" ]]; then if [[ $exit_code -eq 0 ]]; then post_update_to_api "done" "none" else post_update_to_api "failed" "$exit_code" fi fi rm -rf $TEMP_DIR } TEMP_DIR=$(mktemp -d) pushd $TEMP_DIR >/dev/null if whiptail --backtitle "Proxmox VE Helper Scripts" --title "Umbrel OS VM" --yesno "This will create a New Umbrel OS VM. Proceed?" 10 58; then : else header_info && echo -e "${CROSS}${RD}User exited script${CL}\n" && exit fi function msg_info() { local msg="$1" echo -ne "${TAB}${YW}${HOLD}${msg}${HOLD}" } function msg_ok() { local msg="$1" echo -e "${BFR}${CM}${GN}${msg}${CL}" } function msg_error() { local msg="$1" echo -e "${BFR}${CROSS}${RD}${msg}${CL}" } function check_root() { if [[ "$(id -u)" -ne 0 || $(ps -o comm= -p $PPID) == "sudo" ]]; then clear msg_error "Please run this script as root." echo -e "\nExiting..." sleep 2 exit fi } # This function checks the version of Proxmox Virtual Environment (PVE) and exits if the version is not supported. # Supported: Proxmox VE 8.0.x – 8.9.x, 9.0 and 9.1 pve_check() { local PVE_VER PVE_VER="$(pveversion | awk -F'/' '{print $2}' | awk -F'-' '{print $1}')" # Check for Proxmox VE 8.x: allow 8.0–8.9 if [[ "$PVE_VER" =~ ^8\.([0-9]+) ]]; then local MINOR="${BASH_REMATCH[1]}" if ((MINOR < 0 || MINOR > 9)); then msg_error "This version of Proxmox VE is not supported." msg_error "Supported: Proxmox VE version 8.0 – 8.9" exit 105 fi return 0 fi # Check for Proxmox VE 9.x: allow 9.0 and 9.1 if [[ "$PVE_VER" =~ ^9\.([0-9]+) ]]; then local MINOR="${BASH_REMATCH[1]}" if ((MINOR < 0 || MINOR > 1)); then msg_error "This version of Proxmox VE is not supported." msg_error "Supported: Proxmox VE version 9.0 – 9.1" exit 105 fi return 0 fi # All other unsupported versions msg_error "This version of Proxmox VE is not supported." msg_error "Supported versions: Proxmox VE 8.0 – 8.x or 9.0 – 9.1" exit 105 } function arch_check() { if [ "$(dpkg --print-architecture)" != "amd64" ]; then echo -e "\n ${INFO}${YWB}This script will not work with PiMox! \n" echo -e "\n ${YWB}Visit https://github.com/asylumexp/Proxmox for ARM64 support. \n" echo -e "Exiting..." sleep 2 exit fi } function ssh_check() { if command -v pveversion >/dev/null 2>&1; then if [ -n "${SSH_CLIENT:+x}" ]; then if whiptail --backtitle "Proxmox VE Helper Scripts" --defaultno --title "SSH DETECTED" --yesno "It's suggested to use the Proxmox shell instead of SSH, since SSH can create issues while gathering variables. Would you like to proceed with using SSH?" 10 62; then echo "you've been warned" else clear exit fi fi fi } function exit-script() { clear echo -e "\n${CROSS}${RD}User exited script${CL}\n" exit } # Ensure pv is installed or abort with instructions function ensure_pv() { if ! command -v pv &>/dev/null; then msg_info "Installing required package: pv" if ! apt-get update -qq &>/dev/null || ! apt-get install -y pv &>/dev/null; then msg_error "Failed to install pv automatically." echo -e "\nPlease run manually on the Proxmox host:\n apt install pv\n" exit 237 fi msg_ok "Installed pv" fi } # Download an .xz file and validate it # Args: $1=url $2=cache_file function download_and_validate_xz() { local url="$1" local file="$2" # If file exists, check validity if [[ -s "$file" ]]; then if xz -t "$file" &>/dev/null; then msg_ok "Using cached image $(basename "$file")" return 0 else msg_error "Cached file $(basename "$file") is corrupted. Deleting and retrying download..." rm -f "$file" fi fi # Download fresh file msg_info "Downloading image: $(basename "$file")" if ! curl -fSL -o "$file" "$url"; then msg_error "Download failed: $url" rm -f "$file" exit 115 fi # Validate again if ! xz -t "$file" &>/dev/null; then msg_error "Downloaded file $(basename "$file") is corrupted. Please try again later." rm -f "$file" exit 115 fi msg_ok "Downloaded and validated $(basename "$file")" } # Extract .xz with pv # Args: $1=cache_file $2=target_img function extract_xz_with_pv() { set -o pipefail local file="$1" local target="$2" msg_info "Decompressing $(basename "$file") to $target" if ! xz -dc "$file" | pv -N "Extracting" >"$target"; then msg_error "Failed to extract $file" rm -f "$target" exit 115 fi msg_ok "Decompressed to $target" } function default_settings() { VMID=$(get_valid_nextid) MACHINE="q35" FORMAT="" DISK_SIZE="32G" HN="umbrelos" CPU_TYPE="" CORE_COUNT="2" RAM_SIZE="4096" BRG="vmbr0" MAC="$GEN_MAC" VLAN="" MTU="" START_VM="yes" METHOD="default" echo -e "${CONTAINERID}${BOLD}${DGN}Virtual Machine ID: ${BGN}${VMID}${CL}" echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}q35${CL}" echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}${DISK_SIZE}${CL}" echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}${HN}${CL}" echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}KVM64${CL}" echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}${CORE_COUNT}${CL}" echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}${RAM_SIZE}${CL}" echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}${BRG}${CL}" echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}${MAC}${CL}" echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}Default${CL}" echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}Default${CL}" echo -e "${GATEWAY}${BOLD}${DGN}Start VM when completed: ${BGN}yes${CL}" echo -e "${CREATING}${BOLD}${DGN}Creating a Umbrel OS VM using the above default settings${CL}" } function advanced_settings() { METHOD="advanced" [ -z "${VMID:-}" ] && VMID=$(get_valid_nextid) while true; do if VMID=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Virtual Machine ID" 8 58 $VMID --title "VIRTUAL MACHINE ID" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z "$VMID" ]; then VMID=$(get_valid_nextid) fi if pct status "$VMID" &>/dev/null || qm status "$VMID" &>/dev/null; then echo -e "${CROSS}${RD} ID $VMID is already in use${CL}" sleep 2 continue fi echo -e "${CONTAINERID}${BOLD}${DGN}Virtual Machine ID: ${BGN}$VMID${CL}" break else exit-script fi done if MACH=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "MACHINE TYPE" --radiolist --cancel-button Exit-Script "Choose Machine Type" 10 58 2 \ "q35" "Modern (PCIe, UEFI, default)" ON \ "i440fx" "Legacy (older compatibility)" OFF \ 3>&1 1>&2 2>&3); then if [ "$MACH" = "q35" ]; then echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}q35${CL}" FORMAT="" MACHINE=" -machine q35" else echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}i440fx${CL}" FORMAT=",efitype=4m" MACHINE="" fi else exit-script fi if DISK_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Disk Size in GiB (e.g., 10, 20)" 8 58 "$DISK_SIZE" --title "DISK SIZE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then DISK_SIZE=$(echo "$DISK_SIZE" | tr -d ' ') if [[ "$DISK_SIZE" =~ ^[0-9]+$ ]]; then DISK_SIZE="${DISK_SIZE}G" echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}$DISK_SIZE${CL}" elif [[ "$DISK_SIZE" =~ ^[0-9]+G$ ]]; then echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}$DISK_SIZE${CL}" else echo -e "${DISKSIZE}${BOLD}${RD}Invalid Disk Size. Please use a number (e.g., 10 or 10G).${CL}" exit-script fi else exit-script fi if DISK_CACHE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "DISK CACHE" --radiolist "Choose" --cancel-button Exit-Script 10 58 2 \ "0" "None (Default)" ON \ "1" "Write Through" OFF \ 3>&1 1>&2 2>&3); then if [ $DISK_CACHE = "1" ]; then echo -e "${DISKSIZE}${BOLD}${DGN}Disk Cache: ${BGN}Write Through${CL}" DISK_CACHE="cache=writethrough," else echo -e "${DISKSIZE}${BOLD}${DGN}Disk Cache: ${BGN}None${CL}" DISK_CACHE="" fi else exit-script fi if VM_NAME=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Hostname" 8 58 umbrelos --title "HOSTNAME" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z $VM_NAME ]; then HN="umbrelos" echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}" else HN=$(echo "${VM_NAME,,}" | tr -cs 'a-z0-9-' '-' | sed 's/^-//;s/-$//') if [ "$HN" != "${VM_NAME,,}" ]; then whiptail --backtitle "Proxmox VE Helper Scripts" --title "HOSTNAME ADJUSTED" --msgbox "Invalid characters detected. Hostname has been adjusted to:\n\n $HN" 10 58 fi echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}" fi else exit-script fi if CPU_TYPE1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "CPU MODEL" --radiolist "Choose CPU Model" --cancel-button Exit-Script 10 58 2 \ "KVM64" "Default – safe for migration/compatibility" ON \ "Host" "Use host CPU features (faster, no migration)" OFF \ 3>&1 1>&2 2>&3); then case "$CPU_TYPE1" in "Host") echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}Host${CL}" CPU_TYPE=" -cpu host" ;; *) echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}KVM64${CL}" CPU_TYPE="" ;; esac else exit-script fi while true; do if CORE_COUNT=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Allocate CPU Cores" 8 58 2 --title "CORE COUNT" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z "$CORE_COUNT" ]; then CORE_COUNT="2"; fi if [[ "$CORE_COUNT" =~ ^[1-9][0-9]*$ ]]; then echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}$CORE_COUNT${CL}" break fi whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "CPU Cores must be a positive integer (e.g., 2)." 8 58 else exit-script fi done while true; do if RAM_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Allocate RAM in MiB" 8 58 2048 --title "RAM" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z "$RAM_SIZE" ]; then RAM_SIZE="2048"; fi if [[ "$RAM_SIZE" =~ ^[1-9][0-9]*$ ]]; then echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}$RAM_SIZE${CL}" break fi whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "RAM Size must be a positive integer in MiB (e.g., 2048)." 8 58 else exit-script fi done if BRG=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a Bridge" 8 58 vmbr0 --title "BRIDGE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z $BRG ]; then BRG="vmbr0" echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}" else echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}" fi else exit-script fi while true; do if MAC1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a MAC Address" 8 58 $GEN_MAC --title "MAC ADDRESS" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z "$MAC1" ]; then MAC="$GEN_MAC" echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}$MAC${CL}" break fi if [[ "$MAC1" =~ ^([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}$ ]]; then MAC="$MAC1" echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}$MAC1${CL}" break fi whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "Invalid MAC address format. Use XX:XX:XX:XX:XX:XX (e.g., AA:BB:CC:DD:EE:FF)." 8 58 else exit-script fi done while true; do if VLAN1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a Vlan(leave blank for default)" 8 58 --title "VLAN" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z "$VLAN1" ]; then VLAN1="Default" VLAN="" echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}$VLAN1${CL}" break fi if [[ "$VLAN1" =~ ^[0-9]+$ ]] && [ "$VLAN1" -ge 1 ] && [ "$VLAN1" -le 4094 ]; then VLAN=",tag=$VLAN1" echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}$VLAN1${CL}" break fi whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "VLAN must be a number between 1 and 4094, or leave blank for default." 8 58 else exit-script fi done while true; do if MTU1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Interface MTU Size (leave blank for default)" 8 58 --title "MTU SIZE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z "$MTU1" ]; then MTU1="Default" MTU="" echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}$MTU1${CL}" break fi if [[ "$MTU1" =~ ^[0-9]+$ ]] && [ "$MTU1" -ge 576 ] && [ "$MTU1" -le 65520 ]; then MTU=",mtu=$MTU1" echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}$MTU1${CL}" break fi whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "MTU Size must be a number between 576 and 65520, or leave blank for default." 8 58 else exit-script fi done if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "START VIRTUAL MACHINE" --yesno "Start VM when completed?" 10 58); then echo -e "${GATEWAY}${BOLD}${DGN}Start VM when completed: ${BGN}yes${CL}" START_VM="yes" else echo -e "${GATEWAY}${BOLD}${DGN}Start VM when completed: ${BGN}no${CL}" START_VM="no" fi if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "ADVANCED SETTINGS COMPLETE" --yesno "Ready to create a Umbrel OS VM?" --no-button Do-Over 10 58); then echo -e "${CREATING}${BOLD}${DGN}Creating a Umbrel OS VM using the above advanced settings${CL}" else header_info echo -e "${ADVANCED}${BOLD}${RD}Using Advanced Settings${CL}" advanced_settings fi } function start_script() { if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "SETTINGS" --yesno "Use Default Settings?" --no-button Advanced 10 58); then header_info echo -e "${DEFAULT}${BOLD}${BL}Using Default Settings${CL}" default_settings else header_info echo -e "${ADVANCED}${BOLD}${RD}Using Advanced Settings${CL}" advanced_settings fi } check_root arch_check pve_check ssh_check ensure_pv start_script post_to_api_vm msg_info "Validating Storage" STORAGE_MENU=() while read -r tag type free; do ITEM="Type: $type Free: $free" STORAGE_MENU+=("$tag" "$ITEM" "OFF") done < <(pvesm status -content images | awk 'NR>1 {printf "%s %s %s\n", $1, $2, $6}') if [ ${#STORAGE_MENU[@]} -eq 0 ]; then msg_error "Unable to detect a valid storage location." exit 119 elif [ $((${#STORAGE_MENU[@]} / 3)) -eq 1 ]; then STORAGE=${STORAGE_MENU[0]} else while [ -z "${STORAGE:+x}" ]; do STORAGE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "Storage Pools" --radiolist \ "Which storage pool would you like to use for ${HN}?\nTo make a selection, use the Spacebar.\n" \ 16 70 6 \ "${STORAGE_MENU[@]}" 3>&1 1>&2 2>&3) done fi msg_ok "Using ${CL}${BL}$STORAGE${CL} ${GN}for Storage Location." msg_ok "Virtual Machine ID is ${CL}${BL}$VMID${CL}." URL="https://download.umbrel.com/release/latest/umbrelos-amd64.img.xz" CACHE_DIR="/var/lib/vz/template/cache" CACHE_FILE="$CACHE_DIR/$(basename "$URL")" FILE_IMG="/var/lib/vz/template/tmp/${CACHE_FILE##*/%.xz}" mkdir -p "$CACHE_DIR" "$(dirname "$FILE_IMG")" download_and_validate_xz "$URL" "$CACHE_FILE" qm create $VMID -machine q35 -bios ovmf -agent 1 -tablet 0 -localtime 1 ${CPU_TYPE} \ -cores "$CORE_COUNT" -memory "$RAM_SIZE" -name "$HN" -tags community-script \ -net0 "virtio,bridge=$BRG,macaddr=$MAC$VLAN$MTU" -onboot 1 -ostype l26 -scsihw virtio-scsi-pci >/dev/null extract_xz_with_pv "$CACHE_FILE" "$FILE_IMG" if qm disk import --help >/dev/null 2>&1; then IMPORT_CMD=(qm disk import) else IMPORT_CMD=(qm importdisk) fi IMPORT_OUT="$("${IMPORT_CMD[@]}" "$VMID" "$FILE_IMG" "$STORAGE" --format raw 2>&1 || true)" DISK_REF="$(printf '%s\n' "$IMPORT_OUT" | sed -n "s/.*imported disk '\([^']\+\)'.*/\1/p" | tr -d "\r\"'")" [[ -z "$DISK_REF" ]] && DISK_REF="$(pvesm list "$STORAGE" | awk -v id="$VMID" '$5 ~ ("vm-"id"-disk-") {print $1":"$5}' | sort | tail -n1)" qm set $VMID \ --efidisk0 ${STORAGE}:0,efitype=4m \ --scsi0 ${DISK_REF},ssd=1,discard=on \ --boot order=scsi0 \ --serial0 socket >/dev/null qm set $VMID --agent enabled=1 >/dev/null qm resize $VMID scsi0 ${DISK_SIZE} >/dev/null DESCRIPTION=$( cat <<EOF <div align='center'> <a href='https://community-scripts.org' target='_blank' rel='noopener noreferrer'> <img src='https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/images/logo-81x112.png' alt='Logo' style='width:81px;height:112px;'/> </a> <h2 style='font-size: 24px; margin: 20px 0;'>Umbrel OS VM</h2> <p style='margin: 16px 0;'> <a href='https://ko-fi.com/community_scripts' target='_blank' rel='noopener noreferrer'> <img src='https://img.shields.io/badge/☕-Buy us a coffee-blue' alt='spend Coffee' /> </a> </p> <span style='margin: 0 10px;'> <i class="fa fa-github fa-fw" style="color: #f5f5f5;"></i> <a href='https://github.com/community-scripts/ProxmoxVE' target='_blank' rel='noopener noreferrer' style='text-decoration: none; color: #00617f;'>GitHub</a> </span> <span style='margin: 0 10px;'> <i class="fa fa-comments fa-fw" style="color: #f5f5f5;"></i> <a href='https://github.com/community-scripts/ProxmoxVE/discussions' target='_blank' rel='noopener noreferrer' style='text-decoration: none; color: #00617f;'>Discussions</a> </span> <span style='margin: 0 10px;'> <i class="fa fa-exclamation-circle fa-fw" style="color: #f5f5f5;"></i> <a href='https://github.com/community-scripts/ProxmoxVE/issues' target='_blank' rel='noopener noreferrer' style='text-decoration: none; color: #00617f;'>Issues</a> </span> </div> EOF ) qm set $VMID -description "$DESCRIPTION" >/dev/null if whiptail --backtitle "Proxmox VE Helper Scripts" --title "Image Cache" \ --yesno "Keep downloaded Umbrel OS image for future VMs?\n\nFile: $CACHE_FILE" 10 70; then msg_ok "Keeping cached image" else rm -f "$CACHE_FILE" msg_ok "Deleted cached image" fi rm -f "$FILE_IMG" msg_ok "Created a Umbrel OS VM ${CL}${BL}(${HN})" if [ "$START_VM" == "yes" ]; then msg_info "Starting Umbrel OS VM" qm start $VMID msg_ok "Started Umbrel OS VM" fi post_update_to_api "done" "none" msg_ok "Completed successfully!\n"