amir20/dozzle · error

failed to read certificates

Error message

failed to read certificates: %w

What it means

Returned by AgentCmd.Run when ReadCertificates fails to load the embedded or on-disk TLS certificate/key pair the gRPC agent requires. Agents and main servers authenticate each other with these shared certs, so startup aborts without them.

Solutions

  1. Remove custom --cert-path/--key-path flags so the embedded certs are used.
  2. If custom certs are required, verify both files exist, are valid PEM, and match as a pair.
  3. Regenerate the shared certs: `make generate`.
  4. Check file permissions on the cert and key files.
  5. Ensure the agent and main server share the same certificate pair.

Example fix

// before
customCert=/etc/dozzle/old-cert.pem
--cert-path $customCert --key-path /etc/dozzle/old-key.pem
// after
rm -f /etc/dozzle/old-*.pem
make generate  # or drop the custom cert flags entirely
Defensive patterns

Strategy: validation

Validate before calling

for f in "$CERT" "$KEY"; do [ -r "$f" ] && openssl "$([ "${f##*.}" = key ] && echo rsa || echo x509)" -in "$f" -noout >/dev/null || { echo "bad cert file: $f"; exit 1; }; done

Try / catch

if err := agentCmd.Run(args, embeddedCerts); err != nil {
  if strings.Contains(err.Error(), "failed to read certificates") {
    log.Fatal().Err(err).Msg("check --cert-path/--key-path files")
  }
}

Prevention

When it happens

Trigger: Custom --cert-path/--key-path files are missing, unreadable, or malformed PEM; key does not match certificate; certificates were not regenerated after a version upgrade (make generate).

Common situations: User overrides cert paths with files from an old install; cert/key mismatch after regenerating only one; file permissions block reading the PEM files; running a custom build without running `make generate`.

Understand the failure class

Background: "failed to read file", EACCES, ENOENT and "could not read <path>" errors: when a program can't read a file from disk — this error's family across 49 libraries.

Related errors


AI-assisted analysis of amir20/dozzle@d9463cbe21 (2026-09-07). Data as JSON: /api/errors/d7ed2c953ab7ece0. Report an issue: GitHub.

Appendix: source

Thrown at internal/support/cli/agent_command.go:151

	if h.onCloudSet != nil {
		h.onCloudSet()
	}
	if err := os.Remove("./data/cloud.yml"); err != nil && !os.IsNotExist(err) {
		log.Error().Err(err).Msg("Could not remove cloud.yml on agent")
	}
}

func (a *AgentCmd) Run(args Args, embeddedCerts embed.FS) error {
	if args.Mode != "server" {
		return fmt.Errorf("agent command is only available in server mode")
	}
	client, err := docker.NewLocalClient(args.Hostname)
	if err != nil {
		return fmt.Errorf("failed to create docker client: %w", err)
	}
	certs, err := ReadCertificates(embeddedCerts, args.CertPath, args.KeyPath)
	if err != nil {
		return fmt.Errorf("failed to read certificates: %w", err)
	}

	listener, err := net.Listen("tcp", args.Agent.Addr)
	if err != nil {
		return fmt.Errorf("failed to listen: %w", err)
	}
	const agentAddrFile = "/tmp/dozzle-agent.addr"
	if err := os.WriteFile(agentAddrFile, []byte(args.Agent.Addr), 0644); err != nil {
		return fmt.Errorf("failed to write agent address file: %w", err)
	}
	go StartEvent(args, "", client, "agent")

	ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
	defer stop()

	// Create shared client service (single ContainerStore for both agent server and notifications)
	clientService := docker_support.NewDockerClientService(client, args.Filter)

View on GitHub (pinned to d9463cbe21)