Deployment guide

KitchenOwl

Overview

KitchenOwl is an open-source app for managing a household's food. Several people share one shopping list, and items check off in real time as anyone shops, so the list stays current whoever is at the store. You keep a collection of recipes, add their ingredients to the list in one tap, and plan meals across the week on a calendar. KitchenOwl also tracks grocery expenses so a household can split the cost. A Flutter web app fronts everything, and the same account works from the mobile apps.

This SecureAnyCloud image deploys KitchenOwl on Ubuntu 24.04 on Azure. It runs the free, open-source, AGPL-3.0 licensed KitchenOwl the officially supported way: as the upstream all-in-one container, pinned by image digest and captured into the VM, so your instance starts in seconds.

Nothing ships with a known secret. KitchenOwl's upstream token signing secret is a well-known fixed value, so each VM generates a unique JWT signing secret on first boot, before the app is reachable. A per-instance admin account with a random password is seeded once the app is healthy. Open self sign-up is disabled, and onboarding closes as soon as the administrator exists.

KitchenOwl is a trademark of its respective owner. This image is produced by SecureAnyCloud and is not affiliated with, endorsed by or sponsored by the KitchenOwl project. It ships the free, open-source, AGPL-3.0 licensed self-hosted software, unmodified.

What you get:

  • KitchenOwl v0.7.9, the AGPL-3.0 licensed all-in-one container (Flask/uWSGI API plus the built Flutter web app), pinned by image digest
  • A SQLite application database, created empty on first boot and stored on a persistent Docker volume at /data
  • Docker Engine with the app published to the loopback interface only, fronted by nginx on port 80 with WebSocket upgrade for real-time shopping list sync
  • kitchenowl.service, kitchenowl-firstboot.service, kitchenowl-postboot.service and nginx.service as systemd units, enabled and active on boot
  • A unique JWT signing secret and a per-instance admin account with a random password, generated per VM on first boot and never baked into the image
  • No default login, no shipped secret, open self sign-up disabled and an empty database on first boot
  • Ubuntu 24.04 LTS base with the latest security patches applied at build time
  • Azure Linux Agent for seamless cloud integration and SSH key injection
  • 24/7 SecureAnyCloud support with guaranteed 24 hour response SLA

Prerequisites

  • An active Azure subscription, an SSH public key, and a VNet with a subnet in the target region
  • A subscription to the KitchenOwl listing on Azure Marketplace

Recommended virtual machine size: Standard_B2s (2 vCPU, 4 GB RAM) is a sensible starting size for a household. For larger households or many concurrent users, use Standard_D2s_v5 or larger.

Configure the network security group (NSG) to allow inbound 22/tcp from your management network and 80/tcp for the web app from the networks that use it.

1

Deploy from the Azure Portal

Sign in to the Azure Portal, choose Create a resource, search the Marketplace for KitchenOwl by SecureAnyCloud, and select Create.

On the Basics tab, pick your subscription, resource group, region and size. Under Administrator account, choose SSH public key and paste your key. Under Inbound port rules, allow SSH (22) and HTTP (80).

Select Review + create, then Create.

2

Deploy from the Azure CLI

Create the VM from the Marketplace image with the Azure CLI:

RG="kitchenowl-prod"; LOCATION="eastus"; VM_NAME="kitchenowl-01" IMAGE_URN="sac1647283583153:kitchenowl-ubuntu-24-04:default:latest" SSH_KEY="$(cat ~/.ssh/id_rsa.pub)" az group create --name "$RG" --location "$LOCATION" az network vnet create -g "$RG" --name ko-vnet --address-prefix 10.100.0.0/16 --subnet-name ko-subnet --subnet-prefix 10.100.1.0/24 az network nsg create -g "$RG" --name ko-nsg az network nsg rule create -g "$RG" --nsg-name ko-nsg --name allow-ssh --priority 100 \
  --source-address-prefixes "<your-mgmt-cidr>" --destination-port-ranges 22 --access Allow --protocol Tcp az network nsg rule create -g "$RG" --nsg-name ko-nsg --name allow-http --priority 110 \
  --destination-port-ranges 80 --access Allow --protocol Tcp
# Accept the Marketplace terms once per subscription (required before the first CLI deploy)
az vm image terms accept --urn sac1647283583153:kitchenowl-ubuntu-24-04:default:latest
az vm create -g "$RG" --name "$VM_NAME" --image "$IMAGE_URN" \
  --size Standard_B2s --storage-sku StandardSSD_LRS \
  --admin-username azureuser --ssh-key-values "$SSH_KEY" \
  --vnet-name ko-vnet --subnet ko-subnet --nsg ko-nsg --public-ip-sku Standard
3

Connect to your VM

Connect over SSH as azureuser:

ssh azureuser@<vm-ip>
4

Confirm the services are running

The app runs as one all-in-one container under kitchenowl.service, fronted by nginx. The container is published to 127.0.0.1:8080 only, and nginx on :80 is the single public entry point. Confirm that the services are active and check the container:

sudo systemctl is-active docker kitchenowl nginx
sudo docker compose -f /etc/kitchenowl/compose.yaml ps

The services report active and the container reports running. KitchenOwl is bound only to the loopback interface. nginx on :80 proxies / to it and passes through WebSocket upgrades, so the shopping list syncs in real time.

5

Read the per-instance credentials

On first boot, this VM generated a unique JWT signing secret before the app was reachable. Once the app was healthy, it seeded a per-instance admin account with a random password and wrote the details to a root-only file. Read the file:

sudo cat /root/kitchenowl-credentials.txt

The file (mode 0600 root:root) holds ADMIN_USERNAME and ADMIN_PASSWORD, the account you sign in to the web app with, and WEB_URL. None of these values ship in the image. Every value is unique to this VM, so no two instances share a credential and there is no default login to change.

6

Verify the security model

The web app and the health endpoint are reachable without authentication. Every protected API endpoint requires a bearer token, and open self sign-up is disabled.

Confirm that the web app loads, that an unauthenticated request to a protected endpoint is rejected with 401, and that the per-instance admin credential authenticates a real call:

ADMIN_USERNAME=$(sudo grep '^ADMIN_USERNAME=' /root/kitchenowl-credentials.txt | cut -d= -f2-)
ADMIN_PASSWORD=$(sudo grep '^ADMIN_PASSWORD=' /root/kitchenowl-credentials.txt | cut -d= -f2-) HEALTH=/api/health/8M4F88S8ooi4sMbLBfkkV7ctWwgibW6V curl -s -o /dev/null -w 'web UI (no auth): %{http_code}\n' http://localhost/ curl -s -o /dev/null -w 'health (no auth): %{http_code}\n' "http://localhost$HEALTH" curl -s -o /dev/null -w 'GET /api/user (no token): %{http_code}\n' -H 'Accept: application/json' http://localhost/api/user
TOKEN=$(curl -s -X POST http://localhost/api/auth \
  -H 'Content-Type: application/json' -H 'Accept: application/json' \
  -d "{\"username\":\"$ADMIN_USERNAME\",\"password\":\"$ADMIN_PASSWORD\",\"device\":\"guide\"}" | python3 -c 'import sys,json;print(json.load(sys.stdin)["access_token"])')
curl -s -o /dev/null -w 'GET /api/user (token): %{http_code}\n' \
  -H "Authorization: Bearer $TOKEN" -H 'Accept: application/json' http://localhost/api/user

The web app returns 200 and the health endpoint returns 200. The protected user endpoint is rejected with 401 when you send no token, and the same call with the per-instance admin token returns 200.

This is the security model: a bearer token that you obtain by signing in gates the API, and there is no open registration.

The image also bundles the same round trip as a single script. It proves that the credential works end to end by signing in, creating a household, adding a shopping list item and reading it back:

sudo /usr/local/sbin/kitchenowl-roundtrip.sh
7

Open the web app and sign in

Browse to http://<vm-ip>/ to open the KitchenOwl web app. Sign in with the ADMIN_USERNAME and ADMIN_PASSWORD from Step 5.

8

Use the shared shopping list

After you sign in, open a household to reach its shopping list. Type in the search box and tap to add items, then check them off as you shop.

Every member of the household sees the same list, and it syncs in real time. Whoever is at the store always has the current list. You can group items by category and sort them alphabetically or by how often you buy them.

9

Keep a recipe collection

Open Recipes to store your household's recipes with their ingredients, steps, cook time and yield. From a recipe, you can add all of its ingredients to the shopping list in one tap. You can also import recipes from a URL, and find any recipe with the search box.

10

Plan meals for the week

Open Meal planner to plan which recipes you will cook on which days. Add a recipe to a day, and KitchenOwl can add its missing ingredients to the shopping list for you, so the weekly shop follows the plan. The planner starts empty and prompts you to pick from your recipes.

11

Add household members

Open self sign-up is disabled on this image, so the administrator adds new members. Not everyone who reaches the URL can join. Sign in as the admin, open Settings, and create accounts for your household members, or generate an invite.

To enable open registration instead, so that anyone can create their own account, edit /etc/kitchenowl/compose.yaml and set OPEN_REGISTRATION to "true". Then restart the app:

sudo systemctl restart kitchenowl
12

Call the API from your own machine

The REST API backs every feature of the web app, and you can call it directly. Obtain a token by signing in, then call a protected endpoint with the VM address:

TOKEN=$(curl -s -X POST http://<vm-ip>/api/auth \
  -H 'Content-Type: application/json' -H 'Accept: application/json' \
  -d '{"username":"<ADMIN_USERNAME>","password":"<ADMIN_PASSWORD>","device":"cli"}' | python3 -c 'import sys,json;print(json.load(sys.stdin)["access_token"])')
curl -s -H "Authorization: Bearer $TOKEN" -H 'Accept: application/json' \
  "http://<vm-ip>/api/household"

The login returns a short-lived access token (15 minutes) and a refresh token. For automation, you can mint a long-lived token with POST /api/auth/llt. The API is documented at http://<vm-ip>/api/openapi.

13

Server components

The image contains these components:

  • Application: KitchenOwl v0.7.9 all-in-one (Flask/uWSGI API plus Flutter web UI), pinned by image digest
  • Front door: nginx on :80, proxying / to the app with WebSocket upgrade
  • Application database: SQLite at /data/database.db, created empty on first boot, on a persistent volume
  • Orchestration: Docker Compose under kitchenowl.service
  • JWT signing secret: per instance, generated on first boot into /etc/kitchenowl/kitchenowl.env
  • Admin account: per-instance random password, seeded on first boot into /root/kitchenowl-credentials.txt
  • Registration: open self sign-up disabled (OPEN_REGISTRATION=false)
  • Operating system: Ubuntu 24.04 LTS, patched at build time
  • License: AGPL-3.0 (KitchenOwl)
14

Manage the service

Check the service status and read the recent application logs:

sudo systemctl status kitchenowl --no-pager | head -12 sudo docker compose -f /etc/kitchenowl/compose.yaml logs --tail 40 kitchenowl

Restart the app with sudo systemctl restart kitchenowl. Follow the logs live with sudo docker compose -f /etc/kitchenowl/compose.yaml logs -f kitchenowl.

The configuration lives in /etc/kitchenowl/kitchenowl.env and /etc/kitchenowl/compose.yaml. After you change the environment file, restart the service to apply the change.

15

Use your own domain and HTTPS (production)

The image serves the web app over plain HTTP on port 80, which is convenient behind a load balancer or a private network. For production, terminate TLS in front of the VM so that credentials travel encrypted:

  • Front the VM with Azure Application Gateway, a load balancer with a managed certificate, or a CDN, and forward to nginx on port 80.
  • Or install your own certificate. Add a TLS server block to /etc/nginx/sites-available/kitchenowl that references your certificate and key, open 443/tcp on the NSG, then run sudo systemctl reload nginx.
  • If you serve the app from a custom domain, set FRONT_URL in /etc/kitchenowl/kitchenowl.env to that URL and restart the app, so that real-time sync uses the right origin. The app reads its URL at first boot from the resolved public address, so use a stable public IP or set the address before first boot.
16

Security recommendations

  • Restrict the NSG. Allow TCP 80, and TCP 22 for administration, only from the networks that use the app.
  • Terminate TLS in front of the app (Step 15) so that the admin password and tokens are encrypted in transit.
  • Rotate the seeded admin password. Sign in and change it in Settings.
  • Keep registration closed. Open self sign-up is disabled by default. Leave it that way and add members as the administrator, unless you specifically want open registration.
  • Keep the app private. KitchenOwl is bound to loopback and fronted by nginx. Never expose the container port directly.
  • Keep the OS patched. Unattended security upgrades remain enabled on the running VM.
17

Support and licensing

KitchenOwl is distributed under the GNU Affero General Public License v3.0 (AGPL-3.0). This SecureAnyCloud image bundles the unmodified official open-source release.

SecureAnyCloud provides the packaging, the nginx front door, the per-instance secret and admin generation, the paired deployment guide, and 24/7 support with a guaranteed 24 hour response SLA. KitchenOwl is an independent open-source project, and this image is not affiliated with or endorsed by it.

Deploy on Azure

Launch KitchenOwl on Ubuntu 24.04 LTS from the Azure Marketplace and follow this guide to a working household grocery, recipe and meal planning app in minutes.

Deploy on Azure

Launch KitchenOwl on Ubuntu 24.04 LTS on Azure with 24/7 support from SecureAnyCloud.

View this image

Need Help?

Our support team is available 24/7.

sales@secureanycloud.com

Deploy on Azure