Teleport is an access platform that replaces static SSH keys with short-lived certificates issued after the user logs in, and records every session for audit. A single Teleport cluster can also broker access to Kubernetes, databases and web applications, but SSH is where most teams start. In this tutorial you will install a Teleport cluster on Ubuntu 24.04 with a Let's Encrypt certificate, create an admin user with MFA, join a second server as an SSH node, restrict access with a custom role, and replay a recorded session.

Prerequisites

To follow this guide you need:

  • A server running Ubuntu 24.04 LTS for the Teleport cluster, for example a CubePath VPS with at least 1 vCPU and 2 GB of RAM.
  • A second Ubuntu 24.04 server to join as an SSH node.
  • A non-root user with sudo privileges on both servers.
  • A domain name with a DNS A record, such as teleport.your_domain, pointing to the cluster server's public IP.
  • TCP port 443 reachable on the cluster server from the internet, so Let's Encrypt can validate the certificate.
  • An authenticator app (or a hardware key) for the admin user's second factor.

Step 1 - Installing Teleport

Teleport publishes an apt repository per major version. Look up the current major version in the Teleport documentation and set it in a shell variable (18 is used here as an example):

TELEPORT_MAJOR=18

Download the repository signing key to /etc/apt/keyrings and add the repository for your distribution:

sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://apt.releases.teleport.dev/gpg -o /etc/apt/keyrings/teleport-archive-keyring.asc
. /etc/os-release
echo "deb [signed-by=/etc/apt/keyrings/teleport-archive-keyring.asc] https://apt.releases.teleport.dev/${ID} ${VERSION_CODENAME} stable/v${TELEPORT_MAJOR}" | sudo tee /etc/apt/sources.list.d/teleport.list

Install the package, which contains the teleport daemon and the tctl and tsh clients:

sudo apt update
sudo apt install teleport
teleport version
Teleport v18.x.x git:... go1.x

Run this step on both servers.

Step 2 - Configuring the cluster with automatic TLS

On the cluster server, open port 443 in UFW (keep SSH open until you have confirmed Teleport works):

sudo ufw allow OpenSSH
sudo ufw allow 443/tcp

Generate a configuration that runs the Auth service, the Proxy service and a local SSH service, and obtains its certificate from Let's Encrypt. Replace teleport.your_domain and the email address:

sudo teleport configure -o file \
  --acme --acme-email=admin@your_domain \
  --cluster-name=teleport.your_domain

The command writes /etc/teleport.yaml. The proxy listens on port 443 and multiplexes web, SSH and agent traffic on that single port, so you do not need to open Teleport's legacy ports (3022 to 3025). Start the service:

sudo systemctl enable --now teleport
sudo systemctl status teleport

The first start takes a few seconds while Teleport generates its certificate authorities and requests the TLS certificate. Follow the log if you want to watch it:

sudo journalctl -u teleport -f

Open https://teleport.your_domain in a browser. You should see the Teleport login page with a valid certificate.

Step 3 - Creating the admin user

Users are created with tctl, which talks directly to the local Auth service. Create a user with the built-in editor role (manage the cluster) and access role (connect to resources), and allow it to log in as root and ubuntu on nodes:

sudo tctl users add teleport-admin --roles=editor,access --logins=root,ubuntu
User "teleport-admin" has been created but requires a password. Share this URL with the user to complete user setup, link is valid for 1h:
https://teleport.your_domain:443/web/invite/0123456789abcdef

NOTE: Make sure teleport.your_domain:443 points at a Teleport proxy which users can access.

Open the link, set a password and register your authenticator app. Teleport requires a second factor by default.

Now log in from your workstation with tsh. On Linux it comes with the package from Step 1; on macOS and Windows, download the client tools from goteleport.com. Use the same major version as the cluster:

tsh login --proxy=teleport.your_domain --user=teleport-admin
> Profile URL:        https://teleport.your_domain:443
  Logged in as:       teleport-admin
  Cluster:            teleport.your_domain
  Roles:              access, editor
  Logins:             root, ubuntu
  Valid until:        2026-09-25 22:10:00 +0000 UTC [valid for 12h0m0s]

tsh now holds a certificate valid for 12 hours. List the servers you can reach; the cluster server itself already appears:

tsh ls

Step 4 - Joining a server as an SSH node

Nodes join the cluster with a short-lived token. On the cluster server, create one valid for one hour:

sudo tctl tokens add --type=node --ttl=1h

The output contains the token and an example command. On the second server, where you installed Teleport in Step 1, generate a node configuration with that token:

sudo teleport node configure \
  --output=file:///etc/teleport.yaml \
  --token=your_join_token \
  --proxy=teleport.your_domain:443

Before starting the node, add labels to describe it. Labels are what roles use to decide who can reach which server. Edit the generated file:

sudo nano /etc/teleport.yaml

In the ssh_service section, add a labels block:

ssh_service:
  enabled: true
  labels:
    env: staging

Keep the other keys in that section as generated. Start the node:

sudo systemctl enable --now teleport

The node opens an outbound connection to the proxy on port 443, so it needs no inbound ports for Teleport. From your workstation, confirm it joined:

tsh ls
Node Name    Address          Labels
------------ ---------------- ------------------------------
teleport     127.0.0.1:3022   hostname=teleport
staging-01   ⟵ Tunnel         env=staging,hostname=staging-01

Connect to it:

tsh ssh ubuntu@staging-01

Step 5 - Restricting access with a role

The access role lets a user reach every node with the logins listed on their user. For a developer who should only reach staging servers as ubuntu, create a dedicated role. On your workstation (you are logged in as an editor), create a file:

nano staging-access.yaml
kind: role
version: v7
metadata:
  name: staging-access
spec:
  allow:
    logins: ['ubuntu']
    node_labels:
      env: 'staging'
  options:
    max_session_ttl: 8h

Upload it and create a user with only this role:

tctl create -f staging-access.yaml
tctl users add alice --roles=staging-access

tctl uses your tsh credentials when run from a workstation, so sudo is not needed there. Send the invite link to the user. After logging in, tsh ls for that user shows only nodes labeled env=staging, and SSH as any login other than ubuntu is denied.

To change an existing user's roles later, run tctl users update alice --set-roles=staging-access,another-role.

Step 6 - Reviewing recorded sessions

By default Teleport records every interactive SSH session and stores the recordings on the Auth service under /var/lib/teleport/log. List recent recordings:

tsh recordings ls
ID                                   Type Participants  Hostname    Timestamp
------------------------------------ ---- ------------- ----------- -------------------
6b2c1a3e-8d4f-4a61-9b0e-2f7d5c8e9a10 ssh  teleport-admin staging-01 Sep 25 10:24:01 UTC

Replay one in your terminal:

tsh play 6b2c1a3e-8d4f-4a61-9b0e-2f7d5c8e9a10

The web UI shows the same recordings under Audit > Session Recordings, next to the audit log of logins, role changes and certificate issuance.

Step 7 - Closing direct SSH access

Once you have confirmed you can reach every server through Teleport, you can stop exposing port 22 to the internet on the nodes, since Teleport reaches them through the outbound tunnel:

sudo ufw delete allow OpenSSH

Do this only after testing, and keep a way in that does not depend on Teleport, such as the provider's web console, in case the Teleport service is down.

Troubleshooting

The browser shows a certificate error. Let's Encrypt could not validate the domain. Check that the A record points to the server and that port 443 is open, then look for ACME errors with sudo journalctl -u teleport | grep -i acme.

A node does not appear in tsh ls. On the node, run sudo journalctl -u teleport -n 50. token expired or not found means the one-hour token lapsed; create a new one with sudo tctl tokens add --type=node --ttl=1h, put it in the token field of /etc/teleport.yaml and restart.

access denied when connecting. The login you used is not in the user's allowed logins, or the node labels do not match any of the user's roles. Run tsh status to see your roles and logins, and compare with the node's labels in tsh ls.

tsh reports a version mismatch. Install a client with the same major version as the cluster.

Conclusion

You now have a Teleport cluster with a valid TLS certificate, an MFA-protected admin, a labeled SSH node joined through a reverse tunnel, a least-privilege role, and recorded sessions you can replay. Next, connect GitHub as a single sign-on provider so users log in with their existing accounts, add Kubernetes clusters or databases to the same cluster, and automate node enrollment with longer-lived join methods for autoscaled servers.