Skip to content

Repository files navigation

N.E.V.E.R. - Network Endpoint Validation with Endless Retries

Go ReferenceReleaseGitHub tagUnit TestsLintBuildlicense


N.E.V.E.R. (Network Endpoint Validation with Endless Retries) is a lightweight Go application that obsessively checks whether a TCP, HTTP, or ICMP target is reachable. It loops endlessly until the target responds — or until it’s killed.

Designed to run as a Kubernetes initContainer, N.E.V.E.R. ensures your service dependencies are fully up before anything else gets a chance to boot.

Features

  • Continuously retries until the target responds.
  • Supports multiple concurrent targets, each with its own config.
  • Configurable via command-line flags or environment variables.
  • Supports HTTP, TCP, and ICMP readiness checks.
  • Supports per-target retry backoff and max attempts.
  • Exits with 0 the moment everything is ready.
  • Exits with 1 if any target exceeds --max-attempts.

Whether you're waiting on a port, ping, or a 200 OK, N.E.V.E.R. backs down never.

Configuration

Flags can also be set through environment variables.

The environment variable prefix is:

NEVER__

Two underscores are used intentionally to avoid overlapping with common Kubernetes-style names such as *_SVC, *_PORT, or similar service-discovery variables.

Flag names are converted to environment variables by:

  • removing the leading --
  • replacing dots and hyphens with underscores
  • uppercasing the name
  • prefixing it with NEVER__

Examples:

FlagEnvironment variable
--default-intervalNEVER__DEFAULT_INTERVAL
--max-attemptsNEVER__MAX_ATTEMPTS
--log-formatNEVER__LOG_FORMAT
--http.web.addressNEVER__HTTP_WEB_ADDRESS
--http.web.expected-status-codesNEVER__HTTP_WEB_EXPECTED_STATUS_CODES
--tcp.db.timeoutNEVER__TCP_DB_TIMEOUT
--icmp.host.timeoutNEVER__ICMP_HOST_TIMEOUT

Command-line flags take precedence over environment variables.

Command-Line Flags

never accepts the following command-line flags:

Common Flags

FlagEnv varTypeDefaultDescription
--default-intervalNEVER__DEFAULT_INTERVALduration2sDefault interval between checks. Can be overridden for each target.
--max-attemptsNEVER__MAX_ATTEMPTSint-1Maximum attempts before giving up. Use -1 to retry endlessly.
--log-formatNEVER__LOG_FORMATenumjsonLog output format: json or text.
--versionboolfalseShow version and exit.
--help, -hboolfalseShow help.

Target Flags

never accepts dynamic flags that can be defined in startup arguments or environment variables.

Use the following flag format:

--<TYPE>.<IDENTIFIER>.<PROPERTY>=<VALUE>

Use the following environment variable format:

NEVER__<TYPE>_<IDENTIFIER>_<PROPERTY>=<VALUE>

Types are:

  • http
  • icmp
  • tcp

Examples:

--http.web.address=http://example.com
NEVER__HTTP_WEB_ADDRESS=http://example.com
--tcp.db.address=postgres.default.svc.cluster.local:5432
NEVER__TCP_DB_ADDRESS=postgres.default.svc.cluster.local:5432
--icmp.host.address=example.com
NEVER__ICMP_HOST_ADDRESS=example.com

HTTP Flags

FlagTypeDefaultDescription
--http.<IDENTIFIER>.namestring<IDENTIFIER>Name of the HTTP checker.
--http.<IDENTIFIER>.addressstringrequiredHTTP target URL. *
--http.<IDENTIFIER>.intervalduration0Time between HTTP requests. Uses --default-interval when unset or 0.
--http.<IDENTIFIER>.max-attemptsint0Maximum attempts before giving up. Uses --max-attempts when unset or 0.
--http.<IDENTIFIER>.backoffenumlinearRetry backoff mode. Allowed values: linear, exponential.
--http.<IDENTIFIER>.max-intervalduration0Maximum retry interval when backoff increases the delay. Uncapped when unset or 0.
--http.<IDENTIFIER>.methodenumGETHTTP method. Allowed values: GET, HEAD, POST, PUT, PATCH, DELETE, CONNECT, OPTIONS, TRACE.
--http.<IDENTIFIER>.headerstring listemptyHTTP header in KEY=VALUE format. Can be passed multiple times as a flag. Header values can be resolved. *
--http.<IDENTIFIER>.allow-duplicate-headersboolfalseAllow duplicate HTTP headers.
--http.<IDENTIFIER>.expected-status-codesstring list200Expected HTTP status codes. Supports comma-separated codes and ranges, for example 200,204,301-302.
--http.<IDENTIFIER>.skip-tls-verifyboolfalseSkip TLS certificate verification.
--http.<IDENTIFIER>.timeoutduration2sHTTP request timeout.

Environment variables use NEVER__HTTP_<IDENTIFIER>_<PROPERTY>. Example: --http.web.address becomes NEVER__HTTP_WEB_ADDRESS.

ICMP Flags

FlagTypeDefaultDescription
--icmp.<IDENTIFIER>.namestring<IDENTIFIER>Name of the ICMP checker.
--icmp.<IDENTIFIER>.addressstringrequiredICMP target hostname or IP address. *
--icmp.<IDENTIFIER>.intervalduration0Time between ICMP requests. Uses --default-interval when unset or 0.
--icmp.<IDENTIFIER>.max-attemptsint0Maximum attempts before giving up. Uses --max-attempts when unset or 0.
--icmp.<IDENTIFIER>.backoffenumlinearRetry backoff mode. Allowed values: linear, exponential.
--icmp.<IDENTIFIER>.max-intervalduration0Maximum retry interval when backoff increases the delay. Uncapped when unset or 0.
--icmp.<IDENTIFIER>.timeoutduration2sTimeout for ICMP read and write operations.
--icmp.<IDENTIFIER>.read-timeoutduration0Advanced override for the ICMP read timeout. Uses --icmp.<IDENTIFIER>.timeout when unset or 0.
--icmp.<IDENTIFIER>.write-timeoutduration0Advanced override for the ICMP write timeout. Uses --icmp.<IDENTIFIER>.timeout when unset or 0.

Environment variables use NEVER__ICMP_<IDENTIFIER>_<PROPERTY>. Example: --icmp.host.address becomes NEVER__ICMP_HOST_ADDRESS.

TCP Flags

FlagTypeDefaultDescription
--tcp.<IDENTIFIER>.namestring<IDENTIFIER>Name of the TCP checker.
--tcp.<IDENTIFIER>.addressstringrequiredTCP target address in host:port format. *
--tcp.<IDENTIFIER>.timeoutduration2sTCP connection timeout.
--tcp.<IDENTIFIER>.intervalduration0Time between TCP requests. Uses --default-interval when unset or 0.
--tcp.<IDENTIFIER>.max-attemptsint0Maximum attempts before giving up. Uses --max-attempts when unset or 0.
--tcp.<IDENTIFIER>.backoffenumlinearRetry backoff mode. Allowed values: linear, exponential.
--tcp.<IDENTIFIER>.max-intervalduration0Maximum retry interval when backoff increases the delay. Uncapped when unset or 0.

Environment variables use NEVER__TCP_<IDENTIFIER>_<PROPERTY>. Example: --tcp.db.address becomes NEVER__TCP_DB_ADDRESS.

Resolving Variables

Some flag values can be resolved from environment variables, files, JSON, YAML, and INI files.

Fields marked with * in the flag tables support resolving variables.

This is separate from NEVER__... flag environment variables.

PrefixSourceExampleDescription
env:Environment variableenv:PATHResolves the value from an environment variable.
file:Key-value filefile:/config/app.txt//KeyNameResolves KeyName from a simple key-value file.
json:JSON filejson:/config/app.json//database.hostResolves a value from a JSON file. Supports dot notation for nested keys.
yaml:YAML fileyaml:/config/app.yaml//server.portResolves a value from a YAML file. Supports dot notation for nested keys.
ini:INI fileini:/config/app.ini//Section.KeyResolves a value from an INI file section and key.
noneLiteral valuehttp://example.comUses the value as-is.

HTTP header values can also be resolved using the same mechanism:

never \
--http.web.address=http://example.com \
--http.web.header="Authorization=env:SECRET_HEADER"

Examples

Define an HTTP Target with Flags

never \
--http.web.address=http://example.com:80 \
--http.web.method=GET \
--http.web.expected-status-codes=200,204 \
--http.web.header="Authorization=Bearer token" \
--http.web.header="Content-Type=application/json" \
--http.web.skip-tls-verify=false \
--default-interval=5s

Define an HTTP Target with Environment Variables

NEVER__DEFAULT_INTERVAL=5s \
NEVER__HTTP_WEB_ADDRESS=http://example.com:80 \
NEVER__HTTP_WEB_METHOD=GET \
NEVER__HTTP_WEB_EXPECTED_STATUS_CODES=200,204 \
NEVER__HTTP_WEB_HEADER="Authorization=Bearer token" \
NEVER__HTTP_WEB_SKIP_TLS_VERIFY=false \
never

Define Multiple Targets Running in Parallel

never \
--http.web.address=http://example.com:80 \
--tcp.db.address=localhost:5432 \
--tcp.db.backoff=exponential \
--tcp.db.max-interval=30s \
--icmp.host.address=example.com \
--default-interval=10s

Define Multiple Targets with Environment Variables

NEVER__DEFAULT_INTERVAL=10s \
NEVER__HTTP_WEB_ADDRESS=http://example.com:80 \
NEVER__TCP_DB_ADDRESS=localhost:5432 \
NEVER__TCP_DB_BACKOFF=exponential \
NEVER__TCP_DB_MAX_INTERVAL=30s \
NEVER__ICMP_HOST_ADDRESS=example.com \
never

Notes

Proxy Settings: Proxy configurations (HTTP_PROXY, HTTPS_PROXY, NO_PROXY) are managed via standard environment variables used by Go's HTTP client.

Behavior Flowchart

TCP Check

Click here to see the flowchart
graph TD;
classDef noFill fill:none;
classDef violet stroke:#9775fa;
classDef green stroke:#2f9e44;
classDef error stroke:#fa5252;
classDef transparent stroke:none,font-size:20px;
subgraph MainFlow[ ]
direction TB
start((Start)) --> attemptConnect[Attempt to connect to <font color=orange>TARGET_ADDRESS</font>];
class start violet;
subgraph RetryLoop[Retry Loop]
subgraph InnerLoop[ ]
direction TB
attemptConnect -->|Connection successful| targetReady[Target is ready];
attemptConnect -->|Connection failed| waitRetry[Wait for retry <font color=orange>CHECK_INTERVAL</font>];
waitRetry --> attemptConnect;
end
end
targetReady --> processEnd((End));
class targetReady green;
class processEnd violet;
end
programTerminated[Program terminated or canceled] --> processEnd;
class programTerminated error;
class start,attemptConnect,targetReady,waitRetry,processEnd,programTerminated,MainFlow,RetryLoop noFill;
class MainFlow,RetryLoop transparent;
Loading

HTTP Check

Click here to see the flowchart
flowchart TD;
direction TB
classDef noFill fill:none;
classDef violet stroke:#9775fa;
classDef green stroke:#2f9e44;
classDef error stroke:#fa5252;
classDef decision stroke:#1971c2;
classDef transparent stroke:none,font-size:20px;
subgraph MainFlow[ ]
direction TB
processStart((Start)) --> createRequest[Create HTTP request for <font color=orange>TARGET_ADDRESS</font>];
class processStart violet;
createRequest --> addHeaders[Add headers from <font color=orange>HTTP_HEADERS</font>];
addHeaders --> addSkipTLS[Add skip TLS verify if <font color=orange>HTTP_SKIP_TLS_VERIFY</font> is set];
addSkipTLS --> sendRequest[Send HTTP request];
subgraph RetryLoop[Retry Loop]
subgraph InnerLoop[ ]
direction TB
sendRequest --> checkTimeout{Answers within <font color=orange>HTTP_TIMEOUT</font>?};
class checkTimeout decision;
checkTimeout -->|Yes| checkStatusCode[Check response status code <font color=orange>HTTP_EXPECTED_STATUS_CODES</font>];
checkTimeout -->|No| targetNotReady[Target is not ready];
checkStatusCode --> statusMatch{Matches?};
class statusMatch decision;
statusMatch -->|Yes| targetReady[Target is ready];
statusMatch -->|No| targetNotReady;
class targetReady green;
class targetNotReady error;
targetNotReady --> waitRetry[Wait for retry <font color=orange>CHECK_INTERVAL</font>];
waitRetry --> sendRequest;
end
end
targetReady --> processEnd((End));
class processEnd violet;
end
programTerminated[Program terminated or canceled] --> processEnd;
class programTerminated error;
class processStart,createRequest,addHeaders,addSkipTLS,sendRequest,checkTimeout,checkStatusCode,statusMatch,targetReady,targetNotReady,waitRetry,programTerminated,processEnd,MainFlow,RetryLoop noFill;
class MainFlow,RetryLoop transparent;
Loading

ICMP Check

Click here to see the flowchart
flowchart TD;
direction TB
classDef noFill fill:none;
classDef violet stroke:#9775fa;
classDef green stroke:#2f9e44;
classDef error stroke:#fa5252;
classDef decision stroke:#1971c2;
classDef transparent stroke:none,font-size:20px;
subgraph MainFlow[ ]
direction TB
processStart((Start)) --> createRequest[Create ICMP request for <font color=orange>TARGET_ADDRESS</font>];
class processStart violet;
createRequest --> sendRequest[Send ICMP request];
subgraph RetryLoop[Retry Loop]
subgraph InnerLoop[ ]
direction TB
sendRequest --> checkTimeout{Answers within <font color=orange>ICMP_TIMEOUT</font>?};
class checkTimeout decision;
checkTimeout -->|Connection successful| targetReady[Target is ready];
checkTimeout -->|Connection failed| waitRetry[Wait for retry <font color=orange>CHECK_INTERVAL</font>];
class targetReady green;
waitRetry --> sendRequest;
end
end
targetReady --> processEnd((End));
class processEnd violet;
end
programTerminated[Program terminated or canceled] --> processEnd;
class programTerminated error;
class processStart,createRequest,sendRequest,checkTimeout,targetReady,waitRetry,programTerminated,processEnd,MainFlow,RetryLoop noFill;
class MainFlow,RetryLoop transparent;
Loading

Permissions

Only ICMP checks in Kubernetes require additional permissions. The container needs the CAP_NET_RAW capability to send ICMP packets.

Example:

- name: wait-for-hostimage: ghcr.io/containeroo/never:latestargs:
- --icmp.host.address=hostname.domain.com
- --icmp.host.timeout=2ssecurityContext:
readOnlyRootFilesystem: trueallowPrivilegeEscalation: falsecapabilities:
add: ["CAP_NET_RAW"]

For TCP and HTTP checks, the container does not require any additional permissions.

Kubernetes initContainer Configuration

Configure your Kubernetes deployment to use never as an init container.

Using Args

initContainers:
- name: wait-for-vmimage: ghcr.io/containeroo/never:latestargs:
- --icmp.vm.address=hostname.domain.tld
- --icmp.vm.timeout=2ssecurityContext:
readOnlyRootFilesystem: trueallowPrivilegeEscalation: falsecapabilities:
add: ["CAP_NET_RAW"]
- name: wait-for-servicesimage: ghcr.io/containeroo/never:latestargs:
- --http.postgres.address=http://postgres.default.svc.cluster.local:9000/healthz
- --http.postgres.method=POST
- --http.postgres.header=Authorization=env:BEARER_TOKEN
- --http.postgres.expected-status-codes=200,202
- --tcp.redis.name=redis
- --tcp.redis.address=redis.default.svc.cluster.local:6379
- --tcp.vaultkey.address=valkey.default.svc.cluster.local:6379
- --tcp.vaultkey.interval=5s
- --tcp.vaultkey.timeout=5senvFrom:
- secretRef:
name: bearer-token

Using Environment Variables

initContainers:
- name: wait-for-servicesimage: ghcr.io/containeroo/never:latestenv:
- name: NEVER__DEFAULT_INTERVALvalue: 5s
- name: NEVER__HTTP_POSTGRES_ADDRESSvalue: http://postgres.default.svc.cluster.local:9000/healthz
- name: NEVER__HTTP_POSTGRES_METHODvalue: POST
- name: NEVER__HTTP_POSTGRES_HEADERvalue: Authorization=env:BEARER_TOKEN
- name: NEVER__HTTP_POSTGRES_EXPECTED_STATUS_CODESvalue: 200,202
- name: NEVER__TCP_REDIS_NAMEvalue: redis
- name: NEVER__TCP_REDIS_ADDRESSvalue: redis.default.svc.cluster.local:6379
- name: NEVER__TCP_VAULTKEY_ADDRESSvalue: valkey.default.svc.cluster.local:6379
- name: NEVER__TCP_VAULTKEY_INTERVALvalue: 5s
- name: NEVER__TCP_VAULTKEY_TIMEOUTvalue: 5senvFrom:
- secretRef:
name: bearer-token

License

This project is licensed under the Apache License. See the LICENSE file for details.

About

Simple application that checks if a specified TCP, HTTP or ICMP target is available.

Resources

Stars

4 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages