Wakapi on Ubuntu 24.04 on Azure User Guide
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.servicerunning aswakapi, bound to loopback127.0.0.1:3000nginx.servicereverse proxy on TCP 80, TLS readywakapi-firstboot.servicegenerating 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",...))

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>/

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"]
}

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

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):

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:

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:

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

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_cookiestofalse. - 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.cfgon your workstations afterwards. - Back up
/var/lib/wakapi/wakapi.dbto 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