NixOS caddyfile

Hey i am trying to configure the caddy service and I want to use an existing Caddyfile which is located on disk.

{
  config,
  pkgs,
  lib,
  ...
}:
{
  services.caddy = {
    enable = true;
    package = pkgs.caddy;
    configFile = pkgs.writeText "Caddyfile" (builtins.readFile ./Caddyfile);
  };
}

which results in:

error:
       … while calling the 'seq' builtin

         at /nix/store/l3amk5lsakpc93him5kry24kax23sn4h-source/lib/modules.nix:334:18:

          333|         options = checked options;
          334|         config = checked (removeAttrs config [ "_module" ]);
             |                  ^
          335|         _module = checked (config._module);

       … while evaluating a branch condition

         at /nix/store/l3amk5lsakpc93him5kry24kax23sn4h-source/lib/modules.nix:273:9:

          272|       checkUnmatched =
          273|         if config._module.check && config._module.freeformType == null && merged.unmatchedDefns != [] then
             |         ^
          274|           let

       … in the left operand of the AND (&&) operator

         at /nix/store/l3amk5lsakpc93him5kry24kax23sn4h-source/lib/modules.nix:273:72:

          272|       checkUnmatched =
          273|         if config._module.check && config._module.freeformType == null && merged.unmatchedDefns != [] then
             |                                                                        ^
          274|           let

       … in the left operand of the AND (&&) operator

         at /nix/store/l3amk5lsakpc93him5kry24kax23sn4h-source/lib/modules.nix:273:33:

          272|       checkUnmatched =
          273|         if config._module.check && config._module.freeformType == null && merged.unmatchedDefns != [] then
             |                                 ^
          274|           let

       … while evaluating a branch condition

         at /nix/store/l3amk5lsakpc93him5kry24kax23sn4h-source/lib/modules.nix:266:12:

          265|
          266|         in if declaredConfig._module.freeformType == null then declaredConfig
             |            ^
          267|           # Because all definitions that had an associated option ended in

       … from call site

         at /nix/store/l3amk5lsakpc93him5kry24kax23sn4h-source/lib/modules.nix:254:28:

          253|           # For definitions that have an associated option
          254|           declaredConfig = mapAttrsRecursiveCond (v: ! isOption v) (_: v: v.value) options;
             |                            ^
          255|

       … while calling 'mapAttrsRecursiveCond'

         at /nix/store/l3amk5lsakpc93him5kry24kax23sn4h-source/lib/attrsets.nix:1201:5:

         1200|     f:
         1201|     set:
             |     ^
         1202|     let

       … from call site

         at /nix/store/l3amk5lsakpc93him5kry24kax23sn4h-source/lib/modules.nix:246:33:

          245|           ({ inherit lib options config specialArgs; } // specialArgs);
          246|         in mergeModules prefix (reverseList collected);
             |                                 ^
          247|

       … while calling 'reverseList'

         at /nix/store/l3amk5lsakpc93him5kry24kax23sn4h-source/lib/lists.nix:1116:17:

         1115|   */
         1116|   reverseList = xs:
             |                 ^
         1117|     let l = length xs; in genList (n: elemAt xs (l - n - 1)) l;

       … from call site

         at /nix/store/l3amk5lsakpc93him5kry24kax23sn4h-source/lib/modules.nix:241:25:

          240|       merged =
          241|         let collected = collectModules
             |                         ^
          242|           class

       … while calling anonymous lambda

         at /nix/store/l3amk5lsakpc93him5kry24kax23sn4h-source/lib/modules.nix:457:37:

          456|
          457|     in modulesPath: initialModules: args:
             |                                     ^
          458|       filterModules modulesPath (collectStructuredModules unknownModule "" initialModules args);

       … from call site

         at /nix/store/l3amk5lsakpc93him5kry24kax23sn4h-source/lib/modules.nix:458:7:

          457|     in modulesPath: initialModules: args:
          458|       filterModules modulesPath (collectStructuredModules unknownModule "" initialModules args);
             |       ^
          459|

       … while calling 'filterModules'

         at /nix/store/l3amk5lsakpc93him5kry24kax23sn4h-source/lib/modules.nix:425:36:

          424|       # modules recursively. It returns the final list of unique-by-key modules
          425|       filterModules = modulesPath: { disabled, modules }:
             |                                    ^
          426|         let

       … while calling anonymous lambda

         at /nix/store/l3amk5lsakpc93him5kry24kax23sn4h-source/lib/modules.nix:451:31:

          450|           disabledKeys = concatMap ({ file, disabled }: map (moduleKey file) disabled) disabled;
          451|           keyFilter = filter (attrs: ! elem attrs.key disabledKeys);
             |                               ^
          452|         in map (attrs: attrs.module) (genericClosure {

       … from call site

         at /nix/store/l3amk5lsakpc93him5kry24kax23sn4h-source/lib/modules.nix:410:73:

          409|           };
          410|         in parentFile: parentKey: initialModules: args: collectResults (imap1 (n: x:
             |                                                                         ^
          411|           let

       … while calling 'imap1'

         at /nix/store/l3amk5lsakpc93him5kry24kax23sn4h-source/lib/lists.nix:334:14:

          333|   */
          334|   imap1 = f: list: genList (n: f (n + 1) (elemAt list n)) (length list);
             |              ^
          335|

       error: syntax error, unexpected ID, expecting '.' or '='

       at /nix/store/vwgh3zziyjk41mk1yxn54wpha6mwkifk-source/modules/services/Caddyfile:46:9:

           45| www.jellyfin.mydomain.org jellyfin.mydomain.org {
           46| 	import caddy_security.conf
             |         ^
           47| 	reverse_proxy localhost:8096

Why is the caddyfile treated as nix code and not as text file?
How to fix this?

Documentation says that it should be a path.

Have you tried:

    configFile = ./Caddyfile;

No still the same problem.

{
  config,
  pkgs,
  lib,
  ...
}:
{
  services.caddy = {
    enable = true;
    package = pkgs.caddy;
    configFile = ./Caddyfile;
  };
}

Did you git add . ?

yes i did git add the caddyfile however when i remove the import to the other file in the caddyfile it still throws the error as it probably assumes that the Caddyfile contains nix code.

error:
       … while calling the 'seq' builtin

         at /nix/store/l3amk5lsakpc93him5kry24kax23sn4h-source/lib/modules.nix:334:18:

          333|         options = checked options;
          334|         config = checked (removeAttrs config [ "_module" ]);
             |                  ^
          335|         _module = checked (config._module);

       … while evaluating a branch condition

         at /nix/store/l3amk5lsakpc93him5kry24kax23sn4h-source/lib/modules.nix:273:9:

          272|       checkUnmatched =
          273|         if config._module.check && config._module.freeformType == null && merged.unmatchedDefns != [] then
             |         ^
          274|           let

       … in the left operand of the AND (&&) operator

         at /nix/store/l3amk5lsakpc93him5kry24kax23sn4h-source/lib/modules.nix:273:72:

          272|       checkUnmatched =
          273|         if config._module.check && config._module.freeformType == null && merged.unmatchedDefns != [] then
             |                                                                        ^
          274|           let

       … in the left operand of the AND (&&) operator

         at /nix/store/l3amk5lsakpc93him5kry24kax23sn4h-source/lib/modules.nix:273:33:

          272|       checkUnmatched =
          273|         if config._module.check && config._module.freeformType == null && merged.unmatchedDefns != [] then
             |                                 ^
          274|           let

       … while evaluating a branch condition

         at /nix/store/l3amk5lsakpc93him5kry24kax23sn4h-source/lib/modules.nix:266:12:

          265|
          266|         in if declaredConfig._module.freeformType == null then declaredConfig
             |            ^
          267|           # Because all definitions that had an associated option ended in

       … from call site

         at /nix/store/l3amk5lsakpc93him5kry24kax23sn4h-source/lib/modules.nix:254:28:

          253|           # For definitions that have an associated option
          254|           declaredConfig = mapAttrsRecursiveCond (v: ! isOption v) (_: v: v.value) options;
             |                            ^
          255|

       … while calling 'mapAttrsRecursiveCond'

         at /nix/store/l3amk5lsakpc93him5kry24kax23sn4h-source/lib/attrsets.nix:1201:5:

         1200|     f:
         1201|     set:
             |     ^
         1202|     let

       … from call site

         at /nix/store/l3amk5lsakpc93him5kry24kax23sn4h-source/lib/modules.nix:246:33:

          245|           ({ inherit lib options config specialArgs; } // specialArgs);
          246|         in mergeModules prefix (reverseList collected);
             |                                 ^
          247|

       … while calling 'reverseList'

         at /nix/store/l3amk5lsakpc93him5kry24kax23sn4h-source/lib/lists.nix:1116:17:

         1115|   */
         1116|   reverseList = xs:
             |                 ^
         1117|     let l = length xs; in genList (n: elemAt xs (l - n - 1)) l;

       … from call site

         at /nix/store/l3amk5lsakpc93him5kry24kax23sn4h-source/lib/modules.nix:241:25:

          240|       merged =
          241|         let collected = collectModules
             |                         ^
          242|           class

       … while calling anonymous lambda

         at /nix/store/l3amk5lsakpc93him5kry24kax23sn4h-source/lib/modules.nix:457:37:

          456|
          457|     in modulesPath: initialModules: args:
             |                                     ^
          458|       filterModules modulesPath (collectStructuredModules unknownModule "" initialModules args);

       … from call site

         at /nix/store/l3amk5lsakpc93him5kry24kax23sn4h-source/lib/modules.nix:458:7:

          457|     in modulesPath: initialModules: args:
          458|       filterModules modulesPath (collectStructuredModules unknownModule "" initialModules args);
             |       ^
          459|

       … while calling 'filterModules'

         at /nix/store/l3amk5lsakpc93him5kry24kax23sn4h-source/lib/modules.nix:425:36:

          424|       # modules recursively. It returns the final list of unique-by-key modules
          425|       filterModules = modulesPath: { disabled, modules }:
             |                                    ^
          426|         let

       … while calling anonymous lambda

         at /nix/store/l3amk5lsakpc93him5kry24kax23sn4h-source/lib/modules.nix:451:31:

          450|           disabledKeys = concatMap ({ file, disabled }: map (moduleKey file) disabled) disabled;
          451|           keyFilter = filter (attrs: ! elem attrs.key disabledKeys);
             |                               ^
          452|         in map (attrs: attrs.module) (genericClosure {

       … from call site

         at /nix/store/l3amk5lsakpc93him5kry24kax23sn4h-source/lib/modules.nix:410:73:

          409|           };
          410|         in parentFile: parentKey: initialModules: args: collectResults (imap1 (n: x:
             |                                                                         ^
          411|           let

       … while calling 'imap1'

         at /nix/store/l3amk5lsakpc93him5kry24kax23sn4h-source/lib/lists.nix:334:14:

          333|   */
          334|   imap1 = f: list: genList (n: f (n + 1) (elemAt list n)) (length list);
             |              ^
          335|

       error: syntax error, unexpected URI, expecting '.' or '='

       at /nix/store/h8r4fjcqjxnkf4160vnwqk1nigjvb8hl-source/modules/services/Caddyfile:47:16:

           46| 	#import caddy_security.conf
           47| 	reverse_proxy localhost:8096
             |                ^
           48| }

Are you sure your Caddyfile doesn’t have any syntax errors itself?

it probably assumes that the Caddyfile contains nix code.

Nix doesn’t attempt to parse the file, it just drops it in to /etc/ : nixpkgs/nixos/modules/services/web-servers/caddy/default.nix at 944b2aea7f0a2d7c79f72468106bc5510cbf5101 · NixOS/nixpkgs · GitHub

This looks like it’s an error from Nix itself, it’s trying to import the Caddyfile as a Nix file for some reason?

Pretty sure this is the whole debacle (imo) with the --config flag in Caddy and the fact that it doesn’t expect a Caddyfile. Caddy has its Caddyfile format, but also a more verbose JSON format. I think that’s at the heart of what you’re hitting here.

As aos said above, the module just uses environment.etc.<name>.source, the file contents aren’t read into the nix evaluator, only the path would be.

ExecStart does not get executed at eval time.

@SZanko can you provide an example caddyfile? I can test this out on my end.

Example Caddyfile

www.jellyfin.mydomain.org jellyfin.mydomain.org {
	reverse_proxy localhost:8096
}

results in this error

error:
       … while calling the 'seq' builtin

         at /nix/store/l3amk5lsakpc93him5kry24kax23sn4h-source/lib/modules.nix:334:18:

          333|         options = checked options;
          334|         config = checked (removeAttrs config [ "_module" ]);
             |                  ^
          335|         _module = checked (config._module);

       … while evaluating a branch condition

         at /nix/store/l3amk5lsakpc93him5kry24kax23sn4h-source/lib/modules.nix:273:9:

          272|       checkUnmatched =
          273|         if config._module.check && config._module.freeformType == null && merged.unmatchedDefns != [] then
             |         ^
          274|           let

       … in the left operand of the AND (&&) operator

         at /nix/store/l3amk5lsakpc93him5kry24kax23sn4h-source/lib/modules.nix:273:72:

          272|       checkUnmatched =
          273|         if config._module.check && config._module.freeformType == null && merged.unmatchedDefns != [] then
             |                                                                        ^
          274|           let

       … in the left operand of the AND (&&) operator

         at /nix/store/l3amk5lsakpc93him5kry24kax23sn4h-source/lib/modules.nix:273:33:

          272|       checkUnmatched =
          273|         if config._module.check && config._module.freeformType == null && merged.unmatchedDefns != [] then
             |                                 ^
          274|           let

       … while evaluating a branch condition

         at /nix/store/l3amk5lsakpc93him5kry24kax23sn4h-source/lib/modules.nix:266:12:

          265|
          266|         in if declaredConfig._module.freeformType == null then declaredConfig
             |            ^
          267|           # Because all definitions that had an associated option ended in

       … from call site

         at /nix/store/l3amk5lsakpc93him5kry24kax23sn4h-source/lib/modules.nix:254:28:

          253|           # For definitions that have an associated option
          254|           declaredConfig = mapAttrsRecursiveCond (v: ! isOption v) (_: v: v.value) options;
             |                            ^
          255|

       … while calling 'mapAttrsRecursiveCond'

         at /nix/store/l3amk5lsakpc93him5kry24kax23sn4h-source/lib/attrsets.nix:1201:5:

         1200|     f:
         1201|     set:
             |     ^
         1202|     let

       … from call site

         at /nix/store/l3amk5lsakpc93him5kry24kax23sn4h-source/lib/modules.nix:246:33:

          245|           ({ inherit lib options config specialArgs; } // specialArgs);
          246|         in mergeModules prefix (reverseList collected);
             |                                 ^
          247|

       … while calling 'reverseList'

         at /nix/store/l3amk5lsakpc93him5kry24kax23sn4h-source/lib/lists.nix:1116:17:

         1115|   */
         1116|   reverseList = xs:
             |                 ^
         1117|     let l = length xs; in genList (n: elemAt xs (l - n - 1)) l;

       … from call site

         at /nix/store/l3amk5lsakpc93him5kry24kax23sn4h-source/lib/modules.nix:241:25:

          240|       merged =
          241|         let collected = collectModules
             |                         ^
          242|           class

       … while calling anonymous lambda

         at /nix/store/l3amk5lsakpc93him5kry24kax23sn4h-source/lib/modules.nix:457:37:

          456|
          457|     in modulesPath: initialModules: args:
             |                                     ^
          458|       filterModules modulesPath (collectStructuredModules unknownModule "" initialModules args);

       … from call site

         at /nix/store/l3amk5lsakpc93him5kry24kax23sn4h-source/lib/modules.nix:458:7:

          457|     in modulesPath: initialModules: args:
          458|       filterModules modulesPath (collectStructuredModules unknownModule "" initialModules args);
             |       ^
          459|

       … while calling 'filterModules'

         at /nix/store/l3amk5lsakpc93him5kry24kax23sn4h-source/lib/modules.nix:425:36:

          424|       # modules recursively. It returns the final list of unique-by-key modules
          425|       filterModules = modulesPath: { disabled, modules }:
             |                                    ^
          426|         let

       … while calling anonymous lambda

         at /nix/store/l3amk5lsakpc93him5kry24kax23sn4h-source/lib/modules.nix:451:31:

          450|           disabledKeys = concatMap ({ file, disabled }: map (moduleKey file) disabled) disabled;
          451|           keyFilter = filter (attrs: ! elem attrs.key disabledKeys);
             |                               ^
          452|         in map (attrs: attrs.module) (genericClosure {

       … from call site

         at /nix/store/l3amk5lsakpc93him5kry24kax23sn4h-source/lib/modules.nix:410:73:

          409|           };
          410|         in parentFile: parentKey: initialModules: args: collectResults (imap1 (n: x:
             |                                                                         ^
          411|           let

       … while calling 'imap1'

         at /nix/store/l3amk5lsakpc93him5kry24kax23sn4h-source/lib/lists.nix:334:14:

          333|   */
          334|   imap1 = f: list: genList (n: f (n + 1) (elemAt list n)) (length list);
             |              ^
          335|

       error: syntax error, unexpected URI, expecting '.' or '='

       at /nix/store/4rrlhycpfbqfll85qw55n8wbh8wbwmxw-source/modules/services/Caddyfile:2:16:

            1| www.jellyfin.mydomain.org jellyfin.mydomain.org {
            2| 	reverse_proxy localhost:8096
             |                ^
            3| }

Errors in the caddyfile should result in a systemd service runtime error and not a build error I think.
Also my original caddyfile ran on archlinux without any problem so I am pretty sure that there isn’t any syntax error in it

I’m far from expert at nix, but somehow it looks like your caddyfile is getting imported as a module? I think it must be a problem with context code outside of the snippet you posted … how are you calling it?

  imports =
    [ 

      outputs.nixosModules.default
      # Include the results of the hardware scan.
      ./hardware-configuration.nix
      ./main-user.nix
    ];
  myNixOS.services.caddy.enable = true;
sudo nixos-rebuild switch --flake .#desktop --show-trace

Correct.

I tried the following:

# flake.nix
{
  inputs = {
    nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-unstable";
  };

  outputs =
    { nixpkgs, ... }:
    {
      nixosConfigurations.test = nixpkgs.lib.nixosSystem {
        modules = [
          (
            { modulesPath, ... }:
            {
              imports = [ "${modulesPath}/profiles/minimal.nix" ];

              boot.loader.grub.enable = false;
              fileSystems."/".device = "nodev";
              nixpkgs.hostPlatform = "x86_64-linux";
              system.stateVersion = "24.05";

              # custom config here
              services.caddy = {
                enable = true;
                configFile = ./caddyfile;
              };
            }
          )
        ];
      };
    };
}

with caddyfile containing:

www.mydomain.org mydomain.org {
  reverse_proxy localhost:1111
}

And nixos-rebuild build '.#test' works, so you must be doing something wrong, though I can’t guess further without seeing the full config or some reproducer.

1 Like

The error comes from Nix failing to parse the reverse_proxy section an attribute set, which could be caused for example by doing import ./Caddyfile instead of ./Caddyfile.

@SZanko do you happen to have your configuration in a public repository we can look into?

1 Like

Yes that was the problem that I had I had a snippet which imported any file in my service directory as nix → I moved the config files to another directory and the problem was solved

2 Likes