Deployment guide

Tududi

Overview

Tududi is an open source, self-hosted task and project management application built around a calm, uncluttered way of working. It organizes your work into tasks, projects, notes and higher-level areas of responsibility. You can view the same work in 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 matters most. Recurring tasks, due dates, priorities, tags and subtasks keep long-running commitments visible without noise.

The SecureAnyCloud image installs Tududi 1.2.4, built from the pinned upstream release with Node.js 22. It runs as a systemd service bound to the loopback interface behind an nginx reverse proxy on port 80. The image is backed by 24/7 SecureAnyCloud support.

What you get:

  • Tududi 1.2.4 (MIT), built from the pinned upstream release tag
  • The REST API and the prebuilt web application, published on port 80 through nginx
  • A unique administrator account, generated on the first boot of every VM and written to a root-only credentials file
  • A unique session signing secret for each 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
  • A SQLite database and uploads under /var/lib/tududi, with no external database to operate
  • tududi.service and nginx.service as systemd units, enabled and active
  • 24/7 SecureAnyCloud support

Prerequisites

Before you deploy this image, make sure you have the following:

  • An active Azure subscription.
  • An SSH key pair.
  • A VNet and subnet in the target region.
  • A VM size of Standard_B2s (2 vCPU / 4 GiB RAM), which is a good starting point.
  • Network security group (NSG) inbound rules that allow 22/tcp from your management network and 80/tcp for the web interface. Put TLS in front of the web interface for public exposure (see Step 9, Enabling HTTPS).

Step 1 - Deploy from the Azure Marketplace

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

Step 2 - Deploy from the Azure CLI

Accept the Marketplace terms, then create the VM:

# Accept the Marketplace terms once per subscription (required before the first CLI deploy)
az vm image terms accept --urn sac1647283583153:tududi-ubuntu-24-04:default:latest
az vm create \
  --resource-group <your-rg> \
  --name tududi \
  --image sac1647283583153:tududi-ubuntu-24-04:default:latest \
  --size Standard_B2s \
  --admin-username azureuser \
  --generate-ssh-keys \
  --public-ip-sku Standard

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 the image contains no default password. Connect over SSH and read the file:

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.

Keep this password in a safe place. It is the credential for both the web interface and 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 commands 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 listens on the loopback interface only, with nginx holding port 80. This 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 [::]:*

Step 5 - Sign in to the web interface

Open http://<vm-public-ip>/ in a browser. On the sign-in page, enter the email address and password from Step 3.

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

Step 6 - Organize tasks, projects and areas

Use the left-hand navigation to move between views of 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 Kanban board arranges the same tasks across their status columns. Drag work from To Do through In Progress to Done.

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

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 you create 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

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

Sign-in attempts are rate-limited to five per fifteen minutes for each 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 instead of retrying immediately.

Step 8 - Where your data lives

Tududi stores everything in a single SQLite database, with file attachments alongside it. Both are 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

Taking a backup

SQLite runs in write-ahead log mode, so copying the .sqlite3 file on its own can miss recent changes. Use SQLite's own backup command instead. It 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 as soon as it boots. For any deployment 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 and 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. When it completes, add your HTTPS address to the allowed origins so that 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 is patched at build time and ships with unattended security updates enabled, so the operating system stays current. To apply updates immediately, run:

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

Tududi's configuration is in /etc/tududi/tududi.env. Restart the service after you change it:

sudo systemctl restart tududi.service

Troubleshooting

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

If you cannot sign in, read the password again from /root/tududi-credentials.txt and enter it exactly as written. The password is case-sensitive. Also check that you have not used up the limit of five attempts per fifteen minutes.

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 right after launch, check that the first boot has finished:

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

Expected output:

active first boot complete

Support

Every SecureAnyCloud image comes with 24/7 support. Contact us at sales@secureanycloud.com or visit secureanycloud.com.

Tududi is open source software published under the MIT license by the Tududi project. SecureAnyCloud packages and supports the deployment image. SecureAnyCloud is not affiliated with or endorsed by the Tududi project.

Deploy on Azure

Launch Tududi 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