Its easy enough to recreate as just create a new docker image.
I am fairly competent in unix admin and programming so happy to do any testing or getting extra debug.
Below is my docker compose file (copy of the default file apart from ngrok service included):
$ cat docker-compose.yml
services:
grampsweb: &grampsweb
image: ghcr.io/gramps-project/grampsweb:latest
restart: always
ports:
- "80:5000" # host:docker
entrypoint: []
environment:
GRAMPSWEB_TREE: "Gramps Web" # will create a new tree if not exists
GRAMPSWEB_CELERY_CONFIG__broker_url: "redis://grampsweb_redis:6379/0"
GRAMPSWEB_CELERY_CONFIG__result_backend: "redis://grampsweb_redis:6379/0"
GRAMPSWEB_RATELIMIT_STORAGE_URI: redis://grampsweb_redis:6379/1
GRAMPSWEB_SECRET_KEY: #REDACTED#
depends_on:
- grampsweb_redis
volumes:
- gramps_users:/app/users # persist user database
- gramps_index:/app/indexdir # persist search index
- gramps_thumb_cache:/app/thumbnail_cache # persist thumbnails
- gramps_cache:/app/cache # persist export and report caches
- gramps_secret:/app/secret # persist flask secret
- gramps_db:/root/.gramps/grampsdb # persist Gramps database
- gramps_media:/app/media # persist media files
- gramps_tmp:/tmp
grampsweb_celery:
<<: *grampsweb # YAML merge key copying the entire grampsweb service config
ports: []
container_name: grampsweb_celery
depends_on:
- grampsweb
- grampsweb_redis
command: celery -A gramps_webapi.celery worker --loglevel=INFO --concurrency=2
grampsweb_redis:
image: docker.io/library/redis:7.2.4-alpine
container_name: grampsweb_redis
restart: always
ngrok:
image: ngrok/ngrok:latest
restart: unless-stopped
extra_hosts:
- "host.docker.internal:host-gateway"
command:
- "http"
- "http://host.docker.internal:80"
- "--url=#REDACTED#"
environment:
NGROK_AUTHTOKEN: #REDACTED#
ports:
- 4040:4040
volumes:
gramps_users:
gramps_index:
gramps_thumb_cache:
gramps_cache:
gramps_secret:
gramps_db:
gramps_media:
gramps_tmp:
Below is logs for first start and when I launch the GUI:
$ docker compose logs grampsweb
grampsweb-1 | [2025-11-14 09:36:50 +0000] [7] [INFO] Starting gunicorn 23.0.0
grampsweb-1 | [2025-11-14 09:36:50 +0000] [7] [INFO] Listening at: http://0.0.0.0:5000 (7)
grampsweb-1 | [2025-11-14 09:36:50 +0000] [7] [INFO] Using worker: sync
grampsweb-1 | [2025-11-14 09:36:50 +0000] [8] [INFO] Booting worker with pid: 8
grampsweb-1 | [2025-11-14 09:36:50 +0000] [9] [INFO] Booting worker with pid: 9
grampsweb-1 | [2025-11-14 09:36:50 +0000] [10] [INFO] Booting worker with pid: 10
grampsweb-1 | [2025-11-14 09:36:50 +0000] [11] [INFO] Booting worker with pid: 11
grampsweb-1 | [2025-11-14 09:36:51 +0000] [12] [INFO] Booting worker with pid: 12
grampsweb-1 | [2025-11-14 09:36:51 +0000] [13] [INFO] Booting worker with pid: 13
grampsweb-1 | [2025-11-14 09:36:51 +0000] [14] [INFO] Booting worker with pid: 14
grampsweb-1 | [2025-11-14 09:36:51 +0000] [15] [INFO] Booting worker with pid: 15
grampsweb-1 |
grampsweb-1 | (gunicorn:9): Gtk-CRITICAL **: 09:36:56.443: gtk_icon_theme_get_for_screen: assertion 'GDK_IS_SCREEN (screen)' failed
grampsweb-1 |
grampsweb-1 | (gunicorn:8): Gtk-CRITICAL **: 09:36:56.635: gtk_icon_theme_get_for_screen: assertion 'GDK_IS_SCREEN (screen)' failed
grampsweb-1 |
grampsweb-1 | (gunicorn:10): Gtk-CRITICAL **: 09:36:56.792: gtk_icon_theme_get_for_screen: assertion 'GDK_IS_SCREEN (screen)' failed
grampsweb-1 |
grampsweb-1 | (gunicorn:13): Gtk-CRITICAL **: 09:36:56.838: gtk_icon_theme_get_for_screen: assertion 'GDK_IS_SCREEN (screen)' failed
grampsweb-1 |
grampsweb-1 | (gunicorn:15): Gtk-CRITICAL **: 09:36:56.978: gtk_icon_theme_get_for_screen: assertion 'GDK_IS_SCREEN (screen)' failed
grampsweb-1 |
grampsweb-1 | (gunicorn:11): Gtk-CRITICAL **: 09:36:57.238: gtk_icon_theme_get_for_screen: assertion 'GDK_IS_SCREEN (screen)' failed
grampsweb-1 |
grampsweb-1 | (gunicorn:14): Gtk-CRITICAL **: 09:36:57.376: gtk_icon_theme_get_for_screen: assertion 'GDK_IS_SCREEN (screen)' failed
grampsweb-1 |
grampsweb-1 | (gunicorn:12): Gtk-CRITICAL **: 09:36:57.406: gtk_icon_theme_get_for_screen: assertion 'GDK_IS_SCREEN (screen)' failed
grampsweb-1 | [2025-11-14 09:37:35 +0000] [9] [ERROR] Error handling request /api/token/create_owner/
grampsweb-1 | Traceback (most recent call last):
grampsweb-1 | File "/usr/local/lib/python3.11/dist-packages/sqlalchemy/engine/base.py", line 1967, in _exec_single_context
grampsweb-1 | self.dialect.do_execute(
grampsweb-1 | File "/usr/local/lib/python3.11/dist-packages/sqlalchemy/engine/default.py", line 951, in do_execute
grampsweb-1 | cursor.execute(statement, parameters)
grampsweb-1 | sqlite3.OperationalError: no such table: users
grampsweb-1 |
grampsweb-1 | The above exception was the direct cause of the following exception:
grampsweb-1 |
grampsweb-1 | Traceback (most recent call last):
grampsweb-1 | File "/usr/local/lib/python3.11/dist-packages/gunicorn/workers/sync.py", line 134, in handle
grampsweb-1 | self.handle_request(listener, req, client, addr)
grampsweb-1 | File "/usr/local/lib/python3.11/dist-packages/gunicorn/workers/sync.py", line 177, in handle_request
grampsweb-1 | respiter = self.wsgi(environ, resp.start_response)
grampsweb-1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
grampsweb-1 | File "/usr/local/lib/python3.11/dist-packages/flask/app.py", line 1536, in __call__
grampsweb-1 | return self.wsgi_app(environ, start_response)
grampsweb-1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
grampsweb-1 | File "/usr/local/lib/python3.11/dist-packages/flask/app.py", line 1514, in wsgi_app
grampsweb-1 | response = self.handle_exception(e)
grampsweb-1 | ^^^^^^^^^^^^^^^^^^^^^^^^
grampsweb-1 | File "/usr/local/lib/python3.11/dist-packages/flask/app.py", line 1511, in wsgi_app
grampsweb-1 | response = self.full_dispatch_request()
grampsweb-1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
grampsweb-1 | File "/usr/local/lib/python3.11/dist-packages/flask/app.py", line 919, in full_dispatch_request
grampsweb-1 | rv = self.handle_user_exception(e)
grampsweb-1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
grampsweb-1 | File "/usr/local/lib/python3.11/dist-packages/flask/app.py", line 917, in full_dispatch_request
grampsweb-1 | rv = self.dispatch_request()
grampsweb-1 | ^^^^^^^^^^^^^^^^^^^^^^^
grampsweb-1 | File "/usr/local/lib/python3.11/dist-packages/flask/app.py", line 902, in dispatch_request
grampsweb-1 | return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args) # type: ignore[no-any-return]
grampsweb-1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
grampsweb-1 | File "/usr/local/lib/python3.11/dist-packages/flask/views.py", line 110, in view
grampsweb-1 | return current_app.ensure_sync(self.dispatch_request)(**kwargs) # type: ignore[no-any-return]
grampsweb-1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
grampsweb-1 | File "/usr/local/lib/python3.11/dist-packages/flask/views.py", line 191, in dispatch_request
grampsweb-1 | return current_app.ensure_sync(meth)(**kwargs) # type: ignore[no-any-return]
grampsweb-1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
grampsweb-1 | File "/usr/local/lib/python3.11/dist-packages/flask_limiter/_limits.py", line 326, in __inner
grampsweb-1 | return cast(R, flask.current_app.ensure_sync(obj)(*a, **k))
grampsweb-1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
grampsweb-1 | File "/usr/local/lib/python3.11/dist-packages/webargs/core.py", line 652, in wrapper
grampsweb-1 | return func(*args, **kwargs)
grampsweb-1 | ^^^^^^^^^^^^^^^^^^^^^
grampsweb-1 | File "/usr/local/lib/python3.11/dist-packages/gramps_webapi/api/resources/token.py", line 170, in post
grampsweb-1 | if get_all_user_details(
grampsweb-1 | ^^^^^^^^^^^^^^^^^^^^^
grampsweb-1 | File "/usr/local/lib/python3.11/dist-packages/gramps_webapi/auth/__init__.py", line 247, in get_all_user_details
grampsweb-1 | users = query.all()
grampsweb-1 | ^^^^^^^^^^^
grampsweb-1 | File "/usr/local/lib/python3.11/dist-packages/sqlalchemy/orm/query.py", line 2704, in all
grampsweb-1 | return self._iter().all() # type: ignore
grampsweb-1 | ^^^^^^^^^^^^
grampsweb-1 | File "/usr/local/lib/python3.11/dist-packages/sqlalchemy/orm/query.py", line 2857, in _iter
grampsweb-1 | result: Union[ScalarResult[_T], Result[_T]] = self.session.execute(
grampsweb-1 | ^^^^^^^^^^^^^^^^^^^^^
grampsweb-1 | File "/usr/local/lib/python3.11/dist-packages/sqlalchemy/orm/session.py", line 2351, in execute
grampsweb-1 | return self._execute_internal(
grampsweb-1 | ^^^^^^^^^^^^^^^^^^^^^^^
grampsweb-1 | File "/usr/local/lib/python3.11/dist-packages/sqlalchemy/orm/session.py", line 2249, in _execute_internal
grampsweb-1 | result: Result[Any] = compile_state_cls.orm_execute_statement(
grampsweb-1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
grampsweb-1 | File "/usr/local/lib/python3.11/dist-packages/sqlalchemy/orm/context.py", line 306, in orm_execute_statement
grampsweb-1 | result = conn.execute(
grampsweb-1 | ^^^^^^^^^^^^^
grampsweb-1 | File "/usr/local/lib/python3.11/dist-packages/sqlalchemy/engine/base.py", line 1419, in execute
grampsweb-1 | return meth(
grampsweb-1 | ^^^^^
grampsweb-1 | File "/usr/local/lib/python3.11/dist-packages/sqlalchemy/sql/elements.py", line 526, in _execute_on_connection
grampsweb-1 | return connection._execute_clauseelement(
grampsweb-1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
grampsweb-1 | File "/usr/local/lib/python3.11/dist-packages/sqlalchemy/engine/base.py", line 1641, in _execute_clauseelement
grampsweb-1 | ret = self._execute_context(
grampsweb-1 | ^^^^^^^^^^^^^^^^^^^^^^
grampsweb-1 | File "/usr/local/lib/python3.11/dist-packages/sqlalchemy/engine/base.py", line 1846, in _execute_context
grampsweb-1 | return self._exec_single_context(
grampsweb-1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^
grampsweb-1 | File "/usr/local/lib/python3.11/dist-packages/sqlalchemy/engine/base.py", line 1986, in _exec_single_context
grampsweb-1 | self._handle_dbapi_exception(
grampsweb-1 | File "/usr/local/lib/python3.11/dist-packages/sqlalchemy/engine/base.py", line 2355, in _handle_dbapi_exception
grampsweb-1 | raise sqlalchemy_exception.with_traceback(exc_info[2]) from e
grampsweb-1 | File "/usr/local/lib/python3.11/dist-packages/sqlalchemy/engine/base.py", line 1967, in _exec_single_context
grampsweb-1 | self.dialect.do_execute(
grampsweb-1 | File "/usr/local/lib/python3.11/dist-packages/sqlalchemy/engine/default.py", line 951, in do_execute
grampsweb-1 | cursor.execute(statement, parameters)
grampsweb-1 | sqlalchemy.exc.OperationalError: (sqlite3.OperationalError) no such table: users
grampsweb-1 | [SQL: SELECT users.id AS users_id, users.name AS users_name, users.email AS users_email, users.fullname AS users_fullname, users.pwhash AS users_pwhash, users.role AS users_role, users.tree AS users_tree
grampsweb-1 | FROM users]
grampsweb-1 | (Background on this error at: https://sqlalche.me/e/20/e3q8)