Applications Azure

Tududi on Ubuntu 24.04 on Azure User Guide

| Product: Tududi on Ubuntu 24.04 LTS on Azure

Overview

Tududi is an open source, self hosted task and project management application built around a calm, uncluttered way of working. It organises what you have to do into tasks, projects, notes and higher level areas of responsibility, then lets you look at the same work the way that suits the moment: an inbox for quick capture, a Today list, a Kanban board, a calendar, or an Eisenhower matrix for deciding what actually matters. Recurring tasks, due dates, priorities, tags and subtasks keep long running commitments visible without noise.

The cloudimg image installs Tududi 1.2.4 built from the pinned upstream release with Node.js 22, runs it as a systemd service bound to the loopback interface behind an nginx reverse proxy on port 80, keeps its SQLite database and uploads under /var/lib/tududi, and generates a unique administrator account and session signing secret on the first boot of every VM. Backed by 24/7 cloudimg support.

What is included:

  • Tududi 1.2.4 (MIT), built from the pinned upstream release tag
  • The REST API and the prebuilt web application published on port 80 via nginx
  • A unique administrator account generated on first boot, written to a root only credentials file
  • A unique session signing secret generated per VM, so no two deployments can forge each other's sessions
  • The application server bound to 127.0.0.1:3002 behind the proxy rather than exposed directly
  • SQLite database and uploads under /var/lib/tududi, with no external database to operate
  • tududi.service + nginx.service as systemd units, enabled and active
  • 24/7 cloudimg support

Prerequisites

An active Azure subscription, an SSH key pair, and a VNet + subnet in the target region. Standard_B2s (2 vCPU / 4 GiB RAM) is a good starting point. NSG inbound: allow 22/tcp from your management network and 80/tcp for the web interface (front with TLS for public exposure, see Enabling HTTPS).

Step 1 - Deploy from the Azure Marketplace

Sign in to the Azure Portal, choose Create a resource, search the Marketplace for Tududi by cloudimg, and select Create. On Basics 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). Then Review + create -> Create.

Step 2 - Deploy from the Azure CLI

az vm create \
  --resource-group <your-rg> \
  --name tududi \
  --image <marketplace-image-urn> \
  --size Standard_B2s \
  --admin-username azureuser \
  --generate-ssh-keys \
  --public-ip-sku Standard

Then open the web interface port on the VM's network security group:

az vm open-port --resource-group <your-rg> --name tududi --port 80 --priority 1001

Step 3 - Retrieve the per VM administrator login

A unique administrator account is generated on the first boot of every VM and written to a root only credentials file. Nothing is shared between deployments and no default password exists in the image. SSH in and read it:

ssh azureuser@<vm-public-ip>
sudo cat /root/tududi-credentials.txt

The file contains the site URL, the administrator email address and the generated password:

The per VM Tududi credentials file showing the site URL, administrator email address and generated password, with root only 0600 permissions

Keep this password safe. It is the credential for the web interface and for the REST API. You can change it from Profile settings after your first sign in.

Step 4 - Confirm Tududi is healthy

The /api/health endpoint is deliberately unauthenticated so you can probe it from monitoring. Run these on the VM:

systemctl is-active tududi.service nginx.service
curl -s http://127.0.0.1/api/health

Expected output:

active
active
{"status":"ok","timestamp":"2026-07-26T00:31:15.827Z","uptime":61.853046212,"environment":"production","trustProxy":1}

Confirm that the application server is listening on the loopback interface only, with nginx holding port 80. This is what keeps the Node.js process off the public interface:

ss -tln | grep -E ':80 |:3002 '

Expected output:

LISTEN 0      511          0.0.0.0:80        0.0.0.0:*
LISTEN 0      511        127.0.0.1:3002      0.0.0.0:*
LISTEN 0      511             [::]:80           [::]:*

Terminal output showing tududi.service and nginx.service active, the health endpoint returning ok, and the application bound to loopback behind nginx

Step 5 - Sign in to the web interface

Open http://<vm-public-ip>/ in a browser. You are presented with the sign in page. Use the email address and password from Step 3.

The Tududi sign in page asking for an email address and password

After signing in you land on the Today view: what is due today, the active projects, and how your work is balanced across areas. Each task shows its priority as a coloured bar and its current status.

The Tududi Today view listing the tasks due today with their priorities, statuses and owning projects

Step 6 - Organise tasks, projects and areas

Use the left hand navigation to move between the ways of looking at the same work. Inbox is for quick capture, Today and Upcoming are time based, and All Tasks is the full list. Select Add New Task to capture something without leaving the page.

The Tududi All Tasks view listing every task with its status, due date and project

The Kanban board shows the same tasks arranged across their status columns, so you can drag work from To Do through In Progress to Done.

The Tududi Kanban board with task cards arranged across the To Do, In Progress, Waiting and Done columns

Projects group related tasks and track completion, and Areas group projects into ongoing areas of responsibility such as Work or Personal.

The Tududi Projects view listing projects with their completion progress

Step 7 - Use the REST API

Everything in the web interface is available over the REST API using the same administrator account. Sign in to obtain a session cookie, then call the API with it. Tududi protects state changing requests with a CSRF token, so fetch one before creating anything:

PASS=$(sudo grep '^tududi.admin.pass=' /root/tududi-credentials.txt | cut -d= -f2-)
MAIL=$(sudo grep '^tududi.admin.email=' /root/tududi-credentials.txt | cut -d= -f2-)
JAR=$(mktemp)
curl -s -o /dev/null -w 'sign in:            %{http_code}\n' -c "$JAR" \
  -H 'Content-Type: application/json' \
  -d "{\"email\":\"$MAIL\",\"password\":\"$PASS\"}" http://127.0.0.1/api/login
curl -s -o /dev/null -w 'list tasks:         %{http_code}\n' -b "$JAR" http://127.0.0.1/api/tasks
CSRF=$(curl -s -b "$JAR" -c "$JAR" http://127.0.0.1/api/csrf-token | sed -n 's/.*"csrfToken":"\([^"]*\)".*/\1/p')
curl -s -o /dev/null -w 'create a task:      %{http_code}\n' -b "$JAR" -c "$JAR" \
  -H 'Content-Type: application/json' -H "x-csrf-token: $CSRF" \
  -d '{"name":"Try Tududi from the API","priority":"medium","status":"not_started"}' \
  http://127.0.0.1/api/task
rm -f "$JAR"

Expected output:

sign in:            200
list tasks:         200
create a task:      201

Terminal output showing an unauthenticated request rejected with 401, a successful sign in, an authenticated task listing and the number of tasks tracked

Refresh the browser and the task you just created appears in All Tasks.

Sign in attempts are rate limited to five per fifteen minutes per client IP address, which protects the account against password guessing. If you are locked out after mistyping the password, wait for the window to pass rather than retrying immediately.

Step 8 - Where your data lives

Tududi stores everything in a single SQLite database, with file attachments alongside it. Both live under /var/lib/tududi, owned by the unprivileged tududi service account:

sudo ls -la /var/lib/tududi/db

Expected output (the -wal and -shm files are SQLite's write ahead log and shared memory index, and are a normal part of a healthy database):

total 4644
drwxr-x--- 2 tududi tududi    4096 Jul 26 00:22 .
drwxr-x--- 4 tududi tududi    4096 Jul 26 00:05 ..
-rw-r--r-- 1 tududi tududi  589824 Jul 26 00:22 production.sqlite3
-rw-r--r-- 1 tududi tududi   32768 Jul 26 00:31 production.sqlite3-shm
-rw-r--r-- 1 tududi tududi 4124152 Jul 26 00:31 production.sqlite3-wal

Terminal output showing the installed Node.js and Tududi versions, the SQLite database and uploads directories, and recent service log lines

Taking a backup

Because SQLite runs in write ahead log mode, copying the .sqlite3 file on its own can miss recent changes. Use SQLite's own backup command, which captures a consistent snapshot while Tududi keeps running:

sudo mkdir -p /var/backups/tududi
sudo sqlite3 /var/lib/tududi/db/production.sqlite3 ".backup '/var/backups/tududi/tududi-backup.sqlite3'"
sudo ls -la /var/backups/tududi

Copy the resulting file, together with /var/lib/tududi/uploads, to storage outside the VM. To restore, stop tududi.service, put the backup back at /var/lib/tududi/db/production.sqlite3 owned by tududi:tududi, remove any stale -wal and -shm files beside it, then start the service again.

Step 9 - Enabling HTTPS

The image serves plain HTTP on port 80 so it works the moment it boots. For anything reachable from the internet, put TLS in front of it. The session cookie follows the connection automatically: nginx passes the real scheme through X-Forwarded-Proto, so the cookie becomes secure as soon as the site is served over HTTPS, with no configuration change.

Point a DNS A record at the VM's public IP, open 443/tcp on the network security group, then install a certificate with Certbot:

sudo apt-get update && sudo apt-get install -y certbot python3-certbot-nginx
sudo certbot --nginx -d your-domain.example.com --redirect --agree-tos -m you@example.com

Certbot rewrites the nginx server block, installs the certificate and sets up automatic renewal. After it completes, add your HTTPS address to the allowed origins so browser requests from it are accepted:

sudo sed -i 's|^TUDUDI_ALLOWED_ORIGINS=.*|TUDUDI_ALLOWED_ORIGINS=https://your-domain.example.com|' /etc/tududi/tududi.env
sudo systemctl restart tududi.service

Step 10 - Maintenance

Check service health and read the application log:

systemctl is-active tududi.service
sudo journalctl -u tududi.service -n 20 --no-pager -o cat

The image ships fully patched with unattended security updates enabled, so the operating system keeps itself current. To apply updates immediately:

sudo apt-get update && sudo apt-get -y upgrade

Tududi's own configuration lives in /etc/tududi/tududi.env. Restart the service after changing it:

sudo systemctl restart tududi.service

Troubleshooting

If the web interface does not load, confirm both services are active and that the application answered on the loopback port (Step 4). A 502 from nginx means the Node.js process is not running, and journalctl -u tududi.service will say why.

If you cannot sign in, re read the password from /root/tududi-credentials.txt exactly as written, remembering that it is case sensitive, and check that you have not exhausted the five attempts per fifteen minutes rate limit.

On the very first boot the application deliberately does not start until tududi-firstboot.service has generated the per VM secret and administrator account. If the interface is briefly unavailable immediately after launch, check that first boot finished:

systemctl is-active tududi-firstboot.service
test -f /var/lib/cloudimg/tududi-firstboot.done && echo "first boot complete"

Expected output:

active
first boot complete

Support

Every cloudimg image comes with 24/7 support. Contact us at support@cloudimg.co.uk or visit www.cloudimg.co.uk.

Tududi is open source software published under the MIT licence by the Tududi project. cloudimg packages and supports the deployment image; it is not affiliated with or endorsed by the Tududi project.