sshhostkeycache captures a host's SSH key during terraform apply and reuses the cached result during later terraform plan runs.
This avoids doing network-dependent SSH key discovery at plan time, which can make plans noisy or flaky. Especially if the vm employs anti-recon protections like fail2ban.
Requires OpenSSH client tools (
ssh,ssh-keyscan) available in PATH on the machine running Terraform.
sshhostkeycache_entry scans the target host:
- on resource create
- when
refresh_keychanges
It does not rescan during normal Read or plan behavior. Instead, it reuses the previously captured result.
Use refresh_key as the signal that the machine should be treated as new. Good choices include:
- instance ID
- VM replacement trigger
- image or build version
- a manually bumped value
terraform {
required_providers {
sshhostkeycache = {
source = "linode/sshhostkeycache"
version = ">= 0.1.9"
}
linode = {
source = "linode/linode"
}
}
}
variable "linode_root_pass" {
type = string
sensitive = true
}
resource "linode_instance" "vm" {
label = "example-vm"
region = "us-east"
type = "g6-standard-1"
image = "linode/ubuntu24.04"
root_pass = var.linode_root_pass
}
resource "sshhostkeycache_entry" "vm" {
host = linode_instance.vm.ip_address
port = 22
types = "ed25519,ecdsa,rsa"
refresh_key = tostring(linode_instance.vm.id)
}
output "host_key" {
value = sshhostkeycache_entry.vm.host_key
}
output "known_hosts_line" {
value = sshhostkeycache_entry.vm.known_hosts_line
}types is a comma-separated preference list, for example:
types = "ed25519,ecdsa,rsa"The provider selects the first available matching key type from the cached scan result.
Changing types does not force a new scan by itself. It only changes which cached key is selected.
If you need a fresh probe of the host, change refresh_key.
host(String) — Target host to scan.refresh_key(String) — Controls when a new scan happens. Changing this forces replacement and a fresh scan.
port(Number) — SSH port. Defaults to22.types(String) — Comma-separated preferred key types. Defaults to"ecdsa".retries(Number) — Number of scan attempts before giving up. Defaults to60.sleep_secs(Number) — Delay between retries in seconds. Defaults to5.timeout_secs(Number) — Per-attempt timeout forssh-keyscan. Defaults to5.
Use this when the target is only reachable through a bastion host.
host(String) — Bastion hostname or IP.port(Number) — Bastion SSH port. Defaults to22.user(String) — SSH username for the bastion.private_key(String, Sensitive) — Private key used to connect to the bastion.known_host(String) — Optional bastionknown_hostsline. If omitted, bastion host key checking is disabled.
id(String) — Resource ID inhost:portform.host_key(String) — Selected host key inkeytype base64form.known_hosts_line(String) — Full selectedknown_hostsline.
- A scan happens on:
- create
- replacement caused by changing
refresh_key
- A scan does not happen on:
- normal
Read - normal
plan
- normal
- If
typeschanges and cached scan results are available, the provider reselects from the cached keys without rescanning. - If
typeschanges and no cache is available, update fails and you must changerefresh_keyto force a new scan.
types is a comma-separated preference list, for example:
types = "ed25519,ecdsa,rsa"The provider selects the first available matching key from the cached scan result.
Aliases supported in types include:
ecdsa→ matches available ECDSA variantsed25519→ matchesssh-ed25519rsa→ matchesssh-rsadsa→ matchesssh-dss