{"id":41728629,"url":"https://github.com/containeroo/never","last_synced_at":"2026-01-24T23:19:27.168Z","repository":{"id":253954649,"uuid":"845034818","full_name":"containeroo/never","owner":"containeroo","description":"Simple application that checks if a specified TCP, HTTP or ICMP target is available.","archived":false,"fork":false,"pushed_at":"2025-12-07T22:05:19.000Z","size":553,"stargazers_count":4,"open_issues_count":1,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-12-08T05:51:50.144Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/containeroo.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2024-08-20T13:04:03.000Z","updated_at":"2025-12-07T22:05:08.000Z","dependencies_parsed_at":"2024-12-05T07:29:04.370Z","dependency_job_id":"7caa3254-59fa-48cf-bfa5-55277787ce3f","html_url":"https://github.com/containeroo/never","commit_stats":null,"previous_names":["containeroo/toast","containeroo/portpatrol","containeroo/never"],"tags_count":58,"template":false,"template_full_name":null,"purl":"pkg:github/containeroo/never","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/containeroo%2Fnever","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/containeroo%2Fnever/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/containeroo%2Fnever/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/containeroo%2Fnever/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/containeroo","download_url":"https://codeload.github.com/containeroo/never/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/containeroo%2Fnever/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28738982,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-24T22:12:27.248Z","status":"ssl_error","status_checked_at":"2026-01-24T22:12:10.529Z","response_time":89,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-01-24T23:19:25.343Z","updated_at":"2026-01-24T23:19:27.117Z","avatar_url":"https://github.com/containeroo.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# N.E.V.E.R. - Network Endpoint Validation with Endless Retries\n\n[![Go Report Card](https://goreportcard.com/badge/github.com/containeroo/never?style=flat-square)](https://goreportcard.com/report/github.com/containeroo/never)\n[![Go Doc](https://img.shields.io/badge/godoc-reference-blue.svg?style=flat-square)](https://godoc.org/github.com/containeroo/never)\n[![Release](https://img.shields.io/github/release/containeroo/never.svg?style=flat-square)](https://github.com/containeroo/never/releases/latest)\n[![GitHub tag](https://img.shields.io/github/tag/containeroo/never.svg?style=flat-square)](https://github.com/containeroo/never/releases/latest)\n![Tests](https://github.com/containeroo/never/actions/workflows/tests.yml/badge.svg)\n[![Build](https://github.com/containeroo/never/actions/workflows/release.yml/badge.svg)](https://github.com/containeroo/never/actions/workflows/release.yml)\n[![license](https://img.shields.io/github/license/containeroo/never.svg?style=flat-square)](LICENSE)\n\n---\n\n\u003e **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.\n\u003e It loops endlessly until the target responds — or until it’s killed.\n\nDesigned 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.\n\n## Features\n\n- Continuously retries until the target responds — no backoff, no shame.\n- Supports multiple concurrent targets, each with its own config.\n- Configurable entirely via command-line arguments.\n- Exits with `0` the moment everything is ready.\n\nWhether you're waiting on a `port`, `ping`, or a `200 OK`, `N.E.V.E.R.` backs down never.\n\n## Command-Line Flags\n\n`never` accepts the following command-line flags:\n\n### Common Flags\n\n| Flag                 | Type     | Default | Description                                                         |\n| -------------------- | -------- | ------- | ------------------------------------------------------------------- |\n| `--default-interval` | duration | `2s`    | Default interval between checks. Can be overridden for each target. |\n| `--version`          | bool     | `false` | Show version and exit.                                              |\n| `--help`, `-h`       | bool     | `false` | Show help.                                                          |\n\n### Target Flags\n\n`never` accepts \"dynamic\" flags that can be defined in the startup arguments.\nUse the `--\u003cTYPE\u003e.\u003cIDENTIFIER\u003e.\u003cPROPERTY\u003e=\u003cVALUE\u003e` format to define targets.\nTypes are: `http`, `icmp` or `tcp`.\n\n#### HTTP-Flags\n\n- **`--http.\u003cIDENTIFIER\u003e.name`** = `string`\n  The name of the target. If not specified, it uses the `\u003cIDENTIFIER\u003e` as the name.\n\n- **`--http.\u003cIDENTIFIER\u003e.address`** = `string`\n  The target's address.\n  **Resolvable:** See [Resolving Variables](#resolving-variables) below.\n\n  - **`--http.\u003cIDENTIFIER\u003e.interval`** = `duration`\n    The interval between HTTP requests (e.g., `1s`). Overwrites the global `--default-interval`.\n\n- **`--http.\u003cIDENTIFIER\u003e.method`** = `string`\n  The HTTP method to use (e.g., `GET`, `POST`). Defaults to `GET`.\n\n- **`--http.\u003cIDENTIFIER\u003e.header`** = `string`\n  A HTTP header in `key=value` format. Can be specified multiple times.\n  **Example:** `Authorization=Bearer token`\n  **Resolvable:** See [Resolving Variables](#resolving-variables) below.\n\n- **`--http.\u003cIDENTIFIER\u003e.allow-duplicate-headers`** = `bool`\n  Allow duplicate headers. Defaults to `false`.\n\n- **`--http.\u003cIDENTIFIER\u003e.expected-status-codes`** = `string`\n  A comma-separated list of expected HTTP status codes or ranges (e.g., `200,301-302`). Defaults to `200`.\n\n- **`--http.\u003cIDENTIFIER\u003e.skip-tls-verify`** = `bool`\n  Whether to skip TLS verification. Defaults to `false`.\n\n- **`--http.\u003cIDENTIFIER\u003e.timeout`** = `duration`\n  The timeout for the HTTP request (e.g., `5s`). Defaults to `1s`.\n\n#### ICMP Flags\n\n- **`--icmp.\u003cIDENTIFIER\u003e.name`** = `string`\n  The name of the target. If not specified, it uses the `\u003cIDENTIFIER\u003e` as the name.\n\n- **`--icmp.\u003cIDENTIFIER\u003e.address`** = `string`\n  The target's address.\n  **Resolvable:** See [Resolving Variables](#resolving-variables) below.\n\n- **`--icmp.\u003cIDENTIFIER\u003e.interval`** = `duration`\n  The interval between ICMP requests (e.g., `1s`). Overwrites the global `--default-interval`.\n\n- **`--icmp.\u003cIDENTIFIER\u003e.read-timeout`** = `duration`\n  The read timeout for the ICMP connection (e.g., `1s`). Defaults to `1s`.\n\n- **`--icmp.\u003cIDENTIFIER\u003e.write-timeout`** = `duration`\n  The write timeout for the ICMP connection (e.g., `1s`).Defaults to `1s`.\n\n#### TCP Flags\n\n- **`--tcp.\u003cIDENTIFIER\u003e.name`** = `string`\n  The name of the target. If not specified, it uses the `\u003cIDENTIFIER\u003e` as the name.\n\n- **`--tcp.\u003cIDENTIFIER\u003e.address`** = `string`\n  The target's address.\n  **Resolvable:** See [Resolving Variables](#resolving-variables) below.\n\n- **`--tcp.\u003cIDENTIFIER\u003e.interval`** = `duration`\n  The interval between ICMP requests (e.g., `1s`). Overwrites the global `--default-interval`.\n\n#### Resolving variables\n\nEach `address` field can be resolved using `environment variables`, `files`, `JSON`, `YAML`, and `INI` files.\n\n- `env`: – Resolves environment variables.\n  Example: `env:PATH` returns the value of the `PATH` environment variable.\n- `file`: – Resolves values from a simple key-value file.\n  Example: `file:/config/app.txt//KeyName` returns the value associated with `KeyName` in `app.txt`.\n- `json`: – Resolves values from a JSON file. Supports dot notation for nested keys.\n  Example: `json:/config/app.json//database.host` returns `host` field under `database` in `app.json`. It is also possible to indexing into arrays (e.g., `json:/config/app.json//servers.0.host`).\n- `yaml`: – Resolves values from a YAML file. Supports dot notation for nested keys.\n  Example: `yaml:/config/app.yaml//server.port` returns `port` under `server` in `app.yaml`.It is also possible to indexing into arrays (e.g., `yaml:/config/app.yaml//servers.0.host`).\n- `ini`: – Resolves values from an INI file. Can specify a section and key, or just a key in the default section.\n  Example: `ini:/config/app.ini//Section.Key` returns the value of `Key` under `Section`.\n- No prefix – Returns the value as-is, unchanged.\n\nHTTP headers values can also be resolved using the same mechanism, (from a environment variable `--http.\u003cIDENTIFIER\u003e.header=\"header=env:SECRET_HEADER\"` or from a file `--http.\u003cIDENTIFIER\u003e.header=\"header=file:PATH_TO_FILE\"`).\n\n### Examples\n\n#### Define an HTTP Target\n\n```sh\nnever \\\n  --http.web.address=http://example.com:80 \\\n  --http.web.method=GET \\\n  --http.web.expected-status-codes=200,204 \\\n  --http.web.header=\"Authorization=Bearer token\" \\\n  --http.web.header=\"Content-Type=application/json\" \\\n  --http.web.skip-tls-verify=false \\\n  --default-interval=5s\n```\n\n#### Define Multiple Targets (HTTP and TCP) Running in Parallel\n\n```sh\nnever \\\n  --http.web.address=http://example.com:80 \\\n  --tcp.db.address=tcp://localhost:5432 \\\n  --default-interval=10s\n```\n\n#### Notes\n\n**Proxy Settings**: Proxy configurations (`HTTP_PROXY`, `HTTPS_PROXY`, `NO_PROXY`) are managed via environment variables.\n\n## Behavior Flowchart\n\n### TCP Check\n\n\u003cdetails\u003e\n  \u003csummary\u003eClick here to see the flowchart\u003c/summary\u003e\n\n```mermaid\ngraph TD;\n    classDef noFill fill:none;\n    classDef violet stroke:#9775fa;\n    classDef green stroke:#2f9e44;\n    classDef error stroke:#fa5252;\n    classDef transparent stroke:none,font-size:20px;\n\n    subgraph MainFlow[ ]\n        direction TB\n        start((Start)) --\u003e attemptConnect[Attempt to connect to \u003cfont color=orange\u003eTARGET_ADDRESS\u003c/font\u003e];\n        class start violet;\n\n        subgraph RetryLoop[Retry Loop]\n            subgraph InnerLoop[ ]\n                direction TB\n                attemptConnect --\u003e|Connection successful| targetReady[Target is ready];\n                attemptConnect --\u003e|Connection failed| waitRetry[Wait for retry \u003cfont color=orange\u003eCHECK_INTERVAL\u003c/font\u003e];\n                waitRetry --\u003e attemptConnect;\n            end\n        end\n\n        targetReady --\u003e processEnd((End));\n        class processEnd violet;\n        waitRetry --\u003e processEnd;\n    end\n\n    programTerminated[Program terminated or canceled] --\u003e processEnd;\n    class programTerminated error;\n\n    class start,attemptConnect,targetReady,waitRetry,processEnd,programTerminated,MainFlow,RetryLoop noFill;\n    class MainFlow,RetryLoop transparent;\n```\n\n\u003c/details\u003e\n\n## Permissions\n\n**Only** when using `ICMP` checks in Kubernetes, it's important to ensure that the container has the necessary permissions to send ICMP packets. It is necessary to add the `CAP_NET_RAW` capability to the container's security context.\n\nExample:\n\n```yaml\n- name: wait-for-host\n  image: ghcr.io/containeroo/never:latest\n  env:\n    - name: TARGET_ADDRESS\n      value: icmp://hostname.domain.com\n  securityContext:\n    readOnlyRootFilesystem: true\n    allowPrivilegeEscalation: false\n    capabilities:\n      add: [\"CAP_NET_RAW\"]\n```\n\nFor `TCP` and `HTTP` checks, the container does not require any additional permissions.\n\n### HTTP Check\n\n\u003cdetails\u003e\n  \u003csummary\u003eClick here to see the flowchart\u003c/summary\u003e\n\n```mermaid\nflowchart TD;\n    direction TB\n    classDef noFill fill:none;\n    classDef violet stroke:#9775fa;\n    classDef green stroke:#2f9e44;\n    classDef error stroke:#fa5252;\n    classDef decision stroke:#1971c2;\n    classDef transparent stroke:none,font-size:20px;\n\n    subgraph MainFlow[ ]\n        direction TB\n        processStart((Start)) --\u003e createRequest[Create HTTP request for \u003cfont color=orange\u003eTARGET_ADDRESS\u003c/font\u003e];\n        class start processStart;\n\n        createRequest --\u003e addHeaders[Add headers from \u003cfont color=orange\u003eHTTP_HEADERS\u003c/font\u003e];\n        addHeaders --\u003e addSkipTLS[Add skip TLS verify if \u003cfont color=orange\u003eHTTP_SKIP_TLS_VERIFY\u003c/font\u003e is set];\n        addSkipTLS --\u003e sendRequest[Send HTTP request];\n\n        subgraph RetryLoop[Retry Loop]\n            subgraph InnerLoop[ ]\n                direction TB\n                sendRequest --\u003e checkTimeout{Answers within \u003cfont color=orange\u003eDIAL_TIMEOUT\u003c/font\u003e?};\n                class checkTimeout decision;\n                checkTimeout --\u003e|Yes| checkStatusCode[Check response status code \u003cfont color=orange\u003eHTTP_EXPECTED_STATUS_CODES\u003c/font\u003e];\n                checkStatusCode --\u003e statusMatch{Matches?};\n                class statusMatch decision;\n\n                statusMatch --\u003e|Yes| targetReady[Target is ready];\n                class targetReady success;\n\n                statusMatch --\u003e|No| targetNotReady[Target is not ready];\n                class targetNotReady error;\n                targetNotReady --\u003e waitRetry[Wait for retry \u003cfont color=orange\u003eCHECK_INTERVAL\u003c/font\u003e];\n                waitRetry --\u003e sendRequest;\n            end\n        end\n\n    targetReady --\u003e processEnd((End));\n    class processEnd violet;\n    end\n\n    programTerminated[Program terminated or canceled] --\u003e processEnd;\n    class programTerminated error;\n\nclass processStart,createRequest,addHeaders,addSkipTLS,sendRequest,checkTimeout,checkStatusCode,statusMatch,targetReady,targetNotReady,waitRetry,programTerminated,processEnd,MainFlow,RetryLoop noFill;\nclass MainFlow,RetryLoop transparent;\n```\n\n\u003c/details\u003e\n\n### ICMP Check\n\n\u003cdetails\u003e\n  \u003csummary\u003eClick here to see the flowchart\u003c/summary\u003e\n\n```mermaid\nflowchart TD;\n    direction TB\n    classDef noFill fill:none;\n    classDef violet stroke:#9775fa;\n    classDef green stroke:#2f9e44;\n    classDef error stroke:#fa5252;\n    classDef decision stroke:#1971c2;\n    classDef transparent stroke:none,font-size:20px;\n\n    subgraph MainFlow[ ]\n        direction TB\n        processStart((Start)) --\u003e createRequest[Create ICMP request for \u003cfont color=orange\u003eTARGET_ADDRESS\u003c/font\u003e];\n        class start processStart;\n\n        createRequest --\u003e sendRequest[Send ICMP request];\n\n        subgraph RetryLoop[Retry Loop]\n            subgraph InnerLoop[ ]\n                direction TB\n                sendRequest --\u003e checkTimeout{Answers within timeouts \u003cfont color=orange\u003eDIAL_TIMEOUT\u003c/font\u003e/\u003cfont color=orange\u003eICMP_READ_TIMEOUT\u003c/font\u003e?};\n\n                checkTimeout --\u003e|Connection successful| targetReady[Target is ready];\n                checkTimeout --\u003e|Connection failed| waitRetry[Wait for retry \u003cfont color=orange\u003eCHECK_INTERVAL\u003c/font\u003e];\n                waitRetry --\u003e sendRequest;\n\n            end\n        end\n\n    targetReady --\u003e processEnd((End));\n    class processEnd violet;\n    end\n\n    programTerminated[Program terminated or canceled] --\u003e processEnd;\n    class programTerminated error;\n\nclass processStart,createRequest,sendRequest,checkTimeout,targetReady,waitRetry,processEnd,MainFlow,RetryLoop noFill;\nclass MainFlow,RetryLoop transparent;\n```\n\n\u003c/details\u003e\n\n## Kubernetes initContainer Configuration\n\nConfigure your Kubernetes deployment to use this init container:\n\n```yaml\ninitContainers:\n  - name: wait-for-vm\n    image: ghcr.io/containeroo/never:latest\n    args:\n      - --icmp.vm.address=hostname.domain.tld\n    securityContext: # icmp requires CAP_NET_RAW\n      readOnlyRootFilesystem: true\n      allowPrivilegeEscalation: false\n      capabilities:\n        add: [\"CAP_NET_RAW\"]\n  - name: wait-for-it\n    image: ghcr.io/containeroo/never:latest\n    args:\n      - --target.postgres.address=postgres.default.svc.cluster.local:9000/healthz # use healthz endpoint to check if postgres is ready\n      - --target.postgres.method=POST\n      - --target.postgres.header=Authorization=env:BEARER_TOKEN\n      - --target.postgres.expected-status-codes=200,202\n      - --target.redis.name=redis\n      - --target.redis.address=redis.default.svc.cluster.local:6437\n      - --tcp.vaultkey.address=valkey.default.svc.cluster.local:6379\n      - --tcp.vaultkey.interval=5s\n      - --tcp.vaultkey.timeout=5s\n    envFrom:\n      - secretRef:\n          name: bearer-token\n```\n\n## License\n\nThis project is licensed under the Apache License. See the [LICENSE](LICENSE) file for details.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcontaineroo%2Fnever","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcontaineroo%2Fnever","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcontaineroo%2Fnever/lists"}