{"record":{"id":"ea19a0fb9322f79d","repo":"docker/compose","slug":"cannot-create-s-q-in-read-only-service-s-file","errorCode":null,"errorMessage":"cannot create %s %q in read-only service %s: `file` is the sole supported option","messagePattern":"cannot create (.+?) %q in read-only service (.+?): `file` is the sole supported option","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/compose/secrets.go","lineNumber":59,"sourceCode":"\nfunc (s *composeService) injectConfigs(ctx context.Context, project *types.Project, service types.ServiceConfig, id string) error {\n\treturn s.injectFileReferences(ctx, project, service, id, configMount)\n}\n\nfunc (s *composeService) injectFileReferences(ctx context.Context, project *types.Project, service types.ServiceConfig, id string, mountType mountType) error {\n\tmounts, sources := s.getFilesAndMap(project, service, mountType)\n\n\tfor _, mount := range mounts {\n\t\tcontent, err := s.resolveFileContent(project, sources[mount.Source], mountType)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif content == \"\" {\n\t\t\tcontinue\n\t\t}\n\n\t\tif service.ReadOnly {\n\t\t\treturn fmt.Errorf(\"cannot create %s %q in read-only service %s: `file` is the sole supported option\", mountType, sources[mount.Source].Name, service.Name)\n\t\t}\n\n\t\tif mount.Target == \"\" {\n\t\t\tif mountType == secretMount {\n\t\t\t\tmount.Target = \"/run/secrets/\" + mount.Source\n\t\t\t} else {\n\t\t\t\tmount.Target = \"/\" + mount.Source\n\t\t\t}\n\t\t} else if mountType == secretMount && !isAbsTarget(mount.Target) {\n\t\t\tmount.Target = \"/run/secrets/\" + mount.Target\n\t\t}\n\n\t\tif err := s.copyFileToContainer(ctx, id, content, mount); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}","sourceCodeStart":41,"sourceCodeEnd":77,"githubUrl":"https://github.com/docker/compose/blob/ddc4b044b62e9f715212ea4143fa830fac76382f/pkg/compose/secrets.go#L41-L77","documentation":"When starting a service, compose materializes secrets/configs that are inlined (content:), from environment, or CDK-provided by creating them inside the container's filesystem at the mount target. If the service is declared read_only: true, compose cannot write those files and requires the source to come from a host file (file:), which is bind-mounted read-only instead.","triggerScenarios":"A service with read_only: true that mounts a secret or config defined via content:, via environment:, or otherwise resolved to inline content. The file-creation path requires a writable container filesystem, so compose errors naming the service and mount.","commonSituations":"Hardening a service by adding read_only: true without migrating its inline configs to files; using secrets: top-level entries with file: pointing at a real path works, but content: / env: variants do not.","solutions":["Define the secret/config with file: pointing at a host path so it is mounted rather than written.","Remove read_only: true (or scope it) if the service must use inline content secrets.","Use an external secret driver that surfaces the secret as a file compatible with read-only roots.","Ensure the file path exists and is readable by the daemon host user."],"exampleFix":"# before\nservices:\n  app:\n    read_only: true\n    secrets: [api_token]\nsecrets:\n  api_token:\n    environment: API_TOKEN   # must be written into container → error\n\n# after\nservices:\n  app:\n    read_only: true\n    secrets: [api_token]\nsecrets:\n  api_token:\n    file: ./secrets/api_token.txt","handlingStrategy":"validation","validationCode":"func readOnlySafeMounts(project *types.Project) error {\n\tfor name, svc := range project.Services {\n\t\tif !svc.ReadOnly {\n\t\t\tcontinue\n\t\t}\n\t\tfor _, sec := range svc.Secrets { checkFileSource(project.Secrets, sec, \"secret\", name) }\n\t\tfor _, cfg := range svc.Configs { checkFileSource(project.Configs, cfg, \"config\", name) }\n\t}\n\treturn nil // checkFileSource errors when source uses content:/environment: without file:\n}","typeGuard":null,"tryCatchPattern":"if err := compose.Up(ctx, project, opts); err != nil {\n    if strings.Contains(err.Error(), \"read-only service\") && strings.Contains(err.Error(), \"sole supported option\") {\n        // convert the named secret/config to file: or drop read_only\n    }\n    return err\n}","preventionTips":["Pair read_only: true services only with file-based secrets/configs.","Prefer file: sources in production compose files generally.","Test up on a staging project before rolling hardened read-only settings out."],"tags":["compose","secrets","configs","read-only","containers"],"backgroundTag":null,"analyzedSha":"ddc4b044b62e9f715212ea4143fa830fac76382f","analyzedAt":"2026-08-15T13:31:42.319Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}