Lesstep
Bitbucket Cloud · Pipelines · Checked November 2, 2026

Bitbucket Pipelines: "Host key verification failed" and "Permission denied (publickey)"

A step that runs ssh, scp, rsync or git over SSH needs two things: the remote host in the build's known_hosts, and a key the host accepts. Without the first, ssh stops before it offers any key:

Host key verification failed.

Without the second, the host turns the connection down:

deploy@203.0.113.10: Permission denied (publickey).

Host key verification failed

Atlassian's page Set up Pipelines SSH keys on Linux keeps known hosts next to the key. In Repository settings, SSH keys under Pipelines, add the host's address and select Fetch to see its fingerprint. Compare it with the one the server reports for itself, then add the host. Later builds check the host against it.

ssh-keygen -lf /etc/ssh/ssh_host_ed25519_key.pub   # on the server

Pipelines adds the fingerprints of Bitbucket and GitHub to every build by itself, without listing them there. Since October 2026 that includes ssh.bitbucket.org, the host Git over SSH is moving to (the schedule). Atlassian closed BCLOUD-24132 as fixed on October 20, with the comment "Added ssh.bitbucket.org to known hosts list". Before that, a step running ssh -T git@ssh.bitbucket.org stopped here.

When a server is rebuilt or gets new host keys, the saved fingerprint no longer matches, and ssh stops the same way. Remove the host from the list, check the new fingerprint, and fetch it again.

Where those known hosts do not reach

ssh-keyscan in a script trusts whatever answers at that moment. Atlassian's page Using SSH keys in Bitbucket Pipelines uses it in its examples, and warns that turning the check off with StrictHostKeyChecking=no leaves you open to man-in-the-middle attacks. A known_hosts file committed to the repository, made once from a host you checked, avoids both (Use multiple SSH keys in your pipeline):

- mkdir -p ~/.ssh
- cat my_known_hosts >> ~/.ssh/known_hosts

Permission denied (publickey)

The key comes from the same settings page. Generate keys makes a pair in Bitbucket. Use my own keys takes a pair you made, in the Private key and Public key fields, then Save key pair. Pipelines adds the private key as the default identity in ~/.ssh/config for steps on Atlassian's machines and on Linux runners, so ssh in the step uses it without -i.

Atlassian also notes that everyone with write access to the repository can reach the remote host through this key.

A second key, and "Load key ... invalid format"

Pipelines holds one SSH key per repository. Use multiple SSH keys in your pipeline keeps more keys as secured repository variables. Variables cannot hold line breaks, so the private key is base64-encoded first:

base64 -w 0 < deploy_key     # on macOS: base64 < deploy_key

In the step, decode it into a file only you can read, and pass it with -i:

- (umask 077; echo $DEPLOY_KEY | base64 --decode > ~/.ssh/deploy_key)
- ssh -i ~/.ssh/deploy_key deploy@203.0.113.10 'echo ok'

The page warns that secured variables can be read by everyone with write access, and that repository variables are passed to child processes. It recommends a key made for Pipelines, not your personal key, and deployment variables with deployment permissions to narrow who can use it.

Atlassian's pipes use the repository's Pipelines key unless you give one in SSH_KEY, and that one has to be base64-encoded the same way. A key pasted as it is fails like this (Atlassian's article Bitbucket Cloud Pipe "atlassian/scp-deploy" error, updated September 26, 2025):

debug1: Trying private key: /root/.ssh/pipelines_id
Load key "/root/.ssh/pipelines_id": invalid format
debug1: No more authentication methods to try.
user@xx.xx.xx.xx: Permission denied (publickey,gssapi-keyex,gssapi-with-mic).

Your own image

Not every Docker image has an SSH client. The default Pipelines image does. With your own image, install it in the script, for example apt-get update -y and apt-get install -y ssh (Atlassian's Linux page). Without it the step stops at ssh: command not found.

Bitbucket Pipelines in IntelliJ IDEA

Pipeline Inbox for Bitbucket Pipelines, a plugin by Lesstep for JetBrains IDEs 2024.1 or later, lists the pipelines of all your Bitbucket Cloud repositories in an IDE tool window, with their steps, the failed step and the command it stopped at, and the step's log as Bitbucket writes it. The list, steps and logs are free; running, re-running and stopping pipelines are Pro (US$1.50 a month, with a 30-day trial).

Get it on JetBrains Marketplace

Atlassian, Bitbucket and Bitbucket Pipelines are trademarks of Atlassian; Docker is a trademark of Docker, Inc.; GitHub is a trademark of GitHub, Inc.; IntelliJ IDEA is a trademark of JetBrains s.r.o. This page is not affiliated with or endorsed by Atlassian, Docker, GitHub or JetBrains.

More: "Cannot connect to the Docker daemon" · "authorization denied by plugin pipelines" (Docker) · "Container 'docker' exceeded memory limit." · Pipelines did not run after a push · "Container 'Build' exceeded memory limit." · "Exceeded build time limit of 120 minutes" · The cache is not used · "unexpected system error" · "command not found" · Submodules not cloned · "We couldn't clone the repository" · "No online runner available" · "No space left on device" · "Step exceeded processing limits" · The artifact is missing in the next step · The build passed before and fails now · Build setup takes too long · "You've run out of build minutes" · "Permission denied to update branch" · "You have reached your pull rate limit" (Docker Hub) · "There is an error in your bitbucket-pipelines.yml" · "This pipeline was paused" (deployments) · A variable is empty · Test results not showing · Bitbucket Pipelines plugins for IntelliJ IDEA, compared · 日本語