Connect a GitHub Actions job to your Octelium Cluster and reach its Services over the tunnel.
The action installs the Octelium client on the runner, connects to your Cluster in the background, and waits until the tunnel is actually up before the rest of the job runs. By default it authenticates without any secret at all, using GitHub's own OIDC identity token.
permissions: id-token: write # lets the runner mint the OIDC token used to authenticate jobs: example: runs-on: ubuntu-latest steps: - name: Connect to Octelium uses: octelium/github-action@v2 with: domain: example.com - name: Reach a Service over the tunnel run: curl http://demo-nginx
With no auth-token, the action authenticates using GitHub's OIDC identity token through an assertion based IdentityProvider on your Cluster, so no secret is stored in the repository. The job needs the id-token: write permission; the action fails with an explicit message if the permission is missing rather than timing out.
Read more about assertion based IdentityProviders here.
- uses: octelium/github-action@v2 with: domain: example.com audience: https://example.com
The Cluster matches the assertion against its own IdentityProviders, so nothing needs to name one here even when several are configured.
- uses: octelium/github-action@v2 with: domain: example.com auth-token: ${{ secrets.OCTELIUM_AUTH_TOKEN }}
The Token is only ever passed to the client through an environment variable, never on the command line, and the action masks it in the log. Note that secrets are not available to workflows triggered by pull_request from forked repositories, so the OIDC path is the one that works there.
assertion takes a raw --assertion value for anything else the client supports, for example a JWT your workflow obtained by other means:
- uses: octelium/github-action@v2 with: domain: example.com assertion: jwt:env=MY_IDENTITY_TOKEN
An explicit assertion carries its own options, including its audience, so put the audience inside the value: github-actions:audience=https://example.com.
The action rejects combinations that would silently do nothing rather than accepting them:
| You set | What happens | What is rejected |
|---|---|---|
auth-token |
Token authentication | assertion, audience |
assertion |
Your assertion, verbatim | auth-token, audience |
| neither | GitHub OIDC assertion | auth-token |
The OIDC path additionally requires both of the runner's ACTIONS_ID_TOKEN_REQUEST_* variables, which is what id-token: write provides.
Once connected, the job resolves and reaches every Service assigned to the User:
- run: curl http://demo-nginx - run: psql -h postgres -c 'select 1'
You can also map a Service onto a runner port with publish, which is useful for tools that only speak to localhost:
- uses: octelium/github-action@v2 with: domain: example.com publish: | postgres:5432 redis.ns1:6379
Each entry is service:port, service.namespace:port or service:address:port. Bracket an IPv6 listen address, as in svc:[::1]:8080.
A runner can also serve a Service back to the Cluster, for example to expose a preview environment built in the job:
- uses: octelium/github-action@v2 with: domain: example.com serve: | preview-app
scopes limits what the Session can reach, in the same spirit as OAuth2 scopes. Scope a workflow down to only what it actually needs:
- uses: octelium/github-action@v2 with: domain: example.com scopes: | service:postgres.db service:ci/*
By default the action installs the latest Octelium release. Pin it for reproducible runs:
- uses: octelium/github-action@v2 with: domain: example.com version: "0.42.0"
The action downloads only the binaries it needs straight from the Octelium release, verifies them against the release SHA256SUMS, and refuses to install anything whose checksum does not match. Nothing is piped into a shell.
components installs the other Octelium binaries alongside the client, which is useful for workflows that also manage the Cluster:
- uses: octelium/github-action@v2 with: command: install components: | octelium octeliumctl
The connection lives for the rest of the job. Tear it down explicitly to revoke the Session immediately instead of leaving it to expire:
- name: Disconnect if: always() uses: octelium/github-action@v2 with: domain: example.com command: logout
The logout step stops the client, waits for it to shut down gracefully, and revokes the Session. It must run in the same job as the connection and never fails the job on its own, but it does warn loudly if the connector stopped while the Session could not be revoked, since the Session then stays valid until it expires.
A composite action cannot register a GitHub post-job hook, so this explicit step is the reliable cleanup path. The action does clean up after itself if the connection never comes up: a failed or timed-out connect terminates the client before failing the step, so a runner is never left with an untracked tunnel.
| Input | Default | Description |
|---|---|---|
domain |
Your Octelium Cluster domain. Required for connect and logout. |
|
command |
connect |
connect, logout or install. |
version |
latest | Octelium client version to install, e.g. 0.42.0. |
components |
octelium |
Binaries to install: octelium, octeliumctl, octops. |
github-token |
github.token |
Used only to resolve the latest release without hitting the API rate limit. |
| Input | Default | Description |
|---|---|---|
auth-token |
Authentication Token. Falls back to the GitHub OIDC assertion when unset. | |
assertion |
Raw --assertion value, e.g. jwt:env=MY_TOKEN. |
|
audience |
Audience of the GitHub Actions OIDC assertion. | |
scopes |
Session scopes, one per line. | |
logout-on-exit |
true |
Log the Session out when the client shuts down. |
| Input | Default | Description |
|---|---|---|
serve |
Services to serve from the runner, one per line. | |
serve-all |
false |
Serve every Service assigned to the User. |
publish |
Services to publish on the runner, one per line, as service:port, service.namespace:port or service:address:port. |
|
essh |
false |
Run the embedded SSH server. |
essh-user |
Force a host user for eSSH sessions. | |
esocks5 |
false |
Run the embedded SOCKS5 server. |
| Input | Default | Description |
|---|---|---|
no-dns |
false |
Do not apply the Cluster private DNS. |
local-dns |
false |
Run the local DNS server. |
local-dns-address |
Local DNS listen address, as IP or IP:port. |
|
full-dns |
false |
Route every DNS query to the Cluster DNS. |
ip-mode |
v4, v6 or both. |
|
implementation |
Datapath: kernel, tun or gvisor. |
|
tunnel-mode |
wireguard or quicv0. |
|
mtu |
Tunnel MTU, up to 1500. | |
keepalive |
Tunnel keepalive in seconds. |
| Input | Default | Description |
|---|---|---|
extra-args |
Extra octelium connect arguments, one per line in --flag=value form. See below. |
|
timeout |
60 |
Seconds to wait for the tunnel before failing. |
sudo |
auto |
Elevate the client: auto, true or false. |
log-file |
Where the connect output is written. Defaults to a file under RUNNER_TEMP. |
|
dev |
false |
Dev mode. For development and testing only, see the warning below. |
insecure-tls |
false |
Skip TLS verification. Testing only, never against a production Cluster. |
Every list input is one entry per line. Blank lines and # comments are ignored.
Boolean inputs accept true, yes, on, 1 and their negatives. Anything else is an error rather than a silent false, so a typo like full-dns: ture fails the step instead of quietly changing what the client does.
extra-args exists so a new client flag is usable before this action grows an input for it. It is not a way to reconfigure what the action manages:
- One argument per line, in
--flag=valueform.--serve svc1on one line is two arguments and is rejected; a value may still contain spaces, as in--example=a b c. --detach,-d,--domain,--auth-token,--assertionand--homedirare rejected.--detachin particular would fork the client away from the action, leaving a tunnel nothing tracks, and the auth flags would put a credential on the command line.
Prefer the dedicated inputs whenever one exists.
Warning
dev: true makes the client print the raw HTTP exchange it uses to fetch the GitHub OIDC identity token, and the action echoes the client log into the workflow output. The action masks anything JWT shaped before printing, but treat any log produced with dev: true as sensitive and keep it out of public workflow runs.
| Output | Description |
|---|---|
version |
The client version that was installed, e.g. v0.42.0. |
install-dir |
Directory the binaries were installed into. |
log-file |
File the octelium connect output is written to. |
name: integration-tests on: push permissions: contents: read id-token: write jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Connect to Octelium uses: octelium/github-action@v2 with: domain: example.com version: "0.42.0" scopes: | service:postgres.db publish: | postgres:5432 - name: Run the tests against the Cluster database run: go test ./... -tags=integration env: DATABASE_URL: postgres://localhost:5432/app - name: Disconnect if: always() uses: octelium/github-action@v2 with: domain: example.com command: logout
v2 reorganizes the inputs, so review the table above when you bump the major version.
| v1 | v2 |
|---|---|
args: "--serve svc1 --no-dns" |
serve, no-dns and the other dedicated inputs, or extra-args with one argument per line |
wait: "4" |
timeout, which waits for the tunnel instead of sleeping |
args: "--detach" |
rejected; the action manages the client lifecycle |
Other changes:
- The action waits for a real connection.
v1slept forwaitseconds and only checked that the process was still alive, so a job could start using a tunnel that was not up yet.v2waits for the client to report a successful connection and fails with the client's own output if it does not. extra-argsis split on newlines, not whitespace. One argument per line, so values containing spaces survive. Use the--flag=valueform.- The client is downloaded and checksum verified, instead of piping an install script into a shell. Add
versionto pin it. - A missing
id-token: writepermission fails immediately with an explanation instead of failing to authenticate. - Boolean inputs reject typos instead of treating every unrecognized value as
false. - New inputs cover the rest of
octelium connect:serve,serve-all,publish,scopes,assertion, the DNS and networking options,essh,esocks5,components,sudoandlog-file.
Linux and macOS runners are supported on both amd64 and arm64. Windows runners can install the client and are supported on a best-effort basis for connecting.
Apache-2.0. See LICENSE.