Kimai on Ubuntu 24.04 on Azure User Guide
Overview
Kimai is a popular open source, self hosted time tracking application for teams and freelancers. From an ordinary browser you record working hours against customers, projects and activities, then turn those timesheets into invoices and exportable reports, control access with roles and teams, and keep every record private on infrastructure you control. The cloudimg image serves Kimai 2.60.0, a Symfony application, behind nginx with PHP 8.3 FPM and OPcache over a hardened, fully patched Ubuntu 24.04 LTS base. Kimai stores its data in a local MariaDB database, which Kimai recommends for production, and the schema is migrated in the image so the application is ready the moment the VM boots. A fresh application secret, a rotated database password and a unique administrator password are all generated on the first boot of every VM and written to a root only file, so no shared or default login ever ships in the image. Backed by 24/7 cloudimg support.
What is included:
- Kimai 2.60.0 served by nginx through the PHP 8.3 front controller, managed by systemd
- The Kimai web interface on
:80with a super administrator account required to sign in - A per VM application secret, database password and administrator password generated on first boot and recorded in a root only file
- A local MariaDB database, the datastore Kimai recommends for production
- Two dedicated Azure data disks: the MariaDB data directory at
/var/lib/mysqland the Kimai application tree at/opt/kimai, each independently resizable nginx.service,php8.3-fpm.serviceandmariadb.serviceas enabled systemd units- 24/7 cloudimg support
Prerequisites
An active Azure subscription, an SSH key pair, and a VNet plus subnet in the target region. Standard_B2s (2 vCPU / 4 GiB RAM) is a sensible starting point; size up for larger teams and heavier concurrent use. NSG inbound: allow 22/tcp from your management network and 80/tcp (and 443/tcp if you terminate TLS on the VM) for the web interface. Kimai serves plain HTTP on port 80; for production, terminate TLS in front of it with your own domain.
Step 1 - Deploy from the Azure Marketplace
Sign in to the Azure Portal, choose Create a resource, search the Marketplace for Kimai 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). Review the two dedicated data disks on the Disks tab, then Review + create then Create.
Step 2 - Deploy from the Azure CLI
az vm create \
--resource-group <your-rg> \
--name kimai \
--image <marketplace-image-urn> \
--size Standard_B2s \
--admin-username azureuser \
--generate-ssh-keys \
--public-ip-sku Standard
Then open port 80 to the web interface:
az vm open-port --resource-group <your-rg> --name kimai --port 80
Step 3 - Confirm the services are running
SSH in as azureuser and confirm nginx, PHP-FPM and MariaDB are active. nginx serves Kimai on port 80.
systemctl is-active nginx php8.3-fpm mariadb
ss -tlnp | grep ':80 ' | sed 's/ */ /g'

Step 4 - Retrieve the per VM administrator password
Every VM generates its own administrator password on first boot and writes it, along with the login user and URL, to a root only credentials file. Read it with sudo:
sudo cat /root/kimai-credentials.txt

You sign in to Kimai with username admin and the kimai.admin.pass value from this file. You can prove the login works from the command line by establishing an authenticated session with those credentials. A successful login reaches the dashboard, which no longer contains the sign in form; a wrong password is bounced back to it:
U=$(sudo grep '^kimai.admin.user=' /root/kimai-credentials.txt | cut -d= -f2-); P=$(sudo grep '^kimai.admin.pass=' /root/kimai-credentials.txt | cut -d= -f2-); CJ=$(mktemp); LF=$(curl -s -c "$CJ" http://127.0.0.1/en/login); T=$(printf '%s' "$LF" | grep -m1 -oE 'name="_csrf_token"[^>]*value="[^"]+"' | sed -E 's/.*value="([^"]+)".*/\1/'); curl -s -b "$CJ" -c "$CJ" -o /dev/null -L --data-urlencode "_csrf_token=$T" --data-urlencode "_username=$U" --data-urlencode "_password=$P" http://127.0.0.1/en/login_check; BODY=$(curl -s -b "$CJ" -L http://127.0.0.1/); rm -f "$CJ"; case "$BODY" in *_password*) echo "login FAILED";; *) echo "login OK";; esac
Step 5 - Sign in to Kimai
Browse to http://<vm-public-ip>/. Kimai opens on its sign in screen. Enter username admin and the password from Step 4, then select Login.

Step 6 - The dashboard
After signing in you reach the Kimai dashboard, which summarises your recorded working time, your most recent timesheets and your active work. The large start button at the top punches you in against the selected project and activity so you can begin tracking time straight away.

Step 7 - Record timesheets, customers, projects and activities
Kimai organises time into a customer, project and activity hierarchy. Before you record billable time you create a customer, add a project under that customer, and add one or more activities to the project. From the left hand menu you can:
- Open Timesheet to start and stop time records, edit durations, and review the time you have logged.
- Open Customers to manage the clients you bill, including their address, currency and budget.
- Open Projects to manage the projects under each customer, with their own budgets, dates and teams.
- Open Activities to manage the kinds of work you record time against, either globally or per project.
- Open Invoices to turn recorded time into invoices, and Export to produce CSV, Excel and PDF reports.

To change the administrator password, select your user avatar at the top right of any page, choose Profile, open the Password tab, and set a new password.
Step 8 - Manage users, roles and teams
Kimai supports multiple users with role based access control. As a super administrator you can invite colleagues and control what they can see and do. Open Users under the administration area to create accounts and assign roles (user, teamlead, administrator, super administrator), and open Teams to group users and grant a team access to specific customers, projects and activities so each person only sees the work that concerns them.

New users can also be created from the command line on the VM. The console command prompts interactively for a password, so run it directly in your own terminal:
sudo -u www-data php /opt/kimai/bin/console kimai:user:create alice alice@example.com ROLE_ADMIN
Step 9 - Verify the stack
Confirm the Kimai version, that the services are enabled, and that the web interface answers on port 80:
sudo -u www-data php /opt/kimai/bin/console kimai:version
systemctl is-enabled nginx php8.3-fpm mariadb
curl -s -o /dev/null -w 'Kimai login HTTP %{http_code}\n' http://127.0.0.1/en/login

Step 10 - Where your data lives
The MariaDB data directory and the Kimai application tree each live on their own dedicated Azure data disk, separate from the OS disk: the database at /var/lib/mysql and the code, .env configuration and var/ runtime data at /opt/kimai. Both disks are captured into the image and re provisioned on every VM, and you can resize either tier independently. Confirm the mounts and filesystems:
df -h /var/lib/mysql /opt/kimai | tail -2
for m in /var/lib/mysql /opt/kimai; do findmnt -no SOURCE,TARGET,FSTYPE "$m"; done

Step 11 - Enable HTTPS with Let's Encrypt
The image serves Kimai over HTTP on port 80 so it works immediately behind the VM's public address. For production you should put it behind HTTPS. Point a DNS name at the VM, open 443/tcp in the NSG, then install Certbot and request a certificate. Replace your-domain.example.com with the DNS name that resolves to the VM:
sudo apt-get update
sudo apt-get install -y certbot python3-certbot-nginx
sudo certbot --nginx -d your-domain.example.com
Certbot obtains a certificate, updates the nginx configuration to serve Kimai over HTTPS, and sets up automatic renewal.
Step 12 - Back up and maintain your VM
All of Kimai's data is in the MariaDB database on the /var/lib/mysql disk and the application tree on the /opt/kimai disk. To back up your time tracking data, either snapshot the two Azure data disks from the portal or the CLI, or take a logical database dump:
sudo mariadb-dump kimai > /tmp/kimai-backup.sql
Apply operating system security updates regularly; the image ships fully patched with unattended security upgrades enabled. Because the data lives on its own disks, you can resize either tier independently if your time tracking store grows.
Security notes
- Kimai serves plain HTTP on port 80. For production, put it behind your own TLS terminating reverse proxy or an Azure Application Gateway with a certificate for your domain, and restrict port 80 in the NSG to trusted networks.
- The per VM administrator password lives in
/root/kimai-credentials.txt, readable only by root, and the application secret and database password are rotated on first boot too, so no shared or default credential ships in the image. Sign in and change the administrator password from the profile menu, and keep a copy somewhere safe. - Kimai exposes a full REST API and supports LDAP and SAML single sign on. Create scoped API tokens or wire up your identity provider for integrations rather than sharing your login.
- Keep the VM patched. The image ships fully patched with unattended security upgrades enabled.
Architecture summary
| Component | Detail |
|---|---|
| Application | Kimai 2.60.0 (Symfony) |
| Web server | nginx serving the public/ document root on port 80 |
| Runtime | PHP 8.3 FPM with OPcache |
| Database | MariaDB, data directory on a dedicated data disk at /var/lib/mysql |
| Application disk | Dedicated data disk mounted at /opt/kimai (code, .env, var/) |
| Credentials | Generated on first boot, written to /root/kimai-credentials.txt (mode 0600) |
| First user | The administrator is a super administrator of the installation |
Support
cloudimg images come with 24/7 support. If you have any questions about this Kimai image or need help with your deployment, contact us through the cloudimg website.
All product and company names are trademarks or registered trademarks of their respective holders. Use of them does not imply any affiliation with or endorsement by them.