# Deploying tbot with Bound Keypair Joining

In this guide, you will install Machine & Workload Identity's agent, `tbot`, on an arbitrary host using Bound Keypair Joining. This host could be a bare-metal machine, a VM, a container, or any other host - the only requirement is that the host has persistent storage.

Bound Keypair Joining is an improved alternative to [secret-based join methods](https://goteleport.com/docs/reference/deployment/join-methods.md#secret-vs-delegated) and can function as a drop-in replacement. It is more secure than static token joining, and is more flexible than ephemeral token joining with renewable certificates: when its certificates expire, it can perform an automated recovery to ensure the bot can rejoin even after an extended outage.

Note that platform-specific join methods may be available that are better suited to your environment; refer to the [deployment guides](https://goteleport.com/docs/machine-workload-identity/deployment.md) for a full list of options.

## How it works

With Bound Keypair Joining, Machine & Workload Identity bots generate a unique keypair which is persistently stored in their internal data directory. Teleport is then configured to trust this public key for future joining attempts.

Later, when the bot attempts to join the cluster, Teleport issues it a challenge that can only be completed using its private key. The bot returns the solved challenge, attesting to its own identity, and is conditionally allowed to join the cluster. This process is repeated for every join attempt, but if the bot has been offline long enough for its certificates to expire, it is additionally forced to perform an automatic recovery to join again.

As self attestation is inherently less secure than the external verification that would be provided by a cloud provider like AWS or a dedicated TPM, Bound Keypair Joining enforces a number of additional checks to prevent abuse, including:

- Join state verification to ensure the keypair cannot be usefully shared or duplicated
- Certificate generation counter checks to ensure regular bot certificates cannot be usefully shared or duplicated
- Configurable limits on how often - if at all - bots may be allowed to automatically recover using this keypair

An important benefit to Bound Keypair Joining is that all joining restrictions can be reconfigured at any time, and bots that expire or go offline can be recovered by making a server-side exemption without any client-side intervention.

Refer to the [reference page](https://goteleport.com/docs/reference/machine-workload-identity/bound-keypair.md) for further details on how this join method works and how to use it in production.

## Prerequisites

- A running Teleport cluster version 18.1.0 or above.
- The `tsh` and `tctl` clients.
- To check that you can connect to your Teleport cluster, sign in with `tsh login`, then verify that you can run `tctl` commands using your current credentials. For example, run the following command, assigning teleport.example.com to the domain name of the Teleport Proxy Service in your cluster and email\@example.com to your Teleport username:
  ```
  $ tsh login --proxy=teleport.example.com --user=email@example.com
  $ tctl status
  Cluster  teleport.example.com
  Version  19.0.0-dev
  CA pin   sha256:abdc1245efgh5678abdc1245efgh5678abdc1245efgh5678abdc1245efgh5678
  ```
  If you can connect to the cluster and run the `tctl status` command, you can use your current credentials to run subsequent `tctl` commands from your workstation. If you host your own Teleport cluster, you can also run `tctl` commands on the computer that hosts the Teleport Auth Service for full permissions.
- This guide assumes the bot host has mutable persistent storage for internal bot data. While it is possible to use Bound Keypair Joining can on immutable hosts (like CI runs), doing so will reduce security guarantees; see the [reference page](https://goteleport.com/docs/reference/machine-workload-identity/bound-keypair.md) for further information.

## Step 1/5. Install `tbot`

**This step is completed on the bot host.**

First, `tbot` needs to be installed on the host that you wish to use Machine & Workload Identity on.

Download and install the appropriate Teleport package for your platform:

To install a Teleport Agent on your Linux server:

The recommended installation method is the cluster install script. It will select the correct version, edition, and installation mode for your cluster.

1. Assign teleport.example.com:443 to your Teleport cluster hostname and port, but not the scheme (https\://).

2. Run your cluster's install script:

   ```
   $ curl "https://teleport.example.com:443/scripts/install.sh" | sudo bash
   ```

## Step 2/5. Create a Bot

**This step is completed on your local machine.**

Next, you need to create a Bot. A Bot is a Teleport identity for a machine or group of machines. Like users, bots have a set of roles and traits which define what they can access.

Create `bot.yaml`:

```
kind: bot
version: v1
metadata:
  # name is a unique identifier for the Bot in the cluster.
  name: example
spec:
  # roles is a list of roles to grant to the Bot. Don't worry if you don't know
  # what roles you need to specify here, the Access Guides will walk you through
  # creating and assigning roles to the already created Bot.
  roles: []

```

Make sure you replace `example` with a unique, descriptive name for your Bot.

Use `tctl` to apply this file:

```
$ tctl create bot.yaml
```

## Step 3/5. Create a join token

**This step is completed on your local machine.**

In this guide, we'll demonstrate joining a bot using a registration secret: this is a one-time use secret the bot can provide to Teleport to authenticate its first join. Once authenticated, the bot automatically generates a keypair and registers its public key with Teleport for use in all future join attempts.

Create `token-example.yaml`:

```
kind: token
version: v2
metadata:
  # This name will be used in tbot's `onboarding.token` field.
  name: example
spec:
  roles: [Bot]
  # bot_name should match the name of the bot created earlier in this guide.
  bot_name: example
  join_method: bound_keypair
  bound_keypair:
    recovery:
      mode: standard
      limit: 1

```

Replace `example` in `spec.bot_name` with the name of the bot you created in the second step.

For this example, we don't need to set any additional options for the bound keypair token. We've allowed a single recovery attempt, which will be used to allow the bot's initial join, and Teleport will generate a registration secret automatically when the token is created as we have not preregistered a public key to use.

---

ONBOARDING OPTIONS

This example makes use of registration secrets to authenticate the initial join. If desired, it is also possible to generate a key on the bot host first and register it with Teleport out-of-band, avoiding the need to copy secrets between hosts.

To learn more about preregistering public keys, see the [alternative flow](#alternative-preregistered-keys) below. For more information on Bound Keypair Joining's other onboarding and recovery options, refer to the [reference page](https://goteleport.com/docs/reference/machine-workload-identity/bound-keypair.md).

---

Use `tctl` to apply this file:

```
$ tctl create -f token-example.yaml
```

Next, retrieve the generated registration secret, which will be needed for the next step:

```
$ tctl get token/example --format=json | jq -r '.[0].status.bound_keypair.registration_secret'
```

This assumes `jq` is installed. If not, run `tctl get token/example` and inspect the `.status.bound_keypair.registration_secret` field.

## Step 4/5. Configure `tbot`

**This step is completed on the bot host.**

### Prepare a storage directory

Create the bot data directory and grant permissions to access it to the Linux user (in our example, `teleport`) which `tbot` will run as.

```
Make the bot directory and assign ownership to teleport user
$ sudo mkdir -p /var/lib/teleport/bot
$ sudo chown teleport:teleport /var/lib/teleport/bot
Allow teleport user to open directory
$ sudo chmod +x /var/lib/teleport /var/lib/teleport/bot
```

### Create a configuration file

Create `/etc/tbot.yaml`:

```
version: v2
proxy_server: example.teleport.sh:443
onboarding:
  join_method: bound_keypair
  token: example
  bound_keypair:
    registration_secret: SECRET
storage:
  type: directory
  path: /var/lib/teleport/bot
# services will be filled in during the completion of an access guide.
services: []

```

Replace the following:

- `example.teleport.sh:443` with the address of your Teleport Proxy.
- `example` with the name of the token created in the previous step, if you changed it from `example`.
- `SECRET` with the registration secret retrieved in the previous step.

Now, you must decide if you want to run `tbot` as a daemon or in one-shot mode.

In daemon mode, `tbot` runs continually, renewing the short-lived credentials for the configured outputs on a fixed interval. This is often combined with a service manager (such as systemd) in order to run `tbot` in the background. This is the default behaviour of `tbot`.

In one-shot mode, `tbot` generates short-lived credentials and then exits. This is useful when combining `tbot` with scripting (such as in CI/CD) as it allows further steps to be dependent on `tbot` having succeeded. It is important to note that the credentials will expire if not renewed and to ensure that the TTL for the certificates is long enough to cover the length of the CI/CD job.

#### Configuring `tbot` as a daemon

By default, `tbot` will run in daemon mode. However, this must then be configured as a service within the service manager on the Linux host. The service manager will start `tbot` on boot and ensure it is restarted if it fails.

**If tbot was installed using the Teleport install script or `teleport-update` command, the `tbot` systemd service is automatically created for you.**

After `tbot.yaml` is created, enable and start the service:

```
$ sudo systemctl enable tbot --now
```

Check the service has started successfully:

```
$ sudo systemctl status tbot
```

Service properties like `User` and `Group` may be configured using `systemctl edit tbot`.

**If tbot was installed manually, service configuration will need to be performed manually as well.**

For this guide, systemd will be demonstrated but `tbot` should be compatible with all common alternatives.

Use `tbot install systemd` to generate a systemd service file:

```
$ sudo tbot install systemd \
   --write \
   --config /etc/tbot.yaml \
   --user teleport \
   --group teleport \
   --pid-file /run/tbot.pid \
   --anonymous-telemetry
```

Ensure that you replace:

- `teleport` with the name of Linux user you wish to run `tbot` as.
- `/etc/tbot.yaml` with the path to the configuration file you have created.
- `/run/tbot.pid` with a path that the user configured for `tbot` has write access to. By default, `tbot` writes its PID file to `/run/tbot.pid`.

You can omit `--write` to print the systemd service file to the console instead of writing it to disk.

`--anonymous-telemetry` enables the submission of anonymous usage telemetry. This helps us shape the future development of `tbot`. You can disable this by omitting this.

Next, enable the service so that it will start on boot and then start the service:

```
$ sudo systemctl daemon-reload
$ sudo systemctl enable tbot --now
```

Check the service has started successfully:

```
$ sudo systemctl status tbot
```

#### Configuring `tbot` for one-shot mode

To use `tbot` in one-shot mode, modify `/etc/tbot.yaml` to add `oneshot: true`:

```
version: v2
oneshot: true
proxy_server: ...

```

Now, you should test your `tbot` configuration. When started, several log messages will be emitted before it exits with status 0:

```
$ export TELEPORT_ANONYMOUS_TELEMETRY=1
$ tbot start -c /etc/tbot.yaml
```

`TELEPORT_ANONYMOUS_TELEMETRY` enables the submission of anonymous usage telemetry. This helps us shape the future development of `tbot`. You can disable this by omitting this.

## Step 5/5. Configure outputs

You have now prepared the base configuration for `tbot`. At this point, it identifies itself to the Teleport cluster and renews its own credentials but does not output any credentials for other applications to use.

Follow one of the [access guides](https://goteleport.com/docs/machine-workload-identity/access-guides.md) to configure a service that meets your access needs.

## Alternative: preregistered keys

This guide makes use of a registration secret - a single use shared secret that's consumed on the first bot join and allows a bot to automatically register its public key with Teleport. If you don't want to use any shared secrets, you can instead opt to generate the bot's keypair ahead of time and inform Teleport of the public key yourself.

To generate a public key, run the following command on the bot host:

```
# If needed, create the bot's storage directory
$ mkdir -p /var/lib/teleport/bot
$ tbot keypair create --storage /var/lib/teleport/bot --proxy-server=example.teleport.sh:443
2025-07-08T16:31:48.000-00:00 INFO [TBOT]      keypair has been written to storage storage:directory: /var/lib/teleport/bot tbot/keypair.go:135

To register the keypair with Teleport, include this public key in the token's
`spec.bound_keypair.onboarding.initial_public_key`:

	ssh-ed25519 <data>
```

Note the SSH-style public key written to the console - you'll need this value in the next step.

---

AUTOMATION NOTE

Be aware that if a keypair already exists within the specified storage directory it will not be overwritten by default. If an existing key is found, a warning will be logged and the existing public key will be printed to the console.

If you explicitly want to create a new public key, pass the `--overwrite` flag to `tbot keypair create`. A warning will also be logged if any keys are actually overwritten.

If you'd like to automate this process, the `--format=json` flag will write the public key string in a JSON document for use in scripts.

---

Note that while a proxy server address must be provided, this is only used to ping the cluster to determine its configured [signature algorithms](https://goteleport.com/docs/reference/deployment/signature-algorithms.md). Once the keypair has been generated, the public key will be printed to the console.

Next, on your local machine, create a file named `token-example.yaml`:

```
kind: token
version: v2
metadata:
  name: example
spec:
  roles: [Bot]
  join_method: bound_keypair
  bot_name: example
  bound_keypair:
    onboarding:
      initial_public_key: "ssh-ed25519 <data>"
    recovery:
      mode: standard
      limit: 1

```

The SSH-style public key generated by the bot should be copied into the `initial_public_key` field. As shown above, the value should be quoted to ensure the YAML remains valid.

Create the token with `tctl`:

```
$ tctl create -f token-example.yaml
```

Back on the bot machine, configure the bot as in the original guide, but this time with no `registration_secret` field set. Create a `tbot.yaml` with this content:

```
version: v2
proxy_server: example.teleport.sh:443
onboarding:
  join_method: bound_keypair
  token: example
storage:
  type: directory
  path: /var/lib/teleport/bot
# outputs will be filled in during the completion of an access guide.
outputs: []

```

As in the original example, replace:

- `example.teleport.sh:443` with the address of your Teleport Proxy.
- `example` with the name of the token created in the previous step, if you changed it from `example`.

Note that the `storage.path` must point to the same storage directory you passed to `tbot keypair create` above.

Once the config file has been created, you can start the bot as usual:

```
$ tbot start -c tbot.yaml
```

For additional information on how onboarding a new bot works with Bound Keypair Joining, refer to the [onboarding reference](https://goteleport.com/docs/reference/machine-workload-identity/bound-keypair/concepts.md#onboarding).

## Next steps

- Read the [Bound Keypair Joining Reference](https://goteleport.com/docs/reference/machine-workload-identity/bound-keypair.md) for more details about the join method and the available configuration options.
- Follow the [access guides](https://goteleport.com/docs/machine-workload-identity/access-guides.md) to finish configuring `tbot` for your environment.
- Read the [configuration reference](https://goteleport.com/docs/reference/machine-workload-identity/configuration.md) to explore all the available configuration options.
- [More information about `TELEPORT_ANONYMOUS_TELEMETRY`.](https://goteleport.com/docs/reference/machine-workload-identity/telemetry.md)
