SelfHost Hub SelfHost Hub
中文
← Back to all services

GitHub repository information

Fetched · June 5, 2026
★ 185 Latest: 4.2.0 Updated: June 1, 2026
README
<a href="https://github.com/nanawel/our-shopping-list">
  <img src="client/src/assets/logo.png"
    alt="OSL logo"
    title="Our Shopping List" align="right"
    height="120" />
</a>

# Our Shopping List

OSL is a simple **shared list** application. Typical uses include **shopping
lists** of course, and any other small todo-list that needs to be used
**collaboratively**.

<p align="center"><img src="doc/osl-usage.gif" height="400" /></p>

The current implementation provides the following features:
- **Multiple boards** (can be disabled, see `VITE_APP_SINGLEBOARD_MODE`)
- Each board with **multiple lists**
- **Real-time sync** between users
- Items with the following fields: name, quantity, details
- **Checkable** items
- 2 **display modes** for items (unchecked only / checked only, sorted by check time)
- Intuitive **search**
- **Mobile-first UI** with swipeable items
- [PWA](https://en.wikipedia.org/wiki/Progressive_web_application) basic support
- Internationalisation (i18n) ([available languages listed here](./client/src/locales))
  - Want to help? Create or update translations on [Weblate](https://hosted.weblate.org/projects/our-shopping-list/client-src-locales/)
- Mass-create items by [pasting a CSV](https://github.com/nanawel/our-shopping-list/issues/51#issuecomment-3985512155)
  in the search field (can be disabled, see `VITE_APP_DISABLE_PASTE_CSV`)

But, at this date it lacks the following:
- Full PWA support with offline mode and deferred sync

## ⭐ New in v2: Boards feature

Before v2, **all of the lists** on an instance were available to **all users**.

Version 2 introduces a new feature called "boards". It's simply a way to group
lists together under a common name. That name can then be shared so that
people use the lists from a board collaboratively.

But, you might want to disable that feature and keep using a unique board for
your whole instance. In that case, just use the provided
`VITE_APP_SINGLEBOARD_MODE` environment variable.

**But have no fear, you can always:**

- Switch from _singleboard_ mode to multi-board
  - In that case you'll have to
    create a new board and choose it as target for existing lists with the
    provided CLI tool.
- Switch from multi-board mode to _singleboard_
  - In that case you can choose which lists to migrate to the special
    unique board, but you'll lose access to all other lists (they are not
    deleted, just not accessible anymore)

> See next § for instructions on how to enable one mode or the other.

## ☝ Instructions when migrating from v1 to v2

Version 2 has added the _multiboard_ feature which changes the default mode
the application runs into.

If you already had a working v1, and would like to upgrade to v2 please follow
the steps below:

> ⚠ **Back up your data before proceeding!**

### If you want to keep using one single board on your instance (just like on v1)

  - Make sure you set the `VITE_APP_SINGLEBOARD_MODE` to `1`
  - Once started, use the CLI to migrate existing lists to the special board
    used as common parent for lists in "singleboard" mode.
    ```shell
    docker compose exec app node cli.js board:create --singleboard
    docker compose exec app node cli.js list:move-to-board --all --singleboard
    ```
  - Use the application as usual (you might have to clear your browser's cache
    to make sure there's no invalid data left).

### If you want to enable the new _boards_ feature and migrate your existing lists to a dedicated board

  - Make sure `VITE_APP_SINGLEBOARD_MODE` is **not set** or set to `0`
  - Create a new board with the name of your choice
    ```shell
    # Get the created board's slug from the output and use it in the following command
    docker compose exec app node cli.js board:create my-board
    docker compose exec app node cli.js list:move-to-board --all --board my-board
    ```
  - Open the application, and from the home screen open the board you've just created
    to find your lists.

## 🖼 Screenshots

### Mobile

> ☝ Screenshots are from v1, but v2 looks mostly the same.

<a href="doc/mobile-01.png">
  <img src="doc/mobile-01.png" height="240" />
</a>

<details>
  <summary>Click here to see more!</summary>
  <a href="doc/mobile-02-menu.png">
    <img src="doc/mobile-02-menu.png" height="240" />
  </a>
  <a href="doc/mobile-03-search.png">
    <img src="doc/mobile-03-search.png" height="240" />
  </a>
  <a href="doc/mobile-04-edit-list.png">
    <img src="doc/mobile-04-edit-list.png" height="240" />
  </a>
</details>

### Desktop

> ☝ Screenshots are from v1, but v2 looks mostly the same.

<a href="doc/desktop-01.png">
  <img src="doc/desktop-01.png" height="240" />
</a>

<details>
  <summary>Click here to see more!</summary>
  <a href="doc/desktop-01-swipe.png">
    <img src="doc/desktop-01-swipe.png" height="240" />
  </a>
  <a href="doc/desktop-02-edit-item.png">
    <img src="doc/desktop-02-edit-item.png" height="240" />
  </a>
  <a href="doc/desktop-03-search.png">
    <img src="doc/desktop-03-search.png" height="240" />
  </a>
</details>

## 📦 Installation

### 🐋 With Docker

With a running [MongoDB](https://hub.docker.com/_/mongo) container as
`mymongo` on the host:

```shell
docker run --detach \
  --name our-shopping-list \
  --link mymongo:mongodb \
  --publish 80:8080 \
  ourshoppinglist/our-shopping-list
```

### 🐋 With `docker compose`

Use the provided [`docker-compose.yml`](doc/docker-compose.yml) and adapt it to
your needs.

Then to start the containers:

```shell
docker compose up -d
```

**Available environment variables for the `app` container**

⚠️ The original `VUE_APP_` prefix has been replaced due to the migration to Vite.  
You must now use `VITE_APP_` instead.

- **System keys**
  - `LISTEN_PORT` (default: `8080`)
  - `MONGODB_HOST` (default: `mongodb`)
  - `MONGODB_PORT` (default: `27017`)
  - `MONGODB_DB` (default: `osl`)
  - `BASE_URL` (default: _empty_) Set to base path if your web root is not `/` (ex: `/my-osl/`)
    - ⚠️ Not supported in development mode (Vite-related limitation)

  > MongoDB authentication is not supported yet.

- **Application keys**
  - `VITE_APP_APM_ENABLED` (default: `0`) [Reference](https://www.elastic.co/guide/en/apm/agent/rum-js/current/intro.html)
  - `VITE_APP_APM_LOGLEVEL` (default: `warn`) [Reference](https://www.elastic.co/guide/en/apm/agent/rum-js/current/configuration.html#log-level)
  - `VITE_APP_APM_SERVERURL` (default: `http://localhost:8200`) [Reference](https://www.elastic.co/guide/en/apm/agent/rum-js/current/configuration.html#server-url)
  - `VITE_APP_APM_SERVERURLPREFIX` (default: `/intake/v${apiVersion}/rum/events`) [Reference](https://www.elastic.co/guide/en/apm/agent/rum-js/current/configuration.html#server-url-prefix)
  - `VITE_APP_APM_SERVICENAME` [Reference](https://www.elastic.co/guide/en/apm/agent/rum-js/current/configuration.html#service-name)
  - `VITE_APP_BOARD_DELETION_ENABLED` (default: `0`) [Reference](https://github.com/nanawel/our-shopping-list/issues/17)
  - `VITE_APP_CHECKED_ITEMS_HISTORY_SORT_FIELD` (default: `lastCheckedAt`, see available fields [here](./client/src/models/Item.js))
  - `VITE_APP_CHECKED_ITEMS_HISTORY_SORT_ORDER` (default: `desc`)
  - `VITE_APP_CLIENT_LOG_CONSOLE_ENABLED` (default: `false`, [see doc here](https://github.com/dev-tavern/vue-logger-plugin/tree/v1.2.3#enabled-vs-consoleenabled))
  - `VITE_APP_CLIENT_LOG_ENABLED` (default: `false`, [see doc here](https://github.com/dev-tavern/vue-logger-plugin/tree/v1.2.3#enabled-vs-consoleenabled))
  - `VITE_APP_CLIENT_LOG_LEVEL` (default: `debug`)
  - `VITE_APP_DISABLE_PASTE_CSV` (default: `0`)
  - `VITE_APP_EDIT_ITEM_ON_CREATE` (default: `0`)
  - `VITE_APP_HIDE_FORCE_REFRESH_HINT` (default: `0`)
  - `VITE_APP_HOME_MESSAGE` (default: _empty_)
  - `VITE_APP_I18N_FALLBACK_LOCALE` (default: `en`)
  - `VITE_APP_I18N_FORCE_LOCALE` (default: `0`)
  - `VITE_APP_I18N_LOCALE` (default: `en`)
  - `VITE_APP_LIST_ALL_BOARDS_ENABLED` (default: `0`, has no effect in _singleboard_ mode)
  - `VITE_APP_LOCALSTORAGE_KEY_PREFIX` (default: `OurShoppingList_`)
  - `VITE_APP_PASTE_CSV_SEPARATOR` (default: `,`)
  - `VITE_APP_SHORT_TITLE` (default: `OSL`)
  - `VITE_APP_SINGLEBOARD_MODE` (default: `0`)
  - `VITE_APP_SOCKETIO_CSR_MAXDISCONNECTIONDURATION` (default: `1800000` = 30mn)
  - `VITE_APP_SOCKETIO_PING_INTERVAL` (default: `25000` = 25sec.)
  - `VITE_APP_SOCKETIO_PING_TIMEOUT` (default: `20000` = 20sec.)
  - `VITE_APP_THEME` (`system` | `light` | `dark`, default: `system`) [Reference](https://github.com/nanawel/our-shopping-list/issues/48)
  - `VITE_APP_TITLE` (default: `Our Shopping List`)
  - `VITE_APP_USE_ITEM_QUICK_SYNTAX` (default: `0`) [Reference](https://github.com/nanawel/our-shopping-list/issues/20)

### Robots.txt

By default, the embedded `robots.txt` prevents search engines from browsing the application:

```
User-agent: *
Disallow: /
```

You can change this behavior by mounting the `robots.txt` of your choice at `/app/robots.txt` in the container.

### 🗒 Notes for reverse-proxy (SSL offloading)

OSL uses a WebSocket to allow server-to-client communication. So using a
reverse-proxy to forward the connection implies the presence of the following
sections below in the corresponding virtual host.

Replace `127.0.0.1` and `8080` with the IP of the OSL host if your RP is not
the host itself and/or if you're using another port.

#### Apache

```
<Proxy *>
    Allow from all
</Proxy>
ProxyPass         /  http://127.0.0.1:8080/
ProxyPassReverse  /  http://127.0.0.1:8080/
ProxyPreserveHost On

RewriteEngine On
RewriteCond %{HTTP:Upgrade} =websocket [NC]
RewriteRule /(.*)           ws://127.0.0.1:8080/$1 [P,L]
RewriteCond %{HTTP:Upgrade} !=websocket [NC]
RewriteRule /(.*)           http://127.0.0.1:8080/$1 [P,L]
```

#### Nginx

```
location / {
    proxy_set_header    Host $host;
    proxy_set_header    X-Real-IP $remote_addr;
    proxy_set_header    X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header    X-Forwarded-Proto $scheme;
    proxy_pass          http://localhost:8080;

    proxy_http_version  1.1;
    proxy_set_header    Upgrade $http_upgrade;
    proxy_set_header    Connection "Upgrade";
    
    proxy_redirect      http://localhost:8080 https://ourshoppinglist.myhost;
}
```

### ⚠ Notes when serving multiple instances on different web roots

Remember to set the `BASE_URL` variable to the matching web root on each instance.

Make sure you set `VITE_APP_LOCALSTORAGE_KEY_PREFIX` to a unique value too, otherwise clients switching from
one instance to another might corrupt the internal cache in their browser.

Example:

- 1st instance on https://my.host/public-osl
  - `BASE_URL` = `public-osl/`
  - `VITE_APP_LOCALSTORAGE_KEY_PREFIX` = `OSL1_`
- 2nd instance on https://my.host/private-osl
  - `BASE_URL` = `private-osl/`
  - `VITE_APP_LOCALSTORAGE_KEY_PREFIX` = `OSL2_`
- etc.

### Debugging on server side

You can use the [standard `DEBUG`](https://dev.to/aderchox/what-is-the-debug-environment-variable-in-nodejs-and-how-to-use-it-3fal)
environment variable to enable the verbose mode of the server:

Example to log all operations related to **socket-io** (WebSocket) and the **URL rewrite** process
(when using a custom `BASE_URL`):

```
DEBUG=socket.io:server,express-urlrewrite
```

Or if you need to log everything:

```
DEBUG=*
```

## Upgrade MongoDB to 7.x

From the original release until june 2024, the stack was shipped with `mongo:4` but this version
of MongoDB is deprecated and you can safely upgrade to MongoDB 7 while keeping your existing data.

Use the provided automated script as follows:

```shell
# Make a backup with mongodump first!
docker compose exec -T mongodb mongodump -d osl --archive > osl-backup.archive

bash doc/update-mongo7.sh
```

## Star History

[![Star History Chart](https://api.star-history.com/svg?repos=nanawel/our-shopping-list&type=date&legend=top-left)](https://www.star-history.com/#nanawel/our-shopping-list&type=date&legend=top-left)

## 👷 Developer installation

> 🐋 This method also uses Docker, but with the local source files mounted
> into the `node` container.

First of all, clone this project in the directory of your choice. Then from it:

```shell
make dev-pull
make dev-init
make dev-upd
```

Now start the Webpack Development Server with

```shell
make dev-watch
```

> If you don't, you'll get 504 errors in the console on `/sockjs-node/*` requests
> and you won't get hot reloading on changes.

If you want to enter the container, just use

```shell
make dev-shell
```

###  Translation

Translations can be very easily added and improved using the files from the `client/src/locales/`
directory.

If you want to translate OSL in a new language, feel free to add your contribution using
[Weblate](https://weblate.org/).  
Register on Weblate and go to https://hosted.weblate.org/projects/our-shopping-list/client-src-locales/

### Special cases

In development mode, the service worker is not enabled.
To workaround this limitation, you may want to ponctually build the JS bundle
in "production" mode.

Here's how:

```shell
make dev-shell

cd client/
NODE_ENV=production yarn build
```

Then reload the page in your browser and the SW should be activated.
You have to make sure you are running the app **with TLS enabled**. Use the
`ENABLE_TLS` variable to use the embedded TLS proxy if needed.

### Upgrade MongoDB to 7.x

Use the provided automated script as follows:

```shell
export COMPOSE_FILE=docker-compose.dev.yml
export DOCKER_COMPOSE_FILE=docker-compose.dev.yml
bash doc/update-mongo7.sh
```

Discover more