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

  1. Point --data-dir at the complete server data directory (the one containing cred/, tls/, db/)
  2. Restore the missing directories from a full data-dir backup or an etcd snapshot restore flow
  3. 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

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


AI-assisted analysis of k3s-io/k3s@6ba341e396 (2026-08-15). Data as JSON: /api/errors/aad178304f19b63f. Report an issue: GitHub.