Antes de actualizar Karakeep a la 0.34, comprueba si hay cuentas cuyo correo solo se distingue por las mayúsculas, como Qux@example.com y QUX@example.com: la migración pasa todos los correos a minúsculas excepto esas, y a partir de ese momento ninguna de las dos puede iniciar sesión. La pantalla responde Incorrect email or password aunque la contraseña sea buena. Se arregla parando el contenedor y cambiando el correo con un UPDATE en SQLite. Esta guía repite la actualización de la 0.33.2 a la 0.34.1 el 2 de noviembre de 2026 y documenta también el nuevo inicio de sesión, la imagen karakeep-chrome y AUTH_TRUSTED_ORIGINS. La misma guía está disponible en inglés.

Puntos clave

  • La 0.34.0 salió el 17 de octubre de 2026 y la 0.34.1 el 22 de octubre. A 2 de noviembre, la etiqueta release apunta a la 0.34.1, así que un docker compose pull con la configuración por defecto ya te actualiza.
  • Las sesiones web pasan de tokens firmados a sesiones en la base de datos. Todo el mundo vuelve a iniciar sesión una vez; las claves de API siguen funcionando.
  • De mis 6 cuentas, la migración pasó 2 a minúsculas y dejó 4 intactas por colisionar. Las dos de la pareja Qux/QUX quedaron bloqueadas, y quien escribía Foo@example.com entraba en la cuenta de foo@example.com.
  • Un UPDATE ... SET email = lower(email) sobre las dos falla con UNIQUE constraint failed: user.email. Hay que renombrar una de las dos a otra dirección.
  • La imagen karakeep-chrome:release pasó de 517 MB a 1,42 GB en arm64. Si cambias solo la imagen y dejas los argumentos de depuración remota, los marcadores se guardan sin captura.

Qué cambia en Karakeep 0.34 y por qué rompe el inicio de sesión

La 0.34 cambia la biblioteca de autenticación: Karakeep deja next-auth y pasa a better-auth. Las notas de la versión 0.34.0[1] explican que la migración se aplica sola al arrancar. Trae tres avisos: cierra todas las sesiones web, pasa los correos a minúsculas y añade la variable AUTH_TRUSTED_ORIGINS.

La 0.34.1 es una versión de seguridad que vuelve a la imagen base node:24-slim. Dentro de la imagen comprobé Node v24.22.1, frente a la v24.18.1 de la 0.33.2.

El aviso que importa está en la sección de actualización. Mohamed Bassem, el mantenedor, lo escribe así: "You’ll get locked out if you have two conflicting accounts (neither of which are lowercase). If you get locked out, change the email address in the sqlite database to be lowercase to gain access back."

Mi prueba muestra que el problema es algo más amplio que esa frase: la cuenta con mayúsculas de una pareja también queda inaccesible aunque su gemela ya esté en minúsculas.

Si aún no tienes Karakeep, empieza por la guía para instalar Karakeep, que monta la 0.33.2 con el compose oficial. Esta parte de una instalación como aquella.

Cómo he probado la actualización

Monté Karakeep 0.33.2 con Docker 29.5.2 y Docker Compose 2.40.3 en una máquina Linux arm64 de 18 núcleos, con Meilisearch v1.41.0 y el Chrome antiguo, zenika/alpine-chrome:124. Los datos estaban en un directorio ./data montado en /data. Creé 6 usuarios por la API de registro: Ana@example.com, Bar@Example.com, la pareja Foo@example.com y foo@example.com, y la pareja Qux@example.com y QUX@example.com.

La 0.33.2 acepta las dos parejas sin quejarse y distingue mayúsculas al iniciar sesión: Foo@example.com y foo@example.com entran en cuentas distintas, y FOO@example.com devuelve 401. Guardé un marcador en 4 de las cuentas y creé una clave de API para Foo@example.com, para ver qué sobrevivía.

Paso 1: busca correos que solo cambian en mayúsculas

La consulta que detecta las colisiones agrupa por el correo en minúsculas. La imagen de Karakeep no trae el cliente sqlite3, así que uso keinos/sqlite3 en modo solo lectura sobre el mismo directorio:

docker run --rm -v "$PWD/data":/data:ro keinos/sqlite3 \
  sqlite3 -header /data/db.db \
  "SELECT lower(email) AS email, count(*) AS cuentas,
          group_concat(email, ' ') AS variantes
   FROM user GROUP BY lower(email) HAVING count(*) > 1;"

En mi instalación devolvió dos filas: foo@example.com|2|Foo@example.com foo@example.com y qux@example.com|2|QUX@example.com Qux@example.com. Si no sale ninguna fila, la parte delicada de la actualización no te afecta. Si usas el volumen con nombre del compose oficial, cambia "$PWD/data" por su nombre, que Compose prefija con el del proyecto (por ejemplo karakeep_data).

Las notas de la versión piden resolver cada pareja antes de actualizar, borrando la cuenta que no se usa. Si las dos tienen marcadores, no la borres: decide cuál conserva el correo y prepara el cambio de dirección de la otra, que es lo que hago en la sección del arreglo.

Paso 2: haz la copia del directorio de datos con Karakeep parado

La copia del directorio data es la única vuelta atrás, porque la migración reescribe las tablas de usuarios y sesiones. Para el servicio web y archiva el directorio desde un contenedor auxiliar, ya que los ficheros pertenecen a root:

docker compose stop web
docker run --rm -v "$PWD/data":/data:ro \
  -v "$PWD/backup":/backup alpine:3.22 \
  tar czf /backup/karakeep-data-0.33.2.tar.gz -C /data .

Parar antes de copiar evita una copia incoherente de db.db y queue.db, que SQLite escribe mientras la aplicación está viva. Mi archivo ocupó 13,1 MB, casi todo capturas y páginas archivadas. Si ya haces copias cifradas con restic, incluye este fichero antes de seguir. Meilisearch no necesita copia: su índice se reconstruye desde el panel de administración.

Paso 3: fija la 0.34.1 y arranca la migración

Con la copia hecha, la actualización es una línea del .env y dos órdenes de Compose. Fija la versión en lugar de dejar release, así decides tú cuándo llega la siguiente:

sed -i 's/^KARAKEEP_VERSION=.*/KARAKEEP_VERSION=0.34.1/' .env
docker compose pull web
docker compose up -d web
docker compose logs -f web

El registro muestra Running db migration script, luego Next.js 16.4.1 y Workers version: 0.34.1. No hay ninguna línea que avise de las cuentas que la migración se saltó, así que la comprobación la haces tú. Esta consulta lista los correos que siguen teniendo mayúsculas:

docker run --rm -v "$PWD/data":/data:ro keinos/sqlite3 \
  sqlite3 /data/db.db \
  "SELECT name, email FROM user WHERE email <> lower(email);"

En mi caso Ana@example.com y Bar@Example.com ya eran ana@example.com y bar@example.com. Quedaban 3 filas: Foo@example.com, Qux@example.com y QUX@example.com; foo@example.com no aparece porque ya estaba en minúsculas. Cualquier fila que salga aquí es una cuenta a la que su dueño no puede entrar.

Error "Incorrect email or password" después de actualizar a la 0.34

El error aparece con la contraseña correcta porque la 0.34 pasa a minúsculas lo que escribes en el formulario y después busca el correo tal cual en la tabla user. Para la pareja Qux/QUX, ninguna fila coincide con qux@example.com, y la pantalla responde lo mismo para las tres formas de escribirlo:

Incorrect email or password

La API de autenticación devuelve un 401 con {"message":"Invalid email or password","code":"INVALID_EMAIL_OR_PASSWORD"}. Con la pareja Foo/foo el síntoma engaña más: al escribir Foo@example.com el inicio de sesión funciona, pero entra en la cuenta de foo@example.com. En mi prueba las contraseñas eran iguales; con contraseñas distintas, el dueño de Foo@example.com vería el mismo error. Sus marcadores siguen en la base, solo que nadie puede llegar a ellos desde la web.

La clave de API que creé para Foo@example.com siguió funcionando: GET /api/v1/users/me devolvió su usuario después de actualizar. Si usas la extensión del navegador o la aplicación móvil con una clave, no notarás nada hasta que cierres sesión.

Cómo arreglar el bloqueo editando SQLite

El arreglo consiste en dejar un único correo en minúsculas para cada persona y dar a la otra cuenta una dirección distinta. El atajo de pasar las dos a minúsculas no funciona, porque la tabla tiene un índice único sobre email:

Error in 2nd command line argument: UNIQUE constraint failed: user.email

Para el servicio web antes de tocar la base y lanza el cliente como root, porque db.db pertenece a root y la imagen keinos/sqlite3 corre con el usuario sqlite (sin --user root responde attempt to write a readonly database):

docker compose stop web
docker run --rm --user root -v "$PWD/data":/data keinos/sqlite3 \
  sqlite3 /data/db.db "
  UPDATE user SET email = 'qux+antigua@example.com'
    WHERE email = 'QUX@example.com';
  UPDATE user SET email = 'qux@example.com'
    WHERE email = 'Qux@example.com';
  UPDATE user SET email = 'foo+antigua@example.com'
    WHERE email = 'Foo@example.com';"
docker compose start web

El orden importa: primero se aparta la cuenta que deja el correo, después se pasa a minúsculas la que lo conserva. La contraseña vive en la tabla account y va ligada al identificador del usuario, no al correo, así que no hay que tocarla. Tras arrancar, las cuatro direcciones resultantes iniciaron sesión con un 200, y QUX@example.com escrito así entra ya en la cuenta qux@example.com.

Si quieres reunir los marcadores en una sola cuenta, hazlo después desde la web: exporta los de la cuenta apartada e impórtalos en la buena. La 0.34.1 ya no deja crear parejas nuevas: al registrar Zed@Example.com guardó zed@example.com, y el registro de zed@example.com respondió User already exists. Use another email.

Todos vuelven a iniciar sesión: sesiones en la base de datos

Las sesiones web de la 0.33 eran un JSON Web Token (JWT) cifrado en una cookie; las de la 0.34 son filas de la tabla session. Justo después de migrar, esa tabla tenía 0 filas, la cookie antigua devolvía null en /api/auth/get-session y el panel redirigía al formulario de entrada con un 307. Avisa a quien use tu instancia antes de actualizar.

Las aplicaciones móviles y las claves de API no se ven afectadas: lo dicen las notas y lo comprobé con la clave del laboratorio.

Las variables NEXTAUTH_URL y NEXTAUTH_SECRET conservan su nombre. Siguen siendo obligatorias, así que el .env de la 0.33 vale sin cambios.

Migrar a la imagen karakeep-chrome sin perder las capturas

Karakeep pide pasar a su propia imagen de Chrome, ghcr.io/karakeep-app/karakeep-chrome, y la ruta antigua de Google ya ni se descarga. El 2 de noviembre de 2026 un docker pull gcr.io/zenika-hub/alpine-chrome:124 respondió esto:

Error response from daemon: error from registry: This API method
requires billing to be enabled. Please enable billing on project
#24742942950 by visiting https://console.developers.google.com/billing/
enable?project=24742942950 then retry.

La copia de Docker Hub, zenika/alpine-chrome:124, sí se descargó, y con ella la 0.34.1 rastreó una página y guardó su captura. Funciona, pero se queda sin las mejoras contra los bloqueos antibots de esta versión. La guía de migración de la imagen de Chrome[2] indica el servicio que debes dejar:

  chrome:
    image: ghcr.io/karakeep-app/karakeep-chrome:release
    restart: unless-stopped
    init: true
    command:
      - --disable-gpu
      - --disable-dev-shm-usage
      - --hide-scrollbars
      - --disable-blink-features=AutomationControlled
      - --window-size=1440,900

Respecto al servicio antiguo, desaparecen --no-sandbox, --remote-debugging-address y --remote-debugging-port, y aparece init: true. Aplica el cambio con docker compose pull chrome y docker compose up -d chrome; la migración no toca los datos de Karakeep.

Qué pasa si solo cambias la imagen

Probé primero a cambiar la línea image: y dejar los argumentos antiguos. El contenedor arranca, pero Chrome registra bind() failed: Address already in use (98) y el reenvío interno responde TCP4:127.0.0.1:9223: Connection refused. El punto de entrada de la imagen arranca Chrome en el puerto 9223 y reenvía el 9222 con socat, así que tu --remote-debugging-port=9222 choca con ese reenvío.

Karakeep no se cae. Su registro repite Failed to connect to the browser instance, will retry in 5 secs: browserType.connectOverCDP: socket hang up, y los marcadores nuevos terminan con Completed successfully y la línea Skipping storing the screenshot as it's empty. Es un fallo silencioso: guardas enlaces sin captura ni archivo. Con el servicio corregido, el siguiente marcador registró Captured the screenshot (121583 bytes).

Cuánto pesa la imagen nueva y qué navegador lleva en arm64

La imagen engorda porque cambia de navegador. La etiqueta release que tenía descargada era la 151.0.7922.47-r1, con Chrome Headless Shell, y ocupaba 517 MB; la del 2 de noviembre era la 156.0.8078.47-r2 y ocupaba 1,42 GB en arm64. El 12 de noviembre release ya apuntaba a la 157.0.8103.38-r1, que ocupa lo mismo. Las notas lo reconocen: "This comes at the cost of the chrome image getting bigger unfortunatly."

En arm64 no lleva Google Chrome. El Dockerfile de la imagen[3] instala Google Chrome solo en amd64, porque Google no publica Chrome para Linux arm64, y en arm64 recurre al Chromium de Debian. Dentro del contenedor, karakeep-chrome --version devolvió Chromium 156.0.8078.73 built on Debian GNU/Linux 13 (trixie), y con la 157.0.8103.38-r1 devolvió Chromium 156.0.8078.79: en arm64, el número de la etiqueta es el de Google Chrome, no el del navegador que lleva. Si quieres controlar cuándo cambia el navegador, usa la etiqueta versionada (157.0.8103.38-r1) en lugar de release.

AUTH_TRUSTED_ORIGINS: cuándo tienes que tocarla

No la toques si tu instancia funciona tras la actualización. Su valor por defecto, *, no significa "cualquier sitio": según la documentación de variables de Karakeep[4], representa la dirección a la que llegó la petición, además de NEXTAUTH_URL. Lo comprobé: entrar por la IP de la red local con el Host coherente dio 200, y una petición con el origen de otro sitio dio 403 con {"message":"Invalid origin","code":"INVALID_ORIGIN"}.

Si la fijas, la lista sustituye a ese comodín. Con AUTH_TRUSTED_ORIGINS=http://localhost:33000, entrar por http://192.168.1.50:33000 falló con el mismo 403, y el registro dijo ERROR [Better Auth]: Invalid origin: http://192.168.1.50:33000. Añade cada dirección por la que entres separada por comas. Si publicas Karakeep detrás de Nginx Proxy Manager, la dirección pública es la de NEXTAUTH_URL y no hace falta repetirla.

Las notas de la versión añaden otro cambio en los procesos de fondo: si uno falla, ahora se cae todo el proceso de los trabajadores para que actúe la política de reinicio. Por eso conviene mantener restart: unless-stopped en el servicio web. Intenté provocarlo arrancando la 0.34.1 con Meilisearch parado y no se cayó; el trabajador de búsqueda arrancó igual.

Cómo volver a Karakeep 0.33.2 si algo sale mal

Vuelve atrás restaurando el archivo, no cambiando solo la etiqueta, porque la 0.34 ya reescribió las tablas de usuarios y sesiones. Para el servicio, vacía data, extrae karakeep-data-0.33.2.tar.gz y fija KARAKEEP_VERSION=0.33.2 antes del up -d. Si actualizas tus contenedores con Watchtower, exclúyelo mientras tanto, o la etiqueta release te devolverá a la 0.34.1.

Preguntas frecuentes

¿Puedo seguir con KARAKEEP_VERSION=release?

Sí, y a 2 de noviembre de 2026 release es la 0.34.1, con el mismo resumen que la etiqueta 0.34.1 en el registro de contenedores de GitHub. El problema es que te actualiza sin darte ocasión de buscar antes las cuentas en colisión. Fija la versión para esta actualización y vuelve a release después si lo prefieres.

¿La 0.34 borra alguna de las cuentas duplicadas?

No. En mi prueba las 6 cuentas y sus 4 marcadores seguían en la base después de migrar. La migración solo se salta las cuentas en colisión, que quedan con el correo original y sin forma de entrar desde la web hasta que las renombras.

¿Tengo que repetir el proceso si tengo una sola cuenta?

No hace falta buscar colisiones con una sola cuenta: la consulta del paso 1 no puede devolver nada. Te queda iniciar sesión otra vez y, si usas un compose antiguo, cambiar el servicio de Chrome.

Conclusión

Actualizar Karakeep a la 0.34 con Docker es un cambio de etiqueta, salvo por una consulta que tienes que lanzar antes. Si hay correos que solo cambian en mayúsculas, resuélvelos antes de migrar o arréglalos después con un UPDATE que aparte una cuenta y pase la otra a minúsculas, con el servicio parado.

Haz la copia de data y avisa de que todos volverán a iniciar sesión. Revisa también que el servicio de Chrome no conserve los argumentos de depuración remota, o te quedarás sin capturas sin ningún error visible. Si el contenedor de Chrome te interesa por sí mismo, compara opciones en navegadores headless para agentes.

Fuentes: [1] Karakeep, notas de la versión 0.34.0[1], [2] Karakeep, notas de la versión 0.34.1[5], [3] Karakeep, migrar a la imagen de Chrome[2], [4] Karakeep, variables de entorno[4], [5] Dockerfile de karakeep-chrome en la 0.34.1[3], [6] Better Auth, orígenes de confianza[6], [7] SQLite, sentencia UPDATE[7].

Fuentes

  1. notas de la versión 0.34.0
  2. guía de migración de la imagen de Chrome
  3. Dockerfile de la imagen
  4. documentación de variables de Karakeep
  5. Karakeep, notas de la versión 0.34.1
  6. Better Auth, orígenes de confianza
  7. SQLite, sentencia UPDATE