beefproject / beefproject/beef
fix(docker): healthcheck passes literal `$UI_PORT` to curl
- Dominant language
- JavaScript
- Stars
- 11k
- Forks
- 2.4k
- Avg merge
- 1d 1h
- Merged PRs (30d)
- 15
Description
## Summary
The repository `Dockerfile` defines its healthcheck in JSON exec form and passes
`localhost:$UI_PORT` as a literal argv element. Exec-form healthchecks do not
run through a shell, so `$UI_PORT` is not expanded. The built image consequently
contains a healthcheck URL with a non-numeric port and cannot report healthy,
even when BeEF is listening on its default port.
## Steps to reproduce
1. Check out BeEF revision
`7f4d40432f84b82098433008d5dc6d9be64053df`.
2. Inspect the relevant Dockerfile instructions:
```dockerfile
ARG UI_PORT=3000
EXPOSE $UI_PORT $PROXY_PORT $WEBSOCKET_PORT $WEBSOCKET_SECURE_PORT
HEALTHCHECK --interval=30s --timeout=30s --start-period=5s --retries=3 CMD [ "curl", "-fS", "localhost:$UI_PORT" ]
```
3. Build the image, optionally using a non-default value to make the distinction
explicit:
```sh
docker build --build-arg UI_PORT=4321 -t beef-healthcheck-test .
```
4. Inspect the stored healthcheck command:
```sh
docker image inspect beef-healthcheck-test \
--format '{{json .Config.Healthcheck.Test}}'
```
5. Observe that the result still contains the literal variable reference:
```json
["CMD","curl","-fS","localhost:$UI_PORT"]
```
A direct invocation demonstrates why the stored argv cannot probe the service:
```text
$ curl -fS 'localhost:$UI_PORT'
curl: (3) URL rejected: Port number was not a decimal number between 0 and 65535
```
## Expected behavior
The image healthcheck probes a numeric in-container HTTP port that matches the
BeEF listener, defaulting to port 3000. If `UI_PORT` is intended to be a
configurable image input, the Docker metadata and the application configuration
should use the same resolved value.
## Actual behavior
- `UI_PORT` is declared as a build-time `ARG` in the final image stage.
- Docker expands it for Dockerfile instructions such as `EXPOSE`, but the JSON
healthcheck stores each argument literally.
- No shell processes the healthcheck argv, so `$UI_PORT` remains unexpanded.
- The image therefore invokes curl with `localhost:$UI_PORT`, which curl rejects
as an invalid port before making an HTTP request.
Publishing `UI_PORT` as an `ENV` by itself would not fix this JSON exec-form
command: environment variables are not interpolated inside an argv element.
The healthcheck must use a resolved numeric value or explicitly invoke a shell
with a runtime environment variable.
## Affected area
- Root `Dockerfile` healthcheck definition
- Docker and Compose workflows that wait for the image's Docker health status
- Orchestrators that consume the Docker image healthcheck rather than defining
an independent probe
## Runtime or environment
- BeEF: `0.6.0.0`
- Revision: `7f4d40432f84b82098433008d5dc6d9be64053df`
- Dockerfile base: `ruby:3.4.7-slim-bookworm`
- Verified with Docker Engine `29.5.3`
- Verified: 2026-08-06
## Evidence
A minimal image using the same Dockerfile construct and
`--build-arg UI_PORT=4321` produced:
```json
["CMD","curl","-fS","localhost:$UI_PORT"]
```
The image environment did not contain `UI_PORT`. This independently confirms
the Dockerfile behavior without requiring BeEF's Ruby dependencies or valid
Admin UI credentials.
No open or closed BeEF issue matching `HEALTHCHECK` plus `UI_PORT` was found in
the repository issue search on 2026-08-06.
## Impact
Containers running BeEF can remain `unhealthy` even when the HTTP listener is
working. Automation that gates readiness or dependent services on Docker health
can time out, restart the container, or require operators to override the
upstream healthcheck.
The defect affects the in-container probe. Host port publishing (for example,
`-p 8080:3000`) is separate and does not require the healthcheck to use the host
port.
## Additional context
- Target upstream: https://github.com/beefproject/beef
- BeEF still requires non-default credentials before the normal image entrypoint
will keep the server running. That startup gate is independent of the invalid
healthcheck argv.
- Closure signal: image inspection shows a numeric or shell-expanded healthcheck
target, and a normally configured container listening on the expected internal
port reaches Docker's `healthy` state.
## Blocked by
None — can start immediately
Contributor guide
Research direction
Start with the healthcheck definition in the root Dockerfile and inspect how Docker stores it after building with a non-default UI_PORT. Verify the resulting image healthcheck with docker image inspect and confirm that a normally configured container reaches healthy status using a numeric or shell-expanded internal port.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- docker
- Domain
- devops
- Issue type
- Bug
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 78/100