slackhq/nebula · error
error while loading sshd.host_key file: %s
Error message
error while loading sshd.host_key file: %s
What it means
When sshd.host_key is a path (not an inline PEM literal), configSSH reads it with os.ReadFile. Any read failure — missing file, permission denied, wrong path — is wrapped as "error while loading sshd.host_key file". The server cannot start without the host key bytes.
Source
Thrown at ssh.go:106
if err != nil {
return nil, fmt.Errorf("invalid sshd.listen address: %s", err)
}
if port == "22" {
return nil, fmt.Errorf("sshd.listen can not use port 22")
}
hostKeyPathOrKey := c.GetString("sshd.host_key", "")
if hostKeyPathOrKey == "" {
return nil, fmt.Errorf("sshd.host_key must be provided")
}
var hostKeyBytes []byte
if strings.Contains(hostKeyPathOrKey, "-----BEGIN") {
hostKeyBytes = []byte(hostKeyPathOrKey)
} else {
hostKeyBytes, err = os.ReadFile(hostKeyPathOrKey)
if err != nil {
return nil, fmt.Errorf("error while loading sshd.host_key file: %s", err)
}
}
err = ssh.SetHostKey(hostKeyBytes)
if err != nil {
return nil, fmt.Errorf("error while adding sshd.host_key: %s", err)
}
// Clear existing trusted CAs and authorized keys
ssh.ClearTrustedCAs()
ssh.ClearAuthorizedKeys()
rawCAs := c.GetStringSlice("sshd.trusted_cas", []string{})
for _, caAuthorizedKey := range rawCAs {
err := ssh.AddTrustedCA(caAuthorizedKey)
if err != nil {
l.Warn("SSH CA had an error, ignoring", "error", err, "sshCA", caAuthorizedKey)
continueView on GitHub (pinned to dd8f660c0a)
Solutions
- Verify the path exists and is readable by the process user (ls -l / test with os.ReadFile).
- Use an absolute path in sshd.host_key instead of a relative one.
- If providing an inline key, ensure it includes the "-----BEGIN ... PRIVATE KEY-----" header so it isn't treated as a path.
Example fix
// before host_key = "host_key" // relative, wrong cwd // after host_key = "/etc/myapp/ssh/host_key"
Defensive patterns
Strategy: validation
Validate before calling
hk := cfg.GetString("sshd.host_key", "")
if !strings.Contains(hk, "-----BEGIN") { // treated as a path
if _, err := os.Stat(hk); err != nil {
return fmt.Errorf("sshd.host_key file unreadable: %w", err)
}
} Try / catch
run, err := configSSH(logger, srv, c)
if err != nil {
var pe *fs.PathError
if strings.Contains(err.Error(), "error while loading sshd.host_key file") {
logger.Error("cannot read sshd.host_key file; check path and permissions")
os.Exit(78)
}
_ = pe
return err
} Prevention
- Use absolute paths for sshd.host_key; never rely on the process working directory.
- Ensure the key file is readable by the runtime user (check with sudo -u <user> test -r).
- If embedding the key inline, it must contain a "-----BEGIN" PEM header.
When it happens
Trigger: sshd.host_key contains a path and os.ReadFile fails: nonexistent file, unreadable permissions, wrong working directory for a relative path, or the value is a PEM body without "-----BEGIN" so it is treated as a path (ssh.go:106).
Common situations: Container images that never copied the key file; running the binary as a non-root user lacking read permission; relative paths resolving differently under systemd's WorkingDirectory; pasting a public key or truncated key without the BEGIN header.
Related errors
- sshd.host_key must be provided
- error while adding sshd.host_key: %s
- sshd.listen must be provided
- invalid sshd.listen address: %s
- sshd.listen can not use port 22
AI-assisted analysis of slackhq/nebula@dd8f660c0a (2026-09-03).
Data as JSON: /api/errors/4df526d02fc1f300.
Report an issue: GitHub.