kegtrack
MIT licenceRust · PostgreSQL 17FR · EN

Scan a keg, know where it has been.

kegtrack puts a QR label on every returnable beer keg and turns each move, in the cellar, on the truck or at the customer, into one scan. It is a multi-tenant app made for breweries.

Docker with the compose plugin is all you need. No Rust, Node or PostgreSQL on the machine.

Lifecycle

Seven states, and rules between them

A transition that makes no sense is refused with a clear message. Filled needs a beer, a lot and a tank; Delivered needs a customer and an address. An administrator can force a transition by giving a reason, and the timeline keeps it marked in red.

  • Received
  • Dirty
  • Clean
  • Filled
  • Delivered
  • Out of service
  • Lost

Features

Made for the cellar floor, not the boardroom

  • A scanner in every pocket

    A Bluetooth scanner (it looks like a keyboard), the rear camera with torch, or manual entry. Each read answers with a full-screen colour, a sound and a vibration.

  • Batch operations

    Washing, filling, delivery, customer return: set the operation once, then scan dozens of kegs. A keg that does not fit is refused on the spot, and a batch validates all or nothing.

  • Delivery notes

    Created when a delivery is validated, ready to print, with two signature fields.

  • Fleet and keg page

    Full-text search, filters you can combine, CSV and Excel export. Each keg shows its next action, its whole timeline and a label reprint.

  • Lots and product recall

    Pick a lot and see every customer concerned, with contacts, quantities and the exact QR codes. Export it or print it.

  • Customers

    Each one with the kegs they hold, in red past the threshold. A customer with kegs cannot be archived, and no customer is ever deleted.

  • Roles

    Read, Production and Administrator inside a brewery, plus a separate back-office for the publisher. Sessions lock after 15 minutes and ask for a six-digit PIN.

  • Mobile first

    Below 720 px lists turn into cards, touch targets are 44 px or more, and the bottom navigation follows the role. Light and dark themes.

  • French and English

    The whole interface, and a missing translation fails the build.

The admin side covers beers, locations, tanks, users, QR label sheets (3 x 8, 70 x 37 mm), a spreadsheet import checked before anything is written, settings and a filterable audit log.

Questions it answers
  • Where is this keg, and for how long?
  • What is in it: beer, lot, tank?
  • How many clean kegs are available?
  • Which ones have not come back after N days?
  • Who received lot 2026-017? (the product recall, in one click)

The history of operations is the source of truth, not the keg record.

Under the hood

Rust (Axum and SQLx) on PostgreSQL 17, rendered on the server with Maud and htmx for the management screens. There is no SPA, no front-end build chain and no CDN, and the CSP allows no inline JavaScript.

Install

Docker Compose, then your first brewery

One prerequisite: Docker with the compose plugin.

  1. Secrets, then start

    There is no default secret: the application refuses to start and lists every missing variable at once.

    # 1. Secrets: no default value exists
    gen() { head -c 32 /dev/urandom | base64 | tr -d '/+=' | head -c 32; }
    cp .env.exemple .env
    # fill .env using gen() for each password, SECRET_APP=$(gen)$(gen)
    chmod 600 .env
    
    # 2. Start: Caddy, application, PostgreSQL and backups
    docker compose up -d --build
    docker compose ps                     # services must be "healthy"
  2. Create the first brewery

    The admin password is shown only once.

    docker compose run --rm app provisionner-brasserie \
      --nom "Brasserie du Phare" --slug phare --prefixe-qr PHARE \
      --admin-email contact@brasserie.fr --admin-nom Martin --admin-prenom Claire
  3. Optional: demo data

    docker compose run --rm app semer-demonstration --locataire phare --futs 3000 injects a year of demonstration history.

  4. Check

    Health check: curl -fsS http://localhost:6666/sante. Migrations run on their own when you update.

The camera needs HTTPS. Browsers silently block it on plain http:// addresses. Use a tunnel or a domain name with a certificate.

Paper labels do not survive an 80 °C caustic wash. Plan for engraved plates or industrial polyester labels.

Start with one brewery.

Operations commands, backups and secret rotation are documented in the repository.