Ejecutar tu propio Progress Watch

El servidor es de código abierto bajo la AGPL, y el servicio alojado ejecuta la misma imagen que ejecutarías tú. Alojarlo tú mismo cuesta un contenedor y un volumen, y la base de datos se mantiene pequeña para siempre porque el progreso nunca se escribe en ella.

Un solo archivo

docker-compose.yml es toda la instalación. Ejecuta la imagen publicada, así que no hay nada que clonar ni nada que compilar. Cópialo de la página de Docker, que imprime el archivo actual, y arráncalo:

Eso es toda la instalación: la aplicación en http://localhost:7979, con Redis y SQLite dentro del mismo contenedor y todo lo que guardan en el volumen con nombre. Ábrela, crea un espacio, y el menú Conectar de ese espacio te da los comandos con su UUID ya puesto.

Configurarlo

El archivo es tuyo desde el momento en que lo guardas, y es el único sitio donde se configura nada. Cada ajuste es una línea de un bloque environment: bajo el servicio, que el archivo omite hasta que te haga falta:

environment:
- DATABASE_URL=postgresql://progresswatch:secret@db.internal/progresswatch
- VAPID_PUBLIC_KEY=BJ1w…
- VAPID_PRIVATE_KEY=xK2p…

Variables de entorno es la lista completa, y todo lo que hay en ella funciona aquí. Para publicarlo en un puerto distinto del 7979, cambia el lado izquierdo de 7979:3000: la aplicación dentro del contenedor siempre está en el 3000.

curl http://localhost:7979/up

{"status":"ok","database":true,"redis":true,"worker":true}

Qué está corriendo

Dos procesos y un archivo, todo en un contenedor. La aplicación responde a las peticiones y ejecuta el worker dentro de sí misma, porque Sidekiq va embebido en Puma, y arranca Redis a su lado y lo detiene al salir. Redis guarda el progreso actual de cada tarea y la cola de trabajos, y hace instantáneas de ambos en el volumen; SQLite guarda los espacios y la forma de cada tarea. Define REDIS_URL y la aplicación usa ese Redis en su lugar y no arranca ninguno propio.

Actualizarlo

Descarga la imagen nueva y arráncalo otra vez:

docker compose pull
docker compose up -d

Cada tarea en curso se queda donde estaba. Al detener el contenedor, Redis escribe una instantánea en el volumen, y el contenedor nuevo la carga al arrancar. Solo un fallo o un corte de luz se salta esa escritura, y entonces vuelve la última instantánea periódica, de hace minutos si los informes son frecuentes y de hasta una hora si no lo son: una tarea muestra «Esperando datos…» o un número anterior hasta su siguiente informe.

Por qué el 7979

No el 3000. Este es un servicio que dejas corriendo durante semanas mientras trabajas, y el 3000 es donde aterrizan tus propios servidores de desarrollo: Rails, Vite, Next, Grafana y la mitad de las líneas docker run de otras guías. Un servicio en segundo plano no debería ocupar el puerto más disputado de la máquina, y menos aún uno cuya URL pegas en un secreto de CI y luego olvidas.

Dentro del contenedor sigue siendo el 3000; solo se movió el puerto publicado, y eso es la línea 7979:3000.

Perder el volumen es recuperable

En él están los espacios, la estructura de las tareas, una instantánea de Redis y la clave secreta. Si desaparece, creas un espacio nuevo y tus procesos lo repueblan todo en su siguiente petición, y una clave secreta nueva no cuesta nada, porque no hay cuentas de las que echar a nadie. Haz copia de seguridad si prefieres no repartir un UUID de espacio nuevo a todo lo que reporta ahí, pero no hay nada en él que no se pueda reconstruir ejecutando los trabajos otra vez.

SQLite o PostgreSQL

Los dos, desde la misma imagen y las mismas migraciones, elegido con DATABASE_URL. SQLite es lo predeterminado y es la respuesta correcta para una sola máquina: dos tablas, búsquedas por clave primaria, casi nunca escrituras. Apunta a postgresql://… cuando ejecutes varios contenedores de la aplicación, porque no pueden compartir un archivo.

Detrás de un proxy

Pon FORCE_SSL=true en cuanto algo delante de la aplicación termine el TLS. Está desactivado por defecto para que una instalación a la que se llega por HTTP simple no te redirija a un certificado que no tienes.

Notificaciones

Pon un par de claves VAPID y aparecerá un botón «Avisarme» en cada espacio; deja las claves sin poner y la función no existe. No hay nada que registrar con Apple ni con Google. Mira notificaciones para la historia completa, incluido lo que tiene que pasar en un teléfono.

Barrer los espacios vacíos

Abrir el sitio crea un espacio para ti, así que se crean muchos que nunca se usan, incluidos los de cualquier cosa que rastree la página. Un espacio que alguna vez haya tenido una tarea se conserva para siempre, por mucho que nadie lo mire. Solo se barren los que nunca tuvieron ni una tarea, y solo cuando tienen más de 30 días.

Nada lo ejecuta por ti. Prográmalo como ya programes las cosas:

# crontab, a diario a las 04:17
17 4 * * * cd /path/to/progresswatch && docker compose exec -T progresswatch bin/rails sweep_empty_spaces

Como nada corre con temporizador, 30 días es un mínimo y no un calendario: ejecútalo a diario y los espacios vacíos se van el día en que califican; ejecútalo una vez al año y se quedan hasta entonces. Saltárselo no pone en riesgo tus datos — el barrido solo toca filas que nunca se usaron — simplemente significa que la tabla crece con espacios que nadie abrió jamás.

Tu instancia no es indexable

Toda página de una instalación propia es noindex, robots.txt lo prohíbe todo y /sitemap.xml responde 404. Desde la máquina de alguien no hay público al que llegar ni nada allí que quiera encontrarse en una búsqueda, y no hay que configurar nada para ello: es lo que hay.