# Stack for Portainer. Everything it needs lives in the image, so it can be # deployed by pasting this file — no checkout of the repository on the server. # # Environment variables to set in the Portainer stack: # IMAGE registry.tibiscuit.ch/vm-selector:latest # POSTGRES_PASSWORD anything, it never leaves the internal network # APP_PORT host port for the reverse proxy to point at (default 3010) services: db: image: postgres:18.3-alpine restart: unless-stopped environment: POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in the stack} POSTGRES_DB: vmselector volumes: # Postgres 18 moved PGDATA to /var/lib/postgresql/18/docker and its declared # VOLUME to /var/lib/postgresql. Mounting the pre-18 /var/lib/postgresql/data # leaves the real data outside the volume, and trips the entrypoint. - db_data:/var/lib/postgresql healthcheck: test: ['CMD-SHELL', 'pg_isready -U postgres -d vmselector'] interval: 5s timeout: 5s retries: 10 # initdb runs on the very first boot: do not count failures before it is done start_period: 30s networks: [internal] # Runs once per deployment, before the app: applies any new migration, then exits migrate: image: ${IMAGE:?set IMAGE in the stack} restart: 'no' entrypoint: ['dbmate', '--migrations-dir', '/app/db/migrations', '--no-dump-schema', 'up'] environment: DATABASE_URL: postgres://postgres:${POSTGRES_PASSWORD}@db:5432/vmselector?sslmode=disable depends_on: db: condition: service_healthy networks: [internal] app: image: ${IMAGE:?set IMAGE in the stack} restart: unless-stopped ports: - '${APP_PORT:-3010}:3000' environment: APP__POSTGRES__HOST: db APP__POSTGRES__PASSWORD: ${POSTGRES_PASSWORD} APP__POSTGRES__NAME: vmselector APP__VM__DRIVER: libvirt # The socket bind-mounted below, not ssh: the app runs on the same host APP__VM__URI: qemu:///system volumes: # The whole directory, not a single socket: depending on whether the host # runs the monolithic libvirtd or the split daemons, the app needs # libvirt-sock or virtqemud-sock. - /var/run/libvirt:/var/run/libvirt # No `group_add`: the container runs as root (see the Dockerfile for why), so # it needs no group membership to reach the socket. depends_on: db: condition: service_healthy migrate: condition: service_completed_successfully networks: [internal] volumes: db_data: networks: internal: