{"record":{"id":"bac3d583f31268f0","repo":"Dokploy/dokploy","slug":"invalid-let-s-encrypt-configuration-structure","errorCode":null,"errorMessage":"Invalid Let's Encrypt configuration structure.","messagePattern":"Invalid Let's Encrypt configuration structure\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/server/src/utils/traefik/web-server.ts","lineNumber":93,"sourceCode":"\n\tif (newHost) {\n\t\twriteTraefikConfig(config, appName);\n\t} else {\n\t\tremoveTraefikConfig(appName);\n\t}\n};\n\nexport const updateLetsEncryptEmail = (newEmail: string | null) => {\n\ttry {\n\t\tif (!newEmail) return;\n\t\tconst { MAIN_TRAEFIK_PATH } = paths();\n\t\tconst configPath = join(MAIN_TRAEFIK_PATH, \"traefik.yml\");\n\t\tconst configContent = readFileSync(configPath, \"utf8\");\n\t\tconst config = parse(configContent) as MainTraefikConfig;\n\t\tif (config?.certificatesResolvers?.letsencrypt?.acme) {\n\t\t\tconfig.certificatesResolvers.letsencrypt.acme.email = newEmail;\n\t\t} else {\n\t\t\tthrow new Error(\"Invalid Let's Encrypt configuration structure.\");\n\t\t}\n\t\tconst newYamlContent = stringify(config);\n\t\twriteFileSync(configPath, newYamlContent, \"utf8\");\n\t} catch (error) {\n\t\tthrow error;\n\t}\n};\n\nexport const readMainConfig = () => {\n\tconst { MAIN_TRAEFIK_PATH } = paths();\n\tconst configPath = join(MAIN_TRAEFIK_PATH, \"traefik.yml\");\n\tif (existsSync(configPath)) {\n\t\tconst yamlStr = readFileSync(configPath, \"utf8\");\n\t\treturn yamlStr;\n\t}\n\treturn null;\n};\n","sourceCodeStart":75,"sourceCodeEnd":111,"githubUrl":"https://github.com/Dokploy/dokploy/blob/546686ea3587f12ec5652217dedd9f7960fb6d15/packages/server/src/utils/traefik/web-server.ts#L75-L111","documentation":"updateLetsEncryptEmail parses traefik.yml and expects config.certificatesResolvers.letsencrypt.acme to exist so it can overwrite the email. If that structure is absent (custom config, older layout, resolver removed), it refuses with this error rather than writing a broken config.","triggerScenarios":"traefik.yml customized so the letsencrypt certificatesResolver was renamed or deleted; a Dokploy version that stores ACME config differently; first-run before Let's Encrypt was configured.","commonSituations":"User hand-edited traefik.yml; upgraded instance with a stale config file; changed the ACME resolver name.","solutions":["Inspect traefik.yml and restore/patch the certificatesResolvers.letsencrypt.acme block","Set the email through the normal Dokploy Let's Encrypt settings flow so config is initialized first","Back up and regenerate the config, then re-apply customizations","Ensure you're on a current Dokploy version matching the config schema"],"exampleFix":"# before (traefik.yml missing resolver)\ncertificatesResolvers: {}\n\n# after\ncertificatesResolvers:\n  letsencrypt:\n    acme:\n      email: old@example.com\n      storage: /letsencrypt/acme.json\n      httpChallenge:\n        entryPoint: web","handlingStrategy":"validation","validationCode":"const cfg = parse(readFileSync(configPath, 'utf8')) as MainTraefikConfig;\nif (!cfg?.certificatesResolvers?.letsencrypt?.acme) {\n  // restore expected structure before updating email\n}","typeGuard":"const hasAcmeResolver = (c: MainTraefikConfig | undefined): c is MainTraefikConfig & { certificatesResolvers: { letsencrypt: { acme: object } } } =>\n  !!c?.certificatesResolvers?.letsencrypt?.acme;","tryCatchPattern":null,"preventionTips":["Back up traefik.yml before manual edits","Keep the letsencrypt resolver name unchanged"],"tags":["traefik","lets-encrypt","acme","config"],"backgroundTag":"config-structure-invalid","analyzedSha":"546686ea3587f12ec5652217dedd9f7960fb6d15","analyzedAt":"2026-08-27T05:18:58.095Z","schemaVersion":2},"datasetVersion":"2026-08-27T08:17:20.692Z"}