This guide explains how to manage, view, and troubleshoot logs in the Local Docker Proxy. It covers both container logs (via Docker's logging driver) and optional Traefik access logs for request tracing.
- Overview
- Log Rotation Configuration
- Viewing Container Logs
- Traefik Access Logs
- Log File Locations
- Troubleshooting
- Best Practices
The Local Docker Proxy implements unified logging through Docker's json-file logging driver:
-
Container Logs: All containers (Traefik, Redis, MySQL, phpMyAdmin) use Docker's
json-filelogging driver with automatic rotation and compression. These logs capture stdout/stderr from each service. -
Traefik Access Logs (Optional): When enabled, detailed HTTP request logs are written to stdout in JSON format, captured and rotated by Docker's logging driver alongside other Traefik logs.
Key Benefits:
- ✅ Automatic log rotation and compression prevents disk space exhaustion
- ✅ Structured JSON format for easy parsing
- ✅ Built-in Docker tooling for log access
- ✅ Optional detailed request tracing without performance impact
- ✅ All logs managed consistently by Docker's logging driver
All services in the proxy use Docker's json-file logging driver with rotation configured to:
- Max File Size: 10 MB per file
- Max Files: 3 files retained
- Total Space: ~30 MB per container maximum
When a log file reaches 10 MB, Docker automatically:
- Rotates the current log to a numbered file (e.g.,
container.log.1) - Compresses older logs (e.g.,
container.log.2.gz) - Deletes the oldest file when the limit is reached
- Starts a new log file
This happens transparently without interrupting the container or requiring configuration changes.
The logging configuration is defined once in docker-compose.yml using a YAML anchor for consistency:
x-logging: &default-logging
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
compress: "true"
services:
traefik:
# ... other configuration ...
logging: *default-loggingConfiguration Options:
driver: "json-file": Uses Docker's default JSON logging drivermax-size: "10m": Rotates when file reaches 10 megabytesmax-file: "3": Keeps up to 3 log files (current + 2 rotated)compress: "true": Automatically compresses rotated logs
This configuration is applied to all four services: traefik, redis, mysql, and pma using the YAML anchor *default-logging.
Per-Service Limits:
- Traefik: ~30 MB maximum
- Redis: ~30 MB maximum
- MySQL: ~30 MB maximum
- phpMyAdmin: ~30 MB maximum
Total Proxy Logs: ~120 MB maximum for all container logs combined.
Note: When Traefik access logs are enabled, they are included in Traefik's container logs and subject to the same rotation limits (30 MB max).
View logs from any service using the docker logs command:
# View all Traefik logs
docker logs traefik
# View last 50 lines
docker logs traefik --tail 50
# View logs from the last hour
docker logs traefik --since 1h
# View logs from a specific timestamp
docker logs traefik --since 2024-01-28T10:00:00
# View logs for other services
docker logs redis
docker logs mysql
docker logs pmaTip: Use the container name defined in docker-compose.yml (traefik, redis, mysql, pma).
Combine docker logs with standard Unix tools for powerful filtering:
# Search for errors
docker logs traefik | grep -i error
# Search for a specific domain
docker logs traefik | grep "my-app.docker.localhost"
# Count occurrences of a pattern
docker logs traefik | grep -c "404"
# View context around a match (3 lines before and after)
docker logs traefik | grep -C 3 "connection refused"
# Search with case-insensitive matching
docker logs traefik | grep -i "timeout"
# Search for multiple patterns
docker logs traefik | grep -E "error|warning|fail"Follow logs in real-time:
# Follow Traefik logs (like tail -f)
docker logs traefik --follow
# Follow with timestamp
docker logs traefik --follow --timestamps
# Follow last 20 lines and continue
docker logs traefik --follow --tail 20
# Follow and filter
docker logs traefik --follow | grep -i errorTip: Press Ctrl+C to stop following logs.
View logs from all running services:
# Using docker compose
docker compose logs
# Follow all services
docker compose logs --follow
# Specific services only
docker compose logs traefik mysql --follow
# With timestamps and tail limit
docker compose logs --follow --timestamps --tail 50Traefik access logs provide detailed information about every HTTP request routed through the proxy. They are disabled by default to minimize disk usage and improve performance. When enabled, access logs are written to stdout and captured by Docker's logging driver alongside other Traefik logs.
-
Edit your
.envfile:# Enable access logs TRAEFIK_ACCESS_LOG_ENABLED=true # Choose format: json (recommended) or common TRAEFIK_ACCESS_LOG_FORMAT=json
-
Restart the Traefik container:
docker compose up -d traefik
-
Verify access logs are being generated:
# Generate some traffic curl https://traefik.docker.localhost/ # Check for access log entries docker logs traefik --tail 20 | grep -i "clientaddr\|requestpath"
You should see JSON-formatted access log entries mixed with regular Traefik logs.
Access logs are written to stdout and captured by Docker's logging driver, viewable via docker logs:
# View all Traefik logs (includes access logs when enabled)
docker logs traefik
# View last 50 entries
docker logs traefik --tail 50
# Follow in real-time
docker logs traefik --follow
# View with timestamps
docker logs traefik --timestamps --tail 20When using TRAEFIK_ACCESS_LOG_FORMAT=json (recommended), access logs are structured JSON objects. Use jq for powerful parsing:
Installing jq:
# Debian/Ubuntu
sudo apt install jq
# macOS
brew install jqCommon jq Queries:
# Pretty-print access log entries (filter out non-JSON lines)
docker logs traefik | jq -R 'fromjson? | select(.RequestPath != null)'
# Extract only the request path and status code
docker logs traefik | jq -R 'fromjson? | select(.RequestPath != null) | {path: .RequestPath, status: .DownstreamStatus}'
# Filter only 404 errors
docker logs traefik | jq -R 'fromjson? | select(.DownstreamStatus == 404)'
# Count requests by status code
docker logs traefik | jq -R 'fromjson? | select(.DownstreamStatus != null) | .DownstreamStatus' | sort | uniq -c | sort -rn
# Find requests to a specific domain
docker logs traefik | jq -R 'fromjson? | select(.RequestHost == "my-app.docker.localhost")'
# Show requests with duration > 1 second (1,000,000,000 nanoseconds)
docker logs traefik | jq -R 'fromjson? | select(.Duration > 1000000000)'
# Extract client IPs
docker logs traefik | jq -R 'fromjson? | select(.ClientHost != null) | .ClientHost' | sort | uniq -c | sort -rn
# Show all backend servers used
docker logs traefik | jq -R 'fromjson? | select(.DownstreamServer != null) | .DownstreamServer' | sort | uniqNote: The -R 'fromjson?' pattern safely handles mixed log output (regular Traefik logs + JSON access logs). It attempts to parse each line as JSON and skips non-JSON lines.
Example JSON Log Entry:
{
"ClientAddr": "172.20.0.1:54321",
"ClientHost": "172.20.0.1",
"ClientPort": "54321",
"ClientUsername": "-",
"DownstreamContentSize": 1234,
"DownstreamStatus": 200,
"Duration": 12345678,
"OriginContentSize": 1234,
"OriginDuration": 12000000,
"OriginStatus": 200,
"Overhead": 345678,
"RequestAddr": "my-app.docker.localhost",
"RequestHost": "my-app.docker.localhost",
"RequestMethod": "GET",
"RequestPath": "/api/users",
"RequestPort": "443",
"RequestProtocol": "HTTP/2.0",
"RequestScheme": "https",
"RouterName": "my-app@docker",
"ServiceName": "my-app@docker",
"StartUTC": "2024-01-28T10:15:30.123456789Z",
"entryPointName": "websecure"
}Key Fields:
RequestHost: Domain name requestedRequestPath: URL pathRequestMethod: HTTP method (GET, POST, etc.)DownstreamStatus: HTTP response status codeDuration: Total request time in nanoseconds (1s = 1,000,000,000ns)ClientHost: IP address of the clientRouterName: Traefik router that handled the requestDownstreamServer: Backend container that served the request
Debugging Routing Issues:
# Check if requests are reaching Traefik (follow in real-time)
docker logs traefik --follow | jq -R 'fromjson? | select(.RequestPath != null) | {host: .RequestHost, path: .RequestPath, status: .DownstreamStatus}'
# Find which backend served a request
docker logs traefik | jq -R 'fromjson? | select(.RequestPath == "/api/users") | .DownstreamServer'
# Check for routing errors (502, 503, 504)
docker logs traefik | jq -R 'fromjson? | select(.DownstreamStatus >= 502 and .DownstreamStatus <= 504)'Performance Analysis:
# Find slow requests (> 5 seconds = 5,000,000,000 nanoseconds)
docker logs traefik | jq -R 'fromjson? | select(.Duration > 5000000000) | {host: .RequestHost, path: .RequestPath, duration_s: (.Duration / 1000000000)}'
# Average response time by endpoint
docker logs traefik | jq -R 'fromjson? | select(.RequestPath != null) | "\(.RequestPath) \(.Duration)"' | awk '{sum[$1]+=$2; count[$1]++} END {for (path in sum) print path, sum[path]/count[path]/1000000000 "s"}'Security Monitoring:
# List all unique client IPs
docker logs traefik | jq -R 'fromjson? | select(.ClientHost != null) | .ClientHost' | sort | uniq
# Find requests with authentication errors (401)
docker logs traefik | jq -R 'fromjson? | select(.DownstreamStatus == 401)'
# Check for suspicious paths
docker logs traefik | jq -R 'fromjson? | select(.RequestPath != null) | select(.RequestPath | test("admin|config|env|\\.\\."))'Access logs are written to stdout and automatically rotated by Docker's logging driver with the same settings as other container logs:
- Max File Size: 10 MB per file
- Max Files: 3 files retained
- Compression: Enabled
To disable access logs when not needed:
Set TRAEFIK_ACCESS_LOG_ENABLED=false in .env and restart:
docker compose up -d traefikAccess logs are typically only needed during active debugging and add volume to the container logs.
All Logs (Container + Access):
- Managed by Docker daemon
- Physical location:
/var/lib/docker/containers/<container-id>/<container-id>-json.log - Access via:
docker logs <container-name>(recommended) - Rotation: Automatic (configured in docker-compose.yml)
- Compression: Enabled for rotated files
Traefik Access Logs (when enabled):
- Written to stdout alongside regular Traefik logs
- Captured by Docker's logging driver
- Same rotation policy as container logs (10 MB per file, 3 files max)
- Access via:
docker logs traefik
Note: All logs are managed exclusively through Docker's logging driver. There are no separate log files in the project directory.
Symptom: docker logs returns no output or very little output.
Possible Causes:
- Container recently restarted (logs are lost on restart unless using external logging drivers)
- Container is not producing any log output
- Log rotation removed old logs
Solutions:
# Check container status
docker ps -a | grep traefik
# Verify logging configuration
docker inspect traefik | grep -A 10 "LogConfig"
# Check if container is running
docker compose ps
# Force container restart to see startup logs
docker compose restart traefik && docker logs traefik --followSymptom: No access log entries appear in docker logs traefik output after enabling.
Solutions:
# Verify environment variable is set correctly
docker exec traefik ps aux | grep accesslog
# Check Traefik command-line flags
docker inspect traefik | grep -i accesslog
# Restart Traefik to apply changes
docker compose up -d traefik
# Generate traffic to trigger access logs
curl https://traefik.docker.localhost
# Check logs again (access logs are JSON, mixed with regular logs)
docker logs traefik --tail 50 | grep RequestPathSymptom: System warns about disk space despite log rotation.
Investigation:
# Check size of all container logs
sudo du -sh /var/lib/docker/containers/*/
# Check total Docker disk usage
docker system df -v
# Check individual container log sizes
docker ps --format '{{.Names}}' | xargs -I {} sh -c 'echo -n "{}: " && sudo du -sh /var/lib/docker/containers/$(docker inspect -f "{{.Id}}" {})/ 2>/dev/null | cut -f1'Solutions:
# Clean up old Docker resources
docker system prune -a
# Reduce log retention (edit docker-compose.yml x-logging anchor)
# Change max-size to "5m" or max-file to "2"
# Disable Traefik access logs to reduce log volume
# Set TRAEFIK_ACCESS_LOG_ENABLED=false in .env
docker compose up -d traefik
# Recreate containers to reset logs
docker compose down
docker compose up -dSymptom: jq fails with "parse error" when parsing Traefik logs.
Causes:
- Mixed log output (regular Traefik logs + JSON access logs)
- Changed format while Traefik was running
- Attempting to parse non-JSON log lines
Solutions:
# Use the safe parsing pattern that skips non-JSON lines
docker logs traefik | jq -R 'fromjson? | select(.RequestPath != null)'
# This pattern:
# -R: Read each line as a raw string
# fromjson?: Attempt to parse JSON (? suppresses errors for non-JSON lines)
# select(.RequestPath != null): Filter only access log entries
# To debug, see what's not parsing:
docker logs traefik | jq -R 'fromjson? | select(. == null)'
# Validate a specific log section
docker logs traefik --tail 100 | jq -R 'fromjson? | select(.DownstreamStatus != null)'Symptom: Logs include passwords, tokens, or other sensitive information.
Prevention:
# For Traefik access logs: Use filters to exclude headers
# (Requires custom Traefik configuration beyond this guide)
# For container logs: Ensure applications don't log secrets
# Review application logging configurationRemediation:
# Recreate containers to clear all logs
docker compose down
docker compose up -d
# Or, manually remove container log files (requires sudo)
docker compose stop
sudo rm -rf /var/lib/docker/containers/$(docker inspect -f '{{.Id}}' traefik)/*-json.log*
docker compose startRecommendation: Only enable Traefik access logs when actively debugging.
# Enable for debugging
TRAEFIK_ACCESS_LOG_ENABLED=true
# Disable after resolving issues
TRAEFIK_ACCESS_LOG_ENABLED=falseReason: Access logs add significant volume to container logs (each HTTP request generates a log entry). While they are automatically rotated by Docker's logging driver, they can quickly fill the allocated log space (30 MB max).
Recommendation: Periodically check Docker's disk usage:
# Quick check
docker system df
# Detailed view
docker system df -v
# Check container log sizes
docker ps --format '{{.Names}}' | xargs -I {} sh -c 'echo -n "{}: " && docker inspect {} | jq -r ".[0].LogPath" | xargs sudo du -sh 2>/dev/null'Recommendation: Keep TRAEFIK_ACCESS_LOG_FORMAT=json for easier parsing.
Example:
# JSON format enables powerful queries
docker logs traefik | jq -R 'fromjson? | select(.DownstreamStatus >= 500)'
# Common format would be harder to parse when mixed with regular logs
docker logs traefik | awk '$9 >= 500' # Less reliable with mixed outputRecommendation: Use docker logs with filters instead of viewing full logs:
# Instead of: docker logs traefik | less
# Use targeted queries:
docker logs traefik --since 1h | grep error
docker logs traefik --tail 100 | grep "my-app"Good News: All logs (including access logs) are automatically rotated by Docker's logging driver.
Configuration:
- Max size: 10 MB per file
- Max files: 3 files retained
- Compression: Enabled for rotated files
No manual rotation needed! Docker handles everything automatically.
Recommendation: Use timestamps to correlate events across services:
# View logs from multiple services with timestamps
docker compose logs --timestamps traefik mysql | sort
# Find events around a specific time
docker logs traefik --since "2024-01-28T10:00:00" --until "2024-01-28T10:05:00"Recommendation: Before restarting containers during troubleshooting, save logs:
# Save logs before restart
docker logs traefik > traefik-debug-$(date +%Y%m%d-%H%M%S).log
docker compose restart traefikReason: Container logs are lost when a container is removed (but not on restart).
Recommendation: Configure application log levels based on environment:
- Development: INFO or DEBUG
- Production: WARNING or ERROR
For Traefik, log level is controlled via command flags (not currently configured in this setup).
Recommendation: Never log:
- Passwords or API keys
- Personally identifiable information (PII)
- Financial data
- Session tokens
Implementation: Review application code and configuration to ensure sensitive data is redacted or excluded from logs.
Recommendation: If you customize logging (e.g., add external logging drivers, change rotation settings), document changes in your project's README.
Example:
## Custom Logging Configuration
This project uses a custom log rotation policy:
- max-size: 5m (reduced from 10m)
- max-file: 5 (increased from 3)
- Access logs enabled by default for monitoringHave questions or suggestions? Open an issue in the GitHub repository or check the Integration Guide for related topics.