kestra-io/kestra · error · PebbleException

Unable to get random port

Error message

Unable to get random port

What it means

The randomPort() Pebble function opens a ServerSocket on port 0 to let the OS assign an ephemeral port, then returns it. It throws a PebbleException wrapping the original IOException when the socket cannot be created. This is a thin JVM-level operation: the function takes no arguments and simply asks the OS for a free TCP port.

Source

Thrown at core/src/main/java/io/kestra/core/runners/pebble/functions/RandomPortFunction.java:20

import java.io.IOException;
import java.net.ServerSocket;
import java.util.List;
import java.util.Map;

import io.pebbletemplates.pebble.error.PebbleException;
import io.pebbletemplates.pebble.template.EvaluationContext;
import io.pebbletemplates.pebble.template.PebbleTemplate;

public class RandomPortFunction implements KestraFunction {
    public static final String NAME = "randomPort";

    @Override
    public Object execute(Map<String, Object> args, PebbleTemplate self, EvaluationContext context, int lineNumber) {
        try (ServerSocket tempSocket = new ServerSocket(0)) {
            return tempSocket.getLocalPort();
        } catch (IOException e) {
            throw new PebbleException(
                e,
                "Unable to get random port",
                lineNumber,
                self.getName()
            );
        }
    }

    @Override
    public List<String> getArgumentNames() {
        return List.of();
    }

    @Override
    public Map<String, String> getArgumentDefaults() {
        return Map.of();
    }
}

View on GitHub (pinned to 823fada927)

Solutions

  1. Check the OS file-descriptor and socket limits (ulimit -n, sysctl net.ipv4.ip_local_port_range) and raise them if the error occurs under load.
  2. Avoid calling randomPort() repeatedly in parallel tasks; cache the result in a flow variable or input and pass it down.
  3. If running in a sandboxed container, ensure the seccomp/AppArmor profile permits the socket(2) syscall.
  4. Fall back to a static port assignment from flow inputs or environment variables instead of relying on dynamic allocation.

Example fix

# before (exhausts ephemeral ports under parallel load)
id: many-parallel-tasks
tasks:
  - id: each
    type: io.kestra.plugin.core.flow.ForEach
    value: "{{ range(1, 100) }}"
    tasks:
      - id: use_port
        type: io.kestra.plugin.scripts.shell.Commands
        commands:
          - "echo {{ randomPort() }}"

# after (allocate once, reuse)
id: allocate-port-once
inputs:
  - id: port
    type: INT
    defaults: "{{ randomPort() }}"
tasks:
  - id: use_port
    type: io.kestra.plugin.scripts.shell.Commands
    commands:
      - "echo {{ inputs.port }}"
Defensive patterns

Strategy: retry

Validate before calling

# Before calling randomPort() in a high-throughput flow, verify OS socket limits are healthy:
# (run on the Kestra worker host)
# ulimit -n          # check file descriptor limit
# sysctl net.ipv4.ip_local_port_range  # check ephemeral port range
# If calling from a script task, pre-check socket availability:
# python3 -c "import socket; s=socket.socket(); s.bind(('',0)); print(s.getsockname()[1]); s.close()"

Prevention

When it happens

Trigger: Calling {{ randomPort() }} in a Pebble expression when the OS refuses to open a new socket — e.g., the process has exhausted its file-descriptor limit (ulimit -n), a SecurityManager denies ServerSocket creation, the network stack is unavailable (container with no networking), or all ephemeral ports are in use.

Common situations: Running Kestra in a locked-down container with a very low ulimit or seccomp profile that blocks socket creation. CI runners or sandboxed workers where networking is disabled. A flow that calls randomPort() in a tight loop across many parallel task runs, exhausting ephemeral ports (TIME_WAIT storm).

Related errors


AI-assisted analysis of kestra-io/kestra@823fada927 (2026-08-14). Data as JSON: /api/errors/d2fa2d34c52a04a0. Report an issue: GitHub.