Effective debugging starts with visibility into what your applications are doing. The docker compose logs command provides direct access to container output streams, making it the primary tool for troubleshooting issues, monitoring application behavior, and understanding system events.
Understanding the logs Command
The logs command retrieves output written to standard output (stdout) and standard error (stderr) by processes running inside containers. This output includes application logs, error messages, stack traces, and debug information.
Basic Usage
View logs from all containers:
docker compose logs
This displays logs from every container in your stack, interleaved chronologically. Each line is prefixed with the container name, making it easy to identify the source.
The command shows historical logs—everything that has been logged since the containers started—then exits. This snapshot view helps review what happened during container initialization or investigate past events.
Following Logs in Real-Time
Watch logs as they happen:
docker compose logs -f
The -f or --follow flag keeps the command running and displays new log entries as they appear. This real-time stream is invaluable during active development or when monitoring live systems.
Press Ctrl+C to stop following and return to your terminal.
Viewing Specific Service Logs
Focus on a single service:
docker compose logs web
This shows logs only from the web service, filtering out output from other containers. When debugging a specific component, this isolation reduces noise and helps you focus.
View multiple specific services:
docker compose logs web worker
This displays logs from both the web and worker services while ignoring others.
Controlling Log Output
Limiting Log Lines
Show only the most recent entries:
docker compose logs --tail=50
This displays the last 50 lines from each container's logs. Adjust the number based on your needs. Use --tail=100 for more context or --tail=20 for a quick glance.
The tail limit applies per container, not across all containers. If you have three services, --tail=50 might show up to 150 lines total (50 from each).
Combine with follow mode to start viewing recent logs then continue streaming:
docker compose logs --tail=50 -f
This shows the last 50 lines immediately, then follows new entries. It's perfect for joining a live debugging session without seeing the entire history.
Timestamp Display
Add timestamps to each log line:
docker compose logs -t
The -t or --timestamps flag prepends RFC3339 timestamps to every log entry. Timestamps help correlate logs with external events, identify when issues occurred, and measure timing between events.
Example output with timestamps:
2024-12-22T10:15:30.123456789Z web_1 | Starting server on port 8000 2024-12-22T10:15:30.456789123Z worker_1 | Connecting to message queue 2024-12-22T10:15:31.789123456Z web_1 | Server ready to accept connections
Timestamps are especially valuable when debugging race conditions, timing issues, or when you need to correlate logs with monitoring metrics.
Time Range Filtering
View logs from a specific time period:
docker compose logs --since 30m
This shows only logs from the last 30 minutes. Time specifications accept:
- 30s - 30 seconds
- 5m - 5 minutes
- 2h - 2 hours
- 2024-12-22T10:00:00 - Absolute timestamp
The --since flag is powerful for investigating recent incidents without wading through historical logs.
View logs until a specific time:
docker compose logs --until 2024-12-22T11:00:00
This shows logs up to the specified timestamp. Combine --since and --until to create precise time windows:
docker compose logs --since 2024-12-22T10:00:00 --until 2024-12-22T11:00:00
This extracts exactly the logs from that one-hour period.
No Log Prefix
Remove container name prefixes:
docker compose logs --no-log-prefix
The --no-log-prefix flag strips the container name from each line. Use this when piping logs to tools that don't expect prefixes or when you want cleaner output for a single service:
docker compose logs web --no-log-prefix
Advanced Log Filtering
Following Specific Services
Combine follow mode with service selection:
docker compose logs -f web
This follows only the web service in real-time, ignoring logs from other containers. During active development, this keeps your terminal focused on the component you're modifying.
Multiple Services with Tail
Focus on recent logs from specific services:
docker compose logs --tail=20 web worker
This shows the last 20 lines from both web and worker services specifically.
Time-Based Following
Start following from a specific point in time:
docker compose logs -f --since 5m
This shows logs from the last 5 minutes, then continues following new entries. It's useful when joining an investigation in progress—you see recent context then stay updated.
Log Content and Formatting
Standard Output vs Standard Error
The logs command captures both stdout and stderr streams. Most applications write normal output to stdout and errors to stderr, but this convention varies.
Both streams appear in the logs output, typically without distinguishing which stream produced each line. Applications that use structured logging often include severity levels (INFO, ERROR, etc.) directly in the log messages.
Multi-Line Logs
Stack traces and complex error messages span multiple lines. The logs command preserves these multi-line entries, displaying them exactly as written:
web_1 | Error: Database connection failed web_1 | at Database.connect (/app/db.js:45:15) web_1 | at Server.start (/app/server.js:23:8) web_1 | at Object.<anonymous> (/app/index.js:12:3)
Each line retains the container prefix, making it clear all lines belong to the same container's output.
Color Coding
By default, the logs command uses colors to distinguish different containers. Each container's logs appear in a different color, making it easier to visually track which container produced which output.
Colors work only in interactive terminals. When piping output to files or other programs, colors are automatically disabled.
Debugging Patterns
Finding Errors
Search for error messages in logs:
docker compose logs | grep -i error
This filters the output to show only lines containing "error" (case-insensitive). Common patterns include:
docker compose logs | grep -i "error\|exception\|fail"
This matches multiple error-related terms.
Tracking Requests
Follow a specific request through your system:
docker compose logs -f | grep "request-id-12345"
If your application includes request IDs in logs, this filters the real-time stream to show only log entries related to that specific request across all containers.
Monitoring Service Startup
Watch containers initialize:
docker compose logs -f web
Keep this running in a dedicated terminal window during development. You'll see:
- Application startup messages
- Configuration loading
- Connection establishment
- Error messages if startup fails
This immediate feedback accelerates the development cycle.
Investigating Historical Issues
When users report issues that occurred earlier:
docker compose logs --since "2024-12-22T14:30:00" --until "2024-12-22T14:35:00" -t
This extracts the exact time window when the issue occurred, with timestamps for precise correlation.
Log Persistence and Rotation
Log Storage
Container logs are stored on the host filesystem. The Docker daemon manages these log files automatically. When you run docker compose logs, it reads from these stored files.
Log files grow over time. Busy applications can generate gigabytes of logs daily. Without management, log files eventually consume all available disk space.
Automatic Log Rotation
Configure log rotation through the Docker daemon or per-container settings. Log rotation limits storage consumption by:
- Limiting maximum log file size
- Limiting the number of log files retained
- Automatically archiving or deleting old logs
Rotation settings prevent runaway log growth while preserving recent history for debugging.
Checking Log File Sizes
Logs are stored in Docker's data directory. Large log files indicate either high logging volume or missing rotation configuration. Monitor log directory sizes to catch issues before disk space exhaustion occurs.
Debugging Techniques
Comparative Analysis
Compare logs from multiple instances of the same service:
Terminal 1:
docker compose logs -f web_1
Terminal 2:
docker compose logs -f web_2
Watch both simultaneously to identify differences in behavior between instances. This helps debug load balancer issues, state problems, or inconsistent configurations.
Correlation with Events
Time-correlate logs with external actions:
- Note the current time
- Perform an action (trigger an API call, submit a form)
- Use --since to view logs from that moment:
docker compose logs --since 30s -t
The timestamps show exactly what happened when you triggered the action.
Progressive Filtering
Start broad, then narrow focus:
Step 1 - View all logs:
docker compose logs --tail=100
Step 2 - Identify problematic service:
docker compose logs --tail=100 worker
Step 3 - Focus on error timeframe:
docker compose logs worker --since 10m | grep -i error
This progressive approach efficiently locates issues in complex systems.
Silent Container Investigation
When a container runs but produces no output:
docker compose logs worker
If this shows nothing, the application may not be writing to stdout/stderr. Check:
- Application logging configuration
- Whether logs are being written to files instead of stdout
- If the application started at all
- Container status using docker compose ps
Real-Time Debugging Workflow
Active Development
During development, maintain a dedicated terminal for logs:
docker compose logs -f web
This provides immediate feedback for:
- Code changes (if using live reload)
- Request handling
- Error messages
- Debug output
You see results instantly without switching contexts.
Production Monitoring
In production scenarios, follow logs to observe system behavior:
docker compose logs -f --tail=10
This shows the last 10 lines from each service, then continues following. You get immediate context without overwhelming history.
Incident Response
When alerts fire or issues are reported:
- Check recent logs across all services:
docker compose logs --since 5m -t
- Identify the problematic service
- Follow that service's logs:
docker compose logs -f problematic-service -t
- Extract relevant timeframe for analysis:
docker compose logs problematic-service --since "2024-12-22T15:00:00" --until "2024-12-22T15:05:00" > incident.log
This workflow quickly isolates issues and preserves evidence for deeper analysis.
Log Output Redirection
Saving Logs to Files
Capture logs for later analysis:
docker compose logs > application.log
This saves all logs to a file. The output includes container prefixes and all historical logs.
Save logs from a specific timeframe:
docker compose logs --since 1h > last-hour.log
Filtering Before Saving
Process logs before saving:
docker compose logs | grep -i error > errors.log
This saves only lines containing errors, creating a focused error report.
Streaming to Analysis Tools
Pipe logs to real-time analysis tools:
docker compose logs -f | your-analysis-tool
The continuous stream feeds log aggregation systems, alerting tools, or custom analysis scripts.
Troubleshooting Common Issues
No Logs Appear
If docker compose logs shows nothing:
- Verify containers are running: docker compose ps
- Check if the service name is correct
- Confirm the application writes to stdout/stderr
- Verify the container isn't in a restart loop before logging begins
Logs Are Truncated
Old logs may be truncated due to:
- Log rotation settings
- Maximum log size limits
- Container restarts (which may archive old logs)
Use --since to focus on available recent logs.
Logs Contain Binary Data
If logs show unreadable characters, the application may be writing binary data to stdout. This breaks log readability. Applications should write only text to stdout/stderr.
Performance Impact
Running docker compose logs on busy systems with extensive log history can be slow. The command must read through all stored logs to display them. Use --since or --tail to limit the amount of data processed.
Integration with External Tools
Log Aggregation Systems
Send logs to centralized logging systems by configuring appropriate logging drivers. The docker compose logs command remains available for local debugging even when logs are also forwarded elsewhere.
Monitoring Dashboards
Extract metrics from logs using pattern matching:
docker compose logs | grep "response_time" | awk '{print $5}'
This type of processing can feed monitoring dashboards with real-time metrics extracted from log streams.
Alerting Systems
Monitor logs for specific patterns:
docker compose logs -f | grep -i "fatal\|critical" | alert-handler
This creates a simple alerting pipeline that watches for critical messages and triggers alerts.
Best Practices for Effective Logging
Structured Log Output
Applications that output structured logs (JSON format) are easier to parse and analyze:
{"timestamp": "2024-12-22T10:15:30Z", "level": "ERROR", "message": "Connection failed", "request_id": "12345"}
Structured logs can be filtered, searched, and analyzed programmatically much more effectively than unstructured text.
Consistent Log Levels
Use standard log levels (DEBUG, INFO, WARN, ERROR, FATAL) consistently. This allows filtering by severity:
docker compose logs | grep "ERROR\|FATAL"
Meaningful Messages
Log messages should include:
- What happened
- Why it matters
- Relevant context (IDs, values, states)
- Timestamps (if not added by the logging framework)
Good: "Failed to connect to database postgres-primary after 3 retries, error: connection timeout"
Poor: "Error occurred"
Avoid Logging Sensitive Data
Never log:
- Passwords
- API keys
- Personal identifying information
- Credit card numbers
- Session tokens
Logs are often stored insecurely and shared widely during debugging. Sensitive data in logs creates security vulnerabilities.
Balance Log Volume
Log enough information to debug issues but not so much that you can't find relevant entries. Consider:
- DEBUG logs for development
- INFO for normal operations
- WARN for recoverable issues
- ERROR for failures requiring attention
Adjust logging levels based on environment (development vs production).
Advanced Debugging Scenarios
Intermittent Issues
For issues that occur occasionally:
docker compose logs -f -t | tee continuous-log.txt
This follows logs in real-time while also saving everything to a file. When the intermittent issue occurs, you have a complete record captured.
Multi-Service Coordination Issues
When debugging issues spanning multiple services:
docker compose logs -f -t service1 service2 service3
The timestamps allow you to see the sequence of events across services. You can identify which service first showed symptoms and how the issue propagated.
Performance Problems
When investigating slow performance:
docker compose logs -f -t | grep "duration\|took\|time"
If your applications log execution times, this filters to show only timing-related messages, helping identify slow operations.
Deadlock Detection
For suspected deadlocks or hanging processes:
docker compose logs --since 30s
Check if the application is logging anything at all. Silence combined with hanging requests suggests a deadlock. Look for the last log entries before the hang to identify what the application was attempting.
Memory Issues
When containers are being killed due to memory limits:
docker compose logs | grep -i "killed\|oom\|memory"
This searches for out-of-memory indicators. Container logs often show memory-related errors before the container is terminated.
Log Command Performance
Large Log Files
The docker compose logs command can be slow when containers have generated gigabytes of logs. To improve performance:
- Always use --tail to limit output
- Use --since to restrict the time range
- Filter to specific services
- Consider log rotation to keep log files manageable
Filtering at Source
Filtering with docker compose logs flags is more efficient than piping to grep:
Better:
docker compose logs --since 5m web
Less efficient:
docker compose logs web | tail -n 100
The command can optimize queries when you use its built-in filtering options.
Debugging Container Startup
Watching Initialization
Containers may crash or fail during initialization. Capture startup logs:
docker compose logs web
Even if the container immediately exits, logs are preserved. This output shows what happened during the failed startup attempt.
Startup Timing Issues
Use timestamps to measure startup duration:
docker compose logs -t web
Calculate the time between "container started" and "application ready" messages to identify slow initialization.
Dependencies and Startup Order
When containers depend on each other, watch startup sequences:
docker compose logs -f -t
Verify that services start in the correct order and that dependent services wait appropriately for their dependencies to become ready.
Log-Based Health Checking
Verifying Service Health
Check recent logs to verify a service is healthy:
docker compose logs --tail=20 web
Healthy services typically show:
- Regular request processing
- No error messages
- Expected periodic tasks
- Normal operational messages
Detecting Degradation
Compare recent logs to normal patterns:
docker compose logs --since 10m web | grep -i "error\|warning"
An increase in errors or warnings indicates degradation even if the service hasn't fully failed.
Identifying Saturation
Watch for patterns indicating resource saturation:
- Slow query warnings
- Queue full messages
- Thread pool exhaustion
- Connection pool depletion
These appear in logs before complete service failure, providing early warning.
Practical Debugging Exercises
Exercise 1: Find the Error
When a service misbehaves:
- Get recent logs: docker compose logs --tail=50 service-name
- Search for errors: Look for ERROR, Exception, or stack traces
- Note the timestamp
- Get context: docker compose logs --since 5m service-name -t
- Analyze the sequence of events leading to the error
Exercise 2: Trace a Request
To understand request flow:
- Trigger a request and note the time
- Extract logs from that moment: docker compose logs --since 30s -t
- Identify the request across all services
- Follow the request's path through your system
- Measure time spent in each service
Exercise 3: Diagnose Startup Failure
When a container won't start:
- Attempt to start: docker compose up service-name
- Immediately check logs: docker compose logs service-name
- Identify the last message before failure
- Look for missing dependencies, configuration errors, or permission issues
- Fix the issue and retry
Conclusion
The docker compose logs command provides essential visibility into containerized applications. Master its various flags and filtering options to efficiently debug issues, monitor behavior, and understand system events. Combine log analysis with structured logging practices in your applications to create a robust debugging and monitoring foundation. Regular practice with different filtering techniques builds debugging proficiency and reduces mean time to resolution when issues occur.