# Koffan is a shared shopping list that fits in a Go binary

> Koffan is a self hosted shopping list for families and shared households, rewritten in Go with Fiber, HTMX, Alpine.js and SQLite so that it uses about 2.5 MB of RAM instead of the hundreds of megabytes the original Next.js version needed, with offline editing, real time sync and a 2.16.0 database migration you cannot undo. It suits a household that wants a shared list without handing over an account to a vendor.

**PanSalut/Koffan** — Free selfhosted groceries list for families and shared households

- Repository: https://github.com/PanSalut/Koffan
- Stars: 802 · Forks: 84
- Language: Go
- License: NOASSERTION
- Published: 2026-10-20 · Updated: 2026-10-20 · Language: en
- Canonical page: https://hysenlabs.com/projects/pansalut-koffan

## A shopping list sized for a home server

Koffan is a lightweight web application for managing shopping lists, designed for couples and families, and it allows real-time synchronization between multiple devices so everyone knows what to buy and what is already in the cart. The design constraint that shaped it is stated plainly in the README. The author needed an app for shopping with his wife, tested various solutions, and found none simple and fast enough. The first version was built in Next.js, but it turned out to be very resource-heavy, and with a lot of other things running on his server he decided to optimize, so he rewrote the app in Go. The result is quoted at roughly 2.5 MB of RAM instead of hundreds of megabytes, and roughly 16 MB on disk. The rest of the feature list follows from that audience, so multiple lists with custom icons, sections such as Dairy or Vegetables, marking products as purchased or as uncertain when you cannot find something in the store, and a responsive, mobile first interface with dark mode following the system preference.

## The stack, and what each layer is doing

The tech stack is three lines in the README. The backend is Go 1.25 or later with a Go 1.26.6 toolchain, using Fiber as the web framework. The frontend is HTMX with Alpine.js and Tailwind CSS, which is a deliberate choice against a JavaScript framework, since the page updates are server rendered and the browser side stays small. The database is SQLite in a single file. The `go.mod` confirms the shape of the backend, with `github.com/gofiber/fiber/v2` for the server, the Fiber template and websocket modules, `github.com/mattn/go-sqlite3` for the database, `github.com/google/uuid` for identifiers and `golang.org/x/crypto` for the password hashing. The `package.json` exists only for the CSS, with a `build:css` script that runs the Tailwind CLI over `tailwind/input.css` into `static/app.css`, a `watch:css` counterpart, and a `test` script that runs the Node test runner over `test/*.test.js`.

## Offline editing and how conflicting edits resolve

The offline story is the most detailed part of the documentation, and it is worth reading closely before you deploy it. You open Koffan online and sign in once so the app and shopping data can be saved on the device, and for offline page loads and the installed PWA you need to serve Koffan over HTTPS, with `localhost` also working for development, since a plain HTTP address on your home network does not give the same offline support. What you can manage offline covers lists, sections, products and bulk actions, and changes are saved on the device before the view updates, so they survive a reload or reopening the app. When the connection returns, Koffan sends the pending changes and refreshes the list, and retrying a request after a lost response does not duplicate the same operation.

## Last write wins, and what happens when a change cannot be applied

Conflict handling is described in a way that avoids a distributed systems paper. Other shoppers see your offline changes after they reach the server, changes to different products are combined, and for overlapping edits the last value accepted by the server for each updated field wins, with a product's name, note and quantity sent together. If a change cannot be applied, for example because another shopper deleted the product you edited, it stays pending and the app offers retry and discard controls, and discarding a failed change can also remove local changes that depend on it. The limitation that follows from this is worth naming. The offline copy belongs to one account and one workspace, so switching to another workspace or signing in with another account in the same browser removes it, including changes that have not been synchronized yet, so pending changes should be synchronized, retried or discarded before you switch.

## One shared password, or optional personal accounts

The login model is deliberately light. The app works in any browser on mobile and desktop with just one password to log in and no complicated registration, and if several households share one server you can optionally give everyone their own login and keep separate lists for each household, which is enabled with `MULTI_USER=true`. Protection against guessing is rate limiting, and the feature list mentions rate limiting protection against brute-force attacks. The same area of the configuration is where the API and admin surfaces live, since the compose file takes `APP_PASSWORD`, `API_TOKEN`, `MULTI_USER`, `ADMIN_USER` and `API_WORKSPACE_ID` as environment variables. The offline history is tied to the workspace concept, which is why the 2.16.0 migration note below matters even if you never enable accounts.

## The 2.16.0 migration has no downgrade path

The upgrading section contains the sharpest warning in the README. If you are upgrading to 2.16.0, back up your database file first, because on its first start 2.16.0 migrates the database, with item history becoming per workspace, whether or not you enable accounts. Older versions cannot save item history on a migrated database, and they ignore workspaces, so anyone with the shared password would see the lists of every workspace. There is no downgrade path. To go back you stop Koffan and restore the backup, and the changes made since then are lost. The database is the file set in `DB_PATH`, which is `/data/shopping.db` in Docker. The releases around it show how fast this moves, with v2.16.0, v2.16.1 and v2.17.0 all published in the second half of October 2026.

## Deployment buttons, and a compose file you can read

Koffan ships one click deploy buttons for Railway, Render, DigitalOcean and Heroku, and the repository carries the matching configuration for each, with `railway.json`, `render.yaml`, `fly.toml`, `heroku.yml`, `app.json` and a `.do/` directory, plus `docker-compose.yaml` and `docker-compose.local.yaml`. The compose file is the clearest statement of the configuration surface:

```yaml
    environment:
      - APP_ENV=production
      - APP_PASSWORD=${APP_PASSWORD:-shopping123}
      - API_TOKEN=${API_TOKEN:-}
      - MULTI_USER=${MULTI_USER:-}
      - ADMIN_USER=${ADMIN_USER:-}
      - PORT=8080
      - DB_PATH=/data/shopping.db
      - WEBHOOK_URL=${WEBHOOK_URL:-}
      - WEBHOOK_SECRET=${WEBHOOK_SECRET:-}
```

The port is exposed as `8080`, the database lives in the `shopping-data` volume, and the health check calls the login page with wget, which is why the runtime image has to carry a shell tool. The default `APP_PASSWORD` in the file is `shopping123`, and the README gives you a Docker path and a local Go path, with the latter working on macOS, Linux and Windows.

## REST API, webhooks, and a MIT labelled image

Two integration surfaces are worth knowing about. There is a REST API for programmatic access, with documentation in the project wiki, and outbound webhooks carrying signed item events, aimed at automation tools such as n8n, Node-RED and Zapier, configured through `WEBHOOK_URL`, `WEBHOOK_SECRET` and `WEBHOOK_EVENTS`. The repository has a dedicated `webhook/` directory and an `api/` directory alongside the `handlers/`, `db/`, `i18n/`, `templates/` and `static/` directories. The interface ships in 18 languages according to the feature list, including the offline and synchronisation messages, with the language files under `i18n/`. The container image labels itself with an MIT licence in the Dockerfile metadata, while the platform classifies the repository licence as a custom one that it cannot map to a standard identifier, so the `LICENSE` file is the place to check the terms. The repository is not archived and the last push was on 2026-10-16.

## Conclusion

Adopt Koffan when a household or a small group wants a shared shopping list that runs on your own server with a small memory footprint, since the Go rewrite is the reason it can sit alongside everything else on a home box. Do not adopt it if you need multi user accounts with per person roles out of the box, since the default is a single shared password and personal logins are optional. Verify first what the 2.16.0 migration does to your data before you start it, because the README says the upgrade has no downgrade path and that older versions ignore workspaces, and back up the database file before the first start of that version.

## FAQ

### What are the system requirements for Koffan?

The backend needs Go 1.25 or later with a Go 1.26.6 toolchain, and the project is built on Fiber with HTMX, Alpine.js, Tailwind CSS and SQLite. The README also states that Koffan can be run directly on your machine using Go on macOS, Linux or Windows, or through Docker.

### What happens to my data when I upgrade to Koffan 2.16.0?

On its first start 2.16.0 migrates the database, with item history becoming per workspace, whether or not you enable accounts. The README says to back up the database file first and that there is no downgrade path, since older versions ignore workspaces and would show the lists of every workspace to anyone with the shared password.

### Can I use Koffan offline?

Yes, for offline page loads and the installed PWA you serve Koffan over HTTPS, with `localhost` also working in development, and a plain HTTP address on your home network does not give the same offline support. You can create, edit and delete lists, sections and products offline, and changes sync automatically when the connection returns.

### How do separate households share one Koffan server?

By default the app uses one shared password. You can optionally enable personal accounts and separate lists per household with `MULTI_USER=true`, and the environment also takes `ADMIN_USER` and `API_TOKEN` for the administrative and API surfaces.

## Sources

- [Issues](https://github.com/PanSalut/Koffan/issues)
- [PanSalut/Koffan on GitHub](https://github.com/PanSalut/Koffan)
- [README](https://github.com/PanSalut/Koffan/blob/main/README.md)
- [Releases](https://github.com/PanSalut/Koffan/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/pansalut-koffan
