Skip to content

Deployment and Operations

This page describes how the Wine Platform is deployed on the Raspberry Pi.

The platform uses systemd for:

  • always-on services
  • kiosk startup
  • daily stock scheduling

Canonical unit names

Use these names consistently.

Purpose Unit
FastAPI backend winecellar-api.service
Qt kiosk frontend winecellar-kiosk.service
Daily stock batch wine-inventory-stock.service
Daily stock scheduler wine-inventory-stock.timer

FastAPI Backend

Source location:

/home/pi/wine_platform/apps/winecellar/backend

Manual start for development:

cd /home/pi/wine_platform/apps/winecellar/backend
source ../.venv/bin/activate
python -m uvicorn app.main:app --host 0.0.0.0 --port 8000

The API becomes reachable at:

http://<pi-ip>:8000/docs

FastAPI Production Service

Service file:

/etc/systemd/system/winecellar-api.service

Example unit:

[Unit]
Description=Winecellar FastAPI
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
User=pi
Group=pi
WorkingDirectory=/home/pi/wine_platform/apps/winecellar
EnvironmentFile=/home/pi/wine_platform/shared/config/shared.env
Environment=PYTHONPATH=/home/pi/wine_platform/apps/winecellar/backend
Environment=GOOGLE_APPLICATION_CREDENTIALS=/home/pi/secret/gcp-vision-key.json
ExecStart=/home/pi/wine_platform/apps/winecellar/.venv/bin/python -m uvicorn backend.app.main:app --host 0.0.0.0 --port 8000
Restart=always
RestartSec=2

[Install]
WantedBy=multi-user.target

Activate and manage:

sudo systemctl daemon-reload
sudo systemctl enable --now winecellar-api.service
sudo systemctl restart winecellar-api.service
systemctl status winecellar-api.service --no-pager
journalctl -u winecellar-api.service -n 100 --no-pager

Qt Kiosk Service

Launcher:

/home/pi/wine_platform/apps/winecellar/frontend/qt_kiosk/run_qt_kiosk.sh

Service file:

/etc/systemd/system/winecellar-kiosk.service

Example unit:

[Unit]
Description=Winecellar Qt Kiosk
After=network-online.target winecellar-api.service
Wants=network-online.target

[Service]
Type=simple
User=pi
Group=pi
Environment=DISPLAY=:0
Environment=XAUTHORITY=/home/pi/.Xauthority
WorkingDirectory=/home/pi/wine_platform/apps/winecellar/frontend/qt_kiosk
ExecStart=/home/pi/wine_platform/apps/winecellar/frontend/qt_kiosk/run_qt_kiosk.sh
Restart=always
RestartSec=3

[Install]
WantedBy=graphical.target

Useful commands:

sudo systemctl daemon-reload
sudo systemctl enable --now winecellar-kiosk.service
sudo systemctl restart winecellar-kiosk.service
systemctl status winecellar-kiosk.service --no-pager
journalctl -u winecellar-kiosk.service -f

Daily Stock Runtime Scheduling

Agreed production solution

The daily stock scan is scheduled with:

  • wine-inventory-stock.service as a oneshot batch service
  • wine-inventory-stock.timer as the daily scheduler

This is preferred over cron.


Confirmed script path

/home/pi/wine_platform/workers/wine_inventory/src/pince_shelf/cli/stock_runtime.py

Do not use the old documentation path under pince_shelf/vision/stock_runtime.py.


Daily stock service

Service file:

/etc/systemd/system/wine-inventory-stock.service

Canonical content:

[Unit]
Description=Wine Inventory Daily Stock Runtime
After=network-online.target
Wants=network-online.target

[Service]
Type=oneshot
User=pi
Group=pi
WorkingDirectory=/home/pi/wine_platform/workers/wine_inventory
ExecStart=/usr/bin/flock -n /tmp/wine_stock_runtime.lock /home/pi/wine_platform/workers/wine_inventory/.venv/bin/python /home/pi/wine_platform/workers/wine_inventory/src/pince_shelf/cli/stock_runtime.py
StandardOutput=journal
StandardError=journal

[Install]
WantedBy=multi-user.target

Why flock is used:

  • prevents overlap if a previous batch is still running
  • prevents overlap if someone manually starts the service during a scheduled run

Daily stock timer

Timer file:

/etc/systemd/system/wine-inventory-stock.timer

Canonical content:

[Unit]
Description=Run Wine Inventory Stock Runtime Daily

[Timer]
OnCalendar=*-*-* 03:00:00
Persistent=true
Unit=wine-inventory-stock.service

[Install]
WantedBy=timers.target

Behavior:

  • runs every day at 03:00
  • Persistent=true catches up a missed run after reboot

Activation

sudo systemctl daemon-reload
sudo systemctl enable --now wine-inventory-stock.timer

Manual testing

Run one daily stock batch immediately:

sudo systemctl start wine-inventory-stock.service

Verify:

systemctl status wine-inventory-stock.service --no-pager
systemctl status wine-inventory-stock.timer --no-pager
systemctl list-timers --all | grep wine-inventory-stock
journalctl -u wine-inventory-stock.service -n 200 --no-pager

Vision pipeline flow

stock_runtime.py
    ↓
take_clean_snapshot.py
    ↓
shelf_rectify.py
    ↓
crop_with_json.py
    ↓
onnx_batch.py
    ↓
DB_compare.py

Deployment overview

flowchart TD
    BOOT[Raspberry Pi boot]
    NET[Network online]
    NAS[NAS mount]
    API[winecellar-api.service]
    KIOSK[winecellar-kiosk.service]
    TIMER[wine-inventory-stock.timer]
    SERVICE[wine-inventory-stock.service]
    SCRIPT[stock_runtime.py]

    BOOT --> NET
    NET --> NAS
    NAS --> API
    API --> KIOSK
    API --> TIMER
    TIMER --> SERVICE
    SERVICE --> SCRIPT

Scheduled execution sequence

sequenceDiagram
    participant Timer as wine-inventory-stock.timer
    participant Service as wine-inventory-stock.service
    participant Script as stock_runtime.py
    participant DB as MariaDB
    participant API as Backend API

    Timer->>Service: trigger daily at 03:00
    Service->>Script: run via worker venv
    Script->>DB: update comparison results
    Script->>API: optional notifications or sync
    Script-->>Service: exit

Runtime data paths

/mnt/nasWeb/web_images/Stock_vision/data/snapshots
/mnt/nasWeb/web_images/Stock_vision/data/cells_runtime
/mnt/nasWeb/web_images/Stock_vision/data/cells_runtime_res

Synology internal equivalent:

/volume2/web/web_images/Stock_vision/data/...

Operational monitoring

systemctl | egrep 'winecellar|wine-inventory'
journalctl -u winecellar-api.service -f
journalctl -u winecellar-kiosk.service -f
journalctl -u wine-inventory-stock.service -f
systemctl list-timers --all | grep wine-inventory

Summary

Use this deployment model consistently:

winecellar-api.service
winecellar-kiosk.service
wine-inventory-stock.timer
    → wine-inventory-stock.service
        → /home/pi/wine_platform/workers/wine_inventory/src/pince_shelf/cli/stock_runtime.py