More in Setting up a platform

Run the agent on your own systems

Install a runner in your own Kubernetes cluster, or on one server with Docker, so the agent works there, next to your systems. It calls us for work; you open no ports.

In the appPlatformSettingsAgentWhere it runs

Normally we run the agent for you, in our cloud. With a runner, the agent runs in your own Kubernetes cluster, or on one server of yours, instead. Choose this when your databases, logs or cluster must stay private: the agent reaches them from inside your network, and they never need to be open to us.

A runner is a small program you install with Helm in Kubernetes, or with Docker Compose on one server. It calls us for work over the internet. You open no ports, and nothing in your network accepts calls from us. Tickets, findings and approvals stay in Cerberius Agents, as before.

Beta

The runner is new. With a runner, the keys to your own systems (the agent's Claude key, repository keys, cluster and log logins, database passwords) are kept by you: scrambled for your runner as you type them, or in your own store. We never hold a copy of them we can read: see Where your keys are kept. With a database's or your cluster's change login kept like that, your runner makes the changes you approve to them; with a repository's key, it opens and merges the pull requests for fixes: see Changes made by your runner. It also makes the Check buttons' checks and the log watcher's reads with the keys you keep: see Reads made by your runner. With the Microsoft 365, Google, GoDaddy or Cloudflare key kept by you, your runner signs in to them, reads the DNS records and makes the changes you approve there: see Microsoft 365, Google and domains.

What you need#

  • One of these:
    • a Kubernetes cluster where you may install a Helm chart, with Helm 3.14 or later;
    • one Linux server with Docker and Docker Compose, and no Kubernetes.
  • A way out to the internet for the runner, to https://cerberius.com. Runners installed before November 2026 may call our old address, https://ai-agent.cerberius.com, which keeps working. If your firewall lists the addresses it lets out, allow both.
  • A way to download images from ghcr.io (GitHub's container registry), where the runner and the agent are published.
  • For the agent: a way out to the Claude API, and to your repositories' host (GitHub or GitLab). Plus whatever inside your network you want it to look at, such as your databases or logs.

Install a runner#

  1. 1

    Go to Settings › Agent. Under Where it runs, click Add a runner.

  2. 2

    Give it a Name, such as Production cluster or Office server, and click Create key.

  3. 3

    Copy the key now. It is shown once. Below it, choose how to install the runner: Kubernetes (Helm) or One server (Docker). Each has one block to copy, with the key already in it, and Copy.

  4. 4

    Run it: the Helm command where you manage your cluster, or the Docker commands on your server. See Kubernetes (Helm) and One server (Docker) below for what they do.

  5. 5

    Back in Settings › Agent, the runner shows Online within a minute.

  6. 6

    Under Where it runs, choose Your own runner.

From the next run on, the agent works on your runner. Runs already under way finish where they started.

What you seeWhat it means
OnlineThe runner called us within the last minute and a half.
OfflineIt called before, but not lately. Check it's running and can reach us.
Not connected yetIt has never called. Check its key and its way out to the internet.
RevokedIts key was revoked. It can't take work any more.

Each runner also shows when it last called, its version and the name of its cluster or server. An older runner also shows Update available (see Update the runner).

Kubernetes (Helm)#

The command installs the runner from oci://ghcr.io/cerberius-lab/helm/cerberius-runner into a namespace of its own, cerberius-runner. It needs Helm 3.14 or later. Your cluster then downloads two public images, so it needs no login for them: the runner (ghcr.io/cerberius-lab/cerberius-runner) and the agent (ghcr.io/cerberius-lab/cerberius-agent). Each ticket runs as a Kubernetes Job in that namespace.

One server (Docker)#

For one Linux server with Docker and Docker Compose. Run the commands in the folder where you want the runner's files, as a user who may use Docker. They:

  1. Make a folder cerberius-runner, with an empty secrets folder for the keys you keep in the runner's own store, and an empty rules.yaml for your rules.
  2. Get docker-compose.yml from the runner's own image, for the newest version.
  3. Write the runner's settings to .env, which only you can read: its key, a name for the server, and our signing key.
  4. Start the runner.

The server downloads the same two public images as a cluster, with no login: the runner (ghcr.io/cerberius-lab/cerberius-runner) and the agent (ghcr.io/cerberius-lab/cerberius-agent).

To see what it is doing, run docker compose logs in that folder. Within a minute it says Connected to Cerberius Agents and Waiting for work. Each ticket runs as a container of its own, on a Docker network called cerberius-runner, and is removed when the agent is done. The runner stops an agent that runs past its time limit.

One server: good to know#

  • The Docker socket. The runner uses Docker on the server to start the agents. Anyone who controls the runner controls Docker on this server, so keep the server for this, or for things you trust it with.
  • One runner per server. A runner treats every agent container on the server as its own.
  • Memory. An agent's working folders are kept in memory and count toward its memory limit, 2 GB unless you set AGENT_MEMORY_LIMIT in .env (such as AGENT_MEMORY_LIMIT=4Gi). Raise it for very large repositories, then run docker compose up -d.
  • The firewall. Agents can reach the internet and anything this server can reach. Use your firewall if they should reach less.
  • Keys in the runner's own store go in the secrets folder: one file per key, named as Where your keys are kept shows it, holding just the key. For example printf '%s\n' 'sk-ant-…' > secrets/claude-key, then chmod 600 secrets/* so only you can read them.
  • Its private code is the file identity.pem in the runner's own Docker volume (state). Keep the volume (docker compose down -v deletes it): without it, the keys scrambled for this runner must be typed again. See How scrambling works.
  • Your rules for the changes the runner makes go in rules.yaml. Empty means it makes no changes (see Changes made by your runner). If an edit doesn't apply, run docker compose restart: some editors save a new file, which the runner doesn't see.

Update the runner#

When we publish a newer runner, Settings › Agent shows Update available next to each older runner, with the command to update it and Copy. The runner also says so in its log when it starts, and once a day. An older runner keeps working: update when it suits you. A run under way carries on, and the updated runner picks it up.

In Kubernetes, run this where you manage your cluster:

helm upgrade cerberius-runner oci://ghcr.io/cerberius-lab/helm/cerberius-runner \
  --namespace cerberius-runner --reset-then-reuse-values

It gets the newest chart and keeps the settings you installed it with. It needs Helm 3.14 or later.

On one server, run these in the runner's folder, with the new version in place of 0.6.0:

docker run --rm ghcr.io/cerberius-lab/cerberius-runner:0.6.0 compose > docker-compose.yml
docker compose up -d

Your .env, rules.yaml and secrets stay as they are. If you changed docker-compose.yml yourself, change only the version at the end of its image line instead, then run docker compose up -d.

What happens with a run#

  • The runner asks us for work about every half minute. When a ticket needs the agent, it gets the run and starts the agent: as a Kubernetes Job in its own namespace, or as a Docker container on its server.
  • The agent talks to us only through the runner.
  • If no runner takes a run within 5 minutes, the run stops, and the ticket says your runner didn't ask for work. Start the runner, then click Investigate again on the ticket.
  • If the runner goes quiet during a run for 3 minutes, that run stops, and the ticket says so.
  • If the runner can't reach us for 5 minutes, it stops its agents, and waits until it can reach us again.

Where your keys are kept#

With a runner there are two kinds of keys.

KindWhich keysWhere they are
Kept by youKeys into your own systems: the agent's AI key, repository keys, your cluster logins (to read, and for changes), Loki's login, database passwords (to read, and for changes), and the Microsoft 365, Google Workspace and GoDaddy keys.With you. We never hold a copy we can read.
Kept by usKeys our service uses itself: your ticket system's login and webhook secrets, Slack and Telegram, chatbots, and the AI key for what runs in our cloud (chatbots and the help chat).With us, encrypted. The keys we encrypt them with are themselves encrypted by Google Cloud's key service.

The plain settings stay on our pages either way: repository names, namespaces, database addresses and names, the Microsoft and Google IDs, and your domains. Settings › Agent › Where your keys are kept lists every key of both kinds, and says why each one kept by us stays with us.

Two ways to keep your keys#

For each key kept by you, you choose one of two ways, on the key's own settings page:

  • Scrambled for your runner (the usual way). You type the key into our page, as for any setting. Your browser scrambles it so only your runner can unscramble it. We store it scrambled, and can't read it.
  • In your store. You put the key on your runner's side yourself: in its Kubernetes Secret (cerberius-runner-secrets), or in the secrets folder on a Docker server. On our page you choose In your store and give its name. Nothing about the key comes to us. See Put a key in your store.

How scrambling works#

Below, password means any key, token or password you type in.

  1. Your computer

    Your browser scrambles it with your runner's public code.
  2. Cerberius Agents

    We store it scrambled. We can't unscramble it.
  3. Your runner

    It unscrambles it with its private code, for one ticket.
The trip of a password you type, in short
  1. 1

    When your runner starts for the first time, it makes two matching codes. A private code, which stays on the runner and is never sent anywhere. And a public code, which isn't secret, so the runner sends it to us. Something scrambled with the public code can only be unscrambled with the private code.

  2. 2

    When you type a password into our page, your browser scrambles it with your runner's public code before sending it. We store the scrambled password. We can't unscramble it: we never have the private code.

  3. 3

    When the agent needs the password for a ticket, we send the scrambled password to your runner. Your runner unscrambles it with its private code and gives it to the agent for that ticket only. It doesn't save it.

Change logins, and the Microsoft 365, Google and GoDaddy keys, your runner uses itself: the agent never gets them.

  • Two runners. Your browser scrambles the password twice, once with each runner's public code. We store both.
  • A runner added later. We can't scramble the password for it ourselves, because we can't unscramble the copies we have. So you type the password once more. Settings › Agent › Where your keys are kept lists which keys need it.
  • Reinstalling. The private code is kept in the runner's own storage: in Kubernetes, the Secret cerberius-runner-identity in its namespace; on one server, the file identity.pem in the runner's data volume (state). Reinstalling in the same place keeps it. Deleting that storage (the namespace, docker compose down -v, or moving to a new server) loses it. Then nobody can unscramble the passwords scrambled for it any more, so you type them again. Settings says which.

This is the same kind of protection every https website uses.

What scrambling protects against#

  • It protects against our database, our backups or our staff reading your keys. We never hold a copy we can read, so there is none to lose.
  • It can't protect against our service itself being taken over at the moment you type a key: it could serve a page that keeps the key, or give your page a public code that isn't your runner's. If that matters to you, keep the key In your store: then it never passes through our pages at all.

Which should I choose?#

  • Scrambled for your runner, if you want it simple: you type each key on our pages, like any setting.
  • In your store, if a key must never pass through our pages: you put it on your runner yourself.

Choose how a key is kept#

  1. 1

    Go to the key's own settings page: Agent for the AI key, Code for a repository (its Edit), Live system for your cluster, its changes login and Loki, Databases for passwords (the connection's Edit), Microsoft 365 and Google for those two, and Domains and websites for GoDaddy and Cloudflare. Or go to Settings › Agent › Where your keys are kept, where every key is listed.

  2. 2

    Under the key, choose Scrambled for your runner or In your store. A box opens.

  3. 3

    For Scrambled for your runner: type the key, and click Scramble and save. For In your store: keep the name it shows, such as claude-key or db-orders, or change it (lower-case letters, digits and hyphens), and click Keep it in my store. Then put it in your store.

When you add a repository or a database connection, its form asks the same question: type the key there, or choose In your store.

Under each key, each runner shows with a dot: green when it has the key, amber when it doesn't, and why. A key already set to Scrambled for your runner but not typed in yet has Type it…; one typed in has Type it again…. A key in your store has Change its name….

Put a key in your store#

Settings › Agent › Where your keys are kept shows the exact command for the keys you keep in your store.

  • In Kubernetes: copy the command under In Kubernetes, replace each <…> with the key itself, and run it. It makes the Secret cerberius-runner-secrets in the cerberius-runner namespace.
  • On one server: put one file per key in the runner's secrets folder, named after the key and holding just the key, then chmod 600 secrets/*.

Your runner looks in its own store first, then at the scrambled copy. It tells us the names in its store (never the keys), so each key shows whether each runner has it. It reads the store fresh for each run: no restart needed.

For Loki's Authorization in your store, give the whole header, such as Bearer and the token. If your Loki needs another header, such as X-Api-Key, name it under Settings › Live system › Grafana Loki, in Headers from your runner.

Check it's your runner's public code#

Settings › Agent shows each runner's fingerprint, such as bd6a eadd f02d 9190: a short check code made from its public code. The runner writes the same one in its log when it starts. Check they match. Runners older than 0.5.0 make no codes: update them to scramble keys for them.

Good to know#

  • Chatbots and the help chat run in our cloud. They never use a key kept by you, so give them a key of their own from your key library (Organisation › Keys).
  • A key we still hold from before. A key typed in before the agent moved to your runner shows We still hold it. Choose a way for it, or click Scramble our copy for your runner (or Scramble all for your runner above the list): we scramble the copy we hold for each of your runners, then delete ours, in one step. A key from your key library stays in the library for anything else that uses it, and Settings says where. This platform stops using it.

What doesn't work yet#

Our service never has a key kept by you. Your runner does with it what we would: the agent's work, the Check buttons and the log watcher (see Reads made by your runner), and changes you approve. One thing can't use it yet:

Key kept by youWhat doesn't work yet
A database's change passwordCheck change login under Databases can't run. It is used only for a change you approve.

When the runner lacks a key#

If a run needs a key your runner doesn't have, the run stops at once, and the ticket says which one, and why, in your runner's words:

On the ticketWhat to do
This runner has no key named claude-key. Add it to its secrets.Add it to the runner's store under that exact name.
…: it is locked for another runner.It was scrambled before this runner was added. Type the key again, so it is scrambled for this runner too.
…: it was locked for this runner before its identity changed.The runner lost its private code (see Reinstalling above). Type the key again.
…: it hasn't been typed in yet.Type it on its settings page.

Then click Investigate again on the ticket.

Going back to our cloud#

If you choose Our cloud under Where it runs while keys are kept by you, Settings asks first and names them. They stay with your runner: we keep no copy just in case. Runs in our cloud work without them until you enter them again on their settings pages.

Changes made by your runner#

When a change login is kept by you, the changes you approve with it are made by your runner, not by us. That covers databases (rows or MongoDB documents to insert, update or delete), Kubernetes (scale, restart, patch and the other cluster operations), pull requests for fixes, when the repository's key is kept by you (see Pull requests), and Microsoft 365, Google Workspace and DNS (see Microsoft 365, Google and domains).

Your runner makes nothing until its own rules allow it. Whatever we ask, it refuses a change its rules don't allow.

  1. 1

    Keep the change login with your runner, like any other key: scrambled for your runner, or in your own store. Under Settings › Databases, type the change password when you add or edit the connection. For your cluster, under Settings › Live system › Kubernetes, type the kubeconfig under Changes login. In your store, they are named db-<connection name>-write for a database (such as db-orders-write) and kubeconfig-write for your cluster. Where your keys are kept shows their exact names.

  2. 2

    Write your rules. In Kubernetes, put them in the chart value rules, for example in a file my-values.yaml, and install or upgrade with -f my-values.yaml. On one server, put them in the file rules.yaml next to docker-compose.yml.

An example of the rules:

database:
  hosts: [db.internal:5432]       # the database servers it may change; required
  databases: [orders]             # the database's own name on its server; "*" for any
  refuse: [DROP, TRUNCATE, ALTER, GRANT, REVOKE, CREATE]   # words refused anywhere in a statement
  max_rows: 100                   # never more rows than this, whatever the change says
kubernetes:
  namespaces: [shop]              # "*" for any
  operations: [scale, restart, patch]   # also delete, create, create_job_from_cronjob
code:
  repositories: [acme/shop]       # the repositories it may open pull requests on; "*" for any
  merge: false                    # true: it merges a pull request when one of your team asks
  # gitlab_url: https://gitlab.acme.com   # GitLab only: your GitLab server

The hosts list is required: your runner sends a change password only to a server named there, so nobody can trick it into sending the password elsewhere. For Microsoft SQL Server or Azure SQL, name the server and its port as you entered them, such as sql.internal:1433 or bookings.database.windows.net:1433. SQL Server databases need runner 0.10.0 or later. For MongoDB, name the server and its port, such as mongo.internal:27017. With an Atlas-style address (mongodb+srv), name the cluster's name as you entered it, without a port, such as cluster0.abcde.mongodb.net: your runner then uses only the servers DNS lists for that name, under the same domain. MongoDB databases need runner 0.11.0 or later. With no rules file, or a kind of change your rules don't name, every such change is refused. The runner reads the rules for each change, so an edit applies to the next one.

Microsoft 365, Google and domains#

With Microsoft 365's client secret, Google Workspace's service account key or the GoDaddy key kept by you (runner 0.8.0 or later), your runner does everything we would do with them:

  • For each investigation, it signs in and gives the agent the same hour-long pass to Microsoft 365 or Google Workspace, and reads your domains' DNS records for it. The agent never gets the secret or the keys.
  • Check connection on Settings › Microsoft 365 and Google and Settings › Domains and websites is made by your runner.
  • Changes you approve are made by your runner: it reads everything the change touches first, then makes the exact requests or records. It never changes apps, admin roles, policies or the organisation itself in Microsoft 365, never admin roles or credentials in Google, and never name servers in DNS, whatever its rules say.

Where each key goes is fixed in your runner, whatever it is asked: the Microsoft secret only to Microsoft's sign-in page (login.microsoftonline.com) and the pass only to Microsoft Graph; the Google key never leaves your runner (it signs a request sent only to Google's sign-in service) and the pass goes only to Google's APIs; the GoDaddy key only to api.godaddy.com.

Changes need their own rules, like the others:

microsoft365:
  paths: [users, groups]          # what changes may touch: the part of the Graph path after
                                  # /v1.0/ or /beta/; "*" for any
  delete: false                   # true: it may send DELETE requests
google_workspace:
  hosts: [admin.googleapis.com]   # the Google APIs changes may use; "*" for any
  delete: false
dns:
  domains: [example.com]          # the only domains it uses the GoDaddy key for; required, no "*"
  types: [TXT, MX, CNAME]         # record types it may change (all five if left out)
  delete: false                   # true: it may remove records

min_approvers works here too. With dns.domains set, your runner also reads only those domains for the agent and the Check button; without a dns section it reads the domains in your settings, and changes none.

A runner older than 0.8.0 can't use these keys: the agent is told to update it, and a change waits, then fails saying so. Update the runner, then approve the change again.

Cloudflare (runner 0.9.0 or later) works the same way with its API token (cloudflare-token in your store): your runner reads the zones for the agent and the Check button, and makes approved record changes under the same dns rules. It sends the token only to api.cloudflare.com. See Cloudflare.

Two people for a change#

Your runner can ask for more than one approver, whatever we say. Add min_approvers to a kind of change:

database:
  hosts: [db.internal:5432]
  databases: [orders]
  min_approvers: 2                # two different people must approve each database change
kubernetes:
  namespaces: [shop]
  operations: [scale]
  min_approvers: 2
code:
  repositories: [acme/shop]
  min_approvers: 1                # the default. A fix is approved by one person, so 2 here
                                  # refuses every pull request

Your runner counts the different people named in the signed approval: one person approving twice counts once. With fewer, it refuses, and the ticket says so: Refused by your runner: it needs 2 approvers, and 1 approved this change. To have us ask for the second person before the change reaches your runner, tick Needs two people to approve for that kind of change (see Two people to approve), then approve the change again with two people. Checking whether a pull request is still open needs no approver.

Pull requests#

With a repository's key kept by you, your runner does everything we would do on GitHub or GitLab for that repository's fixes:

  • Opens the pull request once you approve a fix, in the name of the person who approved it. When the ticket already has an open pull request, it checks that it's still open, then adds the fix to it.
  • Merges it when someone asks: Merge on the ticket, merge it in the chat with the agent, or Merge and Merge and deploy without asking on a development request. It merges only if your rules say merge: true. With merge: false (the default) it refuses, and the ticket says so; merge it yourself on GitHub or GitLab.
  • Checks it every five minutes, so the ticket knows when it was merged or closed outside Cerberius Agents.

Your rules must list the repository under code.repositories. A pull request's branch always starts with support-agent/: your runner makes no other branch, and adds to or merges only pull requests on such a branch of the same repository (never a fork's). It never changes git's own files or CI settings, such as .github. A GitHub key goes only to GitHub. A GitLab key goes only to the server in gitlab_url, which must be the same server as the repository's.

Our signing key#

We sign every change we hand to your runner: the exact change, who approved it, and when the approval runs out (15 minutes later). Your runner checks the signature before it does anything.

Pin our signing key so your runner trusts only changes signed with it. Settings › Agent shows it under Our signing key, and the install command there already pins it. To pin it yourself, set cerberius.signingPublicKey in Helm, or SIGNING_PUBLIC_KEY in the .env file on one server. Unpinned, the runner trusts the key we send it when it connects, and says so in its log.

What the ticket shows#

On the ticketWhat it means
Waiting for your runner to take it.We handed the change over. No runner has taken it yet.
Your runner is making it now.A runner took it and hasn't said how it went yet.
The approved change was made.Your runner made it. Its report follows, and the agent writes the reply.
Refused by your runner: …Your runner's checks or rules stopped it, and say which. Nothing was changed.
No runner took the change within 5 minutes. Nothing was changed.No runner is online. Start it, then approve the change again.
Your runner is older than 0.8.0, so it can't make DNS changes. …The same for Microsoft 365 and Google. Update the runner, then approve the change again.
Your runner is older than 0.10.0, so it can't make SQL Server changes. …Update the runner, then approve the change again.
Your runner is older than 0.11.0, so it can't make MongoDB changes. …Update the runner, then approve the change again.
Your runner took the change but never said what happened. It may have run. Check before approving it again.Your runner stopped or lost its connection while it had the change. Look at the database or cluster first.
Waiting for your runner to open the pull request.The fix is made. Your runner hasn't opened its pull request yet.
Waiting for your runner to merge it.Someone asked to merge it. Your runner hasn't said yet.
The pull request could not be merged: Refused by your runner: …Your rules don't allow merging (code.merge), or another check failed. Nothing was merged.
No runner took the pull request within 5 minutes. Nothing was changed.No runner is online. Start it, then approve the fix (or ask to merge) again.
Your runner took the pull request but never said what happened. Check the repository before trying again.Your runner stopped or lost its connection while it had the pull request. Look at the repository first.

If your runner reports after that, the ticket says what it reported, but the change stays failed.

Reads made by your runner#

When a key is kept by you, our service can't read with it, so your runner reads for us, with the same read keys the agent uses:

  • Check access under Code, and Check connection under Live system (cluster and logs), Databases (the read login, and which databases it can see), Microsoft 365 and Google (each sign-in), and Domains and websites (your domains at GoDaddy or Cloudflare);
  • the log watcher: every 5 minutes your runner looks in Loki for new errors, and we turn them into tickets as usual.

Each read is signed by us like a change, but nobody approves it: it only reads. Your runner refuses anything in it that could change something. A Check button says Your runner is checking… until your runner answers, usually within seconds. If no runner answers within 45 seconds, it says Your runner didn't answer. Is it online? If the log watcher's runner is offline, that look is skipped and said on the log watcher's page; the next one catches up. A runner older than 0.6.0 can't read for us: the page says Update your runner to check from it. For a SQL Server database, a runner older than 0.10.0 says Your runner checks SQL Server from version 0.10.0. For a MongoDB database, a runner older than 0.11.0 says Your runner checks MongoDB from version 0.11.0.

Reads are on unless your rules switch them off. Add a reads section to your rules:

reads:
  checks: true        # the Check buttons
  pickers: true       # lists of names from your systems (namespaces, Loki labels, databases)
  log_watcher: false  # the log watcher's look at your logs

reads: false switches them all off. A read switched off is refused, and the page shows your runner's words: Refused by your runner: its local rules switch off the log watcher's look at your logs (reads.log_watcher: false). With pickers: false, a check still says whether it works, but not which namespaces, labels or databases it found.

In Kubernetes, if you turned on the chart's network rules (networkPolicy.enabled), add your Loki's and your databases' ports to networkPolicy.runnerPorts, so the runner can reach them.

The agent's own machine on your runner#

If you give the agent its own machine (Settings › Agent › Its own machine), your runner starts it next to the agent: in your cluster, or on your server. It needs runner 0.7.0 or later. With an older runner the agent works without one, says so in its findings, and Its own machine asks you to update the runner.

  • It's a separate container for each ticket, from the same image as the agent. It holds none of your keys, files or repositories. Only that ticket's agent can use it, with a password made for that ticket.
  • It reaches the internet only when Let it reach the internet is on and your runner's rules allow it too. Your rules keep it off the internet until you allow it.
  • It's removed when the agent finishes. If your runner stops its agents (it can't reach us, or its key was revoked), their machines go too.
  • Every command the agent runs on it shows on the ticket, as in our cloud.

Your runner's rules decide too:

machine:
  allowed: true    # false: your runner never starts one
  internet: false  # true: it may reach the public internet, when Settings › Agent allows it too

With no machine section, the machine is allowed, without the internet. If the rules file can't be read, no machine starts. When the agent has no machine on a ticket, its findings say which checks it couldn't run, and why, in your runner's words. For example: Your runner started no machine for this run: its local rules don't allow one (machine.allowed: false).

In Kubernetes#

Turn on the chart's network rules (networkPolicy.enabled: true), on a cluster whose network plugin enforces them (Calico and Cilium do). Then:

  • the machine takes traffic only from its own ticket's agent, on port 8700;
  • without the internet, it reaches nothing at all, not even DNS;
  • with the internet, it reaches DNS and public addresses, never the private ranges in networkPolicy.privateRanges.

For this, your runner may also create network rules (NetworkPolicies) in its own namespace: one per machine, removed with it.

Without the network rules, nothing limits the machine: it reaches whatever pods in your cluster can, the internet included, whatever the switches say. Your runner says so in its log.

On one server (Docker)#

Each machine gets a Docker network of its own, shared only with its agent. Other containers on the server can't reach it.

  • Without the internet, the network is internal: Docker gives it no way out. Use Docker 25.0.5 or later: older versions let the machine's name lookups (DNS) out even then.
  • With the internet, Docker can't tell public addresses from private ones. The machine can reach anything this server can: your local network, and ports open on the server itself. Docker has no setting to stop that. If it matters, keep internet: false, block private addresses for the machines with the server's own firewall, or install the runner in Kubernetes with network rules.

Stop using a runner#

To go back to our cloud, choose Our cloud under Where it runs (see Going back to our cloud). To stop a runner for good, click ⋯ on its row, then Revoke key…, and confirm: its next call is refused, any run on it stops, and the keys scrambled for it are deleted. Then uninstall the chart from your cluster (helm uninstall cerberius-runner --namespace cerberius-runner), or on one server run docker compose down in the runner's folder.

Not with a runner yet#

  • Setup assistants and chatbots still answer from our cloud. The content writer's runs go to the runner like the agent's.
  • Microsoft 365, Google and DNS need runner 0.8.0 or later to use keys kept by you.
  • Microsoft SQL Server and Azure SQL databases need runner 0.10.0 or later for its checks and changes.
  • MongoDB and MongoDB Atlas databases need runner 0.11.0 or later for its checks and changes.