Developer Tools Azure

Wakapi on Ubuntu 24.04 on Azure User Guide

| Product: Wakapi on Ubuntu 24.04 LTS on Azure

Overview

Wakapi is a self hosted, WakaTime compatible coding activity and statistics server. It receives heartbeats from the standard WakaTime editor plugins for Visual Studio Code, JetBrains IDEs, Vim, Sublime Text and many more, then renders clear dashboards of how much time you spend per project, per language, per editor and per machine. Because it speaks the WakaTime API, your existing editor setup points at your own server by changing a single line of configuration, and your coding history stays entirely on infrastructure you control. It is a single Go binary with an embedded SQLite backend, so it stays lightweight and starts instantly.

The cloudimg image installs Wakapi 2.17.5 and puts it behind an nginx reverse proxy that binds the server to loopback. The appliance is single tenant and secure by default: public registration is disabled, and there is no default login. On first boot wakapi-firstboot.service generates a per VM password salt and a unique administrator account, writing the credentials to a root only file at /root/wakapi-info.txt. Nothing is baked into the image.

What is included:

  • Wakapi 2.17.5 single Go binary (/usr/local/bin/wakapi)
  • SQLite activity database at /var/lib/wakapi/wakapi.db (no separate database to maintain)
  • wakapi.service running as wakapi, bound to loopback 127.0.0.1:3000
  • nginx.service reverse proxy on TCP 80, TLS ready
  • wakapi-firstboot.service generating a per VM password salt and admin account on first boot
  • Public signup disabled and invite codes disabled, so the server is single tenant, not an open sign up service
  • A WakaTime compatible REST API for ingesting heartbeats and reading summaries
  • Ubuntu 24.04 LTS base, latest patches, unattended security upgrades enabled
  • 24/7 cloudimg support, 24h response SLA

Prerequisites

An active Azure subscription, an SSH key, and a VNet with a subnet. Recommended VM size: Standard_B2s (Wakapi is very light; 4 GB RAM is plenty).

Step 1: Deploy from the Azure Portal

Search the Azure Marketplace for Wakapi, choose the plan, and create the VM. In the networking step attach an NSG that allows inbound TCP 22 (SSH) and TCP 80 (the web UI) from your client networks only. Put a TLS reverse proxy in front of port 80 for production.

Step 2: Deploy from the Azure CLI

RG="wakapi-prod"; LOCATION="eastus"; VM_NAME="wakapi-01"
GALLERY_IMAGE_ID="/subscriptions/<sub-id>/resourceGroups/azure-cloudimg/providers/Microsoft.Compute/galleries/cloudimgGallery/images/wakapi-ubuntu-24-04/versions/<version>"
SSH_KEY="$(cat ~/.ssh/id_rsa.pub)"
az group create --name "$RG" --location "$LOCATION"
az network vnet create -g "$RG" --name wk-vnet --address-prefix 10.104.0.0/16 --subnet-name wk-subnet --subnet-prefix 10.104.1.0/24
az network nsg create -g "$RG" --name wk-nsg
az network nsg rule create -g "$RG" --nsg-name wk-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 wk-nsg --name allow-web --priority 110 \
  --source-address-prefixes "<your-mgmt-cidr>" --destination-port-ranges 80 --access Allow --protocol Tcp
az vm create -g "$RG" --name "$VM_NAME" --image "$GALLERY_IMAGE_ID" \
  --size Standard_B2s --storage-sku StandardSSD_LRS \
  --admin-username azureuser --ssh-key-values "$SSH_KEY" \
  --vnet-name wk-vnet --subnet wk-subnet --nsg wk-nsg --public-ip-sku Standard

Step 3: Connect via SSH

ssh azureuser@<vm-ip>

wakapi.service, nginx.service and wakapi-firstboot.service all start automatically on first boot.

Step 4: Verify the Service

sudo systemctl is-active wakapi nginx wakapi-firstboot
sudo test -f /var/lib/cloudimg/wakapi-firstboot.done && echo FIRSTBOOT_DONE
sudo ss -tlnp | grep -E ':80 |:3000 '

Expected output — all three services are active, nginx is bound to the public port 80 and Wakapi is bound to loopback 127.0.0.1:3000 only:

active
active
active
FIRSTBOOT_DONE
LISTEN 0 4096 127.0.0.1:3000 0.0.0.0:*  users:(("wakapi",...))
LISTEN 0 511  0.0.0.0:80     0.0.0.0:*  users:(("nginx",...))

Wakapi, nginx and the firstboot service all active, with nginx bound to public port 80 and Wakapi bound to loopback 127.0.0.1:3000 only

Step 5: Get Your Admin Credentials

Wakapi has no default login. On first boot a unique administrator account and a per VM password salt are generated and written to a root only note. Public registration is disabled, so this account is the only way in:

sudo cat /root/wakapi-info.txt
sudo ss -tlnp | grep 3000
grep -E 'allow_signup|invite_codes|listen_ipv4' /etc/wakapi/config.yml

The note contains the administrator username, password and API key, all unique to this machine and never baked into the image:

WAKAPI_ADMIN_USERNAME=admin
WAKAPI_ADMIN_PASSWORD=<WAKAPI_ADMIN_PASSWORD>
WAKAPI_API_KEY=<per-VM api key>
WAKAPI_URL=http://<vm-ip>/

The per VM admin note with the password and API key masked, Wakapi bound to loopback only, and the config showing allow_signup false and invite_codes false

Step 6: Send a Heartbeat with the API

Wakapi speaks the WakaTime API. POST a heartbeat with your API key, then read it back from the summary endpoint. This is exactly what an editor plugin does automatically:

API_KEY=$(sudo grep '^WAKAPI_API_KEY=' /root/wakapi-info.txt | cut -d= -f2-)
NOW=$(date +%s)
curl -s -o /dev/null -w 'POST /api/heartbeat -> HTTP %{http_code}\n' \
  -H 'Content-Type: application/json' -H 'User-Agent: wakatime/v1.0 (Linux) go1.22 vscode/1.90' \
  --data "[{\"entity\":\"/srv/app/main.go\",\"type\":\"file\",\"time\":$NOW,\"language\":\"Go\",\"project\":\"demo\",\"category\":\"coding\"}]" \
  "http://127.0.0.1/api/heartbeat?api_key=$API_KEY"
curl -s "http://127.0.0.1/api/summary?interval=today&recompute=true&api_key=$API_KEY" \
  | jq '{total_seconds:(.projects|map(.total)|add), projects:[.projects[].key]}'

The heartbeat returns HTTP 201 and the summary reflects the projects you have logged today:

POST /api/heartbeat -> HTTP 201
{
  "total_seconds": 5676,
  "projects": ["web-frontend","api-gateway","infra","data-pipeline","demo"]
}

Sending a heartbeat with the API key returns HTTP 201, and the summary endpoint returns the total seconds and the list of projects logged today

Step 7: Log In to the Web UI

Open the web UI in a browser and log in with the administrator username and password from Step 5. Because signup is disabled, the sign up button is greyed out — this is a single tenant server:

open http://<vm-ip>/login

The Wakapi login page with the username and password filled in, the sign up button greyed out because public registration is disabled, and the footer showing version 2.17.5 on sqlite3

Step 8: Read Your Coding Dashboard

After logging in you land on the dashboard. The stat tiles show total time, total heartbeats, top project, top language and top editor, and the charts break your time down by project and by language for the selected interval (Today, This Week, This Month and so on):

The Wakapi dashboard showing total time and heartbeats, a projects bar chart, a languages pie chart and an editors panel for the selected interval

Step 9: Find Your API Key

Open Settings → API Keys to see the API key that identifies your account. This is the key your editor plugin uses to send heartbeats. You can reset the primary key or create additional read only or read write keys for different applications:

The Settings API Keys tab showing the primary API key, a reset button, and the option to add additional read only or read write keys

Step 10: Account Settings

The Settings → Account tab is where you set your time zone (which controls how daily statistics are bucketed), your start of week, an optional e-mail address for weekly reports, and where you change the administrator password. Change the generated password here after your first login:

The Wakapi account settings page with time zone, start of week, e-mail address, a save button and a change password section

Step 11: Connect Your Editor

Install the WakaTime plugin for your editor (available for VS Code, JetBrains IDEs, Vim, Sublime Text and dozens more), then point it at your Wakapi server by editing ~/.wakatime.cfg on your workstation. Use the API key from Step 9 and your server's /api endpoint:

[settings]
api_url = http://<vm-ip>/api
api_key = <your-api-key>

From then on the plugin sends heartbeats automatically as you code, and your dashboard fills in with real activity.

Step 12: Server Components

Component Path
Wakapi binary /usr/local/bin/wakapi
Config file /etc/wakapi/config.yml
Environment file (per VM) /etc/wakapi/wakapi.env
SQLite database /var/lib/wakapi/wakapi.db
Systemd unit /etc/systemd/system/wakapi.service
Firstboot script /usr/local/sbin/wakapi-firstboot.sh
Per VM info note /root/wakapi-info.txt (mode 0600)
Sentinel /var/lib/cloudimg/wakapi-firstboot.done
/usr/local/bin/wakapi -version
ls /usr/local/bin/wakapi /var/lib/wakapi/wakapi.db /etc/wakapi/config.yml

Wakapi reporting version 2.17.5, the installed component paths, and the count of heartbeats, projects and admin users stored in the SQLite database

Step 13: Managing the Service

sudo systemctl restart wakapi.service
sudo journalctl -u wakapi.service --no-pager -n 20

Wakapi restarts in under a second because it is a single binary with a local SQLite database. The service is hardened with NoNewPrivileges, ProtectSystem=strict and ProtectHome, and only has write access to its own data directory.

Step 14: Configuration

Wakapi is configured through /etc/wakapi/config.yml plus WAKAPI_* overrides in /etc/wakapi/wakapi.env. Useful keys already set by the image:

Setting Meaning Image default
server.listen_ipv4 Bind address 127.0.0.1 (loopback)
server.port Bind port 3000
db.name SQLite database file /var/lib/wakapi/wakapi.db
security.allow_signup Public registration false (single tenant)
security.invite_codes Invite code signup false
security.insecure_cookies Allow cookies over plain HTTP true (serving HTTP behind nginx)
WAKAPI_PASSWORD_SALT Password hashing salt (per VM) generated at first boot
WAKAPI_PUBLIC_URL Base URL used for links resolved to this VM's address

After editing either file, apply changes with sudo systemctl restart wakapi.service. Set WAKAPI_PUBLIC_URL to your public hostname once you add TLS so links resolve for external users.

Step 15: Add TLS (Optional)

For production, terminate TLS at nginx. Point a DNS record at the VM, then use the packaged nginx with a certificate from Let's Encrypt:

sudo apt-get install -y certbot python3-certbot-nginx
sudo certbot --nginx -d <your-domain>

certbot installs the certificate, rewrites the nginx server block to listen on 443 and sets up automatic renewal. After enabling HTTPS you can set security.insecure_cookies to false in /etc/wakapi/config.yml for stricter session cookies.

Step 16: Security Recommendations

  • Restrict the NSG so ports 80 and 22 only reach trusted networks.
  • Terminate TLS at nginx (Step 15) so credentials and API keys travel encrypted, then set insecure_cookies to false.
  • Change the admin password after first login (Step 10), and keep public signup disabled so the server stays single tenant.
  • Rotate the API key from Settings → API Keys if it is ever exposed, and update ~/.wakatime.cfg on your workstations afterwards.
  • Back up /var/lib/wakapi/wakapi.db to Azure Blob if you need your coding history to survive.
  • Patch the OS monthly with sudo apt-get update && sudo apt-get upgrade; unattended security upgrades are already enabled.

Step 17: Support and Licensing

Wakapi is MIT licensed — no per CPU or per user fee. cloudimg provides commercial support separately.

  • Email: support@cloudimg.co.uk
  • Website: www.cloudimg.co.uk
  • Support hours: 24/7, 24h response SLA

Deploy on Azure

Launch Wakapi on Ubuntu 24.04 with 24/7 support from cloudimg.

View on Marketplace

Need Help?

Our support team is available 24/7. support@cloudimg.co.uk