When working with microservices, CI pipelines, or containerized environments, it’s often useful to verify MySQL connectivity independently of your application code.
In this post, we’ll build a lightweight Docker container that:
- Connects to a MySQL database
- Accepts all connection parameters at runtime
- Waits and retries until MySQL is available
- Fails fast if configuration is missing
- Supports connection timeouts
- Lists up to 5 tables if the user has privileges
- Returns CI-friendly exit codes
This approach is ideal for smoke tests, health checks, and CI/CD pipelines.
Why a Dedicated MySQL Connection Tester?
Common scenarios where this is useful:
- Verifying database access in CI before running migrations
- Testing credentials in Kubernetes or Docker Compose
- Debugging network or permission issues
- Smoke-testing production or staging databases safely
Instead of baking logic into your app, we use a single-purpose container.
Solution Overview
We will build:
- A Python-based Docker image
- Runtime-configurable MySQL connection
- Built-in retry and timeout logic
- Optional table listing (non-fatal if permissions are missing)
Final Dockerfile
FROM python:3.11-slim
WORKDIR /app
RUN pip install mysql-connector-python
# Create Python test script
RUN echo "import os" > test_db.py && \
echo "import sys" >> test_db.py && \
echo "import time" >> test_db.py && \
echo "import mysql.connector" >> test_db.py && \
echo "" >> test_db.py && \
echo "REQUIRED_VARS = ['MYSQL_HOST', 'MYSQL_USER', 'MYSQL_PASSWORD', 'MYSQL_DATABASE']" >> test_db.py && \
echo "missing = [v for v in REQUIRED_VARS if not os.getenv(v)]" >> test_db.py && \
echo "if missing:" >> test_db.py && \
echo " print('Missing required environment variables: ' + ', '.join(missing))" >> test_db.py && \
echo " sys.exit(1)" >> test_db.py && \
echo "" >> test_db.py && \
echo "MAX_RETRIES = int(os.getenv('MYSQL_MAX_RETRIES', 10))" >> test_db.py && \
echo "RETRY_DELAY = int(os.getenv('MYSQL_RETRY_DELAY', 3))" >> test_db.py && \
echo "CONNECT_TIMEOUT = int(os.getenv('MYSQL_CONNECT_TIMEOUT', 5))" >> test_db.py && \
echo "" >> test_db.py && \
echo "for attempt in range(1, MAX_RETRIES + 1):" >> test_db.py && \
echo " try:" >> test_db.py && \
echo " print(f'Attempt {attempt}: Connecting to MySQL...')" >> test_db.py && \
echo " conn = mysql.connector.connect(" >> test_db.py && \
echo " host=os.getenv('MYSQL_HOST')," >> test_db.py && \
echo " user=os.getenv('MYSQL_USER')," >> test_db.py && \
echo " password=os.getenv('MYSQL_PASSWORD')," >> test_db.py && \
echo " database=os.getenv('MYSQL_DATABASE')," >> test_db.py && \
echo " port=int(os.getenv('MYSQL_PORT', 3306))," >> test_db.py && \
echo " connection_timeout=CONNECT_TIMEOUT" >> test_db.py && \
echo " )" >> test_db.py && \
echo "" >> test_db.py && \
echo " if conn.is_connected():" >> test_db.py && \
echo " print('MySQL connection successful!')" >> test_db.py && \
echo "" >> test_db.py && \
echo " try:" >> test_db.py && \
echo " cursor = conn.cursor()" >> test_db.py && \
echo " cursor.execute('SHOW TABLES')" >> test_db.py && \
echo " tables = cursor.fetchmany(5)" >> test_db.py && \
echo "" >> test_db.py && \
echo " if tables:" >> test_db.py && \
echo " print('Listing up to 5 tables:')" >> test_db.py && \
echo " for t in tables:" >> test_db.py && \
echo " print(' - ' + t[0])" >> test_db.py && \
echo " else:" >> test_db.py && \
echo " print('No tables found in database.')" >> test_db.py && \
echo "" >> test_db.py && \
echo " except mysql.connector.Error as e:" >> test_db.py && \
echo " print('Connected, but cannot list tables (permission issue?): ' + str(e))" >> test_db.py && \
echo "" >> test_db.py && \
echo " conn.close()" >> test_db.py && \
echo " sys.exit(0)" >> test_db.py && \
echo "" >> test_db.py && \
echo " except Exception as e:" >> test_db.py && \
echo " print('Connection failed: ' + str(e))" >> test_db.py && \
echo " if attempt == MAX_RETRIES:" >> test_db.py && \
echo " print('Max retries reached. Exiting.')" >> test_db.py && \
echo " sys.exit(2)" >> test_db.py && \
echo " time.sleep(RETRY_DELAY)" >> test_db.py
CMD ["python", "test_db.py"]
Build the Image
docker build -t mysql-test-client .
Run the Container (Passing Parameters at Runtime)
sudo docker run --rm --network=esearchnetwork -e MYSQL_HOST=your_host -e MYSQL_PORT=port2connect -e MYSQL_USER=your_db -e MYSQL_PASSWORD=your_super_secret_password -e MYSQL_DATABASE=your_database -e MYSQL_CONNECT_TIMEOUT=10 -e MYSQL_MAX_RETRIES=5 -e MYSQL_RETRY_DELAY=2 mysql-test-client
Example Output
Successful connection with privileges
Attempt 1: Connecting to MySQL…
MySQL connection successful!
Listing up to 5 tables:
users
orders
products
invoices
logs
Successful connection without table privileges
MySQL connection successful!
Connected, but cannot list tables (permission issue?): SHOW command denied
Exit Codes (CI-Friendly)
| Exit Code | Meaning |
|---|---|
0 | Connection successful |
1 | Missing required environment variables |
2 | MySQL unreachable after retries |
Best Practices & Improvements
Recommended enhancements:
- Replace
echowithCOPY test_db.pyfor maintainability - Query
information_schema.tablesfor limited users - Output JSON for CI parsing
- Add TLS / SSL parameters
Conclusion
This lightweight Docker-based MySQL connection tester is a reliable, reusable, and secure way to validate database access across environments.
It works equally well for:
- Local development
- Docker Compose
- Kubernetes
- CI/CD pipelines
If you need a cleaner version with a standalone Python file, or a Compose/Kubernetes variant, this setup is easy to extend.
Happy testing