Skip to main content

Documentation Index

Fetch the complete documentation index at: https://tally.wharflab.com/llms.txt

Use this file to discover all available pages before exploring further.

Either use Wget or Curl but not both.
PropertyValue
SeverityWarning
CategoryBest Practice
DefaultEnabled
Auto-fixYes (--fix --fix-unsafe; AI AutoFix fallback)

Description

Don’t install two tools that have the same effect. Using both wget and curl in a Dockerfile adds unnecessary cruft to the image. Pick one and use it consistently. The rule fires when both tools are present in the Dockerfile in any combination: both invoked, both installed, or one installed while the other is invoked. Installing the second tool is itself the offense — even if you never call it, it still inflates the image.

Examples

Problematic code

FROM debian
RUN wget http://google.com
RUN curl http://bing.com

Correct code

FROM debian
RUN curl http://google.com
RUN curl http://bing.com

Auto-fix

The rule rewrites offending commands to the tool it picks as the winner for the Dockerfile. Rewriting is bidirectional: curl → wget or wget → curl. All fixes are unsafe (FixUnsafe) and require --fix --fix-unsafe.

How the winning tool is chosen (auto mode)

Auto mode uses usage-based heuristics, not tool-install flags. The winner — and therefore the direction of the rewrite — is decided in this order:
  1. Used without explicit install wins. If exactly one of curl / wget is used without being explicitly installed by the Dockerfile (e.g., it comes from the base image), that tool wins and the other one is rewritten. Installing the tool you already get “for free” is the thing DL4001 is pushing back on.
  2. Most invocations wins. If both tools are in the same install category (both installed, or both used-without-install), the tool with more total invocations across the Dockerfile wins.
  3. First seen wins. If invocation counts tie, the tool whose first use appears earlier in the Dockerfile wins.
The fix runs in two phases:
  • Rewrite each invocation. Tally doesn’t try to translate curl flags into the equivalent wget flags argument-by-argument — that’s a fragile game with many edge cases. Instead, each offending command is read as a single HTTP action (“download this URL into this file”, “download this URL and pipe it into tar”, etc.), and the replacement is produced from scratch using whichever target tool was chosen. What matters for a Docker image build is the outcome — which bytes end up where, which errors fail the build — and that’s what the rewrite preserves. When a command is too elaborate to reinterpret this way (non-HTTP features, shell interpolation, custom scripting), tally falls back to the AI AutoFix path, which additionally requires an ACP-capable agent configured in the top-level [ai] section.
  • Clean up what’s left. After all the invocation rewrites are applied, tally re-reads the resulting Dockerfile and removes anything that exists only to serve the evicted tool:
    • the tool’s entry in install commands (dropped from the package list, or the whole install RUN if it only installed that one tool),
    • COPY heredocs that write the tool’s config file (.curlrc / .wgetrc / /etc/wgetrc),
    • ENV bindings that point at those config paths (CURL_HOME, WGETRC, WGETHOSTS),
    • any annotation comments tally itself added when it inserted the config.
Doing the cleanup after the main rewrite is what lets DL4001 play nicely with peer rules. If tally/sort-packages reorders the install line or tally/prefer-curl-config inserts a .curlrc heredoc, those edits are already applied by the time cleanup runs, so the cleanup sees the finished state and does the right thing instead of fighting anyone for the same source range. The rule refuses to attach a sync fix when the preferred tool already appears in the same RUN (for example curl ... || wget ...). On Windows stages the replacement is emitted with a .exe suffix (curl.exe, wget.exe); on Linux stages the bare tool name is used.

Deterministic rewrite examples

# Before — curl is explicitly installed, wget comes from the base image,
# so wget wins (used-without-install) and the installed curl is rewritten.
RUN apt-get update && apt-get install -y curl
RUN curl -fsSL https://example.com/bootstrap.tgz
RUN wget https://example.com/file.tgz

# After (with --fix --fix-unsafe)
RUN apt-get update && apt-get install -y curl
RUN wget -nv -O- https://example.com/bootstrap.tgz
RUN wget https://example.com/file.tgz
# Before — both tools are installed and both are used. curl wins the first-seen
# tie-break, and wget is dropped from the install list so it isn't pulled at all.
RUN apt-get update && apt-get install -y curl wget
RUN curl https://example.com/bootstrap.tgz
RUN wget https://example.com/file.tgz

# After (with --fix --fix-unsafe)
RUN apt-get update && apt-get install -y curl
RUN curl https://example.com/bootstrap.tgz
RUN curl -fL -O https://example.com/file.tgz
# Before — neither tool is installed (both come from the base image) and
# curl is used twice vs wget once, so curl wins the count tie-break.
RUN wget https://example.com/file1
RUN curl https://example.com/file2
RUN curl https://example.com/file3

# After (with --fix --fix-unsafe)
RUN curl -fL -O https://example.com/file1
RUN curl https://example.com/file2
RUN curl https://example.com/file3

When the AI AutoFix fallback is used

Examples where deterministic lowering cannot preserve Dockerfile-relevant behavior and the rule falls back to AI AutoFix:
# curl without -L: redirect-following semantics cannot be preserved deterministically.
RUN curl -fsS -o /tmp/file https://example.com/file
# curl without -f: fail-on-HTTP-status semantics cannot be preserved deterministically.
RUN curl -sSL https://example.com/app.tgz | tar -xz -C /opt

Configuration

fix-preference

  • Type: string
  • Allowed values: "auto", "curl", "wget"
  • Default: "auto"
Controls which tool auto-fixes converge on, and therefore which of the two offending tools is reported as the violation.
  • "auto" (default): pick the winner using the usage-based heuristics described in the Auto-fix section above (used-without-install → invocation count → first seen).
  • "curl": always report and rewrite wget calls to curl, regardless of usage or install state.
  • "wget": always report and rewrite curl calls to wget, regardless of usage or install state.
Explicit preferences are useful when you want a project-wide convention and don’t want auto mode’s heuristics to decide per-file. An invalid value falls back to "auto".
[rules.hadolint.DL4001]
fix-preference = "curl"

Reference