Post

Geopulse - Selfhosted Alternative To Google Timeline - Big Updates

Geopulse - Selfhosted Alternative To Google Timeline - Big Updates

Geopulse - Selfhosted Alternative To Google Timeline - Big Updates

Introduction

In today’s privacy‑conscious era, many homelab enthusiasts and DevOps practitioners are looking for alternatives to cloud‑centric location‑tracking services. The recent surge of interest in self‑hosted solutions has turned tools like GeoPulse into essential components of a privacy‑first infrastructure. If you have ever felt uneasy about entrusting Google with a detailed timeline of your movements, you are not alone. This guide walks you through the latest developments in GeoPulse, explains why it matters for a modern self‑hosted stack, and provides a step‑by‑step walkthrough of installation, configuration, and operational best practices.

By the end of this post you will understand:

  • The core purpose of GeoPulse and how it differs from commercial timeline services.
  • The evolution of the project from its early versions to the current v1.33.0 release.
  • The prerequisites required to run GeoPulse in a homelab environment.
  • Detailed installation and configuration procedures, including Docker‑specific commands that respect Jekyll templating constraints.
  • Strategies for security hardening, performance tuning, and long‑term maintenance.
  • Common troubleshooting techniques and where to find reliable external resources.

Whether you are managing a personal homelab, a small office network, or a larger edge‑computing deployment, the information below will help you evaluate GeoPulse as a viable, open‑source alternative to Google Timeline and integrate it smoothly into your existing infrastructure.


Understanding the Topic

What is GeoPulse?

GeoPulse is an open‑source, self‑hosted service that ingests GPS‑derived location data, normalizes it, and stores it in a time‑series database for later querying. Unlike Google Timeline, which aggregates location history on Google’s servers, GeoPulse keeps all data under your control, offering full transparency and compliance with data‑privacy regulations.

Key functional blocks include:

  • Data ingestion – Accepts JSON or CSV payloads from mobile clients, IoT gateways, or custom scripts.
  • Time‑series storage – Uses SQLite or PostgreSQL (configurable) to maintain a chronological record of points.
  • Query API – Provides RESTful endpoints for retrieving filtered location histories, supporting pagination and sorting.
  • Visualization – Optional web UI that renders maps and timelines using OpenStreetMap tiles.

Historical Development

The project began as a hobby experiment in early 2022, initially released as v1.0 with a single‑file Docker image. Since then, the maintainer community has contributed over 250 commits, pushing the version number to v1.33.0. Each release introduced refinements such as:

  • Support for multi‑arch Docker images (amd64, arm64).
  • Integration with Prometheus metrics for health monitoring.
  • Enhanced CSV import capabilities.
  • Introduction of a migration layer for SQLite‑to‑PostgreSQL upgrades.

The GitHub repository now sits at nearly 1,000 stars, reflecting growing adoption in the self‑hosted ecosystem.

Core Features and Capabilities

FeatureDescriptionBenefit
Privacy‑first designAll data stored on‑premises; no external telemetry.Eliminates third‑party data exposure.
Multi‑backend storageConfigurable SQLite (default) or PostgreSQL for larger deployments.Scales from homelab to small‑business use cases.
OpenAPI‑compatible APIREST endpoints follow OpenAPI 3.0 spec.Easy integration with third‑party clients.
Docker‑native deploymentOfficial images tagged with semantic versions.Simplifies CI/CD pipelines and version control.
Prometheus exporterExposes /metrics for scraping.Enables proactive monitoring and alerting.
Web UI (optional)Minimalist frontend built with React and Leaflet.Provides quick visual inspection of location history.

Pros and Cons

Pros

  • Full control over data residency and retention policies.
  • Transparent codebase; community‑driven development.
  • Lightweight Docker image (< 150 MB) suitable for resource‑constrained devices.
  • Active release cadence with backward‑compatible version bumps.

Cons

  • No built‑in end‑to‑end encryption for data in transit (requires TLS termination at the reverse proxy).
  • UI is functional but not as feature‑rich as commercial alternatives.
  • Requires manual setup of reverse proxy and TLS certificates for external access.

Use Cases and Scenarios

  • Personal homelab – Track family members’ commutes without relying on Google services.
  • Edge IoT deployments – Collect location data from fleet vehicles or delivery drones.
  • Privacy‑focused research – Analyze movement patterns while complying with GDPR or CCPA.
  • Educational labs – Demonstrate time‑series databases and API design in a controlled environment.

Comparison to Alternatives

SolutionHosting ModelPrimary LanguageDatabaseNotable Limitation
GeoPulseSelf‑hosted (Docker)GoSQLite / PostgreSQLLimited UI polish
OpenTracksSelf‑hosted (Java)JavaSQLiteNo built‑in API
Strava API (self‑hosted)Self‑hosted (Node)Node.jsMySQLPrimarily sports‑focused
Google Timeline (cloud)Cloud‑onlyProprietaryData stored on Google servers

GeoPulse stands out for its balance of simplicity, configurability, and community support, making it a strong candidate for anyone seeking a privacy‑centric timeline service.


Prerequisites

System Requirements

ComponentMinimum SpecificationRecommended Specification
CPU1 vCPU2 vCPUs
RAM1 GB2 GB
Storage2 GB SSD10 GB SSD (for long‑term history)
OSLinux (Ubuntu 22.04, Debian 12)Any modern 64‑bit Linux distribution
NetworkOutbound internet access for Docker pullsStatic public IP or dynamic DNS for external access

Required Software

  • Docker Engine ≥ 24.0 (Community Edition)
  • Docker Compose ≥ 2.20 (optional, but recommended)
  • curl and jq for API testing
  • nginx or caddy as a reverse proxy (for TLS termination)
  • certbot or equivalent for automated TLS certificate issuance

Network and Security Considerations

  • Port exposure – The GeoPulse container listens on port 8080 internally; external traffic must be routed through a reverse proxy.
  • Firewall rules – Allow inbound traffic only on ports 80 and 443 for HTTP/HTTPS, and restrict access to management ports (e.g., 2375) to trusted hosts.
  • User permissions – Run the container under a non‑root user ($CONTAINER_USER) to limit privilege escalation.

Pre‑Installation Checklist

  1. Verify Docker Engine is up‑to‑date (docker version).
  2. Create a dedicated system user for running containers (adduser --system --group geopulse).
  3. Reserve a directory for persistent data (mkdir -p /opt/geopulse/data).
  4. Generate TLS certificates for your domain (certbot certonly --standalone -d geopulse.example.com).
  5. Ensure DNS A record points to your server’s public IP.

Installation & Setup

Pulling the Official Image

The maintainer publishes multi‑arch images tagged with the semantic version. The following command fetches the latest stable release (v1.33.0) and stores it in a local repository:

1
docker pull ghcr.io/geopulse/geopulse:$CONTAINER_IMAGE
  • $CONTAINER_IMAGE resolves to v1.33.0 at runtime, ensuring you always pull the intended version.

Docker Run Command

Below is a production‑ready docker run invocation that incorporates best practices for restart policy, resource limits, and environment variable configuration:

1
2
3
4
5
6
7
8
9
10
11
docker run -d \
  --name $CONTAINER_NAME \
  --restart unless-stopped \
  --cpus="1.5" \
  --memory="1g" \
  -p 8080:8080 \
  -e DATABASE_URL="postgres://geopulse:password@postgres:5432/geopulse?sslmode=disable" \
  -e ENABLE_UI=true \
  -e LOG_LEVEL=info \
  -v /opt/geopulse/data:/app/data \
  $CONTAINER_IMAGE

Explanation of key flags:

  • --restart unless-stopped – Guarantees automatic recovery after host reboots.
  • --cpus and --memory – Constrain resource usage to prevent contention with other services.
  • -p 8080:8080 – Maps the internal HTTP port to the host’s port 8080; the reverse proxy will handle external routing.
  • DATABASE_URL – Points to a PostgreSQL instance; adjust credentials and hostnames as needed.
  • ENABLE_UI – Enables the optional web UI; set to false for API‑only deployments.
  • -v /opt/geopulse/data:/app/data – Persists location data across container upgrades.

Docker Compose Alternative

For environments that already use Compose, the following docker-compose.yml snippet provides a declarative approach:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
version: "3.9"

services:
  geopulse:
    image: ghcr.io/geopulse/geopulse:$CONTAINER_IMAGE
    container_name: $CONTAINER_NAME
    restart: unless-stopped
    ports:
      - "8080:8080"
    environment:
      DATABASE_URL: "postgres://geopulse:password@postgres:5432/geopulse?sslmode=disable"
      ENABLE_UI: "true"
      LOG_LEVEL: "info"
    volumes:
      - /opt/geopulse/data:/app/data
    deploy:
      resources:
        limits:
          cpus: "1.5"
          memory: "1g"

Replace $CONTAINER_IMAGE, $CONTAINER_NAME, and other placeholders with concrete values before applying.

Initializing the Database

If you opt for PostgreSQL, run the following migration command after the container starts:

1
docker exec -it $CONTAINER_NAME geopulse db migrate --url $DATABASE_URL

The command executes the built‑in migration tool, creating necessary tables and indexes. Subsequent upgrades can be performed with the same command, ensuring schema compatibility across versions.

Verifying the Deployment

  1. Health check – Query the /health endpoint to confirm the service is operational:

    1
    
    curl -s http://localhost:8080/health | jq .
    

    Expected output includes "status":"ok" and a version field matching $CONTAINER_IMAGE.

  2. API test – Retrieve a sample location record (replace with your own auth token if applicable):

    1
    
    curl -H "Authorization: Bearer $API_TOKEN" http://localhost:8080/api/v1/locations?limit=5
    
  3. UI access – Navigate to https://geopulse.example.com/ui (or http://localhost:8080/ui for local testing) to view the interactive map.

If any of these steps fail, consult the container logs (docker logs $CONTAINER_NAME) for detailed error messages.

Common Installation Pitfalls

IssueSymptomResolution
Port conflictbind: address already in useChange host port mapping (-p 8081:8080) and update reverse proxy configuration.
Database connection failureconnection refusedVerify that the PostgreSQL container is reachable (docker network inspect <network>).
Missing environment variablesundefined variable errors in logsDouble‑check .env file or Compose environment section for typos.
Permission denied on data volumepermission denied when mountingEnsure the host directory is owned by the container user (chown -R 1000:1000 /opt/geopulse/data).

Configuration & Optimization

Configuration File Overview

GeoPulse reads a YAML file located at /app/config/config.yaml. Below is a representative example with inline comments:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# /app/config/config.yaml
server:
  port: 8080
  read_timeout: 30s
  write_timeout: 30s

database:
  type: postgres
  url: "postgres://geopulse:password@postgres:5432/geopulse?sslmode=disable"

auth:
  jwt_secret: "a-very-long-random-string"
  token_expiration: "24h"

storage:
  path: "/app/data"
  max_file_size_mb: 250

ui:
  enable: true
  map_style: "osm-bright"

logging:
  level: "info"
  output: "stdout"
  format: "json"

Key sections explained:

  • server – Controls the HTTP server’s binding and timeout behavior. Adjust read_timeout for large payloads.
  • database – Determines the backend; switch to SQLite by setting type: sqlite and providing a path.
  • auth – Configures JWT signing; use a strong secret generated via openssl rand -hex 32.
  • storage – Governs file‑based limits; increasing max_file_size_mb allows longer retention periods.
  • ui – Toggles the optional web UI and selects a map style.
  • logging – Switches between human‑readable and JSON formats; JSON is preferred for log aggregation tools.

Security Hardening

  1. TLS Termination – Terminate TLS at the reverse proxy (nginx or caddy) and forward only plain HTTP to the container. Example nginx snippet:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    
    server {
        listen 443 ssl;
        server_name geopulse.example.com;
    
        ssl_certificate /etc/letsencrypt/live/geopulse.example.com/fullchain.pem;
        ssl_certificate_key /etc/letsencrypt/live/geopulse.example.com/privkey.pem;
    
        location / {
            proxy_pass http://127.0.0.1:8080;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;
        }
    }
    
  2. Rate Limiting – Apply per‑IP request caps to mitigate abuse:

    1
    2
    3
    
    limit_req_zone $binary_remote_addr zone=geopulse_limit:10m rate=30r/m;
    location /api/ {
        limit_req zone=geopulse_limit burst=
    
This post is licensed under CC BY 4.0 by the author.