OwnTracks with NixOS and Tailscale

Posted on Fri 21 February 2025 in self-hosting

In an effort to reduce my reliance on big tech I recently decided to try using OwnTracks to replace Google Maps Timeline. This article provides a rundown of how I accomplished this using an existing home server running NixOS and my existing Tailnet.

Background

For various reasons I find it useful to be able to review my past whereabouts, and Google Timeline has worked fairly well for this purpose. At some point Google changed Timeline to only store location history on the mobile device, removing it from the Maps web app. I've also found that the UI for Timeline on my phone has become laggy and feels like it could fail at any time. These seem like warning signs that Google may be neglecting the service or that it may be dropped entirely.

To hedge against that possibility and in furtherance of my general goal of reducing dependence on big tech, I've decided try replacing this service with OwnTracks.

OwnTracks

OwnTracks consists of a few components, including mobile apps for Android and iOS that send location updates to a server. The server can either be an MQTT broker or the OwnTracks Recorder. Using an MQTT broker permits live location sharing among devices running the app. Since I only care about recording my location history, I can forgo the MQTT broker and set up the app to publish updates directly to the recorder via HTTP.

The OwnTracks Recorder acts as a database to store location histories published by the mobile apps. It also provides APIs for querying the stored histories.

There is also an OwnTracks Frontend which provides a web app for viewing the stored location histories from the Recorder service.

Tailscale

Tailscale is a peer-to-peer VPN solution. Since I already have Tailscale set up on my home server and my phone, I can avoid a lot of fiddly work securing (in both senses of the word) a publicly available IP address for my server. As long as my phone is connected to my Tailscale VPN, known as a Tailnet, my phone can directly connect to the server.

Running the OwnTracks Recorder

I added a systemd service declaration to my NixOS configuration on my server to run the OwnTracks Recorder service. To avoid running the service with root privileges I'm using the dynamic user facility provided by systemd which automatically creates an unprivileged user to run the process. The StateDirectory directive causes a directory to be created under /var/lib with permissions allowing the service to store its persistent state (the location histories, in this case) across restarts.

Since OwnTracks Recorder has been packaged for NixOS I can reference the package directly to invoke the service, and Nix will automatically make sure it is installed. I'm using the unstable channel so the package includes a recent commit which fixes the HTTP mode for the service. This is necessary for the setup to work. The fix should be included in the next stable release of NixOS.

When the executable is invoked it is provided with the path to the dynamic user's state directory via the --storage flag. The --port 0 flag tells Recorder not to try to connect to an MQTT broker. I leave the HTTP port at the default 8083. Other command line options are in the documentation.

  systemd.services.owntracks = {
    enable = true;
    description = "owntracks recorder";
    serviceConfig = {
      ExecStart = ''
        ${pkgs.owntracks-recorder}/bin/ot-recorder \
           --storage /var/lib/owntracks/recorder/store \
           --port 0
        '';
      DynamicUser = true;
      StateDirectory = "owntracks";
      Restart = "always";
    };
    wantedBy = [ "multi-user.target" ];
  };

Running sudo nixos-rebuild switch will start the Recorder service. This can be verified by visiting the HTTP endpoint in a browser. Within my Tailnet my server is reachable at wimpy.bleak-moth.ts.net. Since the Recorder service is using its default port, I can confirm it is running and available from my Tailscale connected laptop or phone by visiting http://wimpy.bleak-moth.ts.net:8083. There I will see the minimal web page the server provides:

OwnTracks Recorder web page

Setting Up the Mobile App

The OwnTracks mobile app can be installed from the Google or Apple app stores. It needs to be configured to send updates to the recorder by setting the Mode and Endpoint URL values in the app's Preferences -> Connection menu. The mode is set to HTTP since I'm not using MQTT. The endpoint URL is set to http://wimpy.bleak-moth.ts.net:8083/pub. This is my server's FQDN plus the path for publishing updates. I was stuck for awhile due to not knowing the /pub path was required.

OwnTracks app connection
settings

As seen above, the Connection settings also include Identification and Credentials sections. Since the VPN prevents anyone else from accessing the server, I am not setting up any sort of access control in the OwnTracks system. Thus the credentials do not need to be set, but it is worth setting a user name so that the stored location history will be recorded under that name. This name will then appear on the maps. Likewise, the values for Device ID and Tracker ID.

With the Connection settings configured, the mobile app should start sending location updates to the server. Updates can be manually pushed by clicking on the upload button in the map header of the app's main view.

OwnTracks app main map view

I'm able to confirm the server is receiving location updates by checking the logs for the OwnTracks Recorder service. Notice the POST requests to the /pub endpoint. These correspond to pushing the upload button in the app on my phone.

OwnTracks Recorder logs

I can also check that locations have been uploaded by opening the location table link from the server's main web page.

OwnTracks Recorder Location
Table

For troubleshooting purposes, the mobile app provides a Status menu with useful information including logs. I found it helpful in getting the connection established. I think the logs were what clued me into the /pub path.

Setting Up the Frontend

Once I had my location updates being recorded, it was time to set up the frontend so that I could view the history. The OwnTracks Frontend is a single page application that can be served from any webserver. Since I already had Nginx running and serving static pages out of /var/www/html/, it was pretty easy.

The README offers instructions to either use Docker or build the app manually, but I just download the zip archive from the repo's releases page. Inside, in typical NPM package fashion, is a dist directory. I copied the contents to /var/www/html/owntracks.

The app is configured by copying the config/config.example.js file to config/config.js and making edits there. For this setup I needed to set two values. The api.baseURL for the OwnTracks Recorder's API is set to http://wimpy.bleak-moth.ts.net:8083. And router.basePath is set to owntracks since I'm hosting the app under that path. If router.basePath is not set correctly, the app will load but not display the map.

// Here you can overwite the default configuration values
window.owntracks = window.owntracks || {};
window.owntracks.config = {
    api: {
        baseUrl: "http://wimpy.bleak-moth.ts.net:8083"
    },
    router: {
        basePath: "owntracks"
    }
};

The working frontend looks like the image below and provides options to view location history for past date-time ranges and with options for points, lines, and a heatmap. Here I have zoomed the map way out to avoid advertising my exact location to the Internet.

OwnTracks Frontend

My use case mostly involves viewing location histories for specific days. At first I was confused by the UI for selecting dates. The trick is that you have to select the date range by clicking on the start day in the calendar and then the end day. If you want just a single day, you have to click on that day twice.

OwnTracks Frontend date picker

Conclusion

At this point I have a setup which seems to be working well enough for my purposes. I've been using it for a few weeks, and I'm able to look back over my history to see where I was at particular times. There are a couple of things I would still like to do. First, it seems like I should be able to write an ad-hoc Nix derivation for the OwnTracks Frontend so that I can include it declaratively in my NixOS configuration. This would be preferable to manually downloading and copying the distribution. Secondly, I'd like my data to be backed up. I think this should be as simple as setting up Borg and pointing it at the OwnTracks Recorder's state directory.