Self-hosted service
Cybrarist/Discount-Bandit avatar
Cybrarist/Discount-Bandit

Discount Bandit is a self-hosted price tracker with per-user rules

A Multi-User Selfhosted price tracker for Amazon, Aliexpress, ebay and many more along with custom stores. Get notified when price matches one or more criterias set by the user

755 stars42 forksPHPLicense varies

At a glance

What is it?
Discount Bandit is a Laravel application that watches product prices across Amazon, AliExpress, eBay, Newegg, Noon, Target, Walmart and custom stores, then notifies a user when a price matches the criteria that user set. The trade is that it scrapes pages, so it needs a headless Chromium, and the licence of the repository is not stated.
Who is it for?
Discount Bandit suits a household or a small group that wants price alerts on a handful of products across several storefronts, and that is willing to run a Docker container with a headless browser inside it. It does not suit a commercial price-monitoring service, because the scrapers are the fragile part and there is no stated licence to build on.
Can I use it commercially?
Not without permission. GitHub finds no licence file in the repository, and without a licence all rights are reserved by default: you may read the code but not reuse it. Check the README, or ask the authors, before using it.
Is it still maintained?
Yes. The repository received new commits within the last day.
What is it written in?
Mainly PHP, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Price alerts, but the rules belong to each user

The problem is the one every shopper has solved badly with a spreadsheet and a browser bookmark. Discount Bandit is a price tracker for your favorite products across multiple stores, with a notification system built in, so you can be notified when the price drops or matches criteria you specified. The multi-user part is what makes it more than a personal script, and the description is explicit about it: a multi-user self-hosted price tracker for Amazon, AliExpress, eBay and many more along with custom stores, with notification when the price matches one or more criteria set by the user. The intended audience is therefore a household, a family or a small team sharing one instance, and the criteria are per user rather than global, so two people tracking the same product can set different thresholds.

Laravel on the server, Vite and Tailwind on the client

The stack is modern Laravel, and the repository layout shows the conventional shape of one. The top level holds an app/ directory for the application code, config/, database/ with migrations and seeds, routes/, resources/ for views, lang/ for translations, storage/, tests/, and the standard bootstrap/, public/ and artisan entry point. The frontend is a separate build: the package.json in the repository root is marked private, uses the module type, and its only scripts are build, which runs vite build, and dev, which runs vite. The devDependencies are Tailwind CSS with its Vite plugin at version 4, laravel-vite-plugin, Vite 6, axios, and concurrently for running tasks in parallel. There is a pint.json for Laravel Pint, the PHP formatter, and a phpunit.xml for the test suite, so the project uses the standard tooling on both sides.

Scraping drives the whole design, and Chromium comes with it

This is a scraper product, and the Dockerfile shows what that costs. The base image is dunglas/frankenphp at 1.9.1 with PHP 8.4 on trixie, and the build installs a long list of system packages that are only needed because a browser has to run: libx11-xcb1, libxcomposite1, libxdamage1, libxrandr2, libatk1.0-0, libnspr4, libnss3, libgtk-3-0, libgbm-dev, libpango-1.0-0, libatspi2.0-0, libxshmfence1, libxtst6, chromium, chromium-driver and xvfb, plus xdg-utils. The environment then points Chrome at the binary with a headless configuration and a remote debugging port, and sets DISPLAY to :99 so the headless browser still has a display to draw on. The PHP extensions compiled in are the other half of the requirement list, with pcntl, opcache, pdo_mysql, sockets, zip, intl, bcmath and calendar among them.

The compose file is a five minute poll of the scraper

The Docker setup is one file, and the interesting part of it is the cron schedule. The service runs the image cybrarist/discount-bandit:v4 with a commented out build section, so by default you pull a published image rather than build locally, and the ports map 8080 on the host to 80 in the container. Two volumes are mounted, the SQLite database file from./database/database.sqlite to /app/database/sqlite and a logs directory to /logs, and the environment sets the database connection to sqlite, the timezone to UTC, a theme colour of Red and the app and asset URLs to http://localhost:8080. The line that explains the whole polling model is CRON: "*/5 * * * *", every five minutes. That is the cadence at which the trackers run, and it is a setting you can change. The service block in full:

yaml
networks:
  discount-bandit:
    driver: bridge

volumes:
  discount-bandit:
  discount-bandit-logs:

services:
  discount-bandit:
    image: cybrarist/discount-bandit:v4
#    build:
#      context: .
    ports:
      - 8080:80
    networks:
      - discount-bandit
    volumes:
      - ./database/database.sqlite:/app/database/sqlite
      - ./logs:/logs
    environment:
      DB_CONNECTION: sqlite
      APP_TIMEZONE: UTC
      THEME_COLOR: Red
      APP_URL: "http://localhost:8080"
      ASSET_URL: "http://localhost:8080"
      EXCHANGE_RATE_API_KEY:
      CRON: "*/5 * * * *"

SQLite by default, MySQL in the wings, and a queue on the database

The environment file is where the real configuration surface is, and it is a Laravel .env with the usual shape. The defaults lean towards the simplest possible deployment: DB_CONNECTION is sqlite with the MySQL host, port, database and credentials lines present but commented out, SESSION_DRIVER is file with a lifetime of 120, CACHE_STORE is file, QUEUE_CONNECTION is database, BROADCAST_CONNECTION is log and MAIL_MAILER is log, which means notifications are written to the log rather than sent until you configure a mailer. Redis is configured with a phpredis client on the default local port, and the AWS keys are present but empty, which suggests object storage is optional. Two entries relate directly to the price tracking: EXCHANGE_RATE_API_KEY, empty by default, and GITHUB_REPOSITORY pointing at Cybrarist/Discount-Bandit. The header of the file is small enough to read in full:

env
APP_NAME="Discount Bandit"
APP_ENV=production
APP_DEBUG=false

APP_LOCALE=en
APP_FALLBACK_LOCALE=en
APP_FAKER_LOCALE=en_US

APP_MAINTENANCE_DRIVER=file
# APP_MAINTENANCE_STORE=database

PHP_CLI_SERVER_WORKERS=4

BCRYPT_ROUNDS=12

LOG_CHANNEL=daily
LOG_STACK=single
LOG_DEPRECATIONS_CHANNEL=null
LOG_LEVEL=debug

DB_CONNECTION=sqlite

Installing means composer, a .env and a web server

The manual deployment path is documented in the README, and the dependency list is the whole story: php, composer, apache or nginx, mysql, and sqlite as the default option, with a note that something like MAMP can be installed instead of the individual pieces. Then it is one command for the packages, and the environment file is copied and filled in:

bash
composer install 

The environment variables needed are documented separately on the project's own documentation site, at the installation and environments page, and the README links the Docker Hub repository for people who would rather pull a prebuilt image. There is also a deploy.sh script at the root of the repository, and a docker/ directory that holds the files the Dockerfile copies in, such as the PHP ini override and the supervisord configuration. The full documentation site is at discount-bandit.cybrarist.com, and the README points to a Discord for people who want to ask questions outside GitHub.

Notifications are the payoff, and the mailer is off by default

The notification system is the reason the product exists, and it is worth understanding how it is wired because the default configuration does not send anything. MAIL_MAILER is set to log in the environment file, and MAIL_HOST points at localhost on port 2525 with null credentials, and MAIL_FROM_ADDRESS is the placeholder [email protected] with the sender name interpolated from the app name. So out of the box a price rule that fires produces a log entry and nothing else. To get real mail you need to point the mailer at an actual server, and that is a configuration step the environment documentation covers. The same pattern applies to the exchange rate API key, which is empty by default and would be needed if the interface shows prices in a currency other than the store's own.

A licence that the metadata does not state

One thing stands out for anyone planning to build on this, and it is not a feature. The licence field in the repository metadata is left as unknown, and there is no LICENSE file among the top level entries, which run from.dockerignore and.editorconfig through app/, config/, database/, docker/, extra/, lang/, resources/, routes/, storage/ and tests/ to composer.json, docker-compose.yaml, the Dockerfile and vite.config.js. The repository is not archived, and the last push was on 2026-10-22, with the most recent tagged releases being V4.0.4 on 2026-05-23, V4.0.2 on 2026-01-31 and V4 on 2025-11-07. Running the software yourself on your own server is a different question from redistributing or modifying it, and the README does not discuss the licence at all, so anyone who wants to do more than run it should ask the maintainer directly.

Editorial conclusion

Discount Bandit suits a household or a small group that wants price alerts on a handful of products across several storefronts, and that is willing to run a Docker container with a headless browser inside it. It does not suit a commercial price-monitoring service, because the scrapers are the fragile part and there is no stated licence to build on. Before adopting, read the environment documentation for the variables the .env.example file leaves blank, decide between the SQLite default and MySQL, and check the repository licence yourself, since the project metadata lists it as unknown.

Frequently asked questions

How do I install Discount Bandit?

The README lists php, composer, apache or nginx, mysql and sqlite as dependencies, or a bundle such as MAMP. Then run `composer install`, copy the .env.example file to .env and fill in the environment variables, which are documented on the project's installation and environments page. A Docker image is also published on Docker Hub.

Which stores does Discount Bandit track?

The repository description names Amazon, AliExpress, eBay, Newegg, Noon, Target and Walmart, along with custom stores. The topic tags on the repository list the same set, with amazon-price-tracker, ebay, newegg, noon, target and walmart among them.

How often does Discount Bandit check prices?

The Docker compose file sets the CRON environment variable to "*/5 * * * *", which is a five minute schedule, and the same value appears in the .env.example file. Both are configuration entries you can change.

Why does Discount Bandit's Docker image install Chromium?

Because the trackers scrape product pages, so the container needs a browser. The Dockerfile installs chromium, chromium-driver and xvfb, sets CHROME_BIN to the chromium binary, and sets DISPLAY to :99, and it runs on the frankenphp image with PHP 8.4.

Does Discount Bandit send email notifications by default?

No. The .env.example file sets MAIL_MAILER to log and points MAIL_HOST at localhost on port 2525, so notification output goes to the log until you configure a real mailer. The EXCHANGE_RATE_API_KEY is also empty by default.

Official sources

  1. Cybrarist/Discount-Bandit on GitHub
  2. Issues
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/cybrarist-discount-bandit.svg)](https://hysenlabs.com/projects/cybrarist-discount-bandit)

Was this useful?