Docker Compose部署Jetty+MariaDB应用时连接拒绝问题:如何让Jetty等待MariaDB就绪后启动
Great question! You're absolutely right about the limitation of depends_on — it only waits for the target container to start, not for the service inside it to be fully ready. MariaDB takes a few moments to initialize the database and accept connections after the container launches, which is why your Jetty app is throwing that connection refused error even if you wait for the DB to be ready later (and why restarting Jetty fixes it).
Here are three reliable solutions to fix this, ordered by recommended practice:
1. Use Docker Healthcheck + Conditional Dependencies (Official Recommended)
This is the cleanest Docker-native approach, requiring you to upgrade your Compose file to version 3.4 or higher. We'll add a health check to MariaDB to verify when it's ready, then configure Jetty to only start once the health check passes.
Update your docker-compose.yml like this:
version: '3.8' # Upgrade to support conditional dependencies services: jetty: build: context: . dockerfile: docker/jetty/Dockerfile container_name: app-jetty ports: - "8080:8080" depends_on: mariadb: condition: service_healthy # Wait until MariaDB is healthy networks: - app mariadb: image: mariadb:10.7 container_name: app-mariadb restart: always environment: MARIADB_ROOT_PASSWORD: myPassword MARIADB_DATABASE: APPBD ports: - "3307:3306" networks: - app healthcheck: test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-p${MARIADB_ROOT_PASSWORD}"] interval: 5s # Check every 5 seconds timeout: 5s # Fail check after 5 seconds of no response retries: 10 # Allow up to 10 retries (total 50s wait time) adminer: image: adminer container_name: app-adminer restart: always ports: - "2002:8080" depends_on: mariadb: condition: service_healthy networks: - app networks: app: driver: bridge
The mysqladmin ping command verifies that MariaDB is accepting connections. Jetty will only start once this check passes consistently.
2. Add a Wait Script to the Jetty Container
If you can't upgrade your Compose version, use a script like wait-for-it.sh to delay Jetty's startup until MariaDB's port is open.
Step 1: Get the wait script
Download the script to your project's docker/jetty/ directory and make it executable:
curl -o docker/jetty/wait-for-it.sh https://raw.githubusercontent.com/vishnubob/wait-for-it/master/wait-for-it.sh chmod +x docker/jetty/wait-for-it.sh
Step 2: Modify your Jetty Dockerfile
Update the Dockerfile to copy the script and use it to wait for MariaDB before starting Jetty:
FROM gradle:7.2-jdk17 as grad WORKDIR /tmp RUN mkdir src COPY src /tmp/src COPY build.gradle /tmp/build.gradle RUN gradle --warning-mode all war FROM jetty:latest COPY --from=grad /tmp/build/libs/tmp.war /var/lib/jetty/webapps/ROOT.war # Copy wait script into container COPY docker/jetty/wait-for-it.sh /usr/local/bin/wait-for-it.sh RUN chmod +x /usr/local/bin/wait-for-it.sh # Wait for MariaDB port, then start Jetty CMD ["wait-for-it.sh", "mariadb:3306", "--", "java", "-jar", "/usr/local/jetty/start.jar"] EXPOSE 8080
Now the Jetty container will first wait for MariaDB's 3306 port to be reachable before launching the app server.
3. Add JDBC Connection Retry Logic to Your App
This is the most robust long-term fix, as it makes your app resilient to database restarts or temporary unavailability (not just startup delays).
Modify your JDBC connection string to include MariaDB's built-in retry parameters:
String jdbcUrl = "jdbc:mariadb://mariadb:3306/APPBD?user=root&password=myPassword" + "&connectTimeout=5000" # Timeout after 5 seconds per connection attempt + "&autoReconnect=true" # Automatically retry connections if dropped + "&maxReconnects=10" # Max number of retry attempts + "&socketTimeout=30000"; # Timeout idle connections after 30 seconds
If you're using a framework like Spring Boot, you can also add a startup hook to loop until the database is reachable:
@PostConstruct public void waitForDatabase() { int retries = 10; while (retries > 0) { try (Connection conn = DriverManager.getConnection(jdbcUrl)) { System.out.println("Successfully connected to database!"); return; } catch (SQLException e) { retries--; System.out.println("Waiting for database... (" + retries + " retries left)"); try { Thread.sleep(5000); } catch (InterruptedException ie) {} } } throw new RuntimeException("Failed to connect to database after multiple attempts"); }
Recommendation
Start with Solution 1 for a clean, Docker-native fix. Pair it with Solution 3 to make your app more resilient to runtime database issues.
内容的提问来源于stack exchange,提问作者Auraylien

