---
title: "Autogestionar HitKeep con Docker Compose | HitKeep"
description: "Autogestiona HitKeep con Docker Compose, almacenamiento persistente, redes preconfiguradas y ejemplos de proxy inverso para Caddy, nginx y Traefik."
canonical: "https://hitkeep.com/es/guides/installation/docker-compose/"
---

# Autogestionar HitKeep con Docker Compose

Docker Compose permite un despliegue reproducible y controlado por versiones con volúmenes de almacenamiento persistente. Tus datos de analítica permanecen en un volumen Docker con nombre dentro de tu servidor, incluidos el archivo compartido `hitkeep.db` y las bases de datos locales de cada tenant creadas bajo `tenants/*/hitkeep.db`.

Esta página explica cómo ejecutar HitKeep como servicio autogestionado. Si buscas el entorno para colaboradores con recarga en caliente, Go, Air, Angular, Mailpit y datos de demostración en Docker, consulta la [guía para contribuir](https://hitkeep.com/guides/contributing/).

En cada versión, las imágenes de HitKeep se publican en dos registros:

| Registro | Imagen |
| --- | --- |
| Docker Hub | pascalebeier/hitkeep |
| GitHub Container Registry | ghcr.io/pascalebeier/hitkeep |

Ambos registros contienen imágenes multiplataforma idénticas (`linux/amd64`, `linux/arm64`) con declaraciones de procedencia firmadas. Usa el registro que mejor se adapte a tu red o a tus límites de descargas.

No guardes secretos en los archivos de Compose

Pasa los valores sensibles (`HITKEEP_JWT_SECRET`, contraseñas SMTP) mediante variables de entorno, un archivo `.env` o secretos de Docker, no incrustados en `compose.yml` ni en flags de comandos. Excluye `.env` del control de versiones. Para una gestión más estricta, usa secretos de Docker.

## Inicio rápido

Un despliegue consta de dos archivos en el mismo directorio: `compose.yml` (cópialo literalmente; no tendrás que editarlo) y `.env` (aquí defines tus valores). Al iniciar, Compose sustituye cada `${VARIABLE}` de `compose.yml` por la línea correspondiente de `.env`.

### 1. Crear compose.yml

Crea un directorio y añade este `compose.yml`. Esta configuración base conserva los datos activos, los archivos de retención y las instantáneas automáticas de respaldo en volúmenes con nombres separados. El mismo archivo se mantiene en el repositorio como [`examples/compose.yml`](https://github.com/PascaleBeier/hitkeep/blob/main/examples/compose.yml), junto con [`examples/.env.example`](https://github.com/PascaleBeier/hitkeep/blob/main/examples/.env.example), por si prefieres copiarlo desde el código fuente.

```
services:
  hitkeep:
    image: pascalebeier/hitkeep:latest
    container_name: hitkeep
    restart: unless-stopped
    ports:
      - "8080:8080"
    volumes:
      - hitkeep_data:/var/lib/hitkeep/data
      - hitkeep_archive:/var/lib/hitkeep/archive
      - hitkeep_backups:/var/lib/hitkeep/backups
    environment:
      # Public URL must match the browser-visible origin, including any path prefix.
      HITKEEP_PUBLIC_URL: ${HITKEEP_PUBLIC_URL:-http://localhost:8080}
      # Required for stable sessions. Generate with: openssl rand -hex 32
      HITKEEP_JWT_SECRET: ${HITKEEP_JWT_SECRET:?set HITKEEP_JWT_SECRET in .env}
      # Behind a reverse proxy, set to the proxy network CIDR so real client IPs are used.
      HITKEEP_TRUSTED_PROXIES: ${HITKEEP_TRUSTED_PROXIES:-} # config-default-override: require an explicit trusted proxy network
      # Keep live data, retention archives, and backup snapshots on persistent volumes.
      HITKEEP_DB_PATH: /var/lib/hitkeep/data/hitkeep.db
      HITKEEP_DATA_PATH: /var/lib/hitkeep/data
      HITKEEP_ARCHIVE_PATH: /var/lib/hitkeep/archive
      HITKEEP_BACKUP_PATH: /var/lib/hitkeep/backups
      HITKEEP_BACKUP_INTERVAL: ${HITKEEP_BACKUP_INTERVAL:-60}
      HITKEEP_BACKUP_RETENTION: ${HITKEEP_BACKUP_RETENTION:-24}
      # Optional OSS spam-list refresh. Disable for fully offline/air-gapped installs.
      HITKEEP_SPAM_FILTER_AUTO_UPDATE: "true"
      HITKEEP_SPAM_FILTER_UPDATE_INTERVAL: ${HITKEEP_SPAM_FILTER_UPDATE_INTERVAL:-1440}
      HITKEEP_SPAM_FILTER_PATH: /var/lib/hitkeep/data/spam-filter.json
      # Optional read-only MCP endpoint for governed assistant/reporting access.
      HITKEEP_MCP_ENABLED: "true"
      HITKEEP_MCP_PATH: /mcp
      HITKEEP_MCP_MAX_RANGE_DAYS: ${HITKEEP_MCP_MAX_RANGE_DAYS:-366}
      # Optional AI model route for Opportunity enrichment. Provider credentials
      # use the selected goAI provider's own env vars, such as OPENAI_API_KEY.
      HITKEEP_AI_ENABLED: ${HITKEEP_AI_ENABLED:-false}
      HITKEEP_AI_PROVIDER: ${HITKEEP_AI_PROVIDER:-}
      HITKEEP_AI_MODEL: ${HITKEEP_AI_MODEL:-}
      HITKEEP_AI_BASE_URL: ${HITKEEP_AI_BASE_URL:-}
      HITKEEP_AI_REGION: ${HITKEEP_AI_REGION:-}
      HITKEEP_AI_API_KEY: ${HITKEEP_AI_API_KEY:-}
      HITKEEP_AI_REQUEST_LIMIT: ${HITKEEP_AI_REQUEST_LIMIT:-100}
      HITKEEP_AI_TOKEN_LIMIT: ${HITKEEP_AI_TOKEN_LIMIT:-100000}
      HITKEEP_AI_BUDGET_WINDOW: ${HITKEEP_AI_BUDGET_WINDOW:-1440}
      # Optional Google Search Console OAuth integration.
      HITKEEP_GOOGLE_SEARCH_CONSOLE_CLIENT_ID: ${HITKEEP_GOOGLE_SEARCH_CONSOLE_CLIENT_ID:-}
      HITKEEP_GOOGLE_SEARCH_CONSOLE_CLIENT_SECRET: ${HITKEEP_GOOGLE_SEARCH_CONSOLE_CLIENT_SECRET:-}
      HITKEEP_GOOGLE_SEARCH_CONSOLE_REDIRECT_URL: ${HITKEEP_GOOGLE_SEARCH_CONSOLE_REDIRECT_URL:-}
      # Optional Google, GitHub, and Microsoft social sign-in.
      HITKEEP_SOCIAL_GOOGLE_CLIENT_ID: ${HITKEEP_SOCIAL_GOOGLE_CLIENT_ID:-}
      HITKEEP_SOCIAL_GOOGLE_CLIENT_SECRET: ${HITKEEP_SOCIAL_GOOGLE_CLIENT_SECRET:-}
      HITKEEP_SOCIAL_GITHUB_CLIENT_ID: ${HITKEEP_SOCIAL_GITHUB_CLIENT_ID:-}
      HITKEEP_SOCIAL_GITHUB_CLIENT_SECRET: ${HITKEEP_SOCIAL_GITHUB_CLIENT_SECRET:-}
      HITKEEP_SOCIAL_MICROSOFT_CLIENT_ID: ${HITKEEP_SOCIAL_MICROSOFT_CLIENT_ID:-}
      HITKEEP_SOCIAL_MICROSOFT_CLIENT_SECRET: ${HITKEEP_SOCIAL_MICROSOFT_CLIENT_SECRET:-}
      HITKEEP_SOCIAL_MICROSOFT_TENANT: ${HITKEEP_SOCIAL_MICROSOFT_TENANT:-common}
      # SMTP powers invites, password reset, email reports, and security mail.
      HITKEEP_MAIL_HOST: ${HITKEEP_MAIL_HOST:-}
      HITKEEP_MAIL_PORT: ${HITKEEP_MAIL_PORT:-587}
      HITKEEP_MAIL_USERNAME: ${HITKEEP_MAIL_USERNAME:-}
      HITKEEP_MAIL_PASSWORD: ${HITKEEP_MAIL_PASSWORD:-}
      HITKEEP_MAIL_ENCRYPTION: ${HITKEEP_MAIL_ENCRYPTION:-tls}
      HITKEEP_MAIL_FROM_ADDRESS: ${HITKEEP_MAIL_FROM_ADDRESS:-hitkeep@localhost}
      HITKEEP_MAIL_FROM_NAME: ${HITKEEP_MAIL_FROM_NAME:-HitKeep}

volumes:
  hitkeep_data: {}
  hitkeep_archive: {}
  hitkeep_backups: {}
```

### 2. Crear .env

Genera primero la clave de firma de sesiones. Ejecuta este comando en la terminal y copia el resultado:

```
openssl rand -hex 32
```

Si no tienes `openssl`, `head -c 32 /dev/urandom | od -An -tx1 | tr -d ' \n'; echo` produce un valor equivalente en cualquier máquina Linux.

Crea después un archivo llamado `.env` junto a `compose.yml`, ábrelo con tu editor y completa tus valores:

```
# Required. Paste the generated 64-character hex string here.
HITKEEP_JWT_SECRET=paste-the-generated-value-here
# Required. The exact URL you open in the browser. Behind a reverse proxy
# this is your public domain, e.g. https://analytics.example.com
HITKEEP_PUBLIC_URL=http://localhost:8080
# Optional social sign-in. Set both values for each provider you enable.
HITKEEP_SOCIAL_GOOGLE_CLIENT_ID=
HITKEEP_SOCIAL_GOOGLE_CLIENT_SECRET=
HITKEEP_SOCIAL_GITHUB_CLIENT_ID=
HITKEEP_SOCIAL_GITHUB_CLIENT_SECRET=
HITKEEP_SOCIAL_MICROSOFT_CLIENT_ID=
HITKEEP_SOCIAL_MICROSOFT_CLIENT_SECRET=
HITKEEP_SOCIAL_MICROSOFT_TENANT=common
# Optional SMTP. Powers invites, password resets, and email reports.
# Leave the values empty to run without outbound mail.
HITKEEP_MAIL_HOST=
HITKEEP_MAIL_PORT=587
HITKEEP_MAIL_USERNAME=
HITKEEP_MAIL_PASSWORD=
HITKEEP_MAIL_FROM_ADDRESS=hitkeep@localhost
HITKEEP_MAIL_FROM_NAME=HitKeep
```

.env no es un script del shell

`.env` contiene líneas sencillas con el formato `KEY=value`: sin `echo`, sin llaves y sin sustituciones `$(command)`. Docker Compose lo lee literalmente, por lo que cualquier comando pegado en el archivo se convierte en texto literal. No es necesario entrecomillar los valores. Excluye el archivo del control de versiones añadiéndolo a `.gitignore`.

Los dos archivos usan sintaxis distintas de forma intencionada: `compose.yml` es YAML, así que sus entradas de entorno tienen el formato `KEY: value`; `.env` usa el formato dotenv, `KEY=value`.

### 3. Iniciar HitKeep

```
docker compose up -d
docker compose logs -f hitkeep
```

Las líneas de registro `Starting HitKeep` y `HTTP server starting` indican que el servicio está activo.

### 4. Iniciar sesión

Abre `HITKEEP_PUBLIC_URL` en el navegador. El asistente crea la primera cuenta, que se convierte automáticamente en propietaria de la instancia, y te guía para añadir tu primer sitio.

El archivo de base de datos reside en el volumen `hitkeep_data`. Por tanto, el directorio de recuperación automática predeterminado, `/var/lib/hitkeep/data/recovery`, también es persistente. Los archivos de retención se guardan en `hitkeep_archive` y las instantáneas automáticas de la base de datos en `hitkeep_backups`. Los paquetes de recuperación contienen material de la base de datos, no se rotan con la retención de copias y deben protegerse y eliminarse por separado. MCP se publica en `/mcp`, pero los clientes siguen necesitando tokens bearer de clientes API con el alcance adecuado.

MCP es opcional en el producto y está activado en este ejemplo de Compose para equipos que quieran acceso de solo lectura desde asistentes o herramientas de informes. Elimina `HITKEEP_MCP_ENABLED`, `HITKEEP_MCP_PATH` y `HITKEEP_MCP_MAX_RANGE_DAYS` si no vas a publicar un endpoint MCP.

El enriquecimiento mediante proveedores de IA es opcional y está desactivado de forma predeterminada. Mantén `HITKEEP_AI_ENABLED=false` hasta elegir un proveedor y un modelo, configurar las credenciales y definir límites de presupuesto locales. Consulta [Recomendaciones de oportunidades](https://hitkeep.com/guides/analytics/opportunities/) y la [guía de configuración de modelos de IA](https://hitkeep.com/guides/admin/ai-model-configuration/) para conocer los campos exactos.

El inicio de sesión social es opcional. Define el ID y el secreto de cliente de cada proveedor que quieras mostrar en HitKeep y registra después la URL de callback exacta derivada de `HITKEEP_PUBLIC_URL`. La [guía de inicio de sesión social](https://hitkeep.com/guides/security/social-sign-in/) explica el registro de proveedores, las rutas de callback, la verificación del correo, las invitaciones, MFA y la vinculación de cuentas. `HITKEEP_SOCIAL_SIGNUP_ENABLED` no habilita el registro público en una instancia autogestionada.

Los datos de consulta de ciudad, proveedor y ASN están integrados en las imágenes de cada versión. Los despliegues con Compose no necesitan `IP2LOCATION_DOWNLOAD_TOKEN` durante la ejecución.

## Verificación

Confirma que Compose muestra un servicio en ejecución y que ambos endpoints responden correctamente:

```
docker compose ps
curl --fail http://localhost:8080/healthz
curl --fail http://localhost:8080/readyz
docker compose logs --since=5m hitkeep
```

`/healthz` confirma que el proceso está activo. `/readyz` confirma que la base de datos compartida y todas las bases de datos de tenants abiertas están listas. Si HitKeep solo es accesible a través del proxy inverso, comprueba en su lugar la URL visible en el navegador.

## Actualización

Lee las [notas de la última versión](https://github.com/PascaleBeier/hitkeep/releases/latest) y confirma que existe una copia de seguridad actual. Descarga después la imagen estable vigente y vuelve a crear únicamente el servicio HitKeep:

```
docker compose pull hitkeep
docker compose up -d hitkeep
docker compose ps
curl --fail http://localhost:8080/readyz
```

Compose conserva los volúmenes con nombre para datos, archivos y copias cuando reemplaza el contenedor. No ejecutes `docker compose down -v` durante una actualización: `-v` elimina esos volúmenes.

## Configuraciones de proxy inverso

Ejecuta HitKeep detrás de un proxy inverso para ofrecer HTTPS en producción. Configura los [proxies de confianza](https://hitkeep.com/guides/installation/trusted-proxies/) para usar las IP reales de los clientes en la analítica y los límites de solicitudes.

`HITKEEP_PUBLIC_URL` puede incluir un prefijo de ruta. Por ejemplo, define `HITKEEP_PUBLIC_URL=https://www.example.net/hitkeep/` si HitKeep se publica debajo de un sitio existente. El proxy debe exponer el mismo prefijo, como `/hitkeep/*`, y reenviarlo al contenedor de HitKeep. HitKeep servirá entonces bajo ese prefijo el `base href` del panel, las solicitudes de API, `hk.js`, `hk-vitals.js` y los endpoints de ingestión.

- Caddy
- nginx
- Traefik

`caddy-docker-proxy` gestiona HTTPS automático (Let’s Encrypt) y genera la configuración de Caddy directamente a partir de etiquetas de Docker. La práctica recomendada es usar una red de entrada específica y confiar únicamente en el CIDR de esa red en HitKeep.

```
services:
caddy:
  image: lucaslorentz/caddy-docker-proxy:2.9-alpine
  container_name: caddy-proxy
  restart: unless-stopped
  ports:
    - "80:80"
    - "443:443"
    - "443:443/udp"
  environment:
    CADDY_INGRESS_NETWORKS: caddy
  volumes:
    - /var/run/docker.sock:/var/run/docker.sock:ro
    - caddy_data:/data
    - caddy_config:/config
  networks:
    - caddy
  labels:
    caddy.email: ${CADDY_EMAIL:?set in .env}

hitkeep:
  image: pascalebeier/hitkeep:latest
  container_name: hitkeep
  restart: unless-stopped
  networks:
    - caddy
  volumes:
    - hitkeep_data:/var/lib/hitkeep/data
    - hitkeep_archive:/var/lib/hitkeep/archive
    - hitkeep_backups:/var/lib/hitkeep/backups
  environment:
    HITKEEP_NODE_NAME: leader
    # Public URL must match the HTTPS origin served by Caddy, including any path prefix.
    HITKEEP_PUBLIC_URL: ${HITKEEP_PUBLIC_URL:?set in .env}
    # Required for stable sessions. Generate with: openssl rand -hex 32
    HITKEEP_JWT_SECRET: ${HITKEEP_JWT_SECRET:?set in .env}
    # Must be the Caddy network CIDR. This controls real visitor IP, geo/network metadata,
    # country exclusions, rate limiting, and spam checks.
    HITKEEP_TRUSTED_PROXIES: ${HITKEEP_TRUSTED_PROXIES:?set to your caddy network CIDR}
    # Keep live data, retention archives, and backup snapshots on persistent volumes.
    HITKEEP_DB_PATH: /var/lib/hitkeep/data/hitkeep.db
    HITKEEP_DATA_PATH: /var/lib/hitkeep/data
    HITKEEP_ARCHIVE_PATH: /var/lib/hitkeep/archive
    HITKEEP_BACKUP_PATH: /var/lib/hitkeep/backups
    HITKEEP_BACKUP_INTERVAL: ${HITKEEP_BACKUP_INTERVAL:-60}
    HITKEEP_BACKUP_RETENTION: ${HITKEEP_BACKUP_RETENTION:-24}
    # Optional OSS spam-list refresh. Disable for fully offline/air-gapped installs.
    HITKEEP_SPAM_FILTER_AUTO_UPDATE: "true"
    HITKEEP_SPAM_FILTER_UPDATE_INTERVAL: ${HITKEEP_SPAM_FILTER_UPDATE_INTERVAL:-1440}
    HITKEEP_SPAM_FILTER_PATH: /var/lib/hitkeep/data/spam-filter.json
    # Optional read-only MCP endpoint for governed assistant/reporting access.
    HITKEEP_MCP_ENABLED: "true"
    HITKEEP_MCP_PATH: /mcp
    HITKEEP_MCP_MAX_RANGE_DAYS: ${HITKEEP_MCP_MAX_RANGE_DAYS:-366}
    # Optional AI model route for Opportunity enrichment. Leave disabled until a provider is configured.
    HITKEEP_AI_ENABLED: ${HITKEEP_AI_ENABLED:-false}
    HITKEEP_AI_PROVIDER: ${HITKEEP_AI_PROVIDER:-}
    HITKEEP_AI_MODEL: ${HITKEEP_AI_MODEL:-}
    HITKEEP_AI_BASE_URL: ${HITKEEP_AI_BASE_URL:-}
    HITKEEP_AI_REGION: ${HITKEEP_AI_REGION:-}
    HITKEEP_AI_API_KEY: ${HITKEEP_AI_API_KEY:-}
    HITKEEP_AI_REQUEST_LIMIT: ${HITKEEP_AI_REQUEST_LIMIT:-100}
    HITKEEP_AI_TOKEN_LIMIT: ${HITKEEP_AI_TOKEN_LIMIT:-100000}
    HITKEEP_AI_BUDGET_WINDOW: ${HITKEEP_AI_BUDGET_WINDOW:-1440}
    # Optional Google Search Console OAuth integration.
    HITKEEP_GOOGLE_SEARCH_CONSOLE_CLIENT_ID: ${HITKEEP_GOOGLE_SEARCH_CONSOLE_CLIENT_ID:-}
    HITKEEP_GOOGLE_SEARCH_CONSOLE_CLIENT_SECRET: ${HITKEEP_GOOGLE_SEARCH_CONSOLE_CLIENT_SECRET:-}
    HITKEEP_GOOGLE_SEARCH_CONSOLE_REDIRECT_URL: ${HITKEEP_GOOGLE_SEARCH_CONSOLE_REDIRECT_URL:-}
    # Optional Google, GitHub, and Microsoft social sign-in.
    HITKEEP_SOCIAL_GOOGLE_CLIENT_ID: ${HITKEEP_SOCIAL_GOOGLE_CLIENT_ID:-}
    HITKEEP_SOCIAL_GOOGLE_CLIENT_SECRET: ${HITKEEP_SOCIAL_GOOGLE_CLIENT_SECRET:-}
    HITKEEP_SOCIAL_GITHUB_CLIENT_ID: ${HITKEEP_SOCIAL_GITHUB_CLIENT_ID:-}
    HITKEEP_SOCIAL_GITHUB_CLIENT_SECRET: ${HITKEEP_SOCIAL_GITHUB_CLIENT_SECRET:-}
    HITKEEP_SOCIAL_MICROSOFT_CLIENT_ID: ${HITKEEP_SOCIAL_MICROSOFT_CLIENT_ID:-}
    HITKEEP_SOCIAL_MICROSOFT_CLIENT_SECRET: ${HITKEEP_SOCIAL_MICROSOFT_CLIENT_SECRET:-}
    HITKEEP_SOCIAL_MICROSOFT_TENANT: ${HITKEEP_SOCIAL_MICROSOFT_TENANT:-common}
    # SMTP powers invites, password reset, email reports, and security mail.
    HITKEEP_MAIL_HOST: ${HITKEEP_MAIL_HOST:-}
    HITKEEP_MAIL_PORT: ${HITKEEP_MAIL_PORT:-587}
    HITKEEP_MAIL_USERNAME: ${HITKEEP_MAIL_USERNAME:-}
    HITKEEP_MAIL_PASSWORD: ${HITKEEP_MAIL_PASSWORD:-}
    HITKEEP_MAIL_ENCRYPTION: ${HITKEEP_MAIL_ENCRYPTION:-tls}
    HITKEEP_MAIL_FROM_ADDRESS: ${HITKEEP_MAIL_FROM_ADDRESS:-hitkeep@localhost}
    HITKEEP_MAIL_FROM_NAME: ${HITKEEP_MAIL_FROM_NAME:-HitKeep}
  labels:
    caddy: ${HITKEEP_HOSTNAME:?set in .env}
    caddy.reverse_proxy: "{{upstreams 8080}}"
    caddy.encode: "zstd gzip"

volumes:
caddy_data: {}
caddy_config: {}
hitkeep_data: {}
hitkeep_archive: {}
hitkeep_backups: {}

networks:
caddy:
  external: true
```

Hay que obtener dos valores desde la terminal: el secreto de sesión y el CIDR de la red de Caddy:

```
openssl rand -hex 32
docker network inspect caddy --format '{{(index .IPAM.Config 0).Subnet}}'
```

Crea después el archivo `.env` con tu editor:

```
# Required. Paste the generated 64-character hex string here.
HITKEEP_JWT_SECRET=paste-the-generated-value-here
HITKEEP_PUBLIC_URL=https://analytics.example.com
HITKEEP_HOSTNAME=analytics.example.com
CADDY_EMAIL=ops@example.com
# Paste the Caddy network subnet from the docker network inspect command.
HITKEEP_TRUSTED_PROXIES=paste-the-caddy-subnet-here
# Optional social sign-in. Set both values for each provider you enable.
HITKEEP_SOCIAL_GOOGLE_CLIENT_ID=
HITKEEP_SOCIAL_GOOGLE_CLIENT_SECRET=
HITKEEP_SOCIAL_GITHUB_CLIENT_ID=
HITKEEP_SOCIAL_GITHUB_CLIENT_SECRET=
HITKEEP_SOCIAL_MICROSOFT_CLIENT_ID=
HITKEEP_SOCIAL_MICROSOFT_CLIENT_SECRET=
HITKEEP_SOCIAL_MICROSOFT_TENANT=common
HITKEEP_MAIL_HOST=smtp.example.com
HITKEEP_MAIL_PORT=587
HITKEEP_MAIL_USERNAME=postmaster@example.com
HITKEEP_MAIL_PASSWORD=change-me
HITKEEP_MAIL_FROM_ADDRESS=analytics@example.com
HITKEEP_MAIL_FROM_NAME=HitKeep
```

`HITKEEP_TRUSTED_PROXIES` debe ser el CIDR de la red del proxy inverso, no `0.0.0.0/0`.

Para publicar en un subdirectorio, mantén alineados `HITKEEP_PUBLIC_URL` y el prefijo de ruta del proxy inverso. La ruta equivalente de Caddy es:

```
HITKEEP_PUBLIC_URL=https://www.example.net/hitkeep/
HITKEEP_HOSTNAME=www.example.net
```

```
www.example.net {
handle /hitkeep* {
  reverse_proxy hitkeep:8080
}
encode zstd gzip
}
```

Para un nginx existente en el host, que administre sus propios certificados, por ejemplo mediante certbot, conserva el `compose.yml` del inicio rápido y dirige nginx al puerto publicado.

Aplica dos cambios a los archivos del inicio rápido:

1. En `compose.yml`, enlaza el puerto al loopback para que solo el nginx local pueda llegar al contenedor:

```
    ports:
    - "127.0.0.1:8080:8080"
```

1. En `.env`, define la URL pública con el dominio servido por nginx y confía en la red Docker. El tráfico del host llega al contenedor mediante la puerta de enlace de la red Docker, situada en el intervalo `172.16.0.0/12`:

```
HITKEEP_PUBLIC_URL=https://analytics.example.com
HITKEEP_TRUSTED_PROXIES=172.16.0.0/12
```

Añade después un bloque de servidor a nginx:

```
server {
  listen 443 ssl;
  http2 on;
  server_name analytics.example.com;

  # ssl_certificate / ssl_certificate_key as issued by certbot or your CA.

  location / {
      proxy_pass http://127.0.0.1:8080;
      proxy_http_version 1.1;
      proxy_set_header Host $host;
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_set_header X-Forwarded-Proto $scheme;
      proxy_set_header X-Forwarded-Host $host;
      # Live dashboard updates stream over server-sent events.
      proxy_set_header Connection "";
      proxy_buffering off;
      proxy_read_timeout 300s;
  }
}
```

Recarga nginx (`nginx -t && systemctl reload nginx`) y abre `https://analytics.example.com`. Si todos los visitantes parecen proceder de una única IP privada, revisa los [proxies de confianza](https://hitkeep.com/guides/installation/trusted-proxies/).

En stacks existentes de Traefik, publica HitKeep mediante etiquetas:

```
services:
hitkeep:
  image: pascalebeier/hitkeep:latest
  restart: unless-stopped
  volumes:
    - hitkeep_data:/var/lib/hitkeep/data
    - hitkeep_archive:/var/lib/hitkeep/archive
    - hitkeep_backups:/var/lib/hitkeep/backups
  environment:
    # Public URL must match the HTTPS origin served by Traefik, including any path prefix.
    HITKEEP_PUBLIC_URL: ${HITKEEP_PUBLIC_URL:?set in .env}
    # Required for stable sessions. Generate with: openssl rand -hex 32
    HITKEEP_JWT_SECRET: ${HITKEEP_JWT_SECRET:?set in .env}
    # Must be the Traefik network CIDR. This controls real visitor IP, geo/network metadata,
    # country exclusions, rate limiting, and spam checks.
    HITKEEP_TRUSTED_PROXIES: ${HITKEEP_TRUSTED_PROXIES:?set to your traefik network CIDR}
    # Keep live data, retention archives, and backup snapshots on persistent volumes.
    HITKEEP_DB_PATH: /var/lib/hitkeep/data/hitkeep.db
    HITKEEP_DATA_PATH: /var/lib/hitkeep/data
    HITKEEP_ARCHIVE_PATH: /var/lib/hitkeep/archive
    HITKEEP_BACKUP_PATH: /var/lib/hitkeep/backups
    HITKEEP_BACKUP_INTERVAL: ${HITKEEP_BACKUP_INTERVAL:-60}
    HITKEEP_BACKUP_RETENTION: ${HITKEEP_BACKUP_RETENTION:-24}
    # Optional OSS spam-list refresh. Disable for fully offline/air-gapped installs.
    HITKEEP_SPAM_FILTER_AUTO_UPDATE: "true"
    HITKEEP_SPAM_FILTER_UPDATE_INTERVAL: ${HITKEEP_SPAM_FILTER_UPDATE_INTERVAL:-1440}
    HITKEEP_SPAM_FILTER_PATH: /var/lib/hitkeep/data/spam-filter.json
    # Optional read-only MCP endpoint for governed assistant/reporting access.
    HITKEEP_MCP_ENABLED: "true"
    HITKEEP_MCP_PATH: /mcp
    HITKEEP_MCP_MAX_RANGE_DAYS: ${HITKEEP_MCP_MAX_RANGE_DAYS:-366}
    # Optional AI model route for Opportunity enrichment. Leave disabled until a provider is configured.
    HITKEEP_AI_ENABLED: ${HITKEEP_AI_ENABLED:-false}
    HITKEEP_AI_PROVIDER: ${HITKEEP_AI_PROVIDER:-}
    HITKEEP_AI_MODEL: ${HITKEEP_AI_MODEL:-}
    HITKEEP_AI_BASE_URL: ${HITKEEP_AI_BASE_URL:-}
    HITKEEP_AI_REGION: ${HITKEEP_AI_REGION:-}
    HITKEEP_AI_API_KEY: ${HITKEEP_AI_API_KEY:-}
    HITKEEP_AI_REQUEST_LIMIT: ${HITKEEP_AI_REQUEST_LIMIT:-100}
    HITKEEP_AI_TOKEN_LIMIT: ${HITKEEP_AI_TOKEN_LIMIT:-100000}
    HITKEEP_AI_BUDGET_WINDOW: ${HITKEEP_AI_BUDGET_WINDOW:-1440}
    # Optional Google Search Console OAuth integration.
    HITKEEP_GOOGLE_SEARCH_CONSOLE_CLIENT_ID: ${HITKEEP_GOOGLE_SEARCH_CONSOLE_CLIENT_ID:-}
    HITKEEP_GOOGLE_SEARCH_CONSOLE_CLIENT_SECRET: ${HITKEEP_GOOGLE_SEARCH_CONSOLE_CLIENT_SECRET:-}
    HITKEEP_GOOGLE_SEARCH_CONSOLE_REDIRECT_URL: ${HITKEEP_GOOGLE_SEARCH_CONSOLE_REDIRECT_URL:-}
    # Optional Google, GitHub, and Microsoft social sign-in.
    HITKEEP_SOCIAL_GOOGLE_CLIENT_ID: ${HITKEEP_SOCIAL_GOOGLE_CLIENT_ID:-}
    HITKEEP_SOCIAL_GOOGLE_CLIENT_SECRET: ${HITKEEP_SOCIAL_GOOGLE_CLIENT_SECRET:-}
    HITKEEP_SOCIAL_GITHUB_CLIENT_ID: ${HITKEEP_SOCIAL_GITHUB_CLIENT_ID:-}
    HITKEEP_SOCIAL_GITHUB_CLIENT_SECRET: ${HITKEEP_SOCIAL_GITHUB_CLIENT_SECRET:-}
    HITKEEP_SOCIAL_MICROSOFT_CLIENT_ID: ${HITKEEP_SOCIAL_MICROSOFT_CLIENT_ID:-}
    HITKEEP_SOCIAL_MICROSOFT_CLIENT_SECRET: ${HITKEEP_SOCIAL_MICROSOFT_CLIENT_SECRET:-}
    HITKEEP_SOCIAL_MICROSOFT_TENANT: ${HITKEEP_SOCIAL_MICROSOFT_TENANT:-common}
    # SMTP powers invites, password reset, email reports, and security mail.
    HITKEEP_MAIL_HOST: ${HITKEEP_MAIL_HOST:-}
    HITKEEP_MAIL_PORT: ${HITKEEP_MAIL_PORT:-587}
    HITKEEP_MAIL_USERNAME: ${HITKEEP_MAIL_USERNAME:-}
    HITKEEP_MAIL_PASSWORD: ${HITKEEP_MAIL_PASSWORD:-}
    HITKEEP_MAIL_ENCRYPTION: ${HITKEEP_MAIL_ENCRYPTION:-tls}
    HITKEEP_MAIL_FROM_ADDRESS: ${HITKEEP_MAIL_FROM_ADDRESS:-hitkeep@localhost}
    HITKEEP_MAIL_FROM_NAME: ${HITKEEP_MAIL_FROM_NAME:-HitKeep}
  labels:
    - "traefik.enable=true"
    - "traefik.http.routers.hitkeep.rule=Host(`analytics.example.com`)"
    - "traefik.http.routers.hitkeep.entrypoints=websecure"
    - "traefik.http.routers.hitkeep.tls.certresolver=myresolver"
    - "traefik.http.services.hitkeep.loadbalancer.server.port=8080"

volumes:
hitkeep_data: {}
hitkeep_archive: {}
hitkeep_backups: {}
```

## Dominios de seguimiento personalizados

Los equipos que autogestionan HitKeep pueden usar [dominios de seguimiento personalizados](https://hitkeep.com/guides/tracking/custom-tracking-domains/) con cualquier proxy inverso que termine TLS y conserve el encabezado `Host` original. Usa el modo TLS externo cuando nginx, Traefik, un balanceador de carga u otro proceso ACME administren los certificados. Usa el perfil de Caddy solo si quieres incorporar dominios desde el panel con emisión de certificados bajo demanda.

### TLS externo con nginx o Traefik

Añade esta configuración de HitKeep cuando el proxy inverso ya gestione los certificados:

```
HITKEEP_CUSTOM_TRACKING_TLS_MODE: external
```

En el modo externo, añade cada hostname de seguimiento al proxy antes de que el equipo pulse **Verificar** en HitKeep. La comprobación requiere que `https://<hostname>/hk.js` devuelva el tracker con un certificado válido.

El repositorio de HitKeep incluye ejemplos sin Caddy:

- [`examples/nginx.custom-tracking.conf`](https://github.com/PascaleBeier/hitkeep/blob/main/examples/nginx.custom-tracking.conf)
- [`examples/compose.nginx-custom-tracking.yml`](https://github.com/PascaleBeier/hitkeep/blob/main/examples/compose.nginx-custom-tracking.yml)
- [`examples/traefik.custom-tracking.yml`](https://github.com/PascaleBeier/hitkeep/blob/main/examples/traefik.custom-tracking.yml)
- [`examples/compose.traefik-custom-tracking.yml`](https://github.com/PascaleBeier/hitkeep/blob/main/examples/compose.traefik-custom-tracking.yml)

El ejemplo de nginx limita los hosts de seguimiento personalizados a los recursos del tracker y las rutas de ingestión en el proxy. El ejemplo de Traefik usa entradas `Host(...)` exactas para cada hostname; no las sustituyas por una regla comodín salvo que quieras aceptar deliberadamente hostnames arbitrarios.

### TLS bajo demanda de Caddy

Añade esta configuración al servicio `hitkeep`:

```
HITKEEP_CUSTOM_TRACKING_TLS_MODE: caddy-on-demand
HITKEEP_CADDY_TLS_ASK_TOKEN: ${HITKEEP_CADDY_TLS_ASK_TOKEN:?set in .env}
# Optional. Use only when tracker hostnames point somewhere other than
# the host in HITKEEP_PUBLIC_URL.
HITKEEP_CUSTOM_TRACKING_DNS_TARGET: ${HITKEEP_CUSTOM_TRACKING_DNS_TARGET:-}
```

Genera el token de consulta en la terminal (`openssl rand -hex 32`) y añádelo como una línea nueva a `.env`:

```
HITKEEP_CADDY_TLS_ASK_TOKEN=paste-the-generated-value-here
```

Usa un Caddyfile con almacenamiento persistente y un endpoint de consulta TLS bajo demanda restringido:

```
{
email ops@example.com
storage file_system {
  root /data
}
on_demand_tls {
  ask http://hitkeep:8080/internal/caddy/on-demand-tls/{$HITKEEP_CADDY_TLS_ASK_TOKEN}
}
}

{$HITKEEP_HOSTNAME} {
reverse_proxy hitkeep:8080
encode zstd gzip
}

https:// {
tls {
  on_demand
}

reverse_proxy hitkeep:8080 {
  header_up Host {host}
  header_up X-Forwarded-Host {host}
  header_up X-Forwarded-Proto {scheme}
}

encode zstd gzip
}
```

Caddy envía `?domain=` a la URL de consulta antes de emitir un certificado. HitKeep responde con `204` únicamente si el token coincide y el hostname solicitado pertenece a un dominio de seguimiento personalizado activado y verificado mediante DNS. No ejecutes TLS bajo demanda de Caddy sin `ask`: cualquier hostname que alcance el listener de Caddy podría intentar emitir un certificado.

El repositorio de HitKeep incluye un perfil opcional completo:

- [`examples/Caddyfile.custom-tracking`](https://github.com/PascaleBeier/hitkeep/blob/main/examples/Caddyfile.custom-tracking)
- [`examples/compose.caddy-on-demand.yml`](https://github.com/PascaleBeier/hitkeep/blob/main/examples/compose.caddy-on-demand.yml)

## Copias de seguridad

El inicio rápido activa el worker de copias con checkpoints de HitKeep y guarda las instantáneas en el volumen persistente `hitkeep_backups`. Prueba una restauración antes de depender de ellas y copia los respaldos fuera del host Docker para que un fallo del host no elimine a la vez los datos activos y los de recuperación. Consulta [Copias de seguridad y restauración](https://hitkeep.com/guides/data/backups-and-restore/) para conocer el conjunto completo de datos y el proceso de restauración.

### Usar copias de seguridad o archivos compatibles con S3

Los ejemplos anteriores usan volúmenes Docker locales. Para escribir instantáneas de respaldo o archivos de retención en almacenamiento de objetos, sustituye las rutas locales por URL `s3://` y añade la configuración S3 a `.env`:

```
# Local or S3 backup snapshots. Empty HITKEEP_BACKUP_PATH disables automatic backups.
HITKEEP_BACKUP_PATH=s3://my-analytics-bucket/hitkeep/backups
# Retention archives can use local paths or S3-compatible URLs.
HITKEEP_ARCHIVE_PATH=s3://my-analytics-bucket/hitkeep/archive
HITKEEP_S3_REGION=eu-central-1
# Static credentials are optional when your container runtime provides an AWS credential chain.
HITKEEP_S3_ACCESS_KEY_ID=AKIAIOSFODNN7EXAMPLE
HITKEEP_S3_SECRET_ACCESS_KEY=change-me
```

Para MinIO, Cloudflare R2, Backblaze B2 u otro endpoint compatible con S3, define también `HITKEEP_S3_ENDPOINT` y, cuando sea necesario, `HITKEEP_S3_URL_STYLE=path`. Consulta [Copias de seguridad en S3](https://hitkeep.com/guides/data/s3-backups/) para ver ejemplos específicos de cada proveedor.

## Solución de problemas

**El panel muestra “An unexpected error occurred” durante la configuración o el inicio de sesión.** Revisa estos puntos en orden:

1. **Lee primero los registros del contenedor**: `docker compose logs hitkeep`. HitKeep registra cada fallo del servidor con nivel de error. Si el navegador muestra un error pero los registros permanecen vacíos, la solicitud no ha alcanzado el contenedor; esto apunta al proxy inverso o a un `HITKEEP_PUBLIC_URL` incorrecto, no a HitKeep.
2. **`HITKEEP_PUBLIC_URL` debe coincidir exactamente con la barra de direcciones**: esquema, host y puerto. Con una URL pública `https://`, las cookies de sesión se marcan como seguras y el inicio de sesión mediante HTTP sin cifrar falla de forma silenciosa. Después de cambiar `.env`, vuelve a ejecutar `docker compose up -d`; Compose solo aplica los cambios de entorno al recrear el contenedor.
3. **`Setup has already been completed.`** significa que ya existe un usuario en la base de datos, normalmente en un volumen `hitkeep_data` de un intento anterior. `docker compose down -v` elimina los volúmenes con nombre y permite empezar de cero; esta acción borra todos los datos de analítica.
4. **La configuración del correo no provoca fallos durante la configuración inicial.** Crear la primera cuenta no envía correo; los valores `HITKEEP_MAIL_*` vacíos nunca la bloquean. SMTP solo interviene después para invitaciones, restablecimientos de contraseña e informes por correo.
5. **¿Necesitas más detalle?** Añade `HITKEEP_LOG_LEVEL: debug` bajo `environment:` en `compose.yml` y vuelve a ejecutar `docker compose up -d`.

## Contenido relacionado

- [Proxies de confianza](https://hitkeep.com/guides/installation/trusted-proxies/)
- [Inicio de sesión social](https://hitkeep.com/guides/security/social-sign-in/)
- [Dominios de seguimiento personalizados](https://hitkeep.com/guides/tracking/custom-tracking-domains/)
- [Referencia de configuración](https://hitkeep.com/reference/configuration/)
- [Retención de datos](https://hitkeep.com/guides/data/retention/)
- [Copias de seguridad en S3](https://hitkeep.com/guides/data/s3-backups/)
- [Instalación mediante binario](https://hitkeep.com/es/guides/installation/binary/)

¿Necesitas alojamiento administrado con elección explícita de región? [Compara HitKeep Cloud](https://hitkeep.com/es/pricing/).

[Página anterior Binario para Linux](https://hitkeep.com/es/guides/installation/binary/)[Siguiente página Kubernetes y Helm](https://hitkeep.com/es/guides/installation/kubernetes/)
