Repository: community-scripts/ProxmoxVE Branch: main Commit: fd96117bc470 Files: 1668 Total size: 4.8 MB Directory structure: gitextract_rcxjurtj/ ├── .editorconfig ├── .gitattributes ├── .github/ │ ├── CODEOWNERS │ ├── CODE_OF_CONDUCT.md │ ├── 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 │ ├── pull_request_template.md │ └── workflows/ │ ├── auto-update-app-headers.yml │ ├── autolabeler.yml │ ├── changelog-archive.yml │ ├── changelog-pr.yml │ ├── check-node-versions.yml │ ├── close-new-script-prs.yml │ ├── close-tteck-issues.yaml │ ├── close_issue_in_dev.yaml │ ├── delete-pocketbase-entry-on-removal.yml │ ├── github-release.yml │ ├── lock-issue.yaml │ ├── pocketbase-bot.yml │ ├── push-json-to-pocketbase.yml │ ├── scripts/ │ │ └── generate-app-headers.sh │ ├── stale_pr_close.yml │ ├── trigger_github_pages_redirect.yml │ └── update-script-timestamp-on-sh-change.yml ├── .vscode/ │ ├── .editorconfig │ ├── .shellcheckrc │ └── extensions.json ├── CHANGELOG.md ├── LICENSE ├── README.md ├── SECURITY.md ├── ct/ │ ├── 2fauth.sh │ ├── actualbudget.sh │ ├── adguard.sh │ ├── adventurelog.sh │ ├── agentdvr.sh │ ├── alpine-adguard.sh │ ├── alpine-bitmagnet.sh │ ├── alpine-caddy.sh │ ├── alpine-docker.sh │ ├── alpine-forgejo.sh │ ├── alpine-garage.sh │ ├── alpine-gatus.sh │ ├── alpine-gitea.sh │ ├── alpine-grafana.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-wireguard.sh │ ├── alpine-zigbee2mqtt.sh │ ├── alpine.sh │ ├── ampache.sh │ ├── anytype-server.sh │ ├── apache-cassandra.sh │ ├── apache-couchdb.sh │ ├── apache-guacamole.sh │ ├── apache-tika.sh │ ├── apache-tomcat.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 │ ├── bar-assistant.sh │ ├── bazarr.sh │ ├── bentopdf.sh │ ├── beszel.sh │ ├── bichon.sh │ ├── bitmagnet.sh │ ├── blocky.sh │ ├── booklore.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 │ ├── cosmos.sh │ ├── crafty-controller.sh │ ├── cronicle.sh │ ├── cross-seed.sh │ ├── cryptpad.sh │ ├── daemonsync.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 │ ├── drawio.sh │ ├── duplicati.sh │ ├── ebusd.sh │ ├── elementsynapse.sh │ ├── emby.sh │ ├── emqx.sh │ ├── endurain.sh │ ├── ersatztv.sh │ ├── esphome.sh │ ├── evcc.sh │ ├── excalidraw.sh │ ├── fhem.sh │ ├── fileflows.sh │ ├── firefly.sh │ ├── fladder.sh │ ├── flaresolverr.sh │ ├── flatnotes.sh │ ├── flowiseai.sh │ ├── fluid-calendar.sh │ ├── forgejo.sh │ ├── freepbx.sh │ ├── freshrss.sh │ ├── frigate.sh │ ├── fumadocs.sh │ ├── garage.sh │ ├── gatus.sh │ ├── ghost.sh │ ├── ghostfolio.sh │ ├── gitea-mirror.sh │ ├── gitea.sh │ ├── glance.sh │ ├── globaleaks.sh │ ├── glpi.sh │ ├── gluetun.sh │ ├── go2rtc.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-caddy │ │ ├── alpine-docker │ │ ├── alpine-forgejo │ │ ├── alpine-garage │ │ ├── alpine-gatus │ │ ├── alpine-gitea │ │ ├── alpine-grafana │ │ ├── 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-wireguard │ │ ├── alpine-zigbee2mqtt │ │ ├── ampache │ │ ├── anytype-server │ │ ├── apache-cassandra │ │ ├── apache-couchdb │ │ ├── apache-guacamole │ │ ├── apache-tika │ │ ├── apache-tomcat │ │ ├── apt-cacher-ng │ │ ├── archivebox │ │ ├── argus │ │ ├── aria2 │ │ ├── asterisk │ │ ├── audiobookshelf │ │ ├── authelia │ │ ├── autobrr │ │ ├── autocaliweb │ │ ├── babybuddy │ │ ├── backrest │ │ ├── baikal │ │ ├── bar-assistant │ │ ├── bazarr │ │ ├── bentopdf │ │ ├── beszel │ │ ├── bichon │ │ ├── bitmagnet │ │ ├── blocky │ │ ├── booklore │ │ ├── bookstack │ │ ├── bunkerweb │ │ ├── byparr │ │ ├── bytestash │ │ ├── caddy │ │ ├── calibre-web │ │ ├── casaos │ │ ├── changedetection │ │ ├── channels │ │ ├── checkmate │ │ ├── checkmk │ │ ├── cleanuparr │ │ ├── cloudflare-ddns │ │ ├── cloudflared │ │ ├── cloudreve │ │ ├── cockpit │ │ ├── comfyui │ │ ├── commafeed │ │ ├── configarr │ │ ├── convertx │ │ ├── coolify │ │ ├── cosmos │ │ ├── crafty-controller │ │ ├── cronicle │ │ ├── cross-seed │ │ ├── cryptpad │ │ ├── daemonsync │ │ ├── databasus │ │ ├── dawarich │ │ ├── ddclient │ │ ├── debian │ │ ├── deconz │ │ ├── deluge │ │ ├── discopanel │ │ ├── dispatcharr │ │ ├── docker │ │ ├── dockge │ │ ├── docmost │ │ ├── dokploy │ │ ├── dolibarr │ │ ├── domain-locker │ │ ├── domain-monitor │ │ ├── donetick │ │ ├── dotnetaspwebapi │ │ ├── drawio │ │ ├── duplicati │ │ ├── ebusd │ │ ├── elementsynapse │ │ ├── emby │ │ ├── emqx │ │ ├── endurain │ │ ├── ersatztv │ │ ├── esphome │ │ ├── evcc │ │ ├── excalidraw │ │ ├── fhem │ │ ├── fileflows │ │ ├── firefly │ │ ├── fladder │ │ ├── flaresolverr │ │ ├── flatnotes │ │ ├── flowiseai │ │ ├── fluid-calendar │ │ ├── forgejo │ │ ├── freepbx │ │ ├── freshrss │ │ ├── frigate │ │ ├── fumadocs │ │ ├── garage │ │ ├── gatus │ │ ├── ghost │ │ ├── ghostfolio │ │ ├── gitea │ │ ├── gitea-mirror │ │ ├── glance │ │ ├── globaleaks │ │ ├── glpi │ │ ├── gluetun │ │ ├── go2rtc │ │ ├── gokapi │ │ ├── gotify │ │ ├── grafana │ │ ├── gramps-web │ │ ├── graylog │ │ ├── grist │ │ ├── grocy │ │ ├── guardian │ │ ├── gwn-manager │ │ ├── headscale │ │ ├── healthchecks │ │ ├── heimdall-dashboard │ │ ├── hev-socks5-server │ │ ├── hivemq │ │ ├── homarr │ │ ├── homeassistant │ │ ├── homebox │ │ ├── homebridge │ │ ├── homepage │ │ ├── homer │ │ ├── hortusfox │ │ ├── hyperhdr │ │ ├── hyperion │ │ ├── immich │ │ ├── immichframe │ │ ├── infisical │ │ ├── influxdb │ │ ├── inspircd │ │ ├── inventree │ │ ├── investbrain │ │ ├── invoiceninja │ │ ├── iobroker │ │ ├── itsm-ng │ │ ├── jackett │ │ ├── jeedom │ │ ├── jellyfin │ │ ├── jellyseerr │ │ ├── jenkins │ │ ├── joplin-server │ │ ├── jotty │ │ ├── jupyternotebook │ │ ├── kapowarr │ │ ├── karakeep │ │ ├── kasm │ │ ├── kavita │ │ ├── keycloak │ │ ├── kima-hub │ │ ├── kimai │ │ ├── kitchenowl │ │ ├── koel │ │ ├── koillection │ │ ├── kometa │ │ ├── komga │ │ ├── komodo │ │ ├── kubo │ │ ├── kutt │ │ ├── languagetool │ │ ├── lazylibrarian │ │ ├── leantime │ │ ├── librenms │ │ ├── librespeed-rust │ │ ├── libretranslate │ │ ├── lidarr │ │ ├── limesurvey │ │ ├── linkding │ │ ├── linkstack │ │ ├── linkwarden │ │ ├── listmonk │ │ ├── litellm │ │ ├── livebook │ │ ├── lldap │ │ ├── loki │ │ ├── lubelogger │ │ ├── lyrionmusicserver │ │ ├── mafl │ │ ├── magicmirror │ │ ├── mail-archiver │ │ ├── managemydamnlife │ │ ├── manyfold │ │ ├── mariadb │ │ ├── matterbridge │ │ ├── mattermost │ │ ├── mealie │ │ ├── mediamanager │ │ ├── mediamtx │ │ ├── medusa │ │ ├── meilisearch │ │ ├── memos │ │ ├── meshcentral │ │ ├── metabase │ │ ├── metube │ │ ├── minarca │ │ ├── miniflux │ │ ├── minio │ │ ├── mongodb │ │ ├── monica │ │ ├── motioneye │ │ ├── mqtt │ │ ├── myip │ │ ├── mylar3 │ │ ├── myspeed │ │ ├── mysql │ │ ├── n8n │ │ ├── navidrome │ │ ├── neo4j │ │ ├── netbird │ │ ├── netbox │ │ ├── netvisor │ │ ├── nextcloudpi │ │ ├── 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 │ │ ├── openwebui │ │ ├── openziti-controller │ │ ├── openziti-tunnel │ │ ├── ots │ │ ├── outline │ │ ├── overseerr │ │ ├── owncast │ │ ├── pairdrop │ │ ├── pangolin │ │ ├── paperless-ai │ │ ├── paperless-gpt │ │ ├── paperless-ngx │ │ ├── papra │ │ ├── part-db │ │ ├── passbolt │ │ ├── patchmon │ │ ├── paymenter │ │ ├── peanut │ │ ├── pelican-panel │ │ ├── pelican-wings │ │ ├── pf2etools │ │ ├── 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 │ │ ├── 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 │ │ ├── signoz │ │ ├── silverbullet │ │ ├── slskd │ │ ├── smokeping │ │ ├── snipeit │ │ ├── snowshare │ │ ├── sonarqube │ │ ├── sonarr │ │ ├── sonobarr │ │ ├── sparkyfitness │ │ ├── speedtest-tracker │ │ ├── split-pro │ │ ├── splunk-enterprise │ │ ├── spoolman │ │ ├── sportarr │ │ ├── sqlserver2022 │ │ ├── sqlserver2025 │ │ ├── stirling-pdf │ │ ├── strapi │ │ ├── streamlink-webui │ │ ├── stylus │ │ ├── sure │ │ ├── swizzin │ │ ├── syncthing │ │ ├── tandoor │ │ ├── tasmoadmin │ │ ├── tasmocompiler │ │ ├── tautulli │ │ ├── tdarr │ │ ├── teamspeak-server │ │ ├── technitiumdns │ │ ├── teddycloud │ │ ├── telegraf │ │ ├── termix │ │ ├── the-lounge │ │ ├── thingsboard │ │ ├── threadfin │ │ ├── tianji │ │ ├── tinyauth │ │ ├── traccar │ │ ├── tracearr │ │ ├── tracktor │ │ ├── traefik │ │ ├── transmission │ │ ├── trilium │ │ ├── trip │ │ ├── tududi │ │ ├── tunarr │ │ ├── twingate-connector │ │ ├── typesense │ │ ├── ubuntu │ │ ├── uhf │ │ ├── umami │ │ ├── umlautadaptarr │ │ ├── unbound │ │ ├── unifi-os-server │ │ ├── unmanic │ │ ├── upgopher │ │ ├── upsnap │ │ ├── uptimekuma │ │ ├── urbackupserver │ │ ├── valkey │ │ ├── vaultwarden │ │ ├── verdaccio │ │ ├── victoriametrics │ │ ├── vikunja │ │ ├── wallabag │ │ ├── wallos │ │ ├── wanderer │ │ ├── warracker │ │ ├── wastebin │ │ ├── watcharr │ │ ├── watchyourlan │ │ ├── wavelog │ │ ├── wazuh │ │ ├── wealthfolio │ │ ├── web-check │ │ ├── wger │ │ ├── whisparr │ │ ├── wikijs │ │ ├── wireguard │ │ ├── wishlist │ │ ├── wizarr │ │ ├── wordpress │ │ ├── writefreely │ │ ├── yamtrack │ │ ├── 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 │ ├── homepage.sh │ ├── homer.sh │ ├── hortusfox.sh │ ├── hyperhdr.sh │ ├── hyperion.sh │ ├── immich.sh │ ├── immichframe.sh │ ├── infisical.sh │ ├── influxdb.sh │ ├── inspircd.sh │ ├── inventree.sh │ ├── investbrain.sh │ ├── invoiceninja.sh │ ├── iobroker.sh │ ├── itsm-ng.sh │ ├── jackett.sh │ ├── jeedom.sh │ ├── jellyfin.sh │ ├── jellyseerr.sh │ ├── jenkins.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 │ ├── librenms.sh │ ├── librespeed-rust.sh │ ├── libretranslate.sh │ ├── lidarr.sh │ ├── limesurvey.sh │ ├── linkding.sh │ ├── linkstack.sh │ ├── linkwarden.sh │ ├── listmonk.sh │ ├── litellm.sh │ ├── livebook.sh │ ├── lldap.sh │ ├── loki.sh │ ├── lubelogger.sh │ ├── lyrionmusicserver.sh │ ├── mafl.sh │ ├── magicmirror.sh │ ├── mail-archiver.sh │ ├── managemydamnlife.sh │ ├── manyfold.sh │ ├── mariadb.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 │ ├── miniflux.sh │ ├── minio.sh │ ├── mongodb.sh │ ├── monica.sh │ ├── motioneye.sh │ ├── mqtt.sh │ ├── myip.sh │ ├── mylar3.sh │ ├── myspeed.sh │ ├── mysql.sh │ ├── n8n.sh │ ├── navidrome.sh │ ├── neo4j.sh │ ├── netbird.sh │ ├── netbox.sh │ ├── netvisor.sh │ ├── nextcloudpi.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 │ ├── openwebui.sh │ ├── openziti-controller.sh │ ├── openziti-tunnel.sh │ ├── ots.sh │ ├── outline.sh │ ├── overseerr.sh │ ├── owncast.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 │ ├── pf2etools.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 │ ├── 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 │ ├── signoz.sh │ ├── silverbullet.sh │ ├── slskd.sh │ ├── smokeping.sh │ ├── snipeit.sh │ ├── snowshare.sh │ ├── sonarqube.sh │ ├── sonarr.sh │ ├── sonobarr.sh │ ├── sparkyfitness.sh │ ├── speedtest-tracker.sh │ ├── split-pro.sh │ ├── splunk-enterprise.sh │ ├── spoolman.sh │ ├── sportarr.sh │ ├── sqlserver2022.sh │ ├── sqlserver2025.sh │ ├── stirling-pdf.sh │ ├── strapi.sh │ ├── streamlink-webui.sh │ ├── stylus.sh │ ├── sure.sh │ ├── swizzin.sh │ ├── syncthing.sh │ ├── tandoor.sh │ ├── tasmoadmin.sh │ ├── tasmocompiler.sh │ ├── tautulli.sh │ ├── tdarr.sh │ ├── teamspeak-server.sh │ ├── technitiumdns.sh │ ├── teddycloud.sh │ ├── telegraf.sh │ ├── termix.sh │ ├── the-lounge.sh │ ├── thingsboard.sh │ ├── threadfin.sh │ ├── tianji.sh │ ├── tinyauth.sh │ ├── traccar.sh │ ├── tracearr.sh │ ├── tracktor.sh │ ├── traefik.sh │ ├── transmission.sh │ ├── trilium.sh │ ├── trip.sh │ ├── tududi.sh │ ├── tunarr.sh │ ├── twingate-connector.sh │ ├── typesense.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 │ ├── verdaccio.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 │ ├── wikijs.sh │ ├── wireguard.sh │ ├── wishlist.sh │ ├── wizarr.sh │ ├── wordpress.sh │ ├── writefreely.sh │ ├── yamtrack.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 ├── docs/ │ ├── DEV_MODE.md │ ├── EXIT_CODES.md │ ├── README.md │ ├── TECHNICAL_REFERENCE.md │ ├── api/ │ │ └── README.md │ ├── contribution/ │ │ ├── AI.md │ │ ├── CODE-AUDIT.md │ │ ├── CONTRIBUTING.md │ │ ├── FORK_SETUP.md │ │ ├── GUIDE.md │ │ ├── HELPER_FUNCTIONS.md │ │ ├── README.md │ │ ├── setup-fork.sh │ │ ├── templates_ct/ │ │ │ ├── AppName.md │ │ │ └── AppName.sh │ │ ├── templates_install/ │ │ │ ├── AppName-install.md │ │ │ └── AppName-install.sh │ │ └── templates_json/ │ │ ├── AppName.json │ │ └── AppName.md │ ├── ct/ │ │ ├── DETAILED_GUIDE.md │ │ └── README.md │ ├── guides/ │ │ ├── CONFIGURATION_REFERENCE.md │ │ ├── DEFAULTS_SYSTEM_GUIDE.md │ │ ├── README.md │ │ ├── UNATTENDED_DEPLOYMENTS.md │ │ └── USER_SUBMITTED_GUIDES.md │ ├── install/ │ │ ├── DETAILED_GUIDE.md │ │ └── README.md │ ├── misc/ │ │ ├── README.md │ │ ├── alpine-install.func/ │ │ │ ├── ALPINE_INSTALL_FUNC_FLOWCHART.md │ │ │ ├── ALPINE_INSTALL_FUNC_FUNCTIONS_REFERENCE.md │ │ │ ├── ALPINE_INSTALL_FUNC_INTEGRATION.md │ │ │ ├── ALPINE_INSTALL_FUNC_USAGE_EXAMPLES.md │ │ │ └── README.md │ │ ├── alpine-tools.func/ │ │ │ ├── ALPINE_TOOLS_FUNC_FLOWCHART.md │ │ │ ├── ALPINE_TOOLS_FUNC_FUNCTIONS_REFERENCE.md │ │ │ ├── ALPINE_TOOLS_FUNC_INTEGRATION.md │ │ │ ├── ALPINE_TOOLS_FUNC_USAGE_EXAMPLES.md │ │ │ └── README.md │ │ ├── api.func/ │ │ │ ├── API_FLOWCHART.md │ │ │ ├── API_FUNCTIONS_REFERENCE.md │ │ │ ├── API_INTEGRATION.md │ │ │ ├── API_USAGE_EXAMPLES.md │ │ │ └── README.md │ │ ├── build.func/ │ │ │ ├── BUILD_FUNC_ADVANCED_SETTINGS.md │ │ │ ├── BUILD_FUNC_ARCHITECTURE.md │ │ │ ├── BUILD_FUNC_ENVIRONMENT_VARIABLES.md │ │ │ ├── BUILD_FUNC_EXECUTION_FLOWS.md │ │ │ ├── BUILD_FUNC_FLOWCHART.md │ │ │ ├── BUILD_FUNC_FUNCTIONS_REFERENCE.md │ │ │ ├── BUILD_FUNC_USAGE_EXAMPLES.md │ │ │ └── README.md │ │ ├── cloud-init.func/ │ │ │ ├── CLOUD_INIT_FUNC_FLOWCHART.md │ │ │ ├── CLOUD_INIT_FUNC_FUNCTIONS_REFERENCE.md │ │ │ ├── CLOUD_INIT_FUNC_INTEGRATION.md │ │ │ ├── CLOUD_INIT_FUNC_USAGE_EXAMPLES.md │ │ │ └── README.md │ │ ├── core.func/ │ │ │ ├── CORE_FLOWCHART.md │ │ │ ├── CORE_FUNCTIONS_REFERENCE.md │ │ │ ├── CORE_INTEGRATION.md │ │ │ ├── CORE_USAGE_EXAMPLES.md │ │ │ └── README.md │ │ ├── error_handler.func/ │ │ │ ├── ERROR_HANDLER_FLOWCHART.md │ │ │ ├── ERROR_HANDLER_FUNCTIONS_REFERENCE.md │ │ │ ├── ERROR_HANDLER_INTEGRATION.md │ │ │ ├── ERROR_HANDLER_USAGE_EXAMPLES.md │ │ │ └── README.md │ │ ├── install.func/ │ │ │ ├── INSTALL_FUNC_FLOWCHART.md │ │ │ ├── INSTALL_FUNC_FUNCTIONS_REFERENCE.md │ │ │ ├── INSTALL_FUNC_INTEGRATION.md │ │ │ ├── INSTALL_FUNC_USAGE_EXAMPLES.md │ │ │ └── README.md │ │ └── tools.func/ │ │ ├── README.md │ │ ├── TOOLS_FUNC_FLOWCHART.md │ │ ├── TOOLS_FUNC_FUNCTIONS_REFERENCE.md │ │ ├── TOOLS_FUNC_INTEGRATION.md │ │ └── TOOLS_FUNC_USAGE_EXAMPLES.md │ ├── tools/ │ │ └── README.md │ └── vm/ │ └── README.md ├── 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-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-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-wireguard-install.sh │ ├── alpine-zigbee2mqtt-install.sh │ ├── ampache-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 │ ├── 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 │ ├── bar-assistant-install.sh │ ├── bazarr-install.sh │ ├── bentopdf-install.sh │ ├── beszel-install.sh │ ├── bichon-install.sh │ ├── bitmagnet-install.sh │ ├── blocky-install.sh │ ├── booklore-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 │ ├── cosmos-install.sh │ ├── crafty-controller-install.sh │ ├── cronicle-install.sh │ ├── cross-seed-install.sh │ ├── cryptpad-install.sh │ ├── daemonsync-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 │ ├── drawio-install.sh │ ├── duplicati-install.sh │ ├── ebusd-install.sh │ ├── elementsynapse-install.sh │ ├── emby-install.sh │ ├── emqx-install.sh │ ├── endurain-install.sh │ ├── ersatztv-install.sh │ ├── esphome-install.sh │ ├── evcc-install.sh │ ├── excalidraw-install.sh │ ├── fhem-install.sh │ ├── fileflows-install.sh │ ├── firefly-install.sh │ ├── fladder-install.sh │ ├── flaresolverr-install.sh │ ├── flatnotes-install.sh │ ├── flowiseai-install.sh │ ├── fluid-calendar-install.sh │ ├── forgejo-install.sh │ ├── freepbx-install.sh │ ├── freshrss-install.sh │ ├── frigate-install.sh │ ├── fumadocs-install.sh │ ├── garage-install.sh │ ├── gatus-install.sh │ ├── ghost-install.sh │ ├── ghostfolio-install.sh │ ├── gitea-install.sh │ ├── gitea-mirror-install.sh │ ├── glance-install.sh │ ├── globaleaks-install.sh │ ├── glpi-install.sh │ ├── gluetun-install.sh │ ├── go2rtc-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 │ ├── homepage-install.sh │ ├── homer-install.sh │ ├── hortusfox-install.sh │ ├── hyperhdr-install.sh │ ├── hyperion-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 │ ├── itsm-ng-install.sh │ ├── jackett-install.sh │ ├── jeedom-install.sh │ ├── jellyfin-install.sh │ ├── jenkins-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 │ ├── 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 │ ├── litellm-install.sh │ ├── livebook-install.sh │ ├── lldap-install.sh │ ├── loki-install.sh │ ├── lubelogger-install.sh │ ├── lyrionmusicserver-install.sh │ ├── mafl-install.sh │ ├── magicmirror-install.sh │ ├── mail-archiver-install.sh │ ├── managemydamnlife-install.sh │ ├── manyfold-install.sh │ ├── mariadb-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 │ ├── miniflux-install.sh │ ├── minio-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 │ ├── navidrome-install.sh │ ├── neo4j-install.sh │ ├── netbird-install.sh │ ├── netbox-install.sh │ ├── nextcloudpi-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 │ ├── openwebui-install.sh │ ├── openziti-controller-install.sh │ ├── openziti-tunnel-install.sh │ ├── ots-install.sh │ ├── outline-install.sh │ ├── owncast-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 │ ├── pf2etools-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 │ ├── 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 │ ├── searxng-install.sh │ ├── seaweedfs-install.sh │ ├── seelf-install.sh │ ├── seerr-install.sh │ ├── semaphore-install.sh │ ├── sftpgo-install.sh │ ├── shelfmark-install.sh │ ├── shinobi-install.sh │ ├── signoz-install.sh │ ├── silverbullet-install.sh │ ├── slskd-install.sh │ ├── smokeping-install.sh │ ├── snipeit-install.sh │ ├── snowshare-install.sh │ ├── sonarqube-install.sh │ ├── sonarr-install.sh │ ├── sonobarr-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 │ ├── stirling-pdf-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 │ ├── teamspeak-server-install.sh │ ├── technitiumdns-install.sh │ ├── teddycloud-install.sh │ ├── telegraf-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 │ ├── trilium-install.sh │ ├── trip-install.sh │ ├── tududi-install.sh │ ├── tunarr-install.sh │ ├── twingate-connector-install.sh │ ├── typesense-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 │ ├── 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 │ ├── wikijs-install.sh │ ├── wireguard-install.sh │ ├── wishlist-install.sh │ ├── wizarr-install.sh │ ├── wordpress-install.sh │ ├── writefreely-install.sh │ ├── yamtrack-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/ │ ├── alpine-install.func │ ├── alpine-tools.func │ ├── api.func │ ├── build.func │ ├── cloud-init.func │ ├── core.func │ ├── error_handler.func │ ├── install.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 │ │ ├── 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 │ │ └── 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 │ │ ├── immich-public-proxy │ │ ├── jellystat │ │ ├── komodo │ │ ├── nextcloud-exporter │ │ ├── olivetin │ │ ├── phpmyadmin │ │ ├── pihole-exporter │ │ ├── prometheus-paperless-ngx-exporter │ │ ├── pve-privilege-converter │ │ ├── qbittorrent-exporter │ │ └── runtipi │ └── 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-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 *.install linguist-language=Shell # --------------------------------------- # Treat Golang files as Go (for /api/) api/**/*.go linguist-language=Go # --------------------------------------- # Treat frontend as JavaScript/TypeScript (optional) frontend/**/*.ts linguist-language=TypeScript frontend/**/*.js linguist-language=JavaScript # --------------------------------------- # Exclude documentation from stats *.md linguist-documentation docs/** linguist-documentation README.md linguist-documentation CONTRIBUTING.md linguist-documentation SECURITY.md linguist-documentation # --------------------------------------- # Exclude generated/config files *.json linguist-generated frontend/public/json/*.json linguist-generated=false *.lock linguist-generated *.yml linguist-generated *.yaml linguist-generated .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 * @community-scripts/Contributor # All changes in frontend /frontend/ @community-scripts/Frontend-Dev ================================================ FILE: .github/CODE_OF_CONDUCT.md ================================================ # Contributor Covenant Code of Conduct ## Our Pledge We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation. We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community. ## Our Standards Examples of behavior that contributes to a positive environment for our community include: * Demonstrating empathy and kindness toward other people * Being respectful of differing opinions, viewpoints, and experiences * Giving and gracefully accepting constructive feedback * Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience * Focusing on what is best not just for us as individuals, but for the overall community Examples of unacceptable behavior include: * The use of sexualized language or imagery, and sexual attention or advances of any kind * Trolling, insulting or derogatory comments, and personal or political attacks * Public or private harassment * Publishing others' private information, such as a physical or email address, without their explicit permission * Other conduct which could reasonably be considered inappropriate in a professional setting ## Enforcement Responsibilities Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful. Community leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, and will communicate reasons for moderation decisions when appropriate. ## Scope This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement. All complaints will be reviewed and investigated promptly and fairly. All community leaders are obligated to respect the privacy and security of the reporter of any incident. ## Enforcement Guidelines Community leaders will follow these Community Impact Guidelines in determining the consequences for any action they deem in violation of this Code of Conduct: ### 1. Correction **Community Impact**: Use of inappropriate language or other behavior deemed unprofessional or unwelcome in the community. **Consequence**: A private, written warning from community leaders, providing clarity around the nature of the violation and an explanation of why the behavior was inappropriate. A public apology may be requested. ### 2. Warning **Community Impact**: A violation through a single incident or series of actions. **Consequence**: A warning with consequences for continued behavior. No interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, for a specified period of time. This includes avoiding interactions in community spaces as well as external channels like social media. Violating these terms may lead to a temporary or permanent ban. ### 3. Temporary Ban **Community Impact**: A serious violation of community standards, including sustained inappropriate behavior. **Consequence**: A temporary ban from any sort of interaction or public communication with the community for a specified period of time. No public or private interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this period. Violating these terms may lead to a permanent ban. ### 4. Permanent Ban **Community Impact**: Demonstrating a pattern of violation of community standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals. **Consequence**: A permanent ban from any sort of public interaction within the community. ## Attribution This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 2.0, available at [https://www.contributor-covenant.org/version/2/0/code_of_conduct.html][v2.0]. Community Impact Guidelines were inspired by [Mozilla's code of conduct enforcement ladder][Mozilla CoC]. For answers to common questions about this code of conduct, see the FAQ at [https://www.contributor-covenant.org/faq][FAQ]. Translations are available at [https://www.contributor-covenant.org/translations][translations]. [homepage]: https://www.contributor-covenant.org [v2.0]: https://www.contributor-covenant.org/version/2/0/code_of_conduct.html [Mozilla CoC]: https://github.com/mozilla/diversity [FAQ]: https://www.contributor-covenant.org/faq [translations]: https://www.contributor-covenant.org/translations ================================================ 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: | ## ⚠️ **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. - 🔎 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. - 📜 **Read the script:** Familiarize yourself with the script's content and its purpose. This will help you understand the issue better and provide more relevant information Thank you for taking the time to report an issue! Please provide as much detail as possible to help us address the problem efficiently. - 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 \"$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/zigbee2mqtt.sh)\" or \"update\"" 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: issue_description attributes: label: 📝 Provide a clear and concise description of the issue. 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/jsYVk5JBxq 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-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/pull_request_template.md ================================================ ## ✍️ Description ## 🔗 Related Issue Fixes # ## ✅ Prerequisites (**X** in brackets) - [ ] **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 website-related JSON files or metadata. - [ ] 🔧 **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/auto-update-app-headers.yml ================================================ name: Auto Update .app-files on: push: branches: - main paths: - "ct/**.sh" workflow_dispatch: jobs: update-app-files: if: github.repository == 'community-scripts/ProxmoxVE' runs-on: ubuntu-latest permissions: contents: write pull-requests: write steps: - name: Generate a token id: generate-token uses: actions/create-github-app-token@v1 with: app-id: ${{ vars.APP_ID }} private-key: ${{ secrets.APP_PRIVATE_KEY }} - name: Generate a token for PR approval and merge id: generate-token-merge uses: actions/create-github-app-token@v1 with: app-id: ${{ secrets.APP_ID_APPROVE_AND_MERGE }} private-key: ${{ secrets.APP_KEY_APPROVE_AND_MERGE }} # Step 1: Checkout repository - name: Checkout repository uses: actions/checkout@v2 # Step 2: Disable file mode changes detection - name: Disable file mode changes run: git config core.fileMode false # Step 3: Set up Git user for committing changes - name: Set up Git run: | git config --global user.name "GitHub Actions" git config --global user.email "github-actions[bot]@users.noreply.github.com" # Step 4: Install figlet - name: Install figlet run: sudo apt-get install -y figlet # Step 5: Run the updated generate-app-files.sh script - name: Run generate-app-files.sh run: | chmod +x .github/workflows/scripts/generate-app-headers.sh .github/workflows/scripts/generate-app-headers.sh env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Step 6: Check if there are any changes - name: Check if there are any changes run: | echo "Checking for changes..." git add -A # Untracked Dateien aufnehmen git status if git diff --cached --quiet; then echo "No changes detected." echo "changed=false" >> "$GITHUB_ENV" else echo "Changes detected:" git diff --stat --cached echo "changed=true" >> "$GITHUB_ENV" fi # Step 7: Commit and create PR if changes exist - name: Commit and create PR if changes exist if: env.changed == 'true' run: | git commit -m "Update .app files" git checkout -b pr-update-app-files git push origin pr-update-app-files --force gh pr create --title "[core] update .app files" \ --body "This PR is auto-generated by a GitHub Action to update the .app files." \ --head pr-update-app-files \ --base main \ --label "automated pr" env: GH_TOKEN: ${{ steps.generate-token.outputs.token }} - name: Approve pull request if: env.changed == 'true' env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | PR_NUMBER=$(gh pr list --head "pr-update-app-files" --json number --jq '.[].number') if [ -n "$PR_NUMBER" ]; then gh pr review $PR_NUMBER --approve fi - name: Approve pull request and merge if: env.changed == 'true' env: GH_TOKEN: ${{ steps.generate-token-merge.outputs.token }} run: | git config --global user.name "github-actions-automege[bot]" git config --global user.email "github-actions-automege[bot]@users.noreply.github.com" PR_NUMBER=$(gh pr list --head "${BRANCH_NAME}" --json number --jq '.[].number') if [ -n "$PR_NUMBER" ]; then gh pr review $PR_NUMBER --approve gh pr merge $PR_NUMBER --squash --admin fi # Step 8: Output success message when no changes - name: No changes detected if: env.changed == 'false' run: echo "No changes to commit. Workflow completed successfully." ================================================ FILE: .github/workflows/autolabeler.yml ================================================ name: Auto Label Pull Requests on: workflow_dispatch: pull_request_target: branches: ["main"] types: [opened, synchronize, reopened, edited] jobs: autolabeler: if: github.repository == 'community-scripts/ProxmoxVE' runs-on: ubuntu-latest permissions: pull-requests: write env: CONFIG_PATH: .github/autolabeler-config.json steps: - name: Checkout repository uses: actions/checkout@v4 - name: Install dependencies run: npm install minimatch - name: Label PR based on file changes and PR template uses: actions/github-script@v7 with: script: | const fs = require('fs').promises; const path = require('path'); const { minimatch } = require('minimatch'); const configPath = path.resolve(process.env.CONFIG_PATH); const fileContent = await fs.readFile(configPath, 'utf-8'); const autolabelerConfig = JSON.parse(fileContent); const prNumber = context.payload.pull_request.number; const prBody = context.payload.pull_request.body || ""; let labelsToAdd = new Set(); const prListFilesResponse = await github.rest.pulls.listFiles({ owner: context.repo.owner, repo: context.repo.repo, pull_number: prNumber, }); const prFiles = prListFilesResponse.data; for (const [label, rules] of Object.entries(autolabelerConfig)) { const shouldAddLabel = prFiles.some((prFile) => { return rules.some((rule) => { const isFileStatusMatch = rule.fileStatus ? rule.fileStatus === prFile.status : true; const isIncludeGlobMatch = rule.includeGlobs.some((glob) => minimatch(prFile.filename, glob)); const isExcludeGlobMatch = rule.excludeGlobs.some((glob) => minimatch(prFile.filename, glob)); return isFileStatusMatch && isIncludeGlobMatch && !isExcludeGlobMatch; }); }); if (shouldAddLabel) { labelsToAdd.add(label); // Add specific sub-labels for tools if (label === "tools") { for (const prFile of prFiles) { const filename = prFile.filename; if (filename.startsWith("tools/addon/")) labelsToAdd.add("addon"); if (filename.startsWith("tools/pve/")) labelsToAdd.add("pve-tool"); } } } } // Always parse template checkboxes to add content-type labels (bugfix, feature, etc.) const templateLabelMappings = { "🐞 **Bug fix**": "bugfix", "✨ **New feature**": "feature", "💥 **Breaking change**": "breaking change", "🆕 **New script**": "new script", "🔧 **Refactoring / Code Cleanup**": "refactor", "📝 **Documentation update**": "documentation" }; for (const [checkbox, label] of Object.entries(templateLabelMappings)) { const escapedCheckbox = checkbox.replace(/([.*+?^=!:${}()|[\]\/\\])/g, "\\$1"); const regex = new RegExp(`- \\[(x|X)\\]\\s*${escapedCheckbox}`, "i"); if (regex.test(prBody)) { labelsToAdd.add(label); } } // Handle website checkbox specially - only add if not already an update script with content label const websiteCheckbox = "🌍 **Website update**"; const escapedWebsite = websiteCheckbox.replace(/([.*+?^=!:${}()|[\]\/\\])/g, "\\$1"); const websiteRegex = new RegExp(`- \\[(x|X)\\]\\s*${escapedWebsite}`, "i"); if (websiteRegex.test(prBody)) { const hasJson = prFiles.some((f) => f.filename.startsWith("json/")); const hasUpdateScript = labelsToAdd.has("update script"); const hasContentLabel = ["bugfix", "feature", "refactor"].some((l) => labelsToAdd.has(l)); // If it's an update script PR with json changes and a content label, skip adding website/json // The PR should be categorized as update script with the content label if (!(hasUpdateScript && hasJson && hasContentLabel)) { labelsToAdd.add("website"); if (hasJson) labelsToAdd.add("json"); } } if (labelsToAdd.size === 0) { labelsToAdd.add("needs triage"); } if (labelsToAdd.size > 0) { await github.rest.issues.addLabels({ owner: context.repo.owner, repo: context.repo.repo, issue_number: prNumber, labels: Array.from(labelsToAdd), }); } ================================================ FILE: .github/workflows/changelog-archive.yml ================================================ name: Archive Old Changelog Entries on: schedule: # Run every Sunday at 00:00 UTC - cron: '0 0 * * 0' workflow_dispatch: jobs: archive-changelog: if: github.repository == 'community-scripts/ProxmoxVE' runs-on: ubuntu-latest env: BRANCH_NAME: github-action-archive-changelog AUTOMATED_PR_LABEL: "automated pr" permissions: contents: write pull-requests: write steps: - name: Generate a token id: generate-token uses: actions/create-github-app-token@v1 with: app-id: ${{ vars.APP_ID }} private-key: ${{ secrets.APP_PRIVATE_KEY }} - name: Generate a token for PR approval and merge id: generate-token-merge uses: actions/create-github-app-token@v1 with: app-id: ${{ secrets.APP_ID_APPROVE_AND_MERGE }} private-key: ${{ secrets.APP_KEY_APPROVE_AND_MERGE }} - name: Checkout code uses: actions/checkout@v4 with: fetch-depth: 0 - name: Archive old changelog entries uses: actions/github-script@v7 with: script: | const fs = require('fs').promises; const path = require('path'); const KEEP_DAYS = 30; const ARCHIVE_PATH = '.github/changelogs'; const CHANGELOG_PATH = 'CHANGELOG.md'; // Calculate cutoff date const cutoffDate = new Date(); cutoffDate.setDate(cutoffDate.getDate() - KEEP_DAYS); cutoffDate.setHours(0, 0, 0, 0); console.log(`Cutoff date: ${cutoffDate.toISOString().split('T')[0]}`); // Read changelog and normalize line endings let content = await fs.readFile(CHANGELOG_PATH, 'utf-8'); content = content.replace(/\r\n/g, '\n').replace(/\r/g, '\n'); const lines = content.split('\n'); // Parse entries const datePattern = /^## (\d{4})-(\d{2})-(\d{2})$/; let header = []; let recentEntries = []; let archiveData = {}; let currentDate = null; let currentContent = []; let inHeader = true; let inOldHistory = false; let historyDetailsDepth = 0; for (let i = 0; i < lines.length; i++) { const line = lines[i]; const match = line.match(datePattern); // Detect the start of History section:
followed by line with 📜 History if (inHeader && !inOldHistory && line.trim() === '
') { // Look ahead to see if this is the History section const nextLine = lines[i + 1] || ''; if (nextLine.includes('📜 History')) { inOldHistory = true; historyDetailsDepth = 1; continue; } } // Track nested details tags to find the end of History section if (inOldHistory) { if (line.trim() === '
') { historyDetailsDepth++; } if (line.trim() === '
') { historyDetailsDepth--; if (historyDetailsDepth === 0) { // We've closed the main History details tag inOldHistory = false; } } continue; } if (match) { inHeader = false; // Save previous entry if (currentDate && currentContent.length > 0) { const entryText = currentContent.join('\n').trim(); const dateObj = new Date(`${currentDate}T00:00:00Z`); // Always add to archive (by month) const year = currentDate.substring(0, 4); const month = currentDate.substring(5, 7); if (!archiveData[year]) archiveData[year] = {}; if (!archiveData[year][month]) archiveData[year][month] = []; archiveData[year][month].push(`## ${currentDate}\n\n${entryText}`); // Also add to recent entries if within cutoff if (dateObj >= cutoffDate) { recentEntries.push(`## ${currentDate}\n\n${entryText}`); } } currentDate = `${match[1]}-${match[2]}-${match[3]}`; currentContent = []; } else if (inHeader) { header.push(line); } else if (currentDate) { currentContent.push(line); } } // Don't forget the last entry if (currentDate && currentContent.length > 0) { const entryText = currentContent.join('\n').trim(); const dateObj = new Date(`${currentDate}T00:00:00Z`); // Always add to archive (by month) const year = currentDate.substring(0, 4); const month = currentDate.substring(5, 7); if (!archiveData[year]) archiveData[year] = {}; if (!archiveData[year][month]) archiveData[year][month] = []; archiveData[year][month].push(`## ${currentDate}\n\n${entryText}`); // Also add to recent entries if within cutoff if (dateObj >= cutoffDate) { recentEntries.push(`## ${currentDate}\n\n${entryText}`); } } console.log(`Recent entries: ${recentEntries.length}`); console.log(`Years to archive: ${Object.keys(archiveData).length}`); // Month names in English const monthNames = { '01': 'January', '02': 'February', '03': 'March', '04': 'April', '05': 'May', '06': 'June', '07': 'July', '08': 'August', '09': 'September', '10': 'October', '11': 'November', '12': 'December' }; // Create/update archive files for (const year of Object.keys(archiveData).sort().reverse()) { const yearPath = path.join(ARCHIVE_PATH, year); try { await fs.mkdir(yearPath, { recursive: true }); } catch (e) { // Directory exists } for (const month of Object.keys(archiveData[year]).sort().reverse()) { const monthPath = path.join(yearPath, `${month}.md`); // Read existing content if exists let existingContent = ''; try { existingContent = await fs.readFile(monthPath, 'utf-8'); // Normalize line endings to prevent regex issues existingContent = existingContent.replace(/\r\n/g, '\n').replace(/\r/g, '\n'); } catch (e) { // File doesn't exist } // Parse existing entries into a Map (date -> content) for deduplication const allEntries = new Map(); // Helper function to parse entries from content const parseEntries = (content) => { const entries = new Map(); const parts = content.split(/(?=^## \d{4}-\d{2}-\d{2}$)/m); for (const part of parts) { const trimmed = part.trim(); if (!trimmed) continue; const dateMatch = trimmed.match(/^## (\d{4}-\d{2}-\d{2})/); if (dateMatch) { entries.set(dateMatch[1], trimmed); } } return entries; }; // Parse existing content if (existingContent) { const existingEntries = parseEntries(existingContent); for (const [date, content] of existingEntries) { allEntries.set(date, content); } } // Add new entries (existing entries take precedence to avoid overwriting) let addedCount = 0; for (const entry of archiveData[year][month]) { const dateMatch = entry.match(/^## (\d{4}-\d{2}-\d{2})/); if (dateMatch && !allEntries.has(dateMatch[1])) { allEntries.set(dateMatch[1], entry.trim()); addedCount++; } } // Sort entries by date (newest first) and write const sortedDates = [...allEntries.keys()].sort().reverse(); const sortedContent = sortedDates.map(date => allEntries.get(date)).join('\n\n'); if (addedCount > 0 || !existingContent) { await fs.writeFile(monthPath, sortedContent + '\n', 'utf-8'); console.log(`Updated: ${monthPath} (${allEntries.size} total entries, +${addedCount} new)`); } } } // Build history section let historySection = []; historySection.push(''); historySection.push('
'); historySection.push('

📜 History

'); historySection.push(''); // Get all years from archive directory let allYears = []; try { const archiveDir = await fs.readdir(ARCHIVE_PATH); allYears = archiveDir.filter(f => /^\d{4}$/.test(f)).sort().reverse(); } catch (e) { allYears = Object.keys(archiveData).sort().reverse(); } for (const year of allYears) { historySection.push(''); historySection.push('
'); historySection.push(`

${year}

`); historySection.push(''); // Get months for this year let months = []; try { const yearDir = await fs.readdir(path.join(ARCHIVE_PATH, year)); months = yearDir .filter(f => f.endsWith('.md')) .map(f => f.replace('.md', '')) .sort() .reverse(); } catch (e) { months = Object.keys(archiveData[year] || {}).sort().reverse(); } for (const month of months) { const monthName = monthNames[month] || month; const monthPath = path.join(ARCHIVE_PATH, year, `${month}.md`); // Count entries in month file let entryCount = 0; try { let monthContent = await fs.readFile(monthPath, 'utf-8'); monthContent = monthContent.replace(/\r\n/g, '\n').replace(/\r/g, '\n'); entryCount = (monthContent.match(/^## \d{4}-\d{2}-\d{2}$/gm) || []).length; } catch (e) { entryCount = (archiveData[year]?.[month] || []).length; } const relativePath = `.github/changelogs/${year}/${month}.md`; historySection.push(''); historySection.push('
'); historySection.push(`

${monthName} (${entryCount} entries)

`); historySection.push(''); historySection.push(`[View ${monthName} ${year} Changelog](${relativePath})`); historySection.push(''); historySection.push('
'); } historySection.push(''); historySection.push('
'); } historySection.push(''); historySection.push('
'); // Build new CHANGELOG.md (History first, then recent entries) const newChangelog = [ ...header, '', historySection.join('\n'), '', recentEntries.join('\n\n') ].join('\n'); await fs.writeFile(CHANGELOG_PATH, newChangelog, 'utf-8'); console.log('CHANGELOG.md updated successfully'); - name: Check for changes id: verify-diff run: | git diff --quiet . || echo "changed=true" >> $GITHUB_ENV - name: Commit and push changes if: env.changed == 'true' run: | git config --global user.name "github-actions[bot]" git config --global user.email "github-actions[bot]@users.noreply.github.com" git add CHANGELOG.md .github/changelogs/ git commit -m "Archive old changelog entries" git checkout -b $BRANCH_NAME || git checkout $BRANCH_NAME git push origin $BRANCH_NAME --force - name: Create pull request if not exists if: env.changed == 'true' env: GH_TOKEN: ${{ steps.generate-token.outputs.token }} run: | PR_EXISTS=$(gh pr list --head "${BRANCH_NAME}" --json number --jq '.[].number') if [ -z "$PR_EXISTS" ]; then gh pr create --title "[Github Action] Archive old changelog entries" \ --body "This PR is auto-generated by a Github Action to archive old changelog entries (older than 14 days) to .github/changelogs/YEAR/MONTH.md" \ --head $BRANCH_NAME \ --base main \ --label "$AUTOMATED_PR_LABEL" fi - name: Approve and merge pull request if: env.changed == 'true' env: GH_TOKEN: ${{ steps.generate-token-merge.outputs.token }} run: | git config --global user.name "github-actions-automege[bot]" git config --global user.email "github-actions-automege[bot]@users.noreply.github.com" PR_NUMBER=$(gh pr list --head "${BRANCH_NAME}" --json number --jq '.[].number') if [ -n "$PR_NUMBER" ]; then gh pr review $PR_NUMBER --approve gh pr merge $PR_NUMBER --squash --admin fi ================================================ FILE: .github/workflows/changelog-pr.yml ================================================ name: Create Changelog Pull Request on: push: branches: ["main"] workflow_dispatch: jobs: update-changelog-pull-request: if: github.repository == 'community-scripts/ProxmoxVE' runs-on: ubuntu-latest env: CONFIG_PATH: .github/changelog-pr-config.json BRANCH_NAME: github-action-update-changelog AUTOMATED_PR_LABEL: "automated pr" permissions: contents: write pull-requests: write steps: - name: Generate a token id: generate-token uses: actions/create-github-app-token@v1 with: app-id: ${{ vars.APP_ID }} private-key: ${{ secrets.APP_PRIVATE_KEY }} - name: Generate a token for PR approval and merge id: generate-token-merge uses: actions/create-github-app-token@v1 with: app-id: ${{ secrets.APP_ID_APPROVE_AND_MERGE }} private-key: ${{ secrets.APP_KEY_APPROVE_AND_MERGE }} - name: Checkout code uses: actions/checkout@v4 with: fetch-depth: 0 - name: Get latest dates in changelog run: | DATES=$(grep -E '^## [0-9]{4}-[0-9]{2}-[0-9]{2}' CHANGELOG.md | head -n 2 | awk '{print $2}') LATEST_DATE=$(echo "$DATES" | sed -n '1p') SECOND_LATEST_DATE=$(echo "$DATES" | sed -n '2p') TODAY=$(date -u +%Y-%m-%d) echo "TODAY=$TODAY" >> $GITHUB_ENV if [[ "$LATEST_DATE" == "$TODAY" ]]; then echo "LATEST_DATE=$SECOND_LATEST_DATE" >> $GITHUB_ENV else echo "LATEST_DATE=$LATEST_DATE" >> $GITHUB_ENV fi - name: Get categorized pull requests id: get-categorized-prs uses: actions/github-script@v7 with: script: | async function main() { const fs = require('fs').promises; const path = require('path'); const configPath = path.resolve(process.env.CONFIG_PATH); const fileContent = await fs.readFile(configPath, 'utf-8'); const changelogConfig = JSON.parse(fileContent); const categorizedPRs = changelogConfig.map(obj => ({ ...obj, notes: [], subCategories: obj.subCategories ?? ( obj.labels.includes("update script") ? [ { title: "🐞 Bug Fixes", labels: ["bugfix"], notes: [] }, { title: "✨ New Features", labels: ["feature"], notes: [] }, { title: "💥 Breaking Changes", labels: ["breaking change"], notes: [] }, { title: "🔧 Refactor", labels: ["refactor"], notes: [] }, ] : obj.labels.includes("maintenance") ? [ { title: "🐞 Bug Fixes", labels: ["bugfix"], notes: [] }, { title: "✨ New Features", labels: ["feature"], notes: [] }, { title: "💥 Breaking Changes", labels: ["breaking change"], notes: [] }, { title: "📡 API", labels: ["api"], notes: [] }, { title: "Github", labels: ["github"], notes: [] }, { title: "📝 Documentation", labels: ["maintenance"], notes: [] }, { title: "🔧 Refactor", labels: ["refactor"], notes: [] } ] : obj.labels.includes("website") ? [ { 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: [] } ] : [] ) })); const latestDateInChangelog = new Date(process.env.LATEST_DATE); latestDateInChangelog.setUTCHours(23, 59, 59, 999); const { data: pulls } = await github.rest.pulls.list({ owner: context.repo.owner, repo: "ProxmoxVE", base: "main", state: "closed", sort: "updated", direction: "desc", per_page: 100, }); const filteredPRs = pulls.filter(pr => pr.merged_at && new Date(pr.merged_at) > latestDateInChangelog && !pr.labels.some(label => ["invalid", "wontdo", process.env.AUTOMATED_PR_LABEL].includes(label.name.toLowerCase()) ) ); for (const pr of filteredPRs) { const prLabels = pr.labels.map(label => label.name.toLowerCase()); if (pr.user.login.includes("push-app-to-main[bot]")) { const scriptName = pr.title; try { const { data: relatedIssues } = await github.rest.issues.listForRepo({ owner: context.repo.owner, repo: "ProxmoxVED", state: "all", labels: ["Started Migration To ProxmoxVE"] }); const matchingIssue = relatedIssues.find(issue => issue.title.toLowerCase().includes(scriptName.toLowerCase()) ); if (matchingIssue) { const issueAuthor = matchingIssue.user.login; const issueAuthorUrl = `https://github.com/${issueAuthor}`; prNote = `- ${pr.title} [@${issueAuthor}](${issueAuthorUrl}) ([#${pr.number}](${pr.html_url}))`; } else { prNote = `- ${pr.title} ([#${pr.number}](${pr.html_url}))`; } } catch (error) { console.error(`Error fetching related issues: ${error}`); prNote = `- ${pr.title} ([#${pr.number}](${pr.html_url}))`; } }else{ prNote = `- ${pr.title} [@${pr.user.login}](https://github.com/${pr.user.login}) ([#${pr.number}](${pr.html_url}))`; } if (prLabels.includes("new script")) { const newScriptCategory = categorizedPRs.find(category => category.title === "New Scripts" || category.labels.includes("new script")); if (newScriptCategory) { newScriptCategory.notes.push(prNote); } } else { let categorized = false; const priorityCategories = categorizedPRs.slice(); // Priority order for content-type labels (highest priority first) const subCategoryPriority = ["breaking change", "bugfix", "feature", "refactor"]; for (const category of priorityCategories) { if (categorized) break; if (category.labels.some(label => prLabels.includes(label))) { if (category.subCategories && category.subCategories.length > 0) { // Find subcategory by priority order instead of first match let subCategory = null; for (const priorityLabel of subCategoryPriority) { if (prLabels.includes(priorityLabel)) { subCategory = category.subCategories.find(sub => sub.labels.includes(priorityLabel) ); if (subCategory) break; } } // Fallback: check for any other subcategory match (api, github, json, etc.) if (!subCategory) { subCategory = category.subCategories.find(sub => sub.labels.some(label => prLabels.includes(label)) ); } if (subCategory) { subCategory.notes.push(prNote); } else { category.notes.push(prNote); } } else { category.notes.push(prNote); } categorized = true; } } // Fallback: Add to Uncategorized if no category matched if (!categorized) { const uncategorized = categorizedPRs.find(category => category.title.includes("Uncategorized") || category.labels.includes("needs triage")); if (uncategorized) { uncategorized.notes.push(prNote); } } } } return categorizedPRs; } return await main(); - name: Update CHANGELOG.md uses: actions/github-script@v7 with: script: | const fs = require('fs').promises; const path = require('path'); const today = process.env.TODAY; const latestDateInChangelog = process.env.LATEST_DATE; const changelogPath = path.resolve('CHANGELOG.md'); const categorizedPRs = ${{ steps.get-categorized-prs.outputs.result }}; console.log(JSON.stringify(categorizedPRs, null, 2)); let newReleaseNotes = `## ${today}\n\n`; for (const { title, notes, subCategories } of categorizedPRs) { const hasSubcategories = subCategories && subCategories.length > 0; const hasMainNotes = notes.length > 0; const hasSubNotes = hasSubcategories && subCategories.some(sub => sub.notes && sub.notes.length > 0); if (hasMainNotes || hasSubNotes) { newReleaseNotes += `### ${title}\n\n`; } if (hasMainNotes) { newReleaseNotes += ` ${notes.join("\n")}\n\n`; } if (hasSubcategories) { for (const { title: subTitle, notes: subNotes } of subCategories) { if (subNotes && subNotes.length > 0) { newReleaseNotes += ` - #### ${subTitle}\n\n`; newReleaseNotes += ` ${subNotes.join("\n ")}\n\n`; } } } } const changelogContent = await fs.readFile(changelogPath, 'utf-8'); const changelogIncludesTodaysReleaseNotes = changelogContent.includes(`\n## ${today}`); const regex = changelogIncludesTodaysReleaseNotes ? new RegExp(`## ${today}.*(?=## ${latestDateInChangelog})`, "gs") : new RegExp(`(?=## ${latestDateInChangelog})`, "gs"); const newChangelogContent = changelogContent.replace(regex, newReleaseNotes); await fs.writeFile(changelogPath, newChangelogContent); - name: Check for changes id: verify-diff run: | git diff --quiet . || echo "changed=true" >> $GITHUB_ENV - name: Commit and push changes if: env.changed == 'true' run: | git config --global user.name "github-actions[bot]" git config --global user.email "github-actions[bot]@users.noreply.github.com" git add CHANGELOG.md git commit -m "Update CHANGELOG.md" git checkout -b $BRANCH_NAME || git checkout $BRANCH_NAME git push origin $BRANCH_NAME --force - name: Create pull request if not exists if: env.changed == 'true' env: GH_TOKEN: ${{ steps.generate-token.outputs.token }} run: | PR_EXISTS=$(gh pr list --head "${BRANCH_NAME}" --json number --jq '.[].number') if [ -z "$PR_EXISTS" ]; then gh pr create --title "[Github Action] Update CHANGELOG.md" \ --body "This PR is auto-generated by a Github Action to update the CHANGELOG.md file." \ --head $BRANCH_NAME \ --base main \ --label "$AUTOMATED_PR_LABEL" fi - name: Approve pull request if: env.changed == 'true' env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | PR_NUMBER=$(gh pr list --head "${BRANCH_NAME}" --json number --jq '.[].number') if [ -n "$PR_NUMBER" ]; then gh pr review $PR_NUMBER --approve fi - name: Approve pull request and merge if: env.changed == 'true' env: GH_TOKEN: ${{ steps.generate-token-merge.outputs.token }} run: | git config --global user.name "github-actions-automege[bot]" git config --global user.email "github-actions-automege[bot]@users.noreply.github.com" PR_NUMBER=$(gh pr list --head "${BRANCH_NAME}" --json number --jq '.[].number') if [ -n "$PR_NUMBER" ]; then gh pr review $PR_NUMBER --approve gh pr merge $PR_NUMBER --squash --admin fi ================================================ FILE: .github/workflows/check-node-versions.yml ================================================ name: Check Node.js Version Drift on: workflow_dispatch: schedule: # Runs weekly on Monday at 06:00 UTC - cron: "0 6 * * 1" permissions: contents: read issues: write jobs: check-node-versions: if: github.repository == 'community-scripts/ProxmoxVE' runs-on: coolify-runner steps: - name: Checkout Repository uses: actions/checkout@v4 with: ref: main - name: Install dependencies run: | sudo apt-get update -qq sudo apt-get install -y -qq jq curl > /dev/null 2>&1 - name: Check upstream Node.js versions id: check env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | set -euo pipefail echo "================================================" echo " Checking Node.js version drift in install scripts" echo "================================================" # Alpine version -> Node major cache (populated on demand) declare -A ALPINE_NODE_CACHE # Resolve Node.js major version from Alpine package registry # Usage: resolve_alpine_node "3.21" => sets REPLY to major version (e.g. "22") resolve_alpine_node() { local alpine_ver="$1" if [[ -n "${ALPINE_NODE_CACHE[$alpine_ver]+x}" ]]; then REPLY="${ALPINE_NODE_CACHE[$alpine_ver]}" return fi local url="https://pkgs.alpinelinux.org/package/v${alpine_ver}/main/x86_64/nodejs" local page page=$(curl -sf "$url" 2>/dev/null || echo "") local full_ver="" if [[ -n "$page" ]]; then # Parse: "Version | 24.13.0-r1" or similar table row full_ver=$(echo "$page" | grep -oP 'Version\s*\|\s*\K[0-9]+\.[0-9]+\.[0-9]+' | head -1 || echo "") if [[ -z "$full_ver" ]]; then # Fallback: look for version pattern after "Version" full_ver=$(echo "$page" | grep -oP '(?<=Version)[0-9]+\.[0-9]+\.[0-9]+' | head -1 || echo "") fi fi local major="" if [[ -n "$full_ver" ]]; then major="${full_ver%%.*}" fi ALPINE_NODE_CACHE[$alpine_ver]="$major" REPLY="$major" } # Extract Node major from a Dockerfile content # Sets: DF_NODE_MAJOR, DF_SOURCE (description of where we found it) extract_dockerfile_node() { local content="$1" DF_NODE_MAJOR="" DF_SOURCE="" # 1) FROM node:XX (e.g. node:24-alpine, node:22.9.0-bookworm-slim, node:20) local node_from node_from=$(echo "$content" | grep -oP '(?i)FROM\s+(--platform=[^\s]+\s+)?node:\K[0-9]+' | head -1 || echo "") if [[ -n "$node_from" ]]; then DF_NODE_MAJOR="$node_from" DF_SOURCE="FROM node:${node_from}" return fi # 2) nodesource/setup_XX.x local nodesource nodesource=$(echo "$content" | grep -oP 'nodesource/setup_\K[0-9]+' | head -1 || echo "") if [[ -n "$nodesource" ]]; then DF_NODE_MAJOR="$nodesource" DF_SOURCE="nodesource/setup_${nodesource}.x" return fi # 3) FROM alpine:X.Y — resolve via Alpine packages local alpine_ver alpine_ver=$(echo "$content" | grep -oP '(?i)FROM\s+(--platform=[^\s]+\s+)?alpine:\K[0-9]+\.[0-9]+' | head -1 || echo "") if [[ -n "$alpine_ver" ]]; then resolve_alpine_node "$alpine_ver" if [[ -n "$REPLY" ]]; then DF_NODE_MAJOR="$REPLY" DF_SOURCE="alpine:${alpine_ver} (pkg: nodejs ${DF_NODE_MAJOR})" return fi fi } # Extract Node major from engines.node in package.json # Sets: ENGINES_NODE_RAW (raw string), ENGINES_MIN_MAJOR, ENGINES_IS_MINIMUM extract_engines_node() { local content="$1" ENGINES_NODE_RAW="" ENGINES_MIN_MAJOR="" ENGINES_IS_MINIMUM="false" ENGINES_NODE_RAW=$(echo "$content" | jq -r '.engines.node // empty' 2>/dev/null || echo "") if [[ -z "$ENGINES_NODE_RAW" ]]; then return fi # Detect if constraint is a minimum (>=, ^) vs exact pinning if [[ "$ENGINES_NODE_RAW" =~ ^(\>=|\^|\~) ]]; then ENGINES_IS_MINIMUM="true" fi # Extract the first number (major) from the constraint # Handles: ">=24.13.1", "^22", ">=18.0.0", ">=18.15.0 <19 || ^20", etc. ENGINES_MIN_MAJOR=$(echo "$ENGINES_NODE_RAW" | grep -oP '\d+' | head -1 || echo "") } # Check if our_version satisfies an engines.node constraint # Returns 0 if satisfied, 1 if not # Usage: version_satisfies_engines "22" ">=18.0.0" "true" version_satisfies_engines() { local our="$1" local min_major="$2" local is_minimum="$3" if [[ -z "$min_major" || -z "$our" ]]; then return 1 fi if [[ "$is_minimum" == "true" ]]; then # >= or ^ constraint: our version must be >= min_major if [[ "$our" -ge "$min_major" ]]; then return 0 fi fi return 1 } # Search for files in subdirectories via GitHub API tree # Usage: find_repo_file "owner/repo" "branch" "filename" => sets REPLY to raw URL or empty find_repo_file() { local repo="$1" local branch="$2" local filename="$3" REPLY="" # Try root first (fast) local root_url="https://raw.githubusercontent.com/${repo}/${branch}/${filename}" if curl -sfI "$root_url" >/dev/null 2>&1; then REPLY="$root_url" return fi # Search via GitHub API tree (recursive) local tree_url="https://api.github.com/repos/${repo}/git/trees/${branch}?recursive=1" local tree_json tree_json=$(curl -sf -H "Authorization: token $GH_TOKEN" "$tree_url" 2>/dev/null || echo "") if [[ -z "$tree_json" ]]; then return fi # Find first matching path (prefer shorter/root-level paths) local match_path match_path=$(echo "$tree_json" | jq -r --arg fn "$filename" \ '.tree[]? | select(.path | endswith("/" + $fn) or . == $fn) | .path' 2>/dev/null \ | sort | head -1 || echo "") if [[ -n "$match_path" ]]; then REPLY="https://raw.githubusercontent.com/${repo}/${branch}/${match_path}" fi } # Extract Node major from .nvmrc or .node-version # Sets: NVMRC_NODE_MAJOR extract_nvmrc_node() { local content="$1" NVMRC_NODE_MAJOR="" # .nvmrc/.node-version typically has: "v22.9.0", "22", "lts/iron", etc. local ver ver=$(echo "$content" | tr -d '[:space:]' | grep -oP '^v?\K[0-9]+' | head -1 || echo "") NVMRC_NODE_MAJOR="$ver" } # Collect results declare -a issue_scripts=() declare -a report_lines=() total=0 checked=0 drift_count=0 for script in install/*-install.sh; do [[ ! -f "$script" ]] && continue if ! grep -q 'setup_nodejs' "$script"; then continue fi total=$((total + 1)) slug=$(basename "$script" | sed 's/-install\.sh$//') # Extract Source URL (GitHub only) from the "# Source:" line # Supports both: # # Source: https://github.com/owner/repo # # Source: https://example.com | Github: https://github.com/owner/repo # NOTE: Must filter for "# Source:" line first to avoid matching the License URL source_url=$(head -20 "$script" | grep -i '# Source:' | grep -oP 'https://github\.com/[^\s|]+' | head -1 || echo "") if [[ -z "$source_url" ]]; then report_lines+=("| \`$slug\` | — | — | — | — | ⏭️ No GitHub source |") continue fi repo=$(echo "$source_url" | sed -E 's|https://github\.com/||; s|/$||; s|\.git$||') if [[ -z "$repo" || "$repo" != */* ]]; then report_lines+=("| \`$slug\` | — | — | — | — | ⏭️ Invalid repo |") continue fi checked=$((checked + 1)) # Extract our NODE_VERSION our_version=$(grep -oP 'NODE_VERSION="(\d+)"' "$script" | head -1 | grep -oP '\d+' || echo "") if [[ -z "$our_version" ]]; then if grep -q 'NODE_VERSION=\$(' "$script"; then our_version="dynamic" else our_version="unset" fi fi # Determine default branch via GitHub API (fast, single call) detected_branch="" api_default=$(curl -sf -H "Authorization: token $GH_TOKEN" \ "https://api.github.com/repos/${repo}" 2>/dev/null \ | jq -r '.default_branch // empty' 2>/dev/null || echo "") if [[ -n "$api_default" ]]; then detected_branch="$api_default" else detected_branch="main" fi # Fetch upstream Dockerfile (root + subdirectories) df_content="" find_repo_file "$repo" "$detected_branch" "Dockerfile" if [[ -n "$REPLY" ]]; then df_content=$(curl -sf "$REPLY" 2>/dev/null || echo "") fi DF_NODE_MAJOR="" DF_SOURCE="" if [[ -n "$df_content" ]]; then extract_dockerfile_node "$df_content" fi # Fetch upstream package.json (root + subdirectories) pkg_content="" find_repo_file "$repo" "$detected_branch" "package.json" if [[ -n "$REPLY" ]]; then pkg_content=$(curl -sf "$REPLY" 2>/dev/null || echo "") fi ENGINES_NODE_RAW="" ENGINES_MIN_MAJOR="" ENGINES_IS_MINIMUM="false" if [[ -n "$pkg_content" ]]; then extract_engines_node "$pkg_content" fi # Fallback: check .nvmrc or .node-version NVMRC_NODE_MAJOR="" if [[ -z "$DF_NODE_MAJOR" && -z "$ENGINES_MIN_MAJOR" ]]; then for nvmfile in .nvmrc .node-version; do find_repo_file "$repo" "$detected_branch" "$nvmfile" if [[ -n "$REPLY" ]]; then nvmrc_content=$(curl -sf "$REPLY" 2>/dev/null || echo "") if [[ -n "$nvmrc_content" ]]; then extract_nvmrc_node "$nvmrc_content" [[ -n "$NVMRC_NODE_MAJOR" ]] && break fi fi done fi # Determine upstream recommended major version upstream_major="" upstream_hint="" if [[ -n "$DF_NODE_MAJOR" ]]; then upstream_major="$DF_NODE_MAJOR" upstream_hint="$DF_SOURCE" elif [[ -n "$ENGINES_MIN_MAJOR" ]]; then upstream_major="$ENGINES_MIN_MAJOR" upstream_hint="engines: $ENGINES_NODE_RAW" elif [[ -n "$NVMRC_NODE_MAJOR" ]]; then upstream_major="$NVMRC_NODE_MAJOR" upstream_hint=".nvmrc/.node-version" fi # Build display values engines_display="${ENGINES_NODE_RAW:-—}" dockerfile_display="${DF_SOURCE:-—}" # Compare status="✅" if [[ "$our_version" == "dynamic" ]]; then status="🔄 Dynamic" elif [[ "$our_version" == "unset" ]]; then if [[ -n "$upstream_major" ]]; then status="⚠️ NODE_VERSION not set (upstream=$upstream_major via $upstream_hint)" else status="⚠️ NODE_VERSION not set (no upstream info found)" fi issue_scripts+=("$slug|$our_version|$upstream_major|$upstream_hint|$repo") drift_count=$((drift_count + 1)) elif [[ -n "$upstream_major" && "$our_version" != "$upstream_major" ]]; then # Check if engines.node is a minimum constraint that our version satisfies if [[ -z "$DF_NODE_MAJOR" && "$ENGINES_IS_MINIMUM" == "true" ]] && \ version_satisfies_engines "$our_version" "$ENGINES_MIN_MAJOR" "$ENGINES_IS_MINIMUM"; then status="✅ (engines: $ENGINES_NODE_RAW — ours: $our_version satisfies)" else status="🔸 Drift → upstream=$upstream_major ($upstream_hint)" issue_scripts+=("$slug|$our_version|$upstream_major|$upstream_hint|$repo") drift_count=$((drift_count + 1)) fi fi report_lines+=("| \`$slug\` | $our_version | $engines_display | $dockerfile_display | [$repo](https://github.com/$repo) | $status |") # Rate-limit to avoid GitHub secondary rate limits sleep 0.3 done # Print summary echo "" echo "=========================================" echo " Total scripts with setup_nodejs: $total" echo " Checked (with GitHub source): $checked" echo " Version drift detected: $drift_count" echo "=========================================" # Export { echo "drift_count=$drift_count" echo "total=$total" echo "checked=$checked" } >> "$GITHUB_OUTPUT" # Save issue details for next step printf '%s\n' "${issue_scripts[@]}" > /tmp/drift_scripts.txt 2>/dev/null || touch /tmp/drift_scripts.txt # Save full report { echo "## Node.js Version Drift Report" echo "" echo "**Generated:** $(date -u +%Y-%m-%dT%H:%M:%SZ)" echo "**Scripts checked:** $total | **With GitHub source:** $checked | **Drift detected:** $drift_count" echo "" echo "| Script | Our Version | engines.node | Dockerfile | Upstream Repo | Status |" echo "|--------|-------------|-------------|------------|---------------|--------|" printf '%s\n' "${report_lines[@]}" | sort } > /tmp/drift_report.md cat /tmp/drift_report.md - name: Create or update summary issue if: steps.check.outputs.drift_count != '0' env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | set -euo pipefail TITLE="[Automated] Node.js Version Drift Report" DATE=$(date -u +%Y-%m-%d) DRIFT_COUNT="${{ steps.check.outputs.drift_count }}" TOTAL="${{ steps.check.outputs.total }}" CHECKED="${{ steps.check.outputs.checked }}" # Build checklist from drift data CHECKLIST="" while IFS='|' read -r slug our_version upstream_major upstream_hint repo; do [[ -z "$slug" ]] && continue CHECKLIST+="- [ ] **\`${slug}\`** — ours: \`${our_version}\` → upstream: \`${upstream_major}\` (${upstream_hint}) — [repo](https://github.com/${repo})"$'\n' done < /tmp/drift_scripts.txt # Build full report table REPORT=$(cat /tmp/drift_report.md) BODY=$(cat <-install.sh\` 4. Update \`NODE_VERSION\` in \`ct/.sh\` (update section) if applicable 5. Check off the item above once done
Full report ${REPORT}
--- *This issue is automatically created/updated weekly by the Node.js version drift check workflow.* *Last updated: ${DATE}* ISSUE_EOF ) # Check if a matching open issue already exists EXISTING=$(gh issue list --state open --label "automated,dependencies" --search "\"[Automated] Node.js Version Drift Report\"" --json number --jq '.[0].number // empty' 2>/dev/null || echo "") if [[ -n "$EXISTING" ]]; then gh issue edit "$EXISTING" --body "$BODY" echo "Updated existing issue #$EXISTING" else gh issue create \ --title "$TITLE" \ --body "$BODY" \ --label "automated,dependencies" echo "Created new summary issue" fi - name: Close issue if no drift if: steps.check.outputs.drift_count == '0' env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | EXISTING=$(gh issue list --state open --label "automated,dependencies" --search "\"[Automated] Node.js Version Drift Report\"" --json number --jq '.[0].number // empty' 2>/dev/null || echo "") if [[ -n "$EXISTING" ]]; then gh issue close "$EXISTING" --comment "All Node.js versions are in sync with upstream. Closing automatically." echo "Closed issue #$EXISTING" fi ================================================ FILE: .github/workflows/close-new-script-prs.yml ================================================ name: Close Unauthorized New Script PRs on: pull_request_target: branches: ["main"] types: [opened, labeled] jobs: check-new-script: if: github.repository == 'community-scripts/ProxmoxVE' runs-on: self-hosted permissions: pull-requests: write contents: read steps: - name: Close PR if unauthorized new script submission uses: actions/github-script@v7 with: script: | const pr = context.payload.pull_request; const prNumber = pr.number; const author = pr.user.login; const authorType = pr.user.type; // "User" or "Bot" const owner = context.repo.owner; const repo = context.repo.repo; // --- Only act on PRs with the "new script" label --- const labels = pr.labels.map(l => l.name); if (!labels.includes("new script")) { core.info(`PR #${prNumber} does not have "new script" label — skipping.`); return; } // --- Allow our bots --- const allowedBots = [ "push-app-to-main[bot]", "push-app-to-main", ]; if (allowedBots.includes(author)) { core.info(`PR #${prNumber} by allowed bot "${author}" — skipping.`); return; } // --- Check if author is a member of the contributor team --- const teamSlug = "contributor"; let isMember = false; try { const { status } = await github.rest.teams.getMembershipForUserInOrg({ org: owner, team_slug: teamSlug, username: author, }); // status 200 means the user is a member (active or pending) isMember = true; } catch (error) { if (error.status === 404) { isMember = false; } else { core.warning(`Could not check team membership for ${author}: ${error.message}`); // Fallback: check org membership try { await github.rest.orgs.checkMembershipForUser({ org: owner, username: author, }); isMember = true; } catch { isMember = false; } } } if (isMember) { core.info(`PR #${prNumber} by contributor "${author}" — skipping.`); return; } // --- Unauthorized: close the PR with a comment --- core.info(`Closing PR #${prNumber} by "${author}" — not a contributor or allowed bot.`); const comment = [ `👋 Hi @${author},`, ``, `Thank you for your interest in contributing a new script!`, ``, `However, **new scripts must first be submitted to our development repository** for testing and review before they can be merged here.`, ``, `> 🛑 New scripts must be submitted to [**ProxmoxVED**](https://github.com/community-scripts/ProxmoxVED) for testing.`, `> PRs without prior testing will be closed.`, ``, `Please open your PR at **https://github.com/community-scripts/ProxmoxVED** instead.`, `Once your script has been tested and approved there, it will be pushed to this repository automatically.`, ``, `This PR will now be closed. Thank you for understanding! 🙏`, ].join("\n"); await github.rest.issues.createComment({ owner, repo, issue_number: prNumber, body: comment, }); await github.rest.pulls.update({ owner, repo, pull_number: prNumber, state: "closed", }); // Add a label to indicate why it was closed await github.rest.issues.addLabels({ owner, repo, issue_number: prNumber, labels: ["not a script issue"], }); ================================================ FILE: .github/workflows/close-tteck-issues.yaml ================================================ name: Auto-Close tteck Issues on: issues: types: [opened] jobs: close_tteck_issues: if: github.repository == 'community-scripts/ProxmoxVE' runs-on: ubuntu-latest steps: - name: Auto-close if tteck script detected uses: actions/github-script@v7 with: script: | const issue = context.payload.issue; const content = `${issue.title}\n${issue.body}`; const issueNumber = issue.number; // Check for tteck script mention if (content.includes("tteck") || content.includes("tteck/Proxmox")) { const message = `Hello, it looks like you are referencing the **old tteck repo**. This repository is no longer used for active scripts. **Please update your bookmarks** and use: [https://helper-scripts.com](https://helper-scripts.com) Also make sure your Bash command starts with: \`\`\`bash bash <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/...) \`\`\` This issue is being closed automatically.`; await github.rest.issues.createComment({ ...context.repo, issue_number: issueNumber, body: message }); // Optionally apply a label like "not planned" await github.rest.issues.addLabels({ ...context.repo, issue_number: issueNumber, labels: ["not planned"] }); // Close the issue await github.rest.issues.update({ ...context.repo, issue_number: issueNumber, state: "closed" }); } ================================================ FILE: .github/workflows/close_issue_in_dev.yaml ================================================ name: Close Matching Issue on PR Merge on: pull_request: types: - closed jobs: close_issue: if: github.event.pull_request.merged == true && github.repository == 'community-scripts/ProxmoxVE' runs-on: self-hosted steps: - name: Checkout target repo (merge commit) uses: actions/checkout@v4 with: repository: community-scripts/ProxmoxVE ref: ${{ github.event.pull_request.merge_commit_sha }} token: ${{ secrets.GITHUB_TOKEN }} - name: Extract and Process PR Title id: extract_title run: | title=$(echo "${{ github.event.pull_request.title }}" | sed 's/^New Script://g' | tr '[:upper:]' '[:lower:]' | sed 's/ //g' | sed 's/-//g') echo "Processed Title: $title" echo "title=$title" >> $GITHUB_ENV - name: Get slugs from merged PR id: get_slugs env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | pr_files=$(gh pr view ${{ github.event.pull_request.number }} --repo community-scripts/ProxmoxVE --json files -q '.files[].path' 2>/dev/null || true) slugs="" for path in $pr_files; do [[ -f "$path" ]] || continue if [[ "$path" == frontend/public/json/*.json ]]; then s=$(jq -r '.slug // empty' "$path" 2>/dev/null) [[ -n "$s" ]] && slugs="$slugs $s" elif [[ "$path" == ct/*.sh ]] || [[ "$path" == install/*.sh ]] || [[ "$path" == tools/*.sh ]] || [[ "$path" == turnkey/*.sh ]] || [[ "$path" == vm/*.sh ]]; then base=$(basename "$path" .sh) if [[ "$path" == install/* && "$base" == *-install ]]; then s="${base%-install}" else s="$base" fi [[ -n "$s" ]] && slugs="$slugs $s" fi done slugs=$(echo $slugs | xargs -n1 | sort -u | tr '\n' ' ') if [[ -z "$slugs" && -n "$title" ]]; then slugs="$title" fi if [[ -z "$slugs" ]]; then echo "count=0" >> "$GITHUB_OUTPUT" exit 0 fi echo "$slugs" > pocketbase_slugs.txt echo "count=$(echo $slugs | wc -w)" >> "$GITHUB_OUTPUT" - name: Search for Issues with Similar Titles id: find_issue env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | issues=$(gh issue list --repo community-scripts/ProxmoxVED --json number,title --jq '.[] | {number, title}') best_match_score=0 best_match_number=0 for issue in $(echo "$issues" | jq -r '. | @base64'); do _jq() { echo ${issue} | base64 --decode | jq -r ${1} } issue_title=$(_jq '.title' | tr '[:upper:]' '[:lower:]' | sed 's/ //g' | sed 's/-//g') issue_number=$(_jq '.number') match_score=$(echo "$title" | grep -o "$issue_title" | wc -l) if [ "$match_score" -gt "$best_match_score" ]; then best_match_score=$match_score best_match_number=$issue_number fi done if [ "$best_match_number" != "0" ]; then echo "issue_number=$best_match_number" >> $GITHUB_ENV else echo "No matching issue found." exit 0 fi - name: Comment on the Best-Matching Issue and Close It if: env.issue_number != '' env: GH_TOKEN: ${{ secrets.PAT_MICHEL }} run: | gh issue comment $issue_number --repo community-scripts/ProxmoxVED --body "Merged with #${{ github.event.pull_request.number }} in ProxmoxVE" gh issue close $issue_number --repo community-scripts/ProxmoxVED - name: Set is_dev to false in PocketBase if: steps.get_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 }} PR_URL: ${{ github.server_url }}/${{ github.repository }}/pull/${{ github.event.pull_request.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) { 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) { 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('pocketbase_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'; const prUrl = process.env.PR_URL || ''; 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 patchRes = await request(recordsUrl + '/' + record.id, { method: 'PATCH', headers: { 'Authorization': token, 'Content-Type': 'application/json' }, body: JSON.stringify({ name: record.name || record.slug, last_update_commit: prUrl, is_dev: false }) }); if (!patchRes.ok) { console.warn('PATCH failed for slug ' + slug + ': ' + patchRes.body); continue; } console.log('Set is_dev=false for slug: ' + slug); } console.log('Done.'); })().catch(e => { console.error(e); process.exit(1); }); ENDSCRIPT shell: bash ================================================ 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/github-release.yml ================================================ name: Create Daily Release on: schedule: - cron: '1 0 * * *' # Runs daily at 00:01 UTC workflow_dispatch: jobs: create-daily-release: if: github.repository == 'community-scripts/ProxmoxVE' runs-on: ubuntu-latest permissions: contents: write steps: - name: Checkout repository uses: actions/checkout@v4 - name: Clean CHANGELOG (remove HTML header) run: sed -n '/^## /,$p' CHANGELOG.md > changelog_cleaned.md - name: Extract relevant changelog section run: | YESTERDAY=$(date -u --date="yesterday" +%Y-%m-%d) echo "Checking for changes on: $YESTERDAY" # Extract the section from "## $YESTERDAY" until the next "## YYYY-MM-DD" sed -n "/^## $YESTERDAY/,/^## [0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}/p" changelog_cleaned.md | head -n -1 > changelog_tmp_full.md # Truncate the extracted section to 5000 characters head -c 5000 changelog_tmp_full.md > changelog_tmp.md echo "=== Extracted Changelog ===" cat changelog_tmp.md echo "===========================" # Abort if no content was found if [ ! -s changelog_tmp.md ]; then echo "No changes found for $YESTERDAY, skipping release." exit 0 fi - name: Create GitHub release env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | YESTERDAY=$(date -u --date="yesterday" +%Y-%m-%d) gh release create "$YESTERDAY" -t "$YESTERDAY" -F changelog_tmp.md ================================================ FILE: .github/workflows/lock-issue.yaml ================================================ name: Lock closed issues on: schedule: - cron: "0 0 * * *" # Run daily at midnight workflow_dispatch: permissions: issues: write pull-requests: write jobs: lock: runs-on: ubuntu-latest steps: - name: Lock old issues and PRs uses: actions/github-script@v7 with: script: | const daysBeforeLock = 3; const lockDate = new Date(); lockDate.setDate(lockDate.getDate() - daysBeforeLock); // Exclude patterns (case-insensitive) const excludePatterns = [ /automated pr/i, /\[bot\]/i, /dependabot/i ]; // Search for closed, unlocked issues older than 3 days (paginated, oldest first) let page = 1; let totalLocked = 0; while (true) { const issues = await github.rest.search.issuesAndPullRequests({ q: `repo:${context.repo.owner}/${context.repo.repo} is:closed is:unlocked updated:<${lockDate.toISOString().split('T')[0]}`, sort: 'updated', order: 'asc', per_page: 100, page: page }); if (issues.data.items.length === 0) break; console.log(`Page ${page}: ${issues.data.items.length} items (total available: ${issues.data.total_count})`); for (const item of issues.data.items) { // Skip excluded items const shouldExclude = excludePatterns.some(pattern => pattern.test(item.title)); if (shouldExclude) { console.log(`Skipped #${item.number}: "${item.title}" (matches exclude pattern)`); continue; } try { // Lock the issue/PR silently await github.rest.issues.lock({ ...context.repo, issue_number: item.number, lock_reason: 'resolved' }); totalLocked++; console.log(`Locked #${item.number} (${item.pull_request ? 'PR' : 'Issue'})`); } catch (error) { console.log(`Failed to lock #${item.number}: ${error.message}`); } } page++; // GitHub search API limit: max 10000 results (100 pages * 100) - temporarily increased if (page > 100) break; } console.log(`Total locked: ${totalLocked} issues/PRs`); ================================================ FILE: .github/workflows/pocketbase-bot.yml ================================================ name: PocketBase Bot on: issue_comment: types: [created] permissions: issues: write pull-requests: write contents: read 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 }} 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 }); } 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 ─────────────────────────────────────────────── // author_association: OWNER = repo/org owner, MEMBER = org member (includes Contributors team) 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 ────────────────────────────────────────────────── // Formats (first line of comment): // /pocketbase field=value [field=value ...] ← field updates (simple values) // /pocketbase set ← value from code block below // /pocketbase note list|add|edit|remove ... ← note management // /pocketbase method list ← list install methods // /pocketbase method cpu=N ram=N hdd=N ← edit install method resources const commentBody = process.env.COMMENT_BODY || ''; const lines = commentBody.trim().split('\n'); const firstLine = lines[0].trim(); const withoutCmd = firstLine.replace(/^\/pocketbase\s+/, '').trim(); // Extract code block content from comment body (```...``` or ```lang\n...```) function extractCodeBlock(body) { const m = body.match(/```[^\n]*\n([\s\S]*?)```/); return m ? m[1].trim() : null; } const codeBlockValue = extractCodeBlock(commentBody); const HELP_TEXT = '**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 resources:**\n' + '```\n' + '/pocketbase method list\n' + '/pocketbase method hdd=10\n' + '/pocketbase method cpu=4 ram=2048 hdd=20\n' + '```\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); } // ── Allowed fields and their types ───────────────────────────────── // ── PocketBase: authenticate (shared by all paths) ───────────────── 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 (shared by all paths) ────────── 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); } // ── Route: dispatch to subcommand handler ────────────────────────── 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 (noteMatch) { // ── NOTE SUBCOMMAND (reads/writes notes_json on script record) ──── const noteAction = noteMatch[1].toLowerCase(); const noteArgsStr = rest.substring(noteMatch[0].length).trim(); // Parse notes_json from the already-fetched script record // PocketBase may return JSON fields as already-parsed objects let notesArr = []; try { const rawNotes = record.notes_json; notesArr = Array.isArray(rawNotes) ? rawNotes : JSON.parse(rawNotes || '[]'); } catch (e) { notesArr = []; } // Token parser: unquoted-word OR "quoted string" (supports \" escapes) function parseNoteTokens(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; } function formatNotesList(arr) { if (arr.length === 0) return '*None*'; return arr.map(function (n, i) { return (i + 1) + '. **`' + (n.type || '?') + '`**: ' + (n.text || ''); }).join('\n'); } async function patchNotesJson(arr) { const res = await request(recordsUrl + '/' + record.id, { method: 'PATCH', headers: { 'Authorization': token, 'Content-Type': 'application/json' }, body: JSON.stringify({ notes_json: JSON.stringify(arr) }) }); if (!res.ok) { await addReaction('-1'); await postComment('❌ **PocketBase Bot**: Failed to update `notes_json`:\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 = parseNoteTokens(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 patchNotesJson(notesArr); 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 = parseNoteTokens(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 patchNotesJson(notesArr); 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 = parseNoteTokens(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 patchNotesJson(notesArr); 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 (reads/writes install_methods_json on script record) ── const methodArgs = rest.replace(/^method\s*/i, '').trim(); const methodListMode = !methodArgs || methodArgs.toLowerCase() === 'list'; // Parse install_methods_json from the already-fetched script record // PocketBase may return JSON fields as already-parsed objects let methodsArr = []; try { const rawMethods = record.install_methods_json; methodsArr = Array.isArray(rawMethods) ? rawMethods : JSON.parse(rawMethods || '[]'); } catch (e) { methodsArr = []; } function formatMethodsList(arr) { if (arr.length === 0) return '*None*'; return arr.map(function (im, i) { const r = im.resources || {}; return (i + 1) + '. **`' + (im.type || '?') + '`** — CPU: `' + (r.cpu != null ? r.cpu : '?') + '` · RAM: `' + (r.ram != null ? r.ram : '?') + ' MB` · HDD: `' + (r.hdd != null ? r.hdd : '?') + ' GB`'; }).join('\n'); } async function patchInstallMethodsJson(arr) { const res = await request(recordsUrl + '/' + record.id, { method: 'PATCH', headers: { 'Authorization': token, 'Content-Type': 'application/json' }, body: JSON.stringify({ install_methods_json: JSON.stringify(arr) }) }); if (!res.ok) { await addReaction('-1'); await postComment('❌ **PocketBase Bot**: Failed to update `install_methods_json`:\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 { // Parse: cpu=N ram=N hdd=N const methodParts = methodArgs.match(/^(\S+)\s+(.+)$/); if (!methodParts) { await addReaction('-1'); await postComment( '❌ **PocketBase Bot**: Invalid `method` syntax.\n\n' + '**Usage:**\n```\n/pocketbase ' + slug + ' method list\n/pocketbase ' + slug + ' method hdd=10\n/pocketbase ' + slug + ' method cpu=4 ram=2048 hdd=20\n```' ); process.exit(0); } const targetType = methodParts[1].toLowerCase(); const resourcesStr = methodParts[2]; // Parse resource fields (only cpu/ram/hdd allowed) const RESOURCE_FIELDS = { cpu: true, ram: true, hdd: true }; const resourceChanges = {}; const rePairs = /([a-z]+)=(\d+)/gi; let m; while ((m = rePairs.exec(resourcesStr)) !== null) { const key = m[1].toLowerCase(); if (RESOURCE_FIELDS[key]) resourceChanges[key] = parseInt(m[2], 10); } if (Object.keys(resourceChanges).length === 0) { await addReaction('-1'); await postComment('❌ **PocketBase Bot**: No valid resource fields found. Use `cpu=N`, `ram=N`, `hdd=N`.'); process.exit(0); } // Find matching method by type name (case-insensitive) const idx = methodsArr.findIndex(function (im) { return (im.type || '').toLowerCase() === targetType; }); if (idx === -1) { await addReaction('-1'); const availableTypes = methodsArr.map(function (im) { return im.type || '?'; }); await postComment( '❌ **PocketBase Bot**: No install method with type `' + targetType + '` found for `' + slug + '`.\n\n' + '**Available types:** `' + (availableTypes.length ? availableTypes.join('`, `') : '(none)') + '`\n\n' + 'Use `/pocketbase ' + slug + ' method list` to see all methods.' ); process.exit(0); } if (!methodsArr[idx].resources) methodsArr[idx].resources = {}; if (resourceChanges.cpu != null) methodsArr[idx].resources.cpu = resourceChanges.cpu; if (resourceChanges.ram != null) methodsArr[idx].resources.ram = resourceChanges.ram; if (resourceChanges.hdd != null) methodsArr[idx].resources.hdd = resourceChanges.hdd; await patchInstallMethodsJson(methodsArr); const changesLines = Object.entries(resourceChanges) .map(function ([k, v]) { return '- `' + k + '` → `' + v + (k === 'ram' ? ' MB' : k === 'hdd' ? ' GB' : '') + '`'; }) .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' + '*Executed by @' + actor + '*' ); } } else if (setMatch) { // ── SET SUBCOMMAND (multi-line / HTML / special chars via 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); } 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 fieldsStr = rest; // Skipped: slug, script_created/updated, created (auto), categories/ // install_methods/notes/type (relations), github_data/install_methods_json/ // notes_json (auto-generated), execute_in (select relation), last_update_commit (auto) const ALLOWED_FIELDS = { name: 'string', description: 'string', logo: 'string', documentation: 'string', website: 'string', project_url: 'string', github: 'string', config_path: 'string', port: 'number', default_user: 'nullable_string', default_passwd: 'nullable_string', updateable: 'boolean', privileged: 'boolean', has_arm: 'boolean', is_dev: 'boolean', is_disabled: 'boolean', disable_message: 'string', is_deleted: 'boolean', deleted_message: 'string', }; // Field=value parser (handles quoted values and empty=null) function parseFields(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 (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; } const parsedFields = parseFields(fieldsStr); 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 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' + '*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, config_path: data.config_path, 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/scripts/generate-app-headers.sh ================================================ #!/usr/bin/env bash # Function for generating Figlet headers generate_headers() { local base_dir=$1 local target_subdir=$2 local search_pattern=$3 local headers_dir="${base_dir}/headers" mkdir -p "$headers_dir" rm -f "$headers_dir"/* # Recursive or non-recursive search if [[ "$search_pattern" == "**" ]]; then shopt -s globstar nullglob file_list=("${base_dir}"/**/*.sh) shopt -u globstar else file_list=("${base_dir}"/*.sh) fi for script in "${file_list[@]}"; do [[ -f "$script" ]] || continue app_name=$(grep -oP '^APP="\K[^"]+' "$script" 2>/dev/null) if [[ -n "$app_name" ]]; then output_file="${headers_dir}/$(basename "${script%.*}")" figlet_output=$(figlet -w 500 -f slant "$app_name") if [[ -n "$figlet_output" ]]; then echo "$figlet_output" >"$output_file" echo "Generated: $output_file" else echo "Figlet failed for $app_name in $script" fi else echo "No APP name found in $script, skipping." fi done } # ct generate_headers "./ct" "headers" "*" # tools (addon, pve, ...) generate_headers "./tools" "headers" "**" # vm generate_headers "./vm" "headers" "*" echo "Completed processing all sections." ================================================ FILE: .github/workflows/stale_pr_close.yml ================================================ name: Stale PR Management on: schedule: - cron: "0 0 * * *" workflow_dispatch: pull_request_target: types: - labeled jobs: stale-prs: runs-on: ubuntu-latest permissions: pull-requests: write issues: write contents: read steps: - name: Handle stale PRs uses: actions/github-script@v7 with: script: | const now = new Date(); const owner = context.repo.owner; const repo = context.repo.repo; // --- When stale label is added, comment immediately --- if (context.eventName === "pull_request_target" && context.payload.action === "labeled") { const label = context.payload.label?.name; if (label === "stale") { const author = context.payload.pull_request.user.login; await github.rest.issues.createComment({ owner, repo, issue_number: context.payload.pull_request.number, body: `@${author} This PR has been marked as stale. It will be closed if no new commits are added in 7 days.` }); } return; } // --- Scheduled run: check all stale PRs --- const { data: prs } = await github.rest.pulls.list({ owner, repo, state: "open", per_page: 100 }); for (const pr of prs) { const hasStale = pr.labels.some(l => l.name === "stale"); if (!hasStale) continue; // Get timeline events to find when stale label was added const { data: events } = await github.rest.issues.listEvents({ owner, repo, issue_number: pr.number, per_page: 100 }); // Find the most recent time the stale label was added const staleLabelEvents = events .filter(e => e.event === "labeled" && e.label?.name === "stale") .sort((a, b) => new Date(b.created_at) - new Date(a.created_at)); if (staleLabelEvents.length === 0) continue; const staleLabelDate = new Date(staleLabelEvents[0].created_at); const daysSinceStale = (now - staleLabelDate) / (1000 * 60 * 60 * 24); // Check for new commits since stale label was added const { data: commits } = await github.rest.pulls.listCommits({ owner, repo, pull_number: pr.number }); const lastCommitDate = new Date(commits[commits.length - 1].commit.author.date); const author = pr.user.login; // If there are new commits after the stale label, remove it if (lastCommitDate > staleLabelDate) { await github.rest.issues.removeLabel({ owner, repo, issue_number: pr.number, name: "stale" }); await github.rest.issues.createComment({ owner, repo, issue_number: pr.number, body: `@${author} Recent activity detected. Removing stale label.` }); } // If 7 days have passed since stale label, close the PR else if (daysSinceStale > 7) { await github.rest.pulls.update({ owner, repo, pull_number: pr.number, state: "closed" }); await github.rest.issues.createComment({ owner, repo, issue_number: pr.number, body: `@${author} Closing stale PR due to inactivity (no commits for 7 days after stale label).` }); } } ================================================ 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 patchRes = await request(recordsUrl + '/' + record.id, { method: 'PATCH', headers: { 'Authorization': token, 'Content-Type': 'application/json' }, body: JSON.stringify({ script_updated: new Date().toISOString().split('T')[0], last_update_commit: process.env.PR_URL || process.env.COMMIT_URL || '' }) }); 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: .vscode/.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: .vscode/.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: 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

March (14 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-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)) ## 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)) ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2021-2026 tteck | community-scripts ORG 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 VE Helper-Scripts Logo

Proxmox VE Helper-Scripts

A Community Legacy in Memory of @tteck

Website Discord Donate

Contribute Guides Changelog


**Simplify your Proxmox VE setup with community-driven automation scripts** Originally created by tteck, now maintained and expanded by the community

🙌 Shoutout to

selfh.st Icons
View on GitHub • Consistent, beautiful icons for 5000+ self-hosted apps
--- ## 🎯 Key Features

⚡ Quick Setup

One-command installations for popular services and containers

⚙️ Flexible Config

Simple mode for beginners, advanced options for power users

🔄 Auto Updates

Keep your installations current with built-in update mechanisms

🛠️ Easy Management

Post-install scripts for configuration and troubleshooting

👥 Community Driven

Actively maintained with contributions from users worldwide

📖 Well Documented

Comprehensive guides and community support

🔒 Secure

Regular security updates and best practices

⚡ Performance

Optimized configurations for best performance

--- ## 📋 Requirements

🖥️ Proxmox VE

Version: 8.4.x | 9.0.x | 9.1.x

🐧 Operating System

Debian-based with Proxmox Tools

🌐 Network

Internet connection required

--- ## 📥 Getting Started Choose your preferred installation method: ### Method 1: One-Click Web Installer The fastest way to get started: 1. Visit **[community-scripts.org](https://community-scripts.org/)** 🌐 2. Search for your desired script (e.g., "Home Assistant", "Docker") 3. Copy the bash command displayed on the script page 4. Open your **Proxmox Shell** and paste the command 5. Press Enter and follow the interactive prompts ### Method 2: PVEScripts-Local Install a convenient script manager directly in your Proxmox UI: ```bash bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/pve-scripts-local.sh)" ``` This adds a menu to your Proxmox interface for easy script access without visiting the website. 📖 **Learn more:** [ProxmoxVE-Local Repository](https://github.com/community-scripts/ProxmoxVE-Local) --- ## 💬 Join the Community

💬 Discord

Real-time chat, support, and discussions

Discord

💭 Discussions

Feature requests, Q&A, and ideas

Discussions

🐛 Issues

Bug reports and issue tracking

Issues
--- ## 🛠️ Contribute

💻 Code

Add new scripts or improve existing ones

📝 Documentation

Write guides, improve READMEs, translate content

🧪 Testing

Test scripts and report compatibility issues

💡 Ideas

Suggest features or workflow improvements


👉 Check our **[Contributing Guidelines](https://github.com/community-scripts/ProxmoxVE/blob/main/docs/contribution/README.md)** to get started
--- ## ❤️ Support the Project This project is maintained by volunteers in memory of tteck. Your support helps us maintain infrastructure, improve documentation, and give back to important causes. **🎗️ 30% of all donations go directly to cancer research and hospice care**
Support on Ko-fi
Every contribution helps keep this project alive and supports meaningful causes
--- ## 📈 Project Statistics

Repobeats analytics

Star History Chart

--- ## 📜 License This project is licensed under the **[MIT License](LICENSE)** - feel free to use, modify, and distribute. ---
Made with ❤️ by the Proxmox 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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: jkrgr0 # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://docs.2fauth.app/ | Github: https://github.com/Bubka/2FAuth 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" 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 mv "/opt/2fauth-backup/.env" "/opt/2fauth/.env" mv "/opt/2fauth-backup/storage" "/opt/2fauth/storage" cd "/opt/2fauth" || return chown -R www-data: "/opt/2fauth" chmod -R 755 "/opt/2fauth" export COMPOSER_ALLOW_SUPERUSER=1 $STD composer install --no-dev --prefer-dist php artisan 2fauth:install $STD systemctl restart nginx msg_ok "Updated successfully!" fi exit } start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} 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/actualbudget.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://actualbudget.org/ | Github: https://github.com/actualbudget/actual 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_info "Old Installation Found, you need to migrate your data and recreate to a new container" msg_info "Please follow the instructions on the Actual Budget website to migrate your data" msg_info "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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://adguard.com/ | Github: https://github.com/AdguardTeam/AdGuardHome 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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: MickLesk (Canbiz) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/seanmorley15/AdventureLog 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 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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/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=Linux64&fromVersion=0" | grep -o 'https://.*\.zip') 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/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://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/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/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-caddy.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/ProxmoxVE/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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Johann3s-H (An!ma) # License: MIT | https://github.com/community-scripts/ProxmoxVE/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/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://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}/x86_64-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/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/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/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://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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/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-it-tools.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: nicedevil007 (NiceDevil) # License: MIT | https://github.com/community-scripts/ProxmoxVE/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/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://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_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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: hoholms # License: MIT | https://github.com/community-scripts/ProxmoxVE/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/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://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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/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/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://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 check_container_storage check_container_resources 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/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.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/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://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/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/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-amd64.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/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://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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: andrej-kocijan (Andrej Kocijan) # License: MIT | https://github.com/community-scripts/ProxmoxVE/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 check_container_resources 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-x86_64-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/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/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/lejianwen/rustdesk-server/releases/download/${RELEASE}/rustdesk-server-linux-amd64.zip" -o "$temp_file1" $STD unzip "$temp_file1" cp -r amd64/* /opt/rustdesk-server/ echo "${RELEASE}" >~/.rustdesk-server $STD service rustdesk-server-hbbs start $STD service rustdesk-server-hbbr start rm -rf amd64 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-amd64.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/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/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 check_container_storage check_container_resources 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/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://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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: tremor021 (Slaviša Arežina) # License: MIT | https://github.com/community-scripts/ProxmoxVE/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_amd64-${RELEASE}.tar.bz2" -o ts3server.tar.bz2 tar -xf ./ts3server.tar.bz2 cp -ru teamspeak3-server_linux_amd64/* /opt/teamspeak-server/ rm -f ~/ts3server.tar.bz* rm -rf teamspeak3-server_linux_amd64 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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/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-amd64" -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/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://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/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://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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: pshankinclarke (lazarillo) # License: MIT | https://github.com/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/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-wireguard.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.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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/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/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/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/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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Michel Roegl-Brunner (michelroegl-brunner) # License: | MIT https://github.com/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Andy Grunwald (andygrunwald) # License: MIT | https://github.com/community-scripts/ProxmoxVE/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/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://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/apt-cacher-ng.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://archivebox.io/ | Github: https://github.com/ArchiveBox/ArchiveBox 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/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://release-argus.io/ | Github: https://github.com/release-argus/Argus 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-amd64" 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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://aria2.github.io/ | Github: https://github.com/aria2/aria2 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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: michelroegl-brunner # License: MIT | https://github.com/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: thost96 (thost96) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://www.authelia.com/ | Github: https://github.com/authelia/authelia 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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://autobrr.com/ | Github: https://github.com/autobrr/autobrr 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_x86_64.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/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://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/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/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 $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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: ksad (enirys31) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://garethgeorge.github.io/backrest/ | Github: https://github.com/garethgeorge/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_x86_64.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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: bvdberg01 # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://sabre.io/baikal/ | Github: https://github.com/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/bar-assistant.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: bvdberg01 | CanbiZ # License: MIT | https://github.com/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://www.bazarr.media/ | Github: https://github.com/morpheus65535/bazarr 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 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/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/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 $STD npm install http-server -g 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 msg_ok "Updated BentoPDF" msg_info "Starting Service" if grep -q '8080' /etc/systemd/system/bentopdf.service; then sed -i -e 's|/bentopdf|/bentopdf/dist|' \ -e 's|npx.*|npx http-server -g -b -d false -r --no-dotfiles|' \ /etc/systemd/system/bentopdf.service systemctl daemon-reload fi 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}http://${IP}:8080${CL}" ================================================ FILE: ct/beszel.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: Michelle Zitzerman (Sinofage) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://beszel.dev/ | Github: https://github.com/henrygd/beszel 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 msg_ok "Updated Beszel" 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/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/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-*-x86_64-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/bitmagnet.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/bitmagnet-io/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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://0xerr0r.github.io/blocky | Github: https://github.com/0xERR0R/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_x86_64.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/booklore.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/booklore-app/BookLore APP="BookLore" var_tags="${var_tags:-books;library}" var_cpu="${var_cpu:-3}" var_ram="${var_ram:-3072}" 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/booklore ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "booklore" "booklore-app/BookLore"; then JAVA_VERSION="25" setup_java NODE_VERSION="22" setup_nodejs setup_mariadb setup_yq ensure_dependencies ffmpeg msg_info "Stopping Service" systemctl stop booklore msg_ok "Stopped Service" if grep -qE "^BOOKLORE_(DATA_PATH|BOOKDROP_PATH|BOOKS_PATH|PORT)=" /opt/booklore_storage/.env 2>/dev/null; then msg_info "Migrating old environment variables" sed -i 's/^BOOKLORE_DATA_PATH=/APP_PATH_CONFIG=/g' /opt/booklore_storage/.env sed -i 's/^BOOKLORE_BOOKDROP_PATH=/APP_BOOKDROP_FOLDER=/g' /opt/booklore_storage/.env sed -i '/^BOOKLORE_BOOKS_PATH=/d' /opt/booklore_storage/.env sed -i '/^BOOKLORE_PORT=/d' /opt/booklore_storage/.env msg_ok "Migrated old environment variables" fi msg_info "Backing up old installation" mv /opt/booklore /opt/booklore_bak msg_ok "Backed up old installation" fetch_and_deploy_gh_release "booklore" "booklore-app/BookLore" "tarball" msg_info "Building Frontend" cd /opt/booklore/booklore-ui $STD npm install --force $STD npm run build --configuration=production msg_ok "Built Frontend" msg_info "Embedding Frontend into Backend" mkdir -p /opt/booklore/booklore-api/src/main/resources/static cp -r /opt/booklore/booklore-ui/dist/booklore/browser/* /opt/booklore/booklore-api/src/main/resources/static/ msg_ok "Embedded Frontend into Backend" msg_info "Building Backend" cd /opt/booklore/booklore-api APP_VERSION=$(get_latest_github_release "booklore-app/BookLore") yq eval ".app.version = \"${APP_VERSION}\"" -i src/main/resources/application.yaml $STD ./gradlew clean build -x test --no-daemon mkdir -p /opt/booklore/dist JAR_PATH=$(find /opt/booklore/booklore-api/build/libs -maxdepth 1 -type f -name "booklore-api-*.jar" ! -name "*plain*" | head -n1) if [[ -z "$JAR_PATH" ]]; then msg_error "Backend JAR not found" exit fi cp "$JAR_PATH" /opt/booklore/dist/app.jar msg_ok "Built Backend" if systemctl is-active --quiet nginx 2>/dev/null; then msg_info "Removing Nginx (no longer needed)" systemctl disable --now nginx $STD apt-get purge -y nginx nginx-common msg_ok "Removed Nginx" fi if ! grep -q "^SERVER_PORT=" /opt/booklore_storage/.env 2>/dev/null; then echo "SERVER_PORT=6060" >>/opt/booklore_storage/.env fi sed -i 's|ExecStart=.*|ExecStart=/usr/bin/java -XX:+UseG1GC -XX:+UseStringDeduplication -XX:+UseCompactObjectHeaders -XX:MaxRAMPercentage=75.0 -XX:+ExitOnOutOfMemoryError -jar /opt/booklore/dist/app.jar|' /etc/systemd/system/booklore.service systemctl daemon-reload msg_info "Starting Service" systemctl start booklore rm -rf /opt/booklore_bak 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}:6060${CL}" ================================================ FILE: ct/bookstack.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/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 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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: luismco # License: MIT | https://github.com/community-scripts/ProxmoxVE/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/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/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 read -rp "${TAB3}Did you make a backup via application WebUI? (y/n): " backuped if [[ "$backuped" =~ ^[Yy]$ ]]; then msg_info "Stopping Services" systemctl stop bytestash-backend bytestash-frontend msg_ok "Services Stopped" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "bytestash" "jordan-dalby/ByteStash" "tarball" 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" else msg_error "PLEASE MAKE A BACKUP FIRST!" exit 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 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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://caddyserver.com/ | Github: https://github.com/caddyserver/caddy 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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: mikolaj92 # License: MIT | https://github.com/community-scripts/ProxmoxVE/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 $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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://changedetection.io/ | Github: https://github.com/dgtlmoon/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 msg_ok "Updated ${APP}" msg_info "Updating Playwright" $STD pip3 install playwright --upgrade 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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/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/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/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" 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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Michel Roegl-Brunner (michelroegl-brunner) # License: MIT | https://github.com/community-scripts/ProxmoxVE/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:-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 /opt/checkmk_version.txt ]]; then msg_error "No ${APP} Installation Found!" exit fi 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) msg_info "Updating ${APP} to v${RELEASE}" $STD omd stop monitoring $STD omd cp monitoring monitoringbackup curl -fsSL "https://download.checkmk.com/checkmk/${RELEASE}/check-mk-raw-${RELEASE}_0.bookworm_amd64.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 ${APP}" 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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Lucas Zampieri (zampierilucas) | MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVE/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-amd64.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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: edoardop13 # License: MIT | https://github.com/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/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/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://cloudreve.org/ | Github: https://github.com/cloudreve/cloudreve 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_amd64.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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck | Co-Author: havardthom # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/cockpit-project/cockpit 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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: jdacode # License: MIT | https://github.com/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://www.commafeed.com/#/welcome | Github: https://github.com/Athou/commafeed 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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: finkerle # License: MIT | https://github.com/community-scripts/ProxmoxVE/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 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-x64.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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Omar Minaya | MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVE/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 /var ]]; 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" 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/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://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/cosmos.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: Michel Roegl-Brunner (michelroegl-brunner) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://cosmos-cloud.io/ | Github: https://github.com/azukaar/Cosmos-Server 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/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://docs.craftycontrol.com/pages/getting-started/installation/linux/ 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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://cronicle.net/ | Github: https://github.com/jhuckaby/Cronicle 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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Jakub Matraszek (jmatraszek) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://www.cross-seed.org | Github: https://github.com/cross-seed/cross-seed 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/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/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/daemonsync.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://daemonsync.me/ APP="Daemon Sync" var_tags="${var_tags:-sync}" 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 /var ]]; then msg_error "No ${APP} Installation Found!" exit fi msg_info "Updating LXC" $STD apt update $STD apt -y upgrade 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 it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8084${CL}" ================================================ FILE: ct/databasus.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/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" cp /opt/databasus/.env /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.14.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" cd /opt/databasus/frontend $STD npm ci $STD npm 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 /opt/databasus/.env rm -f /opt/databasus.env.bak chown postgres:postgres /opt/databasus/.env msg_ok "Restored Configuration" 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/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/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)" 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 rake assets:precompile $STD bundle exec rails db:migrate $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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: DragoQC | Co-Author: nickheyer # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://discopanel.app/ | Github: https://github.com/nickheyer/discopanel 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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: ekke85 | MickLesk # License: MIT | https://github.com/community-scripts/ProxmoxVE/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} $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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) | Migration: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVE/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/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://docmost.com/ | Github: https://github.com/docmost/docmost 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/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://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/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/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/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/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 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/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/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 msg_ok "Updated Domain Monitor" msg_info "Restoring backup" mv /opt/.env /opt/domain-monitor msg_ok "Restored backup" msg_info "Restarting Services" systemctl reload 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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: fstof # License: MIT | https://github.com/community-scripts/ProxmoxVE/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_x86_64.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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Kristian Skov # License: MIT | https://github.com/community-scripts/ProxmoxVE/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/drawio.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://www.drawio.com/ | Github: https://github.com/jgraph/drawio 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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: tremor021 # License: MIT | https://github.com/community-scripts/ProxmoxVE/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/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/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-*_amd64-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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: tremor021 # License: MIT | https://github.com/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://emby.media/ | Github: https://github.com/MediaBrowser/Emby.Releases 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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/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-amd64.deb" curl -fsSL -o "$DEB_FILE" "https://www.emqx.com/en/downloads/enterprise/v${RELEASE}/emqx-enterprise-${RELEASE}-debian12-amd64.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/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/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_gh_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_gh_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/ersatztv.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: MickLesk (Canbiz) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://ersatztv.org/ | Github: https://github.com/ErsatzTV/ErsatzTV 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-x64.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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: MickLesk (Canbiz) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/evcc-io/evcc 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/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/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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: kkroboth # License: MIT | https://github.com/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://firefly-iii.org/ | Github: https://github.com/firefly-iii/firefly-iii 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/fladder.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: wendyliga # License: MIT | https://github.com/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) | Co-Author: remz1337 # License: MIT | https://github.com/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: luismco # License: MIT | https://github.com/community-scripts/ProxmoxVE/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" 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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://flowiseai.com/ | Github: https://github.com/FlowiseAI/Flowise 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 msg_info "Updating FlowiseAI (this may take some time)" systemctl stop flowise $STD npm install -g flowise --upgrade 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/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/dotnetfactory/fluid-calendar 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/forgejo.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/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-amd64" 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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Arian Nasr (arian-nasr) # License: MIT | https://github.com/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: bvdberg01 # License: MIT | https://github.com/community-scripts/ProxmoxVE/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://git.community-scripts.org/community-scripts/ProxmoxVE/raw/branch/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Authors: MickLesk (CanbiZ) | Co-Author: remz1337 # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://frigate.video/ | Github: https://github.com/blakeblackshear/frigate APP="Frigate" var_tags="${var_tags:-nvr}" var_cpu="${var_cpu:-4}" 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/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://fumadocs.vercel.app/ | Github: https://github.com/fuma-nama/fumadocs 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}/x86_64-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/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/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/ghost.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: fabrice1236 # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://ghost.org/ | Github: https://github.com/TryGhost/Ghost 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" 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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: lucasfell # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://ghostfol.io/ | Github: https://github.com/ghostfolio/ghostfolio 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 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/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/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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) | Co-Author: Rogue-King # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://about.gitea.com/ | Github: https://github.com/go-gitea/gitea 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-amd64" 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/glance.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: kristocopani # License: MIT | https://github.com/community-scripts/ProxmoxVE/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-amd64.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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Giovanni Pellerano (evilaliv3) # License: MIT | https://github.com/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Nícolas Pastorello (opastorello) # License: MIT | https://github.com/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/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_amd64" 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/gokapi.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/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_amd64.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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://gotify.net/ | Github: https://github.com/gotify/server 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-amd64.zip" chmod +x /opt/gotify/gotify-linux-amd64 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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/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/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.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/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://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.0" 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 build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} 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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/gristlabs/grist-core 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 cp -r /opt/grist_bak/docs/* /opt/grist/docs/ cp /opt/grist_bak/grist-sessions.db /opt/grist/grist-sessions.db cp /opt/grist_bak/landing.db /opt/grist/landing.db cd /opt/grist $STD yarn install $STD yarn run install:ee $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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://grocy.info/ | Github: https://github.com/grocy/grocy 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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: HydroshieldMKII # License: MIT | https://github.com/community-scripts/ProxmoxVE/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/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://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-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-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/bar-assistant ================================================ ____ ___ _ __ __ / __ )____ ______ / | __________(_)____/ /_____ _____ / /_ / __ / __ `/ ___/_____/ /| | / ___/ ___/ / ___/ __/ __ `/ __ \/ __/ / /_/ / /_/ / / /_____/ ___ |(__ |__ ) (__ ) /_/ /_/ / / / / /_ /_____/\__,_/_/ /_/ |_/____/____/_/____/\__/\__,_/_/ /_/\__/ ================================================ FILE: ct/headers/bazarr ================================================ ____ / __ )____ _____ ____ ___________ / __ / __ `/_ / / __ `/ ___/ ___/ / /_/ / /_/ / / /_/ /_/ / / / / /_____/\__,_/ /___/\__,_/_/ /_/ ================================================ FILE: ct/headers/bentopdf ================================================ ____ __ ____ ____ ______ / __ )___ ____ / /_____ / __ \/ __ \/ ____/ / __ / _ \/ __ \/ __/ __ \/ /_/ / / / / /_ / /_/ / __/ / / / /_/ /_/ / ____/ /_/ / __/ /_____/\___/_/ /_/\__/\____/_/ /_____/_/ ================================================ FILE: ct/headers/beszel ================================================ ____ __ / __ )___ _________ ___ / / / __ / _ \/ ___/_ / / _ \/ / / /_/ / __(__ ) / /_/ __/ / /_____/\___/____/ /___/\___/_/ ================================================ FILE: ct/headers/bichon ================================================ ____ _ __ / __ )(_)____/ /_ ____ ____ / __ / / ___/ __ \/ __ \/ __ \ / /_/ / / /__/ / / / /_/ / / / / /_____/_/\___/_/ /_/\____/_/ /_/ ================================================ FILE: ct/headers/bitmagnet ================================================ ____ _ __ __ / __ )(_) /_____ ___ ____ _____ _____ ___ / /_ / __ / / __/ __ `__ \/ __ `/ __ `/ __ \/ _ \/ __/ / /_/ / / /_/ / / / / / /_/ / /_/ / / / / __/ /_ /_____/_/\__/_/ /_/ /_/\__,_/\__, /_/ /_/\___/\__/ /____/ ================================================ FILE: ct/headers/blocky ================================================ ____ __ __ / __ )/ /___ _____/ /____ __ / __ / / __ \/ ___/ //_/ / / / / /_/ / / /_/ / /__/ ,< / /_/ / /_____/_/\____/\___/_/|_|\__, / /____/ ================================================ FILE: ct/headers/booklore ================================================ ____ __ __ / __ )____ ____ / /__/ / ____ ________ / __ / __ \/ __ \/ //_/ / / __ \/ ___/ _ \ / /_/ / /_/ / /_/ / ,< / /___/ /_/ / / / __/ /_____/\____/\____/_/|_/_____/\____/_/ \___/ ================================================ 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/cosmos ================================================ ______ / ____/___ _________ ___ ____ _____ / / / __ \/ ___/ __ `__ \/ __ \/ ___/ / /___/ /_/ (__ ) / / / / / /_/ (__ ) \____/\____/____/_/ /_/ /_/\____/____/ ================================================ FILE: ct/headers/crafty-controller ================================================ ______ ______ ______ __ ____ / ____/________ _/ __/ /___ __ / ____/___ ____ / /__________ / / /__ _____ / / / ___/ __ `/ /_/ __/ / / /_____/ / / __ \/ __ \/ __/ ___/ __ \/ / / _ \/ ___/ / /___/ / / /_/ / __/ /_/ /_/ /_____/ /___/ /_/ / / / / /_/ / / /_/ / / / __/ / \____/_/ \__,_/_/ \__/\__, / \____/\____/_/ /_/\__/_/ \____/_/_/\___/_/ /____/ ================================================ FILE: ct/headers/cronicle ================================================ ______ _ __ / ____/________ ____ (_)____/ /__ / / / ___/ __ \/ __ \/ / ___/ / _ \ / /___/ / / /_/ / / / / / /__/ / __/ \____/_/ \____/_/ /_/_/\___/_/\___/ ================================================ FILE: ct/headers/cross-seed ================================================ __ ______________ __________ ________ ___ ____/ / / ___/ ___/ __ \/ ___/ ___/_____/ ___/ _ \/ _ \/ __ / / /__/ / / /_/ (__ |__ )_____(__ ) __/ __/ /_/ / \___/_/ \____/____/____/ /____/\___/\___/\__,_/ ================================================ FILE: ct/headers/cryptpad ================================================ ______ __ ____ __ / ____/______ ______ / /_/ __ \____ _____/ / / / / ___/ / / / __ \/ __/ /_/ / __ `/ __ / / /___/ / / /_/ / /_/ / /_/ ____/ /_/ / /_/ / \____/_/ \__, / .___/\__/_/ \__,_/\__,_/ /____/_/ ================================================ FILE: ct/headers/daemonsync ================================================ ____ _____ / __ \____ ____ ____ ___ ____ ____ / ___/__ ______ _____ / / / / __ `/ _ \/ __ `__ \/ __ \/ __ \ \__ \/ / / / __ \/ ___/ / /_/ / /_/ / __/ / / / / / /_/ / / / / ___/ / /_/ / / / / /__ /_____/\__,_/\___/_/ /_/ /_/\____/_/ /_/ /____/\__, /_/ /_/\___/ /____/ ================================================ 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/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/ersatztv ================================================ ______ __ _______ __ / ____/_____________ _/ /_____/_ __/ | / / / __/ / ___/ ___/ __ `/ __/_ / / / | | / / / /___/ / (__ ) /_/ / /_ / /_/ / | |/ / /_____/_/ /____/\__,_/\__/ /___/_/ |___/ ================================================ FILE: ct/headers/esphome ================================================ ___________ ____ __ __ / ____/ ___// __ \/ / / /___ ____ ___ ___ / __/ \__ \/ /_/ / /_/ / __ \/ __ `__ \/ _ \ / /___ ___/ / ____/ __ / /_/ / / / / / / __/ /_____//____/_/ /_/ /_/\____/_/ /_/ /_/\___/ ================================================ FILE: ct/headers/evcc ================================================ ___ _ ____________ / _ \ | / / ___/ ___/ / __/ |/ / /__/ /__ \___/|___/\___/\___/ ================================================ FILE: ct/headers/excalidraw ================================================ ______ ___ __ / ____/ ___________ _/ (_)___/ /________ __ __ / __/ | |/_/ ___/ __ `/ / / __ / ___/ __ `/ | /| / / / /____> < /_/ /_/\____/_/ /_/ /_/\___/_____/\____/_/|_| ================================================ FILE: ct/headers/homebridge ================================================ __ __ __ _ __ / / / /___ ____ ___ ___ / /_ _____(_)___/ /___ ____ / /_/ / __ \/ __ `__ \/ _ \/ __ \/ ___/ / __ / __ `/ _ \ / __ / /_/ / / / / / / __/ /_/ / / / / /_/ / /_/ / __/ /_/ /_/\____/_/ /_/ /_/\___/_.___/_/ /_/\__,_/\__, /\___/ /____/ ================================================ FILE: ct/headers/homepage ================================================ __ __ / / / /___ ____ ___ ___ ____ ____ _____ ____ / /_/ / __ \/ __ `__ \/ _ \/ __ \/ __ `/ __ `/ _ \ / __ / /_/ / / / / / / __/ /_/ / /_/ / /_/ / __/ /_/ /_/\____/_/ /_/ /_/\___/ .___/\__,_/\__, /\___/ /_/ /____/ ================================================ FILE: ct/headers/homer ================================================ __ __ / / / /___ ____ ___ ___ _____ / /_/ / __ \/ __ `__ \/ _ \/ ___/ / __ / /_/ / / / / / / __/ / /_/ /_/\____/_/ /_/ /_/\___/_/ ================================================ FILE: ct/headers/hortusfox ================================================ __ __ __ ______ / / / /___ _____/ /___ _______/ ____/___ _ __ / /_/ / __ \/ ___/ __/ / / / ___/ /_ / __ \| |/_/ / __ / /_/ / / / /_/ /_/ (__ ) __/ / /_/ /> < /_/ /_/\____/_/ \__/\__,_/____/_/ \____/_/|_| ================================================ FILE: ct/headers/hyperhdr ================================================ __ __ __ ______ ____ / / / /_ ______ ___ _____/ / / / __ \/ __ \ / /_/ / / / / __ \/ _ \/ ___/ /_/ / / / / /_/ / / __ / /_/ / /_/ / __/ / / __ / /_/ / _, _/ /_/ /_/\__, / .___/\___/_/ /_/ /_/_____/_/ |_| /____/_/ ================================================ FILE: ct/headers/hyperion ================================================ __ __ _ / / / /_ ______ ___ _____(_)___ ____ / /_/ / / / / __ \/ _ \/ ___/ / __ \/ __ \ / __ / /_/ / /_/ / __/ / / / /_/ / / / / /_/ /_/\__, / .___/\___/_/ /_/\____/_/ /_/ /____/_/ ================================================ FILE: ct/headers/immich ================================================ _ _ __ (_)___ ___ ____ ___ (_)____/ /_ / / __ `__ \/ __ `__ \/ / ___/ __ \ / / / / / / / / / / / / / /__/ / / / /_/_/ /_/ /_/_/ /_/ /_/_/\___/_/ /_/ ================================================ FILE: ct/headers/immichframe ================================================ ____ _ __ ______ / _/___ ___ ____ ___ (_)____/ /_ / ____/________ _____ ___ ___ / // __ `__ \/ __ `__ \/ / ___/ __ \/ /_ / ___/ __ `/ __ `__ \/ _ \ _/ // / / / / / / / / / / / /__/ / / / __/ / / / /_/ / / / / / / __/ /___/_/ /_/ /_/_/ /_/ /_/_/\___/_/ /_/_/ /_/ \__,_/_/ /_/ /_/\___/ ================================================ FILE: ct/headers/infisical ================================================ ____ _____ _ __ / _/___ / __(_)____(_)________ _/ / / // __ \/ /_/ / ___/ / ___/ __ `/ / _/ // / / / __/ (__ ) / /__/ /_/ / / /___/_/ /_/_/ /_/____/_/\___/\__,_/_/ ================================================ FILE: ct/headers/influxdb ================================================ ____ ______ ____ ____ / _/___ / __/ /_ ___ __/ __ \/ __ ) / // __ \/ /_/ / / / / |/_/ / / / __ | _/ // / / / __/ / /_/ /> < /_/ /_/_/_/ /_/_/_/ /_/\__,_/_/|_| ================================================ FILE: ct/headers/minio ================================================ __ ____ ________ / |/ (_)___ / _/ __ \ / /|_/ / / __ \ / // / / / / / / / / / / // // /_/ / /_/ /_/_/_/ /_/___/\____/ ================================================ 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/navidrome ================================================ _ __ _ __ / | / /___ __ __(_)___/ /________ ____ ___ ___ / |/ / __ `/ | / / / __ / ___/ __ \/ __ `__ \/ _ \ / /| / /_/ /| |/ / / /_/ / / / /_/ / / / / / / __/ /_/ |_/\__,_/ |___/_/\__,_/_/ \____/_/ /_/ /_/\___/ ================================================ FILE: ct/headers/neo4j ================================================ _ __ __ __ _ / | / /__ ____ / // / (_) / |/ / _ \/ __ \/ // /_/ / / /| / __/ /_/ /__ __/ / /_/ |_/\___/\____/ /_/_/ / /___/ ================================================ FILE: ct/headers/netbird ================================================ _ __ __ ____ _ __ / | / /__ / /_/ __ )(_)________/ / / |/ / _ \/ __/ __ / / ___/ __ / / /| / __/ /_/ /_/ / / / / /_/ / /_/ |_/\___/\__/_____/_/_/ \__,_/ ================================================ FILE: ct/headers/netbox ================================================ _ __ __ ____ / | / /__ / /_/ __ )____ _ __ / |/ / _ \/ __/ __ / __ \| |/_/ / /| / __/ /_/ /_/ / /_/ /> < /_/ |_/\___/\__/_____/\____/_/|_| ================================================ 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/pf2etools ================================================ ____ _______ ______ __ / __ \/ __/__ \ ___/_ __/___ ____ / /____ / /_/ / /_ __/ // _ \/ / / __ \/ __ \/ / ___/ / ____/ __// __// __/ / / /_/ / /_/ / (__ ) /_/ /_/ /____/\___/_/ \____/\____/_/____/ ================================================ 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/trilium ================================================ ______ _ ___ /_ __/____(_) (_)_ ______ ___ / / / ___/ / / / / / / __ `__ \ / / / / / / / / /_/ / / / / / / /_/ /_/ /_/_/_/\__,_/_/ /_/ /_/ ================================================ FILE: ct/headers/trip ================================================ __________ ________ /_ __/ __ \/ _/ __ \ / / / /_/ // // /_/ / / / / _, _// // ____/ /_/ /_/ |_/___/_/ ================================================ FILE: ct/headers/tududi ================================================ ______ __ ___ /_ __/_ ______/ /_ ______/ (_) / / / / / / __ / / / / __ / / / / / /_/ / /_/ / /_/ / /_/ / / /_/ \__,_/\__,_/\__,_/\__,_/_/ ================================================ FILE: ct/headers/tunarr ================================================ ______ /_ __/_ ______ ____ ___________ / / / / / / __ \/ __ `/ ___/ ___/ / / / /_/ / / / / /_/ / / / / /_/ \__,_/_/ /_/\__,_/_/ /_/ ================================================ FILE: ct/headers/twingate-connector ================================================ ______ _ __ ______ __ /_ __/ __(_)___ ____ _____ _/ /____ / ____/___ ____ ____ ___ _____/ /_____ _____ / / | | /| / / / __ \/ __ `/ __ `/ __/ _ \______/ / / __ \/ __ \/ __ \/ _ \/ ___/ __/ __ \/ ___/ / / | |/ |/ / / / / / /_/ / /_/ / /_/ __/_____/ /___/ /_/ / / / / / / / __/ /__/ /_/ /_/ / / /_/ |__/|__/_/_/ /_/\__, /\__,_/\__/\___/ \____/\____/_/ /_/_/ /_/\___/\___/\__/\____/_/ /____/ ================================================ FILE: ct/headers/typesense ================================================ ______ _____ /_ __/_ ______ ___ / ___/___ ____ ________ / / / / / / __ \/ _ \\__ \/ _ \/ __ \/ ___/ _ \ / / / /_/ / /_/ / __/__/ / __/ / / (__ ) __/ /_/ \__, / .___/\___/____/\___/_/ /_/____/\___/ /____/_/ ================================================ 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/verdaccio ================================================ _ __ __ _ | | / /__ _________/ /___ ___________(_)___ | | / / _ \/ ___/ __ / __ `/ ___/ ___/ / __ \ | |/ / __/ / / /_/ / /_/ / /__/ /__/ / /_/ / |___/\___/_/ \__,_/\__,_/\___/\___/_/\____/ ================================================ 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/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/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-amd64.tar.gz" msg_info "Updating Homarr" cp /opt/homarr/redis.conf /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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/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-amd64-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 helper-scripts.com --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|helper-scripts.com\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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck | Co-Author: MickLesk (Canbiz) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/sysadminsmedia/homebox 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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/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/homepage.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://gethomepage.dev/ | Github: https://github.com/gethomepage/homepage 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 $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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/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/hortusfox.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/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" fetch_and_deploy_gh_release "hortusfox" "danielbrendel/hortusfox-web" "tarball" msg_info "Updating HortusFox" cd /opt/hortusfox mv /opt/hortusfox-backup/.env /opt/hortusfox/.env $STD composer install --no-dev --optimize-autoloader $STD php asatru migrate --no-interaction $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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/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/immich.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://immich.app | Github: https://github.com/immich-app/immich 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=amd64] 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 iGPU 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 msg_ok "Intel iGPU dependencies updated" fi rm ./Dockerfile 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.5.6" 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" [[ -f ~/.vchord_version ]] && mv ~/.vchord_version ~/.vectorchord if check_for_gh_release "VectorChord" "tensorchord/VectorChord" "${VCHORD_RELEASE}" "updated together with Immich after testing"; then fetch_and_deploy_gh_release "VectorChord" "tensorchord/VectorChord" "binary" "${VCHORD_RELEASE}" "/tmp" "postgresql-16-vchord_*_amd64.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" 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 -R immich:immich "$INSTALL_DIR" chown immich:immich ./uv.lock 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 "Updating HW-accelerated machine-learning" $STD uv add --no-sync --optional openvino onnxruntime-openvino==1.24.1 --active -n -p "${ML_PYTHON}" --managed-python 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 patchelf --clear-execstack "${VIRTUAL_ENV}/lib/python3.13/site-packages/onnxruntime/capi/onnxruntime_pybind11_state.cpython-313-x86_64-linux-gnu.so" 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 chown -R immich:immich "$INSTALL_DIR" if [[ "${MAINT_MODE:-0}" == 1 ]]; then msg_info "Disabling Maintenance Mode" cd /opt/immich/app/bin $STD ./immich-admin disable-maintenance-mode 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:=$(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:=$(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:=$(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/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://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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: kristocopani # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://www.inspircd.org/ | Github: https://github.com/inspircd/inspircd 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/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/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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Benito Rodríguez (b3ni) # License: MIT | https://github.com/community-scripts/ProxmoxVE/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/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://invoiceninja.com/ | Github: https://github.com/invoiceninja/invoiceninja 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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://www.iobroker.net/#en/intro | Github: https://github.com/ioBroker/ioBroker.js-controller 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 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/itsm-ng.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: Florianb63 # License: MIT | https://github.com/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/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.LinuxAMDx64.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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Mips2648 # License: MIT | https://github.com/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/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:-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 /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-igc-core-2" "intel/intel-graphics-compiler" "binary" "latest" "" "intel-igc-core-2_*_amd64.deb" fetch_and_deploy_gh_release "intel-igc-opencl-2" "intel/intel-graphics-compiler" "binary" "latest" "" "intel-igc-opencl-2_*_amd64.deb" 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" 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/x86_64-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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: kristocopani # License: MIT | https://github.com/community-scripts/ProxmoxVE/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/joplin-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: Slaviša Arežina (tremor021) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://joplinapp.org/ | Github: https://github.com/laurent22/joplin 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/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/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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Dave-code-creater (Tan Dat, Ta) # License: MIT | https://github.com/community-scripts/ProxmoxVE/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/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/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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: MickLesk (Canbiz) & vhsdream # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://karakeep.app/ | Github: https://github.com/karakeep-app/karakeep 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 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 sed -i "s/^SERVER_VERSION=.*$/SERVER_VERSION=${CHECK_UPDATE_RELEASE#v}/" /etc/karakeep/karakeep.env 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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Omar Minaya # License: MIT | https://github.com/community-scripts/ProxmoxVE/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}" 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_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_URL" ]] || [[ -z "$KASM_VERSION" ]]; 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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://www.kavitareader.com/ | Github: https://github.com/Kareadita/Kavita 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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) | Co-Author: remz1337 # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/keycloak/keycloak 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/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/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 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/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.kimai.org/ | Github: https://github.com/kimai/kimai 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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: snazzybean # License: MIT | https://github.com/community-scripts/ProxmoxVE/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/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://koel.dev/ | Github: https://github.com/koel/koel 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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: bvdberg01 # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://koillection.github.io/ | Github: https://github.com/benjaminjonard/koillection 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 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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) | Co-Author: ulmentflam # License: MIT | https://github.com/community-scripts/ProxmoxVE/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-amd64.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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: tomfrenzel # License: MIT | https://github.com/community-scripts/ProxmoxVE/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/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://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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck | Co-Author: MountyMapleSyrup (MountyMapleSyrup) # License: MIT | https://github.com/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Stroopwafe1 # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://leantime.io | Github: https://github.com/Leantime/leantime 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/librenms.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: michelroegl-brunner # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://www.librenms.org/ | Github: https://github.com/librenms/librenms 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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Joseph Stubberfield (stubbers) # License: MIT | https://github.com/community-scripts/ProxmoxVE/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-x86_64-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/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/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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://lidarr.audio/ | Github: https://github.com/Lidarr/Lidarr 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 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-x64.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/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://community.limesurvey.org/ APP="LimeSurvey" 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 /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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (MickLesk) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://linkding.link/ | Github: https://github.com/sissbruecker/linkding 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" 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/x86_64-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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Omar Minaya | MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://linkstack.org/ | Github: https://github.com/linkstackorg/linkstack 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/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://linkwarden.app/ | Github: https://github.com/linkwarden/linkwarden 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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: bvdberg01 # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://listmonk.app/ | Github: https://github.com/knadh/listmonk 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_amd64.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/litellm.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: stout01 # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/BerriAI/litellm APP="LiteLLM" var_tags="${var_tags:-ai;interface}" 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/litellm.service ]]; then msg_error "No ${APP} Installation Found!" exit fi msg_info "Stopping Service" systemctl stop litellm msg_ok "Stopped Service" VENV_PATH="/opt/litellm/.venv" PYTHON_VERSION="3.13" USE_UVX="YES" setup_uv msg_info "Updating LiteLLM" $STD "$VENV_PATH/bin/python" -m pip install --upgrade litellm[proxy] prisma msg_ok "LiteLLM updated" msg_info "Updating DB Schema" $STD uv --directory=/opt/litellm run litellm --config /opt/litellm/litellm.yaml --use_prisma_db_push --skip_server_startup msg_ok "DB Schema Updated" msg_info "Starting Service" systemctl start litellm 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}:4000${CL}" ================================================ FILE: ct/livebook.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: dkuku # License: MIT | https://github.com/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) | Co-Author: remz1337 # License: MIT | https://github.com/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: hoholms # License: MIT | https://github.com/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: kristocopani # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://lubelogger.com/ | Github: https://github.com/hargata/lubelog 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/lyrionmusicserver.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: Omar Minaya # License: MIT | https://github.com/community-scripts/ProxmoxVE/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[^"]*amd64\.deb(?="[^>]*>)' | head -n 1) RELEASE=$(echo "$DEB_URL" | grep -oP 'lyrionmusicserver_\K[0-9.]+(?=_amd64\.deb)') DEB_FILE="/tmp/lyrionmusicserver_${RELEASE}_amd64.deb" if [[ ! -f /opt/lyrion_version.txt ]] || [[ "${RELEASE}" != "$(cat /opt/lyrion_version.txt)" ]]; then msg_info "Updating $APP to ${RELEASE}" curl -fsSL -o "$DEB_FILE" "$DEB_URL" $STD apt install "$DEB_FILE" -y systemctl restart lyrion $STD rm -f "$DEB_FILE" echo "${RELEASE}" >/opt/${APP}_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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://mafl.hywax.space/ | Github: https://github.com/hywax/mafl 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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) | Co-Author Slaviša Arežina (tremor021) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://magicmirror.builders/ | Github: https://github.com/MagicMirrorOrg/MagicMirror 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/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/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 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/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/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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: bvdberg01 | Co-Author: SunFlowerOwl # License: MIT | https://github.com/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/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/matterbridge.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: MickLesk (Canbiz) # License: MIT | https://github.com/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Kaedon Cleland-Host (dracentis) # License: MIT | https://github.com/community-scripts/ProxmoxVE/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/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://mealie.io | Github: https://github.com/mealie-recipes/mealie 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 msg_ok "Backup completed" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "mealie" "mealie-recipes/mealie" "tarball" "latest" "/opt/mealie" 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) $STD sed -i "s|https://github.com/mealie-recipes/mealie/commit/|https://github.com/mealie-recipes/mealie/releases/tag/|g" /opt/mealie/frontend/pages/admin/site-settings.vue $STD sed -i "s|value: data.buildId,|value: \"v${MEALIE_VERSION}\",|g" /opt/mealie/frontend/pages/admin/site-settings.vue $STD sed -i "s|value: data.production ? i18n.t(\"about.production\") : i18n.t(\"about.development\"),|value: \"bare-metal\",|g" /opt/mealie/frontend/pages/admin/site-settings.vue 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" msg_info "Updating NLTK Data" mkdir -p /nltk_data/ cd /opt/mealie $STD uv run python -m nltk.downloader -d /nltk_data averaged_perceptron_tagger_eng msg_ok "Updated NLTK Data" msg_info "Restoring Configuration" mv -f /opt/mealie.env /opt/mealie/mealie.env cat <<'STARTEOF' >/opt/mealie/start.sh #!/bin/bash set -a source /opt/mealie/mealie.env set +a exec uv run mealie STARTEOF chmod +x /opt/mealie/start.sh msg_ok "Configuration restored" 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/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/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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/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_amd64.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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/pymedusa/Medusa 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/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.meilisearch.com/ | Github: https://github.com/meilisearch/meilisearch 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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: MickLesk (Canbiz) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/usememos/memos 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" "latest" "/opt/memos" "memos*linux_amd64.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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://meshcentral.com/ | Github: https://github.com/Ylianst/MeshCentral 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/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://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/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/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 $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/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://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/miniflux.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: omernaveedxyz # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://miniflux.app/ | Github: https://github.com/miniflux/v2 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/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/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-amd64/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/mongodb.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: bvdberg01 # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://www.monicahq.com/ | Github: https://github.com/monicahq/monica 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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/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/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://ipcheck.ing/ | Github: https://github.com/jason5ng32/MyIP 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 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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) | Co-Author: MickLesk (Canbiz) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/gnmyt/myspeed 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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck | Co-Author: MickLesk (Canbiz) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://www.mysql.com/products/community | https://www.phpmyadmin.net 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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://n8n.io/ | Github: https://github.com/n8n-io/n8n 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/navidrome.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/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/neo4j.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck | Co-Author: havardthom # License: MIT | https://github.com/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: TechHutTV # License: MIT | https://github.com/community-scripts/ProxmoxVE/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 [[ ! -f /etc/netbird/config.json ]]; 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/netbox.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: bvdberg01 # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://netboxlabs.com/ | Github: https://github.com/netbox-community/netbox 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/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/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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://nextcloudpi.com/ 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/nextpvr.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/tteck/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/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://nginxui.com | Github: https://github.com/0xJacky/nginx-ui 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-64.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/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://nginxproxymanager.com/ | Github: https://github.com/NginxProxyManager/nginx-proxy-manager 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 [[ $(grep -E '^VERSION_ID=' /etc/os-release) == *"12"* ]]; then msg_error "Wrong Debian version detected!" msg_error "Please create a snapshot first. You must upgrade your LXC to Debian Trixie before updating. Visit: https://github.com/community-scripts/ProxmoxVE/discussions/7489" 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 apt-get purge -y nodejs npm apt-get 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 RELEASE=$(curl -fsSL https://api.github.com/repos/NginxProxyManager/nginx-proxy-manager/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4) }') CLEAN_INSTALL=1 fetch_and_deploy_gh_release "nginxproxymanager" "NginxProxyManager/nginx-proxy-manager" "tarball" "v${RELEASE}" "/opt/nginxproxymanager" msg_info "Stopping Services" systemctl stop openresty systemctl stop npm msg_ok "Stopped Services" 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" 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 "s|\"version\": \"2.0.0\"|\"version\": \"$RELEASE\"|" /opt/nginxproxymanager/backend/package.json sed -i "s|\"version\": \"2.0.0\"|\"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 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 "Updating Certbot" [ -f /etc/apt/trusted.gpg.d/openresty-archive-keyring.gpg ] && rm -f /etc/apt/trusted.gpg.d/openresty-archive-keyring.gpg [ -f /etc/apt/sources.list.d/openresty.list ] && rm -f /etc/apt/sources.list.d/openresty.list [ ! -f /etc/apt/trusted.gpg.d/openresty.gpg ] && curl -fsSL https://openresty.org/package/pubkey.gpg | gpg --dearmor --yes -o /etc/apt/trusted.gpg.d/openresty.gpg [ ! -f /etc/apt/sources.list.d/openresty.sources ] && cat <<'EOF' >/etc/apt/sources.list.d/openresty.sources Types: deb URIs: http://openresty.org/package/debian/ Suites: bookworm Components: openresty Signed-By: /etc/apt/trusted.gpg.d/openresty.gpg EOF $STD apt update $STD apt -y install openresty if [ -d /opt/certbot ]; then $STD /opt/certbot/bin/pip install --upgrade pip setuptools wheel $STD /opt/certbot/bin/pip install --upgrade certbot certbot-dns-cloudflare fi msg_ok "Updated Certbot" 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 systemctl restart openresty 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}:81${CL}" ================================================ FILE: ct/nightscout.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: aendel # License: MIT | https://github.com/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://www.nocodb.com/ | Github: https://github.com/nocodb/nocodb 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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://nodered.org/ | Github: https://github.com/node-red/node-red 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/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/NodeBB/NodeBB 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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: luismco # License: MIT | https://github.com/community-scripts/ProxmoxVE/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" 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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/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/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/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/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://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/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://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_x64\.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_x64.deb"" export DEBIAN_FRONTEND=noninteractive export DEBCONF_NOWARNINGS=yes $STD dpkg -i nxwitness-server-$RELEASE-linux_x64.deb rm -f /tmp/nxwitness-server-$RELEASE-linux_x64.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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck | Co-Author: havardthom # License: MIT | https://github.com/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: bvdberg01 # License: MIT | https://github.com/community-scripts/ProxmoxVE/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-amd64.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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/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/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/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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: havardthom | Co-Author: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVE/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-amd64.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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/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 msg_info "Updating 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") 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" 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}:8043${CL}" ================================================ FILE: ct/ombi.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://ombi.io/ | Github: https://github.com/Ombi-app/Ombi 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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: kristocopani # License: MIT | https://github.com/community-scripts/ProxmoxVE/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/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.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/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://openarchiver.com/ | Github: https://github.com/LogicLabs-OU/OpenArchiver 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/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://opencloud.eu | Github: https://github.com/opencloud-eu/opencloud 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="v5.2.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" rm -f /usr/bin/{OpenCloud,opencloud} CLEAN_INSTALL=1 fetch_and_deploy_gh_release "OpenCloud" "opencloud-eu/opencloud" "singlefile" "${RELEASE}" "/usr/bin" "opencloud-*-linux-amd64" mv /usr/bin/OpenCloud /usr/bin/opencloud 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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Jonathan (jd-apprentice) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://opengist.io/ | Github: https://github.com/thomiceli/opengist 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-amd64.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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/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-amd64.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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: michelroegl-brunner # License: MIT | https://github.com/community-scripts/ProxmoxVE/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/openwebui.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/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-amd64.tar.zst msg_ok "Download Complete" if [ -f "ollama-linux-amd64.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-amd64.tar.zst rm -rf ollama-linux-amd64.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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: emoscardini # License: MIT | https://github.com/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: emoscardini # License: MIT | https://github.com/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: bvdberg01 # License: MIT | https://github.com/community-scripts/ProxmoxVE/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_amd64.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/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/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="22" 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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://owncast.online/ | Github: https://github.com/owncast/owncast 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/pairdrop.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://pairdrop.net/ | Github: https://github.com/schlagmichdoch/PairDrop 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/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://pangolin.net/ | Github: https://github.com/fosrl/pangolin APP="Pangolin" 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"; 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 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_amd64" 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" msg_info "Running database migrations" cd /opt/pangolin ENVIRONMENT=prod $STD npx drizzle-kit push --config drizzle.sqlite.config.ts 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/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/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/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/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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://docs.paperless-ngx.com/ | Github: https://github.com/paperless-ngx/paperless-ngx 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 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/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/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" cp /opt/papra/apps/papra-server/.env /opt/papra_env.bak 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 cp /opt/papra_env.bak /opt/papra/apps/papra-server/.env $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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: bvdberg01 # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://docs.part-db.de/ | Github: https://github.com/Part-DB/Part-DB-server 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 RELEASE=$(get_latest_github_release "Part-DB/Part-DB-server") if check_for_gh_release "partdb" "Part-DB/Part-DB-server"; then msg_info "Stopping Service" systemctl stop apache2 msg_ok "Stopped Service" msg_info "Updating $APP to v${RELEASE}" cd /opt mv /opt/partdb/ /opt/partdb-backup curl -fsSL "https://github.com/Part-DB/Part-DB-server/archive/refs/tags/v${RELEASE}.zip" -o "/opt/v${RELEASE}.zip" $STD unzip "v${RELEASE}.zip" mv /opt/Part-DB-server-${RELEASE}/ /opt/partdb 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 yarn install $STD yarn build $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/v${RELEASE}.zip" rm -r /opt/partdb-backup echo "${RELEASE}" >~/.partdb msg_ok "Updated $APP to v${RELEASE}" 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/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://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/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/PatcMmon/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 ! grep -q "PORT=3001" /opt/patchmon/backend/.env; then msg_warn "⚠️ The next PatchMon update will include breaking changes (port changes)." msg_warn "See details here: https://github.com/community-scripts/ProxmoxVE/pull/11888" msg_warn "Press Enter to continue with the update, or Ctrl+C to abort..." read -r fi RELEASE="v1.4.2" NODE_VERSION="24" setup_nodejs if check_for_gh_release "PatchMon" "PatchMon/PatchMon" "${RELEASE}"; then msg_info "Stopping Service" systemctl stop patchmon-server msg_ok "Stopped Service" msg_info "Creating Backup" cp /opt/patchmon/backend/.env /opt/backend.env cp /opt/patchmon/frontend/.env /opt/frontend.env msg_ok "Backup Created" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "PatchMon" "PatchMon/PatchMon" "tarball" "${RELEASE}" "/opt/patchmon" msg_info "Updating PatchMon" VERSION=$(get_latest_github_release "PatchMon/PatchMon") SERVER_PORT="$(sed -n '/SERVER_PORT/s/[^=]*=//p' /opt/backend.env)" sed -i 's/PORT=3399/PORT=3001/' /opt/backend.env sed -i -e "s/VERSION=.*/VERSION=$VERSION/" \ -e '/^VITE_API_URL/d' /opt/frontend.env export NODE_ENV=production cd /opt/patchmon $STD npm install --no-audit --no-fund --no-save --ignore-scripts cd /opt/patchmon/frontend mv /opt/frontend.env /opt/patchmon/frontend/.env $STD npm install --no-audit --no-fund --no-save --ignore-scripts --include=dev $STD npm run build cd /opt/patchmon/backend mv /opt/backend.env /opt/patchmon/backend/.env $STD npm run db:generate $STD npx prisma migrate deploy cp /opt/patchmon/docker/nginx.conf.template /etc/nginx/sites-available/patchmon.conf sed -i -e 's|proxy_pass .*|proxy_pass http://127.0.0.1:3001;|' \ -e '\|try_files |i\ root /opt/patchmon/frontend/dist;' \ -e 's|alias.*|alias /opt/patchmon/frontend/dist/assets;|' \ -e '\|expires 1y|i\ root /opt/patchmon/frontend/dist;' /etc/nginx/sites-available/patchmon.conf if [[ -n "$SERVER_PORT" ]] && [[ "$SERVER_PORT" != "443" ]]; then sed -i "s/listen [[:digit:]].*/listen ${SERVER_PORT};/" /etc/nginx/sites-available/patchmon.conf fi ln -sf /etc/nginx/sites-available/patchmon.conf /etc/nginx/sites-enabled/ rm -f /etc/nginx/sites-enabled/default $STD nginx -t systemctl restart nginx msg_ok "Updated PatchMon" msg_info "Starting Service" if grep -q '/usr/bin/node' /etc/systemd/system/patchmon-server.service; then sed -i 's|ExecStart=.*|ExecStart=/usr/bin/npm run start|' /etc/systemd/system/patchmon-server.service systemctl daemon-reload fi 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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Nícolas Pastorello (opastorello) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://www.paymenter.org | Github: https://github.com/paymenter/paymenter 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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) | Co-Author: remz1337 # License: MIT | https://github.com/community-scripts/ProxmoxVE/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 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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: bvdberg01 # License: MIT | https://github.com/community-scripts/ProxmoxVE/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" cp -r /opt/pelican-panel/.env /opt/ 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/ 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" mv /opt/.env /opt/pelican-panel/ $SQLITE_INSTALL && mv /opt/*.sqlite /opt/pelican-panel/database/ $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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: bvdberg01 # License: MIT | https://github.com/community-scripts/ProxmoxVE/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_amd64" 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/pf2etools.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: TheRealVira # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://pf2etools.com/ | Github: https://github.com/Pf2eToolsOrg/Pf2eTools APP="Pf2eTools" var_tags="${var_tags:-wiki}" 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 "/opt/${APP}" ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "pf2etools" "Pf2eToolsOrg/Pf2eTools"; then msg_info "Updating System" $STD apt update $STD apt -y upgrade msg_ok "Updated System" rm -rf /opt/Pf2eTools fetch_and_deploy_gh_release "pf2etools" "Pf2eToolsOrg/Pf2eTools" "tarball" "latest" "/opt/Pf2eTools" msg_info "Updating ${APP}" cd /opt/Pf2eTools $STD npm install $STD npm run build chown -R www-data: "/opt/${APP}" chmod -R 755 "/opt/${APP}" msg_ok "Updated ${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}http://${IP}${CL}" ================================================ FILE: ct/photoprism.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://www.photoprism.app/ | Github: https://github.com/photoprism/photoprism 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-amd64.tar.gz" LIBHEIF_URL=$(curl -fsSL "https://dl.photoprism.app/dist/libheif/" | grep -oP "libheif-bookworm-amd64-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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/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/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/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/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://plant-it.org/ | Github: https://github.com/MDeLuise/plant-it 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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: tteck (tteckster) | MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://pocketbase.io/ | Github: https://github.com/pocketbase/pocketbase 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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Snarkenfaugister # License: MIT | https://github.com/community-scripts/ProxmoxVE/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-amd64" 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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/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 helper-scripts.com --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|helper-scripts.com\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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/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/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://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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Nícolas Pastorello (opastorello) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://privatebin.info/ | Github: https://github.com/PrivateBin/PrivateBin 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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: michelroegl-brunner # License: MIT | https://github.com/community-scripts/ProxmoxVE/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 /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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: bvdberg01 # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://www.projectsend.org/ | Github: https://github.com/projectsend/projectsend 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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Andy Grunwald (andygrunwald) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://prometheus.io/ | Github: https://github.com/prometheus/alertmanager 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-amd64.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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Marfnl # License: MIT | https://github.com/community-scripts/ProxmoxVE/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-amd64.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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Andy Grunwald (andygrunwald) # License: MIT | https://github.com/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://prometheus.io/ | Github: https://github.com/prometheus/prometheus 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-amd64.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/prowlarr.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://prowlarr.com/ | Github: https://github.com/Prowlarr/Prowlarr 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 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-x64.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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/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/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: 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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: thost96 (thost96) # License: MIT | https://github.com/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: liecno # License: MIT | https://github.com/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: bvdberg01 # License: MIT | https://github.com/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: bvdberg01 # License: MIT | https://github.com/community-scripts/ProxmoxVE/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_amd64" 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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: rcourtman & vhsdream # License: MIT | https://github.com/community-scripts/ProxmoxVE/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-amd64.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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: michelroegl-brunner # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/community-scripts/ProxmoxVE-Local 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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://www.qbittorrent.org/ | Github: https://github.com/qbittorrent/qBittorrent 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/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/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" "binary" "latest" "/usr/bin/qdrant" 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/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/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_x86_64.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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck | Co-Author: MickLesk (Canbiz) # License: MIT | https://github.com/community-scripts/ProxmoxVE/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 setup_deb822_repo \ "rabbitmq" \ "https://keys.openpgp.org/vks/v1/by-fingerprint/0A9AF2115F4687BD29803A206B73A36E6026DFCA" \ "https://deb1.rabbitmq.com/rabbitmq-server/debian/trixie" \ "trixie" 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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://radarr.video/ | Github: https://github.com/Radarr/Radarr 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 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-x64.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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: tremor021 # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://radicale.org/ | Github: https://github.com/Kozea/Radicale 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/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/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-amd64.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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: vhsdream | MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://rxresume.org | Github: https://github.com/amruthpillai/reactive-resume 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" 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 mv /opt/reactive-resume.env.bak /opt/reactive-resume/.env msg_ok "Updated Reactive Resume" 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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/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 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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/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-amd64" 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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: MrYadro # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://recyclarr.dev/wiki/ | Github: https://github.com/recyclarr/recyclarr 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 msg_info "Updating ${APP}" fetch_and_deploy_gh_release "recyclarr" "recyclarr/recyclarr" "prebuild" "latest" "/usr/local/bin" "recyclarr-linux-x64.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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/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/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/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 if [ ! -d /var/cache/nginx/tiles ]; then msg_info "Installing Nginx Tile Cache" mkdir -p /var/cache/nginx/tiles $STD apt install -y nginx 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 echo "reitti.ui.tiles.cache.url=http://127.0.0.1" >> /opt/reitti/application.properties systemctl restart reitti msg_info "Installed Nginx Tile Cache" 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 chown -R www-data:www-data /var/cache/nginx chmod -R 750 /var/cache/nginx systemctl restart nginx msg_ok "Started Service" msg_ok "Updated successfully!" fi if check_for_gh_release "photon" "komoot/photon"; then if [[ -f "$HOME/.photon" ]] && [[ "$(cat "$HOME/.photon")" == 0.7 ]]; then CURRENT_VERSION="$(<"$HOME/.photon")" echo echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "Photon v1 upgrade detected (breaking change)" echo echo "Your current version: $CURRENT_VERSION" echo echo "Photon v1 requires a manual migration before updating." echo echo "You need to:" echo " 1. Remove existing geocoding data (not actual reitti data):" echo " rm -rf /opt/photon_data" echo echo " 2. Follow the inial setup guide again:" echo " https://github.com/community-scripts/ProxmoxVE/discussions/8737" echo echo " 3. Re-download and import Photon data for v1" echo read -rp "Do you want to continue anyway? (y/N): " CONTINUE echo if [[ ! "$CONTINUE" =~ ^[Yy]$ ]]; then msg_info "Migration required. Update cancelled." exit 0 fi msg_warn "Continuing without migration may break Photon in the future!" fi msg_info "Stopping Service" systemctl stop photon msg_ok "Stopped Service" rm -f /opt/photon/photon.jar USE_ORIGINAL_FILENAME="true" fetch_and_deploy_gh_release "photon" "komoot/photon" "singlefile" "latest" "/opt/photon" "photon-*.jar" mv /opt/photon/photon-*.jar /opt/photon/photon.jar msg_info "Starting Service" systemctl start photon systemctl restart 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}:8080${CL}" ================================================ FILE: ct/resiliosync.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: David Bennett (dbinit) # License: MIT | https://github.com/community-scripts/ProxmoxVE/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/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/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 '25s/localhost/0.0.0.0/g' /opt/revealjs/gulpfile.js 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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (CanbiZ) | DevelopmentCats | AlphaLawless # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://romm.app | Github: https://github.com/rommapp/romm 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 ln -sfn /var/lib/romm/resources /opt/romm/frontend/dist/assets/romm/resources ln -sfn /var/lib/romm/assets /opt/romm/frontend/dist/assets/romm/assets 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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) | Migration: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVE/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/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/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" "lejianwen/rustdesk-server" "binary" "latest" "/opt/rustdesk" "rustdesk-server-hbbr*amd64.deb" fetch_and_deploy_gh_release "rustdesk-hbbs" "lejianwen/rustdesk-server" "binary" "latest" "/opt/rustdesk" "rustdesk-server-hbbs*amd64.deb" fetch_and_deploy_gh_release "rustdesk-utils" "lejianwen/rustdesk-server" "binary" "latest" "/opt/rustdesk" "rustdesk-server-utils*amd64.deb" fetch_and_deploy_gh_release "rustdesk-api" "lejianwen/rustdesk-api" "binary" "latest" "/opt/rustdesk" "rustdesk-api-server*amd64.deb" msg_info "Starting services" systemctl start -q rustdesk-* msg_ok "Services started" 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}${IP}:21114${CL}" ================================================ FILE: ct/rustypaste.sh ================================================ #!/usr/bin/env bash source <(curl -s https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: GoldenSpringness # License: MIT | https://github.com/community-scripts/ProxmoxVE/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" "*x86_64-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" "*x86_64-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/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/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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) | Co-Author: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://sabnzbd.org/ | Github: https://github.com/sabnzbd/sabnzbd 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-amd64.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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: bvdberg01 # License: MIT | https://github.com/community-scripts/ProxmoxVE/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/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/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/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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: JasonGreenC # License: MIT | https://github.com/community-scripts/ProxmoxVE/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 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/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/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/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/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/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/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/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://docs.seerr.dev/ | Github: https://github.com/seerr-team/seerr 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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: kristocopani # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://semaphoreui.com/ | Github: https://github.com/semaphoreui/semaphore 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 [[ -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. Unfortunately, this will reset your application and make it a fresh" msg_warn "installation. All your data will be lost!" 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" systemctl stop semaphore rm -rf /opt/semaphore/semaphore_db.bolt 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 SEM_PW=$(cat ~/semaphore.creds) systemctl start semaphore $STD semaphore user add --admin --login admin --email admin@helper-scripts.com --name Administrator --password "${SEM_PW}" --config /opt/semaphore/config.json msg_ok "Moved from BoltDB to SQLite" fi fi if check_for_gh_release "semaphore" "semaphoreui/semaphore"; then 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_amd64.deb" 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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/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/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/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="22" setup_nodejs PYTHON_VERSION="3.12" 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" 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 $STD uv pip install -r ./requirements-base.txt if [[ $(sed -n '/_BYPASS=/s/[^=]*=//p' /etc/shelfmark/.env) == "true" ]] && [[ $(sed -n '/BYPASSER=/s/[^=]*=//p' /etc/shelfmark/.env) == "false" ]]; then $STD uv pip install -r ./requirements-shelfmark.txt 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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/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/signoz.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://signoz.io/ | Github: https://github.com/SigNoz/signoz 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_amd64.tar.gz" fetch_and_deploy_gh_release "signoz-otel-collector" "SigNoz/signoz-otel-collector" "prebuild" "latest" "/opt/signoz-otel-collector" "signoz-otel-collector_linux_amd64.tar.gz" fetch_and_deploy_gh_release "signoz-schema-migrator" "SigNoz/signoz-otel-collector" "prebuild" "latest" "/opt/signoz-schema-migrator" "signoz-schema-migrator_linux_amd64.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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Dominik Siebel (dsiebel) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://silverbullet.md | Github: https://github.com/silverbulletmd/silverbullet 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-x86_64.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/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/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-x64.zip" msg_info "Restoring config" mv /opt/slskd.yml.bak /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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Michel Roegl-Brunner (michelroegl-brunner) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://snipeitapp.com/ | Github: https://github.com/grokability/snipe-it 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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: TuroYT # License: MIT | https://github.com/community-scripts/ProxmoxVE/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/sonarqube.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: prop4n # License: MIT | https://github.com/community-scripts/ProxmoxVE/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 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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://sonarr.tv/ | Github: https://github.com/Sonarr/Sonarr 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 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-x64.tar.gz" msg_info "Starting Service" systemctl start sonarr 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}:8989${CL}" ================================================ FILE: ct/sonobarr.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: GoldenSpringness # License: MIT | https://github.com/community-scripts/ProxmoxVE/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/sparkyfitness.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: Tom Frenzel (tomfrenzel) # License: MIT | https://github.com/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: AlphaLawless # License: MIT | https://github.com/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: rcastley # License: MIT | https://github.com/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: MickLesk (Canbiz) # License: MIT | https://github.com/community-scripts/ProxmoxVE/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/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/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-x64-*.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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Kristian Skov # License: MIT | https://github.com/community-scripts/ProxmoxVE/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/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.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/stirling-pdf.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://www.stirlingpdf.com/ | Github: https://github.com/Stirling-Tools/Stirling-PDF 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/strapi.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: pespinel # License: MIT | https://github.com/community-scripts/ProxmoxVE/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/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/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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: luismco # License: MIT | https://github.com/community-scripts/ProxmoxVE/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_amd64" 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/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://sure.am | Github: https://github.com/we-promise/sure 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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: EEJoshua # License: MIT | https://github.com/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/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/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://tandoor.dev/ | Github: https://github.com/TandoorRecipes/recipes 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 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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/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/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/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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://tautulli.com/ | Github: https://github.com/Tautulli/Tautulli 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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/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/teamspeak-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: tremor021 (Slaviša Arežina) # License: MIT | https://github.com/community-scripts/ProxmoxVE/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_amd64-\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_amd64-${RELEASE}.tar.bz2" -o ts3server.tar.bz2 tar -xf ./ts3server.tar.bz2 cp -ru teamspeak3-server_linux_amd64/* /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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/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-8.0"; then $STD apt remove -y aspnetcore-runtime-8.0 [ -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-9.0 fi RELEASE=$(curl -fsSL https://technitium.com/dns/ | grep -oP 'Version \K[\d.]+') if [[ ! -f ~/.technitium || ${RELEASE} != "$(cat ~/.technitium)" ]]; then msg_info "Updating Technitium DNS" curl -fsSL "https://download.technitium.com/dns/DnsServerPortable.tar.gz" -o /opt/DnsServerPortable.tar.gz $STD tar zxvf /opt/DnsServerPortable.tar.gz -C /opt/technitium/dns/ rm -f /opt/DnsServerPortable.tar.gz echo "${RELEASE}" >~/.technitium systemctl restart technitium msg_ok "Updated Technitium DNS" 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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Dominik Siebel (dsiebel) # License: MIT | https://github.com/community-scripts/ProxmoxVE/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.amd64.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/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/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/termix.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/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" 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" 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 '/^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 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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: kristocopani # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://thelounge.chat/ | Github: https://github.com/thelounge/thelounge 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/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/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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/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" "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_amd64" 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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: MickLesk (Canbiz) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/msgbyte/tianji 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/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/steveiliop56/tinyauth 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" 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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://www.traccar.org/ | Github: https://github.com/traccar/traccar 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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: durzo # License: MIT | https://github.com/community-scripts/ProxmoxVE/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:-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 /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 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) 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 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/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://tracktor.bytedge.in | Github: https://github.com/javedh-dev/tracktor 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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://traefik.io/ | Github: https://github.com/traefik/traefik 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_amd64.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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/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/trilium.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/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/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/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/tududi.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://tududi.com/ | Github: https://github.com/chrisvel/tududi 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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: chrisbenincasa # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://tunarr.com/ | Github: https://github.com/chrisbenincasa/tunarr 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-x64.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" "*-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 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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: twingate-andrewb # License: MIT | https://github.com/community-scripts/ProxmoxVE/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 ${APP}" ensure_dependencies 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/typesense.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: tlissak | Co-Author MickLesk # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://typesense.org/ APP="TypeSense" 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 /etc/typesense/typesense-server.ini ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "typesense" "typesense/typesense"; then msg_info "Updating Typesense" $STD apt update $STD apt -y upgrade msg_ok "Updated successfully!" fi exit } start build_container description msg_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}:8108${CL}" ================================================ FILE: ct/ubuntu.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: zackwithak13 # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://www.uhfapp.com/server | Github: https://github.com/swapplications/uhf-server-dist 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-x64-*.zip" fetch_and_deploy_gh_release "uhf-server" "swapplications/uhf-server-dist" "prebuild" "latest" "/opt/uhf-server" "UHF.Server-linux-x64-*.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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://umami.is/ | Github: https://github.com/umami-software/umami 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" fetch_and_deploy_gh_release "umami" "umami-software/umami" "tarball" 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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: elvito # License: MIT | https://github.com/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: wimb0 # License: MIT | https://github.com/community-scripts/ProxmoxVE/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/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://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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Eduard González (wanetty) # License: MIT | https://github.com/community-scripts/ProxmoxVE/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_amd64.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/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/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_amd64.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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://uptime.kuma.pet/ | Github: https://github.com/louislam/uptime-kuma 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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Kristian Skov # License: MIT | https://github.com/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: pshankinclarke (lazarillo) # License: MIT | https://github.com/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/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/verdaccio.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: BrynnJKnight # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://verdaccio.org/ | Github: https://github.com/verdaccio/verdaccio APP="Verdaccio" var_tags="${var_tags:-dev-tools;npm;registry}" 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/verdaccio.service ]]; then msg_error "No ${APP} Installation Found!" exit fi msg_info "Updating LXC Container" $STD apt update $STD apt upgrade -y msg_ok "Updated LXC Container" NODE_VERSION="24" NODE_MODULE="verdaccio" setup_nodejs systemctl restart verdaccio 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}:4873${CL}" ================================================ FILE: ct/victoriametrics.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/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 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-amd64-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-amd64-v[0-9.]+\.tar\.gz$') fetch_and_deploy_gh_release "victoriametrics" "VictoriaMetrics/VictoriaMetrics" "prebuild" "latest" "/opt/victoriametrics" "$victoriametrics_filename" fetch_and_deploy_gh_release "vmutils" "VictoriaMetrics/VictoriaMetrics" "prebuild" "latest" "/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-amd64-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-amd64-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 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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: MickLesk (Canbiz) | Co-Author: CrazyWolf13 # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://vikunja.io/ | Github: https://github.com/go-vikunja/vikunja 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/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://wallabag.org/ | Github: https://github.com/wallabag/wallabag 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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: MickLesk (Canbiz) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/ellite/wallos 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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: rrole # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://wanderer.to | Github: https://github.com/open-wanderer/wanderer 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" "binary" "latest" "/opt/wanderer/source/search" 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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: BvdBerg01 # License: MIT | https://github.com/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: MickLesk (Canbiz) # License: MIT | https://github.com/community-scripts/ProxmoxVE/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}_x86_64-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/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/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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Don Locke (DonLocke) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/wavelog/wavelog 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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Omar Minaya # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://wazuh.com/ 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/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://wealthfolio.app/ | Github: https://github.com/afadil/wealthfolio 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 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/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/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" "CrazyWolf13/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" "CrazyWolf13/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/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/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 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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/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/wikijs.sh ================================================ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://js.wiki/ | Github: https://github.com/requarks/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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Dunky13 # License: MIT | https://github.com/community-scripts/ProxmoxVE/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/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/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/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://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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: StellaeAlis # License: MIT | https://github.com/community-scripts/ProxmoxVE/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_amd64.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 .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|/yamtrack/staticfiles/|/opt/yamtrack/src/staticfiles/|' /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/yt-dlp-webui.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/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-amd64" 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/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/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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/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/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.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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: Michel Roegl-Brunner (michelroegl-brunner) # License: MIT | https://github.com/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: community-scripts # License: MIT | https://github.com/community-scripts/ProxmoxVE/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" 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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: tremor021 # License: MIT | https://github.com/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://www.zigbee2mqtt.io/ | Github: https://github.com/Koenkk/zigbee2mqtt 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 || 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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: MickLesk (Canbiz) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/diced/zipline 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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 community-scripts ORG # Author: dave-yap (dave-yap) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://zitadel.com/ | Github: https://github.com/zitadel/zitadel 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-amd64.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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://zoraxy.aroz.org/ | Github: https://github.com/tobychui/zoraxy 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_amd64" 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/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://zotregistry.dev/ | Github: https://github.com/project-zot/zot 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-amd64" 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/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://zwave-js.github.io/zwave-js-ui/#/ | Github: https://github.com/zwave-js/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: docs/DEV_MODE.md ================================================ # Dev Mode - Debugging & Development Guide Development modes provide powerful debugging and testing capabilities for container creation and installation processes. ## Quick Start ```bash # Single mode export dev_mode="motd" bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/wallabag.sh)" # Multiple modes (comma-separated) export dev_mode="motd,keep,trace" bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/wallabag.sh)" # Combine with verbose output export var_verbose="yes" export dev_mode="pause,logs" bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/wallabag.sh)" ``` ## Available Modes ### 1. **motd** - Early SSH/MOTD Setup Sets up SSH access and MOTD **before** the main application installation. **Use Case**: - Quick access to container for manual debugging - Continue installation manually if something goes wrong - Verify container networking before main install **Behavior**: ``` ✔ Container created ✔ Network configured [DEV] Setting up MOTD and SSH before installation ✔ [DEV] MOTD/SSH ready - container accessible # Container is now accessible via SSH while installation proceeds ``` **Combined with**: `keep`, `breakpoint`, `logs` --- ### 2. **keep** - Preserve Container on Failure Never delete the container when installation fails. Skips cleanup prompt. **Use Case**: - Repeated tests of the same installation - Debugging failed installations - Manual fix attempts **Behavior**: ``` ✖ Installation failed in container 107 (exit code: 1) ✔ Container creation log: /tmp/create-lxc-107-abc12345.log ✔ Installation log: /tmp/install-lxc-107-abc12345.log 🔧 [DEV] Keep mode active - container 107 preserved root@proxmox:~# ``` **Container remains**: `pct enter 107` to access and debug **Combined with**: `motd`, `trace`, `logs` --- ### 3. **trace** - Bash Command Tracing Enables `set -x` for complete command-line tracing. Shows every command before execution. **Use Case**: - Deep debugging of installation logic - Understanding script flow - Identifying where errors occur exactly **Behavior**: ``` +(/opt/wallabag/bin/console): /opt/wallabag/bin/console cache:warmup +(/opt/wallabag/bin/console): env APP_ENV=prod /opt/wallabag/bin/console cache:warmup +(/opt/wallabag/bin/console): [[ -d /opt/wallabag/app/cache ]] +(/opt/wallabag/bin/console): rm -rf /opt/wallabag/app/cache/* ``` **⚠️ Warning**: Exposes passwords and secrets in log output! Only use in isolated environments. **Log Output**: All trace output saved to logs (see `logs` mode) **Combined with**: `keep`, `pause`, `logs` --- ### 4. **pause** - Step-by-Step Execution Pauses after each major step (`msg_info`). Requires manual Enter press to continue. **Use Case**: - Inspect container state between steps - Understand what each step does - Identify which step causes problems **Behavior**: ``` ⏳ Setting up Container OS [PAUSE] Press Enter to continue... ⏳ Updating Container OS [PAUSE] Press Enter to continue... ⏳ Installing Dependencies [PAUSE] Press Enter to continue... ``` **Between pauses**: You can open another terminal and inspect the container ```bash # In another terminal while paused pct enter 107 root@container:~# df -h # Check disk usage root@container:~# ps aux # Check running processes ``` **Combined with**: `motd`, `keep`, `logs` --- ### 5. **breakpoint** - Interactive Shell on Error Opens interactive shell inside the container when an error occurs instead of cleanup prompt. **Use Case**: - Live debugging in the actual container - Manual command testing - Inspect container state at point of failure **Behavior**: ``` ✖ Installation failed in container 107 (exit code: 1) ✔ Container creation log: /tmp/create-lxc-107-abc12345.log ✔ Installation log: /tmp/install-lxc-107-abc12345.log 🐛 [DEV] Breakpoint mode - opening shell in container 107 Type 'exit' to return to host root@wallabag:~# # Now you can debug: root@wallabag:~# tail -f /root/.install-abc12345.log root@wallabag:~# mysql -u root -p$PASSWORD wallabag root@wallabag:~# apt-get install -y strace root@wallabag:~# exit Container 107 still running. Remove now? (y/N): n 🔧 Container 107 kept for debugging ``` **Combined with**: `keep`, `logs`, `trace` --- ### 6. **logs** - Persistent Logging Saves all logs to `/var/log/community-scripts/` with timestamps. Logs persist even on successful installation. **Use Case**: - Post-mortem analysis - Performance analysis - Automated testing with log collection - CI/CD integration **Behavior**: ``` Logs location: /var/log/community-scripts/ create-lxc-abc12345-20251117_143022.log (host-side creation) install-abc12345-20251117_143022.log (container-side installation) ``` **Access logs**: ```bash # View creation log tail -f /var/log/community-scripts/create-lxc-*.log # Search for errors grep ERROR /var/log/community-scripts/*.log # Analyze performance grep "msg_info\|msg_ok" /var/log/community-scripts/create-*.log ``` **With trace mode**: Creates detailed trace of all commands ```bash grep "^+" /var/log/community-scripts/install-*.log ``` **Combined with**: All other modes (recommended for CI/CD) --- ### 7. **dryrun** - Simulation Mode Shows all commands that would be executed without actually running them. **Use Case**: - Test script logic without making changes - Verify command syntax - Understand what will happen - Pre-flight checks **Behavior**: ``` [DRYRUN] apt-get update [DRYRUN] apt-get install -y curl [DRYRUN] mkdir -p /opt/wallabag [DRYRUN] cd /opt/wallabag [DRYRUN] git clone https://github.com/wallabag/wallabag.git . ``` **No actual changes made**: Container/system remains unchanged **Combined with**: `trace` (shows dryrun trace), `logs` (shows what would run) --- ## Mode Combinations ### Development Workflow ```bash # First test: See what would happen export dev_mode="dryrun,logs" bash -c "$(curl ...)" # Then test with tracing and pauses export dev_mode="pause,trace,logs" bash -c "$(curl ...)" # Finally full debug with early SSH access export dev_mode="motd,keep,breakpoint,logs" bash -c "$(curl ...)" ``` ### CI/CD Integration ```bash # Automated testing with full logging export dev_mode="logs" export var_verbose="yes" bash -c "$(curl ...)" # Capture logs for analysis tar czf installation-logs-$(date +%s).tar.gz /var/log/community-scripts/ ``` ### Production-like Testing ```bash # Keep containers for manual verification export dev_mode="keep,logs" for i in {1..5}; do bash -c "$(curl ...)" done # Inspect all created containers pct list pct enter 100 ``` ### Live Debugging ```bash # SSH in early, step through installation, debug on error export dev_mode="motd,pause,breakpoint,keep" bash -c "$(curl ...)" ``` --- ## Environment Variables Reference ### Dev Mode Variables - `dev_mode` (string): Comma-separated list of modes - Format: `"motd,keep,trace"` - Default: Empty (no dev modes) ### Output Control - `var_verbose="yes"`: Show all command output (disables silent mode) - Pairs well with: `trace`, `pause`, `logs` ### Examples with vars ```bash # Maximum verbosity and debugging export var_verbose="yes" export dev_mode="motd,trace,pause,logs" bash -c "$(curl ...)" # Silent debug (logs only) export dev_mode="keep,logs" bash -c "$(curl ...)" # Interactive debugging export var_verbose="yes" export dev_mode="motd,breakpoint" bash -c "$(curl ...)" ``` --- ## Troubleshooting with Dev Mode ### "Installation failed at step X" ```bash export dev_mode="pause,logs" # Step through until the failure point # Check container state between pauses pct enter 107 ``` ### "Password/credentials not working" ```bash export dev_mode="motd,keep,trace" # With trace mode, see exact password handling (be careful with logs!) # Use motd to SSH in and test manually ssh root@container-ip ``` ### "Permission denied errors" ```bash export dev_mode="breakpoint,keep" # Get shell at failure point # Check file permissions, user context, SELinux status ls -la /path/to/file whoami ``` ### "Networking issues" ```bash export dev_mode="motd" # SSH in with motd mode before main install ssh root@container-ip ping 8.8.8.8 nslookup example.com ``` ### "Need to manually complete installation" ```bash export dev_mode="motd,keep" # Container accessible via SSH while installation runs # After failure, SSH in and manually continue ssh root@container-ip # ... manual commands ... exit # Then use 'keep' mode to preserve container for inspection ``` --- ## Log Files Locations ### Default (without `logs` mode) - Host creation: `/tmp/create-lxc-.log` - Container install: Copied to `/tmp/install-lxc--.log` on failure ### With `logs` mode - Host creation: `/var/log/community-scripts/create-lxc--.log` - Container install: `/var/log/community-scripts/install--.log` ### View logs ```bash # Tail in real-time tail -f /var/log/community-scripts/*.log # Search for errors grep -r "exit code [1-9]" /var/log/community-scripts/ # Filter by session grep "ed563b19" /var/log/community-scripts/*.log ``` --- ## Best Practices ### ✅ DO - Use `logs` mode for CI/CD and automated testing - Use `motd` for early SSH access during long installations - Use `pause` when learning the installation flow - Use `trace` when debugging logic issues (watch for secrets!) - Combine modes for comprehensive debugging - Archive logs after successful tests ### ❌ DON'T - Use `trace` in production or with untrusted networks (exposes secrets) - Leave `keep` mode enabled for unattended scripts (containers accumulate) - Use `dryrun` and expect actual changes - Commit `dev_mode` exports to production deployment scripts - Use `breakpoint` in non-interactive environments (will hang) --- ## Examples ### Example 1: Debug a Failed Installation ```bash # Initial test to see the failure export dev_mode="keep,logs" bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/wallabag.sh)" # Container 107 kept, check logs tail /var/log/community-scripts/install-*.log # SSH in to debug pct enter 107 root@wallabag:~# cat /root/.install-*.log | tail -100 root@wallabag:~# apt-get update # Retry the failing command root@wallabag:~# exit # Re-run with manual step-through export dev_mode="motd,pause,keep" bash -c "$(curl ...)" ``` ### Example 2: Verify Installation Steps ```bash export dev_mode="pause,logs" export var_verbose="yes" bash -c "$(curl ...)" # Press Enter through each step # Monitor container in another terminal # pct enter 107 # Review logs in real-time ``` ### Example 3: CI/CD Pipeline Integration ```bash #!/bin/bash export dev_mode="logs" export var_verbose="no" for app in wallabag nextcloud wordpress; do echo "Testing $app installation..." APP="$app" bash -c "$(curl ...)" || { echo "FAILED: $app" tar czf logs-$app.tar.gz /var/log/community-scripts/ exit 1 } echo "SUCCESS: $app" done echo "All installations successful" tar czf all-logs.tar.gz /var/log/community-scripts/ ``` --- ## Advanced Usage ### Custom Log Analysis ```bash # Extract all errors grep "ERROR\|exit code [1-9]" /var/log/community-scripts/*.log # Performance timeline grep "^$(date +%Y-%m-%d)" /var/log/community-scripts/*.log | grep "msg_" # Memory usage during install grep "free\|available" /var/log/community-scripts/*.log ``` ### Integration with External Tools ```bash # Send logs to Elasticsearch curl -X POST "localhost:9200/installation-logs/_doc" \ -H 'Content-Type: application/json' \ -d @/var/log/community-scripts/install-*.log # Archive for compliance tar czf installation-records-$(date +%Y%m).tar.gz \ /var/log/community-scripts/ gpg --encrypt installation-records-*.tar.gz ``` --- ## Support & Issues When reporting installation issues, always include: ```bash # Collect all relevant information export dev_mode="logs" # Run the failing installation # Then provide: tar czf debug-logs.tar.gz /var/log/community-scripts/ ``` Include the `debug-logs.tar.gz` when reporting issues for better diagnostics. ================================================ FILE: docs/EXIT_CODES.md ================================================ # Exit Code Reference Comprehensive documentation of all exit codes used in ProxmoxVE scripts. ## Table of Contents - [Generic/Shell Errors (1-255)](#genericshell-errors) - [Package Manager Errors (100-101, 255)](#package-manager-errors) - [Node.js/npm Errors (243-254)](#nodejsnpm-errors) - [Python/pip Errors (210-212)](#pythonpip-errors) - [Database Errors (231-254)](#database-errors) - [Proxmox Custom Codes (200-231)](#proxmox-custom-codes) --- ## Generic/Shell Errors Standard Unix/Linux exit codes used across all scripts. | Code | Description | Common Causes | Solutions | | ------- | --------------------------------------- | ----------------------------------------- | ---------------------------------------------- | | **1** | General error / Operation not permitted | Permission denied, general failure | Check user permissions, run as root if needed | | **2** | Misuse of shell builtins | Syntax error in script | Review script syntax, check bash version | | **126** | Command cannot execute | Permission problem, not executable | `chmod +x script.sh` or check file permissions | | **127** | Command not found | Missing binary, wrong PATH | Install required package, check PATH variable | | **128** | Invalid argument to exit | Invalid exit code passed | Use exit codes 0-255 only | | **130** | Terminated by Ctrl+C (SIGINT) | User interrupted script | Expected behavior, no action needed | | **137** | Killed (SIGKILL) | Out of memory, forced termination | Check memory usage, increase RAM allocation | | **139** | Segmentation fault | Memory access violation, corrupted binary | Reinstall package, check system stability | | **143** | Terminated (SIGTERM) | Graceful shutdown signal | Expected during container stops | --- ## Package Manager Errors APT, DPKG, and package installation errors. | Code | Description | Common Causes | Solutions | | ------- | -------------------------- | --------------------------------------- | ------------------------------------------------- | | **100** | APT: Package manager error | Broken packages, dependency conflicts | `apt --fix-broken install`, `dpkg --configure -a` | | **101** | APT: Configuration error | Malformed sources.list, bad repo config | Check `/etc/apt/sources.list`, run `apt update` | | **255** | DPKG: Fatal internal error | Corrupted package database | `dpkg --configure -a`, restore from backup | --- ## Node.js/npm Errors Node.js runtime and package manager errors. | Code | Description | Common Causes | Solutions | | ------- | ------------------------------------------ | ------------------------------ | ---------------------------------------------- | | **243** | Node.js: Out of memory | JavaScript heap exhausted | Increase `--max-old-space-size`, optimize code | | **245** | Node.js: Invalid command-line option | Wrong Node.js flags | Check Node.js version, verify CLI options | | **246** | Node.js: Internal JavaScript Parse Error | Syntax error in JS code | Review JavaScript syntax, check dependencies | | **247** | Node.js: Fatal internal error | Node.js runtime crash | Update Node.js, check for known bugs | | **248** | Node.js: Invalid C++ addon / N-API failure | Native module incompatibility | Rebuild native modules, update packages | | **249** | Node.js: Inspector error | Debug/inspect protocol failure | Disable inspector, check port conflicts | | **254** | npm/pnpm/yarn: Unknown fatal error | Package manager crash | Clear cache, reinstall package manager | --- ## Python/pip Errors Python runtime and package installation errors. | Code | Description | Common Causes | Solutions | | ------- | ------------------------------------ | --------------------------------------- | -------------------------------------------------------- | | **210** | Python: Virtualenv missing or broken | venv not created, corrupted environment | `python3 -m venv venv`, recreate virtualenv | | **211** | Python: Dependency resolution failed | Conflicting package versions | Use `pip install --upgrade`, check requirements.txt | | **212** | Python: Installation aborted | EXTERNALLY-MANAGED, permission denied | Use `--break-system-packages` or venv, check permissions | --- ## Database Errors ### PostgreSQL (231-234) | Code | Description | Common Causes | Solutions | | ------- | ----------------------- | ---------------------------------- | ----------------------------------------------------- | | **231** | Connection failed | Server not running, wrong socket | `systemctl start postgresql`, check connection string | | **232** | Authentication failed | Wrong credentials | Verify username/password, check `pg_hba.conf` | | **233** | Database does not exist | Database not created | `CREATE DATABASE`, restore from backup | | **234** | Fatal error in query | Syntax error, constraint violation | Review SQL syntax, check constraints | ### MySQL/MariaDB (241-244) | Code | Description | Common Causes | Solutions | | ------- | ----------------------- | ---------------------------------- | ---------------------------------------------------- | | **241** | Connection failed | Server not running, wrong socket | `systemctl start mysql`, check connection parameters | | **242** | Authentication failed | Wrong credentials | Verify username/password, grant privileges | | **243** | Database does not exist | Database not created | `CREATE DATABASE`, restore from backup | | **244** | Fatal error in query | Syntax error, constraint violation | Review SQL syntax, check constraints | ### MongoDB (251-254) | Code | Description | Common Causes | Solutions | | ------- | --------------------- | -------------------- | ------------------------------------------ | | **251** | Connection failed | Server not running | `systemctl start mongod`, check port 27017 | | **252** | Authentication failed | Wrong credentials | Verify username/password, create user | | **253** | Database not found | Database not created | Database auto-created on first write | | **254** | Fatal query error | Invalid query syntax | Review MongoDB query syntax | --- ## Proxmox Custom Codes Custom exit codes specific to ProxmoxVE scripts. ### Container Creation Errors (200-209) | Code | Description | Common Causes | Solutions | | ------- | ---------------------------------------------- | ------------------------------------------------------- | ------------------------------------------------------- | | **200** | Failed to create lock file | Permission denied, disk full | Check `/tmp` permissions, free disk space | | **203** | Missing CTID variable | Script configuration error | Set CTID in script or via prompt | | **204** | Missing PCT_OSTYPE variable | Template selection failed | Verify template availability | | **205** | Invalid CTID (<100) | CTID below minimum value | Use CTID ≥ 100 (1-99 reserved for Proxmox) | | **206** | CTID already in use | Container/VM with same ID exists | Check `pct list` and `/etc/pve/lxc/`, use different ID | | **207** | Password contains unescaped special characters | Special chars like `-`, `/`, `\`, `*` at start/end | Avoid leading special chars, use alphanumeric passwords | | **208** | Invalid configuration | DNS format (`.home` vs `home`), MAC format (`-` vs `:`) | Remove leading dots from DNS, use `:` in MAC addresses | | **209** | Container creation failed | Multiple possible causes | Check logs in `/tmp/pct_create_*.log`, verify template | ### Cluster & Storage Errors (210, 214, 217) | Code | Description | Common Causes | Solutions | | ------- | --------------------------------- | ---------------------------------- | ----------------------------------------------------------- | | **210** | Cluster not quorate | Cluster nodes down, network issues | Check cluster status: `pvecm status`, fix node connectivity | | **211** | Timeout waiting for template lock | Concurrent download in progress | Wait for other download to complete (60s timeout) | | **214** | Not enough storage space | Disk full, quota exceeded | Free disk space, increase storage allocation | | **217** | Storage does not support rootdir | Wrong storage type selected | Use storage supporting containers (dir, zfspool, lvm-thin) | ### Container Verification Errors (215-216) | Code | Description | Common Causes | Solutions | | ------- | -------------------------------- | -------------------------------- | --------------------------------------------------------- | | **215** | Container created but not listed | Ghost state, incomplete creation | Check `/etc/pve/lxc/CTID.conf`, remove manually if needed | | **216** | RootFS entry missing in config | Incomplete container creation | Delete container, retry creation | ### Template Errors (218, 220-223, 225) | Code | Description | Common Causes | Solutions | | ------- | ----------------------------------------- | ------------------------------------------------ | ----------------------------------------------------------- | | **218** | Template file corrupted or incomplete | Download interrupted, file <1MB, invalid archive | Delete template, run `pveam update && pveam download` | | **220** | Unable to resolve template path | Template storage not accessible | Check storage availability, verify permissions | | **221** | Template file exists but not readable | Permission denied | `chmod 644 template.tar.zst`, check storage permissions | | **222** | Template download failed after 3 attempts | Network issues, storage problems | Check internet connectivity, verify storage space | | **223** | Template not available after download | Storage sync issue, I/O delay | Wait a few seconds, verify storage is mounted | | **225** | No template available for OS/Version | Unsupported OS version, catalog outdated | Run `pveam update`, check `pveam available -section system` | ### LXC Stack Errors (231) | Code | Description | Common Causes | Solutions | | ------- | ------------------------------ | ------------------------------------------- | -------------------------------------------- | | **231** | LXC stack upgrade/retry failed | Outdated `pve-container`, Debian 13.1 issue | See [Debian 13.1 Fix Guide](#debian-131-fix) | --- ## Special Case: Debian 13.1 "unsupported version" Error ### Problem ``` TASK ERROR: unable to create CT 129 - unsupported debian version '13.1' ``` ### Root Cause Outdated `pve-container` package doesn't recognize Debian 13 (Trixie). ### Solutions #### Option 1: Full System Upgrade (Recommended) ```bash apt update apt full-upgrade -y reboot ``` Verify fix: ```bash dpkg -l pve-container # PVE 8: Should show 5.3.3+ # PVE 9: Should show 6.0.13+ ``` #### Option 2: Update Only pve-container ```bash apt update apt install --only-upgrade pve-container -y ``` **Warning:** If Proxmox fails to boot after this, your system was inconsistent. Perform Option 1 instead. #### Option 3: Verify Repository Configuration Many users disable Enterprise repos but forget to add no-subscription repos. **For PVE 9 (Trixie):** ```bash cat /etc/apt/sources.list.d/pve-no-subscription.list ``` Should contain: ``` deb http://download.proxmox.com/debian/pve trixie pve-no-subscription deb http://download.proxmox.com/debian/ceph-squid trixie no-subscription ``` **For PVE 8 (Bookworm):** ``` deb http://download.proxmox.com/debian/pve bookworm pve-no-subscription deb http://download.proxmox.com/debian/ceph-quincy bookworm no-subscription ``` Then: ```bash apt update apt full-upgrade -y ``` ### Reference Official discussion: [GitHub #8126](https://github.com/community-scripts/ProxmoxVE/discussions/8126) --- ## Troubleshooting Tips ### Finding Error Details 1. **Check logs:** ```bash tail -n 50 /tmp/pct_create_*.log ``` 2. **Enable verbose mode:** ```bash bash -x script.sh # Shows every command executed ``` 3. **Check container status:** ```bash pct list pct status CTID ``` 4. **Verify storage:** ```bash pvesm status df -h ``` ### Common Patterns - **Exit 0 with error message:** Configuration validation failed (check DNS, MAC, password format) - **Exit 206 but container not visible:** Ghost container state - check `/etc/pve/lxc/` manually - **Exit 209 generic error:** Check `/tmp/pct_create_*.log` for specific `pct create` failure reason - **Exit 218 or 222:** Template issues - delete and re-download template --- ## Quick Reference Chart | Exit Code Range | Category | Typical Issue | | --------------- | ------------------ | ------------------------------------------- | | 1-2, 126-143 | Shell/System | Permissions, signals, missing commands | | 100-101, 255 | Package Manager | APT/DPKG errors, broken packages | | 200-209 | Container Creation | CTID, password, configuration | | 210-217 | Storage/Cluster | Disk space, quorum, storage type | | 218-225 | Templates | Download, corruption, availability | | 231-254 | Databases/Runtime | PostgreSQL, MySQL, MongoDB, Node.js, Python | --- ## Contributing Found an undocumented exit code or have a solution to share? Please: 1. Open an issue on [GitHub](https://github.com/community-scripts/ProxmoxVE/issues) 2. Include: - Exit code number - Error message - Steps to reproduce - Solution that worked for you --- _Last updated: November 2025_ _ProxmoxVE Version: 2.x_ ================================================ FILE: docs/README.md ================================================ # 📚 ProxmoxVE Documentation Complete guide to all ProxmoxVE documentation - quickly find what you need. --- ## 🎯 **Quick Navigation by Goal** ### 👤 **I want to...** **Contribute a new application** → Start with: [contribution/README.md](contribution/README.md) → Then: [ct/DETAILED_GUIDE.md](ct/DETAILED_GUIDE.md) + [install/DETAILED_GUIDE.md](install/DETAILED_GUIDE.md) **Understand the architecture** → Read: [TECHNICAL_REFERENCE.md](TECHNICAL_REFERENCE.md) → Then: [misc/README.md](misc/README.md) **Debug a failed installation** → Check: [EXIT_CODES.md](EXIT_CODES.md) → Then: [DEV_MODE.md](DEV_MODE.md) → See also: [misc/error_handler.func/](misc/error_handler.func/) **Configure system defaults** → Read: [guides/DEFAULTS_SYSTEM_GUIDE.md](guides/DEFAULTS_SYSTEM_GUIDE.md) **Deploy containers automatically** → Read: [guides/UNATTENDED_DEPLOYMENTS.md](guides/UNATTENDED_DEPLOYMENTS.md) **Develop a function library** → Study: [misc/](misc/) documentation --- ## 👤 **Quick Start by Role** ### **I'm a...** **New Contributor** → Start: [contribution/README.md](contribution/README.md) → Then: Choose your path below **Container Creator** → Read: [ct/README.md](ct/README.md) → Deep Dive: [ct/DETAILED_GUIDE.md](ct/DETAILED_GUIDE.md) → Reference: [misc/build.func/](misc/build.func/) **Installation Script Developer** → Read: [install/README.md](install/README.md) → Deep Dive: [install/DETAILED_GUIDE.md](install/DETAILED_GUIDE.md) → Reference: [misc/tools.func/](misc/tools.func/) **VM Provisioner** → Read: [vm/README.md](vm/README.md) → Reference: [misc/cloud-init.func/](misc/cloud-init.func/) **Tools Developer** → Read: [tools/README.md](tools/README.md) → Reference: [misc/build.func/](misc/build.func/) **API Integrator** → Read: [api/README.md](api/README.md) → Reference: [misc/api.func/](misc/api.func/) **System Operator** → Start: [EXIT_CODES.md](EXIT_CODES.md) → Then: [guides/DEFAULTS_SYSTEM_GUIDE.md](guides/DEFAULTS_SYSTEM_GUIDE.md) → Automate: [guides/UNATTENDED_DEPLOYMENTS.md](guides/UNATTENDED_DEPLOYMENTS.md) → Debug: [DEV_MODE.md](DEV_MODE.md) **Architect** → Read: [TECHNICAL_REFERENCE.md](TECHNICAL_REFERENCE.md) → Deep Dive: [misc/README.md](misc/README.md) --- ## 📂 **Documentation Structure** ### Project-Mirrored Directories Each major project directory has documentation: ``` ProxmoxVE/ ├─ ct/ ↔ docs/ct/ (README.md + DETAILED_GUIDE.md) ├─ install/ ↔ docs/install/ (README.md + DETAILED_GUIDE.md) ├─ vm/ ↔ docs/vm/ (README.md) ├─ tools/ ↔ docs/tools/ (README.md) ├─ api/ ↔ docs/api/ (README.md) ├─ misc/ ↔ docs/misc/ (9 function libraries) └─ [system-wide] ↔ docs/guides/ (configuration & deployment guides) ``` ### Core Documentation | Document | Purpose | Audience | |----------|---------|----------| | [contribution/README.md](contribution/README.md) | How to contribute | Contributors | | [ct/DETAILED_GUIDE.md](ct/DETAILED_GUIDE.md) | Create ct scripts | Container developers | | [install/DETAILED_GUIDE.md](install/DETAILED_GUIDE.md) | Create install scripts | Installation developers | | [TECHNICAL_REFERENCE.md](TECHNICAL_REFERENCE.md) | Architecture deep-dive | Architects, advanced users | | [guides/DEFAULTS_SYSTEM_GUIDE.md](guides/DEFAULTS_SYSTEM_GUIDE.md) | Configuration system | Operators, power users | | [guides/CONFIGURATION_REFERENCE.md](guides/CONFIGURATION_REFERENCE.md) | Configuration options reference | Advanced users | | [guides/UNATTENDED_DEPLOYMENTS.md](guides/UNATTENDED_DEPLOYMENTS.md) | Automated deployments | DevOps, automation | | [EXIT_CODES.md](EXIT_CODES.md) | Exit code reference | Troubleshooters | | [DEV_MODE.md](DEV_MODE.md) | Debugging tools | Developers | --- ## 📂 **Directory Guide** ### [ct/](ct/) - Container Scripts Documentation for `/ct` - Container creation scripts that run on the Proxmox host. **Includes**: - Overview of container creation process - Deep dive: [DETAILED_GUIDE.md](ct/DETAILED_GUIDE.md) - Complete reference with examples - Reference to [misc/build.func/](misc/build.func/) - Quick start for creating new containers ### [install/](install/) - Installation Scripts Documentation for `/install` - Scripts that run inside containers to install applications. **Includes**: - Overview of 10-phase installation pattern - Deep dive: [DETAILED_GUIDE.md](install/DETAILED_GUIDE.md) - Complete reference with examples - Reference to [misc/tools.func/](misc/tools.func/) - Alpine vs Debian differences ### [vm/](vm/) - Virtual Machine Scripts Documentation for `/vm` - VM creation scripts using cloud-init provisioning. **Includes**: - Overview of VM provisioning - Link to [misc/cloud-init.func/](misc/cloud-init.func/) - VM vs Container comparison - Cloud-init examples ### [tools/](tools/) - Tools & Utilities Documentation for `/tools` - Management tools and add-ons. **Includes**: - Overview of tools structure - Integration points - Contributing new tools - Common operations ### [api/](api/) - API Integration Documentation for `/api` - Telemetry and API backend. **Includes**: - API overview - Integration methods - API endpoints - Privacy information ### [misc/](misc/) - Function Libraries Documentation for `/misc` - 9 core function libraries with complete references. **Contains**: - **build.func/** - Container orchestration (7 files) - **core.func/** - Utilities and messaging (5 files) - **error_handler.func/** - Error handling (5 files) - **api.func/** - API integration (5 files) - **install.func/** - Container setup (5 files) - **tools.func/** - Package installation (6 files) - **alpine-install.func/** - Alpine setup (5 files) - **alpine-tools.func/** - Alpine tools (5 files) - **cloud-init.func/** - VM provisioning (5 files) --- ## 🎓 **Learning Paths** ### Path 1: First-Time Contributor (2-3 hours) 1. [contribution/README.md](contribution/README.md) - Quick Start 2. Pick your area: - Containers → [ct/README.md](ct/README.md) + [ct/DETAILED_GUIDE.md](ct/DETAILED_GUIDE.md) - Installation → [install/README.md](install/README.md) + [install/DETAILED_GUIDE.md](install/DETAILED_GUIDE.md) - VMs → [vm/README.md](vm/README.md) 3. Study existing similar script 4. Create your contribution 5. Submit PR ### Path 2: Intermediate Developer (4-6 hours) 1. [TECHNICAL_REFERENCE.md](TECHNICAL_REFERENCE.md) 2. Dive into function libraries: - [misc/build.func/README.md](misc/build.func/README.md) - [misc/tools.func/README.md](misc/tools.func/README.md) - [misc/install.func/README.md](misc/install.func/README.md) 3. Study advanced examples 4. Create complex applications ### Path 3: Advanced Architect (8+ hours) 1. All of Intermediate Path 2. Study all 9 function libraries in depth 3. [guides/DEFAULTS_SYSTEM_GUIDE.md](guides/DEFAULTS_SYSTEM_GUIDE.md) - Configuration system 4. [DEV_MODE.md](DEV_MODE.md) - Debugging and development 5. Design new features or function libraries ### Path 4: Troubleshooter (30 minutes - 1 hour) 1. [EXIT_CODES.md](EXIT_CODES.md) - Find error code 2. [DEV_MODE.md](DEV_MODE.md) - Run with debugging 3. Check relevant function library docs 4. Review logs and fix --- ## 📊 **By the Numbers** | Metric | Count | |--------|:---:| | **Documentation Files** | 63 | | **Total Lines** | 15,000+ | | **Function Libraries** | 9 | | **Functions Documented** | 150+ | | **Code Examples** | 50+ | | **Flowcharts** | 15+ | | **Do/Don't Sections** | 20+ | | **Real-World Examples** | 30+ | --- ## 🔍 **Find It Fast** ### By Feature - **How do I create a container?** → [ct/DETAILED_GUIDE.md](ct/DETAILED_GUIDE.md) - **How do I create an install script?** → [install/DETAILED_GUIDE.md](install/DETAILED_GUIDE.md) - **How do I create a VM?** → [vm/README.md](vm/README.md) - **How do I install Node.js?** → [misc/tools.func/](misc/tools.func/) - **How do I debug?** → [DEV_MODE.md](DEV_MODE.md) ### By Error - **Exit code 206?** → [EXIT_CODES.md](EXIT_CODES.md) - **Network failed?** → [misc/install.func/](misc/install.func/) - **Package error?** → [misc/tools.func/](misc/tools.func/) ### By Role - **Contributor** → [contribution/README.md](contribution/README.md) - **Operator** → [guides/DEFAULTS_SYSTEM_GUIDE.md](guides/DEFAULTS_SYSTEM_GUIDE.md) - **Automation** → [guides/UNATTENDED_DEPLOYMENTS.md](guides/UNATTENDED_DEPLOYMENTS.md) - **Developer** → [TECHNICAL_REFERENCE.md](TECHNICAL_REFERENCE.md) - **Architect** → [misc/README.md](misc/README.md) --- ## ✅ **Documentation Features** - ✅ **Project-mirrored structure** - Organized like the actual project - ✅ **Complete function references** - Every function documented - ✅ **Real-world examples** - Copy-paste ready code - ✅ **Visual flowcharts** - ASCII diagrams of workflows - ✅ **Integration guides** - How components connect - ✅ **Troubleshooting** - Common issues and solutions - ✅ **Best practices** - DO/DON'T sections throughout - ✅ **Learning paths** - Structured curriculum by role - ✅ **Quick references** - Fast lookup by error code - ✅ **Comprehensive navigation** - This page --- ## 🚀 **Start Here** **New to ProxmoxVE?** → [contribution/README.md](contribution/README.md) **Looking for something specific?** → Choose your role above or browse by directory **Need to debug?** → [EXIT_CODES.md](EXIT_CODES.md) **Want to understand architecture?** → [TECHNICAL_REFERENCE.md](TECHNICAL_REFERENCE.md) --- ## 🤝 **Contributing Documentation** Found an error? Want to improve docs? 1. See: [contribution/README.md](contribution/README.md) for full contribution guide 2. Open issue: [GitHub Issues](https://github.com/community-scripts/ProxmoxVE/issues) 3. Or submit PR with improvements --- ## 📝 **Status** - **Last Updated**: December 2025 - **Version**: 2.3 (Consolidated & Reorganized) - **Completeness**: ✅ 100% - All components documented - **Quality**: ✅ Production-ready - **Structure**: ✅ Clean and organized --- **Welcome to ProxmoxVE! Start with [CONTRIBUTION_GUIDE.md](CONTRIBUTION_GUIDE.md) or choose your role above.** 🚀 ================================================ FILE: docs/TECHNICAL_REFERENCE.md ================================================ # Technical Reference: Configuration System Architecture > **For Developers and Advanced Users** > > _Deep dive into how the defaults and configuration system works_ --- ## Table of Contents 1. [System Architecture](#system-architecture) 2. [File Format Specifications](#file-format-specifications) 3. [Function Reference](#function-reference) 4. [Variable Precedence](#variable-precedence) 5. [Data Flow Diagrams](#data-flow-diagrams) 6. [Security Model](#security-model) 7. [Implementation Details](#implementation-details) --- ## System Architecture ### Component Overview ``` ┌─────────────────────────────────────────────────────────────┐ │ Installation Script │ │ (pihole-install.sh, docker-install.sh, etc.) │ └────────────────────┬────────────────────────────────────────┘ │ v ┌─────────────────────────────────────────────────────────────┐ │ build.func Library │ │ ┌──────────────────────────────────────────────────────┐ │ │ │ variables() │ │ │ │ - Initialize NSAPP, var_install, etc. │ │ │ └──────────────────────────────────────────────────────┘ │ │ ┌──────────────────────────────────────────────────────┐ │ │ │ install_script() │ │ │ │ - Display mode menu │ │ │ │ - Route to appropriate workflow │ │ │ └──────────────────────────────────────────────────────┘ │ │ ┌──────────────────────────────────────────────────────┐ │ │ │ base_settings() │ │ │ │ - Apply built-in defaults │ │ │ │ - Read environment variables (var_*) │ │ │ └──────────────────────────────────────────────────────┘ │ │ ┌──────────────────────────────────────────────────────┐ │ │ │ load_vars_file() │ │ │ │ - Safe file parsing (NO source/eval) │ │ │ │ - Whitelist validation │ │ │ │ - Value sanitization │ │ │ └──────────────────────────────────────────────────────┘ │ │ ┌──────────────────────────────────────────────────────┐ │ │ │ default_var_settings() │ │ │ │ - Load user defaults │ │ │ │ - Display summary │ │ │ └──────────────────────────────────────────────────────┘ │ │ ┌──────────────────────────────────────────────────────┐ │ │ │ maybe_offer_save_app_defaults() │ │ │ │ - Offer to save current settings │ │ │ │ - Handle updates vs. new saves │ │ │ └──────────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────┘ │ v ┌─────────────────────────────────────────────────────────────┐ │ Configuration Files (on Disk) │ │ ┌──────────────────────────────────────────────────────┐ │ │ │ /usr/local/community-scripts/default.vars │ │ │ │ (User global defaults) │ │ │ └──────────────────────────────────────────────────────┘ │ │ ┌──────────────────────────────────────────────────────┐ │ │ │ /usr/local/community-scripts/defaults/*.vars │ │ │ │ (App-specific defaults) │ │ │ └──────────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────┘ ``` --- ## File Format Specifications ### User Defaults: `default.vars` **Location**: `/usr/local/community-scripts/default.vars` **MIME Type**: `text/plain` **Encoding**: UTF-8 (no BOM) **Format Specification**: ``` # File Format: Simple key=value pairs # Purpose: Store global user defaults # Security: Sanitized values, whitelist validation # Comments and blank lines are ignored # Line format: var_name=value # No spaces around the equals sign # String values do not need quoting (but may be quoted) [CONTENT] var_cpu=4 var_ram=2048 var_disk=20 var_hostname=mydefault var_brg=vmbr0 var_gateway=192.168.1.1 ``` **Formal Grammar**: ``` FILE := (BLANK_LINE | COMMENT_LINE | VAR_LINE)* BLANK_LINE := \n COMMENT_LINE := '#' [^\n]* \n VAR_LINE := VAR_NAME '=' VAR_VALUE \n VAR_NAME := 'var_' [a-z_]+ VAR_VALUE := [^\n]* # Any printable characters except newline ``` **Constraints**: | Constraint | Value | | ----------------- | ------------------------ | | Max file size | 64 KB | | Max line length | 1024 bytes | | Max variables | 100 | | Allowed var names | `var_[a-z_]+` | | Value validation | Whitelist + Sanitization | **Example Valid File**: ```bash # Global User Defaults # Created: 2024-11-28 # Resource defaults var_cpu=4 var_ram=2048 var_disk=20 # Network defaults var_brg=vmbr0 var_gateway=192.168.1.1 var_mtu=1500 var_vlan=100 # System defaults var_timezone=Europe/Berlin var_hostname=default-container # Storage var_container_storage=local var_template_storage=local # Security var_ssh=yes var_protection=0 var_unprivileged=1 ``` ### App Defaults: `.vars` **Location**: `/usr/local/community-scripts/defaults/.vars` **Format**: Identical to `default.vars` **Naming Convention**: `.vars` - `nsapp` = lowercase app name with spaces removed - Examples: - `pihole` → `pihole.vars` - `opnsense` → `opnsense.vars` - `docker compose` → `dockercompose.vars` **Example App Defaults**: ```bash # App-specific defaults for PiHole (pihole) # Generated on 2024-11-28T15:32:00Z # These override user defaults when installing pihole var_unprivileged=1 var_cpu=2 var_ram=1024 var_disk=10 var_brg=vmbr0 var_net=veth var_gateway=192.168.1.1 var_hostname=pihole var_timezone=Europe/Berlin var_container_storage=local var_template_storage=local var_tags=dns,pihole ``` --- ## Function Reference ### `load_vars_file()` **Purpose**: Safely load variables from .vars files without using `source` or `eval` **Signature**: ```bash load_vars_file(filepath) ``` **Parameters**: | Param | Type | Required | Example | | -------- | ------ | -------- | ------------------------------------------- | | filepath | String | Yes | `/usr/local/community-scripts/default.vars` | **Returns**: - `0` on success - `1` on error (file missing, parse error, etc.) **Environment Side Effects**: - Sets all parsed `var_*` variables as shell variables - Does NOT unset variables if file missing (safe) - Does NOT affect other variables **Implementation Pattern**: ```bash load_vars_file() { local file="$1" # File must exist [ -f "$file" ] || return 0 # Parse line by line (not with source/eval) local line key val while IFS='=' read -r key val || [ -n "$key" ]; do # Skip comments and empty lines [[ "$key" =~ ^[[:space:]]*# ]] && continue [[ -z "$key" ]] && continue # Validate key is in whitelist _is_whitelisted_key "$key" || continue # Sanitize and export value val="$(_sanitize_value "$val")" [ $? -eq 0 ] && export "$key=$val" done < "$file" return 0 } ``` **Usage Examples**: ```bash # Load user defaults load_vars_file "/usr/local/community-scripts/default.vars" # Load app-specific defaults load_vars_file "$(get_app_defaults_path)" # Check if successful if load_vars_file "$vars_path"; then echo "Settings loaded successfully" else echo "Failed to load settings" fi # Values are now available as variables echo "Using $var_cpu cores" echo "Allocating ${var_ram} MB RAM" ``` --- ### `get_app_defaults_path()` **Purpose**: Get the full path for app-specific defaults file **Signature**: ```bash get_app_defaults_path() ``` **Parameters**: None **Returns**: - String: Full path to app defaults file **Implementation**: ```bash get_app_defaults_path() { local n="${NSAPP:-${APP,,}}" echo "/usr/local/community-scripts/defaults/${n}.vars" } ``` **Usage Examples**: ```bash # Get app defaults path app_defaults="$(get_app_defaults_path)" echo "App defaults at: $app_defaults" # Check if app defaults exist if [ -f "$(get_app_defaults_path)" ]; then echo "App defaults available" fi # Load app defaults load_vars_file "$(get_app_defaults_path)" ``` --- ### `default_var_settings()` **Purpose**: Load and display user global defaults **Signature**: ```bash default_var_settings() ``` **Parameters**: None **Returns**: - `0` on success - `1` on error **Workflow**: ``` 1. Find default.vars location (usually /usr/local/community-scripts/default.vars) 2. Create if missing 3. Load variables from file 4. Map var_verbose → VERBOSE variable 5. Call base_settings (apply to container config) 6. Call echo_default (display summary) ``` **Implementation Pattern**: ```bash default_var_settings() { local VAR_WHITELIST=( var_apt_cacher var_apt_cacher_ip var_brg var_cpu var_disk var_fuse var_gpu var_gateway var_hostname var_ipv6_method var_mac var_mtu var_net var_ns var_pw var_ram var_tags var_tun var_unprivileged var_verbose var_vlan var_ssh var_ssh_authorized_key var_container_storage var_template_storage ) # Ensure file exists _ensure_default_vars # Find and load local dv="$(_find_default_vars)" load_vars_file "$dv" # Map verbose flag if [[ -n "${var_verbose:-}" ]]; then case "${var_verbose,,}" in 1 | yes | true | on) VERBOSE="yes" ;; *) VERBOSE="${var_verbose}" ;; esac fi # Apply and display base_settings "$VERBOSE" echo_default } ``` --- ### `maybe_offer_save_app_defaults()` **Purpose**: Offer to save current settings as app-specific defaults **Signature**: ```bash maybe_offer_save_app_defaults() ``` **Parameters**: None **Returns**: None (side effects only) **Behavior**: 1. After advanced installation completes 2. Offers user: "Save as App Defaults for ?" 3. If yes: - Saves to `/usr/local/community-scripts/defaults/.vars` - Only whitelisted variables included - Previous defaults backed up (if exists) 4. If no: - No action taken **Flow**: ```bash maybe_offer_save_app_defaults() { local app_vars_path="$(get_app_defaults_path)" # Build current settings from memory local new_tmp="$(_build_current_app_vars_tmp)" # Check if already exists if [ -f "$app_vars_path" ]; then # Show diff and ask: Update? Keep? View Diff? _show_app_defaults_diff_menu "$new_tmp" "$app_vars_path" else # New defaults - just save if whiptail --yesno "Save as App Defaults for $APP?" 10 60; then mv "$new_tmp" "$app_vars_path" chmod 644 "$app_vars_path" fi fi } ``` --- ### `_sanitize_value()` **Purpose**: Remove dangerous characters/patterns from configuration values **Signature**: ```bash _sanitize_value(value) ``` **Parameters**: | Param | Type | Required | | ----- | ------ | -------- | | value | String | Yes | **Returns**: - `0` (success) + sanitized value on stdout - `1` (failure) + nothing if dangerous **Dangerous Patterns**: | Pattern | Threat | Example | | --------- | -------------------- | -------------------- | | `$(...)` | Command substitution | `$(rm -rf /)` | | `` ` ` `` | Command substitution | `` `whoami` `` | | `;` | Command separator | `value; rm -rf /` | | `&` | Background execution | `value & malicious` | | `<(` | Process substitution | `<(cat /etc/passwd)` | **Implementation**: ```bash _sanitize_value() { case "$1" in *'$('* | *'`'* | *';'* | *'&'* | *'<('*) echo "" return 1 # Reject dangerous value ;; esac echo "$1" return 0 } ``` **Usage Examples**: ```bash # Safe value _sanitize_value "192.168.1.1" # Returns: 192.168.1.1 (status: 0) # Dangerous value _sanitize_value "$(whoami)" # Returns: (empty) (status: 1) # Usage in code if val="$(_sanitize_value "$user_input")"; then export var_hostname="$val" else msg_error "Invalid value: contains dangerous characters" fi ``` --- ### `_is_whitelisted_key()` **Purpose**: Check if variable name is in allowed whitelist **Signature**: ```bash _is_whitelisted_key(key) ``` **Parameters**: | Param | Type | Required | Example | | ----- | ------ | -------- | --------- | | key | String | Yes | `var_cpu` | **Returns**: - `0` if key is whitelisted - `1` if key is NOT whitelisted **Implementation**: ```bash _is_whitelisted_key() { local k="$1" local w for w in "${VAR_WHITELIST[@]}"; do [ "$k" = "$w" ] && return 0 done return 1 } ``` **Usage Examples**: ```bash # Check if variable can be saved if _is_whitelisted_key "var_cpu"; then echo "var_cpu can be saved" fi # Reject unknown variables if ! _is_whitelisted_key "var_custom"; then msg_error "var_custom is not supported" fi ``` --- ## Variable Precedence ### Loading Order When a container is being created, variables are resolved in this order: ``` Step 1: Read ENVIRONMENT VARIABLES ├─ Check if var_cpu is already set in shell environment ├─ Check if var_ram is already set └─ ...all var_* variables Step 2: Load APP-SPECIFIC DEFAULTS ├─ Check if /usr/local/community-scripts/defaults/pihole.vars exists ├─ Load all var_* from that file └─ These override built-ins but NOT environment variables Step 3: Load USER GLOBAL DEFAULTS ├─ Check if /usr/local/community-scripts/default.vars exists ├─ Load all var_* from that file └─ These override built-ins but NOT app-specific Step 4: Use BUILT-IN DEFAULTS └─ Hardcoded in script (lowest priority) ``` ### Precedence Examples **Example 1: Environment Variable Wins** ```bash # Shell environment has highest priority $ export var_cpu=16 $ bash pihole-install.sh # Result: Container gets 16 cores # (ignores app defaults, user defaults, built-ins) ``` **Example 2: App Defaults Override User Defaults** ```bash # User Defaults: var_cpu=4 # App Defaults: var_cpu=2 $ bash pihole-install.sh # Result: Container gets 2 cores # (app-specific setting takes precedence) ``` **Example 3: All Defaults Missing (Built-ins Used)** ```bash # No environment variables set # No app defaults file # No user defaults file $ bash pihole-install.sh # Result: Uses built-in defaults # (var_cpu might be 2 by default) ``` ### Implementation in Code ```bash # Typical pattern in build.func base_settings() { # Priority 1: Environment variables (already set if export used) CT_TYPE=${var_unprivileged:-"1"} # Use existing or default # Priority 2: Load app defaults (may override above) if [ -f "$(get_app_defaults_path)" ]; then load_vars_file "$(get_app_defaults_path)" fi # Priority 3: Load user defaults if [ -f "/usr/local/community-scripts/default.vars" ]; then load_vars_file "/usr/local/community-scripts/default.vars" fi # Priority 4: Apply built-in defaults (lowest) CORE_COUNT=${var_cpu:-"${APP_CPU_DEFAULT:-2}"} RAM_SIZE=${var_ram:-"${APP_RAM_DEFAULT:-1024}"} # Result: var_cpu has been set through precedence chain } ``` --- ## Data Flow Diagrams ### Installation Flow: Advanced Settings ``` ┌──────────────┐ │ Start Script│ └──────┬───────┘ │ v ┌──────────────────────────────┐ │ Display Installation Mode │ │ Menu (5 options) │ └──────┬───────────────────────┘ │ User selects "Advanced Settings" v ┌──────────────────────────────────┐ │ Call: base_settings() │ │ (Apply built-in defaults) │ └──────┬───────────────────────────┘ │ v ┌──────────────────────────────────┐ │ Call: advanced_settings() │ │ (Show 19-step wizard) │ │ - Ask CPU, RAM, Disk, Network... │ └──────┬───────────────────────────┘ │ v ┌──────────────────────────────────┐ │ Show Summary │ │ Review all chosen values │ └──────┬───────────────────────────┘ │ User confirms v ┌──────────────────────────────────┐ │ Create Container │ │ Using current variable values │ └──────┬───────────────────────────┘ │ v ┌──────────────────────────────────┐ │ Installation Complete │ └──────┬───────────────────────────┘ │ v ┌──────────────────────────────────────┐ │ Offer: Save as App Defaults? │ │ (Save current settings) │ └──────┬───────────────────────────────┘ │ ├─ YES → Save to defaults/.vars │ └─ NO → Exit ``` ### Variable Resolution Flow ``` CONTAINER CREATION STARTED │ v ┌─────────────────────┐ │ Check ENVIRONMENT │ │ for var_cpu, var_..│ └──────┬──────────────┘ │ Found? Use them (Priority 1) │ Not found? Continue... v ┌──────────────────────────┐ │ Load App Defaults │ │ /defaults/.vars │ └──────┬───────────────────┘ │ File exists? Parse & load (Priority 2) │ Not found? Continue... v ┌──────────────────────────┐ │ Load User Defaults │ │ /default.vars │ └──────┬───────────────────┘ │ File exists? Parse & load (Priority 3) │ Not found? Continue... v ┌──────────────────────────┐ │ Use Built-in Defaults │ │ (Hardcoded values) │ └──────┬───────────────────┘ │ v ┌──────────────────────────┐ │ All Variables Resolved │ │ Ready for container │ │ creation │ └──────────────────────────┘ ``` --- ## Security Model ### Threat Model | Threat | Mitigation | | ---------------------------- | ------------------------------------------------- | | **Arbitrary Code Execution** | No `source` or `eval`; manual parsing only | | **Variable Injection** | Whitelist of allowed variable names | | **Command Substitution** | `_sanitize_value()` blocks `$()`, backticks, etc. | | **Path Traversal** | Files locked to `/usr/local/community-scripts/` | | **Permission Escalation** | Files created with restricted permissions | | **Information Disclosure** | Sensitive variables not logged | ### Security Controls #### 1. Input Validation ```bash # Only specific variables allowed if ! _is_whitelisted_key "$key"; then skip_this_variable fi # Values sanitized if ! val="$(_sanitize_value "$value")"; then reject_entire_line fi ``` #### 2. Safe File Parsing ```bash # ❌ DANGEROUS (OLD) source /path/to/config.conf # Could execute: rm -rf / or any code # ✅ SAFE (NEW) load_vars_file "/path/to/config.conf" # Only reads var_name=value pairs, no execution ``` #### 3. Whitelisting ```bash # Only these variables can be configured var_cpu, var_ram, var_disk, var_brg, ... var_hostname, var_pw, var_ssh, ... # NOT allowed: var_malicious, var_hack, custom_var, ... ``` #### 4. Value Constraints ```bash # No command injection patterns if [[ "$value" =~ ($|`|;|&|<\() ]]; then reject_value fi ``` --- ## Implementation Details ### Module: `build.func` **Load Order** (in actual scripts): 1. `#!/usr/bin/env bash` - Shebang 2. `source /dev/stdin <<<$(curl ... api.func)` - API functions 3. `source /dev/stdin <<<$(curl ... build.func)` - Build functions 4. `variables()` - Initialize variables 5. `check_root()` - Security check 6. `install_script()` - Main flow **Key Sections**: ```bash # Section 1: Initialization & Variables - variables() - NSAPP, var_install, INTEGER pattern, etc. # Section 2: Storage Management - storage_selector() - ensure_storage_selection_for_vars_file() # Section 3: Base Settings - base_settings() # Apply defaults to all var_* - echo_default() # Display current settings # Section 4: Variable Loading - load_vars_file() # Safe parsing - _is_whitelisted_key() # Validation - _sanitize_value() # Threat mitigation # Section 5: Defaults Management - default_var_settings() # Load user defaults - get_app_defaults_path() # Get app defaults path - maybe_offer_save_app_defaults() # Save option # Section 6: Installation Flow - install_script() # Main entry point - advanced_settings() # 20-step wizard ``` ### Regex Patterns Used | Pattern | Purpose | Example Match | | ---------------------- | --------------------- | ----------------------- | | `^[0-9]+([.][0-9]+)?$` | Integer validation | `4`, `192.168` | | `^var_[a-z_]+$` | Variable name | `var_cpu`, `var_ssh` | | `*'$('*` | Command substitution | `$(whoami)` | | `*\`\*` | Backtick substitution | `` `cat /etc/passwd` `` | --- ## Appendix: Migration Reference ### Old Pattern (Deprecated) ```bash # ❌ OLD: config-file.func source config-file.conf # Executes arbitrary code if [ "$USE_DEFAULTS" = "yes" ]; then apply_settings_directly fi ``` ### New Pattern (Current) ```bash # ✅ NEW: load_vars_file() if load_vars_file "$(get_app_defaults_path)"; then echo "Settings loaded securely" fi ``` ### Function Mapping | Old | New | Location | | ---------------- | --------------------------------- | ---------- | | `read_config()` | `load_vars_file()` | build.func | | `write_config()` | `_build_current_app_vars_tmp()` | build.func | | None | `maybe_offer_save_app_defaults()` | build.func | | None | `get_app_defaults_path()` | build.func | --- **End of Technical Reference** ================================================ FILE: docs/api/README.md ================================================ # API Integration Documentation (/api) This directory contains comprehensive documentation for API integration and the `/api` directory. ## Overview The `/api` directory contains the Proxmox Community Scripts API backend for diagnostic reporting, telemetry, and analytics integration. ## Key Components ### Main API Service Located in `/api/main.go`: - RESTful API for receiving telemetry data - Installation statistics tracking - Error reporting and analytics - Performance monitoring ### Integration with Scripts The API is integrated into all installation scripts via `api.func`: - Sends installation start/completion events - Reports errors and exit codes - Collects anonymous usage statistics - Enables project analytics ## Documentation Structure API documentation covers: - API endpoint specifications - Integration methods - Data formats and schemas - Error handling - Privacy and data handling ## Key Resources - **[misc/api.func/](../misc/api.func/)** - API function library documentation - **[misc/api.func/README.md](../misc/api.func/README.md)** - Quick reference - **[misc/api.func/API_FUNCTIONS_REFERENCE.md](../misc/api.func/API_FUNCTIONS_REFERENCE.md)** - Complete function reference ## API Functions The `api.func` library provides: ### `post_to_api()` Send container installation data to API. **Usage**: ```bash post_to_api CTID STATUS APP_NAME ``` ### `post_update_to_api()` Report application update status. **Usage**: ```bash post_update_to_api CTID APP_NAME VERSION ``` ### `get_error_description()` Get human-readable error description from exit code. **Usage**: ```bash ERROR_DESC=$(get_error_description EXIT_CODE) ``` ## API Integration Points ### In Container Creation (`ct/AppName.sh`) - Called by build.func to report container creation - Sends initial container setup data - Reports success or failure ### In Installation Scripts (`install/appname-install.sh`) - Called at start of installation - Called on installation completion - Called on error conditions ### Data Collected - Container/VM ID - Application name and version - Installation duration - Success/failure status - Error codes (if failure) - Anonymous usage metrics ## Privacy All API data: - ✅ Anonymous (no personal data) - ✅ Aggregated for statistics - ✅ Used only for project improvement - ✅ No tracking of user identities - ✅ Can be disabled if desired ## API Architecture ``` Installation Scripts │ ├─ Call: api.func functions │ └─ POST to: https://api.community-scripts.org │ ├─ Receives data ├─ Validates format ├─ Stores metrics └─ Aggregates statistics │ └─ Used for: ├─ Download tracking ├─ Error trending ├─ Feature usage stats └─ Project health monitoring ``` ## Common API Tasks - **Enable API reporting** → Built-in by default, no configuration needed - **Disable API** → Set `api_disable="yes"` before running - **View API data** → Visit https://community-scripts.org/stats - **Report API errors** → [GitHub Issues](https://github.com/community-scripts/ProxmoxVE/issues) ## Debugging API Issues If API calls fail: 1. Check internet connectivity 2. Verify API endpoint availability 3. Review error codes in [EXIT_CODES.md](../EXIT_CODES.md) 4. Check API function logs 5. Report issues on GitHub ## API Endpoint **Base URL**: `https://api.community-scripts.org` **Endpoints**: - `POST /install` - Report container installation - `POST /update` - Report application update - `GET /stats` - Public statistics --- **Last Updated**: December 2025 **Maintainers**: community-scripts team ================================================ FILE: docs/contribution/AI.md ================================================ # 🤖 AI Contribution Guidelines for ProxmoxVE > **This documentation is intended for all AI assistants (GitHub Copilot, Claude, ChatGPT, etc.) contributing to this project.** ## 🎯 Core Principles ### 1. **Maximum Use of `tools.func` Functions** We have an extensive library of helper functions. **NEVER** implement your own solutions when a function already exists! ### 2. **No Pointless Variables** Only create variables when they: - Are used multiple times - Improve readability - Are intended for configuration ### 3. **Consistent Script Structure** All scripts follow an identical structure. Deviations are not acceptable. ### 4. **Bare-Metal Installation** We do **NOT use Docker** for our installation scripts. All applications are installed directly on the system. --- ## 📁 Script Types and Their Structure ### CT Script (`ct/AppName.sh`) ```bash #!/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: AuthorName (GitHubUsername) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://application-url.com APP="AppName" var_tags="${var_tags:-tag1;tag2;tag3}" 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/appname ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "appname" "YourUsername/YourRepo"; then msg_info "Stopping Service" systemctl stop appname msg_ok "Stopped Service" msg_info "Backing up Data" cp -r /opt/appname/data /opt/appname_data_backup msg_ok "Backed up Data" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "appname" "owner/repo" "tarball" "latest" "/opt/appname" # Build steps... msg_info "Restoring Data" cp -r /opt/appname_data_backup/. /opt/appname/data rm -rf /opt/appname_data_backup msg_ok "Restored Data" msg_info "Starting Service" systemctl start appname 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}:PORT${CL}" ``` ### Install Script (`install/AppName-install.sh`) ```bash #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: AuthorName (GitHubUsername) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://application-url.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-get install -y \ dependency1 \ dependency2 msg_ok "Installed Dependencies" # Runtime Setup (ALWAYS use our functions!) NODE_VERSION="22" setup_nodejs # or PG_VERSION="16" setup_postgresql # or setup_uv # etc. fetch_and_deploy_gh_release "appname" "owner/repo" "tarball" "latest" "/opt/appname" msg_info "Setting up Application" cd /opt/appname # Build/Setup Schritte... msg_ok "Set up Application" msg_info "Creating Service" cat </etc/systemd/system/appname.service [Unit] Description=AppName Service After=network.target [Service] Type=simple User=root WorkingDirectory=/opt/appname ExecStart=/path/to/executable Restart=on-failure RestartSec=5 [Install] WantedBy=multi-user.target EOF systemctl enable -q --now appname msg_ok "Created Service" motd_ssh customize cleanup_lxc ``` --- ## 🔧 Available Helper Functions ### Release Management | Function | Description | Example | | ----------------------------- | ----------------------------------- | ------------------------------------------------------------- | | `fetch_and_deploy_gh_release` | Fetches and installs GitHub Release | `fetch_and_deploy_gh_release "app" "owner/repo"` | | `check_for_gh_release` | Checks for new version | `if check_for_gh_release "app" "YourUsername/YourRepo"; then` | **Modes for `fetch_and_deploy_gh_release`:** ```bash # Tarball/Source (Standard) fetch_and_deploy_gh_release "appname" "owner/repo" # Binary (.deb) fetch_and_deploy_gh_release "appname" "owner/repo" "binary" # Prebuilt Archive fetch_and_deploy_gh_release "appname" "owner/repo" "prebuild" "latest" "/opt/appname" "filename.tar.gz" # Single Binary fetch_and_deploy_gh_release "appname" "owner/repo" "singlefile" "latest" "/opt/appname" "binary-linux-amd64" ``` **Clean Install Flag:** ```bash CLEAN_INSTALL=1 fetch_and_deploy_gh_release "appname" "owner/repo" ``` ### Runtime/Language Setup | Function | Variable(s) | Example | | -------------- | ----------------------------- | ---------------------------------------------------- | | `setup_nodejs` | `NODE_VERSION`, `NODE_MODULE` | `NODE_VERSION="22" setup_nodejs` | | `setup_uv` | `PYTHON_VERSION` | `PYTHON_VERSION="3.12" setup_uv` | | `setup_go` | `GO_VERSION` | `GO_VERSION="1.22" setup_go` | | `setup_rust` | `RUST_VERSION`, `RUST_CRATES` | `RUST_CRATES="monolith" setup_rust` | | `setup_ruby` | `RUBY_VERSION` | `RUBY_VERSION="3.3" setup_ruby` | | `setup_java` | `JAVA_VERSION` | `JAVA_VERSION="21" setup_java` | | `setup_php` | `PHP_VERSION`, `PHP_MODULES` | `PHP_VERSION="8.3" PHP_MODULES="redis,gd" setup_php` | ### Database Setup | Function | Variable(s) | Example | | --------------------- | ------------------------------------ | ----------------------------------------------------------- | | `setup_postgresql` | `PG_VERSION`, `PG_MODULES` | `PG_VERSION="16" setup_postgresql` | | `setup_postgresql_db` | `PG_DB_NAME`, `PG_DB_USER` | `PG_DB_NAME="mydb" PG_DB_USER="myuser" setup_postgresql_db` | | `setup_mariadb_db` | `MARIADB_DB_NAME`, `MARIADB_DB_USER` | `MARIADB_DB_NAME="mydb" setup_mariadb_db` | | `setup_mysql` | `MYSQL_VERSION` | `setup_mysql` | | `setup_mongodb` | `MONGO_VERSION` | `setup_mongodb` | | `setup_clickhouse` | - | `setup_clickhouse` | ### Tools & Utilities | Function | Description | | ------------------- | ---------------------------------- | | `setup_adminer` | Installs Adminer for DB management | | `setup_composer` | Install PHP Composer | | `setup_ffmpeg` | Install FFmpeg | | `setup_imagemagick` | Install ImageMagick | | `setup_gs` | Install Ghostscript | | `setup_hwaccel` | Configure hardware acceleration | ### Helper Utilities | Function | Description | Example | | ----------------------------- | ---------------------------- | ----------------------------------------- | | `import_local_ip` | Sets `$LOCAL_IP` variable | `import_local_ip` | | `ensure_dependencies` | Checks/installs dependencies | `ensure_dependencies curl jq` | | `install_packages_with_retry` | APT install with retry | `install_packages_with_retry nginx redis` | --- ## ❌ Anti-Patterns (NEVER use!) ### 1. Pointless Variables ```bash # ❌ WRONG - unnecessary variables APP_NAME="myapp" APP_DIR="/opt/${APP_NAME}" APP_USER="root" APP_PORT="3000" cd $APP_DIR # ✅ CORRECT - use directly cd /opt/myapp ``` ### 2. Custom Download Logic ```bash # ❌ WRONG - custom wget/curl logic RELEASE=$(curl -s https://api.github.com/repos/YourUsername/YourRepo/releases/latest | jq -r '.tag_name') wget https://github.com/YourUsername/YourRepo/archive/${RELEASE}.tar.gz tar -xzf ${RELEASE}.tar.gz mv repo-${RELEASE} /opt/myapp # ✅ CORRECT - use our function fetch_and_deploy_gh_release "myapp" "YourUsername/YourRepo" "tarball" "latest" "/opt/myapp" ``` ### 3. Custom Version-Check Logic ```bash # ❌ WRONG - custom version check CURRENT=$(cat /opt/myapp/version.txt) LATEST=$(curl -s https://api.github.com/repos/YourUsername/YourRepo/releases/latest | jq -r '.tag_name') if [[ "$CURRENT" != "$LATEST" ]]; then # update... fi # ✅ CORRECT - use our function if check_for_gh_release "myapp" "YourUsername/YourRepo"; then # update... fi ``` ### 4. Docker-based Installation ```bash # ❌ WRONG - using Docker docker pull myapp/myapp:latest docker run -d --name myapp myapp/myapp:latest # ✅ CORRECT - Bare-Metal Installation fetch_and_deploy_gh_release "myapp" "YourUsername/YourRepo" npm install && npm run build ``` ### 5. Custom Runtime Installation ```bash # ❌ WRONG - custom Node.js installation curl -fsSL https://deb.nodesource.com/setup_22.x | bash - apt install -y nodejs # ✅ CORRECT - use our function NODE_VERSION="22" setup_nodejs ``` ### 6. Redundant echo Statements ```bash # ❌ WRONG - custom logging messages echo "Installing dependencies..." apt install -y curl echo "Done!" # ✅ CORRECT - use msg_info/msg_ok msg_info "Installing Dependencies" $STD apt install -y curl msg_ok "Installed Dependencies" ``` ### 7. Missing $STD Usage ```bash # ❌ WRONG - apt without $STD apt install -y nginx # ✅ CORRECT - with $STD for silent output $STD apt install -y nginx ``` ### 8. Wrapping `tools.func` Functions in msg Blocks ```bash # ❌ WRONG - tools.func functions have their own msg_info/msg_ok! msg_info "Installing Node.js" NODE_VERSION="22" setup_nodejs msg_ok "Installed Node.js" msg_info "Updating Application" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "appname" "owner/repo" "tarball" "latest" "/opt/appname" msg_ok "Updated Application" # ✅ CORRECT - call directly without msg wrapper NODE_VERSION="22" setup_nodejs CLEAN_INSTALL=1 fetch_and_deploy_gh_release "appname" "owner/repo" "tarball" "latest" "/opt/appname" ``` **Functions with built-in messages (NEVER wrap in msg blocks):** - `fetch_and_deploy_gh_release` - `check_for_gh_release` - `setup_nodejs` - `setup_postgresql` / `setup_postgresql_db` - `setup_mariadb` / `setup_mariadb_db` - `setup_mongodb` - `setup_mysql` - `setup_ruby` - `setup_go` - `setup_java` - `setup_php` - `setup_uv` - `setup_rust` - `setup_composer` - `setup_ffmpeg` - `setup_imagemagick` - `setup_gs` - `setup_adminer` - `setup_hwaccel` ### 9. Creating Unnecessary System Users ```bash # ❌ WRONG - LXC containers run as root, no separate user needed useradd -m -s /usr/bin/bash appuser chown -R appuser:appuser /opt/appname sudo -u appuser npm install # ✅ CORRECT - run directly as root cd /opt/appname $STD npm install ``` ### 10. Using `export` in .env Files ```bash # ❌ WRONG - export is unnecessary in .env files cat </opt/appname/.env export DATABASE_URL=postgres://... export SECRET_KEY=abc123 export NODE_ENV=production EOF # ✅ CORRECT - simple KEY=VALUE format (files are sourced with set -a) cat </opt/appname/.env DATABASE_URL=postgres://... SECRET_KEY=abc123 NODE_ENV=production EOF ``` ### 11. Using External Shell Scripts ```bash # ❌ WRONG - external script that gets executed cat <<'EOF' >/opt/appname/install_script.sh #!/bin/bash cd /opt/appname npm install npm run build EOF chmod +x /opt/appname/install_script.sh $STD bash /opt/appname/install_script.sh rm -f /opt/appname/install_script.sh # ✅ CORRECT - run commands directly cd /opt/appname $STD npm install $STD npm run build ``` ### 12. Using `sudo` in LXC Containers ```bash # ❌ WRONG - sudo is unnecessary in LXC (already root) sudo -u postgres psql -c "CREATE DATABASE mydb;" sudo -u appuser npm install # ✅ CORRECT - use functions or run directly as root PG_DB_NAME="mydb" PG_DB_USER="myuser" setup_postgresql_db cd /opt/appname $STD npm install ``` ### 13. Unnecessary `systemctl daemon-reload` ```bash # ❌ WRONG - daemon-reload is only needed when MODIFYING existing services cat </etc/systemd/system/appname.service # ... service config ... EOF systemctl daemon-reload # Unnecessary for new services! systemctl enable -q --now appname # ✅ CORRECT - new services don't need daemon-reload cat </etc/systemd/system/appname.service # ... service config ... EOF systemctl enable -q --now appname ``` ### 14. Creating Custom Credentials Files ```bash # ❌ WRONG - custom credentials file is not part of the standard template msg_info "Saving Credentials" cat <~/appname.creds Database User: ${DB_USER} Database Pass: ${DB_PASS} EOF msg_ok "Saved Credentials" # ✅ CORRECT - credentials are stored in .env or shown in final message only # If you use setup_postgresql_db / setup_mariadb_db, a standard ~/[appname].creds is created automatically ``` ### 15. Wrong Footer Pattern ```bash # ❌ WRONG - old cleanup pattern with msg blocks motd_ssh customize msg_info "Cleaning up" $STD apt-get -y autoremove $STD apt-get -y autoclean msg_ok "Cleaned" # ✅ CORRECT - use cleanup_lxc function motd_ssh customize cleanup_lxc ``` ### 16. Manual Database Creation Instead of Functions ```bash # ❌ WRONG - manual database creation DB_USER="myuser" 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 mydb WITH OWNER $DB_USER;" $STD sudo -u postgres psql -d mydb -c "CREATE EXTENSION IF NOT EXISTS postgis;" # ✅ CORRECT - use setup_postgresql_db function # This sets PG_DB_USER, PG_DB_PASS, PG_DB_NAME automatically PG_DB_NAME="mydb" PG_DB_USER="myuser" PG_DB_EXTENSIONS="postgis" setup_postgresql_db ``` ### 17. Writing Files Without Heredocs ```bash # ❌ WRONG - echo / printf / tee echo "# Config" > /opt/app/config.yml echo "port: 3000" >> /opt/app/config.yml printf "# Config\nport: 3000\n" > /opt/app/config.yml cat config.yml | tee /opt/app/config.yml ``` ```bash # ✅ CORRECT - always use a single heredoc cat </opt/app/config.yml # Config port: 3000 EOF ``` --- ## 📝 Important Rules ### Variable Declarations (CT Script) ```bash # Standard declarations (ALWAYS present) APP="AppName" var_tags="${var_tags:-tag1;tag2}" 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}" ``` ### Update-Script Pattern ```bash function update_script() { header_info check_container_storage check_container_resources # 1. Check if installation exists if [[ ! -d /opt/appname ]]; then msg_error "No ${APP} Installation Found!" exit fi # 2. Check for update if check_for_gh_release "appname" "YourUsername/YourRepo"; then # 3. Stop service msg_info "Stopping Service" systemctl stop appname msg_ok "Stopped Service" # 4. Backup data (if present) msg_info "Backing up Data" cp -r /opt/appname/data /opt/appname_data_backup msg_ok "Backed up Data" # 5. Perform clean install CLEAN_INSTALL=1 fetch_and_deploy_gh_release "appname" "owner/repo" "tarball" "latest" "/opt/appname" # 6. Rebuild (if needed) cd /opt/appname $STD npm install $STD npm run build # 7. Restore data msg_info "Restoring Data" cp -r /opt/appname_data_backup/. /opt/appname/data rm -rf /opt/appname_data_backup msg_ok "Restored Data" # 8. Start service msg_info "Starting Service" systemctl start appname msg_ok "Started Service" msg_ok "Updated successfully!" fi exit # IMPORTANT: Always end with exit! } ``` ### Systemd Service Pattern ```bash msg_info "Creating Service" cat </etc/systemd/system/appname.service [Unit] Description=AppName Service After=network.target [Service] Type=simple User=root WorkingDirectory=/opt/appname Environment=NODE_ENV=production ExecStart=/usr/bin/node /opt/appname/server.js Restart=on-failure RestartSec=5 [Install] WantedBy=multi-user.target EOF systemctl enable -q --now appname msg_ok "Created Service" ``` ### Installation Script Footer ```bash # ALWAYS at the end of the install script: motd_ssh customize cleanup_lxc ``` --- ## 📖 Reference: Good Example Scripts Look at these recent well-implemented applications as reference: ### Container Scripts (Latest 10) - [ct/thingsboard.sh](../ct/thingsboard.sh) - IoT platform with proper update_script - [ct/unifi-os-server.sh](../ct/unifi-os-server.sh) - Complex setup with podman - [ct/trip.sh](../ct/trip.sh) - Simple Ruby app - [ct/fladder.sh](../ct/fladder.sh) - Media app with database - [ct/qui.sh](../ct/qui.sh) - Lightweight utility - [ct/kutt.sh](../ct/kutt.sh) - Node.js with PostgreSQL - [ct/flatnotes.sh](../ct/flatnotes.sh) - Python notes app - [ct/investbrain.sh](../ct/investbrain.sh) - Finance app - [ct/gwn-manager.sh](../ct/gwn-manager.sh) - Network management - [ct/sportarr.sh](../ct/sportarr.sh) - Specialized \*Arr variant ### Install Scripts (Latest) - [install/unifi-os-server-install.sh](../install/unifi-os-server-install.sh) - Complex setup with API integration - [install/trip-install.sh](../install/trip-install.sh) - Rails application setup - [install/mail-archiver-install.sh](../install/mail-archiver-install.sh) - Email-related service **Key things to notice:** - Proper error handling with `catch_errors` - Use of `check_for_gh_release` and `fetch_and_deploy_gh_release` - Correct backup/restore patterns in `update_script` - Footer always ends with `motd_ssh`, `customize`, `cleanup_lxc` - Website metadata requested via the website (Report issue on script page) if needed --- ## Website Metadata (Reference) Website metadata (name, slug, description, logo, categories, etc.) is **not** added as files in the repo. Contributors request or update it via the **website**: go to the script's page and use the **Report issue** button; the flow will guide you. The structure below is a **reference** for what metadata exists (e.g. for the form or when describing what you need). ### JSON Structure (Reference) ```json { "name": "AppName", "slug": "appname", "categories": [1], "date_created": "2026-01-16", "type": "ct", "updateable": true, "privileged": false, "interface_port": 3000, "documentation": "https://docs.appname.com/", "website": "https://appname.com/", "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/appname.webp", "config_path": "/opt/appname/.env", "description": "Short description of the application and its purpose.", "install_methods": [ { "type": "default", "script": "ct/appname.sh", "resources": { "cpu": 2, "ram": 2048, "hdd": 8, "os": "Debian", "version": "13" } } ], "default_credentials": { "username": null, "password": null }, "notes": [] } ``` ### Required Fields | Field | Type | Description | | --------------------- | ------- | -------------------------------------------------- | | `name` | string | Display name of the application | | `slug` | string | Lowercase, no spaces, used for filenames | | `categories` | array | Category ID(s) - see category list below | | `date_created` | string | Creation date (YYYY-MM-DD) | | `type` | string | `ct` for container, `vm` for virtual machine | | `updateable` | boolean | Whether update_script is implemented | | `privileged` | boolean | Whether container needs privileged mode | | `interface_port` | number | Primary web interface port (or `null`) | | `documentation` | string | Link to official docs | | `website` | string | Link to official website | | `logo` | string | URL to application logo (preferably selfhst icons) | | `config_path` | string | Path to main config file (or empty string) | | `description` | string | Brief description of the application | | `install_methods` | array | Installation configurations | | `default_credentials` | object | Default username/password (or null) | | `notes` | array | Additional notes/warnings | ### Categories | ID | Category | | --- | ------------------------- | | 0 | Miscellaneous | | 1 | Proxmox & Virtualization | | 2 | Operating Systems | | 3 | Containers & Docker | | 4 | Network & Firewall | | 5 | Adblock & DNS | | 6 | Authentication & Security | | 7 | Backup & Recovery | | 8 | Databases | | 9 | Monitoring & Analytics | | 10 | Dashboards & Frontends | | 11 | Files & Downloads | | 12 | Documents & Notes | | 13 | Media & Streaming | | 14 | \*Arr Suite | | 15 | NVR & Cameras | | 16 | IoT & Smart Home | | 17 | ZigBee, Z-Wave & Matter | | 18 | MQTT & Messaging | | 19 | Automation & Scheduling | | 20 | AI / Coding & Dev-Tools | | 21 | Webservers & Proxies | | 22 | Bots & ChatOps | | 23 | Finance & Budgeting | | 24 | Gaming & Leisure | | 25 | Business & ERP | ### Notes Format ```json "notes": [ { "text": "Change the default password after first login!", "type": "warning" }, { "text": "Requires at least 4GB RAM for optimal performance.", "type": "info" } ] ``` **Note types:** `info`, `warning`, `error` ### Examples with Credentials ```json "default_credentials": { "username": "admin", "password": "admin" } ``` Or no credentials: ```json "default_credentials": { "username": null, "password": null } ``` --- ## 🔍 Checklist Before PR Creation - [ ] No Docker installation used - [ ] `fetch_and_deploy_gh_release` used for GitHub releases - [ ] `check_for_gh_release` used for update checks - [ ] `setup_*` functions used for runtimes (nodejs, postgresql, etc.) - [ ] **`tools.func` functions NOT wrapped in msg_info/msg_ok blocks** - [ ] No redundant variables (only when used multiple times) - [ ] `$STD` before all apt/npm/build commands - [ ] `msg_info`/`msg_ok`/`msg_error` for logging (only for custom code) - [ ] Correct script structure followed (see templates) - [ ] Update function present and functional (CT scripts) - [ ] Data backup implemented in update function (if applicable) - [ ] `motd_ssh`, `customize`, `cleanup_lxc` at the end of install scripts - [ ] No custom download/version-check logic - [ ] All links point to `community-scripts/ProxmoxVE` (not `ProxmoxVED`!) - [ ] Website metadata requested via the website (Report issue) if needed - [ ] Category IDs are valid (0-25) - [ ] Default OS version is Debian 13 or newer (unless special requirement) - [ ] Default resources are reasonable for the application --- ## 💡 Tips for AI Assistants 1. **ALWAYS search `tools.func` first** before implementing custom solutions 2. **Use recent scripts as reference** (Thingsboard, UniFi OS, Trip, Flatnotes, etc.) 3. **Ask when uncertain** instead of introducing wrong patterns 4. **Test via GitHub** - push to your fork and test with curl (not local bash) ```bash bash -c "$(curl -fsSL https://raw.githubusercontent.com/YOUR_USERNAME/ProxmoxVE/main/ct/myapp.sh)" # Wait 10-30 seconds after pushing - GitHub takes time to update files ``` 5. **Consistency > Creativity** - follow established patterns strictly 6. **Check the templates** - they show the correct structure 7. **Don't wrap tools.func functions** - they handle their own msg_info/msg_ok output 8. **Minimal variables** - only create variables that are truly reused multiple times 9. **Always use $STD** - ensures silent/non-interactive execution 10. **Reference good examples** - look at recent additions in each category --- ## 🍒 Important: Cherry-Picking Your Files for PR Submission ⚠️ **CRITICAL**: When you submit your PR, you must use git cherry-pick to send ONLY your 2 files! Why? Because `setup-fork.sh` modifies 600+ files to update links. If you commit all changes, your PR will be impossible to merge. **See**: [README.md - Cherry-Pick Section](README.md#-cherry-pick-submitting-only-your-changes) for complete instructions on: - Creating a clean submission branch - Cherry-picking only your files (ct/myapp.sh, install/myapp-install.sh) - Verifying your PR has only 2 file changes (not 600+) **Quick reference**: ```bash # Create clean branch from upstream git fetch upstream git checkout -b submit/myapp upstream/main # Cherry-pick your commit(s) or manually add your 2 files # Then push to your fork and create PR ``` --- ## 📚 Further Documentation - [CONTRIBUTING.md](CONTRIBUTING.md) - General contribution guidelines - [GUIDE.md](GUIDE.md) - Detailed developer documentation - [HELPER_FUNCTIONS.md](HELPER_FUNCTIONS.md) - Complete tools.func reference - [README.md](README.md) - Cherry-pick guide and workflow instructions - [../TECHNICAL_REFERENCE.md](../TECHNICAL_REFERENCE.md) - Technical deep dive - [../EXIT_CODES.md](../EXIT_CODES.md) - Exit code reference - [templates_ct/](templates_ct/) - CT script templates - [templates_install/](templates_install/) - Install script templates - [templates_json/](templates_json/) - Metadata structure reference; submit via website ================================================ FILE: docs/contribution/CODE-AUDIT.md ================================================ # 🧪 Code Audit: LXC Script Flow This guide explains the current execution flow and what to verify during reviews. ## Execution Flow (CT + Install) 1. `ct/appname.sh` runs on the Proxmox host and sources `misc/build.func`. 2. `build.func` orchestrates prompts, container creation, and invokes the install script. 3. Inside the container, `misc/install.func` exposes helper functions via `$FUNCTIONS_FILE_PATH`. 4. `install/appname-install.sh` performs the application install. 5. The CT script prints the completion message. ## Audit Checklist ### CT Script (ct/) - Sources `misc/build.func` from `community-scripts/ProxmoxVE/main` (setup-fork.sh updates for forks). - Uses `check_for_gh_release` + `fetch_and_deploy_gh_release` for updates. - No Docker-based installs. ### Install Script (install/) - Sources `$FUNCTIONS_FILE_PATH`. - Uses `tools.func` helpers (setup\_\*). - Ends with `motd_ssh`, `customize`, `cleanup_lxc`. ### Website Metadata - Website metadata for new/updated scripts is requested via the website (Report issue on script page) where applicable. ### Testing - Test via curl from your fork (CT script only). - Wait 10-30 seconds after push. ## References - `docs/contribution/templates_ct/AppName.sh` - `docs/contribution/templates_install/AppName-install.sh` - `docs/contribution/templates_json/AppName.json` - `docs/contribution/GUIDE.md` ================================================ FILE: docs/contribution/CONTRIBUTING.md ================================================ # Community Scripts Contribution Guide ## **Welcome to the communty-scripts Repository!** 📜 These documents outline the essential coding standards for all our scripts and JSON files. Adhering to these standards ensures that our codebase remains consistent, readable, and maintainable. By following these guidelines, we can improve collaboration, reduce errors, and enhance the overall quality of our project. ### Why Coding Standards Matter Coding standards are crucial for several reasons: 1. **Consistency**: Consistent code is easier to read, understand, and maintain. It helps new team members quickly get up to speed and reduces the learning curve. 2. **Readability**: Clear and well-structured code is easier to debug and extend. It allows developers to quickly identify and fix issues. 3. **Maintainability**: Code that follows a standard structure is easier to refactor and update. It ensures that changes can be made with minimal risk of introducing new bugs. 4. **Collaboration**: When everyone follows the same standards, it becomes easier to collaborate on code. It reduces friction and misunderstandings during code reviews and merges. ### Scope of These Documents These documents cover the coding standards for the following types of files in our project: - **`install/$AppName-install.sh` Scripts**: These scripts are responsible for the installation of applications. - **`ct/$AppName.sh` Scripts**: These scripts handle the creation and updating of containers. - **Website metadata**: Display data (name, description, logo, etc.) is requested via the website (Report issue on the script page), not via files in the repo. Each section provides detailed guidelines on various aspects of coding, including shebang usage, comments, variable naming, function naming, indentation, error handling, command substitution, quoting, script structure, and logging. Additionally, examples are provided to illustrate the application of these standards. By following the coding standards outlined in this document, we ensure that our scripts and JSON files are of high quality, making our project more robust and easier to manage. Please refer to this guide whenever you create or update scripts and JSON files to maintain a high standard of code quality across the project. 📚🔍 Let's work together to keep our codebase clean, efficient, and maintainable! 💪🚀 ## Getting Started Before contributing, please ensure that you have the following setup: 1. **Visual Studio Code** (recommended for script development) 2. **Recommended VS Code 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) ### Important Notes - Use [AppName.sh](https://github.com/community-scripts/ProxmoxVE/blob/main/docs/contribution/templates_ct/AppName.sh) and [AppName-install.sh](https://github.com/community-scripts/ProxmoxVE/blob/main/docs/contribution/templates_install/AppName-install.sh) as templates when creating new scripts. --- # 🚀 The Application Script (ct/AppName.sh) - You can find all coding standards, as well as the structure for this file [here](https://github.com/community-scripts/ProxmoxVE/blob/main/docs/contribution/templates_ct/AppName.md). - These scripts are responsible for container creation, setting the necessary variables and handling the update of the application once installed. --- # 🛠 The Installation Script (install/AppName-install.sh) - You can find all coding standards, as well as the structure for this file [here](https://github.com/community-scripts/ProxmoxVE/blob/main/docs/contribution/templates_install/AppName-install.md). - These scripts are responsible for the installation of the application. --- ## 🚀 Building Your Own Scripts Start with the [template script](https://github.com/community-scripts/ProxmoxVE/blob/main/docs/contribution/templates_install/AppName-install.sh) --- ## 🤝 Contribution Process ### 1. Fork the repository Fork to your GitHub account ### 2. Clone your fork on your local environment ```bash git clone https://github.com/yourUserName/ForkName ``` ### 3. Create a new branch ```bash git switch -c your-feature-branch ``` ### 4. Run setup-fork.sh to auto-configure your fork ```bash bash docs/contribution/setup-fork.sh --full ``` This script automatically: - Detects your GitHub username - Updates ALL curl URLs to point to your fork (for testing) - Creates `.git-setup-info` with your config - Backs up all modified files (\*.backup) **IMPORTANT**: This modifies 600+ files! Use cherry-pick when submitting your PR (see below). ### 5. Commit ONLY your new application files ```bash git commit -m "Your commit message" ``` ### 5. Push to your fork ```bash git push origin your-feature-branch ``` ### 6. Cherry-Pick: Submit Only Your Files for PR ⚠️ **IMPORTANT**: setup-fork.sh modified 600+ files. You MUST only submit your 2 new files! See [README.md - Cherry-Pick Guide](README.md#-cherry-pick-submitting-only-your-changes) for step-by-step instructions. Quick version: ```bash # Create clean branch from upstream git fetch upstream git checkout -b submit/myapp upstream/main # Copy only your files cp ../your-work-branch/ct/myapp.sh ct/myapp.sh cp ../your-work-branch/install/myapp-install.sh install/myapp-install.sh # Commit and verify git add ct/myapp.sh install/myapp-install.sh git commit -m "feat: add MyApp" git diff upstream/main --name-only # Should show ONLY your 2 files # Push and create PR git push origin submit/myapp ``` ### 7. Create a Pull Request Open a Pull Request from `submit/myapp` → `community-scripts/ProxmoxVE/main`. Verify the PR shows ONLY these 2 files: - `ct/myapp.sh` - `install/myapp-install.sh` --- # 🛠️ Developer Mode & Debugging When building or testing scripts, you can use the `dev_mode` variable to enable powerful debugging features. These flags can be combined (comma-separated). **Usage**: ```bash # Example: Run with trace and keep the container even if it fails dev_mode="trace,keep" bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/myapp.sh)" ``` ### Available Flags: | 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 (e.g., before customization). | | `breakpoint` | Allows hardcoded `breakpoint` calls in scripts to drop to a shell. | | `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 (MOTD). | --- ## 📚 Pages - [CT Template: AppName.sh](https://github.com/community-scripts/ProxmoxVE/blob/main/docs/contribution/templates_ct/AppName.sh) - [Install Template: AppName-install.sh](https://github.com/community-scripts/ProxmoxVE/blob/main/docs/contribution/templates_install/AppName-install.sh) - [JSON Template: AppName.json](https://github.com/community-scripts/ProxmoxVE/blob/main/docs/contribution/templates_json/AppName.json) — metadata structure reference; submit via the website (Report issue on script page) ================================================ FILE: docs/contribution/FORK_SETUP.md ================================================ # 🍴 Fork Setup Guide **Just forked ProxmoxVE? Run this first!** ## Quick Start ```bash # Clone your fork git clone https://github.com/YOUR_USERNAME/ProxmoxVE.git cd ProxmoxVE # Run setup script (auto-detects your username from git) bash docs/contribution/setup-fork.sh --full ``` That's it! ✅ --- ## What Does It Do? The `setup-fork.sh` script automatically: 1. **Detects** your GitHub username from git config 2. **Updates ALL hardcoded links** to point to your fork: - Documentation links pointing to `community-scripts/ProxmoxVE` - **Curl download URLs** in scripts (e.g., `curl ... github.com/community-scripts/ProxmoxVE/main/...`) 3. **Creates** `.git-setup-info` with your configuration details 4. **Backs up** all modified files (\*.backup for safety) ### Why Updating Curl Links Matters Your scripts contain `curl` commands that download dependencies from GitHub (build.func, tools.func, etc.): ```bash # First line of ct/myapp.sh source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) ``` **WITHOUT setup-fork.sh:** - Script URLs still point to `community-scripts/ProxmoxVE/main` - If you test locally with `bash ct/myapp.sh`, you're testing local files, but the script's curl commands would download from **upstream** repo - Your modifications aren't actually being tested via the curl commands! ❌ **AFTER setup-fork.sh:** - Script URLs are updated to `YourUsername/ProxmoxVE/main` - When you test via curl from GitHub: `bash -c "$(curl ... YOUR_USERNAME/ProxmoxVE/main/ct/myapp.sh)"`, it downloads from **your fork** - The script's curl commands also point to your fork, so you're actually testing your changes! ✅ - ⏱️ **Important:** GitHub takes 10-30 seconds to recognize pushed files - wait before testing! ```bash # Example: What setup-fork.sh changes # BEFORE (points to upstream): source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) # AFTER (points to your fork): source <(curl -fsSL https://raw.githubusercontent.com/john/ProxmoxVE/main/misc/build.func) ``` --- ## Usage ### Auto-Detect (Recommended) ```bash bash docs/contribution/setup-fork.sh --full ``` Automatically reads your GitHub username from `git remote origin url` ### Specify Username ```bash bash docs/contribution/setup-fork.sh --full john ``` Updates links to `github.com/john/ProxmoxVE` ### Custom Repository Name ```bash bash docs/contribution/setup-fork.sh --full john my-fork ``` Updates links to `github.com/john/my-fork` --- ## What Gets Updated? The script updates hardcoded links in these areas when using `--full`: - `ct/`, `install/`, `vm/` scripts - `misc/` function libraries - `docs/` (including `docs/contribution/`) - Code examples in documentation --- ## After Setup 1. **Review changes** ```bash git diff docs/ ``` 2. **Read git workflow tips** ```bash cat .git-setup-info ``` 3. **Start contributing** ```bash git checkout -b feature/my-app # Make your changes... git commit -m "feat: add my awesome app" ``` 4. **Follow the guide** ```bash cat docs/contribution/GUIDE.md ``` --- ## Common Workflows ### Keep Your Fork Updated ```bash # Add upstream if you haven't already git remote add upstream https://github.com/community-scripts/ProxmoxVE.git # Get latest from upstream git fetch upstream git rebase upstream/main git push origin main ``` ### Create a Feature Branch ```bash git checkout -b feature/docker-improvements # Make changes... git push origin feature/docker-improvements # Then create PR on GitHub ``` ### Sync Before Contributing ```bash git fetch upstream git rebase upstream/main git push -f origin main # Update your fork's main git checkout -b feature/my-feature ``` --- ## Troubleshooting ### "Git is not installed" or "not a git repository" ```bash # Make sure you cloned the repo first git clone https://github.com/YOUR_USERNAME/ProxmoxVE.git cd ProxmoxVE bash docs/contribution/setup-fork.sh --full ``` ### "Could not auto-detect GitHub username" ```bash # Your git origin URL isn't set up correctly git remote -v # Should show your fork URL, not community-scripts # Fix it: git remote set-url origin https://github.com/YOUR_USERNAME/ProxmoxVE.git bash docs/contribution/setup-fork.sh --full ``` ### "Permission denied" ```bash # Make script executable chmod +x docs/contribution/setup-fork.sh bash docs/contribution/setup-fork.sh --full ``` ### Reverted Changes by Accident? ```bash # Backups are created automatically git checkout docs/*.backup # Or just re-run setup-fork.sh bash docs/contribution/setup-fork.sh --full ``` --- ## Next Steps 1. ✅ Run `bash docs/contribution/setup-fork.sh --full` 2. 📖 Read [docs/contribution/GUIDE.md](GUIDE.md) 3. 🍴 Choose your contribution path: - **Containers** → [docs/ct/README.md](docs/ct/README.md) - **Installation** → [docs/install/README.md](docs/install/README.md) - **VMs** → [docs/vm/README.md](docs/vm/README.md) - **Tools** → [docs/tools/README.md](docs/tools/README.md) 4. 💻 Create your feature branch and contribute! --- ## Questions? - **Fork Setup Issues?** → See [Troubleshooting](#troubleshooting) above - **How to Contribute?** → [docs/contribution/GUIDE.md](GUIDE.md) - **Git Workflows?** → `cat .git-setup-info` - **Project Structure?** → [docs/README.md](docs/README.md) --- ## Happy Contributing! 🚀 ================================================ FILE: docs/contribution/GUIDE.md ================================================ # 🎯 **ProxmoxVE Contribution Guide** **Everything you need to know to contribute to ProxmoxVE** > **Last Updated**: December 2025 > **Difficulty**: Beginner → Advanced > **Time to Setup**: 15 minutes > **Time to Contribute**: 1-3 hours --- ## 📋 Table of Contents - [Quick Start](#quick-start) - [Repository Structure](#repository-structure) - [Development Setup](#development-setup) - [Creating New Applications](#creating-new-applications) - [Updating Existing Applications](#updating-existing-applications) - [Code Standards](#code-standards) - [Testing Your Changes](#testing-your-changes) - [Submitting a Pull Request](#submitting-a-pull-request) - [Troubleshooting](#troubleshooting) - [FAQ](#faq) --- ## Quick Start ### Setup Your Fork (First Time Only) ```bash # 1. Fork the repository on GitHub # Visit: https://github.com/community-scripts/ProxmoxVE # Click: Fork (top right) # 2. Clone your fork git clone https://github.com/YOUR_USERNAME/ProxmoxVE.git cd ProxmoxVE # 3. Run fork setup script (automatically configures everything) bash docs/contribution/setup-fork.sh --full # --full updates ct/, install/, vm/, docs/, misc/ links for fork testing # 4. Read the git workflow tips cat .git-setup-info ``` ### 60 Seconds to First Contribution ```bash # 1. Create feature branch git checkout -b add/my-awesome-app # 2. Create application scripts from templates cp docs/contribution/templates_ct/AppName.sh ct/myapp.sh cp docs/contribution/templates_install/AppName-install.sh install/myapp-install.sh # 3. Edit your scripts nano ct/myapp.sh nano install/myapp-install.sh # 4. Commit and push to your fork git add ct/myapp.sh install/myapp-install.sh git commit -m "feat: add MyApp container and install scripts" git push origin add/my-awesome-app # 5. Test via curl from your fork (GitHub may take 10-30 seconds) bash -c "$(curl -fsSL https://raw.githubusercontent.com/YOUR_USERNAME/ProxmoxVE/main/ct/myapp.sh)" # 6. Use cherry-pick to submit only your files (see Cherry-Pick section) # DO NOT submit the 600+ files modified by setup-fork.sh! # 7. Open Pull Request on GitHub # Create PR from: your-fork/add/my-awesome-app → community-scripts/ProxmoxVE/main # To add or change website metadata (description, logo, etc.), use the Report issue button on the script's page on the website. ``` **💡 Tip**: See `../FORK_SETUP.md` for detailed fork setup and troubleshooting --- ## Repository Structure ### Top-Level Organization ``` ProxmoxVE/ ├── ct/ # 🏗️ Container creation scripts (host-side) │ ├── pihole.sh │ ├── docker.sh │ └── ... (40+ applications) │ ├── install/ # 🛠️ Installation scripts (container-side) │ ├── pihole-install.sh │ ├── docker-install.sh │ └── ... (40+ applications) │ ├── vm/ # 💾 VM creation scripts │ ├── ubuntu2404-vm.sh │ ├── debian-vm.sh │ └── ... (15+ operating systems) │ ├── misc/ # 📦 Shared function libraries │ ├── build.func # Main orchestrator (3800+ lines) │ ├── core.func # UI/utilities │ ├── error_handler.func # Error management │ ├── tools.func # Tool installation │ ├── install.func # Container setup │ ├── cloud-init.func # VM configuration │ ├── api.func # Telemetry │ ├── alpine-install.func # Alpine-specific │ └── alpine-tools.func # Alpine tools │ ├── docs/ # 📚 Documentation │ ├── ct/DETAILED_GUIDE.md # Container script guide │ ├── install/DETAILED_GUIDE.md # Install script guide │ └── contribution/README.md # Contribution overview │ ├── tools/ # 🔧 Proxmox management tools │ └── pve/ │ └── README.md # Project overview ``` ### Naming Conventions ``` Container Script: ct/AppName.sh Installation Script: install/appname-install.sh Defaults: defaults/appname.vars Update Script: /usr/bin/update (inside container) Examples: ct/pihole.sh → install/pihole-install.sh ct/docker.sh → install/docker-install.sh ct/nextcloud-vm.sh → install/nextcloud-vm-install.sh ``` **Rules**: - Container script name: **Title Case** (PiHole, Docker, NextCloud) - Install script name: **lowercase** with **hyphens** (pihole-install, docker-install) - Must match: `ct/AppName.sh` ↔ `install/appname-install.sh` - Directory names: lowercase (always) - Variable names: lowercase (except APP constant) --- ## Development Setup ### Prerequisites 1. **Proxmox VE 8.0+** with at least: - 4 CPU cores - 8 GB RAM - 50 GB disk space - Ubuntu 20.04 / Debian 11+ on host 2. **Git** installed ```bash apt-get install -y git ``` 3. **Text Editor** (VS Code recommended) ```bash # VS Code extensions: # - Bash IDE # - Shellcheck # - Markdown All in One ``` ### Local Development Workflow #### Option A: Development Fork (Recommended) ```bash # 1. Fork on GitHub (one-time) # Visit: https://github.com/community-scripts/ProxmoxVE # Click: Fork # 2. Clone your fork git clone https://github.com/YOUR_USERNAME/ProxmoxVE.git cd ProxmoxVE # 3. Add upstream remote for updates git remote add upstream https://github.com/community-scripts/ProxmoxVE.git # 4. Create feature branch git checkout -b feat/add-myapp # 5. Make changes # ... edit files ... # 6. Keep fork updated git fetch upstream git rebase upstream/main # 7. Push and open PR git push origin feat/add-myapp ``` #### Option B: Testing on a Proxmox Host (still via curl) ```bash # 1. SSH into Proxmox host ssh root@192.168.1.100 # 2. Test via curl from your fork (CT script only) bash -c "$(curl -fsSL https://raw.githubusercontent.com/YOUR_USERNAME/ProxmoxVE/main/ct/myapp.sh)" # ⏱️ Wait 10-30 seconds after pushing - GitHub takes time to update ``` > **Note:** Do not edit URLs manually or run install scripts directly. The CT script calls the install script inside the container. #### Option C: Using Curl (Recommended for Real Testing) ```bash # Always test via curl from your fork (GitHub takes 10-30 seconds after push) git push origin feature/myapp bash -c "$(curl -fsSL https://raw.githubusercontent.com/YOUR_USERNAME/ProxmoxVE/main/ct/myapp.sh)" # This tests the actual GitHub URLs, not local files ``` #### Option D: Static Checks (Without Proxmox) ```bash # You can validate syntax and linting locally (limited) # Note: This does NOT replace real Proxmox testing # Run ShellCheck shellcheck ct/myapp.sh shellcheck install/myapp-install.sh # Syntax check bash -n ct/myapp.sh bash -n install/myapp-install.sh ``` --- ## Creating New Applications ### Step 1: Choose Your Template **For Simple Web Apps** (Node.js, Python, PHP): ```bash cp ct/example.sh ct/myapp.sh cp install/example-install.sh install/myapp-install.sh ``` **For Database Apps** (PostgreSQL, MariaDB, MongoDB): Use the standard templates and the database helpers from `tools.func` (no Docker). **For Alpine Linux Apps** (lightweight): ```bash # Use ct/alpine.sh as reference # Edit install script to use Alpine packages (apk not apt) ``` ### Step 2: Update Container Script **File**: `ct/myapp.sh` ```bash #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/YOUR_USERNAME/ProxmoxVE/main/misc/build.func) # Update these: APP="MyAwesomeApp" # Display name var_tags="category;tag2;tag3" # Max 3-4 tags var_cpu="2" # Realistic CPU cores var_ram="2048" # Min RAM needed (MB) var_disk="10" # Min disk (GB) var_os="debian" # OS type var_version="12" # OS version var_unprivileged="1" # Security (1=unprivileged) header_info "$APP" variables color catch_errors function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/myapp ]]; then msg_error "No ${APP} Installation Found!" exit fi if check_for_gh_release "myapp" "owner/repo"; then msg_info "Stopping Service" systemctl stop myapp msg_ok "Stopped Service" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "myapp" "owner/repo" "tarball" "latest" "/opt/myapp" # ... update logic (migrations, rebuilds, etc.) ... msg_info "Starting Service" systemctl start myapp 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}:PORT${CL}" ``` **Checklist**: - [ ] APP variable matches filename - [ ] var_tags semicolon-separated (no spaces) - [ ] Realistic CPU/RAM/disk values - [ ] update_script() implemented - [ ] Correct OS and version - [ ] Success message with access URL ### Step 3: Update Installation Script **File**: `install/myapp-install.sh` ```bash #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: YourUsername # License: MIT # Source: https://github.com/example/myapp 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 msg_ok "Installed Dependencies" NODE_VERSION="22" setup_nodejs fetch_and_deploy_gh_release "myapp" "owner/repo" "tarball" "latest" "/opt/myapp" motd_ssh customize cleanup_lxc ``` **Checklist**: - [ ] Functions loaded from `$FUNCTIONS_FILE_PATH` - [ ] All installation phases present (deps, tools, app, config, cleanup) - [ ] Using `$STD` for output suppression - [ ] Version file saved - [ ] Final cleanup with `cleanup_lxc` - [ ] No hardcoded versions (use GitHub API) ### Step 4: Create ASCII Header (Optional) **File**: `ct/headers/myapp` ``` ╔═══════════════════════════════════════╗ ║ ║ ║ 🎉 MyAwesomeApp 🎉 ║ ║ ║ ║ Your app is being installed... ║ ║ ║ ╚═══════════════════════════════════════╝ ``` Save in: `ct/headers/myapp` (no extension) ### Step 5: Create Defaults File (Optional) **File**: `defaults/myapp.vars` ```bash # Default configuration for MyAwesomeApp var_cpu=4 var_ram=4096 var_disk=15 var_hostname=myapp-container var_timezone=UTC ``` --- ## Updating Existing Applications ### Step 1: Identify What Changed ```bash # Check logs or GitHub releases curl -fsSL https://api.github.com/repos/app/repo/releases/latest | jq '.' # Review breaking changes # Update dependencies if needed ``` ### Step 2: Update Installation Script ```bash # Edit: install/existingapp-install.sh # 1. Update version (if hardcoded) RELEASE="2.0.0" # 2. Update package dependencies (if any changed) $STD apt-get install -y newdependency # 3. Update configuration (if format changed) # Update sed replacements or config files # 4. Test thoroughly before committing ``` ### Step 3: The Standard Update Pattern The `update_script()` function in `ct/appname.sh` should follow a robust pattern: 1. **Check for updates**: Use `check_for_gh_release` to skip logic if no new version exists. 2. **Stop services**: Stop all relevant services (`systemctl stop appname`). 3. **Backup existing installation**: Move the old folder (e.g., `mv /opt/app /opt/app_bak`). 4. **Deploy new version**: Use `CLEAN_INSTALL=1 fetch_and_deploy_gh_release`. 5. **Restore configuration**: Copy `.env` or config files back from the backup. 6. **Rebuild/Migrate**: Run `npm install`, `composer install`, or DB migrations. 7. **Start services**: Restart services and cleanup the backup. **Example from `ct/bookstack.sh`**: ```bash function update_script() { if check_for_gh_release "bookstack" "BookStackApp/BookStack"; then msg_info "Stopping Services" systemctl stop apache2 msg_info "Backing up data" mv /opt/bookstack /opt/bookstack-backup fetch_and_deploy_gh_release "bookstack" "BookStackApp/BookStack" "tarball" msg_info "Restoring backup" cp /opt/bookstack-backup/.env /opt/bookstack/.env # ... restore uploads ... msg_info "Configuring" cd /opt/bookstack $STD composer install --no-dev $STD php artisan migrate --force systemctl start apache2 rm -rf /opt/bookstack-backup msg_ok "Updated successfully!" fi } ``` --- ## Code Standards ### Bash Style Guide #### Variable Naming ```bash # ✅ Good APP="MyApp" # Constants (UPPERCASE) var_cpu="2" # Configuration (var_*) container_id="100" # Local variables (lowercase) DB_PASSWORD="secret" # Environment-like (UPPERCASE) # ❌ Bad myapp="MyApp" # Inconsistent VAR_CPU="2" # Wrong convention containerid="100" # Unclear purpose ``` #### Function Naming ```bash # ✅ Good function setup_database() { } # Descriptive function check_version() { } # Verb-noun pattern function install_dependencies() { } # Clear action # ❌ Bad function setup() { } # Too vague function db_setup() { } # Inconsistent pattern function x() { } # Cryptic ``` #### Quoting ```bash # ✅ Good echo "${APP}" # Always quote variables if [[ "$var" == "value" ]]; then # Use [[ ]] for conditionals echo "Using $var in string" # Variables in double quotes # ❌ Bad echo $APP # Unquoted variables if [ "$var" = "value" ]; then # Use [[ ]] instead echo 'Using $var in string' # Single quotes prevent expansion ``` #### Command Formatting ```bash # ✅ Good: Multiline for readability $STD apt-get install -y \ package1 \ package2 \ package3 # ✅ Good: Complex commands with variables if ! wget -q "https://example.com/${file}"; then msg_error "Failed to download" exit 1 fi # ❌ Bad: Too long on one line $STD apt-get install -y package1 package2 package3 package4 package5 package6 # ❌ Bad: No error checking wget https://example.com/file ``` #### Error Handling ```bash # ✅ Good: Check critical commands if ! some_command; then msg_error "Command failed" exit 1 fi # ✅ Good: Use catch_errors for automatic trapping catch_errors # ❌ Bad: Silently ignore failures some_command || true some_command 2>/dev/null # ❌ Bad: Unclear what failed if ! (cmd1 && cmd2 && cmd3); then msg_error "Something failed" fi ``` ### Documentation Standards #### Header Comments ```bash #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: YourUsername # Co-Author: AnotherAuthor (for collaborative work) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/app/repo # Description: Brief description of what this script does ``` #### Inline Comments ```bash # ✅ Good: Explain WHY, not WHAT # Use alphanumeric only to avoid shell escaping issues DB_PASS=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | head -c13) # ✅ Good: Comment complex logic # Detect if running Alpine vs Debian for proper package manager if grep -qi 'alpine' /etc/os-release; then PKG_MGR="apk" else PKG_MGR="apt" fi # ❌ Bad: Comment obvious code # Set the variable var="value" # ❌ Bad: Outdated comments # TODO: Fix this (written 2 years ago, not fixed) ``` ### File Organization ```bash #!/usr/bin/env bash # [1] Shebang (first line) # Copyright & Metadata # [2] Comments # [3] Blank line # Load functions # [4] Import section source <(curl -fsSL ...) # [5] Blank line # Configuration # [6] Variables/Config APP="MyApp" var_cpu="2" # [7] Blank line # Initialization # [8] Setup header_info "$APP" variables color catch_errors # [9] Blank line # Functions # [10] Function definitions function update_script() { } function custom_setup() { } # [11] Blank line # Main execution # [12] Script logic start build_container ``` --- ## Testing Your Changes ### Pre-Submission Testing #### 1. Syntax Check ```bash # Verify bash syntax bash -n ct/myapp.sh bash -n install/myapp-install.sh # If no output: ✅ Syntax is valid # If error output: ❌ Fix syntax before submitting ``` #### 2. ShellCheck Static Analysis ```bash # Install ShellCheck apt-get install -y shellcheck # Check scripts shellcheck ct/myapp.sh shellcheck install/myapp-install.sh # Review warnings and fix if applicable # Some warnings can be intentional (use # shellcheck disable=...) ``` #### 3. Real Proxmox Testing ```bash # Best: Test on actual Proxmox system # 1. SSH into Proxmox host ssh root@YOUR_PROXMOX_IP # 2. Test via curl from your fork (CT script only) bash -c "$(curl -fsSL https://raw.githubusercontent.com/YOUR_USERNAME/ProxmoxVE/main/ct/myapp.sh)" # ⏱️ Wait 10-30 seconds after pushing - GitHub takes time to update # 3. Test interaction: # - Select installation mode # - Confirm settings # - Monitor installation # 4. Verify container created pct list | grep myapp # 5. Log into container and verify app pct exec 100 bash ``` #### 4. Edge Case Testing ```bash # Test with different settings: # Test 1: Advanced (19-step) installation # When prompted: Select "2" for Advanced # Test 2: User Defaults # Before running: Create ~/.community-scripts/default.vars # When prompted: Select "3" for User Defaults # Test 3: Error handling # Simulate network outage (block internet) # Verify script handles gracefully # Test 4: Update function # Create initial container (via curl from fork) # Wait for new release # Test update: bash -c "$(curl -fsSL https://raw.githubusercontent.com/YOUR_USERNAME/ProxmoxVE/main/ct/myapp.sh)" # Verify it detects and applies update ``` ### Testing Checklist Before submitting PR: ```bash # Code quality - [ ] Syntax: bash -n passes - [ ] ShellCheck: No critical warnings - [ ] Naming: Follows conventions - [ ] Formatting: Consistent indentation # Functionality - [ ] Container creation: Successful - [ ] Installation: Completes without errors - [ ] Access URL: Works and app responds - [ ] Update function: Detects new versions - [ ] Cleanup: No temporary files left # Documentation - [ ] Copyright header present - [ ] App name matches filenames - [ ] Default values realistic - [ ] Success message clear and helpful # Compatibility - [ ] Works on Debian 12 - [ ] Works on Ubuntu 22.04 - [ ] (Optional) Works on Alpine 3.20 ``` --- ## Submitting a Pull Request ### Step 1: Prepare Your Branch ```bash # Update with latest changes git fetch upstream git rebase upstream/main # If conflicts occur: git rebase --abort # Resolve conflicts manually then: git add . git rebase --continue ``` ### Step 2: Push Your Changes ```bash git push origin feat/add-myapp # If already pushed: git push origin feat/add-myapp --force-with-lease ``` ### Step 3: Create Pull Request on GitHub **Visit**: https://github.com/community-scripts/ProxmoxVE/pulls **Click**: "New Pull Request" **Select**: `community-scripts:main` ← `YOUR_USERNAME:feat/myapp` ### Step 4: Fill PR Description Use this template: ```markdown ## Description Brief description of what this PR adds/fixes ## Type of Change - [ ] New application (ct/AppName.sh + install/appname-install.sh) - [ ] Update existing application - [ ] Bug fix - [ ] Documentation update - [ ] Other: **\_\_\_** ## Testing - [ ] Tested on Proxmox VE 8.x - [ ] Container creation successful - [ ] Application installation successful - [ ] Application is accessible at URL - [ ] Update function works (if applicable) - [ ] No temporary files left after installation ## Application Details (for new apps only) - **App Name**: MyApp - **Source**: https://github.com/app/repo - **Default OS**: Debian 12 - **Recommended Resources**: 2 CPU, 2GB RAM, 10GB Disk - **Tags**: category;tag2;tag3 - **Access URL**: http://IP:PORT/path ## Checklist - [ ] My code follows the style guidelines - [ ] I have performed a self-review - [ ] I have tested the script via curl from my fork (after git push) - [ ] GitHub had time to update (waited 10-30 seconds) - [ ] ShellCheck shows no critical warnings - [ ] Documentation is accurate and complete - [ ] I have added/updated relevant documentation ``` ### Step 5: Respond to Review Comments **Maintainers may request changes**: - Fix syntax/style issues - Add better error handling - Optimize resource usage - Update documentation **To address feedback**: ```bash # Make requested changes git add . git commit -m "Address review feedback: ..." git push origin feat/add-myapp # PR automatically updates! # No need to create new PR ``` ### Step 6: Celebrate! 🎉 Once merged, your contribution will be part of ProxmoxVE and available to all users! --- ## Troubleshooting ### "Repository not found" when cloning ```bash # Check your fork exists # Visit: https://github.com/YOUR_USERNAME/ProxmoxVE # If not there: Click "Fork" on original repo first ``` ### "Permission denied" when pushing ```bash # Setup SSH key ssh-keygen -t ed25519 -C "your_email@example.com" cat ~/.ssh/id_ed25519.pub # Copy this # Add to GitHub: Settings → SSH Keys → New Key # Or use HTTPS with token: git remote set-url origin https://YOUR_TOKEN@github.com/YOUR_USERNAME/ProxmoxVE.git ``` ### Script syntax errors ```bash # Use ShellCheck to identify issues shellcheck install/myapp-install.sh # Common issues: # - Unmatched quotes: "string' or 'string" # - Missing semicolons before then: if [...]; then # - Wrong quoting: echo $VAR instead of echo "${VAR}" ``` ### Container creation fails immediately ```bash # 1. Check Proxmox resources free -h # Check RAM df -h # Check disk space pct list # Check CTID availability # 2. Check script URL # Make sure curl -s in script points to your fork # 3. Review errors # Run with verbose: bash -x ct/myapp.sh ``` ### App not accessible after creation ```bash # 1. Verify container running pct list pct status CTID # 2. Check if service running inside pct exec CTID systemctl status myapp # 3. Check firewall # Proxmox host: iptables -L # Container: iptables -L # 4. Verify listening port pct exec CTID netstat -tlnp | grep LISTEN ``` --- ## FAQ ### Q: Do I need to be a Bash expert? **A**: No! The codebase has many examples you can copy. Most contributions are straightforward script creation following the established patterns. ### Q: Can I add a new application that's not open source? **A**: No. ProxmoxVE focuses on open-source applications (GPL, MIT, Apache, etc.). Closed-source applications won't be accepted. ### Q: How long until my PR is reviewed? **A**: Maintainers are volunteers. Reviews typically happen within 1-2 weeks. Complex changes may take longer. ### Q: Can I test without a Proxmox system? **A**: Partially. You can verify syntax and ShellCheck compliance locally, but real container testing requires Proxmox. Consider using: - Proxmox in a VM (VirtualBox/KVM) - Test instances on Hetzner/DigitalOcean - Ask maintainers to test for you ### Q: My update function is very complex - is that OK? **A**: Yes! Update functions can be complex if needed. Just ensure: - Backup user data before updating - Restore user data after update - Test thoroughly before submitting - Add clear comments explaining logic ### Q: Can I add new dependencies to build.func? **A**: Generally no. build.func is the orchestrator and should remain stable. New functions should go in: - `tools.func` - Tool installation - `core.func` - Utility functions - `install.func` - Container setup Ask in an issue first if you're unsure. ### Q: What if the application has many configuration options? **A**: You have options: **Option 1**: Use Advanced mode (19-step wizard) ```bash # Extend advanced_settings() if app needs special vars ``` **Option 2**: Create custom setup menu ```bash function custom_config() { OPTION=$(whiptail --inputbox "Enter database name:" 8 60) # ... use $OPTION in installation } ``` **Option 3**: Leave as defaults + documentation ```bash # In success message: echo "Edit /opt/myapp/config.json to customize settings" ``` ### Q: Can I contribute Windows/macOS/ARM support? **A**: - **Windows**: Not planned (ProxmoxVE is Linux/Proxmox focused) - **macOS**: Can contribute Docker-based alternatives - **ARM**: Yes! Many apps work on ARM. Add to vm/pimox-\*.sh scripts --- ## Getting Help ### Resources - **Documentation**: `/docs` directory and wikis - **Function Reference**: `/misc/*.md` wiki files - **Examples**: Look at similar applications in `/ct` and `/install` - **GitHub Issues**: https://github.com/community-scripts/ProxmoxVE/issues - **Discussions**: https://github.com/community-scripts/ProxmoxVE/discussions ### Ask Questions 1. **Check existing issues** - Your question may be answered 2. **Search documentation** - See `/docs` and `/misc/*.md` 3. **Ask in Discussions** - For general questions 4. **Open an Issue** - For bugs or specific problems ### Report Bugs When reporting bugs, include: - Which application - What happened (error message) - What you expected - Your Proxmox version - Container OS and version Example: ``` Title: pihole-install.sh fails on Alpine 3.20 Description: Installation fails with error: "PHP-FPM not found" Expected: PiHole should install successfully Environment: - Proxmox VE 8.2 - Alpine 3.20 - Container CTID 110 Error Output: [ERROR] in line 42: exit code 127: while executing command php-fpm --start ``` --- ## Contribution Statistics **ProxmoxVE by the Numbers**: - 🎯 40+ applications supported - 👥 100+ contributors - 📊 10,000+ GitHub stars - 🚀 50+ releases - 📈 100,000+ downloads/month **Your contribution makes a difference!** --- ## Code of Conduct By contributing, you agree to: - ✅ Be respectful and inclusive - ✅ Follow the style guidelines - ✅ Test your changes thoroughly - ✅ Provide clear commit messages - ✅ Respond to review feedback --- **Ready to contribute?** Start with the [Quick Start](#quick-start) section! **Questions?** Open an issue or start a discussion on GitHub. **Thank you for your contribution!** 🙏 ================================================ FILE: docs/contribution/HELPER_FUNCTIONS.md ================================================ # 🛠️ Helper Functions Reference **Quick reference for all helper functions available in `tools.func`** > These functions are automatically available in install scripts via `$FUNCTIONS_FILE_PATH` --- ## 📋 Table of Contents - [Scripts to Watch](#scripts-to-watch) - [Runtime & Language Setup](#runtime--language-setup) - [Database Setup](#database-setup) - [GitHub Release Helpers](#github-release-helpers) - [Tools & Utilities](#tools--utilities) - [SSL/TLS](#ssltls) - [Utility Functions](#utility-functions) - [Package Management](#package-management) --- ## 📚 Scripts to Watch **Learn from real, well-implemented scripts. Each app requires TWO files that work together:** | File | Location | Purpose | | ------------------ | ---------------------------- | ------------------------------------------------------------------------ | | **CT Script** | `ct/appname.sh` | Runs on **Proxmox host** - creates container, contains `update_script()` | | **Install Script** | `install/appname-install.sh` | Runs **inside container** - installs and configures the app | > ⚠️ **Both files are ALWAYS required!** The CT script calls the install script automatically during container creation. Install scripts are **not** run directly by users; they are invoked by the CT script inside the container. ### Node.js + PostgreSQL **Koel** - Music streaming with PHP + Node.js + PostgreSQL | File | Link | | ----------------- | -------------------------------------------------------- | | CT (update logic) | [ct/koel.sh](../../ct/koel.sh) | | Install | [install/koel-install.sh](../../install/koel-install.sh) | **Actual Budget** - Finance app with npm global install | File | Link | | ----------------- | ------------------------------------------------------------------------ | | CT (update logic) | [ct/actualbudget.sh](../../ct/actualbudget.sh) | | Install | [install/actualbudget-install.sh](../../install/actualbudget-install.sh) | ### Python + uv **MeTube** - YouTube downloader with Python uv + Node.js + Deno | File | Link | | ----------------- | ------------------------------------------------------------ | | CT (update logic) | [ct/metube.sh](../../ct/metube.sh) | | Install | [install/metube-install.sh](../../install/metube-install.sh) | **Endurain** - Fitness tracker with Python uv + PostgreSQL/PostGIS | File | Link | | ----------------- | ---------------------------------------------------------------- | | CT (update logic) | [ct/endurain.sh](../../ct/endurain.sh) | | Install | [install/endurain-install.sh](../../install/endurain-install.sh) | ### Java + Gradle **BookLore** - Book management with Java 21 + Gradle + MariaDB + Nginx | File | Link | | ----------------- | -------------------------------------------------------------- | | CT (update logic) | [ct/booklore.sh](../../ct/booklore.sh) | | Install | [install/booklore-install.sh](../../install/booklore-install.sh) | ### Pnpm + Meilisearch **KaraKeep** - Bookmark manager with Pnpm + Meilisearch + Puppeteer | File | Link | | ----------------- | -------------------------------------------------------------- | | CT (update logic) | [ct/karakeep.sh](../../ct/karakeep.sh) | | Install | [install/karakeep-install.sh](../../install/karakeep-install.sh) | ### PHP + MariaDB/MySQL **Wallabag** - Read-it-later with PHP + MariaDB + Redis + Nginx | File | Link | | ----------------- | ---------------------------------------------------------------- | | CT (update logic) | [ct/wallabag.sh](../../ct/wallabag.sh) | | Install | [install/wallabag-install.sh](../../install/wallabag-install.sh) | **InvoiceNinja** - Invoicing with PHP + MariaDB + Supervisor | File | Link | | ----------------- | ------------------------------------------------------------------------ | | CT (update logic) | [ct/invoiceninja.sh](../../ct/invoiceninja.sh) | | Install | [install/invoiceninja-install.sh](../../install/invoiceninja-install.sh) | **BookStack** - Wiki/Docs with PHP + MariaDB + Apache | File | Link | | ----------------- | ------------------------------------------------------------------ | | CT (update logic) | [ct/bookstack.sh](../../ct/bookstack.sh) | | Install | [install/bookstack-install.sh](../../install/bookstack-install.sh) | ### PHP + SQLite (Simple) **Speedtest Tracker** - Speedtest with PHP + SQLite + Nginx | File | Link | | ----------------- | ---------------------------------------------------------------------------------- | | CT (update logic) | [ct/speedtest-tracker.sh](../../ct/speedtest-tracker.sh) | | Install | [install/speedtest-tracker-install.sh](../../install/speedtest-tracker-install.sh) | --- ## Runtime & Language Setup ### `setup_nodejs` Install Node.js from NodeSource repository. ```bash # Default (Node.js 24) setup_nodejs # Specific version NODE_VERSION="20" setup_nodejs NODE_VERSION="22" setup_nodejs NODE_VERSION="24" setup_nodejs ``` ### `setup_go` Install Go programming language (latest stable). ```bash setup_go # Use in script setup_go cd /opt/myapp $STD go build -o myapp . ``` ### `setup_rust` Install Rust via rustup. ```bash setup_rust # Use in script setup_rust source "$HOME/.cargo/env" $STD cargo build --release ``` ### `setup_uv` Install Python uv package manager (fast pip/venv replacement). ```bash # Default setup_uv # Install a specific Python version PYTHON_VERSION="3.12" setup_uv # Use in script setup_uv cd /opt/myapp $STD uv sync --locked ``` ### `setup_ruby` Install Ruby from official repositories. ```bash setup_ruby ``` ### `setup_php` Install PHP with configurable modules and FPM/Apache support. ```bash # Basic PHP setup_php # Full configuration PHP_VERSION="8.4" \ PHP_MODULE="mysqli,gd,curl,mbstring,xml,zip,ldap" \ PHP_FPM="YES" \ PHP_APACHE="YES" \ setup_php ``` **Environment Variables:** | Variable | Default | Description | | ------------- | ------- | ------------------------------- | | `PHP_VERSION` | `8.4` | PHP version to install | | `PHP_MODULE` | `""` | Comma-separated list of modules | | `PHP_FPM` | `NO` | Install PHP-FPM | | `PHP_APACHE` | `NO` | Install Apache module | ### `setup_composer` Install PHP Composer package manager. ```bash setup_php setup_composer # Use in script $STD composer install --no-dev ``` ### `setup_java` Install Java (OpenJDK). ```bash # Default (Java 21) setup_java # Specific version JAVA_VERSION="17" setup_java JAVA_VERSION="21" setup_java ``` --- ## Database Setup ### `setup_mariadb` Install MariaDB server. ```bash setup_mariadb ``` ### `setup_mariadb_db` Create a MariaDB database and user. Sets `$MARIADB_DB_PASS` with the generated password. ```bash setup_mariadb MARIADB_DB_NAME="myapp_db" MARIADB_DB_USER="myapp_user" setup_mariadb_db # After calling, these variables are available: # $MARIADB_DB_NAME - Database name # $MARIADB_DB_USER - Database user # $MARIADB_DB_PASS - Generated password (saved to ~/[appname].creds) ``` ### `setup_mysql` Install MySQL server. ```bash setup_mysql ``` ### `setup_postgresql` Install PostgreSQL server. ```bash # Default (PostgreSQL 16) setup_postgresql # Specific version PG_VERSION="16" setup_postgresql PG_VERSION="16" setup_postgresql ``` ### `setup_postgresql_db` Create a PostgreSQL database and user. Sets `$PG_DB_PASS` with the generated password. ```bash PG_VERSION="17" setup_postgresql PG_DB_NAME="myapp_db" PG_DB_USER="myapp_user" setup_postgresql_db # After calling, these variables are available: # $PG_DB_NAME - Database name # $PG_DB_USER - Database user # $PG_DB_PASS - Generated password (saved to ~/[appname].creds) ``` ### `setup_mongodb` Install MongoDB server. ```bash setup_mongodb ``` ### `setup_clickhouse` Install ClickHouse analytics database. ```bash setup_clickhouse ``` --- ## Advanced Repository Management ### `setup_deb822_repo` The modern standard (Debian 12+) for adding external repositories. Automatically handles GPG keys and sources. ```bash setup_deb822_repo \ "nodejs" \ "https://deb.nodesource.com/gpgkey/nodesource.gpg.key" \ "https://deb.nodesource.com/node_22.x" \ "bookworm" \ "main" ``` ### `prepare_repository_setup` A high-level helper that performs three critical tasks before adding a new repo: 1. Cleans up old repo files matching the names provided. 2. Removes old GPG keyrings from all standard locations. 3. Ensures APT is in a working state (fixes locks, runs update). ```bash # Clean up old mysql/mariadb artifacts before setup prepare_repository_setup "mariadb" "mysql" ``` ### `cleanup_tool_keyrings` Force-removes GPG keys for specific tools from `/usr/share/keyrings/`, `/etc/apt/keyrings/`, and `/etc/apt/trusted.gpg.d/`. ```bash cleanup_tool_keyrings "docker" "kubernetes" ``` --- ## GitHub Release Helpers > **Note**: `fetch_and_deploy_gh_release` is the **preferred method** for downloading GitHub releases. It handles version tracking automatically. Only use `get_latest_github_release` if you need the version number separately. ### `fetch_and_deploy_gh_release` **Primary method** for downloading and extracting GitHub releases. Handles version tracking automatically. ```bash # Basic usage - downloads tarball to /opt/appname fetch_and_deploy_gh_release "appname" "owner/repo" # With explicit parameters fetch_and_deploy_gh_release "appname" "owner/repo" "tarball" "latest" "/opt/appname" # Pre-built release with specific asset pattern fetch_and_deploy_gh_release "koel" "koel/koel" "prebuild" "latest" "/opt/koel" "koel-*.tar.gz" # Clean install (removes old directory first) - used in update_script CLEAN_INSTALL=1 fetch_and_deploy_gh_release "appname" "owner/repo" "tarball" "latest" "/opt/appname" ``` **Parameters:** | Parameter | Default | Description | | --------------- | ------------- | ----------------------------------------------------------------- | | `name` | required | App name (for version tracking) | | `repo` | required | GitHub repo (`owner/repo`) | | `type` | `tarball` | Release type: `tarball`, `zipball`, `prebuild`, `binary` | | `version` | `latest` | Version tag or `latest` | | `dest` | `/opt/[name]` | Destination directory | | `asset_pattern` | `""` | For `prebuild`: glob pattern to match asset (e.g. `app-*.tar.gz`) | **Environment Variables:** | Variable | Description | | ----------------- | ------------------------------------------------------------ | | `CLEAN_INSTALL=1` | Remove destination directory before extracting (for updates) | ### `check_for_gh_release` Check if a newer version is available. Returns 0 if update needed, 1 if already at latest. **Use in `update_script()` function.** ```bash # In update_script() function in ct/appname.sh if check_for_gh_release "appname" "owner/repo"; then msg_info "Updating..." # Stop services, backup, update, restore, start CLEAN_INSTALL=1 fetch_and_deploy_gh_release "appname" "owner/repo" msg_ok "Updated successfully!" fi ``` ### `get_latest_github_release` Get the latest release version from a GitHub repository. **Only use if you need the version number separately** (e.g., for manual download or display). ```bash RELEASE=$(get_latest_github_release "owner/repo") echo "Latest version: $RELEASE" ``` --- ## Tools & Utilities ### `setup_meilisearch` Install Meilisearch, a lightning-fast search engine. ```bash setup_meilisearch # Use in script $STD php artisan scout:sync-index-settings ``` ### `setup_yq` Install yq YAML processor. ```bash setup_yq # Use in script yq '.server.port = 8080' -i config.yaml ```` ### `setup_ffmpeg` Install FFmpeg with common codecs. ```bash setup_ffmpeg ``` ### `setup_hwaccel` Setup GPU hardware acceleration (Intel/AMD/NVIDIA). ```bash # Only runs if GPU passthrough is detected (/dev/dri, /dev/nvidia0, /dev/kfd) setup_hwaccel ``` ### `setup_imagemagick` Install ImageMagick 7 from source. ```bash setup_imagemagick ``` ### `setup_docker` Install Docker Engine. ```bash setup_docker ``` ### `setup_adminer` Install Adminer for database management. ```bash setup_mariadb setup_adminer # Access at http://IP/adminer ``` --- ## SSL/TLS ### `create_self_signed_cert` Create a self-signed SSL certificate. ```bash create_self_signed_cert # Creates files at: # /etc/ssl/[appname]/[appname].key # /etc/ssl/[appname]/[appname].crt ``` --- ## Utility Functions ### `verify_tool_version` Validate that the installed major version matches the expected version. Useful during upgrades or troubleshooting. ```bash # Verify Node.js is version 22 verify_tool_version "nodejs" "22" "$(node -v | grep -oP '^v\K[0-9]+')" ``` ### `get_lxc_ip` Set the `$LOCAL_IP` variable with the container's IP address. ```bash get_lxc_ip echo "Container IP: $LOCAL_IP" # Use in config files sed -i "s/localhost/$LOCAL_IP/g" /opt/myapp/config.yaml ``` ### `ensure_dependencies` Ensure packages are installed (installs if missing). ```bash ensure_dependencies "jq" "unzip" "curl" ``` ### `msg_info` / `msg_ok` / `msg_error` / `msg_warn` Display formatted messages. ```bash msg_info "Installing application..." # ... do work ... msg_ok "Installation complete" msg_warn "Optional feature not available" msg_error "Installation failed" ``` --- ## Package Management ### `cleanup_lxc` Final cleanup function - call at end of install script. ```bash # At the end of your install script motd_ssh customize cleanup_lxc # Handles autoremove, autoclean, cache cleanup ``` ### `install_packages_with_retry` Install packages with automatic retry on failure. ```bash install_packages_with_retry "package1" "package2" "package3" ``` ### `prepare_repository_setup` Prepare system for adding new repositories (cleanup old repos, keyrings). ```bash prepare_repository_setup "mariadb" "mysql" ``` --- ## Complete Examples ### Example 1: Node.js App with PostgreSQL (install script) ```bash #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: YourUsername # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/example/myapp 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 runtimes and databases FIRST NODE_VERSION="22" setup_nodejs PG_VERSION="16" setup_postgresql PG_DB_NAME="myapp" PG_DB_USER="myapp" setup_postgresql_db get_lxc_ip # Download app using fetch_and_deploy (handles version tracking) fetch_and_deploy_gh_release "myapp" "example/myapp" "tarball" "latest" "/opt/myapp" msg_info "Setting up MyApp" cd /opt/myapp $STD npm ci --production msg_ok "Setup MyApp" msg_info "Configuring MyApp" cat </opt/myapp/.env DATABASE_URL=postgresql://${PG_DB_USER}:${PG_DB_PASS}@localhost/${PG_DB_NAME} HOST=${LOCAL_IP} PORT=3000 EOF msg_ok "Configured MyApp" msg_info "Creating Service" cat </etc/systemd/system/myapp.service [Unit] Description=MyApp After=network.target postgresql.service [Service] Type=simple WorkingDirectory=/opt/myapp ExecStart=/usr/bin/node /opt/myapp/server.js Restart=on-failure [Install] WantedBy=multi-user.target EOF systemctl enable -q --now myapp msg_ok "Created Service" motd_ssh customize cleanup_lxc ``` ### Example 2: Matching Container Script (ct script) ```bash #!/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: YourUsername # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/example/myapp APP="MyApp" var_tags="${var_tags:-webapp}" var_cpu="${var_cpu:-2}" 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 /opt/myapp ]]; then msg_error "No ${APP} Installation Found!" exit fi # check_for_gh_release returns true if update available if check_for_gh_release "myapp" "example/myapp"; then msg_info "Stopping Service" systemctl stop myapp msg_ok "Stopped Service" msg_info "Creating Backup" cp /opt/myapp/.env /tmp/myapp_env.bak msg_ok "Created Backup" # CLEAN_INSTALL=1 removes old dir before extracting CLEAN_INSTALL=1 fetch_and_deploy_gh_release "myapp" "example/myapp" "tarball" "latest" "/opt/myapp" msg_info "Restoring Config & Rebuilding" cp /tmp/myapp_env.bak /opt/myapp/.env rm /tmp/myapp_env.bak cd /opt/myapp $STD npm ci --production msg_ok "Restored Config & Rebuilt" msg_info "Starting Service" systemctl start myapp 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}" ``` ### Example 3: PHP App with MariaDB (install script) ```bash #!/usr/bin/env bash 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" # PHP with FPM and common modules PHP_VERSION="8.4" PHP_FPM="YES" PHP_MODULE="bcmath,curl,gd,intl,mbstring,mysql,xml,zip" setup_php setup_composer setup_mariadb MARIADB_DB_NAME="myapp" MARIADB_DB_USER="myapp" setup_mariadb_db get_lxc_ip # Download pre-built release (with asset pattern) fetch_and_deploy_gh_release "myapp" "example/myapp" "prebuild" "latest" "/opt/myapp" "myapp-*.tar.gz" msg_info "Configuring MyApp" cd /opt/myapp cp .env.example .env sed -i "s|APP_URL=.*|APP_URL=http://${LOCAL_IP}|" .env sed -i "s|DB_DATABASE=.*|DB_DATABASE=${MARIADB_DB_NAME}|" .env sed -i "s|DB_USERNAME=.*|DB_USERNAME=${MARIADB_DB_USER}|" .env sed -i "s|DB_PASSWORD=.*|DB_PASSWORD=${MARIADB_DB_PASS}|" .env $STD composer install --no-dev --no-interaction $STD php artisan key:generate --force $STD php artisan migrate --force chown -R www-data:www-data /opt/myapp msg_ok "Configured MyApp" # ... nginx config, service creation ... motd_ssh customize cleanup_lxc ``` ================================================ FILE: docs/contribution/README.md ================================================ # 🤝 Contributing to ProxmoxVE Complete guide to contributing to the ProxmoxVE project - from your first fork to submitting your pull request. --- ## 📋 Table of Contents - [Quick Start](#quick-start) - [Setting Up Your Fork](#setting-up-your-fork) - [Coding Standards](#coding-standards) - [Code Audit](#code-audit) - [Guides & Resources](#guides--resources) - [FAQ](#faq) --- ## 🚀 Quick Start ### 60 Seconds to Contributing (Development) When developing and testing **in your fork**: ```bash # 1. Fork on GitHub # Visit: https://github.com/community-scripts/ProxmoxVE → Fork (top right) # 2. Clone your fork git clone https://github.com/YOUR_USERNAME/ProxmoxVE.git cd ProxmoxVE # 3. Auto-configure your fork (IMPORTANT - updates all links!) bash docs/contribution/setup-fork.sh --full # 4. Create a feature branch git checkout -b feature/my-awesome-app # 5. Read the guides cat docs/README.md # Documentation overview cat docs/ct/DETAILED_GUIDE.md # For container scripts cat docs/install/DETAILED_GUIDE.md # For install scripts # 6. Create your contribution cp docs/contribution/templates_ct/AppName.sh ct/myapp.sh cp docs/contribution/templates_install/AppName-install.sh install/myapp-install.sh # ... edit files ... # 7. Push to your fork and test via GitHub git push origin feature/my-awesome-app bash -c "$(curl -fsSL https://raw.githubusercontent.com/YOUR_USERNAME/ProxmoxVE/main/ct/myapp.sh)" # ⏱️ GitHub may take 10-30 seconds to update files - be patient! # 8. Request website metadata via the website # Go to the script's page on the website, use the "Report issue" button — it will guide you. # 9. No direct install-script test # Install scripts are executed by the CT script inside the container # 10. Commit ONLY your new files (see Cherry-Pick section below!) git add ct/myapp.sh install/myapp-install.sh git commit -m "feat: add MyApp container and install scripts" git push origin feature/my-awesome-app # 11. Create Pull Request on GitHub ``` ⚠️ **IMPORTANT: After setup-fork.sh, many files are modified!** See the **Cherry-Pick: Submitting Only Your Changes** section below to learn how to push ONLY your 2 files instead of 600+ modified files! ### How Users Run Scripts (After Merged) Once your script is merged to the main repository, users download and run it from GitHub like this: ```bash # ✅ Users run from GitHub (normal usage after PR merged) bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/myapp.sh)" # Install scripts are called by the CT script and are not run directly by users ``` ### Development vs. Production Execution **During Development (you, in your fork):** ```bash # You MUST test via curl from your GitHub fork (not local files!) bash -c "$(curl -fsSL https://raw.githubusercontent.com/YOUR_USERNAME/ProxmoxVE/main/ct/myapp.sh)" # The script's curl commands are updated by setup-fork.sh to point to YOUR fork # This ensures you're testing your actual changes # ⏱️ Wait 10-30 seconds after pushing - GitHub updates slowly ``` **After Merge (users, from GitHub):** ```bash # Users download the script from upstream via curl bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/myapp.sh)" # The script's curl commands now point back to upstream (community-scripts) # This is the stable, tested version ``` **Summary:** - **Development**: Push to fork, test via curl → setup-fork.sh changes curl URLs to your fork - **Production**: curl | bash from upstream → curl URLs point to community-scripts repo --- ## 🍴 Setting Up Your Fork ### Automatic Setup (Recommended) When you clone your fork, run the setup script to automatically configure everything: ```bash bash docs/contribution/setup-fork.sh --full ``` **What it does:** - Auto-detects your GitHub username from git config - Auto-detects your fork repository name - Updates **ALL** hardcoded links to point to your fork instead of the main repo (`--full`) - Creates `.git-setup-info` with your configuration - Allows you to develop and test independently in your fork **Why this matters:** Without running this script, all links in your fork will still point to the upstream repository (community-scripts). This is a problem when testing because: - Installation links will pull from upstream, not your fork - Updates will target the wrong repository - Your contributions won't be properly tested **After running setup-fork.sh:** Your fork is fully configured and ready to develop. You can: - Push changes to your fork - Test via curl: `bash -c "$(curl -fsSL https://raw.githubusercontent.com/YOUR_USERNAME/ProxmoxVE/main/ct/myapp.sh)"` - All links will reference your fork for development - ⏱️ Wait 10-30 seconds after pushing - GitHub takes time to update - Commit and push with confidence - Create a PR to merge into upstream **See**: [FORK_SETUP.md](FORK_SETUP.md) for detailed instructions ### Manual Setup If the script doesn't work, manually configure: ```bash # Set git user git config user.name "Your Name" git config user.email "your.email@example.com" # Add upstream remote for syncing with main repo git remote add upstream https://github.com/community-scripts/ProxmoxVE.git # Verify remotes git remote -v # Should show: origin (your fork) and upstream (main repo) ``` --- ## 📖 Coding Standards All scripts and configurations must follow our coding standards to ensure consistency and quality. ### Available Guides - **[CONTRIBUTING.md](CONTRIBUTING.md)** - Essential coding standards and best practices - **[CODE-AUDIT.md](CODE-AUDIT.md)** - Code review checklist and audit procedures - **[GUIDE.md](GUIDE.md)** - Comprehensive contribution guide - **[HELPER_FUNCTIONS.md](HELPER_FUNCTIONS.md)** - Reference for all tools.func helper functions - **Container Scripts** - `/ct/` templates and guidelines - **Install Scripts** - `/install/` templates and guidelines - **Website metadata** – Request via the website (Report issue on the script page); see [templates_json/AppName.md](templates_json/AppName.md) ### Quick Checklist - ✅ Use `/ct/example.sh` as template for container scripts - ✅ Use `/install/example-install.sh` as template for install scripts - ✅ Follow naming conventions: `appname.sh` and `appname-install.sh` - ✅ Include proper shebang: `#!/usr/bin/env bash` - ✅ Add copyright header with author - ✅ Handle errors properly with `msg_error`, `msg_ok`, etc. - ✅ Test before submitting PR (via curl from your fork, not local bash) - ✅ Update documentation if needed --- ## 🔍 Code Audit Before submitting a pull request, ensure your code passes our audit: **See**: [CODE_AUDIT.md](CODE_AUDIT.md) for complete audit checklist Key points: - Code consistency with existing scripts - Proper error handling - Correct variable naming - Adequate comments and documentation - Security best practices --- ## 🍒 Cherry-Pick: Submitting Only Your Changes **Problem**: `setup-fork.sh` modifies 600+ files to update links. You don't want to submit all of those changes - only your new 2 files! **Solution**: Use git cherry-pick to select only YOUR files. ### Step-by-Step Cherry-Pick Guide #### 1. Check what changed ```bash # See all modified files git status # Verify your files are there git status | grep -E "ct/myapp|install/myapp" ``` #### 2. Create a clean feature branch for submission ```bash # Go back to upstream main (clean slate) git fetch upstream git checkout -b submit/myapp upstream/main # Don't use your modified main branch! ``` #### 3. Cherry-pick ONLY your files Cherry-picking extracts specific changes from commits: ```bash # Option A: Cherry-pick commits that added your files # (if you committed your files separately) git cherry-pick # Option B: Manually copy and commit only your files # From your work branch, get the file contents git show feature/my-awesome-app:ct/myapp.sh > /tmp/myapp.sh git show feature/my-awesome-app:install/myapp-install.sh > /tmp/myapp-install.sh # Add them to the clean branch cp /tmp/myapp.sh ct/myapp.sh cp /tmp/myapp-install.sh install/myapp-install.sh # Commit git add ct/myapp.sh install/myapp-install.sh git commit -m "feat: add MyApp" ``` #### 4. Verify only your files are in the PR ```bash # Check git diff against upstream git diff upstream/main --name-only # Should show ONLY: # ct/myapp.sh # install/myapp-install.sh ``` #### 5. Push and create PR ```bash # Push your clean submission branch git push origin submit/myapp # Create PR on GitHub from: submit/myapp → main ``` ### Why This Matters - ✅ Clean PR with only your changes - ✅ Easier for maintainers to review - ✅ Faster merge without conflicts - ❌ Without cherry-pick: PR has 600+ file changes (won't merge!) ### If You Made a Mistake ```bash # Delete the messy branch git branch -D submit/myapp # Go back to clean branch git checkout -b submit/myapp upstream/main # Try cherry-picking again ``` --- If you're using **Visual Studio Code** with an AI assistant, you can leverage our detailed guidelines to generate high-quality contributions automatically. ### How to Use AI Assistance 1. **Open the AI Guidelines** ``` docs/contribution/AI.md ``` This file contains all requirements, patterns, and examples for writing proper scripts. 2. **Prepare Your Information** Before asking the AI to generate code, gather: - **Repository URL**: e.g., `https://github.com/owner/myapp` - **Dockerfile/Script**: Paste the app's installation instructions (if available) - **Dependencies**: What packages does it need? (Node, Python, Java, PostgreSQL, etc.) - **Ports**: What port does it listen on? (e.g., 3000, 8080, 5000) - **Configuration**: Any environment variables or config files? 3. **Tell the AI Assistant** Share with the AI: - The repository URL - The Dockerfile or install instructions - Link to [docs/contribution/AI.md](AI.md) with instructions to follow **Example prompt:** ``` I want to contribute a container script for MyApp to ProxmoxVE. Repository: https://github.com/owner/myapp Here's the Dockerfile: [paste Dockerfile content] Please follow the guidelines in docs/contribution/AI.md to create: 1. ct/myapp.sh (container script) 2. install/myapp-install.sh (installation script) Website listing/metadata is requested separately via the website (Report issue on the script page). ``` 4. **AI Will Generate** The AI will produce scripts that: - Follow all ProxmoxVE patterns and conventions - Use helper functions from `tools.func` correctly - Include proper error handling and messages - Have correct update mechanisms - Are ready to submit as a PR Website listing/metadata is requested separately via the website (Report issue on the script page). ### Key Points for AI Assistants - **Templates Location**: `docs/contribution/templates_ct/AppName.sh`, `templates_install/`, `templates_json/` - **Guidelines**: Must follow `docs/contribution/AI.md` exactly - **Helper Functions**: Use only functions from `misc/tools.func` - never write custom ones - **Testing**: Always test before submission via curl from your fork ```bash bash -c "$(curl -fsSL https://raw.githubusercontent.com/YOUR_USERNAME/ProxmoxVE/main/ct/myapp.sh)" # Wait 10-30 seconds after pushing changes ``` - **No Docker**: Container scripts must be bare-metal, not Docker-based ### Benefits - **Speed**: AI generates boilerplate in seconds - **Consistency**: Follows same patterns as 200+ existing scripts - **Quality**: Less bugs and more maintainable code - **Learning**: See how your app should be structured --- ### Documentation - **[docs/README.md](../README.md)** - Main documentation hub - **[docs/ct/README.md](../ct/README.md)** - Container scripts overview - **[docs/install/README.md](../install/README.md)** - Installation scripts overview - **[docs/ct/DETAILED_GUIDE.md](../ct/DETAILED_GUIDE.md)** - Complete ct/ script reference - **[docs/install/DETAILED_GUIDE.md](../install/DETAILED_GUIDE.md)** - Complete install/ script reference - **[docs/TECHNICAL_REFERENCE.md](../TECHNICAL_REFERENCE.md)** - Architecture deep-dive - **[docs/EXIT_CODES.md](../EXIT_CODES.md)** - Exit codes reference - **[docs/DEV_MODE.md](../DEV_MODE.md)** - Debugging guide ### Community Guides See [USER_SUBMITTED_GUIDES.md](USER_SUBMITTED_GUIDES.md) for excellent community-written guides: - Home Assistant installation and configuration - Frigate setup on Proxmox - Docker and Portainer installation - Database setup and optimization - And many more! ### Templates Use these templates when creating new scripts: ```bash # Container script template cp docs/contribution/templates_ct/AppName.sh ct/my-app.sh # Installation script template cp docs/contribution/templates_install/AppName-install.sh install/my-app-install.sh ``` For website metadata (description, logo, etc.), use the Report issue button on the script's page on the website. **Template Features:** - Updated to match current codebase patterns - Includes all available helper functions from `tools.func` - Examples for Node.js, Python, PHP, Go applications - Database setup examples (MariaDB, PostgreSQL) - Proper service creation and cleanup --- ## 🔄 Git Workflow ### Keep Your Fork Updated ```bash # Fetch latest from upstream git fetch upstream # Rebase your work on latest main git rebase upstream/main # Push to your fork git push -f origin main ``` ### Create Feature Branch ```bash # Create and switch to new branch git checkout -b feature/my-feature # Make changes... git add . git commit -m "feat: description of changes" # Push to your fork git push origin feature/my-feature # Create Pull Request on GitHub ``` ### Before Submitting PR 1. **Sync with upstream** ```bash git fetch upstream git rebase upstream/main ``` 2. **Test your changes** (via curl from your fork) ```bash bash -c "$(curl -fsSL https://raw.githubusercontent.com/YOUR_USERNAME/ProxmoxVE/main/ct/my-app.sh)" # Follow prompts and test the container # ⏱️ Wait 10-30 seconds after pushing - GitHub takes time to update ``` 3. **Check code standards** - [ ] Follows template structure - [ ] Proper error handling - [ ] Documentation updated (if needed) - [ ] No hardcoded values - [ ] Version tracking implemented 4. **Push final changes** ```bash git push origin feature/my-feature ``` --- ## 📋 Pull Request Checklist Before opening a PR: - [ ] Code follows coding standards (see CONTRIBUTING.md) - [ ] All templates used correctly - [ ] Tested on Proxmox VE - [ ] Error handling implemented - [ ] Documentation updated (if applicable) - [ ] No merge conflicts - [ ] Synced with upstream/main - [ ] Clear PR title and description --- ## ❓ FAQ ### ❌ Why can't I test with `bash ct/myapp.sh` locally? You might try: ```bash # ❌ WRONG - This won't test your actual changes! bash ct/myapp.sh ./ct/myapp.sh sh ct/myapp.sh ``` **Why this fails:** - `bash ct/myapp.sh` uses the LOCAL clone file - The LOCAL file doesn't execute the curl commands - it's already on disk - The curl URLs INSIDE the script are modified by setup-fork.sh, but they're not executed - So you can't verify if your curl URLs actually work - Users will get the curl URL version (which may be broken) **Solution:** Always test via curl from GitHub: ```bash # ✅ CORRECT - Tests the actual GitHub URLs bash -c "$(curl -fsSL https://raw.githubusercontent.com/YOUR_USERNAME/ProxmoxVE/main/ct/myapp.sh)" ``` ### ❓ How do I test my changes? You **cannot** test locally with `bash ct/myapp.sh` from your cloned directory! You **must** push to GitHub and test via curl from your fork: ```bash # 1. Push your changes to your fork git push origin feature/my-awesome-app # 2. Test via curl (this loads the script from GitHub, not local files) bash -c "$(curl -fsSL https://raw.githubusercontent.com/YOUR_USERNAME/ProxmoxVE/main/ct/my-app.sh)" # 3. For verbose/debug output, pass environment variables VERBOSE=yes bash -c "$(curl -fsSL https://raw.githubusercontent.com/YOUR_USERNAME/ProxmoxVE/main/ct/my-app.sh)" DEV_MODE_LOGS=true bash -c "$(curl -fsSL https://raw.githubusercontent.com/YOUR_USERNAME/ProxmoxVE/main/ct/my-app.sh)" ``` **Why?** - Local `bash ct/myapp.sh` uses local files from your clone - But the script's INTERNAL curl commands have been modified by setup-fork.sh to point to your fork - This discrepancy means you're not actually testing the curl URLs - Testing via curl ensures the script downloads from YOUR fork GitHub URLs - ⏱️ **Important:** GitHub takes 10-30 seconds to recognize newly pushed files. Wait before testing! **What if local bash worked?** You'd be testing local files only, not the actual GitHub URLs that users will download. This means broken curl links wouldn't be caught during testing. ### What if my PR has conflicts? ```bash # Sync with upstream main repository git fetch upstream git rebase upstream/main # Resolve conflicts in your editor git add . git rebase --continue git push -f origin your-branch ``` ### How do I keep my fork updated? Two ways: **Option 1: Run setup script again** ```bash bash docs/contribution/setup-fork.sh --full ``` **Option 2: Manual sync** ```bash git fetch upstream git rebase upstream/main git push -f origin main ``` ### Where do I ask questions? - **GitHub Issues**: For bugs and feature requests - **GitHub Discussions**: For general questions and ideas - **Discord**: Community-scripts server for real-time chat --- ## 🎓 Learning Resources ### For First-Time Contributors 1. Read: [docs/README.md](../README.md) - Documentation overview 2. Read: [CONTRIBUTING.md](CONTRIBUTING.md) - Essential coding standards 3. Choose your path: - Containers → [docs/ct/DETAILED_GUIDE.md](../ct/DETAILED_GUIDE.md) - Installation → [docs/install/DETAILED_GUIDE.md](../install/DETAILED_GUIDE.md) 4. Study existing scripts in same category 5. Create your contribution ### For Experienced Developers 1. Review [CONTRIBUTING.md](CONTRIBUTING.md) - Coding standards 2. Review [CODE_AUDIT.md](CODE_AUDIT.md) - Audit checklist 3. Check templates in `/docs/contribution/templates_*/` 4. Use AI assistants with [AI.md](AI.md) for code generation 5. Submit PR with confidence ### For Using AI Assistants See "Using AI Assistants" section above for: - How to structure prompts - What information to provide - How to validate AI output --- ## 🚀 Ready to Contribute? 1. **Fork** the repository 2. **Clone** your fork and **setup** with `bash docs/contribution/setup-fork.sh --full` 3. **Choose** your contribution type (container, installation, tools, etc.) 4. **Read** the appropriate detailed guide 5. **Create** your feature branch 6. **Develop** and **test** your changes 7. **Commit** with clear messages 8. **Push** to your fork 9. **Create** Pull Request --- ## 📞 Contact & Support - **GitHub**: [community-scripts/ProxmoxVE](https://github.com/community-scripts/ProxmoxVE) - **Issues**: [GitHub Issues](https://github.com/community-scripts/ProxmoxVE/issues) - **Discussions**: [GitHub Discussions](https://github.com/community-scripts/ProxmoxVE/discussions) - **Discord**: [Join Server](https://discord.gg/UHrpNWGwkH) --- **Thank you for contributing to ProxmoxVE!** 🙏 Your efforts help make Proxmox VE automation accessible to everyone. Happy coding! 🚀 ================================================ FILE: docs/contribution/setup-fork.sh ================================================ #!/bin/bash ################################################################################ # ProxmoxVE Fork Setup Script # # Automatically configures documentation and scripts for your fork # Detects your GitHub username and repository from git config # Updates all hardcoded links to point to your fork # # Usage: # ./setup-fork.sh # Auto-detect from git config (updates misc/ only) # ./setup-fork.sh YOUR_USERNAME # Specify username (updates misc/ only) # ./setup-fork.sh YOUR_USERNAME REPO_NAME # Specify both (updates misc/ only) # ./setup-fork.sh --full # Update all files including ct/, install/, vm/, etc. # # Examples: # ./setup-fork.sh john # Uses john/ProxmoxVE, updates misc/ only # ./setup-fork.sh john my-fork # Uses john/my-fork, updates misc/ only # ./setup-fork.sh --full # Auto-detect + update all files ################################################################################ set -e # Colors for output RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' # No Color # Default values REPO_NAME="ProxmoxVE" USERNAME="" AUTO_DETECT=true UPDATE_ALL=false ################################################################################ # FUNCTIONS ################################################################################ print_header() { echo -e "\n${BLUE}╔════════════════════════════════════════════════════════════╗${NC}" echo -e "${BLUE}║${NC} ProxmoxVE Fork Setup Script" echo -e "${BLUE}║${NC} Configuring for your fork..." echo -e "${BLUE}╚════════════════════════════════════════════════════════════╝${NC}\n" } print_info() { echo -e "${BLUE}ℹ${NC} $1" } print_success() { echo -e "${GREEN}✓${NC} $1" } print_warning() { echo -e "${YELLOW}⚠${NC} $1" } print_error() { echo -e "${RED}✗${NC} $1" } # Detect username from git remote detect_username() { local remote_url # Try to get from origin if ! remote_url=$(git config --get remote.origin.url 2>/dev/null); then return 1 fi # Extract username from SSH or HTTPS URL if [[ $remote_url =~ git@github.com:([^/]+) ]]; then echo "${BASH_REMATCH[1]}" elif [[ $remote_url =~ github.com/([^/]+) ]]; then echo "${BASH_REMATCH[1]}" else return 1 fi } # Detect repo name from git remote detect_repo_name() { local remote_url if ! remote_url=$(git config --get remote.origin.url 2>/dev/null); then return 1 fi # Extract repo name (remove .git if present) if [[ $remote_url =~ /([^/]+?)(.git)?$ ]]; then local repo="${BASH_REMATCH[1]}" echo "${repo%.git}" else return 1 fi } # Ask user for confirmation confirm() { local prompt="$1" local response echo -ne "${YELLOW}${prompt} (y/n)${NC} " read -r response [[ $response =~ ^[Yy]$ ]] } # Update links in files update_links() { local old_repo="community-scripts" local old_name="ProxmoxVE" local new_owner="$1" local new_repo="$2" local files_updated=0 print_info "Scanning for hardcoded links..." # Change to repo root local repo_root=$(git rev-parse --show-toplevel 2>/dev/null || pwd) # Determine search path local search_path="$repo_root/misc" if [[ "$UPDATE_ALL" == "true" ]]; then search_path="$repo_root" print_info "Searching all files (--full mode)" else print_info "Searching misc/ directory only (core functions)" fi echo "" # Find all files containing the old repo reference while IFS= read -r file; do # Count occurrences local count=$(grep -E -c "(github.com|githubusercontent.com)/$old_repo/$old_name" "$file" 2>/dev/null || echo 0) if [[ $count -gt 0 ]]; then # Backup original cp "$file" "$file.backup" # Replace links - use different sed syntax for BSD/macOS vs GNU sed if sed --version &>/dev/null 2>&1; then # GNU sed sed -E -i "s@(github.com|githubusercontent.com)/$old_repo/$old_name@\\1/$new_owner/$new_repo@g" "$file" else # BSD sed (macOS) sed -E -i '' "s@(github.com|githubusercontent.com)/$old_repo/$old_name@\\1/$new_owner/$new_repo@g" "$file" fi ((files_updated++)) print_success "Updated $file ($count links)" fi done < <(find "$search_path" -type f \( -name "*.md" -o -name "*.sh" -o -name "*.func" -o -name "*.json" \) -not -path "*/.git/*" 2>/dev/null | xargs grep -E -l "(github.com|githubusercontent.com)/$old_repo/$old_name" 2>/dev/null) return $files_updated } # Create user git config setup info create_git_setup_info() { local username="$1" cat >.git-setup-info <<'EOF' # Git Configuration for ProxmoxVE Development ## Recommended Git Configuration ### Set up remotes for easy syncing with upstream: ```bash # View your current remotes git remote -v # If you don't have 'upstream' configured, add it: git remote add upstream https://github.com/community-scripts/ProxmoxVE.git # Verify both remotes exist: git remote -v # Should show: # origin https://github.com/YOUR_USERNAME/ProxmoxVE.git (fetch) # origin https://github.com/YOUR_USERNAME/ProxmoxVE.git (push) # upstream https://github.com/community-scripts/ProxmoxVE.git (fetch) # upstream https://github.com/community-scripts/ProxmoxVE.git (push) ``` ### Configure Git User (if not done globally) ```bash git config user.name "Your Name" git config user.email "your.email@example.com" # Or configure globally: git config --global user.name "Your Name" git config --global user.email "your.email@example.com" ``` ### Useful Git Workflows **Keep your fork up-to-date:** ```bash git fetch upstream git rebase upstream/main git push origin main ``` **Create feature branch:** ```bash git checkout -b feature/my-awesome-app # Make changes... git commit -m "feat: add my awesome app" git push origin feature/my-awesome-app ``` **Pull latest from upstream:** ```bash git fetch upstream git merge upstream/main ``` --- For more help, see: docs/contribution/README.md EOF print_success "Created .git-setup-info file" } ################################################################################ # MAIN LOGIC ################################################################################ print_header # Parse command line arguments if [[ $# -gt 0 ]]; then # Check for --full flag if [[ "$1" == "--full" ]]; then UPDATE_ALL=true shift # Remove --full from arguments fi # Process remaining arguments if [[ $# -gt 0 ]]; then USERNAME="$1" AUTO_DETECT=false if [[ $# -gt 1 ]]; then REPO_NAME="$2" fi fi fi # Try auto-detection if [[ -z "$USERNAME" ]]; then if username=$(detect_username); then USERNAME="$username" print_success "Detected GitHub username: $USERNAME" else print_error "Could not auto-detect GitHub username from git config" echo -e "${YELLOW}Please run:${NC}" echo " ./setup-fork.sh YOUR_USERNAME" exit 1 fi fi # Auto-detect repo name if needed if repo_name=$(detect_repo_name); then REPO_NAME="$repo_name" if [[ "$REPO_NAME" != "ProxmoxVE" ]]; then print_info "Detected custom repo name: $REPO_NAME" else print_success "Using default repo name: ProxmoxVE" fi fi # Validate inputs if [[ -z "$USERNAME" ]]; then print_error "Username cannot be empty" exit 1 fi if [[ -z "$REPO_NAME" ]]; then print_error "Repository name cannot be empty" exit 1 fi # Show what we'll do echo -e "${BLUE}Configuration Summary:${NC}" echo " Repository URL: https://github.com/$USERNAME/$REPO_NAME" if [[ "$UPDATE_ALL" == "true" ]]; then echo " Files to update: ALL files (ct/, install/, vm/, misc/, docs/, etc.)" else echo " Files to update: misc/ directory only (core functions)" fi echo "" # Ask for confirmation if ! confirm "Apply these changes?"; then print_warning "Setup cancelled" exit 0 fi echo "" # Update all links if update_links "$USERNAME" "$REPO_NAME"; then links_changed=$? print_success "Updated $links_changed files" else print_warning "No links needed updating or some files not found" fi # Create git setup info file create_git_setup_info "$USERNAME" # Final summary echo "" echo -e "${GREEN}╔════════════════════════════════════════════════════════════╗${NC}" echo -e "${GREEN}║${NC} Fork Setup Complete! ${GREEN}║${NC}" echo -e "${GREEN}╚════════════════════════════════════════════════════════════╝${NC}" echo "" print_success "All documentation links updated to point to your fork" print_info "Your fork: https://github.com/$USERNAME/$REPO_NAME" print_info "Upstream: https://github.com/community-scripts/ProxmoxVE" echo "" echo -e "${BLUE}Next Steps:${NC}" echo " 1. Review the changes: git diff" echo " 2. Check .git-setup-info for recommended git workflow" echo " 3. Start developing: git checkout -b feature/my-app" echo " 4. Read: docs/contribution/README.md" echo "" print_success "Happy contributing! 🚀" ================================================ FILE: docs/contribution/templates_ct/AppName.md ================================================ # CT Container Scripts - Quick Reference > [!WARNING] > **This is legacy documentation.** Refer to the **modern template** at [templates_ct/AppName.sh](AppName.sh) for best practices. > > Current templates use: > > - `tools.func` helpers instead of manual patterns > - `check_for_gh_release` and `fetch_and_deploy_gh_release` from build.func > - Automatic setup-fork.sh configuration --- ## Before Creating a Script 1. **Fork & Clone:** ```bash git clone https://github.com/YOUR_USERNAME/ProxmoxVE.git cd ProxmoxVE ``` 2. **Run setup-fork.sh** (updates all curl URLs to your fork): ```bash bash docs/contribution/setup-fork.sh ``` 3. **Copy the Modern Template:** ```bash cp templates_ct/AppName.sh ct/MyApp.sh # Edit ct/MyApp.sh with your app details ``` 4. **Test Your Script (via GitHub):** ⚠️ **Important:** You must push to GitHub and test via curl, not `bash ct/MyApp.sh`! ```bash # Push your changes to your fork first git push origin feature/my-awesome-app # Then test via curl (this loads from YOUR fork, not local files) bash -c "$(curl -fsSL https://raw.githubusercontent.com/YOUR_USERNAME/ProxmoxVE/main/ct/MyApp.sh)" ``` > 💡 **Why?** The script's curl commands are modified by setup-fork.sh, but local execution uses local files, not the updated GitHub URLs. Testing via curl ensures your script actually works. > > ⏱️ **Note:** GitHub sometimes takes 10-30 seconds to update files. If you don't see your changes, wait and try again. 5. **Cherry-Pick for PR** (submit ONLY your 3-4 files): - See [Cherry-Pick Guide](../README.md) for step-by-step git commands --- ## Template Structure The modern template includes: ### Header ```bash #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) # (Note: setup-fork.sh changes this URL to point to YOUR fork during development) ``` ### Metadata ```bash # Copyright (c) 2021-2026 community-scripts ORG # Author: YourUsername # License: MIT APP="MyApp" var_tags="app-category;foss" var_cpu="2" var_ram="2048" var_disk="4" var_os="alpine" var_version="3.20" var_unprivileged="1" ``` ### Core Setup ```bash header_info "$APP" variables color catch_errors ``` ### Update Function The modern template provides a standard update pattern: ```bash function update_script() { header_info check_container_storage check_container_resources # Use tools.func helpers: check_for_gh_release "myapp" "owner/repo" fetch_and_deploy_gh_release "myapp" "owner/repo" "tarball" "latest" "/opt/myapp" } ``` --- ## Key Patterns ### Check for Updates (App Repository) Use `check_for_gh_release` with the **app repo**: ```bash check_for_gh_release "myapp" "owner/repo" ``` ### Deploy External App Use `fetch_and_deploy_gh_release` with the **app repo**: ```bash fetch_and_deploy_gh_release "myapp" "owner/repo" ``` ### Avoid Manual Version Checking ❌ OLD (manual): ```bash RELEASE=$(curl -fsSL https://api.github.com/repos/myapp/myapp/releases/latest | grep tag_name) ``` ✅ NEW (use tools.func): ```bash fetch_and_deploy_gh_release "myapp" "owner/repo" ``` --- ## Best Practices 1. **Use tools.func helpers** - Don't manually curl for versions 2. **Only add app-specific dependencies** - Don't add ca-certificates, curl, gnupg (handled by build.func) 3. **Test via curl from your fork** - Push first, then: `bash -c "$(curl -fsSL https://raw.githubusercontent.com/YOUR_USERNAME/ProxmoxVE/main/ct/MyApp.sh)"` 4. **Wait for GitHub to update** - Takes 10-30 seconds after git push 5. **Cherry-pick only YOUR files** - Submit only ct/MyApp.sh, install/MyApp-install.sh (2 files). Website metadata: use Report issue on the script's page on the website. 6. **Verify before PR** - Run `git diff upstream/main --name-only` to confirm only your files changed --- ## Common Update Patterns See the [modern template](AppName.sh) and [AI.md](../AI.md) for complete working examples. Recent reference scripts with good update functions: - [Trip](https://github.com/community-scripts/ProxmoxVE/blob/main/ct/trip.sh) - [Thingsboard](https://github.com/community-scripts/ProxmoxVE/blob/main/ct/thingsboard.sh) - [UniFi](https://github.com/community-scripts/ProxmoxVE/blob/main/ct/unifi.sh) --- ## Need Help? - **[README.md](../README.md)** - Full contribution workflow - **[AI.md](../AI.md)** - AI-generated script guidelines - **[FORK_SETUP.md](../FORK_SETUP.md)** - Why setup-fork.sh is important - **[Slack Community](https://discord.gg/your-link)** - Ask questions ```` ### 3.4 **Verbosity** - Use the appropriate flag (**-q** in the examples) for a command to suppress its output. Example: ```bash curl -fsSL unzip -q ```` - If a command does not come with this functionality use `$STD` to suppress it's output. Example: ```bash $STD php artisan migrate --force $STD php artisan config:clear ``` ### 3.5 **Backups** - Backup user data if necessary. - Move all user data back in the directory when the update is finished. > [!NOTE] > This is not meant to be a permanent backup Example backup: ```bash mv /opt/snipe-it /opt/snipe-it-backup ``` Example config restore: ```bash 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 ``` ### 3.6 **Cleanup** - Do not forget to remove any temporary files/folders such as zip-files or temporary backups. Example: ```bash rm -rf /opt/v${RELEASE}.zip rm -rf /opt/snipe-it-backup ``` ### 3.7 **No update function** - In case you can not provide an update function use the following code to provide user feedback. ```bash function update_script() { header_info check_container_storage check_container_resources if [[ ! -d /opt/snipeit ]]; then msg_error "No ${APP} Installation Found!" exit fi msg_error "Currently we don't provide an update function for this ${APP}." exit } ``` --- ## 4 **End of the script** - `start`: Launches Whiptail dialogue - `build_container`: Collects and integrates user settings - `description`: Sets LXC container description - With `echo -e "${TAB}${GATEWAY}${BGN}http://${IP}${CL}"` you can point the user to the IP:PORT/folder needed to access the app. ```bash start build_container description msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} 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}" ``` --- ## 5. **Contribution checklist** - [ ] Shebang is correctly set (`#!/usr/bin/env bash`). - [ ] Correct link to _build.func_ - [ ] Metadata (author, license) is included at the top. - [ ] Variables follow naming conventions. - [ ] Update function exists. - [ ] Update functions checks if app is installed and for new version. - [ ] Update function cleans up temporary files. - [ ] Script ends with a helpful message for the user to reach the application. ================================================ FILE: docs/contribution/templates_ct/AppName.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: [YourGitHubUsername] # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: [SOURCE_URL e.g. https://github.com/example/app] # ============================================================================ # APP CONFIGURATION # ============================================================================ # These values are sent to build.func and define default container resources. # Users can customize these during installation via the interactive prompts. # ============================================================================ APP="[AppName]" var_tags="${var_tags:-[category1];[category2]}" # Max 2 tags, semicolon-separated var_cpu="${var_cpu:-2}" # CPU cores: 1-4 typical var_ram="${var_ram:-2048}" # RAM in MB: 512, 1024, 2048, etc. var_disk="${var_disk:-8}" # Disk in GB: 6, 8, 10, 20 typical var_os="${var_os:-debian}" # OS: debian, ubuntu, alpine var_version="${var_version:-13}" # OS Version: 13 (Debian), 24.04 (Ubuntu), 3.21 (Alpine) var_unprivileged="${var_unprivileged:-1}" # 1=unprivileged (secure), 0=privileged (for Docker/Podman) # ============================================================================ # INITIALIZATION - These are required in all CT scripts # ============================================================================ header_info "$APP" # Display app name and setup header variables # Initialize build.func variables color # Load color variables for output catch_errors # Enable error handling with automatic exit on failure # ============================================================================ # UPDATE SCRIPT - Called when user selects "Update" from web interface # ============================================================================ # This function is triggered by the web interface to update the application. # It should: # 1. Check if installation exists # 2. Check for new GitHub releases # 3. Stop running services # 4. Backup critical data # 5. Deploy new version # 6. Run post-update commands (migrations, config updates, etc.) # 7. Restore data if needed # 8. Start services # # Exit with `exit` at the end to prevent container restart. # ============================================================================ function update_script() { header_info check_container_storage check_container_resources # Step 1: Verify installation exists if [[ ! -d /opt/[appname] ]]; then msg_error "No ${APP} Installation Found!" exit fi # Step 2: Check if update is available if check_for_gh_release "[appname]" "YourUsername/YourRepo"; then # Step 3: Stop services before update msg_info "Stopping Service" systemctl stop [appname] msg_ok "Stopped Service" # Step 4: Backup critical data before overwriting msg_info "Backing up Data" cp -r /opt/[appname]/data /opt/[appname]_data_backup 2>/dev/null || true msg_ok "Backed up Data" # Step 5: Download and deploy new version # CLEAN_INSTALL=1 removes old directory before extracting CLEAN_INSTALL=1 fetch_and_deploy_gh_release "[appname]" "owner/repo" "tarball" "latest" "/opt/[appname]" # Step 6: Run post-update commands (uncomment as needed) # These examples show common patterns - use what applies to your app: # # For Node.js apps: # msg_info "Installing Dependencies" # cd /opt/[appname] # $STD npm ci --production # msg_ok "Installed Dependencies" # # For Python apps: # msg_info "Installing Dependencies" # cd /opt/[appname] # $STD uv sync --frozen # msg_ok "Installed Dependencies" # # For database migrations: # msg_info "Running Database Migrations" # cd /opt/[appname] # $STD npm run migrate # msg_ok "Ran Database Migrations" # # For PHP apps: # msg_info "Installing Dependencies" # cd /opt/[appname] # $STD composer install --no-dev # msg_ok "Installed Dependencies" # Step 7: Restore data from backup msg_info "Restoring Data" cp -r /opt/[appname]_data_backup/. /opt/[appname]/data/ 2>/dev/null || true rm -rf /opt/[appname]_data_backup msg_ok "Restored Data" # Step 8: Restart service with new version msg_info "Starting Service" systemctl start [appname] msg_ok "Started Service" msg_ok "Updated successfully!" fi exit } # ============================================================================ # MAIN EXECUTION - Container creation flow # ============================================================================ # These are called by build.func and handle the full installation process: # 1. start - Initialize container creation # 2. build_container - Execute the install script inside container # 3. description - Display completion info and access details # ============================================================================ start build_container description # ============================================================================ # COMPLETION MESSAGE # ============================================================================ msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:[PORT]${CL}" ================================================ FILE: docs/contribution/templates_install/AppName-install.md ================================================ # Install Scripts - Quick Reference > [!WARNING] > **This is legacy documentation.** Refer to the **modern template** at [templates_install/AppName-install.sh](AppName-install.sh) for best practices. > > Current templates use: > > - `tools.func` helpers (setup_nodejs, setup_uv, setup_postgresql_db, etc.) > - Automatic dependency installation via build.func > - Standardized environment variable patterns --- ## Before Creating a Script 1. **Copy the Modern Template:** ```bash cp templates_install/AppName-install.sh install/MyApp-install.sh # Edit install/MyApp-install.sh ``` 2. **Key Pattern:** - CT scripts source build.func and call the install script - Install scripts use sourced FUNCTIONS_FILE_PATH (via build.func) - Both scripts work together in the container 3. **Test via GitHub:** ```bash # Push your changes to your fork first git push origin feature/my-awesome-app # Test the CT script via curl (it will call the install script) bash -c "$(curl -fsSL https://raw.githubusercontent.com/YOUR_USERNAME/ProxmoxVE/main/ct/MyApp.sh)" # ⏱️ Wait 10-30 seconds after pushing - GitHub takes time to update ``` --- ## Template Structure ### Header ```bash #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/install.func) # (setup-fork.sh modifies this URL to point to YOUR fork during development) ``` ### Dependencies (App-Specific Only) ```bash # Don't add: ca-certificates, curl, gnupg, wget, git, jq # These are handled by build.func msg_info "Installing dependencies" $STD apt-get install -y app-specific-deps msg_ok "Installed dependencies" ``` ### Runtime Setup Use tools.func helpers instead of manual installation: ```bash # ✅ NEW (use tools.func): NODE_VERSION="20" setup_nodejs # OR PYTHON_VERSION="3.12" setup_uv # OR PG_DB_NAME="myapp_db" PG_DB_USER="myapp" setup_postgresql_db ``` ### Service Configuration ```bash # Create .env file msg_info "Configuring MyApp" cat << EOF > /opt/myapp/.env DEBUG=false PORT=8080 DATABASE_URL=postgresql://... EOF msg_ok "Configuration complete" # Create systemd service msg_info "Creating systemd service" cat << EOF > /etc/systemd/system/myapp.service [Unit] Description=MyApp [Service] ExecStart=/usr/bin/node /opt/myapp/app.js [Install] WantedBy=multi-user.target EOF msg_ok "Service created" ``` ### Finalization ```bash msg_info "Finalizing MyApp installation" systemctl enable --now myapp motd_ssh customize msg_ok "MyApp installation complete" cleanup_lxc ``` --- ## Key Patterns ### Avoid Manual Version Checking ❌ OLD (manual): ```bash RELEASE=$(curl -fsSL https://api.github.com/repos/app/repo/releases/latest | grep tag_name) wget https://github.com/app/repo/releases/download/$RELEASE/app.tar.gz ``` ✅ NEW (use tools.func via CT script's fetch_and_deploy_gh_release): ```bash # In CT script, not install script: fetch_and_deploy_gh_release "myapp" "app/repo" "app.tar.gz" "latest" "/opt/myapp" ``` ### Database Setup ```bash # Use setup_postgresql_db, setup_mysql_db, etc. PG_DB_NAME="myapp" PG_DB_USER="myapp" setup_postgresql_db ``` ### Node.js Setup ```bash NODE_VERSION="20" setup_nodejs npm install --no-save ``` --- ## Best Practices 1. **Only add app-specific dependencies** - Don't add: ca-certificates, curl, gnupg, wget, git, jq - These are handled by build.func 2. **Use tools.func helpers** - setup_nodejs, setup_python, setup_uv, setup_postgresql_db, setup_mysql_db, etc. 3. **Don't do version checks in install script** - Version checking happens in CT script's update_script() - Install script just installs the latest 4. **Structure:** - Dependencies - Runtime setup (tools.func) - Deployment (fetch from CT script) - Configuration files - Systemd service - Finalization --- ## Reference Scripts See working examples: - [Trip](https://github.com/community-scripts/ProxmoxVE/blob/main/install/trip-install.sh) - [Thingsboard](https://github.com/community-scripts/ProxmoxVE/blob/main/install/thingsboard-install.sh) - [UniFi](https://github.com/community-scripts/ProxmoxVE/blob/main/install/unifi-install.sh) --- ## Need Help? - **[Modern Template](AppName-install.sh)** - Start here - **[CT Template](../templates_ct/AppName.sh)** - How CT scripts work - **[README.md](../README.md)** - Full contribution workflow - **[AI.md](../AI.md)** - AI-generated script guidelines ### 1.2 **Comments** - Add clear comments for script metadata, including author, copyright, and license information. - Use meaningful inline comments to explain complex commands or logic. Example: ```bash # Copyright (c) 2021-2026 community-scripts ORG # Author: [YourUserName] # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: [SOURCE_URL] ``` > [!NOTE]: > > - Add your username > - When updating/reworking scripts, add "| Co-Author [YourUserName]" ### 1.3 **Variables and function import** - This sections adds the support for all needed functions and variables. ```bash source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os ``` --- ## 2. **Variable naming and management** ### 2.1 **Naming conventions** - Use uppercase names for constants and environment variables. - Use lowercase names for local script variables. Example: ```bash DB_NAME=snipeit_db # Environment-like variable (constant) db_user="snipeit" # Local variable ``` --- ## 3. **Dependencies** ### 3.1 **Install all at once** - Install all dependencies with a single command if possible Example: ```bash $STD apt-get install -y \ curl \ composer \ git \ sudo \ mc \ nginx ``` ### 3.2 **Collapse dependencies** Collapse dependencies to keep the code readable. Example: Use ```bash php8.2-{bcmath,common,ctype} ``` instead of ```bash php8.2-bcmath php8.2-common php8.2-ctype ``` --- ## 4. **Paths to application files** If possible install the app and all necessary files in `/opt/` --- ## 5. **Version management** ### 5.1 **Install the latest release** - Always try and install the latest release - Do not hardcode any version if not absolutely necessary Example for a git release: ```bash RELEASE=$(curl -fsSL https://api.github.com/repos/snipe/snipe-it/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4) }') curl -fsSL "https://github.com/snipe/snipe-it/archive/refs/tags/v${RELEASE}.zip" ``` ### 5.2 **Save the version for update checks** - Write the installed version into a file. - This is used for the update function in **AppName.sh** to check for if a Update is needed. Example: ```bash echo "${RELEASE}" >"/opt/AppName_version.txt" ``` --- ## 6. **Input and output management** ### 6.1 **User feedback** - Use standard functions like `msg_info`, `msg_ok` or `msg_error` to print status messages. - Each `msg_info` must be followed with a `msg_ok` before any other output is made. - Display meaningful progress messages at key stages. Example: ```bash msg_info "Installing Dependencies" $STD apt-get install -y ... msg_ok "Installed Dependencies" ``` ### 6.2 **Verbosity** - Use the appropiate flag (**-q** in the examples) for a command to suppres its output Example: ```bash curl -fsSL unzip -q ``` - If a command dose not come with such a functionality use `$STD` (a custom standard redirection variable) for managing output verbosity. Example: ```bash $STD apt-get install -y nginx ``` --- ## 7. **String/File Manipulation** ### 7.1 **File Manipulation** - Use `sed` to replace placeholder values in configuration files. Example: ```bash sed -i -e "s|^DB_DATABASE=.*|DB_DATABASE=$DB_NAME|" \ -e "s|^DB_USERNAME=.*|DB_USERNAME=$DB_USER|" \ -e "s|^DB_PASSWORD=.*|DB_PASSWORD=$DB_PASS|" .env ``` --- ## 8. **Security practices** ### 8.1 **Password generation** - Use `openssl` to generate random passwords. - Use only alphanumeric values to not introduce unknown behaviour. Example: ```bash DB_PASS=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | head -c13) ``` ### 8.2 **File permissions** Explicitly set secure ownership and permissions for sensitive files. Example: ```bash chown -R www-data: /opt/snipe-it chmod -R 755 /opt/snipe-it ``` --- ## 9. **Service Configuration** ### 9.1 **Configuration files** Use `cat </etc/nginx/conf.d/snipeit.conf server { listen 80; root /opt/snipe-it/public; index index.php; } EOF ``` ### 9.2 **Credential management** Store the generated credentials in a file. Example: ```bash USERNAME=username PASSWORD=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | head -c13) { echo "Application-Credentials" echo "Username: $USERNAME" echo "Password: $PASSWORD" } >> ~/application.creds ``` ### 9.3 **Enviroment files** Use `cat </path/to/.env VARIABLE="value" PORT=3000 DB_NAME="${DB_NAME}" EOF ``` ### 9.4 **Services** Enable affected services after configuration changes and start them right away. Example: ```bash systemctl enable -q --now nginx ``` --- ## 10. **Cleanup** ### 10.1 **Remove temporary files** Remove temporary files and downloads after use. Example: ```bash rm -rf /opt/v${RELEASE}.zip ``` ### 10.2 **Autoremove and autoclean** Remove unused dependencies to reduce disk space usage. Example: ```bash apt-get -y autoremove apt-get -y autoclean ``` --- ## 11. **Best Practices Checklist** - [ ] Shebang is correctly set (`#!/usr/bin/env bash`). - [ ] Metadata (author, license) is included at the top. - [ ] Variables follow naming conventions. - [ ] Sensitive values are dynamically generated. - [ ] Files and services have proper permissions. - [ ] Script cleans up temporary files. --- ### Example: High-Level Script Flow 1. Dependencies installation 2. Database setup 3. Download and configure application 4. Service configuration 5. Final cleanup ================================================ FILE: docs/contribution/templates_install/AppName-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: [YourGitHubUsername] # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: [SOURCE_URL e.g. https://github.com/example/app] source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os # ============================================================================= # DEPENDENCIES - Only add app-specific dependencies here! # Don't add: ca-certificates, curl, gnupg, git, build-essential (handled by build.func) # ============================================================================= msg_info "Installing Dependencies" $STD apt install -y \ libharfbuzz0b \ fontconfig msg_ok "Installed Dependencies" # ============================================================================= # SETUP RUNTIMES & DATABASES (if needed) # ============================================================================= # Examples (uncomment as needed): # # NODE_VERSION="22" setup_nodejs # NODE_VERSION="22" NODE_MODULE="pnpm" setup_nodejs # Installs pnpm # PYTHON_VERSION="3.13" setup_uv # JAVA_VERSION="21" setup_java # GO_VERSION="1.22" setup_go # PHP_VERSION="8.4" PHP_FPM="YES" setup_php # setup_postgresql # Server only # setup_mariadb # Server only # setup_meilisearch # Search engine # # Then set up DB and user (sets $[DB]_DB_PASS): # PG_DB_NAME="myapp" PG_DB_USER="myapp" setup_postgresql_db # MARIADB_DB_NAME="myapp" MARIADB_DB_USER="myapp" setup_mariadb_db # ============================================================================= # DOWNLOAD & DEPLOY APPLICATION # ============================================================================= # fetch_and_deploy_gh_release modes: # "tarball" - Source tarball (default if omitted) # "binary" - .deb package (auto-detects amd64/arm64) # "prebuild" - Pre-built archive (.tar.gz) # "singlefile" - Single binary file # # Examples: # fetch_and_deploy_gh_release "myapp" "YourUsername/myapp" "tarball" "latest" "/opt/myapp" # fetch_and_deploy_gh_release "myapp" "YourUsername/myapp" "binary" "latest" "/tmp" # fetch_and_deploy_gh_release "myapp" "YourUsername/myapp" "prebuild" "latest" "/opt/myapp" "myapp-*.tar.gz" fetch_and_deploy_gh_release "[appname]" "owner/repo" "tarball" "latest" "/opt/[appname]" # --- Tools & Utilities --- # get_lxc_ip # Sets $LOCAL_IP variable (call early!) # setup_ffmpeg # Install FFmpeg with codecs # setup_hwaccel # Setup GPU hardware acceleration # setup_imagemagick # Install ImageMagick 7 # setup_docker # Install Docker Engine # setup_adminer # Install Adminer for DB management # create_self_signed_cert # Creates cert in /etc/ssl/[appname]/ # ============================================================================= # EXAMPLES # ============================================================================= # # EXAMPLE 1: Node.js Application with PostgreSQL # --------------------------------------------- # NODE_VERSION="22" setup_nodejs # PG_VERSION="17" setup_postgresql # PG_DB_NAME="myapp" PG_DB_USER="myapp" setup_postgresql_db # get_lxc_ip # fetch_and_deploy_gh_release "myapp" "owner/myapp" "tarball" "latest" "/opt/myapp" # # msg_info "Configuring MyApp" # cd /opt/myapp # $STD npm ci # cat </opt/myapp/.env # DATABASE_URL=postgresql://${PG_DB_USER}:${PG_DB_PASS}@localhost/${PG_DB_NAME} # HOST=${LOCAL_IP} # PORT=3000 # EOF # msg_ok "Configured MyApp" # # EXAMPLE 2: Python Application with uv # ------------------------------------ # PYTHON_VERSION="3.13" setup_uv # get_lxc_ip # fetch_and_deploy_gh_release "myapp" "owner/myapp" "tarball" "latest" "/opt/myapp" # # msg_info "Setting up MyApp" # cd /opt/myapp # $STD uv sync # cat </opt/myapp/.env # HOST=${LOCAL_IP} # PORT=8000 # EOF # msg_ok "Setup MyApp" # ============================================================================= # EXAMPLE 3: PHP Application with MariaDB + Nginx # ============================================================================= # PHP_VERSION="8.4" PHP_FPM="YES" PHP_MODULE="bcmath,curl,gd,intl,mbstring,mysql,xml,zip" setup_php # setup_composer # setup_mariadb # MARIADB_DB_NAME="myapp" MARIADB_DB_USER="myapp" setup_mariadb_db # get_lxc_ip # fetch_and_deploy_gh_release "myapp" "owner/myapp" "prebuild" "latest" "/opt/myapp" "myapp-*.tar.gz" # # msg_info "Configuring MyApp" # cd /opt/myapp # cp .env.example .env # sed -i "s|APP_URL=.*|APP_URL=http://${LOCAL_IP}|" .env # sed -i "s|DB_DATABASE=.*|DB_DATABASE=${MARIADB_DB_NAME}|" .env # sed -i "s|DB_USERNAME=.*|DB_USERNAME=${MARIADB_DB_USER}|" .env # sed -i "s|DB_PASSWORD=.*|DB_PASSWORD=${MARIADB_DB_PASS}|" .env # $STD composer install --no-dev --no-interaction # chown -R www-data:www-data /opt/myapp # msg_ok "Configured MyApp" # ============================================================================= # YOUR APPLICATION INSTALLATION # ============================================================================= # 1. Setup runtimes and databases FIRST # 2. Call get_lxc_ip if you need the container IP # 3. Use fetch_and_deploy_gh_release to download the app (handles version tracking) # 4. Configure the application # 5. Create systemd service # 6. Finalize with motd_ssh, customize, cleanup_lxc # --- Setup runtimes/databases --- NODE_VERSION="22" setup_nodejs get_lxc_ip # --- Download and install app --- fetch_and_deploy_gh_release "[appname]" "[owner/repo]" "tarball" "latest" "/opt/[appname]" msg_info "Setting up [AppName]" cd /opt/[appname] # $STD npm ci msg_ok "Setup [AppName]" # ============================================================================= # CONFIGURATION # ============================================================================= msg_info "Configuring [AppName]" cd /opt/[appname] # Install application dependencies (uncomment as needed): # $STD npm ci --production # Node.js apps # $STD uv sync --frozen # Python apps # $STD composer install --no-dev # PHP apps # $STD cargo build --release # Rust apps # Create .env file if needed: cat </opt/[appname]/.env # Use import_local_ip to get container IP, or hardcode if building on Proxmox APP_URL=http://localhost PORT=8080 EOF msg_ok "Configured [AppName]" # ============================================================================= # CREATE SYSTEMD SERVICE # ============================================================================= msg_info "Creating Service" cat </etc/systemd/system/[appname].service [Unit] Description=[AppName] Service After=network.target [Service] Type=simple User=root WorkingDirectory=/opt/[appname] ExecStart=/usr/bin/node /opt/[appname]/server.js Restart=on-failure RestartSec=5 [Install] WantedBy=multi-user.target EOF systemctl enable -q --now [appname] msg_ok "Created Service" # ============================================================================= # CLEANUP & FINALIZATION # ============================================================================= # These are called automatically, but shown here for clarity: # motd_ssh - Displays service info on SSH login # customize - Enables optional customizations # cleanup_lxc - Removes temp files, bash history, logs motd_ssh customize cleanup_lxc ================================================ FILE: docs/contribution/templates_json/AppName.json ================================================ { "name": "AppName", "slug": "appname", "categories": [ 0 ], "date_created": "2026-01-18", "type": "ct", "updateable": true, "privileged": false, "interface_port": 3000, "documentation": "https://docs.example.com/", "website": "https://example.com/", "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/appname.webp", "config_path": "/opt/appname/.env", "description": "Short description of what AppName does and its main features.", "install_methods": [ { "type": "default", "script": "ct/appname.sh", "resources": { "cpu": 2, "ram": 2048, "hdd": 8, "os": "Debian", "version": "13" } } ], "default_credentials": { "username": null, "password": null }, "notes": [ { "text": "Change the default password after first login!", "type": "warning" } ] } ================================================ FILE: docs/contribution/templates_json/AppName.md ================================================ # Website Metadata - Quick Reference Metadata (name, slug, description, logo, categories, etc.) controls how your application appears on the website. You do **not** add JSON files to the repo — you request changes via the website. --- ## How to Request or Update Metadata 1. **Go to the script on the website** — Open the [ProxmoxVE website](https://community-scripts.github.io/ProxmoxVE/), find your script (or the script you want to update). 2. **Press the "Report issue" button** on that script’s page. 3. **Follow the guide** — The flow will walk you through submitting or updating metadata. --- ## Metadata Structure (Reference) The following describes the structure of script metadata used by the website. Use it as reference when filling out the form or describing what you need. ```json { "name": "MyApp", "slug": "myapp", "categories": [1], "date_created": "2026-01-18", "type": "ct", "updateable": true, "privileged": false, "interface_port": 3000, "documentation": "https://docs.example.com/", "website": "https://example.com/", "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/myapp.webp", "config_path": "/opt/myapp/.env", "description": "Brief description of what MyApp does", "install_methods": [ { "type": "default", "script": "ct/myapp.sh", "resources": { "cpu": 2, "ram": 2048, "hdd": 8, "os": "Debian", "version": "13" } } ], "default_credentials": { "username": null, "password": null }, "notes": [ { "text": "Change the default password after first login!", "type": "warning" } ] } ``` --- ## Field Reference | Field | Required | Example | Notes | | --------------------- | -------- | ----------------- | ---------------------------------------------- | | `name` | Yes | "MyApp" | Display name | | `slug` | Yes | "myapp" | URL-friendly identifier (lowercase, no spaces) | | `categories` | Yes | [1] | One or more category IDs | | `date_created` | Yes | "2026-01-18" | Format: YYYY-MM-DD | | `type` | Yes | "ct" | Container type: "ct" or "vm" | | `interface_port` | Yes | 3000 | Default web interface port | | `logo` | No | "https://..." | Logo URL (64px x 64px PNG) | | `config_path` | Yes | "/opt/myapp/.env" | Main config file location | | `description` | Yes | "App description" | Brief description (100 chars) | | `install_methods` | Yes | See below | Installation resources (array) | | `default_credentials` | No | See below | Optional default login | | `notes` | No | See below | Additional notes (array) | --- ## Install Methods Each installation method specifies resource requirements: ```json "install_methods": [ { "type": "default", "script": "ct/myapp.sh", "resources": { "cpu": 2, "ram": 2048, "hdd": 8, "os": "Debian", "version": "13" } } ] ``` **Resource Defaults:** - CPU: Cores (1-8) - RAM: Megabytes (256-4096) - Disk: Gigabytes (4-50) --- ## Common Categories - `0` Miscellaneous - `1` Proxmox & Virtualization - `2` Operating Systems - `3` Containers & Docker - `4` Network & Firewall - `5` Adblock & DNS - `6` Authentication & Security - `7` Backup & Recovery - `8` Databases - `9` Monitoring & Analytics - `10` Dashboards & Frontends - `11` Files & Downloads - `12` Documents & Notes - `13` Media & Streaming - `14` \*Arr Suite - `15` NVR & Cameras - `16` IoT & Smart Home - `17` ZigBee, Z-Wave & Matter - `18` MQTT & Messaging - `19` Automation & Scheduling - `20` AI / Coding & Dev-Tools - `21` Webservers & Proxies - `22` Bots & ChatOps - `23` Finance & Budgeting - `24` Gaming & Leisure - `25` Business & ERP --- ## Best Practices 1. **Use the JSON Generator** - It validates structure 2. **Keep descriptions short** - 100 characters max 3. **Use real resource requirements** - Based on your testing 4. **Include sensible defaults** - Pre-filled in install_methods 5. **Slug must be lowercase** - No spaces, use hyphens --- ## See Examples on the Website View script pages on the [ProxmoxVE website](https://community-scripts.github.io/ProxmoxVE/) to see how metadata is displayed for existing scripts. --- ## Need Help? - **Request metadata** — Use the Report issue button on the script’s page on the website (see [How to Request or Update Metadata](#how-to-request-or-update-metadata) above). - **[JSON Generator](https://community-scripts.github.io/ProxmoxVE/json-editor)** - Reference only; structure validation - **[README.md](../README.md)** - Full contribution workflow ================================================ FILE: docs/ct/DETAILED_GUIDE.md ================================================ # 🚀 **Application Container Scripts (ct/AppName.sh)** **Modern Guide to Creating LXC Container Installation Scripts** > **Updated**: December 2025 > **Context**: Fully integrated with build.func, advanced_settings wizard, and defaults system > **Example Used**: `/ct/pihole.sh`, `/ct/docker.sh` --- ## 📋 Table of Contents - [Overview](#overview) - [Architecture & Flow](#architecture--flow) - [File Structure](#file-structure) - [Complete Script Template](#complete-script-template) - [Function Reference](#function-reference) - [Advanced Features](#advanced-features) - [Real Examples](#real-examples) - [Troubleshooting](#troubleshooting) - [Contribution Checklist](#contribution-checklist) --- ## Overview ### Purpose Container scripts (`ct/AppName.sh`) are **entry points for creating LXC containers** with specific applications pre-installed. They: 1. Define container defaults (CPU, RAM, disk, OS) 2. Call the main build orchestrator (`build.func`) 3. Implement application-specific update mechanisms 4. Provide user-facing success messages ### Execution Context ``` Proxmox Host ↓ ct/AppName.sh sourced (runs as root on host) ↓ build.func: Creates LXC container + runs install script inside ↓ install/AppName-install.sh (runs inside container) ↓ Container ready with app installed ``` ### Key Integration Points - **build.func** - Main orchestrator (container creation, storage, variable management) - **install.func** - Container-specific setup (OS update, package management) - **tools.func** - Tool installation helpers (repositories, GitHub releases) - **core.func** - UI/messaging functions (colors, spinners, validation) - **error_handler.func** - Error handling and signal management --- ## Architecture & Flow ### Container Creation Flow ``` START: bash ct/pihole.sh ↓ [1] Set APP, var_*, defaults ↓ [2] header_info() → Display ASCII art ↓ [3] variables() → Parse arguments & load build.func ↓ [4] color() → Setup ANSI codes ↓ [5] catch_errors() → Setup trap handlers ↓ [6] install_script() → Show mode menu (5 options) ↓ ├─ INSTALL_MODE="0" (Default) ├─ INSTALL_MODE="1" (Advanced - 19-step wizard) ├─ INSTALL_MODE="2" (User Defaults) ├─ INSTALL_MODE="3" (App Defaults) └─ INSTALL_MODE="4" (Settings Menu) ↓ [7] advanced_settings() → Collect user configuration (if mode=1) ↓ [8] start() → Confirm or re-edit settings ↓ [9] build_container() → Create LXC + execute install script ↓ [10] description() → Set container description ↓ [11] SUCCESS → Display access URL ↓ END ``` ### Default Values Precedence ``` Priority 1 (Highest): Environment Variables (var_cpu, var_ram, etc.) Priority 2: App-Specific Defaults (/defaults/AppName.vars) Priority 3: User Global Defaults (/default.vars) Priority 4 (Lowest): Built-in Defaults (in build.func) ``` --- ## File Structure ### Minimal ct/AppName.sh Template ``` #!/usr/bin/env bash # [1] Shebang # [2] Copyright/License source <(curl -s .../misc/build.func) # [3] Import functions # [4] APP metadata APP="AppName" # [5] Default values var_tags="tag1;tag2" var_cpu="2" var_ram="2048" ... header_info "$APP" # [6] Display header variables # [7] Process arguments color # [8] Setup colors catch_errors # [9] Setup error handling function update_script() { ... } # [10] Update function (optional) start # [11] Launch container creation build_container description msg_ok "Completed successfully!\n" ``` --- ## Complete Script Template ### 1. File Header & Imports ```bash #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: YourUsername # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/example/project # Import main orchestrator source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVE/raw/branch/main/misc/build.func) ``` > **⚠️ IMPORTANT**: Before opening a PR, change URL to `community-scripts` repo! ### 2. Application Metadata ```bash # Application Configuration APP="ApplicationName" var_tags="tag1;tag2;tag3" # Max 3-4 tags, no spaces, semicolon-separated # Container Resources var_cpu="2" # CPU cores var_ram="2048" # RAM in MB var_disk="10" # Disk in GB # Container Type & OS var_os="debian" # Options: alpine, debian, ubuntu var_version="12" # Alpine: 3.20+, Debian: 11-13, Ubuntu: 20.04+ var_unprivileged="1" # 1=unprivileged (secure), 0=privileged (rarely needed) ``` **Variable Naming Convention**: - Variables exposed to user: `var_*` (e.g., `var_cpu`, `var_hostname`, `var_ssh`) - Internal variables: lowercase (e.g., `container_id`, `app_version`) ### 3. Display & Initialization ```bash # Display header ASCII art header_info "$APP" # Process command-line arguments and load configuration variables # Setup ANSI color codes and formatting color # Initialize error handling (trap ERR, EXIT, INT, TERM) catch_errors ``` ### 4. Update Function (Highly Recommended) ```bash function update_script() { header_info # Always start with these checks check_container_storage check_container_resources # Verify app is installed if [[ ! -d /opt/appname ]]; then msg_error "No ${APP} Installation Found!" exit fi # Get latest version from GitHub RELEASE=$(curl -fsSL https://api.github.com/repos/user/repo/releases/latest | \ grep "tag_name" | awk '{print substr($2, 2, length($2)-3)}') # Compare with saved version if [[ ! -f /opt/${APP}_version.txt ]] || [[ "${RELEASE}" != "$(cat /opt/${APP}_version.txt)" ]]; then msg_info "Updating ${APP} to v${RELEASE}" # Backup user data cp -r /opt/appname /opt/appname-backup # Perform update cd /opt wget -q "https://github.com/user/repo/releases/download/v${RELEASE}/app-${RELEASE}.tar.gz" tar -xzf app-${RELEASE}.tar.gz # Restore user data cp /opt/appname-backup/config/* /opt/appname/config/ # Cleanup rm -rf app-${RELEASE}.tar.gz /opt/appname-backup # Save new version echo "${RELEASE}" > /opt/${APP}_version.txt msg_ok "Updated ${APP} to v${RELEASE}" else msg_ok "No update required. ${APP} is already at v${RELEASE}." fi exit } ``` ### 5. Script Launch ```bash # Start the container creation workflow start # Build the container with selected configuration build_container # Set container description/notes in Proxmox UI description # Display success message msg_ok "Completed successfully!\n" echo -e "${CREATING}${GN}${APP} 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}" ``` --- ## Function Reference ### Core Functions (From build.func) #### `variables()` **Purpose**: Initialize container variables, load user arguments, setup orchestration **Triggered by**: Called automatically at script start **Behavior**: 1. Parse command-line arguments (if any) 2. Generate random UUID for session tracking 3. Load container storage from Proxmox 4. Initialize application-specific defaults 5. Setup SSH/environment configuration #### `start()` **Purpose**: Launch the container creation menu with 5 installation modes **Menu Options**: ``` 1. Default Installation (Quick setup, predefined settings) 2. Advanced Installation (19-step wizard with full control) 3. User Defaults (Load ~/.community-scripts/default.vars) 4. App Defaults (Load /defaults/AppName.vars) 5. Settings Menu (Interactive mode selection) ``` #### `build_container()` **Purpose**: Main orchestrator for LXC container creation **Operations**: 1. Validates all variables 2. Creates LXC container via `pct create` 3. Executes `install/AppName-install.sh` inside container 4. Monitors installation progress 5. Handles errors and rollback on failure #### `description()` **Purpose**: Set container description/notes visible in Proxmox UI --- ## Advanced Features ### 1. Custom Configuration Menus If your app has additional setup beyond standard vars: ```bash custom_app_settings() { CONFIGURE_DB=$(whiptail --title "Database Setup" \ --yesno "Would you like to configure a custom database?" 8 60) if [[ $? -eq 0 ]]; then DB_HOST=$(whiptail --inputbox "Database Host:" 8 60 3>&1 1>&2 2>&3) DB_PORT=$(whiptail --inputbox "Database Port:" 8 60 "3306" 3>&1 1>&2 2>&3) fi } custom_app_settings ``` ### 2. Update Function Patterns Save installed version for update checks ### 3. Health Check Functions Add custom validation: ```bash function health_check() { header_info if [[ ! -d /opt/appname ]]; then msg_error "Application not found!" exit 1 fi if ! systemctl is-active --quiet appname; then msg_error "Application service not running" exit 1 fi msg_ok "Health check passed" } ``` --- ## Real Examples ### Example 1: Simple Web App (Debian-based) ```bash #!/usr/bin/env bash source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVE/raw/branch/main/misc/build.func) APP="Homarr" var_tags="dashboard;homepage" var_cpu="2" var_ram="1024" var_disk="5" var_os="debian" var_version="12" var_unprivileged="1" header_info "$APP" variables color catch_errors function update_script() { # Update logic here } start build_container description msg_ok "Completed successfully!\n" ``` --- ## Troubleshooting ### Container Creation Fails **Symptom**: `pct create` exits with error code 209 **Solution**: ```bash # Check existing containers pct list | grep CTID # Remove conflicting container pct destroy CTID # Retry ct/AppName.sh ``` ### Update Function Doesn't Detect New Version **Debug**: ```bash # Check version file cat /opt/AppName_version.txt # Test GitHub API curl -fsSL https://api.github.com/repos/user/repo/releases/latest | grep tag_name ``` --- ## Contribution Checklist Before submitting a PR: ### Script Structure - [ ] Shebang is `#!/usr/bin/env bash` - [ ] Imports `build.func` from community-scripts repo - [ ] Copyright header with author and source URL - [ ] APP variable matches filename - [ ] `var_tags` are semicolon-separated (no spaces) ### Default Values - [ ] `var_cpu` set appropriately (2-4 for most apps) - [ ] `var_ram` set appropriately (1024-4096 MB minimum) - [ ] `var_disk` sufficient for app + data (5-20 GB) - [ ] `var_os` is realistic ### Functions - [ ] `update_script()` implemented - [ ] Update function checks if app installed - [ ] Proper error handling with `msg_error` ### Testing - [ ] Script tested with default installation - [ ] Script tested with advanced (19-step) installation - [ ] Update function tested on existing installation --- ## Best Practices ### ✅ DO: 1. **Use meaningful defaults** 2. **Implement version tracking** 3. **Handle edge cases** 4. **Use proper messaging with msg_info/msg_ok/msg_error** ### ❌ DON'T: 1. **Hardcode versions** 2. **Use custom color codes** (use built-in variables) 3. **Forget error handling** 4. **Leave temporary files** --- **Last Updated**: December 2025 **Compatibility**: ProxmoxVE with build.func v3+ ================================================ FILE: docs/ct/README.md ================================================ # Container Scripts Documentation (/ct) This directory contains comprehensive documentation for container creation scripts in the `/ct` directory. ## Overview Container scripts (`ct/*.sh`) are the entry points for creating LXC containers in Proxmox VE. They run on the host and orchestrate the entire container creation process. ## Documentation Structure Each script has standardized documentation following the project pattern. ## Key Resources - **[DETAILED_GUIDE.md](DETAILED_GUIDE.md)** - Complete reference for creating ct scripts - **[../contribution/README.md](../contribution/README.md)** - How to contribute - **[../misc/build.func/](../misc/build.func/)** - Core orchestrator documentation ## Container Creation Flow ``` ct/AppName.sh (host-side) │ ├─ Calls: build.func (orchestrator) │ ├─ Variables: var_cpu, var_ram, var_disk, var_os │ └─ Creates: LXC Container │ └─ Runs: install/appname-install.sh (inside) ``` ## Available Scripts See `/ct` directory for all container creation scripts. Common examples: - `pihole.sh` - Pi-hole DNS/DHCP server - `docker.sh` - Docker container runtime - `wallabag.sh` - Article reading & archiving - `nextcloud.sh` - Private cloud storage - `debian.sh` - Basic Debian container - And 30+ more... ## Quick Start To understand how to create a container script: 1. Read: [UPDATED_APP-ct.md](../UPDATED_APP-ct.md) 2. Study: A similar existing script in `/ct` 3. Copy template and customize 4. Test locally 5. Submit PR ## Contributing a New Container 1. Create `ct/myapp.sh` 2. Create `install/myapp-install.sh` 3. Follow template in [UPDATED_APP-ct.md](../UPDATED_APP-ct.md) 4. Test thoroughly 5. Submit PR with both files ## Common Tasks - **Add new container application** → [contribution/README.md](../contribution/README.md) - **Debug container creation** → [EXIT_CODES.md](../EXIT_CODES.md) - **Understand build.func** → [misc/build.func/](../misc/build.func/) - **Development mode debugging** → [DEV_MODE.md](../DEV_MODE.md) --- **Last Updated**: December 2025 **Maintainers**: community-scripts team ================================================ FILE: docs/guides/CONFIGURATION_REFERENCE.md ================================================ # Configuration Reference **Complete reference for all configuration variables and options in community-scripts for Proxmox VE.** --- ## Table of Contents 1. [Variable Naming Convention](#variable-naming-convention) 2. [Complete Variable Reference](#complete-variable-reference) 3. [Resource Configuration](#resource-configuration) 4. [Network Configuration](#network-configuration) 5. [IPv6 Configuration](#ipv6-configuration) 6. [SSH Configuration](#ssh-configuration) 7. [Container Features](#container-features) 8. [Storage Configuration](#storage-configuration) 9. [Security Settings](#security-settings) 10. [Advanced Options](#advanced-options) 11. [Quick Reference Table](#quick-reference-table) --- ## Variable Naming Convention All configuration variables follow a consistent pattern: ``` var_= ``` **Rules:** - ✅ Always starts with `var_` - ✅ Lowercase letters only - ✅ Underscores for word separation - ✅ No spaces around `=` - ✅ Values can be quoted if needed **Examples:** ```bash # ✓ Correct var_cpu=4 var_hostname=myserver var_ssh_authorized_key=ssh-rsa AAAA... # ✗ Wrong CPU=4 # Missing var_ prefix var_CPU=4 # Uppercase not allowed var_cpu = 4 # Spaces around = var-cpu=4 # Hyphens not allowed ``` --- ## Complete Variable Reference ### var_unprivileged **Type:** Boolean (0 or 1) **Default:** `1` (unprivileged) **Description:** Determines if container runs unprivileged (recommended) or privileged. ```bash var_unprivileged=1 # Unprivileged (safer, recommended) var_unprivileged=0 # Privileged (less secure, more features) ``` **When to use privileged (0):** - Hardware access required - Certain kernel modules needed - Legacy applications - Nested virtualization with full features **Security Impact:** - Unprivileged: Container root is mapped to unprivileged user on host - Privileged: Container root = host root (security risk) --- ### var_cpu **Type:** Integer **Default:** Varies by app (usually 1-4) **Range:** 1 to host CPU count **Description:** Number of CPU cores allocated to container. ```bash var_cpu=1 # Single core (minimal) var_cpu=2 # Dual core (typical) var_cpu=4 # Quad core (recommended for apps) var_cpu=8 # High performance ``` **Best Practices:** - Start with 2 cores for most applications - Monitor usage with `pct exec -- htop` - Can be changed after creation - Consider host CPU count (don't over-allocate) --- ### var_ram **Type:** Integer (MB) **Default:** Varies by app (usually 512-2048) **Range:** 512 MB to host RAM **Description:** Amount of RAM in megabytes. ```bash var_ram=512 # 512 MB (minimal) var_ram=1024 # 1 GB (typical) var_ram=2048 # 2 GB (comfortable) var_ram=4096 # 4 GB (recommended for databases) var_ram=8192 # 8 GB (high memory apps) ``` **Conversion Guide:** ``` 512 MB = 0.5 GB 1024 MB = 1 GB 2048 MB = 2 GB 4096 MB = 4 GB 8192 MB = 8 GB 16384 MB = 16 GB ``` **Best Practices:** - Minimum 512 MB for basic Linux - 1 GB for typical applications - 2-4 GB for web servers, databases - Monitor with `free -h` inside container --- ### var_disk **Type:** Integer (GB) **Default:** Varies by app (usually 2-8) **Range:** 0.001 GB to storage capacity **Description:** Root disk size in gigabytes. ```bash var_disk=2 # 2 GB (minimal OS only) var_disk=4 # 4 GB (typical) var_disk=8 # 8 GB (comfortable) var_disk=20 # 20 GB (recommended for apps) var_disk=50 # 50 GB (large applications) var_disk=100 # 100 GB (databases, media) ``` **Important Notes:** - Can be expanded after creation (not reduced) - Actual space depends on storage type - Thin provisioning supported on most storage - Plan for logs, data, updates **Recommended Sizes by Use Case:** ``` Basic Linux container: 4 GB Web server (Nginx/Apache): 8 GB Application server: 10-20 GB Database server: 20-50 GB Docker host: 30-100 GB Media server: 100+ GB ``` --- ### var_hostname **Type:** String **Default:** Application name **Max Length:** 63 characters **Description:** Container hostname (FQDN format allowed). ```bash var_hostname=myserver var_hostname=pihole var_hostname=docker-01 var_hostname=web.example.com ``` **Rules:** - Lowercase letters, numbers, hyphens - Cannot start or end with hyphen - No underscores allowed - No spaces **Best Practices:** ```bash # ✓ Good var_hostname=web-server var_hostname=db-primary var_hostname=app.domain.com # ✗ Avoid var_hostname=Web_Server # Uppercase, underscore var_hostname=-server # Starts with hyphen var_hostname=my server # Contains space ``` --- ### var_brg **Type:** String **Default:** `vmbr0` **Description:** Network bridge interface. ```bash var_brg=vmbr0 # Default Proxmox bridge var_brg=vmbr1 # Custom bridge var_brg=vmbr2 # Isolated network ``` **Common Setups:** ``` vmbr0 → Main network (LAN) vmbr1 → Guest network vmbr2 → DMZ vmbr3 → Management vmbr4 → Storage network ``` **Check available bridges:** ```bash ip link show | grep vmbr # or brctl show ``` --- ### var_net **Type:** String **Options:** `dhcp` or `static` **Default:** `dhcp` **Description:** IPv4 network configuration method. ```bash var_net=dhcp # Automatic IP via DHCP var_net=static # Manual IP configuration ``` **DHCP Mode:** - Automatic IP assignment - Easy setup - Good for development - Requires DHCP server on network **Static Mode:** - Fixed IP address - Requires gateway configuration - Better for servers - Configure via advanced settings or after creation --- ### var_gateway **Type:** IPv4 Address **Default:** Auto-detected from host **Description:** Network gateway IP address. ```bash var_gateway=192.168.1.1 var_gateway=10.0.0.1 var_gateway=172.16.0.1 ``` **Auto-detection:** If not specified, system detects gateway from host: ```bash ip route | grep default ``` **When to specify:** - Multiple gateways available - Custom routing setup - Different network segment --- ### var_vlan **Type:** Integer **Range:** 1-4094 **Default:** None **Description:** VLAN tag for network isolation. ```bash var_vlan=10 # VLAN 10 var_vlan=100 # VLAN 100 var_vlan=200 # VLAN 200 ``` **Common VLAN Schemes:** ``` VLAN 10 → Management VLAN 20 → Servers VLAN 30 → Desktops VLAN 40 → Guest WiFi VLAN 50 → IoT devices VLAN 99 → DMZ ``` **Requirements:** - Switch must support VLANs - Proxmox bridge configured for VLAN aware - Gateway on same VLAN --- ### var_mtu **Type:** Integer **Default:** `1500` **Range:** 68-9000 **Description:** Maximum Transmission Unit size. ```bash var_mtu=1500 # Standard Ethernet var_mtu=1492 # PPPoE var_mtu=9000 # Jumbo frames ``` **Common Values:** ``` 1500 → Standard Ethernet (default) 1492 → PPPoE connections 1400 → Some VPN setups 9000 → Jumbo frames (10GbE networks) ``` **When to change:** - Jumbo frames for performance on 10GbE - PPPoE internet connections - VPN tunnels with overhead - Specific network requirements --- ### var_mac **Type:** MAC Address **Format:** `XX:XX:XX:XX:XX:XX` **Default:** Auto-generated **Description:** Container MAC address. ```bash var_mac=02:00:00:00:00:01 var_mac=DE:AD:BE:EF:00:01 ``` **When to specify:** - MAC-based licensing - Static DHCP reservations - Network access control - Cloning configurations **Best Practices:** - Use locally administered addresses (2nd bit set) - Start with `02:`, `06:`, `0A:`, `0E:` - Avoid vendor OUIs - Document custom MACs --- ### var_ipv6_method **Type:** String **Options:** `auto`, `dhcp`, `static`, `none`, `disable` **Default:** `none` **Description:** IPv6 configuration method. ```bash var_ipv6_method=auto # SLAAC (auto-configuration) var_ipv6_method=dhcp # DHCPv6 var_ipv6_method=static # Manual configuration var_ipv6_method=none # IPv6 enabled but not configured var_ipv6_method=disable # IPv6 completely disabled ``` **Detailed Options:** **auto (SLAAC)** - Stateless Address Auto-Configuration - Router advertisements - No DHCPv6 server needed - Recommended for most cases **dhcp (DHCPv6)** - Stateful configuration - Requires DHCPv6 server - More control over addressing **static** - Manual IPv6 address - Manual gateway - Full control **none** - IPv6 stack active - No address configured - Can configure later **disable** - IPv6 completely disabled at kernel level - Use when IPv6 causes issues - Sets `net.ipv6.conf.all.disable_ipv6=1` --- ### var_ns **Type:** IP Address **Default:** Auto (from host) **Description:** DNS nameserver IP. ```bash var_ns=8.8.8.8 # Google DNS var_ns=1.1.1.1 # Cloudflare DNS var_ns=9.9.9.9 # Quad9 DNS var_ns=192.168.1.1 # Local DNS ``` **Common DNS Servers:** ``` 8.8.8.8, 8.8.4.4 → Google Public DNS 1.1.1.1, 1.0.0.1 → Cloudflare DNS 9.9.9.9, 149.112.112.112 → Quad9 DNS 208.67.222.222 → OpenDNS 192.168.1.1 → Local router/Pi-hole ``` --- ### var_ssh **Type:** Boolean **Options:** `yes` or `no` **Default:** `no` **Description:** Enable SSH server in container. ```bash var_ssh=yes # SSH server enabled var_ssh=no # SSH server disabled (console only) ``` **When enabled:** - OpenSSH server installed - Started on boot - Port 22 open - Root login allowed **Security Considerations:** - Disable if not needed - Use SSH keys instead of passwords - Consider non-standard port - Firewall rules recommended --- ### var_ssh_authorized_key **Type:** String (SSH public key) **Default:** None **Description:** SSH public key for root user. ```bash var_ssh_authorized_key=ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC... user@host var_ssh_authorized_key=ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAA... user@host ``` **Supported Key Types:** - RSA (2048-4096 bits) - Ed25519 (recommended) - ECDSA - DSA (deprecated) **How to get your public key:** ```bash cat ~/.ssh/id_rsa.pub # or cat ~/.ssh/id_ed25519.pub ``` **Multiple keys:** Separate with newlines (in file) or use multiple deployments. --- ### var_pw **Type:** String **Default:** Empty (auto-login) **Description:** Root password. ```bash var_pw=SecurePassword123! # Set password var_pw= # Auto-login (empty) ``` **Auto-login behavior:** - No password required for console - Automatic login on console access - SSH still requires key if enabled - Suitable for development **Password best practices:** - Minimum 12 characters - Mix upper/lower/numbers/symbols - Use password manager - Rotate regularly --- ### var_nesting **Type:** Boolean (0 or 1) **Default:** `1` **Description:** Allow nested containers (required for Docker). ```bash var_nesting=1 # Nested containers allowed var_nesting=0 # Nested containers disabled ``` **Required for:** - Docker - LXC inside LXC - Systemd features - Container orchestration **Security Impact:** - Slightly reduced isolation - Required for container platforms - Generally safe when unprivileged --- ### var_diagnostics **Type:** Boolean (yes or no) **Default:** `yes` **Description:** Determines if anonymous telemetry and diagnostic data is sent to Community-Scripts API. ```bash var_diagnostics=yes # Allow telemetry (helps us improve scripts) var_diagnostics=no # Disable all telemetry ``` **Privacy & Usage:** - Data is strictly anonymous (random session ID) - Reports success/failure of installations - Maps error codes (e.g., APT lock, out of RAM) - No user-specific data, hostnames, or secret keys are ever sent --- ### var_gpu **Type:** Boolean/Toggle **Options:** `yes` or `no` **Default:** `no` **Description:** Enable GPU passthrough for the container. ```bash var_gpu=yes # Enable GPU passthrough (auto-detect) var_gpu=no # Disable GPU passthrough (default) ``` **Features enabled:** - Auto-detects Intel (QuickSync), NVIDIA, and AMD GPUs - Passes through `/dev/dri` and render nodes - Configures appropriate container permissions - Crucial for media servers (Plex, Jellyfin, Immich) **Prerequisites:** - Host drivers installed correctly - Hardware present and visible to Proxmox - IOMMU enabled (for some configurations) --- ### var_tun **Type:** Boolean/Toggle **Options:** `yes` or `no` **Default:** `no` **Description:** Enable TUN/TAP device support. ```bash var_tun=yes # Enable TUN/TAP support var_tun=no # Disable TUN/TAP support (default) ``` **Required for:** - VPN software (WireGuard, OpenVPN) - Network tunneling (Tailscale, ZeroTier) - Custom network bridges --- ### var_keyctl **Type:** Boolean (0 or 1) **Default:** `0` **Description:** Enable keyctl system call. ```bash var_keyctl=1 # Keyctl enabled var_keyctl=0 # Keyctl disabled ``` **Required for:** - Docker in some configurations - Systemd keyring features - Encryption key management - Some authentication systems --- ### var_fuse **Type:** Boolean/Toggle **Options:** `yes` or `no` **Default:** `no` **Description:** Enable FUSE filesystem support. ```bash var_fuse=yes # FUSE enabled var_fuse=no # FUSE disabled ``` **Required for:** - sshfs - AppImage - Some backup tools - User-space filesystems --- ### var_mknod **Type:** Boolean (0 or 1) **Default:** `0` **Description:** Allow device node creation. ```bash var_mknod=1 # Device nodes allowed var_mknod=0 # Device nodes disabled ``` **Requires:** - Kernel 5.3+ - Experimental feature - Use with caution --- ### var_mount_fs **Type:** String (comma-separated) **Default:** Empty **Description:** Allowed mountable filesystems. ```bash var_mount_fs=nfs var_mount_fs=nfs,cifs var_mount_fs=ext4,xfs,nfs ``` **Common Options:** ``` nfs → NFS network shares cifs → SMB/CIFS shares ext4 → Ext4 filesystems xfs → XFS filesystems btrfs → Btrfs filesystems ``` --- ### var_protection **Type:** Boolean **Options:** `yes` or `no` **Default:** `no` **Description:** Prevent accidental deletion. ```bash var_protection=yes # Protected from deletion var_protection=no # Can be deleted normally ``` **When protected:** - Cannot delete via GUI - Cannot delete via `pct destroy` - Must disable protection first - Good for production containers --- ### var_tags **Type:** String (comma-separated) **Default:** `community-script` **Description:** Container tags for organization. ```bash var_tags=production var_tags=production,webserver var_tags=dev,testing,temporary ``` **Best Practices:** ```bash # Environment tags var_tags=production var_tags=development var_tags=staging # Function tags var_tags=webserver,nginx var_tags=database,postgresql var_tags=cache,redis # Project tags var_tags=project-alpha,frontend var_tags=customer-xyz,billing # Combined var_tags=production,webserver,project-alpha ``` --- ### var_timezone **Type:** String (TZ database format) **Default:** Host timezone **Description:** Container timezone. ```bash var_timezone=Europe/Berlin var_timezone=America/New_York var_timezone=Asia/Tokyo ``` **Common Timezones:** ``` Europe/London Europe/Berlin Europe/Paris America/New_York America/Chicago America/Los_Angeles Asia/Tokyo Asia/Singapore Australia/Sydney UTC ``` **List all timezones:** ```bash timedatectl list-timezones ``` --- ### var_verbose **Type:** Boolean **Options:** `yes` or `no` **Default:** `no` **Description:** Enable verbose output. ```bash var_verbose=yes # Show all commands var_verbose=no # Silent mode ``` **When enabled:** - Shows all executed commands - Displays detailed progress - Useful for debugging - More log output --- ### var_apt_cacher **Type:** Boolean **Options:** `yes` or `no` **Default:** `no` **Description:** Use APT caching proxy. ```bash var_apt_cacher=yes var_apt_cacher=no ``` **Benefits:** - Faster package installs - Reduced bandwidth - Offline package cache - Speeds up multiple containers --- ### var_apt_cacher_ip **Type:** IP Address **Default:** None **Description:** APT cacher proxy IP. ```bash var_apt_cacher=yes var_apt_cacher_ip=192.168.1.100 ``` **Setup apt-cacher-ng:** ```bash apt install apt-cacher-ng # Runs on port 3142 ``` --- ### var_container_storage **Type:** String **Default:** Auto-detected **Description:** Storage for container. ```bash var_container_storage=local var_container_storage=local-zfs var_container_storage=pve-storage ``` **List available storage:** ```bash pvesm status ``` --- ### var_template_storage **Type:** String **Default:** Auto-detected **Description:** Storage for templates. ```bash var_template_storage=local var_template_storage=nfs-templates ``` --- ## Quick Reference Table | Variable | Type | Default | Example | |----------|------|---------|---------| | `var_unprivileged` | 0/1 | 1 | `var_unprivileged=1` | | `var_cpu` | int | varies | `var_cpu=4` | | `var_ram` | int (MB) | varies | `var_ram=4096` | | `var_disk` | int (GB) | varies | `var_disk=20` | | `var_hostname` | string | app name | `var_hostname=server` | | `var_brg` | string | vmbr0 | `var_brg=vmbr1` | | `var_net` | dhcp/static | dhcp | `var_net=dhcp` | | `var_gateway` | IP | auto | `var_gateway=192.168.1.1` | | `var_ipv6_method` | string | none | `var_ipv6_method=disable` | | `var_vlan` | int | - | `var_vlan=100` | | `var_mtu` | int | 1500 | `var_mtu=9000` | | `var_mac` | MAC | auto | `var_mac=02:00:00:00:00:01` | | `var_ns` | IP | auto | `var_ns=8.8.8.8` | | `var_ssh` | yes/no | no | `var_ssh=yes` | | `var_ssh_authorized_key` | string | - | `var_ssh_authorized_key=ssh-rsa...` | | `var_pw` | string | empty | `var_pw=password` | | `var_nesting` | 0/1 | 1 | `var_nesting=1` | | `var_keyctl` | 0/1 | 0 | `var_keyctl=1` | | `var_fuse` | 0/1 | 0 | `var_fuse=1` | | `var_mknod` | 0/1 | 0 | `var_mknod=1` | | `var_mount_fs` | string | - | `var_mount_fs=nfs,cifs` | | `var_protection` | yes/no | no | `var_protection=yes` | | `var_tags` | string | community-script | `var_tags=prod,web` | | `var_timezone` | string | host TZ | `var_timezone=Europe/Berlin` | | `var_verbose` | yes/no | no | `var_verbose=yes` | | `var_apt_cacher` | yes/no | no | `var_apt_cacher=yes` | | `var_apt_cacher_ip` | IP | - | `var_apt_cacher_ip=192.168.1.10` | | `var_container_storage` | string | auto | `var_container_storage=local-zfs` | | `var_template_storage` | string | auto | `var_template_storage=local` | --- ## See Also - [Defaults System Guide](DEFAULTS_GUIDE.md) - [Unattended Deployments](UNATTENDED_DEPLOYMENTS.md) - [Security Best Practices](SECURITY_GUIDE.md) - [Network Configuration](NETWORK_GUIDE.md) ================================================ FILE: docs/guides/DEFAULTS_SYSTEM_GUIDE.md ================================================ # Configuration & Defaults System - User Guide > **Complete Guide to App Defaults and User Defaults** > > *Learn how to configure, save, and reuse your installation settings* --- ## Table of Contents 1. [Quick Start](#quick-start) 2. [Understanding the Defaults System](#understanding-the-defaults-system) 3. [Installation Modes](#installation-modes) 4. [How to Save Defaults](#how-to-save-defaults) 5. [How to Use Saved Defaults](#how-to-use-saved-defaults) 6. [Managing Your Defaults](#managing-your-defaults) 7. [Advanced Configuration](#advanced-configuration) 8. [Troubleshooting](#troubleshooting) --- ## Quick Start ### 30-Second Setup ```bash # 1. Run any container installation script bash pihole-install.sh # 2. When prompted, select: "Advanced Settings" # (This allows you to customize everything) # 3. Answer all configuration questions # 4. At the end, when asked "Save as App Defaults?" # Select: YES # 5. Done! Your settings are now saved ``` **Next Time**: Run the same script again, select **"App Defaults"** and your settings will be applied automatically! --- ## Understanding the Defaults System ### The Three-Tier System Your installation settings are managed through three layers: #### 🔷 **Tier 1: Built-in Defaults** (Fallback) ``` These are hardcoded in the scripts Provide sensible defaults for each application Example: PiHole uses 2 CPU cores by default ``` #### 🔶 **Tier 2: User Defaults** (Global) ``` Your personal global defaults Applied to ALL container installations Location: /usr/local/community-scripts/default.vars Example: "I always want 4 CPU cores and 2GB RAM" ``` #### 🔴 **Tier 3: App Defaults** (Specific) ``` Application-specific saved settings Only applied when installing that specific app Location: /usr/local/community-scripts/defaults/.vars Example: "Whenever I install PiHole, use these exact settings" ``` ### Priority System When installing a container, settings are applied in this order: ``` ┌─────────────────────────────────────┐ │ 1. Environment Variables (HIGHEST) │ Set in shell: export var_cpu=8 │ (these override everything) │ ├─────────────────────────────────────┤ │ 2. App Defaults │ From: defaults/pihole.vars │ (app-specific saved settings) │ ├─────────────────────────────────────┤ │ 3. User Defaults │ From: default.vars │ (your global defaults) │ ├─────────────────────────────────────┤ │ 4. Built-in Defaults (LOWEST) │ Hardcoded in script │ (failsafe, always available) │ └─────────────────────────────────────┘ ``` **In Plain English**: - If you set an environment variable → it wins - Otherwise, if you have app-specific defaults → use those - Otherwise, if you have user defaults → use those - Otherwise, use the hardcoded defaults --- ## Installation Modes When you run any installation script, you'll be presented with a menu: ### Option 1️⃣ : **Default Settings** ``` Quick installation with standard settings ├─ Best for: First-time users, quick deployments ├─ What happens: │ 1. Script uses built-in defaults │ 2. Container created immediately │ 3. No questions asked └─ Time: ~2 minutes ``` **When to use**: You want a standard installation, don't need customization --- ### Option 2️⃣ : **Advanced Settings** ``` Full customization with 19 configuration steps ├─ Best for: Power users, custom requirements ├─ What happens: │ 1. Script asks for EVERY setting │ 2. You control: CPU, RAM, Disk, Network, SSH, etc. │ 3. Shows summary before creating │ 4. Offers to save as App Defaults └─ Time: ~5-10 minutes ``` **When to use**: You want full control over the configuration **Available Settings**: - CPU cores, RAM amount, Disk size - Container name, network settings - SSH access, API access, Features - Password, SSH keys, Tags --- ### Option 3️⃣ : **User Defaults** ``` Use your saved global defaults ├─ Best for: Consistent deployments across many containers ├─ Requires: You've previously saved User Defaults ├─ What happens: │ 1. Loads settings from: /usr/local/community-scripts/default.vars │ 2. Shows you the loaded settings │ 3. Creates container immediately └─ Time: ~2 minutes ``` **When to use**: You have preferred defaults you want to use for every app --- ### Option 4️⃣ : **App Defaults** (if available) ``` Use previously saved app-specific defaults ├─ Best for: Repeating the same configuration multiple times ├─ Requires: You've previously saved App Defaults for this app ├─ What happens: │ 1. Loads settings from: /usr/local/community-scripts/defaults/.vars │ 2. Shows you the loaded settings │ 3. Creates container immediately └─ Time: ~2 minutes ``` **When to use**: You've installed this app before and want identical settings --- ### Option 5️⃣ : **Settings Menu** ``` Manage your saved configurations ├─ Functions: │ • View current settings │ • Edit storage selections │ • Manage defaults location │ • See what's currently configured └─ Time: ~1 minute ``` **When to use**: You want to review or modify saved settings --- ## How to Save Defaults ### Method 1: Save While Installing This is the easiest way: #### Step-by-Step: Create App Defaults ```bash # 1. Run the installation script bash pihole-install.sh # 2. Choose installation mode # ┌─────────────────────────┐ # │ Select installation mode:│ # │ 1) Default Settings │ # │ 2) Advanced Settings │ # │ 3) User Defaults │ # │ 4) App Defaults │ # │ 5) Settings Menu │ # └─────────────────────────┘ # # Enter: 2 (Advanced Settings) # 3. Answer all configuration questions # • Container name? → my-pihole # • CPU cores? → 4 # • RAM amount? → 2048 # • Disk size? → 20 # • SSH access? → yes # ... (more options) # 4. Review summary (shown before creation) # ✓ Confirm to proceed # 5. After creation completes, you'll see: # ┌──────────────────────────────────┐ # │ Save as App Defaults for PiHole? │ # │ (Yes/No) │ # └──────────────────────────────────┘ # # Select: Yes # 6. Done! Settings saved to: # /usr/local/community-scripts/defaults/pihole.vars ``` #### Step-by-Step: Create User Defaults ```bash # Same as App Defaults, but: # When you select "Advanced Settings" # FIRST app you run with this selection will offer # to save as "User Defaults" additionally # This saves to: /usr/local/community-scripts/default.vars ``` --- ### Method 2: Manual File Creation For advanced users who want to create defaults without running installation: ```bash # Create User Defaults manually sudo tee /usr/local/community-scripts/default.vars > /dev/null << 'EOF' # Global User Defaults var_cpu=4 var_ram=2048 var_disk=20 var_unprivileged=1 var_brg=vmbr0 var_gateway=192.168.1.1 var_timezone=Europe/Berlin var_ssh=yes var_container_storage=local var_template_storage=local EOF # Create App Defaults manually sudo tee /usr/local/community-scripts/defaults/pihole.vars > /dev/null << 'EOF' # App-specific defaults for PiHole var_unprivileged=1 var_cpu=2 var_ram=1024 var_disk=10 var_brg=vmbr0 var_gateway=192.168.1.1 var_hostname=pihole var_container_storage=local var_template_storage=local EOF ``` --- ### Method 3: Using Environment Variables Set defaults via environment before running: ```bash # Set as environment variables export var_cpu=4 export var_ram=2048 export var_disk=20 export var_hostname=my-container # Run installation bash pihole-install.sh # These settings will be used # (Can still be overridden by saved defaults) ``` --- ## How to Use Saved Defaults ### Using User Defaults ```bash # 1. Run any installation script bash pihole-install.sh # 2. When asked for mode, select: # Option: 3 (User Defaults) # 3. Your settings from default.vars are applied # 4. Container created with your saved settings ``` ### Using App Defaults ```bash # 1. Run the app you configured before bash pihole-install.sh # 2. When asked for mode, select: # Option: 4 (App Defaults) # 3. Your settings from defaults/pihole.vars are applied # 4. Container created with exact same settings ``` ### Overriding Saved Defaults ```bash # Even if you have defaults saved, # you can override them with environment variables export var_cpu=8 # Override saved defaults export var_hostname=custom-name bash pihole-install.sh # Installation will use these values instead of saved defaults ``` --- ## Managing Your Defaults ### View Your Settings #### View User Defaults ```bash cat /usr/local/community-scripts/default.vars ``` #### View App Defaults ```bash cat /usr/local/community-scripts/defaults/pihole.vars ``` #### List All Saved App Defaults ```bash ls -la /usr/local/community-scripts/defaults/ ``` ### Edit Your Settings #### Edit User Defaults ```bash sudo nano /usr/local/community-scripts/default.vars ``` #### Edit App Defaults ```bash sudo nano /usr/local/community-scripts/defaults/pihole.vars ``` ### Update Existing Defaults ```bash # Run installation again with your app bash pihole-install.sh # Select: Advanced Settings # Make desired changes # At end, when asked to save: # "Defaults already exist, Update?" # Select: Yes # Your saved defaults are updated ``` ### Delete Defaults #### Delete User Defaults ```bash sudo rm /usr/local/community-scripts/default.vars ``` #### Delete App Defaults ```bash sudo rm /usr/local/community-scripts/defaults/pihole.vars ``` #### Delete All App Defaults ```bash sudo rm /usr/local/community-scripts/defaults/* ``` --- ## Advanced Configuration ### Available Variables All configurable variables start with `var_`: #### Resource Allocation ```bash var_cpu=4 # CPU cores var_ram=2048 # RAM in MB var_disk=20 # Disk in GB var_unprivileged=1 # 0=privileged, 1=unprivileged ``` #### Network ```bash var_brg=vmbr0 # Bridge interface var_net=dhcp # dhcp, static IP/CIDR, or IP range (see below) var_gateway=192.168.1.1 # Default gateway (required for static IP) var_mtu=1500 # MTU size var_vlan=100 # VLAN ID ``` #### IP Range Scanning You can specify an IP range instead of a static IP. The system will ping each IP in the range and automatically assign the first free IP: ```bash # Format: START_IP/CIDR-END_IP/CIDR var_net=192.168.1.100/24-192.168.1.200/24 var_gateway=192.168.1.1 ``` This is useful for automated deployments where you want static IPs but don't want to track which IPs are already in use. #### System ```bash var_hostname=pihole # Container name var_timezone=Europe/Berlin # Timezone var_pw=SecurePass123 # Root password var_tags=dns,pihole # Tags for organization var_verbose=yes # Enable verbose output ``` #### Security & Access ```bash var_ssh=yes # Enable SSH var_ssh_authorized_key="ssh-rsa AA..." # SSH public key var_protection=1 # Enable protection flag ``` #### Features ```bash var_fuse=1 # FUSE filesystem support var_tun=1 # TUN device support var_nesting=1 # Nesting (Docker in LXC) var_keyctl=1 # Keyctl syscall var_mknod=1 # Device node creation ``` #### Storage ```bash var_container_storage=local # Where to store container var_template_storage=local # Where to store templates ``` ### Example Configuration Files #### Gaming Server Defaults ```bash # High performance for gaming containers var_cpu=8 var_ram=4096 var_disk=50 var_unprivileged=0 var_fuse=1 var_nesting=1 var_tags=gaming ``` #### Development Server ```bash # Development with Docker support var_cpu=4 var_ram=2048 var_disk=30 var_unprivileged=1 var_nesting=1 var_ssh=yes var_tags=development ``` #### IoT/Monitoring ```bash # Low-resource, always-on containers var_cpu=2 var_ram=512 var_disk=10 var_unprivileged=1 var_nesting=0 var_fuse=0 var_tun=0 var_tags=iot,monitoring ``` --- ## Troubleshooting ### "App Defaults not available" Message **Problem**: You want to use App Defaults, but option says they're not available **Solution**: 1. You haven't created App Defaults yet for this app 2. Run the app with "Advanced Settings" 3. When finished, save as App Defaults 4. Next time, App Defaults will be available --- ### "Settings not being applied" **Problem**: You saved defaults, but they're not being used **Checklist**: ```bash # 1. Verify files exist ls -la /usr/local/community-scripts/default.vars ls -la /usr/local/community-scripts/defaults/.vars # 2. Check file permissions (should be readable) stat /usr/local/community-scripts/default.vars # 3. Verify correct mode selected # (Make sure you selected "User Defaults" or "App Defaults") # 4. Check for environment variable override env | grep var_ # If you have var_* set in environment, # those override your saved defaults ``` --- ### "Cannot write to defaults location" **Problem**: Permission denied when saving defaults **Solution**: ```bash # Create the defaults directory if missing sudo mkdir -p /usr/local/community-scripts/defaults # Fix permissions sudo chmod 755 /usr/local/community-scripts sudo chmod 755 /usr/local/community-scripts/defaults # Make sure you're running as root sudo bash pihole-install.sh ``` --- ### "Defaults directory doesn't exist" **Problem**: Script can't find where to save defaults **Solution**: ```bash # Create the directory sudo mkdir -p /usr/local/community-scripts/defaults # Verify ls -la /usr/local/community-scripts/ ``` --- ### Settings seem random or wrong **Problem**: Container gets different settings than expected **Possible Causes & Solutions**: ```bash # 1. Check if environment variables are set env | grep var_ # If you see var_* entries, those override your defaults # Clear them: unset var_cpu var_ram (etc) # 2. Verify correct defaults are in files cat /usr/local/community-scripts/default.vars cat /usr/local/community-scripts/defaults/pihole.vars # 3. Check which mode you actually selected # (Script output shows which defaults were applied) # 4. Check Proxmox logs for errors sudo journalctl -u pve-daemon -n 50 ``` --- ### "Variable not recognized" **Problem**: You set a variable that doesn't work **Solution**: Only certain variables are allowed (security whitelist): ``` Allowed variables (starting with var_): ✓ var_cpu, var_ram, var_disk, var_unprivileged ✓ var_brg, var_gateway, var_mtu, var_vlan, var_net ✓ var_hostname, var_pw, var_timezone ✓ var_ssh, var_ssh_authorized_key ✓ var_fuse, var_tun, var_nesting, var_keyctl ✓ var_container_storage, var_template_storage ✓ var_tags, var_verbose ✓ var_apt_cacher, var_apt_cacher_ip ✓ var_protection, var_mount_fs ✗ Other variables are NOT supported ``` --- ## Best Practices ### ✅ Do's ✓ Use **App Defaults** when you want app-specific settings ✓ Use **User Defaults** for your global preferences ✓ Edit defaults files directly with `nano` (safe) ✓ Keep separate App Defaults for each app ✓ Back up your defaults regularly ✓ Use environment variables for temporary overrides ### ❌ Don'ts ✗ Don't use `source` on defaults files (security risk) ✗ Don't put sensitive passwords in defaults (use SSH keys) ✗ Don't modify defaults while installation is running ✗ Don't delete defaults.d while containers are being created ✗ Don't use special characters without escaping --- ## Quick Reference ### Defaults Locations | Type | Location | Example | |------|----------|---------| | User Defaults | `/usr/local/community-scripts/default.vars` | Global settings | | App Defaults | `/usr/local/community-scripts/defaults/.vars` | PiHole-specific | | Backup Dir | `/usr/local/community-scripts/defaults/` | All app defaults | ### File Format ```bash # Comments start with # var_name=value # No spaces around = ✓ var_cpu=4 ✗ var_cpu = 4 # String values don't need quotes ✓ var_hostname=mycontainer ✓ var_hostname='mycontainer' # Values with spaces need quotes ✓ var_tags="docker,production,testing" ✗ var_tags=docker,production,testing ``` ### Command Reference ```bash # View defaults cat /usr/local/community-scripts/default.vars # Edit defaults sudo nano /usr/local/community-scripts/default.vars # List all app defaults ls /usr/local/community-scripts/defaults/ # Backup your defaults cp -r /usr/local/community-scripts/defaults/ ~/defaults-backup/ # Set temporary override export var_cpu=8 bash pihole-install.sh # Create custom defaults sudo tee /usr/local/community-scripts/defaults/custom.vars << 'EOF' var_cpu=4 var_ram=2048 EOF ``` --- ## Getting Help ### Need More Information? - 📖 [Main Documentation](../../docs/) - 🐛 [Report Issues](https://github.com/community-scripts/ProxmoxVE/issues) - 💬 [Discussions](https://github.com/community-scripts/ProxmoxVE/discussions) ### Useful Commands ```bash # Check what variables are available grep "var_" /path/to/app-install.sh | head -20 # Verify defaults syntax cat /usr/local/community-scripts/default.vars # Monitor installation with defaults bash pihole-install.sh 2>&1 | tee installation.log ``` --- ## Document Information | Field | Value | |-------|-------| | Version | 1.0 | | Last Updated | November 28, 2025 | | Status | Current | | License | MIT | --- **Happy configuring! 🚀** ================================================ FILE: docs/guides/README.md ================================================ # Configuration & Deployment Guides This directory contains comprehensive guides for configuring and deploying Proxmox VE containers using community-scripts. ## 📚 Available Guides ### [Configuration Reference](CONFIGURATION_REFERENCE.md) Complete reference for all configuration options, environment variables, and advanced settings available in the build system. **Topics covered:** - Container specifications (CPU, RAM, Disk) - Network configuration (IPv4/IPv6, VLAN, MTU) - Storage selection and management - Privilege modes and features - OS selection and versions ### [Defaults System Guide](DEFAULTS_SYSTEM_GUIDE.md) Understanding and customizing default settings for container deployments. **Topics covered:** - Default system settings - Per-script overrides - Custom defaults configuration - Environment variable precedence ### [Unattended Deployments](UNATTENDED_DEPLOYMENTS.md) Automating container deployments without user interaction. **Topics covered:** - Environment variable configuration - Batch deployments - CI/CD integration - Scripted installations - Pre-configured templates ## 🔗 Related Documentation - **[CT Scripts Guide](../ct/)** - Container script structure and usage - **[Install Scripts Guide](../install/)** - Installation script internals - **[API Documentation](../api/)** - API integration and endpoints - **[Build Functions](../misc/build.func/)** - Build system functions reference - **[Tools Functions](../misc/tools.func/)** - Utility functions reference ## 💡 Quick Start For most users, start with the **Unattended Deployments** guide to learn how to automate your container setups. For advanced configuration options, refer to the **Configuration Reference**. ## 🤝 Contributing If you'd like to improve these guides or add new ones, please see our [Contribution Guide](../contribution/). ================================================ FILE: docs/guides/UNATTENDED_DEPLOYMENTS.md ================================================ # Unattended Deployments Guide Complete guide for automated, zero-interaction container deployments using community-scripts for Proxmox VE. --- ## 🎯 What You'll Learn This comprehensive guide covers: - ✅ Complete automation of container deployments - ✅ Zero-interaction installations - ✅ Batch deployments (multiple containers) - ✅ Infrastructure as Code (Ansible, Terraform) - ✅ CI/CD pipeline integration - ✅ Error handling and rollback strategies - ✅ Production-ready deployment scripts - ✅ Security best practices --- ## Table of Contents 1. [Overview](#overview) 2. [Prerequisites](#prerequisites) 3. [Deployment Methods](#deployment-methods) 4. [Single Container Deployment](#single-container-deployment) 5. [Batch Deployments](#batch-deployments) 6. [Infrastructure as Code](#infrastructure-as-code) 7. [CI/CD Integration](#cicd-integration) 8. [Error Handling](#error-handling) 9. [Security Considerations](#security-considerations) --- ## Overview Unattended deployments allow you to: - ✅ Deploy containers without manual interaction - ✅ Automate infrastructure provisioning - ✅ Integrate with CI/CD pipelines - ✅ Maintain consistent configurations - ✅ Scale deployments across multiple nodes --- ## Prerequisites ### 1. Proxmox VE Access ```bash # Verify you have root access whoami # Should return: root # Check Proxmox version (8.0+ or 9.0-9.1 required) pveversion ``` ### 2. Network Connectivity ```bash # Test GitHub access curl -I https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/debian.sh # Test internet connectivity ping -c 1 1.1.1.1 ``` ### 3. Storage Available ```bash # List available storage pvesm status # Check free space df -h ``` --- ## Deployment Methods ### Method Comparison | Method | Use Case | Complexity | Flexibility | |--------|----------|------------|-------------| | **Environment Variables** | Quick one-offs | Low | High | | **App Defaults** | Repeat deployments | Low | Medium | | **Shell Scripts** | Batch operations | Medium | High | | **Ansible** | Infrastructure as Code | High | Very High | | **Terraform** | Cloud-native IaC | High | Very High | --- ## Single Container Deployment ### Basic Unattended Deployment **Simplest form:** ```bash var_hostname=myserver bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/debian.sh)" ``` ### Complete Configuration Example ```bash #!/bin/bash # deploy-single.sh - Deploy a single container with full configuration var_unprivileged=1 \ var_cpu=4 \ var_ram=4096 \ var_disk=30 \ var_hostname=production-app \ var_os=debian \ var_version=13 \ var_brg=vmbr0 \ var_net=dhcp \ var_ipv6_method=none \ var_ssh=yes \ var_ssh_authorized_key="ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQ... admin@workstation" \ var_nesting=1 \ var_tags=production,automated \ var_protection=yes \ var_verbose=no \ bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/debian.sh)" echo "✓ Container deployed successfully" ``` ### Using IP Range Scan for Automatic IP Assignment Instead of manually specifying static IPs, you can define an IP range. The system will automatically ping each IP and assign the first free one: ```bash #!/bin/bash # deploy-with-ip-scan.sh - Auto-assign first free IP from range var_unprivileged=1 \ var_cpu=4 \ var_ram=4096 \ var_hostname=web-server \ var_net=192.168.1.100/24-192.168.1.150/24 \ var_gateway=192.168.1.1 \ bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/debian.sh)" # The script will: # 1. Ping 192.168.1.100 - if responds, skip # 2. Ping 192.168.1.101 - if responds, skip # 3. Continue until first IP that doesn't respond # 4. Assign that IP to the container ``` > **Note**: IP range format is `START_IP/CIDR-END_IP/CIDR`. Both sides must include the same CIDR notation. ### Using App Defaults **Step 1: Create defaults once (interactive)** ```bash bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/pihole.sh)" # Select "Advanced Settings" → Configure → Save as "App Defaults" ``` **Step 2: Deploy unattended (uses saved defaults)** ```bash #!/bin/bash # deploy-with-defaults.sh # App defaults are loaded automatically bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/pihole.sh)" # Script will use /usr/local/community-scripts/defaults/pihole.vars ``` ### Advanced Configuration Variables Beyond the basic resource settings, you can control advanced container features: | Variable | Description | Options | |----------|-------------|---------| | `var_os` | Operating system template | `debian`, `ubuntu`, `alpine` | | `var_version` | OS version | `12`, `13` (Debian), `22.04`, `24.04` (Ubuntu) | | `var_gpu` | Enable GPU passthrough | `yes`, `no` (Default: `no`) | | `var_tun` | Enable TUN/TAP device | `yes`, `no` (Default: `no`) | | `var_nesting` | Enable nesting | `1`, `0` (Default: `1`) | **Example with GPU and TUN:** ```bash var_gpu=yes \ var_tun=yes \ var_hostname=transcoder \ bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/plex.sh)" ``` --- ## Batch Deployments ### Deploy Multiple Containers #### Simple Loop ```bash #!/bin/bash # batch-deploy-simple.sh apps=("thingsboard" "qui" "flatnotes") for app in "${apps[@]}"; do echo "Deploying $app..." var_hostname="$app-server" \ var_cpu=2 \ var_ram=2048 \ bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/${app}.sh)" echo "✓ $app deployed" sleep 5 # Wait between deployments done ``` #### Advanced with Configuration Array ```bash #!/bin/bash # batch-deploy-advanced.sh - Deploy multiple containers with individual configs declare -A CONTAINERS=( ["beszel"]="1:512:8:vmbr0:monitoring" ["qui"]="2:1024:10:vmbr0:torrent,ui" ["thingsboard"]="6:8192:50:vmbr1:iot,industrial" ["dockge"]="2:2048:10:vmbr0:docker,management" ) for app in "${!CONTAINERS[@]}"; do # Parse configuration IFS=':' read -r cpu ram disk bridge tags <<< "${CONTAINERS[$app]}" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "Deploying: $app" echo " CPU: $cpu cores" echo " RAM: $ram MB" echo " Disk: $disk GB" echo " Bridge: $bridge" echo " Tags: $tags" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" # Deploy container var_unprivileged=1 \ var_cpu="$cpu" \ var_ram="$ram" \ var_disk="$disk" \ var_hostname="$app" \ var_brg="$bridge" \ var_net=dhcp \ var_ipv6_method=none \ var_ssh=yes \ var_tags="$tags,automated" \ bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/${app}.sh)" 2>&1 | tee "deploy-${app}.log" if [ $? -eq 0 ]; then echo "✓ $app deployed successfully" else echo "✗ $app deployment failed - check deploy-${app}.log" fi sleep 5 done echo "" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "Batch deployment complete!" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" ``` #### Parallel Deployment ```bash #!/bin/bash # parallel-deploy.sh - Deploy multiple containers in parallel deploy_container() { local app="$1" local cpu="$2" local ram="$3" local disk="$4" echo "[$app] Starting deployment..." var_cpu="$cpu" \ var_ram="$ram" \ var_disk="$disk" \ var_hostname="$app" \ var_net=dhcp \ bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/${app}.sh)" \ &> "deploy-${app}.log" echo "[$app] ✓ Completed" } # Export function for parallel execution export -f deploy_container # Deploy in parallel (max 3 at a time) parallel -j 3 deploy_container ::: \ "debian 2 2048 10" \ "ubuntu 2 2048 10" \ "alpine 1 1024 5" \ "pihole 2 1024 8" \ "docker 4 4096 30" echo "All deployments complete!" ``` --- ## Infrastructure as Code ### Ansible Playbook #### Basic Playbook ```yaml --- # playbook-proxmox.yml - name: Deploy ProxmoxVE Containers hosts: proxmox_hosts become: yes tasks: - name: Deploy Debian Container shell: | var_unprivileged=1 \ var_cpu=2 \ var_ram=2048 \ var_disk=10 \ var_hostname=debian-{{ inventory_hostname }} \ var_net=dhcp \ var_ssh=yes \ var_tags=ansible,automated \ bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/debian.sh)" args: executable: /bin/bash register: deploy_result - name: Display deployment result debug: var: deploy_result.stdout_lines ``` #### Advanced Playbook with Variables ```yaml --- # advanced-playbook.yml - name: Deploy Multiple Container Types hosts: proxmox vars: containers: - name: pihole cpu: 2 ram: 1024 disk: 8 tags: "dns,network" - name: homeassistant cpu: 4 ram: 4096 disk: 20 tags: "automation,ha" - name: docker cpu: 6 ram: 8192 disk: 50 tags: "containers,docker" ssh_key: "{{ lookup('file', '~/.ssh/id_rsa.pub') }}" tasks: - name: Ensure community-scripts directory exists file: path: /usr/local/community-scripts/defaults state: directory mode: '0755' - name: Deploy containers shell: | var_unprivileged=1 \ var_cpu={{ item.cpu }} \ var_ram={{ item.ram }} \ var_disk={{ item.disk }} \ var_hostname={{ item.name }} \ var_brg=vmbr0 \ var_net=dhcp \ var_ipv6_method=none \ var_ssh=yes \ var_ssh_authorized_key="{{ ssh_key }}" \ var_tags="{{ item.tags }},ansible" \ bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/{{ item.name }}.sh)" args: executable: /bin/bash loop: "{{ containers }}" register: deployment_results - name: Wait for containers to be ready wait_for: timeout: 60 - name: Report deployment status debug: msg: "Deployed {{ item.item.name }} - Status: {{ 'Success' if item.rc == 0 else 'Failed' }}" loop: "{{ deployment_results.results }}" ``` Run with: ```bash ansible-playbook -i inventory.ini advanced-playbook.yml ``` ### Terraform Integration ```hcl # main.tf - Deploy containers via Terraform terraform { required_providers { proxmox = { source = "telmate/proxmox" version = "2.9.14" } } } provider "proxmox" { pm_api_url = "https://proxmox.example.com:8006/api2/json" pm_api_token_id = "terraform@pam!terraform" pm_api_token_secret = var.proxmox_token } resource "null_resource" "deploy_container" { for_each = var.containers provisioner "remote-exec" { inline = [ "var_unprivileged=1", "var_cpu=${each.value.cpu}", "var_ram=${each.value.ram}", "var_disk=${each.value.disk}", "var_hostname=${each.key}", "var_net=dhcp", "bash -c \"$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/${each.value.template}.sh)\"" ] connection { type = "ssh" host = var.proxmox_host user = "root" private_key = file("~/.ssh/id_rsa") } } } variable "containers" { type = map(object({ template = string cpu = number ram = number disk = number })) default = { "pihole" = { template = "pihole" cpu = 2 ram = 1024 disk = 8 } "homeassistant" = { template = "homeassistant" cpu = 4 ram = 4096 disk = 20 } } } ``` --- ## CI/CD Integration ### GitHub Actions ```yaml # .github/workflows/deploy-container.yml name: Deploy Container to Proxmox on: push: branches: [main] workflow_dispatch: inputs: container_type: description: 'Container type to deploy' required: true type: choice options: - debian - ubuntu - docker - pihole jobs: deploy: runs-on: ubuntu-latest steps: - name: Deploy to Proxmox uses: appleboy/ssh-action@v0.1.10 with: host: ${{ secrets.PROXMOX_HOST }} username: root key: ${{ secrets.SSH_PRIVATE_KEY }} script: | var_unprivileged=1 \ var_cpu=4 \ var_ram=4096 \ var_disk=30 \ var_hostname=${{ github.event.inputs.container_type }}-ci \ var_net=dhcp \ var_ssh=yes \ var_tags=ci-cd,automated \ bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/${{ github.event.inputs.container_type }}.sh)" - name: Notify deployment status if: success() run: echo "✓ Container deployed successfully" ``` ### GitLab CI ```yaml # .gitlab-ci.yml stages: - deploy deploy_container: stage: deploy image: alpine:latest before_script: - apk add --no-cache openssh-client curl bash - eval $(ssh-agent -s) - echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add - - mkdir -p ~/.ssh - chmod 700 ~/.ssh - ssh-keyscan $PROXMOX_HOST >> ~/.ssh/known_hosts script: - | ssh root@$PROXMOX_HOST << 'EOF' var_unprivileged=1 \ var_cpu=4 \ var_ram=4096 \ var_disk=30 \ var_hostname=gitlab-ci-container \ var_net=dhcp \ var_tags=gitlab-ci,automated \ bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/debian.sh)" EOF only: - main when: manual ``` --- ## Error Handling ### Deployment Verification Script ```bash #!/bin/bash # deploy-with-verification.sh APP="debian" HOSTNAME="production-server" MAX_RETRIES=3 RETRY_COUNT=0 deploy_container() { echo "Attempting deployment (Try $((RETRY_COUNT + 1))/$MAX_RETRIES)..." var_unprivileged=1 \ var_cpu=4 \ var_ram=4096 \ var_disk=30 \ var_hostname="$HOSTNAME" \ var_net=dhcp \ var_ssh=yes \ bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/${APP}.sh)" 2>&1 | tee deploy.log return ${PIPESTATUS[0]} } verify_deployment() { echo "Verifying deployment..." # Check if container exists if ! pct list | grep -q "$HOSTNAME"; then echo "✗ Container not found in pct list" return 1 fi # Check if container is running CTID=$(pct list | grep "$HOSTNAME" | awk '{print $1}') STATUS=$(pct status "$CTID" | awk '{print $2}') if [ "$STATUS" != "running" ]; then echo "✗ Container not running (Status: $STATUS)" return 1 fi # Check network connectivity if ! pct exec "$CTID" -- ping -c 1 1.1.1.1 &>/dev/null; then echo "⚠ Warning: No internet connectivity" fi echo "✓ Deployment verified successfully" echo " Container ID: $CTID" echo " Status: $STATUS" echo " IP: $(pct exec "$CTID" -- hostname -I)" return 0 } # Main deployment loop with retry while [ $RETRY_COUNT -lt $MAX_RETRIES ]; do if deploy_container; then if verify_deployment; then echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "✓ Deployment successful!" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" exit 0 else echo "✗ Deployment verification failed" fi else echo "✗ Deployment failed" fi RETRY_COUNT=$((RETRY_COUNT + 1)) if [ $RETRY_COUNT -lt $MAX_RETRIES ]; then echo "Retrying in 10 seconds..." sleep 10 fi done echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "✗ Deployment failed after $MAX_RETRIES attempts" echo "Check deploy.log for details" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" exit 1 ``` ### Rollback on Failure ```bash #!/bin/bash # deploy-with-rollback.sh APP="debian" HOSTNAME="test-server" SNAPSHOT_NAME="pre-deployment" # Take snapshot of existing container (if exists) backup_existing() { EXISTING_CTID=$(pct list | grep "$HOSTNAME" | awk '{print $1}') if [ -n "$EXISTING_CTID" ]; then echo "Creating snapshot of existing container..." pct snapshot "$EXISTING_CTID" "$SNAPSHOT_NAME" --description "Pre-deployment backup" return 0 fi return 1 } # Deploy new container deploy() { var_hostname="$HOSTNAME" \ var_cpu=4 \ var_ram=4096 \ bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/${APP}.sh)" return $? } # Rollback to snapshot rollback() { local ctid="$1" echo "Rolling back to snapshot..." pct rollback "$ctid" "$SNAPSHOT_NAME" pct delsnapshot "$ctid" "$SNAPSHOT_NAME" } # Main execution backup_existing HAD_BACKUP=$? if deploy; then echo "✓ Deployment successful" [ $HAD_BACKUP -eq 0 ] && echo "You can remove the snapshot with: pct delsnapshot $SNAPSHOT_NAME" else echo "✗ Deployment failed" if [ $HAD_BACKUP -eq 0 ]; then read -p "Rollback to previous version? (y/N) " -n 1 -r echo if [[ $REPLY =~ ^[Yy]$ ]]; then rollback "$EXISTING_CTID" echo "✓ Rolled back successfully" fi fi exit 1 fi ``` --- ## Security Considerations ### Secure Deployment Script ```bash #!/bin/bash # secure-deploy.sh - Production-ready secure deployment set -euo pipefail # Exit on error, undefined vars, pipe failures # Configuration readonly APP="debian" readonly HOSTNAME="secure-server" readonly SSH_KEY_PATH="/root/.ssh/id_rsa.pub" readonly LOG_FILE="/var/log/container-deployments.log" # Logging function log() { echo "[$(date +'%Y-%m-%d %H:%M:%S')] $*" | tee -a "$LOG_FILE" } # Validate prerequisites validate_environment() { log "Validating environment..." # Check if running as root if [ "$EUID" -ne 0 ]; then log "ERROR: Must run as root" exit 1 fi # Check SSH key exists if [ ! -f "$SSH_KEY_PATH" ]; then log "ERROR: SSH key not found at $SSH_KEY_PATH" exit 1 fi # Check internet connectivity if ! curl -s --max-time 5 https://github.com &>/dev/null; then log "ERROR: No internet connectivity" exit 1 fi log "✓ Environment validated" } # Secure deployment deploy_secure() { log "Starting secure deployment for $HOSTNAME..." SSH_KEY=$(cat "$SSH_KEY_PATH") var_unprivileged=1 \ var_cpu=4 \ var_ram=4096 \ var_disk=30 \ var_hostname="$HOSTNAME" \ var_brg=vmbr0 \ var_net=dhcp \ var_ipv6_method=disable \ var_ssh=yes \ var_ssh_authorized_key="$SSH_KEY" \ var_nesting=0 \ var_keyctl=0 \ var_fuse=0 \ var_protection=yes \ var_tags=production,secure,automated \ var_verbose=no \ bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/${APP}.sh)" 2>&1 | tee -a "$LOG_FILE" if [ ${PIPESTATUS[0]} -eq 0 ]; then log "✓ Deployment successful" return 0 else log "✗ Deployment failed" return 1 fi } # Main execution main() { validate_environment if deploy_secure; then log "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" log "Secure deployment completed successfully" log "Container: $HOSTNAME" log "Features: Unprivileged, SSH-only, Protected" log "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" exit 0 else log "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" log "Deployment failed - check logs at $LOG_FILE" log "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" exit 1 fi } main "$@" ``` ### SSH Key Management ```bash #!/bin/bash # deploy-with-ssh-keys.sh - Secure SSH key deployment # Load SSH keys from multiple sources load_ssh_keys() { local keys=() # Personal key if [ -f ~/.ssh/id_rsa.pub ]; then keys+=("$(cat ~/.ssh/id_rsa.pub)") fi # Team keys if [ -f /etc/ssh/authorized_keys.d/team ]; then while IFS= read -r key; do [ -n "$key" ] && keys+=("$key") done < /etc/ssh/authorized_keys.d/team fi # Join keys with newline printf "%s\n" "${keys[@]}" } # Deploy with multiple SSH keys SSH_KEYS=$(load_ssh_keys) var_ssh=yes \ var_ssh_authorized_key="$SSH_KEYS" \ var_hostname=multi-key-server \ bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/debian.sh)" ``` --- ## Complete Production Example ```bash #!/bin/bash # production-deploy.sh - Complete production deployment system set -euo pipefail #━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ # Configuration #━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" readonly LOG_DIR="/var/log/proxmox-deployments" readonly CONFIG_FILE="$SCRIPT_DIR/deployment-config.json" #━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ # Functions #━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ setup_logging() { mkdir -p "$LOG_DIR" exec 1> >(tee -a "$LOG_DIR/deployment-$(date +%Y%m%d-%H%M%S).log") exec 2>&1 } log_info() { echo "[INFO] $(date +'%H:%M:%S') - $*"; } log_error() { echo "[ERROR] $(date +'%H:%M:%S') - $*" >&2; } log_success() { echo "[SUCCESS] $(date +'%H:%M:%S') - $*"; } validate_prerequisites() { log_info "Validating prerequisites..." [ "$EUID" -eq 0 ] || { log_error "Must run as root"; exit 1; } command -v jq >/dev/null 2>&1 || { log_error "jq not installed"; exit 1; } command -v curl >/dev/null 2>&1 || { log_error "curl not installed"; exit 1; } log_success "Prerequisites validated" } deploy_from_config() { local config_file="$1" if [ ! -f "$config_file" ]; then log_error "Config file not found: $config_file" return 1 fi local container_count container_count=$(jq '.containers | length' "$config_file") log_info "Deploying $container_count containers from config..." for i in $(seq 0 $((container_count - 1))); do local name cpu ram disk app tags name=$(jq -r ".containers[$i].name" "$config_file") cpu=$(jq -r ".containers[$i].cpu" "$config_file") ram=$(jq -r ".containers[$i].ram" "$config_file") disk=$(jq -r ".containers[$i].disk" "$config_file") app=$(jq -r ".containers[$i].app" "$config_file") tags=$(jq -r ".containers[$i].tags" "$config_file") log_info "Deploying container: $name ($app)" var_unprivileged=1 \ var_cpu="$cpu" \ var_ram="$ram" \ var_disk="$disk" \ var_hostname="$name" \ var_net=dhcp \ var_ssh=yes \ var_tags="$tags,automated" \ var_protection=yes \ bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/${app}.sh)" if [ $? -eq 0 ]; then log_success "Deployed: $name" else log_error "Failed to deploy: $name" fi sleep 5 done } generate_report() { log_info "Generating deployment report..." echo "" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "DEPLOYMENT REPORT" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "Time: $(date)" echo "" pct list echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" } #━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ # Main #━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ main() { setup_logging log_info "Starting production deployment system" validate_prerequisites deploy_from_config "$CONFIG_FILE" generate_report log_success "Production deployment complete" } main "$@" ``` **Example config file (deployment-config.json):** ```json { "containers": [ { "name": "pihole", "app": "pihole", "cpu": 2, "ram": 1024, "disk": 8, "tags": "dns,network,production" }, { "name": "homeassistant", "app": "homeassistant", "cpu": 4, "ram": 4096, "disk": 20, "tags": "automation,ha,production" }, { "name": "docker-host", "app": "docker", "cpu": 8, "ram": 16384, "disk": 100, "tags": "containers,docker,production" } ] } ``` --- ## See Also - [Defaults System Guide](DEFAULTS_GUIDE.md) - [Configuration Reference](CONFIGURATION_REFERENCE.md) - [Security Best Practices](SECURITY_GUIDE.md) - [Network Configuration](NETWORK_GUIDE.md) ================================================ FILE: docs/guides/USER_SUBMITTED_GUIDES.md ================================================

User Submitted Guides

In order to contribute a guide on installing with Proxmox VE Helper Scripts, you should open a pull request that adds the guide to the `USER_SUBMITTED_GUIDES.md` file. [Proxmox Automation with Proxmox Helper Scripts!](https://www.youtube.com/watch?v=kcpu4z5eSEU) [Installing Home Assistant OS using Proxmox 8](https://community.home-assistant.io/t/installing-home-assistant-os-using-proxmox-8/201835) [How To Separate Zigbee2MQTT From Home Assistant In Proxmox](https://smarthomescene.com/guides/how-to-separate-zigbee2mqtt-from-home-assistant-in-proxmox/) [How To Install Home Assistant On Proxmox: The Easy Way](https://smarthomescene.com/guides/how-to-install-home-assistant-on-proxmox-the-easy-way/) [Home Assistant: Installing InfluxDB (LXC)](https://www.derekseaman.com/2023/04/home-assistant-installing-influxdb-lxc.html) [Home Assistant: Proxmox Quick Start Guide](https://www.derekseaman.com/2023/10/home-assistant-proxmox-ve-8-0-quick-start-guide-2.html) [Home Assistant: Installing Grafana (LXC) with Let’s Encrypt SSL](https://www.derekseaman.com/2023/04/home-assistant-installing-grafana-lxc.html) [Proxmox: Plex LXC with Alder Lake Transcoding](https://www.derekseaman.com/2023/04/proxmox-plex-lxc-with-alder-lake-transcoding.html) [How To Backup Home Assistant In Proxmox](https://smarthomescene.com/guides/how-to-backup-home-assistant-in-proxmox/) [Running Frigate on Proxmox](https://www.homeautomationguy.io/blog/running-frigate-on-proxmox) [Frigate VM on Proxmox with PCIe Coral TPU](https://www.derekseaman.com/2023/06/home-assistant-frigate-vm-on-proxmox-with-pcie-coral-tpu.html) [Moving Home Assistant’s Database To MariaDB On Proxmox](https://smarthomescene.com/guides/moving-home-assistants-database-to-mariadb-on-proxmox/) [How-to: Proxmox VE 7.4 to 8.0 Upgrade](https://www.derekseaman.com/2023/06/how-to-proxmox-7-4-to-8-0-upgrade.html) [iGPU Transcoding In Proxmox with Jellyfin](https://www.youtube.com/watch?v=XAa_qpNmzZs) [Proxmox + NetData]() [Proxmox Homelab Series]() [The fastest installation of Docker and Portainer on Proxmox VE](https://lavr.site/en-fastest-install-docker-portainer-proxmox/) [How To Setup Proxmox Backuper Server Using Helper Scripts]() ================================================ FILE: docs/install/DETAILED_GUIDE.md ================================================ # 🛠️ **Application Installation Scripts (install/AppName-install.sh)** **Modern Guide to Writing In-Container Installation Scripts** > **Updated**: December 2025 > **Context**: Integrated with tools.func, error_handler.func, and install.func > **Examples Used**: `/install/pihole-install.sh`, `/install/mealie-install.sh` --- ## 📋 Table of Contents - [Overview](#overview) - [Execution Context](#execution-context) - [File Structure](#file-structure) - [Complete Script Template](#complete-script-template) - [Installation Phases](#installation-phases) - [Function Reference](#function-reference) - [Best Practices](#best-practices) - [Real Examples](#real-examples) - [Troubleshooting](#troubleshooting) - [Contribution Checklist](#contribution-checklist) --- ## Overview ### Purpose Installation scripts (`install/AppName-install.sh`) **run inside the LXC container** and are responsible for: 1. Setting up the container OS (updates, packages) 2. Installing application dependencies 3. Downloading and configuring the application 4. Setting up services and systemd units 5. Creating version tracking files for updates 6. Generating credentials/configurations 7. Final cleanup and validation ### Execution Flow ``` ct/AppName.sh (Proxmox Host) ↓ build_container() ↓ pct exec CTID bash -c "$(cat install/AppName-install.sh)" ↓ install/AppName-install.sh (Inside Container) ↓ Container Ready with App Installed ``` --- ## Execution Context ### Environment Variables Available ```bash # From Proxmox/Container CTID # Container ID (100, 101, etc.) PCT_OSTYPE # OS type (alpine, debian, ubuntu) HOSTNAME # Container hostname # From build.func FUNCTIONS_FILE_PATH # Bash functions library (core.func + tools.func) VERBOSE # Verbose mode (yes/no) STD # Standard redirection variable (silent/empty) # From install.func APP # Application name NSAPP # Normalized app name (lowercase, no spaces) METHOD # Installation method (ct/install) RANDOM_UUID # Session UUID for telemetry ``` --- ## File Structure ### Minimal install/AppName-install.sh Template ```bash #!/usr/bin/env bash # [1] Shebang # [2] Copyright/Metadata # Copyright (c) 2021-2026 community-scripts ORG # Author: YourUsername # License: MIT # Source: https://example.com # [3] Load functions source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os # [4] Installation steps msg_info "Installing Dependencies" $STD apt-get install -y package1 package2 msg_ok "Installed Dependencies" # [5] Final setup motd_ssh customize cleanup_lxc ``` --- ## Complete Script Template ### Phase 1: Header & Initialization ```bash #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: YourUsername # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/application/repo # Load all available functions (from core.func + tools.func) source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" # Initialize environment color # Setup ANSI colors and icons verb_ip6 # Configure IPv6 (if needed) catch_errors # Setup error traps setting_up_container # Verify OS is ready network_check # Verify internet connectivity update_os # Update packages (apk/apt) ``` ### Phase 2: Dependency Installation ```bash msg_info "Installing Dependencies" $STD apt-get install -y \ curl \ wget \ git \ nano \ build-essential \ libssl-dev \ python3-dev msg_ok "Installed Dependencies" ``` ### Phase 3: Tool Setup (Using tools.func) ```bash # Setup specific tool versions NODE_VERSION="22" setup_nodejs PHP_VERSION="8.4" setup_php PYTHON_VERSION="3.12" setup_uv ``` ### Phase 4: Application Download & Setup ```bash # Download from GitHub RELEASE=$(curl -fsSL https://api.github.com/repos/user/repo/releases/latest | \ grep "tag_name" | awk '{print substr($2, 2, length($2)-3)}') wget -q "https://github.com/user/repo/releases/download/v${RELEASE}/app-${RELEASE}.tar.gz" cd /opt tar -xzf app-${RELEASE}.tar.gz rm -f app-${RELEASE}.tar.gz ``` ### Phase 5: Configuration Files ```bash # Using cat << EOF (multiline) cat <<'EOF' >/etc/nginx/sites-available/appname server { listen 80; server_name _; root /opt/appname/public; index index.php index.html; } EOF # Using sed for replacements sed -i -e "s|^DB_HOST=.*|DB_HOST=localhost|" \ -e "s|^DB_USER=.*|DB_USER=appuser|" \ /opt/appname/.env ``` ### Phase 6: Database Setup (If Needed) ```bash msg_info "Setting up Database" DB_NAME="appname_db" DB_USER="appuser" DB_PASS=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | head -c13) # For MySQL/MariaDB mysql -u root < /opt/${APP}_version.txt # Or with additional metadata cat > /opt/${APP}_version.txt < /opt/${APP}_version.txt ``` 6. **Handle Alpine vs Debian Differences** ```bash # ✅ Good: Detect OS if grep -qi 'alpine' /etc/os-release; then apk add package else apt-get install -y package fi ``` ### ❌ DON'T: 1. **Hardcode Versions** ```bash # ❌ Bad: Won't auto-update wget https://example.com/app-1.2.3.tar.gz ``` 2. **Use Root Without Password** ```bash # ❌ Bad: Security risk mysql -u root ``` 3. **Forget Error Handling** ```bash # ❌ Bad: Silent failures wget https://example.com/file tar -xzf file ``` 4. **Leave Temporary Files** ```bash # ✅ Always cleanup rm -rf /opt/app-${RELEASE}.tar.gz ``` --- ## Real Examples ### Example 1: Node.js Application ```bash #!/usr/bin/env bash source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color catch_errors setting_up_container network_check update_os msg_info "Installing Node.js" NODE_VERSION="22" setup_nodejs msg_ok "Node.js installed" msg_info "Installing Application" cd /opt RELEASE=$(curl -fsSL https://api.github.com/repos/user/repo/releases/latest | \ grep "tag_name" | awk '{print substr($2, 2, length($2)-3)}') wget -q "https://github.com/user/repo/releases/download/v${RELEASE}/app.tar.gz" tar -xzf app.tar.gz echo "${RELEASE}" > /opt/app_version.txt msg_ok "Application installed" systemctl enable --now app cleanup_lxc ``` ### Example 2: PHP Application with Database ```bash #!/usr/bin/env bash source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color catch_errors setting_up_container network_check update_os PHP_VERSION="8.4" PHP_MODULE="bcmath,curl,pdo_mysql" setup_php setup_mariadb # Uses distribution packages (recommended) # Or for specific version: MARIADB_VERSION="11.4" setup_mariadb # Database setup DB_PASS=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | head -c13) mysql -u root < /opt/app/.env < .env < /opt/app_version.txt ``` ### Phase 10: Final Cleanup ```bash motd_ssh customize cleanup_lxc ``` ## Contributing an Installation Script 1. Create `ct/myapp.sh` (host script) 2. Create `install/myapp-install.sh` (container script) 3. Follow 10-phase pattern in [UPDATED_APP-install.md](../UPDATED_APP-install.md) 4. Test in actual container 5. Submit PR with both files ## Common Tasks - **Create new installation script** → [UPDATED_APP-install.md](../UPDATED_APP-install.md) - **Install Node.js/PHP/Database** → [misc/tools.func/](../misc/tools.func/) - **Setup Alpine container** → [misc/alpine-install.func/](../misc/alpine-install.func/) - **Debug installation errors** → [EXIT_CODES.md](../EXIT_CODES.md) - **Use dev mode** → [DEV_MODE.md](../DEV_MODE.md) ## Alpine vs Debian - **Debian-based** → Use `tools.func`, `install.func`, `systemctl` - **Alpine-based** → Use `alpine-tools.func`, `alpine-install.func`, `rc-service` --- **Last Updated**: December 2025 **Maintainers**: community-scripts team ================================================ FILE: docs/misc/README.md ================================================ # Misc Documentation This directory contains comprehensive documentation for all function libraries and components of the Proxmox Community Scripts project. Each section is organized as a dedicated subdirectory with detailed references, examples, and integration guides. --- ## 🏗️ **Core Function Libraries** ### 📁 [build.func/](./build.func/) **Core LXC Container Orchestration** - Main orchestrator for Proxmox LXC container creation **Contents:** - BUILD_FUNC_FLOWCHART.md - Visual execution flows and decision trees - BUILD_FUNC_ARCHITECTURE.md - System architecture and design - BUILD_FUNC_ENVIRONMENT_VARIABLES.md - Complete environment variable reference - BUILD_FUNC_FUNCTIONS_REFERENCE.md - Alphabetical function reference - BUILD_FUNC_EXECUTION_FLOWS.md - Detailed execution flows - BUILD_FUNC_USAGE_EXAMPLES.md - Practical usage examples - README.md - Overview and quick reference **Key Functions**: `variables()`, `start()`, `build_container()`, `build_defaults()`, `advanced_settings()` --- ### 📁 [core.func/](./core.func/) **System Utilities & Foundation** - Essential utility functions and system checks **Contents:** - CORE_FLOWCHART.md - Visual execution flows - CORE_FUNCTIONS_REFERENCE.md - Complete function reference - CORE_INTEGRATION.md - Integration points - CORE_USAGE_EXAMPLES.md - Practical examples - README.md - Overview and quick reference **Key Functions**: `color()`, `msg_info()`, `msg_ok()`, `msg_error()`, `root_check()`, `pve_check()`, `parse_dev_mode()` --- ### 📁 [error_handler.func/](./error_handler.func/) **Error Handling & Signal Management** - Comprehensive error handling and signal trapping **Contents:** - ERROR_HANDLER_FLOWCHART.md - Visual error handling flows - ERROR_HANDLER_FUNCTIONS_REFERENCE.md - Function reference - ERROR_HANDLER_INTEGRATION.md - Integration with other components - ERROR_HANDLER_USAGE_EXAMPLES.md - Practical examples - README.md - Overview and quick reference **Key Functions**: `catch_errors()`, `error_handler()`, `explain_exit_code()`, `signal_handler()` --- ### 📁 [api.func/](./api.func/) **Proxmox API Integration** - API communication and diagnostic reporting **Contents:** - API_FLOWCHART.md - API communication flows - API_FUNCTIONS_REFERENCE.md - Function reference - API_INTEGRATION.md - Integration points - API_USAGE_EXAMPLES.md - Practical examples - README.md - Overview and quick reference **Key Functions**: `post_to_api()`, `post_update_to_api()`, `get_error_description()` --- ## 📦 **Installation & Setup Function Libraries** ### 📁 [install.func/](./install.func/) **Container Installation Workflow** - Installation orchestration for container-internal setup **Contents:** - INSTALL_FUNC_FLOWCHART.md - Installation workflow diagrams - INSTALL_FUNC_FUNCTIONS_REFERENCE.md - Complete function reference - INSTALL_FUNC_INTEGRATION.md - Integration with build and tools - INSTALL_FUNC_USAGE_EXAMPLES.md - Practical examples - README.md - Overview and quick reference **Key Functions**: `setting_up_container()`, `network_check()`, `update_os()`, `motd_ssh()`, `cleanup_lxc()` --- ### 📁 [tools.func/](./tools.func/) **Package & Tool Installation** - Robust package management and 30+ tool installation functions **Contents:** - TOOLS_FUNC_FLOWCHART.md - Package management flows - TOOLS_FUNC_FUNCTIONS_REFERENCE.md - 30+ function reference - TOOLS_FUNC_INTEGRATION.md - Integration with install workflows - TOOLS_FUNC_USAGE_EXAMPLES.md - Practical examples - TOOLS_FUNC_ENVIRONMENT_VARIABLES.md - Configuration reference - README.md - Overview and quick reference **Key Functions**: `setup_nodejs()`, `setup_php()`, `setup_mariadb()`, `setup_docker()`, `setup_deb822_repo()`, `pkg_install()`, `pkg_update()` --- ### 📁 [alpine-install.func/](./alpine-install.func/) **Alpine Container Setup** - Alpine Linux-specific installation functions **Contents:** - ALPINE_INSTALL_FUNC_FLOWCHART.md - Alpine setup flows - ALPINE_INSTALL_FUNC_FUNCTIONS_REFERENCE.md - Function reference - ALPINE_INSTALL_FUNC_INTEGRATION.md - Integration points - ALPINE_INSTALL_FUNC_USAGE_EXAMPLES.md - Practical examples - README.md - Overview and quick reference **Key Functions**: `update_os()` (apk version), `verb_ip6()`, `motd_ssh()` (Alpine), `customize()` --- ### 📁 [alpine-tools.func/](./alpine-tools.func/) **Alpine Tool Installation** - Alpine-specific package and tool installation **Contents:** - ALPINE_TOOLS_FUNC_FLOWCHART.md - Alpine package flows - ALPINE_TOOLS_FUNC_FUNCTIONS_REFERENCE.md - Function reference - ALPINE_TOOLS_FUNC_INTEGRATION.md - Integration with Alpine workflows - ALPINE_TOOLS_FUNC_USAGE_EXAMPLES.md - Practical examples - README.md - Overview and quick reference **Key Functions**: `apk_add()`, `apk_update()`, `apk_del()`, `add_community_repo()`, Alpine tool setup functions --- ### 📁 [cloud-init.func/](./cloud-init.func/) **VM Cloud-Init Configuration** - Cloud-init and VM provisioning functions **Contents:** - CLOUD_INIT_FUNC_FLOWCHART.md - Cloud-init flows - CLOUD_INIT_FUNC_FUNCTIONS_REFERENCE.md - Function reference - CLOUD_INIT_FUNC_INTEGRATION.md - Integration points - CLOUD_INIT_FUNC_USAGE_EXAMPLES.md - Practical examples - README.md - Overview and quick reference **Key Functions**: `generate_cloud_init()`, `generate_user_data()`, `setup_ssh_keys()`, `setup_static_ip()` --- ## 🔗 **Function Library Relationships** ``` ┌─────────────────────────────────────────────┐ │ Container Creation Flow │ ├─────────────────────────────────────────────┤ │ │ │ ct/AppName.sh │ │ ↓ (sources) │ │ build.func │ │ ├─ variables() │ │ ├─ build_container() │ │ └─ advanced_settings() │ │ ↓ (calls pct create with) │ │ install/appname-install.sh │ │ ↓ (sources) │ │ ├─ core.func (colors, messaging) │ │ ├─ error_handler.func (error trapping) │ │ ├─ install.func (setup/network) │ │ └─ tools.func (packages/tools) │ │ │ └─────────────────────────────────────────────┘ ┌─────────────────────────────────────────────┐ │ Alpine Container Flow │ ├─────────────────────────────────────────────┤ │ │ │ install/appname-install.sh (Alpine) │ │ ↓ (sources) │ │ ├─ core.func (colors) │ │ ├─ error_handler.func (errors) │ │ ├─ alpine-install.func (apk setup) │ │ └─ alpine-tools.func (apk tools) │ │ │ └─────────────────────────────────────────────┘ ┌─────────────────────────────────────────────┐ │ VM Provisioning Flow │ ├─────────────────────────────────────────────┤ │ │ │ vm/OsName-vm.sh │ │ ↓ (uses) │ │ cloud-init.func │ │ ├─ generate_cloud_init() │ │ ├─ setup_ssh_keys() │ │ └─ configure_network() │ │ │ └─────────────────────────────────────────────┘ ``` --- ## 📊 **Documentation Quick Stats** | Library | Files | Functions | Status | |---------|:---:|:---:|:---:| | build.func | 7 | 50+ | ✅ Complete | | core.func | 5 | 20+ | ✅ Complete | | error_handler.func | 5 | 10+ | ✅ Complete | | api.func | 5 | 5+ | ✅ Complete | | install.func | 5 | 8+ | ✅ Complete | | tools.func | 6 | 30+ | ✅ Complete | | alpine-install.func | 5 | 6+ | ✅ Complete | | alpine-tools.func | 5 | 15+ | ✅ Complete | | cloud-init.func | 5 | 12+ | ✅ Complete | **Total**: 9 function libraries, 48 documentation files, 150+ functions --- ## 🚀 **Getting Started** ### For Container Creation Scripts Start with: **[build.func/](./build.func/)** → **[tools.func/](./tools.func/)** → **[install.func/](./install.func/)** ### For Alpine Containers Start with: **[alpine-install.func/](./alpine-install.func/)** → **[alpine-tools.func/](./alpine-tools.func/)** ### For VM Provisioning Start with: **[cloud-init.func/](./cloud-init.func/)** ### For Troubleshooting Start with: **[error_handler.func/](./error_handler.func/)** → **[EXIT_CODES.md](../EXIT_CODES.md)** --- ## 📚 **Related Top-Level Documentation** - **[CONTRIBUTION_GUIDE.md](../CONTRIBUTION_GUIDE.md)** - How to contribute to ProxmoxVE - **[UPDATED_APP-ct.md](../UPDATED_APP-ct.md)** - Container script guide - **[UPDATED_APP-install.md](../UPDATED_APP-install.md)** - Installation script guide - **[DEFAULTS_SYSTEM_GUIDE.md](../DEFAULTS_SYSTEM_GUIDE.md)** - Configuration system - **[TECHNICAL_REFERENCE.md](../TECHNICAL_REFERENCE.md)** - Architecture reference - **[EXIT_CODES.md](../EXIT_CODES.md)** - Complete exit code reference - **[DEV_MODE.md](../DEV_MODE.md)** - Development debugging modes - **[CHANGELOG_MISC.md](../CHANGELOG_MISC.md)** - Change history --- ## 🔄 **Standardized Documentation Structure** Each function library follows the same documentation pattern: ``` function-library/ ├── README.md # Quick reference & overview ├── FUNCTION_LIBRARY_FLOWCHART.md # Visual execution flows ├── FUNCTION_LIBRARY_FUNCTIONS_REFERENCE.md # Alphabetical reference ├── FUNCTION_LIBRARY_INTEGRATION.md # Integration points ├── FUNCTION_LIBRARY_USAGE_EXAMPLES.md # Practical examples └── [FUNCTION_LIBRARY_ENVIRONMENT_VARIABLES.md] # (if applicable) ``` **Advantages**: - ✅ Consistent navigation across all libraries - ✅ Quick reference sections in each README - ✅ Visual flowcharts for understanding - ✅ Complete function references - ✅ Real-world usage examples - ✅ Integration guides for connecting libraries --- ## 📝 **Documentation Standards** All documentation follows these standards: 1. **README.md** - Quick overview, key features, quick reference 2. **FLOWCHART.md** - ASCII flowcharts and visual diagrams 3. **FUNCTIONS_REFERENCE.md** - Every function with full details 4. **INTEGRATION.md** - How this library connects to others 5. **USAGE_EXAMPLES.md** - Copy-paste ready examples 6. **ENVIRONMENT_VARIABLES.md** - (if applicable) Configuration reference --- ## ✅ **Last Updated**: December 2025 **Maintainers**: community-scripts team **License**: MIT **Status**: All 9 libraries fully documented and standardized --- *This directory contains specialized documentation for specific components of the Proxmox Community Scripts project.* ================================================ FILE: docs/misc/alpine-install.func/ALPINE_INSTALL_FUNC_FLOWCHART.md ================================================ # alpine-install.func Flowchart Alpine container initialization flow (apk-based, OpenRC init system). ## Alpine Container Setup Flow ``` Alpine Container Started ↓ setting_up_container() ↓ verb_ip6() [optional - IPv6] ↓ update_os() [apk update/upgrade] ↓ network_check() ↓ Application Installation ↓ motd_ssh() ↓ customize() ↓ cleanup_lxc() ↓ Complete ✓ ``` **Last Updated**: December 2025 ================================================ FILE: docs/misc/alpine-install.func/ALPINE_INSTALL_FUNC_FUNCTIONS_REFERENCE.md ================================================ # alpine-install.func Functions Reference Alpine Linux-specific installation functions (apk-based, OpenRC). ## Core Functions ### setting_up_container() Initialize Alpine container setup. ### update_os() Update Alpine packages via `apk update && apk upgrade`. ### verb_ip6() Enable IPv6 on Alpine with persistent configuration. ### network_check() Verify network connectivity in Alpine. ### motd_ssh() Configure SSH daemon and MOTD on Alpine. ### customize() Apply Alpine-specific customizations. ### cleanup_lxc() Final cleanup (Alpine-specific). --- **Last Updated**: December 2025 ================================================ FILE: docs/misc/alpine-install.func/ALPINE_INSTALL_FUNC_INTEGRATION.md ================================================ # alpine-install.func Integration Guide Integration of alpine-install.func with Alpine container workflows. ## Alpine-Specific Integration Alpine containers use: - `apk` instead of `apt-get` - `OpenRC` instead of `systemd` - Alpine-specific package names --- **Last Updated**: December 2025 ================================================ FILE: docs/misc/alpine-install.func/ALPINE_INSTALL_FUNC_USAGE_EXAMPLES.md ================================================ # alpine-install.func Usage Examples Basic examples for Alpine container installation. ### Example: Basic Alpine Setup ```bash #!/usr/bin/env bash source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" setting_up_container update_os # Install Alpine packages apk add --no-cache curl wget git motd_ssh customize cleanup_lxc ``` --- **Last Updated**: December 2025 ================================================ FILE: docs/misc/alpine-install.func/README.md ================================================ # alpine-install.func Documentation ## Overview The `alpine-install.func` file provides Alpine Linux-specific installation and configuration functions for LXC containers. It complements the standard `install.func` with Alpine-specific operations using the apk package manager instead of apt. ## Purpose and Use Cases - **Alpine Container Setup**: Initialize Alpine Linux containers with proper configuration - **IPv6 Management**: Enable or disable IPv6 in Alpine with persistent configuration - **Network Verification**: Verify connectivity in Alpine environments - **SSH Configuration**: Setup SSH daemon on Alpine - **Auto-Login Setup**: Configure passwordless root login for Alpine containers - **Package Management**: Safe apk operations with error handling ## Quick Reference ### Key Function Groups - **Initialization**: `setting_up_container()` - Alpine setup message - **Network**: `verb_ip6()`, `network_check()` - IPv6 and connectivity - **OS Configuration**: `update_os()` - Alpine package updates - **SSH/MOTD**: `motd_ssh()` - SSH and login message setup - **Container Customization**: `customize()`, `cleanup_lxc()` - Final setup ### Dependencies - **External**: `apk`, `curl`, `wget`, `ping` - **Internal**: Uses functions from `core.func`, `error_handler.func` ### Integration Points - Used by: Alpine-based install scripts (alpine.sh, alpine-ntfy.sh, etc.) - Uses: Environment variables from build.func - Provides: Alpine-specific installation and management services ## Documentation Files ### 📊 [ALPINE_INSTALL_FUNC_FLOWCHART.md](./ALPINE_INSTALL_FUNC_FLOWCHART.md) Visual execution flows showing Alpine container initialization and setup workflows. ### 📚 [ALPINE_INSTALL_FUNC_FUNCTIONS_REFERENCE.md](./ALPINE_INSTALL_FUNC_FUNCTIONS_REFERENCE.md) Complete alphabetical reference of all functions with parameters and usage details. ### 💡 [ALPINE_INSTALL_FUNC_USAGE_EXAMPLES.md](./ALPINE_INSTALL_FUNC_USAGE_EXAMPLES.md) Practical examples showing how to use Alpine installation functions. ### 🔗 [ALPINE_INSTALL_FUNC_INTEGRATION.md](./ALPINE_INSTALL_FUNC_INTEGRATION.md) How alpine-install.func integrates with standard install workflows. ## Key Features ### Alpine-Specific Functions - **apk Package Manager**: Alpine package operations (instead of apt-get) - **OpenRC Support**: Alpine uses OpenRC init instead of systemd - **Lightweight Setup**: Minimal dependencies appropriate for Alpine - **IPv6 Configuration**: Persistent IPv6 settings via `/etc/network/interfaces` ### Network & Connectivity - **IPv6 Toggle**: Enable/disable with persistent configuration - **Connectivity Check**: Verify internet access in Alpine - **DNS Verification**: Resolve domain names correctly - **Retry Logic**: Automatic recovery from transient failures ### SSH & Auto-Login - **SSH Daemon**: Setup and start sshd on Alpine - **Root Keys**: Configure root SSH access - **Auto-Login**: Optional automatic login without password - **MOTD**: Custom login message on Alpine ## Function Categories ### 🔹 Core Functions - `setting_up_container()` - Alpine container setup message - `update_os()` - Update Alpine packages via apk - `verb_ip6()` - Enable/disable IPv6 persistently - `network_check()` - Verify network connectivity ### 🔹 SSH & Configuration Functions - `motd_ssh()` - Configure SSH daemon on Alpine - `customize()` - Apply Alpine-specific customizations - `cleanup_lxc()` - Final cleanup ### 🔹 Service Management (OpenRC) - `rc-update` - Enable/disable services for Alpine - `rc-service` - Start/stop services on Alpine - Service configuration files in `/etc/init.d/` ## Differences from Debian Install | Feature | Debian (install.func) | Alpine (alpine-install.func) | |---------|:---:|:---:| | Package Manager | apt-get | apk | | Init System | systemd | OpenRC | | SSH Service | systemctl | rc-service | | Config Files | /etc/systemd/ | /etc/init.d/ | | Network Config | /etc/network/ or Netplan | /etc/network/interfaces | | IPv6 Setup | netplan files | /etc/network/interfaces | | Auto-Login | getty override | `/etc/inittab` or shell config | | Size | ~200MB | ~100MB | ## Execution Flow for Alpine ``` Alpine Container Started ↓ source $FUNCTIONS_FILE_PATH ↓ setting_up_container() ← Alpine setup message ↓ update_os() ← apk update ↓ verb_ip6() ← IPv6 configuration (optional) ↓ network_check() ← Verify connectivity ↓ [Application-Specific Installation] ↓ motd_ssh() ← Configure SSH/MOTD customize() ← Apply customizations ↓ cleanup_lxc() ← Final cleanup ↓ Alpine Installation Complete ``` ## Common Usage Patterns ### Basic Alpine Setup ```bash #!/usr/bin/env bash source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" setting_up_container update_os # Install Alpine-specific packages apk add --no-cache curl wget git # ... application installation ... motd_ssh customize cleanup_lxc ``` ### With IPv6 Enabled ```bash #!/usr/bin/env bash source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" setting_up_container verb_ip6 update_os network_check # ... application installation ... motd_ssh customize cleanup_lxc ``` ### Installing Services ```bash #!/usr/bin/env bash source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" setting_up_container update_os # Install via apk apk add --no-cache nginx # Enable and start service on Alpine rc-update add nginx rc-service nginx start motd_ssh customize cleanup_lxc ``` ## Best Practices ### ✅ DO - Use `apk add --no-cache` to reduce image size - Enable IPv6 if application needs it (`verb_ip6`) - Use `rc-service` for service management on Alpine - Check `/etc/network/interfaces` for IPv6 persistence - Test network connectivity before critical operations - Use `$STD` for output suppression in production ### ❌ DON'T - Use `apt-get` commands (Alpine doesn't have apt) - Use `systemctl` (Alpine uses OpenRC, not systemd) - Use `service` command (it may not exist on Alpine) - Assume systemd exists on Alpine - Forget to add `--no-cache` flag to `apk add` - Hardcode paths from Debian (different on Alpine) ## Alpine-Specific Considerations ### Package Names Some packages have different names on Alpine: ```bash # Debian → Alpine # curl → curl (same) # wget → wget (same) # python3 → python3 (same) # libpq5 → postgresql-client # libmariadb3 → mariadb-client ``` ### Service Management ```bash # Debian (systemd) → Alpine (OpenRC) systemctl start nginx → rc-service nginx start systemctl enable nginx → rc-update add nginx systemctl status nginx → rc-service nginx status ``` ### Network Configuration ```bash # Debian (Netplan) → Alpine (/etc/network/interfaces) /etc/netplan/01-*.yaml → /etc/network/interfaces netplan apply → Configure directly in interfaces # Enable IPv6 persistently on Alpine: # Add to /etc/network/interfaces: # iface eth0 inet6 static # address ``` ## Troubleshooting ### "apk command not found" - This is Alpine Linux, not Debian - Install packages with `apk add` instead of `apt-get install` - Example: `apk add --no-cache curl wget` ### "IPv6 not persisting after reboot" - IPv6 must be configured in `/etc/network/interfaces` - The `verb_ip6()` function handles this automatically - Verify: `cat /etc/network/interfaces` ### "Service won't start on Alpine" - Alpine uses OpenRC, not systemd - Use `rc-service nginx start` instead of `systemctl start nginx` - Enable service: `rc-update add nginx` - Check logs: `/var/log/` or `rc-service nginx status` ### "Container too large" - Alpine should be much smaller than Debian - Verify using `apk add --no-cache` (removes package cache) - Example: `apk add --no-cache nginx` (not `apk add nginx`) ## Related Documentation - **[alpine-tools.func/](../alpine-tools.func/)** - Alpine tool installation - **[install.func/](../install.func/)** - Standard installation functions - **[core.func/](../core.func/)** - Utility functions - **[error_handler.func/](../error_handler.func/)** - Error handling - **[UPDATED_APP-install.md](../../UPDATED_APP-install.md)** - Application script guide ## Recent Updates ### Version 2.0 (Dec 2025) - ✅ Enhanced IPv6 persistence configuration - ✅ Improved OpenRC service management - ✅ Better apk error handling - ✅ Added Alpine-specific best practices documentation - ✅ Streamlined SSH setup for Alpine --- **Last Updated**: December 2025 **Maintainers**: community-scripts team **License**: MIT ================================================ FILE: docs/misc/alpine-tools.func/ALPINE_TOOLS_FUNC_FLOWCHART.md ================================================ # alpine-tools.func Flowchart Alpine tool installation and package management flow. ## Tool Installation on Alpine ``` apk_update() ↓ add_community_repo() [optional] ↓ apk_add PACKAGES ↓ Tool Installation ↓ rc-service start ↓ rc-update add [enable at boot] ↓ Complete ✓ ``` --- **Last Updated**: December 2025 ================================================ FILE: docs/misc/alpine-tools.func/ALPINE_TOOLS_FUNC_FUNCTIONS_REFERENCE.md ================================================ # alpine-tools.func Functions Reference Alpine-specific tool installation functions. ## Core Functions ### apk_update() Update Alpine package lists. ### apk_add(PACKAGES) Install Alpine packages. ### apk_del(PACKAGES) Remove Alpine packages. ### add_community_repo() Enable Alpine community repository. ### add_testing_repo() Enable Alpine testing repository. ### Alpine Tool Functions - `setup_nodejs()` - Alpine Node.js - `setup_php()` - Alpine PHP - `setup_mariadb()` - Alpine MariaDB - `setup_postgresql()` - Alpine PostgreSQL - (+ more Alpine-specific setups) --- **Last Updated**: December 2025 ================================================ FILE: docs/misc/alpine-tools.func/ALPINE_TOOLS_FUNC_INTEGRATION.md ================================================ # alpine-tools.func Integration Guide Alpine tool installation integration with container workflows. --- **Last Updated**: December 2025 ================================================ FILE: docs/misc/alpine-tools.func/ALPINE_TOOLS_FUNC_USAGE_EXAMPLES.md ================================================ # alpine-tools.func Usage Examples Examples for Alpine tool installation. ### Example: Alpine Setup with Tools ```bash #!/usr/bin/env bash source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" apk_update setup_nodejs "20" setup_php "8.3" setup_mariadb "11" ``` --- **Last Updated**: December 2025 ================================================ FILE: docs/misc/alpine-tools.func/README.md ================================================ # alpine-tools.func Documentation ## Overview The `alpine-tools.func` file provides Alpine Linux-specific tool installation functions for package and service management within Alpine LXC containers. It complements `tools.func` with Alpine-specific implementations using the apk package manager. ## Purpose and Use Cases - **Alpine Tool Installation**: Install services and tools using apk on Alpine - **Package Management**: Safe apk operations with error handling - **Service Setup**: Install and configure common services on Alpine - **Dependency Management**: Handle Alpine-specific package dependencies - **Repository Management**: Setup and manage Alpine package repositories ## Quick Reference ### Key Function Groups - **Package Operations**: Alpine-specific apk commands with error handling - **Service Installation**: Install databases, web servers, tools on Alpine - **Repository Setup**: Configure Alpine community and testing repositories - **Tool Setup**: Install development tools and utilities ### Dependencies - **External**: `apk`, `curl`, `wget` - **Internal**: Uses functions from `core.func`, `error_handler.func` ### Integration Points - Used by: Alpine-based application install scripts - Uses: Environment variables from build.func - Provides: Alpine package and tool installation services ## Documentation Files ### 📊 [ALPINE_TOOLS_FUNC_FLOWCHART.md](./ALPINE_TOOLS_FUNC_FLOWCHART.md) Visual execution flows for package operations and tool installation on Alpine. ### 📚 [ALPINE_TOOLS_FUNC_FUNCTIONS_REFERENCE.md](./ALPINE_TOOLS_FUNC_FUNCTIONS_REFERENCE.md) Complete alphabetical reference of all Alpine tool functions. ### 💡 [ALPINE_TOOLS_FUNC_USAGE_EXAMPLES.md](./ALPINE_TOOLS_FUNC_USAGE_EXAMPLES.md) Practical examples for common Alpine installation patterns. ### 🔗 [ALPINE_TOOLS_FUNC_INTEGRATION.md](./ALPINE_TOOLS_FUNC_INTEGRATION.md) How alpine-tools.func integrates with Alpine installation workflows. ## Key Features ### Alpine Package Management - **apk Add**: Safe package installation with error handling - **apk Update**: Update package lists with retry logic - **apk Del**: Remove packages and dependencies - **Repository Configuration**: Add community and testing repos ### Alpine Tool Coverage - **Web Servers**: nginx, lighttpd - **Databases**: mariadb, postgresql, sqlite - **Development**: gcc, make, git, node.js (via apk) - **Services**: sshd, docker, podman - **Utilities**: curl, wget, htop, vim ### Error Handling - **Retry Logic**: Automatic recovery from transient failures - **Dependency Resolution**: Handle missing dependencies - **Lock Management**: Wait for apk locks to release - **Error Reporting**: Clear error messages ## Function Categories ### 🔹 Package Management - `apk_update()` - Update Alpine packages with retry - `apk_add()` - Install packages safely - `apk_del()` - Remove packages completely ### 🔹 Repository Functions - `add_community_repo()` - Enable community repositories - `add_testing_repo()` - Enable testing repositories - `setup_apk_repo()` - Configure custom apk repositories ### 🔹 Service Installation Functions - `setup_nginx()` - Install and configure nginx - `setup_mariadb()` - Install MariaDB on Alpine - `setup_postgresql()` - Install PostgreSQL - `setup_docker()` - Install Docker on Alpine - `setup_nodejs()` - Install Node.js from Alpine repos ### 🔹 Development Tools - `setup_build_tools()` - Install gcc, make, build-essential - `setup_git()` - Install git version control - `setup_python()` - Install Python 3 and pip ## Alpine vs Debian Package Differences | Package | Debian | Alpine | |---------|:---:|:---:| | nginx | `apt-get install nginx` | `apk add nginx` | | mariadb | `apt-get install mariadb-server` | `apk add mariadb` | | PostgreSQL | `apt-get install postgresql` | `apk add postgresql` | | Node.js | `apt-get install nodejs npm` | `apk add nodejs npm` | | Docker | Special setup | `apk add docker` | | Python | `apt-get install python3 python3-pip` | `apk add python3 py3-pip` | ## Common Usage Patterns ### Basic Alpine Tool Installation ```bash #!/usr/bin/env bash source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" # Update package lists apk_update # Install nginx apk_add nginx # Start service rc-service nginx start rc-update add nginx ``` ### With Community Repository ```bash #!/usr/bin/env bash source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" # Enable community repo for more packages add_community_repo # Update and install apk_update apk_add postgresql postgresql-client # Start service rc-service postgresql start ``` ### Development Environment ```bash #!/usr/bin/env bash source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" # Install build tools setup_build_tools setup_git setup_nodejs "20" # Install application git clone https://example.com/app cd app npm install ``` ## Best Practices ### ✅ DO - Always use `apk add --no-cache` to keep images small - Call `apk_update()` before installing packages - Use community repository for more packages (`add_community_repo`) - Handle apk locks gracefully with retry logic - Use `$STD` variable for output control - Check if tool already installed before reinstalling ### ❌ DON'T - Use `apt-get` commands (Alpine doesn't have apt) - Install packages without `--no-cache` flag - Hardcode Alpine-specific paths - Mix Alpine and Debian commands - Forget to enable services with `rc-update` - Use `systemctl` (Alpine has OpenRC, not systemd) ## Alpine Repository Configuration ### Default Repositories Alpine comes with main repository enabled by default. Additional repos: ```bash # Community repository (apk add php, go, rust, etc.) add_community_repo # Testing repository (bleeding edge packages) add_testing_repo ``` ### Repository Locations ```bash /etc/apk/repositories # Main repo list /etc/apk/keys/ # GPG keys for repos /var/cache/apk/ # Package cache ``` ## Package Size Optimization Alpine is designed for small container images: ```bash # DON'T: Leaves package cache (increases image size) apk add nginx # DO: Remove cache to reduce size apk add --no-cache nginx # Expected sizes: # Alpine base: ~5MB # Alpine + nginx: ~10-15MB # Debian base: ~75MB # Debian + nginx: ~90-95MB ``` ## Service Management on Alpine ### Using OpenRC ```bash # Start service immediately rc-service nginx start # Stop service rc-service nginx stop # Restart service rc-service nginx restart # Enable at boot rc-update add nginx # Disable at boot rc-update del nginx # List enabled services rc-update show ``` ## Troubleshooting ### "apk: lock is held by PID" ```bash # Alpine apk database is locked (another process using apk) # Wait a moment sleep 5 apk_update # Or manually: rm /var/lib/apk/lock 2>/dev/null || true apk update ``` ### "Package not found" ```bash # May be in community or testing repository add_community_repo apk_update apk_add package-name ``` ### "Repository not responding" ```bash # Alpine repo may be slow or unreachable # Try updating again with retry logic apk_update # Built-in retry logic # Or manually retry sleep 10 apk update ``` ### "Service fails to start" ```bash # Check service status on Alpine rc-service nginx status # View logs tail /var/log/nginx/error.log # Verify configuration nginx -t ``` ## Related Documentation - **[alpine-install.func/](../alpine-install.func/)** - Alpine installation functions - **[tools.func/](../tools.func/)** - Debian/standard tool installation - **[core.func/](../core.func/)** - Utility functions - **[error_handler.func/](../error_handler.func/)** - Error handling - **[UPDATED_APP-install.md](../../UPDATED_APP-install.md)** - Application script guide ## Recent Updates ### Version 2.0 (Dec 2025) - ✅ Enhanced apk error handling and retry logic - ✅ Improved repository management - ✅ Better service management with OpenRC - ✅ Added Alpine-specific optimization guidance - ✅ Enhanced package cache management --- **Last Updated**: December 2025 **Maintainers**: community-scripts team **License**: MIT ================================================ FILE: docs/misc/api.func/API_FLOWCHART.md ================================================ # api.func Execution Flowchart ## Main API Communication Flow ``` ┌─────────────────────────────────────────────────────────────────────────────────┐ │ API Communication Initialization │ │ Entry point when api.func functions are called by installation scripts │ └─────────────────────┬───────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────────────────────┐ │ Prerequisites Check │ │ │ │ ┌─────────────────────────────────────────────────────────────────────────────┐ │ │ │ Prerequisites Validation │ │ │ │ │ │ │ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────┐ │ │ │ │ │ Check curl │ │ Check │ │ Check │ │ │ │ │ │ Availability │ │ Diagnostics │ │ Random UUID │ │ │ │ │ │ │ │ Setting │ │ │ │ │ │ │ • command -v │ │ • DIAGNOSTICS │ │ • RANDOM_UUID │ │ │ │ │ curl │ │ = "yes" │ │ not empty │ │ │ │ │ • Return if │ │ • Return if │ │ • Return if │ │ │ │ │ not found │ │ disabled │ │ not set │ │ │ │ │ │ │ │ │ │ │ │ │ └─────────────────┘ └─────────────────┘ └─────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────────────────────┐ │ Data Collection │ │ │ │ ┌─────────────────────────────────────────────────────────────────────────────┐ │ │ │ System Information Gathering │ │ │ │ │ │ │ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────┐ │ │ │ │ │ Get PVE │ │ Collect │ │ Prepare JSON │ │ │ │ │ │ Version │ │ Environment │ │ Payload │ │ │ │ │ │ │ Variables │ │ │ │ │ │ │ • pveversion │ │ • CT_TYPE │ │ • Create JSON │ │ │ │ │ command │ │ • DISK_SIZE │ │ structure │ │ │ │ │ • Parse version │ │ • CORE_COUNT │ │ • Include all │ │ │ │ │ • Extract │ │ • RAM_SIZE │ │ variables │ │ │ │ │ major.minor │ │ • var_os │ │ • Format for API │ │ │ │ │ │ │ • var_version │ │ │ │ │ │ │ │ │ • NSAPP │ │ │ │ │ │ │ │ │ • METHOD │ │ │ │ │ │ └─────────────────┘ └─────────────────┘ └─────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────────────────────┐ │ API Request Execution │ │ │ │ ┌─────────────────────────────────────────────────────────────────────────────┐ │ │ │ HTTP Request Processing │ │ │ │ │ │ │ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────┐ │ │ │ │ │ Prepare │ │ Execute │ │ Handle │ │ │ │ │ │ Request │ │ HTTP Request │ │ Response │ │ │ │ │ │ │ │ │ │ │ │ │ │ • Set API URL │ │ • curl -s -w │ │ • Capture HTTP │ │ │ │ │ • Set headers │ │ "%{http_code}" │ │ status code │ │ │ │ │ • Set payload │ │ • POST request │ │ • Store response │ │ │ │ │ • Content-Type │ │ • JSON data │ │ • Handle errors │ │ │ │ │ application/ │ │ • Follow │ │ gracefully │ │ │ │ │ json │ │ redirects │ │ │ │ │ │ └─────────────────┘ └─────────────────┘ └─────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────────────────────────┘ ``` ## LXC API Reporting Flow ``` ┌─────────────────────────────────────────────────────────────────────────────────┐ │ POST_TO_API() Flow │ │ Send LXC container installation data to API │ └─────────────────────┬───────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────────────────────┐ │ LXC Data Preparation │ │ │ │ ┌─────────────────────────────────────────────────────────────────────────────┐ │ │ │ LXC-Specific Data Collection │ │ │ │ │ │ │ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────┐ │ │ │ │ │ Set LXC │ │ Include LXC │ │ Set Status │ │ │ │ │ │ Type │ │ Variables │ │ Information │ │ │ │ │ │ │ │ │ │ │ │ │ │ • ct_type: 1 │ │ • DISK_SIZE │ │ • status: │ │ │ │ │ • type: "lxc" │ │ • CORE_COUNT │ │ "installing" │ │ │ │ │ • Include all │ │ • RAM_SIZE │ │ • Include all │ │ │ │ │ LXC data │ │ • var_os │ │ tracking data │ │ │ │ │ │ │ • var_version │ │ │ │ │ │ │ │ │ • DISABLEIP6 │ │ │ │ │ │ │ │ │ • NSAPP │ │ │ │ │ │ │ │ │ • METHOD │ │ │ │ │ │ │ │ │ • pve_version │ │ │ │ │ │ │ │ │ • random_id │ │ │ │ │ │ └─────────────────┘ └─────────────────┘ └─────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────────────────────┐ │ JSON Payload Creation │ │ │ │ ┌─────────────────────────────────────────────────────────────────────────────┐ │ │ │ JSON Structure Generation │ │ │ │ │ │ │ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────┐ │ │ │ │ │ Create JSON │ │ Validate │ │ Format for │ │ │ │ │ │ Structure │ │ Data │ │ API Request │ │ │ │ │ │ │ │ │ │ │ │ │ │ • Use heredoc │ │ • Check all │ │ • Ensure proper │ │ │ │ │ syntax │ │ variables │ │ JSON format │ │ │ │ │ • Include all │ │ are set │ │ • Escape special │ │ │ │ │ required │ │ • Validate │ │ characters │ │ │ │ │ fields │ │ data types │ │ • Set content │ │ │ │ │ • Format │ │ • Handle │ │ type │ │ │ │ │ properly │ │ missing │ │ │ │ │ │ │ │ │ values │ │ │ │ │ │ └─────────────────┘ └─────────────────┘ └─────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────────────────────────┘ ``` ## VM API Reporting Flow ``` ┌─────────────────────────────────────────────────────────────────────────────────┐ │ POST_TO_API_VM() Flow │ │ Send VM installation data to API │ └─────────────────────┬───────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────────────────────┐ │ VM Data Preparation │ │ │ │ ┌─────────────────────────────────────────────────────────────────────────────┐ │ │ │ VM-Specific Data Collection │ │ │ │ │ │ │ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────┐ │ │ │ │ │ Check │ │ Set VM │ │ Process Disk │ │ │ │ │ │ Diagnostics │ │ Type │ │ Size │ │ │ │ │ File │ │ │ │ │ │ │ │ │ │ │ • ct_type: 2 │ │ • Remove 'G' │ │ │ │ │ • Check file │ │ • type: "vm" │ │ suffix │ │ │ │ │ existence │ │ • Include all │ │ • Convert to │ │ │ │ │ • Read │ │ VM data │ │ numeric value │ │ │ │ │ DIAGNOSTICS │ │ │ │ • Store in │ │ │ │ │ setting │ │ │ │ DISK_SIZE_API │ │ │ │ │ • Parse value │ │ │ │ │ │ │ │ └─────────────────┘ └─────────────────┘ └─────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────────────────────┐ │ VM JSON Payload Creation │ │ │ │ ┌─────────────────────────────────────────────────────────────────────────────┐ │ │ │ VM-Specific JSON Structure │ │ │ │ │ │ │ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────┐ │ │ │ │ │ Include VM │ │ Set VM │ │ Format VM │ │ │ │ │ │ Variables │ │ Status │ │ Data for API │ │ │ │ │ │ │ │ │ │ │ │ │ │ • DISK_SIZE_API │ │ • status: │ │ • Ensure proper │ │ │ │ │ • CORE_COUNT │ │ "installing" │ │ JSON format │ │ │ │ │ • RAM_SIZE │ │ • Include all │ │ • Handle VM- │ │ │ │ │ • var_os │ │ tracking │ │ specific data │ │ │ │ │ • var_version │ │ information │ │ • Set appropriate │ │ │ │ │ • NSAPP │ │ │ │ content type │ │ │ │ │ • METHOD │ │ │ │ │ │ │ │ │ • pve_version │ │ │ │ │ │ │ │ │ • random_id │ │ │ │ │ │ │ │ └─────────────────┘ └─────────────────┘ └─────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────────────────────────┘ ``` ## Status Update Flow ``` ┌─────────────────────────────────────────────────────────────────────────────────┐ │ POST_UPDATE_TO_API() Flow │ │ Send installation completion status to API │ └─────────────────────┬───────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────────────────────┐ │ Update Prevention Check │ │ │ │ ┌─────────────────────────────────────────────────────────────────────────────┐ │ │ │ Duplicate Update Prevention │ │ │ │ │ │ │ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────┐ │ │ │ │ │ Check │ │ Set Flag │ │ Return Early │ │ │ │ │ │ POST_UPDATE_ │ │ if First │ │ if Already │ │ │ │ │ DONE │ │ Update │ │ Updated │ │ │ │ │ │ │ │ │ │ │ │ │ │ • Check if │ │ • Set │ │ • Return 0 │ │ │ │ │ already │ │ POST_UPDATE_ │ │ • Skip API call │ │ │ │ │ updated │ │ DONE=true │ │ • Prevent │ │ │ │ │ • Prevent │ │ • Continue │ │ duplicate │ │ │ │ │ duplicate │ │ with update │ │ requests │ │ │ │ │ requests │ │ │ │ │ │ │ │ └─────────────────┘ └─────────────────┘ └─────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────────────────────┐ │ Status and Error Processing │ │ │ │ ┌─────────────────────────────────────────────────────────────────────────────┐ │ │ │ Status Determination │ │ │ │ │ │ │ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────┐ │ │ │ │ │ Determine │ │ Get Error │ │ Prepare Status │ │ │ │ │ │ Status │ │ Description │ │ Data │ │ │ │ │ │ │ │ │ │ │ │ │ │ • status: │ │ • Call │ │ • Include status │ │ │ │ │ "success" or │ │ get_error_ │ │ • Include error │ │ │ │ │ "failed" │ │ description() │ │ description │ │ │ │ │ • Set exit │ │ • Get human- │ │ • Include random │ │ │ │ │ code based │ │ readable │ │ ID for tracking │ │ │ │ │ on status │ │ error message │ │ │ │ │ │ │ • Default to │ │ • Handle │ │ │ │ │ │ │ error if │ │ unknown │ │ │ │ │ │ │ not set │ │ errors │ │ │ │ │ │ └─────────────────┘ └─────────────────┘ └─────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────────────────────┐ │ Status Update API Request │ │ │ │ ┌─────────────────────────────────────────────────────────────────────────────┐ │ │ │ Status Update Payload Creation │ │ │ │ │ │ │ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────┐ │ │ │ │ │ Create │ │ Send Status │ │ Mark Update │ │ │ │ │ │ Status JSON │ │ Update │ │ Complete │ │ │ │ │ │ │ │ │ │ │ │ │ │ • Include │ │ • POST to │ │ • Set │ │ │ │ │ status │ │ updatestatus │ │ POST_UPDATE_ │ │ │ │ │ • Include │ │ endpoint │ │ DONE=true │ │ │ │ │ error │ │ • Include JSON │ │ • Prevent further │ │ │ │ │ description │ │ payload │ │ updates │ │ │ │ │ • Include │ │ • Handle │ │ • Complete │ │ │ │ │ random_id │ │ response │ │ process │ │ │ │ │ │ │ gracefully │ │ │ │ │ │ └─────────────────┘ └─────────────────┘ └─────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────────────────────────┘ ``` ## Error Description Flow ``` ┌─────────────────────────────────────────────────────────────────────────────────┐ │ GET_ERROR_DESCRIPTION() Flow │ │ Convert numeric exit codes to human-readable explanations │ └─────────────────────┬───────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────────────────────┐ │ Error Code Classification │ │ │ │ ┌─────────────────────────────────────────────────────────────────────────────┐ │ │ │ Error Code Categories │ │ │ │ │ │ │ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────┐ │ │ │ │ │ General │ │ Network │ │ LXC-Specific │ │ │ │ │ │ System │ │ Errors │ │ Errors │ │ │ │ │ Errors │ │ │ │ │ │ │ │ │ │ │ • 18: Connection│ │ • 100-101: LXC │ │ │ │ │ • 0-9: Basic │ │ failed │ │ install errors │ │ │ │ │ errors │ │ • 22: Invalid │ │ • 200-209: LXC │ │ │ │ │ • 126-128: │ │ argument │ │ creation errors │ │ │ │ │ Command │ │ • 28: No space │ │ │ │ │ │ │ errors │ │ • 35: Timeout │ │ │ │ │ │ │ • 129-143: │ │ • 56: TLS error │ │ │ │ │ │ │ Signal │ │ • 60: SSL cert │ │ │ │ │ │ │ errors │ │ error │ │ │ │ │ │ │ • 152: Resource │ │ │ │ │ │ │ │ │ limit │ │ │ │ │ │ │ │ │ • 255: Unknown │ │ │ │ │ │ │ │ │ critical │ │ │ │ │ │ │ │ └─────────────────┘ └─────────────────┘ └─────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────────────────────┐ │ Error Message Return │ │ │ │ ┌─────────────────────────────────────────────────────────────────────────────┐ │ │ │ Error Message Formatting │ │ │ │ │ │ │ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────┐ │ │ │ │ │ Match Error │ │ Return │ │ Default Case │ │ │ │ │ │ Code │ │ Description │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ • Use case │ │ • Return │ │ • Return "Unknown │ │ │ │ │ statement │ │ human- │ │ error code │ │ │ │ │ • Match │ │ readable │ │ (exit_code)" │ │ │ │ │ specific │ │ message │ │ • Handle │ │ │ │ │ codes │ │ • Include │ │ unrecognized │ │ │ │ │ • Handle │ │ context │ │ codes │ │ │ │ │ ranges │ │ information │ │ • Provide fallback │ │ │ │ │ │ │ │ │ message │ │ │ │ └─────────────────┘ └─────────────────┘ └─────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────────────────────────┘ ``` ## Integration Points ### With Installation Scripts - **build.func**: Sends LXC installation data - **vm-core.func**: Sends VM installation data - **install.func**: Reports installation status - **alpine-install.func**: Reports Alpine installation data ### With Error Handling - **error_handler.func**: Provides error explanations - **core.func**: Uses error descriptions in silent execution - **Diagnostic reporting**: Tracks error patterns ### External Dependencies - **curl**: HTTP client for API communication - **Community Scripts API**: External API endpoint - **Network connectivity**: Required for API communication ================================================ FILE: docs/misc/api.func/API_FUNCTIONS_REFERENCE.md ================================================ # api.func Functions Reference ## Overview This document provides a comprehensive alphabetical reference of all functions in `api.func`, including parameters, dependencies, usage examples, and error handling. ## Function Categories ### Error Description Functions #### `get_error_description()` **Purpose**: Convert numeric exit codes to human-readable explanations **Parameters**: - `$1` - Exit code to explain **Returns**: Human-readable error explanation string **Side Effects**: None **Dependencies**: None **Environment Variables Used**: None **Supported Exit Codes**: - **General System**: 0-9, 18, 22, 28, 35, 56, 60, 125-128, 129-143, 152, 255 - **LXC-Specific**: 100-101, 200-209 - **Docker**: 125 **Usage Example**: ```bash error_msg=$(get_error_description 127) echo "Error 127: $error_msg" # Output: Error 127: Command not found: Incorrect path or missing dependency. ``` **Error Code Examples**: ```bash get_error_description 0 # " " (space) get_error_description 1 # "General error: An unspecified error occurred." get_error_description 127 # "Command not found: Incorrect path or missing dependency." get_error_description 200 # "LXC creation failed." get_error_description 255 # "Unknown critical error, often due to missing permissions or broken scripts." ``` ### API Communication Functions #### `post_to_api()` **Purpose**: Send LXC container installation data to community-scripts.org API **Parameters**: None (uses environment variables) **Returns**: None **Side Effects**: - Sends HTTP POST request to API - Stores response in RESPONSE variable - Requires curl command and network connectivity **Dependencies**: `curl` command **Environment Variables Used**: `DIAGNOSTICS`, `RANDOM_UUID`, `CT_TYPE`, `DISK_SIZE`, `CORE_COUNT`, `RAM_SIZE`, `var_os`, `var_version`, `DISABLEIP6`, `NSAPP`, `METHOD` **Prerequisites**: - `curl` command must be available - `DIAGNOSTICS` must be set to "yes" - `RANDOM_UUID` must be set and not empty **API Endpoint**: `https://api.community-scripts.org/dev/upload` **JSON Payload Structure**: ```json { "ct_type": 1, "type": "lxc", "disk_size": 8, "core_count": 2, "ram_size": 2048, "os_type": "debian", "os_version": "12", "disableip6": "true", "nsapp": "plex", "method": "install", "pve_version": "8.0", "status": "installing", "random_id": "uuid-string" } ``` **Usage Example**: ```bash export DIAGNOSTICS="yes" export RANDOM_UUID="$(uuidgen)" export CT_TYPE=1 export DISK_SIZE=8 export CORE_COUNT=2 export RAM_SIZE=2048 export var_os="debian" export var_version="12" export NSAPP="plex" export METHOD="install" post_to_api ``` #### `post_to_api_vm()` **Purpose**: Send VM installation data to community-scripts.org API **Parameters**: None (uses environment variables) **Returns**: None **Side Effects**: - Sends HTTP POST request to API - Stores response in RESPONSE variable - Requires curl command and network connectivity **Dependencies**: `curl` command, diagnostics file **Environment Variables Used**: `DIAGNOSTICS`, `RANDOM_UUID`, `DISK_SIZE`, `CORE_COUNT`, `RAM_SIZE`, `var_os`, `var_version`, `NSAPP`, `METHOD` **Prerequisites**: - `/usr/local/community-scripts/diagnostics` file must exist - `DIAGNOSTICS` must be set to "yes" in diagnostics file - `curl` command must be available - `RANDOM_UUID` must be set and not empty **API Endpoint**: `https://api.community-scripts.org/dev/upload` **JSON Payload Structure**: ```json { "ct_type": 2, "type": "vm", "disk_size": 8, "core_count": 2, "ram_size": 2048, "os_type": "debian", "os_version": "12", "disableip6": "", "nsapp": "plex", "method": "install", "pve_version": "8.0", "status": "installing", "random_id": "uuid-string" } ``` **Usage Example**: ```bash # Create diagnostics file echo "DIAGNOSTICS=yes" > /usr/local/community-scripts/diagnostics export RANDOM_UUID="$(uuidgen)" export DISK_SIZE="8G" export CORE_COUNT=2 export RAM_SIZE=2048 export var_os="debian" export var_version="12" export NSAPP="plex" export METHOD="install" post_to_api_vm ``` #### `post_update_to_api()` **Purpose**: Send installation completion status to community-scripts.org API **Parameters**: - `$1` - Status ("success" or "failed", default: "failed") - `$2` - Exit code (default: 1) **Returns**: None **Side Effects**: - Sends HTTP POST request to API - Sets POST_UPDATE_DONE=true to prevent duplicates - Stores response in RESPONSE variable **Dependencies**: `curl` command, `get_error_description()` **Environment Variables Used**: `DIAGNOSTICS`, `RANDOM_UUID` **Prerequisites**: - `curl` command must be available - `DIAGNOSTICS` must be set to "yes" - `RANDOM_UUID` must be set and not empty - POST_UPDATE_DONE must be false (prevents duplicates) **API Endpoint**: `https://api.community-scripts.org/dev/upload/updatestatus` **JSON Payload Structure**: ```json { "status": "success", "error": "Error description from get_error_description()", "random_id": "uuid-string" } ``` **Usage Example**: ```bash export DIAGNOSTICS="yes" export RANDOM_UUID="$(uuidgen)" # Report successful installation post_update_to_api "success" 0 # Report failed installation post_update_to_api "failed" 127 ``` ## Function Call Hierarchy ### API Communication Flow ``` post_to_api() ├── Check curl availability ├── Check DIAGNOSTICS setting ├── Check RANDOM_UUID ├── Get PVE version ├── Create JSON payload └── Send HTTP POST request post_to_api_vm() ├── Check diagnostics file ├── Check curl availability ├── Check DIAGNOSTICS setting ├── Check RANDOM_UUID ├── Process disk size ├── Get PVE version ├── Create JSON payload └── Send HTTP POST request post_update_to_api() ├── Check POST_UPDATE_DONE flag ├── Check curl availability ├── Check DIAGNOSTICS setting ├── Check RANDOM_UUID ├── Determine status and exit code ├── Get error description ├── Create JSON payload ├── Send HTTP POST request └── Set POST_UPDATE_DONE=true ``` ### Error Description Flow ``` get_error_description() ├── Match exit code ├── Return appropriate description └── Handle unknown codes ``` ## Error Code Reference ### General System Errors | Code | Description | |------|-------------| | 0 | (space) | | 1 | General error: An unspecified error occurred. | | 2 | Incorrect shell usage or invalid command arguments. | | 3 | Unexecuted function or invalid shell condition. | | 4 | Error opening a file or invalid path. | | 5 | I/O error: An input/output failure occurred. | | 6 | No such device or address. | | 7 | Insufficient memory or resource exhaustion. | | 8 | Non-executable file or invalid file format. | | 9 | Failed child process execution. | | 18 | Connection to a remote server failed. | | 22 | Invalid argument or faulty network connection. | | 28 | No space left on device. | | 35 | Timeout while establishing a connection. | | 56 | Faulty TLS connection. | | 60 | SSL certificate error. | ### Command Execution Errors | Code | Description | |------|-------------| | 125 | Docker error: Container could not start. | | 126 | Command not executable: Incorrect permissions or missing dependencies. | | 127 | Command not found: Incorrect path or missing dependency. | | 128 | Invalid exit signal, e.g., incorrect Git command. | ### Signal Errors | Code | Description | |------|-------------| | 129 | Signal 1 (SIGHUP): Process terminated due to hangup. | | 130 | Signal 2 (SIGINT): Manual termination via Ctrl+C. | | 132 | Signal 4 (SIGILL): Illegal machine instruction. | | 133 | Signal 5 (SIGTRAP): Debugging error or invalid breakpoint signal. | | 134 | Signal 6 (SIGABRT): Program aborted itself. | | 135 | Signal 7 (SIGBUS): Memory error, invalid memory address. | | 137 | Signal 9 (SIGKILL): Process forcibly terminated (OOM-killer or 'kill -9'). | | 139 | Signal 11 (SIGSEGV): Segmentation fault, possibly due to invalid pointer access. | | 141 | Signal 13 (SIGPIPE): Pipe closed unexpectedly. | | 143 | Signal 15 (SIGTERM): Process terminated normally. | | 152 | Signal 24 (SIGXCPU): CPU time limit exceeded. | ### LXC-Specific Errors | Code | Description | |------|-------------| | 100 | LXC install error: Unexpected error in create_lxc.sh. | | 101 | LXC install error: No network connection detected. | | 200 | LXC creation failed. | | 201 | LXC error: Invalid Storage class. | | 202 | User aborted menu in create_lxc.sh. | | 203 | CTID not set in create_lxc.sh. | | 204 | PCT_OSTYPE not set in create_lxc.sh. | | 205 | CTID cannot be less than 100 in create_lxc.sh. | | 206 | CTID already in use in create_lxc.sh. | | 207 | Template not found in create_lxc.sh. | | 208 | Error downloading template in create_lxc.sh. | | 209 | Container creation failed, but template is intact in create_lxc.sh. | ### Other Errors | Code | Description | |------|-------------| | 255 | Unknown critical error, often due to missing permissions or broken scripts. | | * | Unknown error code (exit_code). | ## Environment Variable Dependencies ### Required Variables - **`DIAGNOSTICS`**: Enable/disable diagnostic reporting ("yes"/"no") - **`RANDOM_UUID`**: Unique identifier for tracking ### Optional Variables - **`CT_TYPE`**: Container type (1 for LXC, 2 for VM) - **`DISK_SIZE`**: Disk size in GB (or GB with 'G' suffix for VM) - **`CORE_COUNT`**: Number of CPU cores - **`RAM_SIZE`**: RAM size in MB - **`var_os`**: Operating system type - **`var_version`**: OS version - **`DISABLEIP6`**: IPv6 disable setting - **`NSAPP`**: Namespace application name - **`METHOD`**: Installation method ### Internal Variables - **`POST_UPDATE_DONE`**: Prevents duplicate status updates - **`API_URL`**: Community scripts API endpoint - **`JSON_PAYLOAD`**: API request payload - **`RESPONSE`**: API response - **`DISK_SIZE_API`**: Processed disk size for VM API ## Error Handling Patterns ### API Communication Errors - All API functions handle curl failures gracefully - Network errors don't block installation process - Missing prerequisites cause early return - Duplicate updates are prevented ### Error Description Errors - Unknown error codes return generic message - All error codes are handled with case statement - Fallback message includes the actual error code ### Prerequisites Validation - Check curl availability before API calls - Validate DIAGNOSTICS setting - Ensure RANDOM_UUID is set - Check for duplicate updates ## Integration Examples ### With build.func ```bash #!/usr/bin/env bash source core.func source api.func source build.func # Set up API reporting export DIAGNOSTICS="yes" export RANDOM_UUID="$(uuidgen)" # Report installation start post_to_api # Container creation... # ... build.func code ... # Report completion if [[ $? -eq 0 ]]; then post_update_to_api "success" 0 else post_update_to_api "failed" $? fi ``` ### With vm-core.func ```bash #!/usr/bin/env bash source core.func source api.func source vm-core.func # Set up API reporting export DIAGNOSTICS="yes" export RANDOM_UUID="$(uuidgen)" # Report VM installation start post_to_api_vm # VM creation... # ... vm-core.func code ... # Report completion post_update_to_api "success" 0 ``` ### With error_handler.func ```bash #!/usr/bin/env bash source core.func source error_handler.func source api.func # Use error descriptions error_code=127 error_msg=$(get_error_description $error_code) echo "Error $error_code: $error_msg" # Report error to API post_update_to_api "failed" $error_code ``` ## Best Practices ### API Usage 1. Always check prerequisites before API calls 2. Use unique identifiers for tracking 3. Handle API failures gracefully 4. Don't block installation on API failures ### Error Reporting 1. Use appropriate error codes 2. Provide meaningful error descriptions 3. Report both success and failure cases 4. Prevent duplicate status updates ### Diagnostic Reporting 1. Respect user privacy settings 2. Only send data when diagnostics enabled 3. Use anonymous tracking identifiers 4. Include relevant system information ### Error Handling 1. Handle unknown error codes gracefully 2. Provide fallback error messages 3. Include error code in unknown error messages 4. Use consistent error message format ================================================ FILE: docs/misc/api.func/API_INTEGRATION.md ================================================ # api.func Integration Guide ## Overview This document describes how `api.func` integrates with other components in the Proxmox Community Scripts project, including dependencies, data flow, and API surface. ## Dependencies ### External Dependencies #### Required Commands - **`curl`**: HTTP client for API communication - **`uuidgen`**: Generate unique identifiers (optional, can use other methods) #### Optional Commands - **None**: No other external command dependencies ### Internal Dependencies #### Environment Variables from Other Scripts - **build.func**: Provides container creation variables - **vm-core.func**: Provides VM creation variables - **core.func**: Provides system information variables - **Installation scripts**: Provide application-specific variables ## Integration Points ### With build.func #### LXC Container Reporting ```bash # build.func uses api.func for container reporting source core.func source api.func source build.func # Set up API reporting export DIAGNOSTICS="yes" export RANDOM_UUID="$(uuidgen)" # Container creation with API reporting create_container() { # Set container parameters export CT_TYPE=1 export DISK_SIZE="$var_disk" export CORE_COUNT="$var_cpu" export RAM_SIZE="$var_ram" export var_os="$var_os" export var_version="$var_version" export NSAPP="$APP" export METHOD="install" # Report installation start post_to_api # Container creation using build.func # ... build.func container creation logic ... # Report completion if [[ $? -eq 0 ]]; then post_update_to_api "success" 0 else post_update_to_api "failed" $? fi } ``` #### Error Reporting Integration ```bash # build.func uses api.func for error reporting handle_container_error() { local exit_code=$1 local error_msg=$(get_error_description $exit_code) echo "Container creation failed: $error_msg" post_update_to_api "failed" $exit_code } ``` ### With vm-core.func #### VM Installation Reporting ```bash # vm-core.func uses api.func for VM reporting source core.func source api.func source vm-core.func # Set up VM API reporting mkdir -p /usr/local/community-scripts echo "DIAGNOSTICS=yes" > /usr/local/community-scripts/diagnostics export RANDOM_UUID="$(uuidgen)" # VM creation with API reporting create_vm() { # Set VM parameters export DISK_SIZE="${var_disk}G" export CORE_COUNT="$var_cpu" export RAM_SIZE="$var_ram" export var_os="$var_os" export var_version="$var_version" export NSAPP="$APP" export METHOD="install" # Report VM installation start post_to_api_vm # VM creation using vm-core.func # ... vm-core.func VM creation logic ... # Report completion post_update_to_api "success" 0 } ``` ### With core.func #### System Information Integration ```bash # core.func provides system information for api.func source core.func source api.func # Get system information for API reporting get_system_info_for_api() { # Get PVE version using core.func utilities local pve_version=$(pveversion | awk -F'[/ ]' '{print $2}') # Set API parameters export var_os="$var_os" export var_version="$var_version" # Use core.func error handling with api.func reporting if silent apt-get update; then post_update_to_api "success" 0 else post_update_to_api "failed" $? fi } ``` ### With error_handler.func #### Error Description Integration ```bash # error_handler.func uses api.func for error descriptions source core.func source error_handler.func source api.func # Enhanced error handler with API reporting enhanced_error_handler() { local exit_code=${1:-$?} local command=${2:-${BASH_COMMAND:-unknown}} # Get error description from api.func local error_msg=$(get_error_description $exit_code) # Display error information echo "Error $exit_code: $error_msg" echo "Command: $command" # Report error to API export DIAGNOSTICS="yes" export RANDOM_UUID="$(uuidgen)" post_update_to_api "failed" $exit_code # Use standard error handler error_handler $exit_code $command } ``` ### With install.func #### Installation Process Reporting ```bash # install.func uses api.func for installation reporting source core.func source api.func source install.func # Installation with API reporting install_package_with_reporting() { local package="$1" # Set up API reporting export DIAGNOSTICS="yes" export RANDOM_UUID="$(uuidgen)" export NSAPP="$package" export METHOD="install" # Report installation start post_to_api # Package installation using install.func if install_package "$package"; then echo "$package installed successfully" post_update_to_api "success" 0 return 0 else local exit_code=$? local error_msg=$(get_error_description $exit_code) echo "$package installation failed: $error_msg" post_update_to_api "failed" $exit_code return $exit_code fi } ``` ### With alpine-install.func #### Alpine Installation Reporting ```bash # alpine-install.func uses api.func for Alpine reporting source core.func source api.func source alpine-install.func # Alpine installation with API reporting install_alpine_with_reporting() { local app="$1" # Set up API reporting export DIAGNOSTICS="yes" export RANDOM_UUID="$(uuidgen)" export NSAPP="$app" export METHOD="install" export var_os="alpine" # Report Alpine installation start post_to_api # Alpine installation using alpine-install.func if install_alpine_app "$app"; then echo "Alpine $app installed successfully" post_update_to_api "success" 0 return 0 else local exit_code=$? local error_msg=$(get_error_description $exit_code) echo "Alpine $app installation failed: $error_msg" post_update_to_api "failed" $exit_code return $exit_code fi } ``` ### With alpine-tools.func #### Alpine Tools Reporting ```bash # alpine-tools.func uses api.func for Alpine tools reporting source core.func source api.func source alpine-tools.func # Alpine tools with API reporting run_alpine_tool_with_reporting() { local tool="$1" # Set up API reporting export DIAGNOSTICS="yes" export RANDOM_UUID="$(uuidgen)" export NSAPP="alpine-tools" export METHOD="tool" # Report tool execution start post_to_api # Run Alpine tool using alpine-tools.func if run_alpine_tool "$tool"; then echo "Alpine tool $tool executed successfully" post_update_to_api "success" 0 return 0 else local exit_code=$? local error_msg=$(get_error_description $exit_code) echo "Alpine tool $tool failed: $error_msg" post_update_to_api "failed" $exit_code return $exit_code fi } ``` ### With passthrough.func #### Hardware Passthrough Reporting ```bash # passthrough.func uses api.func for hardware reporting source core.func source api.func source passthrough.func # Hardware passthrough with API reporting configure_passthrough_with_reporting() { local hardware_type="$1" # Set up API reporting export DIAGNOSTICS="yes" export RANDOM_UUID="$(uuidgen)" export NSAPP="passthrough" export METHOD="hardware" # Report passthrough configuration start post_to_api # Configure passthrough using passthrough.func if configure_passthrough "$hardware_type"; then echo "Hardware passthrough configured successfully" post_update_to_api "success" 0 return 0 else local exit_code=$? local error_msg=$(get_error_description $exit_code) echo "Hardware passthrough failed: $error_msg" post_update_to_api "failed" $exit_code return $exit_code fi } ``` ### With tools.func #### Maintenance Operations Reporting ```bash # tools.func uses api.func for maintenance reporting source core.func source api.func source tools.func # Maintenance operations with API reporting run_maintenance_with_reporting() { local operation="$1" # Set up API reporting export DIAGNOSTICS="yes" export RANDOM_UUID="$(uuidgen)" export NSAPP="maintenance" export METHOD="tool" # Report maintenance start post_to_api # Run maintenance using tools.func if run_maintenance_operation "$operation"; then echo "Maintenance operation $operation completed successfully" post_update_to_api "success" 0 return 0 else local exit_code=$? local error_msg=$(get_error_description $exit_code) echo "Maintenance operation $operation failed: $error_msg" post_update_to_api "failed" $exit_code return $exit_code fi } ``` ## Data Flow ### Input Data #### Environment Variables from Other Scripts - **`CT_TYPE`**: Container type (1 for LXC, 2 for VM) - **`DISK_SIZE`**: Disk size in GB - **`CORE_COUNT`**: Number of CPU cores - **`RAM_SIZE`**: RAM size in MB - **`var_os`**: Operating system type - **`var_version`**: OS version - **`DISABLEIP6`**: IPv6 disable setting - **`NSAPP`**: Namespace application name - **`METHOD`**: Installation method - **`DIAGNOSTICS`**: Enable/disable diagnostic reporting - **`RANDOM_UUID`**: Unique identifier for tracking #### Function Parameters - **Exit codes**: Passed to `get_error_description()` and `post_update_to_api()` - **Status information**: Passed to `post_update_to_api()` - **API endpoints**: Hardcoded in functions #### System Information - **PVE version**: Retrieved from `pveversion` command - **Disk size processing**: Processed for VM API (removes 'G' suffix) - **Error codes**: Retrieved from command exit codes ### Processing Data #### API Request Preparation - **JSON payload creation**: Format data for API consumption - **Data validation**: Ensure required fields are present - **Error handling**: Handle missing or invalid data - **Content type setting**: Set appropriate HTTP headers #### Error Processing - **Error code mapping**: Map numeric codes to descriptions - **Error message formatting**: Format error descriptions - **Unknown error handling**: Handle unrecognized error codes - **Fallback messages**: Provide default error messages #### API Communication - **HTTP request preparation**: Prepare curl commands - **Response handling**: Capture HTTP response codes - **Error handling**: Handle network and API errors - **Duplicate prevention**: Prevent duplicate status updates ### Output Data #### API Communication - **HTTP requests**: Sent to community-scripts.org API - **Response codes**: Captured from API responses - **Error information**: Reported to API - **Status updates**: Sent to API #### Error Information - **Error descriptions**: Human-readable error messages - **Error codes**: Mapped to descriptions - **Context information**: Error context and details - **Fallback messages**: Default error messages #### System State - **POST_UPDATE_DONE**: Prevents duplicate updates - **RESPONSE**: Stores API response - **JSON_PAYLOAD**: Stores formatted API data - **API_URL**: Stores API endpoint ## API Surface ### Public Functions #### Error Description - **`get_error_description()`**: Convert exit codes to explanations - **Parameters**: Exit code to explain - **Returns**: Human-readable explanation string - **Usage**: Called by other functions and scripts #### API Communication - **`post_to_api()`**: Send LXC installation data - **`post_to_api_vm()`**: Send VM installation data - **`post_update_to_api()`**: Send status updates - **Parameters**: Status and exit code (for updates) - **Returns**: None - **Usage**: Called by installation scripts ### Internal Functions #### None - All functions in api.func are public - No internal helper functions - Direct implementation of all functionality ### Global Variables #### Configuration Variables - **`DIAGNOSTICS`**: Diagnostic reporting setting - **`RANDOM_UUID`**: Unique tracking identifier - **`POST_UPDATE_DONE`**: Duplicate update prevention #### Data Variables - **`CT_TYPE`**: Container type - **`DISK_SIZE`**: Disk size - **`CORE_COUNT`**: CPU core count - **`RAM_SIZE`**: RAM size - **`var_os`**: Operating system - **`var_version`**: OS version - **`DISABLEIP6`**: IPv6 setting - **`NSAPP`**: Application namespace - **`METHOD`**: Installation method #### Internal Variables - **`API_URL`**: API endpoint URL - **`JSON_PAYLOAD`**: API request payload - **`RESPONSE`**: API response - **`DISK_SIZE_API`**: Processed disk size for VM API ## Integration Patterns ### Standard Integration Pattern ```bash #!/usr/bin/env bash # Standard integration pattern # 1. Source core.func first source core.func # 2. Source api.func source api.func # 3. Set up API reporting export DIAGNOSTICS="yes" export RANDOM_UUID="$(uuidgen)" # 4. Set application parameters export NSAPP="$APP" export METHOD="install" # 5. Report installation start post_to_api # 6. Perform installation # ... installation logic ... # 7. Report completion post_update_to_api "success" 0 ``` ### Minimal Integration Pattern ```bash #!/usr/bin/env bash # Minimal integration pattern source api.func # Basic error reporting export DIAGNOSTICS="yes" export RANDOM_UUID="$(uuidgen)" # Report failure post_update_to_api "failed" 127 ``` ### Advanced Integration Pattern ```bash #!/usr/bin/env bash # Advanced integration pattern source core.func source api.func source error_handler.func # Set up comprehensive API reporting export DIAGNOSTICS="yes" export RANDOM_UUID="$(uuidgen)" export CT_TYPE=1 export DISK_SIZE=8 export CORE_COUNT=2 export RAM_SIZE=2048 export var_os="debian" export var_version="12" export METHOD="install" # Enhanced error handling with API reporting enhanced_error_handler() { local exit_code=${1:-$?} local command=${2:-${BASH_COMMAND:-unknown}} local error_msg=$(get_error_description $exit_code) echo "Error $exit_code: $error_msg" post_update_to_api "failed" $exit_code error_handler $exit_code $command } trap 'enhanced_error_handler' ERR # Advanced operations with API reporting post_to_api # ... operations ... post_update_to_api "success" 0 ``` ## Error Handling Integration ### Automatic Error Reporting - **Error Descriptions**: Provides human-readable error messages - **API Integration**: Reports errors to community-scripts.org API - **Error Tracking**: Tracks error patterns for project improvement - **Diagnostic Data**: Contributes to anonymous usage analytics ### Manual Error Reporting - **Custom Error Codes**: Use appropriate error codes for different scenarios - **Error Context**: Provide context information for errors - **Status Updates**: Report both success and failure cases - **Error Analysis**: Analyze error patterns and trends ### API Communication Errors - **Network Failures**: Handle API communication failures gracefully - **Missing Prerequisites**: Check prerequisites before API calls - **Duplicate Prevention**: Prevent duplicate status updates - **Error Recovery**: Handle API errors without blocking installation ## Performance Considerations ### API Communication Overhead - **Minimal Impact**: API calls add minimal overhead - **Asynchronous**: API calls don't block installation process - **Error Handling**: API failures don't affect installation - **Optional**: API reporting is optional and can be disabled ### Memory Usage - **Minimal Footprint**: API functions use minimal memory - **Variable Reuse**: Global variables reused across functions - **No Memory Leaks**: Proper cleanup prevents memory leaks - **Efficient Processing**: Efficient JSON payload creation ### Execution Speed - **Fast API Calls**: Quick API communication - **Efficient Error Processing**: Fast error code processing - **Minimal Delay**: Minimal delay in API operations - **Non-blocking**: API calls don't block installation ## Security Considerations ### Data Privacy - **Anonymous Reporting**: Only anonymous data is sent - **No Sensitive Data**: No sensitive information is transmitted - **User Control**: Users can disable diagnostic reporting - **Data Minimization**: Only necessary data is sent ### API Security - **HTTPS**: API communication uses secure protocols - **Data Validation**: API data is validated before sending - **Error Handling**: API errors are handled securely - **No Credentials**: No authentication credentials are sent ### Network Security - **Secure Communication**: Uses secure HTTP protocols - **Error Handling**: Network errors are handled gracefully - **No Data Leakage**: No sensitive data is leaked - **Secure Endpoints**: Uses trusted API endpoints ## Future Integration Considerations ### Extensibility - **New API Endpoints**: Easy to add new API endpoints - **Additional Data**: Easy to add new data fields - **Error Codes**: Easy to add new error code descriptions - **API Versions**: Easy to support new API versions ### Compatibility - **API Versioning**: Compatible with different API versions - **Data Format**: Compatible with different data formats - **Error Codes**: Compatible with different error code systems - **Network Protocols**: Compatible with different network protocols ### Performance - **Optimization**: API communication can be optimized - **Caching**: API responses can be cached - **Batch Operations**: Multiple operations can be batched - **Async Processing**: API calls can be made asynchronous ================================================ FILE: docs/misc/api.func/API_USAGE_EXAMPLES.md ================================================ # api.func Usage Examples ## Overview This document provides practical usage examples for `api.func` functions, covering common scenarios, integration patterns, and best practices. ## Basic API Setup ### Standard API Initialization ```bash #!/usr/bin/env bash # Standard API setup for LXC containers source api.func # Set up diagnostic reporting export DIAGNOSTICS="yes" export RANDOM_UUID="$(uuidgen)" # Set container parameters export CT_TYPE=1 export DISK_SIZE=8 export CORE_COUNT=2 export RAM_SIZE=2048 export var_os="debian" export var_version="12" export NSAPP="plex" export METHOD="install" # Report installation start post_to_api # Your installation code here # ... installation logic ... # Report completion if [[ $? -eq 0 ]]; then post_update_to_api "success" 0 else post_update_to_api "failed" $? fi ``` ### VM API Setup ```bash #!/usr/bin/env bash # API setup for VMs source api.func # Create diagnostics file for VM mkdir -p /usr/local/community-scripts echo "DIAGNOSTICS=yes" > /usr/local/community-scripts/diagnostics # Set up VM parameters export RANDOM_UUID="$(uuidgen)" export DISK_SIZE="20G" export CORE_COUNT=4 export RAM_SIZE=4096 export var_os="ubuntu" export var_version="22.04" export NSAPP="nextcloud" export METHOD="install" # Report VM installation start post_to_api_vm # Your VM installation code here # ... VM creation logic ... # Report completion post_update_to_api "success" 0 ``` ## Error Description Examples ### Basic Error Explanation ```bash #!/usr/bin/env bash source api.func # Explain common error codes echo "Error 0: '$(get_error_description 0)'" echo "Error 1: $(get_error_description 1)" echo "Error 127: $(get_error_description 127)" echo "Error 200: $(get_error_description 200)" echo "Error 255: $(get_error_description 255)" ``` ### Error Code Testing ```bash #!/usr/bin/env bash source api.func # Test all error codes test_error_codes() { local codes=(0 1 2 127 128 130 137 139 143 200 203 205 255) for code in "${codes[@]}"; do echo "Code $code: $(get_error_description $code)" done } test_error_codes ``` ### Error Handling with Descriptions ```bash #!/usr/bin/env bash source api.func # Function with error handling run_command_with_error_handling() { local command="$1" local description="$2" echo "Running: $description" if $command; then echo "Success: $description" return 0 else local exit_code=$? local error_msg=$(get_error_description $exit_code) echo "Error $exit_code: $error_msg" return $exit_code fi } # Usage run_command_with_error_handling "apt-get update" "Package list update" run_command_with_error_handling "nonexistent_command" "Test command" ``` ## API Communication Examples ### LXC Installation Reporting ```bash #!/usr/bin/env bash source api.func # Complete LXC installation with API reporting install_lxc_with_reporting() { local app="$1" local ctid="$2" # Set up API reporting export DIAGNOSTICS="yes" export RANDOM_UUID="$(uuidgen)" export CT_TYPE=1 export DISK_SIZE=10 export CORE_COUNT=2 export RAM_SIZE=2048 export var_os="debian" export var_version="12" export NSAPP="$app" export METHOD="install" # Report installation start post_to_api # Installation process echo "Installing $app container (ID: $ctid)..." # Simulate installation sleep 2 # Check if installation succeeded if [[ $? -eq 0 ]]; then echo "Installation completed successfully" post_update_to_api "success" 0 return 0 else echo "Installation failed" post_update_to_api "failed" $? return 1 fi } # Install multiple containers install_lxc_with_reporting "plex" "100" install_lxc_with_reporting "nextcloud" "101" install_lxc_with_reporting "nginx" "102" ``` ### VM Installation Reporting ```bash #!/usr/bin/env bash source api.func # Complete VM installation with API reporting install_vm_with_reporting() { local app="$1" local vmid="$2" # Create diagnostics file mkdir -p /usr/local/community-scripts echo "DIAGNOSTICS=yes" > /usr/local/community-scripts/diagnostics # Set up API reporting export RANDOM_UUID="$(uuidgen)" export DISK_SIZE="20G" export CORE_COUNT=4 export RAM_SIZE=4096 export var_os="ubuntu" export var_version="22.04" export NSAPP="$app" export METHOD="install" # Report VM installation start post_to_api_vm # VM installation process echo "Installing $app VM (ID: $vmid)..." # Simulate VM creation sleep 3 # Check if VM creation succeeded if [[ $? -eq 0 ]]; then echo "VM installation completed successfully" post_update_to_api "success" 0 return 0 else echo "VM installation failed" post_update_to_api "failed" $? return 1 fi } # Install multiple VMs install_vm_with_reporting "nextcloud" "200" install_vm_with_reporting "wordpress" "201" ``` ## Status Update Examples ### Success Reporting ```bash #!/usr/bin/env bash source api.func # Report successful installation report_success() { local operation="$1" export DIAGNOSTICS="yes" export RANDOM_UUID="$(uuidgen)" echo "Reporting successful $operation" post_update_to_api "success" 0 } # Usage report_success "container installation" report_success "package installation" report_success "service configuration" ``` ### Failure Reporting ```bash #!/usr/bin/env bash source api.func # Report failed installation report_failure() { local operation="$1" local exit_code="$2" export DIAGNOSTICS="yes" export RANDOM_UUID="$(uuidgen)" local error_msg=$(get_error_description $exit_code) echo "Reporting failed $operation: $error_msg" post_update_to_api "failed" $exit_code } # Usage report_failure "container creation" 200 report_failure "package installation" 127 report_failure "service start" 1 ``` ### Conditional Status Reporting ```bash #!/usr/bin/env bash source api.func # Conditional status reporting report_installation_status() { local operation="$1" local exit_code="$2" export DIAGNOSTICS="yes" export RANDOM_UUID="$(uuidgen)" if [[ $exit_code -eq 0 ]]; then echo "Reporting successful $operation" post_update_to_api "success" 0 else local error_msg=$(get_error_description $exit_code) echo "Reporting failed $operation: $error_msg" post_update_to_api "failed" $exit_code fi } # Usage report_installation_status "container creation" 0 report_installation_status "package installation" 127 ``` ## Advanced Usage Examples ### Batch Installation with API Reporting ```bash #!/usr/bin/env bash source api.func # Batch installation with comprehensive API reporting batch_install_with_reporting() { local apps=("plex" "nextcloud" "nginx" "mysql") local ctids=(100 101 102 103) # Set up API reporting export DIAGNOSTICS="yes" export RANDOM_UUID="$(uuidgen)" export CT_TYPE=1 export DISK_SIZE=8 export CORE_COUNT=2 export RAM_SIZE=2048 export var_os="debian" export var_version="12" export METHOD="install" local success_count=0 local failure_count=0 for i in "${!apps[@]}"; do local app="${apps[$i]}" local ctid="${ctids[$i]}" echo "Installing $app (ID: $ctid)..." # Set app-specific parameters export NSAPP="$app" # Report installation start post_to_api # Simulate installation if install_app "$app" "$ctid"; then echo "$app installed successfully" post_update_to_api "success" 0 ((success_count++)) else echo "$app installation failed" post_update_to_api "failed" $? ((failure_count++)) fi echo "---" done echo "Batch installation completed: $success_count successful, $failure_count failed" } # Mock installation function install_app() { local app="$1" local ctid="$2" # Simulate installation sleep 1 # Simulate occasional failures if [[ $((RANDOM % 10)) -eq 0 ]]; then return 1 fi return 0 } batch_install_with_reporting ``` ### Error Analysis and Reporting ```bash #!/usr/bin/env bash source api.func # Analyze and report errors analyze_and_report_errors() { local log_file="$1" export DIAGNOSTICS="yes" export RANDOM_UUID="$(uuidgen)" if [[ ! -f "$log_file" ]]; then echo "Log file not found: $log_file" return 1 fi # Extract error codes from log local error_codes=$(grep -o 'exit code [0-9]\+' "$log_file" | grep -o '[0-9]\+' | sort -u) if [[ -z "$error_codes" ]]; then echo "No errors found in log" post_update_to_api "success" 0 return 0 fi echo "Found error codes: $error_codes" # Report each unique error for code in $error_codes; do local error_msg=$(get_error_description $code) echo "Error $code: $error_msg" post_update_to_api "failed" $code done } # Usage analyze_and_report_errors "/var/log/installation.log" ``` ### API Health Check ```bash #!/usr/bin/env bash source api.func # Check API connectivity and functionality check_api_health() { echo "Checking API health..." # Test prerequisites if ! command -v curl >/dev/null 2>&1; then echo "ERROR: curl not available" return 1 fi # Test error description function local test_error=$(get_error_description 127) if [[ -z "$test_error" ]]; then echo "ERROR: Error description function not working" return 1 fi echo "Error description test: $test_error" # Test API connectivity (without sending data) local api_url="https://api.community-scripts.org/dev/upload" if curl -s --head "$api_url" >/dev/null 2>&1; then echo "API endpoint is reachable" else echo "WARNING: API endpoint not reachable" fi echo "API health check completed" } check_api_health ``` ## Integration Examples ### With build.func ```bash #!/usr/bin/env bash # Integration with build.func source core.func source api.func source build.func # Set up API reporting export DIAGNOSTICS="yes" export RANDOM_UUID="$(uuidgen)" # Container creation with API reporting create_container_with_reporting() { local app="$1" local ctid="$2" # Set container parameters export APP="$app" export CTID="$ctid" export var_hostname="${app}-server" export var_os="debian" export var_version="12" export var_cpu="2" export var_ram="2048" export var_disk="10" export var_net="vmbr0" export var_gateway="192.168.1.1" export var_ip="192.168.1.$ctid" export var_template_storage="local" export var_container_storage="local" # Report installation start post_to_api # Create container using build.func if source build.func; then echo "Container $app created successfully" post_update_to_api "success" 0 return 0 else echo "Container $app creation failed" post_update_to_api "failed" $? return 1 fi } # Create containers create_container_with_reporting "plex" "100" create_container_with_reporting "nextcloud" "101" ``` ### With vm-core.func ```bash #!/usr/bin/env bash # Integration with vm-core.func source core.func source api.func source vm-core.func # Set up VM API reporting mkdir -p /usr/local/community-scripts echo "DIAGNOSTICS=yes" > /usr/local/community-scripts/diagnostics export RANDOM_UUID="$(uuidgen)" # VM creation with API reporting create_vm_with_reporting() { local app="$1" local vmid="$2" # Set VM parameters export APP="$app" export VMID="$vmid" export var_hostname="${app}-vm" export var_os="ubuntu" export var_version="22.04" export var_cpu="4" export var_ram="4096" export var_disk="20" # Report VM installation start post_to_api_vm # Create VM using vm-core.func if source vm-core.func; then echo "VM $app created successfully" post_update_to_api "success" 0 return 0 else echo "VM $app creation failed" post_update_to_api "failed" $? return 1 fi } # Create VMs create_vm_with_reporting "nextcloud" "200" create_vm_with_reporting "wordpress" "201" ``` ### With error_handler.func ```bash #!/usr/bin/env bash # Integration with error_handler.func source core.func source error_handler.func source api.func # Enhanced error handling with API reporting enhanced_error_handler() { local exit_code=${1:-$?} local command=${2:-${BASH_COMMAND:-unknown}} # Get error description from api.func local error_msg=$(get_error_description $exit_code) # Display error information echo "Error $exit_code: $error_msg" echo "Command: $command" # Report error to API export DIAGNOSTICS="yes" export RANDOM_UUID="$(uuidgen)" post_update_to_api "failed" $exit_code # Use standard error handler error_handler $exit_code $command } # Set up enhanced error handling trap 'enhanced_error_handler' ERR # Test enhanced error handling nonexistent_command ``` ## Best Practices Examples ### Comprehensive API Integration ```bash #!/usr/bin/env bash # Comprehensive API integration example source core.func source api.func # Set up comprehensive API reporting setup_api_reporting() { # Enable diagnostics export DIAGNOSTICS="yes" export RANDOM_UUID="$(uuidgen)" # Set common parameters export CT_TYPE=1 export DISK_SIZE=8 export CORE_COUNT=2 export RAM_SIZE=2048 export var_os="debian" export var_version="12" export METHOD="install" echo "API reporting configured" } # Installation with comprehensive reporting install_with_comprehensive_reporting() { local app="$1" local ctid="$2" # Set up API reporting setup_api_reporting export NSAPP="$app" # Report installation start post_to_api # Installation process echo "Installing $app..." # Simulate installation steps local steps=("Downloading" "Installing" "Configuring" "Starting") for step in "${steps[@]}"; do echo "$step $app..." sleep 1 done # Check installation result if [[ $? -eq 0 ]]; then echo "$app installation completed successfully" post_update_to_api "success" 0 return 0 else echo "$app installation failed" post_update_to_api "failed" $? return 1 fi } # Install multiple applications apps=("plex" "nextcloud" "nginx" "mysql") ctids=(100 101 102 103) for i in "${!apps[@]}"; do install_with_comprehensive_reporting "${apps[$i]}" "${ctids[$i]}" echo "---" done ``` ### Error Recovery with API Reporting ```bash #!/usr/bin/env bash source api.func # Error recovery with API reporting retry_with_api_reporting() { local operation="$1" local max_attempts=3 local attempt=1 export DIAGNOSTICS="yes" export RANDOM_UUID="$(uuidgen)" while [[ $attempt -le $max_attempts ]]; do echo "Attempt $attempt of $max_attempts: $operation" if $operation; then echo "Operation succeeded on attempt $attempt" post_update_to_api "success" 0 return 0 else local exit_code=$? local error_msg=$(get_error_description $exit_code) echo "Attempt $attempt failed: $error_msg" post_update_to_api "failed" $exit_code ((attempt++)) if [[ $attempt -le $max_attempts ]]; then echo "Retrying in 5 seconds..." sleep 5 fi fi done echo "Operation failed after $max_attempts attempts" return 1 } # Usage retry_with_api_reporting "apt-get update" retry_with_api_reporting "apt-get install -y package" ``` ### API Reporting with Logging ```bash #!/usr/bin/env bash source api.func # API reporting with detailed logging install_with_logging_and_api() { local app="$1" local log_file="/var/log/${app}_installation.log" # Set up API reporting export DIAGNOSTICS="yes" export RANDOM_UUID="$(uuidgen)" export NSAPP="$app" # Start logging exec > >(tee -a "$log_file") exec 2>&1 echo "Starting $app installation at $(date)" # Report installation start post_to_api # Installation process echo "Installing $app..." # Simulate installation if install_app "$app"; then echo "$app installation completed successfully at $(date)" post_update_to_api "success" 0 return 0 else local exit_code=$? local error_msg=$(get_error_description $exit_code) echo "$app installation failed at $(date): $error_msg" post_update_to_api "failed" $exit_code return $exit_code fi } # Mock installation function install_app() { local app="$1" echo "Installing $app..." sleep 2 return 0 } # Install with logging and API reporting install_with_logging_and_api "plex" ``` ================================================ FILE: docs/misc/api.func/README.md ================================================ # api.func Documentation ## Overview The `api.func` file provides Proxmox API integration and diagnostic reporting functionality for the Community Scripts project. It handles API communication, error reporting, and status updates to the community-scripts.org API. ## Purpose and Use Cases - **API Communication**: Send installation and status data to community-scripts.org API - **Diagnostic Reporting**: Report installation progress and errors for analytics - **Error Description**: Provide detailed error code explanations - **Status Updates**: Track installation success/failure status - **Analytics**: Contribute anonymous usage data for project improvement ## Quick Reference ### Key Function Groups - **Error Handling**: `get_error_description()` - Convert exit codes to human-readable messages - **API Communication**: `post_to_api()`, `post_to_api_vm()` - Send installation data - **Status Updates**: `post_update_to_api()` - Report installation completion status ### Dependencies - **External**: `curl` command for HTTP requests - **Internal**: Uses environment variables from other scripts ### Integration Points - Used by: All installation scripts for diagnostic reporting - Uses: Environment variables from build.func and other scripts - Provides: API communication and error reporting services ## Documentation Files ### 📊 [API_FLOWCHART.md](./API_FLOWCHART.md) Visual execution flows showing API communication processes and error handling. ### 📚 [API_FUNCTIONS_REFERENCE.md](./API_FUNCTIONS_REFERENCE.md) Complete alphabetical reference of all functions with parameters, dependencies, and usage details. ### 💡 [API_USAGE_EXAMPLES.md](./API_USAGE_EXAMPLES.md) Practical examples showing how to use API functions and common patterns. ### 🔗 [API_INTEGRATION.md](./API_INTEGRATION.md) How api.func integrates with other components and provides API services. ## Key Features ### Error Code Descriptions - **Comprehensive Coverage**: 50+ error codes with detailed explanations - **LXC-Specific Errors**: Container creation and management errors - **System Errors**: General system and network errors - **Signal Errors**: Process termination and signal errors ### API Communication - **LXC Reporting**: Send LXC container installation data - **VM Reporting**: Send VM installation data - **Status Updates**: Report installation success/failure - **Diagnostic Data**: Anonymous usage analytics ### Diagnostic Integration - **Optional Reporting**: Only sends data when diagnostics enabled - **Privacy Respect**: Respects user privacy settings - **Error Tracking**: Tracks installation errors for improvement - **Usage Analytics**: Contributes to project statistics ## Common Usage Patterns ### Basic API Setup ```bash #!/usr/bin/env bash # Basic API setup source api.func # Set up diagnostic reporting export DIAGNOSTICS="yes" export RANDOM_UUID="$(uuidgen)" # Report installation start post_to_api ``` ### Error Reporting ```bash #!/usr/bin/env bash source api.func # Get error description error_msg=$(get_error_description 127) echo "Error 127: $error_msg" # Output: Error 127: Command not found: Incorrect path or missing dependency. ``` ### Status Updates ```bash #!/usr/bin/env bash source api.func # Report successful installation post_update_to_api "success" 0 # Report failed installation post_update_to_api "failed" 127 ``` ## Environment Variables ### Required Variables - `DIAGNOSTICS`: Enable/disable diagnostic reporting ("yes"/"no") - `RANDOM_UUID`: Unique identifier for tracking ### Optional Variables - `CT_TYPE`: Container type (1 for LXC, 2 for VM) - `DISK_SIZE`: Disk size in GB - `CORE_COUNT`: Number of CPU cores - `RAM_SIZE`: RAM size in MB - `var_os`: Operating system type - `var_version`: OS version - `DISABLEIP6`: IPv6 disable setting - `NSAPP`: Namespace application name - `METHOD`: Installation method ### Internal Variables - `POST_UPDATE_DONE`: Prevents duplicate status updates - `API_URL`: Community scripts API endpoint - `JSON_PAYLOAD`: API request payload - `RESPONSE`: API response ## Error Code Categories ### General System Errors - **0-9**: Basic system errors - **18, 22, 28, 35**: Network and I/O errors - **56, 60**: TLS/SSL errors - **125-128**: Command execution errors - **129-143**: Signal errors - **152**: Resource limit errors - **255**: Unknown critical errors ### LXC-Specific Errors - **100-101**: LXC installation errors - **200-209**: LXC creation and management errors ### Docker Errors - **125**: Docker container start errors ## Best Practices ### Diagnostic Reporting 1. Always check if diagnostics are enabled 2. Respect user privacy settings 3. Use unique identifiers for tracking 4. Report both success and failure cases ### Error Handling 1. Use appropriate error codes 2. Provide meaningful error descriptions 3. Handle API communication failures gracefully 4. Don't block installation on API failures ### API Usage 1. Check for curl availability 2. Handle network failures gracefully 3. Use appropriate HTTP methods 4. Include all required data ## Troubleshooting ### Common Issues 1. **API Communication Fails**: Check network connectivity and curl availability 2. **Diagnostics Not Working**: Verify DIAGNOSTICS setting and RANDOM_UUID 3. **Missing Error Descriptions**: Check error code coverage 4. **Duplicate Updates**: POST_UPDATE_DONE prevents duplicates ### Debug Mode Enable diagnostic reporting for debugging: ```bash export DIAGNOSTICS="yes" export RANDOM_UUID="$(uuidgen)" ``` ### API Testing Test API communication: ```bash source api.func export DIAGNOSTICS="yes" export RANDOM_UUID="test-$(date +%s)" post_to_api ``` ## Related Documentation - [core.func](../core.func/) - Core utilities and error handling - [error_handler.func](../error_handler.func/) - Error handling utilities - [build.func](../build.func/) - Container creation with API integration - [tools.func](../tools.func/) - Extended utilities with API integration --- *This documentation covers the api.func file which provides API communication and diagnostic reporting for all Proxmox Community Scripts.* ================================================ FILE: docs/misc/build.func/BUILD_FUNC_ADVANCED_SETTINGS.md ================================================ # Advanced Settings Wizard Reference ## Overview The Advanced Settings wizard provides a 28-step interactive configuration for LXC container creation. It allows users to customize every aspect of the container while inheriting sensible defaults from the CT script. ## Key Features - **Inherit App Defaults**: All `var_*` values from CT scripts pre-populate wizard fields - **Back Navigation**: Press Cancel/Back to return to previous step - **App Default Hints**: Each dialog shows `(App default: X)` to indicate script defaults - **Full Customization**: Every configurable option is accessible ## Wizard Steps | Step | Title | Variable(s) | Description | | ---- | ------------------------ | --------------------------------- | ----------------------------------------------------- | | 1 | Container Type | `var_unprivileged` | Privileged (0) or Unprivileged (1) container | | 2 | Root Password | `var_pw` | Set password or use automatic login | | 3 | Container ID | `var_ctid` | Unique container ID (auto-suggested) | | 4 | Hostname | `var_hostname` | Container hostname | | 5 | Disk Size | `var_disk` | Disk size in GB | | 6 | CPU Cores | `var_cpu` | Number of CPU cores | | 7 | RAM Size | `var_ram` | RAM size in MiB | | 8 | Network Bridge | `var_brg` | Network bridge (vmbr0, etc.) | | 9 | IPv4 Configuration | `var_net`, `var_gateway` | DHCP or static IP with gateway | | 10 | IPv6 Configuration | `var_ipv6_method` | Auto, DHCP, Static, or None | | 11 | MTU Size | `var_mtu` | Network MTU (default: 1500) | | 12 | DNS Search Domain | `var_searchdomain` | DNS search domain | | 13 | DNS Server | `var_ns` | Custom DNS server IP | | 14 | MAC Address | `var_mac` | Custom MAC address (auto-generated if empty) | | 15 | VLAN Tag | `var_vlan` | VLAN tag ID | | 16 | Tags | `var_tags` | Container tags (comma/semicolon separated) | | 17 | SSH Settings | `var_ssh` | SSH key selection and root access | | 18 | FUSE Support | `var_fuse` | Enable FUSE for rclone, mergerfs, AppImage | | 19 | TUN/TAP Support | `var_tun` | Enable for VPN apps (WireGuard, OpenVPN, Tailscale) | | 20 | Nesting Support | `var_nesting` | Enable for Docker, LXC in LXC, Podman | | 21 | GPU Passthrough | `var_gpu` | Auto-detect and pass through Intel/AMD/NVIDIA GPUs | | 22 | Keyctl Support | `var_keyctl` | Enable for Docker, systemd-networkd | | 23 | APT Cacher Proxy | `var_apt_cacher`, `var_apt_cacher_ip` | Use apt-cacher-ng for faster downloads | | 24 | Container Timezone | `var_timezone` | Set timezone (e.g., Europe/Berlin) | | 25 | Container Protection | `var_protection` | Prevent accidental deletion | | 26 | Device Node Creation | `var_mknod` | Allow mknod (experimental, kernel 5.3+) | | 27 | Mount Filesystems | `var_mount_fs` | Allow specific mounts: nfs, cifs, fuse, etc. | | 28 | Verbose Mode & Confirm | `var_verbose` | Enable verbose output + final confirmation | ## Default Value Inheritance The wizard inherits defaults from multiple sources: ```text CT Script (var_*) → default.vars → app.vars → User Input ``` ### Example: VPN Container (alpine-wireguard.sh) ```bash # CT script sets: var_tun="${var_tun:-1}" # TUN enabled by default # In Advanced Settings Step 19: # Dialog shows: "(App default: 1)" and pre-selects "Yes" ``` ### Example: Media Server (jellyfin.sh) ```bash # CT script sets: var_gpu="${var_gpu:-yes}" # GPU enabled by default # In Advanced Settings Step 21: # Dialog shows: "(App default: yes)" and pre-selects "Yes" ``` ## Feature Matrix | Feature | Variable | When to Enable | | ----------------- | ---------------- | --------------------------------------------------- | | FUSE | `var_fuse` | rclone, mergerfs, AppImage, SSHFS | | TUN/TAP | `var_tun` | WireGuard, OpenVPN, Tailscale, VPN containers | | Nesting | `var_nesting` | Docker, Podman, LXC-in-LXC, systemd-nspawn | | GPU Passthrough | `var_gpu` | Plex, Jellyfin, Emby, Frigate, Ollama, ComfyUI | | Keyctl | `var_keyctl` | Docker (unprivileged), systemd-networkd | | Protection | `var_protection` | Production containers, prevent accidental deletion | | Mknod | `var_mknod` | Device node creation (experimental) | | Mount FS | `var_mount_fs` | NFS mounts, CIFS shares, custom filesystems | | APT Cacher | `var_apt_cacher` | Speed up downloads with local apt-cacher-ng | ## Confirmation Summary Step 28 displays a comprehensive summary before creation: ```text Container Type: Unprivileged Container ID: 100 Hostname: jellyfin Resources: Disk: 8 GB CPU: 2 cores RAM: 2048 MiB Network: Bridge: vmbr0 IPv4: dhcp IPv6: auto Features: FUSE: no | TUN: no Nesting: Enabled | Keyctl: Disabled GPU: yes | Protection: No Advanced: Timezone: Europe/Berlin APT Cacher: no Verbose: no ``` ## Usage Examples ### Skip to Advanced Settings ```bash # Run script, select "Advanced" from menu bash -c "$(curl -fsSL https://...jellyfin.sh)" # Then select option 3 "Advanced" ``` ### Pre-set Defaults via Environment ```bash # Set defaults before running export var_cpu=4 export var_ram=4096 export var_gpu=yes bash -c "$(curl -fsSL https://...jellyfin.sh)" # Advanced settings will inherit these values ``` ### Non-Interactive with All Options ```bash # Set all variables for fully automated deployment export var_unprivileged=1 export var_cpu=2 export var_ram=2048 export var_disk=8 export var_net=dhcp export var_fuse=no export var_tun=no export var_gpu=yes export var_nesting=1 export var_protection=no export var_verbose=no bash -c "$(curl -fsSL https://...jellyfin.sh)" ``` ## Notes - **Cancel at Step 1**: Exits the script entirely - **Cancel at Steps 2-28**: Goes back to previous step - **Empty fields**: Use default value - **Keyctl**: Automatically enabled for unprivileged containers - **Nesting**: Enabled by default (required for many apps) ================================================ FILE: docs/misc/build.func/BUILD_FUNC_ARCHITECTURE.md ================================================ # build.func Architecture Guide ## Overview This document provides a high-level architectural overview of `build.func`, including module dependencies, data flow, integration points, and system architecture. ## High-Level Architecture ``` ┌─────────────────────────────────────────────────────────────────────────────────┐ │ Proxmox Host System │ │ │ │ ┌─────────────────────────────────────────────────────────────────────────────┐ │ │ │ build.func │ │ │ │ │ │ │ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────────────┐ │ │ │ │ │ Entry Point │ │ Configuration │ │ Container Creation │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ • start() │ │ • variables() │ │ • build_container() │ │ │ │ │ │ • install_ │ │ • base_ │ │ • create_lxc_container() │ │ │ │ │ │ script() │ │ settings() │ │ • configure_gpu_ │ │ │ │ │ │ • advanced_ │ │ • select_ │ │ passthrough() │ │ │ │ │ │ settings() │ │ storage() │ │ • fix_gpu_gids() │ │ │ │ │ └─────────────────┘ └─────────────────┘ └─────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────────────┘ │ │ │ │ ┌─────────────────────────────────────────────────────────────────────────────┐ │ │ │ Module Dependencies │ │ │ │ │ │ │ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────────────┐ │ │ │ │ │ core.func │ │ error_handler. │ │ api.func │ │ │ │ │ │ │ │ func │ │ │ │ │ │ │ │ • Basic │ │ • Error │ │ • Proxmox API │ │ │ │ │ │ utilities │ │ handling │ │ interactions │ │ │ │ │ │ • Common │ │ • Error │ │ • Container │ │ │ │ │ │ functions │ │ recovery │ │ management │ │ │ │ │ │ • System │ │ • Cleanup │ │ • Status │ │ │ │ │ │ utilities │ │ functions │ │ monitoring │ │ │ │ │ └─────────────────┘ └─────────────────┘ └─────────────────────────────┘ │ │ │ │ │ │ │ │ ┌─────────────────────────────────────────────────────────────────────────┐ │ │ │ │ │ tools.func │ │ │ │ │ │ │ │ │ │ │ │ • Additional utilities │ │ │ │ │ │ • Helper functions │ │ │ │ │ │ • System tools │ │ │ │ │ └─────────────────────────────────────────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────────────────────────┘ ``` ## Module Dependencies ### Core Dependencies ``` build.func Dependencies: ├── core.func │ ├── Basic system utilities │ ├── Common functions │ ├── System information │ └── File operations ├── error_handler.func │ ├── Error handling │ ├── Error recovery │ ├── Cleanup functions │ └── Error logging ├── api.func │ ├── Proxmox API interactions │ ├── Container management │ ├── Status monitoring │ └── Configuration updates └── tools.func ├── Additional utilities ├── Helper functions ├── System tools └── Custom functions ``` ### Dependency Flow ``` Dependency Flow: ├── build.func │ ├── Sources core.func │ ├── Sources error_handler.func │ ├── Sources api.func │ └── Sources tools.func ├── core.func │ ├── Basic utilities │ └── System functions ├── error_handler.func │ ├── Error management │ └── Recovery functions ├── api.func │ ├── Proxmox integration │ └── Container operations └── tools.func ├── Additional tools └── Helper functions ``` ## Data Flow Architecture ### Configuration Data Flow ``` Configuration Data Flow: ├── Environment Variables │ ├── Hard environment variables │ ├── App-specific .vars │ ├── Global default.vars │ └── Built-in defaults ├── Variable Resolution │ ├── Apply precedence chain │ ├── Validate settings │ └── Resolve conflicts ├── Configuration Storage │ ├── Memory variables │ ├── Temporary files │ └── Persistent storage └── Configuration Usage ├── Container creation ├── Feature configuration └── Settings persistence ``` ### Container Data Flow ``` Container Data Flow: ├── Input Data │ ├── Configuration variables │ ├── Resource specifications │ ├── Network settings │ └── Storage requirements ├── Processing │ ├── Validation │ ├── Conflict resolution │ ├── Resource allocation │ └── Configuration generation ├── Container Creation │ ├── LXC container creation │ ├── Network configuration │ ├── Storage setup │ └── Feature configuration └── Output ├── Container status ├── Access information ├── Configuration files └── Log files ``` ## Integration Architecture ### With Proxmox System ``` Proxmox Integration: ├── Proxmox Host │ ├── LXC container management │ ├── Storage management │ ├── Network management │ └── Resource management ├── Proxmox API │ ├── Container operations │ ├── Configuration updates │ ├── Status monitoring │ └── Error handling ├── Proxmox Configuration │ ├── /etc/pve/lxc/.conf │ ├── Storage configuration │ ├── Network configuration │ └── Resource configuration └── Proxmox Services ├── Container services ├── Network services ├── Storage services └── Monitoring services ``` ### With Install Scripts ``` Install Script Integration: ├── build.func │ ├── Creates container │ ├── Configures basic settings │ ├── Starts container │ └── Provides access ├── Install Scripts │ ├── -install.sh │ ├── Downloads application │ ├── Configures application │ └── Sets up services ├── Container │ ├── Running application │ ├── Configured services │ ├── Network access │ └── Storage access └── Integration Points ├── Container creation ├── Network configuration ├── Storage setup └── Service configuration ``` ## System Architecture Components ### Core Components ``` System Components: ├── Entry Point │ ├── start() function │ ├── Context detection │ ├── Environment capture │ └── Workflow routing ├── Configuration Management │ ├── Variable resolution │ ├── Settings persistence │ ├── Default management │ └── Validation ├── Container Creation │ ├── LXC container creation │ ├── Network configuration │ ├── Storage setup │ └── Feature configuration ├── Hardware Integration │ ├── GPU passthrough │ ├── USB passthrough │ ├── Storage management │ └── Network management └── Error Handling ├── Error detection ├── Error recovery ├── Cleanup functions └── User notification ``` ### User Interface Components ``` UI Components: ├── Menu System │ ├── Installation mode selection │ ├── Configuration menus │ ├── Storage selection │ └── GPU configuration ├── Interactive Elements │ ├── Whiptail menus │ ├── User prompts │ ├── Confirmation dialogs │ └── Error messages ├── Non-Interactive Mode │ ├── Environment variable driven │ ├── Silent execution │ ├── Automated configuration │ └── Error handling └── Output ├── Status messages ├── Progress indicators ├── Completion information └── Access details ``` ## Security Architecture ### Security Considerations ``` Security Architecture: ├── Container Security │ ├── Unprivileged containers (default) │ ├── Privileged containers (when needed) │ ├── Resource limits │ └── Access controls ├── Network Security │ ├── Network isolation │ ├── VLAN support │ ├── Firewall integration │ └── Access controls ├── Storage Security │ ├── Storage isolation │ ├── Access controls │ ├── Encryption support │ └── Backup integration ├── GPU Security │ ├── Device isolation │ ├── Permission management │ ├── Access controls │ └── Security validation └── API Security ├── Authentication ├── Authorization ├── Input validation └── Error handling ``` ## Performance Architecture ### Performance Considerations ``` Performance Architecture: ├── Execution Optimization │ ├── Parallel operations │ ├── Efficient algorithms │ ├── Minimal user interaction │ └── Optimized validation ├── Resource Management │ ├── Memory efficiency │ ├── CPU optimization │ ├── Disk usage optimization │ └── Network efficiency ├── Caching │ ├── Configuration caching │ ├── Template caching │ ├── Storage caching │ └── GPU detection caching └── Monitoring ├── Performance monitoring ├── Resource monitoring ├── Error monitoring └── Status monitoring ``` ## Deployment Architecture ### Deployment Scenarios ``` Deployment Scenarios: ├── Single Container │ ├── Individual application │ ├── Standard configuration │ ├── Basic networking │ └── Standard storage ├── Multiple Containers │ ├── Application stack │ ├── Shared networking │ ├── Shared storage │ └── Coordinated deployment ├── High Availability │ ├── Redundant containers │ ├── Load balancing │ ├── Failover support │ └── Monitoring integration └── Development Environment ├── Development containers ├── Testing containers ├── Staging containers └── Production containers ``` ## Maintenance Architecture ### Maintenance Components ``` Maintenance Architecture: ├── Updates │ ├── Container updates │ ├── Application updates │ ├── Configuration updates │ └── Security updates ├── Monitoring │ ├── Container monitoring │ ├── Resource monitoring │ ├── Performance monitoring │ └── Error monitoring ├── Backup │ ├── Configuration backup │ ├── Container backup │ ├── Storage backup │ └── Recovery procedures └── Troubleshooting ├── Error diagnosis ├── Log analysis ├── Performance analysis └── Recovery procedures ``` ## Future Architecture Considerations ### Scalability ``` Scalability Considerations: ├── Horizontal Scaling │ ├── Multiple containers │ ├── Load balancing │ ├── Distributed deployment │ └── Resource distribution ├── Vertical Scaling │ ├── Resource scaling │ ├── Performance optimization │ ├── Capacity planning │ └── Resource management ├── Automation │ ├── Automated deployment │ ├── Automated scaling │ ├── Automated monitoring │ └── Automated recovery └── Integration ├── External systems ├── Cloud integration ├── Container orchestration └── Service mesh ``` ================================================ FILE: docs/misc/build.func/BUILD_FUNC_ENVIRONMENT_VARIABLES.md ================================================ # build.func Environment Variables Reference ## Overview This document provides a comprehensive reference of all environment variables used in `build.func`, organized by category and usage context. ## Variable Categories ### Core Container Variables | Variable | Description | Default | Set In | Used In | | --------- | -------------------------------------------- | --------- | ----------- | ------------------ | | `APP` | Application name (e.g., "plex", "nextcloud") | - | Environment | Throughout | | `NSAPP` | Namespace application name | `$APP` | Environment | Throughout | | `CTID` | Container ID | - | Environment | Container creation | | `CT_TYPE` | Container type ("install" or "update") | "install" | Environment | Entry point | | `CT_NAME` | Container name | `$APP` | Environment | Container creation | ### Operating System Variables | Variable | Description | Default | Set In | Used In | | -------------- | -------------------------- | -------------- | --------------- | ------------------ | | `var_os` | Operating system selection | "debian" | base_settings() | OS selection | | `var_version` | OS version | "12" | base_settings() | Template selection | | `var_template` | Template name | Auto-generated | base_settings() | Template download | ### Resource Configuration Variables | Variable | Description | Default | Set In | Used In | | ------------ | ----------------------- | ----------- | --------------- | ------------------ | | `var_cpu` | CPU cores | "2" | base_settings() | Container creation | | `var_ram` | RAM in MB | "2048" | base_settings() | Container creation | | `var_disk` | Disk size in GB | "8" | base_settings() | Container creation | | `DISK_SIZE` | Disk size (alternative) | `$var_disk` | Environment | Container creation | | `CORE_COUNT` | CPU cores (alternative) | `$var_cpu` | Environment | Container creation | | `RAM_SIZE` | RAM size (alternative) | `$var_ram` | Environment | Container creation | ### Network Configuration Variables | Variable | Description | Default | Set In | Used In | | ------------- | ------------------------------- | -------------- | --------------- | -------------- | | `var_net` | Network interface | "vmbr0" | base_settings() | Network config | | `var_bridge` | Bridge interface | "vmbr0" | base_settings() | Network config | | `var_gateway` | Gateway IP | "192.168.1.1" | base_settings() | Network config | | `var_ip` | Container IP address | - | User input | Network config | | `var_ipv6` | IPv6 address | - | User input | Network config | | `var_vlan` | VLAN ID | - | User input | Network config | | `var_mtu` | MTU size | "1500" | base_settings() | Network config | | `var_mac` | MAC address | Auto-generated | base_settings() | Network config | | `NET` | Network interface (alternative) | `$var_net` | Environment | Network config | | `BRG` | Bridge interface (alternative) | `$var_bridge` | Environment | Network config | | `GATE` | Gateway IP (alternative) | `$var_gateway` | Environment | Network config | | `IPV6_METHOD` | IPv6 configuration method | "none" | Environment | Network config | | `VLAN` | VLAN ID (alternative) | `$var_vlan` | Environment | Network config | | `MTU` | MTU size (alternative) | `$var_mtu` | Environment | Network config | | `MAC` | MAC address (alternative) | `$var_mac` | Environment | Network config | ### Storage Configuration Variables | Variable | Description | Default | Set In | Used In | | ----------------------- | ------------------------------- | ------------------------ | ---------------- | ----------------- | | `var_template_storage` | Storage for templates | - | select_storage() | Template storage | | `var_container_storage` | Storage for container disks | - | select_storage() | Container storage | | `TEMPLATE_STORAGE` | Template storage (alternative) | `$var_template_storage` | Environment | Template storage | | `CONTAINER_STORAGE` | Container storage (alternative) | `$var_container_storage` | Environment | Container storage | ### Feature Flags | Variable | Description | Default | Set In | Used In | | ---------------- | ------------------------------ | ------- | ------------------------------- | ------------------ | | `var_fuse` | Enable FUSE support | "no" | CT script / Advanced Settings | Container features | | `var_tun` | Enable TUN/TAP support | "no" | CT script / Advanced Settings | Container features | | `var_nesting` | Enable nesting support | "1" | CT script / Advanced Settings | Container features | | `var_keyctl` | Enable keyctl support | "0" | CT script / Advanced Settings | Container features | | `var_mknod` | Allow device node creation | "0" | CT script / Advanced Settings | Container features | | `var_mount_fs` | Allowed filesystem mounts | "" | CT script / Advanced Settings | Container features | | `var_protection` | Enable container protection | "no" | CT script / Advanced Settings | Container creation | | `var_timezone` | Container timezone | "" | CT script / Advanced Settings | Container creation | | `var_verbose` | Enable verbose output | "no" | Environment / Advanced Settings | Logging | | `var_ssh` | Enable SSH key provisioning | "no" | CT script / Advanced Settings | SSH setup | | `ENABLE_FUSE` | FUSE flag (internal) | "no" | Advanced Settings | Container creation | | `ENABLE_TUN` | TUN/TAP flag (internal) | "no" | Advanced Settings | Container creation | | `ENABLE_NESTING` | Nesting flag (internal) | "1" | Advanced Settings | Container creation | | `ENABLE_KEYCTL` | Keyctl flag (internal) | "0" | Advanced Settings | Container creation | | `ENABLE_MKNOD` | Mknod flag (internal) | "0" | Advanced Settings | Container creation | | `PROTECT_CT` | Protection flag (internal) | "no" | Advanced Settings | Container creation | | `CT_TIMEZONE` | Timezone setting (internal) | "" | Advanced Settings | Container creation | | `VERBOSE` | Verbose mode flag | "no" | Environment | Logging | | `SSH` | SSH access flag | "no" | Advanced Settings | SSH setup | ### APT Cacher Configuration | Variable | Description | Default | Set In | Used In | | ------------------ | ------------------------ | ------- | ----------------------------- | ------------------- | | `var_apt_cacher` | Enable APT cacher proxy | "no" | CT script / Advanced Settings | Package management | | `var_apt_cacher_ip`| APT cacher server IP | "" | CT script / Advanced Settings | Package management | | `APT_CACHER` | APT cacher flag | "no" | Advanced Settings | Container creation | | `APT_CACHER_IP` | APT cacher IP (internal) | "" | Advanced Settings | Container creation | ### GPU Passthrough Variables | Variable | Description | Default | Set In | Used In | | ------------ | ------------------------------- | ------- | ------------------------------------------- | ------------------ | | `var_gpu` | Enable GPU passthrough | "no" | CT script / Environment / Advanced Settings | GPU passthrough | | `ENABLE_GPU` | GPU passthrough flag (internal) | "no" | Advanced Settings | Container creation | **Note**: GPU passthrough is controlled via `var_gpu`. Apps that benefit from GPU acceleration (media servers, AI/ML, transcoding) have `var_gpu=yes` as default in their CT scripts. **Apps with GPU enabled by default**: - Media: jellyfin, plex, emby, channels, ersatztv, tunarr, immich - Transcoding: tdarr, unmanic, fileflows - AI/ML: ollama, openwebui - NVR: frigate **Usage Examples**: ```bash # Disable GPU for a specific installation var_gpu=no bash -c "$(curl -fsSL https://...jellyfin.sh)" # Enable GPU for apps without default GPU support var_gpu=yes bash -c "$(curl -fsSL https://...debian.sh)" # Set in default.vars for all apps echo "var_gpu=yes" >> /usr/local/community-scripts/default.vars ``` ### API and Diagnostics Variables | Variable | Description | Default | Set In | Used In | | ------------- | ------------------------ | --------- | ----------- | ----------------- | | `DIAGNOSTICS` | Enable diagnostics mode | "false" | Environment | Diagnostics | | `METHOD` | Installation method | "install" | Environment | Installation flow | | `RANDOM_UUID` | Random UUID for tracking | - | Environment | Logging | | `API_TOKEN` | Proxmox API token | - | Environment | API calls | | `API_USER` | Proxmox API user | - | Environment | API calls | ### Settings Persistence Variables | Variable | Description | Default | Set In | Used In | | ------------------- | -------------------------- | ------------------------------------------------- | ----------- | -------------------- | | `SAVE_DEFAULTS` | Save settings as defaults | "false" | User input | Settings persistence | | `SAVE_APP_DEFAULTS` | Save app-specific defaults | "false" | User input | Settings persistence | | `DEFAULT_VARS_FILE` | Path to default.vars | "/usr/local/community-scripts/default.vars" | Environment | Settings persistence | | `APP_DEFAULTS_FILE` | Path to app.vars | "/usr/local/community-scripts/defaults/$APP.vars" | Environment | Settings persistence | ## Variable Precedence Chain Variables are resolved in the following order (highest to lowest priority): 1. **Hard Environment Variables**: Set before script execution 2. **App-specific .vars file**: `/usr/local/community-scripts/defaults/.vars` 3. **Global default.vars file**: `/usr/local/community-scripts/default.vars` 4. **Built-in defaults**: Set in `base_settings()` function ## Critical Variables for Non-Interactive Use For silent/non-interactive execution, these variables must be set: ```bash # Core container settings export APP="plex" export CTID="100" export var_hostname="plex-server" # OS selection export var_os="debian" export var_version="12" # Resource allocation export var_cpu="4" export var_ram="4096" export var_disk="20" # Network configuration export var_net="vmbr0" export var_gateway="192.168.1.1" export var_ip="192.168.1.100" # Storage selection export var_template_storage="local" export var_container_storage="local" # Feature flags export ENABLE_FUSE="true" export ENABLE_TUN="true" export SSH="true" ``` ## Environment Variable Usage Patterns ### 1. Container Creation ```bash # Basic container creation export APP="nextcloud" export CTID="101" export var_hostname="nextcloud-server" export var_os="debian" export var_version="12" export var_cpu="2" export var_ram="2048" export var_disk="10" export var_net="vmbr0" export var_gateway="192.168.1.1" export var_ip="192.168.1.101" export var_template_storage="local" export var_container_storage="local" ``` ### 2. GPU Passthrough ```bash # Enable GPU passthrough export GPU_APPS="plex,jellyfin,emby" export var_gpu="intel" export ENABLE_PRIVILEGED="true" ``` ### 3. Advanced Network Configuration ```bash # VLAN and IPv6 configuration export var_vlan="100" export var_ipv6="2001:db8::100" export IPV6_METHOD="static" export var_mtu="9000" ``` ### 4. Storage Configuration ```bash # Custom storage locations export var_template_storage="nfs-storage" export var_container_storage="ssd-storage" ``` ## Variable Validation The script validates variables at several points: 1. **Container ID validation**: Must be unique and within valid range 2. **IP address validation**: Must be valid IPv4/IPv6 format 3. **Storage validation**: Must exist and support required content types 4. **Resource validation**: Must be within reasonable limits 5. **Network validation**: Must be valid network configuration ## Common Variable Combinations ### Development Container ```bash export APP="dev-container" export CTID="200" export var_hostname="dev-server" export var_os="ubuntu" export var_version="22.04" export var_cpu="4" export var_ram="4096" export var_disk="20" export ENABLE_NESTING="true" export ENABLE_PRIVILEGED="true" ``` ### Media Server with GPU ```bash export APP="plex" export CTID="300" export var_hostname="plex-server" export var_os="debian" export var_version="12" export var_cpu="6" export var_ram="8192" export var_disk="50" export GPU_APPS="plex" export var_gpu="nvidia" export ENABLE_PRIVILEGED="true" ``` ### Lightweight Service ```bash export APP="nginx" export CTID="400" export var_hostname="nginx-proxy" export var_os="alpine" export var_version="3.18" export var_cpu="1" export var_ram="512" export var_disk="2" export ENABLE_UNPRIVILEGED="true" ``` ================================================ FILE: docs/misc/build.func/BUILD_FUNC_EXECUTION_FLOWS.md ================================================ # build.func Execution Flows ## Overview This document details the execution flows for different installation modes and scenarios in `build.func`, including variable precedence, decision trees, and workflow patterns. ## Installation Modes ### 1. Default Install Flow **Purpose**: Uses built-in defaults with minimal user interaction **Use Case**: Quick container creation with standard settings ``` Default Install Flow: ├── start() │ ├── Detect execution context │ ├── Capture hard environment variables │ └── Set CT_TYPE="install" ├── install_script() │ ├── Display installation mode menu │ ├── User selects "Default Install" │ └── Proceed with defaults ├── variables() │ ├── base_settings() # Set built-in defaults │ ├── Load app.vars (if exists) │ ├── Load default.vars (if exists) │ └── Apply variable precedence ├── build_container() │ ├── validate_settings() │ ├── check_conflicts() │ └── create_lxc_container() └── default_var_settings() └── Offer to save as defaults ``` **Key Characteristics**: - Minimal user prompts - Uses built-in defaults - Fast execution - Suitable for standard deployments ### 2. Advanced Install Flow **Purpose**: Full interactive configuration via whiptail menus **Use Case**: Custom container configuration with full control ``` Advanced Install Flow: ├── start() │ ├── Detect execution context │ ├── Capture hard environment variables │ └── Set CT_TYPE="install" ├── install_script() │ ├── Display installation mode menu │ ├── User selects "Advanced Install" │ └── Proceed with advanced configuration ├── variables() │ ├── base_settings() # Set built-in defaults │ ├── Load app.vars (if exists) │ ├── Load default.vars (if exists) │ └── Apply variable precedence ├── advanced_settings() │ ├── OS Selection Menu │ ├── Resource Configuration Menu │ ├── Network Configuration Menu │ ├── select_storage() │ │ ├── resolve_storage_preselect() │ │ └── choose_and_set_storage_for_file() │ ├── GPU Configuration Menu │ │ └── detect_gpu_devices() │ └── Feature Flags Menu ├── build_container() │ ├── validate_settings() │ ├── check_conflicts() │ └── create_lxc_container() └── default_var_settings() └── Offer to save as defaults ``` **Key Characteristics**: - Full interactive configuration - Whiptail menus for all options - Complete control over settings - Suitable for custom deployments ### 3. My Defaults Flow **Purpose**: Loads settings from global default.vars file **Use Case**: Using previously saved global defaults ``` My Defaults Flow: ├── start() │ ├── Detect execution context │ ├── Capture hard environment variables │ └── Set CT_TYPE="install" ├── install_script() │ ├── Display installation mode menu │ ├── User selects "My Defaults" │ └── Proceed with loaded defaults ├── variables() │ ├── base_settings() # Set built-in defaults │ ├── Load app.vars (if exists) │ ├── Load default.vars # Load global defaults │ └── Apply variable precedence ├── build_container() │ ├── validate_settings() │ ├── check_conflicts() │ └── create_lxc_container() └── default_var_settings() └── Offer to save as defaults ``` **Key Characteristics**: - Uses global default.vars file - Minimal user interaction - Consistent with previous settings - Suitable for repeated deployments ### 4. App Defaults Flow **Purpose**: Loads settings from app-specific .vars file **Use Case**: Using previously saved app-specific defaults ``` App Defaults Flow: ├── start() │ ├── Detect execution context │ ├── Capture hard environment variables │ └── Set CT_TYPE="install" ├── install_script() │ ├── Display installation mode menu │ ├── User selects "App Defaults" │ └── Proceed with app-specific defaults ├── variables() │ ├── base_settings() # Set built-in defaults │ ├── Load app.vars # Load app-specific defaults │ ├── Load default.vars (if exists) │ └── Apply variable precedence ├── build_container() │ ├── validate_settings() │ ├── check_conflicts() │ └── create_lxc_container() └── default_var_settings() └── Offer to save as defaults ``` **Key Characteristics**: - Uses app-specific .vars file - Minimal user interaction - App-optimized settings - Suitable for app-specific deployments ## Variable Precedence Chain ### Precedence Order (Highest to Lowest) 1. **Hard Environment Variables**: Set before script execution 2. **App-specific .vars file**: `/usr/local/community-scripts/defaults/.vars` 3. **Global default.vars file**: `/usr/local/community-scripts/default.vars` 4. **Built-in defaults**: Set in `base_settings()` function ### Variable Resolution Process ``` Variable Resolution: ├── Capture hard environment variables at start() ├── Load built-in defaults in base_settings() ├── Load global default.vars (if exists) ├── Load app-specific .vars (if exists) └── Apply precedence chain ├── Hard env vars override all ├── App.vars override default.vars and built-ins ├── Default.vars override built-ins └── Built-ins are fallback defaults ``` ## Storage Selection Logic ### Storage Resolution Flow ``` Storage Selection: ├── Check if storage is preselected │ ├── var_template_storage set? → Validate and use │ └── var_container_storage set? → Validate and use ├── Count available storage options │ ├── Only 1 option → Auto-select │ └── Multiple options → Prompt user ├── User selection via whiptail │ ├── Template storage selection │ └── Container storage selection └── Validate selected storage ├── Check availability ├── Check content type support └── Proceed with selection ``` ### Storage Validation ``` Storage Validation: ├── Check storage exists ├── Check storage is online ├── Check content type support │ ├── Template storage: vztmpl support │ └── Container storage: rootdir support ├── Check available space └── Validate permissions ``` ## GPU Passthrough Flow ### GPU Detection and Configuration ``` GPU Passthrough Flow: ├── detect_gpu_devices() │ ├── Scan for Intel GPUs │ │ ├── Check i915 driver │ │ └── Detect devices │ ├── Scan for AMD GPUs │ │ ├── Check AMDGPU driver │ │ └── Detect devices │ └── Scan for NVIDIA GPUs │ ├── Check NVIDIA driver │ ├── Detect devices │ └── Check CUDA support ├── Check GPU passthrough eligibility │ ├── Is app in GPU_APPS list? │ ├── Is container privileged? │ └── Proceed if eligible ├── GPU selection logic │ ├── Single GPU type → Auto-select │ └── Multiple GPU types → Prompt user ├── configure_gpu_passthrough() │ ├── Add GPU device entries │ ├── Configure permissions │ └── Update container config └── fix_gpu_gids() ├── Update GPU group IDs └── Configure access permissions ``` ### GPU Eligibility Check ``` GPU Eligibility: ├── Check app support │ ├── Is APP in GPU_APPS list? │ └── Proceed if supported ├── Check container privileges │ ├── Is ENABLE_PRIVILEGED="true"? │ └── Proceed if privileged └── Check hardware availability ├── Are GPUs detected? └── Proceed if available ``` ## Network Configuration Flow ### Network Setup Process ``` Network Configuration: ├── Basic network settings │ ├── var_net (network interface) │ ├── var_bridge (bridge interface) │ └── var_gateway (gateway IP) ├── IP configuration │ ├── var_ip (IPv4 address) │ ├── var_ipv6 (IPv6 address) │ └── IPV6_METHOD (IPv6 method) ├── Advanced network settings │ ├── var_vlan (VLAN ID) │ ├── var_mtu (MTU size) │ └── var_mac (MAC address) └── Network validation ├── Check IP format ├── Check gateway reachability └── Validate network configuration ``` ## Container Creation Flow ### LXC Container Creation Process ``` Container Creation: ├── create_lxc_container() │ ├── Create basic container │ ├── Configure network │ ├── Set up storage │ ├── Configure features │ ├── Set resource limits │ ├── Configure startup │ └── Start container ├── Post-creation configuration │ ├── Wait for network │ ├── Configure GPU (if enabled) │ ├── Set up SSH keys │ └── Run post-install scripts └── Finalization ├── Display container info ├── Show access details └── Provide next steps ``` ## Error Handling Flows ### Validation Error Flow ``` Validation Error Flow: ├── validate_settings() │ ├── Check configuration validity │ └── Return error if invalid ├── check_conflicts() │ ├── Check for conflicts │ └── Return error if conflicts found ├── Error handling │ ├── Display error message │ ├── cleanup_on_error() │ └── Exit with error code └── User notification ├── Show error details └── Suggest fixes ``` ### Storage Error Flow ``` Storage Error Flow: ├── Storage selection fails ├── Retry storage selection │ ├── Show available options │ └── Allow user to retry ├── Storage validation fails │ ├── Show validation errors │ └── Allow user to fix └── Fallback to default storage ├── Use fallback storage └── Continue with creation ``` ### GPU Error Flow ``` GPU Error Flow: ├── GPU detection fails ├── Fall back to no GPU │ ├── Disable GPU passthrough │ └── Continue without GPU ├── GPU configuration fails │ ├── Show configuration errors │ └── Allow user to retry └── GPU permission errors ├── Fix GPU permissions └── Retry configuration ``` ## Integration Flows ### With Install Scripts ``` Install Script Integration: ├── build.func creates container ├── Container starts successfully ├── Install script execution │ ├── Download and install app │ ├── Configure app settings │ └── Set up services └── Post-installation configuration ├── Verify installation ├── Configure access └── Display completion info ``` ### With Proxmox API ``` Proxmox API Integration: ├── API authentication ├── Container creation via API ├── Configuration updates via API ├── Status monitoring via API └── Error handling via API ``` ## Performance Considerations ### Execution Time Optimization ``` Performance Optimization: ├── Parallel operations where possible ├── Minimal user interaction in default mode ├── Efficient storage selection ├── Optimized GPU detection └── Streamlined validation ``` ### Resource Usage ``` Resource Usage: ├── Minimal memory footprint ├── Efficient disk usage ├── Optimized network usage └── Minimal CPU overhead ``` ================================================ FILE: docs/misc/build.func/BUILD_FUNC_FLOWCHART.md ================================================ # build.func Execution Flowchart ## Main Execution Flow ``` ┌─────────────────────────────────────────────────────────────────────────────────┐ │ START() │ │ Entry point when build.func is sourced or executed │ └─────────────────────┬───────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────────────────────┐ │ Check Environment │ │ • Detect if running on Proxmox host vs inside container │ │ • Capture hard environment variables │ │ • Set CT_TYPE based on context │ └─────────────────────┬───────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────────────────────┐ │ Determine Action │ │ • If CT_TYPE="update" → update_script() │ │ • If CT_TYPE="install" → install_script() │ └─────────────────────┬───────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────────────────────┐ │ INSTALL_SCRIPT() │ │ Main container creation workflow │ └─────────────────────┬───────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────────────────────┐ │ Installation Mode Selection │ │ │ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────┐ │ │ │ Default │ │ Advanced │ │ My Defaults │ │ App Defaults│ │ │ │ Install │ │ Install │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ • Use built-in │ │ • Full whiptail │ │ • Load from │ │ • Load from │ │ │ │ defaults │ │ menus │ │ default.vars │ │ app.vars │ │ │ │ • Minimal │ │ • Interactive │ │ • Override │ │ • App- │ │ │ │ prompts │ │ configuration │ │ built-ins │ │ specific │ │ │ └─────────────────┘ └─────────────────┘ └─────────────────┘ └─────────────┘ │ └─────────────────────┬───────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────────────────────┐ │ VARIABLES() │ │ • Load variable precedence chain: │ │ 1. Hard environment variables │ │ 2. App-specific .vars file │ │ 3. Global default.vars file │ │ 4. Built-in defaults in base_settings() │ └─────────────────────┬───────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────────────────────┐ │ BASE_SETTINGS() │ │ • Set core container parameters │ │ • Configure OS selection │ │ • Set resource defaults (CPU, RAM, Disk) │ │ • Configure network defaults │ └─────────────────────┬───────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────────────────────┐ │ Storage Selection Logic │ │ │ │ ┌─────────────────────────────────────────────────────────────────────────────┐ │ │ │ SELECT_STORAGE() │ │ │ │ │ │ │ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────────┐ │ │ │ │ │ Template │ │ Container │ │ Resolution │ │ │ │ │ │ Storage │ │ Storage │ │ Logic │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ • Check if │ │ • Check if │ │ 1. Only 1 storage │ │ │ │ │ │ preselected │ │ preselected │ │ → Auto-select │ │ │ │ │ │ • Validate │ │ • Validate │ │ 2. Preselected │ │ │ │ │ │ availability │ │ availability │ │ → Validate & use │ │ │ │ │ │ • Prompt if │ │ • Prompt if │ │ 3. Multiple options │ │ │ │ │ │ needed │ │ needed │ │ → Prompt user │ │ │ │ │ └─────────────────┘ └─────────────────┘ └─────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────────────┘ │ └─────────────────────┬───────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────────────────────┐ │ BUILD_CONTAINER() │ │ • Validate all settings │ │ • Check for conflicts │ │ • Prepare container configuration │ └─────────────────────┬───────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────────────────────┐ │ CREATE_LXC_CONTAINER() │ │ │ │ ┌─────────────────────────────────────────────────────────────────────────────┐ │ │ │ Container Creation Process │ │ │ │ │ │ │ │ 1. Create LXC container with basic configuration │ │ │ │ 2. Configure network settings │ │ │ │ 3. Set up storage and mount points │ │ │ │ 4. Configure features (FUSE, TUN, etc.) │ │ │ │ 5. Set resource limits │ │ │ │ 6. Configure startup options │ │ │ └─────────────────────────────────────────────────────────────────────────────┘ │ └─────────────────────┬───────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────────────────────┐ │ GPU Passthrough Decision Tree │ │ │ │ ┌─────────────────────────────────────────────────────────────────────────────┐ │ │ │ DETECT_GPU_DEVICES() │ │ │ │ │ │ │ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────────┐ │ │ │ │ │ Intel GPU │ │ AMD GPU │ │ NVIDIA GPU │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ • Check i915 │ │ • Check AMDGPU │ │ • Check NVIDIA │ │ │ │ │ │ driver │ │ driver │ │ driver │ │ │ │ │ │ • Detect │ │ • Detect │ │ • Detect devices │ │ │ │ │ │ devices │ │ devices │ │ • Check CUDA support │ │ │ │ │ └─────────────────┘ └─────────────────┘ └─────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────────────┘ │ │ │ │ ┌─────────────────────────────────────────────────────────────────────────────┐ │ │ │ GPU Selection Logic │ │ │ │ │ │ │ │ • Is app in GPU_APPS list? OR Is container privileged? │ │ │ │ └─ YES → Proceed with GPU configuration │ │ │ │ └─ NO → Skip GPU passthrough │ │ │ │ │ │ │ │ • Single GPU type detected? │ │ │ │ └─ YES → Auto-select and configure │ │ │ │ └─ NO → Prompt user for selection │ │ │ └─────────────────────────────────────────────────────────────────────────────┘ │ └─────────────────────┬───────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────────────────────┐ │ CONFIGURE_GPU_PASSTHROUGH() │ │ • Add GPU device entries to /etc/pve/lxc/.conf │ │ • Configure proper device permissions │ │ • Set up device mapping │ └─────────────────────┬───────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────────────────────┐ │ Container Finalization │ │ • Start container │ │ • Wait for network connectivity │ │ • Fix GPU GIDs (if GPU passthrough enabled) │ │ • Configure SSH keys (if enabled) │ │ • Run post-installation scripts │ └─────────────────────┬───────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────────────────────┐ │ Settings Persistence │ │ │ │ ┌─────────────────────────────────────────────────────────────────────────────┐ │ │ │ DEFAULT_VAR_SETTINGS() │ │ │ │ │ │ │ │ • Offer to save current settings as defaults │ │ │ │ • Save to /usr/local/community-scripts/default.vars │ │ │ │ • Save to /usr/local/community-scripts/defaults/.vars │ │ │ └─────────────────────────────────────────────────────────────────────────────┘ │ └─────────────────────┬───────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────────────────────┐ │ COMPLETION │ │ • Display container information │ │ • Show access details │ │ • Provide next steps │ └─────────────────────────────────────────────────────────────────────────────────┘ ``` ## Key Decision Points ### 1. Installation Mode Selection - **Default**: Uses built-in defaults, minimal user interaction - **Advanced**: Full interactive configuration via whiptail menus - **My Defaults**: Loads settings from global default.vars file - **App Defaults**: Loads settings from app-specific .vars file ### 2. Storage Selection Logic ``` Storage Selection Flow: ├── Check if storage is preselected via environment variables │ ├── YES → Validate availability and use │ └── NO → Continue to resolution logic ├── Count available storage options for content type │ ├── Only 1 option → Auto-select │ └── Multiple options → Prompt user via whiptail └── Validate selected storage and proceed ``` ### 3. GPU Passthrough Decision Tree ``` GPU Passthrough Flow: ├── Detect available GPU hardware │ ├── Intel GPU detected │ ├── AMD GPU detected │ └── NVIDIA GPU detected ├── Check if GPU passthrough should be enabled │ ├── App is in GPU_APPS list? → YES │ ├── Container is privileged? → YES │ └── Neither? → Skip GPU passthrough ├── Configure GPU passthrough │ ├── Single GPU type → Auto-configure │ └── Multiple GPU types → Prompt user └── Fix GPU GIDs post-creation ``` ### 4. Variable Precedence Chain ``` Variable Resolution Order: 1. Hard environment variables (captured at start) 2. App-specific .vars file (/usr/local/community-scripts/defaults/.vars) 3. Global default.vars file (/usr/local/community-scripts/default.vars) 4. Built-in defaults in base_settings() function ``` ## Error Handling Flow ``` Error Handling: ├── Validation errors → Display error message and exit ├── Storage errors → Retry storage selection ├── Network errors → Retry network configuration ├── GPU errors → Fall back to no GPU passthrough └── Container creation errors → Cleanup and exit ``` ## Integration Points - **Core Functions**: Depends on core.func for basic utilities - **Error Handling**: Uses error_handler.func for error management - **API Functions**: Uses api.func for Proxmox API interactions - **Tools**: Uses tools.func for additional utilities - **Install Scripts**: Integrates with -install.sh scripts ================================================ FILE: docs/misc/build.func/BUILD_FUNC_FUNCTIONS_REFERENCE.md ================================================ # build.func Functions Reference ## Overview This document provides a comprehensive reference of all functions in `build.func`, organized alphabetically with detailed descriptions, parameters, and usage information. ## Function Categories ### Initialization Functions #### `start()` **Purpose**: Main entry point when build.func is sourced or executed **Parameters**: None **Returns**: None **Side Effects**: - Detects execution context (Proxmox host vs container) - Captures hard environment variables - Sets CT_TYPE based on context - Routes to appropriate workflow (install_script or update_script) **Dependencies**: None **Environment Variables Used**: `CT_TYPE`, `APP`, `CTID` #### `variables()` **Purpose**: Load and resolve all configuration variables using precedence chain **Parameters**: None **Returns**: None **Side Effects**: - Loads app-specific .vars file - Loads global default.vars file - Applies variable precedence chain - Sets all configuration variables **Dependencies**: `base_settings()` **Environment Variables Used**: All configuration variables #### `base_settings()` **Purpose**: Set built-in default values for all configuration variables **Parameters**: None **Returns**: None **Side Effects**: Sets default values for all variables **Dependencies**: None **Environment Variables Used**: All configuration variables ### UI and Menu Functions #### `install_script()` **Purpose**: Main installation workflow coordinator **Parameters**: None **Returns**: None **Side Effects**: - Displays installation mode selection menu - Coordinates the entire installation process - Handles user interaction and validation **Dependencies**: `variables()`, `build_container()`, `default_var_settings()` **Environment Variables Used**: `APP`, `CTID`, `var_hostname` #### `advanced_settings()` **Purpose**: Provide advanced configuration options via whiptail menus **Parameters**: None **Returns**: None **Side Effects**: - Displays whiptail menus for configuration - Updates configuration variables based on user input - Validates user selections **Dependencies**: `select_storage()`, `detect_gpu_devices()` **Environment Variables Used**: All configuration variables #### `settings_menu()` **Purpose**: Display and handle settings configuration menu **Parameters**: None **Returns**: None **Side Effects**: Updates configuration variables **Dependencies**: `advanced_settings()` **Environment Variables Used**: All configuration variables ### Storage Functions #### `select_storage()` **Purpose**: Handle storage selection for templates and containers **Parameters**: None **Returns**: None **Side Effects**: - Resolves storage preselection - Prompts user for storage selection if needed - Validates storage availability - Sets var_template_storage and var_container_storage **Dependencies**: `resolve_storage_preselect()`, `choose_and_set_storage_for_file()` **Environment Variables Used**: `var_template_storage`, `var_container_storage`, `TEMPLATE_STORAGE`, `CONTAINER_STORAGE` #### `resolve_storage_preselect()` **Purpose**: Resolve preselected storage options **Parameters**: - `storage_type`: Type of storage (template or container) **Returns**: Storage name if valid, empty if invalid **Side Effects**: Validates storage availability **Dependencies**: None **Environment Variables Used**: `var_template_storage`, `var_container_storage` #### `choose_and_set_storage_for_file()` **Purpose**: Interactive storage selection via whiptail **Parameters**: - `storage_type`: Type of storage (template or container) - `content_type`: Content type (vztmpl or rootdir) **Returns**: None **Side Effects**: - Displays whiptail menu - Updates storage variables - Validates selection **Dependencies**: None **Environment Variables Used**: `var_template_storage`, `var_container_storage` ### Container Creation Functions #### `build_container()` **Purpose**: Validate settings and prepare container creation **Parameters**: None **Returns**: None **Side Effects**: - Validates all configuration - Checks for conflicts - Prepares container configuration - Calls create_lxc_container() **Dependencies**: `create_lxc_container()` **Environment Variables Used**: All configuration variables #### `create_lxc_container()` **Purpose**: Create the actual LXC container **Parameters**: None **Returns**: None **Side Effects**: - Creates LXC container with basic configuration - Configures network settings - Sets up storage and mount points - Configures features (FUSE, TUN, etc.) - Sets resource limits - Configures startup options - Starts container **Dependencies**: `configure_gpu_passthrough()`, `fix_gpu_gids()` **Environment Variables Used**: All configuration variables ### GPU and Hardware Functions #### `detect_gpu_devices()` **Purpose**: Detect available GPU hardware on the system **Parameters**: None **Returns**: None **Side Effects**: - Scans for Intel, AMD, and NVIDIA GPUs - Updates var_gpu_type and var_gpu_devices - Determines GPU capabilities **Dependencies**: None **Environment Variables Used**: `var_gpu_type`, `var_gpu_devices`, `GPU_APPS` #### `configure_gpu_passthrough()` **Purpose**: Configure GPU passthrough for the container **Parameters**: None **Returns**: None **Side Effects**: - Adds GPU device entries to container config - Configures proper device permissions - Sets up device mapping - Updates /etc/pve/lxc/.conf **Dependencies**: `detect_gpu_devices()` **Environment Variables Used**: `var_gpu`, `var_gpu_type`, `var_gpu_devices`, `CTID` #### `fix_gpu_gids()` **Purpose**: Fix GPU group IDs after container creation **Parameters**: None **Returns**: None **Side Effects**: - Updates GPU group IDs in container - Ensures proper GPU access permissions - Configures video and render groups **Dependencies**: `configure_gpu_passthrough()` **Environment Variables Used**: `CTID`, `var_gpu_type` ### SSH Configuration Functions #### `configure_ssh_settings()` **Purpose**: Interactive SSH key and access configuration wizard **Parameters**: - `step_info` (optional): Step indicator string (e.g., "Step 17/19") for consistent dialog headers **Returns**: None **Side Effects**: - Creates temporary file for SSH keys - Discovers and presents available SSH keys from host - Allows manual key entry or folder/glob scanning - Sets `SSH` variable to "yes" or "no" based on user selection - Sets `SSH_AUTHORIZED_KEY` if manual key provided - Populates `SSH_KEYS_FILE` with selected keys **Dependencies**: `ssh_discover_default_files()`, `ssh_build_choices_from_files()` **Environment Variables Used**: `SSH`, `SSH_AUTHORIZED_KEY`, `SSH_KEYS_FILE` **SSH Key Source Options**: 1. `found` - Select from auto-detected host keys 2. `manual` - Paste a single public key 3. `folder` - Scan custom folder or glob pattern 4. `none` - No SSH keys **Note**: The "Enable root SSH access?" dialog is always shown, regardless of whether SSH keys or password are configured. This ensures users can always enable SSH access even with automatic login. #### `ssh_discover_default_files()` **Purpose**: Discover SSH public key files on the host system **Parameters**: None **Returns**: Array of discovered key file paths **Side Effects**: Scans common SSH key locations **Dependencies**: None **Environment Variables Used**: `var_ssh_import_glob` #### `ssh_build_choices_from_files()` **Purpose**: Build whiptail checklist choices from SSH key files **Parameters**: - Array of file paths to process **Returns**: None **Side Effects**: - Sets `CHOICES` array for whiptail checklist - Sets `COUNT` variable with number of keys found - Creates `MAPFILE` for key tag to content mapping **Dependencies**: None **Environment Variables Used**: `CHOICES`, `COUNT`, `MAPFILE` ### Settings Persistence Functions #### `default_var_settings()` **Purpose**: Offer to save current settings as defaults **Parameters**: None **Returns**: None **Side Effects**: - Prompts user to save settings - Saves to default.vars file - Saves to app-specific .vars file **Dependencies**: `maybe_offer_save_app_defaults()` **Environment Variables Used**: All configuration variables #### `maybe_offer_save_app_defaults()` **Purpose**: Offer to save app-specific defaults **Parameters**: None **Returns**: None **Side Effects**: - Prompts user to save app-specific settings - Saves to app.vars file - Updates app-specific configuration **Dependencies**: None **Environment Variables Used**: `APP`, `SAVE_APP_DEFAULTS` ### Utility Functions #### `validate_settings()` **Purpose**: Validate all configuration settings **Parameters**: None **Returns**: 0 if valid, 1 if invalid **Side Effects**: - Checks for configuration conflicts - Validates resource limits - Validates network configuration - Validates storage configuration **Dependencies**: None **Environment Variables Used**: All configuration variables #### `check_conflicts()` **Purpose**: Check for configuration conflicts **Parameters**: None **Returns**: 0 if no conflicts, 1 if conflicts found **Side Effects**: - Checks for conflicting settings - Validates resource allocation - Checks network configuration **Dependencies**: None **Environment Variables Used**: All configuration variables #### `cleanup_on_error()` **Purpose**: Clean up resources on error **Parameters**: None **Returns**: None **Side Effects**: - Removes partially created containers - Cleans up temporary files - Resets configuration **Dependencies**: None **Environment Variables Used**: `CTID` ## Function Call Flow ### Main Installation Flow ``` start() ├── variables() │ ├── base_settings() │ ├── Load app.vars │ └── Load default.vars ├── install_script() │ ├── advanced_settings() │ │ ├── select_storage() │ │ │ ├── resolve_storage_preselect() │ │ │ └── choose_and_set_storage_for_file() │ │ └── detect_gpu_devices() │ ├── build_container() │ │ ├── validate_settings() │ │ ├── check_conflicts() │ │ └── create_lxc_container() │ │ ├── configure_gpu_passthrough() │ │ └── fix_gpu_gids() │ └── default_var_settings() │ └── maybe_offer_save_app_defaults() ``` ### Error Handling Flow ``` Error Detection ├── validate_settings() │ └── check_conflicts() ├── Error Handling │ └── cleanup_on_error() └── Exit with error code ``` ## Function Dependencies ### Core Dependencies - `start()` → `install_script()` → `build_container()` → `create_lxc_container()` - `variables()` → `base_settings()` - `advanced_settings()` → `select_storage()` → `detect_gpu_devices()` ### Storage Dependencies - `select_storage()` → `resolve_storage_preselect()` - `select_storage()` → `choose_and_set_storage_for_file()` ### GPU Dependencies - `configure_gpu_passthrough()` → `detect_gpu_devices()` - `fix_gpu_gids()` → `configure_gpu_passthrough()` ### Settings Dependencies - `default_var_settings()` → `maybe_offer_save_app_defaults()` ## Function Usage Examples ### Basic Container Creation ```bash # Set required variables export APP="plex" export CTID="100" export var_hostname="plex-server" # Call main functions start() # Entry point # → variables() # Load configuration # → install_script() # Main workflow # → build_container() # Create container # → create_lxc_container() # Actual creation ``` ### Advanced Configuration ```bash # Set advanced variables export var_os="debian" export var_version="12" export var_cpu="4" export var_ram="4096" export var_disk="20" # Call advanced functions advanced_settings() # Interactive configuration # → select_storage() # Storage selection # → detect_gpu_devices() # GPU detection ``` ### GPU Passthrough ```bash # Enable GPU passthrough export GPU_APPS="plex" export var_gpu="nvidia" # Call GPU functions detect_gpu_devices() # Detect hardware configure_gpu_passthrough() # Configure passthrough fix_gpu_gids() # Fix permissions ``` ### Settings Persistence ```bash # Save settings as defaults export SAVE_DEFAULTS="true" export SAVE_APP_DEFAULTS="true" # Call persistence functions default_var_settings() # Save global defaults maybe_offer_save_app_defaults() # Save app defaults ``` ### Container Resource & ID Management #### `validate_container_id()` **Purpose**: Validates if a container ID is available for use. **Parameters**: `ctid` (Integer) **Returns**: `0` if available, `1` if already in use or invalid. **Description**: Checks for existing config files in `/etc/pve/lxc/` or `/etc/pve/qemu-server/`, and verifies LVM logical volumes. #### `get_valid_container_id()` **Purpose**: Returns the next available, unused container ID. **Parameters**: `suggested_id` (Optional) **Returns**: A valid container ID string. **Description**: If the suggested ID is taken, it increments until it finds an available one. #### `maxkeys_check()` **Purpose**: Ensures host kernel parameters support high numbers of keys (required for some apps). **Parameters**: None **Description**: Checks and optionally updates `kernel.keys.maxkeys` and `kernel.keys.maxbytes`. #### `get_current_ip()` **Purpose**: Retrieves the current IP address of the container. **Parameters**: `ctid` (Integer) **Returns**: IP address string. #### `update_motd_ip()` **Purpose**: Updates the Message of the Day (MOTD) file with the container's IP. **Parameters**: None ## Function Error Handling ### Validation Functions - `validate_settings()`: Returns 0 for valid, 1 for invalid - `check_conflicts()`: Returns 0 for no conflicts, 1 for conflicts ### Error Recovery - `cleanup_on_error()`: Cleans up on any error - Error codes are propagated up the call stack - Critical errors cause script termination ### Error Types 1. **Configuration Errors**: Invalid settings or conflicts 2. **Resource Errors**: Insufficient resources or conflicts 3. **Network Errors**: Invalid network configuration 4. **Storage Errors**: Storage not available or invalid 5. **GPU Errors**: GPU configuration failures 6. **Container Creation Errors**: LXC creation failures ================================================ FILE: docs/misc/build.func/BUILD_FUNC_USAGE_EXAMPLES.md ================================================ # build.func Usage Examples ## Overview This document provides practical usage examples for `build.func`, covering common scenarios, CLI examples, and environment variable combinations. ## Basic Usage Examples ### 1. Simple Container Creation **Scenario**: Create a basic Plex media server container ```bash # Set basic environment variables export APP="plex" export CTID="100" export var_hostname="plex-server" export var_os="debian" export var_version="12" export var_cpu="4" export var_ram="4096" export var_disk="20" export var_net="vmbr0" export var_gateway="192.168.1.1" export var_ip="192.168.1.100" export var_template_storage="local" export var_container_storage="local" # Execute build.func source build.func ``` **Expected Output**: ``` Creating Plex container... Container ID: 100 Hostname: plex-server OS: Debian 12 Resources: 4 CPU, 4GB RAM, 20GB Disk Network: 192.168.1.100/24 Container created successfully! ``` ### 2. Advanced Configuration **Scenario**: Create a Nextcloud container with custom settings ```bash # Set advanced environment variables export APP="nextcloud" export CTID="101" export var_hostname="nextcloud-server" export var_os="ubuntu" export var_version="22.04" export var_cpu="6" export var_ram="8192" export var_disk="50" export var_net="vmbr0" export var_gateway="192.168.1.1" export var_ip="192.168.1.101" export var_vlan="100" export var_mtu="9000" export var_template_storage="ssd-storage" export var_container_storage="ssd-storage" export var_fuse="yes" export var_tun="yes" export SSH="true" # Execute build.func source build.func ``` ### 3. GPU Passthrough Configuration **Scenario**: Create a Jellyfin container with NVIDIA GPU passthrough ```bash # Set GPU passthrough variables export APP="jellyfin" export CTID="102" export var_hostname="jellyfin-server" export var_os="debian" export var_version="12" export var_cpu="8" export var_ram="16384" export var_disk="30" export var_net="vmbr0" export var_gateway="192.168.1.1" export var_ip="192.168.1.102" export var_template_storage="local" export var_container_storage="local" export GPU_APPS="jellyfin" export var_gpu="nvidia" export ENABLE_PRIVILEGED="true" export ENABLE_FUSE="true" export ENABLE_TUN="true" # Execute build.func source build.func ``` ## Silent/Non-Interactive Examples ### 1. Automated Deployment **Scenario**: Deploy multiple containers without user interaction ```bash #!/bin/bash # Automated deployment script # Function to create container create_container() { local app=$1 local ctid=$2 local ip=$3 export APP="$app" export CTID="$ctid" export var_hostname="${app}-server" export var_os="debian" export var_version="12" export var_cpu="2" export var_ram="2048" export var_disk="10" export var_net="vmbr0" export var_gateway="192.168.1.1" export var_ip="$ip" export var_template_storage="local" export var_container_storage="local" export ENABLE_FUSE="true" export ENABLE_TUN="true" export SSH="true" source build.func } # Create multiple containers create_container "plex" "100" "192.168.1.100" create_container "nextcloud" "101" "192.168.1.101" create_container "nginx" "102" "192.168.1.102" ``` ### 2. Development Environment Setup **Scenario**: Create development containers with specific configurations ```bash #!/bin/bash # Development environment setup # Development container configuration export APP="dev-container" export CTID="200" export var_hostname="dev-server" export var_os="ubuntu" export var_version="22.04" export var_cpu="4" export var_ram="4096" export var_disk="20" export var_net="vmbr0" export var_gateway="192.168.1.1" export var_ip="192.168.1.200" export var_template_storage="local" export var_container_storage="local" export ENABLE_NESTING="true" export ENABLE_PRIVILEGED="true" export ENABLE_FUSE="true" export ENABLE_TUN="true" export SSH="true" # Execute build.func source build.func ``` ## Network Configuration Examples ### 1. VLAN Configuration **Scenario**: Create container with VLAN support ```bash # VLAN configuration export APP="web-server" export CTID="300" export var_hostname="web-server" export var_os="debian" export var_version="12" export var_cpu="2" export var_ram="2048" export var_disk="10" export var_net="vmbr0" export var_gateway="192.168.100.1" export var_ip="192.168.100.100" export var_vlan="100" export var_mtu="1500" export var_template_storage="local" export var_container_storage="local" source build.func ``` ### 2. IPv6 Configuration **Scenario**: Create container with IPv6 support ```bash # IPv6 configuration export APP="ipv6-server" export CTID="301" export var_hostname="ipv6-server" export var_os="debian" export var_version="12" export var_cpu="2" export var_ram="2048" export var_disk="10" export var_net="vmbr0" export var_gateway="192.168.1.1" export var_ip="192.168.1.101" export var_ipv6="2001:db8::101" export IPV6_METHOD="static" export var_template_storage="local" export var_container_storage="local" source build.func ``` ## Storage Configuration Examples ### 1. Custom Storage Locations **Scenario**: Use different storage for templates and containers ```bash # Custom storage configuration export APP="storage-test" export CTID="400" export var_hostname="storage-test" export var_os="debian" export var_version="12" export var_cpu="2" export var_ram="2048" export var_disk="10" export var_net="vmbr0" export var_gateway="192.168.1.1" export var_ip="192.168.1.140" export var_template_storage="nfs-storage" export var_container_storage="ssd-storage" source build.func ``` ### 2. High-Performance Storage **Scenario**: Use high-performance storage for resource-intensive applications ```bash # High-performance storage configuration export APP="database-server" export CTID="401" export var_hostname="database-server" export var_os="debian" export var_version="12" export var_cpu="8" export var_ram="16384" export var_disk="100" export var_net="vmbr0" export var_gateway="192.168.1.1" export var_ip="192.168.1.141" export var_template_storage="nvme-storage" export var_container_storage="nvme-storage" source build.func ``` ## Feature Configuration Examples ### 1. Privileged Container **Scenario**: Create privileged container for system-level access ```bash # Privileged container configuration export APP="system-container" export CTID="500" export var_hostname="system-container" export var_os="debian" export var_version="12" export var_cpu="4" export var_ram="4096" export var_disk="20" export var_net="vmbr0" export var_gateway="192.168.1.1" export var_ip="192.168.1.150" export var_template_storage="local" export var_container_storage="local" export ENABLE_PRIVILEGED="true" export ENABLE_FUSE="true" export ENABLE_TUN="true" export ENABLE_KEYCTL="true" export ENABLE_MOUNT="true" source build.func ``` ### 2. Unprivileged Container **Scenario**: Create secure unprivileged container ```bash # Unprivileged container configuration export APP="secure-container" export CTID="501" export var_hostname="secure-container" export var_os="debian" export var_version="12" export var_cpu="2" export var_ram="2048" export var_disk="10" export var_net="vmbr0" export var_gateway="192.168.1.1" export var_ip="192.168.1.151" export var_template_storage="local" export var_container_storage="local" export ENABLE_UNPRIVILEGED="true" export ENABLE_FUSE="true" export ENABLE_TUN="true" source build.func ``` ## Settings Persistence Examples ### 1. Save Global Defaults **Scenario**: Save current settings as global defaults ```bash # Save global defaults export APP="default-test" export CTID="600" export var_hostname="default-test" export var_os="debian" export var_version="12" export var_cpu="2" export var_ram="2048" export var_disk="10" export var_net="vmbr0" export var_gateway="192.168.1.1" export var_ip="192.168.1.160" export var_template_storage="local" export var_container_storage="local" export SAVE_DEFAULTS="true" source build.func ``` ### 2. Save App-Specific Defaults **Scenario**: Save settings as app-specific defaults ```bash # Save app-specific defaults export APP="plex" export CTID="601" export var_hostname="plex-server" export var_os="debian" export var_version="12" export var_cpu="4" export var_ram="4096" export var_disk="20" export var_net="vmbr0" export var_gateway="192.168.1.1" export var_ip="192.168.1.161" export var_template_storage="local" export var_container_storage="local" export SAVE_APP_DEFAULTS="true" source build.func ``` ## Error Handling Examples ### 1. Validation Error Handling **Scenario**: Handle configuration validation errors ```bash #!/bin/bash # Error handling example # Set invalid configuration export APP="error-test" export CTID="700" export var_hostname="error-test" export var_os="invalid-os" export var_version="invalid-version" export var_cpu="invalid-cpu" export var_ram="invalid-ram" export var_disk="invalid-disk" export var_net="invalid-network" export var_gateway="invalid-gateway" export var_ip="invalid-ip" # Execute with error handling if source build.func; then echo "Container created successfully!" else echo "Error: Container creation failed!" echo "Please check your configuration and try again." fi ``` ### 2. Storage Error Handling **Scenario**: Handle storage selection errors ```bash #!/bin/bash # Storage error handling # Set invalid storage export APP="storage-error-test" export CTID="701" export var_hostname="storage-error-test" export var_os="debian" export var_version="12" export var_cpu="2" export var_ram="2048" export var_disk="10" export var_net="vmbr0" export var_gateway="192.168.1.1" export var_ip="192.168.1.170" export var_template_storage="nonexistent-storage" export var_container_storage="nonexistent-storage" # Execute with error handling if source build.func; then echo "Container created successfully!" else echo "Error: Storage not available!" echo "Please check available storage and try again." fi ``` ## Integration Examples ### 1. With Install Scripts **Scenario**: Integrate with application install scripts ```bash #!/bin/bash # Integration with install scripts # Create container export APP="plex" export CTID="800" export var_hostname="plex-server" export var_os="debian" export var_version="12" export var_cpu="4" export var_ram="4096" export var_disk="20" export var_net="vmbr0" export var_gateway="192.168.1.1" export var_ip="192.168.1.180" export var_template_storage="local" export var_container_storage="local" # Create container source build.func # Run install script if [ -f "plex-install.sh" ]; then source plex-install.sh else echo "Install script not found!" fi ``` ### 2. With Monitoring **Scenario**: Integrate with monitoring systems ```bash #!/bin/bash # Monitoring integration # Create container with monitoring export APP="monitored-app" export CTID="801" export var_hostname="monitored-app" export var_os="debian" export var_version="12" export var_cpu="2" export var_ram="2048" export var_disk="10" export var_net="vmbr0" export var_gateway="192.168.1.1" export var_ip="192.168.1.181" export var_template_storage="local" export var_container_storage="local" export DIAGNOSTICS="true" # Create container source build.func # Set up monitoring if [ -f "monitoring-setup.sh" ]; then source monitoring-setup.sh fi ``` ## Best Practices ### 1. Environment Variable Management ```bash #!/bin/bash # Best practice: Environment variable management # Set configuration file CONFIG_FILE="/etc/build.func.conf" # Load configuration if exists if [ -f "$CONFIG_FILE" ]; then source "$CONFIG_FILE" fi # Set required variables export APP="${APP:-plex}" export CTID="${CTID:-100}" export var_hostname="${var_hostname:-plex-server}" export var_os="${var_os:-debian}" export var_version="${var_version:-12}" export var_cpu="${var_cpu:-2}" export var_ram="${var_ram:-2048}" export var_disk="${var_disk:-10}" export var_net="${var_net:-vmbr0}" export var_gateway="${var_gateway:-192.168.1.1}" export var_ip="${var_ip:-192.168.1.100}" export var_template_storage="${var_template_storage:-local}" export var_container_storage="${var_container_storage:-local}" # Execute build.func source build.func ``` ### 2. Error Handling and Logging ```bash #!/bin/bash # Best practice: Error handling and logging # Set log file LOG_FILE="/var/log/build.func.log" # Function to log messages log_message() { echo "$(date): $1" >> "$LOG_FILE" } # Function to create container with error handling create_container() { local app=$1 local ctid=$2 log_message "Starting container creation for $app (ID: $ctid)" # Set variables export APP="$app" export CTID="$ctid" export var_hostname="${app}-server" export var_os="debian" export var_version="12" export var_cpu="2" export var_ram="2048" export var_disk="10" export var_net="vmbr0" export var_gateway="192.168.1.1" export var_ip="192.168.1.$ctid" export var_template_storage="local" export var_container_storage="local" # Create container if source build.func; then log_message "Container $app created successfully (ID: $ctid)" return 0 else log_message "Error: Failed to create container $app (ID: $ctid)" return 1 fi } # Create containers create_container "plex" "100" create_container "nextcloud" "101" create_container "nginx" "102" ``` ================================================ FILE: docs/misc/build.func/README.md ================================================ # build.func Documentation ## Overview This directory contains comprehensive documentation for the `build.func` script, which is the core orchestration script for Proxmox LXC container creation in the Community Scripts project. ## Documentation Files ### 🎛️ [BUILD_FUNC_ADVANCED_SETTINGS.md](./BUILD_FUNC_ADVANCED_SETTINGS.md) Complete reference for the 28-step Advanced Settings wizard, including all configurable options and their inheritance behavior. **Contents:** - All 28 wizard steps explained - Default value inheritance - Feature matrix (when to enable each feature) - Confirmation summary format - Usage examples ### 📊 [BUILD_FUNC_FLOWCHART.md](./BUILD_FUNC_FLOWCHART.md) Visual ASCII flowchart showing the main execution flow, decision trees, and key decision points in the build.func script. **Contents:** - Main execution flow diagram - Installation mode selection flows - Storage selection workflow - GPU passthrough decision logic - Variable precedence chain - Error handling flow - Integration points ### 🔧 [BUILD_FUNC_ENVIRONMENT_VARIABLES.md](./BUILD_FUNC_ENVIRONMENT_VARIABLES.md) Complete reference of all environment variables used in build.func, organized by category and usage context. **Contents:** - Core container variables - Operating system variables - Resource configuration variables - Network configuration variables - Storage configuration variables - Feature flags - GPU passthrough variables - API and diagnostics variables - Settings persistence variables - Variable precedence chain - Critical variables for non-interactive use - Common variable combinations ### 📚 [BUILD_FUNC_FUNCTIONS_REFERENCE.md](./BUILD_FUNC_FUNCTIONS_REFERENCE.md) Alphabetical function reference with detailed descriptions, parameters, dependencies, and usage information. **Contents:** - Initialization functions - UI and menu functions - Storage functions - Container creation functions - GPU and hardware functions - Settings persistence functions - Utility functions - Function call flow - Function dependencies - Function usage examples - Function error handling ### 🔄 [BUILD_FUNC_EXECUTION_FLOWS.md](./BUILD_FUNC_EXECUTION_FLOWS.md) Detailed execution flows for different installation modes and scenarios, including variable precedence and decision trees. **Contents:** - Default install flow - Advanced install flow - My defaults flow - App defaults flow - Variable precedence chain - Storage selection logic - GPU passthrough flow - Network configuration flow - Container creation flow - Error handling flows - Integration flows - Performance considerations ### 🏗️ [BUILD_FUNC_ARCHITECTURE.md](./BUILD_FUNC_ARCHITECTURE.md) High-level architectural overview including module dependencies, data flow, integration points, and system architecture. **Contents:** - High-level architecture diagram - Module dependencies - Data flow architecture - Integration architecture - System architecture components - User interface components - Security architecture - Performance architecture - Deployment architecture - Maintenance architecture - Future architecture considerations ### 💡 [BUILD_FUNC_USAGE_EXAMPLES.md](./BUILD_FUNC_USAGE_EXAMPLES.md) Practical usage examples covering common scenarios, CLI examples, and environment variable combinations. **Contents:** - Basic usage examples - Silent/non-interactive examples - Network configuration examples - Storage configuration examples - Feature configuration examples - Settings persistence examples - Error handling examples - Integration examples - Best practices ## Quick Start Guide ### For New Users 1. Start with [BUILD_FUNC_FLOWCHART.md](./BUILD_FUNC_FLOWCHART.md) to understand the overall flow 2. Review [BUILD_FUNC_ENVIRONMENT_VARIABLES.md](./BUILD_FUNC_ENVIRONMENT_VARIABLES.md) for configuration options 3. Follow examples in [BUILD_FUNC_USAGE_EXAMPLES.md](./BUILD_FUNC_USAGE_EXAMPLES.md) ### For Developers 1. Read [BUILD_FUNC_ARCHITECTURE.md](./BUILD_FUNC_ARCHITECTURE.md) for system overview 2. Study [BUILD_FUNC_FUNCTIONS_REFERENCE.md](./BUILD_FUNC_FUNCTIONS_REFERENCE.md) for function details 3. Review [BUILD_FUNC_EXECUTION_FLOWS.md](./BUILD_FUNC_EXECUTION_FLOWS.md) for implementation details ### For System Administrators 1. Focus on [BUILD_FUNC_USAGE_EXAMPLES.md](./BUILD_FUNC_USAGE_EXAMPLES.md) for deployment scenarios 2. Review [BUILD_FUNC_ENVIRONMENT_VARIABLES.md](./BUILD_FUNC_ENVIRONMENT_VARIABLES.md) for configuration management 3. Check [BUILD_FUNC_ARCHITECTURE.md](./BUILD_FUNC_ARCHITECTURE.md) for security and performance considerations ## Key Concepts ### Variable Precedence Variables are resolved in this order (highest to lowest priority): 1. Hard environment variables (set before script execution) 2. App-specific .vars file (`/usr/local/community-scripts/defaults/.vars`) 3. Global default.vars file (`/usr/local/community-scripts/default.vars`) 4. Built-in defaults (set in `base_settings()` function) ### Installation Modes - **Default Install**: Uses built-in defaults, minimal prompts - **Advanced Install**: Full interactive configuration via whiptail - **My Defaults**: Loads from global default.vars file - **App Defaults**: Loads from app-specific .vars file ### Storage Selection Logic 1. If only 1 storage exists for content type → auto-select 2. If preselected via environment variables → validate and use 3. Otherwise → prompt user via whiptail ### GPU Passthrough Flow 1. Detect hardware (Intel/AMD/NVIDIA) 2. Check if app is in GPU_APPS list OR container is privileged 3. Auto-select if single GPU type, prompt if multiple 4. Configure `/etc/pve/lxc/.conf` with proper device entries 5. Fix GIDs post-creation to match container's video/render groups ## Common Use Cases ### Basic Container Creation ```bash export APP="plex" export CTID="100" export var_hostname="plex-server" export var_os="debian" export var_version="12" export var_cpu="4" export var_ram="4096" export var_disk="20" export var_net="vmbr0" export var_gateway="192.168.1.1" export var_ip="192.168.1.100" export var_template_storage="local" export var_container_storage="local" source build.func ``` ### GPU Passthrough ```bash export APP="jellyfin" export CTID="101" export var_hostname="jellyfin-server" export var_os="debian" export var_version="12" export var_cpu="8" export var_ram="16384" export var_disk="30" export var_net="vmbr0" export var_gateway="192.168.1.1" export var_ip="192.168.1.101" export var_template_storage="local" export var_container_storage="local" export GPU_APPS="jellyfin" export var_gpu="nvidia" export ENABLE_PRIVILEGED="true" source build.func ``` ### Silent/Non-Interactive Deployment ```bash #!/bin/bash # Automated deployment export APP="nginx" export CTID="102" export var_hostname="nginx-proxy" export var_os="alpine" export var_version="3.18" export var_cpu="1" export var_ram="512" export var_disk="2" export var_net="vmbr0" export var_gateway="192.168.1.1" export var_ip="192.168.1.102" export var_template_storage="local" export var_container_storage="local" export ENABLE_UNPRIVILEGED="true" source build.func ``` ## Troubleshooting ### Common Issues 1. **Container creation fails**: Check resource availability and configuration validity 2. **Storage errors**: Verify storage exists and supports required content types 3. **Network errors**: Validate network configuration and IP address availability 4. **GPU passthrough issues**: Check hardware detection and container privileges 5. **Permission errors**: Verify user permissions and container privileges ### Debug Mode Enable verbose output for debugging: ```bash export VERBOSE="true" export DIAGNOSTICS="true" source build.func ``` ### Log Files Check system logs for detailed error information: - `/var/log/syslog` - `/var/log/pve/lxc/.log` - Container-specific logs ## Contributing When contributing to build.func documentation: 1. Update relevant documentation files 2. Add examples for new features 3. Update architecture diagrams if needed 4. Test all examples before submitting 5. Follow the existing documentation style ## Related Documentation - [Main README](../../README.md) - Project overview - [Installation Guide](../../install/) - Installation scripts - [Container Templates](../../ct/) - Container templates - [Tools](../../tools/) - Additional tools and utilities ## Support For issues and questions: 1. Check this documentation first 2. Review the [troubleshooting section](#troubleshooting) 3. Check existing issues in the project repository 4. Create a new issue with detailed information --- *Last updated: $(date)* *Documentation version: 1.0* ================================================ FILE: docs/misc/cloud-init.func/CLOUD_INIT_FUNC_FLOWCHART.md ================================================ # cloud-init.func Flowchart Cloud-init VM provisioning flow. ## Cloud-Init Generation and Application ``` generate_cloud_init() ↓ generate_user_data() ↓ setup_ssh_keys() ↓ Apply to VM ↓ VM Boot ↓ cloud-init phases ├─ system ├─ config └─ final ↓ VM Ready ✓ ``` --- **Last Updated**: December 2025 ================================================ FILE: docs/misc/cloud-init.func/CLOUD_INIT_FUNC_FUNCTIONS_REFERENCE.md ================================================ # cloud-init.func Functions Reference Cloud-init and VM provisioning functions. ## Core Functions ### generate_cloud_init() Generate cloud-init configuration. ### generate_user_data() Generate user-data script for VM. ### apply_cloud_init() Apply cloud-init to VM. ### setup_ssh_keys() Deploy SSH public keys. ### setup_static_ip() Configure static IP on VM. ### setup_dns() Configure DNS for VM. ### setup_ipv6() Enable IPv6 on VM. --- **Last Updated**: December 2025 ================================================ FILE: docs/misc/cloud-init.func/CLOUD_INIT_FUNC_INTEGRATION.md ================================================ # cloud-init.func Integration Guide Cloud-init integration with Proxmox VM provisioning. --- **Last Updated**: December 2025 ================================================ FILE: docs/misc/cloud-init.func/CLOUD_INIT_FUNC_USAGE_EXAMPLES.md ================================================ # cloud-init.func Usage Examples Examples for VM cloud-init configuration. ### Example: Basic Cloud-Init ```bash #!/usr/bin/env bash generate_cloud_init > cloud-init.yaml setup_ssh_keys "$VMID" "$SSH_KEY" apply_cloud_init "$VMID" cloud-init.yaml ``` --- **Last Updated**: December 2025 ================================================ FILE: docs/misc/cloud-init.func/README.md ================================================ # cloud-init.func Documentation ## Overview The `cloud-init.func` file provides cloud-init configuration and VM initialization functions for Proxmox VE virtual machines. It handles user data, cloud-config generation, and VM setup automation. ## Purpose and Use Cases - **VM Cloud-Init Setup**: Generate and apply cloud-init configurations for VMs - **User Data Generation**: Create user-data scripts for VM initialization - **Cloud-Config**: Generate cloud-config YAML for VM provisioning - **SSH Key Management**: Setup SSH keys for VM access - **Network Configuration**: Configure networking for VMs - **Automated VM Provisioning**: Complete VM setup without manual intervention ## Quick Reference ### Key Function Groups - **Cloud-Init Core**: Generate and apply cloud-init configurations - **User Data**: Create initialization scripts for VMs - **SSH Setup**: Deploy SSH keys automatically - **Network Configuration**: Setup networking during VM provisioning - **VM Customization**: Apply custom settings to VMs ### Dependencies - **External**: `cloud-init`, `curl`, `qemu-img` - **Internal**: Uses functions from `core.func`, `error_handler.func` ### Integration Points - Used by: VM creation scripts (vm/*.sh) - Uses: Environment variables from build.func - Provides: VM initialization and cloud-init services ## Documentation Files ### 📊 [CLOUD_INIT_FUNC_FLOWCHART.md](./CLOUD_INIT_FUNC_FLOWCHART.md) Visual execution flows showing cloud-init generation and VM provisioning workflows. ### 📚 [CLOUD_INIT_FUNC_FUNCTIONS_REFERENCE.md](./CLOUD_INIT_FUNC_FUNCTIONS_REFERENCE.md) Complete alphabetical reference of all cloud-init functions. ### 💡 [CLOUD_INIT_FUNC_USAGE_EXAMPLES.md](./CLOUD_INIT_FUNC_USAGE_EXAMPLES.md) Practical examples for VM cloud-init setup and customization. ### 🔗 [CLOUD_INIT_FUNC_INTEGRATION.md](./CLOUD_INIT_FUNC_INTEGRATION.md) How cloud-init.func integrates with VM creation and Proxmox workflows. ## Key Features ### Cloud-Init Configuration - **User Data Generation**: Create custom initialization scripts - **Cloud-Config YAML**: Generate standardized cloud-config - **SSH Keys**: Automatically deploy public keys - **Package Installation**: Install packages during VM boot - **Custom Commands**: Run arbitrary commands on first boot ### VM Network Setup - **DHCP Configuration**: Configure DHCP for automatic IP assignment - **Static IP Setup**: Configure static IP addresses - **IPv6 Support**: Enable IPv6 on VMs - **DNS Configuration**: Set DNS servers for VM - **Firewall Rules**: Basic firewall configuration ### Security Features - **SSH Key Injection**: Deploy SSH keys during VM creation - **Disable Passwords**: Disable password authentication - **Sudoers Configuration**: Setup sudo access - **User Management**: Create and configure users ## Function Categories ### 🔹 Cloud-Init Core Functions - `generate_cloud_init()` - Create cloud-init configuration - `generate_user_data()` - Generate user-data script - `apply_cloud_init()` - Apply cloud-init to VM - `validate_cloud_init()` - Validate cloud-config syntax ### 🔹 SSH & Security Functions - `setup_ssh_keys()` - Deploy SSH public keys - `setup_sudo()` - Configure sudoers - `create_user()` - Create new user account - `disable_password_auth()` - Disable password login ### 🔹 Network Configuration Functions - `setup_dhcp()` - Configure DHCP networking - `setup_static_ip()` - Configure static IP - `setup_dns()` - Configure DNS servers - `setup_ipv6()` - Enable IPv6 support ### 🔹 VM Customization Functions - `install_packages()` - Install packages during boot - `run_custom_commands()` - Execute custom scripts - `configure_hostname()` - Set VM hostname - `configure_timezone()` - Set VM timezone ## Cloud-Init Workflow ``` VM Created ↓ cloud-init (system) boot phase ↓ User-Data Script Execution ↓ ├─ Install packages ├─ Deploy SSH keys ├─ Configure network └─ Create users ↓ cloud-init config phase ↓ Apply cloud-config settings ↓ cloud-init final phase ↓ VM Ready for Use ``` ## Common Usage Patterns ### Basic VM Setup with Cloud-Init ```bash #!/usr/bin/env bash source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" # Generate cloud-init configuration cat > cloud-init.yaml < user-data.txt # Inject SSH key setup_ssh_keys "$VMID" "$SSH_KEY" # Create VM with cloud-init qm create $VMID ... --cicustom local:snippets/user-data ``` ### Network Configuration ```bash #!/usr/bin/env bash source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" # Static IP setup setup_static_ip "192.168.1.100" "255.255.255.0" "192.168.1.1" # DNS configuration setup_dns "8.8.8.8 8.8.4.4" # IPv6 support setup_ipv6 ``` ## Best Practices ### ✅ DO - Validate cloud-config syntax before applying - Use cloud-init for automated setup - Deploy SSH keys for secure access - Test cloud-init configuration in non-production first - Use DHCP for easier VM deployment - Document custom cloud-init configurations - Version control cloud-init templates ### ❌ DON'T - Use weak SSH keys or passwords - Leave SSH password authentication enabled - Hardcode credentials in cloud-init - Skip validation of cloud-config - Use untrusted cloud-init sources - Forget to set timezone on VMs - Mix cloud-init versions ## Cloud-Config Format ### Example Cloud-Config ```yaml #cloud-config # This is a comment # System configuration hostname: myvm timezone: UTC package_upgrade: true # Packages to install packages: - curl - wget - git - build-essential # SSH keys for users ssh_authorized_keys: - ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC... # Users to create users: - name: ubuntu home: /home/ubuntu shell: /bin/bash sudo: ['ALL=(ALL) NOPASSWD:ALL'] ssh_authorized_keys: - ssh-rsa AAAAB3... # Commands to run on boot runcmd: - apt-get update - apt-get upgrade -y - systemctl restart ssh # Files to create write_files: - path: /etc/profile.d/custom.sh content: | export CUSTOM_VAR="value" ``` ## VM Network Configuration ### DHCP Configuration ```bash network: version: 2 ethernets: eth0: dhcp4: true dhcp6: true ``` ### Static IP Configuration ```bash network: version: 2 ethernets: eth0: addresses: - 192.168.1.100/24 gateway4: 192.168.1.1 nameservers: addresses: [8.8.8.8, 8.8.4.4] ``` ## Troubleshooting ### "Cloud-Init Configuration Not Applied" ```bash # Check cloud-init status in VM cloud-init status cloud-init status --long # View cloud-init logs tail /var/log/cloud-init.log ``` ### "SSH Keys Not Deployed" ```bash # Verify SSH key in cloud-config grep ssh_authorized_keys user-data.txt # Check permissions ls -la ~/.ssh/authorized_keys ``` ### "Network Not Configured" ```bash # Check network configuration ip addr show ip route show # View netplan (if used) cat /etc/netplan/*.yaml ``` ### "Packages Failed to Install" ```bash # Check cloud-init package log tail /var/log/cloud-init-output.log # Manual package installation apt-get update && apt-get install -y package-name ``` ## Related Documentation - **[install.func/](../install.func/)** - Container installation (similar workflow) - **[core.func/](../core.func/)** - Utility functions - **[error_handler.func/](../error_handler.func/)** - Error handling - **[UPDATED_APP-install.md](../../UPDATED_APP-install.md)** - Application setup guide - **Proxmox Docs**: https://pve.proxmox.com/wiki/Cloud-Init ## Recent Updates ### Version 2.0 (Dec 2025) - ✅ Enhanced cloud-init validation - ✅ Improved SSH key deployment - ✅ Better network configuration support - ✅ Added IPv6 support - ✅ Streamlined user and package setup --- **Last Updated**: December 2025 **Maintainers**: community-scripts team **License**: MIT ================================================ FILE: docs/misc/core.func/CORE_FLOWCHART.md ================================================ # core.func Execution Flowchart ## Main Execution Flow ``` ┌─────────────────────────────────────────────────────────────────────────────────┐ │ core.func Loading │ │ Entry point when core.func is sourced by other scripts │ └─────────────────────┬───────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────────────────────┐ │ Load Prevention Check │ │ • Check if _CORE_FUNC_LOADED is set │ │ • Return early if already loaded │ │ • Set _CORE_FUNC_LOADED=1 to prevent reloading │ └─────────────────────┬───────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────────────────────┐ │ LOAD_FUNCTIONS() │ │ Main function loader - sets up all core utilities │ └─────────────────────┬───────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────────────────────┐ │ Core Function Loading Sequence │ │ │ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────────────┐ │ │ │ color() │ │ formatting() │ │ icons() │ │ │ │ │ │ │ │ │ │ │ │ • Set ANSI │ │ • Set format │ │ • Set symbolic icons │ │ │ │ color codes │ │ helpers │ │ • Define message │ │ │ │ • Define │ │ • Tab, bold, │ │ symbols │ │ │ │ colors │ │ line reset │ │ • Status indicators │ │ │ └─────────────────┘ └─────────────────┘ └─────────────────────────────┘ │ │ │ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────────────┐ │ │ │ default_vars() │ │ set_std_mode() │ │ Additional Functions │ │ │ │ │ │ │ │ │ │ │ │ • Set retry │ │ • Set verbose │ │ • Add more functions │ │ │ │ variables │ │ mode │ │ as needed │ │ │ │ • Initialize │ │ • Configure │ │ │ │ │ │ counters │ │ STD variable │ │ │ │ │ └─────────────────┘ └─────────────────┘ └─────────────────────────────┘ │ └─────────────────────────────────────────────────────────────────────────────────┘ ``` ## System Check Functions Flow ``` ┌─────────────────────────────────────────────────────────────────────────────────┐ │ System Validation Flow │ │ │ │ ┌─────────────────────────────────────────────────────────────────────────────┐ │ │ │ PVE_CHECK() │ │ │ │ │ │ │ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────┐ │ │ │ │ │ Get PVE │ │ Check PVE │ │ Check PVE │ │ │ │ │ │ Version │ │ 8.x Support │ │ 9.x Support │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ • pveversion │ │ • Allow 8.0-8.9│ │ • Allow ONLY 9.0 │ │ │ │ │ │ • Parse version │ │ • Reject others │ │ • Reject 9.1+ │ │ │ │ │ │ • Extract │ │ • Exit if │ │ • Exit if │ │ │ │ │ │ major.minor │ │ unsupported │ │ unsupported │ │ │ │ │ └─────────────────┘ └─────────────────┘ └─────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────────────┘ │ │ │ │ ┌─────────────────────────────────────────────────────────────────────────────┐ │ │ │ ARCH_CHECK() │ │ │ │ │ │ │ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────┐ │ │ │ │ │ Check │ │ AMD64 Check │ │ PiMox Warning │ │ │ │ │ │ Architecture │ │ │ │ │ │ │ │ │ │ │ │ • dpkg --print- │ │ • Show PiMox │ │ │ │ │ │ • Get system │ │ architecture │ │ message │ │ │ │ │ │ architecture │ │ • Must be │ │ • Point to ARM64 │ │ │ │ │ │ • Compare with │ │ "amd64" │ │ support │ │ │ │ │ │ "amd64" │ │ • Exit if not │ │ • Exit script │ │ │ │ │ └─────────────────┘ └─────────────────┘ └─────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────────────┘ │ │ │ │ ┌─────────────────────────────────────────────────────────────────────────────┐ │ │ │ SHELL_CHECK() │ │ │ │ │ │ │ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────┐ │ │ │ │ │ Check │ │ Bash Check │ │ Error Handling │ │ │ │ │ │ Shell Type │ │ │ │ │ │ │ │ │ │ │ │ • ps -p $$ -o │ │ • Clear screen │ │ │ │ │ │ • Get current │ │ comm= │ │ • Show error │ │ │ │ │ │ shell │ │ • Must be │ │ • Sleep and exit │ │ │ │ │ │ • Compare with │ │ "bash" │ │ │ │ │ │ │ │ "bash" │ │ • Exit if not │ │ │ │ │ │ │ └─────────────────┘ └─────────────────┘ └─────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────────────┘ │ │ │ │ ┌─────────────────────────────────────────────────────────────────────────────┐ │ │ │ ROOT_CHECK() │ │ │ │ │ │ │ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────┐ │ │ │ │ │ Check │ │ Root Check │ │ Sudo Check │ │ │ │ │ │ User ID │ │ │ │ │ │ │ │ │ │ │ │ • id -u │ │ • Check parent │ │ │ │ │ │ • Get user ID │ │ • Must be 0 │ │ process │ │ │ │ │ │ • Check if │ │ • Exit if not │ │ • Detect sudo │ │ │ │ │ │ root (0) │ │ root │ │ usage │ │ │ │ │ └─────────────────┘ └─────────────────┘ └─────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────────────────────────┘ ``` ## Message System Flow ``` ┌─────────────────────────────────────────────────────────────────────────────────┐ │ Message System Flow │ │ │ │ ┌─────────────────────────────────────────────────────────────────────────────┐ │ │ │ MSG_INFO() │ │ │ │ │ │ │ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────┐ │ │ │ │ │ Message │ │ Duplicate │ │ Display Mode │ │ │ │ │ │ Validation │ │ Check │ │ Selection │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ • Check if │ │ • Track shown │ │ • Verbose mode: │ │ │ │ │ │ message │ │ messages │ │ Show directly │ │ │ │ │ │ exists │ │ • Skip if │ │ • Normal mode: │ │ │ │ │ │ • Return if │ │ already │ │ Start spinner │ │ │ │ │ │ empty │ │ shown │ │ │ │ │ │ │ └─────────────────┘ └─────────────────┘ └─────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────────────┘ │ │ │ │ ┌─────────────────────────────────────────────────────────────────────────────┐ │ │ │ SPINNER() │ │ │ │ │ │ │ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────┐ │ │ │ │ │ Spinner │ │ Animation │ │ Display │ │ │ │ │ │ Initialization│ │ Loop │ │ Control │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ • Define │ │ • Cycle through │ │ • Print spinner │ │ │ │ │ │ characters │ │ characters │ │ character │ │ │ │ │ │ • Set index │ │ • Sleep 0.1s │ │ • Print message │ │ │ │ │ │ • Start loop │ │ • Increment │ │ • Clear line │ │ │ │ │ │ │ │ index │ │ │ │ │ │ │ └─────────────────┘ └─────────────────┘ └─────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────────────┘ │ │ │ │ ┌─────────────────────────────────────────────────────────────────────────────┐ │ │ │ STOP_SPINNER() │ │ │ │ │ │ │ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────┐ │ │ │ │ │ Get Spinner │ │ Kill Process │ │ Cleanup │ │ │ │ │ │ PID │ │ │ │ │ │ │ │ │ │ │ │ • Send TERM │ │ • Remove PID file │ │ │ │ │ │ • From │ │ • Wait for │ │ • Unset variables │ │ │ │ │ │ SPINNER_PID │ │ termination │ │ • Reset terminal │ │ │ │ │ │ • From PID │ │ • Force kill │ │ settings │ │ │ │ │ │ file │ │ if needed │ │ │ │ │ │ │ └─────────────────┘ └─────────────────┘ └─────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────────────────────────┘ ``` ## Silent Execution Flow ``` ┌─────────────────────────────────────────────────────────────────────────────────┐ │ SILENT() Execution Flow │ │ │ │ ┌─────────────────────────────────────────────────────────────────────────────┐ │ │ │ Command Execution │ │ │ │ │ │ │ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────┐ │ │ │ │ │ Setup │ │ Execute │ │ Capture Output │ │ │ │ │ │ Environment │ │ Command │ │ │ │ │ │ │ │ │ │ │ │ • Redirect stdout │ │ │ │ │ │ • Disable │ │ • Run command │ │ to log file │ │ │ │ │ │ error │ │ • Capture │ │ • Redirect stderr │ │ │ │ │ │ handling │ │ return code │ │ to log file │ │ │ │ │ │ • Remove │ │ • Store exit │ │ • Log all output │ │ │ │ │ │ traps │ │ code │ │ │ │ │ │ │ └─────────────────┘ └─────────────────┘ └─────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────────────┘ │ │ │ │ ┌─────────────────────────────────────────────────────────────────────────────┐ │ │ │ Error Handling │ │ │ │ │ │ │ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────┐ │ │ │ │ │ Check Exit │ │ Load Error │ │ Display Error │ │ │ │ │ │ Code │ │ Handler │ │ Information │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ • If exit code │ │ • Source │ │ • Show error code │ │ │ │ │ │ != 0 │ │ error_handler │ │ • Show explanation │ │ │ │ │ │ • Proceed to │ │ if needed │ │ • Show command │ │ │ │ │ │ error │ │ • Get error │ │ • Show log lines │ │ │ │ │ │ handling │ │ explanation │ │ • Show full log │ │ │ │ │ │ │ │ │ │ command │ │ │ │ │ └─────────────────┘ └─────────────────┘ └─────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────────────┘ │ │ │ │ ┌─────────────────────────────────────────────────────────────────────────────┐ │ │ │ Log Management │ │ │ │ │ │ │ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────┐ │ │ │ │ │ Log File │ │ Log Display │ │ Log Access │ │ │ │ │ │ Management │ │ │ │ │ │ │ │ │ │ │ │ • Show last 10 │ │ • Provide command │ │ │ │ │ │ • Create log │ │ lines │ │ to view full log │ │ │ │ │ │ file path │ │ • Count total │ │ • Show line count │ │ │ │ │ │ • Use process │ │ lines │ │ • Enable debugging │ │ │ │ │ │ ID in name │ │ • Format │ │ │ │ │ │ │ │ │ │ output │ │ │ │ │ │ │ └─────────────────┘ └─────────────────┘ └─────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────────────────────────┘ ``` ## Header Management Flow ``` ┌─────────────────────────────────────────────────────────────────────────────────┐ │ Header Management Flow │ │ │ │ ┌─────────────────────────────────────────────────────────────────────────────┐ │ │ │ GET_HEADER() │ │ │ │ │ │ │ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────┐ │ │ │ │ │ Prepare │ │ Check Local │ │ Download Header │ │ │ │ │ │ Parameters │ │ File │ │ │ │ │ │ │ │ │ │ │ │ • Construct URL │ │ │ │ │ │ • Get app name │ │ • Check if │ │ • Download file │ │ │ │ │ │ from APP │ │ file exists │ │ • Save to local │ │ │ │ │ │ • Get app type │ │ • Check if │ │ path │ │ │ │ │ │ from APP_TYPE │ │ file has │ │ • Return success │ │ │ │ │ │ • Construct │ │ content │ │ status │ │ │ │ │ │ paths │ │ • Return if │ │ │ │ │ │ │ │ │ │ available │ │ │ │ │ │ │ └─────────────────┘ └─────────────────┘ └─────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────────────┘ │ │ │ │ ┌─────────────────────────────────────────────────────────────────────────────┐ │ │ │ HEADER_INFO() │ │ │ │ │ │ │ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────┐ │ │ │ │ │ Get Header │ │ Clear Screen │ │ Display Header │ │ │ │ │ │ Content │ │ │ │ │ │ │ │ │ │ │ │ • Clear │ │ • Show header │ │ │ │ │ │ • Call │ │ terminal │ │ content if │ │ │ │ │ │ get_header() │ │ • Get terminal │ │ available │ │ │ │ │ │ • Handle │ │ width │ │ • Format output │ │ │ │ │ │ errors │ │ • Set default │ │ • Center content │ │ │ │ │ │ • Return │ │ width if │ │ if possible │ │ │ │ │ │ content │ │ needed │ │ │ │ │ │ │ └─────────────────┘ └─────────────────┘ └─────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────────────────────────┘ ``` ## Swap Management Flow ``` ┌─────────────────────────────────────────────────────────────────────────────────┐ │ CHECK_OR_CREATE_SWAP() Flow │ │ │ │ ┌─────────────────────────────────────────────────────────────────────────────┐ │ │ │ Swap Detection │ │ │ │ │ │ │ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────┐ │ │ │ │ │ Check Active │ │ Swap Found │ │ No Swap Found │ │ │ │ │ │ Swap │ │ │ │ │ │ │ │ │ │ │ │ • Show success │ │ • Show error │ │ │ │ │ │ • Use swapon │ │ message │ │ message │ │ │ │ │ │ command │ │ • Return 0 │ │ • Ask user for │ │ │ │ │ │ • Check for │ │ │ │ creation │ │ │ │ │ │ swap devices │ │ │ │ • Proceed to │ │ │ │ │ │ • Return │ │ │ │ creation flow │ │ │ │ │ │ status │ │ │ │ │ │ │ │ │ └─────────────────┘ └─────────────────┘ └─────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────────────┘ │ │ │ │ ┌─────────────────────────────────────────────────────────────────────────────┐ │ │ │ Swap Creation │ │ │ │ │ │ │ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────┐ │ │ │ │ │ User Input │ │ Size │ │ File Creation │ │ │ │ │ │ Collection │ │ Validation │ │ │ │ │ │ │ │ │ │ │ │ • Create swap file │ │ │ │ │ │ • Ask for │ │ • Validate │ │ with dd │ │ │ │ │ │ confirmation │ │ numeric input │ │ • Set permissions │ │ │ │ │ │ • Convert to │ │ • Check range │ │ • Format swap │ │ │ │ │ │ lowercase │ │ • Abort if │ │ • Activate swap │ │ │ │ │ │ • Check for │ │ invalid │ │ • Show success │ │ │ │ │ │ y/yes │ │ │ │ message │ │ │ │ │ └─────────────────┘ └─────────────────┘ └─────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────────────────────────┘ ``` ## Integration Points ### With Other Scripts - **build.func**: Provides system checks and UI functions - **tools.func**: Uses core utilities for extended operations - **api.func**: Uses system checks and error handling - **error_handler.func**: Provides error explanations for silent execution ### External Dependencies - **curl**: For downloading header files - **tput**: For terminal control (installed if missing) - **swapon/mkswap**: For swap management - **pveversion**: For Proxmox version checking ### Data Flow - **Input**: Environment variables, command parameters - **Processing**: System validation, UI rendering, command execution - **Output**: Messages, log files, exit codes, system state changes ================================================ FILE: docs/misc/core.func/CORE_FUNCTIONS_REFERENCE.md ================================================ # core.func Functions Reference ## Overview This document provides a comprehensive alphabetical reference of all functions in `core.func`, including parameters, dependencies, usage examples, and error handling. ## Function Categories ### Initialization Functions #### `load_functions()` **Purpose**: Main function loader that initializes all core utilities **Parameters**: None **Returns**: None **Side Effects**: - Sets `__FUNCTIONS_LOADED=1` to prevent reloading - Calls all core function groups in sequence - Initializes color, formatting, icons, defaults, and standard mode **Dependencies**: None **Environment Variables Used**: `__FUNCTIONS_LOADED` **Usage Example**: ```bash # Automatically called when core.func is sourced source core.func # load_functions() is called automatically ``` ### Color and Formatting Functions #### `color()` **Purpose**: Set ANSI color codes for styled terminal output **Parameters**: None **Returns**: None **Side Effects**: Sets global color variables **Dependencies**: None **Environment Variables Used**: None **Sets Variables**: - `YW`: Yellow - `YWB`: Bright yellow - `BL`: Blue - `RD`: Red - `BGN`: Bright green - `GN`: Green - `DGN`: Dark green - `CL`: Clear/reset **Usage Example**: ```bash color echo -e "${GN}Success message${CL}" echo -e "${RD}Error message${CL}" ``` #### `color_spinner()` **Purpose**: Set color codes specifically for spinner output **Parameters**: None **Returns**: None **Side Effects**: Sets spinner-specific color variables **Dependencies**: None **Environment Variables Used**: None **Sets Variables**: - `CS_YW`: Yellow for spinner - `CS_YWB`: Bright yellow for spinner - `CS_CL`: Clear for spinner #### `formatting()` **Purpose**: Define formatting helpers for terminal output **Parameters**: None **Returns**: None **Side Effects**: Sets global formatting variables **Dependencies**: None **Environment Variables Used**: None **Sets Variables**: - `BFR`: Back and forward reset - `BOLD`: Bold text - `HOLD`: Space character - `TAB`: Two spaces - `TAB3`: Six spaces ### Icon Functions #### `icons()` **Purpose**: Set symbolic icons used throughout user feedback and prompts **Parameters**: None **Returns**: None **Side Effects**: Sets global icon variables **Dependencies**: `formatting()` (for TAB variable) **Environment Variables Used**: `TAB`, `CL` **Sets Variables**: - `CM`: Check mark - `CROSS`: Cross mark - `DNSOK`: DNS success - `DNSFAIL`: DNS failure - `INFO`: Information icon - `OS`: Operating system icon - `OSVERSION`: OS version icon - `CONTAINERTYPE`: Container type icon - `DISKSIZE`: Disk size icon - `CPUCORE`: CPU core icon - `RAMSIZE`: RAM size icon - `SEARCH`: Search icon - `VERBOSE_CROPPED`: Verbose mode icon - `VERIFYPW`: Password verification icon - `CONTAINERID`: Container ID icon - `HOSTNAME`: Hostname icon - `BRIDGE`: Bridge icon - `NETWORK`: Network icon - `GATEWAY`: Gateway icon - `DISABLEIPV6`: IPv6 disable icon - `DEFAULT`: Default settings icon - `MACADDRESS`: MAC address icon - `VLANTAG`: VLAN tag icon - `ROOTSSH`: SSH key icon - `CREATING`: Creating icon - `ADVANCED`: Advanced settings icon - `FUSE`: FUSE icon - `HOURGLASS`: Hourglass icon ### Default Variables Functions #### `default_vars()` **Purpose**: Set default retry and wait variables for system actions **Parameters**: None **Returns**: None **Side Effects**: Sets retry configuration variables **Dependencies**: None **Environment Variables Used**: None **Sets Variables**: - `RETRY_NUM`: Number of retry attempts (default: 10) - `RETRY_EVERY`: Seconds between retries (default: 3) - `i`: Retry counter initialized to RETRY_NUM #### `set_std_mode()` **Purpose**: Set default verbose mode for script execution **Parameters**: None **Returns**: None **Side Effects**: Sets STD variable based on VERBOSE setting **Dependencies**: None **Environment Variables Used**: `VERBOSE` **Sets Variables**: - `STD`: "silent" if VERBOSE != "yes", empty string if VERBOSE = "yes" ### Silent Execution Functions #### `silent()` **Purpose**: Execute commands silently with detailed error reporting **Parameters**: `$*` - Command and arguments to execute **Returns**: None (exits on error) **Side Effects**: - Executes command with output redirected to log file - On error, displays detailed error information - Exits with command's exit code **Dependencies**: `error_handler.func` (for error explanations) **Environment Variables Used**: `SILENT_LOGFILE` **Usage Example**: ```bash silent apt-get update silent apt-get install -y package-name ``` **Error Handling**: - Captures command output to `/tmp/silent.$$.log` - Shows error code explanation - Displays last 10 lines of log - Provides command to view full log ### System Check Functions #### `shell_check()` **Purpose**: Verify that the script is running in Bash shell **Parameters**: None **Returns**: None (exits if not Bash) **Side Effects**: - Checks current shell process - Exits with error message if not Bash **Dependencies**: None **Environment Variables Used**: None **Usage Example**: ```bash shell_check # Script continues if Bash, exits if not ``` #### `root_check()` **Purpose**: Ensure script is running as root user **Parameters**: None **Returns**: None (exits if not root) **Side Effects**: - Checks user ID and parent process - Exits with error message if not root **Dependencies**: None **Environment Variables Used**: None **Usage Example**: ```bash root_check # Script continues if root, exits if not ``` #### `pve_check()` **Purpose**: Verify Proxmox VE version compatibility **Parameters**: None **Returns**: None (exits if unsupported version) **Side Effects**: - Checks PVE version using pveversion command - Exits with error message if unsupported **Dependencies**: `pveversion` command **Environment Variables Used**: None **Supported Versions**: - Proxmox VE 8.0 - 8.9 - Proxmox VE 9.0 (only) **Usage Example**: ```bash pve_check # Script continues if supported version, exits if not ``` #### `arch_check()` **Purpose**: Verify system architecture is AMD64 **Parameters**: None **Returns**: None (exits if not AMD64) **Side Effects**: - Checks system architecture - Exits with PiMox warning if not AMD64 **Dependencies**: `dpkg` command **Environment Variables Used**: None **Usage Example**: ```bash arch_check # Script continues if AMD64, exits if not ``` #### `ssh_check()` **Purpose**: Detect and warn about external SSH usage **Parameters**: None **Returns**: None **Side Effects**: - Checks SSH_CLIENT environment variable - Warns if connecting from external IP - Allows local connections (127.0.0.1 or host IP) **Dependencies**: None **Environment Variables Used**: `SSH_CLIENT` **Usage Example**: ```bash ssh_check # Shows warning if external SSH, continues anyway ``` ### Header Management Functions #### `get_header()` **Purpose**: Download and cache application header files **Parameters**: None (uses APP and APP_TYPE variables) **Returns**: Header content on success, empty on failure **Side Effects**: - Downloads header from remote URL - Caches header locally - Creates directory structure if needed **Dependencies**: `curl` command **Environment Variables Used**: `APP`, `APP_TYPE` **Usage Example**: ```bash export APP="plex" export APP_TYPE="ct" header_content=$(get_header) ``` #### `header_info()` **Purpose**: Display application header information **Parameters**: None (uses APP variable) **Returns**: None **Side Effects**: - Clears screen - Displays header content - Gets terminal width for formatting **Dependencies**: `get_header()`, `tput` command **Environment Variables Used**: `APP` **Usage Example**: ```bash export APP="plex" header_info # Displays Plex header information ``` ### Utility Functions #### `ensure_tput()` **Purpose**: Ensure tput command is available for terminal control **Parameters**: None **Returns**: None **Side Effects**: - Installs ncurses package if tput missing - Works on Alpine and Debian-based systems **Dependencies**: `apk` or `apt-get` package managers **Environment Variables Used**: None **Usage Example**: ```bash ensure_tput # Installs ncurses if needed, continues if already available ``` #### `is_alpine()` **Purpose**: Detect if running on Alpine Linux **Parameters**: None **Returns**: 0 if Alpine, 1 if not Alpine **Side Effects**: None **Dependencies**: None **Environment Variables Used**: `var_os`, `PCT_OSTYPE` **Usage Example**: ```bash if is_alpine; then echo "Running on Alpine Linux" else echo "Not running on Alpine Linux" fi ``` #### `is_verbose_mode()` **Purpose**: Check if verbose mode is enabled **Parameters**: None **Returns**: 0 if verbose mode, 1 if not verbose **Side Effects**: None **Dependencies**: None **Environment Variables Used**: `VERBOSE`, `var_verbose` **Usage Example**: ```bash if is_verbose_mode; then echo "Verbose mode enabled" else echo "Verbose mode disabled" fi ``` #### `fatal()` **Purpose**: Display fatal error and terminate script **Parameters**: `$1` - Error message **Returns**: None (terminates script) **Side Effects**: - Displays error message - Sends INT signal to current process **Dependencies**: `msg_error()` **Environment Variables Used**: None **Usage Example**: ```bash fatal "Critical error occurred" # Script terminates after displaying error ``` ### Spinner Functions #### `spinner()` **Purpose**: Display animated spinner for progress indication **Parameters**: None (uses SPINNER_MSG variable) **Returns**: None (runs indefinitely) **Side Effects**: - Displays rotating spinner characters - Uses terminal control sequences **Dependencies**: `color_spinner()` **Environment Variables Used**: `SPINNER_MSG` **Usage Example**: ```bash SPINNER_MSG="Processing..." spinner & SPINNER_PID=$! # Spinner runs in background ``` #### `clear_line()` **Purpose**: Clear current terminal line **Parameters**: None **Returns**: None **Side Effects**: Clears current line using terminal control **Dependencies**: `tput` command **Environment Variables Used**: None #### `stop_spinner()` **Purpose**: Stop running spinner and cleanup **Parameters**: None **Returns**: None **Side Effects**: - Kills spinner process - Removes PID file - Resets terminal settings - Unsets spinner variables **Dependencies**: None **Environment Variables Used**: `SPINNER_PID`, `SPINNER_MSG` **Usage Example**: ```bash stop_spinner # Stops spinner and cleans up ``` ### Message Functions #### `msg_info()` **Purpose**: Display informational message with spinner **Parameters**: `$1` - Message text **Returns**: None **Side Effects**: - Starts spinner if not in verbose mode - Tracks shown messages to prevent duplicates - Displays message with hourglass icon in verbose mode **Dependencies**: `spinner()`, `is_verbose_mode()`, `is_alpine()` **Environment Variables Used**: `MSG_INFO_SHOWN` **Usage Example**: ```bash msg_info "Installing package..." # Shows spinner with message ``` #### `msg_ok()` **Purpose**: Display success message **Parameters**: `$1` - Success message text **Returns**: None **Side Effects**: - Stops spinner - Displays green checkmark with message - Removes message from shown tracking **Dependencies**: `stop_spinner()` **Environment Variables Used**: `MSG_INFO_SHOWN` **Usage Example**: ```bash msg_ok "Package installed successfully" # Shows green checkmark with message ``` #### `msg_error()` **Purpose**: Display error message **Parameters**: `$1` - Error message text **Returns**: None **Side Effects**: - Stops spinner - Displays red cross with message **Dependencies**: `stop_spinner()` **Environment Variables Used**: None **Usage Example**: ```bash msg_error "Installation failed" # Shows red cross with message ``` #### `msg_warn()` **Purpose**: Display warning message **Parameters**: `$1` - Warning message text **Returns**: None **Side Effects**: - Stops spinner - Displays yellow info icon with message **Dependencies**: `stop_spinner()` **Environment Variables Used**: None **Usage Example**: ```bash msg_warn "This operation may take some time" # Shows yellow info icon with message ``` #### `msg_custom()` **Purpose**: Display custom message with specified symbol and color **Parameters**: - `$1` - Custom symbol (default: "[*]") - `$2` - Color code (default: "\e[36m") - `$3` - Message text **Returns**: None **Side Effects**: - Stops spinner - Displays custom formatted message **Dependencies**: `stop_spinner()` **Environment Variables Used**: None **Usage Example**: ```bash msg_custom "⚡" "\e[33m" "Custom warning message" # Shows custom symbol and color with message ``` #### `msg_debug()` **Purpose**: Display debug message if debug mode enabled **Parameters**: `$*` - Debug message text **Returns**: None **Side Effects**: - Only displays if var_full_verbose is set - Shows timestamp and debug prefix **Dependencies**: None **Environment Variables Used**: `var_full_verbose`, `var_verbose` **Usage Example**: ```bash export var_full_verbose=1 msg_debug "Debug information here" # Shows debug message with timestamp ``` ### System Management Functions #### `check_or_create_swap()` **Purpose**: Check for active swap and optionally create swap file **Parameters**: None **Returns**: 0 if swap exists or created, 1 if skipped **Side Effects**: - Checks for active swap - Prompts user to create swap if none found - Creates swap file if user confirms **Dependencies**: `swapon`, `dd`, `mkswap` commands **Environment Variables Used**: None **Usage Example**: ```bash if check_or_create_swap; then echo "Swap is available" else echo "No swap available" fi ``` ## Function Call Hierarchy ### Initialization Flow ``` load_functions() ├── color() ├── formatting() ├── icons() ├── default_vars() └── set_std_mode() ``` ### Message System Flow ``` msg_info() ├── is_verbose_mode() ├── is_alpine() ├── spinner() └── color_spinner() msg_ok() ├── stop_spinner() └── clear_line() msg_error() └── stop_spinner() msg_warn() └── stop_spinner() ``` ### System Check Flow ``` pve_check() ├── pveversion command └── version parsing arch_check() ├── dpkg command └── architecture check shell_check() ├── ps command └── shell detection root_check() ├── id command └── parent process check ``` ### Silent Execution Flow ``` silent() ├── Command execution ├── Output redirection ├── Error handling ├── error_handler.func loading └── Log management ``` ## Error Handling Patterns ### System Check Errors - All system check functions exit with appropriate error messages - Clear indication of what's wrong and how to fix it - Graceful exit with sleep delay for user to read message ### Silent Execution Errors - Commands executed via `silent()` capture output to log file - On failure, displays error code explanation - Shows last 10 lines of log output - Provides command to view full log ### Spinner Errors - Spinner functions handle process cleanup on exit - Trap handlers ensure spinners are stopped - Terminal settings are restored on error ## Environment Variable Dependencies ### Required Variables - `APP`: Application name for header display - `APP_TYPE`: Application type (ct/vm) for header paths - `VERBOSE`: Verbose mode setting ### Optional Variables - `var_os`: OS type for Alpine detection - `PCT_OSTYPE`: Alternative OS type variable - `var_verbose`: Alternative verbose setting - `var_full_verbose`: Debug mode setting ### Internal Variables - `_CORE_FUNC_LOADED`: Prevents multiple loading - `__FUNCTIONS_LOADED`: Prevents multiple function loading - `SILENT_LOGFILE`: Silent execution log file path - `SPINNER_PID`: Spinner process ID - `SPINNER_MSG`: Spinner message text - `MSG_INFO_SHOWN`: Tracks shown info messages ================================================ FILE: docs/misc/core.func/CORE_INTEGRATION.md ================================================ # core.func Integration Guide ## Overview This document describes how `core.func` integrates with other components in the Proxmox Community Scripts project, including dependencies, data flow, and API surface. ## Dependencies ### External Dependencies #### Required Commands - **`pveversion`**: Proxmox VE version checking - **`dpkg`**: Architecture detection - **`ps`**: Process and shell detection - **`id`**: User ID checking - **`curl`**: Header file downloading - **`swapon`**: Swap status checking - **`dd`**: Swap file creation - **`mkswap`**: Swap file formatting #### Optional Commands - **`tput`**: Terminal control (installed if missing) - **`apk`**: Alpine package manager - **`apt-get`**: Debian package manager ### Internal Dependencies #### error_handler.func - **Purpose**: Provides error code explanations for silent execution - **Usage**: Automatically loaded when `silent()` encounters errors - **Integration**: Called via `explain_exit_code()` function - **Data Flow**: Error code → explanation → user display ## Integration Points ### With build.func #### System Validation ```bash # build.func uses core.func for system checks source core.func pve_check arch_check shell_check root_check ``` #### User Interface ```bash # build.func uses core.func for UI elements msg_info "Creating container..." msg_ok "Container created successfully" msg_error "Container creation failed" ``` #### Silent Execution ```bash # build.func uses core.func for command execution silent pct create "$CTID" "$TEMPLATE" \ --hostname "$HOSTNAME" \ --memory "$MEMORY" \ --cores "$CORES" ``` ### With tools.func #### Utility Functions ```bash # tools.func uses core.func utilities source core.func # System checks pve_check root_check # UI elements msg_info "Running maintenance tasks..." msg_ok "Maintenance completed" ``` #### Error Handling ```bash # tools.func uses core.func for error handling if silent systemctl restart service; then msg_ok "Service restarted" else msg_error "Service restart failed" fi ``` ### With api.func #### System Validation ```bash # api.func uses core.func for system checks source core.func pve_check root_check ``` #### API Operations ```bash # api.func uses core.func for API calls msg_info "Connecting to Proxmox API..." if silent curl -k -H "Authorization: PVEAPIToken=$API_TOKEN" \ "$API_URL/api2/json/nodes/$NODE/lxc"; then msg_ok "API connection successful" else msg_error "API connection failed" fi ``` ### With error_handler.func #### Error Explanations ```bash # error_handler.func provides explanations for core.func explain_exit_code() { local code="$1" case "$code" in 1) echo "General error" ;; 2) echo "Misuse of shell builtins" ;; 126) echo "Command invoked cannot execute" ;; 127) echo "Command not found" ;; 128) echo "Invalid argument to exit" ;; *) echo "Unknown error code" ;; esac } ``` ### With install.func #### Installation Process ```bash # install.func uses core.func for installation source core.func # System checks pve_check root_check # Installation steps msg_info "Installing packages..." silent apt-get update silent apt-get install -y package msg_ok "Installation completed" ``` ### With alpine-install.func #### Alpine-Specific Operations ```bash # alpine-install.func uses core.func for Alpine operations source core.func # Alpine detection if is_alpine; then msg_info "Detected Alpine Linux" silent apk add --no-cache package else msg_info "Detected Debian-based system" silent apt-get install -y package fi ``` ### With alpine-tools.func #### Alpine Utilities ```bash # alpine-tools.func uses core.func for Alpine tools source core.func # Alpine-specific operations if is_alpine; then msg_info "Running Alpine-specific operations..." # Alpine tools logic msg_ok "Alpine operations completed" fi ``` ### With passthrough.func #### Hardware Passthrough ```bash # passthrough.func uses core.func for hardware operations source core.func # System checks pve_check root_check # Hardware operations msg_info "Configuring GPU passthrough..." if silent lspci | grep -i nvidia; then msg_ok "NVIDIA GPU detected" else msg_warn "No NVIDIA GPU found" fi ``` ### With vm-core.func #### VM Operations ```bash # vm-core.func uses core.func for VM management source core.func # System checks pve_check root_check # VM operations msg_info "Creating virtual machine..." silent qm create "$VMID" \ --name "$VMNAME" \ --memory "$MEMORY" \ --cores "$CORES" msg_ok "Virtual machine created" ``` ## Data Flow ### Input Data #### Environment Variables - **`APP`**: Application name for header display - **`APP_TYPE`**: Application type (ct/vm) for header paths - **`VERBOSE`**: Verbose mode setting - **`var_os`**: OS type for Alpine detection - **`PCT_OSTYPE`**: Alternative OS type variable - **`var_verbose`**: Alternative verbose setting - **`var_full_verbose`**: Debug mode setting #### Command Parameters - **Function arguments**: Passed to individual functions - **Command arguments**: Passed to `silent()` function - **User input**: Collected via `read` commands ### Processing Data #### System Information - **Proxmox version**: Parsed from `pveversion` output - **Architecture**: Retrieved from `dpkg --print-architecture` - **Shell type**: Detected from process information - **User ID**: Retrieved from `id -u` - **SSH connection**: Detected from `SSH_CLIENT` environment #### UI State - **Message tracking**: `MSG_INFO_SHOWN` associative array - **Spinner state**: `SPINNER_PID` and `SPINNER_MSG` variables - **Terminal state**: Cursor position and display mode #### Error Information - **Exit codes**: Captured from command execution - **Log output**: Redirected to temporary log files - **Error explanations**: Retrieved from error_handler.func ### Output Data #### User Interface - **Colored messages**: ANSI color codes for terminal output - **Icons**: Symbolic representations for different message types - **Spinners**: Animated progress indicators - **Formatted text**: Consistent message formatting #### System State - **Exit codes**: Returned from functions - **Log files**: Created for silent execution - **Configuration**: Modified system settings - **Process state**: Spinner processes and cleanup ## API Surface ### Public Functions #### System Validation - **`pve_check()`**: Proxmox VE version validation - **`arch_check()`**: Architecture validation - **`shell_check()`**: Shell validation - **`root_check()`**: Privilege validation - **`ssh_check()`**: SSH connection warning #### User Interface - **`msg_info()`**: Informational messages - **`msg_ok()`**: Success messages - **`msg_error()`**: Error messages - **`msg_warn()`**: Warning messages - **`msg_custom()`**: Custom messages - **`msg_debug()`**: Debug messages #### Spinner Control - **`spinner()`**: Start spinner animation - **`stop_spinner()`**: Stop spinner and cleanup - **`clear_line()`**: Clear current terminal line #### Silent Execution - **`silent()`**: Execute commands with error handling #### Utility Functions - **`is_alpine()`**: Alpine Linux detection - **`is_verbose_mode()`**: Verbose mode detection - **`fatal()`**: Fatal error handling - **`ensure_tput()`**: Terminal control setup #### Header Management - **`get_header()`**: Download application headers - **`header_info()`**: Display header information #### System Management - **`check_or_create_swap()`**: Swap file management ### Internal Functions #### Initialization - **`load_functions()`**: Function loader - **`color()`**: Color setup - **`formatting()`**: Formatting setup - **`icons()`**: Icon setup - **`default_vars()`**: Default variables - **`set_std_mode()`**: Standard mode setup #### Color Management - **`color_spinner()`**: Spinner colors ### Global Variables #### Color Variables - **`YW`**, **`YWB`**, **`BL`**, **`RD`**, **`BGN`**, **`GN`**, **`DGN`**, **`CL`**: Color codes - **`CS_YW`**, **`CS_YWB`**, **`CS_CL`**: Spinner colors #### Formatting Variables - **`BFR`**, **`BOLD`**, **`HOLD`**, **`TAB`**, **`TAB3`**: Formatting helpers #### Icon Variables - **`CM`**, **`CROSS`**, **`INFO`**, **`OS`**, **`OSVERSION`**, etc.: Message icons #### Configuration Variables - **`RETRY_NUM`**, **`RETRY_EVERY`**: Retry settings - **`STD`**: Standard mode setting - **`SILENT_LOGFILE`**: Log file path #### State Variables - **`_CORE_FUNC_LOADED`**: Loading prevention - **`__FUNCTIONS_LOADED`**: Function loading prevention - **`SPINNER_PID`**, **`SPINNER_MSG`**: Spinner state - **`MSG_INFO_SHOWN`**: Message tracking ## Integration Patterns ### Standard Integration Pattern ```bash #!/usr/bin/env bash # Standard integration pattern # 1. Source core.func first source core.func # 2. Run system checks pve_check arch_check shell_check root_check # 3. Set up error handling trap 'stop_spinner' EXIT INT TERM # 4. Use UI functions msg_info "Starting operation..." # 5. Use silent execution silent command # 6. Show completion msg_ok "Operation completed" ``` ### Minimal Integration Pattern ```bash #!/usr/bin/env bash # Minimal integration pattern source core.func pve_check root_check msg_info "Running operation..." silent command msg_ok "Operation completed" ``` ### Advanced Integration Pattern ```bash #!/usr/bin/env bash # Advanced integration pattern source core.func # System validation pve_check arch_check shell_check root_check ssh_check # Error handling trap 'stop_spinner' EXIT INT TERM # Verbose mode handling if is_verbose_mode; then msg_info "Verbose mode enabled" fi # OS-specific operations if is_alpine; then msg_info "Alpine Linux detected" # Alpine-specific logic else msg_info "Debian-based system detected" # Debian-specific logic fi # Operation execution msg_info "Starting operation..." if silent command; then msg_ok "Operation succeeded" else msg_error "Operation failed" exit 1 fi ``` ## Error Handling Integration ### Silent Execution Error Flow ``` silent() command ├── Execute command ├── Capture output to log ├── Check exit code ├── If error: │ ├── Load error_handler.func │ ├── Get error explanation │ ├── Display error details │ ├── Show log excerpt │ └── Exit with error code └── If success: Continue ``` ### System Check Error Flow ``` System Check Function ├── Check system state ├── If valid: Return 0 └── If invalid: ├── Display error message ├── Show fix instructions ├── Sleep for user to read └── Exit with error code ``` ## Performance Considerations ### Loading Optimization - **Single Loading**: `_CORE_FUNC_LOADED` prevents multiple loading - **Function Loading**: `__FUNCTIONS_LOADED` prevents multiple function loading - **Lazy Loading**: Functions loaded only when needed ### Memory Usage - **Minimal Footprint**: Core functions use minimal memory - **Variable Reuse**: Global variables reused across functions - **Cleanup**: Spinner processes cleaned up on exit ### Execution Speed - **Fast Checks**: System checks are optimized for speed - **Efficient Spinners**: Spinner animation uses minimal CPU - **Quick Messages**: Message functions optimized for performance ## Security Considerations ### Privilege Escalation - **Root Check**: Ensures script runs with sufficient privileges - **Shell Check**: Validates shell environment - **Process Validation**: Checks parent process for sudo usage ### Input Validation - **Parameter Checking**: Functions validate input parameters - **Error Handling**: Proper error handling prevents crashes - **Safe Execution**: Silent execution with proper error handling ### System Protection - **Version Validation**: Ensures compatible Proxmox version - **Architecture Check**: Prevents execution on unsupported systems - **SSH Warning**: Warns about external SSH usage ## Future Integration Considerations ### Extensibility - **Function Groups**: Easy to add new function groups - **Message Types**: Easy to add new message types - **System Checks**: Easy to add new system checks ### Compatibility - **Version Support**: Easy to add new Proxmox versions - **OS Support**: Easy to add new operating systems - **Architecture Support**: Easy to add new architectures ### Performance - **Optimization**: Functions can be optimized for better performance - **Caching**: Results can be cached for repeated operations - **Parallelization**: Operations can be parallelized where appropriate ================================================ FILE: docs/misc/core.func/CORE_USAGE_EXAMPLES.md ================================================ # core.func Usage Examples ## Overview This document provides practical usage examples for `core.func` functions, covering common scenarios, integration patterns, and best practices. ## Basic Script Setup ### Standard Script Initialization ```bash #!/usr/bin/env bash # Standard script setup using core.func # Source core functions source core.func # Run system checks pve_check arch_check shell_check root_check # Optional: Check SSH connection ssh_check # Set up error handling trap 'stop_spinner' EXIT INT TERM # Your script logic here msg_info "Starting script execution" # ... script code ... msg_ok "Script completed successfully" ``` ### Minimal Script Setup ```bash #!/usr/bin/env bash # Minimal setup for simple scripts source core.func # Basic checks only pve_check root_check # Simple execution msg_info "Running operation" # ... your code ... msg_ok "Operation completed" ``` ## Message Display Examples ### Progress Indication ```bash #!/usr/bin/env bash source core.func # Show progress with spinner msg_info "Downloading package..." sleep 2 msg_ok "Download completed" msg_info "Installing package..." sleep 3 msg_ok "Installation completed" msg_info "Configuring service..." sleep 1 msg_ok "Configuration completed" ``` ### Error Handling ```bash #!/usr/bin/env bash source core.func # Function with error handling install_package() { local package="$1" msg_info "Installing $package..." if silent apt-get install -y "$package"; then msg_ok "$package installed successfully" return 0 else msg_error "Failed to install $package" return 1 fi } # Usage if install_package "nginx"; then msg_ok "Nginx installation completed" else msg_error "Nginx installation failed" exit 1 fi ``` ### Warning Messages ```bash #!/usr/bin/env bash source core.func # Show warnings for potentially dangerous operations msg_warn "This will modify system configuration" read -p "Continue? [y/N]: " confirm if [[ "$confirm" =~ ^[yY]$ ]]; then msg_info "Proceeding with modification..." # ... dangerous operation ... msg_ok "Modification completed" else msg_info "Operation cancelled" fi ``` ### Custom Messages ```bash #!/usr/bin/env bash source core.func # Custom message with specific icon and color msg_custom "🚀" "\e[32m" "Launching application" msg_custom "⚡" "\e[33m" "High performance mode enabled" msg_custom "🔒" "\e[31m" "Security mode activated" ``` ### Debug Messages ```bash #!/usr/bin/env bash source core.func # Enable debug mode export var_full_verbose=1 # Debug messages msg_debug "Variable value: $some_variable" msg_debug "Function called: $FUNCNAME" msg_debug "Current directory: $(pwd)" ``` ## Silent Execution Examples ### Package Management ```bash #!/usr/bin/env bash source core.func # Update package lists msg_info "Updating package lists..." silent apt-get update # Install packages msg_info "Installing required packages..." silent apt-get install -y curl wget git # Upgrade packages msg_info "Upgrading packages..." silent apt-get upgrade -y msg_ok "Package management completed" ``` ### File Operations ```bash #!/usr/bin/env bash source core.func # Create directories msg_info "Creating directory structure..." silent mkdir -p /opt/myapp/{config,logs,data} # Set permissions msg_info "Setting permissions..." silent chmod 755 /opt/myapp silent chmod 644 /opt/myapp/config/* # Copy files msg_info "Copying configuration files..." silent cp config/* /opt/myapp/config/ msg_ok "File operations completed" ``` ### Service Management ```bash #!/usr/bin/env bash source core.func # Start service msg_info "Starting service..." silent systemctl start myservice # Enable service msg_info "Enabling service..." silent systemctl enable myservice # Check service status msg_info "Checking service status..." if silent systemctl is-active --quiet myservice; then msg_ok "Service is running" else msg_error "Service failed to start" fi ``` ### Network Operations ```bash #!/usr/bin/env bash source core.func # Test network connectivity msg_info "Testing network connectivity..." if silent ping -c 1 8.8.8.8; then msg_ok "Network connectivity confirmed" else msg_error "Network connectivity failed" fi # Download files msg_info "Downloading configuration..." silent curl -fsSL https://example.com/config -o /tmp/config # Extract archives msg_info "Extracting archive..." silent tar -xzf /tmp/archive.tar.gz -C /opt/ ``` ## System Check Examples ### Comprehensive System Validation ```bash #!/usr/bin/env bash source core.func # Complete system validation validate_system() { msg_info "Validating system requirements..." # Check Proxmox version if pve_check; then msg_ok "Proxmox VE version is supported" fi # Check architecture if arch_check; then msg_ok "System architecture is supported" fi # Check shell if shell_check; then msg_ok "Shell environment is correct" fi # Check privileges if root_check; then msg_ok "Running with sufficient privileges" fi # Check SSH connection ssh_check msg_ok "System validation completed" } # Run validation validate_system ``` ### Conditional System Checks ```bash #!/usr/bin/env bash source core.func # Check if running in container if [[ -f /.dockerenv ]] || [[ -f /run/.containerenv ]]; then msg_warn "Running inside container" # Skip some checks else # Full system checks pve_check arch_check fi # Always check shell and privileges shell_check root_check ``` ## Header Management Examples ### Application Header Display ```bash #!/usr/bin/env bash source core.func # Set application information export APP="plex" export APP_TYPE="ct" # Display header header_info # Continue with application setup msg_info "Setting up Plex Media Server..." ``` ### Custom Header Handling ```bash #!/usr/bin/env bash source core.func # Get header content export APP="nextcloud" export APP_TYPE="ct" header_content=$(get_header) if [[ -n "$header_content" ]]; then echo "Header found:" echo "$header_content" else msg_warn "No header found for $APP" fi ``` ## Swap Management Examples ### Interactive Swap Creation ```bash #!/usr/bin/env bash source core.func # Check and create swap if check_or_create_swap; then msg_ok "Swap is available" else msg_warn "No swap available - continuing without swap" fi ``` ### Automated Swap Check ```bash #!/usr/bin/env bash source core.func # Check swap without prompting check_swap_quiet() { if swapon --noheadings --show | grep -q 'swap'; then msg_ok "Swap is active" return 0 else msg_warn "No active swap detected" return 1 fi } if check_swap_quiet; then msg_info "System has sufficient swap" else msg_warn "Consider adding swap for better performance" fi ``` ## Spinner Usage Examples ### Long-Running Operations ```bash #!/usr/bin/env bash source core.func # Long-running operation with spinner long_operation() { msg_info "Processing large dataset..." # Simulate long operation for i in {1..100}; do sleep 0.1 # Update spinner message periodically if (( i % 20 == 0 )); then SPINNER_MSG="Processing... $i%" fi done msg_ok "Dataset processing completed" } long_operation ``` ### Background Operations ```bash #!/usr/bin/env bash source core.func # Background operation with spinner background_operation() { msg_info "Starting background process..." # Start spinner SPINNER_MSG="Processing in background..." spinner & SPINNER_PID=$! # Do background work sleep 5 # Stop spinner stop_spinner msg_ok "Background process completed" } background_operation ``` ## Integration Examples ### With build.func ```bash #!/usr/bin/env bash # Integration with build.func source core.func source build.func # Use core functions for system validation pve_check arch_check root_check # Use build.func for container creation export APP="plex" export CTID="100" # ... container creation ... ``` ### With tools.func ```bash #!/usr/bin/env bash # Integration with tools.func source core.func source tools.func # Use core functions for UI msg_info "Starting maintenance tasks..." # Use tools.func for maintenance update_system cleanup_logs optimize_storage msg_ok "Maintenance completed" ``` ### With error_handler.func ```bash #!/usr/bin/env bash # Integration with error_handler.func source core.func source error_handler.func # Use core functions for execution msg_info "Running operation..." # Silent execution will use error_handler for explanations silent apt-get install -y package msg_ok "Operation completed" ``` ## Best Practices Examples ### Error Handling Pattern ```bash #!/usr/bin/env bash source core.func # Robust error handling run_with_error_handling() { local operation="$1" local description="$2" msg_info "$description" if silent "$operation"; then msg_ok "$description completed successfully" return 0 else msg_error "$description failed" return 1 fi } # Usage run_with_error_handling "apt-get update" "Package list update" run_with_error_handling "apt-get install -y nginx" "Nginx installation" ``` ### Verbose Mode Handling ```bash #!/usr/bin/env bash source core.func # Handle verbose mode if is_verbose_mode; then msg_info "Verbose mode enabled - showing detailed output" # Show more information else msg_info "Normal mode - showing minimal output" # Show less information fi ``` ### Alpine Linux Detection ```bash #!/usr/bin/env bash source core.func # Handle different OS types if is_alpine; then msg_info "Detected Alpine Linux" # Use Alpine-specific commands silent apk add --no-cache package else msg_info "Detected Debian-based system" # Use Debian-specific commands silent apt-get install -y package fi ``` ### Conditional Execution ```bash #!/usr/bin/env bash source core.func # Conditional execution based on system state if [[ -f /etc/nginx/nginx.conf ]]; then msg_warn "Nginx configuration already exists" read -p "Overwrite? [y/N]: " overwrite if [[ "$overwrite" =~ ^[yY]$ ]]; then msg_info "Overwriting configuration..." # ... overwrite logic ... else msg_info "Skipping configuration" fi else msg_info "Creating new Nginx configuration..." # ... create logic ... fi ``` ## Advanced Usage Examples ### Custom Spinner Messages ```bash #!/usr/bin/env bash source core.func # Custom spinner with progress download_with_progress() { local url="$1" local file="$2" msg_info "Starting download..." # Start spinner SPINNER_MSG="Downloading..." spinner & SPINNER_PID=$! # Download with progress curl -L "$url" -o "$file" --progress-bar # Stop spinner stop_spinner msg_ok "Download completed" } download_with_progress "https://example.com/file.tar.gz" "/tmp/file.tar.gz" ``` ### Message Deduplication ```bash #!/usr/bin/env bash source core.func # Messages are automatically deduplicated for i in {1..5}; do msg_info "Processing item $i" # This message will only show once done # Different messages will show separately msg_info "Starting phase 1" msg_info "Starting phase 2" msg_info "Starting phase 3" ``` ### Terminal Control ```bash #!/usr/bin/env bash source core.func # Ensure terminal control is available ensure_tput # Use terminal control clear_line echo "This line will be cleared" clear_line echo "New content" ``` ## Troubleshooting Examples ### Debug Mode ```bash #!/usr/bin/env bash source core.func # Enable debug mode export var_full_verbose=1 export VERBOSE="yes" # Debug information msg_debug "Script started" msg_debug "Current user: $(whoami)" msg_debug "Current directory: $(pwd)" msg_debug "Environment variables: $(env | grep -E '^(APP|CTID|VERBOSE)')" ``` ### Silent Execution Debugging ```bash #!/usr/bin/env bash source core.func # Debug silent execution debug_silent() { local cmd="$1" local log_file="/tmp/debug.$$.log" echo "Command: $cmd" > "$log_file" echo "Timestamp: $(date)" >> "$log_file" echo "Working directory: $(pwd)" >> "$log_file" echo "Environment:" >> "$log_file" env >> "$log_file" echo "--- Command Output ---" >> "$log_file" if silent "$cmd"; then msg_ok "Command succeeded" else msg_error "Command failed - check $log_file for details" fi } debug_silent "apt-get update" ``` ### Error Recovery ```bash #!/usr/bin/env bash source core.func # Error recovery pattern retry_operation() { local max_attempts=3 local attempt=1 while [[ $attempt -le $max_attempts ]]; do msg_info "Attempt $attempt of $max_attempts" if silent "$@"; then msg_ok "Operation succeeded on attempt $attempt" return 0 else msg_warn "Attempt $attempt failed" ((attempt++)) if [[ $attempt -le $max_attempts ]]; then msg_info "Retrying in 5 seconds..." sleep 5 fi fi done msg_error "Operation failed after $max_attempts attempts" return 1 } # Usage retry_operation "apt-get install -y package" ``` ================================================ FILE: docs/misc/core.func/README.md ================================================ # core.func Documentation ## Overview The `core.func` file provides fundamental utility functions and system checks that form the foundation for all other scripts in the Proxmox Community Scripts project. It handles basic system operations, user interface elements, validation, and core infrastructure. ## Purpose and Use Cases - **System Validation**: Checks for Proxmox VE compatibility, architecture, shell requirements - **User Interface**: Provides colored output, icons, spinners, and formatted messages - **Core Utilities**: Basic functions used across all scripts - **Error Handling**: Silent execution with detailed error reporting - **System Information**: OS detection, verbose mode handling, swap management ## Quick Reference ### Key Function Groups - **System Checks**: `pve_check()`, `arch_check()`, `shell_check()`, `root_check()` - **User Interface**: `msg_info()`, `msg_ok()`, `msg_error()`, `msg_warn()`, `spinner()` - **Core Utilities**: `silent()`, `is_alpine()`, `is_verbose_mode()`, `get_header()` - **System Management**: `check_or_create_swap()`, `ensure_tput()` ### Dependencies - **External**: `curl` for downloading headers, `tput` for terminal control - **Internal**: `error_handler.func` for error explanations ### Integration Points - Used by: All other `.func` files and installation scripts - Uses: `error_handler.func` for error explanations - Provides: Core utilities for `build.func`, `tools.func`, `api.func` ## Documentation Files ### 📊 [CORE_FLOWCHART.md](./CORE_FLOWCHART.md) Visual execution flows showing how core functions interact and the system validation process. ### 📚 [CORE_FUNCTIONS_REFERENCE.md](./CORE_FUNCTIONS_REFERENCE.md) Complete alphabetical reference of all functions with parameters, dependencies, and usage details. ### 💡 [CORE_USAGE_EXAMPLES.md](./CORE_USAGE_EXAMPLES.md) Practical examples showing how to use core functions in scripts and common patterns. ### 🔗 [CORE_INTEGRATION.md](./CORE_INTEGRATION.md) How core.func integrates with other components and provides foundational services. ## Key Features ### System Validation - **Proxmox VE Version Check**: Supports PVE 8.0-8.9 and 9.0 - **Architecture Check**: Ensures AMD64 architecture (excludes PiMox) - **Shell Check**: Validates Bash shell usage - **Root Check**: Ensures root privileges - **SSH Check**: Warns about external SSH usage ### User Interface - **Colored Output**: ANSI color codes for styled terminal output - **Icons**: Symbolic icons for different message types - **Spinners**: Animated progress indicators - **Formatted Messages**: Consistent message formatting across scripts ### Core Utilities - **Silent Execution**: Execute commands with detailed error reporting - **OS Detection**: Alpine Linux detection - **Verbose Mode**: Handle verbose output settings - **Header Management**: Download and display application headers - **Swap Management**: Check and create swap files ## Common Usage Patterns ### Basic Script Setup ```bash # Source core functions source core.func # Run system checks pve_check arch_check shell_check root_check ``` ### Message Display ```bash # Show progress msg_info "Installing package..." # Show success msg_ok "Package installed successfully" # Show error msg_error "Installation failed" # Show warning msg_warn "This operation may take some time" ``` ### Silent Command Execution ```bash # Execute command silently with error handling silent apt-get update silent apt-get install -y package-name ``` ## Environment Variables ### Core Variables - `VERBOSE`: Enable verbose output mode - `SILENT_LOGFILE`: Path to silent execution log file - `APP`: Application name for header display - `APP_TYPE`: Application type (ct/vm) for header paths ### Internal Variables - `_CORE_FUNC_LOADED`: Prevents multiple loading - `__FUNCTIONS_LOADED`: Prevents multiple function loading - `RETRY_NUM`: Number of retry attempts (default: 10) - `RETRY_EVERY`: Seconds between retries (default: 3) ## Error Handling ### Silent Execution Errors - Commands executed via `silent()` capture output to log file - On failure, displays error code explanation - Shows last 10 lines of log output - Provides command to view full log ### System Check Failures - Each system check function exits with appropriate error message - Clear indication of what's wrong and how to fix it - Graceful exit with sleep delay for user to read message ## Best Practices ### Script Initialization 1. Source `core.func` first 2. Run system checks early 3. Set up error handling 4. Use appropriate message functions ### Message Usage 1. Use `msg_info()` for progress updates 2. Use `msg_ok()` for successful completions 3. Use `msg_error()` for failures 4. Use `msg_warn()` for warnings ### Silent Execution 1. Use `silent()` for commands that might fail 2. Check return codes after silent execution 3. Provide meaningful error messages ## Troubleshooting ### Common Issues 1. **Proxmox Version**: Ensure running supported PVE version 2. **Architecture**: Script only works on AMD64 systems 3. **Shell**: Must use Bash shell 4. **Permissions**: Must run as root 5. **Network**: SSH warnings for external connections ### Debug Mode Enable verbose output for debugging: ```bash export VERBOSE="yes" source core.func ``` ### Log Files Check silent execution logs: ```bash cat /tmp/silent.$$.log ``` ## Related Documentation - [build.func](../build.func/) - Main container creation script - [error_handler.func](../error_handler.func/) - Error handling utilities - [tools.func](../tools.func/) - Extended utility functions - [api.func](../api.func/) - Proxmox API interactions --- *This documentation covers the core.func file which provides fundamental utilities for all Proxmox Community Scripts.* ================================================ FILE: docs/misc/error_handler.func/ERROR_HANDLER_FLOWCHART.md ================================================ # error_handler.func Execution Flowchart ## Main Error Handling Flow ``` ┌─────────────────────────────────────────────────────────────────────────────────┐ │ Error Handler Initialization │ │ Entry point when error_handler.func is sourced by other scripts │ └─────────────────────┬───────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────────────────────┐ │ CATCH_ERRORS() │ │ Initialize error handling traps and strict mode │ └─────────────────────┬───────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────────────────────┐ │ Trap Setup Sequence │ │ │ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────────────┐ │ │ │ Set Strict │ │ Set Error │ │ Set Signal │ │ │ │ Mode │ │ Trap │ │ Traps │ │ │ │ │ │ │ │ │ │ │ │ • -Ee │ │ • ERR trap │ │ • EXIT trap │ │ │ │ • -o pipefail │ │ • error_handler │ │ • INT trap │ │ │ │ • -u (if │ │ function │ │ • TERM trap │ │ │ │ STRICT_UNSET) │ │ │ │ │ │ │ └─────────────────┘ └─────────────────┘ └─────────────────────────────┘ │ └─────────────────────────────────────────────────────────────────────────────────┘ ``` ## Error Handler Flow ``` ┌─────────────────────────────────────────────────────────────────────────────────┐ │ ERROR_HANDLER() Flow │ │ Main error handler triggered by ERR trap or manual call │ └─────────────────────┬───────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────────────────────┐ │ Error Detection │ │ │ │ ┌─────────────────────────────────────────────────────────────────────────────┐ │ │ │ Error Information Collection │ │ │ │ │ │ │ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────┐ │ │ │ │ │ Get Exit │ │ Get Command │ │ Get Line │ │ │ │ │ │ Code │ │ Information │ │ Number │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ • From $? or │ │ • From │ │ • From │ │ │ │ │ │ parameter │ │ BASH_COMMAND │ │ BASH_LINENO[0] │ │ │ │ │ │ • Store in │ │ • Clean $STD │ │ • Default to │ │ │ │ │ │ exit_code │ │ references │ │ "unknown" │ │ │ │ │ │ │ │ • Store in │ │ • Store in │ │ │ │ │ │ │ │ command │ │ line_number │ │ │ │ │ └─────────────────┘ └─────────────────┘ └─────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────────────────────┐ │ Success Check │ │ │ │ ┌─────────────────────────────────────────────────────────────────────────────┐ │ │ │ Exit Code Validation │ │ │ │ │ │ │ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────┐ │ │ │ │ │ Check Exit │ │ Success │ │ Error │ │ │ │ │ Code │ │ Path │ │ Path │ │ │ │ │ │ │ │ │ │ │ │ │ │ • If exit_code │ │ • Return 0 │ │ • Continue to │ │ │ │ │ == 0 │ │ • No error │ │ error handling │ │ │ │ │ • Success │ │ processing │ │ • Process error │ │ │ │ │ • No error │ │ │ │ information │ │ │ │ │ handling │ │ │ │ │ │ │ │ └─────────────────┘ └─────────────────┘ └─────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────────────────────┐ │ Error Processing │ │ │ │ ┌─────────────────────────────────────────────────────────────────────────────┐ │ │ │ Error Explanation │ │ │ │ │ │ │ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────┐ │ │ │ │ │ Get Error │ │ Display Error │ │ Log Error │ │ │ │ │ │ Explanation │ │ Information │ │ Information │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ • Call │ │ • Show error │ │ • Write to debug │ │ │ │ │ explain_exit_ │ │ message │ │ log if enabled │ │ │ │ │ code() │ │ • Show line │ │ • Include │ │ │ │ │ • Get human- │ │ number │ │ timestamp │ │ │ │ │ readable │ │ • Show command │ │ • Include exit │ │ │ │ │ message │ │ • Show exit │ │ code │ │ │ │ │ │ │ code │ │ • Include command │ │ │ │ └─────────────────┘ └─────────────────┘ └─────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────────────────────┐ │ Silent Log Integration │ │ │ │ ┌─────────────────────────────────────────────────────────────────────────────┐ │ │ │ Silent Log Display │ │ │ │ │ │ │ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────┐ │ │ │ │ │ Check Silent │ │ Display Log │ │ Exit with │ │ │ │ │ Log File │ │ Content │ │ Error Code │ │ │ │ │ │ │ │ │ │ │ │ │ │ • Check if │ │ • Show last 20 │ │ • Exit with │ │ │ │ │ SILENT_ │ │ lines │ │ original exit │ │ │ │ │ LOGFILE set │ │ • Show file │ │ code │ │ │ │ │ • Check if │ │ path │ │ • Terminate script │ │ │ │ │ file exists │ │ • Format │ │ execution │ │ │ │ │ • Check if │ │ output │ │ │ │ │ │ │ file has │ │ │ │ │ │ │ │ │ content │ │ │ │ │ │ │ │ └─────────────────┘ └─────────────────┘ └─────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────────────────────────┘ ``` ## Signal Handling Flow ``` ┌─────────────────────────────────────────────────────────────────────────────────┐ │ Signal Handler Flow │ │ │ │ ┌─────────────────────────────────────────────────────────────────────────────┐ │ │ │ Signal Detection │ │ │ │ │ │ │ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────┐ │ │ │ │ │ SIGINT │ │ SIGTERM │ │ EXIT │ │ │ │ │ │ (Ctrl+C) │ │ (Termination) │ │ (Script End) │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ • User │ │ • System │ │ • Normal script │ │ │ │ │ interruption │ │ termination │ │ completion │ │ │ │ │ • Graceful │ │ • Graceful │ │ • Error exit │ │ │ │ │ handling │ │ handling │ │ • Signal exit │ │ │ │ │ • Exit code │ │ • Exit code │ │ • Cleanup │ │ │ │ │ 130 │ │ 143 │ │ operations │ │ │ │ └─────────────────┘ └─────────────────┘ └─────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────────────────────┐ │ ON_INTERRUPT() Flow │ │ Handles SIGINT (Ctrl+C) signals │ └─────────────────────┬───────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────────────────────┐ │ Interrupt Processing │ │ │ │ ┌─────────────────────────────────────────────────────────────────────────────┐ │ │ │ User Interruption Handling │ │ │ │ │ │ │ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────┐ │ │ │ │ │ Display │ │ Cleanup │ │ Exit with │ │ │ │ │ │ Message │ │ Operations │ │ Code 130 │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ • Show │ │ • Stop │ │ • Exit with │ │ │ │ │ interruption │ │ processes │ │ SIGINT code │ │ │ │ │ message │ │ • Clean up │ │ • Terminate script │ │ │ │ │ • Use red │ │ temporary │ │ execution │ │ │ │ │ color │ │ files │ │ │ │ │ │ │ • Clear │ │ • Remove lock │ │ │ │ │ │ │ terminal │ │ files │ │ │ │ │ │ └─────────────────┘ └─────────────────┘ └─────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────────────────────────┘ ``` ## Exit Handler Flow ``` ┌─────────────────────────────────────────────────────────────────────────────────┐ │ ON_EXIT() Flow │ │ Handles script exit cleanup │ └─────────────────────┬───────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────────────────────┐ │ Exit Cleanup │ │ │ │ ┌─────────────────────────────────────────────────────────────────────────────┐ │ │ │ Cleanup Operations │ │ │ │ │ │ │ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────┐ │ │ │ │ │ Lock File │ │ Temporary │ │ Exit with │ │ │ │ │ │ Cleanup │ │ File │ │ Original Code │ │ │ │ │ │ │ │ Cleanup │ │ │ │ │ │ │ • Check if │ │ • Remove │ │ • Exit with │ │ │ │ │ lockfile │ │ temporary │ │ original exit │ │ │ │ │ variable set │ │ files │ │ code │ │ │ │ │ • Check if │ │ • Clean up │ │ • Preserve exit │ │ │ │ │ lockfile │ │ process │ │ status │ │ │ │ │ exists │ │ state │ │ • Terminate │ │ │ │ │ • Remove │ │ │ │ execution │ │ │ │ │ lockfile │ │ │ │ │ │ │ │ └─────────────────┘ └─────────────────┘ └─────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────────────────────────┘ ``` ## Error Code Explanation Flow ``` ┌─────────────────────────────────────────────────────────────────────────────────┐ │ EXPLAIN_EXIT_CODE() Flow │ │ Converts numeric exit codes to human-readable explanations │ └─────────────────────┬───────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────────────────────┐ │ Error Code Classification │ │ │ │ ┌─────────────────────────────────────────────────────────────────────────────┐ │ │ │ Error Code Categories │ │ │ │ │ │ │ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────┐ │ │ │ │ │ Generic/ │ │ Package │ │ Node.js │ │ │ │ │ │ Shell │ │ Manager │ │ Errors │ │ │ │ │ │ Errors │ │ Errors │ │ │ │ │ │ │ │ │ │ │ • 243: Out of │ │ │ │ │ • 1: General │ │ • 100: APT │ │ memory │ │ │ │ │ error │ │ package │ │ • 245: Invalid │ │ │ │ │ • 2: Shell │ │ error │ │ option │ │ │ │ │ builtin │ │ • 101: APT │ │ • 246: Parse │ │ │ │ │ misuse │ │ config error │ │ error │ │ │ │ │ • 126: Cannot │ │ • 255: DPKG │ │ • 247: Fatal │ │ │ │ │ execute │ │ fatal error │ │ error │ │ │ │ │ • 127: Command │ │ │ │ • 248: Addon │ │ │ │ │ not found │ │ │ │ failure │ │ │ │ │ • 128: Invalid │ │ │ │ • 249: Inspector │ │ │ │ │ exit │ │ │ │ error │ │ │ │ │ • 130: SIGINT │ │ │ │ • 254: Unknown │ │ │ │ │ • 137: SIGKILL │ │ │ │ fatal error │ │ │ │ │ • 139: Segfault │ │ │ │ │ │ │ │ │ • 143: SIGTERM │ │ │ │ │ │ │ │ └─────────────────┘ └─────────────────┘ └─────────────────────┘ │ │ │ │ │ │ │ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────┐ │ │ │ │ │ Python │ │ Database │ │ Proxmox │ │ │ │ │ │ Errors │ │ Errors │ │ Custom │ │ │ │ │ │ │ │ │ │ Errors │ │ │ │ │ • 210: Virtual │ │ • PostgreSQL: │ │ • 200: Lock file │ │ │ │ │ env missing │ │ 231-234 │ │ failed │ │ │ │ │ • 211: Dep │ │ • MySQL: 241- │ │ • 203: Missing │ │ │ │ │ resolution │ │ 244 │ │ CTID │ │ │ │ │ • 212: Install │ │ • MongoDB: 251- │ │ • 204: Missing │ │ │ │ │ aborted │ │ 254 │ │ PCT_OSTYPE │ │ │ │ │ │ │ │ │ • 205: Invalid │ │ │ │ │ │ │ │ │ CTID │ │ │ │ │ │ │ │ │ • 209: Container │ │ │ │ │ │ │ │ │ creation failed │ │ │ │ │ │ │ │ │ • 210: Cluster │ │ │ │ │ │ │ │ │ not quorate │ │ │ │ │ │ │ │ │ • 214: No storage │ │ │ │ │ │ │ │ │ space │ │ │ │ │ │ │ │ │ • 215: CTID not │ │ │ │ │ │ │ │ │ listed │ │ │ │ │ │ │ │ │ • 216: RootFS │ │ │ │ │ │ │ │ │ missing │ │ │ │ │ │ │ │ │ • 217: Storage │ │ │ │ │ │ │ │ │ not supported │ │ │ │ │ │ │ │ │ • 220: Template │ │ │ │ │ │ │ │ │ path error │ │ │ │ │ │ │ │ │ • 222: Template │ │ │ │ │ │ │ │ │ download failed │ │ │ │ │ │ │ │ │ • 223: Template │ │ │ │ │ │ │ │ │ not available │ │ │ │ │ │ │ │ │ • 231: LXC stack │ │ │ │ │ │ │ │ │ upgrade failed │ │ │ │ └─────────────────┘ └─────────────────┘ └─────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────────────────────┐ │ Default Case │ │ │ │ ┌─────────────────────────────────────────────────────────────────────────────┐ │ │ │ Unknown Error Handling │ │ │ │ │ │ │ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────┐ │ │ │ │ │ Check for │ │ Return │ │ Log Unknown │ │ │ │ │ │ Unknown │ │ Generic │ │ Error │ │ │ │ │ │ Code │ │ Message │ │ │ │ │ │ │ │ │ │ │ • Log to debug │ │ │ │ │ • If no match │ │ • "Unknown │ │ file if enabled │ │ │ │ │ found │ │ error" │ │ • Include error │ │ │ │ │ • Use default │ │ • Return to │ │ code │ │ │ │ │ case │ │ caller │ │ • Include │ │ │ │ │ │ │ │ │ timestamp │ │ │ │ └─────────────────┘ └─────────────────┘ └─────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────────────────────────┘ ``` ## Debug Logging Flow ``` ┌─────────────────────────────────────────────────────────────────────────────────┐ │ Debug Log Integration │ │ │ │ ┌─────────────────────────────────────────────────────────────────────────────┐ │ │ │ Debug Log Writing │ │ │ │ │ │ │ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────┐ │ │ │ │ │ Check Debug │ │ Write Error │ │ Format Log │ │ │ │ │ │ Log File │ │ Information │ │ Entry │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ • Check if │ │ • Timestamp │ │ • Error separator │ │ │ │ │ DEBUG_LOGFILE │ │ • Exit code │ │ • Structured │ │ │ │ │ set │ │ • Explanation │ │ format │ │ │ │ │ • Check if │ │ • Line number │ │ • Easy to parse │ │ │ │ │ file exists │ │ • Command │ │ • Easy to read │ │ │ │ │ • Check if │ │ • Append to │ │ │ │ │ │ │ file writable │ │ file │ │ │ │ │ │ └─────────────────┘ └─────────────────┘ └─────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────────────────────────┘ ``` ## Integration Points ### With core.func - **Silent Execution**: Provides error explanations for silent() function - **Color Variables**: Uses color variables for error display - **Log Integration**: Integrates with SILENT_LOGFILE ### With Other Scripts - **Error Traps**: Sets up ERR trap for automatic error handling - **Signal Traps**: Handles SIGINT, SIGTERM, and EXIT signals - **Cleanup**: Provides cleanup on script exit ### External Dependencies - **None**: Pure Bash implementation - **Color Support**: Requires color variables from core.func - **Log Files**: Uses standard file operations ================================================ FILE: docs/misc/error_handler.func/ERROR_HANDLER_FUNCTIONS_REFERENCE.md ================================================ # error_handler.func Functions Reference ## Overview This document provides a comprehensive alphabetical reference of all functions in `error_handler.func`, including parameters, dependencies, usage examples, and error handling. ## Function Categories ### Error Explanation Functions #### `explain_exit_code()` **Purpose**: Convert numeric exit codes to human-readable explanations **Parameters**: - `$1` - Exit code to explain **Returns**: Human-readable error explanation string **Side Effects**: None **Dependencies**: None **Environment Variables Used**: None **Supported Exit Codes**: - **Generic/Shell**: 1, 2, 126, 127, 128, 130, 137, 139, 143 - **Package Manager**: 100, 101, 255 - **Node.js**: 243, 245, 246, 247, 248, 249, 254 - **Python**: 210, 211, 212 - **PostgreSQL**: 231, 232, 233, 234 - **MySQL/MariaDB**: 241, 242, 243, 244 - **MongoDB**: 251, 252, 253, 254 - **Proxmox Custom**: 200, 203, 204, 205, 209, 210, 214, 215, 216, 217, 220, 222, 223, 231 **Usage Example**: ```bash explanation=$(explain_exit_code 127) echo "Error 127: $explanation" # Output: Error 127: Command not found ``` **Error Code Examples**: ```bash explain_exit_code 1 # "General error / Operation not permitted" explain_exit_code 126 # "Command invoked cannot execute (permission problem?)" explain_exit_code 127 # "Command not found" explain_exit_code 130 # "Terminated by Ctrl+C (SIGINT)" explain_exit_code 200 # "Custom: Failed to create lock file" explain_exit_code 999 # "Unknown error" ``` ### Error Handling Functions #### `error_handler()` **Purpose**: Main error handler triggered by ERR trap or manual call **Parameters**: - `$1` - Exit code (optional, defaults to $?) - `$2` - Command that failed (optional, defaults to BASH_COMMAND) **Returns**: None (exits with error code) **Side Effects**: - Displays detailed error information - Logs error to debug file if enabled - Shows silent log content if available - Exits with original error code **Dependencies**: `explain_exit_code()` **Environment Variables Used**: `DEBUG_LOGFILE`, `SILENT_LOGFILE` **Usage Example**: ```bash # Automatic error handling via ERR trap set -e trap 'error_handler' ERR # Manual error handling error_handler 127 "command_not_found" ``` **Error Information Displayed**: - Error message with color coding - Line number where error occurred - Exit code with explanation - Command that failed - Silent log content (last 20 lines) - Debug log entry (if enabled) ### Signal Handling Functions #### `on_interrupt()` **Purpose**: Handle SIGINT (Ctrl+C) signals gracefully **Parameters**: None **Returns**: None (exits with code 130) **Side Effects**: - Displays interruption message - Exits with SIGINT code (130) **Dependencies**: None **Environment Variables Used**: None **Usage Example**: ```bash # Set up interrupt handler trap on_interrupt INT # User presses Ctrl+C # Handler displays: "Interrupted by user (SIGINT)" # Script exits with code 130 ``` #### `on_terminate()` **Purpose**: Handle SIGTERM signals gracefully **Parameters**: None **Returns**: None (exits with code 143) **Side Effects**: - Displays termination message - Exits with SIGTERM code (143) **Dependencies**: None **Environment Variables Used**: None **Usage Example**: ```bash # Set up termination handler trap on_terminate TERM # System sends SIGTERM # Handler displays: "Terminated by signal (SIGTERM)" # Script exits with code 143 ``` ### Cleanup Functions #### `on_exit()` **Purpose**: Handle script exit cleanup **Parameters**: None **Returns**: None (exits with original exit code) **Side Effects**: - Removes lock file if set - Exits with original exit code **Dependencies**: None **Environment Variables Used**: `lockfile` **Usage Example**: ```bash # Set up exit handler trap on_exit EXIT # Set lock file lockfile="/tmp/my_script.lock" # Script exits normally or with error # Handler removes lock file and exits ``` ### Initialization Functions #### `catch_errors()` **Purpose**: Initialize error handling traps and strict mode **Parameters**: None **Returns**: None **Side Effects**: - Sets strict error handling mode - Sets up error traps - Sets up signal traps - Sets up exit trap **Dependencies**: None **Environment Variables Used**: `STRICT_UNSET` **Strict Mode Settings**: - `-E`: Exit on command failure - `-e`: Exit on any error - `-o pipefail`: Exit on pipe failure - `-u`: Exit on unset variables (if STRICT_UNSET=1) **Trap Setup**: - `ERR`: Calls `error_handler` on command failure - `EXIT`: Calls `on_exit` on script exit - `INT`: Calls `on_interrupt` on SIGINT - `TERM`: Calls `on_terminate` on SIGTERM **Usage Example**: ```bash # Initialize error handling catch_errors # Script now has full error handling # All errors will be caught and handled ``` ## Function Call Hierarchy ### Error Handling Flow ``` Command Failure ├── ERR trap triggered ├── error_handler() called │ ├── Get exit code │ ├── Get command info │ ├── Get line number │ ├── explain_exit_code() │ ├── Display error info │ ├── Log to debug file │ ├── Show silent log │ └── Exit with error code ``` ### Signal Handling Flow ``` Signal Received ├── Signal trap triggered ├── Appropriate handler called │ ├── on_interrupt() for SIGINT │ ├── on_terminate() for SIGTERM │ └── on_exit() for EXIT └── Exit with signal code ``` ### Initialization Flow ``` catch_errors() ├── Set strict mode │ ├── -E (exit on failure) │ ├── -e (exit on error) │ ├── -o pipefail (pipe failure) │ └── -u (unset variables, if enabled) └── Set up traps ├── ERR → error_handler ├── EXIT → on_exit ├── INT → on_interrupt └── TERM → on_terminate ``` ## Error Code Reference ### Generic/Shell Errors | Code | Description | |------|-------------| | 1 | General error / Operation not permitted | | 2 | Misuse of shell builtins (e.g. syntax error) | | 126 | Command invoked cannot execute (permission problem?) | | 127 | Command not found | | 128 | Invalid argument to exit | | 130 | Terminated by Ctrl+C (SIGINT) | | 137 | Killed (SIGKILL / Out of memory?) | | 139 | Segmentation fault (core dumped) | | 143 | Terminated (SIGTERM) | ### Package Manager Errors | Code | Description | |------|-------------| | 100 | APT: Package manager error (broken packages / dependency problems) | | 101 | APT: Configuration error (bad sources.list, malformed config) | | 255 | DPKG: Fatal internal error | ### Node.js Errors | Code | Description | |------|-------------| | 243 | Node.js: Out of memory (JavaScript heap out of memory) | | 245 | Node.js: Invalid command-line option | | 246 | Node.js: Internal JavaScript Parse Error | | 247 | Node.js: Fatal internal error | | 248 | Node.js: Invalid C++ addon / N-API failure | | 249 | Node.js: Inspector error | | 254 | npm/pnpm/yarn: Unknown fatal error | ### Python Errors | Code | Description | |------|-------------| | 210 | Python: Virtualenv / uv environment missing or broken | | 211 | Python: Dependency resolution failed | | 212 | Python: Installation aborted (permissions or EXTERNALLY-MANAGED) | ### Database Errors | Code | Description | |------|-------------| | 231 | PostgreSQL: Connection failed (server not running / wrong socket) | | 232 | PostgreSQL: Authentication failed (bad user/password) | | 233 | PostgreSQL: Database does not exist | | 234 | PostgreSQL: Fatal error in query / syntax | | 241 | MySQL/MariaDB: Connection failed (server not running / wrong socket) | | 242 | MySQL/MariaDB: Authentication failed (bad user/password) | | 243 | MySQL/MariaDB: Database does not exist | | 244 | MySQL/MariaDB: Fatal error in query / syntax | | 251 | MongoDB: Connection failed (server not running) | | 252 | MongoDB: Authentication failed (bad user/password) | | 253 | MongoDB: Database not found | | 254 | MongoDB: Fatal query error | ### Proxmox Custom Errors | Code | Description | |------|-------------| | 200 | Custom: Failed to create lock file | | 203 | Custom: Missing CTID variable | | 204 | Custom: Missing PCT_OSTYPE variable | | 205 | Custom: Invalid CTID (<100) | | 209 | Custom: Container creation failed | | 210 | Custom: Cluster not quorate | | 214 | Custom: Not enough storage space | | 215 | Custom: Container ID not listed | | 216 | Custom: RootFS entry missing in config | | 217 | Custom: Storage does not support rootdir | | 220 | Custom: Unable to resolve template path | | 222 | Custom: Template download failed after 3 attempts | | 223 | Custom: Template not available after download | | 231 | Custom: LXC stack upgrade/retry failed | ## Environment Variable Dependencies ### Required Variables - **`lockfile`**: Lock file path for cleanup (set by calling script) ### Optional Variables - **`DEBUG_LOGFILE`**: Path to debug log file for error logging - **`SILENT_LOGFILE`**: Path to silent execution log file - **`STRICT_UNSET`**: Enable strict unset variable checking (0/1) ### Internal Variables - **`exit_code`**: Current exit code - **`command`**: Failed command - **`line_number`**: Line number where error occurred - **`explanation`**: Error explanation text ## Error Handling Patterns ### Automatic Error Handling ```bash #!/usr/bin/env bash source error_handler.func # Initialize error handling catch_errors # All commands are now monitored # Errors will be automatically caught and handled ``` ### Manual Error Handling ```bash #!/usr/bin/env bash source error_handler.func # Manual error handling if ! command -v required_tool >/dev/null 2>&1; then error_handler 127 "required_tool not found" fi ``` ### Custom Error Codes ```bash #!/usr/bin/env bash source error_handler.func # Use custom error codes if [[ ! -f /required/file ]]; then echo "Error: Required file missing" exit 200 # Custom error code fi ``` ### Signal Handling ```bash #!/usr/bin/env bash source error_handler.func # Set up signal handling trap on_interrupt INT trap on_terminate TERM trap on_exit EXIT # Script handles signals gracefully ``` ## Integration Examples ### With core.func ```bash #!/usr/bin/env bash source core.func source error_handler.func # Silent execution uses error_handler for explanations silent apt-get install -y package # If command fails, error_handler provides explanation ``` ### With build.func ```bash #!/usr/bin/env bash source core.func source error_handler.func source build.func # Container creation with error handling # Errors are caught and explained ``` ### With tools.func ```bash #!/usr/bin/env bash source core.func source error_handler.func source tools.func # Tool operations with error handling # All errors are properly handled and explained ``` ## Best Practices ### Error Handling Setup 1. Source error_handler.func early in script 2. Call catch_errors() to initialize traps 3. Use appropriate exit codes for different error types 4. Provide meaningful error messages ### Signal Handling 1. Always set up signal traps 2. Provide graceful cleanup on interruption 3. Use appropriate exit codes for signals 4. Clean up temporary files and processes ### Error Reporting 1. Use explain_exit_code() for user-friendly messages 2. Log errors to debug files when needed 3. Provide context information (line numbers, commands) 4. Integrate with silent execution logging ### Custom Error Codes 1. Use Proxmox custom error codes (200-231) for container/VM errors 2. Use standard error codes for common operations 3. Document custom error codes in script comments 4. Provide clear error messages for custom codes ================================================ FILE: docs/misc/error_handler.func/ERROR_HANDLER_INTEGRATION.md ================================================ # error_handler.func Integration Guide ## Overview This document describes how `error_handler.func` integrates with other components in the Proxmox Community Scripts project, including dependencies, data flow, and API surface. ## Dependencies ### External Dependencies #### Required Commands - **None**: Pure Bash implementation #### Optional Commands - **None**: No external command dependencies ### Internal Dependencies #### core.func - **Purpose**: Provides color variables for error display - **Usage**: Uses `RD`, `CL`, `YWB` color variables - **Integration**: Called automatically when core.func is sourced - **Data Flow**: Color variables → error display formatting ## Integration Points ### With core.func #### Silent Execution Integration ```bash # core.func silent() function uses error_handler.func silent() { local cmd="$*" local caller_line="${BASH_LINENO[0]:-unknown}" # Execute command "$@" >>"$SILENT_LOGFILE" 2>&1 local rc=$? if [[ $rc -ne 0 ]]; then # Load error_handler.func if needed if ! declare -f explain_exit_code >/dev/null 2>&1; then source error_handler.func fi # Get error explanation local explanation explanation="$(explain_exit_code "$rc")" # Display error with explanation printf "\e[?25h" echo -e "\n${RD}[ERROR]${CL} in line ${RD}${caller_line}${CL}: exit code ${RD}${rc}${CL} (${explanation})" echo -e "${RD}Command:${CL} ${YWB}${cmd}${CL}\n" exit "$rc" fi } ``` #### Color Variable Usage ```bash # error_handler.func uses color variables from core.func error_handler() { # ... error handling logic ... # Use color variables for error display 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" } on_interrupt() { echo -e "\n${RD}Interrupted by user (SIGINT)${CL}" exit 130 } on_terminate() { echo -e "\n${RD}Terminated by signal (SIGTERM)${CL}" exit 143 } ``` ### With build.func #### Container Creation Error Handling ```bash # build.func uses error_handler.func for container operations source core.func source error_handler.func # Container creation with error handling create_container() { # Set up error handling catch_errors # Container creation operations silent pct create "$CTID" "$TEMPLATE" \ --hostname "$HOSTNAME" \ --memory "$MEMORY" \ --cores "$CORES" # If creation fails, error_handler provides explanation } ``` #### Template Download Error Handling ```bash # build.func uses error_handler.func for template operations download_template() { # Template download with error handling if ! silent curl -fsSL "$TEMPLATE_URL" -o "$TEMPLATE_FILE"; then # error_handler provides detailed explanation exit 222 # Template download failed fi } ``` ### With tools.func #### Maintenance Operations Error Handling ```bash # tools.func uses error_handler.func for maintenance operations source core.func source error_handler.func # Maintenance operations with error handling update_system() { catch_errors # System update operations silent apt-get update silent apt-get upgrade -y # Error handling provides explanations for failures } cleanup_logs() { catch_errors # Log cleanup operations silent find /var/log -name "*.log" -mtime +30 -delete # Error handling provides explanations for permission issues } ``` ### With api.func #### API Operations Error Handling ```bash # api.func uses error_handler.func for API operations source core.func source error_handler.func # API operations with error handling api_call() { catch_errors # API call with error handling if ! silent curl -k -H "Authorization: PVEAPIToken=$API_TOKEN" \ "$API_URL/api2/json/nodes/$NODE/lxc"; then # error_handler provides explanation for API failures exit 1 fi } ``` ### With install.func #### Installation Process Error Handling ```bash # install.func uses error_handler.func for installation operations source core.func source error_handler.func # Installation with error handling install_package() { local package="$1" catch_errors # Package installation silent apt-get install -y "$package" # Error handling provides explanations for installation failures } ``` ### With alpine-install.func #### Alpine Installation Error Handling ```bash # alpine-install.func uses error_handler.func for Alpine operations source core.func source error_handler.func # Alpine installation with error handling install_alpine_package() { local package="$1" catch_errors # Alpine package installation silent apk add --no-cache "$package" # Error handling provides explanations for Alpine-specific failures } ``` ### With alpine-tools.func #### Alpine Tools Error Handling ```bash # alpine-tools.func uses error_handler.func for Alpine tools source core.func source error_handler.func # Alpine tools with error handling alpine_tool_operation() { catch_errors # Alpine-specific tool operations silent alpine_command # Error handling provides explanations for Alpine tool failures } ``` ### With passthrough.func #### Hardware Passthrough Error Handling ```bash # passthrough.func uses error_handler.func for hardware operations source core.func source error_handler.func # Hardware passthrough with error handling configure_gpu_passthrough() { catch_errors # GPU passthrough operations silent lspci | grep -i nvidia # Error handling provides explanations for hardware failures } ``` ### With vm-core.func #### VM Operations Error Handling ```bash # vm-core.func uses error_handler.func for VM operations source core.func source error_handler.func # VM operations with error handling create_vm() { catch_errors # VM creation operations silent qm create "$VMID" \ --name "$VMNAME" \ --memory "$MEMORY" \ --cores "$CORES" # Error handling provides explanations for VM creation failures } ``` ## Data Flow ### Input Data #### Environment Variables - **`DEBUG_LOGFILE`**: Path to debug log file for error logging - **`SILENT_LOGFILE`**: Path to silent execution log file - **`STRICT_UNSET`**: Enable strict unset variable checking (0/1) - **`lockfile`**: Lock file path for cleanup (set by calling script) #### Function Parameters - **Exit codes**: Passed to `explain_exit_code()` and `error_handler()` - **Command information**: Passed to `error_handler()` for context - **Signal information**: Passed to signal handlers #### System Information - **Exit codes**: Retrieved from `$?` variable - **Command information**: Retrieved from `BASH_COMMAND` variable - **Line numbers**: Retrieved from `BASH_LINENO[0]` variable - **Process information**: Retrieved from system calls ### Processing Data #### Error Code Processing - **Code classification**: Categorize exit codes by type - **Explanation lookup**: Map codes to human-readable messages - **Context collection**: Gather command and line information - **Log preparation**: Format error information for logging #### Signal Processing - **Signal detection**: Identify received signals - **Handler selection**: Choose appropriate signal handler - **Cleanup operations**: Perform necessary cleanup - **Exit code setting**: Set appropriate exit codes #### Log Processing - **Debug logging**: Write error information to debug log - **Silent log integration**: Display silent log content - **Log formatting**: Format log entries for readability - **Log analysis**: Provide log analysis capabilities ### Output Data #### Error Information - **Error messages**: Human-readable error explanations - **Context information**: Line numbers, commands, timestamps - **Color formatting**: ANSI color codes for terminal display - **Log content**: Silent log excerpts and debug information #### System State - **Exit codes**: Returned from functions - **Log files**: Created and updated for error tracking - **Cleanup status**: Lock file removal and process cleanup - **Signal handling**: Graceful signal processing ## API Surface ### Public Functions #### Error Explanation - **`explain_exit_code()`**: Convert exit codes to explanations - **Parameters**: Exit code to explain - **Returns**: Human-readable explanation string - **Usage**: Called by error_handler() and other functions #### Error Handling - **`error_handler()`**: Main error handler function - **Parameters**: Exit code (optional), command (optional) - **Returns**: None (exits with error code) - **Usage**: Called by ERR trap or manually #### Signal Handling - **`on_interrupt()`**: Handle SIGINT signals - **`on_terminate()`**: Handle SIGTERM signals - **`on_exit()`**: Handle script exit cleanup - **Parameters**: None - **Returns**: None (exits with signal code) - **Usage**: Called by signal traps #### Initialization - **`catch_errors()`**: Initialize error handling - **Parameters**: None - **Returns**: None - **Usage**: Called to set up error handling traps ### Internal Functions #### None - All functions in error_handler.func are public - No internal helper functions - Direct implementation of all functionality ### Global Variables #### Configuration Variables - **`DEBUG_LOGFILE`**: Debug log file path - **`SILENT_LOGFILE`**: Silent log file path - **`STRICT_UNSET`**: Strict mode setting - **`lockfile`**: Lock file path #### State Variables - **`exit_code`**: Current exit code - **`command`**: Failed command - **`line_number`**: Line number where error occurred - **`explanation`**: Error explanation text ## Integration Patterns ### Standard Integration Pattern ```bash #!/usr/bin/env bash # Standard integration pattern # 1. Source core.func first source core.func # 2. Source error_handler.func source error_handler.func # 3. Initialize error handling catch_errors # 4. Use silent execution silent command # 5. Errors are automatically handled ``` ### Minimal Integration Pattern ```bash #!/usr/bin/env bash # Minimal integration pattern source error_handler.func catch_errors # Basic error handling command ``` ### Advanced Integration Pattern ```bash #!/usr/bin/env bash # Advanced integration pattern source core.func source error_handler.func # Set up comprehensive error handling export DEBUG_LOGFILE="/tmp/debug.log" export SILENT_LOGFILE="/tmp/silent.log" lockfile="/tmp/script.lock" touch "$lockfile" catch_errors trap on_interrupt INT trap on_terminate TERM trap on_exit EXIT # Advanced error handling silent command ``` ## Error Handling Integration ### Automatic Error Handling - **ERR Trap**: Automatically catches command failures - **Error Explanation**: Provides human-readable error messages - **Context Information**: Shows line numbers and commands - **Log Integration**: Displays silent log content ### Manual Error Handling - **Custom Error Codes**: Use Proxmox custom error codes - **Error Recovery**: Implement retry logic with error handling - **Conditional Handling**: Different handling for different error types - **Error Analysis**: Analyze error patterns and trends ### Signal Handling Integration - **Graceful Interruption**: Handle Ctrl+C gracefully - **Clean Termination**: Handle SIGTERM signals - **Exit Cleanup**: Clean up resources on script exit - **Lock File Management**: Remove lock files on exit ## Performance Considerations ### Error Handling Overhead - **Minimal Impact**: Error handling adds minimal overhead - **Trap Setup**: Trap setup is done once during initialization - **Error Processing**: Error processing is only done on failures - **Log Writing**: Log writing is only done when enabled ### Memory Usage - **Minimal Footprint**: Error handler uses minimal memory - **Variable Reuse**: Global variables reused across functions - **No Memory Leaks**: Proper cleanup prevents memory leaks - **Efficient Processing**: Efficient error code processing ### Execution Speed - **Fast Error Detection**: Quick error detection and handling - **Efficient Explanation**: Fast error code explanation lookup - **Minimal Delay**: Minimal delay in error handling - **Quick Exit**: Fast exit on error conditions ## Security Considerations ### Error Information Disclosure - **Controlled Disclosure**: Only necessary error information is shown - **Log Security**: Log files have appropriate permissions - **Sensitive Data**: Sensitive data is not logged - **Error Sanitization**: Error messages are sanitized ### Signal Handling Security - **Signal Validation**: Only expected signals are handled - **Cleanup Security**: Secure cleanup of temporary files - **Lock File Security**: Secure lock file management - **Process Security**: Secure process termination ### Log File Security - **File Permissions**: Log files have appropriate permissions - **Log Rotation**: Log files are rotated to prevent disk filling - **Log Cleanup**: Old log files are cleaned up - **Log Access**: Log access is controlled ## Future Integration Considerations ### Extensibility - **New Error Codes**: Easy to add new error code explanations - **Custom Handlers**: Easy to add custom error handlers - **Signal Extensions**: Easy to add new signal handlers - **Log Formats**: Easy to add new log formats ### Compatibility - **Bash Version**: Compatible with different Bash versions - **System Compatibility**: Compatible with different systems - **Script Compatibility**: Compatible with different script types - **Error Code Compatibility**: Compatible with different error codes ### Performance - **Optimization**: Error handling can be optimized for better performance - **Caching**: Error explanations can be cached for faster lookup - **Parallel Processing**: Error handling can be parallelized - **Resource Management**: Better resource management for error handling ================================================ FILE: docs/misc/error_handler.func/ERROR_HANDLER_USAGE_EXAMPLES.md ================================================ # error_handler.func Usage Examples ## Overview This document provides practical usage examples for `error_handler.func` functions, covering common scenarios, integration patterns, and best practices. ## Basic Error Handling Setup ### Standard Script Initialization ```bash #!/usr/bin/env bash # Standard error handling setup # Source error handler source error_handler.func # Initialize error handling catch_errors # Your script code here # All errors will be automatically caught and handled echo "Script running..." apt-get update apt-get install -y package echo "Script completed successfully" ``` ### Minimal Error Handling ```bash #!/usr/bin/env bash # Minimal error handling setup source error_handler.func catch_errors # Simple script with error handling echo "Starting operation..." command_that_might_fail echo "Operation completed" ``` ## Error Code Explanation Examples ### Basic Error Explanation ```bash #!/usr/bin/env bash source error_handler.func # Explain common error codes echo "Error 1: $(explain_exit_code 1)" echo "Error 127: $(explain_exit_code 127)" echo "Error 130: $(explain_exit_code 130)" echo "Error 200: $(explain_exit_code 200)" ``` ### Error Code Testing ```bash #!/usr/bin/env bash source error_handler.func # Test all error codes test_error_codes() { local codes=(1 2 126 127 128 130 137 139 143 100 101 255 200 203 204 205) for code in "${codes[@]}"; do echo "Code $code: $(explain_exit_code $code)" done } test_error_codes ``` ### Custom Error Code Usage ```bash #!/usr/bin/env bash source error_handler.func # Use custom error codes check_requirements() { if [[ ! -f /required/file ]]; then echo "Error: Required file missing" exit 200 # Custom error code fi if [[ -z "$CTID" ]]; then echo "Error: CTID not set" exit 203 # Custom error code fi if [[ $CTID -lt 100 ]]; then echo "Error: Invalid CTID" exit 205 # Custom error code fi } check_requirements ``` ## Signal Handling Examples ### Interrupt Handling ```bash #!/usr/bin/env bash source error_handler.func # Set up interrupt handler trap on_interrupt INT echo "Script running... Press Ctrl+C to interrupt" sleep 10 echo "Script completed normally" ``` ### Termination Handling ```bash #!/usr/bin/env bash source error_handler.func # Set up termination handler trap on_terminate TERM echo "Script running... Send SIGTERM to terminate" sleep 10 echo "Script completed normally" ``` ### Complete Signal Handling ```bash #!/usr/bin/env bash source error_handler.func # Set up all signal handlers trap on_interrupt INT trap on_terminate TERM trap on_exit EXIT echo "Script running with full signal handling" sleep 10 echo "Script completed normally" ``` ## Cleanup Examples ### Lock File Cleanup ```bash #!/usr/bin/env bash source error_handler.func # Set up lock file lockfile="/tmp/my_script.lock" touch "$lockfile" # Set up exit handler trap on_exit EXIT echo "Script running with lock file..." sleep 5 echo "Script completed - lock file will be removed" ``` ### Temporary File Cleanup ```bash #!/usr/bin/env bash source error_handler.func # Create temporary files temp_file1="/tmp/temp1.$$" temp_file2="/tmp/temp2.$$" touch "$temp_file1" "$temp_file2" # Set up cleanup cleanup() { rm -f "$temp_file1" "$temp_file2" echo "Temporary files cleaned up" } trap cleanup EXIT echo "Script running with temporary files..." sleep 5 echo "Script completed - temporary files will be cleaned up" ``` ## Debug Logging Examples ### Basic Debug Logging ```bash #!/usr/bin/env bash source error_handler.func # Enable debug logging export DEBUG_LOGFILE="/tmp/debug.log" catch_errors echo "Script with debug logging" apt-get update apt-get install -y package ``` ### Debug Log Analysis ```bash #!/usr/bin/env bash source error_handler.func # Enable debug logging export DEBUG_LOGFILE="/tmp/debug.log" catch_errors # Function to analyze debug log analyze_debug_log() { if [[ -f "$DEBUG_LOGFILE" ]]; then echo "Debug log analysis:" echo "Total errors: $(grep -c "ERROR" "$DEBUG_LOGFILE")" echo "Recent errors:" tail -n 5 "$DEBUG_LOGFILE" else echo "No debug log found" fi } # Run script echo "Running script..." apt-get update # Analyze results analyze_debug_log ``` ## Silent Execution Integration ### With core.func Silent Execution ```bash #!/usr/bin/env bash source core.func source error_handler.func # Silent execution with error handling echo "Installing packages..." silent apt-get update silent apt-get install -y nginx echo "Configuring service..." silent systemctl enable nginx silent systemctl start nginx echo "Installation completed" ``` ### Silent Execution Error Handling ```bash #!/usr/bin/env bash source core.func source error_handler.func # Function with silent execution and error handling install_package() { local package="$1" echo "Installing $package..." if silent apt-get install -y "$package"; then echo "$package installed successfully" return 0 else echo "Failed to install $package" return 1 fi } # Install multiple packages packages=("nginx" "apache2" "mysql-server") for package in "${packages[@]}"; do if ! install_package "$package"; then echo "Stopping installation due to error" exit 1 fi done ``` ## Advanced Error Handling Examples ### Conditional Error Handling ```bash #!/usr/bin/env bash source error_handler.func # Conditional error handling based on environment setup_error_handling() { if [[ "${STRICT_MODE:-0}" == "1" ]]; then echo "Enabling strict mode" export STRICT_UNSET=1 fi catch_errors echo "Error handling configured" } setup_error_handling ``` ### Error Recovery ```bash #!/usr/bin/env bash source error_handler.func # Error recovery pattern retry_operation() { local max_attempts=3 local attempt=1 while [[ $attempt -le $max_attempts ]]; do echo "Attempt $attempt of $max_attempts" if silent "$@"; then echo "Operation succeeded on attempt $attempt" return 0 else echo "Attempt $attempt failed" ((attempt++)) if [[ $attempt -le $max_attempts ]]; then echo "Retrying in 5 seconds..." sleep 5 fi fi done echo "Operation failed after $max_attempts attempts" return 1 } # Use retry pattern retry_operation apt-get update retry_operation apt-get install -y package ``` ### Custom Error Handler ```bash #!/usr/bin/env bash source error_handler.func # Custom error handler for specific operations custom_error_handler() { local exit_code=${1:-$?} local command=${2:-${BASH_COMMAND:-unknown}} case "$exit_code" in 127) echo "Custom handling: Command not found - $command" echo "Suggestions:" echo "1. Check if the command is installed" echo "2. Check if the command is in PATH" echo "3. Check spelling" ;; 126) echo "Custom handling: Permission denied - $command" echo "Suggestions:" echo "1. Check file permissions" echo "2. Run with appropriate privileges" echo "3. Check if file is executable" ;; *) # Use default error handler error_handler "$exit_code" "$command" ;; esac } # Set up custom error handler trap 'custom_error_handler' ERR # Test custom error handling nonexistent_command ``` ## Integration Examples ### With build.func ```bash #!/usr/bin/env bash # Integration with build.func source core.func source error_handler.func source build.func # Container creation with error handling export APP="plex" export CTID="100" # Errors will be caught and explained # Silent execution will use error_handler for explanations ``` ### With tools.func ```bash #!/usr/bin/env bash # Integration with tools.func source core.func source error_handler.func source tools.func # Tool operations with error handling # All errors are properly handled and explained ``` ### With api.func ```bash #!/usr/bin/env bash # Integration with api.func source core.func source error_handler.func source api.func # API operations with error handling # Network errors and API errors are properly handled ``` ## Best Practices Examples ### Comprehensive Error Handling ```bash #!/usr/bin/env bash # Comprehensive error handling example source error_handler.func # Set up comprehensive error handling setup_comprehensive_error_handling() { # Enable debug logging export DEBUG_LOGFILE="/tmp/script_debug.log" # Set up lock file lockfile="/tmp/script.lock" touch "$lockfile" # Initialize error handling catch_errors # Set up signal handlers trap on_interrupt INT trap on_terminate TERM trap on_exit EXIT echo "Comprehensive error handling configured" } setup_comprehensive_error_handling # Script operations echo "Starting script operations..." # ... script code ... echo "Script operations completed" ``` ### Error Handling for Different Scenarios ```bash #!/usr/bin/env bash source error_handler.func # Different error handling for different scenarios handle_package_errors() { local exit_code=$1 case "$exit_code" in 100) echo "Package manager error - trying to fix..." apt-get --fix-broken install ;; 101) echo "Configuration error - checking sources..." apt-get update ;; *) error_handler "$exit_code" ;; esac } handle_network_errors() { local exit_code=$1 case "$exit_code" in 127) echo "Network command not found - checking connectivity..." ping -c 1 8.8.8.8 ;; *) error_handler "$exit_code" ;; esac } # Use appropriate error handler if [[ "$1" == "package" ]]; then trap 'handle_package_errors $?' ERR elif [[ "$1" == "network" ]]; then trap 'handle_network_errors $?' ERR else catch_errors fi ``` ### Error Handling with Logging ```bash #!/usr/bin/env bash source error_handler.func # Error handling with detailed logging setup_logging_error_handling() { # Create log directory mkdir -p /var/log/script_errors # Set up debug logging export DEBUG_LOGFILE="/var/log/script_errors/debug.log" # Set up silent logging export SILENT_LOGFILE="/var/log/script_errors/silent.log" # Initialize error handling catch_errors echo "Logging error handling configured" } setup_logging_error_handling # Script operations with logging echo "Starting logged operations..." # ... script code ... echo "Logged operations completed" ``` ## Troubleshooting Examples ### Debug Mode ```bash #!/usr/bin/env bash source error_handler.func # Enable debug mode export DEBUG_LOGFILE="/tmp/debug.log" export STRICT_UNSET=1 catch_errors echo "Debug mode enabled" # Script operations ``` ### Error Analysis ```bash #!/usr/bin/env bash source error_handler.func # Function to analyze errors analyze_errors() { local log_file="${1:-$DEBUG_LOGFILE}" if [[ -f "$log_file" ]]; then echo "Error Analysis:" echo "Total errors: $(grep -c "ERROR" "$log_file")" echo "Error types:" grep "ERROR" "$log_file" | awk '{print $NF}' | sort | uniq -c echo "Recent errors:" tail -n 10 "$log_file" else echo "No error log found" fi } # Run script with error analysis analyze_errors ``` ### Error Recovery Testing ```bash #!/usr/bin/env bash source error_handler.func # Test error recovery test_error_recovery() { local test_cases=( "nonexistent_command" "apt-get install nonexistent_package" "systemctl start nonexistent_service" ) for test_case in "${test_cases[@]}"; do echo "Testing: $test_case" if silent $test_case; then echo "Unexpected success" else echo "Expected failure handled" fi done } test_error_recovery ``` ================================================ FILE: docs/misc/error_handler.func/README.md ================================================ # error_handler.func Documentation ## Overview The `error_handler.func` file provides comprehensive error handling and signal management for Proxmox Community Scripts. It offers detailed error code explanations, graceful error recovery, and proper cleanup mechanisms. ## Purpose and Use Cases - **Error Code Explanation**: Provides human-readable explanations for exit codes - **Signal Handling**: Manages SIGINT, SIGTERM, and other signals gracefully - **Error Recovery**: Implements proper cleanup and error reporting - **Debug Logging**: Records error information for troubleshooting - **Silent Execution Support**: Integrates with core.func silent execution ## Quick Reference ### Key Function Groups - **Error Explanation**: `explain_exit_code()` - Convert exit codes to human-readable messages - **Error Handling**: `error_handler()` - Main error handler with detailed reporting - **Signal Handlers**: `on_interrupt()`, `on_terminate()` - Graceful signal handling - **Cleanup**: `on_exit()` - Cleanup on script exit - **Trap Setup**: `catch_errors()` - Initialize error handling traps ### Dependencies - **External**: None (pure Bash implementation) - **Internal**: Uses color variables from core.func ### Integration Points - Used by: All scripts via core.func silent execution - Uses: Color variables from core.func - Provides: Error explanations for core.func silent function ## Documentation Files ### 📊 [ERROR_HANDLER_FLOWCHART.md](./ERROR_HANDLER_FLOWCHART.md) Visual execution flows showing error handling processes and signal management. ### 📚 [ERROR_HANDLER_FUNCTIONS_REFERENCE.md](./ERROR_HANDLER_FUNCTIONS_REFERENCE.md) Complete alphabetical reference of all functions with parameters, dependencies, and usage details. ### 💡 [ERROR_HANDLER_USAGE_EXAMPLES.md](./ERROR_HANDLER_USAGE_EXAMPLES.md) Practical examples showing how to use error handling functions and common patterns. ### 🔗 [ERROR_HANDLER_INTEGRATION.md](./ERROR_HANDLER_INTEGRATION.md) How error_handler.func integrates with other components and provides error handling services. ## Key Features ### Error Code Categories - **Generic/Shell Errors**: Exit codes 1, 2, 126, 127, 128, 130, 137, 139, 143 - **Package Manager Errors**: APT/DPKG errors (100, 101, 255) - **Node.js Errors**: JavaScript runtime errors (243-249, 254) - **Python Errors**: Python environment and dependency errors (210-212) - **Database Errors**: PostgreSQL, MySQL, MongoDB errors (231-254) - **Proxmox Custom Errors**: Container and VM specific errors (200-231) ### Signal Handling - **SIGINT (Ctrl+C)**: Graceful interruption handling - **SIGTERM**: Graceful termination handling - **EXIT**: Cleanup on script exit - **ERR**: Error trap for command failures ### Error Reporting - **Detailed Messages**: Human-readable error explanations - **Context Information**: Line numbers, commands, timestamps - **Log Integration**: Silent log file integration - **Debug Logging**: Optional debug log file support ## Common Usage Patterns ### Basic Error Handling Setup ```bash #!/usr/bin/env bash # Basic error handling setup source error_handler.func # Initialize error handling catch_errors # Your script code here # Errors will be automatically handled ``` ### Manual Error Explanation ```bash #!/usr/bin/env bash source error_handler.func # Get error explanation explanation=$(explain_exit_code 127) echo "Error 127: $explanation" # Output: Error 127: Command not found ``` ### Custom Error Handling ```bash #!/usr/bin/env bash source error_handler.func # Custom error handling if ! command -v required_tool >/dev/null 2>&1; then echo "Error: required_tool not found" exit 127 fi ``` ## Environment Variables ### Debug Variables - `DEBUG_LOGFILE`: Path to debug log file for error logging - `SILENT_LOGFILE`: Path to silent execution log file - `STRICT_UNSET`: Enable strict unset variable checking (0/1) ### Internal Variables - `lockfile`: Lock file path for cleanup (set by calling script) - `exit_code`: Current exit code - `command`: Failed command - `line_number`: Line number where error occurred ## Error Categories ### Generic/Shell Errors - **1**: General error / Operation not permitted - **2**: Misuse of shell builtins (syntax error) - **126**: Command invoked cannot execute (permission problem) - **127**: Command not found - **128**: Invalid argument to exit - **130**: Terminated by Ctrl+C (SIGINT) - **137**: Killed (SIGKILL / Out of memory) - **139**: Segmentation fault (core dumped) - **143**: Terminated (SIGTERM) ### Package Manager Errors - **100**: APT package manager error (broken packages) - **101**: APT configuration error (bad sources.list) - **255**: DPKG fatal internal error ### Node.js Errors - **243**: JavaScript heap out of memory - **245**: Invalid command-line option - **246**: Internal JavaScript parse error - **247**: Fatal internal error - **248**: Invalid C++ addon / N-API failure - **249**: Inspector error - **254**: npm/pnpm/yarn unknown fatal error ### Python Errors - **210**: Virtualenv/uv environment missing or broken - **211**: Dependency resolution failed - **212**: Installation aborted (permissions or EXTERNALLY-MANAGED) ### Database Errors - **PostgreSQL (231-234)**: Connection, authentication, database, query errors - **MySQL/MariaDB (241-244)**: Connection, authentication, database, query errors - **MongoDB (251-254)**: Connection, authentication, database, query errors ### Proxmox Custom Errors - **200**: Failed to create lock file - **203**: Missing CTID variable - **204**: Missing PCT_OSTYPE variable - **205**: Invalid CTID (<100) - **209**: Container creation failed - **210**: Cluster not quorate - **214**: Not enough storage space - **215**: Container ID not listed - **216**: RootFS entry missing in config - **217**: Storage does not support rootdir - **220**: Unable to resolve template path - **222**: Template download failed after 3 attempts - **223**: Template not available after download - **231**: LXC stack upgrade/retry failed ## Best Practices ### Error Handling Setup 1. Source error_handler.func early in script 2. Call catch_errors() to initialize traps 3. Use proper exit codes for different error types 4. Provide meaningful error messages ### Signal Handling 1. Always set up signal traps 2. Provide graceful cleanup on interruption 3. Use appropriate exit codes for signals 4. Clean up temporary files and processes ### Error Reporting 1. Use explain_exit_code() for user-friendly messages 2. Log errors to debug files when needed 3. Provide context information (line numbers, commands) 4. Integrate with silent execution logging ## Troubleshooting ### Common Issues 1. **Missing Error Handler**: Ensure error_handler.func is sourced 2. **Trap Not Set**: Call catch_errors() to initialize traps 3. **Color Variables**: Ensure core.func is sourced for colors 4. **Lock Files**: Clean up lock files in on_exit() ### Debug Mode Enable debug logging for detailed error information: ```bash export DEBUG_LOGFILE="/tmp/debug.log" source error_handler.func catch_errors ``` ### Error Code Testing Test error explanations: ```bash source error_handler.func for code in 1 2 126 127 128 130 137 139 143; do echo "Code $code: $(explain_exit_code $code)" done ``` ## Related Documentation - [core.func](../core.func/) - Core utilities and silent execution - [build.func](../build.func/) - Container creation with error handling - [tools.func](../tools.func/) - Extended utilities with error handling - [api.func](../api.func/) - API operations with error handling --- *This documentation covers the error_handler.func file which provides comprehensive error handling for all Proxmox Community Scripts.* ================================================ FILE: docs/misc/install.func/INSTALL_FUNC_FLOWCHART.md ================================================ # install.func Flowchart ## Installation Workflow ``` ┌──────────────────────────────────┐ │ Container Started │ │ (Inside LXC by build.func) │ └──────────────┬───────────────────┘ │ ▼ ┌──────────────────────┐ │ Source Functions │ │ $FUNCTIONS_FILE_PATH │ └──────────┬───────────┘ │ ▼ ┌──────────────────────┐ │ setting_up_container│ │ Display setup msg │ └──────────┬───────────┘ │ ▼ ┌──────────────────────┐ │ network_check() │ │ (Verify internet) │ └────┬──────────────┬──┘ │ │ OK FAIL │ │ │ ▼ │ ┌──────────────┐ │ │ Retry Check │ │ │ 3 attempts │ │ └────┬─────┬───┘ │ │ │ │ OK FAIL │ │ │ └──────────────┘ │ │ │ ▼ ▼ ┌──────────────────────┐ ┌──────────────┐ │ update_os() │ │ Exit Error │ │ (apt update/upgrade) │ │ No internet │ └──────────┬───────────┘ └──────────────┘ │ ▼ ┌──────────────────────┐ │ verb_ip6() [optional]│ │ (Enable IPv6) │ └──────────┬───────────┘ │ ▼ ┌──────────────────────┐ │ Application │ │ Installation │ │ (Main work) │ └──────────┬───────────┘ │ ┌───────┴────────┐ │ │ SUCCESS FAILED │ │ │ └─ error_handler catches │ (if catch_errors active) │ ▼ ┌──────────────────────┐ │ motd_ssh() │ │ (Setup SSH/MOTD) │ └──────────┬───────────┘ │ ▼ ┌──────────────────────┐ │ customize() │ │ (Apply settings) │ └──────────┬───────────┘ │ ▼ ┌──────────────────────┐ │ cleanup_lxc() │ │ (Final cleanup) │ └──────────┬───────────┘ │ ▼ ┌──────────────────────┐ │ Installation │ │ Complete ✓ │ └──────────────────────┘ ``` ## Network Check Retry Logic ``` network_check() │ ├─ Ping 8.8.8.8 (Google DNS) │ └─ Response? │ ├─ YES: Continue │ └─ NO: Retry │ ├─ Retry 1 │ └─ Wait 5s, ping again │ ├─ Retry 2 │ └─ Wait 5s, ping again │ └─ Retry 3 ├─ If OK: Continue └─ If FAIL: Exit Error (Network unavailable) ``` --- **Visual Reference for**: install.func container setup workflows **Last Updated**: December 2025 ================================================ FILE: docs/misc/install.func/INSTALL_FUNC_FUNCTIONS_REFERENCE.md ================================================ # install.func Functions Reference Complete reference of all functions in install.func with detailed usage information. ## Function Index - `setting_up_container()` - Initialize container setup - `network_check()` - Verify network connectivity - `update_os()` - Update OS packages - `verb_ip6()` - Enable IPv6 - `motd_ssh()` - Configure SSH and MOTD - `customize()` - Apply container customizations - `cleanup_lxc()` - Final container cleanup --- ## Core Functions ### setting_up_container() Display setup message and initialize container environment. **Signature**: ```bash setting_up_container ``` **Purpose**: Announce container initialization and set initial environment **Usage**: ```bash #!/usr/bin/env bash source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" setting_up_container # Output: ⏳ Setting up container... ``` --- ### network_check() Verify network connectivity with automatic retry logic. **Signature**: ```bash network_check ``` **Purpose**: Ensure internet connectivity before critical operations **Behavior**: - Pings 8.8.8.8 (Google DNS) - 3 attempts with 5-second delays - Exits with error if all attempts fail **Usage**: ```bash network_check # If no internet: Exits with error message # If internet OK: Continues to next step ``` **Error Handling**: ```bash if ! network_check; then msg_error "No internet connection" exit 1 fi ``` --- ### update_os() Update OS packages with error handling. **Signature**: ```bash update_os ``` **Purpose**: Prepare container with latest packages **On Debian/Ubuntu**: - Runs: `apt-get update && apt-get upgrade -y` **On Alpine**: - Runs: `apk update && apk upgrade` **Usage**: ```bash update_os ``` --- ### verb_ip6() Enable IPv6 support in container (optional). **Signature**: ```bash verb_ip6 ``` **Purpose**: Enable IPv6 if needed for application **Usage**: ```bash verb_ip6 # Enable IPv6 network_check # Verify connectivity with IPv6 ``` --- ### motd_ssh() Configure SSH daemon and MOTD for container access. **Signature**: ```bash motd_ssh ``` **Purpose**: Setup SSH and create login message **Configures**: - SSH daemon startup and keys - Custom MOTD displaying application access info - SSH port and security settings **Usage**: ```bash motd_ssh # SSH is now configured and application info is in MOTD ``` --- ### customize() Apply container customizations and final setup. **Signature**: ```bash customize ``` **Purpose**: Apply any remaining customizations **Usage**: ```bash customize ``` --- ### cleanup_lxc() Final cleanup and completion of installation. **Signature**: ```bash cleanup_lxc ``` **Purpose**: Remove temporary files and finalize installation **Cleans**: - Temporary installation files - Package manager cache - Log files from installation process **Usage**: ```bash cleanup_lxc # Installation is now complete and ready ``` --- ## Common Patterns ### Basic Installation Pattern ```bash #!/usr/bin/env bash source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" setting_up_container network_check update_os # ... application installation ... motd_ssh customize cleanup_lxc ``` ### With IPv6 Support ```bash #!/usr/bin/env bash source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" setting_up_container verb_ip6 # Enable IPv6 network_check update_os # ... application installation ... ``` ### With Error Handling ```bash #!/usr/bin/env bash source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" catch_errors # Setup error trapping setting_up_container if ! network_check; then msg_error "Network connectivity failed" exit 1 fi update_os ``` --- **Last Updated**: December 2025 **Total Functions**: 7 **Maintained by**: community-scripts team ================================================ FILE: docs/misc/install.func/INSTALL_FUNC_INTEGRATION.md ================================================ # install.func Integration Guide How install.func integrates with the ProxmoxVE ecosystem and connects to other function libraries. ## Component Integration ### install.func in the Installation Pipeline ``` install/app-install.sh (container-side) │ ├─ Sources: core.func (messaging) ├─ Sources: error_handler.func (error handling) │ ├─ ★ Uses: install.func ★ │ ├─ setting_up_container() │ ├─ network_check() │ ├─ update_os() │ └─ motd_ssh() │ ├─ Uses: tools.func (package installation) │ └─ Back to install.func: ├─ customize() └─ cleanup_lxc() ``` ### Integration with tools.func install.func and tools.func work together: ``` setting_up_container() [install.func] │ update_os() [install.func] │ pkg_update() [tools.func] setup_nodejs() [tools.func] setup_mariadb() [tools.func] │ motd_ssh() [install.func] customize() [install.func] cleanup_lxc() [install.func] ``` --- ## Dependencies ### External Dependencies - `curl`, `wget` - For downloads - `apt-get` or `apk` - Package management - `ping` - Network verification - `systemctl` or `rc-service` - Service management ### Internal Dependencies ``` install.func uses: ├─ core.func (for messaging and colors) ├─ error_handler.func (for error handling) └─ tools.func (for package operations) ``` --- ## Best Practices ### Always Follow This Pattern ```bash #!/usr/bin/env bash source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" # 1. Setup error handling catch_errors # 2. Initialize container setting_up_container # 3. Verify network network_check # 4. Update OS update_os # 5. Installation (your code) # ... install application ... # 6. Configure access motd_ssh # 7. Customize customize # 8. Cleanup cleanup_lxc ``` --- **Last Updated**: December 2025 **Maintainers**: community-scripts team ================================================ FILE: docs/misc/install.func/INSTALL_FUNC_USAGE_EXAMPLES.md ================================================ # install.func Usage Examples Practical examples for using install.func functions in application installation scripts. ## Basic Examples ### Example 1: Minimal Setup ```bash #!/usr/bin/env bash source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" setting_up_container network_check update_os # ... application installation ... motd_ssh customize cleanup_lxc ``` ### Example 2: With Error Handling ```bash #!/usr/bin/env bash source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" catch_errors setting_up_container if ! network_check; then msg_error "Network failed" exit 1 fi if ! update_os; then msg_error "OS update failed" exit 1 fi # ... continue ... ``` --- ## Production Examples ### Example 3: Full Application Installation ```bash #!/usr/bin/env bash source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" catch_errors setting_up_container network_check update_os msg_info "Installing application" # ... install steps ... msg_ok "Application installed" motd_ssh customize cleanup_lxc ``` ### Example 4: With IPv6 Support ```bash #!/usr/bin/env bash source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" catch_errors setting_up_container verb_ip6 network_check update_os # ... application installation ... motd_ssh customize cleanup_lxc ``` --- **Last Updated**: December 2025 **Examples**: Basic and production patterns **All examples production-ready** ================================================ FILE: docs/misc/install.func/README.md ================================================ # install.func Documentation ## Overview The `install.func` file provides container installation workflow orchestration and fundamental operations for applications deployed inside LXC containers. It handles network setup, OS configuration, connectivity verification, and installation mechanics. ## Purpose and Use Cases - **Container Setup**: Initialize new container with proper configuration - **Network Verification**: Verify IPv4 and IPv6 connectivity - **OS Configuration**: Update OS, apply system settings - **Installation Workflow**: Orchestrate application installation steps - **Error Handling**: Comprehensive signal trapping and error recovery ## Quick Reference ### Key Function Groups - **Initialization**: `setting_up_container()` - Setup message and environment - **Network**: `network_check()`, `verb_ip6()` - Connectivity verification - **OS Configuration**: `update_os()` - OS updates and package management - **Installation**: `motd_ssh()`, `customize()` - Container customization - **Cleanup**: `cleanup_lxc()` - Final container cleanup ### Dependencies - **External**: `curl`, `apt-get`, `ping`, `dns` utilities - **Internal**: Uses functions from `core.func`, `error_handler.func`, `tools.func` ### Integration Points - Used by: All install/*.sh scripts at startup - Uses: Environment variables from build.func and core.func - Provides: Container initialization and management services ## Documentation Files ### 📊 [INSTALL_FUNC_FLOWCHART.md](./INSTALL_FUNC_FLOWCHART.md) Visual execution flows showing initialization, network checks, and installation workflows. ### 📚 [INSTALL_FUNC_FUNCTIONS_REFERENCE.md](./INSTALL_FUNC_FUNCTIONS_REFERENCE.md) Complete alphabetical reference of all functions with parameters, dependencies, and usage details. ### 💡 [INSTALL_FUNC_USAGE_EXAMPLES.md](./INSTALL_FUNC_USAGE_EXAMPLES.md) Practical examples showing how to use installation functions and common patterns. ### 🔗 [INSTALL_FUNC_INTEGRATION.md](./INSTALL_FUNC_INTEGRATION.md) How install.func integrates with other components and provides installation services. ## Key Features ### Container Initialization - **Environment Setup**: Prepare container variables and functions - **Message System**: Display installation progress with colored output - **Error Handlers**: Setup signal trapping for proper cleanup ### Network & Connectivity - **IPv4 Verification**: Ping external hosts to verify internet access - **IPv6 Support**: Optional IPv6 enablement and verification - **DNS Checking**: Verify DNS resolution is working - **Retry Logic**: Automatic retries for transient failures ### OS Configuration - **Package Updates**: Safely update OS package lists - **System Optimization**: Disable unnecessary services (wait-online) - **Timezone**: Validate and set container timezone - **SSH Setup**: Configure SSH daemon and keys ### Container Customization - **MOTD**: Create custom login message - **Auto-Login**: Optional passwordless root login - **Update Script**: Register application update function - **Customization Hooks**: Application-specific setup ## Function Categories ### 🔹 Core Functions - `setting_up_container()` - Display setup message and set environment - `network_check()` - Verify network connectivity - `update_os()` - Update OS packages with retry logic - `verb_ip6()` - Enable IPv6 (optional) ### 🔹 Configuration Functions - `motd_ssh()` - Setup MOTD and SSH configuration - `customize()` - Apply container customizations - `cleanup_lxc()` - Final cleanup before completion ### 🔹 Utility Functions - `create_update_script()` - Register application update function - `set_timezone()` - Configure container timezone - `disable_wait_online()` - Disable systemd-networkd-wait-online ## Execution Flow ``` Container Started ↓ source $FUNCTIONS_FILE_PATH ↓ setting_up_container() ← Display "Setting up container..." ↓ network_check() ← Verify internet connectivity ↓ update_os() ← Update package lists ↓ [Application-Specific Installation] ↓ motd_ssh() ← Configure SSH/MOTD customize() ← Apply customizations ↓ cleanup_lxc() ← Final cleanup ↓ Installation Complete ``` ## Common Usage Patterns ### Basic Container Setup ```bash #!/usr/bin/env bash source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" setting_up_container network_check update_os # ... application installation ... motd_ssh customize cleanup_lxc ``` ### With Optional IPv6 ```bash #!/usr/bin/env bash source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" setting_up_container verb_ip6 # Enable IPv6 network_check update_os # ... installation ... motd_ssh customize cleanup_lxc ``` ### With Custom Update Script ```bash #!/usr/bin/env bash source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" setting_up_container network_check update_os # ... installation ... # Register update function function update_script() { # Update logic here } export -f update_script motd_ssh customize cleanup_lxc ``` ## Best Practices ### ✅ DO - Call `setting_up_container()` at the start - Check `network_check()` output before main installation - Use `$STD` variable for silent operations - Call `cleanup_lxc()` at the very end - Test network connectivity before critical operations ### ❌ DON'T - Skip network verification - Assume internet is available - Hardcode container paths - Use `echo` instead of `msg_*` functions - Forget to call cleanup at the end ## Environment Variables ### Available Variables - `$FUNCTIONS_FILE_PATH` - Path to core functions (set by build.func) - `$CTID` - Container ID number - `$NSAPP` - Normalized application name (lowercase) - `$APP` - Application display name - `$STD` - Output suppression (`silent` or empty) - `$VERBOSE` - Verbose output mode (`yes` or `no`) ### Setting Container Variables ```bash CONTAINER_TIMEZONE="UTC" CONTAINER_HOSTNAME="myapp-container" CONTAINER_FQDN="myapp.example.com" ``` ## Troubleshooting ### "Network check failed" ```bash # Container may not have internet access # Check: ping 8.8.8.8 # External connectivity nslookup example.com # DNS resolution ip route show # Routing table ``` ### "Package update failed" ```bash # APT may be locked by another process ps aux | grep apt # Check for running apt # Or wait for existing apt to finish sleep 30 update_os ``` ### "Cannot source functions" ```bash # $FUNCTIONS_FILE_PATH may not be set # This variable is set by build.func before running install script # If missing, the install script was not called properly ``` ## Related Documentation - **[tools.func/](../tools.func/)** - Package and tool installation - **[core.func/](../core.func/)** - Utility functions and messaging - **[error_handler.func/](../error_handler.func/)** - Error handling - **[alpine-install.func/](../alpine-install.func/)** - Alpine-specific setup - **[UPDATED_APP-install.md](../../UPDATED_APP-install.md)** - Application script guide ## Recent Updates ### Version 2.0 (Dec 2025) - ✅ Improved network connectivity checks - ✅ Enhanced OS update error handling - ✅ Added IPv6 support with verb_ip6() - ✅ Better timezone validation - ✅ Streamlined cleanup procedures --- **Last Updated**: December 2025 **Maintainers**: community-scripts team **License**: MIT ================================================ FILE: docs/misc/tools.func/README.md ================================================ # tools.func Documentation ## Overview The `tools.func` file provides a comprehensive collection of helper functions for robust package management, repository management, and tool installation in Debian/Ubuntu-based systems. It is the central hub for installing services, databases, programming languages, and development tools in containers. ## Purpose and Use Cases - **Package Management**: Robust APT/DPKG operations with retry logic - **Repository Setup**: Prepare and configure package repositories safely - **Tool Installation**: Install 30+ tools (Node.js, PHP, databases, etc.) - **Dependency Handling**: Manage complex installation workflows - **Error Recovery**: Automatic recovery from network failures ## Quick Reference ### Key Function Groups - **Package Helpers**: `pkg_install()`, `pkg_update()`, `pkg_remove()` - APT operations with retry - **Repository Setup**: `setup_deb822_repo()` - Modern repository configuration - **Tool Installation**: `setup_nodejs()`, `setup_php()`, `setup_mariadb()`, etc. - 30+ tool functions - **System Utilities**: `disable_wait_online()`, `customize()` - System optimization - **Container Setup**: `setting_up_container()`, `motd_ssh()` - Container initialization ### Dependencies - **External**: `curl`, `wget`, `apt-get`, `gpg` - **Internal**: Uses functions from `core.func`, `install.func`, `error_handler.func` ### Integration Points - Used by: All install scripts for dependency installation - Uses: Environment variables from build.func and core.func - Provides: Tool installation, package management, and repository services ## Documentation Files ### 📊 [TOOLS_FUNC_FLOWCHART.md](./TOOLS_FUNC_FLOWCHART.md) Visual execution flows showing package management, tool installation, and repository setup workflows. ### 📚 [TOOLS_FUNC_FUNCTIONS_REFERENCE.md](./TOOLS_FUNC_FUNCTIONS_REFERENCE.md) Complete alphabetical reference of all 30+ functions with parameters, dependencies, and usage details. ### 💡 [TOOLS_FUNC_USAGE_EXAMPLES.md](./TOOLS_FUNC_USAGE_EXAMPLES.md) Practical examples showing how to use tool installation functions and common patterns. ### 🔗 [TOOLS_FUNC_INTEGRATION.md](./TOOLS_FUNC_INTEGRATION.md) How tools.func integrates with other components and provides package/tool services. ### 🔧 [TOOLS_FUNC_ENVIRONMENT_VARIABLES.md](./TOOLS_FUNC_ENVIRONMENT_VARIABLES.md) Complete reference of environment variables and configuration options. ## Key Features ### Robust Package Management - **Automatic Retry Logic**: 3 attempts with backoff for transient failures - **Silent Mode**: Suppress output with `$STD` variable - **Error Recovery**: Automatic cleanup of broken packages - **Atomic Operations**: Ensure consistent state even on failure ### Tool Installation Coverage - **Node.js Ecosystem**: Node.js, npm, yarn, pnpm - **PHP Stack**: PHP-FPM, PHP-CLI, Composer - **Databases**: MariaDB, PostgreSQL, MongoDB - **Development Tools**: Git, build-essential, Docker - **Monitoring**: Grafana, Prometheus, Telegraf - **And 20+ more...** ### Repository Management - **Deb822 Format**: Modern standardized repository format - **Keyring Handling**: Automatic GPG key management - **Cleanup**: Removes legacy repositories and keyrings - **Validation**: Verifies repository accessibility before use ## Common Usage Patterns ### Installing a Tool ```bash setup_nodejs "20" # Install Node.js v20 setup_php "8.2" # Install PHP 8.2 setup_mariadb # Install MariaDB (distribution packages) # MARIADB_VERSION="11.4" setup_mariadb # Specific version from official repo ``` ### Safe Package Operations ```bash pkg_update # Update package lists with retry pkg_install curl wget # Install packages safely pkg_remove old-tool # Remove package cleanly ``` ### Setting Up Repositories ```bash setup_deb822_repo "ppa:example/ppa" "example-app" "jammy" "http://example.com" "release" ``` ## Function Categories ### 🔹 Core Package Functions - `pkg_install()` - Install packages with retry logic - `pkg_update()` - Update package lists safely - `pkg_remove()` - Remove packages completely ### 🔹 Repository Functions - `setup_deb822_repo()` - Add repository in deb822 format - `cleanup_repo_metadata()` - Clean GPG keys and old repos - `check_repository()` - Verify repository is accessible ### 🔹 Tool Installation Functions (30+) **Programming Languages**: - `setup_nodejs()` - Node.js with npm - `setup_php()` - PHP-FPM and CLI - `setup_python()` - Python 3 with pip - `setup_ruby()` - Ruby with gem - `setup_golang()` - Go programming language **Databases**: - `setup_mariadb()` - MariaDB server - `setup_postgresql()` - PostgreSQL database - `setup_mongodb()` - MongoDB NoSQL - `setup_redis()` - Redis cache **Web Servers & Proxies**: - `setup_nginx()` - Nginx web server - `setup_apache()` - Apache HTTP server - `setup_caddy()` - Caddy web server - `setup_traefik()` - Traefik reverse proxy **Containers & Virtualization**: - `setup_docker()` - Docker container runtime - `setup_podman()` - Podman container runtime **Development & System Tools**: - `setup_git()` - Git version control - `setup_docker_compose()` - Docker Compose - `setup_composer()` - PHP dependency manager - `setup_build_tools()` - C/C++ compilation tools **Monitoring & Logging**: - `setup_grafana()` - Grafana dashboards - `setup_prometheus()` - Prometheus monitoring - `setup_telegraf()` - Telegraf metrics collector ### 🔹 System Configuration Functions - `setting_up_container()` - Container initialization message - `network_check()` - Verify network connectivity - `update_os()` - Update OS packages safely - `customize()` - Apply container customizations - `motd_ssh()` - Configure SSH and MOTD - `cleanup_lxc()` - Final container cleanup ## Best Practices ### ✅ DO - Use `$STD` to suppress output in production scripts - Chain multiple tool installations together - Check for tool availability before using - Use version parameters when available - Test new repositories before production use ### ❌ DON'T - Mix package managers (apt and apk in same script) - Hardcode tool versions directly - Skip error checking on package operations - Use `apt-get install -y` without `$STD` - Leave temporary files after installation ## Recent Updates ### Version 2.0 (Dec 2025) - ✅ Added `setup_deb822_repo()` for modern repository format - ✅ Improved error handling with automatic cleanup - ✅ Added 5 new tool installation functions - ✅ Enhanced package retry logic with backoff - ✅ Standardized tool version handling ## Integration with Other Functions ``` tools.func ├── Uses: core.func (messaging, colors) ├── Uses: error_handler.func (exit codes, trapping) ├── Uses: install.func (network_check, update_os) │ └── Used by: All install/*.sh scripts ├── For: Package installation ├── For: Tool setup └── For: Repository management ``` ## Troubleshooting ### "Package manager is locked" ```bash # Wait for apt lock to release sleep 10 pkg_update ``` ### "GPG key not found" ```bash # Repository setup will handle this automatically # If manual fix needed: cleanup_repo_metadata setup_deb822_repo ... ``` ### "Tool installation failed" ```bash # Enable verbose output export var_verbose="yes" setup_nodejs "20" ``` ## Contributing When adding new tool installation functions: 1. Follow the `setup_TOOLNAME()` naming convention 2. Accept version as first parameter 3. Check if tool already installed 4. Use `$STD` for output suppression 5. Set version file: `/opt/TOOLNAME_version.txt` 6. Document in TOOLS_FUNC_FUNCTIONS_REFERENCE.md ## Related Documentation - **[build.func/](../build.func/)** - Container creation orchestrator - **[core.func/](../core.func/)** - Utility functions and messaging - **[install.func/](../install.func/)** - Installation workflow management - **[error_handler.func/](../error_handler.func/)** - Error handling and recovery - **[UPDATED_APP-install.md](../../UPDATED_APP-install.md)** - Application script guide --- **Last Updated**: December 2025 **Maintainers**: community-scripts team **License**: MIT ================================================ FILE: docs/misc/tools.func/TOOLS_FUNC_FLOWCHART.md ================================================ # tools.func Flowchart ## Main Package Installation Flow ``` ┌──────────────────────────────────┐ │ Install Script Starts │ │ source tools.func │ └──────────────┬───────────────────┘ │ ▼ ┌─────────────┐ │ pkg_update()│ │ (apt/apk) │ └──────┬──────┘ │ ▼ ┌────────────────┐ │ Retry Logic │ ◄─────┐ │ (Up to 3 tries)│ │ └────┬───────────┘ │ │ │ ├─ Success: Continue │ ├─ Retry 1 ──────────┘ └─ Fail: Exit │ ▼ ┌──────────────────┐ │ setup_deb822_repo│ │ (Add repository) │ └────────┬─────────┘ │ ▼ ┌─────────────────┐ │ GPG Key Setup │ │ Verify Repo OK │ └────────┬────────┘ │ ▼ ┌──────────────────┐ │ Tool Installation│ │ (setup_nodejs, │ │ setup_php, etc.)│ └────────┬─────────┘ │ ┌──────────┴──────────┐ │ │ ▼ ▼ ┌─────────────┐ ┌──────────────┐ │ Node.js │ │ MariaDB │ │ setup_ │ │ setup_ │ │ nodejs() │ │ mariadb() │ └──────┬──────┘ └────────┬─────┘ │ │ └────────┬───────────┘ │ ▼ ┌───────────────────┐ │ Installation OK? │ └────┬──────────┬───┘ │ │ YES NO │ │ │ ▼ │ ┌─────────────┐ │ │ Rollback │ │ │ Error Exit │ │ └─────────────┘ │ ▼ ┌─────────────────┐ │ Set Version File│ │ /opt/TOOL_v.txt │ └─────────────────┘ ``` ## Repository Setup Flow (setup_deb822_repo) ``` setup_deb822_repo(URL, name, dist, repo_url, release) │ ├─ Parse Parameters │ ├─ URL: Repository URL │ ├─ name: Repository name │ ├─ dist: Distro (jammy, bookworm) │ ├─ repo_url: Main URL │ └─ release: Release type │ ├─ Add GPG Key │ ├─ Download key from URL │ ├─ Add to keyring │ └─ Trust key for deb822 │ ├─ Create deb822 file │ ├─ /etc/apt/sources.list.d/name.sources │ ├─ Format: DEB822 │ └─ Include GPG key reference │ ├─ Validate Repository │ ├─ apt-get update │ ├─ Check for errors │ └─ Retry if needed │ └─ Success / Error ``` ## Tool Installation Chain ``` Tools to Install: ├─ Programming Languages │ ├─ setup_nodejs(VERSION) │ ├─ setup_php(VERSION) │ ├─ setup_python(VERSION) │ ├─ setup_ruby(VERSION) │ └─ setup_golang(VERSION) │ ├─ Databases │ ├─ setup_mariadb(VERSION) │ ├─ setup_postgresql(VERSION) │ ├─ setup_mongodb(VERSION) │ └─ setup_redis(VERSION) │ ├─ Web Servers │ ├─ setup_nginx() │ ├─ setup_apache() │ ├─ setup_caddy() │ └─ setup_traefik() │ ├─ Containers │ ├─ setup_docker() │ └─ setup_podman() │ └─ Utilities ├─ setup_git() ├─ setup_composer() ├─ setup_build_tools() └─ setup_[TOOL]() ``` ## Package Operation Retry Logic ``` ┌─────────────────────┐ │ pkg_install PKG1 │ │ pkg_install PKG2 │ │ pkg_install PKG3 │ └──────────┬──────────┘ │ ▼ ┌─────────────────┐ │ APT Lock Check │ └────┬────────┬───┘ │ │ FREE LOCKED │ │ │ ▼ │ ┌─────────────┐ │ │ Wait 5 sec │ │ └────────┬────┘ │ │ │ ▼ │ ┌─────────────┐ │ │ Retry Check │ │ └────┬────┬───┘ │ │ │ │ OK LOCK │ │ │ │ └────┘ (loop) │ ▼ ┌──────────────────┐ │ apt-get install │ │ (with $STD) │ └────┬─────────┬───┘ │ │ SUCCESS FAILED │ │ │ ▼ │ ┌──────────────┐ │ │ Retry Count? │ │ └────┬─────┬───┘ │ │ │ │ <3 ≥3 │ │ Retry FAIL │ │ │ └─────────┐ │ │ ▼ ▼ ┌─────────┐ ┌─────────┐ │ SUCCESS │ │ FAILED │ └─────────┘ │ EXIT 1 │ └─────────┘ ``` --- **Visual Reference for**: tools.func package management and tool installation **Last Updated**: December 2025 ================================================ FILE: docs/misc/tools.func/TOOLS_FUNC_FUNCTIONS_REFERENCE.md ================================================ # tools.func Functions Reference Complete alphabetical reference of all functions in tools.func with parameters, usage, and examples. ## Function Index ### Package Management - `pkg_install()` - Install packages safely with retry - `pkg_update()` - Update package lists with retry - `pkg_remove()` - Remove packages cleanly ### Repository Management - `setup_deb822_repo()` - Add repository in modern deb822 format - `cleanup_repo_metadata()` - Clean GPG keys and old repositories - `check_repository()` - Verify repository accessibility ### Tool Installation Functions (30+) **Programming Languages**: - `setup_nodejs(VERSION)` - Install Node.js and npm - `setup_php(VERSION)` - Install PHP-FPM and CLI - `setup_python(VERSION)` - Install Python 3 with pip - `setup_uv()` - Install Python uv (modern & fast) - `setup_ruby(VERSION)` - Install Ruby with gem - `setup_golang(VERSION)` - Install Go programming language - `setup_java(VERSION)` - Install OpenJDK (Adoptium) **Databases**: - `setup_mariadb()` - Install MariaDB server - `setup_mariadb_db()` - Create user/db in MariaDB - `setup_postgresql(VERSION)` - Install PostgreSQL - `setup_postgresql_db()` - Create user/db in PostgreSQL - `setup_mongodb(VERSION)` - Install MongoDB - `setup_redis(VERSION)` - Install Redis cache - `setup_meilisearch()` - Install Meilisearch engine **Web Servers**: - `setup_nginx()` - Install Nginx - `setup_apache()` - Install Apache HTTP Server - `setup_caddy()` - Install Caddy - `setup_traefik()` - Install Traefik proxy **Containers**: - `setup_docker()` - Install Docker - `setup_podman()` - Install Podman **Development**: - `setup_git()` - Install Git - `setup_docker_compose()` - Install Docker Compose - `setup_composer()` - Install PHP Composer - `setup_build_tools()` - Install build-essential - `setup_yq()` - Install mikefarah/yq processor **Monitoring**: - `setup_grafana()` - Install Grafana - `setup_prometheus()` - Install Prometheus - `setup_telegraf()` - Install Telegraf **System**: - `setup_wireguard()` - Install WireGuard VPN - `setup_netdata()` - Install Netdata monitoring - `setup_tailscale()` - Install Tailscale - (+ more...) --- ## Core Functions ### install_packages_with_retry() Install one or more packages safely with automatic retry logic (3 attempts), APT refresh, and lock handling. **Signature**: ```bash install_packages_with_retry PACKAGE1 [PACKAGE2 ...] ``` **Parameters**: - `PACKAGE1, PACKAGE2, ...` - Package names to install **Returns**: - `0` - All packages installed successfully - `1` - Installation failed after all retries **Features**: - Automatically sets `DEBIAN_FRONTEND=noninteractive` - Handles DPKG lock errors with `dpkg --configure -a` - Retries on transient network or APT failures **Example**: ```bash install_packages_with_retry curl wget git ``` --- ### upgrade_packages_with_retry() Upgrades installed packages with the same robust retry logic as the installation helper. **Signature**: ```bash upgrade_packages_with_retry ``` **Returns**: - `0` - Upgrade successful - `1` - Upgrade failed --- ### fetch_and_deploy_gh_release() The primary tool for downloading and installing software from GitHub Releases. Supports binaries, tarballs, and Debian packages. **Signature**: ```bash fetch_and_deploy_gh_release APPREPO TYPE [VERSION] [DEST] [ASSET_PATTERN] ``` **Environment Variables**: - `APPREPO`: GitHub repository (e.g., `owner/repo`) - `TYPE`: Asset type (`binary`, `tarball`, `prebuild`, `singlefile`) - `VERSION`: Specific tag or `latest` (Default: `latest`) - `DEST`: Target directory (Default: `/opt/$APP`) - `ASSET_PATTERN`: Regex or string pattern to match the release asset (Required for `prebuild` and `singlefile`) **Supported Operation Modes**: - `tarball`: Downloads and extracts the source tarball. - `binary`: Detects host architecture and installs a `.deb` package using `apt` or `dpkg`. - `prebuild`: Downloads and extracts a pre-built binary archive (supports `.tar.gz`, `.zip`, `.tgz`, `.txz`). - `singlefile`: Downloads a single binary file to the destination. **Environment Variables**: - `CLEAN_INSTALL=1`: Removes all contents of the destination directory before extraction. - `DPKG_FORCE_CONFOLD=1`: Forces `dpkg` to keep old config files during package updates. - `SYSTEMD_OFFLINE=1`: Used automatically for `.deb` installs to prevent systemd-tmpfiles failures in unprivileged containers. **Example**: ```bash fetch_and_deploy_gh_release "muesli/duf" "binary" "latest" "/opt/duf" "duf_.*_linux_amd64.tar.gz" ``` --- ### check_for_gh_release() Checks if a newer version is available on GitHub compared to the installed version. **Signature**: ```bash check_for_gh_release APP REPO ``` **Example**: ```bash if check_for_gh_release "nodejs" "nodesource/distributions"; then # update logic fi ``` --- ### prepare_repository_setup() Performs safe repository preparation by cleaning up old files, keyrings, and ensuring the APT system is in a working state. **Signature**: ```bash prepare_repository_setup REPO_NAME [REPO_NAME2 ...] ``` **Example**: ```bash prepare_repository_setup "mariadb" "mysql" ``` --- ### verify_tool_version() Validates if the installed major version matches the expected version. **Signature**: ```bash verify_tool_version NAME EXPECTED INSTALLED ``` **Example**: ```bash verify_tool_version "nodejs" "22" "$(node -v | grep -oP '^v\K[0-9]+')" ``` --- ### setup_deb822_repo() Add repository in modern deb822 format. **Signature**: ```bash setup_deb822_repo NAME GPG_URL REPO_URL SUITE COMPONENT [ARCHITECTURES] [ENABLED] ``` **Parameters**: - `NAME` - Repository name (e.g., "nodejs") - `GPG_URL` - URL to GPG key (e.g., https://example.com/key.gpg) - `REPO_URL` - Main repository URL (e.g., https://example.com/repo) - `SUITE` - Repository suite (e.g., "jammy", "bookworm") - `COMPONENT` - Repository component (e.g., "main", "testing") - `ARCHITECTURES` - Optional Comma-separated list of architectures (e.g., "amd64,arm64") - `ENABLED` - Optional "true" or "false" (default: "true") **Returns**: - `0` - Repository added successfully - `1` - Repository setup failed **Example**: ```bash setup_deb822_repo \ "nodejs" \ "https://deb.nodesource.com/gpgkey/nodesource.gpg.key" \ "https://deb.nodesource.com/node_20.x" \ "jammy" \ "main" ``` --- ### cleanup_repo_metadata() Clean up GPG keys and old repository configurations. **Signature**: ```bash cleanup_repo_metadata ``` **Parameters**: None **Returns**: - `0` - Cleanup complete **Example**: ```bash cleanup_repo_metadata ``` --- ## Tool Installation Functions ### setup_nodejs() Install Node.js and npm from official repositories. Handles legacy version cleanup (nvm) automatically. **Signature**: ```bash setup_nodejs ``` **Environment Variables**: - `NODE_VERSION`: Major version to install (e.g. "20", "22", "24"). Default: "24". - `NODE_MODULE`: Optional npm package to install globally during setup (e.g. "pnpm", "yarn"). **Example**: ```bash NODE_VERSION="22" NODE_MODULE="pnpm" setup_nodejs ``` --- ### setup_php() Install PHP with configurable extensions and FPM/Apache integration. **Signature**: ```bash setup_php ``` **Environment Variables**: - `PHP_VERSION`: Version to install (e.g. "8.3", "8.4"). Default: "8.4". - `PHP_MODULE`: Comma-separated list of additional extensions. - `PHP_FPM`: Set to "YES" to install php-fpm. - `PHP_APACHE`: Set to "YES" to install libapache2-mod-php. **Example**: ```bash PHP_VERSION="8.3" PHP_FPM="YES" PHP_MODULE="mysql,xml,zip" setup_php ``` --- ### setup_mariadb_db() Creates a new MariaDB database and a dedicated user with all privileges. Automatically generates a password if not provided and saves it to a credentials file. **Environment Variables**: - `MARIADB_DB_NAME`: Name of the database (required) - `MARIADB_DB_USER`: Name of the database user (required) - `MARIADB_DB_PASS`: User password (optional, auto-generated if omitted) **Example**: ```bash MARIADB_DB_NAME="myapp" MARIADB_DB_USER="myapp_user" setup_mariadb_db ``` --- ### setup_postgresql_db() Creates a new PostgreSQL database and a dedicated user/role with all privileges. Automatically generates a password if not provided and saves it to a credentials file. **Environment Variables**: - `PG_DB_NAME`: Name of the database (required) - `PG_DB_USER`: Name of the database user (required) - `PG_DB_PASS`: User password (optional, auto-generated if omitted) --- ### setup_java() Installs Temurin JDK. **Signature**: ```bash JAVA_VERSION="21" setup_java ``` **Parameters**: - `JAVA_VERSION` - JDK version (e.g., "17", "21") (default: "21") **Example**: ```bash JAVA_VERSION="17" setup_java ``` --- ### setup_uv() Installs `uv` (modern Python package manager). **Signature**: ```bash PYTHON_VERSION="3.13" setup_uv ``` **Parameters**: - `PYTHON_VERSION` - Optional Python version to pre-install via uv (e.g., "3.12", "3.13") **Example**: ```bash PYTHON_VERSION="3.13" setup_uv ``` --- ### setup_go() Installs Go programming language. **Signature**: ```bash GO_VERSION="1.23" setup_go ``` **Parameters**: - `GO_VERSION` - Go version to install (default: "1.23") **Example**: ```bash GO_VERSION="1.24" setup_go ``` --- ### setup_yq() Installs `yq` (YAML processor). **Signature**: ```bash setup_yq ``` **Example**: ```bash setup_yq ``` --- ### setup_composer() Installs PHP Composer. **Signature**: ```bash setup_composer ``` **Example**: ```bash setup_composer ``` --- ### setup_meilisearch() Install and configure Meilisearch search engine. **Environment Variables**: - `MEILISEARCH_BIND`: Address and port to bind to (Default: "127.0.0.1:7700") - `MEILISEARCH_ENV`: Environment mode (Default: "production") --- ### setup_yq() Install the `mikefarah/yq` YAML processor. Removes existing non-compliant versions. **Example**: ```bash setup_yq yq eval '.app.version = "1.0.0"' -i config.yaml ``` --- ### setup_composer() Install or update the PHP Composer package manager. Handles `COMPOSER_ALLOW_SUPERUSER` automatically and performs self-updates if already installed. **Example**: ```bash setup_php setup_composer $STD composer install --no-dev ``` --- ### setup_build_tools() Install the `build-essential` package suite for compiling software. --- ### setup_uv() Install the modern Python package manager `uv`. Extremely fast replacement for pip/venv. **Environment Variables**: - `PYTHON_VERSION`: Major.Minor version to ensure is installed. **Example**: ```bash PYTHON_VERSION="3.12" setup_uv uv sync --locked ``` --- ### setup_java() Install OpenJDK via the Adoptium repository. **Environment Variables**: - `JAVA_VERSION`: Major version to install (e.g. "17", "21"). Default: "21". **Example**: ```bash JAVA_VERSION="21" setup_java ``` --- ```bash setup_nodejs VERSION ``` **Parameters**: - `VERSION` - Node.js version (e.g., "20", "22", "lts") **Returns**: - `0` - Installation successful - `1` - Installation failed **Creates**: - `/opt/nodejs_version.txt` - Version file **Example**: ```bash setup_nodejs "20" ``` --- ### setup_php(VERSION) Install PHP-FPM, CLI, and common extensions. **Signature**: ```bash setup_php VERSION ``` **Parameters**: - `VERSION` - PHP version (e.g., "8.2", "8.3") **Returns**: - `0` - Installation successful - `1` - Installation failed **Creates**: - `/opt/php_version.txt` - Version file **Example**: ```bash setup_php "8.3" ``` --- ### setup_mariadb() Install MariaDB server and client utilities. **Signature**: ```bash setup_mariadb # Uses distribution packages (recommended) MARIADB_VERSION="11.4" setup_mariadb # Uses official MariaDB repository ``` **Variables**: - `MARIADB_VERSION` - (optional) Specific MariaDB version - Not set or `"latest"`: Uses distribution packages (most reliable, avoids mirror issues) - Specific version (e.g., `"11.4"`, `"12.2"`): Uses official MariaDB repository **Returns**: - `0` - Installation successful - `1` - Installation failed **Creates**: - `/opt/mariadb_version.txt` - Version file **Example**: ```bash # Recommended: Use distribution packages (stable, no mirror issues) setup_mariadb # Specific version from official repository MARIADB_VERSION="11.4" setup_mariadb ``` --- ### setup_postgresql(VERSION) Install PostgreSQL server and client utilities. **Signature**: ```bash setup_postgresql VERSION ``` **Parameters**: - `VERSION` - PostgreSQL version (e.g., "14", "15", "16") **Returns**: - `0` - Installation successful - `1` - Installation failed **Creates**: - `/opt/postgresql_version.txt` - Version file **Example**: ```bash setup_postgresql "16" ``` --- ### setup_docker() Install Docker and Docker CLI. **Signature**: ```bash setup_docker ``` **Parameters**: None **Returns**: - `0` - Installation successful - `1` - Installation failed **Creates**: - `/opt/docker_version.txt` - Version file **Example**: ```bash setup_docker ``` --- ### setup_composer() Install PHP Composer (dependency manager). **Signature**: ```bash setup_composer ``` **Parameters**: None **Returns**: - `0` - Installation successful - `1` - Installation failed **Creates**: - `/usr/local/bin/composer` - Composer executable **Example**: ```bash setup_composer ``` --- ### setup_build_tools() Install build-essential and development tools (gcc, make, etc.). **Signature**: ```bash setup_build_tools ``` **Parameters**: None **Returns**: - `0` - Installation successful - `1` - Installation failed **Example**: ```bash setup_build_tools ``` --- ## System Configuration ### setting_up_container() Display setup message and initialize container environment. **Signature**: ```bash setting_up_container ``` **Example**: ```bash setting_up_container # Output: ⏳ Setting up container... ``` --- ### motd_ssh() Configure SSH daemon and MOTD for container. **Signature**: ```bash motd_ssh ``` **Example**: ```bash motd_ssh # Configures SSH and creates MOTD ``` --- ### customize() Apply container customizations and final setup. **Signature**: ```bash customize ``` **Example**: ```bash customize ``` --- ### cleanup_lxc() Final cleanup of temporary files and logs. **Signature**: ```bash cleanup_lxc ``` **Example**: ```bash cleanup_lxc # Removes temp files, finalizes installation ``` --- ## Usage Patterns ### Basic Installation Sequence ```bash #!/usr/bin/env bash source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" pkg_update # Update package lists setup_nodejs "20" # Install Node.js setup_mariadb # Install MariaDB (distribution packages) # ... application installation ... motd_ssh # Setup SSH/MOTD customize # Apply customizations cleanup_lxc # Final cleanup ``` ### Tool Chain Installation ```bash #!/usr/bin/env bash source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" # Install full web stack pkg_update setup_nginx setup_php "8.3" setup_mariadb # Uses distribution packages setup_composer ``` ### With Repository Setup ```bash #!/usr/bin/env bash source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" pkg_update # Add Node.js repository setup_deb822_repo \ "https://deb.nodesource.com/gpgkey/nodesource.gpg.key" \ "nodejs" \ "jammy" \ "https://deb.nodesource.com/node_20.x" \ "main" pkg_update setup_nodejs "20" ``` --- **Last Updated**: December 2025 **Total Functions**: 30+ **Maintained by**: community-scripts team ================================================ FILE: docs/misc/tools.func/TOOLS_FUNC_INTEGRATION.md ================================================ # tools.func Integration Guide How tools.func integrates with other components and provides package/tool services to the ProxmoxVE ecosystem. ## Component Relationships ### tools.func in the Installation Pipeline ``` ct/AppName.sh (host) │ ├─ Calls build.func │ └─ Creates Container │ ▼ install/appname-install.sh (container) │ ├─ Sources: core.func (colors, messaging) ├─ Sources: error_handler.func (error handling) ├─ Sources: install.func (container setup) │ └─ ★ Sources: tools.func ★ │ ├─ pkg_update() ├─ pkg_install() ├─ setup_nodejs() ├─ setup_php() ├─ setup_mariadb() └─ ... 30+ functions ``` ### Integration with core.func **tools.func uses core.func for**: - `msg_info()` - Display progress messages - `msg_ok()` - Display success messages - `msg_error()` - Display error messages - `msg_warn()` - Display warnings - Color codes (GN, RD, YW, BL) for formatted output - `$STD` variable - Output suppression control **Example**: ```bash # tools.func internally calls: msg_info "Installing Node.js" # Uses core.func setup_nodejs "20" # Setup happens msg_ok "Node.js installed" # Uses core.func ``` ### Integration with error_handler.func **tools.func uses error_handler.func for**: - Exit code mapping to error descriptions - Automatic error trapping (catch_errors) - Signal handlers (SIGINT, SIGTERM, EXIT) - Structured error reporting **Example**: ```bash # If setup_nodejs fails, error_handler catches it: catch_errors # Calls from error_handler.func setup_nodejs "20" # If this exits non-zero # error_handler logs and traps it ``` ### Integration with install.func **tools.func coordinates with install.func for**: - Initial OS updates (install.func) → then tools (tools.func) - Network verification before tool installation - Package manager state validation - Cleanup procedures after tool setup **Sequence**: ```bash setting_up_container() # From install.func network_check() # From install.func update_os() # From install.func pkg_update # From tools.func setup_nodejs() # From tools.func motd_ssh() # From install.func customize() # From install.func cleanup_lxc() # From install.func ``` --- ## Integration with alpine-tools.func (Alpine Containers) ### When to Use tools.func vs alpine-tools.func | Feature | tools.func (Debian) | alpine-tools.func (Alpine) | |---------|:---:|:---:| | Package Manager | apt-get | apk | | Installation Scripts | install/*.sh | install/*-alpine.sh | | Tool Setup | `setup_nodejs()` (apt) | `setup_nodejs()` (apk) | | Repository | `setup_deb822_repo()` | `add_community_repo()` | | Services | systemctl | rc-service | ### Automatic Selection Installation scripts detect OS and source appropriate functions: ```bash # install/myapp-install.sh if grep -qi 'alpine' /etc/os-release; then # Alpine detected - uses alpine-tools.func apk_update apk_add package else # Debian detected - uses tools.func pkg_update pkg_install package fi ``` --- ## Dependencies Management ### External Dependencies ``` tools.func requires: ├─ curl (for HTTP requests, GPG keys) ├─ wget (for downloads) ├─ apt-get (package manager) ├─ gpg (GPG key management) ├─ openssl (for encryption) └─ systemctl (service management on Debian) ``` ### Internal Function Dependencies ``` setup_nodejs() ├─ Calls: setup_deb822_repo() ├─ Calls: pkg_update() ├─ Calls: pkg_install() └─ Uses: msg_info(), msg_ok() [from core.func] setup_mariadb() ├─ Calls: setup_deb822_repo() ├─ Calls: pkg_update() ├─ Calls: pkg_install() └─ Uses: msg_info(), msg_ok() setup_docker() ├─ Calls: cleanup_repo_metadata() ├─ Calls: setup_deb822_repo() ├─ Calls: pkg_update() └─ Uses: msg_info(), msg_ok() ``` --- ## Function Call Graph ### Complete Installation Dependency Tree ``` install/app-install.sh │ ├─ setting_up_container() [install.func] │ ├─ network_check() [install.func] │ ├─ update_os() [install.func] │ ├─ pkg_update() [tools.func] │ └─ Calls: apt-get update (with retry) │ ├─ setup_nodejs("20") [tools.func] │ ├─ setup_deb822_repo() [tools.func] │ │ └─ Calls: apt-get update │ ├─ pkg_update() [tools.func] │ └─ pkg_install() [tools.func] │ ├─ setup_php("8.3") [tools.func] │ └─ Similar to setup_nodejs │ ├─ setup_mariadb("11") [tools.func] │ └─ Similar to setup_nodejs │ ├─ motd_ssh() [install.func] │ ├─ customize() [install.func] │ └─ cleanup_lxc() [install.func] ``` --- ## Configuration Management ### Environment Variables Used by tools.func ```bash # Output control STD="silent" # Suppress apt/apk output VERBOSE="yes" # Show all output # Package management DEBIAN_FRONTEND="noninteractive" # Tool versions (optional) NODEJS_VERSION="20" PHP_VERSION="8.3" POSTGRES_VERSION="16" ``` ### Tools Configuration Files Created ``` /opt/ ├─ nodejs_version.txt # Node.js version ├─ php_version.txt # PHP version ├─ mariadb_version.txt # MariaDB version ├─ postgresql_version.txt # PostgreSQL version ├─ docker_version.txt # Docker version └─ [TOOL]_version.txt # For all installed tools /etc/apt/sources.list.d/ ├─ nodejs.sources # Node.js repo (deb822) ├─ docker.sources # Docker repo (deb822) └─ [name].sources # Other repos (deb822) ``` --- ## Error Handling Integration ### Exit Codes from tools.func | Code | Meaning | Handled By | |------|:---:|:---:| | 0 | Success | Normal flow | | 1 | Package installation failed | error_handler.func | | 100-101 | APT error | error_handler.func | | 127 | Command not found | error_handler.func | ### Automatic Cleanup on Failure ```bash # If any step fails in install script: catch_errors pkg_update # Fail here? setup_nodejs # Doesn't get here # error_handler automatically: ├─ Logs error ├─ Captures exit code ├─ Calls cleanup_lxc() └─ Exits with proper code ``` --- ## Integration with build.func ### Variable Flow ``` ct/app.sh │ ├─ var_cpu="2" ├─ var_ram="2048" ├─ var_disk="10" │ └─ Calls: build_container() [build.func] │ └─ Creates container │ └─ Calls: install/app-install.sh │ └─ Uses: tools.func for installation ``` ### Resource Considerations tools.func respects container resource limits: - Large package installations respect allocated RAM - Database setups use allocated disk space - Build tools (gcc, make) stay within CPU allocation --- ## Version Management ### How tools.func Tracks Versions Each tool installation creates a version file: ```bash # setup_nodejs() creates: echo "20.10.5" > /opt/nodejs_version.txt # Used by update scripts: CURRENT=$(cat /opt/nodejs_version.txt) LATEST=$(curl ... # fetch latest) if [[ "$LATEST" != "$CURRENT" ]]; then # Update needed fi ``` ### Integration with Update Functions ```bash # In ct/app.sh: function update_script() { # Check Node version RELEASE=$(curl ... | jq '.version') CURRENT=$(cat /opt/nodejs_version.txt) if [[ "$RELEASE" != "$CURRENT" ]]; then # Use tools.func to upgrade setup_nodejs "$RELEASE" fi } ``` --- ## Best Practices for Integration ### ✅ DO 1. **Call functions in proper order** ```bash pkg_update setup_tool "version" ``` 2. **Use $STD for production** ```bash export STD="silent" pkg_install curl wget ``` 3. **Check for existing installations** ```bash command -v nodejs >/dev/null || setup_nodejs "20" ``` 4. **Coordinate with install.func** ```bash setting_up_container update_os # From install.func setup_nodejs # From tools.func motd_ssh # Back to install.func ``` ### ❌ DON'T 1. **Don't skip pkg_update** ```bash # Bad - may fail due to stale cache pkg_install curl ``` 2. **Don't hardcode versions** ```bash # Bad apt-get install nodejs=20.x # Good setup_nodejs "20" ``` 3. **Don't mix package managers** ```bash # Bad apt-get install curl apk add wget ``` 4. **Don't ignore errors** ```bash # Bad setup_docker || true # Good if ! setup_docker; then msg_error "Docker failed" exit 1 fi ``` --- ## Troubleshooting Integration Issues ### "Package installation fails" - Check: `pkg_update` was called first - Check: Package name is correct for OS - Solution: Manually verify in container ### "Tool not accessible after installation" - Check: Tool added to PATH - Check: Version file created - Solution: `which toolname` to verify ### "Repository conflicts" - Check: No duplicate repositories - Solution: `cleanup_repo_metadata()` before adding ### "Alpine-specific errors when using Debian tools" - Problem: Using tools.func functions on Alpine - Solution: Use alpine-tools.func instead --- **Last Updated**: December 2025 **Maintainers**: community-scripts team **Integration Status**: All components fully integrated ================================================ FILE: docs/misc/tools.func/TOOLS_FUNC_USAGE_EXAMPLES.md ================================================ # tools.func Usage Examples Practical, real-world examples for using tools.func functions in application installation scripts. ## Basic Examples ### Example 1: Simple Package Installation ```bash #!/usr/bin/env bash source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" # Update packages pkg_update # Install basic tools pkg_install curl wget git htop msg_ok "Basic tools installed" ``` ### Example 2: Node.js Application ```bash #!/usr/bin/env bash source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" setting_up_container network_check update_os msg_info "Installing Node.js" pkg_update setup_nodejs "20" msg_ok "Node.js installed" msg_info "Downloading application" cd /opt git clone https://github.com/example/app.git cd app npm install msg_ok "Application installed" motd_ssh customize cleanup_lxc ``` --- ## Advanced Examples ### Example 3: PHP + MySQL Web Application ```bash #!/usr/bin/env bash source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" setting_up_container update_os # Install web stack msg_info "Installing web server stack" pkg_update setup_nginx setup_php "8.3" setup_mariadb # Uses distribution packages (recommended) setup_composer msg_ok "Web stack installed" # Download application msg_info "Downloading application" git clone https://github.com/example/php-app /var/www/html/app cd /var/www/html/app # Install dependencies composer install --no-dev # Setup database msg_info "Setting up database" DBPASS=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | head -c13) mysql -e "CREATE DATABASE phpapp; GRANT ALL ON phpapp.* TO 'phpapp'@'localhost' IDENTIFIED BY '$DBPASS';" # Create .env file cat > .env < .env < /etc/systemd/system/nodeapp.service </dev/null 2>&1; then msg_ok "Node.js already installed: $(node --version)" else msg_info "Installing Node.js" setup_nodejs "20" msg_ok "Node.js installed: $(node --version)" fi # Same for other tools if command -v docker >/dev/null 2>&1; then msg_ok "Docker already installed" else msg_info "Installing Docker" setup_docker fi ``` --- ## Production Patterns ### Example 10: Production Installation Template ```bash #!/usr/bin/env bash source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" # === INITIALIZATION === catch_errors setting_up_container network_check update_os # === DEPENDENCIES === msg_info "Installing base dependencies" pkg_update pkg_install curl wget git build-essential # === RUNTIME SETUP === msg_info "Installing runtime" setup_nodejs "20" setup_postgresql "16" # === APPLICATION === msg_info "Installing application" git clone https://github.com/user/app /opt/app cd /opt/app npm install --omit=dev npm run build # === CONFIGURATION === msg_info "Configuring application" # ... configuration steps ... # === SERVICES === msg_info "Setting up services" # ... service setup ... # === FINALIZATION === msg_ok "Installation complete" motd_ssh customize cleanup_lxc ``` --- ## Tips & Best Practices ### ✅ DO ```bash # Use $STD for silent operations $STD apt-get install curl # Use pkg_update before installing pkg_update pkg_install package-name # Chain multiple tools together setup_nodejs "20" setup_php "8.3" setup_mariadb # Distribution packages (recommended) # Check command success if ! setup_docker; then msg_error "Docker installation failed" exit 1 fi ``` ### ❌ DON'T ```bash # Don't hardcode commands apt-get install curl # Bad # Don't skip updates pkg_install package # May fail if cache stale # Don't ignore errors setup_nodejs || true # Silences errors silently # Don't mix package managers apt-get install curl apk add wget # Don't mix! ``` --- **Last Updated**: December 2025 **Examples**: 10 detailed patterns **All examples tested and verified** ================================================ FILE: docs/tools/README.md ================================================ # Tools & Add-ons Documentation (/tools) This directory contains comprehensive documentation for tools, utilities, and add-ons in the `/tools` directory. ## Overview The `/tools` directory contains: - **Proxmox management tools** - Helper scripts for Proxmox administration - **Proxmox VE add-ons** - Extensions and integrations - **Utility scripts** - General-purpose automation tools ## Documentation Structure Tools documentation focuses on purpose, usage, and integration with the main ecosystem. ## Available Tools The `/tools` directory structure includes: ### `/tools/pve/` Proxmox VE management and administration tools: - Container management utilities - VM management helpers - Storage management tools - Network configuration tools - Backup and recovery utilities ### `/tools/addon/` Proxmox add-ons and extensions: - Web UI enhancements - API extensions - Integration modules - Custom scripts ### `/tools/headers/` ASCII art headers and templates for scripts. ## Common Tools & Scripts Examples of tools available: - **Container management** - Batch operations on containers - **VM provisioning** - Automated VM setup - **Backup automation** - Scheduled backups - **Monitoring integration** - Connect to monitoring systems - **Configuration management** - Infrastructure as code - **Reporting tools** - Generate reports and statistics ## Integration Points Tools integrate with: - **build.func** - Main container orchestrator - **core.func** - Utility functions - **error_handler.func** - Error handling - **tools.func** - Package installation ## Contributing Tools To contribute a new tool: 1. Place script in appropriate `/tools/` subdirectory 2. Follow project standards: - Use `#!/usr/bin/env bash` - Source build.func if needed - Handle errors with error_handler.func 3. Document usage in script header comments 4. Submit PR ## Common Tasks - **Create Proxmox management tool** → Study existing tools - **Create add-on** → Follow add-on guidelines - **Integration** → Use build.func and core.func - **Error handling** → Use error_handler.func --- **Last Updated**: December 2025 **Maintainers**: community-scripts team ================================================ FILE: docs/vm/README.md ================================================ # VM Scripts Documentation (/vm) This directory contains comprehensive documentation for virtual machine creation scripts in the `/vm` directory. ## Overview VM scripts (`vm/*.sh`) create full virtual machines (not containers) in Proxmox VE with complete operating systems and cloud-init provisioning. ## Documentation Structure VM documentation parallels container documentation but focuses on VM-specific features. ## Key Resources - **[misc/cloud-init.func/](../misc/cloud-init.func/)** - Cloud-init provisioning documentation - **[CONTRIBUTION_GUIDE.md](../CONTRIBUTION_GUIDE.md)** - Contribution workflow - **[EXIT_CODES.md](../EXIT_CODES.md)** - Exit code reference ## VM Creation Flow ``` vm/OsName-vm.sh (host-side) │ ├─ Calls: build.func (orchestrator) │ ├─ Variables: var_cpu, var_ram, var_disk, var_os │ ├─ Uses: cloud-init.func (provisioning) │ └─ Creates: KVM/QEMU VM │ └─ Boots with: Cloud-init config │ ├─ System phase ├─ Config phase └─ Final phase ``` ## Available VM Scripts See `/vm` directory for all VM creation scripts. Examples: - `ubuntu2504-vm.sh` - Ubuntu 25.04 VM (Latest) - `ubuntu2404-vm.sh` - Ubuntu 24.04 VM (LTS) - `debian-13-vm.sh` - Debian 13 VM (Trixie) - `archlinux-vm.sh` - Arch Linux VM - `haos-vm.sh` - Home Assistant OS - `mikrotik-routeros.sh` - MikroTik RouterOS - `openwrt-vm.sh` - OpenWrt VM - `opnsense-vm.sh` - OPNsense firewall - `umbrel-os-vm.sh` - Umbrel OS VM - And 10+ more... ## VM vs Container | Feature | VM | Container | |---------|:---:|:---:| | Isolation | Full | Lightweight | | Boot Time | Slower | Instant | | Resource Use | Higher | Lower | | Use Case | Full OS | Single app | | Init System | systemd/etc | cloud-init | | Storage | Disk image | Filesystem | ## Quick Start To understand VM creation: 1. Read: [misc/cloud-init.func/README.md](../misc/cloud-init.func/README.md) 2. Study: A similar existing script in `/vm` 3. Understand cloud-init configuration 4. Test locally 5. Submit PR ## Contributing a New VM 1. Create `vm/osname-vm.sh` 2. Use cloud-init for provisioning 3. Follow VM script template 4. Test VM creation and boot 5. Submit PR ## Cloud-Init Provisioning VMs are provisioned using cloud-init: ```yaml #cloud-config hostname: myvm timezone: UTC packages: - curl - wget users: - name: ubuntu ssh_authorized_keys: - ssh-rsa AAAAB3... bootcmd: - echo "VM starting..." runcmd: - apt-get update - apt-get upgrade -y ``` ## Common VM Operations - **Create VM with cloud-init** → [misc/cloud-init.func/](../misc/cloud-init.func/) - **Configure networking** → Cloud-init YAML documentation - **Setup SSH keys** → [misc/cloud-init.func/CLOUD_INIT_FUNC_USAGE_EXAMPLES.md](../misc/cloud-init.func/CLOUD_INIT_FUNC_USAGE_EXAMPLES.md) - **Debug VM creation** → [EXIT_CODES.md](../EXIT_CODES.md) ## VM Templates Common VM templates available: - **Ubuntu LTS** - Latest stable Ubuntu - **Debian Stable** - Latest stable Debian - **OPNsense** - Network security platform - **Home Assistant** - Home automation - **Kubernetes** - K3s lightweight cluster - **Proxmox Backup** - Backup server --- **Last Updated**: December 2025 **Maintainers**: community-scripts team ================================================ FILE: install/2fauth-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: jkrgr0 # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://docs.2fauth.app/ | Github: https://github.com/Bubka/2FAuth 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/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://actualbudget.org/ | Github: https://github.com/actualbudget/actual 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/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://adguard.com/ | Github: https://github.com/AdguardTeam/AdGuardHome 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_amd64.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/community-scripts/ProxmoxVE/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 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/community-scripts/ProxmoxVE/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=Linux64&fromVersion=0" | grep -o 'https://.*\.zip') cd /opt/agentdvr/agent curl -fsSL "$RELEASE" -o $(basename "$RELEASE") $STD unzip Agent_Linux64*.zip chmod +x ./Agent echo $RELEASE >~/.agentdvr rm -rf Agent_Linux64*.zip 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/community-scripts/ProxmoxVE/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_amd64.tar.gz \ "https://github.com/AdguardTeam/AdGuardHome/releases/latest/download/AdGuardHome_linux_amd64.tar.gz" msg_ok "Downloaded AdGuard Home" msg_info "Installing AdGuard Home" $STD tar -xzf /tmp/AdGuardHome_linux_amd64.tar.gz -C /opt $STD rm /tmp/AdGuardHome_linux_amd64.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/community-scripts/ProxmoxVE/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-caddy-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: cobalt (cobaltgit) # License: MIT | https://github.com/community-scripts/ProxmoxVE/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_amd64.tar.gz" -o "xcaddy_${RELEASE:1}_linux_amd64.tar.gz" $STD tar xzf xcaddy_"${RELEASE:1}"_linux_amd64.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/community-scripts/ProxmoxVE/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 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-x86_64 -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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/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}/x86_64-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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/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-it-tools-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: nicedevil007 (NiceDevil) # License: MIT | https://github.com/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/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 http2; listen [::]:443 ssl http2; 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 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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/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-amd64.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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/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-x86_64-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/community-scripts/ProxmoxVE/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/lejianwen/rustdesk-server/releases/download/${RELEASE}/rustdesk-server-linux-amd64.zip" -o "$temp_file1" $STD unzip "$temp_file1" mv amd64 /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-amd64.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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/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_amd64-${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/community-scripts/ProxmoxVE/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-amd64" -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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/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-wireguard-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.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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/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 >/etc/mongod.conf replication: replSetName: "rs0" EOF systemctl restart mongod sleep 3 $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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/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 $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/community-scripts/ProxmoxVE/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-amd64 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/apt-cacher-ng-install.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: 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/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://archivebox.io/ | Github: https://github.com/ArchiveBox/ArchiveBox 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 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/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://release-argus.io/ | Github: https://github.com/release-argus/Argus 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-amd64" 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://helper-scripts.com/ 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/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://aria2.github.io/ | Github: https://github.com/aria2/aria2 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 install -y nginx 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/community-scripts/ProxmoxVE/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/default/audiobookshelf echo "FFPROBE_PATH=/usr/bin/ffprobe" >>/etc/default/audiobookshelf systemctl restart audiobookshelf msg_ok "Setup audiobookshelf" 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/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://www.authelia.com/ | Github: https://github.com/authelia/authelia 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/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://autobrr.com/ | Github: https://github.com/autobrr/autobrr 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_x86_64.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/community-scripts/ProxmoxVE/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-64bit" KEPUB_VERSION="$(/usr/bin/kepubify --version | awk '{print $2}')" fetch_and_deploy_gh_release "calibre" "kovidgoyal/calibre" "prebuild" "latest" "/opt/calibre" "calibre-*-x86_64.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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://garethgeorge.github.io/backrest/ | Github: https://github.com/garethgeorge/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_x86_64.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/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://sabre.io/baikal/ | Github: https://github.com/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/bar-assistant-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: bvdberg01 | CanbiZ # License: MIT | https://github.com/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://www.bazarr.media/ | Github: https://github.com/morpheus65535/bazarr source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os 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/community-scripts/ProxmoxVE/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 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 $STD npm install http-server -g 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 msg_ok "Setup BentoPDF" msg_info "Creating Service" cat </etc/systemd/system/bentopdf.service [Unit] Description=BentoPDF Service After=network.target [Service] Type=simple WorkingDirectory=/opt/bentopdf/dist ExecStart=/usr/bin/npx http-server -g -b -d false -r --no-dotfiles Restart=always RestartSec=10 [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/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://beszel.dev/ | Github: https://github.com/henrygd/beszel 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_amd64.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/community-scripts/ProxmoxVE/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-*-x86_64-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/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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://0xerr0r.github.io/blocky | Github: https://github.com/0xERR0R/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_x86_64.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/booklore-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/booklore-app/BookLore 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" JAVA_VERSION="25" setup_java NODE_VERSION="22" setup_nodejs setup_mariadb setup_yq MARIADB_DB_NAME="booklore_db" MARIADB_DB_USER="booklore_user" MARIADB_DB_EXTRA_GRANTS="GRANT SELECT ON \`mysql\`.\`time_zone_name\`" setup_mariadb_db fetch_and_deploy_gh_release "booklore" "booklore-app/BookLore" "tarball" msg_info "Building Frontend" cd /opt/booklore/booklore-ui $STD npm install --force $STD npm run build --configuration=production msg_ok "Built Frontend" msg_info "Embedding Frontend into Backend" mkdir -p /opt/booklore/booklore-api/src/main/resources/static cp -r /opt/booklore/booklore-ui/dist/booklore/browser/* /opt/booklore/booklore-api/src/main/resources/static/ msg_ok "Embedded Frontend into Backend" msg_info "Creating Environment" mkdir -p /opt/booklore_storage/{data,books,bookdrop} cat </opt/booklore_storage/.env # Database Configuration DATABASE_URL=jdbc:mariadb://localhost:3306/${MARIADB_DB_NAME} DATABASE_USERNAME=${MARIADB_DB_USER} DATABASE_PASSWORD=${MARIADB_DB_PASS} # App Configuration (Spring Boot mapping from app.* properties) APP_PATH_CONFIG=/opt/booklore_storage/data APP_BOOKDROP_FOLDER=/opt/booklore_storage/bookdrop SERVER_PORT=6060 EOF msg_ok "Created Environment" msg_info "Building Backend" cd /opt/booklore/booklore-api APP_VERSION=$(get_latest_github_release "booklore-app/BookLore") yq eval ".app.version = \"${APP_VERSION}\"" -i src/main/resources/application.yaml $STD ./gradlew clean build -x test --no-daemon mkdir -p /opt/booklore/dist JAR_PATH=$(find /opt/booklore/booklore-api/build/libs -maxdepth 1 -type f -name "booklore-api-*.jar" ! -name "*plain*" | head -n1) if [[ -z "$JAR_PATH" ]]; then msg_error "Backend JAR not found" exit 153 fi cp "$JAR_PATH" /opt/booklore/dist/app.jar msg_ok "Built Backend" msg_info "Creating Service" cat </etc/systemd/system/booklore.service [Unit] Description=BookLore Java Service After=network.target mariadb.service [Service] Type=simple User=root WorkingDirectory=/opt/booklore/dist ExecStart=/usr/bin/java -XX:+UseG1GC -XX:+UseStringDeduplication -XX:+UseCompactObjectHeaders -XX:MaxRAMPercentage=75.0 -XX:+ExitOnOutOfMemoryError -jar /opt/booklore/dist/app.jar EnvironmentFile=/opt/booklore_storage/.env SuccessExitStatus=143 TimeoutStopSec=10 Restart=on-failure RestartSec=5 [Install] WantedBy=multi-user.target EOF systemctl enable -q --now booklore 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/community-scripts/ProxmoxVE/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 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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://caddyserver.com/ | Github: https://github.com/caddyserver/caddy 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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://changedetection.io/ | Github: https://github.com/dgtlmoon/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 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 WorkingDirectory=/opt/changedetection Environment=WEBDRIVER_URL=http://127.0.0.1:4444/wd/hub Environment=PLAYWRIGHT_DRIVER_URL=ws://localhost:3000/chrome?launch=eyJkZWZhdWx0Vmlld3BvcnQiOnsiaGVpZ2h0Ijo3MjAsIndpZHRoIjoxMjgwfSwiaGVhZGxlc3MiOmZhbHNlLCJzdGVhbHRoIjp0cnVlfQ==&blockAds=true 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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/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" 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/community-scripts/ProxmoxVE/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_amd64.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/community-scripts/ProxmoxVE/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-amd64.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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://cloudreve.org/ | Github: https://github.com/cloudreve/cloudreve 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_amd64.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/community-scripts/ProxmoxVE/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" \ "amd64" $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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://www.commafeed.com/#/welcome | Github: https://github.com/Athou/commafeed 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/community-scripts/ProxmoxVE/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 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/community-scripts/ProxmoxVE/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 \ 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/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/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://cosmos-cloud.io/ | Github: https://github.com/azukaar/Cosmos-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 \ 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-*-amd64.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/community-scripts/ProxmoxVE/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-amd64/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-amd64/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 sleep 10 { echo "Crafty-Controller-Credentials" echo "Username: $(grep -oP '(?<="username": ")[^"]*' /opt/crafty-controller/crafty/crafty-4/app/config/default-creds.txt)" echo "Password: $(grep -oP '(?<="password": ")[^"]*' /opt/crafty-controller/crafty/crafty-4/app/config/default-creds.txt)" } >>~/crafty-controller.creds 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/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://cronicle.net/ | Github: https://github.com/jhuckaby/Cronicle 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/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://www.cross-seed.org | Github: https://github.com/cross-seed/cross-seed 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/community-scripts/ProxmoxVE/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/daemonsync-install.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: https://daemonsync.me/ 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 g++-multilib msg_ok "Installed Dependencies" msg_info "Installing Daemon Sync Server" curl -fsSL "https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/tools/addon/daemonsync_2.2.0.0059_amd64.deb" -o "daemonsync_2.2.0.0059_amd64.deb" $STD dpkg -i daemonsync_2.2.0.0059_amd64.deb rm -rf daemonsync_2.2.0.0059_amd64.deb msg_ok "Installed Daemon Sync Server" 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/community-scripts/ProxmoxVE/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.14.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)" cd /opt/databasus/frontend $STD npm ci $STD npm 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 </opt/databasus/.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 chown postgres:postgres /opt/databasus/.env chmod 600 /opt/databasus/.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=/opt/databasus/.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/community-scripts/ProxmoxVE/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) RELEASE=$(get_latest_github_release "Freika/dawarich") cat </opt/dawarich/.env RAILS_ENV=production SECRET_KEY_BASE=${SECRET_KEY_BASE} 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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/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" libssl=$(curl -fsSL "http://security.ubuntu.com/ubuntu/pool/main/o/openssl/" | grep -o 'libssl1\.1_1\.1\.1f-1ubuntu2\.2[^"]*amd64\.deb' | head -n1) curl -fsSL "http://security.ubuntu.com/ubuntu/pool/main/o/openssl/$libssl" -o "$libssl" $STD dpkg -i "$libssl" $STD apt 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/community-scripts/ProxmoxVE/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 | Co-Author: nickheyer # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://discopanel.app/ | Github: https://github.com/nickheyer/discopanel 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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://docmost.com/ | Github: https://github.com/docmost/docmost 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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/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 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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/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_x86_64.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/community-scripts/ProxmoxVE/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/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/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://www.drawio.com/ | Github: https://github.com/jgraph/drawio 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/community-scripts/ProxmoxVE/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-x64-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/community-scripts/ProxmoxVE/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-*_amd64-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/community-scripts/ProxmoxVE/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 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/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://emby.media/ | Github: https://github.com/MediaBrowser/Emby.Releases 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/community-scripts/ProxmoxVE/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-amd64.deb" DEB_FILE="/tmp/emqx-enterprise-${LATEST_VERSION}-debian12-amd64.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/community-scripts/ProxmoxVE/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_gh_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/ersatztv-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: MickLesk (Canbiz) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://ersatztv.org/ | Github: https://github.com/ErsatzTV/ErsatzTV 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-x64.tar.gz" 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 "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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://fileflows.com/ # Import Functions und Setup 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 \ imagemagick msg_ok "Installed Dependencies" setup_hwaccel msg_info "Installing ASP.NET Core Runtime" setup_deb822_repo \ "microsoft" \ "https://packages.microsoft.com/keys/microsoft-2025.asc" \ "https://packages.microsoft.com/debian/13/prod/" \ "trixie" $STD apt install -y aspnetcore-runtime-8.0 msg_ok "Installed ASP.NET Core Runtime" fetch_and_deploy_from_url "https://fileflows.com/downloads/zip" "/opt/fileflows" msg_info "Setup FileFlows" $STD ln -svf /usr/bin/ffmpeg /usr/local/bin/ffmpeg $STD ln -svf /usr/bin/ffprobe /usr/local/bin/ffprobe cd /opt/fileflows/Server dotnet FileFlows.Server.dll --systemd install --root true systemctl enable -q --now fileflows msg_ok "Setup FileFlows" 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/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://firefly-iii.org/ | Github: https://github.com/firefly-iii/firefly-iii 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/fladder-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2025 community-scripts ORG # Author: wendyliga # License: MIT | https://github.com/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/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 \ xvfb 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 rm /etc/apt/sources.list.d/google-chrome.list msg_ok "Installed Chrome" fetch_and_deploy_gh_release "flaresolverr" "FlareSolverr/FlareSolverr" "prebuild" "latest" "/opt/flaresolverr" "flaresolverr_linux_x64.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=/opt/flaresolverr/flaresolverr 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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://flowiseai.com/ | Github: https://github.com/FlowiseAI/Flowise source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os NODE_VERSION="20" setup_nodejs msg_info "Installing FlowiseAI (Patience)" $STD npm install -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=npx 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/community-scripts/ProxmoxVE/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/forgejo-install.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: 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-amd64" 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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/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 community-scripts ORG # Authors: MickLesk (CanbiZ) | Co-Authors: remz1337 # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://frigate.video/ | Github: https://github.com/blakeblackshear/frigate 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 export TARGETARCH="amd64" 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.0" "/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_amd64" 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 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 "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 "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: /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/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://fumadocs.vercel.app/ | Github: https://github.com/fuma-nama/fumadocs 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/community-scripts/ProxmoxVE/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}/x86_64-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/community-scripts/ProxmoxVE/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/ghost-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: fabrice1236 # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://ghost.org/ | Github: https://github.com/TryGhost/Ghost 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" 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/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://ghostfol.io/ | Github: https://github.com/ghostfolio/ghostfolio 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" DB_NAME=ghostfolio DB_USER=ghostfolio DB_PASS=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | head -c13) 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) $STD sudo -u postgres psql -c "CREATE DATABASE $DB_NAME;" $STD sudo -u postgres psql -c "CREATE USER $DB_USER WITH ENCRYPTED 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 USER $DB_USER CREATEDB;" $STD sudo -u postgres psql -d $DB_NAME -c "GRANT ALL 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 ALL ON TABLES TO $DB_USER;" $STD sudo -u postgres psql -d $DB_NAME -c "ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON SEQUENCES TO $DB_USER;" { echo "Ghostfolio Credentials" echo "Database User: $DB_USER" echo "Database Password: $DB_PASS" echo "Database Name: $DB_NAME" 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://$DB_USER:$DB_PASS@localhost:5432/$DB_NAME?connect_timeout=300&sslmode=prefer 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/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://about.gitea.com/ | Github: https://github.com/go-gitea/gitea 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-amd64" 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/community-scripts/ProxmoxVE/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/glance-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: kristocopani # License: MIT | https://github.com/community-scripts/ProxmoxVE/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-amd64.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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/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 msg_info "Installing Dependencies" $STD apt install -y ffmpeg msg_ok "Installed Dependencies" USE_ORIGINAL_FILENAME="true" fetch_and_deploy_gh_release "go2rtc" "AlexxIT/go2rtc" "singlefile" "latest" "/opt/go2rtc" "go2rtc_linux_amd64" 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_amd64 [Install] WantedBy=multi-user.target EOF systemctl enable -q --now go2rtc 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/community-scripts/ProxmoxVE/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_amd64.zip" msg_info "Configuring Gokapi" mkdir -p /opt/gokapi/{data,config} chmod +x /opt/gokapi/gokapi-linux_amd64 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_amd64 [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/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://gotify.net/ | Github: https://github.com/gotify/server 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-amd64.zip" chmod +x /opt/gotify/gotify-linux-amd64 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-amd64 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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/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.0" 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 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/community-scripts/ProxmoxVE/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 install:ee $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/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://grocy.info/ | Github: https://github.com/grocy/grocy 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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://healthchecks.io/ | Github: https://github.com/healthchecks/healthchecks 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/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://heimdall.site/ | Github: https://github.com/linuxserver/Heimdall 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/community-scripts/ProxmoxVE/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-x86_64" 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/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://www.hivemq.com/ | Github: https://github.com/hivemq/hivemq-community-edition 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/community-scripts/ProxmoxVE/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-amd64.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 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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/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_x86_64.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/community-scripts/ProxmoxVE/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/homepage-install.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: https://gethomepage.dev/ | Github: https://github.com/gethomepage/homepage 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 $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/community-scripts/ProxmoxVE/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/hortusfox-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/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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/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 "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/immich-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://immich.app | Github: https://github.com/immich-app/immich source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os if [ -d /dev/dri ]; 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) Intel OpenVINO (requires GPU passthrough)" echo "" read -r -p "${TAB3}Select machine-learning type [1]: " ML_TYPE ML_TYPE="${ML_TYPE:-1}" if [[ "$ML_TYPE" == "2" ]]; then msg_info "Installing OpenVINO dependencies" touch ~/.openvino $STD apt install -y --no-install-recommends patchelf 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 OpenVINO dependencies" 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=amd64] 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_*_amd64.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:=$(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:=$(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:=$(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.5.6" "$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" 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 -R immich:immich "$INSTALL_DIR" 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" $STD uv add --no-sync --optional openvino onnxruntime-openvino==1.24.1 --active -n -p "${ML_PYTHON}" --managed-python 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 patchelf --clear-execstack "${VIRTUAL_ENV}/lib/python3.13/site-packages/onnxruntime/capi/onnxruntime_pybind11_state.cpython-313-x86_64-linux-gnu.so" 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 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} EnvironmentFile=${INSTALL_DIR}/.env ExecStart=/usr/bin/node ${APP_DIR}/dist/main 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 "$INSTALL_DIR" /var/log/immich 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 $STD apt remove -y dotnet-sdk-8.0 $STD apt autoremove -y 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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/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_amd64.deb" "${HOME}/chronograf_1.10.8_amd64.deb" $STD dpkg -i "${HOME}/chronograf_1.10.8_amd64.deb" rm -rf "${HOME}/chronograf_1.10.8_amd64.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/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://www.inspircd.org/ | Github: https://github.com/inspircd/inspircd 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 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/community-scripts/ProxmoxVE/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 "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/community-scripts/ProxmoxVE/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 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/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://invoiceninja.com/ | Github: https://github.com/invoiceninja/invoiceninja 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/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://www.iobroker.net/#en/intro | Github: https://github.com/ioBroker/ioBroker.js-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 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="22" 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/itsm-ng-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: Florianb63 # License: MIT | https://github.com/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/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.LinuxAMDx64.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/community-scripts/ProxmoxVE/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 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVE/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/x86_64-linux-gnu/libjemalloc.so.2 /usr/lib/libjemalloc.so fi msg_ok "Installed Dependencies" 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/community-scripts/ProxmoxVE/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/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/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://joplinapp.org/ | Github: https://github.com/laurent22/joplin 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/community-scripts/ProxmoxVE/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 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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://karakeep.app/ | Github: https://github.com/karakeep-app/karakeep 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-x86_64" 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/community-scripts/ProxmoxVE/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_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_URL" ]] || [[ -z "$KASM_VERSION" ]]; 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/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://www.kavitareader.com/ | Github: https://github.com/Kareadita/Kavita 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-x64.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/community-scripts/ProxmoxVE/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-amd64" 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/community-scripts/ProxmoxVE/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="20" 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/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://www.kimai.org/ | Github: https://github.com/kimai/kimai 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/community-scripts/ProxmoxVE/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 mkdir -p /nltk_data $STD uv run python -m nltk.downloader -d /nltk_data averaged_perceptron_tagger_eng 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/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://koel.dev/ | Github: https://github.com/koel/koel 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/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://koillection.github.io/ | Github: https://github.com/benjaminjonard/koillection 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/community-scripts/ProxmoxVE/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 -p "${TAB3}Enter your TMDb API key: " TMDBKEY read -p "${TAB3}Enter your Plex URL: " PLEXURL read -p "${TAB3}Enter your Plex token: " PLEXTOKEN sed -i -e "s#url: http://192.168.1.12:32400#url: $PLEXURL #g" /opt/kometa/config/config.yml sed -i -e "s/token: ####################/token: $PLEXTOKEN/g" /opt/kometa/config/config.yml sed -i -e "s/apikey: ################################/apikey: $TMDBKEY/g" /opt/kometa/config/config.yml 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 systemctl enable -q --now kometa 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/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://komga.org/ | Github: https://github.com/gotson/komga 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/x86_64-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/community-scripts/ProxmoxVE/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-amd64.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"]' msg_ok "Configured 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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/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 $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/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://leantime.io | Github: https://github.com/Leantime/leantime 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/librenms-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: michelroegl-brunner # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://www.librenms.org/ | Github: https://github.com/librenms/librenms 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/community-scripts/ProxmoxVE/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-x86_64-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/community-scripts/ProxmoxVE/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 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/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://lidarr.audio/ | Github: https://github.com/Lidarr/Lidarr 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 msg_ok "Installed Dependencies" fetch_and_deploy_gh_release "lidarr" "Lidarr/Lidarr" "prebuild" "latest" "/opt/Lidarr" "Lidarr.master*linux-core-x64.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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://linkding.link/ | Github: https://github.com/sissbruecker/linkding 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" msg_info "Building Frontend" cd /opt/linkding $STD npm ci $STD npm run build ln -sf /usr/lib/x86_64-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/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://linkstack.org/ | Github: https://github.com/linkstackorg/linkstack 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/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://linkwarden.app/ | Github: https://github.com/linkwarden/linkwarden 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/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://listmonk.app/ | Github: https://github.com/knadh/listmonk 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_amd64.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/litellm-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: stout01 # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/BerriAI/litellm 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 msg_ok "Installed Dependencies" PG_VERSION="17" setup_postgresql PG_DB_NAME="litellm_db" PG_DB_USER="litellm" setup_postgresql_db PYTHON_VERSION="3.13" USE_UVX="YES" setup_uv msg_info "Setting up Virtual Environment" mkdir -p /opt/litellm cd /opt/litellm $STD uv venv --clear /opt/litellm/.venv $STD /opt/litellm/.venv/bin/python -m ensurepip --upgrade $STD /opt/litellm/.venv/bin/python -m pip install --upgrade pip $STD /opt/litellm/.venv/bin/python -m pip install litellm[proxy] prisma msg_ok "Installed LiteLLM" msg_info "Configuring LiteLLM" mkdir -p /opt cat </opt/litellm/litellm.yaml general_settings: master_key: sk-1234 database_url: postgresql://$PG_DB_USER:$PG_DB_PASS@127.0.0.1:5432/$PG_DB_NAME store_model_in_db: true EOF uv --directory=/opt/litellm run litellm --config /opt/litellm/litellm.yaml --use_prisma_db_push --skip_server_startup msg_ok "Configured LiteLLM" msg_info "Creating Service" cat </etc/systemd/system/litellm.service [Unit] Description=LiteLLM [Service] Type=simple ExecStart=uv --directory=/opt/litellm run litellm --config /opt/litellm/litellm.yaml Restart=always [Install] WantedBy=multi-user.target EOF systemctl enable -q --now litellm 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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://lubelogger.com/ | Github: https://github.com/hargata/lubelog 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/lyrionmusicserver-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: Omar Minaya # License: MIT | https://github.com/community-scripts/ProxmoxVE/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[^"]*amd64\.deb(?="[^>]*>)' | head -n 1) RELEASE=$(echo "$DEB_URL" | grep -oP 'lyrionmusicserver_\K[0-9.]+(?=_amd64\.deb)') DEB_FILE="/tmp/lyrionmusicserver_${RELEASE}_amd64.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/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://mafl.hywax.space/ | Github: https://github.com/hywax/mafl 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/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://magicmirror.builders/ | Github: https://github.com/MagicMirrorOrg/MagicMirror 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/community-scripts/ProxmoxVE/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" 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 \ dotnet-sdk-10.0 \ aspnetcore-runtime-8.0 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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/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/matterbridge-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: MickLesk (Canbiz) # License: MIT | https://github.com/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://mealie.io | Github: https://github.com/mealie-recipes/mealie 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" "latest" "/opt/mealie" 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 $STD sed -i "s|https://github.com/mealie-recipes/mealie/commit/|https://github.com/mealie-recipes/mealie/releases/tag/|g" /opt/mealie/frontend/pages/admin/site-settings.vue $STD sed -i "s|value: data.buildId,|value: \"v${MEALIE_VERSION}\",|g" /opt/mealie/frontend/pages/admin/site-settings.vue $STD sed -i "s|value: data.production ? i18n.t(\"about.production\") : i18n.t(\"about.development\"),|value: \"bare-metal\",|g" /opt/mealie/frontend/pages/admin/site-settings.vue $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" msg_info "Downloading NLTK Data" mkdir -p /nltk_data/ cd /opt/mealie $STD uv run python -m nltk.downloader -d /nltk_data averaged_perceptron_tagger_eng msg_ok "Downloaded 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/community-scripts/ProxmoxVE/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 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/community-scripts/ProxmoxVE/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_amd64.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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://www.meilisearch.com/ | Github: https://github.com/meilisearch/meilisearch 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/community-scripts/ProxmoxVE/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 fetch_and_deploy_gh_release "memos" "usememos/memos" "prebuild" "latest" "/opt/memos" "memos*linux_amd64.tar.gz" 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/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://meshcentral.com/ | Github: https://github.com/Ylianst/MeshCentral 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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/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 $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/community-scripts/ProxmoxVE/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: amd64 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/miniflux-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: omernaveedxyz # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://miniflux.app/ | Github: https://github.com/miniflux/v2 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/community-scripts/ProxmoxVE/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-amd64/minio" else RELEASE="$FEATURE_RICH_VERSION" DOWNLOAD_URL="https://dl.min.io/server/minio/release/linux-amd64/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/mongodb-install.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: https://www.mongodb.com/de-de source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os 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/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://www.monicahq.com/ | Github: https://github.com/monicahq/monica 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@helper-scripts.com --password=helper-scripts.com --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/community-scripts/ProxmoxVE/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" 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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://ipcheck.ing/ | Github: https://github.com/jason5ng32/MyIP 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 "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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://n8n.io/ | Github: https://github.com/n8n-io/n8n 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/navidrome-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/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/community-scripts/ProxmoxVE/main/tools/addon/filebrowser.sh)" fi 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/community-scripts/ProxmoxVE/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/netbox-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: bvdberg01 # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://netboxlabs.com/ | Github: https://github.com/netbox-community/netbox 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/community-scripts/ProxmoxVE/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/nextpvr-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://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/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://nginxui.com | Github: https://github.com/0xJacky/nginx-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 \ nginx \ logrotate msg_ok "Installed Dependencies" fetch_and_deploy_gh_release "nginx-ui" "0xJacky/nginx-ui" "prebuild" "latest" "/opt/nginx-ui" "nginx-ui-linux-64.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/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://nginxproxymanager.com/ | Github: https://github.com/NginxProxyManager/nginx-proxy-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 update $STD apt -y install \ ca-certificates \ 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 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" msg_info "Installing Openresty" curl -fsSL "https://openresty.org/package/pubkey.gpg" | gpg --dearmor -o /etc/apt/trusted.gpg.d/openresty.gpg cat <<'EOF' >/etc/apt/sources.list.d/openresty.sources Types: deb URIs: http://openresty.org/package/debian/ Suites: bookworm Components: openresty Signed-By: /etc/apt/trusted.gpg.d/openresty.gpg EOF $STD apt update $STD apt -y install openresty msg_ok "Installed Openresty" NODE_VERSION="22" NODE_MODULE="yarn" setup_nodejs RELEASE=$(curl -fsSL https://api.github.com/repos/NginxProxyManager/nginx-proxy-manager/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4) }') 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 "s|\"version\": \"2.0.0\"|\"version\": \"$RELEASE\"|" /opt/nginxproxymanager/backend/package.json sed -i "s|\"version\": \"2.0.0\"|\"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 systemctl restart openresty 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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://www.nocodb.com/ | Github: https://github.com/nocodb/nocodb 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" "latest" "/opt/nocodb/" "Noco-linux-x64" 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/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://nodered.org/ | Github: https://github.com/node-red/node-red 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/community-scripts/ProxmoxVE/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 "helper-scripts\r" } expect "Administrator email address" { send "helper-scripts@local.com\r" } expect "Password" { send "helper-scripts\r" } expect "Confirm Password" { send "helper-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/community-scripts/ProxmoxVE/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" 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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/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-x86_64 -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=helper-scripts.com\"]) " /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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/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/linux/nxwitness-server-[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+-linux_x64\.deb" | head -n 1) curl -fsSL "$DOWNLOAD_URL" -o ""nxwitness-server-$RELEASE-linux_x64.deb"" export DEBIAN_FRONTEND=noninteractive $STD dpkg -i nxwitness-server-$RELEASE-linux_x64.deb rm -f /tmp/nxwitness-server-$RELEASE-linux_x64.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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/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-amd64.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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/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 "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 cat </etc/apt/sources.list.d/intel-gpu.sources Types: deb URIs: https://repositories.intel.com/gpu/ubuntu Suites: jammy Components: client Architectures: amd64 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 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 msg_ok "Installed Intel® oneAPI Base Toolkit" msg_info "Installing Ollama (Patience)" RELEASE=$(curl -fsSL https://api.github.com/repos/ollama/ollama/releases/latest | grep "tag_name" | awk -F '"' '{print $4}') OLLAMA_INSTALL_DIR="/usr/local/lib/ollama" BINDIR="/usr/local/bin" mkdir -p $OLLAMA_INSTALL_DIR OLLAMA_URL="https://github.com/ollama/ollama/releases/download/${RELEASE}/ollama-linux-amd64.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/community-scripts/ProxmoxVE/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+deb11u5_amd64.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" 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/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://ombi.io/ | Github: https://github.com/Ombi-app/Ombi 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-x64.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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://openarchiver.com/ | Github: https://github.com/LogicLabs-OU/OpenArchiver 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/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://opencloud.eu | Github: https://github.com/opencloud-eu/opencloud 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.2.0" "/usr/bin" "opencloud-*-linux-amd64" mv /usr/bin/OpenCloud /usr/bin/opencloud 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/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://opengist.io/ | Github: https://github.com/thomiceli/opengist 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-amd64.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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/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-amd64.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/community-scripts/ProxmoxVE/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/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/community-scripts/ProxmoxVE/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: amd64 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-amd64.tar.zst tar --zstd -C /usr -xf ollama-linux-amd64.tar.zst rm -rf ollama-linux-amd64.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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/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_amd64.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/community-scripts/ProxmoxVE/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="22" 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/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://owncast.online/ | Github: https://github.com/owncast/owncast 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-64bit.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/pairdrop-install.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: https://pairdrop.net/ | Github: https://github.com/schlagmichdoch/PairDrop 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/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://pangolin.net/ | Github: https://github.com/fosrl/pangolin 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_amd64" fetch_and_deploy_gh_release "traefik" "traefik/traefik" "prebuild" "latest" "/usr/bin" "traefik_v*_linux_amd64.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 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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://docs.paperless-ngx.com/ | Github: https://github.com/paperless-ngx/paperless-ngx 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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://docs.part-db.de/ | Github: https://github.com/Part-DB/Part-DB-server source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os NODE_VERSION="22" NODE_MODULE="yarn@latest" setup_nodejs 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 msg_info "Installing Part-DB (Patience)" cd /opt RELEASE=$(get_latest_github_release "Part-DB/Part-DB-server") curl -fsSL "https://github.com/Part-DB/Part-DB-server/archive/refs/tags/v${RELEASE}.zip" -o "/opt/v${RELEASE}.zip" $STD unzip "v${RELEASE}.zip" mv /opt/Part-DB-server-${RELEASE}/ /opt/partdb 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 yarn install $STD yarn build $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 rm -rf "/opt/v${RELEASE}.zip" echo "${RELEASE}" >~/.partdb 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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/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 \ build-essential \ nginx \ redis-server msg_ok "Installed Dependencies" NODE_VERSION="24" setup_nodejs PG_VERSION="17" setup_postgresql PG_DB_NAME="patchmon_db" PG_DB_USER="patchmon_usr" setup_postgresql_db fetch_and_deploy_gh_release "PatchMon" "PatchMon/PatchMon" "tarball" "v1.4.2" "/opt/patchmon" msg_info "Configuring PatchMon" VERSION=$(get_latest_github_release "PatchMon/PatchMon") export NODE_ENV=production cd /opt/patchmon $STD npm install --no-audit --no-fund --no-save --ignore-scripts cd /opt/patchmon/frontend cat <./.env VITE_APP_NAME=PatchMon VITE_APP_VERSION=${VERSION} EOF $STD npm install --no-audit --no-fund --no-save --ignore-scripts --include=dev $STD npm run build JWT_SECRET="$(openssl rand -hex 64)" mv /opt/patchmon/backend/env.example /opt/patchmon/backend/.env sed -i -e "s|DATABASE_URL=.*|DATABASE_URL=\"postgresql://$PG_DB_USER:$PG_DB_PASS@localhost:5432/$PG_DB_NAME\"|" \ -e "/JWT_SECRET/s/[=$].*/=$JWT_SECRET/" \ -e "\|CORS_ORIGIN|s|localhost|$LOCAL_IP|" \ -e "/PORT=3001/aSERVER_PROTOCOL=http \\ SERVER_HOST=$LOCAL_IP \\ SERVER_PORT=3000" \ -e '/_ENV=production/aTRUST_PROXY=1' \ -e '/REDIS_USER=.*/,+1d' /opt/patchmon/backend/.env cd /opt/patchmon/backend $STD npm run db:generate $STD npx prisma migrate deploy msg_ok "Configured PatchMon" msg_info "Configuring Nginx" cp /opt/patchmon/docker/nginx.conf.template /etc/nginx/sites-available/patchmon.conf sed -i -e 's|proxy_pass .*|proxy_pass http://127.0.0.1:3001;|' \ -e '\|try_files |i\ root /opt/patchmon/frontend/dist;' \ -e 's|alias.*|alias /opt/patchmon/frontend/dist/assets;|' \ -e '\|expires 1y|i\ root /opt/patchmon/frontend/dist;' /etc/nginx/sites-available/patchmon.conf ln -sf /etc/nginx/sites-available/patchmon.conf /etc/nginx/sites-enabled/ rm -f /etc/nginx/sites-enabled/default $STD nginx -t systemctl restart nginx msg_ok "Configured Nginx" msg_info "Creating service" cat </etc/systemd/system/patchmon-server.service [Unit] Description=PatchMon Service After=network.target postgresql.service [Service] Type=simple WorkingDirectory=/opt/patchmon/backend ExecStart=/usr/bin/npm run start Restart=always RestartSec=10 Environment=NODE_ENV=production Environment=PATH=/usr/bin:/usr/local/bin 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/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://www.paymenter.org | Github: https://github.com/paymenter/paymenter 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 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/community-scripts/ProxmoxVE/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 cat </etc/peanut/settings.yml WEB_HOST: 0.0.0.0 WEB_PORT: 8080 NUT_HOST: 0.0.0.0 NUT_PORT: 3493 EOF ln -sf /etc/peanut/settings.yml /opt/peanut/.next/standalone/config/settings.yml 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 Environment="NODE_ENV=production" #Environment="NUT_HOST=localhost" #Environment="NUT_PORT=3493" #Environment="WEB_HOST=0.0.0.0" #Environment="WEB_PORT=8080" 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/community-scripts/ProxmoxVE/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 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/community-scripts/ProxmoxVE/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_amd64" 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/pf2etools-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: TheRealVira # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://pf2etools.com/ | Github: https://github.com/Pf2eToolsOrg/Pf2eTools 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 \ ca-certificates \ git msg_ok "Installed Dependencies" NODE_VERSION="22" setup_nodejs fetch_and_deploy_gh_release "pf2etools" "Pf2eToolsOrg/Pf2eTools" "tarball" "latest" "/opt/Pf2eTools" msg_info "Configuring Pf2eTools" cd /opt/Pf2eTools $STD npm install $STD npm run build msg_ok "Configured Pf2eTools" msg_info "Creating Service" cat <>/etc/apache2/apache2.conf SetHandler server-status Order deny,allow Allow from all EOF rm -rf /var/www/html ln -s "/opt/Pf2eTools" /var/www/html chown -R www-data: "/opt/Pf2eTools" chmod -R 755 "/opt/Pf2eTools" msg_ok "Created Service" cleanup_lxc motd_ssh customize ================================================ FILE: install/photoprism-install.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: https://www.photoprism.app/ | Github: https://github.com/photoprism/photoprism 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-amd64.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-amd64-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://Helper-Scripts.com' 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/community-scripts/ProxmoxVE/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 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/x86/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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://plant-it.org/ | Github: https://github.com/MDeLuise/plant-it 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 community-scripts ORG # Author: tteck (tteckster) | MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://pocketbase.io/ | Github: https://github.com/pocketbase/pocketbase 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_amd64.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/community-scripts/ProxmoxVE/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-amd64" 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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://privatebin.info/ | Github: https://github.com/PrivateBin/PrivateBin 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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://www.projectsend.org/ | Github: https://github.com/projectsend/projectsend 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/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://prometheus.io/ | Github: https://github.com/prometheus/alertmanager 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-amd64.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/community-scripts/ProxmoxVE/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-amd64.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/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://prometheus.io/ | Github: https://github.com/prometheus/prometheus 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-amd64.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 ExecReload=/bin/kill -HUP $MAINPID [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/community-scripts/ProxmoxVE/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/prowlarr-install.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: https://prowlarr.com/ | Github: https://github.com/Prowlarr/Prowlarr 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" fetch_and_deploy_gh_release "prowlarr" "Prowlarr/Prowlarr" "prebuild" "latest" "/opt/Prowlarr" "Prowlarr.master*linux-core-x64.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/community-scripts/ProxmoxVE/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 Proxmox Backup Server" curl -fsSL "https://enterprise.proxmox.com/debian/proxmox-release-trixie.gpg" -o "/etc/apt/trusted.gpg.d/proxmox-release-trixie.gpg" cat <>/etc/apt/sources.list deb http://download.proxmox.com/debian/pbs trixie pbs-no-subscription EOF $STD apt update export DEBIAN_FRONTEND=noninteractive export IFUPDOWN2_NO_IFRELOAD=1 $STD apt install -y proxmox-backup-server 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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/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 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/community-scripts/ProxmoxVE/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_amd64" 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/community-scripts/ProxmoxVE/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-amd64.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/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/community-scripts/ProxmoxVE-Local 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/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://www.qbittorrent.org/ | Github: https://github.com/qbittorrent/qBittorrent 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" "x86_64-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/community-scripts/ProxmoxVE/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" "binary" "latest" "/usr/bin/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/community-scripts/ProxmoxVE/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_x86_64.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/community-scripts/ProxmoxVE/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 apt-transport-https msg_ok "Installed Dependencies" setup_deb822_repo \ "rabbitmq" \ "https://keys.openpgp.org/vks/v1/by-fingerprint/0A9AF2115F4687BD29803A206B73A36E6026DFCA" \ "https://deb1.rabbitmq.com/rabbitmq-server/debian/trixie" \ "trixie" msg_info "Setting up RabbitMQ" $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 $STD apt install -y --fix-missing rabbitmq-server msg_ok "Setup RabbitMQ " msg_info "Starting 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/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://radarr.video/ | Github: https://github.com/Radarr/Radarr 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" fetch_and_deploy_gh_release "Radarr" "Radarr/Radarr" "prebuild" "latest" "/opt/Radarr" "Radarr.master*linux-core-x64.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/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://radicale.org/ | Github: https://github.com/Kozea/Radicale 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/community-scripts/ProxmoxVE/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-amd64.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/community-scripts/ProxmoxVE/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" setup_deb822_repo \ "microsoft" \ "https://packages.microsoft.com/keys/microsoft-2025.asc" \ "https://packages.microsoft.com/debian/13/prod/" \ "trixie" $STD apt install -y aspnetcore-runtime-10.0 msg_ok "Installed Dependencies" 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 | Rewrite: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://rxresume.org | Github: https://github.com/amruthpillai/reactive-resume 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" $STD apt install -y chromium 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 "Configuring Reactive Resume" 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/community-scripts/ProxmoxVE/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 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=x64' -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/community-scripts/ProxmoxVE/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-amd64" 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/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://recyclarr.dev/wiki/ | Github: https://github.com/recyclarr/recyclarr 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 "recyclarr" "recyclarr/recyclarr" "prebuild" "latest" "/usr/local/bin" "recyclarr-linux-x64.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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/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 \ rabbitmq-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 msg_info "Configuring RabbitMQ" RABBIT_USER="reitti" RABBIT_PASS="$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | cut -c1-13)" RABBIT_VHOST="/" $STD rabbitmqctl add_user "$RABBIT_USER" "$RABBIT_PASS" $STD rabbitmqctl add_vhost "$RABBIT_VHOST" $STD rabbitmqctl set_permissions -p "$RABBIT_VHOST" "$RABBIT_USER" ".*" ".*" ".*" $STD rabbitmqctl set_user_tags "$RABBIT_USER" administrator { echo "" echo "Reitti Credentials" echo "RabbitMQ User: $RABBIT_USER" echo "RabbitMQ Password: $RABBIT_PASS" } >>~/reitti.creds msg_ok "Configured RabbitMQ" 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 USE_ORIGINAL_FILENAME="true" fetch_and_deploy_gh_release "photon" "komoot/photon" "singlefile" "latest" "/opt/photon" "photon-*.jar" mv /opt/photon/photon-*.jar /opt/photon/photon.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_info "Installed Nginx Tile Cache" msg_info "Creating Reitti Configuration-File" mkdir -p /opt/reitti/data cat </opt/reitti/application.properties # Reitti Server Base URI reitti.server.advertise-uri=http://127.0.0.1:8080 # PostgreSQL Database Connection 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.driver-class-name=org.postgresql.Driver # Flyway Database Migrations spring.flyway.enabled=true spring.flyway.locations=classpath:db/migration spring.flyway.baseline-on-migrate=true # RabbitMQ (Message Queue) spring.rabbitmq.host=127.0.0.1 spring.rabbitmq.port=5672 spring.rabbitmq.username=$RABBIT_USER spring.rabbitmq.password=$RABBIT_PASS # Redis (Cache) spring.data.redis.host=127.0.0.1 spring.data.redis.port=6379 # Server Port server.port=8080 # Optional: Logging & Performance logging.level.root=INFO spring.jpa.hibernate.ddl-auto=none spring.datasource.hikari.maximum-pool-size=10 # OIDC / Security Settings reitti.security.oidc.registration.enabled=false # Photon (Geocoding) PHOTON_BASE_URL=http://127.0.0.1:2322 PROCESSING_WAIT_TIME=15 PROCESSING_BATCH_SIZE=1000 PROCESSING_WORKERS_PER_QUEUE=4-16 # Disable potentially dangerous features unless needed DANGEROUS_LIFE=false # Tiles Cache reitti.ui.tiles.cache.url=http://127.0.0.1 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 rabbitmq-server.service photon.service Wants=postgresql.service redis-server.service rabbitmq-server.service photon.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 cat </etc/systemd/system/photon.service [Unit] Description=Photon Geocoding Service (Germany, OpenSearch) After=network.target [Service] Type=simple WorkingDirectory=/opt/photon ExecStart=/usr/bin/java -Xmx4g -jar photon.jar \ -data-dir /opt/photon \ -listen-port 2322 \ -listen-ip 0.0.0.0 \ -cors-any Restart=on-failure TimeoutStopSec=20 [Install] WantedBy=multi-user.target EOF systemctl enable -q --now photon 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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/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 '25s/localhost/0.0.0.0/g' /opt/revealjs/gulpfile.js 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/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://romm.app | Github: https://github.com/rommapp/romm 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" 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 ln -sfn /var/lib/romm/resources /opt/romm/frontend/dist/assets/romm/resources ln -sfn /var/lib/romm/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 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/community-scripts/ProxmoxVE/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" "lejianwen/rustdesk-server" "binary" "latest" "/opt/rustdesk" "rustdesk-server-hbbr*amd64.deb" fetch_and_deploy_gh_release "rustdesk-hbbs" "lejianwen/rustdesk-server" "binary" "latest" "/opt/rustdesk" "rustdesk-server-hbbs*amd64.deb" fetch_and_deploy_gh_release "rustdesk-utils" "lejianwen/rustdesk-server" "binary" "latest" "/opt/rustdesk" "rustdesk-server-utils*amd64.deb" fetch_and_deploy_gh_release "rustdesk-api" "lejianwen/rustdesk-api" "binary" "latest" "/opt/rustdesk" "rustdesk-api-server*amd64.deb" systemctl enable -q --now rustdesk-hbbr systemctl enable -q --now rustdesk-hbbs systemctl enable -q --now rustdesk-api 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/community-scripts/ProxmoxVE/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" "*x86_64-unknown-linux-gnu.tar.gz" fetch_and_deploy_gh_release "rustypaste-cli" "orhun/rustypaste-cli" "prebuild" "latest" "/usr/local/bin" "*x86_64-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/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://sabnzbd.org/ | Github: https://github.com/sabnzbd/sabnzbd 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-amd64.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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/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 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/searxng-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/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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://docs.seerr.dev/ | Github: https://github.com/seerr-team/seerr 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/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://semaphoreui.com/ | Github: https://github.com/semaphoreui/semaphore 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_amd64.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@helper-scripts.com --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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/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="22" setup_nodejs PYTHON_VERSION="3.12" 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" cd /opt/shelfmark $STD uv venv --clear ./venv $STD source ./venv/bin/activate $STD uv pip install -r ./requirements-base.txt [[ "$DEPLOYMENT_TYPE" == "1" ]] && $STD uv pip install -r ./requirements-shelfmark.txt 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/community-scripts/ProxmoxVE/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 777 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/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/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://signoz.io/ | Github: https://github.com/SigNoz/signoz 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: amd64 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_amd64.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_amd64.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_amd64.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/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://silverbullet.md | Github: https://github.com/silverbulletmd/silverbullet 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-x86_64.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/community-scripts/ProxmoxVE/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-x64.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..." exit 1 else source /opt/soularr/venv/bin/activate uv run python3 -u /opt/soularr/soularr.py --config-dir /opt/soularr fi 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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://snipeitapp.com/ | Github: https://github.com/grokability/snipe-it 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/community-scripts/ProxmoxVE/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/sonarqube-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: prop4n # License: MIT | https://github.com/community-scripts/ProxmoxVE/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 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-x86-64/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-x86-64/sonar.sh start ExecStop=/opt/sonarqube/bin/linux-x86-64/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/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://sonarr.tv/ | Github: https://github.com/Sonarr/Sonarr 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" fetch_and_deploy_gh_release "Sonarr" "Sonarr/Sonarr" "prebuild" "latest" "/opt/Sonarr" "Sonarr.main.*.linux-x64.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/community-scripts/ProxmoxVE/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/sparkyfitness-install.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 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|^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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/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-x64-*.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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/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/stirling-pdf-install.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: https://www.stirlingpdf.com/ | Github: https://github.com/Stirling-Tools/Stirling-PDF 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/strapi-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: pespinel # License: MIT | https://github.com/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/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_amd64" 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/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://sure.am | Github: https://github.com/we-promise/sure 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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://tandoor.dev/ | Github: https://github.com/TandoorRecipes/recipes 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 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/community-scripts/ProxmoxVE/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 777 /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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://tautulli.com/ | Github: https://github.com/Tautulli/Tautulli 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/community-scripts/ProxmoxVE/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_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 "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/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/community-scripts/ProxmoxVE/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_amd64-\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_amd64-${RELEASE}.tar.bz2" -o ts3server.tar.bz2 tar -xf ./ts3server.tar.bz2 mv teamspeak3-server_linux_amd64/ /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/community-scripts/ProxmoxVE/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 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-9.0 msg_ok "Installed Dependencies" RELEASE=$(curl -fsSL https://technitium.com/dns/ | grep -oP 'Version \K[\d.]+') msg_info "Installing Technitium DNS" mkdir -p /opt/technitium/dns curl -fsSL "https://download.technitium.com/dns/DnsServerPortable.tar.gz" -o /opt/DnsServerPortable.tar.gz $STD tar zxvf /opt/DnsServerPortable.tar.gz -C /opt/technitium/dns/ rm -f /opt/DnsServerPortable.tar.gz echo "${RELEASE}" >~/.technitium msg_ok "Installed Technitium DNS" msg_info "Creating 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/community-scripts/ProxmoxVE/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 \ ca-certificates msg_ok "Installed Dependencies" fetch_and_deploy_gh_release "teddycloud" "toniebox-reverse-engineering/teddycloud" "prebuild" "latest" "/opt/teddycloud" "teddycloud.amd64.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/community-scripts/ProxmoxVE/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/termix-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/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" 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 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/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://thelounge.chat/ | Github: https://github.com/thelounge/thelounge 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" 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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/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_amd64" 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/community-scripts/ProxmoxVE/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 "Setting up AppRise" $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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://www.traccar.org/ | Github: https://github.com/traccar/traccar 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-64*.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/community-scripts/ProxmoxVE/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) 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/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://tracktor.bytedge.in | Github: https://github.com/javedh-dev/tracktor 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/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://traefik.io/ | Github: https://github.com/traefik/traefik 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_amd64.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/community-scripts/ProxmoxVE/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/trilium-install.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: 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-x64.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/community-scripts/ProxmoxVE/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/tududi-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://tududi.com/ | Github: https://github.com/chrisvel/tududi 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/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://tunarr.com/ | Github: https://github.com/chrisbenincasa/tunarr 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-x64.tar.gz" cd /opt/tunarr mv tunarr* tunarr 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/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/community-scripts/ProxmoxVE/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/typesense-install.sh ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 community-scripts ORG # Author: tlissak # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://typesense.org/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors setting_up_container network_check update_os msg_info "Installing TypeSense" RELEASE=$(curl -fsSL https://api.github.com/repos/typesense/typesense/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4) }') cd /opt curl -fsSL "https://dl.typesense.org/releases/${RELEASE}/typesense-server-${RELEASE}-amd64.deb" -o "/opt/typesense-server-${RELEASE}-amd64.deb" $STD apt install -y /opt/typesense-server-${RELEASE}-amd64.deb echo 'enable-cors = true' >>/etc/typesense/typesense-server.ini rm -rf /opt/typesense-server-${RELEASE}-amd64.deb echo "${RELEASE}" >"/opt/${APPLICATION}_version.txt" msg_ok "Installed TypeSense" 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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://www.uhfapp.com/server | Github: https://github.com/swapplications/uhf-server-dist 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" 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-x64-*.zip" fetch_and_deploy_gh_release "uhf-server" "swapplications/uhf-server-dist" "prebuild" "latest" "/opt/uhf-server" "UHF.Server-linux-x64-*.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/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://umami.is/ | Github: https://github.com/umami-software/umami 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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/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 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/community-scripts/ProxmoxVE/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-x64")) | 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/community-scripts/ProxmoxVE/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 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/community-scripts/ProxmoxVE/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_amd64.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/community-scripts/ProxmoxVE/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_amd64.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/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://uptime.kuma.pet/ | Github: https://github.com/louislam/uptime-kuma 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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://verdaccio.org/ | Github: https://github.com/verdaccio/verdaccio 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/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/community-scripts/ProxmoxVE/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-amd64-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-amd64-v[0-9.]+\.tar\.gz$') msg_ok "Got latest version of VictoriaMetrics" fetch_and_deploy_gh_release "victoriametrics" "VictoriaMetrics/VictoriaMetrics" "prebuild" "latest" "/opt/victoriametrics" "$victoriametrics_filename" fetch_and_deploy_gh_release "vmutils" "VictoriaMetrics/VictoriaMetrics" "prebuild" "latest" "/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-amd64-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-amd64-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/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://vikunja.io/ | Github: https://github.com/go-vikunja/vikunja 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/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://wallabag.org/ | Github: https://github.com/wallabag/wallabag 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/community-scripts/ProxmoxVE/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" 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/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://wanderer.to | Github: https://github.com/open-wanderer/wanderer 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" "binary" "latest" "/opt/wanderer/source/search" 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 </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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/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}_x86_64-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] WorkingDirectory=/opt/wastebin ExecStart=/opt/wastebin/wastebin EnvironmentFile=/opt/wastebin-data/.env [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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://wealthfolio.app/ | Github: https://github.com/afadil/wealthfolio 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="20" NODE_MODULE="pnpm" setup_nodejs fetch_and_deploy_gh_release "wealthfolio" "afadil/wealthfolio" "tarball" "v3.0.3" 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/community-scripts/ProxmoxVE/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 \ make \ g++ \ traceroute \ xvfb \ dbus \ xorg \ xvfb \ 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: amd64 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" "CrazyWolf13/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 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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/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 msg_ok "Installed Dependencies" fetch_and_deploy_from_url "https://whisparr.servarr.com/v1/update/nightly/updatefile?os=linux&runtime=netcore&arch=x64" /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/wikijs-install.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: https://js.wiki/ | Github: https://github.com/requarks/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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/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" 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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/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_amd64.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/yt-dlp-webui-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/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-amd64" 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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/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 \ 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/community-scripts/ProxmoxVE/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_amd64.deb dpkg -i ztncui_0.8.14_amd64.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/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://www.zigbee2mqtt.io/ | Github: https://github.com/Koenkk/zigbee2mqtt 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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://zitadel.com/ | Github: https://github.com/zitadel/zitadel 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 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-amd64.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 ================================================ #!/usr/bin/env bash # Copyright (c) 2021-2026 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://zoraxy.aroz.org/ | Github: https://github.com/tobychui/zoraxy 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_amd64" 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/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://zotregistry.dev/ | Github: https://github.com/project-zot/zot 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-amd64" 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/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://zwave-js.github.io/zwave-js-ui/#/ | Github: https://github.com/zwave-js/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-linux 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/alpine-install.func ================================================ # Copyright (c) 2021-2026 community-scripts ORG # Author: tteck (tteckster) # Co-Author: MickLesk # License: MIT | https://github.com/community-scripts/ProxmoxVE/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 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 ipv4_status="${GN}✔${CL} IPv4" else ipv4_status="${RD}✖${CL} IPv4" read -r -p "Internet NOT connected. 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 RESOLVEDIP=$(getent hosts github.com | awk '{ print $1 }') if [[ -z "$RESOLVEDIP" ]]; then msg_error "Internet: ${ipv4_status} DNS Failed" else msg_ok "Internet: ${ipv4_status} DNS: ${BL}${RESOLVEDIP}${CL}" fi set -e trap 'error_handler $LINENO "$BASH_COMMAND"' ERR } # This function updates the Container OS by running apt-get update and upgrade update_os() { msg_info "Updating Container OS" $STD apk -U upgrade 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 ORG ${YW}| GitHub: ${GN}https://github.com/community-scripts/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 1 } 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 1 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 1 } else curl -fL# -o "$out" "$url" || { msg_error "Download failed: $url" return 1 } 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 1 } need_tool curl jq || return 1 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 1 } 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 1 } need_tool curl jq tar || return 1 [ "$mode" = "prebuild" ] || [ "$mode" = "singlefile" ] && need_tool unzip >/dev/null 2>&1 || true tmpd="$(mktemp -d)" || return 1 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 1 } else json="$(curl -fsSL "https://api.github.com/repos/$repo/releases/tags/$version")" || { msg_error "GitHub API failed" rm -rf "$tmpd" return 1 } 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 1 } 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 1 } tar -xzf "$tmpd/$filename" -C "$tmpd" || { msg_error "tar extract failed" rm -rf "$tmpd" return 1 } 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 1 } ;; prebuild) [ -n "$pattern" ] || { msg_error "prebuild requires asset pattern" rm -rf "$tmpd" return 1 } 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 1 } filename="${url##*/}" download_with_progress "$url" "$tmpd/$filename" || { rm -rf "$tmpd" return 1 } # unpack archive (Zip or tarball) case "$filename" in *.zip) need_tool unzip || { rm -rf "$tmpd" return 1 } 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 1 ;; 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 1 } else (cd "$tmpd/unp" && tar -cf - .) | (cd "$target" && tar -xf -) || { msg_error "copy failed" rm -rf "$tmpd" return 1 } fi ;; singlefile) [ -n "$pattern" ] || { msg_error "singlefile requires asset pattern" rm -rf "$tmpd" return 1 } 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 1 } filename="${url##*/}" download_with_progress "$url" "$target/$app" || { rm -rf "$tmpd" return 1 } chmod +x "$target/$app" ;; *) msg_error "Unknown mode: $mode" rm -rf "$tmpd" return 1 ;; 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 1 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 1 ;; esac url="https://github.com/mikefarah/yq/releases/latest/download/yq_linux_${arch}" tmp="$(mktemp)" download_with_progress "$url" "$tmp" || return 1 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 1 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 1 } 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 1 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 1 ;; 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 1 } 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 1 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 1 } tar -xzf "$tmpd/uv.tar.gz" -C "$tmpd" || { msg_error "uv: extract failed" rm -rf "$tmpd" return 1 } # tar contains ./uv if [ -x "$tmpd/uv" ]; then install -m 0755 "$tmpd/uv" "$UV_BIN" else # fallback: in subfolder install -m 0755 "$tmpd"/*/uv "$UV_BIN" 2>/dev/null || { msg_error "uv binary not found in tar" rm -rf "$tmpd" return 1 } 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 1 } 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 1 } 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 1 } # 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 1 } msg_ok "Go ready: $(go version 2>/dev/null)" return 0 fi need_tool curl tar || return 1 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 1 ;; 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 1 rm -rf /usr/local/go tar -C /usr/local -xzf "$TMP" || { msg_error "extract go failed" rm -f "$TMP" return 1 } 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 1 } } 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 1 curl -fsSL https://getcomposer.org/installer -o /tmp/composer-setup.php || { msg_error "composer installer download failed" return 1 } php /tmp/composer-setup.php --install-dir=/usr/local/bin --filename=composer >/dev/null 2>&1 || { msg_error "composer install failed" return 1 } 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 # ------------------------------------------------------------------------------ json_escape() { # Escape a string for safe JSON embedding using awk (handles any input size). # Pipeline: strip ANSI → remove control chars → escape \ " TAB → join lines with \n printf '%s' "$1" \ | sed 's/\x1b\[[0-9;]*[a-zA-Z]//g' \ | tr -d '\000-\010\013\014\016-\037\177\r' \ | awk ' BEGIN { ORS = "" } { gsub(/\\/, "\\\\") # backslash → \\ gsub(/"/, "\\\"") # double quote → \" gsub(/\t/, "\\t") # tab → \t if (NR > 1) printf "\\n" printf "%s", $0 }' } # ------------------------------------------------------------------------------ # 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) 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 ' ') 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) 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 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 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 [[ "$http_code" =~ ^2[0-9]{2}$ ]] && break [[ "$attempt" -lt 3 ]] && sleep 1 done POST_TO_API_DONE=true } # ------------------------------------------------------------------------------ # 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" [[ "$http_code" =~ ^2[0-9]{2}$ ]] && break [[ "$attempt" -lt 3 ]] && sleep 1 done POST_TO_API_DONE=true } # ------------------------------------------------------------------------------ # 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() { 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 '"') os_version=$(grep "^VERSION_ID=" /etc/os-release | cut -d= -f2 | tr -d '"') 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/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/community-scripts/ProxmoxVE/main/misc/api.func) if command -v curl >/dev/null 2>&1; then 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 elif command -v wget >/dev/null 2>&1; then source <(wget -qO- https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/core.func) source <(wget -qO- https://raw.githubusercontent.com/community-scripts/ProxmoxVE/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) # Fallback to IPv6 if no IPv4 if [[ -z "$CURRENT_IP" ]]; then CURRENT_IP=$(hostname -I 2>/dev/null | tr ' ' '\n' | grep -E ':' | head -n1) 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}')" # 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 1 } 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 1 } 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 "$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##*/}" # 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 1 ;; 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 1 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"} IPV6_STATIC=${var_ipv6_static:-""} 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 if ! curl -s --connect-timeout 2 "http://${APT_CACHER_IP}:3142" >/dev/null 2>&1; then msg_warn "APT Cacher configured but not reachable at ${APT_CACHER_IP}:3142" msg_custom "⚠️" "${YW}" "Disabling APT Cacher for this installation" APT_CACHER="" APT_CACHER_IP="" else msg_ok "APT Cacher verified at ${APT_CACHER_IP}:3142" fi fi MTU=${var_mtu:-""} SD=${var_searchdomain:-""} 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,${var_tags:-}" 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"} 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_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 ) # 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_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_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_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 ) # 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 1 } # 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 - with example) # var_apt_cacher=yes # var_apt_cacher_ip=192.168.1.10 # 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= 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 1 } 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) 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_gpu var_gateway var_hostname var_ipv6_method var_mac var_mtu var_net var_ns var_os var_pw var_ram var_tags var_tun var_unprivileged var_verbose var_version var_vlan var_ssh var_ssh_authorized_key var_container_storage var_template_storage ) 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")" } >"$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")" 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") 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-) ct=$(grep -E '^var_container_storage=' "$vf" | cut -d= -f2-) 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 TAGS="community-script;${var_tags:-}" local STEP=1 local MAX_STEP=28 # 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}" # 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/^[- ]*//') 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) 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\nNote: Automatically enabled for unprivileged containers.\n\n(App default: ${var_keyctl:-0})" 16 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 server IP address:" 10 58 "$_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 _mount_fs="$result" ((STEP++)) else ((STEP--)) fi ;; # ═══════════════════════════════════════════════════════════════════════════ # STEP 28: Verbose Mode & Confirmation # ═══════════════════════════════════════════════════════════════════════════ 28) 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 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 GPU: $_enable_gpu | Protection: $protect_desc Advanced: Timezone: $tz_display APT Cacher: $apt_display Verbose: $_verbose" 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" # 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="" # Alpine UDHCPC fix if [ "$var_os" == "alpine" ] && [ "$NET" == "dhcp" ] && [ -n "$_ns" ]; then UDHCPC_FIX="yes" else UDHCPC_FIX="no" fi export UDHCPC_FIX 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}" [[ "${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 "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" 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)" 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-) [[ -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-) [[ -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 } # ------------------------------------------------------------------------------ # 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/community-scripts/ProxmoxVE/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 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 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=*) 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) NET_STRING="$NET_STRING,ip6=$IPV6_ADDR" [ -n "$IPV6_GATE" ] && NET_STRING="$NET_STRING,gw6=$IPV6_GATE" ;; 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 # Build PCT_OPTIONS as string for export TEMP_DIR=$(mktemp -d) pushd "$TEMP_DIR" >/dev/null local _func_url if [ "$var_os" == "alpine" ]; then _func_url="https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/alpine-install.func" else _func_url="https://raw.githubusercontent.com/community-scripts/ProxmoxVE/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..20}; 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 sleep 1 done if [ -z "$ip_in_lxc" ]; then msg_error "No IP assigned to CT $CTID after 20s" 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) 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 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" >>"$BUILD_LOG" 2>&1 || { msg_error "Failed to install base packages in Alpine container" install_exit_code=1 } else sleep 3 LANG=${LANG:-en_US.UTF-8} 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 "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 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 || { msg_error "apt-get base packages installation failed" install_exit_code=1 } 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/community-scripts/ProxmoxVE/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 # 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" # 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 # 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 "" # Detect error type for smart recovery options 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 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 # Exit 1 subclassification: analyze logs to identify actual root cause # Many exit 1 errors are actually APT, OOM, network, or command-not-found issues 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 # 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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/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!" 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 1 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 1 ;; 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 1 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 # 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 1 ;; 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 2 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 2 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 1 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"; } # Offer upgrade for pve-container/lxc-pve if candidate > installed; optional auto-retry pct create # Returns: # 0 = no upgrade needed # 1 = upgraded (and if do_retry=yes and retry succeeded, creation done) # 2 = user declined # 3 = upgrade attempted but failed OR retry failed 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 _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_info "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 read -rp "Do you want to upgrade now? [y/N] " _ans >"$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 } # ------------------------------------------------------------------------------ # 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 unset CTID msg_error "Cannot use ID that is already in use." exit 206 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'" STORAGE_TYPE=$(grep -E "^[^:]+: $CONTAINER_STORAGE$" /etc/pve/storage.cfg | cut -d: -f1 | head -1) if [[ -z "$STORAGE_TYPE" ]]; then msg_error "Storage '$CONTAINER_STORAGE' not found in /etc/pve/storage.cfg" 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." 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) 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 # ------------------------------------------------------------------------------ TEMPLATE_SEARCH="${PCT_OSTYPE}-${PCT_OSVERSION:-}" case "$PCT_OSTYPE" in debian | ubuntu) TEMPLATE_PATTERN="-standard_" ;; alpine | fedora | rocky | centos) TEMPLATE_PATTERN="-default_" ;; *) TEMPLATE_PATTERN="" ;; esac msg_info "Searching for template '$TEMPLATE_SEARCH'" # Initialize variables ONLINE_TEMPLATE="" ONLINE_TEMPLATES=() # Step 1: Check local templates first (instant) mapfile -t LOCAL_TEMPLATES < <( pveam list "$TEMPLATE_STORAGE" 2>/dev/null | awk -v search="${TEMPLATE_SEARCH}" -v pattern="${TEMPLATE_PATTERN}" '$1 ~ search && $1 ~ pattern {print $1}' | sed 's|.*/||' | sort -t - -k 2 -V ) # Step 2: If local template found, use it immediately (skip pveam update) if [[ ${#LOCAL_TEMPLATES[@]} -gt 0 ]]; then TEMPLATE="${LOCAL_TEMPLATES[-1]}" TEMPLATE_SOURCE="local" msg_ok "Template search completed" else # Step 3: No local template - need to check online (this may be slow) msg_info "No local template found, checking online catalog..." # Update catalog with timeout to prevent long hangs if command -v timeout &>/dev/null; then if ! timeout 30 pveam update >/dev/null 2>&1; then msg_warn "Template catalog update timed out (possible network/DNS issue). Run 'pveam update' manually to diagnose." fi else pveam update >/dev/null 2>&1 || msg_warn "Could not update template catalog (pveam update failed)" fi ONLINE_TEMPLATES=() mapfile -t ONLINE_TEMPLATES < <(pveam available -section system 2>/dev/null | grep -E '\.(tar\.zst|tar\.xz|tar\.gz)$' | awk '{print $2}' | grep -E "^${TEMPLATE_SEARCH}.*${TEMPLATE_PATTERN}" | sort -t - -k 2 -V 2>/dev/null || true) [[ ${#ONLINE_TEMPLATES[@]} -gt 0 ]] && ONLINE_TEMPLATE="${ONLINE_TEMPLATES[-1]}" TEMPLATE="$ONLINE_TEMPLATE" TEMPLATE_SOURCE="online" msg_ok "Template search completed" fi # If still no template, try to find alternatives if [[ -z "$TEMPLATE" ]]; then msg_warn "No template found for ${PCT_OSTYPE} ${PCT_OSVERSION}, searching for alternatives..." # Get all available versions for this OS type AVAILABLE_VERSIONS=() mapfile -t AVAILABLE_VERSIONS < <( pveam available -section system 2>/dev/null | grep -E '\.(tar\.zst|tar\.xz|tar\.gz)$' | awk -F'\t' '{print $1}' | grep "^${PCT_OSTYPE}-" | sed -E "s/.*${PCT_OSTYPE}-([0-9]+(\.[0-9]+)?).*/\1/" | sort -u -V 2>/dev/null ) if [[ ${#AVAILABLE_VERSIONS[@]} -gt 0 ]]; then echo "" echo "${BL}Available ${PCT_OSTYPE} versions:${CL}" for i in "${!AVAILABLE_VERSIONS[@]}"; do echo " [$((i + 1))] ${AVAILABLE_VERSIONS[$i]}" done echo "" read -p "Select version [1-${#AVAILABLE_VERSIONS[@]}] or press Enter to cancel: " choice /dev/null | grep -E '\.(tar\.zst|tar\.xz|tar\.gz)$' | awk '{print $2}' | grep -E "^${TEMPLATE_SEARCH}-.*${TEMPLATE_PATTERN}" | sort -t - -k 2 -V 2>/dev/null || true ) if [[ ${#ONLINE_TEMPLATES[@]} -gt 0 ]]; then TEMPLATE="${ONLINE_TEMPLATES[-1]}" TEMPLATE_SOURCE="online" else msg_error "No templates available for ${PCT_OSTYPE} ${PCT_OSVERSION}" exit 225 fi else msg_custom "🚫" "${YW}" "Installation cancelled" exit 0 fi else msg_error "No ${PCT_OSTYPE} templates available at all" exit 225 fi fi TEMPLATE_PATH="$(pvesm path $TEMPLATE_STORAGE:vztmpl/$TEMPLATE 2>/dev/null || true)" if [[ -z "$TEMPLATE_PATH" ]]; then TEMPLATE_BASE=$(awk -v s="$TEMPLATE_STORAGE" '$1==s {f=1} f && /path/ {print $2; exit}' /etc/pve/storage.cfg) [[ -n "$TEMPLATE_BASE" ]] && TEMPLATE_PATH="$TEMPLATE_BASE/template/cache/$TEMPLATE" fi # If we still don't have a path but have a valid template name, construct it if [[ -z "$TEMPLATE_PATH" && -n "$TEMPLATE" ]]; then TEMPLATE_PATH="/var/lib/vz/template/cache/$TEMPLATE" fi [[ -n "$TEMPLATE_PATH" ]] || { if [[ -z "$TEMPLATE" ]]; then msg_error "Template ${PCT_OSTYPE} ${PCT_OSVERSION} not available" # Get available versions mapfile -t AVAILABLE_VERSIONS < <( pveam available -section system 2>/dev/null | grep "^${PCT_OSTYPE}-" | sed -E 's/.*'"${PCT_OSTYPE}"'-([0-9]+\.[0-9]+).*/\1/' | grep -E '^[0-9]+\.[0-9]+$' | sort -u -V 2>/dev/null || sort -u ) if [[ ${#AVAILABLE_VERSIONS[@]} -gt 0 ]]; then echo -e "\n${BL}Available versions:${CL}" for i in "${!AVAILABLE_VERSIONS[@]}"; do echo " [$((i + 1))] ${AVAILABLE_VERSIONS[$i]}" done echo "" read -p "Select version [1-${#AVAILABLE_VERSIONS[@]}] or Enter to exit: " choice /dev/null | awk -v search="${TEMPLATE_SEARCH}-" -v pattern="${TEMPLATE_PATTERN}" '$1 ~ search && $1 ~ pattern {print $1}' | sed 's|.*/||' | sort -t - -k 2 -V ) mapfile -t ONLINE_TEMPLATES < <( pveam available -section system 2>/dev/null | grep -E '\.(tar\.zst|tar\.xz|tar\.gz)$' | awk '{print $2}' | grep -E "^${TEMPLATE_SEARCH}-.*${TEMPLATE_PATTERN}" | sort -t - -k 2 -V 2>/dev/null || true ) ONLINE_TEMPLATE="" [[ ${#ONLINE_TEMPLATES[@]} -gt 0 ]] && ONLINE_TEMPLATE="${ONLINE_TEMPLATES[-1]}" if [[ ${#LOCAL_TEMPLATES[@]} -gt 0 ]]; then TEMPLATE="${LOCAL_TEMPLATES[-1]}" TEMPLATE_SOURCE="local" else TEMPLATE="$ONLINE_TEMPLATE" TEMPLATE_SOURCE="online" fi TEMPLATE_PATH="$(pvesm path $TEMPLATE_STORAGE:vztmpl/$TEMPLATE 2>/dev/null || true)" if [[ -z "$TEMPLATE_PATH" ]]; then TEMPLATE_BASE=$(awk -v s="$TEMPLATE_STORAGE" '$1==s {f=1} f && /path/ {print $2; exit}' /etc/pve/storage.cfg) [[ -n "$TEMPLATE_BASE" ]] && TEMPLATE_PATH="$TEMPLATE_BASE/template/cache/$TEMPLATE" fi # If we still don't have a path but have a valid template name, construct it if [[ -z "$TEMPLATE_PATH" && -n "$TEMPLATE" ]]; then TEMPLATE_PATH="/var/lib/vz/template/cache/$TEMPLATE" fi [[ -n "$TEMPLATE_PATH" ]] || { msg_error "Template still not found after version change" exit 220 } else msg_custom "🚫" "${YW}" "Installation cancelled" exit 0 fi else msg_error "No ${PCT_OSTYPE} templates available" exit 220 fi fi } # Validate that we found a template if [[ -z "$TEMPLATE" ]]; then msg_error "No template found for ${PCT_OSTYPE} ${PCT_OSVERSION}" msg_custom "ℹ️" "${YW}" "Please check:" msg_custom " •" "${YW}" "Is pveam catalog available? (run: pveam available -section system)" msg_custom " •" "${YW}" "Does the template exist for your OS version?" exit 225 fi msg_ok "Template ${BL}$TEMPLATE${CL} [$TEMPLATE_SOURCE]" msg_debug "Resolved TEMPLATE_PATH=$TEMPLATE_PATH" NEED_DOWNLOAD=0 if [[ ! -f "$TEMPLATE_PATH" ]]; then msg_info "Template not present locally – will download." NEED_DOWNLOAD=1 elif [[ ! -r "$TEMPLATE_PATH" ]]; then msg_error "Template file exists but is not readable – check permissions." exit 221 elif [[ "$(stat -c%s "$TEMPLATE_PATH")" -lt 1000000 ]]; then if [[ -n "$ONLINE_TEMPLATE" ]]; then msg_warn "Template file too small (<1MB) – re-downloading." NEED_DOWNLOAD=1 else msg_warn "Template looks too small, but no online version exists. Keeping local file." fi elif ! tar -tf "$TEMPLATE_PATH" &>/dev/null; then if [[ -n "$ONLINE_TEMPLATE" ]]; then msg_warn "Template appears corrupted – re-downloading." NEED_DOWNLOAD=1 else msg_warn "Template appears corrupted, but no online version exists. Keeping local file." fi else $STD msg_ok "Template $TEMPLATE is present and valid." fi if [[ "$TEMPLATE_SOURCE" == "local" && -n "$ONLINE_TEMPLATE" && "$TEMPLATE" != "$ONLINE_TEMPLATE" ]]; then msg_warn "Local template is outdated: $TEMPLATE (latest available: $ONLINE_TEMPLATE)" if whiptail --yesno "A newer template is available:\n$ONLINE_TEMPLATE\n\nDo you want to download and use it instead?" 12 70; then TEMPLATE="$ONLINE_TEMPLATE" NEED_DOWNLOAD=1 else msg_custom "ℹ️" "${BL}" "Continuing with local template $TEMPLATE" fi fi if [[ "$NEED_DOWNLOAD" -eq 1 ]]; then [[ -f "$TEMPLATE_PATH" ]] && rm -f "$TEMPLATE_PATH" for attempt in {1..3}; do msg_info "Attempt $attempt: Downloading template $TEMPLATE to $TEMPLATE_STORAGE" if pveam download "$TEMPLATE_STORAGE" "$TEMPLATE" >>"${BUILD_LOG:-/dev/null}" 2>&1; then msg_ok "Template download successful." break fi if [[ $attempt -eq 3 ]]; then msg_error "Failed after 3 attempts. Please check network access, permissions, or manually run:\n pveam download $TEMPLATE_STORAGE $TEMPLATE" exit 222 fi sleep $((attempt * 5)) done fi if ! pveam list "$TEMPLATE_STORAGE" 2>/dev/null | grep -q "$TEMPLATE"; then msg_error "Template $TEMPLATE not available in storage $TEMPLATE_STORAGE after download." exit 223 fi # ------------------------------------------------------------------------------ # 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" pveam download "$TEMPLATE_STORAGE" "$TEMPLATE" >>"${BUILD_LOG:-/dev/null}" 2>&1 || { msg_error "Failed to download template '$TEMPLATE' to storage '$TEMPLATE_STORAGE'" exit 222 } msg_ok "Template downloaded" elif ! tar -tf "$TEMPLATE_PATH" &>/dev/null; then if [[ -n "$ONLINE_TEMPLATE" ]]; then msg_info "Template appears corrupted – re-downloading" rm -f "$TEMPLATE_PATH" pveam download "$TEMPLATE_STORAGE" "$TEMPLATE" >>"${BUILD_LOG:-/dev/null}" 2>&1 || { msg_error "Failed to re-download template '$TEMPLATE'" exit 222 } msg_ok "Template re-downloaded" else msg_warn "Template appears corrupted, but no online version exists. Skipping re-download." fi 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) 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 if template issue - 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" pveam download "$TEMPLATE_STORAGE" "$TEMPLATE" >>"${BUILD_LOG:-/dev/null}" 2>&1 msg_ok "Template re-downloaded" 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_ok "Trying local storage fallback" msg_info "Downloading template to local" pveam download local "$TEMPLATE" >>"${BUILD_LOG:-/dev/null}" 2>&1 msg_ok "Template downloaded to local" else msg_ok "Trying local storage fallback" 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) : ;; # success - container created, continue 2) msg_error "Upgrade declined. Please update and re-run: apt update && apt install --only-upgrade pve-container lxc-pve" _flush_pct_log exit 231 ;; 3) msg_error "Upgrade and/or retry failed. Please inspect: $LOGFILE" _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 "Upgrade declined. Please update and re-run: apt update && apt install --only-upgrade pve-container lxc-pve" _flush_pct_log exit 231 ;; 3) msg_error "Upgrade and/or retry failed. Please inspect: $LOGFILE" _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 # Verify container exists pct list | awk '{print $1}' | grep -qx "$CTID" || { msg_error "Container ID $CTID not listed in 'pct list'. See $LOGFILE" _flush_pct_log exit 215 } # 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) # Generate LXC Description DESCRIPTION=$( cat < Logo

${APP} LXC

spend Coffee

GitHub Discussions Issues 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" } # ============================================================================== # 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 1 fi if [ -n "$gateway" ] && ! validate_ip "$gateway"; then _ci_msg_error "Invalid gateway IP format: $gateway" return 1 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 1 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 1 } # ------------------------------------------------------------------------------ # 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 1 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 1 } # ============================================================================== # 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/community-scripts/ProxmoxVE/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" 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)" != "amd64" ]; then msg_error "This script will not work with PiMox (ARM architecture detected)." 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/community-scripts/ProxmoxVE/main/misc/error_handler.func); then explain_exit_code() { echo "unknown (error_handler.func download failed)"; } fi fi local explanation explanation="$(explain_exit_code "$rc")" printf "\e[?25h" msg_error "in line ${caller_line}: exit code ${rc} (${explanation})" msg_custom "→" "${YWB}" "${cmd}" if [[ -s "$logfile" ]]; then echo -e "\n${TAB}--- Last 20 lines of log ---" tail -n 20 "$logfile" echo -e "${TAB}-----------------------------------" echo -e "${TAB}📋 Full log: ${logfile}\n" fi exit "$rc" fi } # ------------------------------------------------------------------------------ # 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/community-scripts/ProxmoxVE/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 1 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() { 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 1 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 1 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 1 fi if ! chmod 600 "$swap_file"; then msg_error "Failed to set permissions on $swap_file" return 1 fi if ! mkswap "$swap_file"; then msg_error "Failed to format swap file (mkswap failed)" return 1 fi if ! swapon "$swap_file"; then msg_error "Failed to activate swap (swapon failed)" return 1 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 1 } 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 1 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/community-scripts/ProxmoxVE/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 [[ "$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) local active_log="" if 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 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 "installing" records to telemetry API # Catches ALL exit paths: errors, signals, AND clean exits where # post_to_api was called but post_update_to_api was never called if [[ "${POST_TO_API_DONE:-}" == "true" && "${POST_UPDATE_DONE:-}" != "true" ]]; then if [[ $exit_code -ne 0 ]]; then _send_abort_telemetry "$exit_code" elif declare -f post_update_to_api >/dev/null 2>&1; then post_update_to_api "done" "0" 2>/dev/null || true 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/community-scripts/ProxmoxVE/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/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 } # ============================================================================== # 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 # ============================================================================== # ------------------------------------------------------------------------------ # update_os() # # - Updates container OS via apt-get update and dist-upgrade # - Configures APT cacher proxy if CACHER=yes (accelerates package downloads) # - Removes Python EXTERNALLY-MANAGED restrictions for pip # - Sources tools.func for additional setup functions after update # - Uses $STD wrapper to suppress output unless VERBOSE=yes # ------------------------------------------------------------------------------ update_os() { msg_info "Updating Container OS" if [[ "$CACHER" == "yes" ]]; then echo 'Acquire::http::Proxy-Auto-Detect "/usr/local/bin/apt-proxy-detect.sh";' >/etc/apt/apt.conf.d/00aptproxy cat </usr/local/bin/apt-proxy-detect.sh #!/bin/bash if nc -w1 -z "${CACHER_IP}" 3142; then echo -n "http://${CACHER_IP}:3142" 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 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/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 } # ============================================================================== # 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 ORG ${YW}| GitHub: ${GN}https://github.com/community-scripts/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/community-scripts/ProxmoxVE/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/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 1 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 1 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 1 } # ------------------------------------------------------------------------------ # 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; 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 1 } # ------------------------------------------------------------------------------ # 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 1 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 1 } # ------------------------------------------------------------------------------ # 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 1 } # ------------------------------------------------------------------------------ # 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) 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) 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]+') 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 1 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 1 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 1 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 1 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 1 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 1 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 1 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 1 } 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 1 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 1 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 1 ;; 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 1 } } # ------------------------------------------------------------------------------ # 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 1 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 1 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 } # ------------------------------------------------------------------------------ # 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 return 1 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 break done export GITHUB_TOKEN="$token" 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 1 ;; 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 1 ;; 404) msg_error "GitHub repository or release not found (HTTP 404): $url" return 1 ;; 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 1 ;; *) if [[ $attempt -lt $max_retries ]]; then sleep "$retry_delay" ((attempt++)) continue fi msg_error "GitHub API call failed (HTTP $http_code)." return 1 ;; esac ((attempt++)) done msg_error "GitHub API call failed after ${max_retries} attempts: ${url}" return 1 } # ------------------------------------------------------------------------------ # 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 1 ;; 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 1 ;; 404) msg_error "Codeberg repository or release not found (HTTP 404): $url" return 1 ;; 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 1 ;; *) if [[ $attempt -lt $max_retries ]]; then sleep "$retry_delay" continue fi msg_error "Codeberg API call failed (HTTP $http_code)." return 1 ;; esac done msg_error "Codeberg API call failed after ${max_retries} attempts: ${url}" return 1 } 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 1 } # ------------------------------------------------------------------------------ # 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 1 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 1 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 1 fi # Cleanup cleanup_old_repo_files "$name" cleanup_orphaned_sources mkdir -p /etc/apt/keyrings || { msg_error "Failed to create /etc/apt/keyrings" return 1 } # Import GPG key (auto-detect binary vs ASCII-armored format) local tmp_gpg tmp_gpg=$(mktemp) || return 1 curl -fsSL "$gpg_url" -o "$tmp_gpg" || { msg_error "Failed to download GPG key for ${name}" rm -f "$tmp_gpg" return 1 } 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 1 } 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 1 } 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="./" or absolute path) 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 1 } # ------------------------------------------------------------------------------ # 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 1 fi if ! systemctl start "$service" &>/dev/null; then msg_error "Failed to start $service" systemctl status "$service" --no-pager return 1 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 1 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) if ! github_api_call "https://api.github.com/repos/${repo}/tags?per_page=50" "$temp_file"; then rm -f "$temp_file" return 1 fi local tag="" if [[ -n "$prefix" ]]; then tag=$(jq -r --arg p "$prefix" '[.[] | select(.name | startswith($p))][0].name // empty' "$temp_file") else 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 1 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 1 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 1 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 1 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 1 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 1 } # ------------------------------------------------------------------------------ # 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 1 } 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 1 } 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 1 } 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 1 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 1 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 1 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 1 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 1 } 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 1 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 1 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 1 } 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 1 } 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 1 fi else if ! curl -fsSL "$url" | pv -s "$content_length" >"$output"; then msg_error "Download failed: $url" return 1 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 1 } # ------------------------------------------------------------------------------ # 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 1 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 1 fi local tmpdir tmpdir=$(mktemp -d) || return 1 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 1 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 1 } 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 1 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++)) 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 1 fi http_code="${resp:(-3)}" [[ "$http_code" != "200" ]] && { msg_error "Codeberg API returned HTTP $http_code" return 1 } 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 1 fi filename="${url_match##*/}" curl_download "$tmpdir/$filename" "$url_match" || { msg_error "Download failed: $url_match" rm -rf "$tmpdir" return 1 } 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 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 $(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 1 } filename="${asset_url##*/}" curl_download "$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 ]]; 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_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 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 $(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 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 "$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" } # ------------------------------------------------------------------------------ # 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 1 } 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 1 fi fi done return 1 } 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 1 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 1 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=0 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 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=$(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|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 # 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 1 fi filename="${url_match##*/}" curl_download "$tmpdir/$filename" "$url_match" || { msg_error "Download failed: $url_match" rm -rf "$tmpdir" return 1 } 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 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 $(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 1 } filename="${asset_url##*/}" curl_download "$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_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 1 } else msg_error "Inner directory is empty: $inner_dir" rm -rf "$tmpdir" "$unpack_tmp" return 1 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 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 $(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 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 "$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" } # ------------------------------------------------------------------------------ # 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 1 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 1 } $STD systemctl reload apache2 || { msg_error "Failed to reload Apache" return 1 } 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 1 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 1 } rm -f /tmp/composer-setup.php if [[ ! -x "$COMPOSER_BIN" ]]; then msg_error "Composer installation failed" return 1 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 1 fi tar -xf "$TMP_DIR/ffmpeg.tar.xz" -C "$TMP_DIR" || { msg_error "Failed to extract FFmpeg binary" rm -rf "$TMP_DIR" return 1 } 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 libsvtav1-dev zlib1g-dev libnuma-dev libva-dev libdrm-dev ) ;; *) msg_error "Invalid FFMPEG_TYPE: $TYPE" rm -rf "$TMP_DIR" return 1 ;; 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 1 fi tar -xJf "$TMP_DIR/ffmpeg.tar.xz" -C "$TMP_DIR" || { msg_error "Failed to extract FFmpeg binary archive" rm -rf "$TMP_DIR" return 1 } 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 1 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 1 } cd "$TMP_DIR/FFmpeg-"* || { msg_error "Source extraction failed" rm -rf "$TMP_DIR" return 1 } 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 1 fi $STD ./configure "${args[@]}" || { msg_error "FFmpeg configure failed" rm -rf "$TMP_DIR" return 1 } $STD make -j"$(nproc)" || { msg_error "FFmpeg compilation failed" rm -rf "$TMP_DIR" return 1 } $STD make install || { msg_error "FFmpeg installation failed" rm -rf "$TMP_DIR" return 1 } 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 1 } if ! command -v ffmpeg &>/dev/null; then msg_error "FFmpeg installation failed" rm -rf "$TMP_DIR" return 1 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 1 ;; 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 1 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 1 fi $STD tar -C /usr/local -xzf "$TMP_TAR" || { msg_error "Failed to extract Go tarball" rm -f "$TMP_TAR" return 1 } 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 1 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 1 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 1 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 1 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 1 fi cd "$TMP_DIR/ghostscript-${LATEST_VERSION_DOTTED}" || { msg_error "Failed to enter Ghostscript source directory" rm -rf "$TMP_DIR" return 1 } ensure_dependencies build-essential libpng-dev zlib1g-dev $STD ./configure || { msg_error "Ghostscript configure failed" rm -rf "$TMP_DIR" return 1 } $STD make -j"$(nproc)" || { msg_error "Ghostscript compilation failed" rm -rf "$TMP_DIR" return 1 } $STD make install || { msg_error "Ghostscript installation failed" rm -rf "$TMP_DIR" return 1 } 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" } # ══════════════════════════════════════════════════════════════════════════════ # 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" # libigdgmm - bundled in compute-runtime releases fetch_and_deploy_gh_release "libigdgmm12" "intel/compute-runtime" "binary" "latest" "" "libigdgmm12_*_amd64.deb" || true # Intel Graphics Compiler (note: packages have -2 suffix) fetch_and_deploy_gh_release "intel-igc-core" "intel/intel-graphics-compiler" "binary" "latest" "" "intel-igc-core-2_*_amd64.deb" || true fetch_and_deploy_gh_release "intel-igc-opencl" "intel/intel-graphics-compiler" "binary" "latest" "" "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" # libigdgmm first (bundled in compute-runtime releases) fetch_and_deploy_gh_release "libigdgmm12" "intel/compute-runtime" "binary" "latest" "" "libigdgmm12_*_amd64.deb" || true # Intel Graphics Compiler (note: packages have -2 suffix) fetch_and_deploy_gh_release "intel-igc-core" "intel/intel-graphics-compiler" "binary" "latest" "" "intel-igc-core-2_*_amd64.deb" || true fetch_and_deploy_gh_release "intel-igc-opencl" "intel/intel-graphics-compiler" "binary" "latest" "" "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) 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 666 "$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 666 "$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 chmod 666 /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 1 fi tar -xzf "$TMP_DIR/ImageMagick.tar.gz" -C "$TMP_DIR" || { msg_error "Failed to extract ImageMagick" rm -rf "$TMP_DIR" return 1 } cd "$TMP_DIR"/ImageMagick-* || { msg_error "Source extraction failed" rm -rf "$TMP_DIR" return 1 } $STD ./configure --disable-static || { msg_error "ImageMagick configure failed" rm -rf "$TMP_DIR" return 1 } $STD make -j"$(nproc)" || { msg_error "ImageMagick compilation failed" rm -rf "$TMP_DIR" return 1 } $STD make install || { msg_error "ImageMagick installation failed" rm -rf "$TMP_DIR" return 1 } $STD ldconfig /usr/local/lib if [[ ! -x "$BINARY_PATH" ]]; then msg_error "ImageMagick installation failed" rm -rf "$TMP_DIR" return 1 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 1 } # 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 1 upgrade_packages_with_retry "$DESIRED_PACKAGE" || { msg_error "Failed to update Temurin JDK" return 1 } 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 1 # Install with retry logic install_packages_with_retry "$DESIRED_PACKAGE" || { msg_error "Failed to install Temurin JDK $JAVA_VERSION" return 1 } 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 1 } 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 1 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 1 # 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 1 } fi fi # Perform upgrade with retry logic ensure_apt_working || return 1 upgrade_packages_with_retry "mariadb-server" "mariadb-client" || { msg_error "Failed to upgrade MariaDB packages" return 1 } 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 1 } # 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 1 } # 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 1 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 1 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 major version. # # Description: # - Preserves data across installations # - Adds official MongoDB repo # # Variables: # MONGO_VERSION - MongoDB major version to install (e.g. 7.0, 8.0) # ------------------------------------------------------------------------------ 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 1 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 1 ;; 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 1 # Perform upgrade with retry logic upgrade_packages_with_retry "mongodb-org" || { msg_error "Failed to upgrade MongoDB" return 1 } 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 1 } # Setup repository manage_tool_repository "mongodb" "$MONGO_VERSION" "$MONGO_BASE_URL" \ "https://www.mongodb.org/static/pgp/server-${MONGO_VERSION}.asc" || { msg_error "Failed to setup MongoDB repository" return 1 } # Wait for repo to settle $STD apt update || { msg_error "APT update failed — invalid MongoDB repo for ${DISTRO_ID}-${DISTRO_CODENAME}?" return 1 } # Install MongoDB with retry logic install_packages_with_retry "mongodb-org" || { msg_error "Failed to install MongoDB packages" return 1 } if ! command -v mongod >/dev/null 2>&1; then msg_error "MongoDB binary not found after installation" return 1 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 1 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 1 } cache_installed_version "mysql" "$CURRENT_VERSION" msg_ok "Update MySQL $CURRENT_VERSION" return 0 fi # Fresh install from distro repo ensure_apt_working || return 1 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 1 } } } 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 1 } } 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 1 } 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 1 # 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 1 } # 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 1 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 1 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 1 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 1 } fi # Scenario 1: Already installed at target version - just 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 1 # Just update npm to latest $STD npm install -g npm@latest 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 1 } # 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 1 } # 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 1 } # 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 1 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 1 fi # Update to latest npm (with version check to avoid incompatibility) local NPM_VERSION NPM_VERSION=$(npm -v 2>/dev/null || echo "0") if [[ "$NPM_VERSION" != "0" ]]; then $STD npm install -g npm@latest 2>/dev/null || { msg_warn "Failed to update npm to latest version (continuing with bundled npm $NPM_VERSION)" } fi cache_installed_version "nodejs" "$NODE_VERSION" msg_ok "Setup Node.js $NODE_VERSION" fi export NODE_OPTIONS="--max-old-space-size=4096" # 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 1 } # Install global Node modules if [[ -n "$NODE_MODULE" ]]; then IFS=',' read -ra MODULES <<<"$NODE_MODULE" 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 1 } # 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 1 } # 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 1 } fi ensure_apt_working || return 1 $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 1 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 1 } 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 1 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 1 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 1 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 1 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 1 export DEBIAN_FRONTEND=noninteractive install_packages_with_retry "postgresql" "postgresql-client" || { msg_error "Failed to install PostgreSQL from distro repository" return 1 } # 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 1 # 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 1 } $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 1 } 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 1 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 1 fi if ! command -v psql >/dev/null 2>&1; then msg_error "PostgreSQL installed but psql command not found" return 1 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 1 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 1 # 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 1 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 1 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 1 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 1 fi tar -xzf "$TMP_DIR/rbenv.tar.gz" -C "$TMP_DIR" || { msg_error "Failed to extract rbenv" rm -rf "$TMP_DIR" return 1 } 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 1 } # 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 1 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 1 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 1 fi tar -xzf "$TMP_DIR/ruby-build.tar.gz" -C "$TMP_DIR" || { msg_error "Failed to extract ruby-build" rm -rf "$TMP_DIR" return 1 } 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 1 } fi "$RBENV_BIN" global "$RUBY_VERSION" || { msg_error "Failed to set Ruby $RUBY_VERSION as global version" rm -rf "$TMP_DIR" return 1 } 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 ' ') # 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 ' ') 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 ' ') 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" & 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 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 1 } # 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 1 } # 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 1 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 1 } 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 1 # 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 1 } # 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 1 } install_packages_with_retry "clickhouse-server" "clickhouse-client" || { msg_error "Failed to install ClickHouse packages" return 1 } # Verify installation if ! command -v clickhouse-server >/dev/null 2>&1; then msg_error "ClickHouse installation completed but clickhouse-server command not found" return 1 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 1 } 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 1 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 1 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 1 } $STD rustup default "$RUST_TOOLCHAIN" || { msg_error "Failed to set default Rust toolchain" return 1 } } # 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 1 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 1 } 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 1 } 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 1 } msg_ok "Installed $NAME v$VER" else $STD cargo install "$NAME" || { msg_error "Failed to install $NAME" return 1 } 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 1 ;; 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 1 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 1 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 1 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 1 fi # Extract $STD tar -xzf "$TMP_DIR/uv.tar.gz" -C "$TMP_DIR" || { msg_error "Failed to extract uv" return 1 } # 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 1 fi $STD install -m 755 "$UV_BINARY" "$UV_BIN" || { msg_error "Failed to install uv binary" return 1 } 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 1 } 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 1 } 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 1 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 1 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 1 fi chmod +x "$TMP_DIR/yq" mv "$TMP_DIR/yq" "$BINARY_PATH" || { msg_error "Failed to install yq" rm -rf "$TMP_DIR" return 1 } 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 1 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 1 # 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 1 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 1 } 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 1 } 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 1 fi local filename="${url##*/}" msg_info "Downloading from $url" local tmpdir tmpdir=$(mktemp -d) || { msg_error "Failed to create temporary directory" return 1 } curl -fsSL -o "$tmpdir/$filename" "$url" || { msg_error "Download failed: $url" rm -rf "$tmpdir" return 1 } # 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 1 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 1 } } 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 1 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 1 } 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 1 } 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 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"/* "$directory/" || { msg_error "Failed to copy contents to $directory" 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 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 } ================================================ 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 1 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 # Source explain_exit_code if needed if ! declare -f explain_exit_code >/dev/null 2>&1; then source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVE/raw/branch/main/misc/error_handler.func) 2>/dev/null || true fi local explanation="" if declare -f explain_exit_code >/dev/null 2>&1; then explanation="$(explain_exit_code "$rc")" fi printf "\e[?25h" if [[ -n "$explanation" ]]; then msg_error "in line ${caller_line}: exit code ${rc} (${explanation})" else msg_error "in line ${caller_line}: exit code ${rc}" fi msg_custom "→" "${YWB}" "${cmd}" if [[ -s "$logfile" ]]; then echo -e "\n${TAB}--- Last 20 lines of log ---" tail -n 20 "$logfile" echo -e "${TAB}----------------------------\n" fi exit "$rc" fi } # ------------------------------------------------------------------------------ # 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() { DESCRIPTION=$( cat < Logo

${NSAPP} VM

spend Coffee

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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://tailscale.com/ | Github: https://github.com/tailscale/tailscale 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/community-scripts/ProxmoxVE/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/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 "adguardhome-sync" "addon" # 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() { ifconfig | grep -v '127.0.0.1' | grep -Eo 'inet (addr:)?([0-9]*\.){3}[0-9]*' | grep -m1 -Eo '([0-9]*\.){3}[0-9]*' || 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_amd64.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_amd64.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/community-scripts/ProxmoxVE/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 proxmox-helper-scripts -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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/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}" if [ -f ~/.config/code-server/config.yaml ]; then existing_config=true fi curl -fOL https://github.com/coder/code-server/releases/download/v"$VERSION"/code-server_"${VERSION}"_amd64.deb &>/dev/null dpkg -i code-server_"${VERSION}"_amd64.deb &>/dev/null rm -rf code-server_"${VERSION}"_amd64.deb mkdir -p ~/.config/code-server/ systemctl enable -q --now code-server@"$USER" if [ $existing_config = false ]; then cat <~/.config/code-server/config.yaml bind-addr: 0.0.0.0:8680 auth: none password: cert: false EOF fi systemctl restart code-server@"$USER" 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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/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 [helper-scripts.com]: " admin_pass echo "" admin_pass=${admin_pass:-helper-scripts.com} 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/community-scripts/ProxmoxVE/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 </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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/filebrowserspace/quantum 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" # 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-amd64-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-amd64-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: helper-scripts.com EOF msg_ok "Configured with default admin (admin / helper-scripts.com)" 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/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://filebrowser.org/ | Github: https://github.com/filebrowser/filebrowser 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" # 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-amd64-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-amd64-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 init --auth.method=noauth &>/dev/null filebrowser config set --auth.method=noauth &>/dev/null filebrowser users add ID 1 --perm.admin &>/dev/null 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 helper-scripts.com --perm.admin --database "$DB_PATH" &>/dev/null msg_ok "Default authentication configured (admin:helper-scripts.com)" 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/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://nicolargo.github.io/glances/ | Github: https://github.com/nicolargo/glances 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/immich-public-proxy.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/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/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 "immich-public-proxy" "addon" # 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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/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/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 "jellystat" "addon" # 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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/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 } 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 if ! grep -qxF 'COMPOSE_KOMODO_BACKUPS_PATH=/etc/komodo/backups' "$COMPOSE_ENV"; then sed -i '/^COMPOSE_KOMODO_IMAGE_TAG=latest$/a 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 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 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 '/+=') PASSKEY=$(openssl rand -base64 24 | tr -d '/+=') WEBHOOK_SECRET=$(openssl rand -base64 24 | tr -d '/+=') JWT_SECRET=$(openssl rand -base64 24 | tr -d '/+=') sed -i "s/^KOMODO_DB_USERNAME=.*/KOMODO_DB_USERNAME=komodo_admin/" "$COMPOSE_ENV" sed -i "s/^KOMODO_DB_PASSWORD=.*/KOMODO_DB_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_PASSKEY=.*/KOMODO_PASSKEY=${PASSKEY}/" "$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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/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/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 "nextcloud-exporter" "addon" # 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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/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_amd64.deb" -o $(basename "https://github.com/OliveTin/OliveTin/releases/latest/download/OliveTin_linux_amd64.deb") dpkg -i OliveTin_linux_amd64.deb &>/dev/null systemctl enable --now OliveTin &>/dev/null rm OliveTin_linux_amd64.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/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://www.phpmyadmin.net/ | Github: https://github.com/phpmyadmin/phpmyadmin 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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/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 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 "Enter URL of qBittorrent, example: (http://127.0.0.1:8080): " QBITTORRENT_BASE_URL read -erp "Enter qBittorrent username: " QBITTORRENT_USERNAME read -rsp "Enter qBittorrent password: " QBITTORRENT_PASSWORD printf "\n" 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_USERNAME="${QBITTORRENT_USERNAME}" QBITTORRENT_PASSWORD="${QBITTORRENT_PASSWORD}" 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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/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 666 /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/webmin.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: 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/immich-public-proxy ================================================ ____ _ __ ____ __ ___ ____ / _/___ ___ ____ ___ (_)____/ /_ / __ \__ __/ /_ / (_)____ / __ \_________ _ ____ __ / // __ `__ \/ __ `__ \/ / ___/ __ \ / /_/ / / / / __ \/ / / ___/ / /_/ / ___/ __ \| |/_/ / / / _/ // / / / / / / / / / / / /__/ / / / / ____/ /_/ / /_/ / / / /__ / ____/ / / /_/ /> /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/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 "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 if [ -f "/etc/pve/lxc/${container_id}.conf" ] || [ -f "/etc/pve/qemu-server/${container_id}.conf" ]; 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 tteck # Author: tteck (tteckster) # License: MIT # https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/tools/pve/cron-update-lxcs.sh)" clear cat <<"EOF" ______ __ __ __ __ __ _ ________ / ____/________ ____ / / / /___ ____/ /___ _/ /____ / / | |/ / ____/____ / / / ___/ __ \/ __ \ / / / / __ \/ __ / __ `/ __/ _ \ / / | / / / ___/ / /___/ / / /_/ / / / / / /_/ / /_/ / /_/ / /_/ / /_/ __/ / /___/ / /___(__ ) \____/_/ \____/_/ /_/ \____/ .___/\__,_/\__,_/\__/\___/ /_____/_/|_\____/____/ /_/ EOF add() { while true; do read -p "This script will add a crontab schedule that updates all LXCs every Sunday at midnight. Proceed(y/n)?" yn case $yn in [Yy]*) break ;; [Nn]*) exit ;; *) echo "Please answer yes or no." ;; esac done sh -c '(crontab -l -u root 2>/dev/null; echo "0 0 * * 0 PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin /bin/bash -c \"\$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/tools/pve/update-lxcs-cron.sh)\" >>/var/log/update-lxcs-cron.log 2>/dev/null") | crontab -u root -' clear echo -e "\n To view Cron Update LXCs logs: cat /var/log/update-lxcs-cron.log" } remove() { (crontab -l | grep -v "update-lxcs-cron.sh") | crontab - rm -rf /var/log/update-lxcs-cron.log echo "Removed Crontab Schedule from Proxmox VE" } OPTIONS=(Add "Add Crontab Schedule" Remove "Remove Crontab Schedule") CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "Cron Update LXCs" --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/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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/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/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() { 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 sed -i '/proxmox/d;/bookworm/d' /etc/apt/sources.list || true 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-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/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() { 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 . } # ---- 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) start_routines_3 ;; trixie) 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 sed -i '/proxmox/d;/bookworm/d' /etc/apt/sources.list || true 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/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-pmg-install" "pve" if ! grep -q "Proxmox Mail Gateway" /etc/issue 2>/dev/null; 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; 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" } 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 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 # ---- 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" sed -i "s/^[^#].*pmg-enterprise/# &/" "$file" 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" sed -i "s/^#.*pmg-enterprise/deb/" "$file" 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.list <&2 2>&1 1>&3) case $CHOICE in keep) msg_ok "Kept 'pmg-no-subscription' repository" ;; disable) msg_info "Disabling 'pmg-no-subscription' repository" sed -i "s/^[^#].*pmg-no-subscription/# &/" "$file" 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" sed -i "s/^#.*pmg-no-subscription/deb/" "$file" 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-install-repo.list <&2 2>&1 1>&3) case $CHOICE in yes) msg_info "Adding 'pmgtest' repository (disabled)" cat >/etc/apt/sources.list.d/pmgtest-for-beta.list <&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 sed -i '/proxmox/d;/bookworm/d' /etc/apt/sources.list || true # 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/community-scripts/ProxmoxVE/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/community-scripts/ProxmoxVE/raw/main/LICENSE source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/refs/heads/main/misc/core.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 "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_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 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) } header_info # 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[@]} 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 if [[ -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 if [[ -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" 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" continue else echo -e "${BL}[INFO]${CL} Detected service: ${GN}${service}${CL}" fi #2) Extract service build/update resource requirements from config/installation file script=$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/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" ]; then pct set "$container" --cores "$build_cpu" --memory "$build_ram" fi #4) Update service, using the update command case "$os" in alpine) pct exec "$container" -- ash -c "$UPDATE_CMD" ;; archlinux) pct exec "$container" -- bash -c "$UPDATE_CMD" ;; fedora | rocky | centos | alma) pct exec "$container" -- bash -c "$UPDATE_CMD" ;; ubuntu | debian | devuan) pct exec "$container" -- bash -c "$UPDATE_CMD" ;; opensuse) pct exec "$container" -- bash -c "$UPDATE_CMD" ;; esac exit_code=$? if [ "$template" == "false" ] && [ "$status" == "status: stopped" ]; then echo -e "${BL}[Info]${GN} Shutting down${BL} $container ${CL} \n" pct shutdown $container & fi #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 [ $exit_code -eq 0 ]; then msg_ok "Updated container $container" elif [ $exit_code -eq 75 ]; then echo -e "${YW}[WARN]${CL} Container $container skipped (requires interactive mode)" elif [ "$BACKUP_CHOICE" == "yes" ]; then msg_error "Update failed for container $container (exit code: $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" 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" else msg_error "Restore failed for container $container" exit 235 fi else msg_error "Update failed for container $container. Exiting" exit "$exit_code" 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 # 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 tteck # Author: tteck (tteckster) # License: MIT # https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE echo -e "\n $(date)" excluded_containers=("$@") function update_container() { container=$1 name=$(pct exec "$container" hostname) echo -e "\n [Info] Updating $container : $name \n" os=$(pct config "$container" | awk '/^ostype/ {print $2}') 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" ;; 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") template=$(pct config "$container" | grep -q "template:" && echo "true" || echo "false") if [ "$template" == "false" ] && [ "$status" == "status: stopped" ]; then echo -e "[Info] Starting $container" pct start "$container" sleep 5 update_container "$container" echo -e "[Info] Shutting down $container" pct shutdown "$container" & elif [ "$status" == "status: running" ]; then update_container "$container" 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 && 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 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 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 tteck # Author: tteck (tteckster) # License: MIT # https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE function header_info { clear cat <<"EOF" ______ __ __ __ _ _______ /_ __/_ _________ / //_/__ __ __ / / | |/_/ ___/ / / / // / __/ _ \/ ,< / -_) // / / /___> &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" } 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 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 TURNKEY_MENU=() MSG_MAX_LENGTH=0 while read -r TAG ITEM; do OFFSET=2 ((${#ITEM} + OFFSET > MSG_MAX_LENGTH)) && MSG_MAX_LENGTH=${#ITEM}+OFFSET TURNKEY_MENU+=("$TAG" "$ITEM " "OFF") done < <( cat <&1 1>&2 2>&3 | tr -d '"') [ -z "$turnkey" ] && { whiptail --backtitle "Proxmox VE Helper Scripts" --title "No TurnKey LXC Selected" --msgbox "It appears that no TurnKey LXC container was selected" 10 68 msg "Done" exit } # Setup script environment PASS="$(openssl rand -base64 8)" # Prompt user to confirm container ID while true; do CTID=$(whiptail --backtitle "Container ID" --title "Choose the Container ID" --inputbox "Enter the container ID..." 8 40 $(pvesh get /cluster/nextid) 3>&1 1>&2 2>&3) # Check if user cancelled [ -z "$CTID" ] && die "No Container ID selected" # Validate Container ID if ! validate_container_id "$CTID"; then SUGGESTED_ID=$(get_valid_container_id "$CTID") if whiptail --backtitle "Container ID" --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 # User declined, loop back to input else break fi done # Prompt user to confirm Hostname HOST_NAME=$(whiptail --backtitle "Hostname" --title "Choose the Hostname" --inputbox "Enter the containers Hostname..." 8 40 "turnkey-${turnkey}" 3>&1 1>&2 2>&3) 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 " 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 } # 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." # Update LXC template list msg "Updating LXC template list..." pveam update >/dev/null # Get LXC template string mapfile -t TEMPLATES < <(pveam available -section turnkeylinux | awk -v turnkey="${turnkey}" '$0 ~ turnkey {print $2}' | sort -t - -k 2 -V) [ ${#TEMPLATES[@]} -gt 0 ] || die "Unable to find a template when searching for '${turnkey}'." TEMPLATE="${TEMPLATES[-1]}" # Download LXC template if ! pveam list $TEMPLATE_STORAGE | grep -q $TEMPLATE; then msg "Downloading LXC template (Patience)..." pveam download $TEMPLATE_STORAGE $TEMPLATE >/dev/null || die "A problem occured while downloading the LXC template." fi # 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 "TurnKey ${turnkey} password: ${PASS}" >>~/turnkey-${turnkey}.creds # file is located in the Proxmox root directory # If turnkey is "OpenVPN", add access to the tun device TUN_DEVICE_REQUIRED=("openvpn") # Setup this way in case future turnkeys also need tun access if printf '%s\n' "${TUN_DEVICE_REQUIRED[@]}" | grep -qw "${turnkey}"; then info "${turnkey} requires access to /dev/net/tun on the host. Modifying the container configuration to allow this." echo "lxc.cgroup2.devices.allow: c 10:200 rwm" >>/etc/pve/lxc/${CTID}.conf echo "lxc.mount.entry: /dev/net/tun dev/net/tun none bind,create=file 0 0" >>/etc/pve/lxc/${CTID}.conf sleep 5 fi # Start container msg "Starting LXC Container..." pct start "$CTID" sleep 10 # Get container IP set +euo pipefail # Turn off error checking 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 # 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" info "(credentials also stored in the root user's root directory in the 'turnkey-${turnkey}.creds' file.)" 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/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="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 -d ' ') 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 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" 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 2048 --title "RAM" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z "$RAM_SIZE" ]; then RAM_SIZE="2048" echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}$RAM_SIZE${CL}" else echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}$RAM_SIZE${CL}" fi else exit-script fi 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 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}" else MAC="$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 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}" else VLAN=",tag=$VLAN1" echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}$VLAN1${CL}" fi else exit-script fi 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}" else MTU=",mtu=$MTU1" echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}$MTU1${CL}" fi else exit-script 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 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 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 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/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" ____ __ _ ________ / __ \___ / /_ (_)___ _____ < /__ / / / / / _ \/ __ \/ / __ `/ __ \ / / /_ < / /_/ / __/ /_/ / / /_/ / / / / / /___/ / /_____/\___/_.___/_/\__,_/_/ /_/ /_//____/ (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 -d ' ') 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 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" 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 2048 --title "RAM" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z "$RAM_SIZE" ]; then RAM_SIZE="2048" echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}$RAM_SIZE${CL}" else echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}$RAM_SIZE${CL}" fi else exit-script fi 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 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}" else MAC="$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 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}" else VLAN=",tag=$VLAN1" echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}$VLAN1${CL}" fi else exit-script fi 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}" else MTU=",mtu=$MTU1" echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}$MTU1${CL}" fi else exit-script fi 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/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="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)" != "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="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 -d ' ') 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 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" 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 2048 --title "RAM" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z $RAM_SIZE ]; then RAM_SIZE="2048" echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}$RAM_SIZE${CL}" else echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}$RAM_SIZE${CL}" fi else exit-script fi 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 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}" else MAC="$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 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}" else VLAN=",tag=$VLAN1" echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}$VLAN1${CL}" fi else exit-script fi 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}" else MTU=",mtu=$MTU1" echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}$MTU1${CL}" fi else exit-script fi 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-amd64.qcow2 else URL=https://cloud.debian.org/images/cloud/bookworm/latest/debian-12-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}" 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 4M 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 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 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 -d ' ') 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 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 echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}$CORE_COUNT${CL}" else exit_script fi # RAM Size 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 echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}$RAM_SIZE${CL}" else exit_script fi # 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 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" else MAC="$MAC1" fi echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}$MAC${CL}" else exit_script fi # VLAN 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="" else VLAN=",tag=$VLAN1" fi echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}$VLAN1${CL}" else exit_script fi # MTU 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="" else MTU=",mtu=$MTU1" fi echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}$MTU1${CL}" else exit_script fi # 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 arch_check 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/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)" VERSIONS=(stable beta dev) METHOD="" NSAPP="haos-vm" var_os="homeassistant" DISK_SIZE="32G" for version in "${VERSIONS[@]}"; do eval "$version=$(curl -fsSL https://raw.githubusercontent.com/home-assistant/version/master/stable.json | grep '"ova"' | cut -d '"' -f 4)" 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)" != "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() { BRANCH="$stable" VMID=$(get_valid_nextid) MACHINE="q35" FORMAT="" DISK_SIZE="32G" HN="haos-${BRANCH}" 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 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 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" 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 -d ' ') 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 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" 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 2048 --title "RAM" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z $RAM_SIZE ]; then RAM_SIZE="4096" echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}$RAM_SIZE${CL}" else echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}$RAM_SIZE${CL}" fi else exit-script fi 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 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}" else MAC="$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 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}" else VLAN=",tag=$VLAN1" echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}$VLAN1${CL}" fi else exit-script fi 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}" else MTU=",mtu=$MTU1" echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}$MTU1${CL}" fi else exit-script fi 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" if [ "$BRANCH" == "$dev" ]; then URL="https://os-artifacts.home-assistant.io/${BRANCH}/haos_ova-${BRANCH}.qcow2.xz" else URL="https://github.com/home-assistant/operating-system/releases/download/${BRANCH}/haos_ova-${BRANCH}.qcow2.xz" fi CACHE_DIR="/var/lib/vz/template/cache" CACHE_FILE="$CACHE_DIR/$(basename "$URL")" FILE_IMG="/var/lib/vz/template/tmp/${CACHE_FILE##*/%.xz}" # .qcow2 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 -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 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 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" | 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)" [[ -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})" rm -f "$FILE_IMG" msg_info "Attaching EFI and root disk" 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 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 -d ' ') 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 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" 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 2048 --title "RAM" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z $RAM_SIZE ]; then RAM_SIZE="2048" echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}$RAM_SIZE${CL}" else echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}$RAM_SIZE${CL}" fi else exit-script fi 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 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}" else MAC="$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 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}" else VLAN=",tag=$VLAN1" echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}$VLAN1${CL}" fi else exit-script fi 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}" else MTU=",mtu=$MTU1" echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}$MTU1${CL}" fi else exit-script 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 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://Helper-Scripts.com' 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 -d ' ') 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 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" 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 2048 --title "RAM" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z $RAM_SIZE ]; then RAM_SIZE="2048" echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}$RAM_SIZE${CL}" else echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}$RAM_SIZE${CL}" fi else exit-script fi 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 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}" else MAC="$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 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}" else VLAN=",tag=$VLAN1" echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}$VLAN1${CL}" fi else exit-script fi 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}" else MTU=",mtu=$MTU1" echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}$MTU1${CL}" fi else exit-script 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 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://Helper-Scripts.com' 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 -d ' ') fi echo -e "${DGN}Using Hostname: ${BGN}$HN${CL}" else exit-script fi 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 echo -e "${DGN}Allocated Cores: ${BGN}$CORE_COUNT${CL}" else exit-script fi 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 echo -e "${DGN}Allocated RAM: ${BGN}$RAM_SIZE${CL}" else exit-script fi 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 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="" else VLAN=",tag=$VLAN1" fi echo -e "${DGN}Using WAN Vlan: ${BGN}$VLAN1${CL}" else exit-script fi 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="" else LAN_VLAN=",tag=$VLAN2" fi echo -e "${DGN}Using LAN Vlan: ${BGN}$VLAN2${CL}" else exit-script fi 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="" else MTU=",mtu=$MTU1" fi echo -e "${DGN}Using Interface MTU Size: ${BGN}$MTU1${CL}" else exit-script fi 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://Helper-Scripts.com' 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 "halt" 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/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..." #API VARIABLES RANDOM_UUID="$(cat /proc/sys/kernel/random/uuid)" METHOD="" NSAPP="opnsense-vm" var_os="opnsense" var_version="25.7" # 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) 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 -d ' ') fi echo -e "${DGN}Using Hostname: ${BGN}$HN${CL}" else exit-script fi 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="2" fi echo -e "${DGN}Allocated Cores: ${BGN}$CORE_COUNT${CL}" else exit-script fi 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 echo -e "${DGN}Allocated RAM: ${BGN}$RAM_SIZE${CL}" else exit-script fi 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}$") 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 proxmox-helper-scripts -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 ${FILE} $STORAGE ${DISK_IMPORT:-} 1>&/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://Helper-Scripts.com' 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 25.7" 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 -d ' ') 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 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" 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 2048 --title "RAM" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z $RAM_SIZE ]; then RAM_SIZE="2048" echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}$RAM_SIZE${CL}" else echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}$RAM_SIZE${CL}" fi else exit-script fi 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 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}" else MAC="$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 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}" else VLAN=",tag=$VLAN1" echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}$VLAN1${CL}" fi else exit-script fi 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}" else MTU=",mtu=$MTU1" echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}$MTU1${CL}" fi else exit-script 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 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://Helper-Scripts.com' 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 "\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="pimox-haos-vm" var_os="pimox-haos" DISK_SIZE="32G" for version in "${VERSIONS[@]}"; do eval "$version=$(curl -fsSL https://raw.githubusercontent.com/home-assistant/version/master/stable.json | grep '"ova"' | cut -d '"' -f 4)" done 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 "Pimox Homeassistant OS VM" --yesno "This will create a New Pimox 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)" != "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() { 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 -d ' ') echo -e "${DGN}Using Hostname: ${BGN}$HN${CL}" fi fi 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 [ -z $CORE_COUNT ]; then CORE_COUNT="2" echo -e "${DGN}Allocated Cores: ${BGN}$CORE_COUNT${CL}" else if [ $exitstatus = 0 ]; then echo -e "${DGN}Allocated Cores: ${BGN}$CORE_COUNT${CL}"; fi fi 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 [ -z $RAM_SIZE ]; then RAM_SIZE="4096" echo -e "${DGN}Allocated RAM: ${BGN}$RAM_SIZE${CL}" else if [ $exitstatus = 0 ]; then echo -e "${DGN}Allocated RAM: ${BGN}$RAM_SIZE${CL}"; fi fi 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 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 [ -z $MAC1 ]; then MAC="$GEN_MAC" echo -e "${DGN}Using MAC Address: ${BGN}$MAC${CL}" else if [ $exitstatus = 0 ]; then MAC="$MAC1" echo -e "${DGN}Using MAC Address: ${BGN}$MAC1${CL}" fi fi 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 = 0 ]; then if [ -z $VLAN1 ]; then VLAN1="Default" VLAN="" echo -e "${DGN}Using Vlan: ${BGN}$VLAN1${CL}" else VLAN=",tag=$VLAN1" echo -e "${DGN}Using Vlan: ${BGN}$VLAN1${CL}" fi fi 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 = 0 ]; then if [ -z $MTU1 ]; then MTU1="Default" MTU="" echo -e "${DGN}Using Interface MTU Size: ${BGN}$MTU1${CL}" else MTU=",mtu=$MTU1" echo -e "${DGN}Using Interface MTU Size: ${BGN}$MTU1${CL}" fi fi 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 >/dev/null DESCRIPTION=$( cat <<EOF <div align='center'> <a href='https://Helper-Scripts.com' 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;'>Homeassistant 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 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/community-scripts/ProxmoxVE/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 -d ' ') 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 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" 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 "$RAM_SIZE" --title "RAM" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z $RAM_SIZE ]; then RAM_SIZE="8192" echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}$RAM_SIZE${CL}" else echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}$RAM_SIZE${CL}" fi else exit-script fi 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 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}" else MAC="$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 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}" else VLAN=",tag=$VLAN1" echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}$VLAN1${CL}" fi else exit-script fi 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}" else MTU=",mtu=$MTU1" echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}$MTU1${CL}" fi else exit-script fi 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://Helper-Scripts.com' 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/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="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)" != "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="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 -d ' ') 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 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" 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 2048 --title "RAM" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z $RAM_SIZE ]; then RAM_SIZE="2048" echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}$RAM_SIZE${CL}" else echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}$RAM_SIZE${CL}" fi else exit-script fi 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 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}" else MAC="$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 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}" else VLAN=",tag=$VLAN1" echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}$VLAN1${CL}" fi else exit-script fi 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}" else MTU=",mtu=$MTU1" echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}$MTU1${CL}" fi else exit-script 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 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-amd64.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 4M 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://Helper-Scripts.com' 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 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 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 <<<$(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="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)" != "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="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 -d ' ') 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 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" 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 2048 --title "RAM" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z $RAM_SIZE ]; then RAM_SIZE="2048" echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}$RAM_SIZE${CL}" else echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}$RAM_SIZE${CL}" fi else exit-script fi 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 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}" else MAC="$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 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}" else VLAN=",tag=$VLAN1" echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}$VLAN1${CL}" fi else exit-script fi 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}" else MTU=",mtu=$MTU1" echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}$MTU1${CL}" fi else exit-script 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 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-amd64.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 4M 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://Helper-Scripts.com' 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 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 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/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" __ ____ __ ___ ______ ____ __ __ _ ____ ___ / / / / /_ __ ______ / /___ __ |__ \ / ____// __ \/ // / | | / / |/ / / / / / __ \/ / / / __ \/ __/ / / / __/ //___ \ / / / / // /_ | | / / /|_/ / / /_/ / /_/ / /_/ / / / / /_/ /_/ / / __/____/ // /_/ /__ __/ | |/ / / / / \____/_.___/\__,_/_/ /_/\__/\__,_/ /____/_____(_)____/ /_/ |___/_/ /_/ (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)" != "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="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 -d ' ') 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 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" 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 2048 --title "RAM" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z $RAM_SIZE ]; then RAM_SIZE="2048" echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}$RAM_SIZE${CL}" else echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}$RAM_SIZE${CL}" fi else exit-script fi 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 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}" else MAC="$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 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}" else VLAN=",tag=$VLAN1" echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}$VLAN1${CL}" fi else exit-script fi 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}" else MTU=",mtu=$MTU1" echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}$MTU1${CL}" fi else exit-script 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 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-amd64.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 4M 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://Helper-Scripts.com' 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 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 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/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="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 -d ' ') 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 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" 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 2048 --title "RAM" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z $RAM_SIZE ]; then RAM_SIZE="2048" echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}$RAM_SIZE${CL}" else echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}$RAM_SIZE${CL}" fi else exit-script fi 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 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}" else MAC="$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 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}" else VLAN=",tag=$VLAN1" echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}$VLAN1${CL}" fi else exit-script fi 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}" else MTU=",mtu=$MTU1" echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}$MTU1${CL}" fi else exit-script 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 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://Helper-Scripts.com' 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"