Posts mit dem Label RaspbianOS werden angezeigt. Alle Posts anzeigen
Posts mit dem Label RaspbianOS werden angezeigt. Alle Posts anzeigen

Montag, 6. April 2026

Problem mit PostrgreSQL auf Raspberry PI

Eigentlich wollte ich eine menge Programme auf einem Raspberry Pi installieren und das am besten über eine Docker Compose Datei einrichten. Aber bevor ich eine Finale Docker Compose Datei erstellen kann, muss ich mich mehr mit den Ziel Programmen auseinandere setzen.

Zugegeben kann eine Einrichtung der Anwendung auch über KI Chats abgefragt werden und gewisser maßen ließe sich mit dem richtigen Prompt, das Lernen neuer Technologien neu gestalten. Also Warum noch ein Blog schreiben? KI Chat geben oft nur das Ergebnis, was oft in der Masse genutzt wird und wenn eine Lösung Obsolete ist, hatte ich oft die alten Lösungen erhalten, die selbstverständlich nicht funktionierten. Dem KI Chat fehlt noch die Kreative Lösungen zu erarbeiten. Egal wie gut oder Schlecht der geschriebene Prompt ist, man tritt an diese Grenze und aus der KI Hilfe wird schnell zur Bremse.

Setup

  • Raspberry Pi 4 mit 4GB Arbeitsspeicher
  • RaspbianOS
  • Docker for Linux an Arm
  • Postgres Datenbank

 

Anforderung

Die Datenbank Postgres soll auf dem Raspberry Pi installiert werden und über das Netzwerk erreichbar sein. Zudem dürfen die Daten nicht weg sein, nach einem Neustart durch updates oder Stromausfall des Raspberry Pi. Weil später noch weiterere Programme auf dem Raspberry Pi ausgeführt werden, darf die Datenbank maximal 30% der CPU Leistung beanspruchen. 

Zusatz ist mein Problem, warum ich wieder ein Blogpost dafür schreibe. Daher die Lösung des Problemfalls beschreiben. 


Vorbereitung

Die Einrichtung der Postgres Datenbank in Docker kann komplett in eine Docker Compose festgehalten werden. Einzig und alleine ist sich mit Docker Compose auseinander zusetzen und anschließend die Inhalte zu kommentieren.

Fangen wir an, einen Ordner anzulegen, in dem die Docker Compose für Postgres abgelegt werden. 

sudo mkdir SetupPostgresql  

Anschließend in den Ordner wechseln

cd SetupPostrgresql  

Und dann mit der Anwendung Nano erstellen wir an eine neue Docker Compose Datei. 

sudo nano docker-compose.yaml

Mit dem bestätigen des Commands, wechselt die Ansicht in den Textbearbeitungswerkzeug Nano.

Den Inhalt füllen wir nun mit dem Typischen YAML Code.  Hier nicht vergessem den Zugang mit Benutzername und Passwort festzulegen. 

version: '3.8'

services:
  postgres:
    image: postgres:latest          # installiert die aktuell verfügbare version.
    container_name: postgres_rpi
    environment:
      POSTGRES_USER: dein_benutzername          # muss eingerichtet werden
      POSTGRES_PASSWORD: dein_sicheres_passwort # muss eingerichtet werden
      POSTGRES_DB: deine_datenbank
    volumes:
      - postgres_data:/var/lib/postgresql/data  # stellt sich, dass die Daten bleiben
    ports:
      - "5432:5432"                 # Der Port, auf die Datenbank erreichbar ist.
    deploy:
      resources:
        limits:
          cpus: '0.3'               # Maximale CPU-Auslastung auf 30% begrenzen
    restart: unless-stopped         # Verfügbarkeit nach dem neustart. 

volumes:
  postgres_data:                    # stellt sicher das die Daten bleiben.

Nach dem Einfügen wird mit dem Befehl STRG + o mit anschließenden ENTER gespeichert und mit STRG + x wird die Anwendung Nano verlassen und wechselt wieder in die Terminal Ansicht.

Nun kann mit der fertigen Datei die Installation durchgeführt werden, um den Container zu starten.

sudo docker-compose up -d

 Falls folgende Meldung kommt, dann das binde Strich weg lassen.

 

Wenn die Ausführung funktioniert hat, dann sollte folgendes als Ergebniss ausgegeben werden.


Zugriff prüfen

Zunächst kann geprüft werden, ob der Docker Container wirklich  ausgeführt wird. 

sudo docker ps

Wenn im folgenden Bild der Status mit "Restarting (1) ..." steht, dann funktioniert der Container nicht und startet in diesen Fall immer wieder neu.

 

Fehlerursache

Um nach dem Problem zu schauen, hilft der Blick in den Docker Logs. Im Command ist darauf zu achten, den Log auf einen Docker Container zu filtern.

sudo docker logs postgres_rpi

Wie bereits in meinem Intro Bild, erschien bei mir der Fehler, dass mit PostgreSQL Version 18 der Pfad zum Persistieren sich geändert hat.

 

Klar ist, dass der Pfad zu ändern ist. Also kehren wie in die Docker-Compose Datei zurück.

sudo nano docker-compose.yaml

Folgende Anpassung bzw. Ergänzung sollte gesetzt werden vor dem Eintrag "volumes".

# WICHTIGE Anpassung und hilft auch bisherige Pfade zu verwenden.
PGDATA: /var/lib/posgresql/data

Weil mir eine Beschreibung zu lang werden würde, habe ich ein Bild vom YAML angelegt und die Stelle Markiert, das einfach Zeigt, wo der Eintrag gesetzt werden soll.

 

Anschließend Speichern mit STRG + o und mit Enter bestätigen. Mit STRG + x die Anwendung Nano beenden.

Probieren wir die Änderung aus.

sudo docker compose up -d

Folgendes Ergebnis kam in meinem Fall. Am Status ist zu sehen, dass nun die Datenbank funktioniert.

Zugriff von einem anderen System prüfen 

Für den Zugriff verwende ich von Jetbrains die Anwendung DataGrip, das für verschiedene Datenbanken verwendet werden kann. Für das einrichten einer neuen Datenbank muss Benutzername und Passwort gesetzt sein, sowie die IP Adresse und Port.

Mit "Test Connection" kann dann geprüft werden, ob die Verbindung zur Datenbank funktioniert.

 

Hat das geklapp, kann nun auf die Datenbank "developer_data" auf dem Raspberry Pi zu gegriffen werden.

 

Abschließend

Vielleicht suche ich einfach nach den falschen Begriffen. Mit Google habe ich die Behebung der Fehlerursache nicht gefunden und der lange Eintrag in Github, war für mich mühselig. Besonders, wenn man zu Müde ist. Hilfreich war das Tool Notebook LM in dem man den Link eingibt und dann mit der KI Chat fragen stellen kann. So kam ich relativ schnell auf eine Lösung, wofür ich sonst deutlich länger gebraucht hatte.

Am Anfang hatte ich bereits schon einen KI Chat probiert und festgestellt, dass das Thema wohl zu neu ist oder das es eins von den Dingen ist, wo einem der KI Chat hinhält, um möglichst viele Tokens zu verbrauchen. Das ist mir schon öfter aufgefallen, dass die Lösung schon nach zwei drei Fragen hätte erledigt sein könne. Daher mein anderer Ansatz mit Notebook LM.

Noch was. Ich hätte das Problem weg lassen können und gleich den Lösungsansatz schreiben können. Den YAML Code habe ich über den KI Chat generiert und erkannte gleich ein Problem einer Hinhalte Taktik von KI Chats oder vielleicht doch "Zufall", dass ich schon letztes jahr festgestellt habe. 

 

Quellen:

https://github.com/docker-library/postgres/pull/1259 

Freitag, 26. Dezember 2025

Codexzier DirectSight - Entwicklung (Teil 1)

Dienst mit .NET für Raspberry Pi entwickeln

Was muss ich tun, um einen Raspberry Pi mit Kamera und zwei Servos über einen Windows-PC anzusteuern?

An dieser Stelle ist klar: Dieses Thema lässt sich nicht in einem kurzen Blogpost mit zwei oder drei Seiten vollständig abhandeln. In Zeiten von Künstlicher Intelligenz und sogenanntem Vibe Coding sind größere Vorhaben jedoch deutlich schneller umsetzbar als noch vor fünf Jahren.
Trotzdem habe ich festgestellt, dass selbst der Einstieg – obwohl er nur ein Teilschritt des Gesamtprojekts ist – überraschend zeitintensiv war. Insgesamt habe ich mehr als drei Stunden an einem Tag investiert. Mit dem entsprechenden Vorwissen hätte ich das vermutlich in 30 Minuten erledigt.

Genau darum geht es mir jedoch: Lernen mit Unterstützung von KI, um die Umsetzung und Ausführung der einzelnen Komponenten wirklich zu verstehen.



Was ich verwende

  • Raspberry Pi 4
  • Raspberry Pi Kamera Modul 3
  • Visual Studio 2026 oder JetBrains Rider 2025.3.1
  • C# mit .NET 10
  • GitHub inkl. GitHub Actions
  • Linux bzw. Raspbian OS

 


Anforderung

Ziel ist zunächst, auf dem Raspberry Pi einen Dienst zu betreiben, der das aktuell ein "Hello World!" ausgibt.
Auf einem Windows-PC soll mit der Consolen Anwendung der Text mit Datum und Uhrzeit abgerufen werden.

Der gesamte Aufbau soll innerhalb eines privaten Netzwerks funktionieren.

  


Dienstanwendung für Raspberry Pi

Für den Dienst auf dem Raspberry Pi verwenden wir zunächst das Template „Worker Service“ in Visual Studio bzw. „Services“ in Rider.


Neben .NET 10 sind zunächst keine weiteren Einstellungen notwendig. Ich habe jedoch zusätzlich die Option „Do not use top-level statements“ aktiviert – hier bin ich bewusst altmodisch.

Anschließend kann ein passender Projekt- und Solution-Name vergeben werden.

 


Testklasse mit Socket

Damit später eine Konsolenanwendung ein „Hello World!“ inklusive Datum und Uhrzeit empfangen kann, wird die Klasse TcpTestServer.cs angelegt.

 using System.Net;  
 using System.Net.Sockets;  
 using System.Text;  
 namespace CodexzierDirectSight  
 {  
   public static class TcpTestServer  
   {  
     private const int Port = 5000;  
     public static async Task StartAsync(CancellationToken stoppingToken, ILogger logger)  
     {  
       var listener = new TcpListener(IPAddress.Any, Port);  
       listener.Start();  
       logger.LogInformation($"TCP Server listening on port {Port}");  
       try  
       {  
         while (!stoppingToken.IsCancellationRequested)  
         {  
           if (listener.Pending())  
           {  
             var client = await listener.AcceptTcpClientAsync(stoppingToken);  
             _ = HandleClientAsync(client, logger);  
           }  
           else  
           {  
             await Task.Delay(100, stoppingToken); // kleine Pause  
           }  
         }  
       }  
       catch (OperationCanceledException) { } // Normaler Stop  
       finally  
       {  
         listener.Stop();  
       }  
     }  
     private static async Task HandleClientAsync(TcpClient client, ILogger logger)  
     {  
       await using var stream = client.GetStream();  
       var message = Encoding.UTF8.GetBytes($"Hello World! {DateTime.Now:U}\n");  
       await stream.WriteAsync(message, 0, message.Length);  
       client.Close();  
       logger.LogInformation("Sent Hello World to client");  
     }  
   }  
 }  

Anschließend muss in der Worker-Klasse eine Instanz von TcpTestServer eingebunden werden.

 namespace CodexzierDirectSight;  
 public class Worker(ILogger<Worker> logger) : BackgroundService  
 {  
   protected override async Task ExecuteAsync(CancellationToken stoppingToken)  
   {  
     _ = TcpTestServer.StartAsync(stoppingToken, logger);  
     while (!stoppingToken.IsCancellationRequested)  
     {  
       if (logger.IsEnabled(LogLevel.Information))  
       {  
         logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now);  
       }  
       await Task.Delay(1000, stoppingToken);  
     }  
   }  
 }  

 


Shell-Script für Deployment

Damit die Installation später per Shell-Eingabe erfolgen kann, wird im Repository-Verzeichnis der Ordner deploy angelegt.



In diesem Ordner befindet sich die Datei install.sh, die im Grunde eine einfache Textdatei ist.

#!/usr/bin/env bash  
 set -e  
 APP_NAME="CodexzierDirectSight"  
 SERVICE_NAME="codexzierdirectsight"  
 INSTALL_DIR="/opt/$SERVICE_NAME"  
 SERVICE_FILE="/etc/systemd/system/$SERVICE_NAME.service"  
 GITHUB_REPO="Codexzier/CodexzierDirectSight"  
 ARCH="linux-arm64"  
 TMP_DIR="/tmp/CodexzierDirectSight-install"  
 echo "== Installing $APP_NAME =="  
 # Root-Check  
 if [ "$EUID" -ne 0 ]; then  
  echo "ERROR: Run as root (use sudo)"  
  exit 1  
 fi  
 # Architektur-Check  
 if [[ "$(uname -m)" != "aarch64" ]]; then  
  echo "ERROR: This installer supports only ARM64"  
  exit 1  
 fi  
 # Vorbereitung  
 rm -rf "$TMP_DIR"  
 mkdir -p "$TMP_DIR"  
 mkdir -p "$INSTALL_DIR"  
 # Latest Release URL ermitteln  
 echo "Fetching latest release info..."  
 DOWNLOAD_URL=$(curl -s https://api.github.com/repos/$GITHUB_REPO/releases/latest \  
  | grep browser_download_url \  
  | grep "linux-arm64" \  
  | cut -d '"' -f 4)  
 if [ -z "$DOWNLOAD_URL" ]; then  
  echo "ERROR: Could not find release artifact"  
  exit 1  
 fi  
 # Download  
 echo "Downloading $DOWNLOAD_URL"  
 curl -L "$DOWNLOAD_URL" -o "$TMP_DIR/app.tar.gz"  
 # Stop Service (falls vorhanden)  
 systemctl stop $SERVICE_NAME 2>/dev/null || true  
 # Entpacken  
 tar -xzf "$TMP_DIR/app.tar.gz" -C "$INSTALL_DIR"  
 chmod +x "$INSTALL_DIR/CodexzierDirectSight"  
 # systemd Service installieren  
 cat > "$SERVICE_FILE" <<EOF  
 [Unit]  
 Description=CodexzierDirectSight Worker Service  
 After=network.target  
 [Service]  
 ExecStart=$INSTALL_DIR/CodexzierDirectSight  
 WorkingDirectory=$INSTALL_DIR  
 Restart=always  
 RestartSec=5  
 KillSignal=SIGINT  
 SyslogIdentifier=CodexzierDirectSight  
 User=root  
 [Install]  
 WantedBy=multi-user.target  
 EOF  
 # systemd reload & start  
 systemctl daemon-reexec  
 systemctl daemon-reload  
 systemctl enable $SERVICE_NAME  
 systemctl start $SERVICE_NAME  
 echo "== Installation complete =="  
 systemctl status $SERVICE_NAME --no-pager  

 


GitHub Workflow einrichten

Um die Bereitstellung über GitHub zu ermöglichen, muss ein entsprechender Workflow eingerichtet werden.

name: Release Codexzier DirectSight  
 on:  
  push:  
   tags:  
    - 'release-*.*.*'  
 permissions:  
  contents: write  
 jobs:  
  build-release:  
   runs-on: ubuntu-latest  
   env:  
    APP_NAME: CodexzierDirectSight  
    PROJECT_PATH: CodexzierDirectSight/CodexzierDirectSight.csproj  
    RUNTIME: linux-arm64  
    CONFIGURATION: Release  
   steps:  
   - name: Checkout source   
    uses: actions/checkout@v4  
   - name: Setup .NET  
    uses: actions/setup-dotnet@v4  
    with:  
     dotnet-version: 10.0.x  
   - name: Restore dependencies  
    run: dotnet restore $PROJECT_PATH  
   - name: Publish (self-contained)  
    run: |  
      dotnet publish $PROJECT_PATH \  
       -c $CONFIGURATION \  
       -r $RUNTIME \  
       --self-contained true \  
       /p:PublishSingleFile=true \  
       /p:PublishTrimmed=false \  
       -o publish  
   - name: Prepare artifact  
    run: |  
      mkdir -p artifact  
      cp -r publish/* artifact/  
      echo "${GITHUB_REF_NAME}" > artifact/VERSION  
      tar -czf codexzierdirectsight-linux-arm64.tar.gz -C artifact .  
   - name: Create GitHub Release  
    uses: softprops/action-gh-release@v2  
    with:  
      files: codexzierdirectsight-linux-arm64.tar.gz  
      name: Release ${{ github.ref_name }}  
      generate_release_notes: true  


Bereitstellung

Die Bereitstellung ist so konzipiert, dass ein neuer Tag im GitHub-Repository erstellt werden muss.


Anschließend auf „Draft new release“ klicken.


Der Name des Tags muss der im Workflow definierten Konvention entsprechen:

release-..*


Danach Titel und Beschreibung für das Release ergänzen und auf „Publish Release“ klicken.


Wurde der Tag korrekt gesetzt, startet automatisch der Workflow. Unter Actions sollte ein neuer Eintrag sichtbar sein. Der orangefarbene, animierte Punkt signalisiert, dass der Build aktuell ausgeführt wird.


Nach wenigen Sekunden wechselt der Status auf Grün, und die Bereitstellung kann auf dem Raspberry Pi mit Raspbian OS abgerufen werden. 


Installation auf dem Raspberry Pi

Hinweis: Der Code Stand hat sich bereits verändert und sollte bereits nicht mehr das "Hello World" zurück geben. Siehe am Ende den Link zu dem Branch, welches den Version Stand zu diesen Blogpost wiedergibt.

Mit folgendem Befehl wird die Installation ausgeführt und der Dienst gestartet:

curl -sSL https://raw.githubusercontent.com/Codexzier/CodexzierDirectSight/master/deploy/install.sh | sudo bash 

 


Testen

Prüfen, ob der Dienst nach der Installation läuft:

 systemctl status codexzierdirectsight

Wichtig ist, dass der Status „active (running)“ in grüner Schrift angezeigt wird. Dann ist sichergestellt, dass der Dienst korrekt läuft.

Ausgabe der aktuellen Log-Einträge anzeigen (Abbruch mit STRG + C):

journalctl -u codexzierdirectsight -f

 


Hello World empfangen

Mit einer Konsolenanwendung unter Windows soll nun das „Hello World!“ inklusive Datum empfangen werden.
Dazu wird eine neue C#-Konsolenanwendung erstellt und folgender Code eingefügt:

internal class Program  
 {  
   static async Task Main(string[] args)  
   {  
     string raspberryIp = "192.168.178.88"; // IP Adresse of Raspberry Pi  
     int port = 5000;  
     using var client = new TcpClient();  
     await client.ConnectAsync(raspberryIp, port);  
     using var stream = client.GetStream();  
     byte[] buffer = new byte[1024];  
     int bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length);  
     string message = Encoding.UTF8.GetString(buffer, 0, bytesRead);  
     Console.WriteLine($"Received from server: {message}");  
   }  
 } 

Nach dem Start sollte folgendes Ergebnis erscheinen:



Rückblick

Rückblickend muss ich sagen: Ohne ChatGPT hätte ich für das Installations-Shell-Script und den YAML-Workflow deutlich länger gebraucht. Vor allem das Verständnis der benötigten Befehle und des Zusammenspiels der einzelnen Schritte hätte mich viel Zeit gekostet.

Über klassische Google-Suchen finde ich solche Lösungen nur schwer – entweder fehlen mir die richtigen Schlagwörter oder die gesponserten Ergebnisse erschweren es, schnell zu beurteilen, ob eine Seite überhaupt relevant ist.

Codestand habe ich im ersten Branch abgelegt, dass zu dem Blogpost gehört:

Codexzier/CodexzierDirectSight at Create-Initialize-RaspberryPi-Service

Gehäuseentwurf für Signalleuchten (Part 3)

Der ESP32-6 benötigt eine ausreichende Platzierung, so das die Verbindung zum USB Stecker möglich ist. Da kam mir in den Sinn, dass ein Aufl...