gif gif

The interesting corner

gif gif

Setting up Specifically Clementines on an OpenSUSE VM

Introduction

My gf asked for a way to have a shared grocery list that we can both access for when we go running errands or go to the supermarket, since I have a homelab and all. So like any good boyfriend I got to work to fulfill her request. After some searching I came across Specifically Clementines, a self-hosted open source grocery list (formerly named groceries).

specifically clementines

Installation - Docker

I planned to host this on an OpenSUSE VM I am already running to Automatically generate IG reels. The whole thing will be running in Docker anyway. I followed the Docker Compose setup from the documentation, which was a bit daunting at first (mainly because my docker-compose experience so far has been pretty plug-and play with things like Immich and TriliumNext), but all the info is there if you just read carefully.

I mainly built off the simple example docker-simple.tar.gz, changing the docker-compose values I needed to. The things I ended up changing in the docker-compose.yaml file:

        
...          
DEFAULT_API_URL: https://my_groceries_url/api
...
COUCHDB_URL: https://my_couchdb_url
...
COUCHDB_ADMIN_USER: new username
COUCHDB_ADMIN_PASSWORD: new password
...
GROCERY_URL: https://my_groceries_url
GROCERY_API_URL: https://my_groceries_url/api
...
        
      

In the end I didn't need to change that much, but I did for a moment get stuck on the couchdb parameters, as I first had set my couchdb url to the same url as the api url, but these need to be two different urls. I ended up registering 2 DNS A-records for my domain, one for the api and one for the couchdb database.

Installation - Nginx

Because I want to be able to access this from anywhere in the world, I want to put it behind my Nginx reverse proxy to make it more secure. I had to create 2 new sites, one for the API and one for the couchdb database. The site for the API looks like this:

        
server {
  server_name my_groceries_url;

  # route API requests to the backend server of specifically-clementines
  location /api/ {
          rewrite ^/api(/.*)$ $1 break;
          proxy_pass http://ip-of-vm:3333;
          proxy_http_version 1.1;
          proxy_set_header Host $host;
          proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
          proxy_set_header Upgrade $http_upgrade;
          proxy_set_header X-Forwarded-Host   $host;
          proxy_set_header X-Forwarded-Port   443;
          proxy_set_header X-Forwarded-Proto  https;
          proxy_set_header X-Real-IP $remote_addr;

          client_max_body_size 20M;
  }

  # route all other requests to the frontend
  location / {
          proxy_pass http://ip-of-vm:8100;
          proxy_http_version 1.1;
          proxy_set_header Host $host;
          proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
          proxy_set_header Upgrade $http_upgrade;
          proxy_set_header X-Forwarded-Host   $host;
          proxy_set_header X-Forwarded-Port   443;
          proxy_set_header X-Forwarded-Proto  https;
          proxy_set_header X-Real-IP $remote_addr;

          client_max_body_size 20M;
  }
}
        
      

And the site for the database looks like this:

        
server {
  server_name my_couchdb_url;

  location / {
          proxy_pass http://ip-of-vm:5984;
          proxy_http_version 1.1;
          proxy_set_header Host $host;
          proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
          proxy_set_header Upgrade $http_upgrade;
          proxy_set_header X-Forwarded-Host   $host;
          proxy_set_header X-Forwarded-Port   443;
          proxy_set_header X-Forwarded-Proto  https;
          proxy_set_header X-Real-IP $remote_addr;

          client_max_body_size 20M;
  }
}
        
      

For HTTPS, I of course used certbot.

Removing the system categories and items

After setting everything up, there was one thing I didn't like: the system categories and items. These were in English, and I want to add my items and categories in dutch. They were also items and categories I won't really use, like 1% and 2% milk. You can't remove these from the web ui or the Android app, so I had to delete them from the database by logging into the couchdb front-end, and selected them using this mongo query:

        
{
  "selector": {
    "type": "category"
  }
}
        
      
and this one:
        
{
  "selector": {
    "defaultCategoryID": { "$exists": true }
  },
}
        
      

After removing them from the database, I had to reinstall the Android app to have the changes replicated in the app.

UPDATE 25-05-2025: I moved this to an LXC container that runs all my docker containers some time ago, and today I moved all those to a separate VM