Wakapi 2.18.2
Overview
Wakapi is an open-source, self-hosted coding statistics server that speaks the WakaTime API. You keep using the standard WakaTime plugins for Visual Studio Code, JetBrains IDEs, Vim, Neovim, Sublime Text and dozens of other editors, and you point them at your own server instead.
Wakapi turns the "heartbeats" those plugins send into dashboards of the time you spend per project, language, editor, operating system and machine. It also provides daily and ranged summaries, project pages, badges and a WakaTime-compatible REST API. Your coding history stays inside your own AWS account.
This guide covers the Wakapi 2.18.2 on Ubuntu 24.04 AMI from SecureAnyCloud on Amazon EC2. It is one version of the SecureAnyCloud developer and API tools listing on AWS Marketplace, and you select it as the software version when you subscribe and launch. The SecureAnyCloud image locks Wakapi down.
What you get:
- HTTPS from the first boot. nginx serves Wakapi on port 443 with a certificate generated on your instance's first boot. Port 80 redirects to HTTPS, and Wakapi's session cookies are marked Secure.
- An administrator created for your instance only. The first boot creates the administrator account
admin, with a password and an API key generated on your instance, before nginx or Wakapi accept a single connection. They are written to/var/lib/sac/data/wakapi-credentials.txt(root only). First boot also generates the instance's password salt and the key that signs its session cookies. No account, password or key is baked into the image. - Closed to strangers. Public sign-up and invite codes are off, so nobody can register. Visitors who are not signed in land on the login page, and the public leaderboard is off.
- Your plugins keep working. WakaTime plugins authenticate with your API key on
/api, so there is no extra password prompt in front of the API. A heartbeat with a wrong key is refused. - Outbound requests limited to WakaTime. Wakapi can forward your heartbeats to a WakaTime account. On this image, Wakapi can reach
api.wakatime.com(andwakapi.dev) and nothing else, so a user cannot make the server fetch an address of their choosing. Data imports are off until you enable them (Step 8). Wakapi and nginx are blocked from the EC2 instance metadata service at the kernel level. - Cross-site requests refused. nginx refuses a form post from another website. It also refuses a heartbeat from another site that relies on your browser's session instead of an API key, and another site's attempt to trigger the links that change a setting. Compressed request bodies are refused.
- Unused features closed. The built-in API explorer (
/swagger-ui, which bundles an old copy of the DOMPurify library), OpenID Connect sign-in, the anonymous plugin error report, metrics and subscriptions answer 404. - Host name from configuration, never from the request. Wakapi's public URL is set on first boot from the instance's public DNS name, and nginx refuses requests for host names the instance does not serve. Wakapi always sees the real client address, whatever forwarding headers a request carries, so its login rate limit applies to the real client.
- Built from source on the current Go release. The upstream release binary was built with an older Go and network library that govulncheck flags. SecureAnyCloud builds the 2.18.2 source tag with Go 1.27.3 and the fixed golang.org/x/net, reproducibly, and govulncheck finds no vulnerable code in it.
- Hardened defaults. Wakapi runs as its own unprivileged user with no capabilities and a read-only view of the system except its data directory. nginx and Wakapi refuse to start until first boot has finished, and the AMI requires IMDSv2.
- Fully patched Ubuntu 24.04 LTS at build time, with unattended security upgrades switched on.
What is on the image: Wakapi 2.18.2 (/usr/local/bin/wakapi, with its web UI built in), nginx, SQLite, Ubuntu 24.04 LTS, cloud-init and the AWS Systems Manager agent.
Default user: ubuntu | Architecture: x86_64 | Recommended instance type: m5.large
Prerequisites
- An AWS account subscribed to the listing, an EC2 key pair and a VPC subnet with a route to the internet (or a private route you use for management).
- A security group that allows TCP 22 (SSH), TCP 443 (Wakapi) and TCP 80 (redirects to HTTPS) from your own networks only. Include the networks your developers' editors send heartbeats from.
Launch the instance
In the AWS Marketplace listing, choose Continue to Subscribe, then Continue to Configuration. Select Wakapi 2.18.2 on Ubuntu 24.04 as the software version, pick your Region, and choose Launch through EC2. Select m5.large (a smaller instance is fine for a few developers), your key pair, a security group as described above and a subnet.
Or launch with the AWS CLI. Replace the values in angle brackets:
aws ec2 run-instances --region <region> --image-id <ami-id-from-the-listing> \
--instance-type m5.large --key-name <your-key-pair> \
--security-group-ids <your-security-group> --subnet-id <your-subnet> \
--metadata-options HttpTokens=required,HttpEndpoint=enabled \
--tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=wakapi}]'Connect to your instance
Connect over SSH as the ubuntu user, the default user for Wakapi 2.18.2 on Ubuntu 24.04.
ssh -i <your-key.pem> ubuntu@<public-ip>Check the services
First boot generates the certificate, the password salt, the cookie key and the administrator, then starts Wakapi and nginx. Check the units and the first-boot marker.
systemctl is-active wakapi nginx wakapi-firstboot
sudo test -f /var/lib/sac/data/wakapi-firstboot.done && echo FIRSTBOOT_DONEAll three units are active, and the first-boot marker is present.
active active active FIRSTBOOT_DONEOnly SSH and nginx listen on the network. Wakapi itself listens on 127.0.0.1 only.
sudo ss -lntp | awk 'NR>1 {print $4, $6}' | grep -v '127.0.0.5' | sed -E 's/,pid=[0-9]+,fd=[0-9]+//g' | sort0.0.0.0:22 users:(("sshd"),("systemd")) 0.0.0.0:443 users:(("nginx"),("nginx"),("nginx")) 0.0.0.0:80 users:(("nginx"),("nginx"),("nginx")) 127.0.0.1:3000 users:(("wakapi")) [::]:22 users:(("sshd"),("systemd")) [::]:443 users:(("nginx"),("nginx"),("nginx")) [::]:80 users:(("nginx"),("nginx"),("nginx"))Get your URL, password and API key
Print the credentials file. It is readable by root only.
sudo cat /var/lib/sac/data/wakapi-credentials.txtThe file holds the URL, the administrator's user name, this instance's password and the administrator's API key.
# Wakapi 2.18.2 - generated on first boot by wakapi-firstboot.service
# This administrator was created on THIS instance; no account was baked into the image. Keep this file secret and
# change the password after your first sign-in (Settings > Account). WAKAPI_URL=https://ec2-<public-ip-dashed>.<region>.compute.amazonaws.com/ WAKAPI_ADMIN_USER=admin WAKAPI_ADMIN_PASSWORD=<WAKAPI_ADMIN_PASSWORD> WAKAPI_API_KEY=<WAKAPI_API_KEY>
#
# Open WAKAPI_URL in a browser (self-signed certificate minted on this instance's first boot; port 80 redirects to # https) and sign in. Point your editor's WakaTime plugin at this server in ~/.wakatime.cfg:
# [settings]
# api_url = https://ec2-<public-ip-dashed>.<region>.compute.amazonaws.com/api
# api_key = <WAKAPI_API_KEY>
# (with this image's self-signed certificate also add: no_ssl_verify = true, or install a trusted certificate) # Public sign-up and invite codes are OFF. Forwarding to WakaTime reaches api.wakatime.com only; imports are off. # Self-test:
sudo wakapi-selftest
# Change the name it is served as:
sudo wakapi-set-hostname <your-dns-name>
# User guide: https://secureanycloud.com/stacks/wakapi-2-18-2/The URL uses the instance's public DNS name from EC2 instance metadata. The certificate also names the public and private IP addresses, so https://<public-ip>/ works too. To serve Wakapi under your own DNS name, see Step 10.
Sign in
Open the WAKAPI_URL in your browser. The certificate is self-signed, so your browser asks you to confirm it the first time. Alternatively, install your own certificate (Step 10).
Sign in as admin with the password from Step 4. The Sign up button is still drawn on the page, but registration is switched off, and Wakapi answers "registration is disabled on this server".
Then open Settings and change the password under Account to one only you know.
Connect your editors
Install the WakaTime plugin for your editor. The WakaTime plugin list covers VS Code, JetBrains IDEs, Vim, Neovim, Sublime Text, Xcode and many more.
Then point the plugin at your Wakapi server instead of WakaTime. On each workstation, edit ~/.wakatime.cfg (on Windows, %USERPROFILE%\.wakatime.cfg).
[settings] api_url = https://<your-instance-host>/api api_key = <WAKAPI_API_KEY> no_ssl_verify = trueYou need no_ssl_verify only while the instance uses its self-signed certificate. Remove it once you install a certificate your workstations trust (Step 10). Wakapi's Setup page shows the same snippet for the account you are signed in as.
You can check the API from the instance itself. This command sends one test heartbeat with the administrator's API key and reads it back. The key is read from the credentials file into a temporary file and is never typed on a command line.
H=$(sudo sed -n 's#^WAKAPI_URL=https://\([^/]*\)/#\1#p' /var/lib/sac/data/wakapi-credentials.txt)
CFG=$(mktemp); chmod 600 "$CFG" printf 'header = "Authorization: Basic %s"\n' "$(sudo sed -n 's/^WAKAPI_API_KEY=//p' /var/lib/sac/data/wakapi-credentials.txt | tr -d '\n' | base64 -w0)" > "$CFG" curl -sk -K "$CFG" --resolve "$H:443:127.0.0.1" -o /dev/null -w 'heartbeat: %{http_code}\n' \
-H 'Content-Type: application/json' -A 'wakatime/v1.130.1 (linux) curl-test' \
--data "[{\"entity\":\"/home/dev/hello/main.go\",\"type\":\"file\",\"time\":$(date +%s),\"project\":\"hello-wakapi\",\"language\":\"Go\",\"category\":\"coding\"}]" \
"https://$H/api/heartbeat"
TODAY=$(curl -sk -K "$CFG" --resolve "$H:443:127.0.0.1" "https://$H/api/compat/wakatime/v1/users/current/heartbeats?date=$(date -u +%F)") rm -f "$CFG" grep -o '"project":"hello-wakapi"' <<<"$TODAY" | sort -uheartbeat: 201 "project":"hello-wakapi"Each person who uses the server should have their own account and API key (Step 8). You can also list, add and revoke API keys under Settings > API Keys.
Your dashboard
Once your editors send heartbeats, Dashboard shows totals and breakdowns for the interval you pick: today, this week, the past 7 days, a custom range and more. Breakdowns are by project, language, editor, operating system, machine, label and category. The filters at the top narrow everything to one project, language or machine.
Projects lists every project you worked on, most recent first. Choose one to see its own statistics.
Users, forwarding to WakaTime and imports
The image ships as a single-user server: sign-up, invite codes and imports are off. The settings live in /etc/wakapi/config.yml.
sudo grep -E '^ (public_url|allow_signup|invite_codes|import_enabled|trust_reverse_proxy_ips|insecure_cookies|disable_frontpage):' /etc/wakapi/config.ymlpublic_url: https://ec2-<public-ip-dashed>.<region>.compute.amazonaws.com import_enabled: false insecure_cookies: false allow_signup: false invite_codes: false disable_frontpage: true trust_reverse_proxy_ips: 127.0.0.1- Add teammates with invite codes. Set
invite_codes: trueand restart Wakapi (sudo systemctl restart wakapi). Settings then shows an Invite Friends button that generates a single-use invite link, valid for 24 hours. Send the link to a colleague, who registers with it while open sign-up stays off. Avoidallow_signup: trueunless the instance is reachable only from a private network. - Forward your heartbeats to WakaTime. Under Settings > Integrations, enter your WakaTime API key and choose Connect. Wakapi then relays each heartbeat to
api.wakatime.comas well, so you can use both services. - Forward to another server, or import data. Wakapi's outbound requests are limited to the hosts in
NO_PROXYin/etc/wakapi/egress.env. To forward to your own Wakapi or WakaTime-compatible server, or to import history from WakaTime, add the host names, comma-separated, toNO_PROXY. The WakaTime data export downloads from a separate storage host, so add that host too. For imports, also setimport_enabled: trueand add the host toimport_hosts_whitelistinconfig.yml. Then runsudo systemctl restart wakapi.
Run the self-test
The image includes a self-test. It checks HTTPS, and that anonymous visitors, sign-up and guessed passwords are refused. It then signs in, sends a heartbeat with the API key, reads it back, checks that a wrong key is refused, removes its test heartbeat and checks the listening ports.
sudo wakapi-selftestSELFTEST-OK https://ec2-<public-ip-dashed>.<region>.compute.amazonaws.com/ : anonymous refused, sign-up refused, guessed passwords refused, admin signed in, heartbeat stored + read back with the API key (wrong key 401), listeners 22/80/443Your own DNS name and certificate
Point a DNS name at the instance, then set it as the name Wakapi is served as. This command writes the name into the nginx configuration and Wakapi's public URL, generates a new self-signed certificate for it and restarts Wakapi.
sudo wakapi-set-hostname <your-domain>To use a certificate from your own certificate authority, or one you obtained for the name, replace /etc/ssl/wakapi/wakapi.crt and /etc/ssl/wakapi/wakapi.key. Keep the key at mode 0600, owned by root. Then reload nginx with sudo systemctl reload nginx.
Running wakapi-set-hostname again generates a new self-signed certificate, so install your own after it. Once your workstations trust the certificate, remove no_ssl_verify from their ~/.wakatime.cfg.
Check the security posture from the command line
Run these checks on the instance. Each line of output confirms one protection.
H=$(sudo sed -n 's#^WAKAPI_URL=https://\([^/]*\)/#\1#p' /var/lib/sac/data/wakapi-credentials.txt) c() { curl -sk --resolve "$H:443:127.0.0.1" --resolve "$H:80:127.0.0.1" -o /dev/null "$@"; } c -w 'port 80: %{http_code} -> %{redirect_url}\n' "http://$H/" c -w 'API without a key: %{http_code}\n' "https://$H/api/summary?interval=today" c -w 'heartbeat with a wrong key: %{http_code}\n' -u 'not-a-key:' -H 'Content-Type: application/json' --data '[]' "https://$H/api/heartbeat" c -w '%{http_code}\n' -X POST --data 'username=someone&password=secret123&password_repeat=secret123' "https://$H/signup" | sed -E 's/^(403|429)$/sign-up: refused/' c -w 'cross-origin form post: %{http_code}\n' -X POST -H 'Origin: https://evil.example' "https://$H/login" c -w 'API explorer (/swagger-ui): %{http_code}\n' "https://$H/swagger-ui/" c -w 'compressed request body: %{http_code}\n' -X POST -H 'Content-Encoding: gzip' --data 'x' "https://$H/api/heartbeat" curl -s -o /dev/null -w 'unknown host name: %{http_code} (connection closed)\n' -H 'Host: other.example' http://127.0.0.1/ || trueport 80: 301 -> https://ec2-<public-ip-dashed>.<region>.compute.amazonaws.com/ API without a key: 401 heartbeat with a wrong key: 401 sign-up: refused cross-origin form post: 403 API explorer (/swagger-ui): 404 compressed request body: 415 unknown host name: 000 (connection closed)The sign-up attempt is refused with 403 ("registration is disabled"), or with 429 once the sign-up rate limit for your address is reached.
Wakapi and nginx cannot reach the instance metadata service, and Wakapi's outbound requests go only to the allowed hosts. Check both.
for u in wakapi nginx; do echo "$u denies: $(systemctl show -p IPAddressDeny --value $u | tr ' ' '\n' | sort | xargs)"; done
sudo grep -E '^(HTTPS_PROXY|NO_PROXY)=' /etc/wakapi/egress.envwakapi denies: 168.63.129.16/32 169.254.169.254/32 fd00:ec2::254/128 nginx denies: 168.63.129.16/32 169.254.169.254/32 fd00:ec2::254/128 HTTPS_PROXY=http://127.0.0.1:9 NO_PROXY=api.wakatime.com,wakapi.devHTTPS_PROXY points at a port where nothing listens, so any request to a host that is not in NO_PROXY fails before a connection is made.
Server components
Key files and services:
- Wakapi binary: /usr/local/bin/wakapi
- Configuration: /etc/wakapi/config.yml (root:wakapi, mode 0640)
- Per-instance salt and cookie key: /etc/wakapi/wakapi.env (root, mode 0600)
- Outbound allowlist: /etc/wakapi/egress.env
- Database (SQLite): /var/lib/wakapi/wakapi.db
- Service units: wakapi.service, nginx.service, wakapi-firstboot.service
- nginx site: /etc/nginx/sites-available/sac-wakapi (closed routes: /etc/nginx/wakapi-closed.conf)
- Certificate and key: /etc/ssl/wakapi/
- First-boot script: /usr/local/sbin/wakapi-firstboot.sh
- Host name helper: /usr/local/sbin/wakapi-set-hostname
- Self-test: /usr/local/sbin/wakapi-selftest
- Credentials: /var/lib/sac/data/wakapi-credentials.txt (mode 0600)
Run this command to print the Wakapi version, check for a Go toolchain, print the nginx version and read the build record.
/usr/local/bin/wakapi -version command -v go || echo "no Go toolchain" nginx -v 2>&1 sed -n '1,5p' /usr/share/doc/sac-wakapi/BUILD-INFO.txt2.18.2 no Go toolchain nginx version: nginx/1.24.0 (Ubuntu) wakapi 2.18.2 (tag 2.18.2, commit 1157f203c8b646466b4e72563764738b10f0cd0d), MIT - built on the build instance from the tag https://github.com/muety/wakapi/archive/refs/tags/2.18.2.tar.gz (sha256 1c77089b75b6e88dbb984708d0a03476867913e29c61b09bf783063336e73e1e) go1.27.3 (official tarball, sha256 65efbc80fdc1fa2cd2631cad5b481777a68e8725f34e89f50fc6ac714e379b40; not shipped), CGO_ENABLED=0, GOEXPERIMENT=greenteagc,jsonv2, -trimpath, -ldflags '-w -s'; binary sha256 7a121ee6531fa3c2259f7e3300714db4deab9880b9e189fc30f00978584f9156 (reproducible: identical to the binary audited off-instance)/usr/share/doc/sac-wakapi/BUILD-INFO.txt records the source and toolchain checksums, the dependency update, the bundled web libraries and the govulncheck result. The files go.mod, go.sum and govulncheck-wakapi-source.txt sit beside it.
Backups. Wakapi keeps everything in the SQLite database /var/lib/wakapi/wakapi.db on the root volume. Take an EBS snapshot of the root volume, or use AWS Backup. For a consistent copy, run sudo systemctl stop wakapi, start the snapshot, then run sudo systemctl start wakapi.
Operating system changes. SecureAnyCloud made these changes to the operating system:
- SSH accepts keys only, with no passwords and no root login.
- The root account has no password.
- The firmware metadata refresh timer (fwupd-refresh) is masked because EC2 has no updatable firmware.
- The golden image's boot-time updater and the SSM user's passwordless sudo are removed.
- Unattended security upgrades are on.
Security notes and recommendations
- Restrict network access. Allow ports 22, 80 and 443 in the security group from your own networks only.
- Secure the administrator account. Change the administrator password after your first sign-in (Step 5), keep the credentials file secret, and give each person their own account and API key (Step 8).
- Use a trusted certificate. Use your own DNS name and a certificate your clients trust (Step 10), then drop
no_ssl_verifyfrom the plugins' configuration. - Keep API keys out of URLs. Send API keys in the
Authorizationheader, as the WakaTime plugins do, rather than as anapi_key=URL parameter. Request URLs are written to the nginx and Wakapi logs. - Known advisories. govulncheck lists one advisory for a module in Wakapi's build whose vulnerable package Wakapi never uses, so no vulnerable code is reachable: GO-2026-5932 (the golang.org/x/crypto/openpgp package is unmaintained, and there is no fixed version). The built-in API explorer bundles DOMPurify 2.3.10, and nginx closes its /swagger-ui route on this image. Use the API reference at https://wakapi.dev/swagger-ui/ instead. SecureAnyCloud rechecks both at each Wakapi release.
- Keep the OS patched. Unattended security upgrades are on. Run
sudo apt-get update && sudo apt-get upgradeto apply everything else.
Support and licensing
Wakapi is open source under the MIT licence (Ferdinand Mütsch and contributors). The licence text is in /usr/share/doc/sac-wakapi/, and the source of this exact release is at https://github.com/muety/wakapi/tree/2.18.2. Its built-in web UI includes open-source libraries under the MIT licence and Iconify under the Apache 2.0 licence.
There is no per-user or per-CPU licence fee. The AMI price covers SecureAnyCloud's build, hardening, first-boot setup and support. WakaTime is a trademark of its owner. Wakapi and this image are not affiliated with or endorsed by WakaTime.
- Email: sales@secureanycloud.com
- Website: https://secureanycloud.com
- Support: 24/7, with a 24-hour response SLA
Deploy on AWS
Launch Wakapi 2.18.2 on Ubuntu 24.04 on AWS with 24/7 support from SecureAnyCloud.
View on Marketplace
Need Help?
Our support team is available 24/7.
sales@secureanycloud.com