Traefik Access Log Parsing#
Dokploy reads Traefik's JSON-format access logs from disk and exposes a filtered, paginated view in the Requests dashboard tab. This is entirely server-side: the raw log file is read on each API call, filtered in memory, and the result set is returned β no database is involved.
Log File Location & Reading#
readMonitoringConfig in packages/server/src/utils/traefik/application.ts reads the log file at:
${DYNAMIC_TRAEFIK_PATH}/access.log
- Production:
/etc/dokploy/traefik/dynamic/access.log - Development:
.docker/traefik/dynamic/access.log
When no date-range filter is active, it streams the file and returns the first 500 valid JSON lines to avoid loading the entire file into memory . When a date range is provided, readAll=true is passed and the full file is read .
Internal dashboard traffic is excluded at read time: lines with ServiceName === "dokploy-service-app@file" are skipped .
LogEntry Schema#
Each log line is parsed into a LogEntry object (packages/server/src/utils/access-log/types.ts). Key fields used in filtering and display:
| Field | Purpose |
|---|---|
RequestHost | Hostname filter target (search) |
DownstreamStatus | HTTP status code filter |
StartUTC | Date-range filter |
ServiceName | Internal traffic exclusion |
Duration, OriginDuration, Overhead | Timing (nanoseconds) |
RequestMethod, RequestPath | Displayed in the UI |
Parsing & Filtering Pipeline#
parseRawConfig in packages/server/src/utils/access-log/utils.ts applies filters in this order:
- JSON validation β splits on newlines, keeps only lines that start with
{and end with} - Internal service exclusion β drops
dokploy-service-app@fileentries - Date range β compares
log.StartUTCagainst optionalstart/endISO strings - Hostname search β case-insensitive substring match on
log.RequestHost - Status filter β maps named buckets (
info,success,redirect,client,server) to HTTP status ranges viaisStatusInRange - Sort β
_.orderByon anyLogEntryfield; defaults totime desc - Pagination β slice by
pageIndex * pageSize
Returns { data: LogEntry[], totalCount: number }.
Note:
totalCountreflects the post-filter, pre-pagination count, so the UI can compute page counts correctly.
Hostname Filtering Fix (PR #4255)#
Prior to PR #4255, the search filter matched against RequestPath (the URL path) instead of RequestHost. The fix updated parseRawConfig to use log.RequestHost and changed the UI placeholder from "Filter by name..." to "Filter by hostname..." .
tRPC API: settings.readStatsLogs#
The readStatsLogs tRPC procedure (protectedProcedure) wires everything together:
- Disabled in cloud mode β returns empty data when
IS_CLOUDis true - Accepts:
page,sort,search,status[],dateRange - Calls
readMonitoringConfigthenparseRawConfigand returns the result
Frontend: RequestsTable#
RequestsTable in apps/dokploy/components/dashboard/requests/requests-table.tsx consumes settings.readStatsLogs via api.settings.readStatsLogs.useQuery(...):
- Polls every 1333 ms
- Manages state for
search,statusFilter,sorting,pagination, anddateRange - Uses manual pagination (
manualPagination: true) β TanStack Table defers all paging to the server - Status filter options:
info(1xx),success(2xx),redirect(3xx),client(4xx),server(5xx) - Clicking a row opens a detail sheet; individual entries can be downloaded as JSON
Chart View: processLogs#
A parallel utility, processLogs, aggregates the same log data into hourly bucket counts for the chart view. It applies the same date-range and internal-service exclusion logic but does not filter by hostname or status β it simply counts entries per hour .
Key Source Files#
| File | Role |
|---|---|
packages/server/src/utils/access-log/types.ts | LogEntry type |
packages/server/src/utils/access-log/utils.ts | parseRawConfig, processLogs, isStatusInRange |
packages/server/src/utils/traefik/application.ts | readMonitoringConfig (reads from disk) |
apps/dokploy/server/api/routers/settings.ts | readStatsLogs tRPC endpoint |
apps/dokploy/components/dashboard/requests/requests-table.tsx | Requests dashboard UI |