Tipp · Proxmox · LXC · Docker
Docker und Hawser in einem Debian-13-LXC für Dockhand installieren
Private Anleitung, kostenlos und ohne Gewährleistung, soweit gesetzlich zulässig. Lies das Skript, bevor du es ausführst, und probiere es zuerst in einer Testumgebung aus. Die Nutzung erfolgt auf eigenes Risiko.
Diese Anleitung beschreibt die Vorbereitung eines bereits vorhandenen Debian-13-LXC für die spätere Verwaltung von Docker-Containern mit Dockhand.
Das Installationsscript erledigt Folgendes:
- aktualisiert Debian und installiert benötigte Werkzeuge,
- installiert Docker, sofern Docker noch nicht vorhanden ist,
- legt eine Verzeichnisstruktur für die betreffende Anwendung an,
- lädt das Hawser-Image,
- erzeugt oder übernimmt ein Hawser-Token,
- startet den Hawser-Agenten als Docker-Container,
- zeigt Endpoint und Token für den Dockhand-Eintrag an.
Voraussetzungen
Vor dem Start sollten folgende Punkte erfüllt sein:
- Der Debian-13-LXC ist bereits erstellt und gestartet.
- Du kannst dich als
rootim LXC anmelden. - Der LXC hat Netzwerkzugriff und kann Paketquellen sowie das Container-Image erreichen.
- In der Proxmox-Konfiguration ist der LXC so eingerichtet, dass Docker darin betrieben werden kann.
- Du kennst den Namen, den du für den Anwendungsordner verwenden möchtest, zum Beispiel
paperless.
Ablauf
- Im LXC eine Script-Datei anlegen.
- Das Script einfügen und speichern.
- Das Script ausführbar machen.
- Das Script als
rootausführen. - Endpoint und Token notieren.
- Den Hawser-Agenten in Dockhand als Node hinzufügen.
1. Script-Datei erstellen
Melde dich im Debian-LXC als root an und erstelle die Datei:
nano /root/install-docker.sh
Füge anschließend das folgende Script vollständig in den Editor ein.
Alternative ohne Kopieren: Du kannst das Script auch direkt von GitHub herunterladen und ausführbar machen. Dann entfallen die Schritte 1 bis 4, weiter geht es mit Schritt 5.
wget -O /root/install-docker.sh https://raw.githubusercontent.com/luro611/homelab-tipps/main/docker-hawser-lxc/install-docker.sh
chmod +x /root/install-docker.sh
2. Installationsscript
#!/bin/bash
set -euo pipefail
# =====================================================
# Hawser Worker Setup (Debian 13 "Trixie") – clean & idempotent
# =====================================================
#--- Root check ---
if [ "${EUID:-$(id -u)}" -ne 0 ]; then
echo "Bitte als root ausführen (sudo -i)."
exit 1
fi
export DEBIAN_FRONTEND=noninteractive
# ================================
# 🏷️ 0) LXC / App Name
# ================================
read -rp "👉 Name für diesen LXC / Stack (z.B. paperless): " APP_NAME
if [[ -z "$APP_NAME" ]]; then
echo "❌ Name darf nicht leer sein!"
exit 1
fi
APP_NAME="$(echo "$APP_NAME" | tr '[:upper:]' '[:lower:]' | tr -cd 'a-z0-9-_')"
echo "✅ Verwende Namen: $APP_NAME"
# ================================
# 📁 Struktur
# ================================
BASE_ROOT="/opt/containers"
BASE_DIR="$BASE_ROOT/$APP_NAME"
DATA_DIR="$BASE_DIR/data"
VOLUMES_DIR="$BASE_DIR/volumes"
CONFIGS_DIR="$BASE_DIR/configs"
LOG_FILE="/var/log/hawser-${APP_NAME}-setup.log"
CONTAINER_NAME="hawser-agent"
HAWSER_IMAGE="ghcr.io/finsys/hawser:latest"
# FIX: richtiger Hawser Port
HAWSER_PORT="2376"
# Token-Datei (wird bei erneutem Lauf wiederverwendet)
TOKEN_FILE="$BASE_DIR/hawser.token"
# ================================
# 📝 Logging
# ================================
mkdir -p "$(dirname "$LOG_FILE")"
touch "$LOG_FILE"
chmod 0600 "$LOG_FILE"
exec > >(tee -a "$LOG_FILE") 2>&1
timestamp() { date +"%Y-%m-%d %H:%M:%S %z"; }
log() { echo "$(timestamp) | $*"; }
log "🚀 Starte Hawser Setup für: $APP_NAME"
# ================================
# 🔄 1) System Update
# ================================
log "🔄 Systemupdate..."
apt update
apt -y upgrade
apt -y autoremove
# ================================
# 🔧 2) Tools
# ================================
log "🔧 Installiere Tools..."
apt install -y curl ca-certificates gnupg lsb-release apt-transport-https openssl
# ================================
# 🐳 3) Docker installieren
# ================================
if ! command -v docker >/dev/null 2>&1; then
log "🐳 Installiere Docker..."
install -d -m 0755 /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/debian/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg
chmod a+r /etc/apt/keyrings/docker.gpg
CODENAME="$(lsb_release -cs || echo trixie)"
ARCH="$(dpkg --print-architecture)"
cat >/etc/apt/sources.list.d/docker.list <<EOF
deb [arch=${ARCH} signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/debian ${CODENAME} stable
EOF
if ! apt update; then
log "⚠️ Fallback auf bookworm..."
cat >/etc/apt/sources.list.d/docker.list <<EOF
deb [arch=${ARCH} signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/debian bookworm stable
EOF
apt update
fi
apt install -y --no-install-recommends docker-ce docker-ce-cli containerd.io
apt install -y --no-install-recommends docker-buildx-plugin docker-compose-plugin
systemctl enable --now docker
log "✅ Docker installiert."
else
log "✅ Docker bereits vorhanden."
systemctl enable --now docker || true
fi
# ================================
# 📁 4) Verzeichnisse
# ================================
log "📁 Erstelle Verzeichnisstruktur..."
mkdir -p "$DATA_DIR" "$VOLUMES_DIR" "$CONFIGS_DIR"
chmod -R 755 "$BASE_DIR"
chown -R root:root "$BASE_DIR"
# ================================
# 📦 5) Hawser Image
# ================================
log "📦 Lade Hawser Image..."
docker pull "$HAWSER_IMAGE" || log "⚠️ Pull fehlgeschlagen (offline?)"
# ================================
# 🔑 6) Token erzeugen / wiederverwenden
# ================================
if [ -s "$TOKEN_FILE" ]; then
HAWSER_TOKEN="$(cat "$TOKEN_FILE")"
log "🔑 Vorhandenes Token wird wiederverwendet."
else
HAWSER_TOKEN="$(openssl rand -hex 24)"
( umask 077; echo "$HAWSER_TOKEN" > "$TOKEN_FILE" )
log "🔑 Neues Token erzeugt."
fi
# ================================
# 🚀 7) CLEAN DEPLOY (WICHTIG!)
# ================================
log "🚀 Entferne alte Hawser Instanz (falls vorhanden)..."
docker rm -f "$CONTAINER_NAME" >/dev/null 2>&1 || true
log "🚀 Starte Hawser Agent neu..."
docker run -d \
--name "$CONTAINER_NAME" \
--restart unless-stopped \
-p ${HAWSER_PORT}:2376 \
-e TOKEN="$HAWSER_TOKEN" \
-v /var/run/docker.sock:/var/run/docker.sock \
"$HAWSER_IMAGE"
sleep 3
if [ "$(docker inspect -f '{{.State.Running}}' "$CONTAINER_NAME" 2>/dev/null)" != "true" ]; then
log "❌ Hawser Container läuft nicht! Letzte Logs:"
docker logs "$CONTAINER_NAME" 2>&1 | tail -20
exit 1
fi
log "✅ Hawser Agent läuft."
# ================================
# 🌐 Endpoint + Token
# ================================
HOST_IP="$(hostname -I | awk '{print $1}')"
log "🌐 Hawser Endpoint: http://${HOST_IP}:${HAWSER_PORT}"
# ================================
# 🏁 Done
# ================================
log "🏁 Setup abgeschlossen für: $APP_NAME"
log "📁 Base: $BASE_DIR"
echo
echo "=================== DOCKHAND-EINTRAG ==================="
echo "Endpoint : http://${HOST_IP}:${HAWSER_PORT}"
echo "Token : ${HAWSER_TOKEN}"
echo "(Token gespeichert in: ${TOKEN_FILE})"
echo "========================================================"
echo
echo "➡️ Jetzt in Dockhand als Node hinzufügen (Endpoint + Token)"
3. Datei speichern
In nano:
CTRL + Odrücken.- Mit
ENTERden Dateinamen bestätigen. CTRL + Xdrücken, um den Editor zu schließen.
4. Script ausführbar machen
chmod +x /root/install-docker.sh
5. Script ausführen
/root/install-docker.sh
Das Script fragt nach einem Namen für den LXC beziehungsweise Stack, zum Beispiel:
paperless
Der eingegebene Name wird in Kleinbuchstaben umgewandelt. Zeichen, die nicht zu a-z, 0-9, - oder _ gehören, werden entfernt.
Erklärung der Script-Abschnitte
Start und Root-Prüfung
#!/bin/bash
set -euo pipefail
#!/bin/bashlegt Bash als Interpreter fest.set -ebeendet das Script bei einem nicht abgefangenen Fehler.set -ubehandelt nicht gesetzte Variablen als Fehler.set -o pipefailsorgt dafür, dass auch Fehler innerhalb einer Befehlskette mit|erkannt werden.
Anschließend wird geprüft, ob das Script mit Root-Rechten ausgeführt wird. Ist das nicht der Fall, erscheint eine Fehlermeldung und das Script beendet sich.
export DEBIAN_FRONTEND=noninteractive
Diese Einstellung verhindert bei der Paketinstallation interaktive Rückfragen von Debian-Paketinstallern.
Name für LXC beziehungsweise Anwendung
Das Script fragt nach einem Namen:
read -rp "👉 Name für diesen LXC / Stack (z.B. paperless): " APP_NAME
Dieser Name dient unter anderem dazu, die Verzeichnisse und den Logdateinamen zu bestimmen. Er ist nicht der Name des Hawser-Containers. Dieser heißt im Script fest:
CONTAINER_NAME="hawser-agent"
Verzeichnisstruktur
Bei der Eingabe paperless werden folgende Variablen gesetzt:
BASE_ROOT="/opt/containers"
BASE_DIR="$BASE_ROOT/$APP_NAME"
DATA_DIR="$BASE_DIR/data"
VOLUMES_DIR="$BASE_DIR/volumes"
CONFIGS_DIR="$BASE_DIR/configs"
Daraus ergibt sich:
/opt/containers/ Wurzelverzeichnis für Anwendungen
└── paperless/ BASE_DIR
├── data/ DATA_DIR
├── volumes/ VOLUMES_DIR
└── configs/ CONFIGS_DIR
Wofür sind die Ordner gedacht?
data/– vorgesehener Ablageort für persistente Anwendungsdaten.volumes/– vorgesehener Ablageort für Daten, die als Bind-Mounts in Docker-Container eingebunden werden.configs/– vorgesehener Ablageort für Konfigurationsdateien.
Protokollierung
LOG_FILE="/var/log/hawser-${APP_NAME}-setup.log"
Das Script legt für jeden eingegebenen Namen eine eigene Logdatei unter /var/log an. Bei paperless lautet der Pfad beispielsweise:
/var/log/hawser-paperless-setup.log
Die Ausgabe des Scripts wird sowohl im Terminal angezeigt als auch an die Logdatei angehängt.
Systemupdate und benötigte Werkzeuge
Das Script aktualisiert die Paketlisten, installiert verfügbare Updates und entfernt nicht mehr benötigte Pakete:
apt update
apt -y upgrade
apt -y autoremove
Danach werden Werkzeuge installiert, die das Script für den Docker-Schritt und die Token-Erzeugung benötigt:
apt install -y curl ca-certificates gnupg lsb-release apt-transport-https openssl
Docker installieren oder starten
Das Script prüft zunächst, ob der Befehl docker vorhanden ist.
- Docker ist noch nicht installiert: Das Script richtet den Docker-Paketquelleneintrag ein und installiert Docker sowie die Compose- und Buildx-Plugins.
- Docker ist bereits installiert: Das Script versucht, den Docker-Dienst zu aktivieren und zu starten.
Der Dienst wird mit folgendem Befehl aktiviert und gestartet:
systemctl enable --now docker
enable sorgt dafür, dass Docker beim Start des LXC automatisch gestartet wird. --now startet den Dienst direkt.
Ordner anlegen und Berechtigungen setzen
mkdir -p "$DATA_DIR" "$VOLUMES_DIR" "$CONFIGS_DIR"
chmod -R 755 "$BASE_DIR"
chown -R root:root "$BASE_DIR"
Was bedeuten die Befehle?
mkdir -perstellt die Verzeichnisse einschließlich der benötigten übergeordneten Ordner. Bereits vorhandene Verzeichnisse führen dabei nicht allein deshalb zu einem Fehler.chmod -R 755setzt rekursiv die Berechtigungen unterhalb vonBASE_DIRauf755.chown -R root:rootsetzt rekursiv Eigentümer und Gruppe aufroot.
Bei 755 darf der Eigentümer lesen, schreiben und Verzeichnisse betreten; Gruppe und andere Benutzer dürfen lesen und Verzeichnisse betreten.
Beispiel für eine Anwendung, die mit UID und GID 1000 arbeiten soll:
chown -R 1000:1000 /opt/containers/paperless
Diesen Befehl nicht blind verwenden: Die benötigten IDs hängen von der jeweiligen Anwendung ab.
Hawser-Image herunterladen
HAWSER_IMAGE="ghcr.io/finsys/hawser:latest"
docker pull "$HAWSER_IMAGE"
Das Image wird aus der angegebenen Container-Registry geladen. Falls der Download fehlschlägt, schreibt das Script eine Warnung ins Log und fährt weiter.
Hawser-Token erzeugen oder wiederverwenden
Das Token wird unter folgendem Pfad gespeichert:
/opt/containers/<APP_NAME>/hawser.token
Beispiel:
/opt/containers/paperless/hawser.token
- Ist die Datei bereits vorhanden und nicht leer, wird ihr Inhalt wiederverwendet.
- Andernfalls wird ein neues Token erzeugt und in der Datei gespeichert.
Das Token ist ein Zugangsschlüssel für die Verbindung mit Hawser. Behandle es vertraulich und teile es nicht öffentlich.
Hawser-Container starten
Vor dem Start entfernt das Script einen eventuell bereits vorhandenen Container namens hawser-agent:
docker rm -f "$CONTAINER_NAME" >/dev/null 2>&1 || true
Anschließend wird Hawser gestartet:
docker run -d \
--name "$CONTAINER_NAME" \
--restart unless-stopped \
-p ${HAWSER_PORT}:2376 \
-e TOKEN="$HAWSER_TOKEN" \
-v /var/run/docker.sock:/var/run/docker.sock \
"$HAWSER_IMAGE"
Bedeutung der wichtigsten Optionen
-d– startet den Container im Hintergrund.--name hawser-agent– vergibt den Containernamen.--restart unless-stopped– Docker startet den Container nach einem Neustart erneut, sofern er nicht ausdrücklich gestoppt wurde.-p 2376:2376– veröffentlicht Port2376des Containers auf Port2376des LXC.-e TOKEN=...– übergibt das Token als Umgebungsvariable.-v /var/run/docker.sock:/var/run/docker.sock– stellt dem Hawser-Container den Docker-Socket des LXC bereit.
Nach dem Start wartet das Script drei Sekunden und prüft, ob der Hawser-Container läuft. Falls nicht, gibt es die letzten Container-Logs aus und beendet sich mit einem Fehler.
Endpoint ermitteln
Das Script nimmt die erste von hostname -I ausgegebene IP-Adresse:
HOST_IP="$(hostname -I | awk '{print $1}')"
Anschließend wird der Endpoint nach diesem Schema ausgegeben:
http://<IP-Adresse-des-LXC>:2376
Hawser in Dockhand hinzufügen
Nach erfolgreichem Abschluss zeigt das Script einen Abschnitt ähnlich diesem an:
=================== DOCKHAND-EINTRAG ===================
Endpoint : http://192.168.1.50:2376
Token : <dein-token>
(Token gespeichert in: /opt/containers/paperless/hawser.token)
========================================================
Trage in Dockhand die angezeigten Werte als Hawser-Node ein:
- Endpoint: die angezeigte Adresse, zum Beispiel
http://192.168.1.50:2376 - Token: das vom Script angezeigte Token
Der Endpoint muss vom System, auf dem Dockhand läuft, erreichbar sein. Wenn keine Verbindung zustande kommt, prüfe insbesondere:
- Läuft der Hawser-Container?
- Ist die verwendete IP-Adresse die richtige?
- Ist Port
2376vom Dockhand-System aus erreichbar? - Stimmen Endpoint und Token exakt?
- Gibt es eine Firewall- oder LXC-Netzwerkeinstellung, die die Verbindung blockiert?
Status und Fehlersuche
Docker-Status prüfen
systemctl status docker
Hawser-Container anzeigen
docker ps -a --filter name=hawser-agent
Hawser-Logs anzeigen
docker logs hawser-agent
Setup-Log anzeigen
Ersetze paperless durch den beim Start eingegebenen Namen:
cat /var/log/hawser-paperless-setup.log
Das Log wird bei einem weiteren Lauf ergänzt.
NFS-Shares verwenden
Wenn eine Docker-Anwendung später auf ein NFS-Share zugreifen soll, muss im LXC ein NFS-Client installiert sein:
apt-get update
apt-get install -y nfs-common
Damit ist der NFS-Client im LXC installiert. Das Einbinden des NFS-Shares und dessen Verwendung als Docker-Bind-Mount sind separate Schritte und werden von diesem Hawser-Installationsscript nicht automatisch eingerichtet.
Ergänzende Hinweise zur Verzeichnisstruktur
Beispiel mit paperless
Wenn beim Scriptstart paperless eingegeben wird, entsteht folgende Struktur:
/opt/containers/paperless/
├── data/
├── volumes/
├── configs/
└── hawser.token
Die Ordner können später beispielsweise in einer Docker-Konfiguration als Bind-Mounts verwendet werden. Ein allgemeines Beispiel:
/opt/containers/paperless/data:/Pfad/im/Anwendungscontainer
Der Zielpfad innerhalb des Containers muss zur jeweiligen Anwendung passen. Er ist nicht für alle Anwendungen gleich.
Erneuter Scriptlauf
Das Script kann erneut ausgeführt werden. Dabei gilt:
- Ein vorhandenes Hawser-Token wird wiederverwendet, sofern die Token-Datei vorhanden und nicht leer ist.
- Ein bereits vorhandener Container mit dem Namen
hawser-agentwird entfernt und anschließend neu erstellt. - Die Ordner werden erneut angelegt, falls sie fehlen.
- Die Berechtigungen und Eigentümer unter
BASE_DIRwerden erneut rekursiv auf755beziehungsweiseroot:rootgesetzt. - Die Systempakete werden erneut aktualisiert.
Abschluss
Nach erfolgreichem Abschluss sind Docker und der Hawser-Agent im Debian-13-LXC vorbereitet. Notiere Endpoint und Token sicher und füge den Agenten in Dockhand als Node hinzu.
Die eigentlichen Anwendungen sowie ihre Volumes, Netzwerke und Konfigurationen werden danach in Dockhand eingerichtet.
Tip · Proxmox · LXC · Docker
Installing Docker and Hawser in a Debian 13 LXC for Dockhand
A private guide, free of charge and without warranty to the extent permitted by law. Read the script before you run it and try it in a test environment first. Use at your own risk.
This guide describes how to prepare an existing Debian 13 LXC so that its Docker containers can later be managed with Dockhand.
The install script does the following:
- updates Debian and installs the required tools,
- installs Docker if it is not already present,
- creates a directory structure for the application in question,
- pulls the Hawser image,
- creates a Hawser token or reuses an existing one,
- starts the Hawser agent as a Docker container,
- shows the endpoint and token for the Dockhand entry.
Prerequisites
Before you start, the following should be true:
- The Debian 13 LXC has already been created and started.
- You can log in to the LXC as
root. - The LXC has network access and can reach the package sources and the container image.
- The LXC is configured in Proxmox so that Docker can run inside it.
- You know which name you want to use for the application folder, for example
paperless.
Overview
- Create a script file in the LXC.
- Paste the script and save it.
- Make the script executable.
- Run the script as
root. - Note down the endpoint and token.
- Add the Hawser agent to Dockhand as a node.
1. Create the script file
Log in to the Debian LXC as root and create the file:
nano /root/install-docker.sh
Then paste the complete script below into the editor.
Alternative without copying: You can also download the script straight from GitHub and make it executable. Steps 1 to 4 are then not needed; continue with step 5.
wget -O /root/install-docker.sh https://raw.githubusercontent.com/luro611/homelab-tipps/main/docker-hawser-lxc/install-docker.sh
chmod +x /root/install-docker.sh
2. Install script
The script is the same as in the German guide; its comments and messages are in German. You can also open it as a separate file: install-docker.sh.
#!/bin/bash
set -euo pipefail
# =====================================================
# Hawser Worker Setup (Debian 13 "Trixie") – clean & idempotent
# =====================================================
#--- Root check ---
if [ "${EUID:-$(id -u)}" -ne 0 ]; then
echo "Bitte als root ausführen (sudo -i)."
exit 1
fi
export DEBIAN_FRONTEND=noninteractive
# ================================
# 🏷️ 0) LXC / App Name
# ================================
read -rp "👉 Name für diesen LXC / Stack (z.B. paperless): " APP_NAME
if [[ -z "$APP_NAME" ]]; then
echo "❌ Name darf nicht leer sein!"
exit 1
fi
APP_NAME="$(echo "$APP_NAME" | tr '[:upper:]' '[:lower:]' | tr -cd 'a-z0-9-_')"
echo "✅ Verwende Namen: $APP_NAME"
# ================================
# 📁 Struktur
# ================================
BASE_ROOT="/opt/containers"
BASE_DIR="$BASE_ROOT/$APP_NAME"
DATA_DIR="$BASE_DIR/data"
VOLUMES_DIR="$BASE_DIR/volumes"
CONFIGS_DIR="$BASE_DIR/configs"
LOG_FILE="/var/log/hawser-${APP_NAME}-setup.log"
CONTAINER_NAME="hawser-agent"
HAWSER_IMAGE="ghcr.io/finsys/hawser:latest"
# FIX: richtiger Hawser Port
HAWSER_PORT="2376"
# Token-Datei (wird bei erneutem Lauf wiederverwendet)
TOKEN_FILE="$BASE_DIR/hawser.token"
# ================================
# 📝 Logging
# ================================
mkdir -p "$(dirname "$LOG_FILE")"
touch "$LOG_FILE"
chmod 0600 "$LOG_FILE"
exec > >(tee -a "$LOG_FILE") 2>&1
timestamp() { date +"%Y-%m-%d %H:%M:%S %z"; }
log() { echo "$(timestamp) | $*"; }
log "🚀 Starte Hawser Setup für: $APP_NAME"
# ================================
# 🔄 1) System Update
# ================================
log "🔄 Systemupdate..."
apt update
apt -y upgrade
apt -y autoremove
# ================================
# 🔧 2) Tools
# ================================
log "🔧 Installiere Tools..."
apt install -y curl ca-certificates gnupg lsb-release apt-transport-https openssl
# ================================
# 🐳 3) Docker installieren
# ================================
if ! command -v docker >/dev/null 2>&1; then
log "🐳 Installiere Docker..."
install -d -m 0755 /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/debian/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg
chmod a+r /etc/apt/keyrings/docker.gpg
CODENAME="$(lsb_release -cs || echo trixie)"
ARCH="$(dpkg --print-architecture)"
cat >/etc/apt/sources.list.d/docker.list <<EOF
deb [arch=${ARCH} signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/debian ${CODENAME} stable
EOF
if ! apt update; then
log "⚠️ Fallback auf bookworm..."
cat >/etc/apt/sources.list.d/docker.list <<EOF
deb [arch=${ARCH} signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/debian bookworm stable
EOF
apt update
fi
apt install -y --no-install-recommends docker-ce docker-ce-cli containerd.io
apt install -y --no-install-recommends docker-buildx-plugin docker-compose-plugin
systemctl enable --now docker
log "✅ Docker installiert."
else
log "✅ Docker bereits vorhanden."
systemctl enable --now docker || true
fi
# ================================
# 📁 4) Verzeichnisse
# ================================
log "📁 Erstelle Verzeichnisstruktur..."
mkdir -p "$DATA_DIR" "$VOLUMES_DIR" "$CONFIGS_DIR"
chmod -R 755 "$BASE_DIR"
chown -R root:root "$BASE_DIR"
# ================================
# 📦 5) Hawser Image
# ================================
log "📦 Lade Hawser Image..."
docker pull "$HAWSER_IMAGE" || log "⚠️ Pull fehlgeschlagen (offline?)"
# ================================
# 🔑 6) Token erzeugen / wiederverwenden
# ================================
if [ -s "$TOKEN_FILE" ]; then
HAWSER_TOKEN="$(cat "$TOKEN_FILE")"
log "🔑 Vorhandenes Token wird wiederverwendet."
else
HAWSER_TOKEN="$(openssl rand -hex 24)"
( umask 077; echo "$HAWSER_TOKEN" > "$TOKEN_FILE" )
log "🔑 Neues Token erzeugt."
fi
# ================================
# 🚀 7) CLEAN DEPLOY (WICHTIG!)
# ================================
log "🚀 Entferne alte Hawser Instanz (falls vorhanden)..."
docker rm -f "$CONTAINER_NAME" >/dev/null 2>&1 || true
log "🚀 Starte Hawser Agent neu..."
docker run -d \
--name "$CONTAINER_NAME" \
--restart unless-stopped \
-p ${HAWSER_PORT}:2376 \
-e TOKEN="$HAWSER_TOKEN" \
-v /var/run/docker.sock:/var/run/docker.sock \
"$HAWSER_IMAGE"
sleep 3
if [ "$(docker inspect -f '{{.State.Running}}' "$CONTAINER_NAME" 2>/dev/null)" != "true" ]; then
log "❌ Hawser Container läuft nicht! Letzte Logs:"
docker logs "$CONTAINER_NAME" 2>&1 | tail -20
exit 1
fi
log "✅ Hawser Agent läuft."
# ================================
# 🌐 Endpoint + Token
# ================================
HOST_IP="$(hostname -I | awk '{print $1}')"
log "🌐 Hawser Endpoint: http://${HOST_IP}:${HAWSER_PORT}"
# ================================
# 🏁 Done
# ================================
log "🏁 Setup abgeschlossen für: $APP_NAME"
log "📁 Base: $BASE_DIR"
echo
echo "=================== DOCKHAND-EINTRAG ==================="
echo "Endpoint : http://${HOST_IP}:${HAWSER_PORT}"
echo "Token : ${HAWSER_TOKEN}"
echo "(Token gespeichert in: ${TOKEN_FILE})"
echo "========================================================"
echo
echo "➡️ Jetzt in Dockhand als Node hinzufügen (Endpoint + Token)"
3. Save the file
In nano:
- Press
CTRL + O. - Press
ENTERto confirm the file name. - Press
CTRL + Xto close the editor.
4. Make the script executable
chmod +x /root/install-docker.sh
5. Run the script
/root/install-docker.sh
The script asks for a name for the LXC or stack, for example:
paperless
The name is converted to lower case. Characters other than a-z, 0-9, - or _ are removed.
What each part of the script does
Start and root check
#!/bin/bash
set -euo pipefail
#!/bin/bashsets Bash as the interpreter.set -estops the script on any error that is not handled.set -utreats unset variables as an error.set -o pipefailmakes sure errors inside a pipeline with|are detected as well.
Next, the script checks whether it is running with root privileges. If not, it prints an error message and exits.
export DEBIAN_FRONTEND=noninteractive
This setting prevents Debian package installers from asking interactive questions during installation.
Name for the LXC or application
The script asks for a name:
read -rp "👉 Name für diesen LXC / Stack (z.B. paperless): " APP_NAME
Among other things, this name determines the directories and the name of the log file. It is not the name of the Hawser container, which is fixed in the script:
CONTAINER_NAME="hawser-agent"
Directory structure
If you enter paperless, the following variables are set:
BASE_ROOT="/opt/containers"
BASE_DIR="$BASE_ROOT/$APP_NAME"
DATA_DIR="$BASE_DIR/data"
VOLUMES_DIR="$BASE_DIR/volumes"
CONFIGS_DIR="$BASE_DIR/configs"
This results in:
/opt/containers/ root directory for applications
└── paperless/ BASE_DIR
├── data/ DATA_DIR
├── volumes/ VOLUMES_DIR
└── configs/ CONFIGS_DIR
What are the folders for?
data/– intended for persistent application data.volumes/– intended for data that is mounted into Docker containers as bind mounts.configs/– intended for configuration files.
Logging
LOG_FILE="/var/log/hawser-${APP_NAME}-setup.log"
The script creates a separate log file under /var/log for each name you enter. For paperless, the path is:
/var/log/hawser-paperless-setup.log
The script's output is shown in the terminal and appended to the log file at the same time.
System update and required tools
The script updates the package lists, installs available updates and removes packages that are no longer needed:
apt update
apt -y upgrade
apt -y autoremove
It then installs the tools the script needs for the Docker step and for creating the token:
apt install -y curl ca-certificates gnupg lsb-release apt-transport-https openssl
Install or start Docker
The script first checks whether the docker command exists.
- Docker is not installed yet: The script adds the Docker package source and installs Docker together with the Compose and Buildx plugins.
- Docker is already installed: The script tries to enable and start the Docker service.
The service is enabled and started with:
systemctl enable --now docker
enable makes Docker start automatically whenever the LXC starts. --now starts the service right away.
Create folders and set permissions
mkdir -p "$DATA_DIR" "$VOLUMES_DIR" "$CONFIGS_DIR"
chmod -R 755 "$BASE_DIR"
chown -R root:root "$BASE_DIR"
What do these commands do?
mkdir -pcreates the directories including any parent folders they need. Directories that already exist do not cause an error just because they exist.chmod -R 755recursively sets the permissions belowBASE_DIRto755.chown -R root:rootrecursively sets owner and group toroot.
With 755, the owner may read, write and enter directories; the group and other users may read and enter directories.
Example for an application that is meant to run with UID and GID 1000:
chown -R 1000:1000 /opt/containers/paperless
Do not use this command blindly: the IDs you need depend on the application.
Pull the Hawser image
HAWSER_IMAGE="ghcr.io/finsys/hawser:latest"
docker pull "$HAWSER_IMAGE"
The image is pulled from the container registry given here. If the download fails, the script writes a warning to the log and carries on.
Create or reuse the Hawser token
The token is stored at:
/opt/containers/<APP_NAME>/hawser.token
Example:
/opt/containers/paperless/hawser.token
- If the file already exists and is not empty, its content is reused.
- Otherwise a new token is created and saved to the file.
The token is an access key for the connection to Hawser. Keep it confidential and do not share it publicly.
Start the Hawser container
Before starting, the script removes any existing container named hawser-agent:
docker rm -f "$CONTAINER_NAME" >/dev/null 2>&1 || true
Hawser is then started:
docker run -d \
--name "$CONTAINER_NAME" \
--restart unless-stopped \
-p ${HAWSER_PORT}:2376 \
-e TOKEN="$HAWSER_TOKEN" \
-v /var/run/docker.sock:/var/run/docker.sock \
"$HAWSER_IMAGE"
What the main options mean
-d– runs the container in the background.--name hawser-agent– sets the container name.--restart unless-stopped– Docker starts the container again after a restart unless it was stopped explicitly.-p 2376:2376– publishes port2376of the container on port2376of the LXC.-e TOKEN=...– passes the token as an environment variable.-v /var/run/docker.sock:/var/run/docker.sock– gives the Hawser container access to the LXC's Docker socket.
After starting it, the script waits three seconds and checks whether the Hawser container is running. If it is not, it prints the last container logs and exits with an error.
Determine the endpoint
The script takes the first IP address printed by hostname -I:
HOST_IP="$(hostname -I | awk '{print $1}')"
It then prints the endpoint in this form:
http://<IP-address-of-the-LXC>:2376
Add Hawser to Dockhand
When the script finishes successfully, it shows a block similar to this one (DOCKHAND-EINTRAG means “Dockhand entry”):
=================== DOCKHAND-EINTRAG ===================
Endpoint : http://192.168.1.50:2376
Token : <your-token>
(Token gespeichert in: /opt/containers/paperless/hawser.token)
========================================================
Enter the values shown in Dockhand as a Hawser node:
- Endpoint: the address shown, for example
http://192.168.1.50:2376 - Token: the token shown by the script
The endpoint must be reachable from the system Dockhand runs on. If no connection can be made, check in particular:
- Is the Hawser container running?
- Is the IP address the right one?
- Can port
2376be reached from the Dockhand system? - Do the endpoint and token match exactly?
- Is a firewall or an LXC network setting blocking the connection?
Status and troubleshooting
Check Docker status
systemctl status docker
Show the Hawser container
docker ps -a --filter name=hawser-agent
Show the Hawser logs
docker logs hawser-agent
Show the setup log
Replace paperless with the name you entered at the start:
cat /var/log/hawser-paperless-setup.log
Each further run appends to the log.
Using NFS shares
If a Docker application later needs to access an NFS share, an NFS client must be installed in the LXC:
apt-get update
apt-get install -y nfs-common
This installs the NFS client in the LXC. Mounting the NFS share and using it as a Docker bind mount are separate steps that this Hawser install script does not set up.
More on the directory structure
Example with paperless
If you enter paperless when the script starts, this structure is created:
/opt/containers/paperless/
├── data/
├── volumes/
├── configs/
└── hawser.token
The folders can later be used as bind mounts in a Docker configuration, for example. A general example:
/opt/containers/paperless/data:/path/inside/the/application/container
The target path inside the container has to match the application. It is not the same for every application.
Running the script again
You can run the script again. In that case:
- An existing Hawser token is reused, as long as the token file exists and is not empty.
- An existing container named
hawser-agentis removed and then created again. - The folders are created again if they are missing.
- Permissions and ownership below
BASE_DIRare again set recursively to755androot:root. - The system packages are updated again.
Done
When the script has finished successfully, Docker and the Hawser agent are ready in the Debian 13 LXC. Store the endpoint and token safely and add the agent to Dockhand as a node.
The actual applications and their volumes, networks and configurations are then set up in Dockhand.