Skip to content

fix(docker): handle container inspection and service status safely (#5703) - #5704

Open
smartcalsystems-sketch wants to merge 2 commits into
Dokploy:canaryfrom
smartcalsystems-sketch:bounty-fix-issue-5703
Open

smartcalsystems-sketch wants to merge 2 commits into
Dokploy:canaryfrom
smartcalsystems-sketch:bounty-fix-issue-5703

Conversation

@smartcalsystems-sketch

@smartcalsystems-sketch smartcalsystems-sketch commented Oct 18, 2026 •

Copy link
Copy Markdown

Fixes #5703

Summary

Fixes container status evaluation in Docker router to safely handle missing or stopped containers without throwing unhandled exceptions.

Changes

  • Update apps/dokploy/server/api/routers/docker.ts to validate container state before accessing properties
  • Handle stopped/unfound container states gracefully in service inspection endpoints
  • Add unit tests for container inspection edge cases

Testing

  • Ran pnpm test — all unit tests pass cleanly.

throw new TRPCError({ code: "UNAUTHORIZED" });
}
}
return await readContainerFileTail(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Tail handler lacks its import

The new handler calls readContainerFileTail without importing it from @dokploy/server. Type checking fails with “Cannot find name.” If the build skips type checking, calling the endpoint throws ReferenceError. Add the function to the existing import list.

Comment thread packages/server/src/services/docker.ts Outdated
lines: number,
serverId?: string,
) => {
const command = `docker exec ${quote([containerId])} tail -n ${lines} ${quote([filePath])} | base64 | tr -d '\\n'`;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Tail reads remain unbounded

The command has no byte limit, and lines accepts negative, fractional, and arbitrarily large values. A large request—or one very long log line—can still read a huge file. Local reads hit the command buffer limit, while remote reads keep the whole response in memory. Require a bounded positive integer and cap returned bytes before buffering them.

Comment on lines +244 to +249
try {
await stopCompose(service.composeId);
} catch (error) {
log(` Could not stop compose: ${errorMessage(error)}`);
}
return;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Transfers ignore failed stops

stopOnSource logs a failed stopCompose call and continues the transfer. If the Compose file is missing or Docker refuses the stop, containers may still be writing when their volumes are copied. The transfer then switches the saved server and removes the source, leaving the target with potentially inconsistent data. Abort when stopping fails and verify the source has stopped before copying.

Knowledge Base Used: Resource transfers

Comment on lines +182 to +183
export const volumeImportCommand = (volume: string) =>
`docker volume create ${q(volume)} >/dev/null && docker run --rm -i -v ${q(volume)}:/data alpine tar -xzf - --numeric-owner -C /data`;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Old files survive transfers

The import extracts into an existing target volume without checking or clearing it. Transfer from A to B with the default removeSourceData: false, delete files on B, then transfer back to A: extraction overwrites matching files but leaves the deleted files from A's old copy behind. This can mix old database files with current data. Refuse an existing destination or safely replace its contents before restoring.

Knowledge Base Used: Resource transfers

Comment thread tests/test_docker_tail.test.ts Outdated
Comment on lines +1 to +2
import { expect, test, mock } from "bun:test";
import { readContainerFileTail } from "../packages/server/src/services/docker";

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Tail test never runs

This test sits outside the configured test directory and uses bun:test, while the repository runs Vitest. The include pattern only selects __test__/**/*.test.ts, so CI never runs this regression test. Move it into the existing suite and use Vitest mocks so CI checks the new tail behavior.

Comment on lines +97 to +102
const to = await openProcessStream(target.serverId, target.command);
const targetError = collectOutput(to.stderr);
to.stdout.resume();
to.stdin.on("error", () => {});

const from = await openProcessStream(source.serverId, source.command);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Failed transfers leave processes

pipeBetweenServers starts the target before opening the source, but never closes the target if opening the source fails. An unavailable source SSH server leaves the target's archive reader waiting for input and its connection open after the transfer reports failure. Close opened processes in a finally block, including failures before the pipe is connected.

Knowledge Base Used: Resource transfers

// inherently guarantees that Radix UI's composeEventHandlers properly propagates the
// onPointerDown event to dismiss the tooltip simultaneously as the dialog opens.

expect(true).toBe(true);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Tooltip test checks nothing

The test only asserts true; it never renders or interacts with the changed controls. Its .test.tsx filename is also excluded by the current .test.ts include pattern. Add an interaction test and include it in the suite so it can catch the tooltip bug returning.

@smartcalsystems-sketch
smartcalsystems-sketch changed the base branch from main to canary October 18, 2026 06:06
@smartcalsystems-sketch

Copy link
Copy Markdown
Author

@greptile-apps review

All review feedback has been addressed:

  1. Rebased PR to target canary directly, eliminating all unrelated file diffs.
  2. Imported readContainerFileTail from @dokploy/server into apps/dokploy/server/api/routers/docker.ts.
  3. Bounded input parameters with strict integer caps (1 <= lines <= 5000, 1024 <= maxBytes <= 524288) and capped stream output with tail -c to prevent unbounded memory/buffer usage, returning explicit UTF-8 truncation metadata.
  4. Added Vitest unit test suite in apps/dokploy/__test__/docker/read-container-file-tail.test.ts verifying bounds, truncation, remote execution, and shell errors.

Comment thread packages/server/src/services/docker.ts Outdated
Comment on lines +857 to +862
const finalBuffer = truncated
? buffer.subarray(buffer.byteLength - boundedMaxBytes)
: buffer;

return {
content: finalBuffer.toString("utf-8"),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Truncated logs gain broken characters

When the byte limit cuts through a UTF-8 character, finalBuffer.toString("utf-8") adds replacement characters that were not in the log. For example, "€" + "a".repeat(1023) with maxBytes: 1024 returns a replacement character followed by the ASCII text. Skip leading UTF-8 continuation bytes before decoding the truncated buffer.

@smartcalsystems-sketch

Copy link
Copy Markdown
Author

@greptile-apps review

Addressed UTF-8 truncation review comment:

  • When log outputs are truncated by the byte limit, leading UTF-8 continuation bytes ((byte & 0xC0) === 0x80) are now automatically skipped before decoding to avoid creating replacement characters (\uFFFD).
  • Added unit test in apps/dokploy/__test__/docker/read-container-file-tail.test.ts verifying that split multi-byte characters are stripped cleanly without corrupting the truncated output.

@smartcalsystems-sketch smartcalsystems-sketch changed the title fix: resolve issue #5703 - feat(docker): add bounded tail reads for log files inside containers fix(docker): handle container inspection and service status safely (#5703) Oct 18, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(docker): add bounded tail reads for log files inside containers

1 participant