k3s-io/k3s · critical
missing %s directory from ${data-dir}
Error message
missing %s directory from ${data-dir} What it means
checkCertDirs verifies that <data-dir>/cred, <data-dir>/tls and <data-dir>/tls/etcd exist once storage is ready (pkg/cluster/bootstrap.go:167). A missing directory means the server's credentials and PKI material were never created or were deleted, so k3s aborts startup instead of proceeding with half-present security state.
Source
Thrown at pkg/cluster/bootstrap.go:183
// An error is returned if any dirs are missing or empty.
func (c *Cluster) checkCertDirs() error {
bootstrapDirs := []string{
"cred",
"tls",
"tls/etcd",
}
const (
missingDir = "missing %s directory from ${data-dir}"
emptyDir = "%s directory is empty"
)
for _, dir := range bootstrapDirs {
d := filepath.Join(c.config.DataDir, dir)
if _, err := os.Stat(d); os.IsNotExist(err) {
errMsg := fmt.Sprintf(missingDir, d)
logrus.Debug(errMsg)
return errors.New(errMsg)
}
ok, err := isDirEmpty(d)
if err != nil {
return err
}
if ok {
errMsg := fmt.Sprintf(emptyDir, d)
logrus.Debug(errMsg)
return errors.New(errMsg)
}
}
return nil
}
// migrateBootstrapData migrates bootstrap data from the old format to the new format.View on GitHub (pinned to 6ba341e396)
Solutions
- Point --data-dir at the complete server data directory (the one containing cred/, tls/, db/)
- Restore the missing directories from a full data-dir backup or an etcd snapshot restore flow
- If the node is meant to be fresh, wipe the data-dir entirely and re-join so bootstrap recreates the layout coherently
Example fix
# before data-dir: /var/lib/rancher/k3s/server2 # never initialized as a server # after data-dir: /var/lib/rancher/k3s/server # complete layout: cred/, tls/, tls/etcd/, db/
Defensive patterns
Strategy: validation
Validate before calling
// preflight: required server PKI directories exist
for _, d := range []string{"cred", "tls", "tls/etcd"} {
p := filepath.Join(dataDir, d)
if fi, err := os.Stat(p); err != nil || !fi.IsDir() {
return fmt.Errorf("missing %s directory from data-dir", p)
}
} Type guard
func hasCompleteServerPKI(dataDir string) bool {
for _, d := range []string{"cred", "tls", "tls/etcd"} {
if fi, err := os.Stat(filepath.Join(dataDir, d)); err != nil || !fi.IsDir() {
return false
}
}
return true
} Prevention
- Back up the whole data-dir (not selected files) including cred/ and tls/
- Never delete individual PKI subdirectories to 'force regeneration'
- Verify data-dir completeness after restores with a scripted check before starting k3s
When it happens
Trigger: Server data-dir where any of cred/, tls/ or tls/etcd/ is absent — e.g. after a partial `rm -rf` of /var/lib/rancher/k3s/server, an incomplete data-dir restore, or pointing --data-dir at a directory that never held a server.
Common situations: Operators deleting cert subdirectories to force regeneration; backups that missed hidden files or subdirs; wrong --data-dir after moving storage; interrupted cluster resets.
Related errors
- %s directory is empty
- --token is required
- service %s is not recognized
- cannot perform cluster-reset while server URL is set - remov
- does not exist, please pass --token to complete the restora
AI-assisted analysis of k3s-io/k3s@6ba341e396 (2026-08-15).
Data as JSON: /api/errors/aad178304f19b63f.
Report an issue: GitHub.