Testcontainers-Python provides several strategies to wait for containers to be ready before proceeding with tests. This is crucial for ensuring that your tests don't start before the container is fully initialized and ready to accept connections.
The simplest way to wait for a container is using a structured wait strategy:
from testcontainers.core.wait_strategies import HttpWaitStrategy
class MyContainer(DockerContainer):
def _connect(self):
HttpWaitStrategy(8080).wait_until_ready(self)The strategy will retry until it succeeds or times out. By default, it will retry for 120 seconds with a 1-second interval between attempts.
Wait for specific log messages to appear:
from testcontainers.core.waiting_utils import wait_for_logs
# Wait for a specific log message
container = GenericContainer(
"nginx:alpine",
wait=wait_for_logs("Configuration complete; ready for start")
)
# Wait for a log pattern using regex
container = GenericContainer(
"postgres:latest",
wait=wait_for_logs("database system is ready to accept connections")
)
# Wait for logs in both stdout and stderr
container = GenericContainer(
"myapp:latest",
wait=wait_for_logs("Ready", predicate_streams_and=True)
)Wait for an HTTP endpoint to be accessible:
from testcontainers.core.waiting_utils import wait_for_http
# Wait for an HTTP endpoint
container = GenericContainer(
"nginx:alpine",
wait=wait_for_http("/", port=80)
)
# Wait for a specific HTTP status code
container = GenericContainer(
"myapp:latest",
wait=wait_for_http("/health", port=8080, status_code=200)
)You can create custom wait conditions by implementing your own wait function:
def custom_wait(container):
# Your custom logic here
# Return True if the container is ready, False otherwise
return True
container = GenericContainer(
"myapp:latest",
wait=custom_wait
)Many container implementations include built-in connection waiting. For example:
from testcontainers.redis import RedisContainer
from testcontainers.postgres import PostgresContainer
# Redis container waits for connection
redis = RedisContainer()
redis.start() # Will wait until Redis is ready to accept connections
# PostgreSQL container waits for connection
postgres = PostgresContainer()
postgres.start() # Will wait until PostgreSQL is ready to accept connectionsThe Ryuk container (used for garbage collection) has its own wait mechanism that combines log-based and connection-based waiting:
- Log-based Wait: Waits for the message ".* Started!" with a 20-second timeout
- Connection Wait: After the logs are found, attempts to establish a socket connection to the Ryuk container, retrying up to 50 times with a 0.5-second interval between attempts
This ensures that the Ryuk container is fully operational before any test containers are started.
You can configure the wait behavior using environment variables:
TC_MAX_TRIES: Maximum number of connection attempts (default: 120)TC_POOLING_INTERVAL: Time between connection attempts in seconds (default: 1)
Example:
export TC_MAX_TRIES=60
export TC_POOLING_INTERVAL=2- Always use appropriate wait strategies for your containers
- Set reasonable timeouts for your environment
- Use specific wait conditions rather than generic ones when possible
- Consider using connection-based waiting for database containers
- Use log-based waiting for applications that output clear startup messages
- Use HTTP-based waiting for web services
- Implement custom wait conditions for complex startup scenarios