Because 6pm comes every single day.
There are a hundred of these apps. I built my own because I wanted exactly what I wanted — scan a UPC as you unpack groceries, tag it as a protein, side, or dessert, and when dinner comes around the planner gives you options based on what's actually in your kitchen. Because we've all stood in front of the fridge at 6pm, staring, with absolutely no plan, even though you just went shopping.
No accounts, no huge dependencies, no bloat.
The scanning system is solid enough that I'm pulling it out for other projects. The rest is just a thing that works for my house. YMMV.
A lightweight, self-contained grocery inventory and meal planning app. Scan UPC barcodes with your phone camera, look up product info, track your stock, and get randomized meal suggestions from what you have on hand.
Everything in this app is intentionally simple — sometimes to the point where it might look like a corner was cut. To the contrary...
The rule: if making it more complex doesn't make it noticeably better for a family kitchen, it stays simple.
php-sqlite3 extensionmod_rewrite (or nginx with equivalent rewrite rules)# Upload files to your web root (scp, FTP, rsync, or unzip a release)
scp -r . user@host:/var/www/fridgestare/
# Copy the example config and edit it
cp config.example.php config.php
# Edit config.php with your settings (timezone, API keys, etc.)
# On first visit, the app auto-creates "Default user" / PIN 1234
Requirements: PHP 7.4+ with php-sqlite3, Apache with mod_rewrite.
docker-compose up -d
# App available at http://localhost:8420
The Docker image uses PHP 8.2 Apache with SQLite and zbarimg pre-installed. It strips any local API keys and writes clean defaults. The database is stored in a named Docker volume (survives restarts).
Copy config.example.php to config.php and edit:
| Setting | Description |
|---|---|
timezone | Display timezone for ledger timestamps (e.g. America/New_York) |
session_timeout_days | How long a PIN login lasts (default 30) |
pin_max_attempts | Failed attempts before lockout (default 5) |
pin_lockout_hours | Lockout duration after max attempts (default 1) |
default_qty | Starting quantity for manual adds (default 1) |
debug | Set to true to show scanner debug overlay |
upcitemdb_key | Optional API key for UPCItemDB product lookup |
turnstile_site_key | Cloudflare Turnstile site key (free) |
turnstile_secret_key | Cloudflare Turnstile secret key |
Note: config.php is excluded from version control. The repo only tracks config.example.php (with empty keys) so you never accidentally commit live API keys.
Important: Change the default PIN immediately.
Default user / PIN 1234Default userThat's it. The default user is just a starting key — not meant for daily use.
To wipe all data and restore the factory default user:
php reset-db.php
This is a CLI-only script — it refuses to run via the web. It clears inventory, ledger, products, rate limits, sessions, and users, then recreates Default user / PIN 1234.
PIN auth is intentionally kept simple — no email, no password managers. That also means a 4-digit PIN is not strong on its own. Adding Turnstile blocks automated brute-force attempts, which is the real threat.
It's free for any amount of traffic. Takes 2 minutes:
If you skip it, the CAPTCHA simply doesn't appear — the app works fine either way. But for any public-facing install, it's strongly advised.
MIT
AI was used to generate portions of the code and for security reviews and bug chase/fixing. The architecture and scaffolding was done by a real human (me!)
© 2026 Garrett Wiedmeier