LMS/CMS can't connect to MYSQL database (Tutor 20.0.1)
I want to simply launch an openedx platform via tutor (version 20.0.1) … but the #LMS/CMS can’t connect to MYSQL database.
I have freshly installed tutor (20.0.1) on a new server (all detailed steps for easy reproduction of this problem are described at the bottom). After running tutor local launch the mysql container seems to run correctly (container is up and the logs dont show any error only some warnings (attached at bottom). But the LMS and CMS containers are failing. The reason for this can be found in the logs (complete logs are attached at bottom):
**MYSQL Connectivity error: ** MySQLdb.OperationalError: (2003, “Can’t connect to MySQL server on ‘mysql:3306’ (111)”)
and
User authentication error: MySQLdb.OperationalError: (1045, “Access denied for user ‘openedx’@‘172.18.0.10’ (using password: YES)”)
According to the documentation the password for mysql database is generated automatically.
Using
Tutor version: 20.0.1 OS: Ubuntu-24.0.4
Similiar problems found and what I have tried
As described in these posts (here and here) I removed the data folder in $(tutor config printroot) and in addition to that removed all containers and volumes. But after running tutor local launch the problem persists …
Steps for easy reproduction
- These are all steps after spinning up a fresh ubuntu server (on digital ocean)
create a non-root user
adduser openedx
- (optional) copy the ssh-key from the root-user to the newly created user, to make it possible
to login passwordless via ssh-key
mkdir -p /home/openedx/.ssh
chmod 700 /home/openedx/.ssh
cp /root/.ssh/authorized_keys /home/openedx/.ssh/authorized_keys
chown -R openedx:openedx /home/openedx/.ssh
chmod 600 /home/openedx/.ssh/authorized_keys
add to sudo group (temporary)
usermod -aG sudo "openedx"
- docker installation (based on docker documentation)
# Add Docker's official GPG key:
sudo apt-get update
sudo apt-get install ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
# Add the repository to Apt sources:
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \
$(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}") stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
sudo usermod -aG docker openedx
- Now logout and loggin as the new created user called openedx
install python
sudo apt install python3 python3-pip libyaml-dev -y
sudo apt install python3-venv -y
python3 -m venv .venv
source .venv/bin/activate
- Now install Tutor
pip install "tutor[full]==20.0.1"
- Now after running tutor local launch the website should be accessible… but it is not due the exceptions thrown by CMS and LMS (logs can be seen by running
tutor local logs cmsand are attached above this section and described in detailed at the top of this post.)
Thank you in advance for your support and helpful answers!
Could you try building the images separately, with tutor images build all, so that it is easier to see any build-time errors?
After that, you can just do tutor local launch --skip-build or tutor local do init to initialize the instance.
Hi @ahmed-arb There is some more context here: 17304 I was able to replicate the issue, at least I believe so, where the openedx mysql user does not get created. Manually creating the mysql user and running the init command was able to get the fresh install operational. At least at the time of my testing I was unable to successfully build images until this was done, though i neglected to record my logs at the time... I'm not entirely sure if recreating the user was necessary before running init, but anyway thats what i did :)
@MuPp3t33r, could you share the error logs you got while building the images?
@ahmed-arb Hope this helps but if you need me to run anything more specifically I'm happy to try it again for you...
To start with, I run local launch and follow prompts
tutor local launch
==================================================
Interactive platform configuration
==================================================
Are you configuring a production platform? Type 'n' if you are just testing Tutor on your local computer [Y/n] y
Your website domain name for students (LMS) [www.myopenedx.com] lms.test.local
Your website domain name for teachers (CMS) [studio.lms.test.local] cms.test.local
Your platform name/title [My Open edX] test
Your public contact email address [[email protected]]
The default language code for the platform [en]
Activate SSL/TLS certificates for HTTPS access? Important note: this will NOT work in a development environment. [y/N] n
Configuration saved to /home/tutor/.local/share/tutor/config.yml
Environment generated in /home/tutor/.local/share/tutor/env
======================================
Building Docker images
======================================
No image to build
==============================================
Stopping any existing platform
==============================================
docker compose -f /home/tutor/.local/share/tutor/env/local/docker-compose.yml -f /home/tutor/.local/share/tutor/env/local/docker-compose.prod.yml --project-name tutor_local stop
======================================================
Starting the platform in detached mode
======================================================
docker compose -f /home/tutor/.local/share/tutor/env/local/docker-compose.yml -f /home/tutor/.local/share/tutor/env/dev/docker-compose.yml --project-name tutor_dev ls --format json
docker compose -f /home/tutor/.local/share/tutor/env/local/docker-compose.yml -f /home/tutor/.local/share/tutor/env/local/docker-compose.prod.yml --project-name tutor_local up --remove-orphans -d
[+] Running 13/13
✔ Network tutor_local_default Created 0.0s
✔ Container tutor_local-caddy-1 Started 0.8s
✔ Container tutor_local-mysql-1 Started 0.7s
✔ Container tutor_local-smtp-1 Started 0.8s
✔ Container tutor_local-permissions-1 Started 0.8s
✔ Container tutor_local-meilisearch-1 Started 0.9s
✔ Container tutor_local-redis-1 Started 0.9s
✔ Container tutor_local-mongodb-1 Started 1.0s
✔ Container tutor_local-lms-1 Started 1.1s
✔ Container tutor_local-lms-worker-1 Started 1.3s
✔ Container tutor_local-cms-1 Started 1.3s
✔ Container tutor_local-mfe-1 Started 1.3s
✔ Container tutor_local-cms-worker-1 Started 1.5s
================================================
Database creation and migrations
================================================
Initialising all services...
All services initialised.
The platform is now running and can be accessed at the following urls:
http://lms.test.local
http://cms.test.local
http://meilisearch.lms.test.local
http://apps.lms.test.local
The initialisation happened very quickly, like a few seconds.
here are some logs after starting the platform following launch:
most relevant looking entry:
lms-1 | django.db.utils.OperationalError: (1045, "Access denied for user 'openedx'@'172.18.0.8' (using password: YES)")
lms-1 | [uWSGI] getting INI configuration from /openedx/uwsgi.ini
lms-1 | [uwsgi-static] added mapping for /static => /openedx/staticfiles/
lms-1 | [uwsgi-static] added mapping for /media => /openedx/media/
lms-1 | *** Starting uWSGI 2.0.24 (64bit) on [Mon Oct 13 12:40:38 2025] ***
lms-1 | compiled with version: 11.4.0 on 23 September 2025 06:51:40
lms-1 | os: Linux-6.8.0-85-generic #85-Ubuntu SMP PREEMPT_DYNAMIC Thu Sep 18 15:26:59 UTC 2025
lms-1 | nodename: 33918e032290
lms-1 | machine: x86_64
lms-1 | clock source: unix
lms-1 | detected number of CPU cores: 4
lms-1 | current working directory: /openedx/edx-platform
lms-1 | detected binary path: /openedx/venv/bin/uwsgi
lms-1 | !!! no internal routing support, rebuild with pcre support !!!
lms-1 | your memory page size is 4096 bytes
lms-1 | detected max file descriptor number: 1048576
lms-1 | building mime-types dictionary from file /etc/mime.types...1516 entry found
lms-1 | lock engine: pthread robust mutexes
lms-1 | thunder lock: enabled
lms-1 | uWSGI http bound on 0.0.0.0:8000 fd 4
lms-1 | uwsgi socket 0 bound to TCP address 127.0.0.1:42045 (port auto-assigned) fd 3
lms-1 | Python version: 3.11.8 (main, Sep 2 2025, 08:09:41) [GCC 11.4.0]
lms-1 | Python main interpreter initialized at 0x7e38f0330278
lms-1 | python threads support enabled
lms-1 | your server socket listen backlog is limited to 100 connections
lms-1 | your mercy for graceful operations on workers is 60 seconds
lms-1 | mapped 231048 bytes (225 KB) for 2 cores
lms-1 | *** Operational MODE: preforking ***
lms-1 | Traceback (most recent call last):
lms-1 | File "/openedx/venv/lib/python3.11/site-packages/django/db/backends/base/base.py", line 289, in ensure_connection
lms-1 | self.connect()
lms-1 | File "/openedx/venv/lib/python3.11/site-packages/django/utils/asyncio.py", line 26, in inner
lms-1 | return func(*args, **kwargs)
lms-1 | ^^^^^^^^^^^^^^^^^^^^^
lms-1 | File "/openedx/venv/lib/python3.11/site-packages/django/db/backends/base/base.py", line 270, in connect
lms-1 | self.connection = self.get_new_connection(conn_params)
lms-1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
lms-1 | File "/openedx/venv/lib/python3.11/site-packages/django/utils/asyncio.py", line 26, in inner
lms-1 | return func(*args, **kwargs)
lms-1 | ^^^^^^^^^^^^^^^^^^^^^
lms-1 | File "/openedx/venv/lib/python3.11/site-packages/django/db/backends/mysql/base.py", line 247, in get_new_connection
lms-1 | connection = Database.connect(**conn_params)
lms-1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
lms-1 | File "/openedx/venv/lib/python3.11/site-packages/MySQLdb/__init__.py", line 121, in Connect
lms-1 | return Connection(*args, **kwargs)
lms-1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
lms-1 | File "/openedx/venv/lib/python3.11/site-packages/MySQLdb/connections.py", line 200, in __init__
lms-1 | super().__init__(*args, **kwargs2)
lms-1 | MySQLdb.OperationalError: (1045, "Access denied for user 'openedx'@'172.18.0.8' (using password: YES)")
lms-1 |
lms-1 | The above exception was the direct cause of the following exception:
lms-1 |
lms-1 | Traceback (most recent call last):
lms-1 | File "lms/wsgi.py", line 21, in <module>
lms-1 | application = get_wsgi_application()
lms-1 | ^^^^^^^^^^^^^^^^^^^^^^
lms-1 | File "/openedx/venv/lib/python3.11/site-packages/django/core/wsgi.py", line 13, in get_wsgi_application
lms-1 | return WSGIHandler()
lms-1 | ^^^^^^^^^^^^^
lms-1 | File "/openedx/venv/lib/python3.11/site-packages/django/core/handlers/wsgi.py", line 118, in __init__
lms-1 | self.load_middleware()
lms-1 | File "/openedx/venv/lib/python3.11/site-packages/django/core/handlers/base.py", line 61, in load_middleware
lms-1 | mw_instance = middleware(adapted_handler)
lms-1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
lms-1 | File "/openedx/venv/lib/python3.11/site-packages/edx_django_utils/monitoring/internal/middleware.py", line 518, in __init__
lms-1 | if not self._is_enabled():
lms-1 | ^^^^^^^^^^^^^^^^^^
lms-1 | File "/openedx/venv/lib/python3.11/site-packages/edx_django_utils/monitoring/internal/middleware.py", line 586, in _is_enabled
lms-1 | return waffle.switch_is_active('edx_django_utils.monitoring.enable_frontend_monitoring_middleware')
lms-1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
lms-1 | File "/openedx/venv/lib/python3.11/site-packages/waffle/__init__.py", line 23, in switch_is_active
lms-1 | switch = get_waffle_switch_model().get(switch_name)
lms-1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
lms-1 | File "/openedx/venv/lib/python3.11/site-packages/waffle/models.py", line 59, in get
lms-1 | obj = cls.get_from_db(name)
lms-1 | ^^^^^^^^^^^^^^^^^^^^^
lms-1 | File "/openedx/venv/lib/python3.11/site-packages/waffle/models.py", line 72, in get_from_db
lms-1 | return objects.get(name=name)
lms-1 | ^^^^^^^^^^^^^^^^^^^^^^
lms-1 | File "/openedx/venv/lib/python3.11/site-packages/django/db/models/manager.py", line 87, in manager_method
lms-1 | return getattr(self.get_queryset(), name)(*args, **kwargs)
lms-1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
lms-1 | File "/openedx/venv/lib/python3.11/site-packages/django/db/models/query.py", line 633, in get
lms-1 | num = len(clone)
lms-1 | ^^^^^^^^^^
lms-1 | File "/openedx/venv/lib/python3.11/site-packages/django/db/models/query.py", line 380, in __len__
lms-1 | self._fetch_all()
lms-1 | File "/openedx/venv/lib/python3.11/site-packages/django/db/models/query.py", line 1881, in _fetch_all
lms-1 | self._result_cache = list(self._iterable_class(self))
lms-1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
lms-1 | File "/openedx/venv/lib/python3.11/site-packages/django/db/models/query.py", line 91, in __iter__
lms-1 | results = compiler.execute_sql(
lms-1 | ^^^^^^^^^^^^^^^^^^^^^
lms-1 | File "/openedx/venv/lib/python3.11/site-packages/django/db/models/sql/compiler.py", line 1560, in execute_sql
lms-1 | cursor = self.connection.cursor()
lms-1 | ^^^^^^^^^^^^^^^^^^^^^^^^
lms-1 | File "/openedx/venv/lib/python3.11/site-packages/django/utils/asyncio.py", line 26, in inner
lms-1 | return func(*args, **kwargs)
lms-1 | ^^^^^^^^^^^^^^^^^^^^^
lms-1 | File "/openedx/venv/lib/python3.11/site-packages/django/db/backends/base/base.py", line 330, in cursor
lms-1 | return self._cursor()
lms-1 | ^^^^^^^^^^^^^^
lms-1 | File "/openedx/venv/lib/python3.11/site-packages/django/db/backends/base/base.py", line 306, in _cursor
lms-1 | self.ensure_connection()
lms-1 | File "/openedx/venv/lib/python3.11/site-packages/django/utils/asyncio.py", line 26, in inner
lms-1 | return func(*args, **kwargs)
lms-1 | ^^^^^^^^^^^^^^^^^^^^^
lms-1 | File "/openedx/venv/lib/python3.11/site-packages/django/db/backends/base/base.py", line 288, in ensure_connection
lms-1 | with self.wrap_database_errors:
lms-1 | File "/openedx/venv/lib/python3.11/site-packages/django/db/utils.py", line 91, in __exit__
lms-1 | raise dj_exc_value.with_traceback(traceback) from exc_value
lms-1 | File "/openedx/venv/lib/python3.11/site-packages/django/db/backends/base/base.py", line 289, in ensure_connection
lms-1 | self.connect()
lms-1 | File "/openedx/venv/lib/python3.11/site-packages/django/utils/asyncio.py", line 26, in inner
lms-1 | return func(*args, **kwargs)
lms-1 | ^^^^^^^^^^^^^^^^^^^^^
lms-1 | File "/openedx/venv/lib/python3.11/site-packages/django/db/backends/base/base.py", line 270, in connect
lms-1 | self.connection = self.get_new_connection(conn_params)
lms-1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
lms-1 | File "/openedx/venv/lib/python3.11/site-packages/django/utils/asyncio.py", line 26, in inner
lms-1 | return func(*args, **kwargs)
lms-1 | ^^^^^^^^^^^^^^^^^^^^^
lms-1 | File "/openedx/venv/lib/python3.11/site-packages/django/db/backends/mysql/base.py", line 247, in get_new_connection
lms-1 | connection = Database.connect(**conn_params)
lms-1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
lms-1 | File "/openedx/venv/lib/python3.11/site-packages/MySQLdb/__init__.py", line 121, in Connect
lms-1 | return Connection(*args, **kwargs)
lms-1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
lms-1 | File "/openedx/venv/lib/python3.11/site-packages/MySQLdb/connections.py", line 200, in __init__
lms-1 | super().__init__(*args, **kwargs2)
lms-1 | django.db.utils.OperationalError: (1045, "Access denied for user 'openedx'@'172.18.0.8' (using password: YES)")
lms-1 | unable to load app 0 (mountpoint='') (callable not found or import error)
lms-1 | *** no app loaded. GAME OVER ***
If I try build the images immediately following a launch I get this error specifically during the installation of Python requirements (note I've trimmed the upper portion as it didn't look useful, this is just the last lines):
46.91 Using cached geoip2-5.0.1-py3-none-any.whl.metadata (18 kB)
47.25 WARNING: Ignoring invalid cache entry origin file /openedx/.cache/pip/wheels/96/41/06/9f8fddc6eb1d75bde63db7f491311a4ae26905212617e06eb2/origin.json for glob2-0.7-py2.py3-none-any.whl (Expecting value: line 1 column 1 (char 0))
47.26 Collecting glob2==0.7 (from -r /openedx/edx-platform/requirements/edx/base.txt (line 581))
47.26 Using cached glob2-0.7-py2.py3-none-any.whl
47.26 ERROR: Wheel 'glob2' located at /openedx/.cache/pip/wheels/96/41/06/9f8fddc6eb1d75bde63db7f491311a4ae26905212617e06eb2/glob2-0.7-py2.py3-none-any.whl is invalid.
47.27
47.27 [notice] A new release of pip is available: 24.0 -> 25.2
47.27 [notice] To update, run: pip install --upgrade pip
------
Dockerfile:86
--------------------
85 | # Install base requirements and asset-building requirements
86 | >>> RUN --mount=type=bind,from=edx-platform,source=/requirements/edx/base.txt,target=/openedx/edx-platform/requirements/edx/base.txt \
87 | >>> --mount=type=bind,from=edx-platform,source=/requirements/edx/assets.txt,target=/openedx/edx-platform/requirements/edx/assets.txt \
88 | >>> --mount=type=cache,target=/openedx/.cache/pip,sharing=shared \
89 | >>> pip install -r /openedx/edx-platform/requirements/edx/base.txt -r /openedx/edx-platform/requirements/edx/assets.txt
90 |
--------------------
ERROR: failed to build: failed to solve: process "/bin/sh -c pip install -r /openedx/edx-platform/requirements/edx/base.txt -r /openedx/edx-platform/requirements/edx/assets.txt" did not complete successfully: exit code: 1
Error: Command failed with status 1: docker buildx build --tag=docker.io/overhangio/openedx:20.0.1-indigo --output=type=docker --cache-from=type=registry,ref=docker.io/overhangio/openedx:20.0.1-indigo-cache /home/tutor/.local/share/tutor/env/build/openedx
If I run the tutor local do init command then it runs all the migrations, following which everything seems to run fine. So it looks like the initialisation is not happening in the normal launch process and has to be run separately after launching the platform. So manually creating the openedx user i guess is not needed as I did originally, simply running init command gets everything up and running. after running init i was able to build the openedx container image however I had to run with the --no-cache switch otherwise I got the same failures during the Python Requirements run:
36.37 WARNING: Ignoring invalid cache entry origin file /openedx/.cache/pip/wheels/96/41/06/9f8fddc6eb1d75bde63db7f491311a4ae26905212617e06eb2/origin.json for glob2-0.7-py2.py3-none-any.whl (Expecting value: line 1 column 1 (char 0))
36.37 Collecting glob2==0.7 (from -r /openedx/edx-platform/requirements/edx/base.txt (line 581))
36.37 Using cached glob2-0.7-py2.py3-none-any.whl
36.37 ERROR: Wheel 'glob2' located at /openedx/.cache/pip/wheels/96/41/06/9f8fddc6eb1d75bde63db7f491311a4ae26905212617e06eb2/glob2-0.7-py2.py3-none-any.whl is invalid.
36.38
36.38 [notice] A new release of pip is available: 24.0 -> 25.2
36.38 [notice] To update, run: pip install --upgrade pip
------
Dockerfile:86
--------------------
85 | # Install base requirements and asset-building requirements
86 | >>> RUN --mount=type=bind,from=edx-platform,source=/requirements/edx/base.txt,target=/openedx/edx-platform/requirements/edx/base.txt \
87 | >>> --mount=type=bind,from=edx-platform,source=/requirements/edx/assets.txt,target=/openedx/edx-platform/requirements/edx/assets.txt \
88 | >>> --mount=type=cache,target=/openedx/.cache/pip,sharing=shared \
89 | >>> pip install -r /openedx/edx-platform/requirements/edx/base.txt -r /openedx/edx-platform/requirements/edx/assets.txt
90 |
--------------------
ERROR: failed to build: failed to solve: process "/bin/sh -c pip install -r /openedx/edx-platform/requirements/edx/base.txt -r /openedx/edx-platform/requirements/edx/assets.txt" did not complete successfully: exit code: 1
Error: Command failed with status 1: docker buildx build --tag=docker.io/overhangio/openedx:20.0.1-indigo --output=type=docker --cache-from=type=registry,ref=docker.io/overhangio/openedx:20.0.1-indigo-cache /home/tutor/.local/share/tutor/env/build/openedx