initial: unraid-1 bootstrap (garage + basement + watchtower + host-agent)
This commit is contained in:
@@ -0,0 +1,34 @@
|
||||
# pq/scripts
|
||||
|
||||
Public bootstrap scripts. Only public repo on git.docker.pq.io — everything
|
||||
else is private. This one is public so machines on the internal network
|
||||
can `curl` raw URLs anonymously, without embedding tokens.
|
||||
|
||||
Distribution is over the LAN (gitea is not internet-reachable); "public"
|
||||
here just means anonymous access from the internal network.
|
||||
|
||||
## Layout
|
||||
|
||||
```
|
||||
unraid-1/
|
||||
install.sh # bootstrap entrypoint
|
||||
garage.toml # garage S3 config (single-node, lmdb, rf=1)
|
||||
garage.template.xml # unraid CA template — garage container
|
||||
basement.template.xml # unraid CA template — basement-ui (garage admin UI)
|
||||
watchtower.template.xml # unraid CA template — image auto-updater
|
||||
host-agent.template.xml # unraid CA template — per-host metrics + fan ctrl
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
On the unraid-1 console:
|
||||
|
||||
```sh
|
||||
bash <(curl -sL https://git.docker.pq.io/pq/scripts/raw/branch/main/unraid-1/install.sh)
|
||||
```
|
||||
|
||||
The installer is idempotent. Re-run any time to re-fetch templates or pick
|
||||
up new ones. `FORCE=1` rotates garage's secrets.
|
||||
|
||||
After install: Unraid UI → Docker → Add Container → Template dropdown →
|
||||
Apply each of `my-garage`, `my-basement`, `my-watchtower`, `my-host-agent`.
|
||||
@@ -0,0 +1,48 @@
|
||||
<?xml version="1.0"?>
|
||||
<!--
|
||||
Unraid Community Apps template for basement-ui (Garage admin UI).
|
||||
Source of truth: pq/scripts on Gitea (https://git.docker.pq.io/pq/scripts).
|
||||
Served via gitea raw URL: https://git.docker.pq.io/pq/scripts/raw/branch/main/unraid-1/basement.template.xml
|
||||
|
||||
This file is NOT installed directly. install.sh fetches it, substitutes the
|
||||
__GARAGE_ADMIN_TOKEN__ placeholder with the same secret used by the garage
|
||||
container, and writes the result to /boot/config/plugins/dockerMan/templates-user/my-basement.xml.
|
||||
|
||||
Caddy on classe reverse-proxies https://basement.pq.io to this container's
|
||||
port 8080. DNS for basement.pq.io and the Caddy vhost are managed on classe,
|
||||
not here.
|
||||
-->
|
||||
<Container version="2">
|
||||
<Name>basement</Name>
|
||||
<Repository>ghcr.io/mattjackson/basement-ui:latest</Repository>
|
||||
<Registry>https://github.com/mattjackson/basement-ui</Registry>
|
||||
<Network>bridge</Network>
|
||||
<MyIP/>
|
||||
<Shell>sh</Shell>
|
||||
<Privileged>false</Privileged>
|
||||
<Support/>
|
||||
<Project/>
|
||||
<Overview>basement-ui: admin UI for the local garage S3 container. Caddy on classe terminates TLS for basement.pq.io and reverse-proxies to this container's port 8080.</Overview>
|
||||
<Category>Network:Web Cloud:</Category>
|
||||
<WebUI>http://[IP]:[PORT:8080]/</WebUI>
|
||||
<TemplateURL/>
|
||||
<Icon>https://garagehq.deuxfleurs.fr/img/logo.svg</Icon>
|
||||
<ExtraParams>--restart=unless-stopped --add-host=host.docker.internal:host-gateway --label com.centurylinklabs.watchtower.enable=true</ExtraParams>
|
||||
<PostArgs/>
|
||||
<CPUset/>
|
||||
<DateInstalled/>
|
||||
<DonateText/>
|
||||
<DonateLink/>
|
||||
<Description>
|
||||
Admin UI for the garage container running on this host.
|
||||
|
||||
Reaches garage's admin API at http://host.docker.internal:3903 (the host-gateway alias resolves to unraid-1's docker bridge IP, where the garage container publishes port 3903). The admin token below is shared with the garage container — install.sh pre-fills both.
|
||||
|
||||
Publicly served at https://basement.pq.io via Caddy on classe (reverse proxy → unraid-1:8080).
|
||||
</Description>
|
||||
|
||||
<Config Name="WebUI" Target="8080" Default="8080" Mode="tcp" Description="HTTP port. Caddy on classe proxies basement.pq.io here." Type="Port" Display="always" Required="true" Mask="false">8080</Config>
|
||||
|
||||
<Config Name="GARAGE_ADMIN_URL" Target="GARAGE_ADMIN_URL" Default="http://host.docker.internal:3903" Mode="" Description="Garage admin API base URL (reaches the sibling garage container via the host-gateway alias)." Type="Variable" Display="always" Required="true" Mask="false">http://host.docker.internal:3903</Config>
|
||||
<Config Name="GARAGE_ADMIN_TOKEN" Target="GARAGE_ADMIN_TOKEN" Default="" Mode="" Description="Bearer token for Garage admin API. Pre-filled by install.sh (same value as the garage container's GARAGE_ADMIN_TOKEN)." Type="Variable" Display="always" Required="true" Mask="true">__GARAGE_ADMIN_TOKEN__</Config>
|
||||
</Container>
|
||||
@@ -0,0 +1,63 @@
|
||||
<?xml version="1.0"?>
|
||||
<!--
|
||||
Unraid Community Apps template for Garage (single-node S3 server).
|
||||
Source of truth: pq/scripts on Gitea (https://git.docker.pq.io/pq/scripts).
|
||||
Served via gitea raw URL: https://git.docker.pq.io/pq/scripts/raw/branch/main/unraid-1/garage.template.xml
|
||||
|
||||
This file is NOT installed directly. install.sh fetches this, substitutes
|
||||
the three __PLACEHOLDER__ secrets it just generated, and writes the result
|
||||
to /boot/config/plugins/dockerMan/templates-user/my-garage.xml.
|
||||
|
||||
Unraid then surfaces it in the "Template" dropdown on the Add Container page.
|
||||
-->
|
||||
<Container version="2">
|
||||
<Name>garage</Name>
|
||||
<Repository>dxflrs/garage:v1.0.1</Repository>
|
||||
<Registry>https://hub.docker.com/r/dxflrs/garage</Registry>
|
||||
<Network>bridge</Network>
|
||||
<MyIP/>
|
||||
<Shell>sh</Shell>
|
||||
<Privileged>false</Privileged>
|
||||
<Support/>
|
||||
<Project>https://garagehq.deuxfleurs.fr/</Project>
|
||||
<Overview>Single-node Garage S3 server. Caddy on classe terminates TLS for s3.pq.io and reverse-proxies to this container's port 3900.</Overview>
|
||||
<Category>Backup: Cloud: Network:Other</Category>
|
||||
<WebUI/>
|
||||
<TemplateURL/>
|
||||
<Icon>https://garagehq.deuxfleurs.fr/img/logo.svg</Icon>
|
||||
<ExtraParams>--restart=unless-stopped --label com.centurylinklabs.watchtower.enable=true</ExtraParams>
|
||||
<PostArgs/>
|
||||
<CPUset/>
|
||||
<DateInstalled/>
|
||||
<DonateText/>
|
||||
<DonateLink/>
|
||||
<Description>
|
||||
Single-node Garage S3 (lmdb engine, replication_factor=1). Path-style endpoint: https://s3.pq.io/<bucket>/<key>.
|
||||
|
||||
After the container starts the FIRST time only, initialise the cluster layout from the unraid console:
|
||||
|
||||
docker exec garage /garage status
|
||||
# copy the node-id from the output
|
||||
docker exec garage /garage layout assign -z dc1 -c 1T <node-id>
|
||||
docker exec garage /garage layout apply --version 1
|
||||
|
||||
Then create a bucket and an access key:
|
||||
|
||||
docker exec garage /garage bucket create my-bucket
|
||||
docker exec garage /garage key create my-key
|
||||
docker exec garage /garage bucket allow --read --write --owner my-bucket --key my-key
|
||||
docker exec garage /garage key info --show-secret my-key
|
||||
</Description>
|
||||
|
||||
<Config Name="S3 API" Target="3900" Default="3900" Mode="tcp" Description="S3-compatible API. Caddy on classe proxies s3.pq.io here." Type="Port" Display="always" Required="true" Mask="false">3900</Config>
|
||||
<Config Name="S3 Static Web" Target="3902" Default="3902" Mode="tcp" Description="Static site hosting (optional, not currently exposed via Caddy)." Type="Port" Display="advanced" Required="false" Mask="false">3902</Config>
|
||||
<Config Name="Admin / Metrics" Target="3903" Default="3903" Mode="tcp" Description="Admin API + Prometheus metrics. Caddy proxies /-/health to this port." Type="Port" Display="always" Required="true" Mask="false">3903</Config>
|
||||
|
||||
<Config Name="Metadata (SSD)" Target="/var/lib/garage/meta" Default="/mnt/user/appdata/garage/meta" Mode="rw" Description="LMDB metadata. MUST live on SSD (appdata is cache-only by default — good)." Type="Path" Display="always" Required="true" Mask="false">/mnt/user/appdata/garage/meta</Config>
|
||||
<Config Name="Data (parity)" Target="/var/lib/garage/data" Default="/mnt/user/s3/data" Mode="rw" Description="Object data chunks. Use a parity-protected share with cache:yes for SSD-fast writes + array durability." Type="Path" Display="always" Required="true" Mask="false">/mnt/user/s3/data</Config>
|
||||
<Config Name="garage.toml" Target="/etc/garage.toml" Default="/mnt/user/appdata/garage/garage.toml" Mode="ro" Description="Garage config file. install.sh placed this for you." Type="Path" Display="always" Required="true" Mask="false">/mnt/user/appdata/garage/garage.toml</Config>
|
||||
|
||||
<Config Name="GARAGE_RPC_SECRET" Target="GARAGE_RPC_SECRET" Default="" Mode="" Description="32-byte hex inter-node secret. Pre-filled by install.sh." Type="Variable" Display="always" Required="true" Mask="true">__GARAGE_RPC_SECRET__</Config>
|
||||
<Config Name="GARAGE_ADMIN_TOKEN" Target="GARAGE_ADMIN_TOKEN" Default="" Mode="" Description="32-byte hex bearer token for admin API. Pre-filled by install.sh." Type="Variable" Display="always" Required="true" Mask="true">__GARAGE_ADMIN_TOKEN__</Config>
|
||||
<Config Name="GARAGE_METRICS_TOKEN" Target="GARAGE_METRICS_TOKEN" Default="" Mode="" Description="32-byte hex bearer token for Prometheus /metrics. Pre-filled by install.sh." Type="Variable" Display="always" Required="true" Mask="true">__GARAGE_METRICS_TOKEN__</Config>
|
||||
</Container>
|
||||
@@ -0,0 +1,35 @@
|
||||
# Garage v1 single-node config. Lives on unraid-1.
|
||||
# Secrets (rpc_secret, admin_token, metrics_token) come from env vars
|
||||
# defined in .env — NOT in this file. See Garage docs:
|
||||
# https://garagehq.deuxfleurs.fr/documentation/reference-manual/configuration/
|
||||
|
||||
metadata_dir = "/var/lib/garage/meta"
|
||||
data_dir = "/var/lib/garage/data"
|
||||
|
||||
# lmdb is the v1 default; sled was deprecated. lmdb is more crash-resilient
|
||||
# and the recommended engine for new deployments.
|
||||
db_engine = "lmdb"
|
||||
|
||||
# Single-node: every object stored once. No HA.
|
||||
replication_factor = 1
|
||||
|
||||
rpc_bind_addr = "[::]:3901"
|
||||
rpc_public_addr = "127.0.0.1:3901"
|
||||
# rpc_secret -> GARAGE_RPC_SECRET (env)
|
||||
|
||||
[s3_api]
|
||||
s3_region = "garage"
|
||||
api_bind_addr = "[::]:3900"
|
||||
# Path-style: https://s3.pq.io/<bucket>/<key>
|
||||
# Virtual-host style: https://<bucket>.s3.pq.io/<key> (requires DNS wildcard)
|
||||
root_domain = ".s3.pq.io"
|
||||
|
||||
[s3_web]
|
||||
bind_addr = "[::]:3902"
|
||||
root_domain = ".web.s3.pq.io"
|
||||
index = "index.html"
|
||||
|
||||
[admin]
|
||||
api_bind_addr = "[::]:3903"
|
||||
# admin_token -> GARAGE_ADMIN_TOKEN (env)
|
||||
# metrics_token -> GARAGE_METRICS_TOKEN (env) — used by Prometheus scrape
|
||||
@@ -0,0 +1,62 @@
|
||||
<?xml version="1.0"?>
|
||||
<!--
|
||||
Unraid Community Apps template for host-agent (per-host metrics + fan ctrl).
|
||||
Source of truth: pq/scripts on Gitea (https://git.docker.pq.io/pq/scripts).
|
||||
Served via gitea raw URL: https://git.docker.pq.io/pq/scripts/raw/branch/main/unraid-1/host-agent.template.xml
|
||||
|
||||
This file is NOT installed directly. install.sh fetches it and writes it to
|
||||
/boot/config/plugins/dockerMan/templates-user/my-host-agent.xml. No secrets
|
||||
to substitute.
|
||||
|
||||
host-agent is one container bundling: fan-controller (Dell PowerEdge only —
|
||||
self-disables on consumer hardware like unraid), node_exporter, cadvisor,
|
||||
ipmi_exporter, smartctl_exporter, nvidia_gpu_exporter, vmagent (remote_write
|
||||
to Prometheus). Each sub-service probes its prerequisites on start and
|
||||
sleep-infinity's if absent, so the same image runs on a Dell R730xd and an
|
||||
unraid box on consumer hardware.
|
||||
|
||||
On unraid-1: fan-controller + ipmi_exporter + nvidia_gpu_exporter self-
|
||||
disable (no Dell BMC, no nvidia GPU). node + cadvisor + smartctl + vmagent
|
||||
run, ship metrics to monitoring stack on classe.
|
||||
-->
|
||||
<Container version="2">
|
||||
<Name>host-agent</Name>
|
||||
<Repository>ghcr.io/mattjackson/host-agent:latest</Repository>
|
||||
<Registry>https://github.com/mattjackson/host-agent</Registry>
|
||||
<Network>host</Network>
|
||||
<MyIP/>
|
||||
<Shell>sh</Shell>
|
||||
<Privileged>true</Privileged>
|
||||
<Support/>
|
||||
<Project>https://github.com/mattjackson/host-agent</Project>
|
||||
<Overview>Per-host Prometheus exporters (node, cadvisor, ipmi, smartctl, nvidia-gpu) plus Dell PowerEdge fan controller. Each sub-service self-disables if hardware prerequisites are missing — same image runs on Dell servers and consumer Unraid hardware.</Overview>
|
||||
<Category>Tools: Status: Productivity:</Category>
|
||||
<WebUI/>
|
||||
<TemplateURL/>
|
||||
<Icon>https://raw.githubusercontent.com/prometheus/prometheus/main/web/ui/static/img/prometheus_logo_grey.svg</Icon>
|
||||
<ExtraParams>--restart=unless-stopped --cgroupns=host -v /:/host:ro,rslave --label com.centurylinklabs.watchtower.enable=true</ExtraParams>
|
||||
<PostArgs/>
|
||||
<CPUset/>
|
||||
<DateInstalled/>
|
||||
<DonateText/>
|
||||
<DonateLink/>
|
||||
<Description>
|
||||
host-agent: single container, s6-supervised, bundles per-host metrics + Dell fan control.
|
||||
|
||||
On unraid-1 (consumer hardware): fan-controller + ipmi_exporter + nvidia_gpu_exporter self-disable. node_exporter (CPU/mem/disk/net), cadvisor (per-container), smartctl_exporter (drive SMART), and vmagent (remote_write) run and push to your Prometheus.
|
||||
|
||||
The root mount (/:/host:ro,rslave) is wired via ExtraParams since Unraid's Path config doesn't expose mount propagation flags; rslave is required for node_exporter's rootfs view to track later host mounts.
|
||||
</Description>
|
||||
|
||||
<Config Name="/sys" Target="/sys" Default="/sys" Mode="ro" Description="Required: cadvisor reads /sys/fs/cgroup directly (its own cgroup ns is just /, even with --privileged)." Type="Path" Display="advanced" Required="true" Mask="false">/sys</Config>
|
||||
<Config Name="docker.sock" Target="/run/docker.sock" Default="/var/run/docker.sock" Mode="rw" Description="Required: cadvisor opens /run/docker.sock at this hardcoded path for container metadata." Type="Path" Display="advanced" Required="true" Mask="false">/var/run/docker.sock</Config>
|
||||
<Config Name="containerd" Target="/run/containerd" Default="/run/containerd" Mode="ro" Description="Required: cadvisor opens /run/containerd/containerd.sock at this hardcoded path." Type="Path" Display="advanced" Required="true" Mask="false">/run/containerd</Config>
|
||||
<Config Name="/var/lib/docker" Target="/var/lib/docker" Default="/var/lib/docker" Mode="ro" Description="Required: cadvisor reads container metadata from here." Type="Path" Display="advanced" Required="true" Mask="false">/var/lib/docker</Config>
|
||||
<Config Name="/dev" Target="/dev" Default="/dev" Mode="rw" Description="Required: ipmitool + smartctl hardcode /dev/* paths and Docker doesn't auto-import host devices." Type="Path" Display="advanced" Required="true" Mask="false">/dev</Config>
|
||||
<Config Name="host-agent state" Target="/var/lib/host-agent" Default="/mnt/user/appdata/host-agent" Mode="rw" Description="Persistent state: fan-controller EWMA baseline + vmagent WAL." Type="Path" Display="always" Required="true" Mask="false">/mnt/user/appdata/host-agent</Config>
|
||||
|
||||
<Config Name="PROMETHEUS_REMOTE_WRITE_URL" Target="PROMETHEUS_REMOTE_WRITE_URL" Default="http://docker-1.internal.pq.io:9090/api/v1/write" Mode="" Description="Where vmagent pushes metrics. Defaults to monitoring stack on classe (docker-1). Prometheus must be started with --web.enable-remote-write-receiver (already is)." Type="Variable" Display="always" Required="true" Mask="false">http://docker-1.internal.pq.io:9090/api/v1/write</Config>
|
||||
<Config Name="PROMETHEUS_REMOTE_WRITE_BEARER_TOKEN" Target="PROMETHEUS_REMOTE_WRITE_BEARER_TOKEN" Default="" Mode="" Description="Optional bearer token for remote_write auth (LAN-internal Prometheus typically needs none)." Type="Variable" Display="advanced" Required="false" Mask="true"/>
|
||||
<Config Name="NVIDIA_VISIBLE_DEVICES" Target="NVIDIA_VISIBLE_DEVICES" Default="all" Mode="" Description="Harmless if no NVIDIA driver — nvidia_gpu_exporter sub-service self-disables." Type="Variable" Display="advanced" Required="false" Mask="false">all</Config>
|
||||
<Config Name="NVIDIA_DRIVER_CAPABILITIES" Target="NVIDIA_DRIVER_CAPABILITIES" Default="utility" Mode="" Description="Harmless if no NVIDIA driver." Type="Variable" Display="advanced" Required="false" Mask="false">utility</Config>
|
||||
</Container>
|
||||
Executable
+126
@@ -0,0 +1,126 @@
|
||||
#!/bin/bash
|
||||
# unraid-1 installer — sets up the unraid box with the standard fleet of
|
||||
# containers (garage S3, basement UI, watchtower, host-agent).
|
||||
#
|
||||
# Usage on the unraid console:
|
||||
# bash <(curl -sL https://git.docker.pq.io/pq/scripts/raw/branch/main/unraid-1/install.sh)
|
||||
#
|
||||
# What it does:
|
||||
# 1. Validates this is an unraid host with the array started.
|
||||
# 2. Confirms the "s3" share exists (parity-protected; you create it).
|
||||
# 3. mkdir's /mnt/user/appdata/garage/{meta,} and /mnt/user/s3/data.
|
||||
# 4. Generates 3 garage secrets (rpc, admin, metrics) → secrets.env (preserved).
|
||||
# 5. Fetches garage.toml → /mnt/user/appdata/garage/garage.toml.
|
||||
# 6. Fetches 4 unraid container templates (garage / basement / watchtower /
|
||||
# host-agent), substitutes secrets where needed, writes them to
|
||||
# /boot/config/plugins/dockerMan/templates-user/my-*.xml.
|
||||
#
|
||||
# After: Unraid UI → Docker → Add Container → Template dropdown → Apply each.
|
||||
#
|
||||
# Idempotent: re-running keeps existing secrets and garage.toml. FORCE=1
|
||||
# rotates the garage secrets (you'll then need to re-Apply garage + basement).
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
BASE="${UNRAID1_INSTALL_BASE:-https://git.docker.pq.io/pq/scripts/raw/branch/main/unraid-1}"
|
||||
|
||||
APPDATA=/mnt/user/appdata/garage
|
||||
DATADIR=/mnt/user/s3/data
|
||||
TEMPLATE_DIR=/boot/config/plugins/dockerMan/templates-user
|
||||
TOML_DEST="$APPDATA/garage.toml"
|
||||
SECRETS="$APPDATA/secrets.env"
|
||||
|
||||
say() { printf "install.sh: %s\n" "$*"; }
|
||||
die() { printf "install.sh: %s\n" "$*" >&2; exit 1; }
|
||||
|
||||
# ── Sanity ──
|
||||
[ -d /boot/config/plugins/dockerMan ] || die "no /boot/config/plugins/dockerMan — not an unraid host?"
|
||||
[ -d /mnt/user ] || die "/mnt/user missing — is the array started?"
|
||||
[ -d /mnt/user/s3 ] || die "share /mnt/user/s3 missing. Create it in unraid UI first (Shares → Add Share → 's3', cache:yes recommended), then re-run."
|
||||
|
||||
mkdir -p "$APPDATA/meta" "$DATADIR" "$TEMPLATE_DIR"
|
||||
|
||||
# ── garage.toml ──
|
||||
if [ -f "$TOML_DEST" ] && [ "${FORCE:-}" != "1" ]; then
|
||||
say "$TOML_DEST exists — keeping (FORCE=1 to overwrite)"
|
||||
else
|
||||
say "fetching $BASE/garage.toml"
|
||||
curl -fsSL "$BASE/garage.toml" -o "$TOML_DEST"
|
||||
fi
|
||||
|
||||
# ── Secrets ──
|
||||
if [ -f "$SECRETS" ] && [ "${FORCE:-}" != "1" ]; then
|
||||
say "reusing existing secrets at $SECRETS (FORCE=1 to rotate)"
|
||||
# shellcheck disable=SC1090
|
||||
. "$SECRETS"
|
||||
else
|
||||
GARAGE_RPC_SECRET=$(openssl rand -hex 32)
|
||||
GARAGE_ADMIN_TOKEN=$(openssl rand -hex 32)
|
||||
GARAGE_METRICS_TOKEN=$(openssl rand -hex 32)
|
||||
umask 077
|
||||
cat > "$SECRETS" <<EOF
|
||||
GARAGE_RPC_SECRET=$GARAGE_RPC_SECRET
|
||||
GARAGE_ADMIN_TOKEN=$GARAGE_ADMIN_TOKEN
|
||||
GARAGE_METRICS_TOKEN=$GARAGE_METRICS_TOKEN
|
||||
EOF
|
||||
say "generated secrets → $SECRETS"
|
||||
fi
|
||||
|
||||
# ── Templates ──
|
||||
# Fetch each, optionally sed-substitute __PLACEHOLDER__ secrets, write to
|
||||
# templates-user/. Secrets are hex chars only — safe in sed delimiters.
|
||||
fetch_template() {
|
||||
local src=$1 dest=$2 tmp
|
||||
shift 2
|
||||
tmp=$(mktemp)
|
||||
say "fetching $src"
|
||||
curl -fsSL "$src" -o "$tmp"
|
||||
if [ "$#" -gt 0 ]; then
|
||||
sed "$@" "$tmp" > "$dest"
|
||||
else
|
||||
cp "$tmp" "$dest"
|
||||
fi
|
||||
rm -f "$tmp"
|
||||
say "wrote $dest"
|
||||
}
|
||||
|
||||
fetch_template \
|
||||
"$BASE/garage.template.xml" \
|
||||
"$TEMPLATE_DIR/my-garage.xml" \
|
||||
-e "s|__GARAGE_RPC_SECRET__|$GARAGE_RPC_SECRET|" \
|
||||
-e "s|__GARAGE_ADMIN_TOKEN__|$GARAGE_ADMIN_TOKEN|" \
|
||||
-e "s|__GARAGE_METRICS_TOKEN__|$GARAGE_METRICS_TOKEN|"
|
||||
|
||||
fetch_template \
|
||||
"$BASE/basement.template.xml" \
|
||||
"$TEMPLATE_DIR/my-basement.xml" \
|
||||
-e "s|__GARAGE_ADMIN_TOKEN__|$GARAGE_ADMIN_TOKEN|"
|
||||
|
||||
fetch_template \
|
||||
"$BASE/watchtower.template.xml" \
|
||||
"$TEMPLATE_DIR/my-watchtower.xml"
|
||||
|
||||
fetch_template \
|
||||
"$BASE/host-agent.template.xml" \
|
||||
"$TEMPLATE_DIR/my-host-agent.xml"
|
||||
|
||||
cat <<'EOF'
|
||||
|
||||
────────────────────────────────────────
|
||||
Done. Apply each template in Unraid UI → Docker → Add Container → Template dropdown:
|
||||
|
||||
1. "my-garage" → Apply. Initialise cluster after first start:
|
||||
docker exec garage /garage status # copy node id
|
||||
docker exec garage /garage layout assign -z dc1 -c 1T <node-id>
|
||||
docker exec garage /garage layout apply --version 1
|
||||
Smoke test from your laptop:
|
||||
aws --endpoint-url https://s3.pq.io --region garage s3 ls
|
||||
|
||||
2. "my-basement" → Apply. Reachable at https://basement.pq.io.
|
||||
|
||||
3. "my-watchtower" → Apply. Auto-updates the labelled containers above + below.
|
||||
|
||||
4. "my-host-agent" → Apply. Pushes node/cadvisor/smartctl metrics to your
|
||||
Prometheus (defaults to docker-1.internal.pq.io:9090).
|
||||
────────────────────────────────────────
|
||||
EOF
|
||||
@@ -0,0 +1,54 @@
|
||||
<?xml version="1.0"?>
|
||||
<!--
|
||||
Unraid Community Apps template for Watchtower (auto-image-updater).
|
||||
Source of truth: pq/scripts on Gitea (https://git.docker.pq.io/pq/scripts).
|
||||
Served via gitea raw URL: https://git.docker.pq.io/pq/scripts/raw/branch/main/unraid-1/watchtower.template.xml
|
||||
|
||||
This file is NOT installed directly. install.sh fetches it and writes it to
|
||||
/boot/config/plugins/dockerMan/templates-user/my-watchtower.xml. No secrets
|
||||
to substitute — Watchtower needs none for these (public) images.
|
||||
|
||||
Mirrors the watchtower service on classe (docker-server/docker-compose.yml):
|
||||
same nickfedor/watchtower:1.17.0 image, same fleet-wide 300s poll interval,
|
||||
same opt-in via label. The other unraid-1 templates (garage, basement, host-
|
||||
agent) all carry the enable label, so this container will pick them up.
|
||||
-->
|
||||
<Container version="2">
|
||||
<Name>watchtower</Name>
|
||||
<Repository>nickfedor/watchtower:1.17.0</Repository>
|
||||
<Registry>https://hub.docker.com/r/nickfedor/watchtower</Registry>
|
||||
<Network>bridge</Network>
|
||||
<MyIP/>
|
||||
<Shell>sh</Shell>
|
||||
<Privileged>false</Privileged>
|
||||
<Support/>
|
||||
<Project>https://github.com/nicholas-fedor/watchtower</Project>
|
||||
<Overview>Auto-updates running containers when newer images are published. Opt-in: only watches containers labelled com.centurylinklabs.watchtower.enable=true (garage + basement both carry this).</Overview>
|
||||
<Category>Tools: Utilities:</Category>
|
||||
<WebUI/>
|
||||
<TemplateURL/>
|
||||
<Icon>https://raw.githubusercontent.com/containrrr/watchtower/main/logo.png</Icon>
|
||||
<ExtraParams>--restart=unless-stopped</ExtraParams>
|
||||
<PostArgs/>
|
||||
<CPUset/>
|
||||
<DateInstalled/>
|
||||
<DonateText/>
|
||||
<DonateLink/>
|
||||
<Description>
|
||||
Watches all containers with the label `com.centurylinklabs.watchtower.enable=true` and pulls + restarts them when a newer image tag is published.
|
||||
|
||||
Configured identically to the watchtower service on classe (docker-server/docker-compose.yml) so unraid + classe behave the same way:
|
||||
|
||||
- polls every 300s (5min — fleet-wide default; gentle on ghcr/dockerhub rate limits)
|
||||
- cleans up the old image after replacing a container
|
||||
- opt-in only (WATCHTOWER_LABEL_ENABLE=true) — does NOT touch unlabelled containers
|
||||
|
||||
Does not watch itself: a new Watchtower version requires re-Applying this template (or manually pulling).
|
||||
</Description>
|
||||
|
||||
<Config Name="docker.sock" Target="/var/run/docker.sock" Default="/var/run/docker.sock" Mode="rw" Description="Required: lets Watchtower talk to the Docker daemon to inspect and recreate containers." Type="Path" Display="advanced" Required="true" Mask="false">/var/run/docker.sock</Config>
|
||||
|
||||
<Config Name="WATCHTOWER_POLL_INTERVAL" Target="WATCHTOWER_POLL_INTERVAL" Default="300" Mode="" Description="Seconds between checks for new images. 300s (5min) is the fleet-wide default — gentle on ghcr/dockerhub rate limits, still effectively instant for new pushes." Type="Variable" Display="always" Required="true" Mask="false">300</Config>
|
||||
<Config Name="WATCHTOWER_CLEANUP" Target="WATCHTOWER_CLEANUP" Default="true" Mode="" Description="Delete the previous image after a successful update (frees disk)." Type="Variable" Display="always" Required="true" Mask="false">true</Config>
|
||||
<Config Name="WATCHTOWER_LABEL_ENABLE" Target="WATCHTOWER_LABEL_ENABLE" Default="true" Mode="" Description="Only watch containers with com.centurylinklabs.watchtower.enable=true. Leave true." Type="Variable" Display="always" Required="true" Mask="false">true</Config>
|
||||
</Container>
|
||||
Reference in New Issue
Block a user