Historique de localisation auto-hébergé, alternative à Google Timeline. Le géocodage inverse (adresse et nom des lieux) est assuré par une instance Photon locale, sans service externe ni limite de requêtes.
Architecture
| Conteneur | Rôle |
|---|---|
| dawarich_app | Application web (Rails) |
| dawarich_sidekiq | Tâches de fond : imports, détection des visites, géocodage |
| dawarich_db | PostgreSQL + PostGIS |
| dawarich_redis | File de tâches |
| photon | Géocodage inverse, stack séparée |
Dawarich et Photon communiquent par le réseau proxy, sur lequel se trouve aussi SWAG.
Variables d'environnement
À définir dans l'onglet « Environment variables » de la stack Portainer :
| Variable | Valeur |
|---|---|
| POSTGRES_USER | Utilisateur de la base |
| POSTGRES_PASSWORD | Mot de passe de la base |
| POSTGRES_DB | Nom de la base |
| APPLICATION_HOSTS | Nom de domaine d'accès |
| SECRET_KEY_BASE | Clé générée avec openssl rand -hex 64 |
Stack Dawarich
networks:
dawarich:
proxy:
external: true
services:
dawarich_redis:
image: redis:7.4-alpine
container_name: dawarich_redis
command: >
redis-server
--save 900 1
--save 300 10
--appendonly no
networks:
- dawarich
volumes:
- dawarich_shared:/data
restart: always
healthcheck:
test: [ "CMD", "redis-cli", "--raw", "incr", "ping" ]
interval: 10s
retries: 5
start_period: 30s
timeout: 10s
dawarich_db:
image: postgis/postgis:17-3.5-alpine
shm_size: 1G
container_name: dawarich_db
volumes:
- dawarich_db_data:/var/lib/postgresql/data
- dawarich_shared:/var/shared
networks:
- dawarich
environment:
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_DB: ${POSTGRES_DB}
restart: always
healthcheck:
test: [ "CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}" ]
interval: 10s
retries: 5
start_period: 30s
timeout: 10s
dawarich_app:
image: freikin/dawarich:latest
container_name: dawarich_app
volumes:
- dawarich_public:/var/app/public
- dawarich_watched:/var/app/tmp/imports/watched
- dawarich_storage:/var/app/storage
- dawarich_db_data:/dawarich_db_data
networks:
- dawarich
- proxy
ports:
- "3001:3000"
stdin_open: true
tty: true
entrypoint: web-entrypoint.sh
command: ['bin/rails', 'server', '-p', '3000', '-b', '::']
restart: unless-stopped
environment:
RAILS_ENV: production
REDIS_URL: redis://dawarich_redis:6379
DATABASE_HOST: dawarich_db
DATABASE_PORT: 5432
DATABASE_USERNAME: ${POSTGRES_USER}
DATABASE_PASSWORD: ${POSTGRES_PASSWORD}
DATABASE_NAME: ${POSTGRES_DB}
APPLICATION_HOSTS: ${APPLICATION_HOSTS}
TIME_ZONE: Europe/Paris
APPLICATION_PROTOCOL: https
PROMETHEUS_EXPORTER_ENABLED: "false"
SECRET_KEY_BASE: ${SECRET_KEY_BASE}
RAILS_LOG_TO_STDOUT: "true"
SELF_HOSTED: "true"
STORE_GEODATA: "true"
PHOTON_API_HOST: photon:2322
PHOTON_API_USE_HTTPS: "false"
logging:
driver: "json-file"
options:
max-size: 100m
max-file: "5"
healthcheck:
test: [ "CMD-SHELL", "wget -qO - --header='X-Forwarded-Proto: https' http://127.0.0.1:3000/api/v1/health | grep -q '\"status\"\\s*:\\s*\"ok\"'" ]
interval: 10s
retries: 30
start_period: 30s
timeout: 10s
depends_on:
dawarich_db:
condition: service_healthy
restart: true
dawarich_redis:
condition: service_healthy
restart: true
deploy:
resources:
limits:
cpus: "0.50"
memory: 4G
dawarich_sidekiq:
image: freikin/dawarich:latest
container_name: dawarich_sidekiq
volumes:
- dawarich_public:/var/app/public
- dawarich_watched:/var/app/tmp/imports/watched
- dawarich_storage:/var/app/storage
networks:
- dawarich
- proxy
stdin_open: true
tty: true
entrypoint: sidekiq-entrypoint.sh
command: ['sidekiq']
restart: on-failure
environment:
RAILS_ENV: production
REDIS_URL: redis://dawarich_redis:6379
DATABASE_HOST: dawarich_db
DATABASE_PORT: 5432
DATABASE_USERNAME: ${POSTGRES_USER}
DATABASE_PASSWORD: ${POSTGRES_PASSWORD}
DATABASE_NAME: ${POSTGRES_DB}
APPLICATION_HOSTS: ${APPLICATION_HOSTS}
BACKGROUND_PROCESSING_CONCURRENCY: "5"
APPLICATION_PROTOCOL: https
PROMETHEUS_EXPORTER_ENABLED: "false"
SECRET_KEY_BASE: ${SECRET_KEY_BASE}
RAILS_LOG_TO_STDOUT: "true"
SELF_HOSTED: "true"
STORE_GEODATA: "true"
PHOTON_API_HOST: photon:2322
PHOTON_API_USE_HTTPS: "false"
logging:
driver: "json-file"
options:
max-size: 100m
max-file: "5"
healthcheck:
test: [ "CMD-SHELL", "pgrep -f sidekiq" ]
interval: 10s
retries: 30
start_period: 30s
timeout: 10s
depends_on:
dawarich_db:
condition: service_healthy
restart: true
dawarich_redis:
condition: service_healthy
restart: true
dawarich_app:
condition: service_healthy
restart: true
volumes:
dawarich_db_data:
dawarich_shared:
dawarich_public:
dawarich_watched:
dawarich_storage:
Points particuliers :
- Port hôte 3001 : le 3000 est déjà pris sur le serveur.
- L'en-tête X-Forwarded-Proto: https du healthcheck est indispensable avec APPLICATION_PROTOCOL: https, sinon sidekiq ne démarre jamais (voir Dépannage).
- PHOTON_API_HOST et PHOTON_API_USE_HTTPS doivent être présents sur app et sidekiq.
Stack Photon
networks:
proxy:
external: true
services:
photon:
image: rtuszik/photon-docker:latest
container_name: photon
environment:
REGION: europe
UPDATE_STRATEGY: SEQUENTIAL
UPDATE_INTERVAL: 720h
LOG_LEVEL: INFO
volumes:
- photon_data:/photon/data
networks:
- proxy
ports:
- "2322:2322"
restart: unless-stopped
volumes:
photon_data:
- Région europe : environ 29 Go à télécharger, 47 Go une fois extraits. Compter une heure avant que l'API réponde au premier démarrage.
- L'index se met à jour seul tous les 30 jours (UPDATE_INTERVAL).
- Les données Photon ne sont pas sauvegardées : elles se retéléchargent.
Accès
Publié par SWAG sur un sous-domaine, avec un proxy-conf écrit à la main (SWAG n'en fournit pas pour Dawarich) : voir l'exemple commenté sur la page Reverse proxy.
Pas d'Authelia : l'accès est protégé par l'identifiant de Dawarich. Les chemins /api (applications mobiles de suivi) et /cable (mises à jour en direct) ont chacun leur bloc location.
Import de l'historique
- Source fiable : les fichiers JSON exportés directement depuis l'application Google Maps du téléphone.
- L'archive Google Takeout ne contient plus la Timeline : seulement des métadonnées de photos, qui créent un point par photo. Ne pas l'importer.
Console Rails
Utile pour les opérations en masse ; exemples dans Dépannage.
Lieux et tags
- Un tag s'applique à un lieu, jamais à un point ni à une zone.
- Le rayon d'un tag est un rayon de confidentialité : les points qu'il contient sont masqués dans les exports et les vues publiques.
Sauvegarde
Les données sont dans des volumes Docker nommés, hors du dossier sauvegardé. Seul le dump SQL de dawarich_db les protège : il doit figurer dans la boucle PostgreSQL du script de Sauvegarde.